Merge changes I7bb0b559,If147df2d,I25d7d590
* changes:
libsnapshot: Skip initializing snapshot if not created.
libdm: Fix DmTable::num_sectors
Fix fds libdm_test
diff --git a/adb/Android.bp b/adb/Android.bp
index 3dc70b5..2f9c8fc 100644
--- a/adb/Android.bp
+++ b/adb/Android.bp
@@ -27,7 +27,6 @@
"-DADB_HOST=1", // overridden by adbd_defaults
"-DALLOW_ADBD_ROOT=0", // overridden by adbd_defaults
"-DANDROID_BASE_UNIQUE_FD_DISABLE_IMPLICIT_CONVERSION=1",
- "-DENABLE_FASTDEPLOY=1", // enable fast deploy
],
cpp_std: "experimental",
@@ -691,6 +690,7 @@
name: "libfastdeploy_host",
defaults: ["adb_defaults"],
srcs: [
+ "fastdeploy/deploypatchgenerator/apk_archive.cpp",
"fastdeploy/deploypatchgenerator/deploy_patch_generator.cpp",
"fastdeploy/deploypatchgenerator/patch_utils.cpp",
"fastdeploy/proto/ApkEntry.proto",
@@ -727,6 +727,7 @@
name: "fastdeploy_test",
defaults: ["adb_defaults"],
srcs: [
+ "fastdeploy/deploypatchgenerator/apk_archive_test.cpp",
"fastdeploy/deploypatchgenerator/deploy_patch_generator_test.cpp",
"fastdeploy/deploypatchgenerator/patch_utils_test.cpp",
],
@@ -754,6 +755,9 @@
},
},
data: [
+ "fastdeploy/testdata/rotating_cube-metadata-release.data",
"fastdeploy/testdata/rotating_cube-release.apk",
+ "fastdeploy/testdata/sample.apk",
+ "fastdeploy/testdata/sample.cd",
],
}
diff --git a/adb/client/adb_install.cpp b/adb/client/adb_install.cpp
index 042a2d6..73dcde1 100644
--- a/adb/client/adb_install.cpp
+++ b/adb/client/adb_install.cpp
@@ -37,9 +37,7 @@
#include "commandline.h"
#include "fastdeploy.h"
-#if defined(ENABLE_FASTDEPLOY)
static constexpr int kFastDeployMinApi = 24;
-#endif
namespace {
@@ -146,15 +144,7 @@
*buf = '\0';
}
-#if defined(ENABLE_FASTDEPLOY)
-static int delete_device_patch_file(const char* apkPath) {
- std::string patchDevicePath = get_patch_path(apkPath);
- return delete_device_file(patchDevicePath);
-}
-#endif
-
-static int install_app_streamed(int argc, const char** argv, bool use_fastdeploy,
- bool use_localagent) {
+static int install_app_streamed(int argc, const char** argv, bool use_fastdeploy) {
printf("Performing Streamed Install\n");
// The last argument must be the APK file
@@ -176,31 +166,15 @@
error_exit("--fastdeploy doesn't support .apex files");
}
- if (use_fastdeploy == true) {
-#if defined(ENABLE_FASTDEPLOY)
- TemporaryFile metadataTmpFile;
- std::string patchTmpFilePath;
- {
- TemporaryFile patchTmpFile;
- patchTmpFile.DoNotRemove();
- patchTmpFilePath = patchTmpFile.path;
+ if (use_fastdeploy) {
+ auto metadata = extract_metadata(file);
+ if (metadata.has_value()) {
+ // pass all but 1st (command) and last (apk path) parameters through to pm for
+ // session creation
+ std::vector<const char*> pm_args{argv + 1, argv + argc - 1};
+ auto patchFd = install_patch(pm_args.size(), pm_args.data());
+ return stream_patch(file, std::move(metadata.value()), std::move(patchFd));
}
-
- FILE* metadataFile = fopen(metadataTmpFile.path, "wb");
- extract_metadata(file, metadataFile);
- fclose(metadataFile);
-
- create_patch(file, metadataTmpFile.path, patchTmpFilePath.c_str());
- // pass all but 1st (command) and last (apk path) parameters through to pm for
- // session creation
- std::vector<const char*> pm_args{argv + 1, argv + argc - 1};
- install_patch(file, patchTmpFilePath.c_str(), pm_args.size(), pm_args.data());
- adb_unlink(patchTmpFilePath.c_str());
- delete_device_patch_file(file);
- return 0;
-#else
- error_exit("fastdeploy is disabled");
-#endif
}
struct stat sb;
@@ -265,8 +239,7 @@
return 1;
}
-static int install_app_legacy(int argc, const char** argv, bool use_fastdeploy,
- bool use_localagent) {
+static int install_app_legacy(int argc, const char** argv, bool use_fastdeploy) {
printf("Performing Push Install\n");
// Find last APK argument.
@@ -287,35 +260,26 @@
int result = -1;
std::vector<const char*> apk_file = {argv[last_apk]};
std::string apk_dest = "/data/local/tmp/" + android::base::Basename(argv[last_apk]);
-
- if (use_fastdeploy == true) {
-#if defined(ENABLE_FASTDEPLOY)
- TemporaryFile metadataTmpFile;
- TemporaryFile patchTmpFile;
-
- FILE* metadataFile = fopen(metadataTmpFile.path, "wb");
- extract_metadata(apk_file[0], metadataFile);
- fclose(metadataFile);
-
- create_patch(apk_file[0], metadataTmpFile.path, patchTmpFile.path);
- apply_patch_on_device(apk_file[0], patchTmpFile.path, apk_dest.c_str());
-#else
- error_exit("fastdeploy is disabled");
-#endif
- } else {
- if (!do_sync_push(apk_file, apk_dest.c_str(), false)) goto cleanup_apk;
- }
-
argv[last_apk] = apk_dest.c_str(); /* destination name, not source location */
- result = pm_command(argc, argv);
-cleanup_apk:
- if (use_fastdeploy == true) {
-#if defined(ENABLE_FASTDEPLOY)
- delete_device_patch_file(apk_file[0]);
-#endif
+ if (use_fastdeploy) {
+ auto metadata = extract_metadata(apk_file[0]);
+ if (metadata.has_value()) {
+ auto patchFd = apply_patch_on_device(apk_dest.c_str());
+ int status = stream_patch(apk_file[0], std::move(metadata.value()), std::move(patchFd));
+
+ result = pm_command(argc, argv);
+ delete_device_file(apk_dest);
+
+ return status;
+ }
}
- delete_device_file(apk_dest);
+
+ if (do_sync_push(apk_file, apk_dest.c_str(), false)) {
+ result = pm_command(argc, argv);
+ delete_device_file(apk_dest);
+ }
+
return result;
}
@@ -324,7 +288,6 @@
InstallMode installMode = INSTALL_DEFAULT;
bool use_fastdeploy = false;
bool is_reinstall = false;
- bool use_localagent = false;
FastDeploy_AgentUpdateStrategy agent_update_strategy = FastDeploy_AgentUpdateDifferentVersion;
for (int i = 1; i < argc; i++) {
@@ -353,11 +316,6 @@
} else if (!strcmp(argv[i], "--version-check-agent")) {
processedArgIndicies.push_back(i);
agent_update_strategy = FastDeploy_AgentUpdateDifferentVersion;
-#ifndef _WIN32
- } else if (!strcmp(argv[i], "--local-agent")) {
- processedArgIndicies.push_back(i);
- use_localagent = true;
-#endif
}
}
@@ -369,14 +327,13 @@
error_exit("Attempting to use streaming install on unsupported device");
}
-#if defined(ENABLE_FASTDEPLOY)
- if (use_fastdeploy == true && get_device_api_level() < kFastDeployMinApi) {
+ if (use_fastdeploy && get_device_api_level() < kFastDeployMinApi) {
printf("Fast Deploy is only compatible with devices of API version %d or higher, "
"ignoring.\n",
kFastDeployMinApi);
use_fastdeploy = false;
}
-#endif
+ fastdeploy_set_agent_update_strategy(agent_update_strategy);
std::vector<const char*> passthrough_argv;
for (int i = 0; i < argc; i++) {
@@ -389,26 +346,13 @@
error_exit("install requires an apk argument");
}
- if (use_fastdeploy == true) {
-#if defined(ENABLE_FASTDEPLOY)
- fastdeploy_set_local_agent(use_localagent);
- update_agent(agent_update_strategy);
-
- // The last argument must be the APK file
- const char* file = passthrough_argv.back();
- use_fastdeploy = find_package(file);
-#else
- error_exit("fastdeploy is disabled");
-#endif
- }
-
switch (installMode) {
case INSTALL_PUSH:
return install_app_legacy(passthrough_argv.size(), passthrough_argv.data(),
- use_fastdeploy, use_localagent);
+ use_fastdeploy);
case INSTALL_STREAM:
return install_app_streamed(passthrough_argv.size(), passthrough_argv.data(),
- use_fastdeploy, use_localagent);
+ use_fastdeploy);
case INSTALL_DEFAULT:
default:
return 1;
diff --git a/adb/client/commandline.cpp b/adb/client/commandline.cpp
index a0818d3..0ffdbc2 100644
--- a/adb/client/commandline.cpp
+++ b/adb/client/commandline.cpp
@@ -255,13 +255,8 @@
}
#endif
-// Reads from |fd| and prints received data. If |use_shell_protocol| is true
-// this expects that incoming data will use the shell protocol, in which case
-// stdout/stderr are routed independently and the remote exit code will be
-// returned.
-// if |callback| is non-null, stdout/stderr output will be handled by it.
-int read_and_dump(borrowed_fd fd, bool use_shell_protocol = false,
- StandardStreamsCallbackInterface* callback = &DEFAULT_STANDARD_STREAMS_CALLBACK) {
+int read_and_dump(borrowed_fd fd, bool use_shell_protocol,
+ StandardStreamsCallbackInterface* callback) {
int exit_code = 0;
if (fd < 0) return exit_code;
diff --git a/adb/client/commandline.h b/adb/client/commandline.h
index cd5933a..ab77b29 100644
--- a/adb/client/commandline.h
+++ b/adb/client/commandline.h
@@ -109,6 +109,14 @@
const std::string& command, bool disable_shell_protocol = false,
StandardStreamsCallbackInterface* callback = &DEFAULT_STANDARD_STREAMS_CALLBACK);
+// Reads from |fd| and prints received data. If |use_shell_protocol| is true
+// this expects that incoming data will use the shell protocol, in which case
+// stdout/stderr are routed independently and the remote exit code will be
+// returned.
+// if |callback| is non-null, stdout/stderr output will be handled by it.
+int read_and_dump(borrowed_fd fd, bool use_shell_protocol = false,
+ StandardStreamsCallbackInterface* callback = &DEFAULT_STANDARD_STREAMS_CALLBACK);
+
// Connects to the device "abb" service with |command| and returns the fd.
template <typename ContainerT>
unique_fd send_abb_exec_command(const ContainerT& command_args, std::string* error) {
diff --git a/adb/client/fastdeploy.cpp b/adb/client/fastdeploy.cpp
index fbae219..bdc9e56 100644
--- a/adb/client/fastdeploy.cpp
+++ b/adb/client/fastdeploy.cpp
@@ -30,112 +30,114 @@
#include "deployagent.inc" // Generated include via build rule.
#include "deployagentscript.inc" // Generated include via build rule.
#include "fastdeploy/deploypatchgenerator/deploy_patch_generator.h"
+#include "fastdeploy/deploypatchgenerator/patch_utils.h"
+#include "fastdeploy/proto/ApkEntry.pb.h"
#include "fastdeploycallbacks.h"
#include "sysdeps.h"
#include "adb_utils.h"
-static constexpr long kRequiredAgentVersion = 0x00000002;
+static constexpr long kRequiredAgentVersion = 0x00000003;
-static constexpr const char* kDeviceAgentPath = "/data/local/tmp/";
+static constexpr int kPackageMissing = 3;
+static constexpr int kInvalidAgentVersion = 4;
+
static constexpr const char* kDeviceAgentFile = "/data/local/tmp/deployagent.jar";
static constexpr const char* kDeviceAgentScript = "/data/local/tmp/deployagent";
-static bool g_use_localagent = false;
+static constexpr bool g_verbose_timings = false;
+static FastDeploy_AgentUpdateStrategy g_agent_update_strategy =
+ FastDeploy_AgentUpdateDifferentVersion;
-long get_agent_version() {
- std::vector<char> versionOutputBuffer;
- std::vector<char> versionErrorBuffer;
+using APKMetaData = com::android::fastdeploy::APKMetaData;
- int statusCode = capture_shell_command("/data/local/tmp/deployagent version",
- &versionOutputBuffer, &versionErrorBuffer);
- long version = -1;
+namespace {
- if (statusCode == 0 && versionOutputBuffer.size() > 0) {
- version = strtol((char*)versionOutputBuffer.data(), NULL, 16);
+struct TimeReporter {
+ TimeReporter(const char* label) : label_(label) {}
+ ~TimeReporter() {
+ if (g_verbose_timings) {
+ auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
+ std::chrono::steady_clock::now() - start_);
+ fprintf(stderr, "%s finished in %lldms\n", label_,
+ static_cast<long long>(duration.count()));
+ }
}
- return version;
-}
+ private:
+ const char* label_;
+ std::chrono::steady_clock::time_point start_ = std::chrono::steady_clock::now();
+};
+#define REPORT_FUNC_TIME() TimeReporter reporter(__func__)
+
+struct FileDeleter {
+ FileDeleter(const char* path) : path_(path) {}
+ ~FileDeleter() { adb_unlink(path_); }
+
+ private:
+ const char* const path_;
+};
+
+} // namespace
int get_device_api_level() {
- std::vector<char> sdkVersionOutputBuffer;
- std::vector<char> sdkVersionErrorBuffer;
+ REPORT_FUNC_TIME();
+ std::vector<char> sdk_version_output_buffer;
+ std::vector<char> sdk_version_error_buffer;
int api_level = -1;
- int statusCode = capture_shell_command("getprop ro.build.version.sdk", &sdkVersionOutputBuffer,
- &sdkVersionErrorBuffer);
- if (statusCode == 0 && sdkVersionOutputBuffer.size() > 0) {
- api_level = strtol((char*)sdkVersionOutputBuffer.data(), NULL, 10);
+ int statusCode = capture_shell_command("getprop ro.build.version.sdk",
+ &sdk_version_output_buffer, &sdk_version_error_buffer);
+ if (statusCode == 0 && sdk_version_output_buffer.size() > 0) {
+ api_level = strtol((char*)sdk_version_output_buffer.data(), NULL, 10);
}
return api_level;
}
-void fastdeploy_set_local_agent(bool use_localagent) {
- g_use_localagent = use_localagent;
+void fastdeploy_set_agent_update_strategy(FastDeploy_AgentUpdateStrategy agent_update_strategy) {
+ g_agent_update_strategy = agent_update_strategy;
}
-static bool deploy_agent(bool checkTimeStamps) {
+static void push_to_device(const void* data, size_t byte_count, const char* dst, bool sync) {
std::vector<const char*> srcs;
- // TODO: Deploy agent from bin2c directly instead of writing to disk first.
- TemporaryFile tempAgent;
- android::base::WriteFully(tempAgent.fd, kDeployAgent, sizeof(kDeployAgent));
- srcs.push_back(tempAgent.path);
- if (!do_sync_push(srcs, kDeviceAgentFile, checkTimeStamps)) {
+ {
+ TemporaryFile temp;
+ android::base::WriteFully(temp.fd, data, byte_count);
+ srcs.push_back(temp.path);
+
+ // On Windows, the file needs to be flushed before pushing to device.
+ // closing the file flushes its content, but we still need to remove it after push.
+ // FileDeleter does exactly that.
+ temp.DoNotRemove();
+ }
+ FileDeleter temp_deleter(srcs.back());
+
+ if (!do_sync_push(srcs, dst, sync)) {
error_exit("Failed to push fastdeploy agent to device.");
}
- srcs.clear();
- // TODO: Deploy agent from bin2c directly instead of writing to disk first.
- TemporaryFile tempAgentScript;
- android::base::WriteFully(tempAgentScript.fd, kDeployAgentScript, sizeof(kDeployAgentScript));
- srcs.push_back(tempAgentScript.path);
- if (!do_sync_push(srcs, kDeviceAgentScript, checkTimeStamps)) {
- error_exit("Failed to push fastdeploy agent script to device.");
- }
- srcs.clear();
+}
+
+static bool deploy_agent(bool check_time_stamps) {
+ REPORT_FUNC_TIME();
+
+ push_to_device(kDeployAgent, sizeof(kDeployAgent), kDeviceAgentFile, check_time_stamps);
+ push_to_device(kDeployAgentScript, sizeof(kDeployAgentScript), kDeviceAgentScript,
+ check_time_stamps);
+
// on windows the shell script might have lost execute permission
// so need to set this explicitly
const char* kChmodCommandPattern = "chmod 777 %s";
- std::string chmodCommand =
+ std::string chmod_command =
android::base::StringPrintf(kChmodCommandPattern, kDeviceAgentScript);
- int ret = send_shell_command(chmodCommand);
+ int ret = send_shell_command(chmod_command);
if (ret != 0) {
- error_exit("Error executing %s returncode: %d", chmodCommand.c_str(), ret);
+ error_exit("Error executing %s returncode: %d", chmod_command.c_str(), ret);
}
return true;
}
-void update_agent(FastDeploy_AgentUpdateStrategy agentUpdateStrategy) {
- long agent_version = get_agent_version();
- switch (agentUpdateStrategy) {
- case FastDeploy_AgentUpdateAlways:
- deploy_agent(false);
- break;
- case FastDeploy_AgentUpdateNewerTimeStamp:
- deploy_agent(true);
- break;
- case FastDeploy_AgentUpdateDifferentVersion:
- if (agent_version != kRequiredAgentVersion) {
- if (agent_version < 0) {
- printf("Could not detect agent on device, deploying\n");
- } else {
- printf("Device agent version is (%ld), (%ld) is required, re-deploying\n",
- agent_version, kRequiredAgentVersion);
- }
- deploy_agent(false);
- }
- break;
- }
-
- agent_version = get_agent_version();
- if (agent_version != kRequiredAgentVersion) {
- error_exit("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) {
@@ -147,29 +149,29 @@
return utf8;
}
-static std::string get_packagename_from_apk(const char* apkPath) {
+static std::string get_package_name_from_apk(const char* apk_path) {
#undef open
- std::unique_ptr<android::ZipFileRO> zipFile(android::ZipFileRO::open(apkPath));
+ std::unique_ptr<android::ZipFileRO> zip_file((android::ZipFileRO::open)(apk_path));
#define open ___xxx_unix_open
- if (zipFile == nullptr) {
- perror_exit("Could not open %s", apkPath);
+ if (zip_file == nullptr) {
+ perror_exit("Could not open %s", apk_path);
}
- android::ZipEntryRO entry = zipFile->findEntryByName("AndroidManifest.xml");
+ android::ZipEntryRO entry = zip_file->findEntryByName("AndroidManifest.xml");
if (entry == nullptr) {
- error_exit("Could not find AndroidManifest.xml inside %s", apkPath);
+ error_exit("Could not find AndroidManifest.xml inside %s", apk_path);
}
uint32_t manifest_len = 0;
- if (!zipFile->getEntryInfo(entry, NULL, &manifest_len, NULL, NULL, NULL, NULL)) {
- error_exit("Could not read AndroidManifest.xml inside %s", apkPath);
+ if (!zip_file->getEntryInfo(entry, NULL, &manifest_len, NULL, NULL, NULL, NULL)) {
+ error_exit("Could not read AndroidManifest.xml inside %s", apk_path);
}
std::vector<char> manifest_data(manifest_len);
- if (!zipFile->uncompressEntry(entry, manifest_data.data(), manifest_len)) {
- error_exit("Could not uncompress AndroidManifest.xml inside %s", apkPath);
+ if (!zip_file->uncompressEntry(entry, manifest_data.data(), manifest_len)) {
+ error_exit("Could not uncompress AndroidManifest.xml inside %s", apk_path);
}
android::ResXMLTree tree;
android::status_t setto_status = tree.setTo(manifest_data.data(), manifest_len, true);
if (setto_status != android::OK) {
- error_exit("Could not parse AndroidManifest.xml inside %s", apkPath);
+ error_exit("Could not parse AndroidManifest.xml inside %s", apk_path);
}
android::ResXMLParser::event_code_t code;
while ((code = tree.next()) != android::ResXMLParser::BAD_DOCUMENT &&
@@ -210,80 +212,97 @@
break;
}
}
- error_exit("Could not find package name tag in AndroidManifest.xml inside %s", apkPath);
+ error_exit("Could not find package name tag in AndroidManifest.xml inside %s", apk_path);
}
-void extract_metadata(const char* apkPath, FILE* outputFp) {
- 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());
+static long parse_agent_version(const std::vector<char>& version_buffer) {
+ long version = -1;
+ if (!version_buffer.empty()) {
+ version = strtol((char*)version_buffer.data(), NULL, 16);
+ }
+ return version;
+}
- std::vector<char> extractErrorBuffer;
- DeployAgentFileCallback cb(outputFp, &extractErrorBuffer);
- int returnCode = send_shell_command(extractCommand, false, &cb);
+static void update_agent_if_necessary() {
+ switch (g_agent_update_strategy) {
+ case FastDeploy_AgentUpdateAlways:
+ deploy_agent(/*check_time_stamps=*/false);
+ break;
+ case FastDeploy_AgentUpdateNewerTimeStamp:
+ deploy_agent(/*check_time_stamps=*/true);
+ break;
+ default:
+ break;
+ }
+}
+
+std::optional<APKMetaData> extract_metadata(const char* apk_path) {
+ // Update agent if there is a command line argument forcing to do so.
+ update_agent_if_necessary();
+
+ REPORT_FUNC_TIME();
+
+ std::string package_name = get_package_name_from_apk(apk_path);
+
+ // Dump apk command checks the required vs current agent version and if they match then returns
+ // the APK dump for package. Doing this in a single call saves round-trip and agent launch time.
+ constexpr const char* kAgentDumpCommandPattern = "/data/local/tmp/deployagent dump %ld %s";
+ std::string dump_command = android::base::StringPrintf(
+ kAgentDumpCommandPattern, kRequiredAgentVersion, package_name.c_str());
+
+ std::vector<char> dump_out_buffer;
+ std::vector<char> dump_error_buffer;
+ int returnCode =
+ capture_shell_command(dump_command.c_str(), &dump_out_buffer, &dump_error_buffer);
+ if (returnCode >= kInvalidAgentVersion) {
+ // Agent has wrong version or missing.
+ long agent_version = parse_agent_version(dump_out_buffer);
+ if (agent_version < 0) {
+ printf("Could not detect agent on device, deploying\n");
+ } else {
+ printf("Device agent version is (%ld), (%ld) is required, re-deploying\n",
+ agent_version, kRequiredAgentVersion);
+ }
+ deploy_agent(/*check_time_stamps=*/false);
+
+ // Retry with new agent.
+ dump_out_buffer.clear();
+ dump_error_buffer.clear();
+ returnCode =
+ capture_shell_command(dump_command.c_str(), &dump_out_buffer, &dump_error_buffer);
+ }
if (returnCode != 0) {
- fprintf(stderr, "Executing %s returned %d\n", extractCommand.c_str(), returnCode);
- fprintf(stderr, "%*s\n", int(extractErrorBuffer.size()), extractErrorBuffer.data());
+ if (returnCode == kInvalidAgentVersion) {
+ long agent_version = parse_agent_version(dump_out_buffer);
+ error_exit(
+ "After update agent version remains incorrect! Expected %ld but version is %ld",
+ kRequiredAgentVersion, agent_version);
+ }
+ if (returnCode == kPackageMissing) {
+ fprintf(stderr, "Package %s not found, falling back to install\n",
+ package_name.c_str());
+ return {};
+ }
+ fprintf(stderr, "Executing %s returned %d\n", dump_command.c_str(), returnCode);
+ fprintf(stderr, "%*s\n", int(dump_error_buffer.size()), dump_error_buffer.data());
error_exit("Aborting");
}
+
+ com::android::fastdeploy::APKDump dump;
+ if (!dump.ParseFromArray(dump_out_buffer.data(), dump_out_buffer.size())) {
+ fprintf(stderr, "Can't parse output of %s\n", dump_command.c_str());
+ error_exit("Aborting");
+ }
+
+ return PatchUtils::GetDeviceAPKMetaData(dump);
}
-void create_patch(const char* apkPath, const char* metadataPath, const char* patchPath) {
- DeployPatchGenerator generator(false);
- unique_fd patchFd(adb_open(patchPath, O_WRONLY | O_CREAT | O_CLOEXEC));
- if (patchFd < 0) {
- perror_exit("adb: failed to create %s", patchPath);
- }
- bool success = generator.CreatePatch(apkPath, metadataPath, patchFd);
- if (!success) {
- error_exit("Failed to create patch for %s", apkPath);
- }
-}
+unique_fd install_patch(int argc, const char** argv) {
+ REPORT_FUNC_TIME();
+ constexpr char kAgentApplyServicePattern[] = "shell:/data/local/tmp/deployagent apply - -pm %s";
-std::string get_patch_path(const char* apkPath) {
- std::string packageName = get_packagename_from_apk(apkPath);
- std::string patchDevicePath =
- android::base::StringPrintf("%s%s.patch", kDeviceAgentPath, packageName.c_str());
- return patchDevicePath;
-}
-
-void apply_patch_on_device(const char* apkPath, const char* patchPath, const char* outputPath) {
- 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};
- bool push_ok = do_sync_push(srcs, patchDevicePath.c_str(), false);
- if (!push_ok) {
- error_exit("Error pushing %s to %s returned", patchPath, patchDevicePath.c_str());
- }
-
- std::string applyPatchCommand =
- android::base::StringPrintf(kAgentApplyCommandPattern.c_str(), packageName.c_str(),
- patchDevicePath.c_str(), outputPath);
-
- int returnCode = send_shell_command(applyPatchCommand);
- if (returnCode != 0) {
- error_exit("Executing %s returned %d", applyPatchCommand.c_str(), returnCode);
- }
-}
-
-void install_patch(const char* apkPath, const char* patchPath, int argc, const char** argv) {
- const std::string kAgentApplyCommandPattern = "/data/local/tmp/deployagent apply %s %s -pm %s";
- std::string packageName = get_packagename_from_apk(apkPath);
-
- std::string patchDevicePath =
- android::base::StringPrintf("%s%s.patch", kDeviceAgentPath, packageName.c_str());
-
- std::vector<const char*> srcs{patchPath};
- bool push_ok = do_sync_push(srcs, patchDevicePath.c_str(), false);
- if (!push_ok) {
- error_exit("Error pushing %s to %s returned", patchPath, patchDevicePath.c_str());
- }
-
- std::vector<unsigned char> applyOutputBuffer;
- std::vector<unsigned char> applyErrorBuffer;
+ std::vector<unsigned char> apply_output_buffer;
+ std::vector<unsigned char> apply_error_buffer;
std::string argsString;
bool rSwitchPresent = false;
@@ -298,17 +317,42 @@
argsString.append("-r");
}
- std::string applyPatchCommand =
- android::base::StringPrintf(kAgentApplyCommandPattern.c_str(), packageName.c_str(),
- patchDevicePath.c_str(), argsString.c_str());
- int returnCode = send_shell_command(applyPatchCommand);
- if (returnCode != 0) {
- error_exit("Executing %s returned %d", applyPatchCommand.c_str(), returnCode);
+ std::string error;
+ std::string apply_patch_service_string =
+ android::base::StringPrintf(kAgentApplyServicePattern, argsString.c_str());
+ unique_fd fd{adb_connect(apply_patch_service_string, &error)};
+ if (fd < 0) {
+ error_exit("Executing %s returned %s", apply_patch_service_string.c_str(), error.c_str());
+ }
+ return fd;
+}
+
+unique_fd apply_patch_on_device(const char* output_path) {
+ REPORT_FUNC_TIME();
+ constexpr char kAgentApplyServicePattern[] = "shell:/data/local/tmp/deployagent apply - -o %s";
+
+ std::string error;
+ std::string apply_patch_service_string =
+ android::base::StringPrintf(kAgentApplyServicePattern, output_path);
+ unique_fd fd{adb_connect(apply_patch_service_string, &error)};
+ if (fd < 0) {
+ error_exit("Executing %s returned %s", apply_patch_service_string.c_str(), error.c_str());
+ }
+ return fd;
+}
+
+static void create_patch(const char* apk_path, APKMetaData metadata, borrowed_fd patch_fd) {
+ REPORT_FUNC_TIME();
+ DeployPatchGenerator generator(/*is_verbose=*/false);
+ bool success = generator.CreatePatch(apk_path, std::move(metadata), patch_fd);
+ if (!success) {
+ error_exit("Failed to create patch for %s", apk_path);
}
}
-bool find_package(const char* apkPath) {
- const std::string findCommand =
- "/data/local/tmp/deployagent find " + get_packagename_from_apk(apkPath);
- return !send_shell_command(findCommand);
+int stream_patch(const char* apk_path, APKMetaData metadata, unique_fd patch_fd) {
+ create_patch(apk_path, std::move(metadata), patch_fd);
+
+ REPORT_FUNC_TIME();
+ return read_and_dump(patch_fd.get());
}
diff --git a/adb/client/fastdeploy.h b/adb/client/fastdeploy.h
index 7b7f2ec..830aeb2 100644
--- a/adb/client/fastdeploy.h
+++ b/adb/client/fastdeploy.h
@@ -16,6 +16,11 @@
#pragma once
+#include "adb_unique_fd.h"
+
+#include "fastdeploy/proto/ApkEntry.pb.h"
+
+#include <optional>
#include <string>
enum FastDeploy_AgentUpdateStrategy {
@@ -24,12 +29,11 @@
FastDeploy_AgentUpdateDifferentVersion
};
-void fastdeploy_set_local_agent(bool use_localagent);
+void fastdeploy_set_agent_update_strategy(FastDeploy_AgentUpdateStrategy agent_update_strategy);
int get_device_api_level();
-void update_agent(FastDeploy_AgentUpdateStrategy agentUpdateStrategy);
-void extract_metadata(const char* apkPath, FILE* outputFp);
-void create_patch(const char* apkPath, const char* metadataPath, const char* patchPath);
-void apply_patch_on_device(const char* apkPath, const char* patchPath, const char* outputPath);
-void install_patch(const char* apkPath, const char* patchPath, int argc, const char** argv);
-std::string get_patch_path(const char* apkPath);
-bool find_package(const char* apkPath);
+
+std::optional<com::android::fastdeploy::APKMetaData> extract_metadata(const char* apk_path);
+unique_fd install_patch(int argc, const char** argv);
+unique_fd apply_patch_on_device(const char* output_path);
+int stream_patch(const char* apk_path, com::android::fastdeploy::APKMetaData metadata,
+ unique_fd patch_fd);
diff --git a/adb/client/fastdeploycallbacks.cpp b/adb/client/fastdeploycallbacks.cpp
index 23a0aca..86ceaa1 100644
--- a/adb/client/fastdeploycallbacks.cpp
+++ b/adb/client/fastdeploycallbacks.cpp
@@ -49,35 +49,7 @@
int capture_shell_command(const char* command, std::vector<char>* outBuffer,
std::vector<char>* errBuffer) {
DeployAgentBufferCallback cb(outBuffer, errBuffer);
- return send_shell_command(command, false, &cb);
-}
-
-DeployAgentFileCallback::DeployAgentFileCallback(FILE* outputFile, std::vector<char>* errBuffer) {
- mpOutFile = outputFile;
- mpErrBuffer = errBuffer;
- mBytesWritten = 0;
-}
-
-void DeployAgentFileCallback::OnStdout(const char* buffer, int length) {
- if (mpOutFile != NULL) {
- int bytes_written = fwrite(buffer, 1, length, mpOutFile);
- if (bytes_written != length) {
- printf("Write error %d\n", bytes_written);
- }
- mBytesWritten += bytes_written;
- }
-}
-
-void DeployAgentFileCallback::OnStderr(const char* buffer, int length) {
- appendBuffer(mpErrBuffer, buffer, length);
-}
-
-int DeployAgentFileCallback::Done(int status) {
- return status;
-}
-
-int DeployAgentFileCallback::getBytesWritten() {
- return mBytesWritten;
+ return send_shell_command(command, /*disable_shell_protocol=*/false, &cb);
}
DeployAgentBufferCallback::DeployAgentBufferCallback(std::vector<char>* outBuffer,
diff --git a/adb/client/fastdeploycallbacks.h b/adb/client/fastdeploycallbacks.h
index 7e049c5..4a6fb99 100644
--- a/adb/client/fastdeploycallbacks.h
+++ b/adb/client/fastdeploycallbacks.h
@@ -17,23 +17,6 @@
#pragma once
#include <vector>
-#include "commandline.h"
-
-class DeployAgentFileCallback : public StandardStreamsCallbackInterface {
- public:
- DeployAgentFileCallback(FILE* outputFile, std::vector<char>* errBuffer);
-
- virtual void OnStdout(const char* buffer, int length);
- virtual void OnStderr(const char* buffer, int length);
- virtual int Done(int status);
-
- int getBytesWritten();
-
- private:
- FILE* mpOutFile;
- std::vector<char>* mpErrBuffer;
- int mBytesWritten;
-};
int capture_shell_command(const char* command, std::vector<char>* outBuffer,
std::vector<char>* errBuffer);
diff --git a/adb/fastdeploy/Android.bp b/adb/fastdeploy/Android.bp
index 95e1a28..245d52a 100644
--- a/adb/fastdeploy/Android.bp
+++ b/adb/fastdeploy/Android.bp
@@ -13,15 +13,76 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//
-java_binary {
- name: "deployagent",
+java_library {
+ name: "deployagent_lib",
sdk_version: "24",
- srcs: ["deployagent/src/**/*.java", "deploylib/src/**/*.java", "proto/**/*.proto"],
- static_libs: ["apkzlib_zip"],
+ srcs: [
+ "deployagent/src/**/*.java",
+ "proto/**/*.proto",
+ ],
proto: {
type: "lite",
},
+}
+
+java_binary {
+ name: "deployagent",
+ static_libs: [
+ "deployagent_lib",
+ ],
dex_preopt: {
enabled: false,
}
-}
\ No newline at end of file
+}
+
+android_test {
+ name: "FastDeployTests",
+
+ manifest: "AndroidManifest.xml",
+
+ srcs: [
+ "deployagent/test/com/android/fastdeploy/ApkArchiveTest.java",
+ ],
+
+ static_libs: [
+ "androidx.test.core",
+ "androidx.test.runner",
+ "androidx.test.rules",
+ "deployagent_lib",
+ "mockito-target-inline-minus-junit4",
+ ],
+
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ "android.test.mock",
+ ],
+
+ data: [
+ "testdata/sample.apk",
+ "testdata/sample.cd",
+ ],
+
+ optimize: {
+ enabled: false,
+ },
+}
+
+java_test_host {
+ name: "FastDeployHostTests",
+ srcs: [
+ "deployagent/test/com/android/fastdeploy/FastDeployTest.java",
+ ],
+ data: [
+ "testdata/helloworld5.apk",
+ "testdata/helloworld7.apk",
+ ],
+ libs: [
+ "compatibility-host-util",
+ "cts-tradefed",
+ "tradefed",
+ ],
+ test_suites: [
+ "general-tests",
+ ],
+}
diff --git a/adb/fastdeploy/AndroidManifest.xml b/adb/fastdeploy/AndroidManifest.xml
new file mode 100644
index 0000000..89dc745
--- /dev/null
+++ b/adb/fastdeploy/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.fastdeploytests">
+
+ <application android:testOnly="true"
+ android:debuggable="true">
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.fastdeploytests"
+ android:label="FastDeploy Tests" />
+</manifest>
\ No newline at end of file
diff --git a/adb/fastdeploy/AndroidTest.xml b/adb/fastdeploy/AndroidTest.xml
new file mode 100644
index 0000000..24a72bc
--- /dev/null
+++ b/adb/fastdeploy/AndroidTest.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<configuration description="Runs Device Tests for FastDeploy.">
+ <option name="test-suite-tag" value="FastDeployTests"/>
+
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true"/>
+ <option name="install-arg" value="-t"/>
+ <option name="test-file-name" value="FastDeployTests.apk"/>
+ </target_preparer>
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="false" />
+ <option name="push-file" key="sample.apk" value="/data/local/tmp/FastDeployTests/sample.apk" />
+ <option name="push-file" key="sample.cd" value="/data/local/tmp/FastDeployTests/sample.cd" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+ <option name="package" value="com.android.fastdeploytests"/>
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner"/>
+ </test>
+
+ <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
+ <option name="jar" value="FastDeployHostTests.jar" />
+ </test>
+</configuration>
diff --git a/adb/fastdeploy/deployagent/src/com/android/fastdeploy/ApkArchive.java b/adb/fastdeploy/deployagent/src/com/android/fastdeploy/ApkArchive.java
new file mode 100644
index 0000000..31e0502
--- /dev/null
+++ b/adb/fastdeploy/deployagent/src/com/android/fastdeploy/ApkArchive.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.fastdeploy;
+
+import android.util.Log;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.channels.FileChannel;
+
+/**
+ * Extremely light-weight APK parser class.
+ * Aware of Central Directory, Local File Headers and Signature.
+ * No Zip64 support yet.
+ */
+public final class ApkArchive {
+ private static final String TAG = "ApkArchive";
+
+ // Central Directory constants.
+ private static final int EOCD_SIGNATURE = 0x06054b50;
+ private static final int EOCD_MIN_SIZE = 22;
+ private static final long EOCD_MAX_SIZE = 65_535L + EOCD_MIN_SIZE;
+
+ private static final int CD_ENTRY_HEADER_SIZE_BYTES = 22;
+ private static final int CD_LOCAL_FILE_HEADER_SIZE_OFFSET = 12;
+
+ // Signature constants.
+ private static final int EOSIGNATURE_SIZE = 24;
+
+ public final static class Dump {
+ final byte[] cd;
+ final byte[] signature;
+
+ Dump(byte[] cd, byte[] signature) {
+ this.cd = cd;
+ this.signature = signature;
+ }
+ }
+
+ final static class Location {
+ final long offset;
+ final long size;
+
+ public Location(long offset, long size) {
+ this.offset = offset;
+ this.size = size;
+ }
+ }
+
+ private final RandomAccessFile mFile;
+ private final FileChannel mChannel;
+
+ public ApkArchive(File apk) throws IOException {
+ mFile = new RandomAccessFile(apk, "r");
+ mChannel = mFile.getChannel();
+ }
+
+ /**
+ * Extract the APK metadata: content of Central Directory and Signature.
+ *
+ * @return raw content from APK representing CD and Signature data.
+ */
+ public Dump extractMetadata() throws IOException {
+ Location cdLoc = getCDLocation();
+ byte[] cd = readMetadata(cdLoc);
+
+ byte[] signature = null;
+ Location sigLoc = getSignatureLocation(cdLoc.offset);
+ if (sigLoc != null) {
+ signature = readMetadata(sigLoc);
+ long size = ByteBuffer.wrap(signature).order(ByteOrder.LITTLE_ENDIAN).getLong();
+ if (sigLoc.size != size) {
+ Log.e(TAG, "Mismatching signature sizes: " + sigLoc.size + " != " + size);
+ signature = null;
+ }
+ }
+
+ return new Dump(cd, signature);
+ }
+
+ private long findEndOfCDRecord() throws IOException {
+ final long fileSize = mChannel.size();
+ int sizeToRead = Math.toIntExact(Math.min(fileSize, EOCD_MAX_SIZE));
+ final long readOffset = fileSize - sizeToRead;
+ ByteBuffer buffer = mChannel.map(FileChannel.MapMode.READ_ONLY, readOffset,
+ sizeToRead).order(ByteOrder.LITTLE_ENDIAN);
+
+ buffer.position(sizeToRead - EOCD_MIN_SIZE);
+ while (true) {
+ int signature = buffer.getInt(); // Read 4 bytes.
+ if (signature == EOCD_SIGNATURE) {
+ return readOffset + buffer.position() - 4;
+ }
+ if (buffer.position() == 4) {
+ break;
+ }
+ buffer.position(buffer.position() - Integer.BYTES - 1); // Backtrack 5 bytes.
+ }
+
+ return -1L;
+ }
+
+ private Location findCDRecord(ByteBuffer buf) {
+ if (buf.order() != ByteOrder.LITTLE_ENDIAN) {
+ throw new IllegalArgumentException("ByteBuffer byte order must be little endian");
+ }
+ if (buf.remaining() < CD_ENTRY_HEADER_SIZE_BYTES) {
+ throw new IllegalArgumentException(
+ "Input too short. Need at least " + CD_ENTRY_HEADER_SIZE_BYTES
+ + " bytes, available: " + buf.remaining() + "bytes.");
+ }
+
+ int originalPosition = buf.position();
+ int recordSignature = buf.getInt();
+ if (recordSignature != EOCD_SIGNATURE) {
+ throw new IllegalArgumentException(
+ "Not a Central Directory record. Signature: 0x"
+ + Long.toHexString(recordSignature & 0xffffffffL));
+ }
+
+ buf.position(originalPosition + CD_LOCAL_FILE_HEADER_SIZE_OFFSET);
+ long size = buf.getInt() & 0xffffffffL;
+ long offset = buf.getInt() & 0xffffffffL;
+ return new Location(offset, size);
+ }
+
+ // Retrieve the location of the Central Directory Record.
+ Location getCDLocation() throws IOException {
+ long eocdRecord = findEndOfCDRecord();
+ if (eocdRecord < 0) {
+ throw new IllegalArgumentException("Unable to find End of Central Directory record.");
+ }
+
+ Location location = findCDRecord(mChannel.map(FileChannel.MapMode.READ_ONLY, eocdRecord,
+ CD_ENTRY_HEADER_SIZE_BYTES).order(ByteOrder.LITTLE_ENDIAN));
+ if (location == null) {
+ throw new IllegalArgumentException("Unable to find Central Directory File Header.");
+ }
+
+ return location;
+ }
+
+ // Retrieve the location of the signature block starting from Central
+ // Directory Record or null if signature is not found.
+ Location getSignatureLocation(long cdRecordOffset) throws IOException {
+ long signatureOffset = cdRecordOffset - EOSIGNATURE_SIZE;
+ if (signatureOffset < 0) {
+ Log.e(TAG, "Unable to find Signature.");
+ return null;
+ }
+
+ ByteBuffer signature = mChannel.map(FileChannel.MapMode.READ_ONLY, signatureOffset,
+ EOSIGNATURE_SIZE).order(ByteOrder.LITTLE_ENDIAN);
+
+ long size = signature.getLong();
+
+ byte[] sign = new byte[16];
+ signature.get(sign);
+ String signAsString = new String(sign);
+ if (!"APK Sig Block 42".equals(signAsString)) {
+ Log.e(TAG, "Signature magic does not match: " + signAsString);
+ return null;
+ }
+
+ long offset = cdRecordOffset - size - 8;
+
+ return new Location(offset, size);
+ }
+
+ private byte[] readMetadata(Location loc) throws IOException {
+ byte[] payload = new byte[(int) loc.size];
+ ByteBuffer buffer = mChannel.map(FileChannel.MapMode.READ_ONLY, loc.offset, loc.size);
+ buffer.get(payload);
+ return payload;
+ }
+}
diff --git a/adb/fastdeploy/deployagent/src/com/android/fastdeploy/DeployAgent.java b/adb/fastdeploy/deployagent/src/com/android/fastdeploy/DeployAgent.java
index a8103c4..3812307 100644
--- a/adb/fastdeploy/deployagent/src/com/android/fastdeploy/DeployAgent.java
+++ b/adb/fastdeploy/deployagent/src/com/android/fastdeploy/DeployAgent.java
@@ -24,18 +24,22 @@
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
-import java.util.concurrent.SynchronousQueue;
-import java.util.concurrent.TimeUnit;
-import java.io.OutputStream;
import java.io.RandomAccessFile;
-import java.util.Set;
+import java.nio.channels.Channels;
+import java.nio.channels.FileChannel;
+import java.nio.channels.WritableByteChannel;
+import com.android.fastdeploy.PatchFormatException;
+import com.android.fastdeploy.ApkArchive;
+import com.android.fastdeploy.APKDump;
import com.android.fastdeploy.APKMetaData;
import com.android.fastdeploy.PatchUtils;
+import com.google.protobuf.ByteString;
+
public final class DeployAgent {
private static final int BUFFER_SIZE = 128 * 1024;
- private static final int AGENT_VERSION = 0x00000002;
+ private static final int AGENT_VERSION = 0x00000003;
public static void main(String[] args) {
int exitCode = 0;
@@ -45,68 +49,70 @@
}
String commandString = args[0];
+ switch (commandString) {
+ case "dump": {
+ if (args.length != 3) {
+ showUsage(1);
+ }
- if (commandString.equals("extract")) {
- if (args.length != 2) {
- showUsage(1);
- }
-
- String packageName = args[1];
- extractMetaData(packageName);
- } else if (commandString.equals("find")) {
- if (args.length != 2) {
- showUsage(1);
- }
-
- String packageName = args[1];
- if (getFilenameFromPackageName(packageName) == null) {
- exitCode = 3;
- }
- } else if (commandString.equals("apply")) {
- if (args.length < 4) {
- showUsage(1);
- }
-
- String packageName = args[1];
- String patchPath = args[2];
- String outputParam = args[3];
-
- InputStream deltaInputStream = null;
- if (patchPath.compareTo("-") == 0) {
- deltaInputStream = System.in;
- } else {
- deltaInputStream = new FileInputStream(patchPath);
- }
-
- if (outputParam.equals("-o")) {
- OutputStream outputStream = null;
- if (args.length > 4) {
- String outputPath = args[4];
- if (!outputPath.equals("-")) {
- outputStream = new FileOutputStream(outputPath);
+ String requiredVersion = args[1];
+ if (AGENT_VERSION == Integer.parseInt(requiredVersion)) {
+ String packageName = args[2];
+ String packagePath = getFilenameFromPackageName(packageName);
+ if (packagePath != null) {
+ dumpApk(packageName, packagePath);
+ } else {
+ exitCode = 3;
}
+ } else {
+ System.out.printf("0x%08X\n", AGENT_VERSION);
+ exitCode = 4;
}
- if (outputStream == null) {
- outputStream = System.out;
- }
- File deviceFile = getFileFromPackageName(packageName);
- writePatchToStream(
- new RandomAccessFile(deviceFile, "r"), deltaInputStream, outputStream);
- } else if (outputParam.equals("-pm")) {
- String[] sessionArgs = null;
- if (args.length > 4) {
- int numSessionArgs = args.length-4;
- sessionArgs = new String[numSessionArgs];
- for (int i=0 ; i<numSessionArgs ; i++) {
- sessionArgs[i] = args[i+4];
- }
- }
- exitCode = applyPatch(packageName, deltaInputStream, sessionArgs);
+ break;
}
- } else if (commandString.equals("version")) {
- System.out.printf("0x%08X\n", AGENT_VERSION);
- } else {
- showUsage(1);
+ case "apply": {
+ if (args.length < 3) {
+ showUsage(1);
+ }
+
+ String patchPath = args[1];
+ String outputParam = args[2];
+
+ InputStream deltaInputStream = null;
+ if (patchPath.compareTo("-") == 0) {
+ deltaInputStream = System.in;
+ } else {
+ deltaInputStream = new FileInputStream(patchPath);
+ }
+
+ if (outputParam.equals("-o")) {
+ OutputStream outputStream = null;
+ if (args.length > 3) {
+ String outputPath = args[3];
+ if (!outputPath.equals("-")) {
+ outputStream = new FileOutputStream(outputPath);
+ }
+ }
+ if (outputStream == null) {
+ outputStream = System.out;
+ }
+ writePatchToStream(deltaInputStream, outputStream);
+ } else if (outputParam.equals("-pm")) {
+ String[] sessionArgs = null;
+ if (args.length > 3) {
+ int numSessionArgs = args.length - 3;
+ sessionArgs = new String[numSessionArgs];
+ for (int i = 0; i < numSessionArgs; i++) {
+ sessionArgs[i] = args[i + 3];
+ }
+ }
+ exitCode = applyPatch(deltaInputStream, sessionArgs);
+ }
+ break;
+ }
+ default:
+ showUsage(1);
+ break;
}
} catch (Exception e) {
System.err.println("Error: " + e);
@@ -118,16 +124,16 @@
private static void showUsage(int exitCode) {
System.err.println(
- "usage: deployagent <command> [<args>]\n\n" +
- "commands:\n" +
- "version get the version\n" +
- "find PKGNAME return zero if package found, else non-zero\n" +
- "extract PKGNAME extract an installed package's metadata\n" +
- "apply PKGNAME PATCHFILE [-o|-pm] apply a patch from PATCHFILE (- for stdin) to an installed package\n" +
- " -o <FILE> directs output to FILE, default or - for stdout\n" +
- " -pm <ARGS> directs output to package manager, passes <ARGS> to 'pm install-create'\n"
- );
-
+ "usage: deployagent <command> [<args>]\n\n" +
+ "commands:\n" +
+ "dump VERSION PKGNAME dump info for an installed package given that " +
+ "VERSION equals current agent's version\n" +
+ "apply PATCHFILE [-o|-pm] apply a patch from PATCHFILE " +
+ "(- for stdin) to an installed package\n" +
+ " -o <FILE> directs output to FILE, default or - for stdout\n" +
+ " -pm <ARGS> directs output to package manager, passes <ARGS> to " +
+ "'pm install-create'\n"
+ );
System.exit(exitCode);
}
@@ -162,32 +168,34 @@
}
int equalsIndex = line.lastIndexOf(packageSuffix);
String fileName =
- line.substring(packageIndex + packagePrefix.length(), equalsIndex);
+ line.substring(packageIndex + packagePrefix.length(), equalsIndex);
return fileName;
}
}
return null;
}
- private static File getFileFromPackageName(String packageName) throws IOException {
- String filename = getFilenameFromPackageName(packageName);
- if (filename == null) {
- // Should not happen (function is only called when we know the package exists)
- throw new IOException("package not found");
- }
- return new File(filename);
- }
+ private static void dumpApk(String packageName, String packagePath) throws IOException {
+ File apk = new File(packagePath);
+ ApkArchive.Dump dump = new ApkArchive(apk).extractMetadata();
- private static void extractMetaData(String packageName) throws IOException {
- File apkFile = getFileFromPackageName(packageName);
- APKMetaData apkMetaData = PatchUtils.getAPKMetaData(apkFile);
- apkMetaData.writeTo(System.out);
+ APKDump.Builder apkDumpBuilder = APKDump.newBuilder();
+ apkDumpBuilder.setName(packageName);
+ if (dump.cd != null) {
+ apkDumpBuilder.setCd(ByteString.copyFrom(dump.cd));
+ }
+ if (dump.signature != null) {
+ apkDumpBuilder.setSignature(ByteString.copyFrom(dump.signature));
+ }
+ apkDumpBuilder.setAbsolutePath(apk.getAbsolutePath());
+
+ apkDumpBuilder.build().writeTo(System.out);
}
private static int createInstallSession(String[] args) throws IOException {
StringBuilder commandBuilder = new StringBuilder();
commandBuilder.append("pm install-create ");
- for (int i=0 ; args != null && i<args.length ; i++) {
+ for (int i = 0; args != null && i < args.length; i++) {
commandBuilder.append(args[i] + " ");
}
@@ -199,7 +207,8 @@
String successLineEnd = "]";
while ((line = reader.readLine()) != null) {
if (line.startsWith(successLineStart) && line.endsWith(successLineEnd)) {
- return Integer.parseInt(line.substring(successLineStart.length(), line.lastIndexOf(successLineEnd)));
+ return Integer.parseInt(line.substring(successLineStart.length(),
+ line.lastIndexOf(successLineEnd)));
}
}
@@ -213,16 +222,15 @@
return p.exitValue();
}
- private static int applyPatch(String packageName, InputStream deltaStream, String[] sessionArgs)
+ private static int applyPatch(InputStream deltaStream, String[] sessionArgs)
throws IOException, PatchFormatException {
- File deviceFile = getFileFromPackageName(packageName);
int sessionId = createInstallSession(sessionArgs);
if (sessionId < 0) {
System.err.println("PM Create Session Failed");
return -1;
}
- int writeExitCode = writePatchedDataToSession(new RandomAccessFile(deviceFile, "r"), deltaStream, sessionId);
+ int writeExitCode = writePatchedDataToSession(deltaStream, sessionId);
if (writeExitCode == 0) {
return commitInstallSession(sessionId);
} else {
@@ -230,84 +238,94 @@
}
}
- private static long writePatchToStream(RandomAccessFile oldData, InputStream patchData,
- OutputStream outputStream) throws IOException, PatchFormatException {
+ private static long writePatchToStream(InputStream patchData,
+ OutputStream outputStream) throws IOException, PatchFormatException {
long newSize = readPatchHeader(patchData);
- long bytesWritten = writePatchedDataToStream(oldData, newSize, patchData, outputStream);
+ long bytesWritten = writePatchedDataToStream(newSize, patchData, outputStream);
outputStream.flush();
if (bytesWritten != newSize) {
throw new PatchFormatException(String.format(
- "output size mismatch (expected %ld but wrote %ld)", newSize, bytesWritten));
+ "output size mismatch (expected %ld but wrote %ld)", newSize, bytesWritten));
}
return bytesWritten;
}
private static long readPatchHeader(InputStream patchData)
- throws IOException, PatchFormatException {
+ throws IOException, PatchFormatException {
byte[] signatureBuffer = new byte[PatchUtils.SIGNATURE.length()];
try {
- PatchUtils.readFully(patchData, signatureBuffer, 0, signatureBuffer.length);
+ PatchUtils.readFully(patchData, signatureBuffer);
} catch (IOException e) {
throw new PatchFormatException("truncated signature");
}
- String signature = new String(signatureBuffer, 0, signatureBuffer.length, "US-ASCII");
+ String signature = new String(signatureBuffer);
if (!PatchUtils.SIGNATURE.equals(signature)) {
throw new PatchFormatException("bad signature");
}
- long newSize = PatchUtils.readBsdiffLong(patchData);
- if (newSize < 0 || newSize > Integer.MAX_VALUE) {
- throw new PatchFormatException("bad newSize");
+ long newSize = PatchUtils.readLELong(patchData);
+ if (newSize < 0) {
+ throw new PatchFormatException("bad newSize: " + newSize);
}
return newSize;
}
// Note that this function assumes patchData has been seek'ed to the start of the delta stream
- // (i.e. the signature has already been read by readPatchHeader). For a stream that points to the
- // start of a patch file call writePatchToStream
- private static long writePatchedDataToStream(RandomAccessFile oldData, long newSize,
- InputStream patchData, OutputStream outputStream) throws IOException {
+ // (i.e. the signature has already been read by readPatchHeader). For a stream that points to
+ // the start of a patch file call writePatchToStream
+ private static long writePatchedDataToStream(long newSize, InputStream patchData,
+ OutputStream outputStream) throws IOException {
+ String deviceFile = PatchUtils.readString(patchData);
+ RandomAccessFile oldDataFile = new RandomAccessFile(deviceFile, "r");
+ FileChannel oldData = oldDataFile.getChannel();
+
+ WritableByteChannel newData = Channels.newChannel(outputStream);
+
long newDataBytesWritten = 0;
byte[] buffer = new byte[BUFFER_SIZE];
while (newDataBytesWritten < newSize) {
- long copyLen = PatchUtils.readFormattedLong(patchData);
- if (copyLen > 0) {
- PatchUtils.pipe(patchData, outputStream, buffer, (int) copyLen);
+ long newDataLen = PatchUtils.readLELong(patchData);
+ if (newDataLen > 0) {
+ PatchUtils.pipe(patchData, outputStream, buffer, newDataLen);
}
- long oldDataOffset = PatchUtils.readFormattedLong(patchData);
- long oldDataLen = PatchUtils.readFormattedLong(patchData);
- oldData.seek(oldDataOffset);
- if (oldDataLen > 0) {
- PatchUtils.pipe(oldData, outputStream, buffer, (int) oldDataLen);
+ long oldDataOffset = PatchUtils.readLELong(patchData);
+ long oldDataLen = PatchUtils.readLELong(patchData);
+ if (oldDataLen >= 0) {
+ long offset = oldDataOffset;
+ long len = oldDataLen;
+ while (len > 0) {
+ long chunkLen = Math.min(len, 1024*1024*1024);
+ oldData.transferTo(offset, chunkLen, newData);
+ offset += chunkLen;
+ len -= chunkLen;
+ }
}
- newDataBytesWritten += copyLen + oldDataLen;
+ newDataBytesWritten += newDataLen + oldDataLen;
}
return newDataBytesWritten;
}
- private static int writePatchedDataToSession(RandomAccessFile oldData, InputStream patchData, int sessionId)
+ private static int writePatchedDataToSession(InputStream patchData, int sessionId)
throws IOException, PatchFormatException {
try {
Process p;
long newSize = readPatchHeader(patchData);
- StringBuilder commandBuilder = new StringBuilder();
- commandBuilder.append(String.format("pm install-write -S %d %d -- -", newSize, sessionId));
-
- String command = commandBuilder.toString();
+ String command = String.format("pm install-write -S %d %d -- -", newSize, sessionId);
p = Runtime.getRuntime().exec(command);
OutputStream sessionOutputStream = p.getOutputStream();
- long bytesWritten = writePatchedDataToStream(oldData, newSize, patchData, sessionOutputStream);
+ long bytesWritten = writePatchedDataToStream(newSize, patchData, sessionOutputStream);
sessionOutputStream.flush();
p.waitFor();
if (bytesWritten != newSize) {
throw new PatchFormatException(
- String.format("output size mismatch (expected %d but wrote %)", newSize, bytesWritten));
+ String.format("output size mismatch (expected %d but wrote %)", newSize,
+ bytesWritten));
}
return p.exitValue();
} catch (InterruptedException e) {
diff --git a/adb/fastdeploy/deploylib/src/com/android/fastdeploy/PatchFormatException.java b/adb/fastdeploy/deployagent/src/com/android/fastdeploy/PatchFormatException.java
similarity index 100%
rename from adb/fastdeploy/deploylib/src/com/android/fastdeploy/PatchFormatException.java
rename to adb/fastdeploy/deployagent/src/com/android/fastdeploy/PatchFormatException.java
diff --git a/adb/fastdeploy/deployagent/src/com/android/fastdeploy/PatchUtils.java b/adb/fastdeploy/deployagent/src/com/android/fastdeploy/PatchUtils.java
new file mode 100644
index 0000000..54be26f
--- /dev/null
+++ b/adb/fastdeploy/deployagent/src/com/android/fastdeploy/PatchUtils.java
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+package com.android.fastdeploy;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+class PatchUtils {
+ public static final String SIGNATURE = "FASTDEPLOY";
+
+ /**
+ * Reads a 64-bit signed integer in Little Endian format from the specified {@link
+ * DataInputStream}.
+ *
+ * @param in the stream to read from.
+ */
+ static long readLELong(InputStream in) throws IOException {
+ byte[] buffer = new byte[Long.BYTES];
+ readFully(in, buffer);
+ ByteBuffer buf = ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN);
+ return buf.getLong();
+ }
+
+ static String readString(InputStream in) throws IOException {
+ int size = (int) readLELong(in);
+ byte[] buffer = new byte[size];
+ readFully(in, buffer);
+ return new String(buffer);
+ }
+
+ static void readFully(final InputStream in, final byte[] destination, final int startAt,
+ final int numBytes) throws IOException {
+ int numRead = 0;
+ while (numRead < numBytes) {
+ int readNow = in.read(destination, startAt + numRead, numBytes - numRead);
+ if (readNow == -1) {
+ throw new IOException("truncated input stream");
+ }
+ numRead += readNow;
+ }
+ }
+
+ static void readFully(final InputStream in, final byte[] destination) throws IOException {
+ readFully(in, destination, 0, destination.length);
+ }
+
+ static void pipe(final InputStream in, final OutputStream out, final byte[] buffer,
+ long copyLength) throws IOException {
+ while (copyLength > 0) {
+ int maxCopy = (int) Math.min(buffer.length, copyLength);
+ readFully(in, buffer, 0, maxCopy);
+ out.write(buffer, 0, maxCopy);
+ copyLength -= maxCopy;
+ }
+ }
+}
diff --git a/adb/fastdeploy/deployagent/test/com/android/fastdeploy/ApkArchiveTest.java b/adb/fastdeploy/deployagent/test/com/android/fastdeploy/ApkArchiveTest.java
new file mode 100644
index 0000000..7c2468f
--- /dev/null
+++ b/adb/fastdeploy/deployagent/test/com/android/fastdeploy/ApkArchiveTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.fastdeploy;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import com.android.fastdeploy.ApkArchive;
+
+import java.io.File;
+import java.io.IOException;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ApkArchiveTest {
+ private static final File SAMPLE_APK = new File("/data/local/tmp/FastDeployTests/sample.apk");
+ private static final File WRONG_APK = new File("/data/local/tmp/FastDeployTests/sample.cd");
+
+ @Test
+ public void testApkArchiveSizes() throws IOException {
+ ApkArchive archive = new ApkArchive(SAMPLE_APK);
+
+ ApkArchive.Location cdLoc = archive.getCDLocation();
+ assertNotEquals(cdLoc, null);
+ assertEquals(cdLoc.offset, 2044145);
+ assertEquals(cdLoc.size, 49390);
+
+ // Check that block can be retrieved
+ ApkArchive.Location sigLoc = archive.getSignatureLocation(cdLoc.offset);
+ assertNotEquals(sigLoc, null);
+ assertEquals(sigLoc.offset, 2040049);
+ assertEquals(sigLoc.size, 4088);
+ }
+
+ @Test
+ public void testApkArchiveDump() throws IOException {
+ ApkArchive archive = new ApkArchive(SAMPLE_APK);
+
+ ApkArchive.Dump dump = archive.extractMetadata();
+ assertNotEquals(dump, null);
+ assertNotEquals(dump.cd, null);
+ assertNotEquals(dump.signature, null);
+ assertEquals(dump.cd.length, 49390);
+ assertEquals(dump.signature.length, 4088);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testApkArchiveDumpWrongApk() throws IOException {
+ ApkArchive archive = new ApkArchive(WRONG_APK);
+
+ archive.extractMetadata();
+ }
+}
diff --git a/adb/fastdeploy/deployagent/test/com/android/fastdeploy/FastDeployTest.java b/adb/fastdeploy/deployagent/test/com/android/fastdeploy/FastDeployTest.java
new file mode 100644
index 0000000..ef6ccae
--- /dev/null
+++ b/adb/fastdeploy/deployagent/test/com/android/fastdeploy/FastDeployTest.java
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+
+package com.android.fastdeploy;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class FastDeployTest extends BaseHostJUnit4Test {
+
+ private static final String TEST_APP_PACKAGE = "com.example.helloworld";
+ private static final String TEST_APK5_NAME = "helloworld5.apk";
+ private static final String TEST_APK7_NAME = "helloworld7.apk";
+
+ private String mTestApk5Path;
+ private String mTestApk7Path;
+
+ @Before
+ public void setUp() throws Exception {
+ CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild());
+ getDevice().uninstallPackage(TEST_APP_PACKAGE);
+ mTestApk5Path = buildHelper.getTestFile(TEST_APK5_NAME).getAbsolutePath();
+ mTestApk7Path = buildHelper.getTestFile(TEST_APK7_NAME).getAbsolutePath();
+ }
+
+ @Test
+ public void testAppInstalls() throws Exception {
+ fastInstallPackage(mTestApk5Path);
+ assertTrue(isAppInstalled(TEST_APP_PACKAGE));
+ getDevice().uninstallPackage(TEST_APP_PACKAGE);
+ assertFalse(isAppInstalled(TEST_APP_PACKAGE));
+ }
+
+ @Test
+ public void testAppPatch() throws Exception {
+ fastInstallPackage(mTestApk5Path);
+ assertTrue(isAppInstalled(TEST_APP_PACKAGE));
+ fastInstallPackage(mTestApk7Path);
+ assertTrue(isAppInstalled(TEST_APP_PACKAGE));
+ getDevice().uninstallPackage(TEST_APP_PACKAGE);
+ assertFalse(isAppInstalled(TEST_APP_PACKAGE));
+ }
+
+ private boolean isAppInstalled(String packageName) throws DeviceNotAvailableException {
+ final String commandResult = getDevice().executeShellCommand("pm list packages");
+ final int prefixLength = "package:".length();
+ return Arrays.stream(commandResult.split("\\r?\\n"))
+ .anyMatch(line -> line.substring(prefixLength).equals(packageName));
+ }
+
+ // Mostly copied from PkgInstallSignatureVerificationTest.java.
+ private String fastInstallPackage(String apkPath)
+ throws IOException, DeviceNotAvailableException {
+ return getDevice().executeAdbCommand("install", "-t", "--fastdeploy", "--force-agent",
+ apkPath);
+ }
+}
+
+
diff --git a/adb/fastdeploy/deploylib/src/com/android/fastdeploy/PatchUtils.java b/adb/fastdeploy/deploylib/src/com/android/fastdeploy/PatchUtils.java
deleted file mode 100644
index c60f9a6..0000000
--- a/adb/fastdeploy/deploylib/src/com/android/fastdeploy/PatchUtils.java
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.fastdeploy;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.RandomAccessFile;
-import java.util.Arrays;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-
-import com.android.tools.build.apkzlib.zip.ZFile;
-import com.android.tools.build.apkzlib.zip.ZFileOptions;
-import com.android.tools.build.apkzlib.zip.StoredEntry;
-import com.android.tools.build.apkzlib.zip.StoredEntryType;
-import com.android.tools.build.apkzlib.zip.CentralDirectoryHeaderCompressInfo;
-import com.android.tools.build.apkzlib.zip.CentralDirectoryHeader;
-
-import com.android.fastdeploy.APKMetaData;
-import com.android.fastdeploy.APKEntry;
-
-class PatchUtils {
- private static final long NEGATIVE_MASK = 1L << 63;
- private static final long NEGATIVE_LONG_SIGN_MASK = 1L << 63;
- public static final String SIGNATURE = "FASTDEPLOY";
-
- private static long getOffsetFromEntry(StoredEntry entry) {
- return entry.getCentralDirectoryHeader().getOffset() + entry.getLocalHeaderSize();
- }
-
- public static APKMetaData getAPKMetaData(File apkFile) throws IOException {
- APKMetaData.Builder apkEntriesBuilder = APKMetaData.newBuilder();
- ZFileOptions options = new ZFileOptions();
- ZFile zFile = new ZFile(apkFile, options);
-
- ArrayList<StoredEntry> metaDataEntries = new ArrayList<StoredEntry>();
-
- for (StoredEntry entry : zFile.entries()) {
- if (entry.getType() != StoredEntryType.FILE) {
- continue;
- }
- metaDataEntries.add(entry);
- }
-
- Collections.sort(metaDataEntries, new Comparator<StoredEntry>() {
- private long getOffsetFromEntry(StoredEntry entry) {
- return PatchUtils.getOffsetFromEntry(entry);
- }
-
- @Override
- public int compare(StoredEntry lhs, StoredEntry rhs) {
- // -1 - less than, 1 - greater than, 0 - equal, all inversed for descending
- return Long.compare(getOffsetFromEntry(lhs), getOffsetFromEntry(rhs));
- }
- });
-
- for (StoredEntry entry : metaDataEntries) {
- CentralDirectoryHeader cdh = entry.getCentralDirectoryHeader();
- CentralDirectoryHeaderCompressInfo cdhci = cdh.getCompressionInfoWithWait();
-
- APKEntry.Builder entryBuilder = APKEntry.newBuilder();
- entryBuilder.setCrc32(cdh.getCrc32());
- entryBuilder.setFileName(cdh.getName());
- entryBuilder.setCompressedSize(cdhci.getCompressedSize());
- entryBuilder.setUncompressedSize(cdh.getUncompressedSize());
- entryBuilder.setDataOffset(getOffsetFromEntry(entry));
-
- apkEntriesBuilder.addEntries(entryBuilder);
- apkEntriesBuilder.build();
- }
- return apkEntriesBuilder.build();
- }
-
- /**
- * Writes a 64-bit signed integer to the specified {@link OutputStream}. The least significant
- * byte is written first and the most significant byte is written last.
- * @param value the value to write
- * @param outputStream the stream to write to
- */
- static void writeFormattedLong(final long value, OutputStream outputStream) throws IOException {
- long y = value;
- if (y < 0) {
- y = (-y) | NEGATIVE_MASK;
- }
-
- for (int i = 0; i < 8; ++i) {
- outputStream.write((byte) (y & 0xff));
- y >>>= 8;
- }
- }
-
- /**
- * Reads a 64-bit signed integer written by {@link #writeFormattedLong(long, OutputStream)} from
- * the specified {@link InputStream}.
- * @param inputStream the stream to read from
- */
- static long readFormattedLong(InputStream inputStream) throws IOException {
- long result = 0;
- for (int bitshift = 0; bitshift < 64; bitshift += 8) {
- result |= ((long) inputStream.read()) << bitshift;
- }
-
- if ((result - NEGATIVE_MASK) > 0) {
- result = (result & ~NEGATIVE_MASK) * -1;
- }
- return result;
- }
-
- static final long readBsdiffLong(InputStream in) throws PatchFormatException, IOException {
- long result = 0;
- for (int bitshift = 0; bitshift < 64; bitshift += 8) {
- result |= ((long) in.read()) << bitshift;
- }
-
- if (result == NEGATIVE_LONG_SIGN_MASK) {
- // "Negative zero", which is valid in signed-magnitude format.
- // NB: No sane patch generator should ever produce such a value.
- throw new PatchFormatException("read negative zero");
- }
-
- if ((result & NEGATIVE_LONG_SIGN_MASK) != 0) {
- result = -(result & ~NEGATIVE_LONG_SIGN_MASK);
- }
-
- return result;
- }
-
- static void readFully(final InputStream in, final byte[] destination, final int startAt,
- final int numBytes) throws IOException {
- int numRead = 0;
- while (numRead < numBytes) {
- int readNow = in.read(destination, startAt + numRead, numBytes - numRead);
- if (readNow == -1) {
- throw new IOException("truncated input stream");
- }
- numRead += readNow;
- }
- }
-
- static void pipe(final InputStream in, final OutputStream out, final byte[] buffer,
- long copyLength) throws IOException {
- while (copyLength > 0) {
- int maxCopy = Math.min(buffer.length, (int) copyLength);
- readFully(in, buffer, 0, maxCopy);
- out.write(buffer, 0, maxCopy);
- copyLength -= maxCopy;
- }
- }
-
- static void pipe(final RandomAccessFile in, final OutputStream out, final byte[] buffer,
- long copyLength) throws IOException {
- while (copyLength > 0) {
- int maxCopy = Math.min(buffer.length, (int) copyLength);
- in.readFully(buffer, 0, maxCopy);
- out.write(buffer, 0, maxCopy);
- copyLength -= maxCopy;
- }
- }
-
- static void fill(byte value, final OutputStream out, final byte[] buffer, long fillLength)
- throws IOException {
- while (fillLength > 0) {
- int maxCopy = Math.min(buffer.length, (int) fillLength);
- Arrays.fill(buffer, 0, maxCopy, value);
- out.write(buffer, 0, maxCopy);
- fillLength -= maxCopy;
- }
- }
-}
diff --git a/adb/fastdeploy/deploypatchgenerator/apk_archive.cpp b/adb/fastdeploy/deploypatchgenerator/apk_archive.cpp
new file mode 100644
index 0000000..3dc5e50
--- /dev/null
+++ b/adb/fastdeploy/deploypatchgenerator/apk_archive.cpp
@@ -0,0 +1,415 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define TRACE_TAG ADB
+
+#include "apk_archive.h"
+
+#include "adb_trace.h"
+#include "sysdeps.h"
+
+#include <android-base/endian.h>
+#include <android-base/mapped_file.h>
+
+#include <openssl/md5.h>
+
+constexpr uint16_t kCompressStored = 0;
+
+// mask value that signifies that the entry has a DD
+static const uint32_t kGPBDDFlagMask = 0x0008;
+
+namespace {
+struct FileRegion {
+ FileRegion(borrowed_fd fd, off64_t offset, size_t length)
+ : mapped_(android::base::MappedFile::FromOsHandle(adb_get_os_handle(fd), offset, length,
+ PROT_READ)) {
+ if (mapped_.data() != nullptr) {
+ return;
+ }
+
+ // Mapped file failed, falling back to pread.
+ buffer_.resize(length);
+ if (auto err = adb_pread(fd.get(), buffer_.data(), length, offset); size_t(err) != length) {
+ fprintf(stderr, "Unable to read %lld bytes at offset %" PRId64 " \n",
+ static_cast<long long>(length), offset);
+ buffer_.clear();
+ return;
+ }
+ }
+
+ const char* data() const { return mapped_.data() ? mapped_.data() : buffer_.data(); }
+ size_t size() const { return mapped_.data() ? mapped_.size() : buffer_.size(); }
+
+ private:
+ FileRegion() = default;
+ DISALLOW_COPY_AND_ASSIGN(FileRegion);
+
+ android::base::MappedFile mapped_;
+ std::string buffer_;
+};
+} // namespace
+
+using com::android::fastdeploy::APKDump;
+
+ApkArchive::ApkArchive(const std::string& path) : path_(path), size_(0) {
+ fd_.reset(adb_open(path_.c_str(), O_RDONLY));
+ if (fd_ == -1) {
+ fprintf(stderr, "Unable to open file '%s'\n", path_.c_str());
+ return;
+ }
+
+ struct stat st;
+ if (stat(path_.c_str(), &st) == -1) {
+ fprintf(stderr, "Unable to stat file '%s'\n", path_.c_str());
+ return;
+ }
+ size_ = st.st_size;
+}
+
+ApkArchive::~ApkArchive() {}
+
+APKDump ApkArchive::ExtractMetadata() {
+ D("ExtractMetadata");
+ if (!ready()) {
+ return {};
+ }
+
+ Location cdLoc = GetCDLocation();
+ if (!cdLoc.valid) {
+ return {};
+ }
+
+ APKDump dump;
+ dump.set_absolute_path(path_);
+ dump.set_cd(ReadMetadata(cdLoc));
+
+ Location sigLoc = GetSignatureLocation(cdLoc.offset);
+ if (sigLoc.valid) {
+ dump.set_signature(ReadMetadata(sigLoc));
+ }
+ return dump;
+}
+
+off_t ApkArchive::FindEndOfCDRecord() const {
+ constexpr int endOfCDSignature = 0x06054b50;
+ constexpr off_t endOfCDMinSize = 22;
+ constexpr off_t endOfCDMaxSize = 65535 + endOfCDMinSize;
+
+ auto sizeToRead = std::min(size_, endOfCDMaxSize);
+ auto readOffset = size_ - sizeToRead;
+ FileRegion mapped(fd_, readOffset, sizeToRead);
+
+ // Start scanning from the end
+ auto* start = mapped.data();
+ auto* cursor = start + mapped.size() - sizeof(endOfCDSignature);
+
+ // Search for End of Central Directory record signature.
+ while (cursor >= start) {
+ if (*(int32_t*)cursor == endOfCDSignature) {
+ return readOffset + (cursor - start);
+ }
+ cursor--;
+ }
+ return -1;
+}
+
+ApkArchive::Location ApkArchive::FindCDRecord(const char* cursor) {
+ struct ecdr_t {
+ int32_t signature;
+ uint16_t diskNumber;
+ uint16_t numDisk;
+ uint16_t diskEntries;
+ uint16_t numEntries;
+ uint32_t crSize;
+ uint32_t offsetToCdHeader;
+ uint16_t commentSize;
+ uint8_t comment[0];
+ } __attribute__((packed));
+ ecdr_t* header = (ecdr_t*)cursor;
+
+ Location location;
+ location.offset = header->offsetToCdHeader;
+ location.size = header->crSize;
+ location.valid = true;
+ return location;
+}
+
+ApkArchive::Location ApkArchive::GetCDLocation() {
+ constexpr off_t cdEntryHeaderSizeBytes = 22;
+ Location location;
+
+ // Find End of Central Directory Record
+ off_t eocdRecord = FindEndOfCDRecord();
+ if (eocdRecord < 0) {
+ fprintf(stderr, "Unable to find End of Central Directory record in file '%s'\n",
+ path_.c_str());
+ return location;
+ }
+
+ // Find Central Directory Record
+ FileRegion mapped(fd_, eocdRecord, cdEntryHeaderSizeBytes);
+ location = FindCDRecord(mapped.data());
+ if (!location.valid) {
+ fprintf(stderr, "Unable to find Central Directory File Header in file '%s'\n",
+ path_.c_str());
+ return location;
+ }
+
+ return location;
+}
+
+ApkArchive::Location ApkArchive::GetSignatureLocation(off_t cdRecordOffset) {
+ Location location;
+
+ // Signature constants.
+ constexpr off_t endOfSignatureSize = 24;
+ off_t signatureOffset = cdRecordOffset - endOfSignatureSize;
+ if (signatureOffset < 0) {
+ fprintf(stderr, "Unable to find signature in file '%s'\n", path_.c_str());
+ return location;
+ }
+
+ FileRegion mapped(fd_, signatureOffset, endOfSignatureSize);
+
+ uint64_t signatureSize = *(uint64_t*)mapped.data();
+ auto* signature = mapped.data() + sizeof(signatureSize);
+ // Check if there is a v2/v3 Signature block here.
+ if (memcmp(signature, "APK Sig Block 42", 16)) {
+ return location;
+ }
+
+ // This is likely a signature block.
+ location.size = signatureSize;
+ location.offset = cdRecordOffset - location.size - 8;
+ location.valid = true;
+
+ return location;
+}
+
+std::string ApkArchive::ReadMetadata(Location loc) const {
+ FileRegion mapped(fd_, loc.offset, loc.size);
+ return {mapped.data(), mapped.size()};
+}
+
+size_t ApkArchive::ParseCentralDirectoryRecord(const char* input, size_t size, std::string* md5Hash,
+ int64_t* localFileHeaderOffset, int64_t* dataSize) {
+ // A structure representing the fixed length fields for a single
+ // record in the central directory of the archive. In addition to
+ // the fixed length fields listed here, each central directory
+ // record contains a variable length "file_name" and "extra_field"
+ // whose lengths are given by |file_name_length| and |extra_field_length|
+ // respectively.
+ static constexpr int kCDFileHeaderMagic = 0x02014b50;
+ struct CentralDirectoryRecord {
+ // The start of record signature. Must be |kSignature|.
+ uint32_t record_signature;
+ // Source tool version. Top byte gives source OS.
+ uint16_t version_made_by;
+ // Tool version. Ignored by this implementation.
+ uint16_t version_needed;
+ // The "general purpose bit flags" for this entry. The only
+ // flag value that we currently check for is the "data descriptor"
+ // flag.
+ uint16_t gpb_flags;
+ // The compression method for this entry, one of |kCompressStored|
+ // and |kCompressDeflated|.
+ uint16_t compression_method;
+ // The file modification time and date for this entry.
+ uint16_t last_mod_time;
+ uint16_t last_mod_date;
+ // The CRC-32 checksum for this entry.
+ uint32_t crc32;
+ // The compressed size (in bytes) of this entry.
+ uint32_t compressed_size;
+ // The uncompressed size (in bytes) of this entry.
+ uint32_t uncompressed_size;
+ // The length of the entry file name in bytes. The file name
+ // will appear immediately after this record.
+ uint16_t file_name_length;
+ // The length of the extra field info (in bytes). This data
+ // will appear immediately after the entry file name.
+ uint16_t extra_field_length;
+ // The length of the entry comment (in bytes). This data will
+ // appear immediately after the extra field.
+ uint16_t comment_length;
+ // The start disk for this entry. Ignored by this implementation).
+ uint16_t file_start_disk;
+ // File attributes. Ignored by this implementation.
+ uint16_t internal_file_attributes;
+ // File attributes. For archives created on Unix, the top bits are the
+ // mode.
+ uint32_t external_file_attributes;
+ // The offset to the local file header for this entry, from the
+ // beginning of this archive.
+ uint32_t local_file_header_offset;
+
+ private:
+ CentralDirectoryRecord() = default;
+ DISALLOW_COPY_AND_ASSIGN(CentralDirectoryRecord);
+ } __attribute__((packed));
+
+ const CentralDirectoryRecord* cdr;
+ if (size < sizeof(*cdr)) {
+ return 0;
+ }
+
+ auto begin = input;
+ cdr = reinterpret_cast<const CentralDirectoryRecord*>(begin);
+ if (cdr->record_signature != kCDFileHeaderMagic) {
+ fprintf(stderr, "Invalid Central Directory Record signature\n");
+ return 0;
+ }
+ auto end = begin + sizeof(*cdr) + cdr->file_name_length + cdr->extra_field_length +
+ cdr->comment_length;
+
+ uint8_t md5Digest[MD5_DIGEST_LENGTH];
+ MD5((const unsigned char*)begin, end - begin, md5Digest);
+ md5Hash->assign((const char*)md5Digest, sizeof(md5Digest));
+
+ *localFileHeaderOffset = cdr->local_file_header_offset;
+ *dataSize = (cdr->compression_method == kCompressStored) ? cdr->uncompressed_size
+ : cdr->compressed_size;
+
+ return end - begin;
+}
+
+size_t ApkArchive::CalculateLocalFileEntrySize(int64_t localFileHeaderOffset,
+ int64_t dataSize) const {
+ // The local file header for a given entry. This duplicates information
+ // present in the central directory of the archive. It is an error for
+ // the information here to be different from the central directory
+ // information for a given entry.
+ static constexpr int kLocalFileHeaderMagic = 0x04034b50;
+ struct LocalFileHeader {
+ // The local file header signature, must be |kSignature|.
+ uint32_t lfh_signature;
+ // Tool version. Ignored by this implementation.
+ uint16_t version_needed;
+ // The "general purpose bit flags" for this entry. The only
+ // flag value that we currently check for is the "data descriptor"
+ // flag.
+ uint16_t gpb_flags;
+ // The compression method for this entry, one of |kCompressStored|
+ // and |kCompressDeflated|.
+ uint16_t compression_method;
+ // The file modification time and date for this entry.
+ uint16_t last_mod_time;
+ uint16_t last_mod_date;
+ // The CRC-32 checksum for this entry.
+ uint32_t crc32;
+ // The compressed size (in bytes) of this entry.
+ uint32_t compressed_size;
+ // The uncompressed size (in bytes) of this entry.
+ uint32_t uncompressed_size;
+ // The length of the entry file name in bytes. The file name
+ // will appear immediately after this record.
+ uint16_t file_name_length;
+ // The length of the extra field info (in bytes). This data
+ // will appear immediately after the entry file name.
+ uint16_t extra_field_length;
+
+ private:
+ LocalFileHeader() = default;
+ DISALLOW_COPY_AND_ASSIGN(LocalFileHeader);
+ } __attribute__((packed));
+ static constexpr int kLocalFileHeaderSize = sizeof(LocalFileHeader);
+ CHECK(ready()) << path_;
+
+ const LocalFileHeader* lfh;
+ if (localFileHeaderOffset + kLocalFileHeaderSize > size_) {
+ fprintf(stderr,
+ "Invalid Local File Header offset in file '%s' at offset %lld, file size %lld\n",
+ path_.c_str(), static_cast<long long>(localFileHeaderOffset),
+ static_cast<long long>(size_));
+ return 0;
+ }
+
+ FileRegion lfhMapped(fd_, localFileHeaderOffset, sizeof(LocalFileHeader));
+ lfh = reinterpret_cast<const LocalFileHeader*>(lfhMapped.data());
+ if (lfh->lfh_signature != kLocalFileHeaderMagic) {
+ fprintf(stderr, "Invalid Local File Header signature in file '%s' at offset %lld\n",
+ path_.c_str(), static_cast<long long>(localFileHeaderOffset));
+ return 0;
+ }
+
+ // The *optional* data descriptor start signature.
+ static constexpr int kOptionalDataDescriptorMagic = 0x08074b50;
+ struct DataDescriptor {
+ // CRC-32 checksum of the entry.
+ uint32_t crc32;
+ // Compressed size of the entry.
+ uint32_t compressed_size;
+ // Uncompressed size of the entry.
+ uint32_t uncompressed_size;
+
+ private:
+ DataDescriptor() = default;
+ DISALLOW_COPY_AND_ASSIGN(DataDescriptor);
+ } __attribute__((packed));
+ static constexpr int kDataDescriptorSize = sizeof(DataDescriptor);
+
+ off_t ddOffset = localFileHeaderOffset + kLocalFileHeaderSize + lfh->file_name_length +
+ lfh->extra_field_length + dataSize;
+ int64_t ddSize = 0;
+
+ int64_t localDataSize;
+ if (lfh->gpb_flags & kGPBDDFlagMask) {
+ // There is trailing data descriptor.
+ const DataDescriptor* dd;
+
+ if (ddOffset + int(sizeof(uint32_t)) > size_) {
+ fprintf(stderr,
+ "Error reading trailing data descriptor signature in file '%s' at offset %lld, "
+ "file size %lld\n",
+ path_.c_str(), static_cast<long long>(ddOffset), static_cast<long long>(size_));
+ return 0;
+ }
+
+ FileRegion ddMapped(fd_, ddOffset, sizeof(uint32_t) + sizeof(DataDescriptor));
+
+ off_t localDDOffset = 0;
+ if (kOptionalDataDescriptorMagic == *(uint32_t*)ddMapped.data()) {
+ ddOffset += sizeof(uint32_t);
+ localDDOffset += sizeof(uint32_t);
+ ddSize += sizeof(uint32_t);
+ }
+ if (ddOffset + kDataDescriptorSize > size_) {
+ fprintf(stderr,
+ "Error reading trailing data descriptor in file '%s' at offset %lld, file size "
+ "%lld\n",
+ path_.c_str(), static_cast<long long>(ddOffset), static_cast<long long>(size_));
+ return 0;
+ }
+
+ dd = reinterpret_cast<const DataDescriptor*>(ddMapped.data() + localDDOffset);
+ localDataSize = (lfh->compression_method == kCompressStored) ? dd->uncompressed_size
+ : dd->compressed_size;
+ ddSize += sizeof(*dd);
+ } else {
+ localDataSize = (lfh->compression_method == kCompressStored) ? lfh->uncompressed_size
+ : lfh->compressed_size;
+ }
+ if (localDataSize != dataSize) {
+ fprintf(stderr,
+ "Data sizes mismatch in file '%s' at offset %lld, CDr: %lld vs LHR/DD: %lld\n",
+ path_.c_str(), static_cast<long long>(localFileHeaderOffset),
+ static_cast<long long>(dataSize), static_cast<long long>(localDataSize));
+ return 0;
+ }
+
+ return kLocalFileHeaderSize + lfh->file_name_length + lfh->extra_field_length + dataSize +
+ ddSize;
+}
diff --git a/adb/fastdeploy/deploypatchgenerator/apk_archive.h b/adb/fastdeploy/deploypatchgenerator/apk_archive.h
new file mode 100644
index 0000000..7127800
--- /dev/null
+++ b/adb/fastdeploy/deploypatchgenerator/apk_archive.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <adb_unique_fd.h>
+
+#include "fastdeploy/proto/ApkEntry.pb.h"
+
+class ApkArchiveTester;
+
+// Manipulates an APK archive. Process it by mmaping it in order to minimize
+// I/Os.
+class ApkArchive {
+ public:
+ friend ApkArchiveTester;
+
+ // A convenience struct to store the result of search operation when
+ // locating the EoCDr, CDr, and Signature Block.
+ struct Location {
+ off_t offset = 0;
+ off_t size = 0;
+ bool valid = false;
+ };
+
+ ApkArchive(const std::string& path);
+ ~ApkArchive();
+
+ com::android::fastdeploy::APKDump ExtractMetadata();
+
+ // Parses the CDr starting from |input| and returns number of bytes consumed.
+ // Extracts local file header offset, data size and calculates MD5 hash of the record.
+ // 0 indicates invalid CDr.
+ static size_t ParseCentralDirectoryRecord(const char* input, size_t size, std::string* md5Hash,
+ int64_t* localFileHeaderOffset, int64_t* dataSize);
+ // Calculates Local File Entry size including header using offset and data size from CDr.
+ // 0 indicates invalid Local File Entry.
+ size_t CalculateLocalFileEntrySize(int64_t localFileHeaderOffset, int64_t dataSize) const;
+
+ private:
+ std::string ReadMetadata(Location loc) const;
+
+ // Retrieve the location of the Central Directory Record.
+ Location GetCDLocation();
+
+ // Retrieve the location of the signature block starting from Central
+ // Directory Record
+ Location GetSignatureLocation(off_t cdRecordOffset);
+
+ // Find the End of Central Directory Record, starting from the end of the
+ // file.
+ off_t FindEndOfCDRecord() const;
+
+ // Find Central Directory Record, starting from the end of the file.
+ Location FindCDRecord(const char* cursor);
+
+ // Checks if the archive can be used.
+ bool ready() const { return fd_ >= 0; }
+
+ std::string path_;
+ off_t size_;
+ unique_fd fd_;
+};
diff --git a/adb/fastdeploy/deploypatchgenerator/apk_archive_test.cpp b/adb/fastdeploy/deploypatchgenerator/apk_archive_test.cpp
new file mode 100644
index 0000000..554cb57
--- /dev/null
+++ b/adb/fastdeploy/deploypatchgenerator/apk_archive_test.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <iostream>
+
+#include <gtest/gtest.h>
+
+#include "apk_archive.h"
+
+// Friend test to get around private scope of ApkArchive private functions.
+class ApkArchiveTester {
+ public:
+ ApkArchiveTester(const std::string& path) : archive_(path) {}
+
+ bool ready() { return archive_.ready(); }
+
+ auto ExtractMetadata() { return archive_.ExtractMetadata(); }
+
+ ApkArchive::Location GetCDLocation() { return archive_.GetCDLocation(); }
+ ApkArchive::Location GetSignatureLocation(size_t start) {
+ return archive_.GetSignatureLocation(start);
+ }
+
+ private:
+ ApkArchive archive_;
+};
+
+TEST(ApkArchiveTest, TestApkArchiveSizes) {
+ ApkArchiveTester archiveTester("fastdeploy/testdata/sample.apk");
+ EXPECT_TRUE(archiveTester.ready());
+
+ ApkArchive::Location cdLoc = archiveTester.GetCDLocation();
+ EXPECT_TRUE(cdLoc.valid);
+ ASSERT_EQ(cdLoc.offset, 2044145u);
+ ASSERT_EQ(cdLoc.size, 49390u);
+
+ // Check that block can be retrieved
+ ApkArchive::Location sigLoc = archiveTester.GetSignatureLocation(cdLoc.offset);
+ EXPECT_TRUE(sigLoc.valid);
+ ASSERT_EQ(sigLoc.offset, 2040049u);
+ ASSERT_EQ(sigLoc.size, 4088u);
+}
+
+TEST(ApkArchiveTest, TestApkArchiveDump) {
+ ApkArchiveTester archiveTester("fastdeploy/testdata/sample.apk");
+ EXPECT_TRUE(archiveTester.ready());
+
+ auto dump = archiveTester.ExtractMetadata();
+ ASSERT_EQ(dump.cd().size(), 49390u);
+ ASSERT_EQ(dump.signature().size(), 4088u);
+}
+
+TEST(ApkArchiveTest, WrongApk) {
+ ApkArchiveTester archiveTester("fastdeploy/testdata/sample.cd");
+ EXPECT_TRUE(archiveTester.ready());
+
+ auto dump = archiveTester.ExtractMetadata();
+ ASSERT_EQ(dump.cd().size(), 0u);
+ ASSERT_EQ(dump.signature().size(), 0u);
+}
diff --git a/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator.cpp b/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator.cpp
index 154c9b9..8aa7da7 100644
--- a/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator.cpp
+++ b/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator.cpp
@@ -25,8 +25,12 @@
#include <iostream>
#include <sstream>
#include <string>
+#include <unordered_map>
+
+#include <openssl/md5.h>
#include "adb_unique_fd.h"
+#include "adb_utils.h"
#include "android-base/file.h"
#include "patch_utils.h"
#include "sysdeps.h"
@@ -34,9 +38,6 @@
using namespace com::android::fastdeploy;
void DeployPatchGenerator::Log(const char* fmt, ...) {
- if (!is_verbose_) {
- return;
- }
va_list ap;
va_start(ap, fmt);
vprintf(fmt, ap);
@@ -44,19 +45,34 @@
va_end(ap);
}
-void DeployPatchGenerator::APKEntryToLog(const APKEntry& entry) {
- Log("Filename: %s", entry.filename().c_str());
- Log("CRC32: 0x%08" PRIX64, entry.crc32());
- Log("Data Offset: %" PRId64, entry.dataoffset());
- Log("Compressed Size: %" PRId64, entry.compressedsize());
- Log("Uncompressed Size: %" PRId64, entry.uncompressedsize());
+static std::string HexEncode(const void* in_buffer, unsigned int size) {
+ static const char kHexChars[] = "0123456789ABCDEF";
+
+ // Each input byte creates two output hex characters.
+ std::string out_buffer(size * 2, '\0');
+
+ for (unsigned int i = 0; i < size; ++i) {
+ char byte = ((const uint8_t*)in_buffer)[i];
+ out_buffer[(i << 1)] = kHexChars[(byte >> 4) & 0xf];
+ out_buffer[(i << 1) + 1] = kHexChars[byte & 0xf];
+ }
+ return out_buffer;
}
-void DeployPatchGenerator::APKMetaDataToLog(const char* file, const APKMetaData& metadata) {
+void DeployPatchGenerator::APKEntryToLog(const APKEntry& entry) {
if (!is_verbose_) {
return;
}
- Log("APK Metadata: %s", file);
+ Log("MD5: %s", HexEncode(entry.md5().data(), entry.md5().size()).c_str());
+ Log("Data Offset: %" PRId64, entry.dataoffset());
+ Log("Data Size: %" PRId64, entry.datasize());
+}
+
+void DeployPatchGenerator::APKMetaDataToLog(const APKMetaData& metadata) {
+ if (!is_verbose_) {
+ return;
+ }
+ Log("APK Metadata: %s", metadata.absolute_path().c_str());
for (int i = 0; i < metadata.entries_size(); i++) {
const APKEntry& entry = metadata.entries(i);
APKEntryToLog(entry);
@@ -65,49 +81,93 @@
void DeployPatchGenerator::ReportSavings(const std::vector<SimpleEntry>& identicalEntries,
uint64_t totalSize) {
- long totalEqualBytes = 0;
- int totalEqualFiles = 0;
+ uint64_t totalEqualBytes = 0;
+ uint64_t totalEqualFiles = 0;
for (size_t i = 0; i < identicalEntries.size(); i++) {
if (identicalEntries[i].deviceEntry != nullptr) {
- totalEqualBytes += identicalEntries[i].localEntry->compressedsize();
+ totalEqualBytes += identicalEntries[i].localEntry->datasize();
totalEqualFiles++;
}
}
- float savingPercent = (totalEqualBytes * 100.0f) / totalSize;
- fprintf(stderr, "Detected %d equal APK entries\n", totalEqualFiles);
- fprintf(stderr, "%ld bytes are equal out of %" PRIu64 " (%.2f%%)\n", totalEqualBytes, totalSize,
- savingPercent);
+ double savingPercent = (totalEqualBytes * 100.0f) / totalSize;
+ fprintf(stderr, "Detected %" PRIu64 " equal APK entries\n", totalEqualFiles);
+ fprintf(stderr, "%" PRIu64 " bytes are equal out of %" PRIu64 " (%.2f%%)\n", totalEqualBytes,
+ totalSize, savingPercent);
+}
+
+struct PatchEntry {
+ int64_t deltaFromDeviceDataStart = 0;
+ int64_t deviceDataOffset = 0;
+ int64_t deviceDataLength = 0;
+};
+static void WritePatchEntry(const PatchEntry& patchEntry, borrowed_fd input, borrowed_fd output,
+ size_t* realSizeOut) {
+ if (!(patchEntry.deltaFromDeviceDataStart | patchEntry.deviceDataOffset |
+ patchEntry.deviceDataLength)) {
+ return;
+ }
+
+ PatchUtils::WriteLong(patchEntry.deltaFromDeviceDataStart, output);
+ if (patchEntry.deltaFromDeviceDataStart > 0) {
+ PatchUtils::Pipe(input, output, patchEntry.deltaFromDeviceDataStart);
+ }
+ auto hostDataLength = patchEntry.deviceDataLength;
+ adb_lseek(input, hostDataLength, SEEK_CUR);
+
+ PatchUtils::WriteLong(patchEntry.deviceDataOffset, output);
+ PatchUtils::WriteLong(patchEntry.deviceDataLength, output);
+
+ *realSizeOut += patchEntry.deltaFromDeviceDataStart + hostDataLength;
}
void DeployPatchGenerator::GeneratePatch(const std::vector<SimpleEntry>& entriesToUseOnDevice,
- const char* localApkPath, borrowed_fd output) {
- unique_fd input(adb_open(localApkPath, O_RDONLY | O_CLOEXEC));
+ const std::string& localApkPath,
+ const std::string& deviceApkPath, borrowed_fd output) {
+ unique_fd input(adb_open(localApkPath.c_str(), O_RDONLY | O_CLOEXEC));
size_t newApkSize = adb_lseek(input, 0L, SEEK_END);
adb_lseek(input, 0L, SEEK_SET);
+ // Header.
PatchUtils::WriteSignature(output);
PatchUtils::WriteLong(newApkSize, output);
+ PatchUtils::WriteString(deviceApkPath, output);
+
size_t currentSizeOut = 0;
+ size_t realSizeOut = 0;
// Write data from the host upto the first entry we have that matches a device entry. Then write
// the metadata about the device entry and repeat for all entries that match on device. Finally
// write out any data left. If the device and host APKs are exactly the same this ends up
// writing out zip metadata from the local APK followed by offsets to the data to use from the
// device APK.
- for (auto&& entry : entriesToUseOnDevice) {
- int64_t deviceDataOffset = entry.deviceEntry->dataoffset();
+ PatchEntry patchEntry;
+ for (size_t i = 0, size = entriesToUseOnDevice.size(); i < size; ++i) {
+ auto&& entry = entriesToUseOnDevice[i];
int64_t hostDataOffset = entry.localEntry->dataoffset();
- int64_t deviceDataLength = entry.deviceEntry->compressedsize();
+ int64_t hostDataLength = entry.localEntry->datasize();
+ int64_t deviceDataOffset = entry.deviceEntry->dataoffset();
+ // Both entries are the same, using host data length.
+ int64_t deviceDataLength = hostDataLength;
+
int64_t deltaFromDeviceDataStart = hostDataOffset - currentSizeOut;
- PatchUtils::WriteLong(deltaFromDeviceDataStart, output);
if (deltaFromDeviceDataStart > 0) {
- PatchUtils::Pipe(input, output, deltaFromDeviceDataStart);
+ WritePatchEntry(patchEntry, input, output, &realSizeOut);
+ patchEntry.deltaFromDeviceDataStart = deltaFromDeviceDataStart;
+ patchEntry.deviceDataOffset = deviceDataOffset;
+ patchEntry.deviceDataLength = deviceDataLength;
+ } else {
+ patchEntry.deviceDataLength += deviceDataLength;
}
- PatchUtils::WriteLong(deviceDataOffset, output);
- PatchUtils::WriteLong(deviceDataLength, output);
- adb_lseek(input, deviceDataLength, SEEK_CUR);
- currentSizeOut += deltaFromDeviceDataStart + deviceDataLength;
+
+ currentSizeOut += deltaFromDeviceDataStart + hostDataLength;
}
- if (currentSizeOut != newApkSize) {
+ WritePatchEntry(patchEntry, input, output, &realSizeOut);
+ if (realSizeOut != currentSizeOut) {
+ fprintf(stderr, "Size mismatch current %lld vs real %lld\n",
+ static_cast<long long>(currentSizeOut), static_cast<long long>(realSizeOut));
+ error_exit("Aborting");
+ }
+
+ if (newApkSize > currentSizeOut) {
PatchUtils::WriteLong(newApkSize - currentSizeOut, output);
PatchUtils::Pipe(input, output, newApkSize - currentSizeOut);
PatchUtils::WriteLong(0, output);
@@ -115,44 +175,72 @@
}
}
-bool DeployPatchGenerator::CreatePatch(const char* localApkPath, const char* deviceApkMetadataPath,
- borrowed_fd output) {
- std::string content;
- APKMetaData deviceApkMetadata;
- if (android::base::ReadFileToString(deviceApkMetadataPath, &content)) {
- deviceApkMetadata.ParsePartialFromString(content);
- } else {
- // TODO: What do we want to do if we don't find any metadata.
- // The current fallback behavior is to build a patch with the contents of |localApkPath|.
- }
+bool DeployPatchGenerator::CreatePatch(const char* localApkPath, APKMetaData deviceApkMetadata,
+ android::base::borrowed_fd output) {
+ return CreatePatch(PatchUtils::GetHostAPKMetaData(localApkPath), std::move(deviceApkMetadata),
+ output);
+}
- APKMetaData localApkMetadata = PatchUtils::GetAPKMetaData(localApkPath);
- // Log gathered metadata info.
- APKMetaDataToLog(deviceApkMetadataPath, deviceApkMetadata);
- APKMetaDataToLog(localApkPath, localApkMetadata);
+bool DeployPatchGenerator::CreatePatch(APKMetaData localApkMetadata, APKMetaData deviceApkMetadata,
+ borrowed_fd output) {
+ // Log metadata info.
+ APKMetaDataToLog(deviceApkMetadata);
+ APKMetaDataToLog(localApkMetadata);
+
+ const std::string localApkPath = localApkMetadata.absolute_path();
+ const std::string deviceApkPath = deviceApkMetadata.absolute_path();
std::vector<SimpleEntry> identicalEntries;
uint64_t totalSize =
BuildIdenticalEntries(identicalEntries, localApkMetadata, deviceApkMetadata);
ReportSavings(identicalEntries, totalSize);
- GeneratePatch(identicalEntries, localApkPath, output);
+ GeneratePatch(identicalEntries, localApkPath, deviceApkPath, output);
+
return true;
}
uint64_t DeployPatchGenerator::BuildIdenticalEntries(std::vector<SimpleEntry>& outIdenticalEntries,
const APKMetaData& localApkMetadata,
const APKMetaData& deviceApkMetadata) {
+ outIdenticalEntries.reserve(
+ std::min(localApkMetadata.entries_size(), deviceApkMetadata.entries_size()));
+
+ using md5Digest = std::pair<uint64_t, uint64_t>;
+ struct md5Hash {
+ size_t operator()(const md5Digest& digest) const {
+ std::hash<uint64_t> hasher;
+ size_t seed = 0;
+ seed ^= hasher(digest.first) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
+ seed ^= hasher(digest.second) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
+ return seed;
+ }
+ };
+ static_assert(sizeof(md5Digest) == MD5_DIGEST_LENGTH);
+ std::unordered_map<md5Digest, std::vector<const APKEntry*>, md5Hash> deviceEntries;
+ for (const auto& deviceEntry : deviceApkMetadata.entries()) {
+ md5Digest md5;
+ memcpy(&md5, deviceEntry.md5().data(), deviceEntry.md5().size());
+
+ deviceEntries[md5].push_back(&deviceEntry);
+ }
+
uint64_t totalSize = 0;
- for (int i = 0; i < localApkMetadata.entries_size(); i++) {
- const APKEntry& localEntry = localApkMetadata.entries(i);
- totalSize += localEntry.compressedsize();
- for (int j = 0; j < deviceApkMetadata.entries_size(); j++) {
- const APKEntry& deviceEntry = deviceApkMetadata.entries(j);
- if (deviceEntry.crc32() == localEntry.crc32() &&
- deviceEntry.filename().compare(localEntry.filename()) == 0) {
+ for (const auto& localEntry : localApkMetadata.entries()) {
+ totalSize += localEntry.datasize();
+
+ md5Digest md5;
+ memcpy(&md5, localEntry.md5().data(), localEntry.md5().size());
+
+ auto deviceEntriesIt = deviceEntries.find(md5);
+ if (deviceEntriesIt == deviceEntries.end()) {
+ continue;
+ }
+
+ for (const auto* deviceEntry : deviceEntriesIt->second) {
+ if (deviceEntry->md5() == localEntry.md5()) {
SimpleEntry simpleEntry;
- simpleEntry.localEntry = const_cast<APKEntry*>(&localEntry);
- simpleEntry.deviceEntry = const_cast<APKEntry*>(&deviceEntry);
+ simpleEntry.localEntry = &localEntry;
+ simpleEntry.deviceEntry = deviceEntry;
APKEntryToLog(localEntry);
outIdenticalEntries.push_back(simpleEntry);
break;
diff --git a/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator.h b/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator.h
index 30e41a5..fd7eaee 100644
--- a/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator.h
+++ b/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator.h
@@ -27,12 +27,15 @@
*/
class DeployPatchGenerator {
public:
+ using APKEntry = com::android::fastdeploy::APKEntry;
+ using APKMetaData = com::android::fastdeploy::APKMetaData;
+
/**
* Simple struct to hold mapping between local metadata and device metadata.
*/
struct SimpleEntry {
- com::android::fastdeploy::APKEntry* localEntry;
- com::android::fastdeploy::APKEntry* deviceEntry;
+ const APKEntry* localEntry;
+ const APKEntry* deviceEntry;
};
/**
@@ -41,10 +44,10 @@
*/
explicit DeployPatchGenerator(bool is_verbose) : is_verbose_(is_verbose) {}
/**
- * Given a |localApkPath|, and the |deviceApkMetadataPath| from an installed APK this function
+ * Given a |localApkPath|, and the |deviceApkMetadata| from an installed APK this function
* writes a patch to the given |output|.
*/
- bool CreatePatch(const char* localApkPath, const char* deviceApkMetadataPath,
+ bool CreatePatch(const char* localApkPath, APKMetaData deviceApkMetadata,
android::base::borrowed_fd output);
private:
@@ -57,14 +60,20 @@
/**
* Helper function to log the APKMetaData structure. If |is_verbose_| is false this function
- * early outs. |file| is the path to the file represented by |metadata|. This function is used
- * for debugging / information.
+ * early outs. This function is used for debugging / information.
*/
- void APKMetaDataToLog(const char* file, const com::android::fastdeploy::APKMetaData& metadata);
+ void APKMetaDataToLog(const APKMetaData& metadata);
/**
* Helper function to log APKEntry.
*/
- void APKEntryToLog(const com::android::fastdeploy::APKEntry& entry);
+ void APKEntryToLog(const APKEntry& entry);
+
+ /**
+ * Given the |localApkMetadata| metadata, and the |deviceApkMetadata| from an installed APK this
+ * function writes a patch to the given |output|.
+ */
+ bool CreatePatch(APKMetaData localApkMetadata, APKMetaData deviceApkMetadata,
+ android::base::borrowed_fd output);
/**
* Helper function to report savings by fastdeploy. This function prints out savings even with
@@ -92,11 +101,11 @@
* highest.
*/
void GeneratePatch(const std::vector<SimpleEntry>& entriesToUseOnDevice,
- const char* localApkPath, android::base::borrowed_fd output);
+ const std::string& localApkPath, const std::string& deviceApkPath,
+ android::base::borrowed_fd output);
protected:
- uint64_t BuildIdenticalEntries(
- std::vector<SimpleEntry>& outIdenticalEntries,
- const com::android::fastdeploy::APKMetaData& localApkMetadata,
- const com::android::fastdeploy::APKMetaData& deviceApkMetadataPath);
-};
\ No newline at end of file
+ uint64_t BuildIdenticalEntries(std::vector<SimpleEntry>& outIdenticalEntries,
+ const APKMetaData& localApkMetadata,
+ const APKMetaData& deviceApkMetadata);
+};
diff --git a/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator_test.cpp b/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator_test.cpp
index 9cdc44e..e4c96ea 100644
--- a/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator_test.cpp
+++ b/adb/fastdeploy/deploypatchgenerator/deploy_patch_generator_test.cpp
@@ -15,6 +15,7 @@
*/
#include "deploy_patch_generator.h"
+#include "apk_archive.h"
#include "patch_utils.h"
#include <android-base/file.h>
@@ -31,21 +32,17 @@
return "fastdeploy/testdata/" + name;
}
-class TestPatchGenerator : DeployPatchGenerator {
- public:
- TestPatchGenerator() : DeployPatchGenerator(false) {}
- void GatherIdenticalEntries(std::vector<DeployPatchGenerator::SimpleEntry>& outIdenticalEntries,
- const APKMetaData& metadataA, const APKMetaData& metadataB) {
- BuildIdenticalEntries(outIdenticalEntries, metadataA, metadataB);
- }
+struct TestPatchGenerator : DeployPatchGenerator {
+ using DeployPatchGenerator::BuildIdenticalEntries;
+ using DeployPatchGenerator::DeployPatchGenerator;
};
TEST(DeployPatchGeneratorTest, IdenticalFileEntries) {
std::string apkPath = GetTestFile("rotating_cube-release.apk");
- APKMetaData metadataA = PatchUtils::GetAPKMetaData(apkPath.c_str());
- TestPatchGenerator generator;
+ APKMetaData metadataA = PatchUtils::GetHostAPKMetaData(apkPath.c_str());
+ TestPatchGenerator generator(false);
std::vector<DeployPatchGenerator::SimpleEntry> entries;
- generator.GatherIdenticalEntries(entries, metadataA, metadataA);
+ generator.BuildIdenticalEntries(entries, metadataA, metadataA);
// Expect the entry count to match the number of entries in the metadata.
const uint32_t identicalCount = entries.size();
const uint32_t entriesCount = metadataA.entries_size();
@@ -64,9 +61,28 @@
// Create a patch that is 100% different.
TemporaryFile output;
DeployPatchGenerator generator(true);
- generator.CreatePatch(apkPath.c_str(), "", output.fd);
+ generator.CreatePatch(apkPath.c_str(), {}, output.fd);
// Expect a patch file that has a size at least the size of our initial APK.
long patchSize = adb_lseek(output.fd, 0L, SEEK_END);
EXPECT_GT(patchSize, apkSize);
-}
\ No newline at end of file
+}
+
+TEST(DeployPatchGeneratorTest, ZeroSizePatch) {
+ std::string apkPath = GetTestFile("rotating_cube-release.apk");
+ ApkArchive archive(apkPath);
+ auto dump = archive.ExtractMetadata();
+ EXPECT_NE(dump.cd().size(), 0u);
+
+ APKMetaData metadata = PatchUtils::GetDeviceAPKMetaData(dump);
+
+ // Create a patch that is 100% the same.
+ TemporaryFile output;
+ output.DoNotRemove();
+ DeployPatchGenerator generator(true);
+ generator.CreatePatch(apkPath.c_str(), metadata, output.fd);
+
+ // Expect a patch file that is smaller than 0.5K.
+ int64_t patchSize = adb_lseek(output.fd, 0L, SEEK_END);
+ EXPECT_LE(patchSize, 512);
+}
diff --git a/adb/fastdeploy/deploypatchgenerator/patch_utils.cpp b/adb/fastdeploy/deploypatchgenerator/patch_utils.cpp
index f11ddd1..2b00c80 100644
--- a/adb/fastdeploy/deploypatchgenerator/patch_utils.cpp
+++ b/adb/fastdeploy/deploypatchgenerator/patch_utils.cpp
@@ -16,72 +16,94 @@
#include "patch_utils.h"
-#include <androidfw/ZipFileRO.h>
#include <stdio.h>
#include "adb_io.h"
+#include "adb_utils.h"
#include "android-base/endian.h"
#include "sysdeps.h"
+#include "apk_archive.h"
+
using namespace com::android;
using namespace com::android::fastdeploy;
using namespace android::base;
static constexpr char kSignature[] = "FASTDEPLOY";
-APKMetaData PatchUtils::GetAPKMetaData(const char* apkPath) {
+APKMetaData PatchUtils::GetDeviceAPKMetaData(const APKDump& apk_dump) {
APKMetaData apkMetaData;
-#undef open
- std::unique_ptr<android::ZipFileRO> zipFile(android::ZipFileRO::open(apkPath));
-#define open ___xxx_unix_open
- if (zipFile == nullptr) {
- printf("Could not open %s", apkPath);
- exit(1);
- }
- void* cookie;
- if (zipFile->startIteration(&cookie)) {
- android::ZipEntryRO entry;
- while ((entry = zipFile->nextEntry(cookie)) != NULL) {
- char fileName[256];
- // Make sure we have a file name.
- // TODO: Handle filenames longer than 256.
- if (zipFile->getEntryFileName(entry, fileName, sizeof(fileName))) {
- continue;
- }
+ apkMetaData.set_absolute_path(apk_dump.absolute_path());
- uint32_t uncompressedSize, compressedSize, crc32;
- int64_t dataOffset;
- zipFile->getEntryInfo(entry, nullptr, &uncompressedSize, &compressedSize, &dataOffset,
- nullptr, &crc32);
- APKEntry* apkEntry = apkMetaData.add_entries();
- apkEntry->set_crc32(crc32);
- apkEntry->set_filename(fileName);
- apkEntry->set_compressedsize(compressedSize);
- apkEntry->set_uncompressedsize(uncompressedSize);
- apkEntry->set_dataoffset(dataOffset);
- }
+ std::string md5Hash;
+ int64_t localFileHeaderOffset;
+ int64_t dataSize;
+
+ const auto& cd = apk_dump.cd();
+ auto cur = cd.data();
+ int64_t size = cd.size();
+ while (auto consumed = ApkArchive::ParseCentralDirectoryRecord(
+ cur, size, &md5Hash, &localFileHeaderOffset, &dataSize)) {
+ cur += consumed;
+ size -= consumed;
+
+ auto apkEntry = apkMetaData.add_entries();
+ apkEntry->set_md5(md5Hash);
+ apkEntry->set_dataoffset(localFileHeaderOffset);
+ apkEntry->set_datasize(dataSize);
}
return apkMetaData;
}
+APKMetaData PatchUtils::GetHostAPKMetaData(const char* apkPath) {
+ ApkArchive archive(apkPath);
+ auto dump = archive.ExtractMetadata();
+ if (dump.cd().empty()) {
+ fprintf(stderr, "adb: Could not extract Central Directory from %s\n", apkPath);
+ error_exit("Aborting");
+ }
+
+ auto apkMetaData = GetDeviceAPKMetaData(dump);
+
+ // Now let's set data sizes.
+ for (auto& apkEntry : *apkMetaData.mutable_entries()) {
+ auto dataSize =
+ archive.CalculateLocalFileEntrySize(apkEntry.dataoffset(), apkEntry.datasize());
+ if (dataSize == 0) {
+ error_exit("Aborting");
+ }
+ apkEntry.set_datasize(dataSize);
+ }
+
+ return apkMetaData;
+}
+
void PatchUtils::WriteSignature(borrowed_fd output) {
WriteFdExactly(output, kSignature, sizeof(kSignature) - 1);
}
void PatchUtils::WriteLong(int64_t value, borrowed_fd output) {
- int64_t toLittleEndian = htole64(value);
- WriteFdExactly(output, &toLittleEndian, sizeof(int64_t));
+ int64_t littleEndian = htole64(value);
+ WriteFdExactly(output, &littleEndian, sizeof(littleEndian));
+}
+
+void PatchUtils::WriteString(const std::string& value, android::base::borrowed_fd output) {
+ WriteLong(value.size(), output);
+ WriteFdExactly(output, value);
}
void PatchUtils::Pipe(borrowed_fd input, borrowed_fd output, size_t amount) {
- constexpr static int BUFFER_SIZE = 128 * 1024;
+ constexpr static size_t BUFFER_SIZE = 128 * 1024;
char buffer[BUFFER_SIZE];
size_t transferAmount = 0;
while (transferAmount != amount) {
- long chunkAmount =
- amount - transferAmount > BUFFER_SIZE ? BUFFER_SIZE : amount - transferAmount;
- long readAmount = adb_read(input, buffer, chunkAmount);
+ auto chunkAmount = std::min(amount - transferAmount, BUFFER_SIZE);
+ auto readAmount = adb_read(input, buffer, chunkAmount);
+ if (readAmount < 0) {
+ fprintf(stderr, "adb: failed to read from input: %s\n", strerror(errno));
+ error_exit("Aborting");
+ }
WriteFdExactly(output, buffer, readAmount);
transferAmount += readAmount;
}
-}
\ No newline at end of file
+}
diff --git a/adb/fastdeploy/deploypatchgenerator/patch_utils.h b/adb/fastdeploy/deploypatchgenerator/patch_utils.h
index 0ebfe8f..8dc9b9c 100644
--- a/adb/fastdeploy/deploypatchgenerator/patch_utils.h
+++ b/adb/fastdeploy/deploypatchgenerator/patch_utils.h
@@ -25,11 +25,18 @@
class PatchUtils {
public:
/**
+ * This function takes the dump of Central Directly and builds the APKMetaData required by the
+ * patching algorithm. The if this function has an error a string is printed to the terminal and
+ * exit(1) is called.
+ */
+ static com::android::fastdeploy::APKMetaData GetDeviceAPKMetaData(
+ const com::android::fastdeploy::APKDump& apk_dump);
+ /**
* This function takes a local APK file and builds the APKMetaData required by the patching
* algorithm. The if this function has an error a string is printed to the terminal and exit(1)
* is called.
*/
- static com::android::fastdeploy::APKMetaData GetAPKMetaData(const char* file);
+ static com::android::fastdeploy::APKMetaData GetHostAPKMetaData(const char* file);
/**
* Writes a fixed signature string to the header of the patch.
*/
@@ -39,8 +46,12 @@
*/
static void WriteLong(int64_t value, android::base::borrowed_fd output);
/**
+ * Writes string to the |output|.
+ */
+ static void WriteString(const std::string& value, android::base::borrowed_fd output);
+ /**
* Copy |amount| of data from |input| to |output|.
*/
static void Pipe(android::base::borrowed_fd input, android::base::borrowed_fd output,
size_t amount);
-};
\ No newline at end of file
+};
diff --git a/adb/fastdeploy/deploypatchgenerator/patch_utils_test.cpp b/adb/fastdeploy/deploypatchgenerator/patch_utils_test.cpp
index a7eeebf..3ec5ab3 100644
--- a/adb/fastdeploy/deploypatchgenerator/patch_utils_test.cpp
+++ b/adb/fastdeploy/deploypatchgenerator/patch_utils_test.cpp
@@ -23,10 +23,13 @@
#include <sstream>
#include <string>
+#include <google/protobuf/util/message_differencer.h>
+
#include "adb_io.h"
#include "sysdeps.h"
using namespace com::android::fastdeploy;
+using google::protobuf::util::MessageDifferencer;
static std::string GetTestFile(const std::string& name) {
return "fastdeploy/testdata/" + name;
@@ -86,11 +89,56 @@
TEST(PatchUtilsTest, GatherMetadata) {
std::string apkFile = GetTestFile("rotating_cube-release.apk");
- APKMetaData metadata = PatchUtils::GetAPKMetaData(apkFile.c_str());
+ APKMetaData actual = PatchUtils::GetHostAPKMetaData(apkFile.c_str());
+
std::string expectedMetadata;
android::base::ReadFileToString(GetTestFile("rotating_cube-metadata-release.data"),
&expectedMetadata);
+ APKMetaData expected;
+ EXPECT_TRUE(expected.ParseFromString(expectedMetadata));
+
+ // Test paths might vary.
+ expected.set_absolute_path(actual.absolute_path());
+
std::string actualMetadata;
- metadata.SerializeToString(&actualMetadata);
+ actual.SerializeToString(&actualMetadata);
+
+ expected.SerializeToString(&expectedMetadata);
+
EXPECT_EQ(expectedMetadata, actualMetadata);
-}
\ No newline at end of file
+}
+
+static inline void sanitize(APKMetaData& metadata) {
+ metadata.clear_absolute_path();
+ for (auto&& entry : *metadata.mutable_entries()) {
+ entry.clear_datasize();
+ }
+}
+
+TEST(PatchUtilsTest, GatherDumpMetadata) {
+ APKMetaData hostMetadata;
+ APKMetaData deviceMetadata;
+
+ hostMetadata = PatchUtils::GetHostAPKMetaData(GetTestFile("sample.apk").c_str());
+
+ {
+ std::string cd;
+ android::base::ReadFileToString(GetTestFile("sample.cd"), &cd);
+
+ APKDump dump;
+ dump.set_cd(std::move(cd));
+
+ deviceMetadata = PatchUtils::GetDeviceAPKMetaData(dump);
+ }
+
+ sanitize(hostMetadata);
+ sanitize(deviceMetadata);
+
+ std::string expectedMetadata;
+ hostMetadata.SerializeToString(&expectedMetadata);
+
+ std::string actualMetadata;
+ deviceMetadata.SerializeToString(&actualMetadata);
+
+ EXPECT_EQ(expectedMetadata, actualMetadata);
+}
diff --git a/adb/fastdeploy/proto/ApkEntry.proto b/adb/fastdeploy/proto/ApkEntry.proto
index 9460d15..d84c5a5 100644
--- a/adb/fastdeploy/proto/ApkEntry.proto
+++ b/adb/fastdeploy/proto/ApkEntry.proto
@@ -1,18 +1,26 @@
-syntax = "proto2";
+syntax = "proto3";
package com.android.fastdeploy;
option java_package = "com.android.fastdeploy";
+option java_outer_classname = "ApkEntryProto";
option java_multiple_files = true;
+option optimize_for = LITE_RUNTIME;
+
+message APKDump {
+ string name = 1;
+ bytes cd = 2;
+ bytes signature = 3;
+ string absolute_path = 4;
+}
message APKEntry {
- required int64 crc32 = 1;
- required string fileName = 2;
- required int64 dataOffset = 3;
- required int64 compressedSize = 4;
- required int64 uncompressedSize = 5;
+ bytes md5 = 1;
+ int64 dataOffset = 2;
+ int64 dataSize = 3;
}
message APKMetaData {
- repeated APKEntry entries = 1;
+ string absolute_path = 1;
+ repeated APKEntry entries = 2;
}
diff --git a/adb/fastdeploy/testdata/helloworld5.apk b/adb/fastdeploy/testdata/helloworld5.apk
new file mode 100644
index 0000000..4a1539e
--- /dev/null
+++ b/adb/fastdeploy/testdata/helloworld5.apk
Binary files differ
diff --git a/adb/fastdeploy/testdata/helloworld7.apk b/adb/fastdeploy/testdata/helloworld7.apk
new file mode 100644
index 0000000..82c46df
--- /dev/null
+++ b/adb/fastdeploy/testdata/helloworld7.apk
Binary files differ
diff --git a/adb/fastdeploy/testdata/rotating_cube-metadata-release.data b/adb/fastdeploy/testdata/rotating_cube-metadata-release.data
index 0671bf3..52352ff 100644
--- a/adb/fastdeploy/testdata/rotating_cube-metadata-release.data
+++ b/adb/fastdeploy/testdata/rotating_cube-metadata-release.data
Binary files differ
diff --git a/adb/fastdeploy/testdata/sample.apk b/adb/fastdeploy/testdata/sample.apk
new file mode 100644
index 0000000..c316205
--- /dev/null
+++ b/adb/fastdeploy/testdata/sample.apk
Binary files differ
diff --git a/adb/fastdeploy/testdata/sample.cd b/adb/fastdeploy/testdata/sample.cd
new file mode 100644
index 0000000..5e5b4d4
--- /dev/null
+++ b/adb/fastdeploy/testdata/sample.cd
Binary files differ
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index 987f994..466c2ce 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -601,6 +601,10 @@
return path[0] == '/';
}
+static __inline__ int adb_get_os_handle(borrowed_fd fd) {
+ return fd.get();
+}
+
#endif /* !_WIN32 */
static inline void disable_tcp_nagle(borrowed_fd fd) {
diff --git a/base/file.cpp b/base/file.cpp
index 3dfcfbb..6321fc6 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -49,29 +49,54 @@
#include "android-base/unique_fd.h"
#include "android-base/utf8.h"
+namespace {
+
#ifdef _WIN32
-int mkstemp(char* template_name) {
- if (_mktemp(template_name) == nullptr) {
+static int mkstemp(char* name_template, size_t size_in_chars) {
+ std::wstring path;
+ CHECK(android::base::UTF8ToWide(name_template, &path))
+ << "path can't be converted to wchar: " << name_template;
+ if (_wmktemp_s(path.data(), path.size() + 1) != 0) {
return -1;
}
+
// Use open() to match the close() that TemporaryFile's destructor does.
// Use O_BINARY to match base file APIs.
- return open(template_name, O_CREAT | O_EXCL | O_RDWR | O_BINARY, S_IRUSR | S_IWUSR);
+ int fd = _wopen(path.c_str(), O_CREAT | O_EXCL | O_RDWR | O_BINARY, S_IRUSR | S_IWUSR);
+ if (fd < 0) {
+ return -1;
+ }
+
+ std::string path_utf8;
+ CHECK(android::base::WideToUTF8(path, &path_utf8)) << "path can't be converted to utf8";
+ CHECK(strcpy_s(name_template, size_in_chars, path_utf8.c_str()) == 0)
+ << "utf8 path can't be assigned back to name_template";
+
+ return fd;
}
-char* mkdtemp(char* template_name) {
- if (_mktemp(template_name) == nullptr) {
+static char* mkdtemp(char* name_template, size_t size_in_chars) {
+ std::wstring path;
+ CHECK(android::base::UTF8ToWide(name_template, &path))
+ << "path can't be converted to wchar: " << name_template;
+
+ if (_wmktemp_s(path.data(), path.size() + 1) != 0) {
return nullptr;
}
- if (_mkdir(template_name) == -1) {
+
+ if (_wmkdir(path.c_str()) != 0) {
return nullptr;
}
- return template_name;
+
+ std::string path_utf8;
+ CHECK(android::base::WideToUTF8(path, &path_utf8)) << "path can't be converted to utf8";
+ CHECK(strcpy_s(name_template, size_in_chars, path_utf8.c_str()) == 0)
+ << "utf8 path can't be assigned back to name_template";
+
+ return name_template;
}
#endif
-namespace {
-
std::string GetSystemTempDir() {
#if defined(__ANDROID__)
const auto* tmpdir = getenv("TMPDIR");
@@ -83,15 +108,20 @@
// so try current directory if /data/local/tmp is not accessible.
return ".";
#elif defined(_WIN32)
- char tmp_dir[MAX_PATH];
- DWORD result = GetTempPathA(sizeof(tmp_dir), tmp_dir); // checks TMP env
- CHECK_NE(result, 0ul) << "GetTempPathA failed, error: " << GetLastError();
- CHECK_LT(result, sizeof(tmp_dir)) << "path truncated to: " << result;
+ wchar_t tmp_dir_w[MAX_PATH];
+ DWORD result = GetTempPathW(std::size(tmp_dir_w), tmp_dir_w); // checks TMP env
+ CHECK_NE(result, 0ul) << "GetTempPathW failed, error: " << GetLastError();
+ CHECK_LT(result, std::size(tmp_dir_w)) << "path truncated to: " << result;
// GetTempPath() returns a path with a trailing slash, but init()
// does not expect that, so remove it.
- CHECK_EQ(tmp_dir[result - 1], '\\');
- tmp_dir[result - 1] = '\0';
+ if (tmp_dir_w[result - 1] == L'\\') {
+ tmp_dir_w[result - 1] = L'\0';
+ }
+
+ std::string tmp_dir;
+ CHECK(android::base::WideToUTF8(tmp_dir_w, &tmp_dir)) << "path can't be converted to utf8";
+
return tmp_dir;
#else
const auto* tmpdir = getenv("TMPDIR");
@@ -127,7 +157,11 @@
void TemporaryFile::init(const std::string& tmp_dir) {
snprintf(path, sizeof(path), "%s%cTemporaryFile-XXXXXX", tmp_dir.c_str(), OS_PATH_SEPARATOR);
+#if defined(_WIN32)
+ fd = mkstemp(path, sizeof(path));
+#else
fd = mkstemp(path);
+#endif
}
TemporaryDir::TemporaryDir() {
@@ -167,7 +201,11 @@
bool TemporaryDir::init(const std::string& tmp_dir) {
snprintf(path, sizeof(path), "%s%cTemporaryDir-XXXXXX", tmp_dir.c_str(), OS_PATH_SEPARATOR);
+#if defined(_WIN32)
+ return (mkdtemp(path, sizeof(path)) != nullptr);
+#else
return (mkdtemp(path) != nullptr);
+#endif
}
namespace android {
diff --git a/base/file_test.cpp b/base/file_test.cpp
index f64e81c..120228d 100644
--- a/base/file_test.cpp
+++ b/base/file_test.cpp
@@ -16,18 +16,25 @@
#include "android-base/file.h"
+#include "android-base/utf8.h"
+
#include <gtest/gtest.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
+#include <wchar.h>
#include <string>
#if !defined(_WIN32)
#include <pwd.h>
+#else
+#include <windows.h>
#endif
+#include "android-base/logging.h" // and must be after windows.h for ERROR
+
TEST(file, ReadFileToString_ENOENT) {
std::string s("hello");
errno = 0;
@@ -38,7 +45,7 @@
TEST(file, ReadFileToString_WriteStringToFile) {
TemporaryFile tf;
- ASSERT_TRUE(tf.fd != -1);
+ ASSERT_NE(tf.fd, -1) << tf.path;
ASSERT_TRUE(android::base::WriteStringToFile("abc", tf.path))
<< strerror(errno);
std::string s;
@@ -70,7 +77,7 @@
#if !defined(_WIN32)
TEST(file, WriteStringToFile2) {
TemporaryFile tf;
- ASSERT_TRUE(tf.fd != -1);
+ ASSERT_NE(tf.fd, -1) << tf.path;
ASSERT_TRUE(android::base::WriteStringToFile("abc", tf.path, 0660,
getuid(), getgid()))
<< strerror(errno);
@@ -86,9 +93,92 @@
}
#endif
+#if defined(_WIN32)
+TEST(file, NonUnicodeCharsWindows) {
+ constexpr auto kMaxEnvVariableValueSize = 32767;
+ std::wstring old_tmp;
+ old_tmp.resize(kMaxEnvVariableValueSize);
+ old_tmp.resize(GetEnvironmentVariableW(L"TMP", old_tmp.data(), old_tmp.size()));
+ if (old_tmp.empty()) {
+ // Can't continue with empty TMP folder.
+ return;
+ }
+
+ std::wstring new_tmp = old_tmp;
+ if (new_tmp.back() != L'\\') {
+ new_tmp.push_back(L'\\');
+ }
+
+ {
+ auto path(new_tmp + L"锦绣成都\\");
+ _wmkdir(path.c_str());
+ ASSERT_TRUE(SetEnvironmentVariableW(L"TMP", path.c_str()));
+
+ TemporaryFile tf;
+ ASSERT_NE(tf.fd, -1) << tf.path;
+ ASSERT_TRUE(android::base::WriteStringToFd("abc", tf.fd));
+
+ ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
+
+ std::string s;
+ ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s)) << strerror(errno);
+ EXPECT_EQ("abc", s);
+ }
+ {
+ auto path(new_tmp + L"директория с длинным именем\\");
+ _wmkdir(path.c_str());
+ ASSERT_TRUE(SetEnvironmentVariableW(L"TMP", path.c_str()));
+
+ TemporaryFile tf;
+ ASSERT_NE(tf.fd, -1) << tf.path;
+ ASSERT_TRUE(android::base::WriteStringToFd("abc", tf.fd));
+
+ ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
+
+ std::string s;
+ ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s)) << strerror(errno);
+ EXPECT_EQ("abc", s);
+ }
+ {
+ auto path(new_tmp + L"äüöß weiß\\");
+ _wmkdir(path.c_str());
+ ASSERT_TRUE(SetEnvironmentVariableW(L"TMP", path.c_str()));
+
+ TemporaryFile tf;
+ ASSERT_NE(tf.fd, -1) << tf.path;
+ ASSERT_TRUE(android::base::WriteStringToFd("abc", tf.fd));
+
+ ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
+
+ std::string s;
+ ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s)) << strerror(errno);
+ EXPECT_EQ("abc", s);
+ }
+
+ SetEnvironmentVariableW(L"TMP", old_tmp.c_str());
+}
+
+TEST(file, RootDirectoryWindows) {
+ constexpr auto kMaxEnvVariableValueSize = 32767;
+ std::wstring old_tmp;
+ bool tmp_is_empty = false;
+ old_tmp.resize(kMaxEnvVariableValueSize);
+ old_tmp.resize(GetEnvironmentVariableW(L"TMP", old_tmp.data(), old_tmp.size()));
+ if (old_tmp.empty()) {
+ tmp_is_empty = (GetLastError() == ERROR_ENVVAR_NOT_FOUND);
+ }
+ SetEnvironmentVariableW(L"TMP", L"C:");
+
+ TemporaryFile tf;
+ ASSERT_NE(tf.fd, -1) << tf.path;
+
+ SetEnvironmentVariableW(L"TMP", tmp_is_empty ? nullptr : old_tmp.c_str());
+}
+#endif
+
TEST(file, WriteStringToFd) {
TemporaryFile tf;
- ASSERT_TRUE(tf.fd != -1);
+ ASSERT_NE(tf.fd, -1) << tf.path;
ASSERT_TRUE(android::base::WriteStringToFd("abc", tf.fd));
ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
@@ -100,7 +190,7 @@
TEST(file, WriteFully) {
TemporaryFile tf;
- ASSERT_TRUE(tf.fd != -1);
+ ASSERT_NE(tf.fd, -1) << tf.path;
ASSERT_TRUE(android::base::WriteFully(tf.fd, "abc", 3));
ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET)) << strerror(errno);
@@ -119,7 +209,7 @@
TEST(file, RemoveFileIfExists) {
TemporaryFile tf;
- ASSERT_TRUE(tf.fd != -1);
+ ASSERT_NE(tf.fd, -1) << tf.path;
close(tf.fd);
tf.fd = -1;
std::string err;
@@ -253,7 +343,7 @@
TEST(file, ReadFileToString_capacity) {
TemporaryFile tf;
- ASSERT_TRUE(tf.fd != -1);
+ ASSERT_NE(tf.fd, -1) << tf.path;
// For a huge file, the overhead should still be small.
std::string s;
@@ -280,7 +370,7 @@
TEST(file, ReadFileToString_capacity_0) {
TemporaryFile tf;
- ASSERT_TRUE(tf.fd != -1);
+ ASSERT_NE(tf.fd, -1) << tf.path;
// Because /proc reports its files as zero-length, we don't actually trust
// any file that claims to be zero-length. Rather than add increasingly
diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp
index 354aafa..02c7de6 100644
--- a/fs_mgr/libsnapshot/snapshot.cpp
+++ b/fs_mgr/libsnapshot/snapshot.cpp
@@ -1307,8 +1307,8 @@
// device itself. This device consists of the real blocks in the super
// partition that this logical partition occupies.
auto& dm = DeviceMapper::Instance();
- std::string ignore_path;
- if (!CreateLogicalPartition(params, &ignore_path)) {
+ std::string base_path;
+ if (!CreateLogicalPartition(params, &base_path)) {
LOG(ERROR) << "Could not create logical partition " << params.GetPartitionName()
<< " as device " << params.GetDeviceName();
return false;
@@ -1316,6 +1316,7 @@
created_devices.EmplaceBack<AutoUnmapDevice>(&dm, params.GetDeviceName());
if (!live_snapshot_status.has_value()) {
+ *path = base_path;
created_devices.Release();
return true;
}
diff --git a/init/property_service.cpp b/init/property_service.cpp
index f5d1143..d7e4021 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -963,6 +963,10 @@
// Don't check for failure here, so we always have a sane list of properties.
// E.g. In case of recovery, the vendor partition will not have mounted and we
// still need the system / platform properties to function.
+ if (access("/system_ext/etc/selinux/system_ext_property_contexts", R_OK) != -1) {
+ LoadPropertyInfoFromFile("/system_ext/etc/selinux/system_ext_property_contexts",
+ &property_infos);
+ }
if (!LoadPropertyInfoFromFile("/vendor/etc/selinux/vendor_property_contexts",
&property_infos)) {
// Fallback to nonplat_* if vendor_* doesn't exist.
@@ -980,6 +984,7 @@
if (!LoadPropertyInfoFromFile("/plat_property_contexts", &property_infos)) {
return;
}
+ LoadPropertyInfoFromFile("/system_ext_property_contexts", &property_infos);
if (!LoadPropertyInfoFromFile("/vendor_property_contexts", &property_infos)) {
// Fallback to nonplat_* if vendor_* doesn't exist.
LoadPropertyInfoFromFile("/nonplat_property_contexts", &property_infos);
diff --git a/libkeyutils/Android.bp b/libkeyutils/Android.bp
index dda491a..b388e95 100644
--- a/libkeyutils/Android.bp
+++ b/libkeyutils/Android.bp
@@ -16,17 +16,3 @@
srcs: ["keyutils_test.cpp"],
test_suites: ["device-tests"],
}
-
-cc_binary {
- name: "mini-keyctl",
- srcs: [
- "mini_keyctl.cpp",
- "mini_keyctl_utils.cpp"
- ],
- shared_libs: [
- "libbase",
- "libkeyutils",
- "liblog",
- ],
- cflags: ["-Werror", "-Wall", "-Wextra", "-fexceptions"],
-}
diff --git a/libkeyutils/mini_keyctl.cpp b/libkeyutils/mini_keyctl.cpp
deleted file mode 100644
index fe89e62..0000000
--- a/libkeyutils/mini_keyctl.cpp
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/*
- * A tool loads keys to keyring.
- */
-
-#include "mini_keyctl_utils.h"
-
-#include <error.h>
-#include <stdio.h>
-#include <unistd.h>
-
-#include <android-base/parseint.h>
-
-static void Usage(int exit_code) {
- fprintf(stderr, "usage: mini-keyctl <action> [args,]\n");
- fprintf(stderr, " mini-keyctl add <type> <desc> <data> <keyring>\n");
- fprintf(stderr, " mini-keyctl padd <type> <desc> <keyring>\n");
- fprintf(stderr, " mini-keyctl unlink <key> <keyring>\n");
- fprintf(stderr, " mini-keyctl restrict_keyring <keyring>\n");
- fprintf(stderr, " mini-keyctl security <key>\n");
- _exit(exit_code);
-}
-
-static key_serial_t parseKeyOrDie(const char* str) {
- key_serial_t key;
- if (!android::base::ParseInt(str, &key)) {
- error(1 /* exit code */, 0 /* errno */, "Unparsable key: '%s'\n", str);
- }
- return key;
-}
-
-int main(int argc, const char** argv) {
- if (argc < 2) Usage(1);
- const std::string action = argv[1];
-
- if (action == "add") {
- if (argc != 6) Usage(1);
- std::string type = argv[2];
- std::string desc = argv[3];
- std::string data = argv[4];
- std::string keyring = argv[5];
- return Add(type, desc, data, keyring);
- } else if (action == "padd") {
- if (argc != 5) Usage(1);
- std::string type = argv[2];
- std::string desc = argv[3];
- std::string keyring = argv[4];
- return Padd(type, desc, keyring);
- } else if (action == "restrict_keyring") {
- if (argc != 3) Usage(1);
- std::string keyring = argv[2];
- return RestrictKeyring(keyring);
- } else if (action == "unlink") {
- if (argc != 4) Usage(1);
- key_serial_t key = parseKeyOrDie(argv[2]);
- const std::string keyring = argv[3];
- return Unlink(key, keyring);
- } else if (action == "security") {
- if (argc != 3) Usage(1);
- const char* key_str = argv[2];
- key_serial_t key = parseKeyOrDie(key_str);
- std::string context = RetrieveSecurityContext(key);
- if (context.empty()) {
- perror(key_str);
- return 1;
- }
- fprintf(stderr, "%s\n", context.c_str());
- return 0;
- } else {
- fprintf(stderr, "Unrecognized action: %s\n", action.c_str());
- Usage(1);
- }
-
- return 0;
-}
diff --git a/libkeyutils/mini_keyctl/Android.bp b/libkeyutils/mini_keyctl/Android.bp
new file mode 100644
index 0000000..a04a3db
--- /dev/null
+++ b/libkeyutils/mini_keyctl/Android.bp
@@ -0,0 +1,27 @@
+cc_library_static {
+ name: "libmini_keyctl_static",
+ srcs: [
+ "mini_keyctl_utils.cpp"
+ ],
+ shared_libs: [
+ "libbase",
+ "libkeyutils",
+ ],
+ cflags: ["-Werror", "-Wall", "-Wextra"],
+ export_include_dirs: ["."],
+}
+
+cc_binary {
+ name: "mini-keyctl",
+ srcs: [
+ "mini_keyctl.cpp",
+ ],
+ static_libs: [
+ "libmini_keyctl_static",
+ ],
+ shared_libs: [
+ "libbase",
+ "libkeyutils",
+ ],
+ cflags: ["-Werror", "-Wall", "-Wextra"],
+}
diff --git a/libkeyutils/mini_keyctl/mini_keyctl.cpp b/libkeyutils/mini_keyctl/mini_keyctl.cpp
new file mode 100644
index 0000000..8aace9a
--- /dev/null
+++ b/libkeyutils/mini_keyctl/mini_keyctl.cpp
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * A tool loads keys to keyring.
+ */
+
+#include <dirent.h>
+#include <errno.h>
+#include <error.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <iostream>
+#include <iterator>
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/parseint.h>
+#include <keyutils.h>
+#include <mini_keyctl_utils.h>
+
+constexpr int kMaxCertSize = 4096;
+
+static void Usage(int exit_code) {
+ fprintf(stderr, "usage: mini-keyctl <action> [args,]\n");
+ fprintf(stderr, " mini-keyctl add <type> <desc> <data> <keyring>\n");
+ fprintf(stderr, " mini-keyctl padd <type> <desc> <keyring>\n");
+ fprintf(stderr, " mini-keyctl unlink <key> <keyring>\n");
+ fprintf(stderr, " mini-keyctl restrict_keyring <keyring>\n");
+ fprintf(stderr, " mini-keyctl security <key>\n");
+ _exit(exit_code);
+}
+
+static key_serial_t parseKeyOrDie(const char* str) {
+ key_serial_t key;
+ if (!android::base::ParseInt(str, &key)) {
+ error(1 /* exit code */, 0 /* errno */, "Unparsable key: '%s'\n", str);
+ }
+ return key;
+}
+
+int Unlink(key_serial_t key, const std::string& keyring) {
+ key_serial_t keyring_id = android::GetKeyringId(keyring);
+ if (keyctl_unlink(key, keyring_id) < 0) {
+ error(1, errno, "Failed to unlink key %x from keyring %s", key, keyring.c_str());
+ return 1;
+ }
+ return 0;
+}
+
+int Add(const std::string& type, const std::string& desc, const std::string& data,
+ const std::string& keyring) {
+ if (data.size() > kMaxCertSize) {
+ error(1, 0, "Certificate too large");
+ return 1;
+ }
+
+ key_serial_t keyring_id = android::GetKeyringId(keyring);
+ key_serial_t key = add_key(type.c_str(), desc.c_str(), data.c_str(), data.size(), keyring_id);
+
+ if (key < 0) {
+ error(1, errno, "Failed to add key");
+ return 1;
+ }
+
+ std::cout << key << std::endl;
+ return 0;
+}
+
+int Padd(const std::string& type, const std::string& desc, const std::string& keyring) {
+ key_serial_t keyring_id = android::GetKeyringId(keyring);
+
+ // read from stdin to get the certificates
+ std::istreambuf_iterator<char> begin(std::cin), end;
+ std::string data(begin, end);
+
+ if (data.size() > kMaxCertSize) {
+ error(1, 0, "Certificate too large");
+ return 1;
+ }
+
+ key_serial_t key = add_key(type.c_str(), desc.c_str(), data.c_str(), data.size(), keyring_id);
+
+ if (key < 0) {
+ error(1, errno, "Failed to add key");
+ return 1;
+ }
+
+ std::cout << key << std::endl;
+ return 0;
+}
+
+int RestrictKeyring(const std::string& keyring) {
+ key_serial_t keyring_id = android::GetKeyringId(keyring);
+ if (keyctl_restrict_keyring(keyring_id, nullptr, nullptr) < 0) {
+ error(1, errno, "Cannot restrict keyring '%s'", keyring.c_str());
+ return 1;
+ }
+ return 0;
+}
+
+std::string RetrieveSecurityContext(key_serial_t key) {
+ // Simply assume this size is enough in practice.
+ const int kMaxSupportedSize = 256;
+ std::string context;
+ context.resize(kMaxSupportedSize);
+ long retval = keyctl_get_security(key, context.data(), kMaxSupportedSize);
+ if (retval < 0) {
+ error(1, errno, "Cannot get security context of key %x", key);
+ return std::string();
+ }
+ if (retval > kMaxSupportedSize) {
+ error(1, 0, "The key has unexpectedly long security context than %d", kMaxSupportedSize);
+ return std::string();
+ }
+ context.resize(retval);
+ return context;
+}
+
+int main(int argc, const char** argv) {
+ if (argc < 2) Usage(1);
+ const std::string action = argv[1];
+
+ if (action == "add") {
+ if (argc != 6) Usage(1);
+ std::string type = argv[2];
+ std::string desc = argv[3];
+ std::string data = argv[4];
+ std::string keyring = argv[5];
+ return Add(type, desc, data, keyring);
+ } else if (action == "padd") {
+ if (argc != 5) Usage(1);
+ std::string type = argv[2];
+ std::string desc = argv[3];
+ std::string keyring = argv[4];
+ return Padd(type, desc, keyring);
+ } else if (action == "restrict_keyring") {
+ if (argc != 3) Usage(1);
+ std::string keyring = argv[2];
+ return RestrictKeyring(keyring);
+ } else if (action == "unlink") {
+ if (argc != 4) Usage(1);
+ key_serial_t key = parseKeyOrDie(argv[2]);
+ const std::string keyring = argv[3];
+ return Unlink(key, keyring);
+ } else if (action == "security") {
+ if (argc != 3) Usage(1);
+ const char* key_str = argv[2];
+ key_serial_t key = parseKeyOrDie(key_str);
+ std::string context = RetrieveSecurityContext(key);
+ if (context.empty()) {
+ perror(key_str);
+ return 1;
+ }
+ fprintf(stderr, "%s\n", context.c_str());
+ return 0;
+ } else {
+ fprintf(stderr, "Unrecognized action: %s\n", action.c_str());
+ Usage(1);
+ }
+
+ return 0;
+}
diff --git a/libkeyutils/mini_keyctl/mini_keyctl_utils.cpp b/libkeyutils/mini_keyctl/mini_keyctl_utils.cpp
new file mode 100644
index 0000000..fb9503f
--- /dev/null
+++ b/libkeyutils/mini_keyctl/mini_keyctl_utils.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <mini_keyctl_utils.h>
+
+#include <fstream>
+#include <iterator>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+
+namespace android {
+
+namespace {
+
+std::vector<std::string> SplitBySpace(const std::string& s) {
+ std::istringstream iss(s);
+ return std::vector<std::string>{std::istream_iterator<std::string>{iss},
+ std::istream_iterator<std::string>{}};
+}
+
+} // namespace
+
+// Find the keyring id. request_key(2) only finds keys in the process, session or thread keyring
+// hierarchy, but not internal keyring of a kernel subsystem (e.g. .fs-verity). To support all
+// cases, this function looks up a keyring's ID by parsing /proc/keys. The keyring description may
+// contain other information in the descritption section depending on the key type, only the first
+// word in the keyring description is used for searching.
+key_serial_t GetKeyringId(const std::string& keyring_desc) {
+ // If the keyring id is already a hex number, directly convert it to keyring id
+ key_serial_t keyring_id;
+ if (android::base::ParseInt(keyring_desc.c_str(), &keyring_id)) {
+ return keyring_id;
+ }
+
+ // Only keys allowed by SELinux rules will be shown here.
+ std::ifstream proc_keys_file("/proc/keys");
+ if (!proc_keys_file.is_open()) {
+ PLOG(ERROR) << "Failed to open /proc/keys";
+ return -1;
+ }
+
+ std::string line;
+ while (getline(proc_keys_file, line)) {
+ std::vector<std::string> tokens = SplitBySpace(line);
+ if (tokens.size() < 9) {
+ continue;
+ }
+ std::string key_id = "0x" + tokens[0];
+ std::string key_type = tokens[7];
+ // The key description may contain space.
+ std::string key_desc_prefix = tokens[8];
+ // The prefix has a ":" at the end
+ std::string key_desc_pattern = keyring_desc + ":";
+ if (key_type != "keyring" || key_desc_prefix != key_desc_pattern) {
+ continue;
+ }
+ if (!android::base::ParseInt(key_id.c_str(), &keyring_id)) {
+ LOG(ERROR) << "Unexpected key format in /proc/keys: " << key_id;
+ return -1;
+ }
+ return keyring_id;
+ }
+ return -1;
+}
+
+} // namespace android
diff --git a/libkeyutils/mini_keyctl/mini_keyctl_utils.h b/libkeyutils/mini_keyctl/mini_keyctl_utils.h
new file mode 100644
index 0000000..cc31d29
--- /dev/null
+++ b/libkeyutils/mini_keyctl/mini_keyctl_utils.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MINI_KEYCTL_MINI_KEYCTL_UTILS_H_
+#define _MINI_KEYCTL_MINI_KEYCTL_UTILS_H_
+
+#include <string>
+
+#include <keyutils.h>
+
+namespace android {
+key_serial_t GetKeyringId(const std::string& keyring_desc);
+} // namespace android
+
+#endif // _MINI_KEYCTL_MINI_KEYCTL_UTILS_H_
diff --git a/libkeyutils/mini_keyctl_utils.cpp b/libkeyutils/mini_keyctl_utils.cpp
deleted file mode 100644
index 79b4680..0000000
--- a/libkeyutils/mini_keyctl_utils.cpp
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <mini_keyctl_utils.h>
-
-#include <dirent.h>
-#include <errno.h>
-#include <error.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <fstream>
-#include <iostream>
-#include <iterator>
-#include <sstream>
-#include <string>
-#include <vector>
-
-#include <android-base/file.h>
-#include <android-base/parseint.h>
-#include <android-base/properties.h>
-#include <android-base/strings.h>
-#include <keyutils.h>
-
-static constexpr int kMaxCertSize = 4096;
-
-static std::vector<std::string> SplitBySpace(const std::string& s) {
- std::istringstream iss(s);
- return std::vector<std::string>{std::istream_iterator<std::string>{iss},
- std::istream_iterator<std::string>{}};
-}
-
-// Find the keyring id. Because request_key(2) syscall is not available or the key is
-// kernel keyring, the id is looked up from /proc/keys. The keyring description may contain other
-// information in the descritption section depending on the key type, only the first word in the
-// keyring description is used for searching.
-static key_serial_t GetKeyringIdOrDie(const std::string& keyring_desc) {
- // If the keyring id is already a hex number, directly convert it to keyring id
- key_serial_t keyring_id;
- if (android::base::ParseInt(keyring_desc.c_str(), &keyring_id)) {
- return keyring_id;
- }
-
- // Only keys allowed by SELinux rules will be shown here.
- std::ifstream proc_keys_file("/proc/keys");
- if (!proc_keys_file.is_open()) {
- error(1, errno, "Failed to open /proc/keys");
- return -1;
- }
-
- std::string line;
- while (getline(proc_keys_file, line)) {
- std::vector<std::string> tokens = SplitBySpace(line);
- if (tokens.size() < 9) {
- continue;
- }
- std::string key_id = "0x" + tokens[0];
- std::string key_type = tokens[7];
- // The key description may contain space.
- std::string key_desc_prefix = tokens[8];
- // The prefix has a ":" at the end
- std::string key_desc_pattern = keyring_desc + ":";
- if (key_type != "keyring" || key_desc_prefix != key_desc_pattern) {
- continue;
- }
- if (!android::base::ParseInt(key_id.c_str(), &keyring_id)) {
- error(1, 0, "Unexpected key format in /proc/keys: %s", key_id.c_str());
- return -1;
- }
- return keyring_id;
- }
- return -1;
-}
-
-int Unlink(key_serial_t key, const std::string& keyring) {
- key_serial_t keyring_id = GetKeyringIdOrDie(keyring);
- if (keyctl_unlink(key, keyring_id) < 0) {
- error(1, errno, "Failed to unlink key %x from keyring %s", key, keyring.c_str());
- return 1;
- }
- return 0;
-}
-
-int Add(const std::string& type, const std::string& desc, const std::string& data,
- const std::string& keyring) {
- if (data.size() > kMaxCertSize) {
- error(1, 0, "Certificate too large");
- return 1;
- }
-
- key_serial_t keyring_id = GetKeyringIdOrDie(keyring);
- key_serial_t key = add_key(type.c_str(), desc.c_str(), data.c_str(), data.size(), keyring_id);
-
- if (key < 0) {
- error(1, errno, "Failed to add key");
- return 1;
- }
-
- std::cout << key << std::endl;
- return 0;
-}
-
-int Padd(const std::string& type, const std::string& desc, const std::string& keyring) {
- key_serial_t keyring_id = GetKeyringIdOrDie(keyring);
-
- // read from stdin to get the certificates
- std::istreambuf_iterator<char> begin(std::cin), end;
- std::string data(begin, end);
-
- if (data.size() > kMaxCertSize) {
- error(1, 0, "Certificate too large");
- return 1;
- }
-
- key_serial_t key = add_key(type.c_str(), desc.c_str(), data.c_str(), data.size(), keyring_id);
-
- if (key < 0) {
- error(1, errno, "Failed to add key");
- return 1;
- }
-
- std::cout << key << std::endl;
- return 0;
-}
-
-int RestrictKeyring(const std::string& keyring) {
- key_serial_t keyring_id = GetKeyringIdOrDie(keyring);
- if (keyctl_restrict_keyring(keyring_id, nullptr, nullptr) < 0) {
- error(1, errno, "Cannot restrict keyring '%s'", keyring.c_str());
- return 1;
- }
- return 0;
-}
-
-std::string RetrieveSecurityContext(key_serial_t key) {
- // Simply assume this size is enough in practice.
- const int kMaxSupportedSize = 256;
- std::string context;
- context.resize(kMaxSupportedSize);
- long retval = keyctl_get_security(key, context.data(), kMaxSupportedSize);
- if (retval < 0) {
- error(1, errno, "Cannot get security context of key %x", key);
- return std::string();
- }
- if (retval > kMaxSupportedSize) {
- error(1, 0, "The key has unexpectedly long security context than %d", kMaxSupportedSize);
- return std::string();
- }
- context.resize(retval);
- return context;
-}
diff --git a/libkeyutils/mini_keyctl_utils.h b/libkeyutils/mini_keyctl_utils.h
deleted file mode 100644
index 3616831..0000000
--- a/libkeyutils/mini_keyctl_utils.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "include/keyutils.h"
-
-#include <string>
-
-// Add key to a keyring. Returns non-zero if error happens.
-int Add(const std::string& type, const std::string& desc, const std::string& data,
- const std::string& keyring);
-
-// Add key from stdin to a keyring. Returns non-zero if error happens.
-int Padd(const std::string& type, const std::string& desc, const std::string& keyring);
-
-// Removes the link from a keyring to a key if exists. Return non-zero if error happens.
-int Unlink(key_serial_t key, const std::string& keyring);
-
-// Apply key-linking to a keyring. Return non-zero if error happens.
-int RestrictKeyring(const std::string& keyring);
-
-// Retrieves a key's security context. Return the context string, or empty string on error.
-std::string RetrieveSecurityContext(key_serial_t key);
diff --git a/liblog/Android.bp b/liblog/Android.bp
index 8a63007..c40c5ef 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -15,7 +15,6 @@
//
liblog_sources = [
- "config_write.cpp",
"log_event_list.cpp",
"log_event_write.cpp",
"logger_lock.cpp",
diff --git a/liblog/README.md b/liblog/README.md
index 98bee9f..871399a 100644
--- a/liblog/README.md
+++ b/liblog/README.md
@@ -96,11 +96,6 @@
int android_log_destroy(android_log_context *ctx)
- #include <log/log_transport.h>
-
- int android_set_log_transport(int transport_flag)
- int android_get_log_transport()
-
Description
-----------
@@ -144,11 +139,6 @@
that was used when opening the sub-log. It is recommended to open the log `ANDROID_LOG_RDONLY` in
these cases.
-`android_set_log_transport()` selects transport filters. Argument is either `LOGGER_DEFAULT`,
-`LOGGER_LOGD`, or `LOGGER_NULL`. Log to logger daemon for default or logd, or drop contents on floor
-respectively. `Both android_set_log_transport()` and `android_get_log_transport()` return the
-current transport mask, or a negative errno for any problems.
-
Errors
------
diff --git a/liblog/config_write.cpp b/liblog/config_write.cpp
deleted file mode 100644
index 6ed893d..0000000
--- a/liblog/config_write.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <log/log_transport.h>
-
-#include "config_write.h"
-#include "logger.h"
-
-struct listnode __android_log_transport_write = {&__android_log_transport_write,
- &__android_log_transport_write};
-struct listnode __android_log_persist_write = {&__android_log_persist_write,
- &__android_log_persist_write};
-
-static void __android_log_add_transport(struct listnode* list,
- struct android_log_transport_write* transport) {
- uint32_t i;
-
- /* Try to keep one functioning transport for each log buffer id */
- for (i = LOG_ID_MIN; i < LOG_ID_MAX; i++) {
- struct android_log_transport_write* transp;
-
- if (list_empty(list)) {
- if (!transport->available || ((*transport->available)(static_cast<log_id_t>(i)) >= 0)) {
- list_add_tail(list, &transport->node);
- return;
- }
- } else {
- write_transport_for_each(transp, list) {
- if (!transp->available) {
- return;
- }
- if (((*transp->available)(static_cast<log_id_t>(i)) < 0) &&
- (!transport->available || ((*transport->available)(static_cast<log_id_t>(i)) >= 0))) {
- list_add_tail(list, &transport->node);
- return;
- }
- }
- }
- }
-}
-
-void __android_log_config_write() {
- if ((__android_log_transport == LOGGER_DEFAULT) || (__android_log_transport & LOGGER_LOGD)) {
-#if (FAKE_LOG_DEVICE == 0)
- extern struct android_log_transport_write logdLoggerWrite;
- extern struct android_log_transport_write pmsgLoggerWrite;
-
- __android_log_add_transport(&__android_log_transport_write, &logdLoggerWrite);
- __android_log_add_transport(&__android_log_persist_write, &pmsgLoggerWrite);
-#else
- extern struct android_log_transport_write fakeLoggerWrite;
-
- __android_log_add_transport(&__android_log_transport_write, &fakeLoggerWrite);
-#endif
- }
-}
-
-void __android_log_config_write_close() {
- struct android_log_transport_write* transport;
- struct listnode* n;
-
- write_transport_for_each_safe(transport, n, &__android_log_transport_write) {
- transport->logMask = 0;
- list_remove(&transport->node);
- }
- write_transport_for_each_safe(transport, n, &__android_log_persist_write) {
- transport->logMask = 0;
- list_remove(&transport->node);
- }
-}
diff --git a/liblog/config_write.h b/liblog/config_write.h
deleted file mode 100644
index a901f13..0000000
--- a/liblog/config_write.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <cutils/list.h>
-
-#include "log_portability.h"
-
-__BEGIN_DECLS
-
-extern struct listnode __android_log_transport_write;
-extern struct listnode __android_log_persist_write;
-
-#define write_transport_for_each(transp, transports) \
- for ((transp) = node_to_item((transports)->next, \
- struct android_log_transport_write, node); \
- ((transp) != node_to_item((transports), \
- struct android_log_transport_write, node)) && \
- ((transp) != node_to_item((transp)->node.next, \
- struct android_log_transport_write, node)); \
- (transp) = node_to_item((transp)->node.next, \
- struct android_log_transport_write, node))
-
-#define write_transport_for_each_safe(transp, n, transports) \
- for ((transp) = node_to_item((transports)->next, \
- struct android_log_transport_write, node), \
- (n) = (transp)->node.next; \
- ((transp) != node_to_item((transports), \
- struct android_log_transport_write, node)) && \
- ((transp) != \
- node_to_item((n), struct android_log_transport_write, node)); \
- (transp) = node_to_item((n), struct android_log_transport_write, node), \
- (n) = (transp)->node.next)
-
-void __android_log_config_write();
-void __android_log_config_write_close();
-
-__END_DECLS
diff --git a/liblog/fake_writer.cpp b/liblog/fake_writer.cpp
index c0b0e69..f1ddff1 100644
--- a/liblog/fake_writer.cpp
+++ b/liblog/fake_writer.cpp
@@ -20,11 +20,11 @@
#include <log/log.h>
-#include "config_write.h"
#include "fake_log_device.h"
#include "log_portability.h"
#include "logger.h"
+static int fakeAvailable(log_id_t);
static int fakeOpen();
static void fakeClose();
static int fakeWrite(log_id_t log_id, struct timespec* ts, struct iovec* vec, size_t nr);
@@ -32,15 +32,19 @@
static int logFds[(int)LOG_ID_MAX] = {-1, -1, -1, -1, -1, -1};
struct android_log_transport_write fakeLoggerWrite = {
- .node = {&fakeLoggerWrite.node, &fakeLoggerWrite.node},
- .context.priv = &logFds,
.name = "fake",
- .available = NULL,
+ .logMask = 0,
+ .context.priv = &logFds,
+ .available = fakeAvailable,
.open = fakeOpen,
.close = fakeClose,
.write = fakeWrite,
};
+static int fakeAvailable(log_id_t) {
+ return 0;
+}
+
static int fakeOpen() {
int i;
diff --git a/liblog/include/log/log_transport.h b/liblog/include/log/log_transport.h
deleted file mode 100644
index bda7c25..0000000
--- a/liblog/include/log/log_transport.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
-**
-** Copyright 2017, The Android Open Source Project
-**
-** This file is dual licensed. It may be redistributed and/or modified
-** under the terms of the Apache 2.0 License OR version 2 of the GNU
-** General Public License.
-*/
-
-#pragma once
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*
- * Logging transports, bit mask to select features. Function returns selection.
- */
-/* clang-format off */
-#define LOGGER_DEFAULT 0x00
-#define LOGGER_LOGD 0x01
-#define LOGGER_KERNEL 0x02 /* Reserved/Deprecated */
-#define LOGGER_NULL 0x04 /* Does not release resources of other selections */
-#define LOGGER_RESERVED 0x08 /* Reserved, previously for logging to local memory */
-#define LOGGER_RESERVED2 0x10 /* Reserved, previously for logs sent to stderr */
-/* clang-format on */
-
-/* Both return the selected transport flag mask, or negative errno */
-int android_set_log_transport(int transport_flag);
-int android_get_log_transport();
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/liblog/log_event_list.cpp b/liblog/log_event_list.cpp
index f148ef3..18ea930 100644
--- a/liblog/log_event_list.cpp
+++ b/liblog/log_event_list.cpp
@@ -176,13 +176,6 @@
return 0;
}
-static inline void copy4LE(uint8_t* buf, uint32_t val) {
- buf[0] = val & 0xFF;
- buf[1] = (val >> 8) & 0xFF;
- buf[2] = (val >> 16) & 0xFF;
- buf[3] = (val >> 24) & 0xFF;
-}
-
int android_log_write_int32(android_log_context ctx, int32_t value) {
size_t needed;
android_log_context_internal* context;
@@ -201,22 +194,11 @@
}
context->count[context->list_nest_depth]++;
context->storage[context->pos + 0] = EVENT_TYPE_INT;
- copy4LE(&context->storage[context->pos + 1], value);
+ *reinterpret_cast<int32_t*>(&context->storage[context->pos + 1]) = value;
context->pos += needed;
return 0;
}
-static inline void copy8LE(uint8_t* buf, uint64_t val) {
- buf[0] = val & 0xFF;
- buf[1] = (val >> 8) & 0xFF;
- buf[2] = (val >> 16) & 0xFF;
- buf[3] = (val >> 24) & 0xFF;
- buf[4] = (val >> 32) & 0xFF;
- buf[5] = (val >> 40) & 0xFF;
- buf[6] = (val >> 48) & 0xFF;
- buf[7] = (val >> 56) & 0xFF;
-}
-
int android_log_write_int64(android_log_context ctx, int64_t value) {
size_t needed;
android_log_context_internal* context;
@@ -235,7 +217,7 @@
}
context->count[context->list_nest_depth]++;
context->storage[context->pos + 0] = EVENT_TYPE_LONG;
- copy8LE(&context->storage[context->pos + 1], value);
+ *reinterpret_cast<int64_t*>(&context->storage[context->pos + 1]) = value;
context->pos += needed;
return 0;
}
@@ -267,7 +249,7 @@
}
context->count[context->list_nest_depth]++;
context->storage[context->pos + 0] = EVENT_TYPE_STRING;
- copy4LE(&context->storage[context->pos + 1], len);
+ *reinterpret_cast<ssize_t*>(&context->storage[context->pos + 1]) = len;
if (len) {
memcpy(&context->storage[context->pos + 5], value, len);
}
@@ -299,7 +281,7 @@
ivalue = *(uint32_t*)&value;
context->count[context->list_nest_depth]++;
context->storage[context->pos + 0] = EVENT_TYPE_FLOAT;
- copy4LE(&context->storage[context->pos + 1], ivalue);
+ *reinterpret_cast<uint32_t*>(&context->storage[context->pos + 1]) = ivalue;
context->pos += needed;
return 0;
}
diff --git a/liblog/logd_reader.cpp b/liblog/logd_reader.cpp
index d5bf844..916a428 100644
--- a/liblog/logd_reader.cpp
+++ b/liblog/logd_reader.cpp
@@ -23,6 +23,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/param.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
@@ -38,9 +39,6 @@
#include "logd_reader.h"
#include "logger.h"
-/* branchless on many architectures. */
-#define min(x, y) ((y) ^ (((x) ^ (y)) & -((x) < (y))))
-
static int logdAvailable(log_id_t LogId);
static int logdVersion(struct android_log_logger* logger,
struct android_log_transport_context* transp);
@@ -66,7 +64,6 @@
struct android_log_transport_context* transp, char* buf, size_t len);
struct android_log_transport_read logdLoggerRead = {
- .node = {&logdLoggerRead.node, &logdLoggerRead.node},
.name = "logd",
.available = logdAvailable,
.version = logdVersion,
@@ -279,13 +276,13 @@
size_t n;
n = snprintf(cp, remaining, "getStatistics");
- n = min(n, remaining);
+ n = MIN(n, remaining);
remaining -= n;
cp += n;
logger_for_each(logger, logger_list) {
n = snprintf(cp, remaining, " %d", logger->logId);
- n = min(n, remaining);
+ n = MIN(n, remaining);
remaining -= n;
cp += n;
}
@@ -362,7 +359,7 @@
remaining = sizeof(buffer) - (cp - buffer);
logger_for_each(logger, logger_list) {
ret = snprintf(cp, remaining, "%c%u", c, logger->logId);
- ret = min(ret, remaining);
+ ret = MIN(ret, remaining);
remaining -= ret;
cp += ret;
c = ',';
@@ -370,7 +367,7 @@
if (logger_list->tail) {
ret = snprintf(cp, remaining, " tail=%u", logger_list->tail);
- ret = min(ret, remaining);
+ ret = MIN(ret, remaining);
remaining -= ret;
cp += ret;
}
@@ -379,20 +376,20 @@
if (logger_list->mode & ANDROID_LOG_WRAP) {
// ToDo: alternate API to allow timeout to be adjusted.
ret = snprintf(cp, remaining, " timeout=%u", ANDROID_LOG_WRAP_DEFAULT_TIMEOUT);
- ret = min(ret, remaining);
+ ret = MIN(ret, remaining);
remaining -= ret;
cp += ret;
}
ret = snprintf(cp, remaining, " start=%" PRIu32 ".%09" PRIu32, logger_list->start.tv_sec,
logger_list->start.tv_nsec);
- ret = min(ret, remaining);
+ ret = MIN(ret, remaining);
remaining -= ret;
cp += ret;
}
if (logger_list->pid) {
ret = snprintf(cp, remaining, " pid=%u", logger_list->pid);
- ret = min(ret, remaining);
+ ret = MIN(ret, remaining);
cp += ret;
}
diff --git a/liblog/logd_writer.cpp b/liblog/logd_writer.cpp
index 2c64b0b..06a2baf 100644
--- a/liblog/logd_writer.cpp
+++ b/liblog/logd_writer.cpp
@@ -34,23 +34,19 @@
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
-#include "config_write.h"
#include "log_portability.h"
#include "logger.h"
#include "uio.h"
-/* branchless on many architectures. */
-#define min(x, y) ((y) ^ (((x) ^ (y)) & -((x) < (y))))
-
static int logdAvailable(log_id_t LogId);
static int logdOpen();
static void logdClose();
static int logdWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr);
struct android_log_transport_write logdLoggerWrite = {
- .node = {&logdLoggerWrite.node, &logdLoggerWrite.node},
- .context.sock = -EBADF,
.name = "logd",
+ .logMask = 0,
+ .context.sock = -EBADF,
.available = logdAvailable,
.open = logdOpen,
.close = logdClose,
diff --git a/liblog/logger.h b/liblog/logger.h
index 4b4ef5f..8cae66c 100644
--- a/liblog/logger.h
+++ b/liblog/logger.h
@@ -31,12 +31,9 @@
void* priv;
atomic_int sock;
atomic_int fd;
- struct listnode* node;
- atomic_uintptr_t atomic_pointer;
};
struct android_log_transport_write {
- struct listnode node;
const char* name; /* human name to describe the transport */
unsigned logMask; /* mask cache of available() success */
union android_log_context_union context; /* Initialized by static allocation */
@@ -54,7 +51,6 @@
struct android_log_logger;
struct android_log_transport_read {
- struct listnode node;
const char* name; /* human name to describe the transport */
/* Does not cause resources to be taken */
@@ -149,6 +145,4 @@
int __android_log_trylock();
void __android_log_unlock();
-extern int __android_log_transport;
-
__END_DECLS
diff --git a/liblog/logger_write.cpp b/liblog/logger_write.cpp
index 7fc8747..e1772f1 100644
--- a/liblog/logger_write.cpp
+++ b/liblog/logger_write.cpp
@@ -25,17 +25,28 @@
#endif
#include <log/event_tag_map.h>
-#include <log/log_transport.h>
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
-#include "config_write.h"
#include "log_portability.h"
#include "logger.h"
#include "uio.h"
#define LOG_BUF_SIZE 1024
+#if (FAKE_LOG_DEVICE == 0)
+extern struct android_log_transport_write logdLoggerWrite;
+extern struct android_log_transport_write pmsgLoggerWrite;
+
+android_log_transport_write* android_log_write = &logdLoggerWrite;
+android_log_transport_write* android_log_persist_write = &pmsgLoggerWrite;
+#else
+extern android_log_transport_write fakeLoggerWrite;
+
+android_log_transport_write* android_log_write = &fakeLoggerWrite;
+android_log_transport_write* android_log_persist_write = nullptr;
+#endif
+
static int __write_to_log_init(log_id_t, struct iovec* vec, size_t nr);
static int (*write_to_log)(log_id_t, struct iovec* vec, size_t nr) = __write_to_log_init;
@@ -89,9 +100,8 @@
}
for (i = LOG_ID_MIN; i < LOG_ID_MAX; ++i) {
- if (node->write && (i != LOG_ID_KERNEL) &&
- ((i != LOG_ID_SECURITY) || (check_log_uid_permissions() == 0)) &&
- (!node->available || ((*node->available)(static_cast<log_id_t>(i)) >= 0))) {
+ if (i != LOG_ID_KERNEL && (i != LOG_ID_SECURITY || check_log_uid_permissions() == 0) &&
+ (*node->available)(static_cast<log_id_t>(i)) >= 0) {
node->logMask |= 1 << i;
}
}
@@ -105,7 +115,6 @@
* Release any logger resources. A new log write will immediately re-acquire.
*/
void __android_log_close() {
- struct android_log_transport_write* transport;
#if defined(__ANDROID__)
EventTagMap* m;
#endif
@@ -124,20 +133,14 @@
* disengenuous use of this function.
*/
- write_transport_for_each(transport, &__android_log_persist_write) {
- if (transport->close) {
- (*transport->close)();
- }
+ if (android_log_write != nullptr) {
+ android_log_write->close();
}
- write_transport_for_each(transport, &__android_log_transport_write) {
- if (transport->close) {
- (*transport->close)();
- }
+ if (android_log_persist_write != nullptr) {
+ android_log_persist_write->close();
}
- __android_log_config_write_close();
-
#if defined(__ANDROID__)
/*
* Additional risk here somewhat mitigated by immediately unlock flushing
@@ -161,62 +164,39 @@
#endif
}
+static bool transport_initialize(android_log_transport_write* transport) {
+ if (transport == nullptr) {
+ return false;
+ }
+
+ __android_log_cache_available(transport);
+ if (!transport->logMask) {
+ return false;
+ }
+
+ // TODO: Do we actually need to call close() if open() fails?
+ if (transport->open() < 0) {
+ transport->close();
+ return false;
+ }
+
+ return true;
+}
+
/* log_init_lock assumed */
static int __write_to_log_initialize() {
- struct android_log_transport_write* transport;
- struct listnode* n;
- int i = 0, ret = 0;
-
- __android_log_config_write();
- write_transport_for_each_safe(transport, n, &__android_log_transport_write) {
- __android_log_cache_available(transport);
- if (!transport->logMask) {
- list_remove(&transport->node);
- continue;
- }
- if (!transport->open || ((*transport->open)() < 0)) {
- if (transport->close) {
- (*transport->close)();
- }
- list_remove(&transport->node);
- continue;
- }
- ++ret;
- }
- write_transport_for_each_safe(transport, n, &__android_log_persist_write) {
- __android_log_cache_available(transport);
- if (!transport->logMask) {
- list_remove(&transport->node);
- continue;
- }
- if (!transport->open || ((*transport->open)() < 0)) {
- if (transport->close) {
- (*transport->close)();
- }
- list_remove(&transport->node);
- continue;
- }
- ++i;
- }
- if (!ret && !i) {
+ if (!transport_initialize(android_log_write)) {
return -ENODEV;
}
- return ret;
+ transport_initialize(android_log_persist_write);
+
+ return 1;
}
static int __write_to_log_daemon(log_id_t log_id, struct iovec* vec, size_t nr) {
- struct android_log_transport_write* node;
int ret, save_errno;
struct timespec ts;
- size_t len, i;
-
- for (len = i = 0; i < nr; ++i) {
- len += vec[i].iov_len;
- }
- if (!len) {
- return -EINVAL;
- }
save_errno = errno;
#if defined(__ANDROID__)
@@ -283,30 +263,9 @@
return -EPERM;
}
} else {
- /* Validate the incoming tag, tag content can not split across iovec */
- char prio = ANDROID_LOG_VERBOSE;
- const char* tag = static_cast<const char*>(vec[0].iov_base);
- size_t len = vec[0].iov_len;
- if (!tag) {
- len = 0;
- }
- if (len > 0) {
- prio = *tag;
- if (len > 1) {
- --len;
- ++tag;
- } else {
- len = vec[1].iov_len;
- tag = ((const char*)vec[1].iov_base);
- if (!tag) {
- len = 0;
- }
- }
- }
- /* tag must be nul terminated */
- if (tag && strnlen(tag, len) >= len) {
- tag = NULL;
- }
+ int prio = *static_cast<int*>(vec[0].iov_base);
+ const char* tag = static_cast<const char*>(vec[1].iov_base);
+ size_t len = vec[1].iov_len;
if (!__android_log_is_loggable_len(prio, tag, len - 1, ANDROID_LOG_VERBOSE)) {
errno = save_errno;
@@ -324,21 +283,18 @@
#endif
ret = 0;
- i = 1 << log_id;
- write_transport_for_each(node, &__android_log_transport_write) {
- if (node->logMask & i) {
- ssize_t retval;
- retval = (*node->write)(log_id, &ts, vec, nr);
- if (ret >= 0) {
- ret = retval;
- }
+ size_t i = 1 << log_id;
+
+ if (android_log_write != nullptr && (android_log_write->logMask & i)) {
+ ssize_t retval;
+ retval = android_log_write->write(log_id, &ts, vec, nr);
+ if (ret >= 0) {
+ ret = retval;
}
}
- write_transport_for_each(node, &__android_log_persist_write) {
- if (node->logMask & i) {
- (void)(*node->write)(log_id, &ts, vec, nr);
- }
+ if (android_log_persist_write != nullptr && (android_log_persist_write->logMask & i)) {
+ android_log_persist_write->write(log_id, &ts, vec, nr);
}
errno = save_errno;
@@ -354,9 +310,6 @@
ret = __write_to_log_initialize();
if (ret < 0) {
__android_log_unlock();
- if (!list_empty(&__android_log_persist_write)) {
- __write_to_log_daemon(log_id, vec, nr);
- }
errno = save_errno;
return ret;
}
@@ -546,81 +499,3 @@
return write_to_log(LOG_ID_SECURITY, vec, 4);
}
-
-static int __write_to_log_null(log_id_t log_id, struct iovec* vec, size_t nr) {
- size_t len, i;
-
- if ((log_id < LOG_ID_MIN) || (log_id >= LOG_ID_MAX)) {
- return -EINVAL;
- }
-
- for (len = i = 0; i < nr; ++i) {
- len += vec[i].iov_len;
- }
- if (!len) {
- return -EINVAL;
- }
- return len;
-}
-
-/* Following functions need access to our internal write_to_log status */
-
-int __android_log_transport;
-
-int android_set_log_transport(int transport_flag) {
- int retval;
-
- if (transport_flag < 0) {
- return -EINVAL;
- }
-
- retval = LOGGER_NULL;
-
- __android_log_lock();
-
- if (transport_flag & LOGGER_NULL) {
- write_to_log = __write_to_log_null;
-
- __android_log_unlock();
-
- return retval;
- }
-
- __android_log_transport &= LOGGER_LOGD;
-
- transport_flag &= LOGGER_LOGD;
-
- if (__android_log_transport != transport_flag) {
- __android_log_transport = transport_flag;
- __android_log_config_write_close();
-
- write_to_log = __write_to_log_init;
- /* generically we only expect these two values for write_to_log */
- } else if ((write_to_log != __write_to_log_init) && (write_to_log != __write_to_log_daemon)) {
- write_to_log = __write_to_log_init;
- }
-
- retval = __android_log_transport;
-
- __android_log_unlock();
-
- return retval;
-}
-
-int android_get_log_transport() {
- int ret = LOGGER_DEFAULT;
-
- __android_log_lock();
- if (write_to_log == __write_to_log_null) {
- ret = LOGGER_NULL;
- } else {
- __android_log_transport &= LOGGER_LOGD;
- ret = __android_log_transport;
- if ((write_to_log != __write_to_log_init) && (write_to_log != __write_to_log_daemon)) {
- ret = -EINVAL;
- }
- }
- __android_log_unlock();
-
- return ret;
-}
diff --git a/liblog/pmsg_reader.cpp b/liblog/pmsg_reader.cpp
index 005fec8..2db45a1 100644
--- a/liblog/pmsg_reader.cpp
+++ b/liblog/pmsg_reader.cpp
@@ -37,7 +37,6 @@
struct android_log_transport_context* transp);
struct android_log_transport_read pmsgLoggerRead = {
- .node = {&pmsgLoggerRead.node, &pmsgLoggerRead.node},
.name = "pmsg",
.available = pmsgAvailable,
.version = pmsgVersion,
diff --git a/liblog/pmsg_writer.cpp b/liblog/pmsg_writer.cpp
index 69720ed..54980d9 100644
--- a/liblog/pmsg_writer.cpp
+++ b/liblog/pmsg_writer.cpp
@@ -29,7 +29,6 @@
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
-#include "config_write.h"
#include "log_portability.h"
#include "logger.h"
#include "uio.h"
@@ -40,9 +39,9 @@
static int pmsgWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr);
struct android_log_transport_write pmsgLoggerWrite = {
- .node = {&pmsgLoggerWrite.node, &pmsgLoggerWrite.node},
- .context.fd = -1,
.name = "pmsg",
+ .logMask = 0,
+ .context.fd = -1,
.available = pmsgAvailable,
.open = pmsgOpen,
.close = pmsgClose,
@@ -100,7 +99,7 @@
return -EINVAL;
}
- if (SNET_EVENT_LOG_TAG != *static_cast<uint8_t*>(vec[0].iov_base)) {
+ if (SNET_EVENT_LOG_TAG != *static_cast<uint32_t*>(vec[0].iov_base)) {
return -EPERM;
}
}
diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
index 7163743..4642b9b 100644
--- a/liblog/tests/liblog_benchmark.cpp
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -29,7 +29,6 @@
#include <benchmark/benchmark.h>
#include <cutils/sockets.h>
#include <log/event_tag_map.h>
-#include <log/log_transport.h>
#include <private/android_logger.h>
BENCHMARK_MAIN();
@@ -73,21 +72,6 @@
}
BENCHMARK(BM_log_maximum);
-static void set_log_null() {
- android_set_log_transport(LOGGER_NULL);
-}
-
-static void set_log_default() {
- android_set_log_transport(LOGGER_DEFAULT);
-}
-
-static void BM_log_maximum_null(benchmark::State& state) {
- set_log_null();
- BM_log_maximum(state);
- set_log_default();
-}
-BENCHMARK(BM_log_maximum_null);
-
/*
* Measure the time it takes to collect the time using
* discrete acquisition (state.PauseTiming() to state.ResumeTiming())
@@ -618,13 +602,6 @@
}
BENCHMARK(BM_log_event_overhead_42);
-static void BM_log_event_overhead_null(benchmark::State& state) {
- set_log_null();
- BM_log_event_overhead(state);
- set_log_default();
-}
-BENCHMARK(BM_log_event_overhead_null);
-
/*
* Measure the time it takes to submit the android event logging call
* using discrete acquisition under very-light load (<1% CPU utilization).
@@ -639,15 +616,6 @@
}
BENCHMARK(BM_log_light_overhead);
-static void BM_log_light_overhead_null(benchmark::State& state) {
- set_log_null();
- BM_log_light_overhead(state);
- set_log_default();
-}
-// Default gets out of hand for this test, so we set a reasonable number of
-// iterations for a timely result.
-BENCHMARK(BM_log_light_overhead_null)->Iterations(500);
-
static void caught_latency(int /*signum*/) {
unsigned long long v = 0xDEADBEEFA55A5AA5ULL;
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 34fb909..d8b0ced 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -2664,8 +2664,7 @@
print_barrier();
}
EXPECT_EQ(0, buffer_to_string);
- EXPECT_EQ(strlen(expected_string), strlen(msgBuf));
- EXPECT_EQ(0, strcmp(expected_string, msgBuf));
+ EXPECT_STREQ(expected_string, msgBuf);
}
EXPECT_EQ(1, count);
@@ -2858,7 +2857,7 @@
EXPECT_EQ(ANDROID_LOG_VERBOSE, prio);
EXPECT_FALSE(NULL == strstr(__pmsg_file, filename));
EXPECT_EQ(len, sizeof(max_payload_buf));
- EXPECT_EQ(0, strcmp(max_payload_buf, buf));
+ EXPECT_STREQ(max_payload_buf, buf);
++signaled;
if ((len != sizeof(max_payload_buf)) || strcmp(max_payload_buf, buf)) {
diff --git a/liblog/tests/log_wrap_test.cpp b/liblog/tests/log_wrap_test.cpp
index ebf0b15..c7dd8e8 100644
--- a/liblog/tests/log_wrap_test.cpp
+++ b/liblog/tests/log_wrap_test.cpp
@@ -27,12 +27,9 @@
#include <log/log_properties.h>
#include <log/log_read.h>
#include <log/log_time.h>
-#include <log/log_transport.h>
#ifdef __ANDROID__
static void read_with_wrap() {
- android_set_log_transport(LOGGER_LOGD);
-
// Read the last line in the log to get a starting timestamp. We're assuming
// the log is not empty.
const int mode = ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK;
diff --git a/libnativeloader/public_libraries.cpp b/libnativeloader/public_libraries.cpp
index 93df1d0..010e8cc 100644
--- a/libnativeloader/public_libraries.cpp
+++ b/libnativeloader/public_libraries.cpp
@@ -176,6 +176,11 @@
std::copy(vec.begin(), vec.end(), std::back_inserter(*sonames));
}
+ // If this is for preloading libs, don't remove the libs from APEXes.
+ if (for_preload) {
+ return android::base::Join(*sonames, ':');
+ }
+
// Remove the public libs in the runtime namespace.
// These libs are listed in public.android.txt, but we don't want the rest of android
// in default namespace to dlopen the libs.
diff --git a/libstats/statsd_writer.c b/libstats/statsd_writer.c
index b1c05ea..073b67f 100644
--- a/libstats/statsd_writer.c
+++ b/libstats/statsd_writer.c
@@ -36,25 +36,6 @@
#include <time.h>
#include <unistd.h>
-/* branchless on many architectures. */
-#define min(x, y) ((y) ^ (((x) ^ (y)) & -((x) < (y))))
-
-#ifndef htole32
-#if __BYTE_ORDER == __LITTLE_ENDIAN
-#define htole32(x) (x)
-#else
-#define htole32(x) __bswap_32(x)
-#endif
-#endif
-
-#ifndef htole64
-#if __BYTE_ORDER == __LITTLE_ENDIAN
-#define htole64(x) (x)
-#else
-#define htole64(x) __bswap_64(x)
-#endif
-#endif
-
static pthread_mutex_t log_init_lock = PTHREAD_MUTEX_INITIALIZER;
static atomic_int dropped = 0;
static atomic_int log_error = 0;
@@ -221,14 +202,14 @@
android_log_event_long_t buffer;
header.id = LOG_ID_STATS;
// store the last log error in the tag field. This tag field is not used by statsd.
- buffer.header.tag = htole32(atomic_load(&log_error));
+ buffer.header.tag = atomic_load(&log_error);
buffer.payload.type = EVENT_TYPE_LONG;
// format:
// |atom_tag|dropped_count|
int64_t composed_long = atomic_load(&atom_tag);
// Send 2 int32's via an int64.
composed_long = ((composed_long << 32) | ((int64_t)snapshot));
- buffer.payload.data = htole64(composed_long);
+ buffer.payload.data = composed_long;
newVec[headerLength].iov_base = &buffer;
newVec[headerLength].iov_len = sizeof(buffer);
diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp
index bdfee01..be1f092 100644
--- a/libunwindstack/ElfInterface.cpp
+++ b/libunwindstack/ElfInterface.cpp
@@ -187,8 +187,13 @@
if (!memory->ReadFully(offset, &phdr, sizeof(phdr))) {
return 0;
}
- if (phdr.p_type == PT_LOAD && phdr.p_offset == 0) {
- return phdr.p_vaddr;
+
+ // Find the first executable load when looking for the load bias.
+ if (phdr.p_type == PT_LOAD && (phdr.p_flags & PF_X)) {
+ if (phdr.p_vaddr > phdr.p_offset) {
+ return phdr.p_vaddr - phdr.p_offset;
+ }
+ break;
}
}
return 0;
diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp
index f9ee9eb..5b2036b 100644
--- a/libunwindstack/tests/ElfInterfaceTest.cpp
+++ b/libunwindstack/tests/ElfInterfaceTest.cpp
@@ -131,6 +131,12 @@
template <typename Ehdr, typename Shdr, typename Nhdr, typename ElfInterfaceType>
void BuildIDSectionTooSmallForHeader();
+ template <typename Ehdr, typename Phdr, typename ElfInterfaceType>
+ void CheckLoadBiasInFirstPhdr(uint64_t load_bias);
+
+ template <typename Ehdr, typename Phdr, typename ElfInterfaceType>
+ void CheckLoadBiasInFirstExecPhdr(uint64_t offset, uint64_t vaddr, uint64_t load_bias);
+
MemoryFake memory_;
};
@@ -1495,4 +1501,122 @@
BuildIDSectionTooSmallForHeader<Elf64_Ehdr, Elf64_Shdr, Elf64_Nhdr, ElfInterface64>();
}
+template <typename Ehdr, typename Phdr, typename ElfInterfaceType>
+void ElfInterfaceTest::CheckLoadBiasInFirstPhdr(uint64_t load_bias) {
+ Ehdr ehdr = {};
+ ehdr.e_phoff = 0x100;
+ ehdr.e_phnum = 2;
+ ehdr.e_phentsize = sizeof(Phdr);
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ Phdr phdr = {};
+ phdr.p_type = PT_LOAD;
+ phdr.p_offset = 0;
+ phdr.p_vaddr = load_bias;
+ phdr.p_memsz = 0x10000;
+ phdr.p_flags = PF_R | PF_X;
+ phdr.p_align = 0x1000;
+ memory_.SetMemory(0x100, &phdr, sizeof(phdr));
+
+ memset(&phdr, 0, sizeof(phdr));
+ phdr.p_type = PT_LOAD;
+ phdr.p_offset = 0x1000;
+ phdr.p_memsz = 0x2000;
+ phdr.p_flags = PF_R | PF_X;
+ phdr.p_align = 0x1000;
+ memory_.SetMemory(0x100 + sizeof(phdr), &phdr, sizeof(phdr));
+
+ uint64_t static_load_bias = ElfInterface::GetLoadBias<Ehdr, Phdr>(&memory_);
+ ASSERT_EQ(load_bias, static_load_bias);
+
+ std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+ uint64_t init_load_bias = 0;
+ ASSERT_TRUE(elf->Init(&init_load_bias));
+ ASSERT_EQ(init_load_bias, static_load_bias);
+}
+
+TEST_F(ElfInterfaceTest, get_load_bias_zero_32) {
+ CheckLoadBiasInFirstPhdr<Elf32_Ehdr, Elf32_Phdr, ElfInterface32>(0);
+}
+
+TEST_F(ElfInterfaceTest, get_load_bias_zero_64) {
+ CheckLoadBiasInFirstPhdr<Elf64_Ehdr, Elf64_Phdr, ElfInterface64>(0);
+}
+
+TEST_F(ElfInterfaceTest, get_load_bias_non_zero_32) {
+ CheckLoadBiasInFirstPhdr<Elf32_Ehdr, Elf32_Phdr, ElfInterface32>(0x1000);
+}
+
+TEST_F(ElfInterfaceTest, get_load_bias_non_zero_64) {
+ CheckLoadBiasInFirstPhdr<Elf64_Ehdr, Elf64_Phdr, ElfInterface64>(0x1000);
+}
+
+template <typename Ehdr, typename Phdr, typename ElfInterfaceType>
+void ElfInterfaceTest::CheckLoadBiasInFirstExecPhdr(uint64_t offset, uint64_t vaddr,
+ uint64_t load_bias) {
+ Ehdr ehdr = {};
+ ehdr.e_phoff = 0x100;
+ ehdr.e_phnum = 3;
+ ehdr.e_phentsize = sizeof(Phdr);
+ memory_.SetMemory(0, &ehdr, sizeof(ehdr));
+
+ Phdr phdr = {};
+ phdr.p_type = PT_LOAD;
+ phdr.p_memsz = 0x10000;
+ phdr.p_flags = PF_R;
+ phdr.p_align = 0x1000;
+ memory_.SetMemory(0x100, &phdr, sizeof(phdr));
+
+ memset(&phdr, 0, sizeof(phdr));
+ phdr.p_type = PT_LOAD;
+ phdr.p_offset = offset;
+ phdr.p_vaddr = vaddr;
+ phdr.p_memsz = 0x2000;
+ phdr.p_flags = PF_R | PF_X;
+ phdr.p_align = 0x1000;
+ memory_.SetMemory(0x100 + sizeof(phdr), &phdr, sizeof(phdr));
+
+ // Second executable load should be ignored for load bias computation.
+ memset(&phdr, 0, sizeof(phdr));
+ phdr.p_type = PT_LOAD;
+ phdr.p_offset = 0x1234;
+ phdr.p_vaddr = 0x2000;
+ phdr.p_memsz = 0x2000;
+ phdr.p_flags = PF_R | PF_X;
+ phdr.p_align = 0x1000;
+ memory_.SetMemory(0x200 + sizeof(phdr), &phdr, sizeof(phdr));
+
+ uint64_t static_load_bias = ElfInterface::GetLoadBias<Ehdr, Phdr>(&memory_);
+ ASSERT_EQ(load_bias, static_load_bias);
+
+ std::unique_ptr<ElfInterfaceType> elf(new ElfInterfaceType(&memory_));
+ uint64_t init_load_bias = 0;
+ ASSERT_TRUE(elf->Init(&init_load_bias));
+ ASSERT_EQ(init_load_bias, static_load_bias);
+}
+
+TEST_F(ElfInterfaceTest, get_load_bias_exec_zero_32) {
+ CheckLoadBiasInFirstExecPhdr<Elf32_Ehdr, Elf32_Phdr, ElfInterface32>(0x1000, 0x1000, 0);
+}
+
+TEST_F(ElfInterfaceTest, get_load_bias_exec_zero_64) {
+ CheckLoadBiasInFirstExecPhdr<Elf64_Ehdr, Elf64_Phdr, ElfInterface64>(0x1000, 0x1000, 0);
+}
+
+TEST_F(ElfInterfaceTest, get_load_bias_exec_non_zero_32) {
+ CheckLoadBiasInFirstExecPhdr<Elf32_Ehdr, Elf32_Phdr, ElfInterface32>(0x1000, 0x4000, 0x3000);
+}
+
+TEST_F(ElfInterfaceTest, get_load_bias_exec_non_zero_64) {
+ CheckLoadBiasInFirstExecPhdr<Elf64_Ehdr, Elf64_Phdr, ElfInterface64>(0x1000, 0x4000, 0x3000);
+}
+
+TEST_F(ElfInterfaceTest, get_load_bias_exec_zero_from_error_32) {
+ CheckLoadBiasInFirstExecPhdr<Elf32_Ehdr, Elf32_Phdr, ElfInterface32>(0x5000, 0x1000, 0);
+}
+
+TEST_F(ElfInterfaceTest, get_load_bias_exec_zero_from_error_64) {
+ CheckLoadBiasInFirstExecPhdr<Elf64_Ehdr, Elf64_Phdr, ElfInterface64>(0x5000, 0x1000, 0);
+}
+
} // namespace unwindstack
diff --git a/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp b/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
index f5ac6cb..2c98928 100644
--- a/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
+++ b/libunwindstack/tests/MapInfoGetLoadBiasTest.cpp
@@ -141,6 +141,7 @@
phdr.p_type = PT_NULL;
memory->SetMemory(offset + 0x5000, &phdr, sizeof(phdr));
phdr.p_type = PT_LOAD;
+ phdr.p_flags = PF_X;
phdr.p_offset = 0;
phdr.p_vaddr = 0xe000;
memory->SetMemory(offset + 0x5000 + sizeof(phdr), &phdr, sizeof(phdr));
diff --git a/lmkd/event.logtags b/lmkd/event.logtags
index 065c6db..452f411 100644
--- a/lmkd/event.logtags
+++ b/lmkd/event.logtags
@@ -17,8 +17,8 @@
# Multiple values are separated by commas.
#
# The data type is a number from the following values:
-# 1: int
-# 2: long
+# 1: int32_t
+# 2: int64_t
# 3: string
# 4: list
#
@@ -34,5 +34,5 @@
#
# TODO: generate ".java" and ".h" files with integer constants from this file.
-# for meminfo logs
-10195355 meminfo (MemFree|1),(Cached|1),(SwapCached|1),(Buffers|1),(Shmem|1),(Unevictable|1),(SwapTotal|1),(SwapFree|1),(ActiveAnon|1),(InactiveAnon|1),(ActiveFile|1),(InactiveFile|1),(SReclaimable|1),(SUnreclaim|1),(KernelStack|1),(PageTables|1),(ION_heap|1),(ION_heap_pool|1),(CmaFree|1)
+# for killinfo logs
+10195355 killinfo (Pid|1|5),(Uid|1|5),(OomAdj|1),(MinOomAdj|1),(TaskSize|1),(enum kill_reasons|1|5),(MemFree|1),(Cached|1),(SwapCached|1),(Buffers|1),(Shmem|1),(Unevictable|1),(SwapTotal|1),(SwapFree|1),(ActiveAnon|1),(InactiveAnon|1),(ActiveFile|1),(InactiveFile|1),(SReclaimable|1),(SUnreclaim|1),(KernelStack|1),(PageTables|1),(IonHeap|1),(IonHeapPool|1),(CmaFree|1)
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index 449088a..372e10f 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -84,7 +84,7 @@
#define PERCEPTIBLE_APP_ADJ 200
/* Android Logger event logtags (see event.logtags) */
-#define MEMINFO_LOG_TAG 10195355
+#define KILLINFO_LOG_TAG 10195355
/* gid containing AID_SYSTEM required */
#define INKERNEL_MINFREE_PATH "/sys/module/lowmemorykiller/parameters/minfree"
@@ -926,12 +926,12 @@
static void cmd_procremove(LMKD_CTRL_PACKET packet) {
struct lmk_procremove params;
+ lmkd_pack_get_procremove(packet, ¶ms);
if (use_inkernel_interface) {
stats_remove_taskname(params.pid, kpoll_info.poll_fd);
return;
}
- lmkd_pack_get_procremove(packet, ¶ms);
/*
* WARNING: After pid_remove() procp is freed and can't be used!
* Therefore placed at the end of the function.
@@ -1394,7 +1394,7 @@
char *buf;
char *save_ptr;
char *line;
- char zone_name[LINE_MAX];
+ char zone_name[LINE_MAX + 1];
struct zoneinfo_node *node = NULL;
int node_idx = 0;
int zone_idx = 0;
@@ -1566,7 +1566,17 @@
return 0;
}
-static void meminfo_log(union meminfo *mi) {
+static void killinfo_log(struct proc* procp, int min_oom_score, int tasksize,
+ int kill_reason, union meminfo *mi) {
+ /* log process information */
+ android_log_write_int32(ctx, procp->pid);
+ android_log_write_int32(ctx, procp->uid);
+ android_log_write_int32(ctx, procp->oomadj);
+ android_log_write_int32(ctx, min_oom_score);
+ android_log_write_int32(ctx, (int32_t)min(tasksize * page_k, INT32_MAX));
+ android_log_write_int32(ctx, kill_reason);
+
+ /* log meminfo fields */
for (int field_idx = 0; field_idx < MI_FIELD_COUNT; field_idx++) {
android_log_write_int32(ctx, (int32_t)min(mi->arr[field_idx] * page_k, INT32_MAX));
}
@@ -1640,7 +1650,8 @@
static int last_killed_pid = -1;
/* Kill one process specified by procp. Returns the size of the process killed */
-static int kill_one_process(struct proc* procp, int min_oom_score, const char *reason) {
+static int kill_one_process(struct proc* procp, int min_oom_score, int kill_reason,
+ const char *kill_desc, union meminfo *mi) {
int pid = procp->pid;
uid_t uid = procp->uid;
int tgid;
@@ -1684,9 +1695,12 @@
set_process_group_and_prio(pid, SP_FOREGROUND, ANDROID_PRIORITY_HIGHEST);
inc_killcnt(procp->oomadj);
- if (reason) {
+
+ killinfo_log(procp, min_oom_score, tasksize, kill_reason, mi);
+
+ if (kill_desc) {
ALOGI("Kill '%s' (%d), uid %d, oom_adj %d to free %ldkB; reason: %s", taskname, pid,
- uid, procp->oomadj, tasksize * page_k, reason);
+ uid, procp->oomadj, tasksize * page_k, kill_desc);
} else {
ALOGI("Kill '%s' (%d), uid %d, oom_adj %d to free %ldkB", taskname, pid,
uid, procp->oomadj, tasksize * page_k);
@@ -1712,7 +1726,8 @@
* Find one process to kill at or above the given oom_adj level.
* Returns size of the killed process.
*/
-static int find_and_kill_process(int min_score_adj, const char *reason) {
+static int find_and_kill_process(int min_score_adj, int kill_reason, const char *kill_desc,
+ union meminfo *mi) {
int i;
int killed_size = 0;
bool lmk_state_change_start = false;
@@ -1727,7 +1742,7 @@
if (!procp)
break;
- killed_size = kill_one_process(procp, min_score_adj, reason);
+ killed_size = kill_one_process(procp, min_score_adj, kill_reason, kill_desc, mi);
if (killed_size >= 0) {
if (!lmk_state_change_start) {
lmk_state_change_start = true;
@@ -2051,7 +2066,7 @@
/* Kill a process if necessary */
if (kill_reason != NONE) {
- int pages_freed = find_and_kill_process(min_score_adj, kill_desc);
+ int pages_freed = find_and_kill_process(min_score_adj, kill_reason, kill_desc, &mi);
if (pages_freed > 0) {
killing = true;
if (cut_thrashing_limit) {
@@ -2062,7 +2077,6 @@
thrashing_limit = (thrashing_limit * (100 - thrashing_limit_decay_pct)) / 100;
}
}
- meminfo_log(&mi);
}
no_kill:
@@ -2251,12 +2265,10 @@
do_kill:
if (low_ram_device) {
/* For Go devices kill only one task */
- if (find_and_kill_process(level_oomadj[level], NULL) == 0) {
+ if (find_and_kill_process(level_oomadj[level], -1, NULL, &mi) == 0) {
if (debug_process_killing) {
ALOGI("Nothing to kill");
}
- } else {
- meminfo_log(&mi);
}
} else {
int pages_freed;
@@ -2276,7 +2288,7 @@
min_score_adj = level_oomadj[level];
}
- pages_freed = find_and_kill_process(min_score_adj, NULL);
+ pages_freed = find_and_kill_process(min_score_adj, -1, NULL, &mi);
if (pages_freed == 0) {
/* Rate limit kill reports when nothing was reclaimed */
@@ -2289,9 +2301,7 @@
last_kill_tm = curr_tm;
}
- /* Log meminfo whenever we kill or when report rate limit allows */
- meminfo_log(&mi);
-
+ /* Log whenever we kill or when report rate limit allows */
if (use_minfree_levels) {
ALOGI("Reclaimed %ldkB, cache(%ldkB) and "
"free(%" PRId64 "kB)-reserved(%" PRId64 "kB) below min(%ldkB) for oom_adj %d",
@@ -2714,7 +2724,7 @@
thrashing_limit_decay_pct = clamp(0, 100, property_get_int32("ro.lmk.thrashing_limit_decay",
low_ram_device ? DEF_THRASHING_DECAY_LOWRAM : DEF_THRASHING_DECAY));
- ctx = create_android_logger(MEMINFO_LOG_TAG);
+ ctx = create_android_logger(KILLINFO_LOG_TAG);
statslog_init();
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index a0059db..c8f0a8b 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -58,15 +58,6 @@
endif
#######################################
-# fsverity_init
-
-include $(CLEAR_VARS)
-LOCAL_MODULE:= fsverity_init
-LOCAL_MODULE_CLASS := EXECUTABLES
-LOCAL_SRC_FILES := fsverity_init.sh
-include $(BUILD_PREBUILT)
-
-#######################################
# init.environ.rc
include $(CLEAR_VARS)
diff --git a/rootdir/fsverity_init.sh b/rootdir/fsverity_init.sh
deleted file mode 100644
index 4fee15f..0000000
--- a/rootdir/fsverity_init.sh
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/system/bin/sh
-#
-# Copyright (C) 2019 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-# Enforce fsverity signature checking
-echo 1 > /proc/sys/fs/verity/require_signatures
-
-# Load all keys
-for cert in /product/etc/security/fsverity/*.der; do
- /system/bin/mini-keyctl padd asymmetric fsv_product .fs-verity < "$cert" ||
- log -p e -t fsverity_init "Failed to load $cert"
-done
-
-DEBUGGABLE=$(getprop ro.debuggable)
-if [ $DEBUGGABLE != "1" ]; then
- # Prevent future key links to .fs-verity keyring
- /system/bin/mini-keyctl restrict_keyring .fs-verity ||
- log -p e -t fsverity_init "Failed to restrict .fs-verity keyring"
-fi
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 9183901..bad0642 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -489,10 +489,6 @@
mkdir /data/bootchart 0755 shell shell
bootchart start
- # Load fsverity keys. This needs to happen before apexd, as post-install of
- # APEXes may rely on keys.
- exec -- /system/bin/fsverity_init
-
# Make sure that apexd is started in the default namespace
enter_default_mount_ns
@@ -823,6 +819,9 @@
class_start core
+ # Requires keystore (currently a core service) to be ready first.
+ exec -- /system/bin/fsverity_init
+
on nonencrypted
class_start main
class_start late_start