Merge "Incremental installations in PackageManagerShellCommand."

This commit is contained in:
Alex Buynytskyy
2020-02-11 21:24:55 +00:00
committed by Android (Google) Code Review
20 changed files with 497 additions and 111 deletions

View File

@@ -33,7 +33,4 @@ interface IIncrementalManager {
boolean startDataLoader(int mountId);
void showHealthBlockedUI(int mountId);
void destroyDataLoader(int mountId);
// fileId is a 16 byte long identifier.
void newFileForDataLoader(int mountId, in byte[] fileId, in byte[] metadata);
}

View File

@@ -17,6 +17,7 @@
package android.os.incremental;
import android.content.pm.DataLoaderParamsParcel;
import android.content.pm.IDataLoaderStatusListener;
import android.os.incremental.IncrementalNewFileParams;
/** @hide */
@@ -33,7 +34,7 @@ interface IIncrementalService {
* Opens or creates a storage given a target path and data loader params. Returns the storage ID.
*/
int openStorage(in @utf8InCpp String path);
int createStorage(in @utf8InCpp String path, in DataLoaderParamsParcel params, int createMode);
int createStorage(in @utf8InCpp String path, in DataLoaderParamsParcel params, in IDataLoaderStatusListener listener, int createMode);
int createLinkedStorage(in @utf8InCpp String path, int otherStorageId, int createMode);
/**

View File

@@ -35,6 +35,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.DataLoaderParams;
import android.content.pm.IDataLoaderStatusListener;
import android.content.pm.InstallationFile;
import android.text.TextUtils;
import android.util.Slog;
@@ -74,6 +75,7 @@ public final class IncrementalFileStorages {
public static IncrementalFileStorages initialize(Context context,
@NonNull File stageDir,
@NonNull DataLoaderParams dataLoaderParams,
@Nullable IDataLoaderStatusListener dataLoaderStatusListener,
List<InstallationFile> addedFiles) throws IOException {
// TODO(b/136132412): sanity check if session should not be incremental
IncrementalManager incrementalManager = (IncrementalManager) context.getSystemService(
@@ -85,7 +87,13 @@ public final class IncrementalFileStorages {
IncrementalFileStorages result = null;
try {
result = new IncrementalFileStorages(stageDir, incrementalManager, dataLoaderParams);
result = new IncrementalFileStorages(stageDir, incrementalManager, dataLoaderParams,
dataLoaderStatusListener);
if (!addedFiles.isEmpty()) {
result.mDefaultStorage.bind(stageDir.getAbsolutePath());
}
for (InstallationFile file : addedFiles) {
if (file.getLocation() == LOCATION_DATA_APP) {
try {
@@ -93,14 +101,15 @@ public final class IncrementalFileStorages {
} catch (IOException e) {
// TODO(b/146080380): add incremental-specific error code
throw new IOException(
"Failed to add and configure Incremental File: " + file.getName(),
e);
"Failed to add file to IncFS: " + file.getName() + ", reason: "
+ e.getMessage(), e.getCause());
}
} else {
throw new IOException("Unknown file location: " + file.getLocation());
}
}
// TODO(b/146080380): remove 5 secs wait in startLoading
if (!result.mDefaultStorage.startLoading()) {
// TODO(b/146080380): add incremental-specific error code
throw new IOException("Failed to start loading data for Incremental installation.");
@@ -117,7 +126,8 @@ public final class IncrementalFileStorages {
private IncrementalFileStorages(@NonNull File stageDir,
@NonNull IncrementalManager incrementalManager,
@NonNull DataLoaderParams dataLoaderParams) throws IOException {
@NonNull DataLoaderParams dataLoaderParams,
@Nullable IDataLoaderStatusListener dataLoaderStatusListener) throws IOException {
mStageDir = stageDir;
mIncrementalManager = incrementalManager;
if (dataLoaderParams.getComponentName().getPackageName().equals("local")) {
@@ -134,6 +144,7 @@ public final class IncrementalFileStorages {
}
mDefaultStorage = mIncrementalManager.createStorage(mDefaultDir,
dataLoaderParams,
dataLoaderStatusListener,
IncrementalManager.CREATE_MODE_CREATE
| IncrementalManager.CREATE_MODE_TEMPORARY_BIND, false);
}
@@ -144,10 +155,8 @@ public final class IncrementalFileStorages {
}
private void addApkFile(@NonNull InstallationFile apk) throws IOException {
final String stageDirPath = mStageDir.getAbsolutePath();
mDefaultStorage.bind(stageDirPath);
String apkName = apk.getName();
File targetFile = Paths.get(stageDirPath, apkName).toFile();
final String apkName = apk.getName();
final File targetFile = new File(mStageDir, apkName);
if (!targetFile.exists()) {
mDefaultStorage.makeFile(apkName, apk.getLengthBytes(), null, apk.getMetadata(),
apk.getSignature());

View File

@@ -22,6 +22,7 @@ import android.annotation.Nullable;
import android.annotation.SystemService;
import android.content.Context;
import android.content.pm.DataLoaderParams;
import android.content.pm.IDataLoaderStatusListener;
import android.os.RemoteException;
import android.util.SparseArray;
@@ -103,10 +104,11 @@ public final class IncrementalManager {
*/
@Nullable
public IncrementalStorage createStorage(@NonNull String path,
@NonNull DataLoaderParams params, @CreateMode int createMode,
@NonNull DataLoaderParams params, @Nullable IDataLoaderStatusListener listener,
@CreateMode int createMode,
boolean autoStartDataLoader) {
try {
final int id = mService.createStorage(path, params.getData(), createMode);
final int id = mService.createStorage(path, params.getData(), listener, createMode);
if (id < 0) {
return null;
}

View File

@@ -240,7 +240,7 @@ public abstract class DataLoaderService extends Service {
private native boolean nativeDestroyDataLoader(int storageId);
private native boolean nativePrepareImage(int storageId,
Collection<InstallationFile> addedFiles, Collection<String> removedFiles);
List<InstallationFile> addedFiles, List<String> removedFiles);
private static native void nativeWriteData(long nativeInstance, String name, long offsetBytes,
long lengthBytes, ParcelFileDescriptor incomingFd);

View File

@@ -75,8 +75,9 @@ static const JNINativeMethod dlc_method_table[] = {
{"nativeStartDataLoader", "(I)Z", (void*)nativeStartDataLoader},
{"nativeStopDataLoader", "(I)Z", (void*)nativeStopDataLoader},
{"nativeDestroyDataLoader", "(I)Z", (void*)nativeDestroyDataLoader},
{"nativePrepareImage", "(ILjava/util/Collection;Ljava/util/Collection;)Z", (void*)nativePrepareImage},
{"nativeWriteData", "(JLjava/lang/String;JJLandroid/os/ParcelFileDescriptor;)V", (void*)nativeWriteData},
{"nativePrepareImage", "(ILjava/util/List;Ljava/util/List;)Z", (void*)nativePrepareImage},
{"nativeWriteData", "(JLjava/lang/String;JJLandroid/os/ParcelFileDescriptor;)V",
(void*)nativeWriteData},
};
} // namespace

View File

@@ -261,6 +261,11 @@ private:
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};
@@ -520,6 +525,6 @@ private:
int JNI_OnLoad(JavaVM* jvm, void* /* reserved */) {
android::dataloader::DataLoader::initialize(
[](auto) { return std::make_unique<AdbDataLoader>(); });
[](auto, auto) { return std::make_unique<AdbDataLoader>(); });
return JNI_VERSION_1_6;
}

