[adb] Use incremental installation by default

This CL turns on the incremental installation for all
"adb install ..." commands where no explicit mode has been set.
To disable this, set the ADB_INSTALL_DEFAULT_INCREMENTAL
environment variable to 0/n/no/false. Unset to enable back

+ improve the install command argument parsing a bit: allow
  --wait for all installation modes, --incr is enough for
  an incremental install (and --no-incr to disable it)

Bug: 150183149
Test: adb install with different apks and command line switches
Change-Id: I1a237f34b70d920146746ab16104e28ef555a5fd
Merged-In: I1a237f34b70d920146746ab16104e28ef555a5fd
This commit is contained in:
Yurii Zubrytskyi 2020-03-26 18:20:39 -07:00
parent d456db6d50
commit bc445fbab8
3 changed files with 209 additions and 77 deletions

View file

@ -22,11 +22,12 @@
#include <stdlib.h>
#include <unistd.h>
#include <algorithm>
#include <iostream>
#include <string>
#include <string_view>
#include <vector>
#include <android-base/file.h>
#include <android-base/parsebool.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
@ -39,8 +40,9 @@
#include "fastdeploy.h"
#include "incremental.h"
using namespace std::literals;
static constexpr int kFastDeployMinApi = 24;
static constexpr int kIncrementalMinApi = 29;
namespace {
@ -50,6 +52,8 @@ enum InstallMode {
INSTALL_STREAM,
INSTALL_INCREMENTAL,
};
enum class CmdlineOption { None, Enable, Disable };
}
static bool can_use_feature(const char* feature) {
@ -299,28 +303,23 @@ static int msBetween(TimePoint start, TimePoint end) {
return std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
}
static int install_app_incremental(int argc, const char** argv) {
printf("Performing Incremental Install\n");
static int install_app_incremental(int argc, const char** argv, bool wait, bool silent) {
using clock = std::chrono::high_resolution_clock;
const auto start = clock::now();
int first_apk = -1;
int last_apk = -1;
std::string cert_path;
bool wait = false;
std::vector<std::string_view> args = {"package"};
std::vector<std::string_view> args = {"package"sv};
for (int i = 0; i < argc; ++i) {
const auto arg = std::string_view(argv[i]);
if (android::base::EndsWithIgnoreCase(arg, ".apk")) {
if (android::base::EndsWithIgnoreCase(arg, ".apk"sv)) {
last_apk = i;
if (first_apk == -1) {
first_apk = i;
}
} else if (arg == "--wait") {
wait = true;
} else if (arg.starts_with("install-")) {
} else if (arg.starts_with("install-"sv)) {
// incremental installation command on the device is the same for all its variations in
// the adb, e.g. install-multiple or install-multi-package
args.push_back("install");
args.push_back("install"sv);
} else {
args.push_back(arg);
}
@ -328,16 +327,23 @@ static int install_app_incremental(int argc, const char** argv) {
if (first_apk == -1) error_exit("Need at least one APK file on command line");
const auto afterApk = clock::now();
auto files = incremental::Files{argv + first_apk, argv + last_apk + 1};
if (silent) {
// For a silent installation we want to do the lightweight check first and bail early and
// quietly if it fails.
if (!incremental::can_install(files)) {
return -1;
}
}
auto server_process = incremental::install({argv + first_apk, argv + last_apk + 1});
printf("Performing Incremental Install\n");
auto server_process = incremental::install(files, silent);
if (!server_process) {
return -1;
}
const auto end = clock::now();
printf("Install command complete (ms: %d total, %d apk prep, %d install)\n",
msBetween(start, end), msBetween(start, afterApk), msBetween(afterApk, end));
printf("Install command complete in %d ms\n", msBetween(start, end));
if (wait) {
(*server_process).wait();
@ -346,66 +352,134 @@ static int install_app_incremental(int argc, const char** argv) {
return 0;
}
static std::pair<InstallMode, std::optional<InstallMode>> calculateInstallMode(
InstallMode modeFromArgs, bool fastdeploy, CmdlineOption incrementalRequest) {
if (incrementalRequest == CmdlineOption::Enable) {
if (fastdeploy) {
error_exit(
"--incremental and --fast-deploy options are incompatible. "
"Please choose one");
}
}
if (modeFromArgs != INSTALL_DEFAULT) {
if (incrementalRequest == CmdlineOption::Enable) {
error_exit("--incremental is not compatible with other installation modes");
}
return {modeFromArgs, std::nullopt};
}
if (incrementalRequest != CmdlineOption::Disable && !is_abb_exec_supported()) {
if (incrementalRequest == CmdlineOption::None) {
incrementalRequest = CmdlineOption::Disable;
} else {
error_exit("Device doesn't support incremental installations");
}
}
if (incrementalRequest == CmdlineOption::None) {
// check if the host is ok with incremental by default
if (const char* incrementalFromEnv = getenv("ADB_INSTALL_DEFAULT_INCREMENTAL")) {
using namespace android::base;
auto val = ParseBool(incrementalFromEnv);
if (val == ParseBoolResult::kFalse) {
incrementalRequest = CmdlineOption::Disable;
}
}
}
if (incrementalRequest == CmdlineOption::None) {
// still ok: let's see if the device allows using incremental by default
// it starts feeling like we're looking for an excuse to not to use incremental...
std::string error;
std::vector<std::string> args = {"settings", "get",
"enable_adb_incremental_install_default"};
auto fd = send_abb_exec_command(args, &error);
if (!fd.ok()) {
fprintf(stderr, "adb: retrieving the default device installation mode failed: %s",
error.c_str());
} else {
char buf[BUFSIZ] = {};
read_status_line(fd.get(), buf, sizeof(buf));
using namespace android::base;
auto val = ParseBool(buf);
if (val == ParseBoolResult::kFalse) {
incrementalRequest = CmdlineOption::Disable;
}
}
}
if (incrementalRequest == CmdlineOption::Enable) {
// explicitly requested - no fallback
return {INSTALL_INCREMENTAL, std::nullopt};
}
const auto bestMode = best_install_mode();
if (incrementalRequest == CmdlineOption::None) {
// no opinion - use incremental, fallback to regular on a failure.
return {INSTALL_INCREMENTAL, bestMode};
}
// incremental turned off - use the regular best mode without a fallback.
return {bestMode, std::nullopt};
}
int install_app(int argc, const char** argv) {
std::vector<int> processedArgIndices;
InstallMode installMode = INSTALL_DEFAULT;
bool use_fastdeploy = false;
bool is_reinstall = false;
bool wait = false;
auto incremental_request = CmdlineOption::None;
FastDeploy_AgentUpdateStrategy agent_update_strategy = FastDeploy_AgentUpdateDifferentVersion;
for (int i = 1; i < argc; i++) {
if (!strcmp(argv[i], "--streaming")) {
if (argv[i] == "--streaming"sv) {
processedArgIndices.push_back(i);
installMode = INSTALL_STREAM;
} else if (!strcmp(argv[i], "--no-streaming")) {
} else if (argv[i] == "--no-streaming"sv) {
processedArgIndices.push_back(i);
installMode = INSTALL_PUSH;
} else if (!strcmp(argv[i], "-r")) {
} else if (argv[i] == "-r"sv) {
// Note that this argument is not added to processedArgIndices because it
// must be passed through to pm
is_reinstall = true;
} else if (!strcmp(argv[i], "--fastdeploy")) {
} else if (argv[i] == "--fastdeploy"sv) {
processedArgIndices.push_back(i);
use_fastdeploy = true;
} else if (!strcmp(argv[i], "--no-fastdeploy")) {
} else if (argv[i] == "--no-fastdeploy"sv) {
processedArgIndices.push_back(i);
use_fastdeploy = false;
} else if (!strcmp(argv[i], "--force-agent")) {
} else if (argv[i] == "--force-agent"sv) {
processedArgIndices.push_back(i);
agent_update_strategy = FastDeploy_AgentUpdateAlways;
} else if (!strcmp(argv[i], "--date-check-agent")) {
} else if (argv[i] == "--date-check-agent"sv) {
processedArgIndices.push_back(i);
agent_update_strategy = FastDeploy_AgentUpdateNewerTimeStamp;
} else if (!strcmp(argv[i], "--version-check-agent")) {
} else if (argv[i] == "--version-check-agent"sv) {
processedArgIndices.push_back(i);
agent_update_strategy = FastDeploy_AgentUpdateDifferentVersion;
} else if (!strcmp(argv[i], "--incremental")) {
} else if (strlen(argv[i]) >= "--incr"sv.size() && "--incremental"sv.starts_with(argv[i])) {
processedArgIndices.push_back(i);
installMode = INSTALL_INCREMENTAL;
} else if (!strcmp(argv[i], "--no-incremental")) {
incremental_request = CmdlineOption::Enable;
} else if (strlen(argv[i]) >= "--no-incr"sv.size() &&
"--no-incremental"sv.starts_with(argv[i])) {
processedArgIndices.push_back(i);
installMode = INSTALL_DEFAULT;
incremental_request = CmdlineOption::Disable;
} else if (argv[i] == "--wait"sv) {
processedArgIndices.push_back(i);
wait = true;
}
}
if (installMode == INSTALL_INCREMENTAL) {
if (get_device_api_level() < kIncrementalMinApi || !is_abb_exec_supported()) {
error_exit("Attempting to use incremental install on unsupported device");
}
}
if (installMode == INSTALL_DEFAULT) {
installMode = best_install_mode();
}
if (installMode == INSTALL_STREAM && best_install_mode() == INSTALL_PUSH) {
auto [primaryMode, fallbackMode] =
calculateInstallMode(installMode, use_fastdeploy, incremental_request);
if ((primaryMode == INSTALL_STREAM || fallbackMode.value_or(INSTALL_PUSH) == INSTALL_STREAM) &&
best_install_mode() == INSTALL_PUSH) {
error_exit("Attempting to use streaming install on unsupported device");
}
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);
fprintf(stderr,
"Fast Deploy is only compatible with devices of API version %d or higher, "
"ignoring.\n",
kFastDeployMinApi);
use_fastdeploy = false;
}
fastdeploy_set_agent_update_strategy(agent_update_strategy);
@ -421,19 +495,27 @@ int install_app(int argc, const char** argv) {
error_exit("install requires an apk argument");
}
switch (installMode) {
case INSTALL_PUSH:
return install_app_legacy(passthrough_argv.size(), passthrough_argv.data(),
use_fastdeploy);
case INSTALL_STREAM:
return install_app_streamed(passthrough_argv.size(), passthrough_argv.data(),
use_fastdeploy);
case INSTALL_INCREMENTAL:
return install_app_incremental(passthrough_argv.size(), passthrough_argv.data());
case INSTALL_DEFAULT:
default:
return 1;
auto runInstallMode = [&](InstallMode installMode, bool silent) {
switch (installMode) {
case INSTALL_PUSH:
return install_app_legacy(passthrough_argv.size(), passthrough_argv.data(),
use_fastdeploy);
case INSTALL_STREAM:
return install_app_streamed(passthrough_argv.size(), passthrough_argv.data(),
use_fastdeploy);
case INSTALL_INCREMENTAL:
return install_app_incremental(passthrough_argv.size(), passthrough_argv.data(),
wait, silent);
case INSTALL_DEFAULT:
default:
return 1;
}
};
auto res = runInstallMode(primaryMode, fallbackMode.has_value());
if (res && fallbackMode.value_or(primaryMode) != primaryMode) {
res = runInstallMode(*fallbackMode, false);
}
return res;
}
int install_multiple_app(int argc, const char** argv) {

View file

@ -90,38 +90,58 @@ static inline Size verity_tree_size_for_file(Size fileSize) {
return total_tree_block_count * INCFS_DATA_FILE_BLOCK_SIZE;
}
// Base64-encode signature bytes. Keeping fd at the position of start of verity tree.
static std::pair<unique_fd, std::string> read_and_encode_signature(Size file_size,
std::string signature_file) {
// Read, verify and return the signature bytes. Keeping fd at the position of start of verity tree.
static std::pair<unique_fd, std::vector<char>> read_signature(Size file_size,
std::string signature_file,
bool silent) {
signature_file += IDSIG;
struct stat st;
if (stat(signature_file.c_str(), &st)) {
fprintf(stderr, "Failed to stat signature file %s. Abort.\n", signature_file.c_str());
if (!silent) {
fprintf(stderr, "Failed to stat signature file %s. Abort.\n", signature_file.c_str());
}
return {};
}
unique_fd fd(adb_open(signature_file.c_str(), O_RDONLY | O_CLOEXEC));
if (fd < 0) {
fprintf(stderr, "Failed to open signature file: %s. Abort.\n", signature_file.c_str());
if (!silent) {
fprintf(stderr, "Failed to open signature file: %s. Abort.\n", signature_file.c_str());
}
return {};
}
auto [signature, tree_size] = read_id_sig_headers(fd);
if (auto expected = verity_tree_size_for_file(file_size); tree_size != expected) {
fprintf(stderr,
"Verity tree size mismatch in signature file: %s [was %lld, expected %lld].\n",
signature_file.c_str(), (long long)tree_size, (long long)expected);
if (!silent) {
fprintf(stderr,
"Verity tree size mismatch in signature file: %s [was %lld, expected %lld].\n",
signature_file.c_str(), (long long)tree_size, (long long)expected);
}
return {};
}
return {std::move(fd), std::move(signature)};
}
// Base64-encode signature bytes. Keeping fd at the position of start of verity tree.
static std::pair<unique_fd, std::string> read_and_encode_signature(Size file_size,
std::string signature_file,
bool silent) {
auto [fd, signature] = read_signature(file_size, std::move(signature_file), silent);
if (!fd.ok()) {
return {};
}
size_t base64_len = 0;
if (!EVP_EncodedLength(&base64_len, signature.size())) {
fprintf(stderr, "Fail to estimate base64 encoded length. Abort.\n");
if (!silent) {
fprintf(stderr, "Fail to estimate base64 encoded length. Abort.\n");
}
return {};
}
std::string encoded_signature;
encoded_signature.resize(base64_len);
std::string encoded_signature(base64_len, '\0');
encoded_signature.resize(EVP_EncodeBlock((uint8_t*)encoded_signature.data(),
(const uint8_t*)signature.data(), signature.size()));
@ -130,7 +150,7 @@ static std::pair<unique_fd, std::string> read_and_encode_signature(Size file_siz
// Send install-incremental to the device along with properly configured file descriptors in
// streaming format. Once connection established, send all fs-verity tree bytes.
static unique_fd start_install(const std::vector<std::string>& files) {
static unique_fd start_install(const Files& files, bool silent) {
std::vector<std::string> command_args{"package", "install-incremental"};
// fd's with positions at the beginning of fs-verity
@ -141,11 +161,13 @@ static unique_fd start_install(const std::vector<std::string>& files) {
struct stat st;
if (stat(file.c_str(), &st)) {
fprintf(stderr, "Failed to stat input file %s. Abort.\n", file.c_str());
if (!silent) {
fprintf(stderr, "Failed to stat input file %s. Abort.\n", file.c_str());
}
return {};
}
auto [signature_fd, signature] = read_and_encode_signature(st.st_size, file);
auto [signature_fd, signature] = read_and_encode_signature(st.st_size, file, silent);
if (!signature_fd.ok()) {
return {};
}
@ -161,15 +183,19 @@ static unique_fd start_install(const std::vector<std::string>& files) {
std::string error;
auto connection_fd = unique_fd(send_abb_exec_command(command_args, &error));
if (connection_fd < 0) {
fprintf(stderr, "Failed to run: %s, error: %s\n",
android::base::Join(command_args, " ").c_str(), error.c_str());
if (!silent) {
fprintf(stderr, "Failed to run: %s, error: %s\n",
android::base::Join(command_args, " ").c_str(), error.c_str());
}
return {};
}
// Pushing verity trees for all installation files.
for (auto&& local_fd : signature_fds) {
if (!copy_to_file(local_fd.get(), connection_fd.get())) {
fprintf(stderr, "Failed to stream tree bytes: %s. Abort.\n", strerror(errno));
if (!silent) {
fprintf(stderr, "Failed to stream tree bytes: %s. Abort.\n", strerror(errno));
}
return {};
}
}
@ -179,10 +205,27 @@ static unique_fd start_install(const std::vector<std::string>& files) {
} // namespace
std::optional<Process> install(std::vector<std::string> files) {
auto connection_fd = start_install(files);
bool can_install(const Files& files) {
for (const auto& file : files) {
struct stat st;
if (stat(file.c_str(), &st)) {
return false;
}
auto [fd, _] = read_signature(st.st_size, file, true);
if (!fd.ok()) {
return false;
}
}
return true;
}
std::optional<Process> install(const Files& files, bool silent) {
auto connection_fd = start_install(files, silent);
if (connection_fd < 0) {
fprintf(stderr, "adb: failed to initiate installation on device.\n");
if (!silent) {
fprintf(stderr, "adb: failed to initiate installation on device.\n");
}
return {};
}
@ -198,7 +241,9 @@ std::optional<Process> install(std::vector<std::string> files) {
// pipe for child process to write output
int print_fds[2];
if (adb_socketpair(print_fds) != 0) {
fprintf(stderr, "Failed to create socket pair for child to print to parent\n");
if (!silent) {
fprintf(stderr, "Failed to create socket pair for child to print to parent\n");
}
return {};
}
auto [pipe_read_fd, pipe_write_fd] = print_fds;
@ -210,7 +255,9 @@ std::optional<Process> install(std::vector<std::string> files) {
auto child =
adb_launch_process(adb_path, std::move(args), {connection_fd.get(), pipe_write_fd});
if (!child) {
fprintf(stderr, "adb: failed to fork: %s\n", strerror(errno));
if (!silent) {
fprintf(stderr, "adb: failed to fork: %s\n", strerror(errno));
}
return {};
}

View file

@ -25,7 +25,10 @@
namespace incremental {
std::optional<Process> install(std::vector<std::string> files);
using Files = std::vector<std::string>;
bool can_install(const Files& files);
std::optional<Process> install(const Files& files, bool silent);
enum class Result { Success, Failure, None };
Result wait_for_installation(int read_fd);