[incremental/pm] set health listener on commit and on reboot
This changes allow Incremental Service to directly report health status
to package manager service.
A health listener is created during package installation session to
monitor incremental storage health. After commit, a new listener is
created and will overwrite the old one.
The new listener will listen to incremental storage health and report
the status to package manager service, which will then send the status
to IncrementalStates, where the startability state and unstartable
reason might change, based on the health status code.
During reboot, for each incremental package, if it is not fully loaded,
the package manager service will register a health status listener to
continue monitor the health status of this package.
Test: unit test
Test: manual
BUG: 170435166
Change-Id: I220f230c523cfaf2c96019f9478554665e6af486
diff --git a/core/java/android/os/incremental/IIncrementalService.aidl b/core/java/android/os/incremental/IIncrementalService.aidl
index 52475e9..ca92ad5 100644
--- a/core/java/android/os/incremental/IIncrementalService.aidl
+++ b/core/java/android/os/incremental/IIncrementalService.aidl
@@ -144,4 +144,14 @@
* Stop listening for the loading progress change for a storage.
*/
boolean unregisterLoadingProgressListener(int storageId);
+
+ /**
+ * Register storage health status listener.
+ */
+ boolean registerStorageHealthListener(int storageId, in StorageHealthCheckParams params, in IStorageHealthListener listener);
+
+ /**
+ * Register storage health status listener.
+ */
+ void unregisterStorageHealthListener(int storageId);
}
diff --git a/core/java/android/os/incremental/IStorageHealthListener.aidl b/core/java/android/os/incremental/IStorageHealthListener.aidl
index 9f93ede..c71e73f 100644
--- a/core/java/android/os/incremental/IStorageHealthListener.aidl
+++ b/core/java/android/os/incremental/IStorageHealthListener.aidl
@@ -26,9 +26,15 @@
/** There are reads pending for params.blockedTimeoutMs, waiting till
* params.unhealthyTimeoutMs to confirm unhealthy state. */
const int HEALTH_STATUS_BLOCKED = 2;
- /** There are reads pending for params.unhealthyTimeoutMs>,
- * marking storage as unhealthy. */
+ /** There are reads pending for params.unhealthyTimeoutMs,
+ * marking storage as unhealthy due to unknown issues. */
const int HEALTH_STATUS_UNHEALTHY = 3;
+ /** There are reads pending for params.unhealthyTimeoutMs,
+ * due to data transportation issues. */
+ const int HEALTH_STATUS_UNHEALTHY_TRANSPORT = 4;
+ /** There are reads pending for params.unhealthyTimeoutMs,
+ * due to limited storage space. */
+ const int HEALTH_STATUS_UNHEALTHY_STORAGE = 5;
/** Health status callback. */
void onHealthStatus(in int storageId, in int status);
diff --git a/core/java/android/os/incremental/IncrementalManager.java b/core/java/android/os/incremental/IncrementalManager.java
index 768ef97..fb47ef0 100644
--- a/core/java/android/os/incremental/IncrementalManager.java
+++ b/core/java/android/os/incremental/IncrementalManager.java
@@ -283,6 +283,7 @@
return;
}
mLoadingProgressCallbacks.cleanUpCallbacks(storage);
+ unregisterHealthListener(codePath);
mService.deleteStorage(storage.getId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -297,7 +298,7 @@
* @param callback To report loading progress to.
* @return True if the package name and associated storage id are valid. False otherwise.
*/
- public boolean registerCallback(@NonNull String codePath,
+ public boolean registerLoadingProgressCallback(@NonNull String codePath,
@NonNull IPackageLoadingProgressCallback callback) {
final IncrementalStorage storage = openStorage(codePath);
if (storage == null) {
@@ -314,7 +315,7 @@
* @param codePath Path of the installed package
* @return True if the package name and associated storage id are valid. False otherwise.
*/
- public boolean unregisterCallback(@NonNull String codePath,
+ public boolean unregisterLoadingProgressCallback(@NonNull String codePath,
@NonNull IPackageLoadingProgressCallback callback) {
final IncrementalStorage storage = openStorage(codePath);
if (storage == null) {
@@ -414,6 +415,38 @@
}
}
+ /**
+ * Specify the health check params and listener for listening to Incremental Storage health
+ * status changes. Notice that this will overwrite the previously registered listener.
+ * @param codePath Path of the installed package. This path is on an Incremental Storage.
+ * @param healthCheckParams The params for health state change timeouts.
+ * @param listener To report health status change.
+ * @return True if listener was successfully registered.
+ */
+ public boolean registerHealthListener(@NonNull String codePath,
+ @NonNull StorageHealthCheckParams healthCheckParams,
+ @NonNull IStorageHealthListener.Stub listener) {
+ final IncrementalStorage storage = openStorage(codePath);
+ if (storage == null) {
+ // storage does not exist, package not installed
+ return false;
+ }
+ return storage.registerStorageHealthListener(healthCheckParams, listener);
+ }
+
+ /**
+ * Stop listening to health status changes on an Incremental Storage.
+ * @param codePath Path of the installed package. This path is on an Incremental Storage.
+ */
+ public void unregisterHealthListener(@NonNull String codePath) {
+ final IncrementalStorage storage = openStorage(codePath);
+ if (storage == null) {
+ // storage does not exist, package not installed
+ return;
+ }
+ storage.unregisterStorageHealthListener();
+ }
+
/* Native methods */
private static native boolean nativeIsEnabled();
private static native boolean nativeIsIncrementalPath(@NonNull String path);
diff --git a/core/java/android/os/incremental/IncrementalStorage.java b/core/java/android/os/incremental/IncrementalStorage.java
index a1c3cc6..b913faf 100644
--- a/core/java/android/os/incremental/IncrementalStorage.java
+++ b/core/java/android/os/incremental/IncrementalStorage.java
@@ -545,4 +545,31 @@
return false;
}
}
+
+ /**
+ * Register to listen to the status changes of the storage health.
+ * @param healthCheckParams Params to specify status change timeouts.
+ * @param listener To report health status change from Incremental Service to the caller.
+ */
+ public boolean registerStorageHealthListener(StorageHealthCheckParams healthCheckParams,
+ IStorageHealthListener listener) {
+ try {
+ return mService.registerStorageHealthListener(mId, healthCheckParams, listener);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ return false;
+ }
+ }
+
+ /**
+ * Stops listening to the status changes of the storage health.
+ */
+ public void unregisterStorageHealthListener() {
+ try {
+ mService.unregisterStorageHealthListener(mId);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ return;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/pm/IncrementalStates.java b/services/core/java/com/android/server/pm/IncrementalStates.java
index ababb83..43f4a34 100644
--- a/services/core/java/com/android/server/pm/IncrementalStates.java
+++ b/services/core/java/com/android/server/pm/IncrementalStates.java
@@ -16,18 +16,21 @@
package com.android.server.pm;
-import android.content.pm.IDataLoaderStatusListener;
+import static android.os.incremental.IStorageHealthListener.HEALTH_STATUS_OK;
+import static android.os.incremental.IStorageHealthListener.HEALTH_STATUS_UNHEALTHY;
+import static android.os.incremental.IStorageHealthListener.HEALTH_STATUS_UNHEALTHY_STORAGE;
+import static android.os.incremental.IStorageHealthListener.HEALTH_STATUS_UNHEALTHY_TRANSPORT;
+
import android.content.pm.IncrementalStatesInfo;
import android.content.pm.PackageManager;
import android.os.Handler;
-import android.os.incremental.IStorageHealthListener;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.function.pooled.PooledLambda;
-import java.util.function.BiConsumer;
+import java.util.function.Consumer;
/**
* Manages state transitions of a package installed on Incremental File System. Currently manages:
@@ -36,8 +39,7 @@
*
* The following events might change the states of a package:
* 1. Installation commit
- * 2. Incremental storage health
- * 3. Data loader stream health
+ * 2. Incremental storage health changes
* 4. Loading progress changes
*
* @hide
@@ -48,16 +50,14 @@
private final Handler mHandler = BackgroundThread.getHandler();
private final Object mLock = new Object();
@GuardedBy("mLock")
- private int mStreamStatus = IDataLoaderStatusListener.STREAM_HEALTHY;
- @GuardedBy("mLock")
- private int mStorageHealthStatus = IStorageHealthListener.HEALTH_STATUS_OK;
+ private int mStorageHealthStatus = HEALTH_STATUS_OK;
@GuardedBy("mLock")
private final LoadingState mLoadingState;
@GuardedBy("mLock")
private StartableState mStartableState;
@GuardedBy("mLock")
private Callback mCallback = null;
- private final BiConsumer<Integer, Integer> mStatusConsumer;
+ private final Consumer<Integer> mStatusConsumer;
public IncrementalStates() {
// By default the package is not startable and not fully loaded (i.e., is loading)
@@ -148,12 +148,9 @@
}
}
- private class StatusConsumer implements BiConsumer<Integer, Integer> {
+ private class StatusConsumer implements Consumer<Integer> {
@Override
- public void accept(Integer streamStatus, Integer storageStatus) {
- if (streamStatus == null && storageStatus == null) {
- return;
- }
+ public void accept(Integer storageStatus) {
final boolean oldState, newState;
synchronized (mLock) {
if (!mLoadingState.isLoading()) {
@@ -161,12 +158,7 @@
return;
}
oldState = mStartableState.isStartable();
- if (streamStatus != null) {
- mStreamStatus = (Integer) streamStatus;
- }
- if (storageStatus != null) {
- mStorageHealthStatus = (Integer) storageStatus;
- }
+ mStorageHealthStatus = storageStatus;
updateStartableStateLocked();
newState = mStartableState.isStartable();
}
@@ -188,21 +180,7 @@
Slog.i(TAG, "received storage health status changed event : storageHealthStatus="
+ storageHealthStatus);
}
- mStatusConsumer.accept(null, storageHealthStatus);
- }
-
- /**
- * By calling this method, the caller indicates that the stream status of the package has
- * been
- * changed. This could indicate a streaming error. The state will change according to the
- * status
- * code defined in {@code IDataLoaderStatusListener}.
- */
- public void onStreamStatusChanged(int streamState) {
- if (DEBUG) {
- Slog.i(TAG, "received stream status changed event : streamState=" + streamState);
- }
- mStatusConsumer.accept(streamState, null);
+ mStatusConsumer.accept(storageHealthStatus);
}
/**
@@ -284,35 +262,16 @@
final boolean currentState = mStartableState.isStartable();
boolean nextState = currentState;
if (!currentState) {
- if (mStorageHealthStatus == IStorageHealthListener.HEALTH_STATUS_OK
- && mStreamStatus == IDataLoaderStatusListener.STREAM_HEALTHY) {
- // change from unstartable -> startable when both stream and storage are healthy
+ if (mStorageHealthStatus == HEALTH_STATUS_OK) {
+ // change from unstartable -> startable
nextState = true;
}
} else {
- if (mStorageHealthStatus == IStorageHealthListener.HEALTH_STATUS_UNHEALTHY) {
- // unrecoverable if storage is unhealthy
+ if (mStorageHealthStatus == HEALTH_STATUS_UNHEALTHY
+ || mStorageHealthStatus == HEALTH_STATUS_UNHEALTHY_STORAGE
+ || mStorageHealthStatus == HEALTH_STATUS_UNHEALTHY_TRANSPORT) {
+ // change from startable -> unstartable
nextState = false;
- } else {
- switch (mStreamStatus) {
- case IDataLoaderStatusListener.STREAM_INTEGRITY_ERROR:
- // unrecoverable, fall through
- case IDataLoaderStatusListener.STREAM_SOURCE_ERROR: {
- // unrecoverable
- nextState = false;
- break;
- }
- case IDataLoaderStatusListener.STREAM_STORAGE_ERROR: {
- if (mStorageHealthStatus != IStorageHealthListener.HEALTH_STATUS_OK) {
- // unrecoverable if there is a pending read AND storage is limited
- nextState = false;
- }
- break;
- }
- default:
- // anything else, remain startable
- break;
- }
}
}
if (nextState == currentState) {
@@ -370,17 +329,11 @@
return PackageManager.UNSTARTABLE_REASON_UNKNOWN;
}
// Translate stream status to reason for unstartable state
- switch (mStreamStatus) {
- case IDataLoaderStatusListener.STREAM_TRANSPORT_ERROR:
- // fall through
- case IDataLoaderStatusListener.STREAM_INTEGRITY_ERROR:
- // fall through
- case IDataLoaderStatusListener.STREAM_SOURCE_ERROR: {
- return PackageManager.UNSTARTABLE_REASON_CONNECTION_ERROR;
- }
- case IDataLoaderStatusListener.STREAM_STORAGE_ERROR: {
+ switch (mStorageHealthStatus) {
+ case HEALTH_STATUS_UNHEALTHY_STORAGE:
return PackageManager.UNSTARTABLE_REASON_INSUFFICIENT_STORAGE;
- }
+ case HEALTH_STATUS_UNHEALTHY_TRANSPORT:
+ return PackageManager.UNSTARTABLE_REASON_CONNECTION_ERROR;
default:
return PackageManager.UNSTARTABLE_REASON_UNKNOWN;
}
@@ -464,7 +417,6 @@
}
IncrementalStates l = (IncrementalStates) o;
return l.mStorageHealthStatus == mStorageHealthStatus
- && l.mStreamStatus == mStreamStatus
&& l.mStartableState.equals(mStartableState)
&& l.mLoadingState.equals(mLoadingState);
}
@@ -474,7 +426,6 @@
int hashCode = mStartableState.hashCode();
hashCode = 31 * hashCode + mLoadingState.hashCode();
hashCode = 31 * hashCode + mStorageHealthStatus;
- hashCode = 31 * hashCode + mStreamStatus;
return hashCode;
}
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 649cafb..5d2928e 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -143,7 +143,6 @@
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
-import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
import com.android.server.pm.Installer.InstallerException;
import com.android.server.pm.dex.DexManager;
@@ -1702,28 +1701,6 @@
dispatchSessionFinished(error, detailedMessage, null);
}
- private void onStorageHealthStatusChanged(int status) {
- final String packageName = getPackageName();
- if (TextUtils.isEmpty(packageName)) {
- // The package has not been installed.
- return;
- }
- mHandler.post(PooledLambda.obtainRunnable(
- PackageManagerService::onStorageHealthStatusChanged,
- mPm, packageName, status, userId).recycleOnUse());
- }
-
- private void onStreamHealthStatusChanged(int status) {
- final String packageName = getPackageName();
- if (TextUtils.isEmpty(packageName)) {
- // The package has not been installed.
- return;
- }
- mHandler.post(PooledLambda.obtainRunnable(
- PackageManagerService::onStreamStatusChanged,
- mPm, packageName, status, userId).recycleOnUse());
- }
-
/**
* If session should be sealed, then it's sealed to prevent further modification.
* If the session can't be sealed then it's destroyed.
@@ -3315,19 +3292,11 @@
return;
}
- final boolean isDestroyedOrDataLoaderFinished;
synchronized (mLock) {
- isDestroyedOrDataLoaderFinished = mDestroyed || mDataLoaderFinished;
- }
- if (isDestroyedOrDataLoaderFinished) {
- switch (status) {
- case IDataLoaderStatusListener.DATA_LOADER_UNRECOVERABLE:
- // treat as unhealthy storage
- onStorageHealthStatusChanged(
- IStorageHealthListener.HEALTH_STATUS_UNHEALTHY);
- return;
+ if (mDestroyed || mDataLoaderFinished) {
+ // No need to worry about post installation
+ return;
}
- return;
}
try {
@@ -3423,13 +3392,10 @@
}
@Override
public void reportStreamHealth(int dataLoaderId, int streamStatus) {
- synchronized (mLock) {
- if (!mDestroyed && !mDataLoaderFinished) {
- // ignore streaming status if package isn't installed
- return;
- }
- }
- onStreamHealthStatusChanged(streamStatus);
+ // Currently the stream status is not used during package installation. It is
+ // technically possible for the data loader to report stream status via this
+ // callback, but if something is wrong with the streaming, it is more likely that
+ // prepareDataLoaderLocked will return false and the installation will be aborted.
}
};
@@ -3438,20 +3404,16 @@
healthCheckParams.blockedTimeoutMs = INCREMENTAL_STORAGE_BLOCKED_TIMEOUT_MS;
healthCheckParams.unhealthyTimeoutMs = INCREMENTAL_STORAGE_UNHEALTHY_TIMEOUT_MS;
healthCheckParams.unhealthyMonitoringMs = INCREMENTAL_STORAGE_UNHEALTHY_MONITORING_MS;
-
final boolean systemDataLoader =
params.getComponentName().getPackageName() == SYSTEM_DATA_LOADER_PACKAGE;
final IStorageHealthListener healthListener = new IStorageHealthListener.Stub() {
@Override
public void onHealthStatus(int storageId, int status) {
- final boolean isDestroyedOrDataLoaderFinished;
synchronized (mLock) {
- isDestroyedOrDataLoaderFinished = mDestroyed || mDataLoaderFinished;
- }
- if (isDestroyedOrDataLoaderFinished) {
- // App's installed.
- onStorageHealthStatusChanged(status);
- return;
+ if (mDestroyed || mDataLoaderFinished) {
+ // No need to worry about post installation
+ return;
+ }
}
switch (status) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index ff84e2e..2ea8008 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -271,8 +271,10 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.os.UserManagerInternal;
+import android.os.incremental.IStorageHealthListener;
import android.os.incremental.IncrementalManager;
import android.os.incremental.IncrementalStorage;
+import android.os.incremental.StorageHealthCheckParams;
import android.os.storage.DiskInfo;
import android.os.storage.IStorageManager;
import android.os.storage.StorageEventListener;
@@ -732,6 +734,14 @@
private static final String RANDOM_DIR_PREFIX = "~~";
+ /**
+ * Timeout configurations for incremental storage health monitor.
+ * See {@link IStorageHealthListener}
+ */
+ private static final int INCREMENTAL_STORAGE_BLOCKED_TIMEOUT_MS = 2000;
+ private static final int INCREMENTAL_STORAGE_UNHEALTHY_TIMEOUT_MS = 7000;
+ private static final int INCREMENTAL_STORAGE_UNHEALTHY_MONITORING_MS = 60000;
+
final ServiceThread mHandlerThread;
final Handler mHandler;
@@ -9696,6 +9706,18 @@
mSettings.disableSystemPackageLPw(parsedPackage.getPackageName(), true);
}
}
+ if (mIncrementalManager != null && isIncrementalPath(parsedPackage.getPath())) {
+ if (pkgSetting != null && pkgSetting.isPackageLoading()) {
+ final StorageHealthCheckParams healthCheckParams = new StorageHealthCheckParams();
+ healthCheckParams.blockedTimeoutMs = INCREMENTAL_STORAGE_BLOCKED_TIMEOUT_MS;
+ healthCheckParams.unhealthyTimeoutMs = INCREMENTAL_STORAGE_UNHEALTHY_TIMEOUT_MS;
+ healthCheckParams.unhealthyMonitoringMs =
+ INCREMENTAL_STORAGE_UNHEALTHY_MONITORING_MS;
+ mIncrementalManager.registerHealthListener(parsedPackage.getPath(),
+ healthCheckParams,
+ new IncrementalHealthListener(parsedPackage.getPackageName()));
+ }
+ }
return scanResult.pkgSetting.pkg;
}
@@ -16356,12 +16378,25 @@
// TODO(b/169721400): generalize Incremental States and create a Callback object
// that can be used for all the packages.
- final IncrementalStatesCallback incrementalStatesCallback =
- new IncrementalStatesCallback(ps, userId);
final String codePath = ps.getPathString();
if (IncrementalManager.isIncrementalPath(codePath) && mIncrementalManager != null) {
- mIncrementalManager.registerCallback(codePath, incrementalStatesCallback);
+ final IncrementalStatesCallback incrementalStatesCallback =
+ new IncrementalStatesCallback(ps.name,
+ UserHandle.getUid(userId, ps.appId),
+ getInstalledUsers(ps, userId));
ps.setIncrementalStatesCallback(incrementalStatesCallback);
+ mIncrementalManager.registerLoadingProgressCallback(codePath,
+ new IncrementalProgressListener(ps.name));
+ final IncrementalHealthListener incrementalHealthListener =
+ new IncrementalHealthListener(ps.name);
+ final StorageHealthCheckParams healthCheckParams =
+ new StorageHealthCheckParams();
+ healthCheckParams.blockedTimeoutMs = INCREMENTAL_STORAGE_BLOCKED_TIMEOUT_MS;
+ healthCheckParams.unhealthyTimeoutMs = INCREMENTAL_STORAGE_UNHEALTHY_TIMEOUT_MS;
+ healthCheckParams.unhealthyMonitoringMs =
+ INCREMENTAL_STORAGE_UNHEALTHY_MONITORING_MS;
+ mIncrementalManager.registerHealthListener(codePath,
+ new StorageHealthCheckParams(), incrementalHealthListener);
}
// Ensure that the uninstall reason is UNKNOWN for users with the package installed.
@@ -17264,45 +17299,39 @@
NativeLibraryHelper.waitForNativeBinariesExtraction(incrementalStorages);
}
- private class IncrementalStatesCallback extends IPackageLoadingProgressCallback.Stub
- implements IncrementalStates.Callback {
- @GuardedBy("mPackageSetting")
- private final PackageSetting mPackageSetting;
- private final String mPackageName;
- private final String mPathString;
- private final int mUid;
- private final int[] mInstalledUserIds;
-
- IncrementalStatesCallback(PackageSetting packageSetting, int userId) {
- mPackageSetting = packageSetting;
- mPackageName = packageSetting.name;
- mUid = UserHandle.getUid(userId, packageSetting.appId);
- mPathString = packageSetting.getPathString();
- final int[] allUserIds = resolveUserIds(userId);
- final ArrayList<Integer> installedUserIds = new ArrayList<>();
- for (int i = 0; i < allUserIds.length; i++) {
- if (packageSetting.getInstalled(allUserIds[i])) {
- installedUserIds.add(allUserIds[i]);
- }
- }
- final int numInstalledUserId = installedUserIds.size();
- mInstalledUserIds = new int[numInstalledUserId];
- for (int i = 0; i < numInstalledUserId; i++) {
- mInstalledUserIds[i] = installedUserIds.get(i);
+ private int[] getInstalledUsers(PackageSetting ps, int userId) {
+ final int[] allUserIds = resolveUserIds(userId);
+ final ArrayList<Integer> installedUserIdsList = new ArrayList<>();
+ for (int i = 0; i < allUserIds.length; i++) {
+ if (ps.getInstalled(allUserIds[i])) {
+ installedUserIdsList.add(allUserIds[i]);
}
}
+ final int numInstalledUserId = installedUserIdsList.size();
+ final int[] installedUserIds = new int[numInstalledUserId];
+ for (int i = 0; i < numInstalledUserId; i++) {
+ installedUserIds[i] = installedUserIdsList.get(i);
+ }
+ return installedUserIds;
+ }
- @Override
- public void onPackageLoadingProgressChanged(float progress) {
- synchronized (mPackageSetting) {
- mPackageSetting.setLoadingProgress(progress);
- }
+ /**
+ * Package states callback, used to listen for package state changes and send broadcasts
+ */
+ private final class IncrementalStatesCallback implements IncrementalStates.Callback {
+ private final String mPackageName;
+ private final int mUid;
+ private final int[] mInstalledUserIds;
+ IncrementalStatesCallback(String packageName, int uid, int[] installedUserIds) {
+ mPackageName = packageName;
+ mUid = uid;
+ mInstalledUserIds = installedUserIds;
}
@Override
public void onPackageFullyLoaded() {
- mIncrementalManager.unregisterCallback(mPathString, this);
final SparseArray<int[]> newBroadcastAllowList;
+ final String codePath;
synchronized (mLock) {
final PackageSetting ps = mSettings.mPackages.get(mPackageName);
if (ps == null) {
@@ -17310,6 +17339,7 @@
}
newBroadcastAllowList = mAppsFilter.getVisibilityAllowList(
ps, mInstalledUserIds, mSettings.mPackages);
+ codePath = ps.getPathString();
}
Bundle extras = new Bundle();
extras.putInt(Intent.EXTRA_UID, mUid);
@@ -17318,6 +17348,8 @@
extras, 0 /*flags*/,
null /*targetPackage*/, null /*finishedReceiver*/,
mInstalledUserIds, null /* instantUserIds */, newBroadcastAllowList);
+ // Unregister health listener as it will always be healthy from now
+ mIncrementalManager.unregisterHealthListener(codePath);
}
@Override
@@ -17365,37 +17397,48 @@
}
/**
- * This is an internal method that is used to indicate changes on the health status of the
- * Incremental Storage used by an installed package with an associated user id. This might
- * result in a change in the loading state of the package.
+ * Loading progress callback, used to listen for progress changes and update package setting
*/
- public void onStorageHealthStatusChanged(String packageName, int status, int userId) {
- final int callingUid = Binder.getCallingUid();
- mPermissionManager.enforceCrossUserPermission(
- callingUid, userId, true, false,
- "onStorageHealthStatusChanged");
- final PackageSetting ps = getPackageSettingForUser(packageName, callingUid, userId);
- if (ps == null) {
- return;
+ private class IncrementalProgressListener extends IPackageLoadingProgressCallback.Stub {
+ private final String mPackageName;
+ IncrementalProgressListener(String packageName) {
+ mPackageName = packageName;
}
- ps.setStorageHealthStatus(status);
+
+ @Override
+ public void onPackageLoadingProgressChanged(float progress) {
+ final PackageSetting ps;
+ synchronized (mLock) {
+ ps = mSettings.mPackages.get(mPackageName);
+ }
+ if (ps == null) {
+ return;
+ }
+ ps.setLoadingProgress(progress);
+ }
}
/**
- * This is an internal method that is used to indicate changes on the stream status of the
- * data loader used by an installed package with an associated user id. This might
- * result in a change in the loading state of the package.
+ * Incremental storage health status callback, used to listen for monitoring changes and update
+ * package setting.
*/
- public void onStreamStatusChanged(String packageName, int status, int userId) {
- final int callingUid = Binder.getCallingUid();
- mPermissionManager.enforceCrossUserPermission(
- callingUid, userId, true, false,
- "onStreamStatusChanged");
- final PackageSetting ps = getPackageSettingForUser(packageName, callingUid, userId);
- if (ps == null) {
- return;
+ private class IncrementalHealthListener extends IStorageHealthListener.Stub {
+ private final String mPackageName;
+ IncrementalHealthListener(String packageName) {
+ mPackageName = packageName;
}
- ps.setStreamStatus(status);
+
+ @Override
+ public void onHealthStatus(int storageId, int status) throws RemoteException {
+ final PackageSetting ps;
+ synchronized (mLock) {
+ ps = mSettings.mPackages.get(mPackageName);
+ }
+ if (ps == null) {
+ return;
+ }
+ ps.setStorageHealthStatus(status);
+ }
}
@Nullable PackageSetting getPackageSettingForUser(String packageName, int callingUid,
@@ -25637,7 +25680,7 @@
"Failed registering loading progress callback. Incremental is not enabled");
return false;
}
- return mIncrementalManager.registerCallback(ps.getPathString(),
+ return mIncrementalManager.registerLoadingProgressCallback(ps.getPathString(),
(IPackageLoadingProgressCallback) callback.getBinder());
}
@@ -25656,7 +25699,7 @@
if (mIncrementalManager == null) {
return false;
}
- return mIncrementalManager.unregisterCallback(ps.getPathString(),
+ return mIncrementalManager.unregisterLoadingProgressCallback(ps.getPathString(),
(IPackageLoadingProgressCallback) callback.getBinder());
}
}
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index d52ad46..be7c7c6 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -793,13 +793,6 @@
incrementalStates.onStorageHealthStatusChanged(status);
}
- /**
- * @see IncrementalStates#onStreamStatusChanged(int)
- */
- public void setStreamStatus(int status) {
- incrementalStates.onStreamStatusChanged(status);
- }
-
protected PackageSettingBase updateFrom(PackageSettingBase other) {
super.copyFrom(other);
setPath(other.getPath());
diff --git a/services/incremental/BinderIncrementalService.cpp b/services/incremental/BinderIncrementalService.cpp
index 2f8825b..a31aac9 100644
--- a/services/incremental/BinderIncrementalService.cpp
+++ b/services/incremental/BinderIncrementalService.cpp
@@ -323,6 +323,22 @@
return ok();
}
+binder::Status BinderIncrementalService::registerStorageHealthListener(
+ int32_t storageId,
+ const ::android::os::incremental::StorageHealthCheckParams& healthCheckParams,
+ const ::android::sp<IStorageHealthListener>& healthListener, bool* _aidl_return) {
+ *_aidl_return = mImpl.registerStorageHealthListener(storageId,
+ const_cast<StorageHealthCheckParams&&>(
+ healthCheckParams),
+ healthListener);
+ return ok();
+}
+
+binder::Status BinderIncrementalService::unregisterStorageHealthListener(int32_t storageId) {
+ mImpl.unregisterStorageHealthListener(storageId);
+ return ok();
+}
+
} // namespace android::os::incremental
jlong Incremental_IncrementalService_Start(JNIEnv* env) {
diff --git a/services/incremental/BinderIncrementalService.h b/services/incremental/BinderIncrementalService.h
index 0a89166..8afa0f7 100644
--- a/services/incremental/BinderIncrementalService.h
+++ b/services/incremental/BinderIncrementalService.h
@@ -89,6 +89,11 @@
progressListener,
bool* _aidl_return) final;
binder::Status unregisterLoadingProgressListener(int32_t storageId, bool* _aidl_return) final;
+ binder::Status registerStorageHealthListener(
+ int32_t storageId,
+ const ::android::os::incremental::StorageHealthCheckParams& healthCheckParams,
+ const ::android::sp<IStorageHealthListener>& healthListener, bool* _aidl_return) final;
+ binder::Status unregisterStorageHealthListener(int32_t storageId) final;
private:
android::incremental::IncrementalService mImpl;
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index 5f145f3..599ac93 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -1801,6 +1801,31 @@
return removeTimedJobs(*mProgressUpdateJobQueue, storage);
}
+bool IncrementalService::registerStorageHealthListener(
+ StorageId storage, StorageHealthCheckParams&& healthCheckParams,
+ const StorageHealthListener& healthListener) {
+ DataLoaderStubPtr dataLoaderStub;
+ {
+ std::unique_lock l(mLock);
+ const auto& ifs = getIfsLocked(storage);
+ if (!ifs) {
+ return false;
+ }
+ dataLoaderStub = ifs->dataLoaderStub;
+ if (!dataLoaderStub) {
+ return false;
+ }
+ }
+ dataLoaderStub->setHealthListener(std::move(healthCheckParams), &healthListener);
+ return true;
+}
+
+void IncrementalService::unregisterStorageHealthListener(StorageId storage) {
+ StorageHealthCheckParams invalidCheckParams;
+ invalidCheckParams.blockedTimeoutMs = -1;
+ registerStorageHealthListener(storage, std::move(invalidCheckParams), {});
+}
+
bool IncrementalService::perfLoggingEnabled() {
static const bool enabled = base::GetBoolProperty("incremental.perflogging", false);
return enabled;
@@ -2137,6 +2162,19 @@
binder::Status IncrementalService::DataLoaderStub::reportStreamHealth(MountId mountId,
int newStatus) {
+ if (!isValid()) {
+ return binder::Status::
+ fromServiceSpecificError(-EINVAL,
+ "reportStreamHealth came to invalid DataLoaderStub");
+ }
+ if (id() != mountId) {
+ LOG(ERROR) << "Mount ID mismatch: expected " << id() << ", but got: " << mountId;
+ return binder::Status::fromServiceSpecificError(-EPERM, "Mount ID mismatch.");
+ }
+ {
+ std::lock_guard lock(mMutex);
+ mStreamStatus = newStatus;
+ }
return binder::Status::ok();
}
@@ -2153,6 +2191,33 @@
}
}
+static int adjustHealthStatus(int healthStatus, int streamStatus) {
+ if (healthStatus == IStorageHealthListener::HEALTH_STATUS_OK) {
+ // everything is good; no need to change status
+ return healthStatus;
+ }
+ int newHeathStatus = healthStatus;
+ switch (streamStatus) {
+ case IDataLoaderStatusListener::STREAM_STORAGE_ERROR:
+ // storage is limited and storage not healthy
+ newHeathStatus = IStorageHealthListener::HEALTH_STATUS_UNHEALTHY_STORAGE;
+ break;
+ case IDataLoaderStatusListener::STREAM_INTEGRITY_ERROR:
+ // fall through
+ case IDataLoaderStatusListener::STREAM_SOURCE_ERROR:
+ // fall through
+ case IDataLoaderStatusListener::STREAM_TRANSPORT_ERROR:
+ if (healthStatus == IStorageHealthListener::HEALTH_STATUS_UNHEALTHY) {
+ newHeathStatus = IStorageHealthListener::HEALTH_STATUS_UNHEALTHY_TRANSPORT;
+ }
+ // pending/blocked status due to transportation issues is not regarded as unhealthy
+ break;
+ default:
+ break;
+ }
+ return newHeathStatus;
+}
+
void IncrementalService::DataLoaderStub::updateHealthStatus(bool baseline) {
LOG(DEBUG) << id() << ": updateHealthStatus" << (baseline ? " (baseline)" : "");
@@ -2232,6 +2297,8 @@
checkBackAfter = unhealthyMonitoring;
healthStatusToReport = IStorageHealthListener::HEALTH_STATUS_UNHEALTHY;
}
+ // Adjust health status based on stream status
+ healthStatusToReport = adjustHealthStatus(healthStatusToReport, mStreamStatus);
LOG(DEBUG) << id() << ": updateHealthStatus in " << double(checkBackAfter.count()) / 1000.0
<< "secs";
mService.addTimedJob(*mService.mTimedQueue, id(), checkBackAfter,
@@ -2321,6 +2388,18 @@
mService.mLooper->wake();
}
+void IncrementalService::DataLoaderStub::setHealthListener(
+ StorageHealthCheckParams&& healthCheckParams, const StorageHealthListener* healthListener) {
+ std::lock_guard lock(mMutex);
+ mHealthCheckParams = std::move(healthCheckParams);
+ if (healthListener == nullptr) {
+ // reset listener and params
+ mHealthListener = {};
+ } else {
+ mHealthListener = *healthListener;
+ }
+}
+
void IncrementalService::DataLoaderStub::onDump(int fd) {
dprintf(fd, " dataLoader: {\n");
dprintf(fd, " currentStatus: %d\n", mCurrentStatus);
diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h
index 504c02a..4c4b8bd 100644
--- a/services/incremental/IncrementalService.h
+++ b/services/incremental/IncrementalService.h
@@ -140,7 +140,10 @@
bool registerLoadingProgressListener(StorageId storage,
const StorageLoadingProgressListener& progressListener);
bool unregisterLoadingProgressListener(StorageId storage);
-
+ bool registerStorageHealthListener(StorageId storage,
+ StorageHealthCheckParams&& healthCheckParams,
+ const StorageHealthListener& healthListener);
+ void unregisterStorageHealthListener(StorageId storage);
RawMetadata getMetadata(StorageId storage, std::string_view path) const;
RawMetadata getMetadata(StorageId storage, FileId node) const;
@@ -197,6 +200,8 @@
MountId id() const { return mId.load(std::memory_order_relaxed); }
const content::pm::DataLoaderParamsParcel& params() const { return mParams; }
+ void setHealthListener(StorageHealthCheckParams&& healthCheckParams,
+ const StorageHealthListener* healthListener);
private:
binder::Status onStatusChanged(MountId mount, int newStatus) final;
@@ -251,6 +256,7 @@
BootClockTsUs kernelTsUs;
} mHealthBase = {TimePoint::max(), kMaxBootClockTsUs};
StorageHealthCheckParams mHealthCheckParams;
+ int mStreamStatus = content::pm::IDataLoaderStatusListener::STREAM_HEALTHY;
};
using DataLoaderStubPtr = sp<DataLoaderStub>;
diff --git a/services/incremental/test/IncrementalServiceTest.cpp b/services/incremental/test/IncrementalServiceTest.cpp
index aec9fa1..867312e 100644
--- a/services/incremental/test/IncrementalServiceTest.cpp
+++ b/services/incremental/test/IncrementalServiceTest.cpp
@@ -177,6 +177,18 @@
}
return binder::Status::ok();
}
+ binder::Status storageError(int32_t id) {
+ if (mListener) {
+ mListener->reportStreamHealth(id, IDataLoaderStatusListener::STREAM_STORAGE_ERROR);
+ }
+ return binder::Status::ok();
+ }
+ binder::Status transportError(int32_t id) {
+ if (mListener) {
+ mListener->reportStreamHealth(id, IDataLoaderStatusListener::STREAM_INTEGRITY_ERROR);
+ }
+ return binder::Status::ok();
+ }
int32_t setStorageParams(bool enableReadLogs) {
int32_t result = -1;
EXPECT_NE(mServiceConnector.get(), nullptr);
@@ -1221,4 +1233,83 @@
EXPECT_CALL(*listenerMock, onStorageLoadingProgressChanged(_, _)).Times(0);
mIncrementalService->registerLoadingProgressListener(storageId, listener);
}
+
+TEST_F(IncrementalServiceTest, testRegisterStorageHealthListenerSuccess) {
+ mIncFs->openMountSuccess();
+ sp<NiceMock<MockStorageHealthListener>> listener{new NiceMock<MockStorageHealthListener>};
+ sp<NiceMock<MockStorageHealthListener>> newListener{new NiceMock<MockStorageHealthListener>};
+ NiceMock<MockStorageHealthListener>* newListenerMock = newListener.get();
+
+ TemporaryDir tempDir;
+ int storageId = mIncrementalService->createStorage(tempDir.path, std::move(mDataLoaderParcel),
+ IncrementalService::CreateOptions::CreateNew,
+ {}, StorageHealthCheckParams{}, listener);
+ ASSERT_GE(storageId, 0);
+ StorageHealthCheckParams newParams;
+ newParams.blockedTimeoutMs = 10000;
+ newParams.unhealthyTimeoutMs = 20000;
+ newParams.unhealthyMonitoringMs = 30000;
+ ASSERT_TRUE(mIncrementalService->registerStorageHealthListener(storageId, std::move(newParams),
+ newListener));
+
+ using MS = std::chrono::milliseconds;
+ using MCS = std::chrono::microseconds;
+
+ const auto blockedTimeout = MS(newParams.blockedTimeoutMs);
+ const auto unhealthyTimeout = MS(newParams.unhealthyTimeoutMs);
+
+ const uint64_t kFirstTimestampUs = 1000000000ll;
+ const uint64_t kBlockedTimestampUs =
+ kFirstTimestampUs - std::chrono::duration_cast<MCS>(blockedTimeout).count();
+ const uint64_t kUnhealthyTimestampUs =
+ kFirstTimestampUs - std::chrono::duration_cast<MCS>(unhealthyTimeout).count();
+
+ // test that old listener was not called
+ EXPECT_CALL(*listener.get(),
+ onHealthStatus(_, IStorageHealthListener::HEALTH_STATUS_READS_PENDING))
+ .Times(0);
+ EXPECT_CALL(*newListenerMock,
+ onHealthStatus(_, IStorageHealthListener::HEALTH_STATUS_READS_PENDING))
+ .Times(1);
+ EXPECT_CALL(*newListenerMock, onHealthStatus(_, IStorageHealthListener::HEALTH_STATUS_BLOCKED))
+ .Times(1);
+ EXPECT_CALL(*newListenerMock,
+ onHealthStatus(_, IStorageHealthListener::HEALTH_STATUS_UNHEALTHY_STORAGE))
+ .Times(1);
+ EXPECT_CALL(*newListenerMock,
+ onHealthStatus(_, IStorageHealthListener::HEALTH_STATUS_UNHEALTHY_TRANSPORT))
+ .Times(1);
+ mIncFs->waitForPendingReadsSuccess(kFirstTimestampUs);
+ mLooper->mCallback(-1, -1, mLooper->mCallbackData);
+
+ ASSERT_EQ(IStorageHealthListener::HEALTH_STATUS_READS_PENDING, newListener->mStatus);
+ ASSERT_EQ(storageId, newListener->mStorageId);
+
+ auto timedCallback = mTimedQueue->mWhat;
+ mTimedQueue->clearJob(storageId);
+
+ // test when health status is blocked with transport error
+ mDataLoader->transportError(storageId);
+ mIncFs->waitForPendingReadsSuccess(kBlockedTimestampUs);
+ timedCallback();
+ ASSERT_EQ(IStorageHealthListener::HEALTH_STATUS_BLOCKED, newListener->mStatus);
+ timedCallback = mTimedQueue->mWhat;
+ mTimedQueue->clearJob(storageId);
+
+ // test when health status is blocked with storage error
+ mDataLoader->storageError(storageId);
+ mIncFs->waitForPendingReadsSuccess(kBlockedTimestampUs);
+ timedCallback();
+ ASSERT_EQ(IStorageHealthListener::HEALTH_STATUS_UNHEALTHY_STORAGE, newListener->mStatus);
+ timedCallback = mTimedQueue->mWhat;
+ mTimedQueue->clearJob(storageId);
+
+ // test when health status is unhealthy with transport error
+ mDataLoader->transportError(storageId);
+ mIncFs->waitForPendingReadsSuccess(kUnhealthyTimestampUs);
+ timedCallback();
+ ASSERT_EQ(IStorageHealthListener::HEALTH_STATUS_UNHEALTHY_TRANSPORT, newListener->mStatus);
+ mTimedQueue->clearJob(storageId);
+}
+
} // namespace android::os::incremental
diff --git a/services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java b/services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java
index c4c2f68..86758f1 100644
--- a/services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/IncrementalStatesTest.java
@@ -20,7 +20,6 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import android.content.pm.IDataLoaderStatusListener;
import android.content.pm.PackageManager;
import android.os.ConditionVariable;
import android.os.incremental.IStorageHealthListener;
@@ -113,36 +112,26 @@
}
/**
- * Test that the package is still startable when Incremental Storage is at blocked status.
+ * Test that the package becomes unstartable when health status indicate storage issues.
*/
@Test
public void testStartableTransition_IncrementalStorageBlocked() {
mIncrementalStates.onStorageHealthStatusChanged(
- IStorageHealthListener.HEALTH_STATUS_BLOCKED);
- // Test that package is still startable
- assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
- assertTrue(mIncrementalStates.isStartable());
+ IStorageHealthListener.HEALTH_STATUS_UNHEALTHY_STORAGE);
+ // Test that package is now unstartable
+ assertTrue(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
+ assertFalse(mIncrementalStates.isStartable());
+ assertEquals(PackageManager.UNSTARTABLE_REASON_INSUFFICIENT_STORAGE,
+ mUnstartableReason.get());
}
/**
- * Test that the package is still startable when Data Loader has unknown transportation issues.
- */
- @Test
- public void testStartableTransition_DataLoaderTransportError() {
- mIncrementalStates.onStreamStatusChanged(
- IDataLoaderStatusListener.STREAM_TRANSPORT_ERROR);
- // Test that package is still startable
- assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
- assertTrue(mIncrementalStates.isStartable());
- }
-
- /**
- * Test that the package becomes unstartable when Data Loader has data integrity issues.
+ * Test that the package becomes unstartable when health status indicates transport issues.
*/
@Test
public void testStartableTransition_DataLoaderIntegrityError() {
- mIncrementalStates.onStreamStatusChanged(
- IDataLoaderStatusListener.STREAM_INTEGRITY_ERROR);
+ mIncrementalStates.onStorageHealthStatusChanged(
+ IStorageHealthListener.HEALTH_STATUS_UNHEALTHY_TRANSPORT);
// Test that package is now unstartable
assertTrue(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
assertFalse(mIncrementalStates.isStartable());
@@ -151,62 +140,6 @@
}
/**
- * Test that the package becomes unstartable when Data Loader has data source issues.
- */
- @Test
- public void testStartableTransition_DataLoaderSourceError() {
- mIncrementalStates.onStreamStatusChanged(
- IDataLoaderStatusListener.STREAM_SOURCE_ERROR);
- // Test that package is now unstartable
- assertTrue(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
- assertFalse(mIncrementalStates.isStartable());
- assertEquals(PackageManager.UNSTARTABLE_REASON_CONNECTION_ERROR,
- mUnstartableReason.get());
- }
-
- /**
- * Test that the package becomes unstartable when Data Loader hits limited storage while
- * Incremental storage has a pending reads.
- */
- @Test
- public void testStartableTransition_DataLoaderStorageErrorWhenIncrementalStoragePending()
- throws InterruptedException {
- mIncrementalStates.onStreamStatusChanged(
- IDataLoaderStatusListener.STREAM_STORAGE_ERROR);
- // Test that package is still startable
- assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
- assertTrue(mIncrementalStates.isStartable());
- mIncrementalStates.onStorageHealthStatusChanged(
- IStorageHealthListener.HEALTH_STATUS_READS_PENDING);
- // Test that package is now unstartable
- assertTrue(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
- assertFalse(mIncrementalStates.isStartable());
- assertEquals(PackageManager.UNSTARTABLE_REASON_INSUFFICIENT_STORAGE,
- mUnstartableReason.get());
- }
-
- /**
- * Test that the package becomes unstartable when Data Loader hits limited storage while
- * Incremental storage is at blocked status.
- */
- @Test
- public void testStartableTransition_DataLoaderStorageErrorWhenIncrementalStorageBlocked()
- throws InterruptedException {
- mIncrementalStates.onStreamStatusChanged(
- IDataLoaderStatusListener.STREAM_STORAGE_ERROR);
- // Test that package is still startable
- assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
- assertTrue(mIncrementalStates.isStartable());
- mIncrementalStates.onStorageHealthStatusChanged(
- IStorageHealthListener.HEALTH_STATUS_BLOCKED);
- // Test that package is now unstartable
- assertTrue(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
- assertFalse(mIncrementalStates.isStartable());
- assertEquals(PackageManager.UNSTARTABLE_REASON_INSUFFICIENT_STORAGE,
- mUnstartableReason.get());
- }
-
- /**
* Test that the package becomes unstartable when Incremental Storage is unhealthy, and it
* becomes startable again when Incremental Storage is healthy again.
*/
@@ -227,42 +160,18 @@
}
/**
- * Test that the package becomes unstartable when Data Loader has data integrity issue, and it
- * becomes startable again when Data Loader is healthy again.
+ * Test that the package becomes unstartable when health status indicates transportation issue,
+ * and it becomes startable again when health status is ok again.
*/
@Test
public void testStartableTransition_DataLoaderUnhealthyBackToHealthy()
throws InterruptedException {
- mIncrementalStates.onStreamStatusChanged(IDataLoaderStatusListener.STREAM_INTEGRITY_ERROR);
- // Test that package is unstartable
- assertTrue(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
- assertFalse(mIncrementalStates.isStartable());
-
- mIncrementalStates.onStreamStatusChanged(IDataLoaderStatusListener.STREAM_HEALTHY);
- // Test that package is now startable
- assertTrue(mStartableCalled.block(WAIT_TIMEOUT_MILLIS));
- assertTrue(mIncrementalStates.isStartable());
- }
-
- /**
- * Test that the package becomes unstartable when both Incremental Storage and Data Loader
- * are unhealthy, and it becomes startable again when both Incremental Storage and Data Loader
- * are healthy again.
- */
- @Test
- public void testStartableTransition_DataLoaderAndIncrementalStorageUnhealthyBackToHealthy()
- throws InterruptedException {
mIncrementalStates.onStorageHealthStatusChanged(
- IStorageHealthListener.HEALTH_STATUS_UNHEALTHY);
- mIncrementalStates.onStreamStatusChanged(IDataLoaderStatusListener.STREAM_INTEGRITY_ERROR);
+ IStorageHealthListener.HEALTH_STATUS_UNHEALTHY_TRANSPORT);
// Test that package is unstartable
assertTrue(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
assertFalse(mIncrementalStates.isStartable());
- mIncrementalStates.onStreamStatusChanged(IDataLoaderStatusListener.STREAM_HEALTHY);
- // Test that package is still unstartable
- assertFalse(mStartableCalled.block(WAIT_TIMEOUT_MILLIS));
- assertFalse(mIncrementalStates.isStartable());
mIncrementalStates.onStorageHealthStatusChanged(IStorageHealthListener.HEALTH_STATUS_OK);
// Test that package is now startable
assertTrue(mStartableCalled.block(WAIT_TIMEOUT_MILLIS));