View File

@@ -142,16 +142,6 @@ public class IncrementalManagerService extends IIncrementalManager.Stub {
}
}
// TODO: remove this
@Override
public void newFileForDataLoader(int mountId, byte[] fileId, byte[] metadata) {
IDataLoader dataLoader = mDataLoaderManager.getDataLoader(mountId);
if (dataLoader == null) {
Slog.e(TAG, "Failed to retrieve data loader for ID=" + mountId);
return;
}
}
@Override
public void showHealthBlockedUI(int mountId) {
// TODO(b/136132412): implement this

View File

@@ -2450,17 +2450,6 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
}
// TODO(b/136132412): update with new APIs
if (isIncrementalInstallation()) {
try {
mIncrementalFileStorages = IncrementalFileStorages.initialize(mContext,
stageDir, params.dataLoaderParams, addedFiles);
return true;
} catch (IOException e) {
throw new PackageManagerException(e);
}
}
final DataLoaderManager dataLoaderManager = mContext.getSystemService(
DataLoaderManager.class);
if (dataLoaderManager == null) {
@@ -2468,6 +2457,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
"Failed to find data loader manager service");
}
final boolean manualStartAndDestroy = !isIncrementalInstallation();
IDataLoaderStatusListener listener = new IDataLoaderStatusListener.Stub() {
@Override
public void onStatusChanged(int dataLoaderId, int status) {
@@ -2487,7 +2477,11 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
switch (status) {
case IDataLoaderStatusListener.DATA_LOADER_CREATED: {
dataLoader.start();
if (manualStartAndDestroy) {
// IncrementalFileStorages will call start after all files are
// created in IncFS.
dataLoader.start();
}
break;
}
case IDataLoaderStatusListener.DATA_LOADER_STARTED: {
@@ -2502,7 +2496,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
} else {
dispatchStreamValidateAndCommit();
}
dataLoader.destroy();
if (manualStartAndDestroy) {
dataLoader.destroy();
}
break;
}
case IDataLoaderStatusListener.DATA_LOADER_IMAGE_NOT_READY: {
@@ -2510,7 +2506,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
onSessionVerificationFailure(
new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE,
"Failed to prepare image."));
dataLoader.destroy();
if (manualStartAndDestroy) {
dataLoader.destroy();
}
break;
}
}
@@ -2524,6 +2522,17 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
}
};
if (!manualStartAndDestroy) {
try {
mIncrementalFileStorages = IncrementalFileStorages.initialize(mContext,
stageDir, params.dataLoaderParams, listener, addedFiles);
return false;
} catch (IOException e) {
throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, e.getMessage(),
e.getCause());
}
}
final FileSystemConnector connector = new FileSystemConnector(addedFiles);
final FileSystemControlParcel control = new FileSystemControlParcel();
control.callback = connector;

View File

