Lifecycle: detecting pending reads.

Once pending read detected, try to start the dataloader.

Bug: 153874006
Test: test PackageManagerShellCommandTest PackageManagerShellCommandIncrementalTest IncrementalServiceTest

Change-Id: Ia8169ccbb0f710317715e6fddb9bc6a718543766
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index c3c2157..d412a19 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -266,6 +266,7 @@
         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 @@
     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 @@
     }
     mJobCondition.notify_all();
     mJobProcessor.join();
+    mCmdLooperThread.join();
 }
 
 static const char* toString(IncrementalService::BindKind kind) {
@@ -1315,6 +1327,13 @@
     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 @@
     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 @@
 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::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 @@
     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());
diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h
index cf310b1..640ca53 100644
--- a/services/incremental/IncrementalService.h
+++ b/services/incremental/IncrementalService.h
@@ -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 @@
         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:
         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 @@
         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 @@
                         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 @@
     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
diff --git a/services/incremental/ServiceWrappers.cpp b/services/incremental/ServiceWrappers.cpp
index 85f7441..08fb486 100644
--- a/services/incremental/ServiceWrappers.cpp
+++ b/services/incremental/ServiceWrappers.cpp
@@ -113,6 +113,23 @@
     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 @@
     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;
diff --git a/services/incremental/ServiceWrappers.h b/services/incremental/ServiceWrappers.h
index 3792830..abbf2f4 100644
--- a/services/incremental/ServiceWrappers.h
+++ b/services/incremental/ServiceWrappers.h
@@ -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 @@
     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 @@
     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 @@
     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>
diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp
index 2205bfe..325218d 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -242,6 +242,9 @@
     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 @@
 
     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 @@
 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 @@
                        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 @@
     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 @@
     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 @@
         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 @@
     }
 
 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 @@
     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();