Merge "Implement timeout mechanism to wait for network connectivity." am: f4df08e197 am: 30e24cd0e6
Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/2216691 Change-Id: Ie9e2206b54ef03abded1dd010b49c517ae104dd7 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -36,6 +36,7 @@ import android.content.pm.UserInfo;
|
|||||||
import android.net.ConnectivityManager;
|
import android.net.ConnectivityManager;
|
||||||
import android.net.Network;
|
import android.net.Network;
|
||||||
import android.net.NetworkCapabilities;
|
import android.net.NetworkCapabilities;
|
||||||
|
import android.net.NetworkRequest;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.PowerManager;
|
import android.os.PowerManager;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
@@ -103,12 +104,12 @@ class RebootEscrowManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Number of boots until we consider the escrow data to be stale for the purposes of metrics.
|
* Number of boots until we consider the escrow data to be stale for the purposes of metrics.
|
||||||
* <p>
|
*
|
||||||
* If the delta between the current boot number and the boot number stored when the mechanism
|
* <p>If the delta between the current boot number and the boot number stored when the mechanism
|
||||||
* was armed is under this number and the escrow mechanism fails, we report it as a failure of
|
* was armed is under this number and the escrow mechanism fails, we report it as a failure of
|
||||||
* the mechanism.
|
* the mechanism.
|
||||||
* <p>
|
*
|
||||||
* If the delta over this number and escrow fails, we will not report the metric as failed
|
* <p>If the delta over this number and escrow fails, we will not report the metric as failed
|
||||||
* since there most likely was some other issue if the device rebooted several times before
|
* since there most likely was some other issue if the device rebooted several times before
|
||||||
* getting to the escrow restore code.
|
* getting to the escrow restore code.
|
||||||
*/
|
*/
|
||||||
@@ -120,8 +121,11 @@ class RebootEscrowManager {
|
|||||||
*/
|
*/
|
||||||
private static final int DEFAULT_LOAD_ESCROW_DATA_RETRY_COUNT = 3;
|
private static final int DEFAULT_LOAD_ESCROW_DATA_RETRY_COUNT = 3;
|
||||||
private static final int DEFAULT_LOAD_ESCROW_DATA_RETRY_INTERVAL_SECONDS = 30;
|
private static final int DEFAULT_LOAD_ESCROW_DATA_RETRY_INTERVAL_SECONDS = 30;
|
||||||
|
|
||||||
// 3 minutes. It's enough for the default 3 retries with 30 seconds interval
|
// 3 minutes. It's enough for the default 3 retries with 30 seconds interval
|
||||||
private static final int DEFAULT_WAKE_LOCK_TIMEOUT_MILLIS = 180_000;
|
private static final int DEFAULT_LOAD_ESCROW_BASE_TIMEOUT_MILLIS = 180_000;
|
||||||
|
// 5 seconds. An extension of the overall RoR timeout to account for overhead.
|
||||||
|
private static final int DEFAULT_LOAD_ESCROW_TIMEOUT_EXTENSION_MILLIS = 5000;
|
||||||
|
|
||||||
@IntDef(prefix = {"ERROR_"}, value = {
|
@IntDef(prefix = {"ERROR_"}, value = {
|
||||||
ERROR_NONE,
|
ERROR_NONE,
|
||||||
@@ -133,6 +137,7 @@ class RebootEscrowManager {
|
|||||||
ERROR_PROVIDER_MISMATCH,
|
ERROR_PROVIDER_MISMATCH,
|
||||||
ERROR_KEYSTORE_FAILURE,
|
ERROR_KEYSTORE_FAILURE,
|
||||||
ERROR_NO_NETWORK,
|
ERROR_NO_NETWORK,
|
||||||
|
ERROR_TIMEOUT_EXHAUSTED,
|
||||||
})
|
})
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
@interface RebootEscrowErrorCode {
|
@interface RebootEscrowErrorCode {
|
||||||
@@ -147,6 +152,7 @@ class RebootEscrowManager {
|
|||||||
static final int ERROR_PROVIDER_MISMATCH = 6;
|
static final int ERROR_PROVIDER_MISMATCH = 6;
|
||||||
static final int ERROR_KEYSTORE_FAILURE = 7;
|
static final int ERROR_KEYSTORE_FAILURE = 7;
|
||||||
static final int ERROR_NO_NETWORK = 8;
|
static final int ERROR_NO_NETWORK = 8;
|
||||||
|
static final int ERROR_TIMEOUT_EXHAUSTED = 9;
|
||||||
|
|
||||||
private @RebootEscrowErrorCode int mLoadEscrowDataErrorCode = ERROR_NONE;
|
private @RebootEscrowErrorCode int mLoadEscrowDataErrorCode = ERROR_NONE;
|
||||||
|
|
||||||
@@ -168,6 +174,15 @@ class RebootEscrowManager {
|
|||||||
/** Notified when mRebootEscrowReady changes. */
|
/** Notified when mRebootEscrowReady changes. */
|
||||||
private RebootEscrowListener mRebootEscrowListener;
|
private RebootEscrowListener mRebootEscrowListener;
|
||||||
|
|
||||||
|
/** Set when unlocking reboot escrow times out. */
|
||||||
|
private boolean mRebootEscrowTimedOut = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set when {@link #loadRebootEscrowDataWithRetry} is called to ensure the function is only
|
||||||
|
* called once.
|
||||||
|
*/
|
||||||
|
private boolean mLoadEscrowDataWithRetry = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hold this lock when checking or generating the reboot escrow key.
|
* Hold this lock when checking or generating the reboot escrow key.
|
||||||
*/
|
*/
|
||||||
@@ -192,6 +207,7 @@ class RebootEscrowManager {
|
|||||||
|
|
||||||
PowerManager.WakeLock mWakeLock;
|
PowerManager.WakeLock mWakeLock;
|
||||||
|
|
||||||
|
private ConnectivityManager.NetworkCallback mNetworkCallback;
|
||||||
|
|
||||||
interface Callbacks {
|
interface Callbacks {
|
||||||
boolean isUserSecure(int userId);
|
boolean isUserSecure(int userId);
|
||||||
@@ -246,6 +262,11 @@ class RebootEscrowManager {
|
|||||||
"server_based_ror_enabled", false);
|
"server_based_ror_enabled", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean waitForInternet() {
|
||||||
|
return DeviceConfig.getBoolean(
|
||||||
|
DeviceConfig.NAMESPACE_OTA, "wait_for_internet_ror", false);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isNetworkConnected() {
|
public boolean isNetworkConnected() {
|
||||||
final ConnectivityManager connectivityManager =
|
final ConnectivityManager connectivityManager =
|
||||||
mContext.getSystemService(ConnectivityManager.class);
|
mContext.getSystemService(ConnectivityManager.class);
|
||||||
@@ -263,6 +284,38 @@ class RebootEscrowManager {
|
|||||||
NetworkCapabilities.NET_CAPABILITY_VALIDATED);
|
NetworkCapabilities.NET_CAPABILITY_VALIDATED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request network with internet connectivity with timeout.
|
||||||
|
*
|
||||||
|
* @param networkCallback callback to be executed if connectivity manager exists.
|
||||||
|
* @return true if success
|
||||||
|
*/
|
||||||
|
public boolean requestNetworkWithInternet(
|
||||||
|
ConnectivityManager.NetworkCallback networkCallback) {
|
||||||
|
final ConnectivityManager connectivityManager =
|
||||||
|
mContext.getSystemService(ConnectivityManager.class);
|
||||||
|
if (connectivityManager == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
NetworkRequest request =
|
||||||
|
new NetworkRequest.Builder()
|
||||||
|
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
connectivityManager.requestNetwork(
|
||||||
|
request, networkCallback, getLoadEscrowTimeoutMillis());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stopRequestingNetwork(ConnectivityManager.NetworkCallback networkCallback) {
|
||||||
|
final ConnectivityManager connectivityManager =
|
||||||
|
mContext.getSystemService(ConnectivityManager.class);
|
||||||
|
if (connectivityManager == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
connectivityManager.unregisterNetworkCallback(networkCallback);
|
||||||
|
}
|
||||||
|
|
||||||
public Context getContext() {
|
public Context getContext() {
|
||||||
return mContext;
|
return mContext;
|
||||||
}
|
}
|
||||||
@@ -318,6 +371,16 @@ class RebootEscrowManager {
|
|||||||
DEFAULT_LOAD_ESCROW_DATA_RETRY_INTERVAL_SECONDS);
|
DEFAULT_LOAD_ESCROW_DATA_RETRY_INTERVAL_SECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public int getLoadEscrowTimeoutMillis() {
|
||||||
|
return DEFAULT_LOAD_ESCROW_BASE_TIMEOUT_MILLIS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public int getWakeLockTimeoutMillis() {
|
||||||
|
return getLoadEscrowTimeoutMillis() + DEFAULT_LOAD_ESCROW_TIMEOUT_EXTENSION_MILLIS;
|
||||||
|
}
|
||||||
|
|
||||||
public void reportMetric(boolean success, int errorCode, int serviceType, int attemptCount,
|
public void reportMetric(boolean success, int errorCode, int serviceType, int attemptCount,
|
||||||
int escrowDurationInSeconds, int vbmetaDigestStatus,
|
int escrowDurationInSeconds, int vbmetaDigestStatus,
|
||||||
int durationSinceBootCompleteInSeconds) {
|
int durationSinceBootCompleteInSeconds) {
|
||||||
@@ -351,13 +414,37 @@ class RebootEscrowManager {
|
|||||||
mKeyStoreManager = injector.getKeyStoreManager();
|
mKeyStoreManager = injector.getKeyStoreManager();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onGetRebootEscrowKeyFailed(List<UserInfo> users, int attemptCount) {
|
/** Wrapper function to set error code serialized through handler, */
|
||||||
|
private void setLoadEscrowDataErrorCode(@RebootEscrowErrorCode int value, Handler handler) {
|
||||||
|
if (mInjector.waitForInternet()) {
|
||||||
|
mInjector.post(
|
||||||
|
handler,
|
||||||
|
() -> {
|
||||||
|
mLoadEscrowDataErrorCode = value;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
mLoadEscrowDataErrorCode = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Wrapper function to compare and set error code serialized through handler. */
|
||||||
|
private void compareAndSetLoadEscrowDataErrorCode(
|
||||||
|
@RebootEscrowErrorCode int expectedValue,
|
||||||
|
@RebootEscrowErrorCode int newValue,
|
||||||
|
Handler handler) {
|
||||||
|
if (expectedValue == mLoadEscrowDataErrorCode) {
|
||||||
|
setLoadEscrowDataErrorCode(newValue, handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onGetRebootEscrowKeyFailed(
|
||||||
|
List<UserInfo> users, int attemptCount, Handler retryHandler) {
|
||||||
Slog.w(TAG, "Had reboot escrow data for users, but no key; removing escrow storage.");
|
Slog.w(TAG, "Had reboot escrow data for users, but no key; removing escrow storage.");
|
||||||
for (UserInfo user : users) {
|
for (UserInfo user : users) {
|
||||||
mStorage.removeRebootEscrow(user.id);
|
mStorage.removeRebootEscrow(user.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
onEscrowRestoreComplete(false, attemptCount);
|
onEscrowRestoreComplete(false, attemptCount, retryHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
void loadRebootEscrowDataIfAvailable(Handler retryHandler) {
|
void loadRebootEscrowDataIfAvailable(Handler retryHandler) {
|
||||||
@@ -380,39 +467,130 @@ class RebootEscrowManager {
|
|||||||
mWakeLock = mInjector.getWakeLock();
|
mWakeLock = mInjector.getWakeLock();
|
||||||
if (mWakeLock != null) {
|
if (mWakeLock != null) {
|
||||||
mWakeLock.setReferenceCounted(false);
|
mWakeLock.setReferenceCounted(false);
|
||||||
mWakeLock.acquire(DEFAULT_WAKE_LOCK_TIMEOUT_MILLIS);
|
mWakeLock.acquire(mInjector.getWakeLockTimeoutMillis());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mInjector.waitForInternet()) {
|
||||||
|
// Timeout to stop retrying same as the wake lock timeout.
|
||||||
|
mInjector.postDelayed(
|
||||||
|
retryHandler,
|
||||||
|
() -> {
|
||||||
|
mRebootEscrowTimedOut = true;
|
||||||
|
},
|
||||||
|
mInjector.getLoadEscrowTimeoutMillis());
|
||||||
|
|
||||||
|
mInjector.post(
|
||||||
|
retryHandler,
|
||||||
|
() -> loadRebootEscrowDataOnInternet(retryHandler, users, rebootEscrowUsers));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mInjector.post(retryHandler, () -> loadRebootEscrowDataWithRetry(
|
mInjector.post(retryHandler, () -> loadRebootEscrowDataWithRetry(
|
||||||
retryHandler, 0, users, rebootEscrowUsers));
|
retryHandler, 0, users, rebootEscrowUsers));
|
||||||
}
|
}
|
||||||
|
|
||||||
void scheduleLoadRebootEscrowDataOrFail(Handler retryHandler, int attemptNumber,
|
void scheduleLoadRebootEscrowDataOrFail(
|
||||||
List<UserInfo> users, List<UserInfo> rebootEscrowUsers) {
|
Handler retryHandler,
|
||||||
|
int attemptNumber,
|
||||||
|
List<UserInfo> users,
|
||||||
|
List<UserInfo> rebootEscrowUsers) {
|
||||||
Objects.requireNonNull(retryHandler);
|
Objects.requireNonNull(retryHandler);
|
||||||
|
|
||||||
final int retryLimit = mInjector.getLoadEscrowDataRetryLimit();
|
final int retryLimit = mInjector.getLoadEscrowDataRetryLimit();
|
||||||
final int retryIntervalInSeconds = mInjector.getLoadEscrowDataRetryIntervalSeconds();
|
final int retryIntervalInSeconds = mInjector.getLoadEscrowDataRetryIntervalSeconds();
|
||||||
|
|
||||||
if (attemptNumber < retryLimit) {
|
if (attemptNumber < retryLimit && !mRebootEscrowTimedOut) {
|
||||||
Slog.i(TAG, "Scheduling loadRebootEscrowData retry number: " + attemptNumber);
|
Slog.i(TAG, "Scheduling loadRebootEscrowData retry number: " + attemptNumber);
|
||||||
mInjector.postDelayed(retryHandler, () -> loadRebootEscrowDataWithRetry(
|
mInjector.postDelayed(retryHandler, () -> loadRebootEscrowDataWithRetry(
|
||||||
retryHandler, attemptNumber, users, rebootEscrowUsers),
|
retryHandler, attemptNumber, users, rebootEscrowUsers),
|
||||||
retryIntervalInSeconds * 1000);
|
retryIntervalInSeconds * 1000);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mInjector.waitForInternet()) {
|
||||||
|
if (mRebootEscrowTimedOut) {
|
||||||
|
Slog.w(TAG, "Failed to load reboot escrow data within timeout");
|
||||||
|
compareAndSetLoadEscrowDataErrorCode(
|
||||||
|
ERROR_NONE, ERROR_TIMEOUT_EXHAUSTED, retryHandler);
|
||||||
|
} else {
|
||||||
|
Slog.w(
|
||||||
|
TAG,
|
||||||
|
"Failed to load reboot escrow data after " + attemptNumber + " attempts");
|
||||||
|
compareAndSetLoadEscrowDataErrorCode(
|
||||||
|
ERROR_NONE, ERROR_RETRY_COUNT_EXHAUSTED, retryHandler);
|
||||||
|
}
|
||||||
|
onGetRebootEscrowKeyFailed(users, attemptNumber, retryHandler);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Slog.w(TAG, "Failed to load reboot escrow data after " + attemptNumber + " attempts");
|
Slog.w(TAG, "Failed to load reboot escrow data after " + attemptNumber + " attempts");
|
||||||
if (mInjector.serverBasedResumeOnReboot() && !mInjector.isNetworkConnected()) {
|
if (mInjector.serverBasedResumeOnReboot() && !mInjector.isNetworkConnected()) {
|
||||||
mLoadEscrowDataErrorCode = ERROR_NO_NETWORK;
|
mLoadEscrowDataErrorCode = ERROR_NO_NETWORK;
|
||||||
} else {
|
} else {
|
||||||
mLoadEscrowDataErrorCode = ERROR_RETRY_COUNT_EXHAUSTED;
|
mLoadEscrowDataErrorCode = ERROR_RETRY_COUNT_EXHAUSTED;
|
||||||
}
|
}
|
||||||
onGetRebootEscrowKeyFailed(users, attemptNumber);
|
onGetRebootEscrowKeyFailed(users, attemptNumber, retryHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
void loadRebootEscrowDataWithRetry(Handler retryHandler, int attemptNumber,
|
void loadRebootEscrowDataOnInternet(
|
||||||
List<UserInfo> users, List<UserInfo> rebootEscrowUsers) {
|
Handler retryHandler, List<UserInfo> users, List<UserInfo> rebootEscrowUsers) {
|
||||||
|
|
||||||
|
// HAL-Based RoR does not require network connectivity.
|
||||||
|
if (!mInjector.serverBasedResumeOnReboot()) {
|
||||||
|
loadRebootEscrowDataWithRetry(
|
||||||
|
retryHandler, /* attemptNumber = */ 0, users, rebootEscrowUsers);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mNetworkCallback =
|
||||||
|
new ConnectivityManager.NetworkCallback() {
|
||||||
|
@Override
|
||||||
|
public void onAvailable(Network network) {
|
||||||
|
compareAndSetLoadEscrowDataErrorCode(
|
||||||
|
ERROR_NO_NETWORK, ERROR_NONE, retryHandler);
|
||||||
|
|
||||||
|
if (!mLoadEscrowDataWithRetry) {
|
||||||
|
mLoadEscrowDataWithRetry = true;
|
||||||
|
// Only kickoff retry mechanism on first onAvailable call.
|
||||||
|
loadRebootEscrowDataWithRetry(
|
||||||
|
retryHandler,
|
||||||
|
/* attemptNumber = */ 0,
|
||||||
|
users,
|
||||||
|
rebootEscrowUsers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onUnavailable() {
|
||||||
|
Slog.w(TAG, "Failed to connect to network within timeout");
|
||||||
|
compareAndSetLoadEscrowDataErrorCode(
|
||||||
|
ERROR_NONE, ERROR_NO_NETWORK, retryHandler);
|
||||||
|
onGetRebootEscrowKeyFailed(users, /* attemptCount= */ 0, retryHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onLost(Network lostNetwork) {
|
||||||
|
// TODO(b/231660348): If network is lost, wait for network to become
|
||||||
|
// available again.
|
||||||
|
Slog.w(TAG, "Network lost, still attempting to load escrow key.");
|
||||||
|
compareAndSetLoadEscrowDataErrorCode(
|
||||||
|
ERROR_NONE, ERROR_NO_NETWORK, retryHandler);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Fallback to retrying without waiting for internet on failure.
|
||||||
|
boolean success = mInjector.requestNetworkWithInternet(mNetworkCallback);
|
||||||
|
if (!success) {
|
||||||
|
loadRebootEscrowDataWithRetry(
|
||||||
|
retryHandler, /* attemptNumber = */ 0, users, rebootEscrowUsers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadRebootEscrowDataWithRetry(
|
||||||
|
Handler retryHandler,
|
||||||
|
int attemptNumber,
|
||||||
|
List<UserInfo> users,
|
||||||
|
List<UserInfo> rebootEscrowUsers) {
|
||||||
// Fetch the key from keystore to decrypt the escrow data & escrow key; this key is
|
// Fetch the key from keystore to decrypt the escrow data & escrow key; this key is
|
||||||
// generated before reboot. Note that we will clear the escrow key even if the keystore key
|
// generated before reboot. Note that we will clear the escrow key even if the keystore key
|
||||||
// is null.
|
// is null.
|
||||||
@@ -423,7 +601,7 @@ class RebootEscrowManager {
|
|||||||
|
|
||||||
RebootEscrowKey escrowKey;
|
RebootEscrowKey escrowKey;
|
||||||
try {
|
try {
|
||||||
escrowKey = getAndClearRebootEscrowKey(kk);
|
escrowKey = getAndClearRebootEscrowKey(kk, retryHandler);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Slog.i(TAG, "Failed to load escrow key, scheduling retry.", e);
|
Slog.i(TAG, "Failed to load escrow key, scheduling retry.", e);
|
||||||
scheduleLoadRebootEscrowDataOrFail(retryHandler, attemptNumber + 1, users,
|
scheduleLoadRebootEscrowDataOrFail(retryHandler, attemptNumber + 1, users,
|
||||||
@@ -438,12 +616,12 @@ class RebootEscrowManager {
|
|||||||
? RebootEscrowProviderInterface.TYPE_SERVER_BASED
|
? RebootEscrowProviderInterface.TYPE_SERVER_BASED
|
||||||
: RebootEscrowProviderInterface.TYPE_HAL;
|
: RebootEscrowProviderInterface.TYPE_HAL;
|
||||||
if (providerType != mStorage.getInt(REBOOT_ESCROW_KEY_PROVIDER, -1, USER_SYSTEM)) {
|
if (providerType != mStorage.getInt(REBOOT_ESCROW_KEY_PROVIDER, -1, USER_SYSTEM)) {
|
||||||
mLoadEscrowDataErrorCode = ERROR_PROVIDER_MISMATCH;
|
setLoadEscrowDataErrorCode(ERROR_PROVIDER_MISMATCH, retryHandler);
|
||||||
} else {
|
} else {
|
||||||
mLoadEscrowDataErrorCode = ERROR_LOAD_ESCROW_KEY;
|
setLoadEscrowDataErrorCode(ERROR_LOAD_ESCROW_KEY, retryHandler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
onGetRebootEscrowKeyFailed(users, attemptNumber + 1);
|
onGetRebootEscrowKeyFailed(users, attemptNumber + 1, retryHandler);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -454,10 +632,10 @@ class RebootEscrowManager {
|
|||||||
allUsersUnlocked &= restoreRebootEscrowForUser(user.id, escrowKey, kk);
|
allUsersUnlocked &= restoreRebootEscrowForUser(user.id, escrowKey, kk);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!allUsersUnlocked && mLoadEscrowDataErrorCode == ERROR_NONE) {
|
if (!allUsersUnlocked) {
|
||||||
mLoadEscrowDataErrorCode = ERROR_UNLOCK_ALL_USERS;
|
compareAndSetLoadEscrowDataErrorCode(ERROR_NONE, ERROR_UNLOCK_ALL_USERS, retryHandler);
|
||||||
}
|
}
|
||||||
onEscrowRestoreComplete(allUsersUnlocked, attemptNumber + 1);
|
onEscrowRestoreComplete(allUsersUnlocked, attemptNumber + 1, retryHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void clearMetricsStorage() {
|
private void clearMetricsStorage() {
|
||||||
@@ -497,7 +675,8 @@ class RebootEscrowManager {
|
|||||||
.REBOOT_ESCROW_RECOVERY_REPORTED__VBMETA_DIGEST_STATUS__MISMATCH;
|
.REBOOT_ESCROW_RECOVERY_REPORTED__VBMETA_DIGEST_STATUS__MISMATCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void reportMetricOnRestoreComplete(boolean success, int attemptCount) {
|
private void reportMetricOnRestoreComplete(
|
||||||
|
boolean success, int attemptCount, Handler retryHandler) {
|
||||||
int serviceType = mInjector.serverBasedResumeOnReboot()
|
int serviceType = mInjector.serverBasedResumeOnReboot()
|
||||||
? FrameworkStatsLog.REBOOT_ESCROW_RECOVERY_REPORTED__TYPE__SERVER_BASED
|
? FrameworkStatsLog.REBOOT_ESCROW_RECOVERY_REPORTED__TYPE__SERVER_BASED
|
||||||
: FrameworkStatsLog.REBOOT_ESCROW_RECOVERY_REPORTED__TYPE__HAL;
|
: FrameworkStatsLog.REBOOT_ESCROW_RECOVERY_REPORTED__TYPE__HAL;
|
||||||
@@ -511,52 +690,69 @@ class RebootEscrowManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int vbmetaDigestStatus = getVbmetaDigestStatusOnRestoreComplete();
|
int vbmetaDigestStatus = getVbmetaDigestStatusOnRestoreComplete();
|
||||||
if (!success && mLoadEscrowDataErrorCode == ERROR_NONE) {
|
if (!success) {
|
||||||
mLoadEscrowDataErrorCode = ERROR_UNKNOWN;
|
compareAndSetLoadEscrowDataErrorCode(ERROR_NONE, ERROR_UNKNOWN, retryHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
Slog.i(TAG, "Reporting RoR recovery metrics, success: " + success + ", service type: "
|
Slog.i(
|
||||||
+ serviceType + ", error code: " + mLoadEscrowDataErrorCode);
|
TAG,
|
||||||
|
"Reporting RoR recovery metrics, success: "
|
||||||
|
+ success
|
||||||
|
+ ", service type: "
|
||||||
|
+ serviceType
|
||||||
|
+ ", error code: "
|
||||||
|
+ mLoadEscrowDataErrorCode);
|
||||||
// TODO(179105110) report the duration since boot complete.
|
// TODO(179105110) report the duration since boot complete.
|
||||||
mInjector.reportMetric(success, mLoadEscrowDataErrorCode, serviceType, attemptCount,
|
mInjector.reportMetric(
|
||||||
escrowDurationInSeconds, vbmetaDigestStatus, -1);
|
success,
|
||||||
|
mLoadEscrowDataErrorCode,
|
||||||
|
serviceType,
|
||||||
|
attemptCount,
|
||||||
|
escrowDurationInSeconds,
|
||||||
|
vbmetaDigestStatus,
|
||||||
|
-1);
|
||||||
|
|
||||||
mLoadEscrowDataErrorCode = ERROR_NONE;
|
setLoadEscrowDataErrorCode(ERROR_NONE, retryHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onEscrowRestoreComplete(boolean success, int attemptCount) {
|
private void onEscrowRestoreComplete(boolean success, int attemptCount, Handler retryHandler) {
|
||||||
int previousBootCount = mStorage.getInt(REBOOT_ESCROW_ARMED_KEY, -1, USER_SYSTEM);
|
int previousBootCount = mStorage.getInt(REBOOT_ESCROW_ARMED_KEY, -1, USER_SYSTEM);
|
||||||
|
|
||||||
int bootCountDelta = mInjector.getBootCount() - previousBootCount;
|
int bootCountDelta = mInjector.getBootCount() - previousBootCount;
|
||||||
if (success || (previousBootCount != -1 && bootCountDelta <= BOOT_COUNT_TOLERANCE)) {
|
if (success || (previousBootCount != -1 && bootCountDelta <= BOOT_COUNT_TOLERANCE)) {
|
||||||
reportMetricOnRestoreComplete(success, attemptCount);
|
reportMetricOnRestoreComplete(success, attemptCount, retryHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear the old key in keystore. A new key will be generated by new RoR requests.
|
// Clear the old key in keystore. A new key will be generated by new RoR requests.
|
||||||
mKeyStoreManager.clearKeyStoreEncryptionKey();
|
mKeyStoreManager.clearKeyStoreEncryptionKey();
|
||||||
// Clear the saved reboot escrow provider
|
// Clear the saved reboot escrow provider
|
||||||
mInjector.clearRebootEscrowProvider();
|
mInjector.clearRebootEscrowProvider();
|
||||||
clearMetricsStorage();
|
clearMetricsStorage();
|
||||||
|
|
||||||
|
if (mNetworkCallback != null) {
|
||||||
|
mInjector.stopRequestingNetwork(mNetworkCallback);
|
||||||
|
}
|
||||||
|
|
||||||
if (mWakeLock != null) {
|
if (mWakeLock != null) {
|
||||||
mWakeLock.release();
|
mWakeLock.release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private RebootEscrowKey getAndClearRebootEscrowKey(SecretKey kk) throws IOException {
|
private RebootEscrowKey getAndClearRebootEscrowKey(SecretKey kk, Handler retryHandler)
|
||||||
|
throws IOException {
|
||||||
RebootEscrowProviderInterface rebootEscrowProvider =
|
RebootEscrowProviderInterface rebootEscrowProvider =
|
||||||
mInjector.createRebootEscrowProviderIfNeeded();
|
mInjector.createRebootEscrowProviderIfNeeded();
|
||||||
if (rebootEscrowProvider == null) {
|
if (rebootEscrowProvider == null) {
|
||||||
Slog.w(TAG,
|
Slog.w(
|
||||||
|
TAG,
|
||||||
"Had reboot escrow data for users, but RebootEscrowProvider is unavailable");
|
"Had reboot escrow data for users, but RebootEscrowProvider is unavailable");
|
||||||
mLoadEscrowDataErrorCode = ERROR_NO_PROVIDER;
|
setLoadEscrowDataErrorCode(ERROR_NO_PROVIDER, retryHandler);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Server based RoR always need the decryption key from keystore.
|
// Server based RoR always need the decryption key from keystore.
|
||||||
if (rebootEscrowProvider.getType() == RebootEscrowProviderInterface.TYPE_SERVER_BASED
|
if (rebootEscrowProvider.getType() == RebootEscrowProviderInterface.TYPE_SERVER_BASED
|
||||||
&& kk == null) {
|
&& kk == null) {
|
||||||
mLoadEscrowDataErrorCode = ERROR_KEYSTORE_FAILURE;
|
setLoadEscrowDataErrorCode(ERROR_KEYSTORE_FAILURE, retryHandler);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -870,6 +1066,9 @@ class RebootEscrowManager {
|
|||||||
pw.print("mRebootEscrowListener=");
|
pw.print("mRebootEscrowListener=");
|
||||||
pw.println(mRebootEscrowListener);
|
pw.println(mRebootEscrowListener);
|
||||||
|
|
||||||
|
pw.print("mLoadEscrowDataErrorCode=");
|
||||||
|
pw.println(mLoadEscrowDataErrorCode);
|
||||||
|
|
||||||
boolean keySet;
|
boolean keySet;
|
||||||
synchronized (mKeyGenerationLock) {
|
synchronized (mKeyGenerationLock) {
|
||||||
keySet = mPendingRebootEscrowKey != null;
|
keySet = mPendingRebootEscrowKey != null;
|
||||||
|
|||||||
@@ -50,6 +50,8 @@ import android.content.Context;
|
|||||||
import android.content.ContextWrapper;
|
import android.content.ContextWrapper;
|
||||||
import android.content.pm.UserInfo;
|
import android.content.pm.UserInfo;
|
||||||
import android.hardware.rebootescrow.IRebootEscrow;
|
import android.hardware.rebootescrow.IRebootEscrow;
|
||||||
|
import android.net.ConnectivityManager;
|
||||||
|
import android.net.Network;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.HandlerThread;
|
import android.os.HandlerThread;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
@@ -72,6 +74,7 @@ import org.mockito.ArgumentCaptor;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
@@ -113,6 +116,7 @@ public class RebootEscrowManagerTests {
|
|||||||
private RebootEscrowManager mService;
|
private RebootEscrowManager mService;
|
||||||
private SecretKey mAesKey;
|
private SecretKey mAesKey;
|
||||||
private MockInjector mMockInjector;
|
private MockInjector mMockInjector;
|
||||||
|
private Handler mHandler;
|
||||||
|
|
||||||
public interface MockableRebootEscrowInjected {
|
public interface MockableRebootEscrowInjected {
|
||||||
int getBootCount();
|
int getBootCount();
|
||||||
@@ -132,6 +136,9 @@ public class RebootEscrowManagerTests {
|
|||||||
private final RebootEscrowKeyStoreManager mKeyStoreManager;
|
private final RebootEscrowKeyStoreManager mKeyStoreManager;
|
||||||
private boolean mServerBased;
|
private boolean mServerBased;
|
||||||
private RebootEscrowProviderInterface mRebootEscrowProviderInUse;
|
private RebootEscrowProviderInterface mRebootEscrowProviderInUse;
|
||||||
|
private ConnectivityManager.NetworkCallback mNetworkCallback;
|
||||||
|
private Consumer<ConnectivityManager.NetworkCallback> mNetworkConsumer;
|
||||||
|
private boolean mWaitForInternet;
|
||||||
|
|
||||||
MockInjector(Context context, UserManager userManager,
|
MockInjector(Context context, UserManager userManager,
|
||||||
IRebootEscrow rebootEscrow,
|
IRebootEscrow rebootEscrow,
|
||||||
@@ -142,6 +149,7 @@ public class RebootEscrowManagerTests {
|
|||||||
mRebootEscrow = rebootEscrow;
|
mRebootEscrow = rebootEscrow;
|
||||||
mServiceConnection = null;
|
mServiceConnection = null;
|
||||||
mServerBased = false;
|
mServerBased = false;
|
||||||
|
mWaitForInternet = false;
|
||||||
RebootEscrowProviderHalImpl.Injector halInjector =
|
RebootEscrowProviderHalImpl.Injector halInjector =
|
||||||
new RebootEscrowProviderHalImpl.Injector() {
|
new RebootEscrowProviderHalImpl.Injector() {
|
||||||
@Override
|
@Override
|
||||||
@@ -164,6 +172,7 @@ public class RebootEscrowManagerTests {
|
|||||||
mServiceConnection = serviceConnection;
|
mServiceConnection = serviceConnection;
|
||||||
mRebootEscrow = null;
|
mRebootEscrow = null;
|
||||||
mServerBased = true;
|
mServerBased = true;
|
||||||
|
mWaitForInternet = false;
|
||||||
RebootEscrowProviderServerBasedImpl.Injector injector =
|
RebootEscrowProviderServerBasedImpl.Injector injector =
|
||||||
new RebootEscrowProviderServerBasedImpl.Injector(serviceConnection) {
|
new RebootEscrowProviderServerBasedImpl.Injector(serviceConnection) {
|
||||||
@Override
|
@Override
|
||||||
@@ -198,11 +207,33 @@ public class RebootEscrowManagerTests {
|
|||||||
return mServerBased;
|
return mServerBased;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean waitForInternet() {
|
||||||
|
return mWaitForInternet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWaitForNetwork(boolean waitForNetworkEnabled) {
|
||||||
|
mWaitForInternet = waitForNetworkEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isNetworkConnected() {
|
public boolean isNetworkConnected() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean requestNetworkWithInternet(
|
||||||
|
ConnectivityManager.NetworkCallback networkCallback) {
|
||||||
|
mNetworkCallback = networkCallback;
|
||||||
|
mNetworkConsumer.accept(networkCallback);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stopRequestingNetwork(ConnectivityManager.NetworkCallback networkCallback) {
|
||||||
|
mNetworkCallback = null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RebootEscrowProviderInterface createRebootEscrowProviderIfNeeded() {
|
public RebootEscrowProviderInterface createRebootEscrowProviderIfNeeded() {
|
||||||
mRebootEscrowProviderInUse = mDefaultRebootEscrowProvider;
|
mRebootEscrowProviderInUse = mDefaultRebootEscrowProvider;
|
||||||
@@ -241,6 +272,12 @@ public class RebootEscrowManagerTests {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getLoadEscrowTimeoutMillis() {
|
||||||
|
// Timeout in 3 seconds.
|
||||||
|
return 3000;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getVbmetaDigest(boolean other) {
|
public String getVbmetaDigest(boolean other) {
|
||||||
return other ? "" : "fake digest";
|
return other ? "" : "fake digest";
|
||||||
@@ -291,6 +328,9 @@ public class RebootEscrowManagerTests {
|
|||||||
mMockInjector = new MockInjector(mContext, mUserManager, mRebootEscrow,
|
mMockInjector = new MockInjector(mContext, mUserManager, mRebootEscrow,
|
||||||
mKeyStoreManager, mStorage, mInjected);
|
mKeyStoreManager, mStorage, mInjected);
|
||||||
mService = new RebootEscrowManager(mMockInjector, mCallbacks, mStorage);
|
mService = new RebootEscrowManager(mMockInjector, mCallbacks, mStorage);
|
||||||
|
HandlerThread thread = new HandlerThread("RebootEscrowManagerTest");
|
||||||
|
thread.start();
|
||||||
|
mHandler = new Handler(thread.getLooper());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setServerBasedRebootEscrowProvider() throws Exception {
|
private void setServerBasedRebootEscrowProvider() throws Exception {
|
||||||
@@ -462,7 +502,7 @@ public class RebootEscrowManagerTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void loadRebootEscrowDataIfAvailable_NothingAvailable_Success() throws Exception {
|
public void loadRebootEscrowDataIfAvailable_NothingAvailable_Success() throws Exception {
|
||||||
mService.loadRebootEscrowDataIfAvailable(null);
|
mService.loadRebootEscrowDataIfAvailable(mHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -499,7 +539,7 @@ public class RebootEscrowManagerTests {
|
|||||||
eq(20), eq(0) /* vbmeta status */, anyInt());
|
eq(20), eq(0) /* vbmeta status */, anyInt());
|
||||||
when(mRebootEscrow.retrieveKey()).thenAnswer(invocation -> keyByteCaptor.getValue());
|
when(mRebootEscrow.retrieveKey()).thenAnswer(invocation -> keyByteCaptor.getValue());
|
||||||
|
|
||||||
mService.loadRebootEscrowDataIfAvailable(null);
|
mService.loadRebootEscrowDataIfAvailable(mHandler);
|
||||||
verify(mRebootEscrow).retrieveKey();
|
verify(mRebootEscrow).retrieveKey();
|
||||||
assertTrue(metricsSuccessCaptor.getValue());
|
assertTrue(metricsSuccessCaptor.getValue());
|
||||||
verify(mKeyStoreManager).clearKeyStoreEncryptionKey();
|
verify(mKeyStoreManager).clearKeyStoreEncryptionKey();
|
||||||
@@ -531,9 +571,16 @@ public class RebootEscrowManagerTests {
|
|||||||
// pretend reboot happens here
|
// pretend reboot happens here
|
||||||
when(mInjected.getBootCount()).thenReturn(1);
|
when(mInjected.getBootCount()).thenReturn(1);
|
||||||
ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
|
ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
|
||||||
doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture(),
|
doNothing()
|
||||||
eq(0) /* error code */, eq(2) /* Server based */, eq(1) /* attempt count */,
|
.when(mInjected)
|
||||||
anyInt(), eq(0) /* vbmeta status */, anyInt());
|
.reportMetric(
|
||||||
|
metricsSuccessCaptor.capture(),
|
||||||
|
eq(0) /* error code */,
|
||||||
|
eq(2) /* Server based */,
|
||||||
|
eq(1) /* attempt count */,
|
||||||
|
anyInt(),
|
||||||
|
eq(0) /* vbmeta status */,
|
||||||
|
anyInt());
|
||||||
|
|
||||||
when(mServiceConnection.unwrap(any(), anyLong()))
|
when(mServiceConnection.unwrap(any(), anyLong()))
|
||||||
.thenAnswer(invocation -> invocation.getArgument(0));
|
.thenAnswer(invocation -> invocation.getArgument(0));
|
||||||
@@ -569,15 +616,23 @@ public class RebootEscrowManagerTests {
|
|||||||
when(mInjected.getBootCount()).thenReturn(1);
|
when(mInjected.getBootCount()).thenReturn(1);
|
||||||
ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
|
ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
|
||||||
ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class);
|
ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class);
|
||||||
doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture(),
|
doNothing()
|
||||||
metricsErrorCodeCaptor.capture(), eq(2) /* Server based */,
|
.when(mInjected)
|
||||||
eq(1) /* attempt count */, anyInt(), eq(0) /* vbmeta status */, anyInt());
|
.reportMetric(
|
||||||
|
metricsSuccessCaptor.capture(),
|
||||||
|
metricsErrorCodeCaptor.capture(),
|
||||||
|
eq(2) /* Server based */,
|
||||||
|
eq(1) /* attempt count */,
|
||||||
|
anyInt(),
|
||||||
|
eq(0) /* vbmeta status */,
|
||||||
|
anyInt());
|
||||||
|
|
||||||
when(mServiceConnection.unwrap(any(), anyLong())).thenThrow(RemoteException.class);
|
when(mServiceConnection.unwrap(any(), anyLong())).thenThrow(RemoteException.class);
|
||||||
mService.loadRebootEscrowDataIfAvailable(null);
|
mService.loadRebootEscrowDataIfAvailable(null);
|
||||||
verify(mServiceConnection).unwrap(any(), anyLong());
|
verify(mServiceConnection).unwrap(any(), anyLong());
|
||||||
assertFalse(metricsSuccessCaptor.getValue());
|
assertFalse(metricsSuccessCaptor.getValue());
|
||||||
assertEquals(Integer.valueOf(RebootEscrowManager.ERROR_LOAD_ESCROW_KEY),
|
assertEquals(
|
||||||
|
Integer.valueOf(RebootEscrowManager.ERROR_LOAD_ESCROW_KEY),
|
||||||
metricsErrorCodeCaptor.getValue());
|
metricsErrorCodeCaptor.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -606,18 +661,24 @@ public class RebootEscrowManagerTests {
|
|||||||
when(mInjected.getBootCount()).thenReturn(1);
|
when(mInjected.getBootCount()).thenReturn(1);
|
||||||
ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
|
ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
|
||||||
ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class);
|
ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class);
|
||||||
doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture(),
|
doNothing()
|
||||||
metricsErrorCodeCaptor.capture(), eq(2) /* Server based */,
|
.when(mInjected)
|
||||||
eq(2) /* attempt count */, anyInt(), eq(0) /* vbmeta status */, anyInt());
|
.reportMetric(
|
||||||
|
metricsSuccessCaptor.capture(),
|
||||||
|
metricsErrorCodeCaptor.capture(),
|
||||||
|
eq(2) /* Server based */,
|
||||||
|
eq(2) /* attempt count */,
|
||||||
|
anyInt(),
|
||||||
|
eq(0) /* vbmeta status */,
|
||||||
|
anyInt());
|
||||||
when(mServiceConnection.unwrap(any(), anyLong())).thenThrow(IOException.class);
|
when(mServiceConnection.unwrap(any(), anyLong())).thenThrow(IOException.class);
|
||||||
|
|
||||||
HandlerThread thread = new HandlerThread("RebootEscrowManagerTest");
|
mService.loadRebootEscrowDataIfAvailable(mHandler);
|
||||||
thread.start();
|
|
||||||
mService.loadRebootEscrowDataIfAvailable(new Handler(thread.getLooper()));
|
|
||||||
// Sleep 5s for the retry to complete
|
// Sleep 5s for the retry to complete
|
||||||
Thread.sleep(5 * 1000);
|
Thread.sleep(5 * 1000);
|
||||||
assertFalse(metricsSuccessCaptor.getValue());
|
assertFalse(metricsSuccessCaptor.getValue());
|
||||||
assertEquals(Integer.valueOf(RebootEscrowManager.ERROR_NO_NETWORK),
|
assertEquals(
|
||||||
|
Integer.valueOf(RebootEscrowManager.ERROR_NO_NETWORK),
|
||||||
metricsErrorCodeCaptor.getValue());
|
metricsErrorCodeCaptor.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -645,16 +706,22 @@ public class RebootEscrowManagerTests {
|
|||||||
// pretend reboot happens here
|
// pretend reboot happens here
|
||||||
when(mInjected.getBootCount()).thenReturn(1);
|
when(mInjected.getBootCount()).thenReturn(1);
|
||||||
ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
|
ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
|
||||||
doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture(),
|
doNothing()
|
||||||
anyInt(), anyInt(), eq(2) /* attempt count */, anyInt(), anyInt(), anyInt());
|
.when(mInjected)
|
||||||
|
.reportMetric(
|
||||||
|
metricsSuccessCaptor.capture(),
|
||||||
|
anyInt(),
|
||||||
|
anyInt(),
|
||||||
|
eq(2) /* attempt count */,
|
||||||
|
anyInt(),
|
||||||
|
anyInt(),
|
||||||
|
anyInt());
|
||||||
|
|
||||||
when(mServiceConnection.unwrap(any(), anyLong()))
|
when(mServiceConnection.unwrap(any(), anyLong()))
|
||||||
.thenThrow(new IOException())
|
.thenThrow(new IOException())
|
||||||
.thenAnswer(invocation -> invocation.getArgument(0));
|
.thenAnswer(invocation -> invocation.getArgument(0));
|
||||||
|
|
||||||
HandlerThread thread = new HandlerThread("RebootEscrowManagerTest");
|
mService.loadRebootEscrowDataIfAvailable(mHandler);
|
||||||
thread.start();
|
|
||||||
mService.loadRebootEscrowDataIfAvailable(new Handler(thread.getLooper()));
|
|
||||||
// Sleep 5s for the retry to complete
|
// Sleep 5s for the retry to complete
|
||||||
Thread.sleep(5 * 1000);
|
Thread.sleep(5 * 1000);
|
||||||
verify(mServiceConnection, times(2)).unwrap(any(), anyLong());
|
verify(mServiceConnection, times(2)).unwrap(any(), anyLong());
|
||||||
@@ -662,6 +729,447 @@ public class RebootEscrowManagerTests {
|
|||||||
verify(mKeyStoreManager).clearKeyStoreEncryptionKey();
|
verify(mKeyStoreManager).clearKeyStoreEncryptionKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void loadRebootEscrowDataIfAvailable_serverBasedWaitForInternet_success()
|
||||||
|
throws Exception {
|
||||||
|
setServerBasedRebootEscrowProvider();
|
||||||
|
mMockInjector.setWaitForNetwork(true);
|
||||||
|
|
||||||
|
when(mInjected.getBootCount()).thenReturn(0);
|
||||||
|
RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
|
||||||
|
mService.setRebootEscrowListener(mockListener);
|
||||||
|
mService.prepareRebootEscrow();
|
||||||
|
|
||||||
|
clearInvocations(mServiceConnection);
|
||||||
|
mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
|
||||||
|
verify(mockListener).onPreparedForReboot(eq(true));
|
||||||
|
verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());
|
||||||
|
|
||||||
|
// Use x -> x for both wrap & unwrap functions.
|
||||||
|
when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong()))
|
||||||
|
.thenAnswer(invocation -> invocation.getArgument(0));
|
||||||
|
assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded());
|
||||||
|
verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong());
|
||||||
|
assertTrue(mStorage.hasRebootEscrowServerBlob());
|
||||||
|
|
||||||
|
// pretend reboot happens here
|
||||||
|
when(mInjected.getBootCount()).thenReturn(1);
|
||||||
|
ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
|
||||||
|
doNothing()
|
||||||
|
.when(mInjected)
|
||||||
|
.reportMetric(
|
||||||
|
metricsSuccessCaptor.capture(),
|
||||||
|
eq(0) /* error code */,
|
||||||
|
eq(2) /* Server based */,
|
||||||
|
eq(1) /* attempt count */,
|
||||||
|
anyInt(),
|
||||||
|
eq(0) /* vbmeta status */,
|
||||||
|
anyInt());
|
||||||
|
|
||||||
|
// load escrow data
|
||||||
|
when(mServiceConnection.unwrap(any(), anyLong()))
|
||||||
|
.thenAnswer(invocation -> invocation.getArgument(0));
|
||||||
|
Network mockNetwork = mock(Network.class);
|
||||||
|
mMockInjector.mNetworkConsumer =
|
||||||
|
(callback) -> {
|
||||||
|
callback.onAvailable(mockNetwork);
|
||||||
|
};
|
||||||
|
|
||||||
|
mService.loadRebootEscrowDataIfAvailable(mHandler);
|
||||||
|
verify(mServiceConnection).unwrap(any(), anyLong());
|
||||||
|
assertTrue(metricsSuccessCaptor.getValue());
|
||||||
|
verify(mKeyStoreManager).clearKeyStoreEncryptionKey();
|
||||||
|
assertNull(mMockInjector.mNetworkCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void loadRebootEscrowDataIfAvailable_serverBasedWaitForInternetRemoteException_Failure()
|
||||||
|
throws Exception {
|
||||||
|
setServerBasedRebootEscrowProvider();
|
||||||
|
mMockInjector.setWaitForNetwork(true);
|
||||||
|
|
||||||
|
when(mInjected.getBootCount()).thenReturn(0);
|
||||||
|
RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
|
||||||
|
mService.setRebootEscrowListener(mockListener);
|
||||||
|
mService.prepareRebootEscrow();
|
||||||
|
|
||||||
|
clearInvocations(mServiceConnection);
|
||||||
|
mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
|
||||||
|
verify(mockListener).onPreparedForReboot(eq(true));
|
||||||
|
verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());
|
||||||
|
|
||||||
|
// Use x -> x for both wrap & unwrap functions.
|
||||||
|
when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong()))
|
||||||
|
.thenAnswer(invocation -> invocation.getArgument(0));
|
||||||
|
assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded());
|
||||||
|
verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong());
|
||||||
|
assertTrue(mStorage.hasRebootEscrowServerBlob());
|
||||||
|
|
||||||
|
// pretend reboot happens here
|
||||||
|
when(mInjected.getBootCount()).thenReturn(1);
|
||||||
|
ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
|
||||||
|
ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class);
|
||||||
|
doNothing()
|
||||||
|
.when(mInjected)
|
||||||
|
.reportMetric(
|
||||||
|
metricsSuccessCaptor.capture(),
|
||||||
|
metricsErrorCodeCaptor.capture(),
|
||||||
|
eq(2) /* Server based */,
|
||||||
|
eq(1) /* attempt count */,
|
||||||
|
anyInt(),
|
||||||
|
eq(0) /* vbmeta status */,
|
||||||
|
anyInt());
|
||||||
|
|
||||||
|
// load escrow data
|
||||||
|
when(mServiceConnection.unwrap(any(), anyLong())).thenThrow(RemoteException.class);
|
||||||
|
Network mockNetwork = mock(Network.class);
|
||||||
|
mMockInjector.mNetworkConsumer =
|
||||||
|
(callback) -> {
|
||||||
|
callback.onAvailable(mockNetwork);
|
||||||
|
};
|
||||||
|
|
||||||
|
mService.loadRebootEscrowDataIfAvailable(mHandler);
|
||||||
|
verify(mServiceConnection).unwrap(any(), anyLong());
|
||||||
|
assertFalse(metricsSuccessCaptor.getValue());
|
||||||
|
assertEquals(
|
||||||
|
Integer.valueOf(RebootEscrowManager.ERROR_LOAD_ESCROW_KEY),
|
||||||
|
metricsErrorCodeCaptor.getValue());
|
||||||
|
assertNull(mMockInjector.mNetworkCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void loadRebootEscrowDataIfAvailable_waitForInternet_networkUnavailable()
|
||||||
|
throws Exception {
|
||||||
|
setServerBasedRebootEscrowProvider();
|
||||||
|
mMockInjector.setWaitForNetwork(true);
|
||||||
|
|
||||||
|
when(mInjected.getBootCount()).thenReturn(0);
|
||||||
|
RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
|
||||||
|
mService.setRebootEscrowListener(mockListener);
|
||||||
|
mService.prepareRebootEscrow();
|
||||||
|
|
||||||
|
clearInvocations(mServiceConnection);
|
||||||
|
mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
|
||||||
|
verify(mockListener).onPreparedForReboot(eq(true));
|
||||||
|
verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());
|
||||||
|
|
||||||
|
// Use x -> x for both wrap & unwrap functions.
|
||||||
|
when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong()))
|
||||||
|
.thenAnswer(invocation -> invocation.getArgument(0));
|
||||||
|
assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded());
|
||||||
|
verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong());
|
||||||
|
assertTrue(mStorage.hasRebootEscrowServerBlob());
|
||||||
|
|
||||||
|
// pretend reboot happens here
|
||||||
|
when(mInjected.getBootCount()).thenReturn(1);
|
||||||
|
ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
|
||||||
|
ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class);
|
||||||
|
doNothing()
|
||||||
|
.when(mInjected)
|
||||||
|
.reportMetric(
|
||||||
|
metricsSuccessCaptor.capture(),
|
||||||
|
metricsErrorCodeCaptor.capture(),
|
||||||
|
eq(2) /* Server based */,
|
||||||
|
eq(0) /* attempt count */,
|
||||||
|
anyInt(),
|
||||||
|
eq(0) /* vbmeta status */,
|
||||||
|
anyInt());
|
||||||
|
|
||||||
|
// Network is not available within timeout.
|
||||||
|
mMockInjector.mNetworkConsumer = ConnectivityManager.NetworkCallback::onUnavailable;
|
||||||
|
mService.loadRebootEscrowDataIfAvailable(mHandler);
|
||||||
|
assertFalse(metricsSuccessCaptor.getValue());
|
||||||
|
assertEquals(
|
||||||
|
Integer.valueOf(RebootEscrowManager.ERROR_NO_NETWORK),
|
||||||
|
metricsErrorCodeCaptor.getValue());
|
||||||
|
assertNull(mMockInjector.mNetworkCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void loadRebootEscrowDataIfAvailable_waitForInternet_networkLost() throws Exception {
|
||||||
|
setServerBasedRebootEscrowProvider();
|
||||||
|
mMockInjector.setWaitForNetwork(true);
|
||||||
|
|
||||||
|
when(mInjected.getBootCount()).thenReturn(0);
|
||||||
|
RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
|
||||||
|
mService.setRebootEscrowListener(mockListener);
|
||||||
|
mService.prepareRebootEscrow();
|
||||||
|
|
||||||
|
clearInvocations(mServiceConnection);
|
||||||
|
mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
|
||||||
|
verify(mockListener).onPreparedForReboot(eq(true));
|
||||||
|
verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());
|
||||||
|
|
||||||
|
// Use x -> x for both wrap & unwrap functions.
|
||||||
|
when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong()))
|
||||||
|
.thenAnswer(invocation -> invocation.getArgument(0));
|
||||||
|
assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded());
|
||||||
|
verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong());
|
||||||
|
assertTrue(mStorage.hasRebootEscrowServerBlob());
|
||||||
|
|
||||||
|
// pretend reboot happens here
|
||||||
|
when(mInjected.getBootCount()).thenReturn(1);
|
||||||
|
ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
|
||||||
|
ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class);
|
||||||
|
doNothing()
|
||||||
|
.when(mInjected)
|
||||||
|
.reportMetric(
|
||||||
|
metricsSuccessCaptor.capture(),
|
||||||
|
metricsErrorCodeCaptor.capture(),
|
||||||
|
eq(2) /* Server based */,
|
||||||
|
eq(2) /* attempt count */,
|
||||||
|
anyInt(),
|
||||||
|
eq(0) /* vbmeta status */,
|
||||||
|
anyInt());
|
||||||
|
|
||||||
|
// Network is available, then lost.
|
||||||
|
when(mServiceConnection.unwrap(any(), anyLong())).thenThrow(new IOException());
|
||||||
|
Network mockNetwork = mock(Network.class);
|
||||||
|
mMockInjector.mNetworkConsumer =
|
||||||
|
(callback) -> {
|
||||||
|
callback.onAvailable(mockNetwork);
|
||||||
|
callback.onLost(mockNetwork);
|
||||||
|
};
|
||||||
|
mService.loadRebootEscrowDataIfAvailable(mHandler);
|
||||||
|
// Sleep 5s for the retry to complete
|
||||||
|
Thread.sleep(5 * 1000);
|
||||||
|
assertFalse(metricsSuccessCaptor.getValue());
|
||||||
|
assertEquals(
|
||||||
|
Integer.valueOf(RebootEscrowManager.ERROR_NO_NETWORK),
|
||||||
|
metricsErrorCodeCaptor.getValue());
|
||||||
|
assertNull(mMockInjector.mNetworkCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void loadRebootEscrowDataIfAvailable_waitForInternet_networkAvailableWithDelay()
|
||||||
|
throws Exception {
|
||||||
|
setServerBasedRebootEscrowProvider();
|
||||||
|
mMockInjector.setWaitForNetwork(true);
|
||||||
|
|
||||||
|
when(mInjected.getBootCount()).thenReturn(0);
|
||||||
|
RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
|
||||||
|
mService.setRebootEscrowListener(mockListener);
|
||||||
|
mService.prepareRebootEscrow();
|
||||||
|
|
||||||
|
clearInvocations(mServiceConnection);
|
||||||
|
mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
|
||||||
|
verify(mockListener).onPreparedForReboot(eq(true));
|
||||||
|
verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());
|
||||||
|
|
||||||
|
// Use x -> x for both wrap & unwrap functions.
|
||||||
|
when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong()))
|
||||||
|
.thenAnswer(invocation -> invocation.getArgument(0));
|
||||||
|
assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded());
|
||||||
|
verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong());
|
||||||
|
assertTrue(mStorage.hasRebootEscrowServerBlob());
|
||||||
|
|
||||||
|
// pretend reboot happens here
|
||||||
|
when(mInjected.getBootCount()).thenReturn(1);
|
||||||
|
ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
|
||||||
|
ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class);
|
||||||
|
doNothing()
|
||||||
|
.when(mInjected)
|
||||||
|
.reportMetric(
|
||||||
|
metricsSuccessCaptor.capture(),
|
||||||
|
metricsErrorCodeCaptor.capture(),
|
||||||
|
eq(2) /* Server based */,
|
||||||
|
eq(1) /* attempt count */,
|
||||||
|
anyInt(),
|
||||||
|
eq(0) /* vbmeta status */,
|
||||||
|
anyInt());
|
||||||
|
|
||||||
|
// load escrow data
|
||||||
|
when(mServiceConnection.unwrap(any(), anyLong()))
|
||||||
|
.thenAnswer(invocation -> invocation.getArgument(0));
|
||||||
|
// network available after 1 sec
|
||||||
|
Network mockNetwork = mock(Network.class);
|
||||||
|
mMockInjector.mNetworkConsumer =
|
||||||
|
(callback) -> {
|
||||||
|
try {
|
||||||
|
Thread.sleep(1000);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
callback.onAvailable(mockNetwork);
|
||||||
|
};
|
||||||
|
mService.loadRebootEscrowDataIfAvailable(mHandler);
|
||||||
|
verify(mServiceConnection).unwrap(any(), anyLong());
|
||||||
|
assertTrue(metricsSuccessCaptor.getValue());
|
||||||
|
verify(mKeyStoreManager).clearKeyStoreEncryptionKey();
|
||||||
|
assertNull(mMockInjector.mNetworkCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void loadRebootEscrowDataIfAvailable_waitForInternet_timeoutExhausted()
|
||||||
|
throws Exception {
|
||||||
|
setServerBasedRebootEscrowProvider();
|
||||||
|
mMockInjector.setWaitForNetwork(true);
|
||||||
|
|
||||||
|
when(mInjected.getBootCount()).thenReturn(0);
|
||||||
|
RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
|
||||||
|
mService.setRebootEscrowListener(mockListener);
|
||||||
|
mService.prepareRebootEscrow();
|
||||||
|
|
||||||
|
clearInvocations(mServiceConnection);
|
||||||
|
mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
|
||||||
|
verify(mockListener).onPreparedForReboot(eq(true));
|
||||||
|
verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());
|
||||||
|
|
||||||
|
// Use x -> x for both wrap & unwrap functions.
|
||||||
|
when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong()))
|
||||||
|
.thenAnswer(invocation -> invocation.getArgument(0));
|
||||||
|
assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded());
|
||||||
|
verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong());
|
||||||
|
assertTrue(mStorage.hasRebootEscrowServerBlob());
|
||||||
|
|
||||||
|
// pretend reboot happens here
|
||||||
|
when(mInjected.getBootCount()).thenReturn(1);
|
||||||
|
ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
|
||||||
|
ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class);
|
||||||
|
doNothing()
|
||||||
|
.when(mInjected)
|
||||||
|
.reportMetric(
|
||||||
|
metricsSuccessCaptor.capture(),
|
||||||
|
metricsErrorCodeCaptor.capture(),
|
||||||
|
eq(2) /* Server based */,
|
||||||
|
eq(1) /* attempt count */,
|
||||||
|
anyInt(),
|
||||||
|
eq(0) /* vbmeta status */,
|
||||||
|
anyInt());
|
||||||
|
|
||||||
|
// load reboot escrow data
|
||||||
|
when(mServiceConnection.unwrap(any(), anyLong())).thenThrow(IOException.class);
|
||||||
|
Network mockNetwork = mock(Network.class);
|
||||||
|
// wait past timeout
|
||||||
|
mMockInjector.mNetworkConsumer =
|
||||||
|
(callback) -> {
|
||||||
|
try {
|
||||||
|
Thread.sleep(3500);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
callback.onAvailable(mockNetwork);
|
||||||
|
};
|
||||||
|
mService.loadRebootEscrowDataIfAvailable(mHandler);
|
||||||
|
verify(mServiceConnection).unwrap(any(), anyLong());
|
||||||
|
assertFalse(metricsSuccessCaptor.getValue());
|
||||||
|
assertEquals(
|
||||||
|
Integer.valueOf(RebootEscrowManager.ERROR_TIMEOUT_EXHAUSTED),
|
||||||
|
metricsErrorCodeCaptor.getValue());
|
||||||
|
assertNull(mMockInjector.mNetworkCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void loadRebootEscrowDataIfAvailable_serverBasedWaitForNetwork_retryCountExhausted()
|
||||||
|
throws Exception {
|
||||||
|
setServerBasedRebootEscrowProvider();
|
||||||
|
mMockInjector.setWaitForNetwork(true);
|
||||||
|
|
||||||
|
when(mInjected.getBootCount()).thenReturn(0);
|
||||||
|
RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
|
||||||
|
mService.setRebootEscrowListener(mockListener);
|
||||||
|
mService.prepareRebootEscrow();
|
||||||
|
|
||||||
|
clearInvocations(mServiceConnection);
|
||||||
|
mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
|
||||||
|
verify(mockListener).onPreparedForReboot(eq(true));
|
||||||
|
verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());
|
||||||
|
|
||||||
|
// Use x -> x for both wrap & unwrap functions.
|
||||||
|
when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong()))
|
||||||
|
.thenAnswer(invocation -> invocation.getArgument(0));
|
||||||
|
assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded());
|
||||||
|
verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong());
|
||||||
|
assertTrue(mStorage.hasRebootEscrowServerBlob());
|
||||||
|
|
||||||
|
// pretend reboot happens here
|
||||||
|
when(mInjected.getBootCount()).thenReturn(1);
|
||||||
|
ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
|
||||||
|
ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class);
|
||||||
|
doNothing()
|
||||||
|
.when(mInjected)
|
||||||
|
.reportMetric(
|
||||||
|
metricsSuccessCaptor.capture(),
|
||||||
|
metricsErrorCodeCaptor.capture(),
|
||||||
|
eq(2) /* Server based */,
|
||||||
|
eq(2) /* attempt count */,
|
||||||
|
anyInt(),
|
||||||
|
eq(0) /* vbmeta status */,
|
||||||
|
anyInt());
|
||||||
|
|
||||||
|
when(mServiceConnection.unwrap(any(), anyLong())).thenThrow(new IOException());
|
||||||
|
Network mockNetwork = mock(Network.class);
|
||||||
|
mMockInjector.mNetworkConsumer =
|
||||||
|
(callback) -> {
|
||||||
|
callback.onAvailable(mockNetwork);
|
||||||
|
};
|
||||||
|
|
||||||
|
mService.loadRebootEscrowDataIfAvailable(mHandler);
|
||||||
|
// Sleep 5s for the retry to complete
|
||||||
|
Thread.sleep(5 * 1000);
|
||||||
|
verify(mServiceConnection, times(2)).unwrap(any(), anyLong());
|
||||||
|
assertFalse(metricsSuccessCaptor.getValue());
|
||||||
|
assertEquals(
|
||||||
|
Integer.valueOf(RebootEscrowManager.ERROR_RETRY_COUNT_EXHAUSTED),
|
||||||
|
metricsErrorCodeCaptor.getValue());
|
||||||
|
assertNull(mMockInjector.mNetworkCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void loadRebootEscrowDataIfAvailable_ServerBasedWaitForInternet_RetrySuccess()
|
||||||
|
throws Exception {
|
||||||
|
setServerBasedRebootEscrowProvider();
|
||||||
|
mMockInjector.setWaitForNetwork(true);
|
||||||
|
|
||||||
|
when(mInjected.getBootCount()).thenReturn(0);
|
||||||
|
RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
|
||||||
|
mService.setRebootEscrowListener(mockListener);
|
||||||
|
mService.prepareRebootEscrow();
|
||||||
|
|
||||||
|
clearInvocations(mServiceConnection);
|
||||||
|
mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
|
||||||
|
verify(mockListener).onPreparedForReboot(eq(true));
|
||||||
|
verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());
|
||||||
|
|
||||||
|
// Use x -> x for both wrap & unwrap functions.
|
||||||
|
when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong()))
|
||||||
|
.thenAnswer(invocation -> invocation.getArgument(0));
|
||||||
|
assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded());
|
||||||
|
verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong());
|
||||||
|
assertTrue(mStorage.hasRebootEscrowServerBlob());
|
||||||
|
|
||||||
|
// pretend reboot happens here
|
||||||
|
when(mInjected.getBootCount()).thenReturn(1);
|
||||||
|
ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
|
||||||
|
doNothing()
|
||||||
|
.when(mInjected)
|
||||||
|
.reportMetric(
|
||||||
|
metricsSuccessCaptor.capture(),
|
||||||
|
anyInt(),
|
||||||
|
anyInt(),
|
||||||
|
eq(2) /* attempt count */,
|
||||||
|
anyInt(),
|
||||||
|
anyInt(),
|
||||||
|
anyInt());
|
||||||
|
|
||||||
|
when(mServiceConnection.unwrap(any(), anyLong()))
|
||||||
|
.thenThrow(new IOException())
|
||||||
|
.thenAnswer(invocation -> invocation.getArgument(0));
|
||||||
|
Network mockNetwork = mock(Network.class);
|
||||||
|
mMockInjector.mNetworkConsumer =
|
||||||
|
(callback) -> {
|
||||||
|
callback.onAvailable(mockNetwork);
|
||||||
|
};
|
||||||
|
|
||||||
|
mService.loadRebootEscrowDataIfAvailable(mHandler);
|
||||||
|
// Sleep 5s for the retry to complete
|
||||||
|
Thread.sleep(5 * 1000);
|
||||||
|
verify(mServiceConnection, times(2)).unwrap(any(), anyLong());
|
||||||
|
assertTrue(metricsSuccessCaptor.getValue());
|
||||||
|
verify(mKeyStoreManager).clearKeyStoreEncryptionKey();
|
||||||
|
assertNull(mMockInjector.mNetworkCallback);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void loadRebootEscrowDataIfAvailable_TooManyBootsInBetween_NoMetrics() throws Exception {
|
public void loadRebootEscrowDataIfAvailable_TooManyBootsInBetween_NoMetrics() throws Exception {
|
||||||
when(mInjected.getBootCount()).thenReturn(0);
|
when(mInjected.getBootCount()).thenReturn(0);
|
||||||
@@ -687,7 +1195,7 @@ public class RebootEscrowManagerTests {
|
|||||||
when(mInjected.getBootCount()).thenReturn(10);
|
when(mInjected.getBootCount()).thenReturn(10);
|
||||||
when(mRebootEscrow.retrieveKey()).thenReturn(new byte[32]);
|
when(mRebootEscrow.retrieveKey()).thenReturn(new byte[32]);
|
||||||
|
|
||||||
mService.loadRebootEscrowDataIfAvailable(null);
|
mService.loadRebootEscrowDataIfAvailable(mHandler);
|
||||||
verify(mRebootEscrow).retrieveKey();
|
verify(mRebootEscrow).retrieveKey();
|
||||||
verify(mInjected, never()).reportMetric(anyBoolean(), anyInt(), anyInt(), anyInt(),
|
verify(mInjected, never()).reportMetric(anyBoolean(), anyInt(), anyInt(), anyInt(),
|
||||||
anyInt(), anyInt(), anyInt());
|
anyInt(), anyInt(), anyInt());
|
||||||
@@ -715,7 +1223,7 @@ public class RebootEscrowManagerTests {
|
|||||||
when(mInjected.getBootCount()).thenReturn(10);
|
when(mInjected.getBootCount()).thenReturn(10);
|
||||||
when(mRebootEscrow.retrieveKey()).thenReturn(new byte[32]);
|
when(mRebootEscrow.retrieveKey()).thenReturn(new byte[32]);
|
||||||
|
|
||||||
mService.loadRebootEscrowDataIfAvailable(null);
|
mService.loadRebootEscrowDataIfAvailable(mHandler);
|
||||||
verify(mInjected, never()).reportMetric(anyBoolean(), anyInt(), anyInt(), anyInt(),
|
verify(mInjected, never()).reportMetric(anyBoolean(), anyInt(), anyInt(), anyInt(),
|
||||||
anyInt(), anyInt(), anyInt());
|
anyInt(), anyInt(), anyInt());
|
||||||
}
|
}
|
||||||
@@ -753,7 +1261,7 @@ public class RebootEscrowManagerTests {
|
|||||||
// Trigger a vbmeta digest mismatch
|
// Trigger a vbmeta digest mismatch
|
||||||
mStorage.setString(RebootEscrowManager.REBOOT_ESCROW_KEY_VBMETA_DIGEST,
|
mStorage.setString(RebootEscrowManager.REBOOT_ESCROW_KEY_VBMETA_DIGEST,
|
||||||
"non sense value", USER_SYSTEM);
|
"non sense value", USER_SYSTEM);
|
||||||
mService.loadRebootEscrowDataIfAvailable(null);
|
mService.loadRebootEscrowDataIfAvailable(mHandler);
|
||||||
verify(mInjected).reportMetric(eq(true), eq(0) /* error code */, eq(1) /* HAL based */,
|
verify(mInjected).reportMetric(eq(true), eq(0) /* error code */, eq(1) /* HAL based */,
|
||||||
eq(1) /* attempt count */, anyInt(), eq(2) /* vbmeta status */, anyInt());
|
eq(1) /* attempt count */, anyInt(), eq(2) /* vbmeta status */, anyInt());
|
||||||
assertEquals(mStorage.getString(RebootEscrowManager.REBOOT_ESCROW_KEY_VBMETA_DIGEST,
|
assertEquals(mStorage.getString(RebootEscrowManager.REBOOT_ESCROW_KEY_VBMETA_DIGEST,
|
||||||
@@ -790,7 +1298,7 @@ public class RebootEscrowManagerTests {
|
|||||||
eq(1) /* attempt count */, anyInt(), anyInt(), anyInt());
|
eq(1) /* attempt count */, anyInt(), anyInt(), anyInt());
|
||||||
|
|
||||||
when(mRebootEscrow.retrieveKey()).thenAnswer(invocation -> null);
|
when(mRebootEscrow.retrieveKey()).thenAnswer(invocation -> null);
|
||||||
mService.loadRebootEscrowDataIfAvailable(null);
|
mService.loadRebootEscrowDataIfAvailable(mHandler);
|
||||||
verify(mRebootEscrow).retrieveKey();
|
verify(mRebootEscrow).retrieveKey();
|
||||||
assertFalse(metricsSuccessCaptor.getValue());
|
assertFalse(metricsSuccessCaptor.getValue());
|
||||||
assertEquals(Integer.valueOf(RebootEscrowManager.ERROR_LOAD_ESCROW_KEY),
|
assertEquals(Integer.valueOf(RebootEscrowManager.ERROR_LOAD_ESCROW_KEY),
|
||||||
|
|||||||
Reference in New Issue
Block a user