Merge "[incremental/pm] app states and transitions"
This commit is contained in:
@@ -2731,6 +2731,54 @@ public class Intent implements Parcelable, Cloneable {
|
||||
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
|
||||
public static final String ACTION_MY_PACKAGE_UNSUSPENDED = "android.intent.action.MY_PACKAGE_UNSUSPENDED";
|
||||
|
||||
/**
|
||||
* Broadcast Action: Sent to indicate that the package becomes startable.
|
||||
* The intent will have the following extra values:
|
||||
* <ul>
|
||||
* <li> {@link #EXTRA_UID} containing the integer uid assigned to the package. </li>
|
||||
* <li> {@link #EXTRA_PACKAGE_NAME} containing the package name. </li>
|
||||
* </li>
|
||||
* </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";
|
||||
|
||||
/**
|
||||
* Broadcast Action: Sent to indicate that the package becomes unstartable.
|
||||
* The intent will have the following extra values:
|
||||
* <ul>
|
||||
* <li> {@link #EXTRA_UID} containing the integer uid assigned to the package. </li>
|
||||
* <li> {@link #EXTRA_PACKAGE_NAME} containing the package name. </li>
|
||||
* <li> {@link #EXTRA_REASON} containing the integer indicating the reason for the state change,
|
||||
* @see PackageManager.UnstartableReason
|
||||
* </li>
|
||||
* </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 =
|
||||
"android.intent.action.PACKAGE_UNSTARTABLE";
|
||||
|
||||
/**
|
||||
* Broadcast Action: Sent to indicate that the package is fully loaded.
|
||||
* <ul>
|
||||
* <li> {@link #EXTRA_UID} containing the integer uid assigned to the package. </li>
|
||||
* <li> {@link #EXTRA_PACKAGE_NAME} containing the package name. </li>
|
||||
* </li>
|
||||
* </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_FULLY_LOADED =
|
||||
"android.intent.action.PACKAGE_FULLY_LOADED";
|
||||
|
||||
/**
|
||||
* Broadcast Action: A user ID has been removed from the system. The user
|
||||
* ID number is stored in the extra data under {@link #EXTRA_UID}.
|
||||
|
||||
@@ -50,7 +50,30 @@ oneway interface IDataLoaderStatusListener {
|
||||
* fail and all retry limits are exceeded. */
|
||||
const int DATA_LOADER_UNRECOVERABLE = 8;
|
||||
|
||||
/** There are no known issues with the data stream. */
|
||||
const int STREAM_HEALTHY = 0;
|
||||
|
||||
/** There are issues with the current transport layer (network, adb connection, etc.) that may
|
||||
* recover automatically or could eventually require user intervention. */
|
||||
const int STREAM_TRANSPORT_ERROR = 1;
|
||||
|
||||
/** Integrity failures in the data stream, this could be due to file corruption, decompression
|
||||
* issues or similar. This indicates a likely unrecoverable error. */
|
||||
const int STREAM_INTEGRITY_ERROR = 2;
|
||||
|
||||
/** There are issues with the source of the data, e.g., backend availability issues, account
|
||||
* issues. This indicates a potentially recoverable error, but one that may take a long time to
|
||||
* resolve. */
|
||||
const int STREAM_SOURCE_ERROR = 3;
|
||||
|
||||
/** The device or app is low on storage and cannot complete the stream as a result.
|
||||
* A subsequent page miss resulting in app failure will transition app to unstartable state. */
|
||||
const int STREAM_STORAGE_ERROR = 4;
|
||||
|
||||
/** Data loader status callback */
|
||||
void onStatusChanged(in int dataLoaderId, in int status);
|
||||
|
||||
/** Callback to report streaming health status of a specific data loader */
|
||||
void reportStreamHealth(in int dataLoaderId, in int streamStatus);
|
||||
}
|
||||
|
||||
|
||||
20
core/java/android/content/pm/IncrementalStatesInfo.aidl
Normal file
20
core/java/android/content/pm/IncrementalStatesInfo.aidl
Normal file
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
**
|
||||
** Copyright 2020, The Android Open Source Project
|
||||
**
|
||||
** Licensed under the Apache License, Version 2.0 (the "License")
|
||||
** you may not use this file except in compliance with the License.
|
||||
** You may obtain a copy of the License at
|
||||
**
|
||||
** http://www.apache.org/licenses/LICENSE-2.0
|
||||
**
|
||||
** Unless required by applicable law or agreed to in writing, software
|
||||
** distributed under the License is distributed on an "AS IS" BASIS,
|
||||
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
** See the License for the specific language governing permissions and
|
||||
** limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.pm;
|
||||
|
||||
parcelable IncrementalStatesInfo;
|
||||
76
core/java/android/content/pm/IncrementalStatesInfo.java
Normal file
76
core/java/android/content/pm/IncrementalStatesInfo.java
Normal file
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.content.pm;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
/**
|
||||
* Info about a package's states in Parcelable format.
|
||||
* @hide
|
||||
*/
|
||||
public class IncrementalStatesInfo implements Parcelable {
|
||||
private boolean mIsStartable;
|
||||
private boolean mIsLoading;
|
||||
private float mProgress;
|
||||
|
||||
public IncrementalStatesInfo(boolean isStartable, boolean isLoading, float progress) {
|
||||
mIsStartable = isStartable;
|
||||
mIsLoading = isLoading;
|
||||
mProgress = progress;
|
||||
}
|
||||
|
||||
private IncrementalStatesInfo(Parcel source) {
|
||||
mIsStartable = source.readBoolean();
|
||||
mIsLoading = source.readBoolean();
|
||||
mProgress = source.readFloat();
|
||||
}
|
||||
|
||||
public boolean isStartable() {
|
||||
return mIsStartable;
|
||||
}
|
||||
|
||||
public boolean isLoading() {
|
||||
return mIsLoading;
|
||||
}
|
||||
|
||||
public float getProgress() {
|
||||
return mProgress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeBoolean(mIsStartable);
|
||||
dest.writeBoolean(mIsLoading);
|
||||
dest.writeFloat(mProgress);
|
||||
}
|
||||
|
||||
public static final @android.annotation.NonNull Creator<IncrementalStatesInfo> CREATOR =
|
||||
new Creator<IncrementalStatesInfo>() {
|
||||
public IncrementalStatesInfo createFromParcel(Parcel source) {
|
||||
return new IncrementalStatesInfo(source);
|
||||
}
|
||||
public IncrementalStatesInfo[] newArray(int size) {
|
||||
return new IncrementalStatesInfo[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -3786,6 +3786,39 @@ public abstract class PackageManager {
|
||||
*/
|
||||
public static final int SYSTEM_APP_STATE_UNINSTALLED = 3;
|
||||
|
||||
/**
|
||||
* Reasons for why a package is unstartable.
|
||||
* @hide
|
||||
*/
|
||||
@IntDef({UNSTARTABLE_REASON_UNKNOWN,
|
||||
UNSTARTABLE_REASON_DATALOADER_TRANSPORT,
|
||||
UNSTARTABLE_REASON_DATALOADER_STORAGE
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface UnstartableReason {}
|
||||
|
||||
/**
|
||||
* 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 after hint from dataloader of issues with the transport layer.
|
||||
* This corresponds to an alert window shown to the user indicating network errors.
|
||||
* @hide
|
||||
*/
|
||||
public static final int UNSTARTABLE_REASON_DATALOADER_TRANSPORT = 1;
|
||||
|
||||
/**
|
||||
* Unstartable state after encountering storage limitations.
|
||||
* This corresponds to an alert window indicating limited storage.
|
||||
* @hide
|
||||
*/
|
||||
public static final int UNSTARTABLE_REASON_DATALOADER_STORAGE = 2;
|
||||
|
||||
/** {@hide} */
|
||||
public int getUserId() {
|
||||
return UserHandle.myUserId();
|
||||
|
||||
@@ -44,7 +44,6 @@ import android.util.Slog;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
import com.android.internal.util.CollectionUtils;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
@@ -124,6 +124,11 @@ message PackageProto {
|
||||
optional string originating_package_name = 2;
|
||||
}
|
||||
|
||||
message StatesProto {
|
||||
optional bool is_startable = 1;
|
||||
optional bool is_loading = 2;
|
||||
}
|
||||
|
||||
// Name of package. e.g. "com.android.providers.telephony".
|
||||
optional string name = 1;
|
||||
// UID for this package as assigned by Android OS.
|
||||
@@ -145,4 +150,6 @@ message PackageProto {
|
||||
repeated UserInfoProto users = 9;
|
||||
// Where the request to install this package came from,
|
||||
optional InstallSourceProto install_source = 10;
|
||||
// Whether the package is startable or is still loading
|
||||
optional StatesProto states = 11;
|
||||
}
|
||||
|
||||
@@ -42,6 +42,9 @@
|
||||
<protected-broadcast android:name="android.intent.action.PACKAGE_REMOVED" />
|
||||
<protected-broadcast android:name="android.intent.action.PACKAGE_FULLY_REMOVED" />
|
||||
<protected-broadcast android:name="android.intent.action.PACKAGE_CHANGED" />
|
||||
<protected-broadcast android:name="android.intent.action.PACKAGE_STARTABLE" />
|
||||
<protected-broadcast android:name="android.intent.action.PACKAGE_UNSTARTABLE" />
|
||||
<protected-broadcast android:name="android.intent.action.PACKAGE_FULLY_LOADED" />
|
||||
<protected-broadcast android:name="android.intent.action.PACKAGE_ENABLE_ROLLBACK" />
|
||||
<protected-broadcast android:name="android.intent.action.CANCEL_ENABLE_ROLLBACK" />
|
||||
<protected-broadcast android:name="android.intent.action.ROLLBACK_COMMITTED" />
|
||||
|
||||
480
services/core/java/com/android/server/pm/IncrementalStates.java
Normal file
480
services/core/java/com/android/server/pm/IncrementalStates.java
Normal file
@@ -0,0 +1,480 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.pm;
|
||||
|
||||
import android.content.pm.IDataLoaderStatusListener;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Manages state transitions of a package installed on Incremental File System. Currently manages:
|
||||
* 1. startable state (whether a package is allowed to be launched), and
|
||||
* 2. loading state (whether a package is still loading or has been fully loaded).
|
||||
*
|
||||
* The following events might change the states of a package:
|
||||
* 1. Installation commit
|
||||
* 2. Incremental storage health
|
||||
* 3. Data loader stream health
|
||||
* 4. Loading progress changes
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public final class IncrementalStates {
|
||||
private static final String TAG = "IncrementalStates";
|
||||
private static final boolean DEBUG = false;
|
||||
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;
|
||||
@GuardedBy("mLock")
|
||||
private final LoadingState mLoadingState;
|
||||
@GuardedBy("mLock")
|
||||
private StartableState mStartableState;
|
||||
@GuardedBy("mLock")
|
||||
private Callback mCallback = null;
|
||||
private final BiConsumer<Integer, Integer> mStatusConsumer;
|
||||
|
||||
public IncrementalStates() {
|
||||
// By default the package is not startable and not fully loaded (i.e., is loading)
|
||||
this(false, true);
|
||||
}
|
||||
|
||||
public IncrementalStates(boolean isStartable, boolean isLoading) {
|
||||
mStartableState = new StartableState(isStartable);
|
||||
mLoadingState = new LoadingState(isLoading);
|
||||
mStatusConsumer = new StatusConsumer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback interface to report that the startable state of this package has changed.
|
||||
*/
|
||||
public interface Callback {
|
||||
/**
|
||||
* Reports that the package is now unstartable and the unstartable reason.
|
||||
*/
|
||||
void onPackageUnstartable(int reason);
|
||||
|
||||
/**
|
||||
* Reports that the package is now startable.
|
||||
*/
|
||||
void onPackageStartable();
|
||||
|
||||
/**
|
||||
* Reports that package is fully loaded.
|
||||
*/
|
||||
void onPackageFullyLoaded();
|
||||
}
|
||||
|
||||
/**
|
||||
* By calling this method, the caller indicates that package installation has just been
|
||||
* committed. The package becomes startable. Set the initial loading state after the package
|
||||
* is committed. Incremental packages are by-default loading; non-Incremental packages are not.
|
||||
*
|
||||
* @param isIncremental whether a package is installed on Incremental or not.
|
||||
*/
|
||||
public void onCommit(boolean isIncremental) {
|
||||
if (DEBUG) {
|
||||
Slog.i(TAG, "received package commit event");
|
||||
}
|
||||
synchronized (mLock) {
|
||||
if (!mStartableState.isStartable()) {
|
||||
mStartableState.adoptNewStartableStateLocked(true);
|
||||
}
|
||||
if (mLoadingState.isLoading() != isIncremental) {
|
||||
mLoadingState.adoptNewLoadingStateLocked(isIncremental);
|
||||
}
|
||||
}
|
||||
mHandler.post(PooledLambda.obtainRunnable(
|
||||
IncrementalStates::reportStartableState,
|
||||
IncrementalStates.this).recycleOnUse());
|
||||
if (!isIncremental) {
|
||||
mHandler.post(PooledLambda.obtainRunnable(
|
||||
IncrementalStates::reportFullyLoaded,
|
||||
IncrementalStates.this).recycleOnUse());
|
||||
}
|
||||
}
|
||||
|
||||
private void reportStartableState() {
|
||||
final Callback callback;
|
||||
final boolean startable;
|
||||
final int reason;
|
||||
synchronized (mLock) {
|
||||
callback = mCallback;
|
||||
startable = mStartableState.isStartable();
|
||||
reason = mStartableState.getUnstartableReason();
|
||||
}
|
||||
if (callback == null) {
|
||||
return;
|
||||
}
|
||||
if (startable) {
|
||||
callback.onPackageStartable();
|
||||
} else {
|
||||
callback.onPackageUnstartable(reason);
|
||||
}
|
||||
}
|
||||
|
||||
private void reportFullyLoaded() {
|
||||
final Callback callback;
|
||||
synchronized (mLock) {
|
||||
callback = mCallback;
|
||||
}
|
||||
if (callback != null) {
|
||||
callback.onPackageFullyLoaded();
|
||||
}
|
||||
}
|
||||
|
||||
private class StatusConsumer implements BiConsumer<Integer, Integer> {
|
||||
@Override
|
||||
public void accept(Integer streamStatus, Integer storageStatus) {
|
||||
if (streamStatus == null && storageStatus == null) {
|
||||
return;
|
||||
}
|
||||
final boolean oldState, newState;
|
||||
synchronized (mLock) {
|
||||
if (!mLoadingState.isLoading()) {
|
||||
// Do nothing if the package is already fully loaded
|
||||
return;
|
||||
}
|
||||
oldState = mStartableState.isStartable();
|
||||
if (streamStatus != null) {
|
||||
mStreamStatus = (Integer) streamStatus;
|
||||
}
|
||||
if (storageStatus != null) {
|
||||
mStorageHealthStatus = (Integer) storageStatus;
|
||||
}
|
||||
updateStartableStateLocked();
|
||||
newState = mStartableState.isStartable();
|
||||
}
|
||||
if (oldState != newState) {
|
||||
mHandler.post(PooledLambda.obtainRunnable(IncrementalStates::reportStartableState,
|
||||
IncrementalStates.this).recycleOnUse());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* By calling this method, the caller indicates that there issues with the Incremental
|
||||
* Storage,
|
||||
* on which the package is installed. The state will change according to the status
|
||||
* code defined in {@code IStorageHealthListener}.
|
||||
*/
|
||||
public void onStorageHealthStatusChanged(int storageHealthStatus) {
|
||||
if (DEBUG) {
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the specified callback to report state changing events.
|
||||
*
|
||||
* @param callback Object to report new state.
|
||||
*/
|
||||
public void setCallback(Callback callback) {
|
||||
if (DEBUG) {
|
||||
Slog.i(TAG, "registered callback");
|
||||
}
|
||||
synchronized (mLock) {
|
||||
mCallback = callback;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the package loading progress to specified value. This might change startable state.
|
||||
*
|
||||
* @param progress Value between [0, 1].
|
||||
*/
|
||||
public void setProgress(float progress) {
|
||||
final boolean newLoadingState;
|
||||
final boolean oldStartableState, newStartableState;
|
||||
synchronized (mLock) {
|
||||
oldStartableState = mStartableState.isStartable();
|
||||
updateProgressLocked(progress);
|
||||
newLoadingState = mLoadingState.isLoading();
|
||||
newStartableState = mStartableState.isStartable();
|
||||
}
|
||||
if (!newLoadingState) {
|
||||
mHandler.post(PooledLambda.obtainRunnable(
|
||||
IncrementalStates::reportFullyLoaded,
|
||||
IncrementalStates.this).recycleOnUse());
|
||||
}
|
||||
if (newStartableState != oldStartableState) {
|
||||
mHandler.post(PooledLambda.obtainRunnable(
|
||||
IncrementalStates::reportStartableState,
|
||||
IncrementalStates.this).recycleOnUse());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the current startable state.
|
||||
*/
|
||||
public boolean isStartable() {
|
||||
synchronized (mLock) {
|
||||
return mStartableState.isStartable();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Whether the package is still being loaded or has been fully loaded.
|
||||
*/
|
||||
public boolean isLoading() {
|
||||
synchronized (mLock) {
|
||||
return mLoadingState.isLoading();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return all current states in a Parcelable.
|
||||
*/
|
||||
public IncrementalStatesInfo getIncrementalStatesInfo() {
|
||||
synchronized (mLock) {
|
||||
return new IncrementalStatesInfo(mStartableState.isStartable(),
|
||||
mLoadingState.isLoading(),
|
||||
mLoadingState.getProgress());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the next state based on the current state, current stream status and storage
|
||||
* health
|
||||
* status. If the next state is different from the current state, proceed with state
|
||||
* change.
|
||||
*/
|
||||
private void updateStartableStateLocked() {
|
||||
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
|
||||
nextState = true;
|
||||
}
|
||||
} else {
|
||||
if (mStorageHealthStatus == IStorageHealthListener.HEALTH_STATUS_UNHEALTHY) {
|
||||
// unrecoverable if storage is unhealthy
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
mStartableState.adoptNewStartableStateLocked(nextState);
|
||||
}
|
||||
|
||||
private void updateProgressLocked(float progress) {
|
||||
if (DEBUG) {
|
||||
Slog.i(TAG, "received progress update: " + progress);
|
||||
}
|
||||
mLoadingState.setProgress(progress);
|
||||
if (1 - progress < 0.001) {
|
||||
if (DEBUG) {
|
||||
Slog.i(TAG, "package is fully loaded");
|
||||
}
|
||||
mLoadingState.setProgress(1);
|
||||
if (mLoadingState.isLoading()) {
|
||||
mLoadingState.adoptNewLoadingStateLocked(false);
|
||||
}
|
||||
// Also updates startable state if necessary
|
||||
if (!mStartableState.isStartable()) {
|
||||
mStartableState.adoptNewStartableStateLocked(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class StartableState {
|
||||
private boolean mIsStartable;
|
||||
private int mUnstartableReason = PackageManager.UNSTARTABLE_REASON_UNKNOWN;
|
||||
|
||||
StartableState(boolean isStartable) {
|
||||
mIsStartable = isStartable;
|
||||
}
|
||||
|
||||
public boolean isStartable() {
|
||||
return mIsStartable;
|
||||
}
|
||||
|
||||
public int getUnstartableReason() {
|
||||
return mUnstartableReason;
|
||||
}
|
||||
|
||||
public void adoptNewStartableStateLocked(boolean nextState) {
|
||||
if (DEBUG) {
|
||||
Slog.i(TAG, "startable state changed from " + mIsStartable + " to " + nextState);
|
||||
}
|
||||
mIsStartable = nextState;
|
||||
mUnstartableReason = getUnstartableReasonLocked();
|
||||
}
|
||||
|
||||
private int getUnstartableReasonLocked() {
|
||||
if (mIsStartable) {
|
||||
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_DATALOADER_TRANSPORT;
|
||||
}
|
||||
case IDataLoaderStatusListener.STREAM_STORAGE_ERROR: {
|
||||
return PackageManager.UNSTARTABLE_REASON_DATALOADER_STORAGE;
|
||||
}
|
||||
default:
|
||||
return PackageManager.UNSTARTABLE_REASON_UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof StartableState)) {
|
||||
return false;
|
||||
}
|
||||
StartableState l = (StartableState) o;
|
||||
return l.mIsStartable == mIsStartable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Boolean.hashCode(mIsStartable);
|
||||
}
|
||||
}
|
||||
|
||||
private class LoadingState {
|
||||
private boolean mIsLoading;
|
||||
private float mProgress;
|
||||
|
||||
LoadingState(boolean isLoading) {
|
||||
mIsLoading = isLoading;
|
||||
mProgress = isLoading ? 0 : 1;
|
||||
}
|
||||
|
||||
public boolean isLoading() {
|
||||
return mIsLoading;
|
||||
}
|
||||
|
||||
public float getProgress() {
|
||||
return mProgress;
|
||||
}
|
||||
|
||||
public void setProgress(float progress) {
|
||||
mProgress = progress;
|
||||
}
|
||||
|
||||
public void adoptNewLoadingStateLocked(boolean nextState) {
|
||||
if (DEBUG) {
|
||||
Slog.i(TAG, "Loading state changed from " + mIsLoading + " to " + nextState);
|
||||
}
|
||||
mIsLoading = nextState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof LoadingState)) {
|
||||
return false;
|
||||
}
|
||||
LoadingState l = (LoadingState) o;
|
||||
return l.mIsLoading == mIsLoading && l.mProgress == mProgress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hashCode = Boolean.hashCode(mIsLoading);
|
||||
hashCode = 31 * hashCode + Float.hashCode(mProgress);
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this) {
|
||||
return true;
|
||||
}
|
||||
if (!(o instanceof IncrementalStates)) {
|
||||
return false;
|
||||
}
|
||||
IncrementalStates l = (IncrementalStates) o;
|
||||
return l.mStorageHealthStatus == mStorageHealthStatus
|
||||
&& l.mStreamStatus == mStreamStatus
|
||||
&& l.mStartableState.equals(mStartableState)
|
||||
&& l.mLoadingState.equals(mLoadingState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hashCode = mStartableState.hashCode();
|
||||
hashCode = 31 * hashCode + mLoadingState.hashCode();
|
||||
hashCode = 31 * hashCode + mStorageHealthStatus;
|
||||
hashCode = 31 * hashCode + mStreamStatus;
|
||||
return hashCode;
|
||||
}
|
||||
}
|
||||
@@ -141,6 +141,7 @@ import com.android.internal.util.ArrayUtils;
|
||||
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;
|
||||
@@ -1700,20 +1701,26 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
private void onStorageUnhealthy() {
|
||||
private void onStorageHealthStatusChanged(int status) {
|
||||
final String packageName = getPackageName();
|
||||
if (TextUtils.isEmpty(packageName)) {
|
||||
// The package has not been installed.
|
||||
return;
|
||||
}
|
||||
final PackageManagerService packageManagerService = mPm;
|
||||
mHandler.post(() -> {
|
||||
if (packageManagerService.deletePackageX(packageName,
|
||||
PackageManager.VERSION_CODE_HIGHEST, UserHandle.USER_SYSTEM,
|
||||
PackageManager.DELETE_ALL_USERS) != PackageManager.DELETE_SUCCEEDED) {
|
||||
Slog.e(TAG, "Failed to uninstall package with failed dataloader: " + packageName);
|
||||
}
|
||||
});
|
||||
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());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3261,7 +3268,9 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
||||
if (isDestroyedOrDataLoaderFinished) {
|
||||
switch (status) {
|
||||
case IDataLoaderStatusListener.DATA_LOADER_UNRECOVERABLE:
|
||||
onStorageUnhealthy();
|
||||
// treat as unhealthy storage
|
||||
onStorageHealthStatusChanged(
|
||||
IStorageHealthListener.HEALTH_STATUS_UNHEALTHY);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
@@ -3358,6 +3367,16 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
||||
sendPendingStreaming(mContext, statusReceiver, sessionId, e.getMessage());
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void reportStreamHealth(int dataLoaderId, int streamStatus) {
|
||||
synchronized (mLock) {
|
||||
if (!mDestroyed && !mDataLoaderFinished) {
|
||||
// ignore streaming status if package isn't installed
|
||||
return;
|
||||
}
|
||||
}
|
||||
onStreamHealthStatusChanged(streamStatus);
|
||||
}
|
||||
};
|
||||
|
||||
if (!manualStartAndDestroy) {
|
||||
@@ -3377,11 +3396,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub {
|
||||
}
|
||||
if (isDestroyedOrDataLoaderFinished) {
|
||||
// App's installed.
|
||||
switch (status) {
|
||||
case IStorageHealthListener.HEALTH_STATUS_UNHEALTHY:
|
||||
onStorageUnhealthy();
|
||||
return;
|
||||
}
|
||||
onStorageHealthStatusChanged(status);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -16392,12 +16392,24 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
} else if (!previousUserIds.contains(userId)) {
|
||||
ps.setInstallReason(installReason, userId);
|
||||
}
|
||||
|
||||
// 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);
|
||||
ps.setIncrementalStatesCallback(incrementalStatesCallback);
|
||||
}
|
||||
|
||||
// Ensure that the uninstall reason is UNKNOWN for users with the package installed.
|
||||
for (int currentUserId : allUsersList) {
|
||||
if (ps.getInstalled(currentUserId)) {
|
||||
ps.setUninstallReason(UNINSTALL_REASON_UNKNOWN, currentUserId);
|
||||
}
|
||||
}
|
||||
|
||||
mSettings.writeKernelMappingLPr(ps);
|
||||
}
|
||||
res.name = pkgName;
|
||||
@@ -17194,6 +17206,7 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
mDexManager.notifyPackageUpdated(pkg.getPackageName(),
|
||||
pkg.getBaseApkPath(), pkg.getSplitCodePaths());
|
||||
}
|
||||
reconciledPkg.pkgSetting.setStatesOnCommit();
|
||||
|
||||
// Prepare the application profiles for the new code paths.
|
||||
// This needs to be done before invoking dexopt so that any install-time profile
|
||||
@@ -17290,6 +17303,155 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
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 mUserId;
|
||||
private final int[] mInstalledUserIds;
|
||||
|
||||
IncrementalStatesCallback(PackageSetting packageSetting, int userId) {
|
||||
mPackageSetting = packageSetting;
|
||||
mPackageName = packageSetting.name;
|
||||
mUserId = userId;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPackageLoadingProgressChanged(float progress) {
|
||||
synchronized (mPackageSetting) {
|
||||
mPackageSetting.setLoadingProgress(progress);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPackageFullyLoaded() {
|
||||
mIncrementalManager.unregisterCallback(mPathString, this);
|
||||
final SparseArray<int[]> newBroadcastAllowList;
|
||||
synchronized (mLock) {
|
||||
newBroadcastAllowList = mAppsFilter.getVisibilityAllowList(
|
||||
getPackageSettingInternal(mPackageName, Process.SYSTEM_UID),
|
||||
mInstalledUserIds, mSettings.mPackages);
|
||||
}
|
||||
Bundle extras = new Bundle(1);
|
||||
extras.putInt(Intent.EXTRA_UID, mUserId);
|
||||
extras.putString(Intent.EXTRA_PACKAGE_NAME, mPackageName);
|
||||
sendPackageBroadcast(Intent.ACTION_PACKAGE_FULLY_LOADED, mPackageName,
|
||||
extras, 0 /*flags*/,
|
||||
null /*targetPackage*/, null /*finishedReceiver*/,
|
||||
mInstalledUserIds, null /* instantUserIds */, newBroadcastAllowList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPackageUnstartable(int reason) {
|
||||
final SparseArray<int[]> newBroadcastAllowList;
|
||||
synchronized (mLock) {
|
||||
newBroadcastAllowList = mAppsFilter.getVisibilityAllowList(
|
||||
getPackageSettingInternal(mPackageName, Process.SYSTEM_UID),
|
||||
mInstalledUserIds, mSettings.mPackages);
|
||||
}
|
||||
Bundle extras = new Bundle(1);
|
||||
extras.putInt(Intent.EXTRA_UID, mUserId);
|
||||
extras.putString(Intent.EXTRA_PACKAGE_NAME, mPackageName);
|
||||
extras.putInt(Intent.EXTRA_REASON, reason);
|
||||
// send broadcast to users with this app installed
|
||||
sendPackageBroadcast(Intent.ACTION_PACKAGE_UNSTARTABLE, mPackageName,
|
||||
extras, 0 /*flags*/,
|
||||
null /*targetPackage*/, null /*finishedReceiver*/,
|
||||
mInstalledUserIds, null /* instantUserIds */, newBroadcastAllowList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPackageStartable() {
|
||||
final SparseArray<int[]> newBroadcastAllowList;
|
||||
synchronized (mLock) {
|
||||
newBroadcastAllowList = mAppsFilter.getVisibilityAllowList(
|
||||
getPackageSettingInternal(mPackageName, Process.SYSTEM_UID),
|
||||
mInstalledUserIds, mSettings.mPackages);
|
||||
}
|
||||
Bundle extras = new Bundle(1);
|
||||
extras.putInt(Intent.EXTRA_UID, mUserId);
|
||||
extras.putString(Intent.EXTRA_PACKAGE_NAME, mPackageName);
|
||||
// send broadcast to users with this app installed
|
||||
sendPackageBroadcast(Intent.ACTION_PACKAGE_STARTABLE, mPackageName,
|
||||
extras, 0 /*flags*/,
|
||||
null /*targetPackage*/, null /*finishedReceiver*/,
|
||||
mInstalledUserIds, null /* instantUserIds */, newBroadcastAllowList);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
ps.setStorageHealthStatus(status);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
ps.setStreamStatus(status);
|
||||
}
|
||||
|
||||
@Nullable PackageSetting getPackageSettingForUser(String packageName, int callingUid,
|
||||
int userId) {
|
||||
final PackageSetting ps;
|
||||
synchronized (mLock) {
|
||||
ps = mSettings.mPackages.get(packageName);
|
||||
if (ps == null) {
|
||||
Slog.w(TAG, "Failed to get package setting. Package " + packageName
|
||||
+ " is not installed");
|
||||
return null;
|
||||
}
|
||||
if (!ps.getInstalled(userId)) {
|
||||
Slog.w(TAG, "Failed to get package setting. Package " + packageName
|
||||
+ " is not installed for user " + userId);
|
||||
return null;
|
||||
}
|
||||
if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
|
||||
Slog.w(TAG, "Failed to get package setting. Package " + packageName
|
||||
+ " is not visible to the calling app");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return ps;
|
||||
}
|
||||
|
||||
private void notifyPackageChangeObserversOnUpdate(ReconciledPackage reconciledPkg) {
|
||||
final PackageSetting pkgSetting = reconciledPkg.pkgSetting;
|
||||
final PackageInstalledInfo pkgInstalledInfo = reconciledPkg.installResult;
|
||||
@@ -25409,24 +25571,15 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
@Override
|
||||
public boolean registerInstalledLoadingProgressCallback(String packageName,
|
||||
PackageManagerInternal.InstalledLoadingProgressCallback callback, int userId) {
|
||||
final int callingUid = Binder.getCallingUid();
|
||||
mPermissionManager.enforceCrossUserPermission(
|
||||
callingUid, userId, true, false,
|
||||
"registerLoadingProgressCallback");
|
||||
final PackageSetting ps;
|
||||
synchronized (mLock) {
|
||||
ps = mSettings.mPackages.get(packageName);
|
||||
if (ps == null) {
|
||||
Slog.w(TAG, "Failed registering loading progress callback. Package "
|
||||
+ packageName + " is not installed");
|
||||
return false;
|
||||
}
|
||||
if (shouldFilterApplicationLocked(ps, callingUid, userId)) {
|
||||
Slog.w(TAG, "Failed registering loading progress callback. Package "
|
||||
+ packageName + " is not visible to the calling app");
|
||||
return false;
|
||||
}
|
||||
// TODO(b/165841827): return false if package is fully loaded
|
||||
final PackageSetting ps = getPackageSettingForUser(packageName, Binder.getCallingUid(),
|
||||
userId);
|
||||
if (ps == null) {
|
||||
return false;
|
||||
}
|
||||
if (!ps.isPackageLoading()) {
|
||||
Slog.w(TAG,
|
||||
"Failed registering loading progress callback. Package is fully loaded.");
|
||||
return false;
|
||||
}
|
||||
if (mIncrementalManager == null) {
|
||||
Slog.w(TAG,
|
||||
|
||||
@@ -333,6 +333,8 @@ public class PackageSetting extends PackageSettingBase {
|
||||
installSource.originatingPackageName);
|
||||
proto.end(sourceToken);
|
||||
}
|
||||
proto.write(PackageProto.StatesProto.IS_STARTABLE, incrementalStates.isStartable());
|
||||
proto.write(PackageProto.StatesProto.IS_LOADING, incrementalStates.isLoading());
|
||||
writeUsersInfoToProto(proto, PackageProto.USERS);
|
||||
proto.end(packageToken);
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import android.annotation.Nullable;
|
||||
import android.annotation.UserIdInt;
|
||||
import android.content.ComponentName;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.IncrementalStatesInfo;
|
||||
import android.content.pm.IntentFilterVerificationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.UninstallReason;
|
||||
@@ -33,6 +34,7 @@ import android.content.pm.PackageUserState;
|
||||
import android.content.pm.Signature;
|
||||
import android.content.pm.SuspendDialogInfo;
|
||||
import android.os.PersistableBundle;
|
||||
import android.os.incremental.IncrementalManager;
|
||||
import android.service.pm.PackageProto;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
@@ -133,6 +135,9 @@ public abstract class PackageSettingBase extends SettingBase {
|
||||
|
||||
boolean forceQueryableOverride;
|
||||
|
||||
@NonNull
|
||||
public IncrementalStates incrementalStates;
|
||||
|
||||
PackageSettingBase(String name, String realName, @NonNull File path,
|
||||
String legacyNativeLibraryPathString, String primaryCpuAbiString,
|
||||
String secondaryCpuAbiString, String cpuAbiOverrideString,
|
||||
@@ -151,6 +156,7 @@ public abstract class PackageSettingBase extends SettingBase {
|
||||
this.versionCode = pVersionCode;
|
||||
this.signatures = new PackageSignatures();
|
||||
this.installSource = InstallSource.EMPTY;
|
||||
this.incrementalStates = new IncrementalStates();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -257,6 +263,7 @@ public abstract class PackageSettingBase extends SettingBase {
|
||||
orig.usesStaticLibrariesVersions.length) : null;
|
||||
updateAvailable = orig.updateAvailable;
|
||||
forceQueryableOverride = orig.forceQueryableOverride;
|
||||
incrementalStates = orig.incrementalStates;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -733,6 +740,66 @@ public abstract class PackageSettingBase extends SettingBase {
|
||||
modifyUserState(userId).resetOverrideComponentLabelIcon();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if package is startable, false otherwise.
|
||||
*/
|
||||
public boolean isPackageStartable() {
|
||||
return incrementalStates.isStartable();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if package is still being loaded, false if the package is fully loaded.
|
||||
*/
|
||||
public boolean isPackageLoading() {
|
||||
return incrementalStates.isLoading();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return all current states in a Parcelable.
|
||||
*/
|
||||
public IncrementalStatesInfo getIncrementalStates() {
|
||||
return incrementalStates.getIncrementalStatesInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to indicate that the package installation has been committed. This will create a
|
||||
* new startable state and a new loading state with default values. By default, the package is
|
||||
* startable after commit. For a package installed on Incremental, the loading state is true.
|
||||
* For non-Incremental packages, the loading state is false.
|
||||
*/
|
||||
public void setStatesOnCommit() {
|
||||
incrementalStates.onCommit(IncrementalManager.isIncrementalPath(getPathString()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to set the callback to listen for startable state changes.
|
||||
*/
|
||||
public void setIncrementalStatesCallback(IncrementalStates.Callback callback) {
|
||||
incrementalStates.setCallback(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to report progress changes. This might trigger loading state change.
|
||||
* @see IncrementalStates#setProgress(float)
|
||||
*/
|
||||
public void setLoadingProgress(float progress) {
|
||||
incrementalStates.setProgress(progress);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see IncrementalStates#onStorageHealthStatusChanged(int)
|
||||
*/
|
||||
public void setStorageHealthStatus(int status) {
|
||||
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());
|
||||
@@ -756,6 +823,7 @@ public abstract class PackageSettingBase extends SettingBase {
|
||||
this.updateAvailable = other.updateAvailable;
|
||||
this.verificationInfo = other.verificationInfo;
|
||||
this.forceQueryableOverride = other.forceQueryableOverride;
|
||||
this.incrementalStates = other.incrementalStates;
|
||||
|
||||
if (mOldCodePaths != null) {
|
||||
if (other.mOldCodePaths != null) {
|
||||
|
||||
@@ -2809,6 +2809,12 @@ public final class Settings {
|
||||
if (pkg.forceQueryableOverride) {
|
||||
serializer.attribute(null, "forceQueryable", "true");
|
||||
}
|
||||
if (pkg.isPackageStartable()) {
|
||||
serializer.attribute(null, "isStartable", "true");
|
||||
}
|
||||
if (pkg.isPackageLoading()) {
|
||||
serializer.attribute(null, "isLoading", "true");
|
||||
}
|
||||
|
||||
writeUsesStaticLibLPw(serializer, pkg.usesStaticLibraries, pkg.usesStaticLibrariesVersions);
|
||||
|
||||
@@ -3594,6 +3600,8 @@ public final class Settings {
|
||||
String version = null;
|
||||
long versionCode = 0;
|
||||
String installedForceQueryable = null;
|
||||
String isStartable = null;
|
||||
String isLoading = null;
|
||||
try {
|
||||
name = parser.getAttributeValue(null, ATTR_NAME);
|
||||
realName = parser.getAttributeValue(null, "realName");
|
||||
@@ -3610,6 +3618,8 @@ public final class Settings {
|
||||
cpuAbiOverrideString = parser.getAttributeValue(null, "cpuAbiOverride");
|
||||
updateAvailable = parser.getAttributeValue(null, "updateAvailable");
|
||||
installedForceQueryable = parser.getAttributeValue(null, "forceQueryable");
|
||||
isStartable = parser.getAttributeValue(null, "isStartable");
|
||||
isLoading = parser.getAttributeValue(null, "isLoading");
|
||||
|
||||
if (primaryCpuAbiString == null && legacyCpuAbiString != null) {
|
||||
primaryCpuAbiString = legacyCpuAbiString;
|
||||
@@ -3793,6 +3803,8 @@ public final class Settings {
|
||||
packageSetting.secondaryCpuAbiString = secondaryCpuAbiString;
|
||||
packageSetting.updateAvailable = "true".equals(updateAvailable);
|
||||
packageSetting.forceQueryableOverride = "true".equals(installedForceQueryable);
|
||||
packageSetting.incrementalStates = new IncrementalStates("true".equals(isStartable),
|
||||
"true".equals(isLoading));
|
||||
// Handle legacy string here for single-user mode
|
||||
final String enabledStr = parser.getAttributeValue(null, ATTR_ENABLED);
|
||||
if (enabledStr != null) {
|
||||
|
||||
@@ -2135,6 +2135,11 @@ binder::Status IncrementalService::DataLoaderStub::onStatusChanged(MountId mount
|
||||
return binder::Status::ok();
|
||||
}
|
||||
|
||||
binder::Status IncrementalService::DataLoaderStub::reportStreamHealth(MountId mountId,
|
||||
int newStatus) {
|
||||
return binder::Status::ok();
|
||||
}
|
||||
|
||||
bool IncrementalService::DataLoaderStub::isHealthParamsValid() const {
|
||||
return mHealthCheckParams.blockedTimeoutMs > 0 &&
|
||||
mHealthCheckParams.blockedTimeoutMs < mHealthCheckParams.unhealthyTimeoutMs;
|
||||
|
||||
@@ -200,6 +200,7 @@ private:
|
||||
|
||||
private:
|
||||
binder::Status onStatusChanged(MountId mount, int newStatus) final;
|
||||
binder::Status reportStreamHealth(MountId mount, int newStatus) final;
|
||||
|
||||
sp<content::pm::IDataLoader> getDataLoader();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user