Merge "Delay loadEscrowData from locksettings" am: 60edeec07e
Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1566555 MUST ONLY BE SUBMITTED BY AUTOMERGER Change-Id: I0ef3ce66e9771bc8864c84c91330c2947a74c673
This commit is contained in:
@@ -280,6 +280,7 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
super.onBootPhase(phase);
|
||||
if (phase == PHASE_ACTIVITY_MANAGER_READY) {
|
||||
mLockSettingsService.migrateOldDataAfterSystemReady();
|
||||
mLockSettingsService.loadEscrowData();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -832,11 +833,15 @@ public class LockSettingsService extends ILockSettings.Stub {
|
||||
mSpManager.initWeaverService();
|
||||
getAuthSecretHal();
|
||||
mDeviceProvisionedObserver.onSystemReady();
|
||||
mRebootEscrowManager.loadRebootEscrowDataIfAvailable();
|
||||
|
||||
// TODO: maybe skip this for split system user mode.
|
||||
mStorage.prefetchUser(UserHandle.USER_SYSTEM);
|
||||
}
|
||||
|
||||
private void loadEscrowData() {
|
||||
mRebootEscrowManager.loadRebootEscrowDataIfAvailable(mHandler);
|
||||
}
|
||||
|
||||
private void getAuthSecretHal() {
|
||||
try {
|
||||
mAuthSecretService = IAuthSecret.getService(/* retry */ true);
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.annotation.NonNull;
|
||||
import android.annotation.UserIdInt;
|
||||
import android.content.Context;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.os.Handler;
|
||||
import android.os.SystemClock;
|
||||
import android.os.UserManager;
|
||||
import android.provider.DeviceConfig;
|
||||
@@ -39,6 +40,7 @@ import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
|
||||
@@ -75,6 +77,13 @@ class RebootEscrowManager {
|
||||
*/
|
||||
private static final int BOOT_COUNT_TOLERANCE = 5;
|
||||
|
||||
/**
|
||||
* The default retry specs for loading reboot escrow data. We will attempt to retry loading
|
||||
* escrow data on temporarily errors, e.g. unavailable network.
|
||||
*/
|
||||
private static final int DEFAULT_LOAD_ESCROW_DATA_RETRY_COUNT = 3;
|
||||
private static final int DEFAULT_LOAD_ESCROW_DATA_RETRY_INTERVAL_SECONDS = 30;
|
||||
|
||||
/**
|
||||
* Logs events for later debugging in bugreports.
|
||||
*/
|
||||
@@ -148,6 +157,14 @@ class RebootEscrowManager {
|
||||
return null;
|
||||
}
|
||||
|
||||
void post(Handler handler, Runnable runnable) {
|
||||
handler.post(runnable);
|
||||
}
|
||||
|
||||
void postDelayed(Handler handler, Runnable runnable, long delayMillis) {
|
||||
handler.postDelayed(runnable, delayMillis);
|
||||
}
|
||||
|
||||
public Context getContext() {
|
||||
return mContext;
|
||||
}
|
||||
@@ -199,7 +216,18 @@ class RebootEscrowManager {
|
||||
mKeyStoreManager = injector.getKeyStoreManager();
|
||||
}
|
||||
|
||||
void loadRebootEscrowDataIfAvailable() {
|
||||
private void onGetRebootEscrowKeyFailed(List<UserInfo> users) {
|
||||
Slog.w(TAG, "Had reboot escrow data for users, but no key; removing escrow storage.");
|
||||
for (UserInfo user : users) {
|
||||
mStorage.removeRebootEscrow(user.id);
|
||||
}
|
||||
|
||||
// Clear the old key in keystore.
|
||||
mKeyStoreManager.clearKeyStoreEncryptionKey();
|
||||
onEscrowRestoreComplete(false);
|
||||
}
|
||||
|
||||
void loadRebootEscrowDataIfAvailable(Handler retryHandler) {
|
||||
List<UserInfo> users = mUserManager.getUsers();
|
||||
List<UserInfo> rebootEscrowUsers = new ArrayList<>();
|
||||
for (UserInfo user : users) {
|
||||
@@ -212,17 +240,49 @@ class RebootEscrowManager {
|
||||
return;
|
||||
}
|
||||
|
||||
mInjector.post(retryHandler, () -> loadRebootEscrowDataWithRetry(
|
||||
retryHandler, 0, users, rebootEscrowUsers));
|
||||
}
|
||||
|
||||
void scheduleLoadRebootEscrowDataOrFail(Handler retryHandler, int attemptNumber,
|
||||
List<UserInfo> users, List<UserInfo> rebootEscrowUsers) {
|
||||
Objects.requireNonNull(retryHandler);
|
||||
|
||||
final int retryLimit = DeviceConfig.getInt(DeviceConfig.NAMESPACE_OTA,
|
||||
"load_escrow_data_retry_count", DEFAULT_LOAD_ESCROW_DATA_RETRY_COUNT);
|
||||
final int retryIntervalInSeconds = DeviceConfig.getInt(DeviceConfig.NAMESPACE_OTA,
|
||||
"load_escrow_data_retry_interval_seconds",
|
||||
DEFAULT_LOAD_ESCROW_DATA_RETRY_INTERVAL_SECONDS);
|
||||
|
||||
if (attemptNumber < retryLimit) {
|
||||
Slog.i(TAG, "Scheduling loadRebootEscrowData retry number: " + attemptNumber);
|
||||
mInjector.postDelayed(retryHandler, () -> loadRebootEscrowDataWithRetry(
|
||||
retryHandler, attemptNumber, users, rebootEscrowUsers),
|
||||
retryIntervalInSeconds * 1000);
|
||||
return;
|
||||
}
|
||||
|
||||
Slog.w(TAG, "Failed to load reboot escrow data after " + attemptNumber + " attempts");
|
||||
onGetRebootEscrowKeyFailed(users);
|
||||
}
|
||||
|
||||
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
|
||||
// generated before reboot. Note that we will clear the escrow key even if the keystore key
|
||||
// is null.
|
||||
SecretKey kk = mKeyStoreManager.getKeyStoreEncryptionKey();
|
||||
RebootEscrowKey escrowKey = getAndClearRebootEscrowKey(kk);
|
||||
RebootEscrowKey escrowKey;
|
||||
try {
|
||||
escrowKey = getAndClearRebootEscrowKey(kk);
|
||||
} catch (IOException e) {
|
||||
scheduleLoadRebootEscrowDataOrFail(retryHandler, attemptNumber + 1, users,
|
||||
rebootEscrowUsers);
|
||||
return;
|
||||
}
|
||||
|
||||
if (kk == null || escrowKey == null) {
|
||||
Slog.w(TAG, "Had reboot escrow data for users, but no key; removing escrow storage.");
|
||||
for (UserInfo user : users) {
|
||||
mStorage.removeRebootEscrow(user.id);
|
||||
}
|
||||
onEscrowRestoreComplete(false);
|
||||
onGetRebootEscrowKeyFailed(users);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -249,7 +309,7 @@ class RebootEscrowManager {
|
||||
}
|
||||
}
|
||||
|
||||
private RebootEscrowKey getAndClearRebootEscrowKey(SecretKey kk) {
|
||||
private RebootEscrowKey getAndClearRebootEscrowKey(SecretKey kk) throws IOException {
|
||||
RebootEscrowProviderInterface rebootEscrowProvider = mInjector.getRebootEscrowProvider();
|
||||
if (rebootEscrowProvider == null) {
|
||||
Slog.w(TAG,
|
||||
|
||||
@@ -33,7 +33,7 @@ import javax.crypto.SecretKey;
|
||||
* An implementation of the {@link RebootEscrowProviderInterface} by calling the RebootEscrow HAL.
|
||||
*/
|
||||
class RebootEscrowProviderHalImpl implements RebootEscrowProviderInterface {
|
||||
private static final String TAG = "RebootEscrowProvider";
|
||||
private static final String TAG = "RebootEscrowProviderHal";
|
||||
|
||||
private final Injector mInjector;
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.server.locksettings;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
|
||||
/**
|
||||
@@ -33,9 +35,10 @@ public interface RebootEscrowProviderInterface {
|
||||
|
||||
/**
|
||||
* Returns the stored RebootEscrowKey, and clears the storage. If the stored key is encrypted,
|
||||
* use the input key to decrypt the RebootEscrowKey. Returns null on failure.
|
||||
* use the input key to decrypt the RebootEscrowKey. Returns null on failure. Throws an
|
||||
* IOException if the failure is non-fatal, and a retry may succeed.
|
||||
*/
|
||||
RebootEscrowKey getAndClearRebootEscrowKey(SecretKey decryptionKey);
|
||||
RebootEscrowKey getAndClearRebootEscrowKey(SecretKey decryptionKey) throws IOException;
|
||||
|
||||
/**
|
||||
* Clears the stored RebootEscrowKey.
|
||||
|
||||
@@ -35,7 +35,7 @@ import javax.crypto.SecretKey;
|
||||
* encrypt & decrypt the blob.
|
||||
*/
|
||||
class RebootEscrowProviderServerBasedImpl implements RebootEscrowProviderInterface {
|
||||
private static final String TAG = "RebootEscrowProvider";
|
||||
private static final String TAG = "RebootEscrowProviderServerBased";
|
||||
|
||||
// Timeout for service binding
|
||||
private static final long DEFAULT_SERVICE_TIMEOUT_IN_SECONDS = 10;
|
||||
@@ -50,6 +50,8 @@ class RebootEscrowProviderServerBasedImpl implements RebootEscrowProviderInterfa
|
||||
|
||||
private final Injector mInjector;
|
||||
|
||||
private byte[] mServerBlob;
|
||||
|
||||
static class Injector {
|
||||
private ResumeOnRebootServiceConnection mServiceConnection = null;
|
||||
|
||||
@@ -124,17 +126,20 @@ class RebootEscrowProviderServerBasedImpl implements RebootEscrowProviderInterfa
|
||||
}
|
||||
|
||||
@Override
|
||||
public RebootEscrowKey getAndClearRebootEscrowKey(SecretKey decryptionKey) {
|
||||
byte[] serverBlob = mStorage.readRebootEscrowServerBlob();
|
||||
public RebootEscrowKey getAndClearRebootEscrowKey(SecretKey decryptionKey) throws IOException {
|
||||
if (mServerBlob == null) {
|
||||
mServerBlob = mStorage.readRebootEscrowServerBlob();
|
||||
}
|
||||
// Delete the server blob in storage.
|
||||
mStorage.removeRebootEscrowServerBlob();
|
||||
if (serverBlob == null) {
|
||||
if (mServerBlob == null) {
|
||||
Slog.w(TAG, "Failed to read reboot escrow server blob from storage");
|
||||
return null;
|
||||
}
|
||||
|
||||
Slog.i(TAG, "Loaded reboot escrow server blob from storage");
|
||||
try {
|
||||
byte[] escrowKeyBytes = unwrapServerBlob(serverBlob, decryptionKey);
|
||||
byte[] escrowKeyBytes = unwrapServerBlob(mServerBlob, decryptionKey);
|
||||
if (escrowKeyBytes == null) {
|
||||
Slog.w(TAG, "Decrypted reboot escrow key bytes should not be null");
|
||||
return null;
|
||||
@@ -145,7 +150,7 @@ class RebootEscrowProviderServerBasedImpl implements RebootEscrowProviderInterfa
|
||||
}
|
||||
|
||||
return RebootEscrowKey.fromKeyBytes(escrowKeyBytes);
|
||||
} catch (TimeoutException | RemoteException | IOException e) {
|
||||
} catch (TimeoutException | RemoteException e) {
|
||||
Slog.w(TAG, "Failed to decrypt the server blob ", e);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.hardware.rebootescrow.IRebootEscrow;
|
||||
import android.os.Handler;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceSpecificException;
|
||||
import android.os.UserManager;
|
||||
@@ -154,6 +155,11 @@ public class RebootEscrowManagerTests {
|
||||
mInjected = injected;
|
||||
}
|
||||
|
||||
@Override
|
||||
void post(Handler handler, Runnable runnable) {
|
||||
runnable.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserManager getUserManager() {
|
||||
return mUserManager;
|
||||
@@ -369,7 +375,7 @@ public class RebootEscrowManagerTests {
|
||||
|
||||
@Test
|
||||
public void loadRebootEscrowDataIfAvailable_NothingAvailable_Success() throws Exception {
|
||||
mService.loadRebootEscrowDataIfAvailable();
|
||||
mService.loadRebootEscrowDataIfAvailable(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -401,7 +407,7 @@ public class RebootEscrowManagerTests {
|
||||
doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture());
|
||||
when(mRebootEscrow.retrieveKey()).thenAnswer(invocation -> keyByteCaptor.getValue());
|
||||
|
||||
mService.loadRebootEscrowDataIfAvailable();
|
||||
mService.loadRebootEscrowDataIfAvailable(null);
|
||||
verify(mRebootEscrow).retrieveKey();
|
||||
assertTrue(metricsSuccessCaptor.getValue());
|
||||
verify(mKeyStoreManager).clearKeyStoreEncryptionKey();
|
||||
@@ -435,7 +441,7 @@ public class RebootEscrowManagerTests {
|
||||
|
||||
when(mServiceConnection.unwrap(any(), anyLong()))
|
||||
.thenAnswer(invocation -> invocation.getArgument(0));
|
||||
mService.loadRebootEscrowDataIfAvailable();
|
||||
mService.loadRebootEscrowDataIfAvailable(null);
|
||||
verify(mServiceConnection).unwrap(any(), anyLong());
|
||||
assertTrue(metricsSuccessCaptor.getValue());
|
||||
verify(mKeyStoreManager).clearKeyStoreEncryptionKey();
|
||||
@@ -466,7 +472,7 @@ public class RebootEscrowManagerTests {
|
||||
when(mInjected.getBootCount()).thenReturn(10);
|
||||
when(mRebootEscrow.retrieveKey()).thenReturn(new byte[32]);
|
||||
|
||||
mService.loadRebootEscrowDataIfAvailable();
|
||||
mService.loadRebootEscrowDataIfAvailable(null);
|
||||
verify(mRebootEscrow).retrieveKey();
|
||||
verify(mInjected, never()).reportMetric(anyBoolean());
|
||||
}
|
||||
@@ -493,7 +499,7 @@ public class RebootEscrowManagerTests {
|
||||
when(mInjected.getBootCount()).thenReturn(10);
|
||||
when(mRebootEscrow.retrieveKey()).thenReturn(new byte[32]);
|
||||
|
||||
mService.loadRebootEscrowDataIfAvailable();
|
||||
mService.loadRebootEscrowDataIfAvailable(null);
|
||||
verify(mInjected, never()).reportMetric(anyBoolean());
|
||||
}
|
||||
|
||||
@@ -527,7 +533,7 @@ public class RebootEscrowManagerTests {
|
||||
when(mInjected.getBootCount()).thenReturn(10);
|
||||
when(mRebootEscrow.retrieveKey()).thenAnswer(invocation -> keyByteCaptor.getValue());
|
||||
|
||||
mService.loadRebootEscrowDataIfAvailable();
|
||||
mService.loadRebootEscrowDataIfAvailable(null);
|
||||
verify(mInjected).reportMetric(eq(true));
|
||||
}
|
||||
|
||||
@@ -557,7 +563,7 @@ public class RebootEscrowManagerTests {
|
||||
ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
|
||||
doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture());
|
||||
when(mRebootEscrow.retrieveKey()).thenAnswer(invocation -> new byte[32]);
|
||||
mService.loadRebootEscrowDataIfAvailable();
|
||||
mService.loadRebootEscrowDataIfAvailable(null);
|
||||
verify(mRebootEscrow).retrieveKey();
|
||||
assertFalse(metricsSuccessCaptor.getValue());
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.os.RemoteException;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
@@ -42,7 +43,6 @@ import org.junit.runner.RunWith;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
@@ -130,7 +130,7 @@ public class RebootEscrowProviderServerBasedImplTests {
|
||||
@Test
|
||||
public void getAndClearRebootEscrowKey_ServiceConnectionException_failure() throws Exception {
|
||||
when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())).thenAnswer(mFakeEncryption);
|
||||
doThrow(IOException.class).when(mServiceConnection).unwrap(any(), anyLong());
|
||||
doThrow(RemoteException.class).when(mServiceConnection).unwrap(any(), anyLong());
|
||||
|
||||
assertTrue(mRebootEscrowProvider.hasRebootEscrowSupport());
|
||||
mRebootEscrowProvider.storeRebootEscrowKey(mRebootEscrowKey, mKeyStoreEncryptionKey);
|
||||
|
||||
Reference in New Issue
Block a user