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

Commit 4453934e authored by Haining Chen's avatar Haining Chen Committed by Gerrit Code Review
Browse files

Merge changes I9115b707,I1912b8ad into main

* changes:
  Report primary auth attempts from LockSettingsService
  Add flag for reporting primary auth attempts from LockSettingsService
parents 438d2e20 5a4e185a
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -58,3 +58,10 @@ flag {
    bug: "290312729"
    is_fixed_read_only: true
}

flag {
  name: "report_primary_auth_attempts"
  namespace: "biometrics"
  description: "Report primary auth attempts from LockSettingsService"
  bug: "285053096"
}
+36 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.internal.widget;

/**
 * Callback interface between LockSettingService and other system services to be notified about the
 * state of primary authentication (i.e. PIN/pattern/password).
 * @hide
 */
oneway interface ILockSettingsStateListener {
    /**
     * Defines behavior in response to a successful authentication
     * @param userId The user Id for the requested authentication
     */
    void onAuthenticationSucceeded(int userId);

    /**
     * Defines behavior in response to a failed authentication
     * @param userId The user Id for the requested authentication
     */
    void onAuthenticationFailed(int userId);
}
 No newline at end of file
+12 −0
Original line number Diff line number Diff line
@@ -166,4 +166,16 @@ public abstract class LockSettingsInternal {
     * Refreshes pending strong auth timeout with the latest admin requirement set by device policy.
     */
    public abstract void refreshStrongAuthTimeout(int userId);

    /**
     * Register a LockSettingsStateListener
     * @param listener The listener to be registered
     */
    public abstract void registerLockSettingsStateListener(ILockSettingsStateListener listener);

    /**
     * Unregister a LockSettingsStateListener
     * @param listener The listener to be unregistered
     */
    public abstract void unregisterLockSettingsStateListener(ILockSettingsStateListener listener);
}
+46 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.server.locksettings;

