Merge "remove legacy adb data loader" into rvc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
eaf621c27c
@@ -1,22 +0,0 @@
|
|||||||
// Copyright 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.
|
|
||||||
|
|
||||||
android_app {
|
|
||||||
name: "NativeAdbDataLoaderService",
|
|
||||||
srcs: ["src/**/*.java"],
|
|
||||||
jni_libs: [ "libnativeadbdataloaderservice_jni"],
|
|
||||||
privileged: true,
|
|
||||||
certificate: "platform",
|
|
||||||
platform_apis: true,
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<!--
|
|
||||||
/*
|
|
||||||
** Copyright 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"
|
|
||||||
coreApp="true"
|
|
||||||
package="com.android.incremental.nativeadb"
|
|
||||||
android:sharedUserId="android.uid.system">
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
|
||||||
|
|
||||||
<application android:label="@string/app_name"
|
|
||||||
android:directBootAware="true">
|
|
||||||
|
|
||||||
<service android:enabled="true"
|
|
||||||
android:name="com.android.incremental.nativeadb.NativeAdbDataLoaderService"
|
|
||||||
android:label="@string/app_name"
|
|
||||||
android:exported="false">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.LOAD_DATA" />
|
|
||||||
</intent-filter>
|
|
||||||
</service>
|
|
||||||
</application>
|
|
||||||
|
|
||||||
</manifest>
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
// Copyright 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.
|
|
||||||
|
|
||||||
cc_library_shared {
|
|
||||||
name: "libnativeadbdataloaderservice_jni",
|
|
||||||
cpp_std: "c++2a",
|
|
||||||
cflags: [
|
|
||||||
"-Wall",
|
|
||||||
"-Werror",
|
|
||||||
"-Wunused",
|
|
||||||
"-Wunreachable-code",
|
|
||||||
"-Wno-unused-parameter",
|
|
||||||
],
|
|
||||||
|
|
||||||
srcs: ["com_android_incremental_nativeadb_DataLoaderService.cpp"],
|
|
||||||
|
|
||||||
shared_libs: [
|
|
||||||
"libbase",
|
|
||||||
"libcutils",
|
|
||||||
"libincfs",
|
|
||||||
"libdataloader",
|
|
||||||
"liblog",
|
|
||||||
"libnativehelper",
|
|
||||||
"libutils",
|
|
||||||
],
|
|
||||||
}
|
|
||||||
@@ -1,525 +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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define ATRACE_TAG ATRACE_TAG_ADB
|
|
||||||
#define LOG_TAG "NativeAdbDataLoaderService"
|
|
||||||
|
|
||||||
#include <android-base/file.h>
|
|
||||||
#include <android-base/logging.h>
|
|
||||||
#include <android-base/properties.h>
|
|
||||||
#include <android-base/stringprintf.h>
|
|
||||||
#include <android-base/thread_annotations.h>
|
|
||||||
#include <android-base/unique_fd.h>
|
|
||||||
#include <cutils/trace.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <sys/eventfd.h>
|
|
||||||
#include <sys/poll.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <utils/Log.h>
|
|
||||||
|
|
||||||
#include <charconv>
|
|
||||||
#include <span>
|
|
||||||
#include <string>
|
|
||||||
#include <thread>
|
|
||||||
#include <type_traits>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <unordered_set>
|
|
||||||
|
|
||||||
#include "dataloader.h"
|
|
||||||
|
|
||||||
#ifndef _WIN32
|
|
||||||
#include <endian.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#else
|
|
||||||
#define be32toh(x) _byteswap_ulong(x)
|
|
||||||
#define be16toh(x) _byteswap_ushort(x)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
using android::base::unique_fd;
|
|
||||||
|
|
||||||
using namespace std::literals;
|
|
||||||
|
|
||||||
using BlockSize = int16_t;
|
|
||||||
using FileId = int16_t;
|
|
||||||
using BlockIdx = int32_t;
|
|
||||||
using NumBlocks = int32_t;
|
|
||||||
using CompressionType = int16_t;
|
|
||||||
using RequestType = int16_t;
|
|
||||||
|
|
||||||
static constexpr int COMMAND_SIZE = 2 + 2 + 4; // bytes
|
|
||||||
static constexpr int HEADER_SIZE = 2 + 2 + 4 + 2; // bytes
|
|
||||||
static constexpr std::string_view OKAY = "OKAY"sv;
|
|
||||||
|
|
||||||
static constexpr auto PollTimeoutMs = 5000;
|
|
||||||
|
|
||||||
static constexpr auto ReadLogBufferSize = 128 * 1024 * 1024;
|
|
||||||
static constexpr auto ReadLogMaxEntrySize = 128;
|
|
||||||
|
|
||||||
struct BlockHeader {
|
|
||||||
FileId fileId = -1;
|
|
||||||
CompressionType compressionType = -1;
|
|
||||||
BlockIdx blockIdx = -1;
|
|
||||||
BlockSize blockSize = -1;
|
|
||||||
} __attribute__((packed));
|
|
||||||
|
|
||||||
static_assert(sizeof(BlockHeader) == HEADER_SIZE);
|
|
||||||
|
|
||||||
static constexpr RequestType EXIT = 0;
|
|
||||||
static constexpr RequestType BLOCK_MISSING = 1;
|
|
||||||
static constexpr RequestType PREFETCH = 2;
|
|
||||||
|
|
||||||
struct RequestCommand {
|
|
||||||
RequestType requestType;
|
|
||||||
FileId fileId;
|
|
||||||
BlockIdx blockIdx;
|
|
||||||
} __attribute__((packed));
|
|
||||||
|
|
||||||
static_assert(COMMAND_SIZE == sizeof(RequestCommand));
|
|
||||||
|
|
||||||
static bool sendRequest(int fd, RequestType requestType, FileId fileId = -1,
|
|
||||||
BlockIdx blockIdx = -1) {
|
|
||||||
const RequestCommand command{.requestType = static_cast<int16_t>(be16toh(requestType)),
|
|
||||||
.fileId = static_cast<int16_t>(be16toh(fileId)),
|
|
||||||
.blockIdx = static_cast<int32_t>(be32toh(blockIdx))};
|
|
||||||
return android::base::WriteFully(fd, &command, sizeof(command));
|
|
||||||
}
|
|
||||||
|
|
||||||
static int waitForDataOrSignal(int fd, int event_fd) {
|
|
||||||
struct pollfd pfds[2] = {{fd, POLLIN, 0}, {event_fd, POLLIN, 0}};
|
|
||||||
// Wait indefinitely until either data is ready or stop signal is received
|
|
||||||
int res = poll(pfds, 2, PollTimeoutMs);
|
|
||||||
if (res <= 0) {
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
// First check if there is a stop signal
|
|
||||||
if (pfds[1].revents == POLLIN) {
|
|
||||||
return event_fd;
|
|
||||||
}
|
|
||||||
// Otherwise check if incoming data is ready
|
|
||||||
if (pfds[0].revents == POLLIN) {
|
|
||||||
return fd;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool readChunk(int fd, std::vector<uint8_t>& data) {
|
|
||||||
int32_t size;
|
|
||||||
if (!android::base::ReadFully(fd, &size, sizeof(size))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
size = int32_t(be32toh(size));
|
|
||||||
if (size <= 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
data.resize(size);
|
|
||||||
return android::base::ReadFully(fd, data.data(), data.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
static BlockHeader readHeader(std::span<uint8_t>& data) {
|
|
||||||
BlockHeader header;
|
|
||||||
if (data.size() < sizeof(header)) {
|
|
||||||
return header;
|
|
||||||
}
|
|
||||||
|
|
||||||
header.fileId = static_cast<FileId>(be16toh(*reinterpret_cast<uint16_t*>(&data[0])));
|
|
||||||
header.compressionType =
|
|
||||||
static_cast<CompressionType>(be16toh(*reinterpret_cast<uint16_t*>(&data[2])));
|
|
||||||
header.blockIdx = static_cast<BlockIdx>(be32toh(*reinterpret_cast<uint32_t*>(&data[4])));
|
|
||||||
header.blockSize = static_cast<BlockSize>(be16toh(*reinterpret_cast<uint16_t*>(&data[8])));
|
|
||||||
data = data.subspan(sizeof(header));
|
|
||||||
|
|
||||||
return header;
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::string extractPackageName(const std::string& staticArgs) {
|
|
||||||
static constexpr auto kPrefix = "package="sv;
|
|
||||||
static constexpr auto kSuffix = "&"sv;
|
|
||||||
|
|
||||||
const auto startPos = staticArgs.find(kPrefix);
|
|
||||||
if (startPos == staticArgs.npos || startPos + kPrefix.size() >= staticArgs.size()) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
const auto endPos = staticArgs.find(kSuffix, startPos + kPrefix.size());
|
|
||||||
return staticArgs.substr(startPos + kPrefix.size(),
|
|
||||||
endPos == staticArgs.npos ? staticArgs.npos
|
|
||||||
: (endPos - (startPos + kPrefix.size())));
|
|
||||||
}
|
|
||||||
|
|
||||||
class AdbDataLoader : public android::dataloader::DataLoader {
|
|
||||||
private:
|
|
||||||
// Lifecycle.
|
|
||||||
bool onCreate(const android::dataloader::DataLoaderParams& params,
|
|
||||||
android::dataloader::FilesystemConnectorPtr ifs,
|
|
||||||
android::dataloader::StatusListenerPtr statusListener,
|
|
||||||
android::dataloader::ServiceConnectorPtr,
|
|
||||||
android::dataloader::ServiceParamsPtr) final {
|
|
||||||
CHECK(ifs) << "ifs can't be null";
|
|
||||||
CHECK(statusListener) << "statusListener can't be null";
|
|
||||||
ALOGE("[AdbDataLoader] onCreate: %d/%s/%s/%s/%d", params.type(),
|
|
||||||
params.packageName().c_str(), params.className().c_str(), params.arguments().c_str(),
|
|
||||||
(int)params.dynamicArgs().size());
|
|
||||||
|
|
||||||
if (params.dynamicArgs().empty()) {
|
|
||||||
ALOGE("[AdbDataLoader] Invalid DataLoaderParams. Need in/out FDs.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (auto const& namedFd : params.dynamicArgs()) {
|
|
||||||
if (namedFd.name == "inFd") {
|
|
||||||
mInFd.reset(dup(namedFd.fd));
|
|
||||||
}
|
|
||||||
if (namedFd.name == "outFd") {
|
|
||||||
mOutFd.reset(dup(namedFd.fd));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (mInFd < 0 || mOutFd < 0) {
|
|
||||||
ALOGE("[AdbDataLoader] Failed to dup FDs.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
mEventFd.reset(eventfd(0, EFD_CLOEXEC));
|
|
||||||
if (mEventFd < 0) {
|
|
||||||
ALOGE("[AdbDataLoader] Failed to create eventfd.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string logFile;
|
|
||||||
if (const auto packageName = extractPackageName(params.arguments()); !packageName.empty()) {
|
|
||||||
logFile = android::base::GetProperty("adb.readlog." + packageName, "");
|
|
||||||
}
|
|
||||||
if (logFile.empty()) {
|
|
||||||
logFile = android::base::GetProperty("adb.readlog", "");
|
|
||||||
}
|
|
||||||
if (!logFile.empty()) {
|
|
||||||
int flags = O_WRONLY | O_CREAT | O_CLOEXEC;
|
|
||||||
mReadLogFd.reset(TEMP_FAILURE_RETRY(open(logFile.c_str(), flags, 0666)));
|
|
||||||
}
|
|
||||||
|
|
||||||
mIfs = ifs;
|
|
||||||
mStatusListener = statusListener;
|
|
||||||
ALOGE("[AdbDataLoader] Successfully created data loader.");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool onStart() final {
|
|
||||||
char okay_buf[OKAY.size()];
|
|
||||||
if (!android::base::ReadFully(mInFd, okay_buf, OKAY.size())) {
|
|
||||||
ALOGE("[AdbDataLoader] Failed to receive OKAY. Abort.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (std::string_view(okay_buf, OKAY.size()) != OKAY) {
|
|
||||||
ALOGE("[AdbDataLoader] Received '%.*s', expecting '%.*s'", (int)OKAY.size(), okay_buf,
|
|
||||||
(int)OKAY.size(), OKAY.data());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
mReceiverThread = std::thread([this]() { receiver(); });
|
|
||||||
ALOGI("[AdbDataLoader] started loading...");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void onStop() final {
|
|
||||||
mStopReceiving = true;
|
|
||||||
eventfd_write(mEventFd, 1);
|
|
||||||
if (mReceiverThread.joinable()) {
|
|
||||||
mReceiverThread.join();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void onDestroy() final {
|
|
||||||
ALOGE("[AdbDataLoader] Sending EXIT to server.");
|
|
||||||
sendRequest(mOutFd, EXIT);
|
|
||||||
// Make sure the receiver thread was stopped
|
|
||||||
CHECK(!mReceiverThread.joinable());
|
|
||||||
|
|
||||||
mInFd.reset();
|
|
||||||
mOutFd.reset();
|
|
||||||
|
|
||||||
mNodeToMetaMap.clear();
|
|
||||||
mIdToNodeMap.clear();
|
|
||||||
|
|
||||||
flushReadLog();
|
|
||||||
mReadLogFd.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Installation callback
|
|
||||||
bool onPrepareImage(const android::dataloader::DataLoaderInstallationFiles& addedFiles) final {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// IFS callbacks.
|
|
||||||
void onPendingReads(const android::dataloader::PendingReads& pendingReads) final {
|
|
||||||
std::lock_guard lock{mMapsMutex};
|
|
||||||
CHECK(mIfs);
|
|
||||||
for (auto&& pendingRead : pendingReads) {
|
|
||||||
const android::dataloader::FileId id = pendingRead.id;
|
|
||||||
const auto blockIdx = static_cast<BlockIdx>(pendingRead.block);
|
|
||||||
/*
|
|
||||||
ALOGI("[AdbDataLoader] Missing: %d", (int) blockIdx);
|
|
||||||
*/
|
|
||||||
auto fileIdOr = getFileId(id);
|
|
||||||
if (!fileIdOr) {
|
|
||||||
ALOGE("[AdbDataLoader] Failed to handle event for fileid=%s. "
|
|
||||||
"Ignore.",
|
|
||||||
android::incfs::toString(id).c_str());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const FileId fileId = *fileIdOr;
|
|
||||||
if (mRequestedFiles.insert(fileId).second) {
|
|
||||||
if (!sendRequest(mOutFd, PREFETCH, fileId, blockIdx)) {
|
|
||||||
ALOGE("[AdbDataLoader] Failed to request prefetch for "
|
|
||||||
"fileid=%s. Ignore.",
|
|
||||||
android::incfs::toString(id).c_str());
|
|
||||||
mRequestedFiles.erase(fileId);
|
|
||||||
mStatusListener->reportStatus(DATA_LOADER_NO_CONNECTION);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sendRequest(mOutFd, BLOCK_MISSING, fileId, blockIdx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct TracedRead {
|
|
||||||
uint64_t timestampUs;
|
|
||||||
android::dataloader::FileId fileId;
|
|
||||||
uint32_t firstBlockIdx;
|
|
||||||
uint32_t count;
|
|
||||||
};
|
|
||||||
void onPageReads(const android::dataloader::PageReads& pageReads) final {
|
|
||||||
auto trace = atrace_is_tag_enabled(ATRACE_TAG);
|
|
||||||
auto log = mReadLogFd != -1;
|
|
||||||
if (CC_LIKELY(!(trace || log))) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
TracedRead last = {};
|
|
||||||
std::lock_guard lock{mMapsMutex};
|
|
||||||
for (auto&& read : pageReads) {
|
|
||||||
if (read.id != last.fileId || read.block != last.firstBlockIdx + last.count) {
|
|
||||||
traceOrLogRead(last, trace, log);
|
|
||||||
last = {read.bootClockTsUs, read.id, (uint32_t)read.block, 1};
|
|
||||||
} else {
|
|
||||||
++last.count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
traceOrLogRead(last, trace, log);
|
|
||||||
}
|
|
||||||
void onFileCreated(android::dataloader::FileId fileid,
|
|
||||||
const android::dataloader::RawMetadata& metadata) {}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void receiver() {
|
|
||||||
std::vector<uint8_t> data;
|
|
||||||
std::vector<IncFsDataBlock> instructions;
|
|
||||||
std::unordered_map<android::dataloader::FileId, unique_fd> writeFds;
|
|
||||||
while (!mStopReceiving) {
|
|
||||||
const int res = waitForDataOrSignal(mInFd, mEventFd);
|
|
||||||
if (res == 0) {
|
|
||||||
flushReadLog();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (res < 0) {
|
|
||||||
ALOGE("[AdbDataLoader] failed to poll. Abort.");
|
|
||||||
mStatusListener->reportStatus(DATA_LOADER_NO_CONNECTION);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (res == mEventFd) {
|
|
||||||
ALOGE("[AdbDataLoader] received stop signal. Exit.");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!readChunk(mInFd, data)) {
|
|
||||||
ALOGE("[AdbDataLoader] failed to read a message. Abort.");
|
|
||||||
mStatusListener->reportStatus(DATA_LOADER_NO_CONNECTION);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
auto remainingData = std::span(data);
|
|
||||||
while (!remainingData.empty()) {
|
|
||||||
auto header = readHeader(remainingData);
|
|
||||||
if (header.fileId == -1 && header.compressionType == 0 && header.blockIdx == 0 &&
|
|
||||||
header.blockSize == 0) {
|
|
||||||
ALOGI("[AdbDataLoader] stop signal received. Sending "
|
|
||||||
"exit command (remaining bytes: %d).",
|
|
||||||
int(remainingData.size()));
|
|
||||||
|
|
||||||
sendRequest(mOutFd, EXIT);
|
|
||||||
mStopReceiving = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (header.fileId < 0 || header.blockSize <= 0 || header.compressionType < 0 ||
|
|
||||||
header.blockIdx < 0) {
|
|
||||||
ALOGE("[AdbDataLoader] invalid header received. Abort.");
|
|
||||||
mStopReceiving = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const android::dataloader::FileId id = mIdToNodeMap[header.fileId];
|
|
||||||
if (!android::incfs::isValidFileId(id)) {
|
|
||||||
ALOGE("Unknown data destination for file ID %d. "
|
|
||||||
"Ignore.",
|
|
||||||
header.fileId);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto& writeFd = writeFds[id];
|
|
||||||
if (writeFd < 0) {
|
|
||||||
writeFd = this->mIfs->openWrite(id);
|
|
||||||
if (writeFd < 0) {
|
|
||||||
ALOGE("Failed to open file %d for writing (%d). Aboring.", header.fileId,
|
|
||||||
-writeFd);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto inst = IncFsDataBlock{
|
|
||||||
.fileFd = writeFd,
|
|
||||||
.pageIndex = static_cast<IncFsBlockIndex>(header.blockIdx),
|
|
||||||
.compression = static_cast<IncFsCompressionKind>(header.compressionType),
|
|
||||||
.kind = INCFS_BLOCK_KIND_DATA,
|
|
||||||
.dataSize = static_cast<uint16_t>(header.blockSize),
|
|
||||||
.data = (const char*)remainingData.data(),
|
|
||||||
};
|
|
||||||
instructions.push_back(inst);
|
|
||||||
remainingData = remainingData.subspan(header.blockSize);
|
|
||||||
}
|
|
||||||
writeInstructions(instructions);
|
|
||||||
}
|
|
||||||
writeInstructions(instructions);
|
|
||||||
flushReadLog();
|
|
||||||
}
|
|
||||||
|
|
||||||
void writeInstructions(std::vector<IncFsDataBlock>& instructions) {
|
|
||||||
auto res = this->mIfs->writeBlocks(instructions);
|
|
||||||
if (res != instructions.size()) {
|
|
||||||
ALOGE("[AdbDataLoader] failed to write data to Incfs (res=%d when "
|
|
||||||
"expecting %d)",
|
|
||||||
res, int(instructions.size()));
|
|
||||||
}
|
|
||||||
instructions.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
struct MetaPair {
|
|
||||||
android::dataloader::RawMetadata meta;
|
|
||||||
FileId fileId;
|
|
||||||
};
|
|
||||||
|
|
||||||
MetaPair* updateMapsForFile(android::dataloader::FileId id) {
|
|
||||||
android::dataloader::RawMetadata meta = mIfs->getRawMetadata(id);
|
|
||||||
FileId fileId;
|
|
||||||
auto res = std::from_chars(meta.data(), meta.data() + meta.size(), fileId);
|
|
||||||
if (res.ec != std::errc{} || fileId < 0) {
|
|
||||||
ALOGE("[AdbDataLoader] Invalid metadata for fileid=%s (%s)",
|
|
||||||
android::incfs::toString(id).c_str(), meta.data());
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
mIdToNodeMap[fileId] = id;
|
|
||||||
auto& metaPair = mNodeToMetaMap[id];
|
|
||||||
metaPair.meta = std::move(meta);
|
|
||||||
metaPair.fileId = fileId;
|
|
||||||
return &metaPair;
|
|
||||||
}
|
|
||||||
|
|
||||||
android::dataloader::RawMetadata* getMeta(android::dataloader::FileId id) {
|
|
||||||
auto it = mNodeToMetaMap.find(id);
|
|
||||||
if (it != mNodeToMetaMap.end()) {
|
|
||||||
return &it->second.meta;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto metaPair = updateMapsForFile(id);
|
|
||||||
if (!metaPair) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return &metaPair->meta;
|
|
||||||
}
|
|
||||||
|
|
||||||
FileId* getFileId(android::dataloader::FileId id) {
|
|
||||||
auto it = mNodeToMetaMap.find(id);
|
|
||||||
if (it != mNodeToMetaMap.end()) {
|
|
||||||
return &it->second.fileId;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto* metaPair = updateMapsForFile(id);
|
|
||||||
if (!metaPair) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return &metaPair->fileId;
|
|
||||||
}
|
|
||||||
|
|
||||||
void traceOrLogRead(const TracedRead& read, bool trace, bool log) {
|
|
||||||
if (!read.count) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (trace) {
|
|
||||||
auto* meta = getMeta(read.fileId);
|
|
||||||
auto str = android::base::StringPrintf("page_read: index=%lld count=%lld meta=%.*s",
|
|
||||||
static_cast<long long>(read.firstBlockIdx),
|
|
||||||
static_cast<long long>(read.count),
|
|
||||||
meta ? int(meta->size()) : 0,
|
|
||||||
meta ? meta->data() : "");
|
|
||||||
ATRACE_BEGIN(str.c_str());
|
|
||||||
ATRACE_END();
|
|
||||||
}
|
|
||||||
if (log) {
|
|
||||||
mReadLog.reserve(ReadLogBufferSize);
|
|
||||||
|
|
||||||
auto fileId = getFileId(read.fileId);
|
|
||||||
android::base::StringAppendF(&mReadLog, "%lld:%lld:%lld:%lld\n",
|
|
||||||
static_cast<long long>(read.timestampUs),
|
|
||||||
static_cast<long long>(fileId ? *fileId : -1),
|
|
||||||
static_cast<long long>(read.firstBlockIdx),
|
|
||||||
static_cast<long long>(read.count));
|
|
||||||
|
|
||||||
if (mReadLog.size() >= mReadLog.capacity() - ReadLogMaxEntrySize) {
|
|
||||||
flushReadLog();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void flushReadLog() {
|
|
||||||
if (mReadLog.empty() || mReadLogFd == -1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
android::base::WriteStringToFd(mReadLog, mReadLogFd);
|
|
||||||
mReadLog.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
android::dataloader::FilesystemConnectorPtr mIfs = nullptr;
|
|
||||||
android::dataloader::StatusListenerPtr mStatusListener = nullptr;
|
|
||||||
android::base::unique_fd mInFd;
|
|
||||||
android::base::unique_fd mOutFd;
|
|
||||||
android::base::unique_fd mEventFd;
|
|
||||||
android::base::unique_fd mReadLogFd;
|
|
||||||
std::string mReadLog;
|
|
||||||
std::thread mReceiverThread;
|
|
||||||
std::mutex mMapsMutex;
|
|
||||||
std::unordered_map<android::dataloader::FileId, MetaPair> mNodeToMetaMap GUARDED_BY(mMapsMutex);
|
|
||||||
std::unordered_map<FileId, android::dataloader::FileId> mIdToNodeMap GUARDED_BY(mMapsMutex);
|
|
||||||
/** Tracks which files have been requested */
|
|
||||||
std::unordered_set<FileId> mRequestedFiles;
|
|
||||||
std::atomic<bool> mStopReceiving = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
int JNI_OnLoad(JavaVM* jvm, void* /* reserved */) {
|
|
||||||
android::dataloader::DataLoader::initialize(
|
|
||||||
[](auto, auto) { return std::make_unique<AdbDataLoader>(); });
|
|
||||||
return JNI_VERSION_1_6;
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
<?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.
|
|
||||||
-->
|
|
||||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
|
||||||
<!-- Name of the Data Loader Service. [CHAR LIMIT=40] -->
|
|
||||||
<string name="app_name">Native Adb Data Loader Service</string>
|
|
||||||
</resources>
|
|
||||||
@@ -1,34 +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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.android.incremental.nativeadb;
|
|
||||||
|
|
||||||
import android.annotation.NonNull;
|
|
||||||
import android.content.pm.DataLoaderParams;
|
|
||||||
import android.service.dataloader.DataLoaderService;
|
|
||||||
|
|
||||||
/** This code is used for testing only. */
|
|
||||||
public class NativeAdbDataLoaderService extends DataLoaderService {
|
|
||||||
public static final String TAG = "NativeAdbDataLoaderService";
|
|
||||||
static {
|
|
||||||
System.loadLibrary("nativeadbdataloaderservice_jni");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DataLoader onCreateDataLoader(@NonNull DataLoaderParams dataLoaderParams) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -42281,7 +42281,6 @@ Lcom/android/server/incident/RequestQueue$1;
|
|||||||
Lcom/android/server/incident/RequestQueue$Rec;
|
Lcom/android/server/incident/RequestQueue$Rec;
|
||||||
Lcom/android/server/incident/RequestQueue;
|
Lcom/android/server/incident/RequestQueue;
|
||||||
Lcom/android/server/incremental/IncrementalManagerService;
|
Lcom/android/server/incremental/IncrementalManagerService;
|
||||||
Lcom/android/server/incremental/IncrementalManagerShellCommand;
|
|
||||||
Lcom/android/server/infra/-$$Lambda$AbstractMasterSystemService$1$TLhe3_2yHs5UB69Y7lf2s7OxJCo;
|
Lcom/android/server/infra/-$$Lambda$AbstractMasterSystemService$1$TLhe3_2yHs5UB69Y7lf2s7OxJCo;
|
||||||
Lcom/android/server/infra/-$$Lambda$AbstractMasterSystemService$_fKw-VUP0pSfcMMlgRqoT4OPhxw;
|
Lcom/android/server/infra/-$$Lambda$AbstractMasterSystemService$_fKw-VUP0pSfcMMlgRqoT4OPhxw;
|
||||||
Lcom/android/server/infra/-$$Lambda$AbstractMasterSystemService$su3lJpEVIbL-C7doP4eboTpqjxU;
|
Lcom/android/server/infra/-$$Lambda$AbstractMasterSystemService$su3lJpEVIbL-C7doP4eboTpqjxU;
|
||||||
|
|||||||
@@ -26,9 +26,7 @@ import android.content.pm.IDataLoader;
|
|||||||
import android.content.pm.IDataLoaderStatusListener;
|
import android.content.pm.IDataLoaderStatusListener;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.ResultReceiver;
|
|
||||||
import android.os.ServiceManager;
|
import android.os.ServiceManager;
|
||||||
import android.os.ShellCallback;
|
|
||||||
import android.os.incremental.IIncrementalManager;
|
import android.os.incremental.IIncrementalManager;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
|
|
||||||
@@ -147,14 +145,6 @@ public class IncrementalManagerService extends IIncrementalManager.Stub {
|
|||||||
// TODO(b/136132412): implement this
|
// TODO(b/136132412): implement this
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onShellCommand(@NonNull FileDescriptor in, @NonNull FileDescriptor out,
|
|
||||||
FileDescriptor err, @NonNull String[] args, ShellCallback callback,
|
|
||||||
@NonNull ResultReceiver resultReceiver) {
|
|
||||||
(new IncrementalManagerShellCommand(mContext)).exec(
|
|
||||||
this, in, out, err, args, callback, resultReceiver);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static native long nativeStartService();
|
private static native long nativeStartService();
|
||||||
|
|
||||||
private static native void nativeSystemReady(long nativeInstance);
|
private static native void nativeSystemReady(long nativeInstance);
|
||||||
|
|||||||
@@ -1,313 +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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package com.android.server.incremental;
|
|
||||||
|
|
||||||
import static android.content.pm.PackageInstaller.LOCATION_DATA_APP;
|
|
||||||
|
|
||||||
import android.annotation.IntDef;
|
|
||||||
import android.annotation.NonNull;
|
|
||||||
import android.annotation.Nullable;
|
|
||||||
import android.content.ComponentName;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.IIntentReceiver;
|
|
||||||
import android.content.IIntentSender;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.IntentSender;
|
|
||||||
import android.content.pm.DataLoaderParams;
|
|
||||||
import android.content.pm.InstallationFile;
|
|
||||||
import android.content.pm.PackageInstaller;
|
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.os.IBinder;
|
|
||||||
import android.os.ParcelFileDescriptor;
|
|
||||||
import android.os.ShellCommand;
|
|
||||||
import android.util.Slog;
|
|
||||||
|
|
||||||
import java.io.FileDescriptor;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.PrintWriter;
|
|
||||||
import java.lang.annotation.Retention;
|
|
||||||
import java.lang.annotation.RetentionPolicy;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines actions to handle adb commands like "adb abb incremental ...".
|
|
||||||
*/
|
|
||||||
public final class IncrementalManagerShellCommand extends ShellCommand {
|
|
||||||
private static final String TAG = "IncrementalShellCommand";
|
|
||||||
// Assuming the adb data loader is always installed on the device
|
|
||||||
private static final String LOADER_PACKAGE_NAME = "com.android.incremental.nativeadb";
|
|
||||||
private static final String LOADER_CLASS_NAME =
|
|
||||||
LOADER_PACKAGE_NAME + ".NativeAdbDataLoaderService";
|
|
||||||
private final @NonNull Context mContext;
|
|
||||||
|
|
||||||
private static final int ERROR_INVALID_ARGUMENTS = -1;
|
|
||||||
private static final int ERROR_DATA_LOADER_INIT = -2;
|
|
||||||
private static final int ERROR_COMMAND_EXECUTION = -3;
|
|
||||||
|
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
|
||||||
@IntDef({ERROR_INVALID_ARGUMENTS, ERROR_DATA_LOADER_INIT, ERROR_COMMAND_EXECUTION})
|
|
||||||
public @interface IncrementalShellCommandErrorCode {
|
|
||||||
}
|
|
||||||
|
|
||||||
IncrementalManagerShellCommand(@NonNull Context context) {
|
|
||||||
mContext = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int onCommand(@Nullable String cmd) {
|
|
||||||
if (cmd == null) {
|
|
||||||
return handleDefaultCommands(null);
|
|
||||||
}
|
|
||||||
switch (cmd) {
|
|
||||||
case "install-start":
|
|
||||||
return runInstallStart();
|
|
||||||
case "install-finish":
|
|
||||||
return runInstallFinish();
|
|
||||||
default:
|
|
||||||
return handleDefaultCommands(cmd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onHelp() {
|
|
||||||
PrintWriter pw = getOutPrintWriter();
|
|
||||||
pw.println("Incremental Service Commands:");
|
|
||||||
pw.println(" help");
|
|
||||||
pw.println(" Print this help text.");
|
|
||||||
pw.println(" install-start");
|
|
||||||
pw.println(" Opens an installation session");
|
|
||||||
pw.println(" install-finish SESSION_ID --file NAME:SIZE:INDEX --file NAME:SIZE:INDEX ...");
|
|
||||||
pw.println(" Commits an installation session specified by session ID for an APK ");
|
|
||||||
pw.println(" or a bundle of splits. Configures lib dirs or OBB files if specified.");
|
|
||||||
}
|
|
||||||
|
|
||||||
private int runInstallStart() {
|
|
||||||
final PrintWriter pw = getOutPrintWriter();
|
|
||||||
final PackageInstaller packageInstaller =
|
|
||||||
mContext.getPackageManager().getPackageInstaller();
|
|
||||||
if (packageInstaller == null) {
|
|
||||||
pw.println("Failed to get PackageInstaller.");
|
|
||||||
return ERROR_COMMAND_EXECUTION;
|
|
||||||
}
|
|
||||||
|
|
||||||
final Map<String, ParcelFileDescriptor> fds = getShellFileDescriptors();
|
|
||||||
if (fds == null) {
|
|
||||||
pw.println("File names and sizes don't match.");
|
|
||||||
return ERROR_DATA_LOADER_INIT;
|
|
||||||
}
|
|
||||||
// dup FDs before closing them
|
|
||||||
final Map<String, ParcelFileDescriptor> dataLoaderDynamicArgs = new HashMap<>();
|
|
||||||
for (Map.Entry<String, ParcelFileDescriptor> nfd : fds.entrySet()) {
|
|
||||||
try {
|
|
||||||
dataLoaderDynamicArgs.put(nfd.getKey(), nfd.getValue().dup());
|
|
||||||
} catch (IOException ignored) {
|
|
||||||
pw.println("Failed to dup shell file descriptor");
|
|
||||||
return ERROR_DATA_LOADER_INIT;
|
|
||||||
} finally {
|
|
||||||
try {
|
|
||||||
nfd.getValue().close();
|
|
||||||
} catch (IOException ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final DataLoaderParams params = DataLoaderParams.forIncremental(
|
|
||||||
new ComponentName(LOADER_PACKAGE_NAME, LOADER_CLASS_NAME), "",
|
|
||||||
dataLoaderDynamicArgs);
|
|
||||||
PackageInstaller.SessionParams sessionParams = new PackageInstaller.SessionParams(
|
|
||||||
PackageInstaller.SessionParams.MODE_FULL_INSTALL);
|
|
||||||
sessionParams.installFlags |= PackageManager.INSTALL_ALL_USERS;
|
|
||||||
// Replace existing if same package is already installed
|
|
||||||
sessionParams.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
|
|
||||||
sessionParams.setDataLoaderParams(params);
|
|
||||||
|
|
||||||
try {
|
|
||||||
int sessionId = packageInstaller.createSession(sessionParams);
|
|
||||||
pw.println("Successfully opened session: sessionId = " + sessionId);
|
|
||||||
} catch (IOException ex) {
|
|
||||||
pw.println("Failed to create session.");
|
|
||||||
return ERROR_COMMAND_EXECUTION;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int runInstallFinish() {
|
|
||||||
final PrintWriter pw = getOutPrintWriter();
|
|
||||||
final int sessionId = parseInt(getNextArgRequired());
|
|
||||||
final List<InstallationFile> installationFiles = parseFileArgs(pw);
|
|
||||||
if (installationFiles == null) {
|
|
||||||
pw.println("Must specify at least one file to install.");
|
|
||||||
return ERROR_INVALID_ARGUMENTS;
|
|
||||||
}
|
|
||||||
final int numFiles = installationFiles.size();
|
|
||||||
if (numFiles == 0) {
|
|
||||||
pw.println("Must specify at least one file to install.");
|
|
||||||
return ERROR_INVALID_ARGUMENTS;
|
|
||||||
}
|
|
||||||
|
|
||||||
final PackageInstaller packageInstaller = mContext.getPackageManager()
|
|
||||||
.getPackageInstaller();
|
|
||||||
if (packageInstaller == null) {
|
|
||||||
pw.println("Failed to get PackageInstaller.");
|
|
||||||
return ERROR_COMMAND_EXECUTION;
|
|
||||||
}
|
|
||||||
|
|
||||||
final LocalIntentReceiver localReceiver = new LocalIntentReceiver();
|
|
||||||
boolean success = false;
|
|
||||||
|
|
||||||
PackageInstaller.Session session = null;
|
|
||||||
try {
|
|
||||||
session = packageInstaller.openSession(sessionId);
|
|
||||||
for (int i = 0; i < numFiles; i++) {
|
|
||||||
InstallationFile file = installationFiles.get(i);
|
|
||||||
session.addFile(file.getLocation(), file.getName(), file.getLengthBytes(),
|
|
||||||
file.getMetadata(), file.getSignature());
|
|
||||||
}
|
|
||||||
session.commit(localReceiver.getIntentSender());
|
|
||||||
final Intent result = localReceiver.getResult();
|
|
||||||
final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
|
|
||||||
PackageInstaller.STATUS_FAILURE);
|
|
||||||
if (status == PackageInstaller.STATUS_SUCCESS) {
|
|
||||||
success = true;
|
|
||||||
pw.println("Success");
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
pw.println("Failure ["
|
|
||||||
+ result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
|
|
||||||
return ERROR_COMMAND_EXECUTION;
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace(pw);
|
|
||||||
return ERROR_COMMAND_EXECUTION;
|
|
||||||
} finally {
|
|
||||||
if (!success) {
|
|
||||||
try {
|
|
||||||
if (session != null) {
|
|
||||||
session.abandon();
|
|
||||||
}
|
|
||||||
} catch (Exception ignore) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class LocalIntentReceiver {
|
|
||||||
private final LinkedBlockingQueue<Intent> mResult = new LinkedBlockingQueue<>();
|
|
||||||
|
|
||||||
private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
|
|
||||||
@Override
|
|
||||||
public void send(int code, Intent intent, String resolvedType,
|
|
||||||
IBinder whitelistToken,
|
|
||||||
IIntentReceiver finishedReceiver, String requiredPermission,
|
|
||||||
Bundle options) {
|
|
||||||
try {
|
|
||||||
mResult.offer(intent, 5, TimeUnit.SECONDS);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public IntentSender getIntentSender() {
|
|
||||||
return new IntentSender((IIntentSender) mLocalSender);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Intent getResult() {
|
|
||||||
try {
|
|
||||||
return mResult.take();
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Helpers. */
|
|
||||||
private Map<String, ParcelFileDescriptor> getShellFileDescriptors() {
|
|
||||||
Map<String, ParcelFileDescriptor> fds = new HashMap<>();
|
|
||||||
final FileDescriptor outFd = getOutFileDescriptor();
|
|
||||||
final FileDescriptor inFd = getInFileDescriptor();
|
|
||||||
try {
|
|
||||||
fds.put("inFd", ParcelFileDescriptor.dup(inFd));
|
|
||||||
fds.put("outFd", ParcelFileDescriptor.dup(outFd));
|
|
||||||
return fds;
|
|
||||||
} catch (Exception ex) {
|
|
||||||
Slog.e(TAG, "Failed to dup FDs");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private long parseLong(String arg) {
|
|
||||||
long result = -1;
|
|
||||||
try {
|
|
||||||
result = Long.parseLong(arg);
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int parseInt(String arg) {
|
|
||||||
int result = -1;
|
|
||||||
try {
|
|
||||||
result = Integer.parseInt(arg);
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<InstallationFile> parseFileArgs(PrintWriter pw) {
|
|
||||||
List<InstallationFile> fileList = new ArrayList<>();
|
|
||||||
String opt;
|
|
||||||
while ((opt = getNextOption()) != null) {
|
|
||||||
switch (opt) {
|
|
||||||
case "--file": {
|
|
||||||
final String fileArgs = getNextArgRequired();
|
|
||||||
final String[] args = fileArgs.split(":");
|
|
||||||
if (args.length != 3) {
|
|
||||||
pw.println("Invalid file args: " + fileArgs);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
final String name = args[0];
|
|
||||||
final long size = parseLong(args[1]);
|
|
||||||
if (size < 0) {
|
|
||||||
pw.println("Invalid file size in: " + fileArgs);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
final long index = parseLong(args[2]);
|
|
||||||
if (index < 0) {
|
|
||||||
pw.println("Invalid file index in: " + fileArgs);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
final byte[] metadata = String.valueOf(index).getBytes(
|
|
||||||
StandardCharsets.UTF_8);
|
|
||||||
fileList.add(
|
|
||||||
new InstallationFile(LOCATION_DATA_APP, name, size, metadata, null));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return fileList;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user