Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cca2c111 authored by Alex Buynytskyy's avatar Alex Buynytskyy
Browse files

Lifecycle: detecting pending reads.

Once pending read detected, try to start the dataloader.

Bug: 153874006
Test: test PackageManagerShellCommandTest PackageManagerShellCommandIncrementalTest IncrementalServiceTest

Change-Id: Ia8169ccbb0f710317715e6fddb9bc6a718543766
parent 7efdd5d9
Loading
Loading
Loading
Loading
+107 −9
Original line number Diff line number Diff line
@@ -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";
@@ -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);
@@ -294,6 +305,7 @@ IncrementalService::~IncrementalService() {
    }
    mJobCondition.notify_all();
    mJobProcessor.join();
    mCmdLooperThread.join();
}

static const char* toString(IncrementalService::BindKind kind) {
@@ -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) {
@@ -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>
@@ -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;
    });
@@ -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() {
@@ -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());
+16 −1
Original line number Diff line number Diff line
@@ -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>
@@ -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
@@ -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();

@@ -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>;

@@ -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;
@@ -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
+21 −0
Original line number Diff line number Diff line
@@ -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;
@@ -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;
+13 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@
#include <binder/Status.h>
#include <incfs.h>
#include <jni.h>
#include <utils/Looper.h>

#include <memory>
#include <span>
@@ -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;
@@ -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 ---
@@ -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>
+112 −10
Original line number Diff line number Diff line
@@ -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()) {
@@ -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;
@@ -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 {
@@ -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);
@@ -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;
@@ -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 ---
@@ -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";
@@ -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;
@@ -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();