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

Commit 9bf277f9 authored by Tianjie Xu's avatar Tianjie Xu Committed by Automerger Merge Worker
Browse files

Merge "Improve the error handling for armRebootEscrow" am: 246c14d6 am: cd4836d1 am: f3bde710

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

Change-Id: Ie1a2e4e87e1dcda0b53044c0cb577b938455fdee
parents 9b4ec0cd f3bde710
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
@@ -3658,7 +3658,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