Loading services/incremental/IncrementalService.cpp +107 −9 Original line number Diff line number Diff line Loading @@ -266,6 +266,7 @@ IncrementalService::IncrementalService(ServiceManagerWrapper&& sm, std::string_v mIncFs(sm.getIncFs()), mAppOpsManager(sm.getAppOpsManager()), mJni(sm.getJni()), mLooper(sm.getLooper()), mIncrementalDir(rootDir) { if (!mVold) { LOG(FATAL) << "Vold service is unavailable"; Loading @@ -276,12 +277,22 @@ IncrementalService::IncrementalService(ServiceManagerWrapper&& sm, std::string_v if (!mAppOpsManager) { LOG(FATAL) << "AppOpsManager is unavailable"; } if (!mJni) { LOG(FATAL) << "JNI is unavailable"; } if (!mLooper) { LOG(FATAL) << "Looper is unavailable"; } mJobQueue.reserve(16); mJobProcessor = std::thread([this]() { mJni->initializeForCurrentThread(); runJobProcessing(); }); mCmdLooperThread = std::thread([this]() { mJni->initializeForCurrentThread(); runCmdLooper(); }); const auto mountedRootNames = adoptMountedInstances(); mountExistingImages(mountedRootNames); Loading @@ -294,6 +305,7 @@ IncrementalService::~IncrementalService() { } mJobCondition.notify_all(); mJobProcessor.join(); mCmdLooperThread.join(); } static const char* toString(IncrementalService::BindKind kind) { Loading Loading @@ -1315,6 +1327,13 @@ bool IncrementalService::mountExistingImage(std::string_view root) { return true; } void IncrementalService::runCmdLooper() { constexpr auto kTimeoutMsecs = 1000; while (mRunning.load(std::memory_order_relaxed)) { mLooper->pollAll(kTimeoutMsecs); } } IncrementalService::DataLoaderStubPtr IncrementalService::prepareDataLoader( IncFsMount& ifs, DataLoaderParamsParcel&& params, const DataLoaderStatusListener* externalListener) { Loading @@ -1337,8 +1356,16 @@ void IncrementalService::prepareDataLoaderLocked(IncFsMount& ifs, DataLoaderPara fsControlParcel.incremental->log.reset(dup(ifs.control.logs())); fsControlParcel.service = new IncrementalServiceConnector(*this, ifs.mountId); ifs.dataLoaderStub = new DataLoaderStub(*this, ifs.mountId, std::move(params), std::move(fsControlParcel), externalListener); incfs::UniqueControl healthControl = mIncFs->openMount(ifs.root.c_str()); if (healthControl.pendingReads() < 0) { LOG(ERROR) << "Failed to open health control for: " << ifs.root << "(" << healthControl.cmd() << ":" << healthControl.pendingReads() << ":" << healthControl.logs() << ")"; } ifs.dataLoaderStub = new DataLoaderStub(*this, ifs.mountId, std::move(params), std::move(fsControlParcel), std::move(healthControl), externalListener); } template <class Duration> Loading Loading @@ -1658,24 +1685,34 @@ void IncrementalService::onAppOpChanged(const std::string& packageName) { IncrementalService::DataLoaderStub::DataLoaderStub(IncrementalService& service, MountId id, DataLoaderParamsParcel&& params, FileSystemControlParcel&& control, incfs::UniqueControl&& healthControl, const DataLoaderStatusListener* externalListener) : mService(service), mId(id), mParams(std::move(params)), mControl(std::move(control)), mHealthControl(std::move(healthControl)), mListener(externalListener ? *externalListener : DataLoaderStatusListener()) { addToCmdLooperLocked(); } IncrementalService::DataLoaderStub::~DataLoaderStub() = default; IncrementalService::DataLoaderStub::~DataLoaderStub() { if (mId != kInvalidStorageId) { cleanupResources(); } } void IncrementalService::DataLoaderStub::cleanupResources() { requestDestroy(); auto now = Clock::now(); std::unique_lock lock(mMutex); removeFromCmdLooperLocked(); mParams = {}; mControl = {}; mHealthControl = {}; mStatusCondition.wait_until(lock, now + 60s, [this] { return mCurrentStatus == IDataLoaderStatusListener::DATA_LOADER_DESTROYED; }); Loading Loading @@ -1710,21 +1747,19 @@ bool IncrementalService::DataLoaderStub::requestDestroy() { } bool IncrementalService::DataLoaderStub::setTargetStatus(int newStatus) { int oldStatus, curStatus; { std::unique_lock lock(mMutex); oldStatus = mTargetStatus; curStatus = mCurrentStatus; setTargetStatusLocked(newStatus); } LOG(DEBUG) << "Target status update for DataLoader " << mId << ": " << oldStatus << " -> " << newStatus << " (current " << curStatus << ")"; return fsmStep(); } void IncrementalService::DataLoaderStub::setTargetStatusLocked(int status) { auto oldStatus = mTargetStatus; mTargetStatus = status; mTargetStatusTs = Clock::now(); LOG(DEBUG) << "Target status update for DataLoader " << mId << ": " << oldStatus << " -> " << status << " (current " << mCurrentStatus << ")"; } bool IncrementalService::DataLoaderStub::bind() { Loading Loading @@ -1860,12 +1895,75 @@ binder::Status IncrementalService::DataLoaderStub::onStatusChanged(MountId mount return binder::Status::ok(); } void IncrementalService::DataLoaderStub::addToCmdLooperLocked() { const auto pendingReadsFd = mHealthControl.pendingReads(); if (pendingReadsFd < 0) { return; } mService.mLooper->addFd( pendingReadsFd, android::Looper::POLL_CALLBACK, android::Looper::EVENT_INPUT, [](int, int, void* data) -> int { auto&& self = (DataLoaderStub*)data; return self->onCmdLooperEvent(); }, this); mService.mLooper->wake(); } void IncrementalService::DataLoaderStub::removeFromCmdLooperLocked() { const auto pendingReadsFd = mHealthControl.pendingReads(); if (pendingReadsFd < 0) { return; } mService.mLooper->removeFd(pendingReadsFd); mService.mLooper->wake(); } int IncrementalService::DataLoaderStub::onCmdLooperEvent() { if (!mService.mRunning.load(std::memory_order_relaxed)) { return 0; } bool pendingReadsOccur = false; { std::unique_lock lock(mMutex); const auto now = Clock::now(); if (now < mEarliestMissingPageTs) { // Transition: duration::max -> now. mEarliestMissingPageTs = now; pendingReadsOccur = true; } } if (pendingReadsOccur) { LOG(INFO) << "Pending reads occur for, requesting start for: " << mId; requestStart(); } { // Drop pending reads. static constexpr auto kMaxDropIterations = 3; std::unique_lock lock(mMutex); for (int i = 0; i < kMaxDropIterations; ++i) { if (IncFs_DropPendingReads(mHealthControl) <= 0) { break; } } } return 1; } void IncrementalService::DataLoaderStub::onDump(int fd) { dprintf(fd, " dataLoader: {\n"); dprintf(fd, " currentStatus: %d\n", mCurrentStatus); dprintf(fd, " targetStatus: %d\n", mTargetStatus); dprintf(fd, " targetStatusTs: %lldmcs\n", (long long)(elapsedMcs(mTargetStatusTs, Clock::now()))); dprintf(fd, " earliestMissingPageTs: %lldmcs\n", (long long)(elapsedMcs(mEarliestMissingPageTs, Clock::now()))); const auto& params = mParams; dprintf(fd, " dataLoaderParams: {\n"); dprintf(fd, " type: %s\n", toString(params.type).c_str()); Loading services/incremental/IncrementalService.h +16 −1 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ #include <android/content/pm/BnDataLoaderStatusListener.h> #include <android/content/pm/DataLoaderParamsParcel.h> #include <android/content/pm/FileSystemControlParcel.h> #include <android/content/pm/IDataLoaderStatusListener.h> #include <android/os/incremental/BnIncrementalServiceConnector.h> #include <binder/IAppOpsCallback.h> Loading Loading @@ -160,6 +161,7 @@ private: DataLoaderStub(IncrementalService& service, MountId id, content::pm::DataLoaderParamsParcel&& params, content::pm::FileSystemControlParcel&& control, incfs::UniqueControl&& healthControl, const DataLoaderStatusListener* externalListener); ~DataLoaderStub(); // Cleans up the internal state and invalidates DataLoaderStub. Any subsequent calls will Loading @@ -178,6 +180,10 @@ private: private: binder::Status onStatusChanged(MountId mount, int newStatus) final; void addToCmdLooperLocked(); void removeFromCmdLooperLocked(); int onCmdLooperEvent(); bool isValid() const { return mId != kInvalidStorageId; } sp<content::pm::IDataLoader> getDataLoader(); Loading @@ -197,12 +203,15 @@ private: MountId mId = kInvalidStorageId; content::pm::DataLoaderParamsParcel mParams; content::pm::FileSystemControlParcel mControl; incfs::UniqueControl mHealthControl; DataLoaderStatusListener mListener; std::condition_variable mStatusCondition; int mCurrentStatus = content::pm::IDataLoaderStatusListener::DATA_LOADER_DESTROYED; int mTargetStatus = content::pm::IDataLoaderStatusListener::DATA_LOADER_DESTROYED; TimePoint mTargetStatusTs = {}; TimePoint mEarliestMissingPageTs{Clock::duration::max()}; }; using DataLoaderStubPtr = sp<DataLoaderStub>; Loading Loading @@ -300,12 +309,15 @@ private: const incfs::FileId& libFileId, std::string_view targetLibPath, Clock::time_point scheduledTs); void runCmdLooper(); private: const std::unique_ptr<VoldServiceWrapper> mVold; const std::unique_ptr<DataLoaderManagerWrapper> mDataLoaderManager; const std::unique_ptr<IncFsWrapper> mIncFs; const std::unique_ptr<AppOpsManagerWrapper> mAppOpsManager; const std::unique_ptr<JniWrapper> mJni; const std::unique_ptr<LooperWrapper> mLooper; const std::string mIncrementalDir; mutable std::mutex mLock; Loading @@ -319,13 +331,16 @@ private: std::atomic_bool mSystemReady = false; StorageId mNextId = 0; std::atomic_bool mRunning{true}; using Job = std::function<void()>; std::unordered_map<MountId, std::vector<Job>> mJobQueue; MountId mPendingJobsMount = kInvalidStorageId; std::condition_variable mJobCondition; std::mutex mJobMutex; std::thread mJobProcessor; bool mRunning = true; std::thread mCmdLooperThread; }; } // namespace android::incremental services/incremental/ServiceWrappers.cpp +21 −0 Original line number Diff line number Diff line Loading @@ -113,6 +113,23 @@ 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; Loading Loading @@ -203,6 +220,10 @@ std::unique_ptr<JniWrapper> RealServiceManager::getJni() { return std::make_unique<RealJniWrapper>(mJvm); } std::unique_ptr<LooperWrapper> RealServiceManager::getLooper() { return std::make_unique<RealLooperWrapper>(); } static JavaVM* getJavaVm(JNIEnv* env) { CHECK(env); JavaVM* jvm = nullptr; Loading services/incremental/ServiceWrappers.h +13 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ #include <binder/Status.h> #include <incfs.h> #include <jni.h> #include <utils/Looper.h> #include <memory> #include <span> Loading Loading @@ -106,6 +107,16 @@ public: virtual void initializeForCurrentThread() const = 0; }; class LooperWrapper { public: virtual ~LooperWrapper() = default; virtual int addFd(int fd, int ident, int events, android::Looper_callbackFunc callback, void* data) = 0; virtual int removeFd(int fd) = 0; virtual void wake() = 0; virtual int pollAll(int timeoutMillis) = 0; }; class ServiceManagerWrapper { public: virtual ~ServiceManagerWrapper() = default; Loading @@ -114,6 +125,7 @@ public: virtual std::unique_ptr<IncFsWrapper> getIncFs() = 0; virtual std::unique_ptr<AppOpsManagerWrapper> getAppOpsManager() = 0; virtual std::unique_ptr<JniWrapper> getJni() = 0; virtual std::unique_ptr<LooperWrapper> getLooper() = 0; }; // --- Real stuff --- Loading @@ -127,6 +139,7 @@ public: std::unique_ptr<IncFsWrapper> getIncFs() final; std::unique_ptr<AppOpsManagerWrapper> getAppOpsManager() final; std::unique_ptr<JniWrapper> getJni() final; std::unique_ptr<LooperWrapper> getLooper() final; private: template <class INTERFACE> Loading services/incremental/test/IncrementalServiceTest.cpp +112 −10 Original line number Diff line number Diff line Loading @@ -242,6 +242,9 @@ public: void setDataLoaderStatusDestroyed() { mListener->onStatusChanged(mId, IDataLoaderStatusListener::DATA_LOADER_DESTROYED); } void setDataLoaderStatusUnavailable() { mListener->onStatusChanged(mId, IDataLoaderStatusListener::DATA_LOADER_UNAVAILABLE); } binder::Status unbindFromDataLoaderOk(int32_t id) { if (mDataLoader) { if (auto status = mDataLoader->destroy(id); !status.isOk()) { Loading Loading @@ -286,6 +289,14 @@ public: void makeFileFails() { ON_CALL(*this, makeFile(_, _, _, _, _)).WillByDefault(Return(-1)); } void makeFileSuccess() { ON_CALL(*this, makeFile(_, _, _, _, _)).WillByDefault(Return(0)); } void openMountSuccess() { ON_CALL(*this, openMount(_)).WillByDefault(Invoke(this, &MockIncFs::openMountForHealth)); } static constexpr auto kPendingReadsFd = 42; Control openMountForHealth(std::string_view) { return UniqueControl(IncFs_CreateControl(-1, kPendingReadsFd, -1)); } RawMetadata getMountInfoMetadata(const Control& control, std::string_view path) { metadata::Mount m; Loading Loading @@ -346,7 +357,42 @@ class MockJniWrapper : public JniWrapper { public: MOCK_CONST_METHOD0(initializeForCurrentThread, void()); MockJniWrapper() { EXPECT_CALL(*this, initializeForCurrentThread()).Times(1); } MockJniWrapper() { EXPECT_CALL(*this, initializeForCurrentThread()).Times(2); } }; class MockLooperWrapper : public LooperWrapper { public: MOCK_METHOD5(addFd, int(int, int, int, android::Looper_callbackFunc, void*)); MOCK_METHOD1(removeFd, int(int)); MOCK_METHOD0(wake, void()); MOCK_METHOD1(pollAll, int(int)); MockLooperWrapper() { ON_CALL(*this, addFd(_, _, _, _, _)) .WillByDefault(Invoke(this, &MockLooperWrapper::storeCallback)); ON_CALL(*this, removeFd(_)).WillByDefault(Invoke(this, &MockLooperWrapper::clearCallback)); ON_CALL(*this, pollAll(_)).WillByDefault(Invoke(this, &MockLooperWrapper::sleepFor)); } int storeCallback(int, int, int, android::Looper_callbackFunc callback, void* data) { mCallback = callback; mCallbackData = data; return 0; } int clearCallback(int) { mCallback = nullptr; mCallbackData = nullptr; return 0; } int sleepFor(int timeoutMillis) { std::this_thread::sleep_for(std::chrono::milliseconds(timeoutMillis)); return 0; } android::Looper_callbackFunc mCallback = nullptr; void* mCallbackData = nullptr; }; class MockServiceManager : public ServiceManagerWrapper { Loading @@ -355,12 +401,14 @@ public: std::unique_ptr<MockDataLoaderManager> dataLoaderManager, std::unique_ptr<MockIncFs> incfs, std::unique_ptr<MockAppOpsManager> appOpsManager, std::unique_ptr<MockJniWrapper> jni) std::unique_ptr<MockJniWrapper> jni, std::unique_ptr<MockLooperWrapper> looper) : mVold(std::move(vold)), mDataLoaderManager(std::move(dataLoaderManager)), mIncFs(std::move(incfs)), mAppOpsManager(std::move(appOpsManager)), mJni(std::move(jni)) {} mJni(std::move(jni)), mLooper(std::move(looper)) {} std::unique_ptr<VoldServiceWrapper> getVoldService() final { return std::move(mVold); } std::unique_ptr<DataLoaderManagerWrapper> getDataLoaderManager() final { return std::move(mDataLoaderManager); Loading @@ -368,6 +416,7 @@ public: std::unique_ptr<IncFsWrapper> getIncFs() final { return std::move(mIncFs); } std::unique_ptr<AppOpsManagerWrapper> getAppOpsManager() final { return std::move(mAppOpsManager); } std::unique_ptr<JniWrapper> getJni() final { return std::move(mJni); } std::unique_ptr<LooperWrapper> getLooper() final { return std::move(mLooper); } private: std::unique_ptr<MockVoldService> mVold; Loading @@ -375,6 +424,7 @@ private: std::unique_ptr<MockIncFs> mIncFs; std::unique_ptr<MockAppOpsManager> mAppOpsManager; std::unique_ptr<MockJniWrapper> mJni; std::unique_ptr<MockLooperWrapper> mLooper; }; // --- IncrementalServiceTest --- Loading @@ -394,13 +444,16 @@ public: mAppOpsManager = appOps.get(); auto jni = std::make_unique<NiceMock<MockJniWrapper>>(); mJni = jni.get(); auto looper = std::make_unique<NiceMock<MockLooperWrapper>>(); mLooper = looper.get(); mIncrementalService = std::make_unique<IncrementalService>(MockServiceManager(std::move(vold), std::move( dataloaderManager), std::move(incFs), std::move(appOps), std::move(jni)), std::move(jni), std::move(looper)), mRootDir.path); mDataLoaderParcel.packageName = "com.test"; mDataLoaderParcel.arguments = "uri"; Loading Loading @@ -430,12 +483,13 @@ public: } protected: NiceMock<MockVoldService>* mVold; NiceMock<MockIncFs>* mIncFs; NiceMock<MockDataLoaderManager>* mDataLoaderManager; NiceMock<MockAppOpsManager>* mAppOpsManager; NiceMock<MockJniWrapper>* mJni; NiceMock<MockDataLoader>* mDataLoader; NiceMock<MockVoldService>* mVold = nullptr; NiceMock<MockIncFs>* mIncFs = nullptr; NiceMock<MockDataLoaderManager>* mDataLoaderManager = nullptr; NiceMock<MockAppOpsManager>* mAppOpsManager = nullptr; NiceMock<MockJniWrapper>* mJni = nullptr; NiceMock<MockLooperWrapper>* mLooper = nullptr; NiceMock<MockDataLoader>* mDataLoader = nullptr; std::unique_ptr<IncrementalService> mIncrementalService; TemporaryDir mRootDir; DataLoaderParamsParcel mDataLoaderParcel; Loading Loading @@ -593,6 +647,54 @@ TEST_F(IncrementalServiceTest, testStartDataLoaderPendingStart) { mDataLoaderManager->setDataLoaderStatusCreated(); } TEST_F(IncrementalServiceTest, testStartDataLoaderCreateUnavailable) { mVold->mountIncFsSuccess(); mIncFs->makeFileSuccess(); mVold->bindMountSuccess(); mDataLoader->initializeCreateOkNoStatus(); mDataLoaderManager->bindToDataLoaderSuccess(); mDataLoaderManager->getDataLoaderSuccess(); EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(1); EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(1); EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(1); EXPECT_CALL(*mDataLoader, start(_)).Times(0); EXPECT_CALL(*mDataLoader, destroy(_)).Times(1); EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2); TemporaryDir tempDir; int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {}, IncrementalService::CreateOptions::CreateNew); ASSERT_GE(storageId, 0); mDataLoaderManager->setDataLoaderStatusUnavailable(); } TEST_F(IncrementalServiceTest, testStartDataLoaderRecreateOnPendingReads) { mVold->mountIncFsSuccess(); mIncFs->makeFileSuccess(); mIncFs->openMountSuccess(); mVold->bindMountSuccess(); mDataLoader->initializeCreateOkNoStatus(); mDataLoaderManager->bindToDataLoaderSuccess(); mDataLoaderManager->getDataLoaderSuccess(); EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(2); EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(1); EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(2); EXPECT_CALL(*mDataLoader, start(_)).Times(0); EXPECT_CALL(*mDataLoader, destroy(_)).Times(1); EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2); EXPECT_CALL(*mLooper, addFd(MockIncFs::kPendingReadsFd, _, _, _, _)).Times(1); EXPECT_CALL(*mLooper, removeFd(MockIncFs::kPendingReadsFd)).Times(1); TemporaryDir tempDir; int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {}, IncrementalService::CreateOptions::CreateNew); ASSERT_GE(storageId, 0); mDataLoaderManager->setDataLoaderStatusUnavailable(); ASSERT_NE(nullptr, mLooper->mCallback); ASSERT_NE(nullptr, mLooper->mCallbackData); mLooper->mCallback(-1, -1, mLooper->mCallbackData); } TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccess) { mVold->mountIncFsSuccess(); mIncFs->makeFileSuccess(); Loading Loading
services/incremental/IncrementalService.cpp +107 −9 Original line number Diff line number Diff line Loading @@ -266,6 +266,7 @@ IncrementalService::IncrementalService(ServiceManagerWrapper&& sm, std::string_v mIncFs(sm.getIncFs()), mAppOpsManager(sm.getAppOpsManager()), mJni(sm.getJni()), mLooper(sm.getLooper()), mIncrementalDir(rootDir) { if (!mVold) { LOG(FATAL) << "Vold service is unavailable"; Loading @@ -276,12 +277,22 @@ IncrementalService::IncrementalService(ServiceManagerWrapper&& sm, std::string_v if (!mAppOpsManager) { LOG(FATAL) << "AppOpsManager is unavailable"; } if (!mJni) { LOG(FATAL) << "JNI is unavailable"; } if (!mLooper) { LOG(FATAL) << "Looper is unavailable"; } mJobQueue.reserve(16); mJobProcessor = std::thread([this]() { mJni->initializeForCurrentThread(); runJobProcessing(); }); mCmdLooperThread = std::thread([this]() { mJni->initializeForCurrentThread(); runCmdLooper(); }); const auto mountedRootNames = adoptMountedInstances(); mountExistingImages(mountedRootNames); Loading @@ -294,6 +305,7 @@ IncrementalService::~IncrementalService() { } mJobCondition.notify_all(); mJobProcessor.join(); mCmdLooperThread.join(); } static const char* toString(IncrementalService::BindKind kind) { Loading Loading @@ -1315,6 +1327,13 @@ bool IncrementalService::mountExistingImage(std::string_view root) { return true; } void IncrementalService::runCmdLooper() { constexpr auto kTimeoutMsecs = 1000; while (mRunning.load(std::memory_order_relaxed)) { mLooper->pollAll(kTimeoutMsecs); } } IncrementalService::DataLoaderStubPtr IncrementalService::prepareDataLoader( IncFsMount& ifs, DataLoaderParamsParcel&& params, const DataLoaderStatusListener* externalListener) { Loading @@ -1337,8 +1356,16 @@ void IncrementalService::prepareDataLoaderLocked(IncFsMount& ifs, DataLoaderPara fsControlParcel.incremental->log.reset(dup(ifs.control.logs())); fsControlParcel.service = new IncrementalServiceConnector(*this, ifs.mountId); ifs.dataLoaderStub = new DataLoaderStub(*this, ifs.mountId, std::move(params), std::move(fsControlParcel), externalListener); incfs::UniqueControl healthControl = mIncFs->openMount(ifs.root.c_str()); if (healthControl.pendingReads() < 0) { LOG(ERROR) << "Failed to open health control for: " << ifs.root << "(" << healthControl.cmd() << ":" << healthControl.pendingReads() << ":" << healthControl.logs() << ")"; } ifs.dataLoaderStub = new DataLoaderStub(*this, ifs.mountId, std::move(params), std::move(fsControlParcel), std::move(healthControl), externalListener); } template <class Duration> Loading Loading @@ -1658,24 +1685,34 @@ void IncrementalService::onAppOpChanged(const std::string& packageName) { IncrementalService::DataLoaderStub::DataLoaderStub(IncrementalService& service, MountId id, DataLoaderParamsParcel&& params, FileSystemControlParcel&& control, incfs::UniqueControl&& healthControl, const DataLoaderStatusListener* externalListener) : mService(service), mId(id), mParams(std::move(params)), mControl(std::move(control)), mHealthControl(std::move(healthControl)), mListener(externalListener ? *externalListener : DataLoaderStatusListener()) { addToCmdLooperLocked(); } IncrementalService::DataLoaderStub::~DataLoaderStub() = default; IncrementalService::DataLoaderStub::~DataLoaderStub() { if (mId != kInvalidStorageId) { cleanupResources(); } } void IncrementalService::DataLoaderStub::cleanupResources() { requestDestroy(); auto now = Clock::now(); std::unique_lock lock(mMutex); removeFromCmdLooperLocked(); mParams = {}; mControl = {}; mHealthControl = {}; mStatusCondition.wait_until(lock, now + 60s, [this] { return mCurrentStatus == IDataLoaderStatusListener::DATA_LOADER_DESTROYED; }); Loading Loading @@ -1710,21 +1747,19 @@ bool IncrementalService::DataLoaderStub::requestDestroy() { } bool IncrementalService::DataLoaderStub::setTargetStatus(int newStatus) { int oldStatus, curStatus; { std::unique_lock lock(mMutex); oldStatus = mTargetStatus; curStatus = mCurrentStatus; setTargetStatusLocked(newStatus); } LOG(DEBUG) << "Target status update for DataLoader " << mId << ": " << oldStatus << " -> " << newStatus << " (current " << curStatus << ")"; return fsmStep(); } void IncrementalService::DataLoaderStub::setTargetStatusLocked(int status) { auto oldStatus = mTargetStatus; mTargetStatus = status; mTargetStatusTs = Clock::now(); LOG(DEBUG) << "Target status update for DataLoader " << mId << ": " << oldStatus << " -> " << status << " (current " << mCurrentStatus << ")"; } bool IncrementalService::DataLoaderStub::bind() { Loading Loading @@ -1860,12 +1895,75 @@ binder::Status IncrementalService::DataLoaderStub::onStatusChanged(MountId mount return binder::Status::ok(); } void IncrementalService::DataLoaderStub::addToCmdLooperLocked() { const auto pendingReadsFd = mHealthControl.pendingReads(); if (pendingReadsFd < 0) { return; } mService.mLooper->addFd( pendingReadsFd, android::Looper::POLL_CALLBACK, android::Looper::EVENT_INPUT, [](int, int, void* data) -> int { auto&& self = (DataLoaderStub*)data; return self->onCmdLooperEvent(); }, this); mService.mLooper->wake(); } void IncrementalService::DataLoaderStub::removeFromCmdLooperLocked() { const auto pendingReadsFd = mHealthControl.pendingReads(); if (pendingReadsFd < 0) { return; } mService.mLooper->removeFd(pendingReadsFd); mService.mLooper->wake(); } int IncrementalService::DataLoaderStub::onCmdLooperEvent() { if (!mService.mRunning.load(std::memory_order_relaxed)) { return 0; } bool pendingReadsOccur = false; { std::unique_lock lock(mMutex); const auto now = Clock::now(); if (now < mEarliestMissingPageTs) { // Transition: duration::max -> now. mEarliestMissingPageTs = now; pendingReadsOccur = true; } } if (pendingReadsOccur) { LOG(INFO) << "Pending reads occur for, requesting start for: " << mId; requestStart(); } { // Drop pending reads. static constexpr auto kMaxDropIterations = 3; std::unique_lock lock(mMutex); for (int i = 0; i < kMaxDropIterations; ++i) { if (IncFs_DropPendingReads(mHealthControl) <= 0) { break; } } } return 1; } void IncrementalService::DataLoaderStub::onDump(int fd) { dprintf(fd, " dataLoader: {\n"); dprintf(fd, " currentStatus: %d\n", mCurrentStatus); dprintf(fd, " targetStatus: %d\n", mTargetStatus); dprintf(fd, " targetStatusTs: %lldmcs\n", (long long)(elapsedMcs(mTargetStatusTs, Clock::now()))); dprintf(fd, " earliestMissingPageTs: %lldmcs\n", (long long)(elapsedMcs(mEarliestMissingPageTs, Clock::now()))); const auto& params = mParams; dprintf(fd, " dataLoaderParams: {\n"); dprintf(fd, " type: %s\n", toString(params.type).c_str()); Loading
services/incremental/IncrementalService.h +16 −1 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ #include <android/content/pm/BnDataLoaderStatusListener.h> #include <android/content/pm/DataLoaderParamsParcel.h> #include <android/content/pm/FileSystemControlParcel.h> #include <android/content/pm/IDataLoaderStatusListener.h> #include <android/os/incremental/BnIncrementalServiceConnector.h> #include <binder/IAppOpsCallback.h> Loading Loading @@ -160,6 +161,7 @@ private: DataLoaderStub(IncrementalService& service, MountId id, content::pm::DataLoaderParamsParcel&& params, content::pm::FileSystemControlParcel&& control, incfs::UniqueControl&& healthControl, const DataLoaderStatusListener* externalListener); ~DataLoaderStub(); // Cleans up the internal state and invalidates DataLoaderStub. Any subsequent calls will Loading @@ -178,6 +180,10 @@ private: private: binder::Status onStatusChanged(MountId mount, int newStatus) final; void addToCmdLooperLocked(); void removeFromCmdLooperLocked(); int onCmdLooperEvent(); bool isValid() const { return mId != kInvalidStorageId; } sp<content::pm::IDataLoader> getDataLoader(); Loading @@ -197,12 +203,15 @@ private: MountId mId = kInvalidStorageId; content::pm::DataLoaderParamsParcel mParams; content::pm::FileSystemControlParcel mControl; incfs::UniqueControl mHealthControl; DataLoaderStatusListener mListener; std::condition_variable mStatusCondition; int mCurrentStatus = content::pm::IDataLoaderStatusListener::DATA_LOADER_DESTROYED; int mTargetStatus = content::pm::IDataLoaderStatusListener::DATA_LOADER_DESTROYED; TimePoint mTargetStatusTs = {}; TimePoint mEarliestMissingPageTs{Clock::duration::max()}; }; using DataLoaderStubPtr = sp<DataLoaderStub>; Loading Loading @@ -300,12 +309,15 @@ private: const incfs::FileId& libFileId, std::string_view targetLibPath, Clock::time_point scheduledTs); void runCmdLooper(); private: const std::unique_ptr<VoldServiceWrapper> mVold; const std::unique_ptr<DataLoaderManagerWrapper> mDataLoaderManager; const std::unique_ptr<IncFsWrapper> mIncFs; const std::unique_ptr<AppOpsManagerWrapper> mAppOpsManager; const std::unique_ptr<JniWrapper> mJni; const std::unique_ptr<LooperWrapper> mLooper; const std::string mIncrementalDir; mutable std::mutex mLock; Loading @@ -319,13 +331,16 @@ private: std::atomic_bool mSystemReady = false; StorageId mNextId = 0; std::atomic_bool mRunning{true}; using Job = std::function<void()>; std::unordered_map<MountId, std::vector<Job>> mJobQueue; MountId mPendingJobsMount = kInvalidStorageId; std::condition_variable mJobCondition; std::mutex mJobMutex; std::thread mJobProcessor; bool mRunning = true; std::thread mCmdLooperThread; }; } // namespace android::incremental
services/incremental/ServiceWrappers.cpp +21 −0 Original line number Diff line number Diff line Loading @@ -113,6 +113,23 @@ 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; Loading Loading @@ -203,6 +220,10 @@ std::unique_ptr<JniWrapper> RealServiceManager::getJni() { return std::make_unique<RealJniWrapper>(mJvm); } std::unique_ptr<LooperWrapper> RealServiceManager::getLooper() { return std::make_unique<RealLooperWrapper>(); } static JavaVM* getJavaVm(JNIEnv* env) { CHECK(env); JavaVM* jvm = nullptr; Loading
services/incremental/ServiceWrappers.h +13 −0 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ #include <binder/Status.h> #include <incfs.h> #include <jni.h> #include <utils/Looper.h> #include <memory> #include <span> Loading Loading @@ -106,6 +107,16 @@ public: virtual void initializeForCurrentThread() const = 0; }; class LooperWrapper { public: virtual ~LooperWrapper() = default; virtual int addFd(int fd, int ident, int events, android::Looper_callbackFunc callback, void* data) = 0; virtual int removeFd(int fd) = 0; virtual void wake() = 0; virtual int pollAll(int timeoutMillis) = 0; }; class ServiceManagerWrapper { public: virtual ~ServiceManagerWrapper() = default; Loading @@ -114,6 +125,7 @@ public: virtual std::unique_ptr<IncFsWrapper> getIncFs() = 0; virtual std::unique_ptr<AppOpsManagerWrapper> getAppOpsManager() = 0; virtual std::unique_ptr<JniWrapper> getJni() = 0; virtual std::unique_ptr<LooperWrapper> getLooper() = 0; }; // --- Real stuff --- Loading @@ -127,6 +139,7 @@ public: std::unique_ptr<IncFsWrapper> getIncFs() final; std::unique_ptr<AppOpsManagerWrapper> getAppOpsManager() final; std::unique_ptr<JniWrapper> getJni() final; std::unique_ptr<LooperWrapper> getLooper() final; private: template <class INTERFACE> Loading
services/incremental/test/IncrementalServiceTest.cpp +112 −10 Original line number Diff line number Diff line Loading @@ -242,6 +242,9 @@ public: void setDataLoaderStatusDestroyed() { mListener->onStatusChanged(mId, IDataLoaderStatusListener::DATA_LOADER_DESTROYED); } void setDataLoaderStatusUnavailable() { mListener->onStatusChanged(mId, IDataLoaderStatusListener::DATA_LOADER_UNAVAILABLE); } binder::Status unbindFromDataLoaderOk(int32_t id) { if (mDataLoader) { if (auto status = mDataLoader->destroy(id); !status.isOk()) { Loading Loading @@ -286,6 +289,14 @@ public: void makeFileFails() { ON_CALL(*this, makeFile(_, _, _, _, _)).WillByDefault(Return(-1)); } void makeFileSuccess() { ON_CALL(*this, makeFile(_, _, _, _, _)).WillByDefault(Return(0)); } void openMountSuccess() { ON_CALL(*this, openMount(_)).WillByDefault(Invoke(this, &MockIncFs::openMountForHealth)); } static constexpr auto kPendingReadsFd = 42; Control openMountForHealth(std::string_view) { return UniqueControl(IncFs_CreateControl(-1, kPendingReadsFd, -1)); } RawMetadata getMountInfoMetadata(const Control& control, std::string_view path) { metadata::Mount m; Loading Loading @@ -346,7 +357,42 @@ class MockJniWrapper : public JniWrapper { public: MOCK_CONST_METHOD0(initializeForCurrentThread, void()); MockJniWrapper() { EXPECT_CALL(*this, initializeForCurrentThread()).Times(1); } MockJniWrapper() { EXPECT_CALL(*this, initializeForCurrentThread()).Times(2); } }; class MockLooperWrapper : public LooperWrapper { public: MOCK_METHOD5(addFd, int(int, int, int, android::Looper_callbackFunc, void*)); MOCK_METHOD1(removeFd, int(int)); MOCK_METHOD0(wake, void()); MOCK_METHOD1(pollAll, int(int)); MockLooperWrapper() { ON_CALL(*this, addFd(_, _, _, _, _)) .WillByDefault(Invoke(this, &MockLooperWrapper::storeCallback)); ON_CALL(*this, removeFd(_)).WillByDefault(Invoke(this, &MockLooperWrapper::clearCallback)); ON_CALL(*this, pollAll(_)).WillByDefault(Invoke(this, &MockLooperWrapper::sleepFor)); } int storeCallback(int, int, int, android::Looper_callbackFunc callback, void* data) { mCallback = callback; mCallbackData = data; return 0; } int clearCallback(int) { mCallback = nullptr; mCallbackData = nullptr; return 0; } int sleepFor(int timeoutMillis) { std::this_thread::sleep_for(std::chrono::milliseconds(timeoutMillis)); return 0; } android::Looper_callbackFunc mCallback = nullptr; void* mCallbackData = nullptr; }; class MockServiceManager : public ServiceManagerWrapper { Loading @@ -355,12 +401,14 @@ public: std::unique_ptr<MockDataLoaderManager> dataLoaderManager, std::unique_ptr<MockIncFs> incfs, std::unique_ptr<MockAppOpsManager> appOpsManager, std::unique_ptr<MockJniWrapper> jni) std::unique_ptr<MockJniWrapper> jni, std::unique_ptr<MockLooperWrapper> looper) : mVold(std::move(vold)), mDataLoaderManager(std::move(dataLoaderManager)), mIncFs(std::move(incfs)), mAppOpsManager(std::move(appOpsManager)), mJni(std::move(jni)) {} mJni(std::move(jni)), mLooper(std::move(looper)) {} std::unique_ptr<VoldServiceWrapper> getVoldService() final { return std::move(mVold); } std::unique_ptr<DataLoaderManagerWrapper> getDataLoaderManager() final { return std::move(mDataLoaderManager); Loading @@ -368,6 +416,7 @@ public: std::unique_ptr<IncFsWrapper> getIncFs() final { return std::move(mIncFs); } std::unique_ptr<AppOpsManagerWrapper> getAppOpsManager() final { return std::move(mAppOpsManager); } std::unique_ptr<JniWrapper> getJni() final { return std::move(mJni); } std::unique_ptr<LooperWrapper> getLooper() final { return std::move(mLooper); } private: std::unique_ptr<MockVoldService> mVold; Loading @@ -375,6 +424,7 @@ private: std::unique_ptr<MockIncFs> mIncFs; std::unique_ptr<MockAppOpsManager> mAppOpsManager; std::unique_ptr<MockJniWrapper> mJni; std::unique_ptr<MockLooperWrapper> mLooper; }; // --- IncrementalServiceTest --- Loading @@ -394,13 +444,16 @@ public: mAppOpsManager = appOps.get(); auto jni = std::make_unique<NiceMock<MockJniWrapper>>(); mJni = jni.get(); auto looper = std::make_unique<NiceMock<MockLooperWrapper>>(); mLooper = looper.get(); mIncrementalService = std::make_unique<IncrementalService>(MockServiceManager(std::move(vold), std::move( dataloaderManager), std::move(incFs), std::move(appOps), std::move(jni)), std::move(jni), std::move(looper)), mRootDir.path); mDataLoaderParcel.packageName = "com.test"; mDataLoaderParcel.arguments = "uri"; Loading Loading @@ -430,12 +483,13 @@ public: } protected: NiceMock<MockVoldService>* mVold; NiceMock<MockIncFs>* mIncFs; NiceMock<MockDataLoaderManager>* mDataLoaderManager; NiceMock<MockAppOpsManager>* mAppOpsManager; NiceMock<MockJniWrapper>* mJni; NiceMock<MockDataLoader>* mDataLoader; NiceMock<MockVoldService>* mVold = nullptr; NiceMock<MockIncFs>* mIncFs = nullptr; NiceMock<MockDataLoaderManager>* mDataLoaderManager = nullptr; NiceMock<MockAppOpsManager>* mAppOpsManager = nullptr; NiceMock<MockJniWrapper>* mJni = nullptr; NiceMock<MockLooperWrapper>* mLooper = nullptr; NiceMock<MockDataLoader>* mDataLoader = nullptr; std::unique_ptr<IncrementalService> mIncrementalService; TemporaryDir mRootDir; DataLoaderParamsParcel mDataLoaderParcel; Loading Loading @@ -593,6 +647,54 @@ TEST_F(IncrementalServiceTest, testStartDataLoaderPendingStart) { mDataLoaderManager->setDataLoaderStatusCreated(); } TEST_F(IncrementalServiceTest, testStartDataLoaderCreateUnavailable) { mVold->mountIncFsSuccess(); mIncFs->makeFileSuccess(); mVold->bindMountSuccess(); mDataLoader->initializeCreateOkNoStatus(); mDataLoaderManager->bindToDataLoaderSuccess(); mDataLoaderManager->getDataLoaderSuccess(); EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(1); EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(1); EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(1); EXPECT_CALL(*mDataLoader, start(_)).Times(0); EXPECT_CALL(*mDataLoader, destroy(_)).Times(1); EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2); TemporaryDir tempDir; int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {}, IncrementalService::CreateOptions::CreateNew); ASSERT_GE(storageId, 0); mDataLoaderManager->setDataLoaderStatusUnavailable(); } TEST_F(IncrementalServiceTest, testStartDataLoaderRecreateOnPendingReads) { mVold->mountIncFsSuccess(); mIncFs->makeFileSuccess(); mIncFs->openMountSuccess(); mVold->bindMountSuccess(); mDataLoader->initializeCreateOkNoStatus(); mDataLoaderManager->bindToDataLoaderSuccess(); mDataLoaderManager->getDataLoaderSuccess(); EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _)).Times(2); EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(1); EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(2); EXPECT_CALL(*mDataLoader, start(_)).Times(0); EXPECT_CALL(*mDataLoader, destroy(_)).Times(1); EXPECT_CALL(*mVold, unmountIncFs(_)).Times(2); EXPECT_CALL(*mLooper, addFd(MockIncFs::kPendingReadsFd, _, _, _, _)).Times(1); EXPECT_CALL(*mLooper, removeFd(MockIncFs::kPendingReadsFd)).Times(1); TemporaryDir tempDir; int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel), {}, IncrementalService::CreateOptions::CreateNew); ASSERT_GE(storageId, 0); mDataLoaderManager->setDataLoaderStatusUnavailable(); ASSERT_NE(nullptr, mLooper->mCallback); ASSERT_NE(nullptr, mLooper->mCallbackData); mLooper->mCallback(-1, -1, mLooper->mCallbackData); } TEST_F(IncrementalServiceTest, testSetIncFsMountOptionsSuccess) { mVold->mountIncFsSuccess(); mIncFs->makeFileSuccess(); Loading