@@ -181,6 +181,8 @@ class PackageManagerShellCommand extends ShellCommand {
return runInstall();
case "install-streaming":
return runStreamingInstall();
case "install-incremental":
return runIncrementalInstall();
case "install-abandon":
case "install-destroy":
return runInstallAbandon();
@@ -1168,6 +1170,15 @@ class PackageManagerShellCommand extends ShellCommand {
return doRunInstall(params);
}
private int runIncrementalInstall() throws RemoteException {
final InstallParams params = makeInstallParams();
if (params.sessionParams.dataLoaderParams == null) {
params.sessionParams.setDataLoaderParams(
PackageManagerShellCommandDataLoader.getIncrementalDataLoaderParams(this));
}
return doRunInstall(params);
}
private int runInstall() throws RemoteException {
return doRunInstall(makeInstallParams());
}
@@ -3001,17 +3012,21 @@ class PackageManagerShellCommand extends ShellCommand {
return 1;
}
session.addFile(LOCATION_DATA_APP, name, sizeBytes, STDIN_PATH_BYTES, null);
// Incremental requires unique metadatas, let's add a name to the dash.
session.addFile(LOCATION_DATA_APP, name, sizeBytes,
("-" + name).getBytes(StandardCharsets.UTF_8), null);
continue;
}
// 3. Local file.
final String inPath = arg;
String name = new File(inPath).getName();
final File file = new File(inPath);
final String name = file.getName();
final long size = file.length();
byte[] metadata = inPath.getBytes(StandardCharsets.UTF_8);
session.addFile(LOCATION_DATA_APP, name, -1, metadata, null);
session.addFile(LOCATION_DATA_APP, name, size, metadata, null);
}
return 0;
} finally {

View File

@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.content.ComponentName;
import android.content.pm.DataLoaderParams;
import android.content.pm.InstallationFile;
import android.content.pm.PackageInstaller;
import android.os.ParcelFileDescriptor;
import android.os.ShellCommand;
import android.service.dataloader.DataLoaderService;
@@ -55,6 +56,8 @@ public class PackageManagerShellCommandDataLoader extends DataLoaderService {
private static final String STDIN_PATH = "-";
private static String getDataLoaderParamsArgs(ShellCommand shellCommand) {
nativeInitialize();
int commandId;
synchronized (sShellCommands) {
// Clean up old references.
@@ -86,6 +89,11 @@ public class PackageManagerShellCommandDataLoader extends DataLoaderService {
getDataLoaderParamsArgs(shellCommand));
}
static DataLoaderParams getIncrementalDataLoaderParams(ShellCommand shellCommand) {
return DataLoaderParams.forIncremental(new ComponentName(PACKAGE, CLASS),
getDataLoaderParamsArgs(shellCommand), null);
}
private static int extractShellCommandId(String args) {
int sessionIdIdx = args.indexOf(SHELL_COMMAND_ID_PREFIX);
if (sessionIdIdx < 0) {
@@ -106,7 +114,7 @@ public class PackageManagerShellCommandDataLoader extends DataLoaderService {
}
}
static class DataLoader implements DataLoaderService.DataLoader {
private static class DataLoader implements DataLoaderService.DataLoader {
private DataLoaderParams mParams = null;
private FileSystemConnector mConnector = null;
@@ -121,17 +129,7 @@ public class PackageManagerShellCommandDataLoader extends DataLoaderService {
@Override
public boolean onPrepareImage(@NonNull Collection<InstallationFile> addedFiles,
@NonNull Collection<String> removedFiles) {
final int commandId = extractShellCommandId(mParams.getArguments());
if (commandId == INVALID_SHELL_COMMAND_ID) {
return false;
}
final WeakReference<ShellCommand> shellCommandRef;
synchronized (sShellCommands) {
shellCommandRef = sShellCommands.get(commandId, null);
}
final ShellCommand shellCommand =
shellCommandRef != null ? shellCommandRef.get() : null;
ShellCommand shellCommand = lookupShellCommand(mParams.getArguments());
if (shellCommand == null) {
Slog.e(TAG, "Missing shell command.");
return false;
@@ -139,14 +137,13 @@ public class PackageManagerShellCommandDataLoader extends DataLoaderService {
try {
for (InstallationFile file : addedFiles) {
String filePath = new String(file.getMetadata(), StandardCharsets.UTF_8);
if (STDIN_PATH.equals(filePath) || TextUtils.isEmpty(filePath)) {
final ParcelFileDescriptor inFd = ParcelFileDescriptor.dup(
shellCommand.getInFileDescriptor());
if (TextUtils.isEmpty(filePath) || filePath.startsWith(STDIN_PATH)) {
final ParcelFileDescriptor inFd = getStdInPFD(shellCommand);
mConnector.writeData(file.getName(), 0, file.getLengthBytes(), inFd);
} else {
ParcelFileDescriptor incomingFd = null;
try {
incomingFd = shellCommand.openFileForSystem(filePath, "r");
incomingFd = getLocalFile(shellCommand, filePath);
mConnector.writeData(file.getName(), 0, incomingFd.getStatSize(),
incomingFd);
} finally {
@@ -162,9 +159,45 @@ public class PackageManagerShellCommandDataLoader extends DataLoaderService {
}
}
static ShellCommand lookupShellCommand(String args) {
final int commandId = extractShellCommandId(args);
if (commandId == INVALID_SHELL_COMMAND_ID) {
return null;
}
final WeakReference<ShellCommand> shellCommandRef;
synchronized (sShellCommands) {
shellCommandRef = sShellCommands.get(commandId, null);
}
final ShellCommand shellCommand =
shellCommandRef != null ? shellCommandRef.get() : null;
return shellCommand;
}
static ParcelFileDescriptor getStdInPFD(ShellCommand shellCommand) {
try {
return ParcelFileDescriptor.dup(shellCommand.getInFileDescriptor());
} catch (IOException e) {
Slog.e(TAG, "Exception while obtaining STDIN fd", e);
return null;
}
}
static ParcelFileDescriptor getLocalFile(ShellCommand shellCommand, String filePath) {
return shellCommand.openFileForSystem(filePath, "r");
}
@Override
public DataLoaderService.DataLoader onCreateDataLoader(
@NonNull DataLoaderParams dataLoaderParams) {
return new DataLoader();
if (dataLoaderParams.getType() == PackageInstaller.DATA_LOADER_TYPE_STREAMING) {
// This DataLoader only supports streaming installations.
return new DataLoader();
}
return null;
}
/* Native methods */
private static native void nativeInitialize();
}

View File

@@ -2,6 +2,7 @@ cc_library_static {
name: "libservices.core",
defaults: ["libservices.core-libs"],
cpp_std: "c++2a",
cflags: [
"-Wall",
"-Werror",
@@ -55,6 +56,7 @@ cc_library_static {
"com_android_server_am_CachedAppOptimizer.cpp",
"com_android_server_am_LowMemDetector.cpp",
"com_android_server_incremental_IncrementalManagerService.cpp",
"com_android_server_pm_PackageManagerShellCommandDataLoader.cpp",
"onload.cpp",
":lib_networkStatsFactory_native",
],
@@ -125,6 +127,8 @@ cc_defaults {
"libnetdbpf",
"libnetdutils",
"libpsi",
"libdataloader",
"libincfs",
"android.hardware.audio.common@2.0",
"android.hardware.broadcastradio@1.0",
"android.hardware.broadcastradio@1.1",

View File

@@ -0,0 +1,327 @@
/*
* Copyright (C) 2020 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 "PackageManagerShellCommandDataLoader-jni"
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
#include <nativehelper/JNIHelp.h>
#include <core_jni_helpers.h>
#include "dataloader.h"
#include <chrono>
#include <thread>
namespace android {
namespace {
using android::base::unique_fd;
static constexpr int BUFFER_SIZE = 256 * 1024;
static constexpr int BLOCKS_COUNT = BUFFER_SIZE / INCFS_DATA_FILE_BLOCK_SIZE;
struct JniIds {
jclass packageManagerShellCommandDataLoader;
jmethodID pmscdLookupShellCommand;
jmethodID pmscdGetStdInPFD;
jmethodID pmscdGetLocalFile;
jmethodID parcelFileDescriptorGetFileDescriptor;
jclass ioUtils;
jmethodID ioUtilsCloseQuietly;
JniIds(JNIEnv* env) {
packageManagerShellCommandDataLoader = (jclass)env->NewGlobalRef(
FindClassOrDie(env, "com/android/server/pm/PackageManagerShellCommandDataLoader"));
pmscdLookupShellCommand =
GetStaticMethodIDOrDie(env, packageManagerShellCommandDataLoader,
"lookupShellCommand",
"(Ljava/lang/String;)Landroid/os/ShellCommand;");
pmscdGetStdInPFD =
GetStaticMethodIDOrDie(env, packageManagerShellCommandDataLoader, "getStdInPFD",
"(Landroid/os/ShellCommand;)Landroid/os/"
"ParcelFileDescriptor;");
pmscdGetLocalFile =
GetStaticMethodIDOrDie(env, packageManagerShellCommandDataLoader, "getLocalFile",
"(Landroid/os/ShellCommand;Ljava/lang/String;)Landroid/os/"
"ParcelFileDescriptor;");
auto parcelFileDescriptor = FindClassOrDie(env, "android/os/ParcelFileDescriptor");
parcelFileDescriptorGetFileDescriptor =
GetMethodIDOrDie(env, parcelFileDescriptor, "getFileDescriptor",
"()Ljava/io/FileDescriptor;");
ioUtils = (jclass)env->NewGlobalRef(FindClassOrDie(env, "libcore/io/IoUtils"));
ioUtilsCloseQuietly = GetStaticMethodIDOrDie(env, ioUtils, "closeQuietly",
"(Ljava/lang/AutoCloseable;)V");
}
};
const JniIds& jniIds(JNIEnv* env) {
static const JniIds ids(env);
return ids;
}
static inline unique_fd convertPfdToFdAndDup(JNIEnv* env, const JniIds& jni, jobject pfd) {
if (!pfd) {
ALOGE("Missing In ParcelFileDescriptor.");
return {};
}
auto managedFd = env->CallObjectMethod(pfd, jni.parcelFileDescriptorGetFileDescriptor);
if (!pfd) {
ALOGE("Missing In FileDescriptor.");
return {};
}
return unique_fd{dup(jniGetFDFromFileDescriptor(env, managedFd))};
}
static inline std::pair<unique_fd, bool> openIncomingFile(JNIEnv* env, const JniIds& jni,
jobject shellCommand,
IncFsSpan metadata) {
jobject pfd = nullptr;
const bool stdin = (metadata.size == 0 || *metadata.data == '-');
if (stdin) {
// stdin
pfd = env->CallStaticObjectMethod(jni.packageManagerShellCommandDataLoader,
jni.pmscdGetStdInPFD, shellCommand);
} else {
// file
const std::string filePath(metadata.data, metadata.size);
pfd = env->CallStaticObjectMethod(jni.packageManagerShellCommandDataLoader,
jni.pmscdGetLocalFile, shellCommand,
env->NewStringUTF(filePath.c_str()));
}
auto result = convertPfdToFdAndDup(env, jni, pfd);
if (pfd) {
// Can be closed after dup.
env->CallStaticVoidMethod(jni.ioUtils, jni.ioUtilsCloseQuietly, pfd);
}
return {std::move(result), stdin};
}
static inline JNIEnv* GetJNIEnvironment(JavaVM* vm) {
JNIEnv* env;
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
return 0;
}
return env;
}
static inline JNIEnv* GetOrAttachJNIEnvironment(JavaVM* jvm) {
JNIEnv* env = GetJNIEnvironment(jvm);
if (!env) {
int result = jvm->AttachCurrentThread(&env, nullptr);
CHECK_EQ(result, JNI_OK) << "thread attach failed";
struct VmDetacher {
VmDetacher(JavaVM* vm) : mVm(vm) {}
~VmDetacher() { mVm->DetachCurrentThread(); }
private:
JavaVM* const mVm;
};
static thread_local VmDetacher detacher(jvm);
}
return env;
}
class PackageManagerShellCommandDataLoaderDataLoader : public android::dataloader::DataLoader {
public:
PackageManagerShellCommandDataLoaderDataLoader(JavaVM* jvm) : mJvm(jvm) { CHECK(mJvm); }
private:
// Lifecycle.
bool onCreate(const android::dataloader::DataLoaderParams& params,
android::dataloader::FilesystemConnectorPtr ifs,
android::dataloader::StatusListenerPtr statusListener,
android::dataloader::ServiceConnectorPtr,
android::dataloader::ServiceParamsPtr) final {
mArgs = params.arguments();
mIfs = ifs;
return true;
}
bool onStart() final { return true; }
void onStop() final {}
void onDestroy() final {}
// IFS callbacks.
void onPendingReads(const dataloader::PendingReads& pendingReads) final {}
void onPageReads(const dataloader::PageReads& pageReads) final {}
// FS callbacks.
bool onPrepareImage(const dataloader::DataLoaderInstallationFiles& addedFiles) final {
JNIEnv* env = GetOrAttachJNIEnvironment(mJvm);
const auto& jni = jniIds(env);
jobject shellCommand = env->CallStaticObjectMethod(jni.packageManagerShellCommandDataLoader,
jni.pmscdLookupShellCommand,
env->NewStringUTF(mArgs.c_str()));
if (!shellCommand) {
ALOGE("Missing shell command.");
return false;
}
std::vector<char> buffer;
buffer.reserve(BUFFER_SIZE);
std::vector<IncFsDataBlock> blocks;
blocks.reserve(BLOCKS_COUNT);
for (auto&& file : addedFiles) {
auto [incomingFd, stdin] = openIncomingFile(env, jni, shellCommand, file.metadata);
if (incomingFd < 0) {
ALOGE("Failed to open an IncFS file for metadata: %.*s, final file name is: %s. "
"Error %d",
int(file.metadata.size), file.metadata.data, file.name, errno);
return false;
}
const auto fileId = IncFs_FileIdFromMetadata(file.metadata);
const auto incfsFd(mIfs->openWrite(fileId));
if (incfsFd < 0) {
ALOGE("Failed to open an IncFS file for metadata: %.*s, final file name is: %s. "
"Error %d",
int(file.metadata.size), file.metadata.data, file.name, errno);
return false;
}
IncFsSize size = file.size;
IncFsSize remaining = size;
IncFsSize totalSize = 0;
IncFsBlockIndex blockIdx = 0;
while (remaining > 0) {
constexpr auto capacity = BUFFER_SIZE;
auto size = buffer.size();
if (capacity - size < INCFS_DATA_FILE_BLOCK_SIZE) {
if (!flashToIncFs(incfsFd, false, &blocks, &blockIdx, &buffer)) {
return false;
}
continue;
}
auto toRead = std::min<IncFsSize>(remaining, capacity - size);
buffer.resize(size + toRead);
auto read = ::read(incomingFd, buffer.data() + size, toRead);
if (read == 0) {
if (stdin) {
// eof of stdin, waiting...
ALOGE("eof of stdin, waiting...: %d, remaining: %d, block: %d, read: %d",
int(totalSize), int(remaining), int(blockIdx), int(read));
using namespace std::chrono_literals;
std::this_thread::sleep_for(10ms);
continue;
}
break;
}
if (read < 0) {
ALOGE("Underlying file read error: %.*s: %d", int(file.metadata.size),
file.metadata.data, int(read));
return false;
}
buffer.resize(size + read);
remaining -= read;
totalSize += read;
}
if (!buffer.empty() && !flashToIncFs(incfsFd, true, &blocks, &blockIdx, &buffer)) {
return false;
}
}
ALOGE("All done.");
return true;
}
bool flashToIncFs(int incfsFd, bool eof, std::vector<IncFsDataBlock>* blocks,
IncFsBlockIndex* blockIdx, std::vector<char>* buffer) {
int consumed = 0;
const auto fullBlocks = buffer->size() / INCFS_DATA_FILE_BLOCK_SIZE;
for (int i = 0; i < fullBlocks; ++i) {
const auto inst = IncFsDataBlock{
.fileFd = incfsFd,
.pageIndex = (*blockIdx)++,
.compression = INCFS_COMPRESSION_KIND_NONE,
.kind = INCFS_BLOCK_KIND_DATA,
.dataSize = INCFS_DATA_FILE_BLOCK_SIZE,
.data = buffer->data() + consumed,
};
blocks->push_back(inst);
consumed += INCFS_DATA_FILE_BLOCK_SIZE;
}
const auto remain = buffer->size() - fullBlocks * INCFS_DATA_FILE_BLOCK_SIZE;
if (remain && eof) {
const auto inst = IncFsDataBlock{
.fileFd = incfsFd,
.pageIndex = (*blockIdx)++,
.compression = INCFS_COMPRESSION_KIND_NONE,
.kind = INCFS_BLOCK_KIND_DATA,
.dataSize = static_cast<uint16_t>(remain),
.data = buffer->data() + consumed,
};
blocks->push_back(inst);
consumed += remain;
}
auto res = mIfs->writeBlocks({blocks->data(), blocks->data() + blocks->size()});
blocks->clear();
buffer->erase(buffer->begin(), buffer->begin() + consumed);
if (res < 0) {
ALOGE("Failed to write block to IncFS: %d", int(res));
return false;
}
return true;
}
JavaVM* const mJvm;
std::string mArgs;
android::dataloader::FilesystemConnectorPtr mIfs;
};
static void nativeInitialize(JNIEnv* env, jclass klass) {
jniIds(env);
}
static const JNINativeMethod method_table[] = {
{"nativeInitialize", "()V", (void*)nativeInitialize},
};
} // namespace
int register_android_server_com_android_server_pm_PackageManagerShellCommandDataLoader(
JNIEnv* env) {
android::dataloader::DataLoader::initialize(
[](auto jvm, const auto& params) -> android::dataloader::DataLoaderPtr {
if (params.type() == DATA_LOADER_TYPE_INCREMENTAL) {
// This DataLoader only supports incremental installations.
return std::make_unique<PackageManagerShellCommandDataLoaderDataLoader>(jvm);
}
return {};
});
return jniRegisterNativeMethods(env,
"com/android/server/pm/PackageManagerShellCommandDataLoader",
method_table, NELEM(method_table));
}
} // namespace android

View File

@@ -59,6 +59,7 @@ int register_android_server_am_LowMemDetector(JNIEnv* env);
int register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(
JNIEnv* env);
int register_android_server_incremental_IncrementalManagerService(JNIEnv* env);
int register_android_server_com_android_server_pm_PackageManagerShellCommandDataLoader(JNIEnv* env);
int register_android_server_stats_pull_StatsPullAtomService(JNIEnv* env);
};
@@ -112,6 +113,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(
env);
register_android_server_incremental_IncrementalManagerService(env);
register_android_server_com_android_server_pm_PackageManagerShellCommandDataLoader(env);
register_android_server_stats_pull_StatsPullAtomService(env);
return JNI_VERSION_1_4;
}

View File

@@ -117,10 +117,9 @@ binder::Status BinderIncrementalService::openStorage(const std::string& path,
binder::Status BinderIncrementalService::createStorage(const std::string& path,
const DataLoaderParamsParcel& params,
const ::android::sp<::android::content::pm::IDataLoaderStatusListener>& listener,
int32_t createMode, int32_t* _aidl_return) {
*_aidl_return = mImpl.createStorage(path, const_cast<DataLoaderParamsParcel&&>(params),
android::incremental::IncrementalService::CreateOptions(
createMode));
*_aidl_return = mImpl.createStorage(path, const_cast<DataLoaderParamsParcel&&>(params), listener, android::incremental::IncrementalService::CreateOptions(createMode));
return ok();
}

View File

@@ -38,9 +38,7 @@ public:
void onInvalidStorage(int mountId);
binder::Status openStorage(const std::string& path, int32_t* _aidl_return) final;
binder::Status createStorage(const std::string& path,
const ::android::content::pm::DataLoaderParamsParcel& params,
int32_t createMode, int32_t* _aidl_return) final;
binder::Status createStorage(const ::std::string& path, const ::android::content::pm::DataLoaderParamsParcel& params, const ::android::sp<::android::content::pm::IDataLoaderStatusListener>& listener, int32_t createMode, int32_t* _aidl_return) final;
binder::Status createLinkedStorage(const std::string& path, int32_t otherStorageId,
int32_t createMode, int32_t* _aidl_return) final;
binder::Status makeBindMount(int32_t storageId, const std::string& sourcePath,

View File

@@ -30,7 +30,6 @@
#include <binder/BinderService.h>
#include <binder/ParcelFileDescriptor.h>
#include <binder/Status.h>
#include <openssl/sha.h>
#include <sys/stat.h>
#include <uuid/uuid.h>
#include <zlib.h>
@@ -242,20 +241,7 @@ IncrementalService::IncrementalService(ServiceManagerWrapper&& sm, std::string_v
}
FileId IncrementalService::idFromMetadata(std::span<const uint8_t> metadata) {
incfs::FileId id = {};
if (size_t(metadata.size()) <= sizeof(id)) {
memcpy(&id, metadata.data(), metadata.size());
} else {
uint8_t buffer[SHA_DIGEST_LENGTH];
static_assert(sizeof(buffer) >= sizeof(id));
SHA_CTX ctx;
SHA1_Init(&ctx);
SHA1_Update(&ctx, metadata.data(), metadata.size());
SHA1_Final(buffer, &ctx);
memcpy(&id, buffer, sizeof(id));
}
return id;
return IncFs_FileIdFromMetadata({(const char*)metadata.data(), metadata.size()});
}
IncrementalService::~IncrementalService() = default;
@@ -344,7 +330,7 @@ std::optional<std::future<void>> IncrementalService::onSystemReady() {
std::thread([this, mounts = std::move(mounts)]() {
std::vector<IfsMountPtr> failedLoaderMounts;
for (auto&& ifs : mounts) {
if (prepareDataLoader(*ifs, nullptr)) {
if (prepareDataLoader(*ifs)) {
LOG(INFO) << "Successfully started data loader for mount " << ifs->mountId;
} else {
LOG(WARNING) << "Failed to start data loader for mount " << ifs->mountId;
@@ -377,6 +363,7 @@ auto IncrementalService::getStorageSlotLocked() -> MountMap::iterator {
StorageId IncrementalService::createStorage(std::string_view mountPoint,
DataLoaderParamsParcel&& dataLoaderParams,
const DataLoaderStatusListener& dataLoaderStatusListener,
CreateOptions options) {
LOG(INFO) << "createStorage: " << mountPoint << " | " << int(options);
if (!path::isAbsolute(mountPoint)) {
@@ -509,7 +496,7 @@ StorageId IncrementalService::createStorage(std::string_view mountPoint,
// Done here as well, all data structures are in good state.
secondCleanupOnFailure.release();
if (!prepareDataLoader(*ifs, &dataLoaderParams)) {
if (!prepareDataLoader(*ifs, &dataLoaderParams, &dataLoaderStatusListener)) {
LOG(ERROR) << "prepareDataLoader() failed";
deleteStorageLocked(*ifs, std::move(l));
return kInvalidStorageId;
@@ -767,7 +754,6 @@ int IncrementalService::makeFile(StorageId storage, std::string_view path, int m
if (params.metadata.data && params.metadata.size > 0) {
metadataBytes.assign(params.metadata.data, params.metadata.data + params.metadata.size);
}
mIncrementalManager->newFileForDataLoader(ifs->mountId, id, metadataBytes);
return 0;
}
return -EINVAL;
@@ -1074,7 +1060,8 @@ bool IncrementalService::mountExistingImage(std::string_view root, std::string_v
}
bool IncrementalService::prepareDataLoader(IncrementalService::IncFsMount& ifs,
DataLoaderParamsParcel* params) {
DataLoaderParamsParcel* params,
const DataLoaderStatusListener* externalListener) {
if (!mSystemReady.load(std::memory_order_relaxed)) {
std::unique_lock l(ifs.lock);
if (params) {
@@ -1117,7 +1104,7 @@ bool IncrementalService::prepareDataLoader(IncrementalService::IncFsMount& ifs,
fsControlParcel.incremental->pendingReads.reset(
base::unique_fd(::dup(ifs.control.pendingReads)));
fsControlParcel.incremental->log.reset(base::unique_fd(::dup(ifs.control.logs)));
sp<IncrementalDataLoaderListener> listener = new IncrementalDataLoaderListener(*this);
sp<IncrementalDataLoaderListener> listener = new IncrementalDataLoaderListener(*this, *externalListener);
bool created = false;
auto status = mIncrementalManager->prepareDataLoader(ifs.mountId, fsControlParcel, *dlp,
listener, &created);
@@ -1247,6 +1234,11 @@ bool IncrementalService::configureNativeBinaries(StorageId storage, std::string_
binder::Status IncrementalService::IncrementalDataLoaderListener::onStatusChanged(MountId mountId,
int newStatus) {
if (externalListener) {
// Give an external listener a chance to act before we destroy something.
externalListener->onStatusChanged(mountId, newStatus);
}
std::unique_lock l(incrementalService.mLock);
const auto& ifs = incrementalService.getIfsLocked(mountId);
if (!ifs) {
@@ -1288,6 +1280,12 @@ binder::Status IncrementalService::IncrementalDataLoaderListener::onStatusChange
case IDataLoaderStatusListener::DATA_LOADER_STOPPED: {
break;
}
case IDataLoaderStatusListener::DATA_LOADER_IMAGE_READY: {
break;
}
case IDataLoaderStatusListener::DATA_LOADER_IMAGE_NOT_READY: {
break;
}
default: {
LOG(WARNING) << "Unknown data loader status: " << newStatus
<< " for mount: " << mountId;

View File

@@ -59,6 +59,8 @@ using Clock = std::chrono::steady_clock;
using TimePoint = std::chrono::time_point<Clock>;
using Seconds = std::chrono::seconds;
using DataLoaderStatusListener = ::android::sp<::android::content::pm::IDataLoaderStatusListener>;
class IncrementalService final {
public:
explicit IncrementalService(ServiceManagerWrapper&& sm, std::string_view rootDir);
@@ -94,7 +96,9 @@ public:
std::optional<std::future<void>> onSystemReady();
StorageId createStorage(std::string_view mountPoint, DataLoaderParamsParcel&& dataLoaderParams,
StorageId createStorage(std::string_view mountPoint,
DataLoaderParamsParcel&& dataLoaderParams,
const DataLoaderStatusListener& dataLoaderStatusListener,
CreateOptions options = CreateOptions::Default);
StorageId createLinkedStorage(std::string_view mountPoint, StorageId linkedStorage,
CreateOptions options = CreateOptions::Default);
@@ -129,13 +133,14 @@ public:
std::string_view libDirRelativePath, std::string_view abi);
class IncrementalDataLoaderListener : public android::content::pm::BnDataLoaderStatusListener {
public:
IncrementalDataLoaderListener(IncrementalService& incrementalService)
: incrementalService(incrementalService) {}
IncrementalDataLoaderListener(IncrementalService& incrementalService, DataLoaderStatusListener externalListener)
: incrementalService(incrementalService), externalListener(externalListener) {}
// Callbacks interface
binder::Status onStatusChanged(MountId mount, int newStatus) override;
private:
IncrementalService& incrementalService;
DataLoaderStatusListener externalListener;
};
private:
@@ -201,7 +206,7 @@ private:
std::string&& source, std::string&& target, BindKind kind,
std::unique_lock<std::mutex>& mainLock);
bool prepareDataLoader(IncFsMount& ifs, DataLoaderParamsParcel* params);
bool prepareDataLoader(IncFsMount& ifs, DataLoaderParamsParcel* params = nullptr, const DataLoaderStatusListener* externalListener = nullptr);
BindPathMap::const_iterator findStorageLocked(std::string_view path) const;
StorageId findStorageId(std::string_view path) const;

View File

@@ -60,8 +60,6 @@ public:
bool* _aidl_return) const = 0;
virtual binder::Status startDataLoader(MountId mountId, bool* _aidl_return) const = 0;
virtual binder::Status destroyDataLoader(MountId mountId) const = 0;
virtual binder::Status newFileForDataLoader(MountId mountId, FileId fileid,
const std::vector<uint8_t>& metadata) const = 0;
virtual binder::Status showHealthBlockedUI(MountId mountId) const = 0;
};
@@ -128,13 +126,6 @@ public:
binder::Status destroyDataLoader(MountId mountId) const override {
return mInterface->destroyDataLoader(mountId);
}
binder::Status newFileForDataLoader(MountId mountId, FileId fileid,
const std::vector<uint8_t>& metadata) const override {
return mInterface->newFileForDataLoader(mountId,
{(const uint8_t*)fileid.data,
(const uint8_t*)fileid.data + sizeof(fileid.data)},
metadata);
}
binder::Status showHealthBlockedUI(MountId mountId) const override {
return mInterface->showHealthBlockedUI(mountId);
}

View File

@@ -293,7 +293,7 @@ TEST_F(IncrementalServiceTest, testCreateStorageMountIncFsFails) {
EXPECT_CALL(*mIncrementalManager, prepareDataLoader(_, _, _, _, _)).Times(0);
TemporaryDir tempDir;
int storageId =
mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {},
IncrementalService::CreateOptions::CreateNew);
ASSERT_LT(storageId, 0);
}
@@ -303,7 +303,7 @@ TEST_F(IncrementalServiceTest, testCreateStorageMountIncFsInvalidControlParcel)
EXPECT_CALL(*mIncrementalManager, prepareDataLoader(_, _, _, _, _)).Times(0);
TemporaryDir tempDir;
int storageId =
mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {},
IncrementalService::CreateOptions::CreateNew);
ASSERT_LT(storageId, 0);
}
@@ -316,7 +316,7 @@ TEST_F(IncrementalServiceTest, testCreateStorageMakeFileFails) {
EXPECT_CALL(*mVold, unmountIncFs(_));
TemporaryDir tempDir;
int storageId =
mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {},
IncrementalService::CreateOptions::CreateNew);
ASSERT_LT(storageId, 0);
}
@@ -330,7 +330,7 @@ TEST_F(IncrementalServiceTest, testCreateStorageBindMountFails) {
EXPECT_CALL(*mVold, unmountIncFs(_));
TemporaryDir tempDir;
int storageId =
mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {},
IncrementalService::CreateOptions::CreateNew);
ASSERT_LT(storageId, 0);
}
@@ -344,7 +344,7 @@ TEST_F(IncrementalServiceTest, testCreateStoragePrepareDataLoaderFails) {
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
TemporaryDir tempDir;
int storageId =
mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {},
IncrementalService::CreateOptions::CreateNew);
ASSERT_LT(storageId, 0);
}
@@ -358,7 +358,7 @@ TEST_F(IncrementalServiceTest, testDeleteStorageSuccess) {
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
TemporaryDir tempDir;
int storageId =
mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {},
IncrementalService::CreateOptions::CreateNew);
ASSERT_GE(storageId, 0);
mIncrementalService->deleteStorage(storageId);
@@ -373,7 +373,7 @@ TEST_F(IncrementalServiceTest, testOnStatusNotReady) {
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
TemporaryDir tempDir;
int storageId =
mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {},
IncrementalService::CreateOptions::CreateNew);
ASSERT_GE(storageId, 0);
mIncrementalManager->setDataLoaderStatusNotReady();
@@ -389,7 +389,7 @@ TEST_F(IncrementalServiceTest, testStartDataLoaderSuccess) {
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
TemporaryDir tempDir;
int storageId =
mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {},
IncrementalService::CreateOptions::CreateNew);
ASSERT_GE(storageId, 0);
mIncrementalManager->setDataLoaderStatusReady();
@@ -404,7 +404,7 @@ TEST_F(IncrementalServiceTest, testMakeDirectory) {
mIncrementalManager->startDataLoaderSuccess();
TemporaryDir tempDir;
int storageId =
mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {},
IncrementalService::CreateOptions::CreateNew);
std::string dir_path("test");
@@ -428,7 +428,7 @@ TEST_F(IncrementalServiceTest, testMakeDirectories) {
mIncrementalManager->startDataLoaderSuccess();
TemporaryDir tempDir;
int storageId =
mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {},
IncrementalService::CreateOptions::CreateNew);
auto first = "first"sv;
auto second = "second"sv;