Merge "[pm/incremental] disable unstartable state and hide related APIs"

This commit is contained in:
Songchun Fan
2020-11-04 16:29:07 +00:00
committed by Android (Google) Code Review
7 changed files with 71 additions and 134 deletions

View File

@@ -10683,8 +10683,6 @@ package android.content {
field public static final String ACTION_PACKAGE_REMOVED = "android.intent.action.PACKAGE_REMOVED";
field public static final String ACTION_PACKAGE_REPLACED = "android.intent.action.PACKAGE_REPLACED";
field public static final String ACTION_PACKAGE_RESTARTED = "android.intent.action.PACKAGE_RESTARTED";
field public static final String ACTION_PACKAGE_STARTABLE = "android.intent.action.PACKAGE_STARTABLE";
field public static final String ACTION_PACKAGE_UNSTARTABLE = "android.intent.action.PACKAGE_UNSTARTABLE";
field public static final String ACTION_PACKAGE_VERIFIED = "android.intent.action.PACKAGE_VERIFIED";
field public static final String ACTION_PASTE = "android.intent.action.PASTE";
field public static final String ACTION_PICK = "android.intent.action.PICK";
@@ -12312,9 +12310,6 @@ package android.content.pm {
field public static final int SYNCHRONOUS = 2; // 0x2
field @Nullable public static final java.util.List<java.security.cert.Certificate> TRUST_ALL;
field @NonNull public static final java.util.List<java.security.cert.Certificate> TRUST_NONE;
field public static final int UNSTARTABLE_REASON_CONNECTION_ERROR = 1; // 0x1
field public static final int UNSTARTABLE_REASON_INSUFFICIENT_STORAGE = 2; // 0x2
field public static final int UNSTARTABLE_REASON_UNKNOWN = 0; // 0x0
field public static final int VERIFICATION_ALLOW = 1; // 0x1
field public static final int VERIFICATION_REJECT = -1; // 0xffffffff
field public static final int VERSION_CODE_HIGHEST = -1; // 0xffffffff

View File

@@ -10683,8 +10683,6 @@ package android.content {
field public static final String ACTION_PACKAGE_REMOVED = "android.intent.action.PACKAGE_REMOVED";
field public static final String ACTION_PACKAGE_REPLACED = "android.intent.action.PACKAGE_REPLACED";
field public static final String ACTION_PACKAGE_RESTARTED = "android.intent.action.PACKAGE_RESTARTED";
field public static final String ACTION_PACKAGE_STARTABLE = "android.intent.action.PACKAGE_STARTABLE";
field public static final String ACTION_PACKAGE_UNSTARTABLE = "android.intent.action.PACKAGE_UNSTARTABLE";
field public static final String ACTION_PACKAGE_VERIFIED = "android.intent.action.PACKAGE_VERIFIED";
field public static final String ACTION_PASTE = "android.intent.action.PASTE";
field public static final String ACTION_PICK = "android.intent.action.PICK";
@@ -12312,9 +12310,6 @@ package android.content.pm {
field public static final int SYNCHRONOUS = 2; // 0x2
field @Nullable public static final java.util.List<java.security.cert.Certificate> TRUST_ALL;
field @NonNull public static final java.util.List<java.security.cert.Certificate> TRUST_NONE;
field public static final int UNSTARTABLE_REASON_CONNECTION_ERROR = 1; // 0x1
field public static final int UNSTARTABLE_REASON_INSUFFICIENT_STORAGE = 2; // 0x2
field public static final int UNSTARTABLE_REASON_UNKNOWN = 0; // 0x0
field public static final int VERIFICATION_ALLOW = 1; // 0x1
field public static final int VERIFICATION_REJECT = -1; // 0xffffffff
field public static final int VERSION_CODE_HIGHEST = -1; // 0xffffffff

View File

@@ -2740,6 +2740,7 @@ public class Intent implements Parcelable, Cloneable {
* </ul>
*
* <p class="note">This is a protected intent that can only be sent by the system.
* @hide
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_PACKAGE_STARTABLE = "android.intent.action.PACKAGE_STARTABLE";
@@ -2757,6 +2758,7 @@ public class Intent implements Parcelable, Cloneable {
* </ul>
*
* <p class="note">This is a protected intent that can only be sent by the system.
* @hide
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_PACKAGE_UNSTARTABLE =

View File

@@ -3824,18 +3824,21 @@ public abstract class PackageManager {
* Unstartable state with no root cause specified. E.g., data loader seeing missing pages but
* unclear about the cause. This corresponds to a generic alert window shown to the user when
* the user attempts to launch the app.
* @hide
*/
public static final int UNSTARTABLE_REASON_UNKNOWN = 0;
/**
* Unstartable state due to connection issues that interrupt package loading.
* This corresponds to an alert window shown to the user indicating connection errors.
* @hide
*/
public static final int UNSTARTABLE_REASON_CONNECTION_ERROR = 1;
/**
* Unstartable state after encountering storage limitations.
* This corresponds to an alert window indicating limited storage.
* @hide
*/
public static final int UNSTARTABLE_REASON_INSUFFICIENT_STORAGE = 2;

View File

@@ -101,21 +101,18 @@ public final class IncrementalStates {
if (DEBUG) {
Slog.i(TAG, "received package commit event");
}
final boolean startableStateChanged;
synchronized (mLock) {
if (!mStartableState.isStartable()) {
mStartableState.adoptNewStartableStateLocked(true);
}
startableStateChanged = mStartableState.adoptNewStartableStateLocked(true);
if (!isIncremental) {
updateProgressLocked(1);
}
}
mHandler.post(PooledLambda.obtainRunnable(
IncrementalStates::reportStartableState,
IncrementalStates.this).recycleOnUse());
if (startableStateChanged) {
onStartableStateChanged();
}
if (!isIncremental) {
mHandler.post(PooledLambda.obtainRunnable(
IncrementalStates::reportFullyLoaded,
IncrementalStates.this).recycleOnUse());
onLoadingStateChanged();
}
}
@@ -131,8 +128,7 @@ public final class IncrementalStates {
synchronized (mLock) {
if (mStartableState.isStartable() && mLoadingState.isLoading()) {
// Changing from startable -> unstartable only if app is still loading.
mStartableState.adoptNewStartableStateLocked(false);
startableStateChanged = true;
startableStateChanged = mStartableState.adoptNewStartableStateLocked(false);
} else {
// If the app is fully loaded, the crash or ANR is caused by the app itself, so
// we do not change the startable state.
@@ -140,12 +136,15 @@ public final class IncrementalStates {
}
}
if (startableStateChanged) {
mHandler.post(PooledLambda.obtainRunnable(
IncrementalStates::reportStartableState,
IncrementalStates.this).recycleOnUse());
onStartableStateChanged();
}
}
private void onStartableStateChanged() {
// Disable startable state broadcasts
// TODO(b/171920377): completely remove unstartable state.
}
private void reportStartableState() {
final Callback callback;
final boolean startable;
@@ -165,6 +164,12 @@ public final class IncrementalStates {
}
}
private void onLoadingStateChanged() {
mHandler.post(PooledLambda.obtainRunnable(
IncrementalStates::reportFullyLoaded,
IncrementalStates.this).recycleOnUse());
}
private void reportFullyLoaded() {
final Callback callback;
synchronized (mLock) {
@@ -178,23 +183,20 @@ public final class IncrementalStates {
private class StatusConsumer implements Consumer<Integer> {
@Override
public void accept(Integer storageStatus) {
final boolean oldState, newState;
final boolean startableStateChanged;
synchronized (mLock) {
if (!mLoadingState.isLoading()) {
// Do nothing if the package is already fully loaded
return;
}
oldState = mStartableState.isStartable();
mStorageHealthStatus = storageStatus;
updateStartableStateLocked();
newState = mStartableState.isStartable();
startableStateChanged = updateStartableStateLocked();
}
if (oldState != newState) {
mHandler.post(PooledLambda.obtainRunnable(IncrementalStates::reportStartableState,
IncrementalStates.this).recycleOnUse());
if (startableStateChanged) {
onStartableStateChanged();
}
}
};
}
/**
* By calling this method, the caller indicates that there issues with the Incremental
@@ -239,14 +241,10 @@ public final class IncrementalStates {
newStartableState = mStartableState.isStartable();
}
if (!newLoadingState) {
mHandler.post(PooledLambda.obtainRunnable(
IncrementalStates::reportFullyLoaded,
IncrementalStates.this).recycleOnUse());
onLoadingStateChanged();
}
if (newStartableState != oldStartableState) {
mHandler.post(PooledLambda.obtainRunnable(
IncrementalStates::reportStartableState,
IncrementalStates.this).recycleOnUse());
onStartableStateChanged();
}
}
@@ -284,8 +282,9 @@ public final class IncrementalStates {
* health
* status. If the next state is different from the current state, proceed with state
* change.
* @return True if the new startable state is different from the old one.
*/
private void updateStartableStateLocked() {
private boolean updateStartableStateLocked() {
final boolean currentState = mStartableState.isStartable();
boolean nextState = currentState;
if (!currentState) {
@@ -302,9 +301,9 @@ public final class IncrementalStates {
}
}
if (nextState == currentState) {
return;
return false;
}
mStartableState.adoptNewStartableStateLocked(nextState);
return mStartableState.adoptNewStartableStateLocked(nextState);
}
private void updateProgressLocked(float progress) {
@@ -343,12 +342,30 @@ public final class IncrementalStates {
return mUnstartableReason;
}
public void adoptNewStartableStateLocked(boolean nextState) {
/**
* Adopt new startable state if it is different from the current state.
* @param nextState True if startable, false if unstartable.
* @return True if the state has changed, false otherwise.
*/
public boolean adoptNewStartableStateLocked(boolean nextState) {
if (mIsStartable == nextState) {
return false;
}
if (!nextState) {
// Do nothing if the next state is "unstartable"; keep package always startable.
// TODO(b/171920377): completely remove unstartable state.
if (DEBUG) {
Slog.i(TAG, "Attempting to set startable state to false. Abort.");
}
return false;
}
if (DEBUG) {
Slog.i(TAG, "startable state changed from " + mIsStartable + " to " + nextState);
Slog.i(TAG,
"startable state changed from " + mIsStartable + " to " + nextState);
}
mIsStartable = nextState;
mUnstartableReason = getUnstartableReasonLocked();
return true;
}
private int getUnstartableReasonLocked() {

View File

@@ -16478,8 +16478,8 @@ public class PackageManagerService extends IPackageManager.Stub
healthCheckParams.unhealthyTimeoutMs = INCREMENTAL_STORAGE_UNHEALTHY_TIMEOUT_MS;
healthCheckParams.unhealthyMonitoringMs =
INCREMENTAL_STORAGE_UNHEALTHY_MONITORING_MS;
mIncrementalManager.registerHealthListener(codePath,
new StorageHealthCheckParams(), incrementalHealthListener);
mIncrementalManager.registerHealthListener(codePath, healthCheckParams,
incrementalHealthListener);
}
// Ensure that the uninstall reason is UNKNOWN for users with the package installed.

View File

@@ -44,7 +44,6 @@ import java.util.concurrent.atomic.AtomicInteger;
public class IncrementalStatesTest {
private IncrementalStates mIncrementalStates;
private ConditionVariable mUnstartableCalled = new ConditionVariable();
private ConditionVariable mStartableCalled = new ConditionVariable();
private ConditionVariable mFullyLoadedCalled = new ConditionVariable();
private AtomicInteger mUnstartableReason = new AtomicInteger(0);
private static final int WAIT_TIMEOUT_MILLIS = 1000; /* 1 second */
@@ -57,7 +56,6 @@ public class IncrementalStatesTest {
@Override
public void onPackageStartable() {
mStartableCalled.open();
}
@Override
@@ -77,24 +75,22 @@ public class IncrementalStatesTest {
mIncrementalStates.setCallback(mCallback);
mIncrementalStates.onCommit(true);
// Test that package is now startable and loading
assertTrue(mStartableCalled.block(WAIT_TIMEOUT_MILLIS));
assertTrue(mIncrementalStates.isStartable());
assertTrue(mIncrementalStates.isLoading());
mStartableCalled.close();
mUnstartableCalled.close();
mFullyLoadedCalled.close();
}
/**
* Test that startable state changes to false when Incremental Storage is unhealthy.
* Test that the package is still startable when Incremental Storage is unhealthy.
*/
@Test
public void testStartableTransition_IncrementalStorageUnhealthy() {
mIncrementalStates.onStorageHealthStatusChanged(
IStorageHealthListener.HEALTH_STATUS_UNHEALTHY);
// Test that package is now unstartable
assertTrue(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
assertFalse(mIncrementalStates.isStartable());
// Test that package is still startable
assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
assertTrue(mIncrementalStates.isStartable());
assertEquals(PackageManager.UNSTARTABLE_REASON_UNKNOWN, mUnstartableReason.get());
}
@@ -112,72 +108,33 @@ public class IncrementalStatesTest {
}
/**
* Test that the package becomes unstartable when health status indicate storage issues.
* Test that the package is still startable when health status indicate storage issues.
*/
@Test
public void testStartableTransition_IncrementalStorageBlocked() {
mIncrementalStates.onStorageHealthStatusChanged(
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,
// Test that package is still startable
assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
assertTrue(mIncrementalStates.isStartable());
assertEquals(PackageManager.UNSTARTABLE_REASON_UNKNOWN,
mUnstartableReason.get());
}
/**
* Test that the package becomes unstartable when health status indicates transport issues.
* Test that the package is still startable when health status indicates transport issues.
*/
@Test
public void testStartableTransition_DataLoaderIntegrityError() {
mIncrementalStates.onStorageHealthStatusChanged(
IStorageHealthListener.HEALTH_STATUS_UNHEALTHY_TRANSPORT);
// Test that package is now unstartable
assertTrue(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
assertFalse(mIncrementalStates.isStartable());
assertEquals(PackageManager.UNSTARTABLE_REASON_CONNECTION_ERROR,
// Test that package is still startable
assertFalse(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
assertTrue(mIncrementalStates.isStartable());
assertEquals(PackageManager.UNSTARTABLE_REASON_UNKNOWN,
mUnstartableReason.get());
}
/**
* Test that the package becomes unstartable when Incremental Storage is unhealthy, and it
* becomes startable again when Incremental Storage is healthy again.
*/
@Test
public void testStartableTransition_IncrementalStorageUnhealthyBackToHealthy()
throws InterruptedException {
mIncrementalStates.onStorageHealthStatusChanged(
IStorageHealthListener.HEALTH_STATUS_UNHEALTHY);
// Test that package is unstartable
assertTrue(mUnstartableCalled.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));
assertTrue(mIncrementalStates.isStartable());
}
/**
* 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.onStorageHealthStatusChanged(
IStorageHealthListener.HEALTH_STATUS_UNHEALTHY_TRANSPORT);
// Test that package is unstartable
assertTrue(mUnstartableCalled.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));
assertTrue(mIncrementalStates.isStartable());
}
/**
* Test that when loading progress is 1, the package becomes fully loaded, and the change of
* Incremental Storage health status does not affect the startable state.
@@ -196,44 +153,12 @@ public class IncrementalStatesTest {
assertTrue(mIncrementalStates.isStartable());
}
/**
* Test that when loading progress is 1, the package becomes fully loaded, and if the package
* was unstartable, it becomes startable.
*/
@Test
public void testLoadingTransition_FullyLoadedWhenUnstartable() throws InterruptedException {
mIncrementalStates.onStorageHealthStatusChanged(
IStorageHealthListener.HEALTH_STATUS_UNHEALTHY);
// Test that package is unstartable
assertTrue(mUnstartableCalled.block(WAIT_TIMEOUT_MILLIS));
assertFalse(mIncrementalStates.isStartable());
// Test that package is still loading
assertTrue(mIncrementalStates.isLoading());
mIncrementalStates.setProgress(0.5f);
// Test that package is still unstartable
assertFalse(mStartableCalled.block(WAIT_TIMEOUT_MILLIS));
assertFalse(mIncrementalStates.isStartable());
mIncrementalStates.setProgress(1.0f);
// Test that package is now startable
assertTrue(mStartableCalled.block(WAIT_TIMEOUT_MILLIS));
assertTrue(mIncrementalStates.isStartable());
// Test that package is now fully loaded
assertTrue(mFullyLoadedCalled.block(WAIT_TIMEOUT_MILLIS));
assertFalse(mIncrementalStates.isLoading());
}
/**
* Test startability transitions if app crashes or anrs
*/
@Test
public void testStartableTransition_AppCrashOrAnr() {
mIncrementalStates.onCrashOrAnr();
assertFalse(mIncrementalStates.isStartable());
mIncrementalStates.setProgress(1.0f);
assertTrue(mIncrementalStates.isStartable());
mIncrementalStates.onCrashOrAnr();
// Test that if fully loaded, app remains startable even if it has crashed
assertTrue(mIncrementalStates.isStartable());
}
}