Merge "Delay loadEscrowData from locksettings" am: 60edeec07e am: cd9d960d23

Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1566555

MUST ONLY BE SUBMITTED BY AUTOMERGER

Change-Id: I6ea608a47dc07a2cd96b5c6d1e9dfd4c42f366df
This commit is contained in:
Tianjie Xu
2021-02-11 22:57:36 +00:00
committed by Automerger Merge Worker
7 changed files with 106 additions and 27 deletions

View File

@@ -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);

View File

@@ -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,

View File

@@ -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;

View File

@@ -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.

View File

@@ -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;
}

View File

@@ -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());
}

View File

@@ -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);