Retry on unavailable.
Bug: 182214420
Test: atest PackageManagerShellCommandTest PackageManagerShellCommandIncrementalTest IncrementalServiceTest PackageManagerServiceTest ChecksumsTest
Change-Id: Iaf61b6825ced45ffdc7e9c87dfea830e50633476
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index 4ce336d..9b42799 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -2660,9 +2660,6 @@
}
switch (targetStatus) {
- case IDataLoaderStatusListener::DATA_LOADER_UNAVAILABLE:
- // Do nothing, this is a reset state.
- break;
case IDataLoaderStatusListener::DATA_LOADER_DESTROYED: {
switch (currentStatus) {
case IDataLoaderStatusListener::DATA_LOADER_BINDING:
@@ -2683,8 +2680,12 @@
}
case IDataLoaderStatusListener::DATA_LOADER_CREATED:
switch (currentStatus) {
- case IDataLoaderStatusListener::DATA_LOADER_DESTROYED:
case IDataLoaderStatusListener::DATA_LOADER_UNAVAILABLE:
+ case IDataLoaderStatusListener::DATA_LOADER_UNRECOVERABLE:
+ // Before binding need to make sure we are unbound.
+ // Otherwise we'll get stuck binding.
+ return destroy();
+ case IDataLoaderStatusListener::DATA_LOADER_DESTROYED:
case IDataLoaderStatusListener::DATA_LOADER_BINDING:
return bind();
case IDataLoaderStatusListener::DATA_LOADER_BOUND:
@@ -2709,7 +2710,8 @@
<< ", but got: " << mountId;
return binder::Status::fromServiceSpecificError(-EPERM, "Mount ID mismatch.");
}
- if (newStatus == IDataLoaderStatusListener::DATA_LOADER_UNRECOVERABLE) {
+ if (newStatus == IDataLoaderStatusListener::DATA_LOADER_UNAVAILABLE ||
+ newStatus == IDataLoaderStatusListener::DATA_LOADER_UNRECOVERABLE) {
// User-provided status, let's postpone the handling to avoid possible deadlocks.
mService.addTimedJob(*mService.mTimedQueue, id(), Constants::userStatusDelay,
[this, newStatus]() { setCurrentStatus(newStatus); });
@@ -2721,7 +2723,7 @@
}
void IncrementalService::DataLoaderStub::setCurrentStatus(int newStatus) {
- int targetStatus, oldStatus;
+ int oldStatus, oldTargetStatus, newTargetStatus;
DataLoaderStatusListener listener;
{
std::unique_lock lock(mMutex);
@@ -2730,22 +2732,31 @@
}
oldStatus = mCurrentStatus;
- targetStatus = mTargetStatus;
+ oldTargetStatus = mTargetStatus;
listener = mStatusListener;
// Change the status.
mCurrentStatus = newStatus;
mCurrentStatusTs = mService.mClock->now();
- if (mCurrentStatus == IDataLoaderStatusListener::DATA_LOADER_UNAVAILABLE ||
- mCurrentStatus == IDataLoaderStatusListener::DATA_LOADER_UNRECOVERABLE) {
- // For unavailable, unbind from DataLoader to ensure proper re-commit.
- setTargetStatusLocked(IDataLoaderStatusListener::DATA_LOADER_DESTROYED);
+ switch (mCurrentStatus) {
+ case IDataLoaderStatusListener::DATA_LOADER_UNAVAILABLE:
+ // Unavailable, retry.
+ setTargetStatusLocked(IDataLoaderStatusListener::DATA_LOADER_STARTED);
+ break;
+ case IDataLoaderStatusListener::DATA_LOADER_UNRECOVERABLE:
+ // Unrecoverable, just unbind.
+ setTargetStatusLocked(IDataLoaderStatusListener::DATA_LOADER_DESTROYED);
+ break;
+ default:
+ break;
}
+
+ newTargetStatus = mTargetStatus;
}
LOG(DEBUG) << "Current status update for DataLoader " << id() << ": " << oldStatus << " -> "
- << newStatus << " (target " << targetStatus << ")";
+ << newStatus << " (target " << oldTargetStatus << " -> " << newTargetStatus << ")";
if (listener) {
listener->onStatusChanged(id(), newStatus);
diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp
index 6a3d953..cdf2e89 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -126,7 +126,7 @@
class MockDataLoader : public IDataLoader {
public:
MockDataLoader() {
- ON_CALL(*this, create(_, _, _, _)).WillByDefault(Invoke(this, &MockDataLoader::createOk));
+ initializeCreateOk();
ON_CALL(*this, start(_)).WillByDefault(Invoke(this, &MockDataLoader::startOk));
ON_CALL(*this, stop(_)).WillByDefault(Invoke(this, &MockDataLoader::stopOk));
ON_CALL(*this, destroy(_)).WillByDefault(Invoke(this, &MockDataLoader::destroyOk));
@@ -145,6 +145,10 @@
binder::Status(int32_t id, const std::vector<InstallationFileParcel>& addedFiles,
const std::vector<std::string>& removedFiles));
+ void initializeCreateOk() {
+ ON_CALL(*this, create(_, _, _, _)).WillByDefault(Invoke(this, &MockDataLoader::createOk));
+ }
+
void initializeCreateOkNoStatus() {
ON_CALL(*this, create(_, _, _, _))
.WillByDefault(Invoke(this, &MockDataLoader::createOkNoStatus));
@@ -275,6 +279,14 @@
}
return binder::Status::ok();
}
+ binder::Status bindToDataLoaderOkWithNoDelay(int32_t mountId,
+ const DataLoaderParamsParcel& params,
+ int bindDelayMs,
+ const sp<IDataLoaderStatusListener>& listener,
+ bool* _aidl_return) {
+ CHECK(bindDelayMs == 0) << bindDelayMs;
+ return bindToDataLoaderOk(mountId, params, bindDelayMs, listener, _aidl_return);
+ }
binder::Status bindToDataLoaderOkWith1sDelay(int32_t mountId,
const DataLoaderParamsParcel& params,
int bindDelayMs,
@@ -338,14 +350,13 @@
mListener->onStatusChanged(mId, IDataLoaderStatusListener::DATA_LOADER_UNRECOVERABLE);
}
binder::Status unbindFromDataLoaderOk(int32_t id) {
+ mBindDelayMs = -1;
if (mDataLoader) {
if (auto status = mDataLoader->destroy(id); !status.isOk()) {
return status;
}
mDataLoader = nullptr;
- }
- mBindDelayMs = -1;
- if (mListener) {
+ } else if (mListener) {
mListener->onStatusChanged(id, IDataLoaderStatusListener::DATA_LOADER_DESTROYED);
}
return binder::Status::ok();
@@ -1156,12 +1167,81 @@
ASSERT_GE(storageId, 0);
ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {},
{}, {}));
- mDataLoaderManager->setDataLoaderStatusUnavailable();
+ mDataLoaderManager->setDataLoaderStatusUnrecoverable();
+
+ // Timed callback present.
+ ASSERT_EQ(storageId, mTimedQueue->mId);
+ ASSERT_GE(mTimedQueue->mAfter, 10ms);
+ auto timedCallback = mTimedQueue->mWhat;
+ mTimedQueue->clearJob(storageId);
+
+ // First callback call to propagate unrecoverable.
+ timedCallback();
+
+ // And second call to trigger recreation.
ASSERT_NE(nullptr, mLooper->mCallback);
ASSERT_NE(nullptr, mLooper->mCallbackData);
mLooper->mCallback(-1, -1, mLooper->mCallbackData);
}
+TEST_F(IncrementalServiceTest, testStartDataLoaderUnavailable) {
+ mIncFs->openMountSuccess();
+ mDataLoader->initializeCreateOkNoStatus();
+
+ EXPECT_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _)).Times(3);
+ EXPECT_CALL(*mDataLoaderManager, unbindFromDataLoader(_)).Times(3);
+ EXPECT_CALL(*mDataLoader, create(_, _, _, _)).Times(3);
+ EXPECT_CALL(*mDataLoader, start(_)).Times(1);
+ EXPECT_CALL(*mDataLoader, destroy(_)).Times(2);
+ 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, mDataLoaderParcel,
+ IncrementalService::CreateOptions::CreateNew);
+ ASSERT_GE(storageId, 0);
+
+ ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
+ .WillByDefault(Invoke(mDataLoaderManager,
+ &MockDataLoaderManager::bindToDataLoaderOkWithNoDelay));
+
+ ASSERT_TRUE(mIncrementalService->startLoading(storageId, std::move(mDataLoaderParcel), {}, {},
+ {}, {}));
+
+ // Unavailable.
+ mDataLoaderManager->setDataLoaderStatusUnavailable();
+
+ // Timed callback present.
+ ASSERT_EQ(storageId, mTimedQueue->mId);
+ ASSERT_GE(mTimedQueue->mAfter, 10ms);
+ auto timedCallback = mTimedQueue->mWhat;
+ mTimedQueue->clearJob(storageId);
+
+ // Propagating unavailable and expecting it to trigger rebind with 1s retry delay.
+ ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
+ .WillByDefault(Invoke(mDataLoaderManager,
+ &MockDataLoaderManager::bindToDataLoaderOkWith1sDelay));
+ timedCallback();
+
+ // Unavailable #2.
+ mDataLoaderManager->setDataLoaderStatusUnavailable();
+
+ // Timed callback present.
+ ASSERT_EQ(storageId, mTimedQueue->mId);
+ ASSERT_GE(mTimedQueue->mAfter, 10ms);
+ timedCallback = mTimedQueue->mWhat;
+ mTimedQueue->clearJob(storageId);
+
+ // Propagating unavailable and expecting it to trigger rebind with 10s retry delay.
+ // This time succeed.
+ mDataLoader->initializeCreateOk();
+ ON_CALL(*mDataLoaderManager, bindToDataLoader(_, _, _, _, _))
+ .WillByDefault(Invoke(mDataLoaderManager,
+ &MockDataLoaderManager::bindToDataLoaderOkWith10sDelay));
+ timedCallback();
+}
+
TEST_F(IncrementalServiceTest, testStartDataLoaderUnhealthyStorage) {
mIncFs->openMountSuccess();