diff options
| author | 2021-02-11 22:35:37 +0000 | |
|---|---|---|
| committer | 2021-02-11 22:35:37 +0000 | |
| commit | cd9d960d236b5008aad4c20a9946190eb89fa4fb (patch) | |
| tree | e44d7179d51ed773e1f6a4eb1bba92c08b9d3c0f | |
| parent | 0072df0d0391e6a998053ebc848c730bc6670ab4 (diff) | |
| parent | 60edeec07e2624d5b6b0747a322b953e3814e2c4 (diff) | |
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
7 files changed, 106 insertions, 27 deletions
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index 2044f40eb978..a589fedaec72 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -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); diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java index 06962d414009..53b62ca6ecb5 100644 --- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java +++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java @@ -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; @@ -76,6 +78,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. */ private final RebootEscrowEventLog mEventLog; @@ -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, diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowProviderHalImpl.java b/services/core/java/com/android/server/locksettings/RebootEscrowProviderHalImpl.java index 6c1040b596c8..4b00772088f2 100644 --- a/services/core/java/com/android/server/locksettings/RebootEscrowProviderHalImpl.java +++ b/services/core/java/com/android/server/locksettings/RebootEscrowProviderHalImpl.java @@ -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; diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowProviderInterface.java b/services/core/java/com/android/server/locksettings/RebootEscrowProviderInterface.java index 857ad5fc312a..af6faad3c76e 100644 --- a/services/core/java/com/android/server/locksettings/RebootEscrowProviderInterface.java +++ b/services/core/java/com/android/server/locksettings/RebootEscrowProviderInterface.java @@ -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. diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java b/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java index ba1a680ba7fb..9d09637cdc74 100644 --- a/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java +++ b/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java @@ -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; } diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java index a4ba4c86a8fd..a896f1b0d60f 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java @@ -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; @@ -155,6 +156,11 @@ public class RebootEscrowManagerTests { } @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()); } diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowProviderServerBasedImplTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowProviderServerBasedImplTests.java index bc1e025dd99f..28b737b412d2 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowProviderServerBasedImplTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowProviderServerBasedImplTests.java @@ -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); |