[incremental] native implementation of Incremental Service

The implementation of IIncrementalManager.aidl. TODO to refactor this.

Test: atest service.incremental_test
Change-Id: Ib8c8a9c0e7f0289b4bcd8961fa39746ed12b4310
This commit is contained in:
Songchun Fan
2019-11-29 14:23:45 -08:00
parent 73b4b64be8
commit 3c82a306cd
22 changed files with 2892 additions and 4 deletions

View File

@@ -328,7 +328,9 @@ public final class IncrementalManager {
* Checks if path is mounted on Incremental File System.
*/
public static boolean isIncrementalPath(@NonNull String path) {
// TODO(b/136132412): add jni implementation
return false;
return nativeIsIncrementalPath(path);
}
/* Native methods */
private static native boolean nativeIsIncrementalPath(@NonNull String path);
}

View File

@@ -141,6 +141,7 @@ cc_library_shared {
"android_os_UEventObserver.cpp",
"android_os_VintfObject.cpp",
"android_os_VintfRuntimeInfo.cpp",
"android_os_incremental_IncrementalManager.cpp",
"android_net_LocalSocketImpl.cpp",
"android_net_NetUtils.cpp",
"android_service_DataLoaderService.cpp",

View File

@@ -152,6 +152,7 @@ extern int register_android_os_HidlMemory(JNIEnv* env);
extern int register_android_os_MemoryFile(JNIEnv* env);
extern int register_android_os_SharedMemory(JNIEnv* env);
extern int register_android_service_DataLoaderService(JNIEnv* env);
extern int register_android_os_incremental_IncrementalManager(JNIEnv* env);
extern int register_android_net_LocalSocketImpl(JNIEnv* env);
extern int register_android_net_NetworkUtils(JNIEnv* env);
extern int register_android_text_AndroidCharacter(JNIEnv *env);
@@ -1496,6 +1497,7 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_net_NetworkUtils),
REG_JNI(register_android_os_MemoryFile),
REG_JNI(register_android_os_SharedMemory),
REG_JNI(register_android_os_incremental_IncrementalManager),
REG_JNI(register_com_android_internal_os_ClassLoaderFactory),
REG_JNI(register_com_android_internal_os_Zygote),
REG_JNI(register_com_android_internal_os_ZygoteInit),

View File