import static android.security.Flags.reportPrimaryAuthAttempts;
import static android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE;
import static android.Manifest.permission.MANAGE_BIOMETRIC;
import static android.Manifest.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS;
@@ -90,6 +91,7 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.IProgressListener;
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
@@ -135,6 +137,7 @@ import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.internal.widget.ICheckCredentialProgressCallback;
import com.android.internal.widget.ILockSettings;
import com.android.internal.widget.ILockSettingsStateListener;
import com.android.internal.widget.IWeakEscrowTokenActivatedListener;
import com.android.internal.widget.IWeakEscrowTokenRemovedListener;
import com.android.internal.widget.LockPatternUtils;
@@ -327,6 +330,9 @@ public class LockSettingsService extends ILockSettings.Stub {

    private HashMap<UserHandle, UserManager> mUserManagerCache = new HashMap<>();

    private final RemoteCallbackList<ILockSettingsStateListener> mLockSettingsStateListeners =
            new RemoteCallbackList<>();

    // This class manages life cycle events for encrypted users on File Based Encryption (FBE)
    // devices. The most basic of these is to show/hide notifications about missing features until
    // the user unlocks the account and credential-encrypted storage is available.
@@ -2342,9 +2348,37 @@ public class LockSettingsService extends ILockSettings.Stub {
                requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_LOCKOUT, userId);
            }
        }
        if (reportPrimaryAuthAttempts()) {
            final boolean success =
                    response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK;
            notifyLockSettingsStateListeners(success, userId);
        }
        return response;
    }

    private void notifyLockSettingsStateListeners(boolean success, int userId) {
        int i = mLockSettingsStateListeners.beginBroadcast();
        try {
            while (i > 0) {
                i--;
                try {
                    if (success) {
                        mLockSettingsStateListeners.getBroadcastItem(i)
                                .onAuthenticationSucceeded(userId);
                    } else {
                        mLockSettingsStateListeners.getBroadcastItem(i)
                                .onAuthenticationFailed(userId);
                    }
                } catch (RemoteException e) {
                    Slog.e(TAG, "Exception while notifying LockSettingsStateListener:"
                            + " success = " + success + ", userId = " + userId, e);
                }
            }
        } finally {
            mLockSettingsStateListeners.finishBroadcast();
        }
    }

    @Override
    public VerifyCredentialResponse verifyTiedProfileChallenge(LockscreenCredential credential,
            int userId, @LockPatternUtils.VerifyFlag int flags) {
@@ -3662,6 +3696,18 @@ public class LockSettingsService extends ILockSettings.Stub {
        public void refreshStrongAuthTimeout(int userId) {
            mStrongAuth.refreshStrongAuthTimeout(userId);
        }

        @Override
        public void registerLockSettingsStateListener(
                @NonNull ILockSettingsStateListener listener) {
            mLockSettingsStateListeners.register(listener);
        }

        @Override
        public void unregisterLockSettingsStateListener(
                @NonNull ILockSettingsStateListener listener) {
            mLockSettingsStateListeners.unregister(listener);
        }
    }

    private class RebootEscrowCallbacks implements RebootEscrowManager.Callbacks {
+70 −0
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.server.locksettings;

import static android.security.Flags.FLAG_REPORT_PRIMARY_AUTH_ATTEMPTS;

import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
@@ -30,25 +32,30 @@ import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.app.PropertyInvalidatedCache;
import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
import android.platform.test.flag.junit.SetFlagsRule;
import android.service.gatekeeper.GateKeeperResponse;
import android.text.TextUtils;

import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;

import com.android.internal.widget.ILockSettingsStateListener;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockscreenCredential;
import com.android.internal.widget.VerifyCredentialResponse;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

@@ -59,6 +66,7 @@ import org.junit.runner.RunWith;
@Presubmit
@RunWith(AndroidJUnit4.class)
public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();

    @Before
    public void setUp() {
@@ -398,6 +406,60 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
                        eq(CREDENTIAL_TYPE_PASSWORD), any(), eq(MANAGED_PROFILE_USER_ID));
    }

    @Test
    public void testVerifyCredential_notifyLockSettingsStateListeners_whenGoodPassword()
            throws Exception {
        mSetFlagsRule.enableFlags(FLAG_REPORT_PRIMARY_AUTH_ATTEMPTS);
        final LockscreenCredential password = newPassword("password");
        setCredential(PRIMARY_USER_ID, password);
        final ILockSettingsStateListener listener = mockLockSettingsStateListener();
        mLocalService.registerLockSettingsStateListener(listener);

        assertEquals(VerifyCredentialResponse.RESPONSE_OK,
                mService.verifyCredential(password, PRIMARY_USER_ID, 0 /* flags */)
                        .getResponseCode());

        verify(listener).onAuthenticationSucceeded(PRIMARY_USER_ID);
    }

    @Test
    public void testVerifyCredential_notifyLockSettingsStateListeners_whenBadPassword()
            throws Exception {
        mSetFlagsRule.enableFlags(FLAG_REPORT_PRIMARY_AUTH_ATTEMPTS);
        final LockscreenCredential password = newPassword("password");
        setCredential(PRIMARY_USER_ID, password);
        final LockscreenCredential badPassword = newPassword("badPassword");
        final ILockSettingsStateListener listener = mockLockSettingsStateListener();
        mLocalService.registerLockSettingsStateListener(listener);

        assertEquals(VerifyCredentialResponse.RESPONSE_ERROR,
                mService.verifyCredential(badPassword, PRIMARY_USER_ID, 0 /* flags */)
                        .getResponseCode());

        verify(listener).onAuthenticationFailed(PRIMARY_USER_ID);
    }

    @Test
    public void testLockSettingsStateListener_registeredThenUnregistered() throws Exception {
        mSetFlagsRule.enableFlags(FLAG_REPORT_PRIMARY_AUTH_ATTEMPTS);
        final LockscreenCredential password = newPassword("password");
        setCredential(PRIMARY_USER_ID, password);
        final LockscreenCredential badPassword = newPassword("badPassword");
        final ILockSettingsStateListener listener = mockLockSettingsStateListener();

        mLocalService.registerLockSettingsStateListener(listener);
        assertEquals(VerifyCredentialResponse.RESPONSE_OK,
                mService.verifyCredential(password, PRIMARY_USER_ID, 0 /* flags */)
                        .getResponseCode());
        verify(listener).onAuthenticationSucceeded(PRIMARY_USER_ID);

        mLocalService.unregisterLockSettingsStateListener(listener);
        assertEquals(VerifyCredentialResponse.RESPONSE_ERROR,
                mService.verifyCredential(badPassword, PRIMARY_USER_ID, 0 /* flags */)
                        .getResponseCode());
        verify(listener, never()).onAuthenticationFailed(PRIMARY_USER_ID);
    }

    @Test
    public void testSetCredentialNotPossibleInSecureFrpModeDuringSuw() {
        setUserSetupComplete(false);
@@ -537,4 +599,12 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests {
            assertNotEquals(0, mGateKeeperService.getSecureUserId(userId));
        }
    }

    private ILockSettingsStateListener mockLockSettingsStateListener() {
        ILockSettingsStateListener listener = mock(ILockSettingsStateListener.Stub.class);
        IBinder binder = mock(IBinder.class);
        when(binder.isBinderAlive()).thenReturn(true);
        when(listener.asBinder()).thenReturn(binder);
        return listener;
    }
}