blob: 99a35adb50744b2f83c1a40aaeac112780cf53e3 [file] [log] [blame]
/*
* 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 "IncrementalService"
#include "ServiceWrappers.h"
#include <MountRegistry.h>
#include <android-base/logging.h>
#include <android/content/pm/IDataLoaderManager.h>
#include <android/os/IVold.h>
#include <binder/AppOpsManager.h>
#include <utils/String16.h>
#include <thread>
#include "IncrementalServiceValidation.h"
using namespace std::literals;
namespace android::incremental {
static constexpr auto kVoldServiceName = "vold"sv;
static constexpr auto kDataLoaderManagerName = "dataloader_manager"sv;
class RealVoldService : public VoldServiceWrapper {
public:
RealVoldService(sp<os::IVold> vold) : mInterface(std::move(vold)) {}
~RealVoldService() = default;
binder::Status mountIncFs(
const std::string& backingPath, const std::string& targetDir, int32_t flags,
os::incremental::IncrementalFileSystemControlParcel* _aidl_return) const final {
return mInterface->mountIncFs(backingPath, targetDir, flags, _aidl_return);
}
binder::Status unmountIncFs(const std::string& dir) const final {
return mInterface->unmountIncFs(dir);
}
binder::Status bindMount(const std::string& sourceDir,
const std::string& targetDir) const final {
return mInterface->bindMount(sourceDir, targetDir);
}
binder::Status setIncFsMountOptions(
const ::android::os::incremental::IncrementalFileSystemControlParcel& control,
bool enableReadLogs) const final {
return mInterface->setIncFsMountOptions(control, enableReadLogs);
}
private:
sp<os::IVold> mInterface;
};
class RealDataLoaderManager : public DataLoaderManagerWrapper {
public:
RealDataLoaderManager(sp<content::pm::IDataLoaderManager> manager)
: mInterface(std::move(manager)) {}
~RealDataLoaderManager() = default;
binder::Status bindToDataLoader(MountId mountId,
const content::pm::DataLoaderParamsParcel& params,
const sp<content::pm::IDataLoaderStatusListener>& listener,
bool* _aidl_return) const final {
return mInterface->bindToDataLoader(mountId, params, listener, _aidl_return);
}
binder::Status getDataLoader(MountId mountId,
sp<content::pm::IDataLoader>* _aidl_return) const final {
return mInterface->getDataLoader(mountId, _aidl_return);
}
binder::Status unbindFromDataLoader(MountId mountId) const final {
return mInterface->unbindFromDataLoader(mountId);
}
private:
sp<content::pm::IDataLoaderManager> mInterface;
};
class RealAppOpsManager : public AppOpsManagerWrapper {
public:
~RealAppOpsManager() = default;
binder::Status checkPermission(const char* permission, const char* operation,
const char* package) const final {
return android::incremental::CheckPermissionForDataDelivery(permission, operation, package);
}
void startWatchingMode(int32_t op, const String16& packageName,
const sp<IAppOpsCallback>& callback) final {
mAppOpsManager.startWatchingMode(op, packageName, callback);
}
void stopWatchingMode(const sp<IAppOpsCallback>& callback) final {
mAppOpsManager.stopWatchingMode(callback);
}
private:
android::AppOpsManager mAppOpsManager;
};
class RealJniWrapper final : public JniWrapper {
public:
RealJniWrapper(JavaVM* jvm);
void initializeForCurrentThread() const final;
static JavaVM* getJvm(JNIEnv* env);
private:
JavaVM* const mJvm;
};
class RealLooperWrapper final : public LooperWrapper {
public:
int addFd(int fd, int ident, int events, android::Looper_callbackFunc callback,
void* data) final {
return mLooper.addFd(fd, ident, events, callback, data);
}
int removeFd(int fd) final { return mLooper.removeFd(fd); }
void wake() final { return mLooper.wake(); }
int pollAll(int timeoutMillis) final { return mLooper.pollAll(timeoutMillis); }
private:
struct Looper : public android::Looper {
Looper() : android::Looper(/*allowNonCallbacks=*/false) {}
~Looper() {}
} mLooper;
};
class RealIncFs : public IncFsWrapper {
public:
RealIncFs() = default;
~RealIncFs() final = default;
void listExistingMounts(const ExistingMountCallback& cb) const final {
for (auto mount : incfs::defaultMountRegistry().copyMounts()) {
auto binds = mount.binds(); // span() doesn't like rvalue containers, needs to save it.
cb(mount.root(), mount.backingDir(), binds);
}
}
Control openMount(std::string_view path) const final { return incfs::open(path); }
Control createControl(IncFsFd cmd, IncFsFd pendingReads, IncFsFd logs) const final {
return incfs::createControl(cmd, pendingReads, logs);
}
ErrorCode makeFile(const Control& control, std::string_view path, int mode, FileId id,
incfs::NewFileParams params) const final {
return incfs::makeFile(control, path, mode, id, params);
}
ErrorCode makeDir(const Control& control, std::string_view path, int mode) const final {
return incfs::makeDir(control, path, mode);
}
ErrorCode makeDirs(const Control& control, std::string_view path, int mode) const final {
return incfs::makeDirs(control, path, mode);
}
incfs::RawMetadata getMetadata(const Control& control, FileId fileid) const final {
return incfs::getMetadata(control, fileid);
}
incfs::RawMetadata getMetadata(const Control& control, std::string_view path) const final {
return incfs::getMetadata(control, path);
}
FileId getFileId(const Control& control, std::string_view path) const final {
return incfs::getFileId(control, path);
}
ErrorCode link(const Control& control, std::string_view from, std::string_view to) const final {
return incfs::link(control, from, to);
}
ErrorCode unlink(const Control& control, std::string_view path) const final {
return incfs::unlink(control, path);
}
base::unique_fd openForSpecialOps(const Control& control, FileId id) const final {
return base::unique_fd{incfs::openForSpecialOps(control, id).release()};
}
ErrorCode writeBlocks(std::span<const incfs::DataBlock> blocks) const final {
return incfs::writeBlocks({blocks.data(), size_t(blocks.size())});
}
WaitResult waitForPendingReads(const Control& control, std::chrono::milliseconds timeout,
std::vector<incfs::ReadInfo>* pendingReadsBuffer) const final {
return incfs::waitForPendingReads(control, timeout, pendingReadsBuffer);
}
};
static JNIEnv* getOrAttachJniEnv(JavaVM* jvm);
class RealTimedQueueWrapper : public TimedQueueWrapper {
public:
RealTimedQueueWrapper(JavaVM* jvm) {
mThread = std::thread([this, jvm]() {
(void)getOrAttachJniEnv(jvm);
runTimers();
});
}
~RealTimedQueueWrapper() final {
CHECK(!mRunning) << "call stop first";
CHECK(!mThread.joinable()) << "call stop first";
}
void addJob(MountId id, Milliseconds after, Job what) final {
const auto now = Clock::now();
{
std::unique_lock lock(mMutex);
mJobs.insert(TimedJob{id, now + after, std::move(what)});
}
mCondition.notify_all();
}
void removeJobs(MountId id) final {
std::unique_lock lock(mMutex);
std::erase_if(mJobs, [id](auto&& item) { return item.id == id; });
}
void stop() final {
{
std::unique_lock lock(mMutex);
mRunning = false;
}
mCondition.notify_all();
mThread.join();
mJobs.clear();
}
private:
void runTimers() {
static constexpr TimePoint kInfinityTs{Clock::duration::max()};
TimePoint nextJobTs = kInfinityTs;
std::unique_lock lock(mMutex);
for (;;) {
mCondition.wait_until(lock, nextJobTs, [this, nextJobTs]() {
const auto now = Clock::now();
const auto firstJobTs = !mJobs.empty() ? mJobs.begin()->when : kInfinityTs;
return !mRunning || firstJobTs <= now || firstJobTs < nextJobTs;
});
if (!mRunning) {
return;
}
const auto now = Clock::now();
auto it = mJobs.begin();
// Always acquire begin(). We can't use it after unlock as mTimedJobs can change.
for (; it != mJobs.end() && it->when <= now; it = mJobs.begin()) {
auto job = std::move(it->what);
mJobs.erase(it);
lock.unlock();
job();
lock.lock();
}
nextJobTs = it != mJobs.end() ? it->when : kInfinityTs;
}
}
struct TimedJob {
MountId id;
TimePoint when;
Job what;
friend bool operator<(const TimedJob& lhs, const TimedJob& rhs) {
return lhs.when < rhs.when;
}
};
bool mRunning = true;
std::set<TimedJob> mJobs;
std::condition_variable mCondition;
std::mutex mMutex;
std::thread mThread;
};
RealServiceManager::RealServiceManager(sp<IServiceManager> serviceManager, JNIEnv* env)
: mServiceManager(std::move(serviceManager)), mJvm(RealJniWrapper::getJvm(env)) {}
template <class INTERFACE>
sp<INTERFACE> RealServiceManager::getRealService(std::string_view serviceName) const {
sp<IBinder> binder =
mServiceManager->getService(String16(serviceName.data(), serviceName.size()));
if (!binder) {
return nullptr;
}
return interface_cast<INTERFACE>(binder);
}
std::unique_ptr<VoldServiceWrapper> RealServiceManager::getVoldService() {
sp<os::IVold> vold = RealServiceManager::getRealService<os::IVold>(kVoldServiceName);
if (vold != 0) {
return std::make_unique<RealVoldService>(vold);
}
return nullptr;
}
std::unique_ptr<DataLoaderManagerWrapper> RealServiceManager::getDataLoaderManager() {
sp<content::pm::IDataLoaderManager> manager =
RealServiceManager::getRealService<content::pm::IDataLoaderManager>(
kDataLoaderManagerName);
if (manager) {
return std::make_unique<RealDataLoaderManager>(manager);
}
return nullptr;
}
std::unique_ptr<IncFsWrapper> RealServiceManager::getIncFs() {
return std::make_unique<RealIncFs>();
}
std::unique_ptr<AppOpsManagerWrapper> RealServiceManager::getAppOpsManager() {
return std::make_unique<RealAppOpsManager>();
}
std::unique_ptr<JniWrapper> RealServiceManager::getJni() {
return std::make_unique<RealJniWrapper>(mJvm);
}
std::unique_ptr<LooperWrapper> RealServiceManager::getLooper() {
return std::make_unique<RealLooperWrapper>();
}
std::unique_ptr<TimedQueueWrapper> RealServiceManager::getTimedQueue() {
return std::make_unique<RealTimedQueueWrapper>(mJvm);
}
static JavaVM* getJavaVm(JNIEnv* env) {
CHECK(env);
JavaVM* jvm = nullptr;
env->GetJavaVM(&jvm);
CHECK(jvm);
return jvm;
}
static JNIEnv* getJniEnv(JavaVM* vm) {
JNIEnv* env;
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
return nullptr;
}
return env;
}
static JNIEnv* getOrAttachJniEnv(JavaVM* jvm) {
if (!jvm) {
LOG(ERROR) << "No JVM instance";
return nullptr;
}
JNIEnv* env = getJniEnv(jvm);
if (!env) {
int result = jvm->AttachCurrentThread(&env, nullptr);
if (result != JNI_OK) {
LOG(ERROR) << "JVM thread attach failed: " << result;
return nullptr;
}
struct VmDetacher {
VmDetacher(JavaVM* vm) : mVm(vm) {}
~VmDetacher() { mVm->DetachCurrentThread(); }
private:
JavaVM* const mVm;
};
static thread_local VmDetacher detacher(jvm);
}
return env;
}
RealJniWrapper::RealJniWrapper(JavaVM* jvm) : mJvm(jvm) {
CHECK(!!mJvm) << "JVM is unavailable";
}
void RealJniWrapper::initializeForCurrentThread() const {
(void)getOrAttachJniEnv(mJvm);
}
JavaVM* RealJniWrapper::getJvm(JNIEnv* env) {
return getJavaVm(env);
}
} // namespace android::incremental