@@ -0,0 +1,46 @@
/*
* 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 LOG_TAG "incremental_manager-jni"
#include "core_jni_helpers.h"
#include "incfs_ndk.h"
#include "jni.h"
#include "nativehelper/JNIHelp.h"
#include <iterator>
#include <memory>
namespace android {
static jboolean nativeIsIncrementalPath(JNIEnv* env,
jobject clazz,
jstring javaPath) {
ScopedUtfChars path(env, javaPath);
return (jboolean)IncFs_IsIncFsPath(path.c_str());
}
static const JNINativeMethod method_table[] = {
{"nativeIsIncrementalPath", "(Ljava/lang/String;)Z",
(void*)nativeIsIncrementalPath},
};
int register_android_os_incremental_IncrementalManager(JNIEnv* env) {
return jniRegisterNativeMethods(env, "android/os/incremental/IncrementalManager",
method_table, std::size(method_table));
}
} // namespace android

View File

@@ -67,13 +67,14 @@ public class IncrementalManagerService extends IIncrementalManager.Stub {
mDataLoaderManager = mContext.getSystemService(DataLoaderManager.class);
ServiceManager.addService(BINDER_SERVICE_NAME, this);
// Starts and register IIncrementalManagerNative service
// TODO(b/136132412): add jni implementation
mNativeInstance = nativeStartService();
}
/**
* Notifies native IIncrementalManager service that system is ready.
*/
public void systemReady() {
// TODO(b/136132412): add jni implementation
nativeSystemReady(mNativeInstance);
}
/**
@@ -152,4 +153,8 @@ public class IncrementalManagerService extends IIncrementalManager.Stub {
(new IncrementalManagerShellCommand(mContext)).exec(
this, in, out, err, args, callback, resultReceiver);
}
private static native long nativeStartService();
private static native void nativeSystemReady(long nativeInstance);
}

View File

@@ -52,6 +52,7 @@ cc_library_static {
"com_android_server_GraphicsStatsService.cpp",
"com_android_server_am_AppCompactor.cpp",
"com_android_server_am_LowMemDetector.cpp",
"com_android_server_incremental_IncrementalManagerService.cpp",
"onload.cpp",
":lib_networkStatsFactory_native",
],
@@ -145,6 +146,7 @@ cc_defaults {
"android.frameworks.schedulerservice@1.0",
"android.frameworks.sensorservice@1.0",
"android.system.suspend@1.0",
"service.incremental",
"suspend_control_aidl_interface-cpp",
"vintf-vibrator-cpp",
],

View File

@@ -0,0 +1,47 @@
/*
* 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 LOG_TAG "incremental_manager_service-jni"
#include "incremental_service.h"
#include "jni.h"
#include <memory>
#include <nativehelper/JNIHelp.h>
namespace android {
static jlong nativeStartService(JNIEnv* env, jclass klass, jobject self) {
return Incremental_IncrementalService_Start();
}
static void nativeSystemReady(JNIEnv* env, jclass klass, jlong self) {
Incremental_IncrementalService_OnSystemReady(self);
}
static const JNINativeMethod method_table[] = {
{"nativeStartService", "()J", (void*)nativeStartService},
{"nativeSystemReady", "(J)V", (void*)nativeSystemReady},
};
int register_android_server_incremental_IncrementalManagerService(JNIEnv* env) {
return jniRegisterNativeMethods(env,
"com/android/server/incremental/IncrementalManagerService",
method_table, std::size(method_table));
}
} // namespace android

View File

@@ -58,6 +58,7 @@ int register_android_server_am_AppCompactor(JNIEnv* env);
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);
};
using namespace android;
@@ -109,5 +110,6 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
register_android_server_am_LowMemDetector(env);
register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(
env);
register_android_server_incremental_IncrementalManagerService(env);
return JNI_VERSION_1_4;
}

View File

@@ -0,0 +1,110 @@
// 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_defaults {
name: "service.incremental-proto-defaults",
cpp_std: "c++2a",
proto: {
type: "lite",
},
}
cc_defaults {
name: "service.incremental-defaults",
defaults: ["service.incremental-proto-defaults"],
local_include_dirs: ["include/"],
cflags: [
"-Wall",
"-Werror",
"-Wextra",
"-Wno-unused-parameter",
],
static_libs: [
"libbase",
"libext2_uuid",
"libdataloader_aidl-cpp",
"libincremental_aidl-cpp",
"libincremental_manager_aidl-cpp",
"libnativehelper",
"libprotobuf-cpp-lite",
"service.incremental.proto",
"libutils",
"libvold_binder",
],
shared_libs: [
"libandroidfw",
"libbinder",
"libincfs",
"liblog",
"libz",
"libziparchive",
],
}
filegroup {
name: "service.incremental_srcs",
srcs: [
"incremental_service.c",
"IncrementalService.cpp",
"BinderIncrementalService.cpp",
"path.cpp",
"ServiceWrappers.cpp",
],
}
cc_library {
name: "service.incremental",
defaults: [
"service.incremental-defaults",
"linux_bionic_supported",
],
export_include_dirs: ["include/",],
srcs: [
":service.incremental_srcs",
],
}
cc_library_headers {
name: "service.incremental_headers",
export_include_dirs: ["include/",],
}
cc_library_static {
name: "service.incremental.proto",
defaults: ["service.incremental-proto-defaults"],
proto: {
export_proto_headers: true,
},
srcs: [
"Metadata.proto",
],
}
cc_test {
name: "service.incremental_test",
defaults: ["service.incremental-defaults"],
test_suites: ["device-tests"],
srcs: [
":service.incremental_srcs",
"test/IncrementalServiceTest.cpp",
"test/path_test.cpp",
],
static_libs: [
"libgmock",
]
}

View File

@@ -0,0 +1,230 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "BinderIncrementalService.h"
#include <binder/IResultReceiver.h>
#include <incfs.h>
#include "ServiceWrappers.h"
#include "jni.h"
#include "nativehelper/JNIHelp.h"
#include "path.h"
using namespace std::literals;
using namespace android::incremental;
namespace android::os::incremental {
static constexpr auto kAndroidDataEnv = "ANDROID_DATA"sv;
static constexpr auto kDataDir = "/data"sv;
static constexpr auto kIncrementalSubDir = "incremental"sv;
static std::string getIncrementalDir() {
const char* dataDir = getenv(kAndroidDataEnv.data());
if (!dataDir || !*dataDir) {
dataDir = kDataDir.data();
}
return path::normalize(path::join(dataDir, kIncrementalSubDir));
}
static bool incFsEnabled() {
// TODO(b/136132412): use vold to check /sys/fs/incfs/version (per selinux compliance)
return incfs::enabled();
}
static bool incFsVersionValid(const sp<IVold>& vold) {
int version = -1;
auto status = vold->incFsVersion(&version);
if (!status.isOk() || version <= 0) {
return false;
}
return true;
}
BinderIncrementalService::BinderIncrementalService(const sp<IServiceManager>& sm)
: mImpl(RealServiceManager(sm), getIncrementalDir()) {}
BinderIncrementalService* BinderIncrementalService::start() {
if (!incFsEnabled()) {
return nullptr;
}
IPCThreadState::self()->disableBackgroundScheduling(true);
sp<IServiceManager> sm(defaultServiceManager());
if (!sm) {
return nullptr;
}
sp<IBinder> voldBinder(sm->getService(String16("vold")));
if (voldBinder == nullptr) {
return nullptr;
}
sp<IVold> vold = interface_cast<IVold>(voldBinder);
if (!incFsVersionValid(vold)) {
return nullptr;
}
sp<BinderIncrementalService> self(new BinderIncrementalService(sm));
status_t ret = sm->addService(String16{getServiceName()}, self);
if (ret != android::OK) {
return nullptr;
}
sp<ProcessState> ps(ProcessState::self());
ps->startThreadPool();
ps->giveThreadPoolName();
return self.get();
}
status_t BinderIncrementalService::dump(int fd, const Vector<String16>& args) {
return OK;
}
void BinderIncrementalService::onSystemReady() {
mImpl.onSystemReady();
}
static binder::Status ok() {
return binder::Status::ok();
}
binder::Status BinderIncrementalService::openStorage(const std::string& path,
int32_t* _aidl_return) {
*_aidl_return = mImpl.openStorage(path);
return ok();
}
binder::Status BinderIncrementalService::createStorage(
const std::string& path, const DataLoaderParamsParcel& params,
int32_t createMode, int32_t* _aidl_return) {
*_aidl_return =
mImpl.createStorage(path, const_cast<DataLoaderParamsParcel&&>(params),
android::incremental::IncrementalService::CreateOptions(
createMode));
return ok();
}
binder::Status BinderIncrementalService::createLinkedStorage(const std::string& path,
int32_t otherStorageId,
int32_t createMode,
int32_t* _aidl_return) {
*_aidl_return =
mImpl.createLinkedStorage(path, otherStorageId,
android::incremental::IncrementalService::CreateOptions(
createMode));
return ok();
}
binder::Status BinderIncrementalService::makeBindMount(int32_t storageId,
const std::string& pathUnderStorage,
const std::string& targetFullPath,
int32_t bindType, int32_t* _aidl_return) {
*_aidl_return = mImpl.bind(storageId, pathUnderStorage, targetFullPath,
android::incremental::IncrementalService::BindKind(bindType));
return ok();
}
binder::Status BinderIncrementalService::deleteBindMount(int32_t storageId,
const std::string& targetFullPath,
int32_t* _aidl_return) {
*_aidl_return = mImpl.unbind(storageId, targetFullPath);
return ok();
}
binder::Status BinderIncrementalService::deleteStorage(int32_t storageId) {
mImpl.deleteStorage(storageId);
return ok();
}
binder::Status BinderIncrementalService::makeDirectory(int32_t storageId,
const std::string& pathUnderStorage,
int32_t* _aidl_return) {
auto inode = mImpl.makeDir(storageId, pathUnderStorage);
*_aidl_return = inode < 0 ? inode : 0;
return ok();
}
binder::Status BinderIncrementalService::makeDirectories(int32_t storageId,
const std::string& pathUnderStorage,
int32_t* _aidl_return) {
auto inode = mImpl.makeDirs(storageId, pathUnderStorage);
*_aidl_return = inode < 0 ? inode : 0;
return ok();
}
binder::Status BinderIncrementalService::makeFile(int32_t storageId,
const std::string& pathUnderStorage, int64_t size,
const std::vector<uint8_t>& metadata,
int32_t* _aidl_return) {
auto inode = mImpl.makeFile(storageId, pathUnderStorage, size,
{(const char*)metadata.data(), metadata.size()}, {});
*_aidl_return = inode < 0 ? inode : 0;
return ok();
}
binder::Status BinderIncrementalService::makeFileFromRange(
int32_t storageId, const std::string& pathUnderStorage,
const std::string& sourcePathUnderStorage, int64_t start, int64_t end,
int32_t* _aidl_return) {
// TODO(b/136132412): implement this
*_aidl_return = -1;
return ok();
}
binder::Status BinderIncrementalService::makeLink(int32_t sourceStorageId,
const std::string& relativeSourcePath,
int32_t destStorageId,
const std::string& relativeDestPath,
int32_t* _aidl_return) {
auto sourceInode = mImpl.nodeFor(sourceStorageId, relativeSourcePath);
auto [targetParentInode, name] = mImpl.parentAndNameFor(destStorageId, relativeDestPath);
*_aidl_return = mImpl.link(sourceStorageId, sourceInode, targetParentInode, name);
return ok();
}
binder::Status BinderIncrementalService::unlink(int32_t storageId,
const std::string& pathUnderStorage,
int32_t* _aidl_return) {
auto [parentNode, name] = mImpl.parentAndNameFor(storageId, pathUnderStorage);
*_aidl_return = mImpl.unlink(storageId, parentNode, name);
return ok();
}
binder::Status BinderIncrementalService::isFileRangeLoaded(int32_t storageId,
const std::string& relativePath,
int64_t start, int64_t end,
bool* _aidl_return) {
*_aidl_return = false;
return ok();
}
binder::Status BinderIncrementalService::getFileMetadata(int32_t storageId,
const std::string& relativePath,
std::vector<uint8_t>* _aidl_return) {
auto inode = mImpl.nodeFor(storageId, relativePath);
auto metadata = mImpl.getMetadata(storageId, inode);
_aidl_return->assign(metadata.begin(), metadata.end());
return ok();
}
binder::Status BinderIncrementalService::startLoading(int32_t storageId, bool* _aidl_return) {
*_aidl_return = mImpl.startLoading(storageId);
return ok();
}
} // namespace android::os::incremental
jlong Incremental_IncrementalService_Start() {
return (jlong)android::os::incremental::BinderIncrementalService::start();
}
void Incremental_IncrementalService_OnSystemReady(jlong self) {
if (self) {
((android::os::incremental::BinderIncrementalService*)self)->onSystemReady();
}
}

View File

@@ -0,0 +1,77 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <binder/BinderService.h>
#include <binder/IServiceManager.h>
#include "IncrementalService.h"
#include "android/os/incremental/BnIncrementalManagerNative.h"
#include "incremental_service.h"
namespace android::os::incremental {
class BinderIncrementalService : public BnIncrementalManagerNative,
public BinderService<BinderIncrementalService> {
public:
BinderIncrementalService(const sp<IServiceManager> &sm);
static BinderIncrementalService *start();
static const char16_t *getServiceName() { return u"incremental_service"; }
status_t dump(int fd, const Vector<String16> &args) final;
void onSystemReady();
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 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 &pathUnderStorage,
const std::string &targetFullPath, int32_t bindType,
int32_t *_aidl_return) final;
binder::Status deleteBindMount(int32_t storageId, const std::string &targetFullPath,
int32_t *_aidl_return) final;
binder::Status deleteStorage(int32_t storageId) final;
binder::Status makeDirectory(int32_t storageId, const std::string &pathUnderStorage,
int32_t *_aidl_return) final;
binder::Status makeDirectories(int32_t storageId, const std::string &pathUnderStorage,
int32_t *_aidl_return) final;
binder::Status makeFile(int32_t storageId, const std::string &pathUnderStorage, int64_t size,
const std::vector<uint8_t> &metadata, int32_t *_aidl_return) final;
binder::Status makeFileFromRange(int32_t storageId, const std::string &pathUnderStorage,
const std::string &sourcePathUnderStorage, int64_t start,
int64_t end, int32_t *_aidl_return);
binder::Status makeLink(int32_t sourceStorageId, const std::string &relativeSourcePath,
int32_t destStorageId, const std::string &relativeDestPath,
int32_t *_aidl_return) final;
binder::Status unlink(int32_t storageId, const std::string &pathUnderStorage,
int32_t *_aidl_return) final;
binder::Status isFileRangeLoaded(int32_t storageId, const std::string &relativePath,
int64_t start, int64_t end, bool *_aidl_return) final;
binder::Status getFileMetadata(int32_t storageId, const std::string &relativePath,
std::vector<uint8_t> *_aidl_return) final;
binder::Status startLoading(int32_t storageId, bool *_aidl_return) final;
private:
android::incremental::IncrementalService mImpl;
};
} // namespace android::os::incremental

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,231 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <android/os/incremental/IIncrementalManager.h>
#include <android/content/pm/DataLoaderParamsParcel.h>
#include <binder/IServiceManager.h>
#include <utils/String16.h>
#include <utils/StrongPointer.h>
#include <utils/Vector.h>
#include <atomic>
#include <chrono>
#include <future>
#include <limits>
#include <map>
#include <mutex>
#include <string>
#include <string_view>
#include <unordered_map>
#include <utility>
#include <vector>
#include "ServiceWrappers.h"
#include "android/content/pm/BnDataLoaderStatusListener.h"
#include "incfs.h"
#include "path.h"
using namespace android::os::incremental;
namespace android::os {
class IVold;
}
namespace android::incremental {
using MountId = int;
using StorageId = int;
using Inode = incfs::Inode;
using BlockIndex = incfs::BlockIndex;
using RawMetadata = incfs::RawMetadata;
using Clock = std::chrono::steady_clock;
using TimePoint = std::chrono::time_point<Clock>;
using Seconds = std::chrono::seconds;
class IncrementalService {
public:
explicit IncrementalService(const ServiceManagerWrapper& sm, std::string_view rootDir);
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
~IncrementalService();
#pragma GCC diagnostic pop
static constexpr StorageId kInvalidStorageId = -1;
static constexpr StorageId kMaxStorageId = std::numeric_limits<int>::max();
enum CreateOptions {
TemporaryBind = 1,
PermanentBind = 2,
CreateNew = 4,
OpenExisting = 8,
Default = TemporaryBind | CreateNew
};
enum class BindKind {
Temporary = 0,
Permanent = 1,
};
std::optional<std::future<void>> onSystemReady();
StorageId createStorage(std::string_view mountPoint,
DataLoaderParamsParcel&& dataLoaderParams,
CreateOptions options = CreateOptions::Default);
StorageId createLinkedStorage(std::string_view mountPoint, StorageId linkedStorage,
CreateOptions options = CreateOptions::Default);
StorageId openStorage(std::string_view pathInMount);
Inode nodeFor(StorageId storage, std::string_view subpath) const;
std::pair<Inode, std::string_view> parentAndNameFor(StorageId storage,
std::string_view subpath) const;
int bind(StorageId storage, std::string_view subdir, std::string_view target, BindKind kind);
int unbind(StorageId storage, std::string_view target);
void deleteStorage(StorageId storage);
Inode makeFile(StorageId storage, std::string_view name, long size, std::string_view metadata,
std::string_view signature);
Inode makeDir(StorageId storage, std::string_view name, std::string_view metadata = {});
Inode makeDirs(StorageId storage, std::string_view name, std::string_view metadata = {});
int link(StorageId storage, Inode item, Inode newParent, std::string_view newName);
int unlink(StorageId storage, Inode parent, std::string_view name);
bool isRangeLoaded(StorageId storage, Inode file, std::pair<BlockIndex, BlockIndex> range) {
return false;
}
RawMetadata getMetadata(StorageId storage, Inode node) const;
std::string getSigngatureData(StorageId storage, Inode node) const { return {}; }
std::vector<std::string> listFiles(StorageId storage) const;
bool startLoading(StorageId storage) const;
class IncrementalDataLoaderListener : public android::content::pm::BnDataLoaderStatusListener {
public:
IncrementalDataLoaderListener(IncrementalService& incrementalService)
: incrementalService(incrementalService) {}
// Callbacks interface
binder::Status onStatusChanged(MountId mount, int newStatus) override;
private:
IncrementalService& incrementalService;
};
private:
struct IncFsMount {
struct Bind {
StorageId storage;
std::string savedFilename;
std::string sourceDir;
BindKind kind;
};
struct Storage {
std::string name;
Inode node;
};
struct Control {
operator IncFsControl() const { return {cmdFd, logFd}; }
void reset() {
cmdFd.reset();
logFd.reset();
}
base::unique_fd cmdFd;
base::unique_fd logFd;
};
using BindMap = std::map<std::string, Bind>;
using StorageMap = std::unordered_map<StorageId, Storage>;
mutable std::mutex lock;
const std::string root;
Control control;
/*const*/ MountId mountId;
StorageMap storages;
BindMap bindPoints;
std::optional<DataLoaderParamsParcel> savedDataLoaderParams;
std::atomic<int> nextStorageDirNo{0};
std::atomic<int> dataLoaderStatus = -1;
std::condition_variable dataLoaderReady;
TimePoint connectionLostTime = TimePoint();
const IncrementalService& incrementalService;
IncFsMount(std::string root, MountId mountId, Control control,
const IncrementalService& incrementalService)
: root(std::move(root)),
control(std::move(control)),
mountId(mountId),
incrementalService(incrementalService) {}
IncFsMount(IncFsMount&&) = delete;
IncFsMount& operator=(IncFsMount&&) = delete;
~IncFsMount();
StorageMap::iterator makeStorage(StorageId id);
static void cleanupFilesystem(std::string_view root);
};
using IfsMountPtr = std::shared_ptr<IncFsMount>;
using MountMap = std::unordered_map<MountId, IfsMountPtr>;
using BindPathMap = std::map<std::string, IncFsMount::BindMap::iterator, path::PathLess>;
void mountExistingImages();
bool mountExistingImage(std::string_view root, std::string_view key);
IfsMountPtr getIfs(StorageId storage) const;
const IfsMountPtr& getIfsLocked(StorageId storage) const;
int addBindMount(IncFsMount& ifs, StorageId storage, std::string&& sourceSubdir,
std::string&& target, BindKind kind, std::unique_lock<std::mutex>& mainLock);
int addBindMountWithMd(IncFsMount& ifs, StorageId storage, std::string&& metadataName,
std::string&& sourceSubdir, std::string&& target, BindKind kind,
std::unique_lock<std::mutex>& mainLock);
bool prepareDataLoader(IncFsMount& ifs, DataLoaderParamsParcel* params);
BindPathMap::const_iterator findStorageLocked(std::string_view path) const;
StorageId findStorageId(std::string_view path) const;
void deleteStorage(IncFsMount& ifs);
void deleteStorageLocked(IncFsMount& ifs, std::unique_lock<std::mutex>&& ifsLock);
MountMap::iterator getStorageSlotLocked();
// Member variables
// These are shared pointers for the sake of unit testing
std::shared_ptr<VoldServiceWrapper> mVold;
std::shared_ptr<IncrementalManagerWrapper> mIncrementalManager;
std::shared_ptr<IncFsWrapper> mIncFs;
const std::string mIncrementalDir;
mutable std::mutex mLock;
mutable std::mutex mMountOperationLock;
MountMap mMounts;
BindPathMap mBindsByPath;
std::atomic_bool mSystemReady = false;
StorageId mNextId = 0;
std::promise<void> mPrepareDataLoaders;
};
} // namespace android::incremental

