/* * 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 #include #include #include #include #include #include #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 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 mInterface; }; class RealDataLoaderManager : public DataLoaderManagerWrapper { public: RealDataLoaderManager(sp manager) : mInterface(std::move(manager)) {} ~RealDataLoaderManager() = default; binder::Status bindToDataLoader(MountId mountId, const content::pm::DataLoaderParamsParcel& params, const sp& listener, bool* _aidl_return) const final { return mInterface->bindToDataLoader(mountId, params, listener, _aidl_return); } binder::Status getDataLoader(MountId mountId, sp* _aidl_return) const final { return mInterface->getDataLoader(mountId, _aidl_return); } binder::Status unbindFromDataLoader(MountId mountId) const final { return mInterface->unbindFromDataLoader(mountId); } private: sp 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& callback) final { mAppOpsManager.startWatchingMode(op, packageName, callback); } void stopWatchingMode(const sp& 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 blocks) const final { return incfs::writeBlocks({blocks.data(), size_t(blocks.size())}); } WaitResult waitForPendingReads(const Control& control, std::chrono::milliseconds timeout, std::vector* 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 mJobs; std::condition_variable mCondition; std::mutex mMutex; std::thread mThread; }; RealServiceManager::RealServiceManager(sp serviceManager, JNIEnv* env) : mServiceManager(std::move(serviceManager)), mJvm(RealJniWrapper::getJvm(env)) {} template sp RealServiceManager::getRealService(std::string_view serviceName) const { sp binder = mServiceManager->getService(String16(serviceName.data(), serviceName.size())); if (!binder) { return nullptr; } return interface_cast(binder); } std::unique_ptr RealServiceManager::getVoldService() { sp vold = RealServiceManager::getRealService(kVoldServiceName); if (vold != 0) { return std::make_unique(vold); } return nullptr; } std::unique_ptr RealServiceManager::getDataLoaderManager() { sp manager = RealServiceManager::getRealService( kDataLoaderManagerName); if (manager) { return std::make_unique(manager); } return nullptr; } std::unique_ptr RealServiceManager::getIncFs() { return std::make_unique(); } std::unique_ptr RealServiceManager::getAppOpsManager() { return std::make_unique(); } std::unique_ptr RealServiceManager::getJni() { return std::make_unique(mJvm); } std::unique_ptr RealServiceManager::getLooper() { return std::make_unique(); } std::unique_ptr RealServiceManager::getTimedQueue() { return std::make_unique(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(&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