When "try again" is showing, authentication is canceled internally.
BiometricService caches the client's info so that authentication can
be restarted when "try again" is pressed. Because authentication
is not running when "try again" is showing, BiometricService also needs
to have a TaskStackListener so that BP can be dismissed and an error can
be sent to the client when the app loses focus.
IBiometricServiceReceiver has been split into two. One for BiometricPrompt
to receive messages from BiometricService, and another for BiometricService
to receive messages from SystemUI/<Biometric>Services.
When we get locked out, don't send the last onAuthenticationFailed
to the client, since "Authentication failed" will be shown briefly
and be replaced by "Device locked out" which is janky
Bug: 111461540
Test: Tested with requireConfirmation enabled/disabled
Test: Tested onConfigurationChange corner cases, e.g. when "try again"
or "confirm" buttons are showing, rotate the device. Buttons
persist correctly and don't appear when unexpected
Test: Tested task stack corner cases, e.g. when "try again" is showing,
press home button. BP dismisses and client receives ERROR_CANCELED
Test: BiometricPromptDemo receives all callbacks
Change-Id: I62126708ce8db8b358c666a07aa7c39607642c9d
192 lines
8.8 KiB
XML
192 lines
8.8 KiB
XML
<?xml version="1.0" encoding="utf-8"?>
|
|
<!--
|
|
~ Copyright (C) 2018 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
|
|
-->
|
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
android:id="@+id/layout"
|
|
android:layout_width="match_parent"
|
|
android:layout_height="match_parent">
|
|
|
|
<ImageView
|
|
android:id="@+id/background"
|
|
android:layout_width="match_parent"
|
|
android:layout_height="match_parent"
|
|
android:scaleType="center" />
|
|
|
|
<LinearLayout
|
|
android:layout_width="match_parent"
|
|
android:layout_height="match_parent"
|
|
android:gravity="bottom"
|
|
android:background="@color/biometric_dialog_dim_color"
|
|
android:orientation="vertical">
|
|
|
|
<!-- This is not a Space since Spaces cannot be clicked -->
|
|
<View
|
|
android:id="@+id/space"
|
|
android:layout_width="match_parent"
|
|
android:layout_height="0dp"
|
|
android:layout_weight="1" />
|
|
|
|
<LinearLayout
|
|
android:layout_width="match_parent"
|
|
android:layout_height="wrap_content"
|
|
android:orientation="horizontal">
|
|
|
|
<!-- This is not a Space since Spaces cannot be clicked. The width of this changes depending
|
|
on horizontal/portrait orientation -->
|
|
<View
|
|
android:id="@+id/left_space"
|
|
android:layout_weight="1"
|
|
android:layout_width="0dp"
|
|
android:layout_height="match_parent"/>
|
|
|
|
<LinearLayout
|
|
android:id="@+id/dialog"
|
|
android:layout_width="match_parent"
|
|
android:layout_height="wrap_content"
|
|
android:orientation="vertical"
|
|
android:background="@drawable/biometric_dialog_bg"
|
|
android:layout_marginBottom="@dimen/biometric_dialog_border_padding"
|
|
android:layout_marginLeft="@dimen/biometric_dialog_border_padding"
|
|
android:layout_marginRight="@dimen/biometric_dialog_border_padding">
|
|
|
|
<TextView
|
|
android:id="@+id/title"
|
|
android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
|
|
android:layout_width="match_parent"
|
|
android:layout_height="wrap_content"
|
|
android:layout_marginEnd="24dp"
|
|
android:layout_marginStart="24dp"
|
|
android:layout_marginTop="24dp"
|
|
android:gravity="@integer/biometric_dialog_text_gravity"
|
|
android:textSize="20sp"
|
|
android:maxLines="1"
|
|
android:singleLine="true"
|
|
android:ellipsize="marquee"
|
|
android:marqueeRepeatLimit="marquee_forever"
|
|
android:textColor="?android:attr/textColorPrimary"/>
|
|
|
|
<TextView
|
|
android:id="@+id/subtitle"
|
|
android:layout_width="match_parent"
|
|
android:layout_height="wrap_content"
|
|
android:layout_marginTop="8dp"
|
|
android:layout_marginStart="24dp"
|
|
android:layout_marginEnd="24dp"
|
|
android:gravity="@integer/biometric_dialog_text_gravity"
|
|
android:textSize="16sp"
|
|
android:maxLines="1"
|
|
android:singleLine="true"
|
|
android:ellipsize="marquee"
|
|
android:marqueeRepeatLimit="marquee_forever"
|
|
android:textColor="?android:attr/textColorPrimary"/>
|
|
|
|
<TextView
|
|
android:id="@+id/description"
|
|
android:layout_width="match_parent"
|
|
android:layout_height="wrap_content"
|
|
android:layout_marginEnd="24dp"
|
|
android:layout_marginStart="24dp"
|
|
android:gravity="@integer/biometric_dialog_text_gravity"
|
|
android:paddingTop="8dp"
|
|
android:textSize="16sp"
|
|
android:maxLines="4"
|
|
android:textColor="?android:attr/textColorPrimary"/>
|
|
|
|
<ImageView
|
|
android:id="@+id/biometric_icon"
|
|
android:layout_width="@dimen/biometric_dialog_biometric_icon_size"
|
|
android:layout_height="@dimen/biometric_dialog_biometric_icon_size"
|
|
android:layout_gravity="center_horizontal"
|
|
android:layout_marginTop="48dp"
|
|
android:scaleType="fitXY" />
|
|
|
|
<TextView
|
|
android:id="@+id/error"
|
|
android:layout_width="match_parent"
|
|
android:layout_height="wrap_content"
|
|
android:layout_marginEnd="24dp"
|
|
android:layout_marginStart="24dp"
|
|
android:paddingTop="16dp"
|
|
android:paddingBottom="24dp"
|
|
android:textSize="12sp"
|
|
android:gravity="center_horizontal"
|
|
android:accessibilityLiveRegion="polite"
|
|
android:contentDescription="@string/accessibility_biometric_dialog_help_area"
|
|
android:textColor="?android:attr/textColorSecondary"/>
|
|
|
|
<LinearLayout
|
|
android:layout_width="match_parent"
|
|
android:layout_height="72dip"
|
|
android:paddingTop="24dp"
|
|
android:layout_gravity="center_vertical"
|
|
style="?android:attr/buttonBarStyle"
|
|
android:orientation="horizontal"
|
|
android:measureWithLargestChild="true">
|
|
<Space android:id="@+id/leftSpacer"
|
|
android:layout_width="12dp"
|
|
android:layout_height="match_parent"
|
|
android:visibility="visible" />
|
|
<!-- Negative Button -->
|
|
<Button android:id="@+id/button2"
|
|
android:layout_width="wrap_content"
|
|
android:layout_height="match_parent"
|
|
style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
|
|
android:gravity="center"
|
|
android:maxLines="2" />
|
|
<Space android:id="@+id/middleSpacer"
|
|
android:layout_width="0dp"
|
|
android:layout_height="match_parent"
|
|
android:layout_weight="1"
|
|
android:visibility="visible" />
|
|
<!-- Positive Button -->
|
|
<Button android:id="@+id/button1"
|
|
android:layout_width="wrap_content"
|
|
android:layout_height="match_parent"
|
|
style="@*android:style/Widget.DeviceDefault.Button.Colored"
|
|
android:gravity="center"
|
|
android:maxLines="2"
|
|
android:text="@string/biometric_dialog_confirm"
|
|
android:visibility="gone"/>
|
|
<!-- Try Again Button -->
|
|
<Button android:id="@+id/button_try_again"
|
|
android:layout_width="wrap_content"
|
|
android:layout_height="match_parent"
|
|
style="@*android:style/Widget.DeviceDefault.Button.Colored"
|
|
android:gravity="center"
|
|
android:maxLines="2"
|
|
android:text="@string/biometric_dialog_try_again"
|
|
android:visibility="gone"/>
|
|
<Space android:id="@+id/rightSpacer"
|
|
android:layout_width="12dip"
|
|
android:layout_height="match_parent"
|
|
android:visibility="visible" />
|
|
</LinearLayout>
|
|
</LinearLayout>
|
|
|
|
<!-- This is not a Space since Spaces cannot be clicked. The width of this changes depending
|
|
on horizontal/portrait orientation -->
|
|
<View
|
|
android:id="@+id/right_space"
|
|
android:layout_weight="1"
|
|
android:layout_width="0dp"
|
|
android:layout_height="match_parent" />
|
|
|
|
</LinearLayout>
|
|
|
|
</LinearLayout>
|
|
|
|
</FrameLayout>
|