diff --git a/core/api/system-current.txt b/core/api/system-current.txt index c948f05f38a15..fb36f4742bf82 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -911,6 +911,7 @@ package android.app { public class KeyguardManager { method @RequiresPermission(android.Manifest.permission.MANAGE_WEAK_ESCROW_TOKEN) public long addWeakEscrowToken(@NonNull byte[], @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull android.app.KeyguardManager.WeakEscrowTokenActivatedListener); + method @NonNull @RequiresPermission(android.Manifest.permission.CHECK_REMOTE_LOCKSCREEN) public android.content.Intent createConfirmDeviceCredentialForRemoteValidationIntent(@NonNull android.app.StartLockscreenValidationRequest, @NonNull android.content.ComponentName, @Nullable CharSequence, @Nullable CharSequence, @Nullable CharSequence, @Nullable CharSequence); method public android.content.Intent createConfirmFactoryResetCredentialIntent(CharSequence, CharSequence, CharSequence); method @RequiresPermission("android.permission.SET_INITIAL_LOCK") public int getMinLockLength(boolean, int); method @RequiresPermission(android.Manifest.permission.CONTROL_KEYGUARD_SECURE_NOTIFICATIONS) public boolean getPrivateNotificationsAllowed(); diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java index be3d5a6a236f6..be0d1c9ddb16e 100644 --- a/core/java/android/app/KeyguardManager.java +++ b/core/java/android/app/KeyguardManager.java @@ -31,6 +31,7 @@ import android.app.admin.DevicePolicyManager.PasswordComplexity; import android.app.admin.PasswordMetrics; import android.app.trust.ITrustManager; import android.compat.annotation.UnsupportedAppUsage; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; @@ -108,6 +109,13 @@ public class KeyguardManager { public static final String ACTION_CONFIRM_FRP_CREDENTIAL = "android.app.action.CONFIRM_FRP_CREDENTIAL"; + /** + * Intent used to prompt user to to validate the credentials of a remote device. + * @hide + */ + public static final String ACTION_CONFIRM_REMOTE_DEVICE_CREDENTIAL = + "android.app.action.CONFIRM_REMOTE_DEVICE_CREDENTIAL"; + /** * A CharSequence dialog title to show to the user when used with a * {@link #ACTION_CONFIRM_DEVICE_CREDENTIAL}. @@ -130,10 +138,27 @@ public class KeyguardManager { public static final String EXTRA_ALTERNATE_BUTTON_LABEL = "android.app.extra.ALTERNATE_BUTTON_LABEL"; + /** + * A CharSequence label for the checkbox when used with + * {@link #ACTION_CONFIRM_REMOTE_DEVICE_CREDENTIAL} + * @hide + */ + public static final String EXTRA_CHECKBOX_LABEL = "android.app.extra.CHECKBOX_LABEL"; + + /** + * A {@link StartLockscreenValidationRequest} extra to be sent along with + * {@link #ACTION_CONFIRM_REMOTE_DEVICE_CREDENTIAL} containing the data needed to prompt for + * a remote device's lock screen. + * @hide + */ + public static final String EXTRA_START_LOCKSCREEN_VALIDATION_REQUEST = + "android.app.extra.START_LOCKSCREEN_VALIDATION_REQUEST"; + /** * Result code returned by the activity started by - * {@link #createConfirmFactoryResetCredentialIntent} indicating that the user clicked the - * alternate button. + * {@link #createConfirmFactoryResetCredentialIntent} or + * {@link #createConfirmDeviceCredentialForRemoteValidationIntent} + * indicating that the user clicked the alternate button. * * @hide */ @@ -331,6 +356,47 @@ public class KeyguardManager { return intent; } + /** + * Get an Intent to launch an activity to prompt the user to confirm the + * credentials (pin, pattern or password) of a remote device. + * @param startLockscreenValidationRequest contains information necessary to start remote device + * credential validation. + * @param remoteLockscreenValidationServiceComponent + * the {@link ComponentName} of the implementation of + * {@link android.service.remotelockscreenvalidation.RemoteLockscreenValidationService} + * @param checkboxLabel if not empty, a checkbox is provided with the given label. When checked, + * the validated remote device credential will be set as the device lock of + * the current device. + * @param alternateButtonLabel if not empty, a button is provided with the given label. Upon + * clicking this button, the activity returns + * {@link #RESULT_ALTERNATE}. + * @hide + */ + @SystemApi + @RequiresPermission(Manifest.permission.CHECK_REMOTE_LOCKSCREEN) + @NonNull + public Intent createConfirmDeviceCredentialForRemoteValidationIntent( + @NonNull StartLockscreenValidationRequest startLockscreenValidationRequest, + @NonNull ComponentName remoteLockscreenValidationServiceComponent, + @Nullable CharSequence title, + @Nullable CharSequence description, + @Nullable CharSequence checkboxLabel, + @Nullable CharSequence alternateButtonLabel) { + Intent intent = new Intent(ACTION_CONFIRM_REMOTE_DEVICE_CREDENTIAL) + .putExtra( + EXTRA_START_LOCKSCREEN_VALIDATION_REQUEST, startLockscreenValidationRequest) + .putExtra(Intent.EXTRA_COMPONENT_NAME, remoteLockscreenValidationServiceComponent) + .putExtra(EXTRA_TITLE, title) + .putExtra(EXTRA_DESCRIPTION, description) + .putExtra(EXTRA_CHECKBOX_LABEL, checkboxLabel) + .putExtra(EXTRA_ALTERNATE_BUTTON_LABEL, alternateButtonLabel); + + // explicitly set the package for security + intent.setPackage(getSettingsPackageForIntent(intent)); + + return intent; + } + /** * Controls whether notifications can be shown atop a securely locked screen in their full * private form (same as when the device is unlocked). diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java index ba9847eb46316..c02dd6abe5d31 100644 --- a/core/java/android/util/FeatureFlagUtils.java +++ b/core/java/android/util/FeatureFlagUtils.java @@ -183,6 +183,14 @@ public class FeatureFlagUtils { public static final String SETTINGS_ENABLE_LOCKSCREEN_TRANSFER_API = "settings_enable_lockscreen_transfer_api"; + /** + * Flag to enable remote device credential validation + * @hide + */ + public static final String SETTINGS_REMOTE_DEVICE_CREDENTIAL_VALIDATION = + "settings_remote_device_credential_validation"; + + private static final Map DEFAULT_FLAGS; static { @@ -226,6 +234,7 @@ public class FeatureFlagUtils { DEFAULT_FLAGS.put(SETTINGS_FLASH_ALERTS, "false"); DEFAULT_FLAGS.put(SETTINGS_SHOW_UDFPS_ENROLL_IN_SETTINGS, "true"); DEFAULT_FLAGS.put(SETTINGS_ENABLE_LOCKSCREEN_TRANSFER_API, "false"); + DEFAULT_FLAGS.put(SETTINGS_REMOTE_DEVICE_CREDENTIAL_VALIDATION, "false"); } private static final Set PERSISTENT_FLAGS; diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index 44c37dcb2383d..f72c4c36b6c3f 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -65,6 +65,7 @@ import com.google.android.collect.Lists; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.nio.charset.StandardCharsets; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.ArrayList; @@ -115,6 +116,13 @@ public class LockPatternUtils { public static final int CREDENTIAL_TYPE_PIN = 3; public static final int CREDENTIAL_TYPE_PASSWORD = 4; + /** + * Header used for the encryption and decryption of the device credential for + * remote device lockscreen validation. + */ + public static final byte[] ENCRYPTED_REMOTE_CREDENTIALS_HEADER = + "encrypted_remote_credentials".getBytes(StandardCharsets.UTF_8); + @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = {"CREDENTIAL_TYPE_"}, value = { CREDENTIAL_TYPE_NONE, diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java index 6bf64eb219926..c08958b031576 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java @@ -102,8 +102,6 @@ public class RecoverableKeyStoreManager { private static final String TAG = "RecoverableKeyStoreMgr"; private static final long SYNC_DELAY_MILLIS = 2000; private static final int INVALID_REMOTE_GUESS_LIMIT = 5; - public static final byte[] ENCRYPTED_REMOTE_CREDENTIALS_HEADER = - "encrypted_remote_credentials".getBytes(StandardCharsets.UTF_8); private static RecoverableKeyStoreManager mInstance; @@ -1055,7 +1053,7 @@ public class RecoverableKeyStoreManager { decryptedCredentials = SecureBox.decrypt( session.getKeyPair().getPrivate(), /* sharedSecret= */ null, - ENCRYPTED_REMOTE_CREDENTIALS_HEADER, + LockPatternUtils.ENCRYPTED_REMOTE_CREDENTIALS_HEADER, encryptedCredential); } catch (NoSuchAlgorithmException e) { Log.wtf(TAG, "Missing SecureBox algorithm. AOSP required to support this.", e); diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java index ad6d6b930a52f..2affe92ea31e3 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java @@ -1462,7 +1462,7 @@ public class RecoverableKeyStoreManagerTest { return SecureBox.encrypt( publicKey, /* sharedSecret= */ null, - RecoverableKeyStoreManager.ENCRYPTED_REMOTE_CREDENTIALS_HEADER, + LockPatternUtils.ENCRYPTED_REMOTE_CREDENTIALS_HEADER, credentials); }