Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1c98980d authored by Tianjie's avatar Tianjie Committed by Tianjie Xu
Browse files

Delay loadEscrowData from locksettings

For server-based ror, we want to deplay loadEscrowData until
1. Binding to Gmscore works
2. Network is available

So we factor out the loadEscrowData to be called later in the
start sequence of system server. Also make the function async
so that we can retry on temporarily failures.

Bug: 172780686
Test: run server based ror

Change-Id: If71f8974f94eb1be2ab896e95601377196e01790
parent 3faed136
Loading
Loading
Loading
Loading
+6 −1
Original line number Diff line number Diff line
@@ -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);
+68 −8
Original line number Diff line number Diff line
@@ -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);
        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);
        RebootEscrowKey escrowKey;
        try {
            escrowKey = getAndClearRebootEscrowKey(kk);
        } catch (IOException e) {
            scheduleLoadRebootEscrowDataOrFail(retryHandler, attemptNumber + 1, users,
                    rebootEscrowUsers);
            return;
        }
            onEscrowRestoreComplete(false);

        if (kk == null || escrowKey == null) {
            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,
+1 −1
Original line number Diff line number Diff line
@@ -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;

+5 −2
Original line number Diff line number Diff line
@@ -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.
+11 −6
Original line number Diff line number Diff line
@@ -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;
        }
Loading