| /* |
| * 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 |