View File

@@ -0,0 +1,23 @@
syntax = "proto3";
package android.incremental.metadata;
message BindPoint {
int32 storage_id = 1;
string source_subdir = 2;
string dest_path = 3;
}
message DataLoader {
string package_name = 1;
string arguments = 2;
}
message Storage {
int32 id = 1;
}
message Mount {
Storage storage = 1;
DataLoader loader = 2;
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ServiceWrappers.h"
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <binder/IServiceManager.h>
#include <utils/String16.h>
#include <string>
#include <string_view>
using namespace std::literals;
namespace android::os::incremental {
static constexpr auto kVoldServiceName = "vold"sv;
static constexpr auto kIncrementalManagerName = "incremental"sv;
RealServiceManager::RealServiceManager(const sp<IServiceManager>& serviceManager)
: mServiceManager(serviceManager) {}
template <class INTERFACE>
sp<INTERFACE> RealServiceManager::getRealService(std::string_view serviceName) const {
sp<IBinder> binder = mServiceManager->getService(String16(serviceName.data()));
if (binder == 0) {
return 0;
}
return interface_cast<INTERFACE>(binder);
}
std::shared_ptr<VoldServiceWrapper> RealServiceManager::getVoldService() const {
sp<os::IVold> vold = RealServiceManager::getRealService<os::IVold>(kVoldServiceName);
if (vold != 0) {
return std::make_shared<RealVoldService>(vold);
}
return nullptr;
}
std::shared_ptr<IncrementalManagerWrapper> RealServiceManager::getIncrementalManager() const {
sp<IIncrementalManager> manager =
RealServiceManager::getRealService<IIncrementalManager>(kIncrementalManagerName);
if (manager != 0) {
return std::make_shared<RealIncrementalManager>(manager);
}
return nullptr;
}
std::shared_ptr<IncFsWrapper> RealServiceManager::getIncFs() const {
return std::make_shared<RealIncFs>();
}
} // namespace android::os::incremental

View File

@@ -0,0 +1,183 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <android/content/pm/DataLoaderParamsParcel.h>
#include <android/content/pm/FileSystemControlParcel.h>
#include <android/content/pm/IDataLoaderStatusListener.h>
#include <android/os/IVold.h>
#include <android/os/incremental/IIncrementalManager.h>
#include <binder/IServiceManager.h>
#include <incfs.h>
#include <string>
#include <string_view>
using namespace android::incfs;
using namespace android::content::pm;
namespace android::os::incremental {
// --- Wrapper interfaces ---
class VoldServiceWrapper {
public:
virtual ~VoldServiceWrapper(){};
virtual binder::Status mountIncFs(const std::string& imagePath, const std::string& targetDir,
int32_t flags,
IncrementalFileSystemControlParcel* _aidl_return) const = 0;
virtual binder::Status unmountIncFs(const std::string& dir) const = 0;
virtual binder::Status bindMount(const std::string& sourceDir,
const std::string& targetDir) const = 0;
};
class IncrementalManagerWrapper {
public:
virtual ~IncrementalManagerWrapper() {}
virtual binder::Status prepareDataLoader(
int32_t mountId, const FileSystemControlParcel& control,
const DataLoaderParamsParcel& params,
const sp<IDataLoaderStatusListener>& listener,
bool* _aidl_return) const = 0;
virtual binder::Status startDataLoader(int32_t mountId, bool* _aidl_return) const = 0;
virtual binder::Status destroyDataLoader(int32_t mountId) const = 0;
virtual binder::Status newFileForDataLoader(int32_t mountId, int64_t inode,
const ::std::vector<uint8_t>& metadata) const = 0;
virtual binder::Status showHealthBlockedUI(int32_t mountId) const = 0;
};
class IncFsWrapper {
public:
virtual ~IncFsWrapper() {}
virtual Inode makeFile(Control control, std::string_view name, Inode parent, Size size,
std::string_view metadata) const = 0;
virtual Inode makeDir(Control control, std::string_view name, Inode parent,
std::string_view metadata, int mode = 0555) const = 0;
virtual RawMetadata getMetadata(Control control, Inode inode) const = 0;
virtual ErrorCode link(Control control, Inode item, Inode targetParent,
std::string_view name) const = 0;
virtual ErrorCode unlink(Control control, Inode parent, std::string_view name) const = 0;
virtual ErrorCode writeBlocks(Control control, const incfs_new_data_block blocks[],
int blocksCount) const = 0;
};
class ServiceManagerWrapper {
public:
virtual ~ServiceManagerWrapper() {}
virtual std::shared_ptr<VoldServiceWrapper> getVoldService() const = 0;
virtual std::shared_ptr<IncrementalManagerWrapper> getIncrementalManager() const = 0;
virtual std::shared_ptr<IncFsWrapper> getIncFs() const = 0;
};
// --- Real stuff ---
class RealVoldService : public VoldServiceWrapper {
public:
RealVoldService(const sp<os::IVold> vold) : mInterface(vold) {}
~RealVoldService() = default;
binder::Status mountIncFs(const std::string& imagePath, const std::string& targetDir,
int32_t flags,
IncrementalFileSystemControlParcel* _aidl_return) const override {
return mInterface->mountIncFs(imagePath, targetDir, flags, _aidl_return);
}
binder::Status unmountIncFs(const std::string& dir) const override {
return mInterface->unmountIncFs(dir);
}
binder::Status bindMount(const std::string& sourceDir,
const std::string& targetDir) const override {
return mInterface->bindMount(sourceDir, targetDir);
}
private:
sp<os::IVold> mInterface;
};
class RealIncrementalManager : public IncrementalManagerWrapper {
public:
RealIncrementalManager(const sp<os::incremental::IIncrementalManager> manager)
: mInterface(manager) {}
~RealIncrementalManager() = default;
binder::Status prepareDataLoader(
int32_t mountId, const FileSystemControlParcel& control,
const DataLoaderParamsParcel& params,
const sp<IDataLoaderStatusListener>& listener,
bool* _aidl_return) const override {
return mInterface->prepareDataLoader(mountId, control, params, listener, _aidl_return);
}
binder::Status startDataLoader(int32_t mountId, bool* _aidl_return) const override {
return mInterface->startDataLoader(mountId, _aidl_return);
}
binder::Status destroyDataLoader(int32_t mountId) const override {
return mInterface->destroyDataLoader(mountId);
}
binder::Status newFileForDataLoader(int32_t mountId, int64_t inode,
const ::std::vector<uint8_t>& metadata) const override {
return mInterface->newFileForDataLoader(mountId, inode, metadata);
}
binder::Status showHealthBlockedUI(int32_t mountId) const override {
return mInterface->showHealthBlockedUI(mountId);
}
private:
sp<os::incremental::IIncrementalManager> mInterface;
};
class RealServiceManager : public ServiceManagerWrapper {
public:
RealServiceManager(const sp<IServiceManager>& serviceManager);
~RealServiceManager() = default;
std::shared_ptr<VoldServiceWrapper> getVoldService() const override;
std::shared_ptr<IncrementalManagerWrapper> getIncrementalManager() const override;
std::shared_ptr<IncFsWrapper> getIncFs() const override;
private:
template <class INTERFACE>
sp<INTERFACE> getRealService(std::string_view serviceName) const;
sp<android::IServiceManager> mServiceManager;
};
class RealIncFs : public IncFsWrapper {
public:
RealIncFs() = default;
~RealIncFs() = default;
Inode makeFile(Control control, std::string_view name, Inode parent, Size size,
std::string_view metadata) const override {
return incfs::makeFile(control, name, parent, size, metadata);
}
Inode makeDir(Control control, std::string_view name, Inode parent, std::string_view metadata,
int mode) const override {
return incfs::makeDir(control, name, parent, metadata, mode);
}
RawMetadata getMetadata(Control control, Inode inode) const override {
return incfs::getMetadata(control, inode);
}
ErrorCode link(Control control, Inode item, Inode targetParent,
std::string_view name) const override {
return incfs::link(control, item, targetParent, name);
}
ErrorCode unlink(Control control, Inode parent, std::string_view name) const override {
return incfs::unlink(control, parent, name);
}
ErrorCode writeBlocks(Control control, const incfs_new_data_block blocks[],
int blocksCount) const override {
return incfs::writeBlocks(control, blocks, blocksCount);
}
};
} // namespace android::os::incremental

View File

@@ -0,0 +1,32 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ANDROID_INCREMENTAL_SERVICE_H
#define ANDROID_INCREMENTAL_SERVICE_H
#include <sys/cdefs.h>
#include <jni.h>
__BEGIN_DECLS
#define INCREMENTAL_LIBRARY_NAME "service.incremental.so"
jlong Incremental_IncrementalService_Start();
void Incremental_IncrementalService_OnSystemReady(jlong self);
__END_DECLS
#endif // ANDROID_INCREMENTAL_SERVICE_H

View File

@@ -0,0 +1,17 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "incremental_service.h"

View File

@@ -0,0 +1,178 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "path.h"
#include <android-base/strings.h>
#include <android-base/logging.h>
#include <algorithm>
#include <iterator>
#include <limits>
#include <memory>
#include <dirent.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
using namespace std::literals;
namespace android::incremental::path {
bool PathCharsLess::operator()(char l, char r) const {
int ll = l == '/' ? std::numeric_limits<char>::min() - 1 : l;
int rr = r == '/' ? std::numeric_limits<char>::min() - 1 : r;
return ll < rr;
}
bool PathLess::operator()(std::string_view l, std::string_view r) const {
return std::lexicographical_compare(std::begin(l), std::end(l), std::begin(r), std::end(r),
PathCharsLess());
}
void details::append_next_path(std::string& target, std::string_view path) {
if (path.empty()) {
return;
}
if (!target.empty()) {
target.push_back('/');
}
target += path;
}
bool isAbsolute(std::string_view path) {
return !path.empty() && path[0] == '/';
}
std::string normalize(std::string_view path) {
if (path.empty()) {
return {};
}
if (path.starts_with("../"sv)) {
return {};
}
std::string result;
if (isAbsolute(path)) {
path.remove_prefix(1);
} else {
char buffer[PATH_MAX];
if (!::getcwd(buffer, sizeof(buffer))) {
return {};
}
result += buffer;
}
size_t start = 0;
size_t end = 0;
for (; end != path.npos; start = end + 1) {
end = path.find('/', start);
// Next component, excluding the separator
auto part = path.substr(start, end - start);
if (part.empty() || part == "."sv) {
continue;
}
if (part == ".."sv) {
if (result.empty()) {
return {};
}
auto lastPos = result.rfind('/');
if (lastPos == result.npos) {
result.clear();
} else {
result.resize(lastPos);
}
continue;
}
result += '/';
result += part;
}
return result;
}
std::string_view basename(std::string_view path) {
if (path.empty()) {
return {};
}
if (path == "/"sv) {
return "/"sv;
}
auto pos = path.rfind('/');
while (!path.empty() && pos == path.size() - 1) {
path.remove_suffix(1);
pos = path.rfind('/');
}
if (pos == path.npos) {
return path.empty() ? "/"sv : path;
}
return path.substr(pos + 1);
}
std::string_view dirname(std::string_view path) {
if (path.empty()) {
return {};
}
if (path == "/"sv) {
return "/"sv;
}
const auto pos = path.rfind('/');
if (pos == 0) {
return "/"sv;
}
if (pos == path.npos) {
return "."sv;
}
return path.substr(0, pos);
}
details::CStrWrapper::CStrWrapper(std::string_view sv) {
if (sv[sv.size()] == '\0') {
mCstr = sv.data();
} else {
mCopy.emplace(sv);
mCstr = mCopy->c_str();
}
}
std::optional<bool> isEmptyDir(std::string_view dir) {
const auto d = std::unique_ptr<DIR, decltype(&::closedir)>{::opendir(c_str(dir)), ::closedir};
if (!d) {
if (errno == EPERM || errno == EACCES) {
return std::nullopt;
}
return false;
}
while (auto entry = ::readdir(d.get())) {
if (entry->d_type != DT_DIR) {
return false;
}
if (entry->d_name != "."sv && entry->d_name != ".."sv) {
return false;
}
}
return true;
}
bool startsWith(std::string_view path, std::string_view prefix) {
if (!base::StartsWith(path, prefix)) {
return false;
}
return path.size() == prefix.size() || path[prefix.size()] == '/';
}
} // namespace android::incremental::path

View File

@@ -0,0 +1,89 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <optional>
#include <string>
#include <string_view>
namespace android::incremental::path {
namespace details {
class CStrWrapper {
public:
CStrWrapper(std::string_view sv);
CStrWrapper(const CStrWrapper&) = delete;
void operator=(const CStrWrapper&) = delete;
CStrWrapper(CStrWrapper&&) = delete;
void operator=(CStrWrapper&&) = delete;
const char* get() const { return mCstr; }
operator const char*() const { return get(); }
private:
const char* mCstr;
std::optional<std::string> mCopy;
};
void append_next_path(std::string& res, std::string_view c);
} // namespace details
//
// An std::map<> comparator that makes all nested paths to be ordered before the parents.
//
struct PathCharsLess {
bool operator()(char l, char r) const;
};
struct PathLess {
using is_transparent = void;
bool operator()(std::string_view l, std::string_view r) const;
};
//
// Returns a zero-terminated version of a passed string view
// Only makes a copy if it wasn't zero-terminated already
// Useful for passing string view parameters to system functions.
//
inline details::CStrWrapper c_str(std::string_view sv) {
return {sv};
}
bool isAbsolute(std::string_view path);
std::string normalize(std::string_view path);
std::string_view dirname(std::string_view path);
std::string_view basename(std::string_view path);
std::optional<bool> isEmptyDir(std::string_view dir);
bool startsWith(std::string_view path, std::string_view prefix);
template <class... Paths>
std::string join(std::string_view first, std::string_view second, Paths&&... paths) {
std::string result;
{
using std::size;
result.reserve(first.size() + second.size() + 1 + (sizeof...(paths) + ... + size(paths)));
}
result.assign(first);
(details::append_next_path(result, second), ..., details::append_next_path(result, paths));
return result;
}
} // namespace android::incremental::path

View File

@@ -0,0 +1,461 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
#include <binder/ParcelFileDescriptor.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <utils/Log.h>
#include <future>
#include "IncrementalService.h"
#include "Metadata.pb.h"
#include "ServiceWrappers.h"
using namespace testing;
using namespace android::incremental;
using namespace std::literals;
using testing::_;
using testing::Invoke;
using testing::NiceMock;
#undef LOG_TAG
#define LOG_TAG "IncrementalServiceTest"
using namespace android::incfs;
using namespace android::content::pm;
namespace android::os::incremental {
class MockVoldService : public VoldServiceWrapper {
public:
MOCK_CONST_METHOD4(mountIncFs,
binder::Status(const std::string& imagePath, const std::string& targetDir,
int32_t flags,
IncrementalFileSystemControlParcel* _aidl_return));
MOCK_CONST_METHOD1(unmountIncFs, binder::Status(const std::string& dir));
MOCK_CONST_METHOD2(bindMount,
binder::Status(const std::string& sourceDir, const std::string& argetDir));
void mountIncFsFails() {
ON_CALL(*this, mountIncFs(_, _, _, _))
.WillByDefault(
Return(binder::Status::fromExceptionCode(1, String8("failed to mount"))));
}
void mountIncFsInvalidControlParcel() {
ON_CALL(*this, mountIncFs(_, _, _, _))
.WillByDefault(Invoke(this, &MockVoldService::getInvalidControlParcel));
}
void mountIncFsSuccess() {
ON_CALL(*this, mountIncFs(_, _, _, _))
.WillByDefault(Invoke(this, &MockVoldService::incFsSuccess));
}
void bindMountFails() {
ON_CALL(*this, bindMount(_, _))
.WillByDefault(Return(
binder::Status::fromExceptionCode(1, String8("failed to bind-mount"))));
}
void bindMountSuccess() {
ON_CALL(*this, bindMount(_, _)).WillByDefault(Return(binder::Status::ok()));
}
binder::Status getInvalidControlParcel(const std::string& imagePath,
const std::string& targetDir, int32_t flags,
IncrementalFileSystemControlParcel* _aidl_return) {
_aidl_return->cmd = nullptr;
_aidl_return->log = nullptr;
return binder::Status::ok();
}
binder::Status incFsSuccess(const std::string& imagePath, const std::string& targetDir,
int32_t flags, IncrementalFileSystemControlParcel* _aidl_return) {
_aidl_return->cmd = std::make_unique<os::ParcelFileDescriptor>(std::move(cmdFd));
_aidl_return->log = std::make_unique<os::ParcelFileDescriptor>(std::move(logFd));
return binder::Status::ok();
}
private:
TemporaryFile cmdFile;
TemporaryFile logFile;
base::unique_fd cmdFd;
base::unique_fd logFd;
};
class MockIncrementalManager : public IncrementalManagerWrapper {
public:
MOCK_CONST_METHOD5(prepareDataLoader,
binder::Status(int32_t mountId, const FileSystemControlParcel& control,
const DataLoaderParamsParcel& params,
const sp<IDataLoaderStatusListener>& listener,
bool* _aidl_return));
MOCK_CONST_METHOD2(startDataLoader, binder::Status(int32_t mountId, bool* _aidl_return));
MOCK_CONST_METHOD1(destroyDataLoader, binder::Status(int32_t mountId));
MOCK_CONST_METHOD3(newFileForDataLoader,
binder::Status(int32_t mountId, int64_t inode,
const ::std::vector<uint8_t>& metadata));
MOCK_CONST_METHOD1(showHealthBlockedUI, binder::Status(int32_t mountId));
binder::Status prepareDataLoaderOk(int32_t mountId, const FileSystemControlParcel& control,
const DataLoaderParamsParcel& params,
const sp<IDataLoaderStatusListener>& listener,
bool* _aidl_return) {
mId = mountId;
mListener = listener;
*_aidl_return = true;
return binder::Status::ok();
}
binder::Status startDataLoaderOk(int32_t mountId, bool* _aidl_return) {
*_aidl_return = true;
return binder::Status::ok();
}
void prepareDataLoaderFails() {
ON_CALL(*this, prepareDataLoader(_, _, _, _, _))
.WillByDefault(Return(
(binder::Status::fromExceptionCode(1, String8("failed to prepare")))));
}
void prepareDataLoaderSuccess() {
ON_CALL(*this, prepareDataLoader(_, _, _, _, _))
.WillByDefault(Invoke(this, &MockIncrementalManager::prepareDataLoaderOk));
}
void startDataLoaderSuccess() {
ON_CALL(*this, startDataLoader(_, _))
.WillByDefault(Invoke(this, &MockIncrementalManager::startDataLoaderOk));
}
void setDataLoaderStatusNotReady() {
mListener->onStatusChanged(mId, IDataLoaderStatusListener::DATA_LOADER_NOT_READY);
}
void setDataLoaderStatusReady() {
mListener->onStatusChanged(mId, IDataLoaderStatusListener::DATA_LOADER_READY);
}
private:
int mId;
sp<IDataLoaderStatusListener> mListener;
};
class MockIncFs : public IncFsWrapper {
public:
MOCK_CONST_METHOD5(makeFile,
Inode(Control control, std::string_view name, Inode parent, Size size,
std::string_view metadata));
MOCK_CONST_METHOD5(makeDir,
Inode(Control control, std::string_view name, Inode parent,
std::string_view metadata, int mode));
MOCK_CONST_METHOD2(getMetadata, RawMetadata(Control control, Inode inode));
MOCK_CONST_METHOD4(link,
ErrorCode(Control control, Inode item, Inode targetParent,
std::string_view name));
MOCK_CONST_METHOD3(unlink, ErrorCode(Control control, Inode parent, std::string_view name));
MOCK_CONST_METHOD3(writeBlocks,
ErrorCode(Control control, const incfs_new_data_block blocks[],
int blocksCount));
void makeFileFails() { ON_CALL(*this, makeFile(_, _, _, _, _)).WillByDefault(Return(-1)); }
void makeFileSuccess() { ON_CALL(*this, makeFile(_, _, _, _, _)).WillByDefault(Return(0)); }
RawMetadata getMountInfoMetadata(Control control, Inode inode) {
metadata::Mount m;
m.mutable_storage()->set_id(100);
m.mutable_loader()->set_package_name("com.test");
m.mutable_loader()->set_arguments("com.uri");
const auto metadata = m.SerializeAsString();
m.mutable_loader()->release_arguments();
m.mutable_loader()->release_package_name();
return std::vector<char>(metadata.begin(), metadata.end());
}
RawMetadata getStorageMetadata(Control control, Inode inode) {
metadata::Storage st;
st.set_id(100);
auto metadata = st.SerializeAsString();
return std::vector<char>(metadata.begin(), metadata.end());
}
RawMetadata getBindPointMetadata(Control control, Inode inode) {
metadata::BindPoint bp;
std::string destPath = "dest";
std::string srcPath = "src";
bp.set_storage_id(100);
bp.set_allocated_dest_path(&destPath);
bp.set_allocated_source_subdir(&srcPath);
const auto metadata = bp.SerializeAsString();
bp.release_source_subdir();
bp.release_dest_path();
return std::vector<char>(metadata.begin(), metadata.end());
}
};
class MockServiceManager : public ServiceManagerWrapper {
public:
MockServiceManager(std::shared_ptr<MockVoldService> vold,
std::shared_ptr<MockIncrementalManager> manager,
std::shared_ptr<MockIncFs> incfs)
: mVold(vold), mIncrementalManager(manager), mIncFs(incfs) {}
std::shared_ptr<VoldServiceWrapper> getVoldService() const override { return mVold; }
std::shared_ptr<IncrementalManagerWrapper> getIncrementalManager() const override {
return mIncrementalManager;
}
std::shared_ptr<IncFsWrapper> getIncFs() const override { return mIncFs; }
private:
std::shared_ptr<MockVoldService> mVold;
std::shared_ptr<MockIncrementalManager> mIncrementalManager;
std::shared_ptr<MockIncFs> mIncFs;
};
// --- IncrementalServiceTest ---
static Inode inode(std::string_view path) {
struct stat st;
if (::stat(path::c_str(path), &st)) {
return -1;
}
return st.st_ino;
}
class IncrementalServiceTest : public testing::Test {
public:
void SetUp() override {
mVold = std::make_shared<NiceMock<MockVoldService>>();
mIncrementalManager = std::make_shared<NiceMock<MockIncrementalManager>>();
mIncFs = std::make_shared<NiceMock<MockIncFs>>();
MockServiceManager serviceManager = MockServiceManager(mVold, mIncrementalManager, mIncFs);
mIncrementalService = std::make_unique<IncrementalService>(serviceManager, mRootDir.path);
mDataLoaderParcel.packageName = "com.test";
mDataLoaderParcel.staticArgs = "uri";
mIncrementalService->onSystemReady();
}
void setUpExistingMountDir(const std::string& rootDir) {
const auto dir = rootDir + "/dir1";
const auto mountDir = dir + "/mount";
const auto backingDir = dir + "/backing_store";
const auto storageDir = mountDir + "/st0";
ASSERT_EQ(0, mkdir(dir.c_str(), 0755));
ASSERT_EQ(0, mkdir(mountDir.c_str(), 0755));
ASSERT_EQ(0, mkdir(backingDir.c_str(), 0755));
ASSERT_EQ(0, mkdir(storageDir.c_str(), 0755));
const auto mountInfoFile = rootDir + "/dir1/mount/.info";
const auto mountPointsFile = rootDir + "/dir1/mount/.mountpoint.abcd";
ASSERT_TRUE(base::WriteStringToFile("info", mountInfoFile));
ASSERT_TRUE(base::WriteStringToFile("mounts", mountPointsFile));
ASSERT_GE(inode(mountInfoFile), 0);
ASSERT_GE(inode(mountPointsFile), 0);
ON_CALL(*mIncFs, getMetadata(_, inode(mountInfoFile)))
.WillByDefault(Invoke(mIncFs.get(), &MockIncFs::getMountInfoMetadata));
ON_CALL(*mIncFs, getMetadata(_, inode(mountPointsFile)))
.WillByDefault(Invoke(mIncFs.get(), &MockIncFs::getBindPointMetadata));
ON_CALL(*mIncFs, getMetadata(_, inode(rootDir + "/dir1/mount/st0")))
.WillByDefault(Invoke(mIncFs.get(), &MockIncFs::getStorageMetadata));
}
protected:
std::shared_ptr<NiceMock<MockVoldService>> mVold;
std::shared_ptr<NiceMock<MockIncFs>> mIncFs;
std::shared_ptr<NiceMock<MockIncrementalManager>> mIncrementalManager;
std::unique_ptr<IncrementalService> mIncrementalService;
TemporaryDir mRootDir;
DataLoaderParamsParcel mDataLoaderParcel;
};
/*
TEST_F(IncrementalServiceTest, testBootMountExistingImagesSuccess) {
TemporaryDir tempDir;
setUpExistingMountDir(tempDir.path);
mVold->mountIncFsSuccess();
mVold->bindMountSuccess();
mIncrementalManager->prepareDataLoaderSuccess();
ON_CALL(*mIncrementalManager, destroyDataLoader(_)).WillByDefault(Return(binder::Status::ok()));
EXPECT_CALL(*mVold, mountIncFs(_, _, _, _)).Times(1);
EXPECT_CALL(*mIncrementalManager, prepareDataLoader(_, _, _, _, _)).Times(1);
MockServiceManager serviceManager = MockServiceManager(mVold, mIncrementalManager, mIncFs);
std::unique_ptr<IncrementalService> incrementalService =
std::make_unique<IncrementalService>(serviceManager, tempDir.path);
auto finished = incrementalService->onSystemReady();
if (finished) {
finished->wait();
}
}
*/
TEST_F(IncrementalServiceTest, testCreateStorageMountIncFsFails) {
mVold->mountIncFsFails();
EXPECT_CALL(*mIncrementalManager, prepareDataLoader(_, _, _, _, _)).Times(0);
TemporaryDir tempDir;
int storageId =
mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
IncrementalService::CreateOptions::CreateNew);
ASSERT_LT(storageId, 0);
}
TEST_F(IncrementalServiceTest, testCreateStorageMountIncFsInvalidControlParcel) {
mVold->mountIncFsInvalidControlParcel();
EXPECT_CALL(*mIncrementalManager, prepareDataLoader(_, _, _, _, _)).Times(0);
TemporaryDir tempDir;
int storageId =
mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
IncrementalService::CreateOptions::CreateNew);
ASSERT_LT(storageId, 0);
}
TEST_F(IncrementalServiceTest, testCreateStorageMakeFileFails) {
mVold->mountIncFsSuccess();
mIncFs->makeFileFails();
EXPECT_CALL(*mIncrementalManager, prepareDataLoader(_, _, _, _, _)).Times(0);
EXPECT_CALL(*mIncrementalManager, destroyDataLoader(_));
EXPECT_CALL(*mVold, unmountIncFs(_));
TemporaryDir tempDir;
int storageId =
mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
IncrementalService::CreateOptions::CreateNew);
ASSERT_LT(storageId, 0);
}
TEST_F(IncrementalServiceTest, testCreateStorageBindMountFails) {
mVold->mountIncFsSuccess();
mIncFs->makeFileSuccess();
mVold->bindMountFails();
EXPECT_CALL(*mIncrementalManager, prepareDataLoader(_, _, _, _, _)).Times(0);
EXPECT_CALL(*mIncrementalManager, destroyDataLoader(_));
EXPECT_CALL(*mVold, unmountIncFs(_));
TemporaryDir tempDir;
int storageId =
mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
IncrementalService::CreateOptions::CreateNew);
ASSERT_LT(storageId, 0);
}
TEST_F(IncrementalServiceTest, testCreateStoragePrepareDataLoaderFails) {
mVold->mountIncFsSuccess();
mIncFs->makeFileSuccess();
mVold->bindMountSuccess();
mIncrementalManager->prepareDataLoaderFails();
EXPECT_CALL(*mIncrementalManager, destroyDataLoader(_));
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
TemporaryDir tempDir;
int storageId =
mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
IncrementalService::CreateOptions::CreateNew);
ASSERT_LT(storageId, 0);
}
TEST_F(IncrementalServiceTest, testDeleteStorageSuccess) {
mVold->mountIncFsSuccess();
mIncFs->makeFileSuccess();
mVold->bindMountSuccess();
mIncrementalManager->prepareDataLoaderSuccess();
EXPECT_CALL(*mIncrementalManager, destroyDataLoader(_));
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
TemporaryDir tempDir;
int storageId =
mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
IncrementalService::CreateOptions::CreateNew);
ASSERT_GE(storageId, 0);
mIncrementalService->deleteStorage(storageId);
}
TEST_F(IncrementalServiceTest, testOnStatusNotReady) {
mVold->mountIncFsSuccess();
mIncFs->makeFileSuccess();
mVold->bindMountSuccess();
mIncrementalManager->prepareDataLoaderSuccess();
EXPECT_CALL(*mIncrementalManager, destroyDataLoader(_));
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
TemporaryDir tempDir;
int storageId =
mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
IncrementalService::CreateOptions::CreateNew);
ASSERT_GE(storageId, 0);
mIncrementalManager->setDataLoaderStatusNotReady();
}
TEST_F(IncrementalServiceTest, testStartDataLoaderSuccess) {
mVold->mountIncFsSuccess();
mIncFs->makeFileSuccess();
mVold->bindMountSuccess();
mIncrementalManager->prepareDataLoaderSuccess();
mIncrementalManager->startDataLoaderSuccess();
EXPECT_CALL(*mIncrementalManager, destroyDataLoader(_));
EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2);
TemporaryDir tempDir;
int storageId =
mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
IncrementalService::CreateOptions::CreateNew);
ASSERT_GE(storageId, 0);
mIncrementalManager->setDataLoaderStatusReady();
ASSERT_TRUE(mIncrementalService->startLoading(storageId));
}
TEST_F(IncrementalServiceTest, testMakeDirectory) {
mVold->mountIncFsSuccess();
mIncFs->makeFileSuccess();
mVold->bindMountSuccess();
mIncrementalManager->prepareDataLoaderSuccess();
mIncrementalManager->startDataLoaderSuccess();
TemporaryDir tempDir;
int storageId =
mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
IncrementalService::CreateOptions::CreateNew);
std::string_view dir_path("test");
EXPECT_CALL(*mIncFs, makeDir(_, dir_path, _, _, _));
int fileIno = mIncrementalService->makeDir(storageId, dir_path, "");
ASSERT_GE(fileIno, 0);
}
TEST_F(IncrementalServiceTest, testMakeDirectoryNoParent) {
mVold->mountIncFsSuccess();
mIncFs->makeFileSuccess();
mVold->bindMountSuccess();
mIncrementalManager->prepareDataLoaderSuccess();
mIncrementalManager->startDataLoaderSuccess();
TemporaryDir tempDir;
int storageId =
mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
IncrementalService::CreateOptions::CreateNew);
std::string_view first("first");
std::string_view second("second");
std::string dir_path = std::string(first) + "/" + std::string(second);
EXPECT_CALL(*mIncFs, makeDir(_, first, _, _, _)).Times(0);
EXPECT_CALL(*mIncFs, makeDir(_, second, _, _, _)).Times(0);
int fileIno = mIncrementalService->makeDir(storageId, dir_path, "");
ASSERT_LT(fileIno, 0);
}
TEST_F(IncrementalServiceTest, testMakeDirectories) {
mVold->mountIncFsSuccess();
mIncFs->makeFileSuccess();
mVold->bindMountSuccess();
mIncrementalManager->prepareDataLoaderSuccess();
mIncrementalManager->startDataLoaderSuccess();
TemporaryDir tempDir;
int storageId =
mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
IncrementalService::CreateOptions::CreateNew);
std::string_view first("first");
std::string_view second("second");
std::string_view third("third");
InSequence seq;
EXPECT_CALL(*mIncFs, makeDir(_, first, _, _, _));
EXPECT_CALL(*mIncFs, makeDir(_, second, _, _, _));
EXPECT_CALL(*mIncFs, makeDir(_, third, _, _, _));
std::string dir_path =
std::string(first) + "/" + std::string(second) + "/" + std::string(third);
int fileIno = mIncrementalService->makeDirs(storageId, dir_path, "");
ASSERT_GE(fileIno, 0);
}
} // namespace android::os::incremental

View File

@@ -0,0 +1,43 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "../path.h"
#include <gtest/gtest.h>
using namespace std::literals;
namespace android::incremental::path {
TEST(Path, Normalize) {
EXPECT_STREQ("", normalize("").c_str());
EXPECT_STREQ("/data/app/com.snapchat.android-evzhnJDgPOq8VcxwEkSY5g==/base.apk",
normalize("/data/app/com.snapchat.android-evzhnJDgPOq8VcxwEkSY5g==/base.apk")
.c_str());
EXPECT_STREQ("/a/b", normalize("/a/c/../b").c_str());
}
TEST(Path, Comparator) {
EXPECT_TRUE(PathLess()("/a", "/aa"));
EXPECT_TRUE(PathLess()("/a/b", "/aa/b"));
EXPECT_TRUE(PathLess()("/a", "/a/b"));
EXPECT_TRUE(PathLess()("/a/b"sv, "/a\0"sv));
EXPECT_TRUE(!PathLess()("/aa/b", "/a/b"));
EXPECT_TRUE(!PathLess()("/a/b", "/a/b"));
EXPECT_TRUE(!PathLess()("/a/b", "/a"));
}
} // namespace android::incremental::path