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

Commit 246c14d6 authored by Tianjie Xu's avatar Tianjie Xu Committed by Gerrit Code Review
Browse files

Merge "Improve the error handling for armRebootEscrow"

parents 8e90abbe 6066898f
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
@@ -169,8 +169,9 @@ public class RecoverySystem {
    public @interface ResumeOnRebootRebootErrorCode {}

    /**
     * The preparation of resume on reboot succeeds. Don't expose it because a successful reboot
     * should just reboot the device.
     * The preparation of resume on reboot succeeds.
     *
     * <p> Don't expose it because a successful reboot should just reboot the device.
     *  @hide
     */
    public static final int RESUME_ON_REBOOT_REBOOT_ERROR_NONE = 0;
+28 −2
Original line number Diff line number Diff line
@@ -16,15 +16,41 @@

package com.android.internal.widget;

import android.annotation.IntDef;
import android.annotation.Nullable;
import android.app.admin.PasswordMetrics;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
 * LockSettingsService local system service interface.
 *
 * @hide Only for use within the system server.
 */
public abstract class LockSettingsInternal {
    /** ErrorCode for armRebootEscrow failures. **/
    @IntDef(prefix = {"ARM_REBOOT_ERROR_"}, value = {
            ARM_REBOOT_ERROR_NONE,
            ARM_REBOOT_ERROR_UNSPECIFIED,
            ARM_REBOOT_ERROR_ESCROW_NOT_READY,
            ARM_REBOOT_ERROR_NO_PROVIDER,
            ARM_REBOOT_ERROR_PROVIDER_MISMATCH,
            ARM_REBOOT_ERROR_NO_ESCROW_KEY,
            ARM_REBOOT_ERROR_KEYSTORE_FAILURE,
            ARM_REBOOT_ERROR_STORE_ESCROW_KEY,
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface ArmRebootEscrowErrorCode {}

    public static final int ARM_REBOOT_ERROR_NONE = 0;
    public static final int ARM_REBOOT_ERROR_UNSPECIFIED = 1;
    public static final int ARM_REBOOT_ERROR_ESCROW_NOT_READY = 2;
    public static final int ARM_REBOOT_ERROR_NO_PROVIDER = 3;
    public static final int ARM_REBOOT_ERROR_PROVIDER_MISMATCH = 4;
    public static final int ARM_REBOOT_ERROR_NO_ESCROW_KEY = 5;
    public static final int ARM_REBOOT_ERROR_KEYSTORE_FAILURE = 6;
    public static final int ARM_REBOOT_ERROR_STORE_ESCROW_KEY = 7;
    // TODO(b/183140900) split store escrow key errors into detailed ones.

    /**
     * Create an escrow token for the current user, which can later be used to unlock FBE
@@ -104,9 +130,9 @@ public abstract class LockSettingsInternal {
     * Should be called immediately before rebooting for an update. This depends on {@link
     * #prepareRebootEscrow()} having been called and the escrow completing.
     *
     * @return true if the arming worked
     * @return ARM_ERROR_NONE if the arming worked
     */
    public abstract boolean armRebootEscrow();
    public abstract @ArmRebootEscrowErrorCode int armRebootEscrow();


    /**
+1 −1
Original line number Diff line number Diff line
@@ -3488,7 +3488,7 @@ public class LockSettingsService extends ILockSettings.Stub {
        }

        @Override
        public boolean armRebootEscrow() {
        public @ArmRebootEscrowErrorCode int armRebootEscrow() {
            return mRebootEscrowManager.armRebootEscrowIfNeeded();
        }

+50 −29
Original line number Diff line number Diff line
@@ -18,6 +18,15 @@ package com.android.server.locksettings;

import static android.os.UserHandle.USER_SYSTEM;

import static com.android.internal.widget.LockSettingsInternal.ARM_REBOOT_ERROR_ESCROW_NOT_READY;
import static com.android.internal.widget.LockSettingsInternal.ARM_REBOOT_ERROR_KEYSTORE_FAILURE;
import static com.android.internal.widget.LockSettingsInternal.ARM_REBOOT_ERROR_NONE;
import static com.android.internal.widget.LockSettingsInternal.ARM_REBOOT_ERROR_NO_ESCROW_KEY;
import static com.android.internal.widget.LockSettingsInternal.ARM_REBOOT_ERROR_NO_PROVIDER;
import static com.android.internal.widget.LockSettingsInternal.ARM_REBOOT_ERROR_PROVIDER_MISMATCH;
import static com.android.internal.widget.LockSettingsInternal.ARM_REBOOT_ERROR_STORE_ESCROW_KEY;
import static com.android.internal.widget.LockSettingsInternal.ArmRebootEscrowErrorCode;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
@@ -577,16 +586,14 @@ class RebootEscrowManager {
        mRebootEscrowWanted = false;
        setRebootEscrowReady(false);


        RebootEscrowProviderInterface rebootEscrowProvider = mInjector.getRebootEscrowProvider();
        if (rebootEscrowProvider == null) {
            Slog.w(TAG,
                    "Had reboot escrow data for users, but RebootEscrowProvider is unavailable");
            return;
            Slog.w(TAG, "RebootEscrowProvider is unavailable for clear request");
        } else {
            rebootEscrowProvider.clearRebootEscrowKey();
        }

        clearMetricsStorage();
        rebootEscrowProvider.clearRebootEscrowKey();

        List<UserInfo> users = mUserManager.getUsers();
        for (UserInfo user : users) {
@@ -596,20 +603,30 @@ class RebootEscrowManager {
        mEventLog.addEntry(RebootEscrowEvent.CLEARED_LSKF_REQUEST);
    }

    boolean armRebootEscrowIfNeeded() {
    @ArmRebootEscrowErrorCode int armRebootEscrowIfNeeded() {
        if (!mRebootEscrowReady) {
            return false;
            return ARM_REBOOT_ERROR_ESCROW_NOT_READY;
        }

        RebootEscrowProviderInterface rebootEscrowProvider = mInjector.getRebootEscrowProvider();
        if (rebootEscrowProvider == null) {
            Slog.w(TAG,
                    "Had reboot escrow data for users, but RebootEscrowProvider is unavailable");
            return false;
            clearRebootEscrowIfNeeded();
            return ARM_REBOOT_ERROR_NO_PROVIDER;
        }

        int expectedProviderType = mInjector.serverBasedResumeOnReboot()
                ? RebootEscrowProviderInterface.TYPE_SERVER_BASED
                : RebootEscrowProviderInterface.TYPE_HAL;
        int actualProviderType = rebootEscrowProvider.getType();
        // TODO(b/183140900) Fail the reboot if provider type mismatches.
        if (expectedProviderType != actualProviderType) {
            Slog.w(TAG, "Expect reboot escrow provider " + expectedProviderType
                    + ", but the RoR is prepared with " + actualProviderType
                    + ". Please prepare the RoR again.");
            clearRebootEscrowIfNeeded();
            return ARM_REBOOT_ERROR_PROVIDER_MISMATCH;
        }

        RebootEscrowKey escrowKey;
        synchronized (mKeyGenerationLock) {
@@ -618,17 +635,26 @@ class RebootEscrowManager {

        if (escrowKey == null) {
            Slog.e(TAG, "Escrow key is null, but escrow was marked as ready");
            return false;
            clearRebootEscrowIfNeeded();
            return ARM_REBOOT_ERROR_NO_ESCROW_KEY;
        }

        // We will use the same key from keystore to encrypt the escrow key and escrow data blob.
        SecretKey kk = mKeyStoreManager.getKeyStoreEncryptionKey();
        if (kk == null) {
            Slog.e(TAG, "Failed to get encryption key from keystore.");
            return false;
            clearRebootEscrowIfNeeded();
            return ARM_REBOOT_ERROR_KEYSTORE_FAILURE;
        }

        // TODO(b/183140900) design detailed errors for store escrow key errors.
        // We don't clear rebootEscrow here, because some errors may be recoverable, e.g. network
        // unavailable for server based provider.
        boolean armedRebootEscrow = rebootEscrowProvider.storeRebootEscrowKey(escrowKey, kk);
        if (armedRebootEscrow) {
        if (!armedRebootEscrow) {
            return ARM_REBOOT_ERROR_STORE_ESCROW_KEY;
        }

        mStorage.setInt(REBOOT_ESCROW_ARMED_KEY, mInjector.getBootCount(), USER_SYSTEM);
        mStorage.setLong(REBOOT_ESCROW_KEY_ARMED_TIMESTAMP, mInjector.getCurrentTimeMillis(),
                USER_SYSTEM);
@@ -639,9 +665,8 @@ class RebootEscrowManager {
                mInjector.getVbmetaDigest(true), USER_SYSTEM);
        mStorage.setInt(REBOOT_ESCROW_KEY_PROVIDER, actualProviderType, USER_SYSTEM);
        mEventLog.addEntry(RebootEscrowEvent.SET_ARMED_STATUS);
        }

        return armedRebootEscrow;
        return ARM_REBOOT_ERROR_NONE;
    }

    private void setRebootEscrowReady(boolean ready) {
@@ -663,10 +688,6 @@ class RebootEscrowManager {
    }

    boolean clearRebootEscrow() {
        if (mInjector.getRebootEscrowProvider() == null) {
            return false;
        }

        clearRebootEscrowIfNeeded();
        return true;
    }
+75 −17
Original line number Diff line number Diff line
@@ -25,6 +25,8 @@ import static android.os.RecoverySystem.RESUME_ON_REBOOT_REBOOT_ERROR_UNSPECIFIE
import static android.os.RecoverySystem.ResumeOnRebootRebootErrorCode;
import static android.os.UserHandle.USER_SYSTEM;

import static com.android.internal.widget.LockSettingsInternal.ARM_REBOOT_ERROR_NONE;

import android.annotation.IntDef;
import android.content.Context;
import android.content.IntentSender;
@@ -47,6 +49,7 @@ import android.os.SystemProperties;
import android.provider.DeviceConfig;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.FastImmutableArraySet;
import android.util.Slog;

import com.android.internal.annotations.GuardedBy;
@@ -154,6 +157,39 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo
            ROR_REQUESTED_SKIP_CLEAR })
    private @interface ResumeOnRebootActionsOnClear {}

    /**
     * Fatal arm escrow errors from lock settings that means the RoR is in a bad state. So clients
     * need to prepare RoR again.
     */
    static final FastImmutableArraySet<Integer> FATAL_ARM_ESCROW_ERRORS =
            new FastImmutableArraySet<>(new Integer[]{
                    LockSettingsInternal.ARM_REBOOT_ERROR_ESCROW_NOT_READY,
                    LockSettingsInternal.ARM_REBOOT_ERROR_NO_PROVIDER,
                    LockSettingsInternal.ARM_REBOOT_ERROR_PROVIDER_MISMATCH,
                    LockSettingsInternal.ARM_REBOOT_ERROR_NO_ESCROW_KEY,
                    LockSettingsInternal.ARM_REBOOT_ERROR_KEYSTORE_FAILURE,
            });

    /**
     * The error details for ArmRebootEscrow. It contains error codes from RecoverySystemService
     * and LockSettingsService.
     */
    static class RebootPreparationError {
        final @ResumeOnRebootRebootErrorCode int mRebootErrorCode;
        final int mProviderErrorCode;  // The supplemental error code from lock settings

        RebootPreparationError(int rebootErrorCode, int providerErrorCode) {
            mRebootErrorCode = rebootErrorCode;
            mProviderErrorCode = providerErrorCode;
        }

        int getErrorCodeForMetrics() {
            // The ResumeOnRebootRebootErrorCode are aligned with 1000; so it's safe to add them
            // for metrics purpose.
            return mRebootErrorCode + mProviderErrorCode;
        }
    }

    /**
     * Manages shared preference, i.e. the storage used for metrics reporting.
     */
@@ -709,34 +745,40 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo
        return true;
    }

    private @ResumeOnRebootRebootErrorCode int armRebootEscrow(String packageName,
    private RebootPreparationError armRebootEscrow(String packageName,
            boolean slotSwitch) {
        if (packageName == null) {
            Slog.w(TAG, "Missing packageName when rebooting with lskf.");
            return RESUME_ON_REBOOT_REBOOT_ERROR_INVALID_PACKAGE_NAME;
            return new RebootPreparationError(
                    RESUME_ON_REBOOT_REBOOT_ERROR_INVALID_PACKAGE_NAME, ARM_REBOOT_ERROR_NONE);
        }
        if (!isLskfCaptured(packageName)) {
            return RESUME_ON_REBOOT_REBOOT_ERROR_LSKF_NOT_CAPTURED;
            return new RebootPreparationError(RESUME_ON_REBOOT_REBOOT_ERROR_LSKF_NOT_CAPTURED,
                    ARM_REBOOT_ERROR_NONE);
        }

        if (!verifySlotForNextBoot(slotSwitch)) {
            return RESUME_ON_REBOOT_REBOOT_ERROR_SLOT_MISMATCH;
            return new RebootPreparationError(RESUME_ON_REBOOT_REBOOT_ERROR_SLOT_MISMATCH,
                    ARM_REBOOT_ERROR_NONE);
        }

        final long origId = Binder.clearCallingIdentity();
        boolean result;
        int providerErrorCode;
        try {
            result = mInjector.getLockSettingsService().armRebootEscrow();
            providerErrorCode = mInjector.getLockSettingsService().armRebootEscrow();
        } finally {
            Binder.restoreCallingIdentity(origId);
        }

        if (!result) {
            Slog.w(TAG, "Failure to escrow key for reboot");
            return RESUME_ON_REBOOT_REBOOT_ERROR_PROVIDER_PREPARATION_FAILURE;
        if (providerErrorCode != ARM_REBOOT_ERROR_NONE) {
            Slog.w(TAG, "Failure to escrow key for reboot, providerErrorCode: "
                    + providerErrorCode);
            return new RebootPreparationError(
                    RESUME_ON_REBOOT_REBOOT_ERROR_PROVIDER_PREPARATION_FAILURE, providerErrorCode);
        }

        return RESUME_ON_REBOOT_REBOOT_ERROR_NONE;
        return new RebootPreparationError(RESUME_ON_REBOOT_REBOOT_ERROR_NONE,
                ARM_REBOOT_ERROR_NONE);
    }

    private boolean useServerBasedRoR() {
@@ -750,7 +792,7 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo
    }

    private void reportMetricsOnRebootWithLskf(String packageName, boolean slotSwitch,
            @ResumeOnRebootRebootErrorCode int errorCode) {
            RebootPreparationError escrowError) {
        int uid = mInjector.getUidFromPackageName(packageName);
        boolean serverBased = useServerBasedRoR();
        int preparedClientCount;
@@ -773,15 +815,31 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo
                        + " request count %d, lskf captured count %d, duration since lskf captured"
                        + " %d seconds.", packageName, preparedClientCount, requestCount,
                lskfCapturedCount, durationSeconds));
        mInjector.reportRebootEscrowRebootMetrics(errorCode, uid, preparedClientCount,
                requestCount, slotSwitch, serverBased, durationSeconds, lskfCapturedCount);
        mInjector.reportRebootEscrowRebootMetrics(escrowError.getErrorCodeForMetrics(), uid,
                preparedClientCount, requestCount, slotSwitch, serverBased, durationSeconds,
                lskfCapturedCount);
    }

    private void clearRoRPreparationStateOnRebootFailure(RebootPreparationError escrowError) {
        if (!FATAL_ARM_ESCROW_ERRORS.contains(escrowError.mProviderErrorCode)) {
            return;
        }

        Slog.w(TAG, "Clearing resume on reboot states for all clients on arm escrow error: "
                + escrowError.mProviderErrorCode);
        synchronized (this) {
            mCallerPendingRequest.clear();
            mCallerPreparedForReboot.clear();
        }
    }

    private @ResumeOnRebootRebootErrorCode int rebootWithLskfImpl(String packageName, String reason,
            boolean slotSwitch) {
        @ResumeOnRebootRebootErrorCode int errorCode = armRebootEscrow(packageName, slotSwitch);
        reportMetricsOnRebootWithLskf(packageName, slotSwitch, errorCode);
        RebootPreparationError escrowError = armRebootEscrow(packageName, slotSwitch);
        reportMetricsOnRebootWithLskf(packageName, slotSwitch, escrowError);
        clearRoRPreparationStateOnRebootFailure(escrowError);

        @ResumeOnRebootRebootErrorCode int errorCode = escrowError.mRebootErrorCode;
        if (errorCode != RESUME_ON_REBOOT_REBOOT_ERROR_NONE) {
            return errorCode;
        }
Loading