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

Commit f939f0b3 authored by Christoph Schlosser's avatar Christoph Schlosser Committed by Android (Google) Code Review
Browse files

Merge changes I2142abe2,I2c9e38f7 into main

* changes:
  Hide pattern by default when a non-touch pointing device is available
  Hide last typed number of pin by default on devices with a keyboard
parents 88bd1753 ebe378d2
Loading
Loading
Loading
Loading
+42 −2
Original line number Diff line number Diff line
@@ -25,6 +25,8 @@ import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED
import static android.security.Flags.reportPrimaryAuthAttempts;
import static android.security.Flags.shouldTrustManagerListenForPrimaryAuth;

import static com.android.internal.widget.flags.Flags.hideLastCharWithPhysicalInput;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -42,6 +44,7 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.hardware.input.InputManagerGlobal;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
@@ -59,6 +62,7 @@ import android.util.Log;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.SparseLongArray;
import android.view.InputDevice;

import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
@@ -1097,12 +1101,20 @@ public class LockPatternUtils {
        return type == CREDENTIAL_TYPE_PATTERN;
    }

    private boolean hasActivePointerDeviceAttached() {
        return !getEnabledNonTouchInputDevices(InputDevice.SOURCE_CLASS_POINTER).isEmpty();
    }

    /**
     * @return Whether the visible pattern is enabled.
     */
    @UnsupportedAppUsage
    public boolean isVisiblePatternEnabled(int userId) {
        return getBoolean(Settings.Secure.LOCK_PATTERN_VISIBLE, true, userId);
        boolean defaultValue = true;
        if (hideLastCharWithPhysicalInput()) {
            defaultValue = !hasActivePointerDeviceAttached();
        }
        return getBoolean(Settings.Secure.LOCK_PATTERN_VISIBLE, defaultValue, userId);
    }

    /**
@@ -1116,11 +1128,39 @@ public class LockPatternUtils {
        return getString(Settings.Secure.LOCK_PATTERN_VISIBLE, userId) != null;
    }

    private List<InputDevice> getEnabledNonTouchInputDevices(int source) {
        final InputManagerGlobal inputManager = InputManagerGlobal.getInstance();
        final int[] inputIds = inputManager.getInputDeviceIds();
        List<InputDevice> matchingDevices = new ArrayList<InputDevice>();
        for (final int deviceId : inputIds) {
            final InputDevice inputDevice = inputManager.getInputDevice(deviceId);
            if (!inputDevice.isEnabled()) continue;
            if (inputDevice.supportsSource(InputDevice.SOURCE_TOUCHSCREEN)) continue;
            if (inputDevice.isVirtual()) continue;
            if (!inputDevice.supportsSource(source)) continue;
            matchingDevices.add(inputDevice);
        }
        return matchingDevices;
    }

    private boolean hasPhysicalKeyboardActive() {
        final List<InputDevice> keyboards =
                getEnabledNonTouchInputDevices(InputDevice.SOURCE_KEYBOARD);
        for (final InputDevice keyboard : keyboards) {
            if (keyboard.isFullKeyboard()) return true;
        }
        return false;
    }

    /**
     * @return Whether enhanced pin privacy is enabled.
     */
    public boolean isPinEnhancedPrivacyEnabled(int userId) {
        return getBoolean(LOCK_PIN_ENHANCED_PRIVACY, false, userId);
        boolean defaultValue = false;
        if (hideLastCharWithPhysicalInput()) {
            defaultValue = hasPhysicalKeyboardActive();
        }
        return getBoolean(LOCK_PIN_ENHANCED_PRIVACY, defaultValue, userId);
    }

    /**
+162 −0
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.doNothing;
@@ -44,20 +45,27 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.pm.UserInfo;
import android.hardware.input.IInputManager;
import android.hardware.input.InputManagerGlobal;
import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.annotations.IgnoreUnderRavenwood;
import android.platform.test.flag.junit.SetFlagsRule;
import android.platform.test.ravenwood.RavenwoodRule;
import android.provider.Settings;
import android.test.mock.MockContentResolver;
import android.view.InputDevice;

import androidx.test.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;

import com.android.internal.util.test.FakeSettingsProvider;
import com.android.internal.widget.flags.Flags;

import com.google.android.collect.Lists;

@@ -76,6 +84,8 @@ import java.util.concurrent.CompletableFuture;
public class LockPatternUtilsTest {
    @Rule
    public final RavenwoodRule mRavenwood = new RavenwoodRule();
    @Rule
    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();

    private ILockSettings mLockSettings;
    private static final int USER_ID = 1;
@@ -395,4 +405,156 @@ public class LockPatternUtilsTest {
            }
        };
    }

    private InputManagerGlobal.TestSession configureExternalHardwareTest(InputDevice[] devices)
            throws RemoteException {
        final Context context = new ContextWrapper(InstrumentationRegistry.getTargetContext());
        final ILockSettings ils = mock(ILockSettings.class);
        when(ils.getBoolean(anyString(), anyBoolean(), anyInt())).thenThrow(RemoteException.class);
        mLockPatternUtils = new LockPatternUtils(context, ils);

        IInputManager inputManagerMock = mock(IInputManager.class);

        int[] deviceIds = new int[devices.length];

        for (int i = 0; i < devices.length; i++) {
            when(inputManagerMock.getInputDevice(i)).thenReturn(devices[i]);
        }

        when(inputManagerMock.getInputDeviceIds()).thenReturn(deviceIds);
        InputManagerGlobal.TestSession session =
                InputManagerGlobal.createTestSession(inputManagerMock);

        return session;
    }

    @Test
    @EnableFlags(Flags.FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
    public void isPinEnhancedPrivacyEnabled_noDevicesAttached() throws RemoteException {
        InputManagerGlobal.TestSession session = configureExternalHardwareTest(new InputDevice[0]);
        assertFalse(mLockPatternUtils.isPinEnhancedPrivacyEnabled(USER_ID));
        session.close();
    }

    @Test
    @EnableFlags(Flags.FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
    public void isPinEnhancedPrivacyEnabled_noEnabledDeviceAttached() throws RemoteException {
        InputDevice.Builder builder = new InputDevice.Builder();
        builder.setEnabled(false);
        InputManagerGlobal.TestSession session =
                configureExternalHardwareTest(new InputDevice[]{builder.build()});
        assertFalse(mLockPatternUtils.isPinEnhancedPrivacyEnabled(USER_ID));
        session.close();
    }

    @Test
    @EnableFlags(Flags.FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
    public void isPinEnhancedPrivacyEnabled_withoutHwKeyboard() throws RemoteException {
        InputDevice.Builder builder = new InputDevice.Builder();
        builder.setEnabled(true).setSources(InputDevice.SOURCE_TOUCHSCREEN);
        InputManagerGlobal.TestSession session =
                configureExternalHardwareTest(new InputDevice[]{builder.build()});
        assertFalse(mLockPatternUtils.isPinEnhancedPrivacyEnabled(USER_ID));
        session.close();
    }

    @Test
    @EnableFlags(Flags.FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
    public void isPinEnhancedPrivacyEnabled_withoutFullHwKeyboard() throws RemoteException {
        InputDevice.Builder builder = new InputDevice.Builder();
        builder
                .setEnabled(true)
                .setSources(InputDevice.SOURCE_KEYBOARD)
                .setKeyboardType(InputDevice.KEYBOARD_TYPE_NON_ALPHABETIC);
        InputManagerGlobal.TestSession session =
                configureExternalHardwareTest(new InputDevice[]{builder.build()});
        assertFalse(mLockPatternUtils.isPinEnhancedPrivacyEnabled(USER_ID));
        session.close();
    }

    @Test
    @DisableFlags(Flags.FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
    public void isPinEnhancedPrivacyEnabled_withHwKeyboardOldDefault() throws RemoteException {
        InputDevice.Builder builder = new InputDevice.Builder();
        builder
                .setEnabled(true)
                .setSources(InputDevice.SOURCE_KEYBOARD)
                .setKeyboardType(InputDevice.KEYBOARD_TYPE_ALPHABETIC);
        InputManagerGlobal.TestSession session =
                configureExternalHardwareTest(new InputDevice[]{builder.build()});
        assertFalse(mLockPatternUtils.isPinEnhancedPrivacyEnabled(USER_ID));
        session.close();
    }

    @Test
    @EnableFlags(Flags.FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
    public void isPinEnhancedPrivacyEnabled_withHwKeyboard() throws RemoteException {
        InputDevice.Builder builder = new InputDevice.Builder();
        builder
                .setEnabled(true)
                .setSources(InputDevice.SOURCE_KEYBOARD)
                .setKeyboardType(InputDevice.KEYBOARD_TYPE_ALPHABETIC);
        InputManagerGlobal.TestSession session =
                configureExternalHardwareTest(new InputDevice[]{builder.build()});
        assertTrue(mLockPatternUtils.isPinEnhancedPrivacyEnabled(USER_ID));
        session.close();
    }

    @Test
    @EnableFlags(Flags.FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
    public void isVisiblePatternEnabled_noDevices() throws RemoteException {
        InputManagerGlobal.TestSession session = configureExternalHardwareTest(new InputDevice[0]);
        assertTrue(mLockPatternUtils.isVisiblePatternEnabled(USER_ID));
        session.close();
    }

    @Test
    @EnableFlags(Flags.FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
    public void isVisiblePatternEnabled_noEnabledDevices() throws RemoteException {
        InputDevice.Builder builder = new InputDevice.Builder();
        builder.setEnabled(false);
        InputManagerGlobal.TestSession session =
                configureExternalHardwareTest(new InputDevice[]{builder.build()});
        assertTrue(mLockPatternUtils.isVisiblePatternEnabled(USER_ID));
        session.close();
    }

    @Test
    @EnableFlags(Flags.FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
    public void isVisiblePatternEnabled_noPointingDevices() throws RemoteException {
        InputDevice.Builder builder = new InputDevice.Builder();
        builder
                .setEnabled(true)
                .setSources(InputDevice.SOURCE_TOUCHSCREEN);
        InputManagerGlobal.TestSession session =
                configureExternalHardwareTest(new InputDevice[]{builder.build()});
        assertTrue(mLockPatternUtils.isVisiblePatternEnabled(USER_ID));
        session.close();
    }

    @Test
    @EnableFlags(Flags.FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
    public void isVisiblePatternEnabled_externalPointingDevice() throws RemoteException {
        InputDevice.Builder builder = new InputDevice.Builder();
        builder
                .setEnabled(true)
                .setSources(InputDevice.SOURCE_CLASS_POINTER);
        InputManagerGlobal.TestSession session =
                configureExternalHardwareTest(new InputDevice[]{builder.build()});
        assertFalse(mLockPatternUtils.isVisiblePatternEnabled(USER_ID));
        session.close();
    }

    @Test
    @DisableFlags(Flags.FLAG_HIDE_LAST_CHAR_WITH_PHYSICAL_INPUT)
    public void isVisiblePatternEnabled_externalPointingDeviceOldDefault() throws RemoteException {
        InputDevice.Builder builder = new InputDevice.Builder();
        builder
                .setEnabled(true)
                .setSources(InputDevice.SOURCE_CLASS_POINTER);
        InputManagerGlobal.TestSession session =
                configureExternalHardwareTest(new InputDevice[]{builder.build()});
        assertTrue(mLockPatternUtils.isVisiblePatternEnabled(USER_ID));
        session.close();
    }
}