How to identify if device has in-display Biometric fingerprint support? - android

I'm writing a application feature to authenticate user using Biometric fingerprint authentication API. And it worked as expected with combination of BiometricPrompt API.
In general it display own UI dialog so it can be unified across Android device.(Improvement from Fingerprint Manager API)
In one of device testing scenario I come across in-display(on screen, e.g. Oneplus 6T device) fingerprint support instead rear biometric hardware option.
When I run application on it, on call of biometricPrompt.authenticate(..) instead of dialog it display in-display fingerprint authentication option. Which is ok, and manage by internal API of BiometricPrompt.
But it create some inconsistency to manage for developer.
When it provide in-build authentication dialog, all fallback error displayed in dialog itself.
But in case of in-display authentication I didn't found a way where it manage to display error message it-self. And I have to handle this fallback and display in a custom way.
Now question is
Is there a way to manage/display message by in-display authentication view component.
How can identify if device is support in-device biometric authentication.
Edit: Code reference I'm using:
import android.content.Context
import androidx.biometric.BiometricPrompt
import androidx.fragment.app.FragmentActivity
import java.lang.Exception
import java.util.concurrent.Executors
import javax.crypto.Cipher
class BiometricAuthenticationManager(private val context: Context) :
BiometricPrompt.AuthenticationCallback() {
private var biometricPrompt: BiometricPrompt? = null
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
super.onAuthenticationError(errorCode, errString)
biometricPrompt?.cancelAuthentication()
}
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
super.onAuthenticationSucceeded(result)
}
override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
}
fun init(cipher: Cipher, promptInfo: BiometricPrompt.PromptInfo) {
if (context is FragmentActivity) {
val executor = Executors.newSingleThreadExecutor()
biometricPrompt = BiometricPrompt(context, executor, this)
biometricPrompt?.authenticate(promptInfo, BiometricPrompt.CryptoObject(cipher))
} else {
throw Exception(
"Check for FragmentActivity context.")
}
}
}
For further reference about how it look like, please find following attachment.
I try to check same scenario for lock screen, where I guess it uses custom implementation using FingerPrintManager class and display message.

Faced with the same problem some time ago - OnePlus 6T has no Biometric UI, at the same time Samsung A51 - has its own (custom) UI for Fingerprint API and another for BiometricPrompt API.
I tried to watch for Activity window focus loss (For OnePlus - activity do not lose focus, at the same time BiometricPrompt UI leads to focus loss), but appear a problem with the Samsung device.
The only solution that I found, and it works for now:
Need to get the correct device name (for OnePlus for example, it should be not ONEPLUS A6003, but One Plus 6 instead)
Need to perform request to https://m.gsmarena.com/res.php3?sSearch=Device+Name, you should get the list with matches, usually, first one is needed
Parse HTML (from previews step) to get the link for device specification (like https://m.gsmarena.com/oneplus_6t-9350.php)
Now you need to perform a request to this link and again parse HTML and get the "Sensors" section. You should get a string like "Fingerprint (under display, optical), accelerometer, gyro, proximity, compass"
Split this string to get the list with sensors
Now when you need to check if an in-display scanner is present on the device, just check for "fingerprint" and "under display" for the same sensor.
Link with impl. that can be used as an example

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
//Fingerprint API only available on from Android 6.0 (M)
FingerprintManager fingerprintManager = (FingerprintManager) context.getSystemService(Context.FINGERPRINT_SERVICE);
if (!fingerprintManager.isHardwareDetected()) {
// Device doesn't support fingerprint authentication
} else if (!fingerprintManager.hasEnrolledFingerprints()) {
// User hasn't enrolled any fingerprints to authenticate with
} else {
// Everything is ready for fingerprint authentication
}
}
Dont Forget to Add
<uses-permission android:name=" android.permission.USE_BIOMETRIC" />

Related

CompanionDeviceManager without using an intent chooser and scanning

I already have mac addresses of the Bluetooth device that I need to connect to. I do not wish to ask for any location permission. when my app is launched I need to scan and connect to the Bluetooth device using the mac address that is specified. Companion Device pairing that has been introduced in the latest android version.. I do not wish to open a chooser and get the callback result in onActivityResult. If there is any other way I could route all the scanning done by the Companion Device.
deviceManager.associate(pairingRequest,
object : CompanionDeviceManager.Callback() {
// Called when a device is found. Launch the IntentSender so the user
// can select the device they want to pair with.
override fun onDeviceFound(chooserLauncher: IntentSender) {
startIntentSenderForResult(chooserLauncher,
SELECT_DEVICE_REQUEST_CODE, null, 0, 0, 0)
}
override fun onFailure(error: CharSequence?) {
// Handle the failure.
}
}, null)
in the below code if there is any way I could use a different mechanism so that my scanning can happen and I get the result somewhere and it does not ask for a dialog box for the user to enter.
startIntentSenderForResult(chooserLauncher,
SELECT_DEVICE_REQUEST_CODE, null, 0, 0, 0)
or is there any way to use bluetooth functionality without asking for location permission?
If you already know the mac address there is no need to scan. You can use
BluetoothDevice device = BluetoothAdapter.getDefaultAdapter().getRemoteDevice(macAddress)
val scanFilter = ScanFilter.Builder().setDeviceAddress(staticBtMac).build()
val ledeviceFilter: BluetoothLeDeviceFilter = BluetoothLeDeviceFilter.Builder()
.setScanFilter(scanFilter)
.build()
Before you create the pairingRequest above in AssociationRequest.Builder
You would
val pairingRequest: AssociationRequest = AssociationRequest.Builder()
.addDeviceFilter(ledeviceFilter)
.setSingleDevice(true)
This will not list devices but just a permission pop up dialog with ->
"Allow 'Appname' to access your 'BT ADV Device name'" [Allow] [Don't Allow] buttons. It looks slightly different depending on if its Pixel or Samsung or other phone but it comes down to simple yes/no.
There is also the
..setSelfManaged(true)
But this involves far more complexity and even more interaction with the framework.
If you really want to get into it you can see the actual code at https://cs.android.com/

Programmatically test if device fingerprint works correctly

Is there a way to test programatically that my phone's fingerprint sensor, works correctly?
I would like to be able to perform the test automatically, without enrolling my fingerprint and then trying to login with it to see if the sensor is working.
SDK 23 +
fun isHardwareSupported(context: Context): Boolean {
val fingerprintManager = FingerprintManagerCompat.from(context)
return fingerprintManager.isHardwareDetected
}
SDK 28+
fun isHardwareSupported(context: Context): Boolean {
val pm = context.packageManager
return pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)
}
Another alternative is to use BiometricManager androidx Library
The method can be used to determine if biometric hardware is present and if the user is enrolled or not.
fun isHardwareSupported(context: Context): Boolean {
return BiometricManager.from(context).canAuthenticate() != BIOMETRIC_ERROR_HW_UNAVAILABLE)
}
Returns BIOMETRIC_ERROR_NONE_ENROLLED if the user does not have any enrolled, or BIOMETRIC_ERROR_HW_UNAVAILABLE if none are currently supported/enabled.
Returns BIOMETRIC_SUCCESS if a biometric can currently be used (enrolled and available). Value is BIOMETRIC_SUCCESS, BIOMETRIC_ERROR_HW_UNAVAILABLE, BIOMETRIC_ERROR_NONE_ENROLLED, or BIOMETRIC_ERROR_NO_HARDWARE

Android Biometrics - which approach to choose (fallback to password)?

I have an application which has a PIN/Lock screen. In order to open the app user needs to enter his PIN code (which he had set up before in the app).
I want to add Biometric option -> instead of entering the PIN just place your fingerprint. However you should still have an option to use the PIN as fallback. Exactly the same as Revolut, LastPass or bunch of other banking apps. Pretty straightforward, right?
I've looked at the new Biometric API and it does not support fallback to a custom pin/password (only fallback to a lock screen). I could somehow add that manually (when user cancels the dialog) but this creates poor UX (switching from Google style dialog to app style screen). Also, Google dialog has a transparent background (which could reveal sensitive information) so I would need to put it in a separate blank activity (again poor experience). I wonder how banking apps are planning to migrate to that?
Should I do this the old way (FingerprintManager)? Is fallback to device lock safe enough? If someone knows your phone PIN he could access all of your apps.
Have you looked at this blog post? or that one? The AndroidX Biometrics Library provides a method called setNegativeButtonText() that provides an option for using an account/app credential if the user doesn't want to use biometrics.
And then in the callback you would do
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
super.onAuthenticationError(errorCode, errString)
Log.d(TAG, "$errorCode :: $errString")
if(errorCode == BiometricPrompt.ERROR_NEGATIVE_BUTTON) {
loginWithAppAccountCredentials() // Because negative button says use application/account password
}
}
Also when your user clicks the login button in your UI, your onClick could look like this:
override fun onClick(view: View) {
val promptInfo = createPromptInfo()
if (BiometricManager.from(context)
.canAuthenticate() == BiometricManager.BIOMETRIC_SUCCESS) {
biometricPrompt.authenticate(promptInfo, cryptoObject)
} else {
loginWithAppAccountCredentials()
}
}

Twilio Video Android SDK disconnects after participant lost connection

I've implemented Android client using Twilio SDK to do video calls. It works as expected but I found an edge case which I can not figure out how to fix. Here is essence of video call code:
val connectionOptions = ConnectOptions.Builder(accessToken)
.audioTracks(listOf(audioManager.getLocalAudioTrack()))
.roomName(roomId)
.build()
val roomListener = RoomListener()
Video.connect(androidContext, connectOptions, roomListener)
class RoomEventListener : Room.Listener {
override fun onParticipantDisconnected(room: Room, remoteParticipant: RemoteParticipant) {
// remove participant from the screen, unsubscribe from events
}
override fun onConnectFailure(room: Room, twilioException: TwilioException) {
exitScreenWithErrorMessage(R.string.video_consult_room_connection_error)
}
override fun onReconnected(room: Room) {
_shouldShowReconnectionActivity.value = false
}
override fun onParticipantConnected(room: Room, remoteParticipant: RemoteParticipant) {
onRemoteParticipantConnected(remoteParticipant)
}
override fun onConnected(room: Room) {
_shouldShowConnectionActivity.value = false
this#VideoCallViewModel.room = room
room.remoteParticipants.forEach { onRemoteParticipantConnected(it) }
determineMainParticipant()
onLocalParticipantConnected(room)
}
override fun onDisconnected(room: Room, twilioException: TwilioException?) {
exitVideoConsultScreen()
}
override fun onReconnecting(room: Room, twilioException: TwilioException) {
_shouldShowReconnectionActivity.value = true
}
}
Test case:
Bob joins video call using Android phone
Jane joins same video call from any device (iOS, web, Android)
When Jane loses connection (i.e. turn off internet)
Then Bob sees for 1-2 minutes reconnecting (programmatically "onReconnecting" callback triggered)
[Actual] And Bob disconnected from room (in logs I see Media connection failed or Media activity ceased with error code 53405)
[Expected] Bob stays at the room.
I'm not sure why under such conditions Android client has been disconnected (We tested it on different devices with Android 8/9).
Couple more details:
If Jane exits room using "End call" button (so room.disconnect() code from Twilio SDK has been called) then Bob stays in the room.
When Bob using iOS device (implementation of iOS and Android quite the same) then described use case passes.
We tried 5.0.1 and 5.1.0 version of com.twilio:video-android library.
I noticed Known Issue for Android Twilio Video library in Release notes and I'm not sure can it affects described use case or not:
Unpublishing and republishing a LocalAudioTrack or LocalVideoTrack might not be seen by Participants. As a result, tracks published after a Room.State.RECONNECTED event might not be subscribed to by a RemoteParticipant.
I opened issue on twilio github repo https://github.com/twilio/video-quickstart-android/issues/454 - and this is expected behaviour for twilio video sdk 5.x+. Both for Android and iOS sdks.

How to check user's Bio-Metric Preference through Biometric api?

As we all know, from android 9.0, android introduced BiometricPrompt Api to provide standard authentication experience across growing range of biometric sensors (E.g Fingerprint,Face ID etc).
Now with this new BiometricPrompt Api user can get authenticated via fingerprint, face scanner or iris scanned (depend on their biometric preference). BiometricPrompt api will take care of this and it will notify us via various callbacks.
Below is my code to display Biometric Prompt.
biometricPrompt = new BiometricPrompt.Builder(context)
.setTitle("FingerPrint Authentication")
.setSubtitle("Login via Fingerprint")
.setDescription("Touch Fingerprint Sensor")
.setNegativeButton("Cancel", context.getMainExecutor(),
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
Log.d(TAG,"Cancelled");
}
})
.build();
Now if you see my code, i am setting title as a Fingerprint Authentication. Now in device setting, if user had set a Biometric Preference as a Face ID instead of FingerPrint then this biometricPrompt will authenticate user via faceID and fingerprint sensor wont work even if user keep touching sensor. This would create confusion as Biometric title is saying that "Fingerprint authentication" and user is actually getting authenticated via faceID
Is there any way by which we can know what Biometric preference user has selected (e.g Fingerprint or FaceID)? So based upon that preference i can show appropriate message on BiometricPrompt so user wont get confused.
I already explored all api from BiometricPrompt but could find anything related to BiometricPreference.
Any help would be highly appreciated.
While not a perfect solution, you can use the PackageManager API to determine whether a device has the authenticator hardware, e.g.:
if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FACE))
{
}
I've created a helper class as follows:
class BiometricAuthenticator
{
public enum BiometricType
{
FINGERPRINT,
FACE,
IRIS,
NONE
}
public static boolean hasBiometricAuthenticator(Context context)
{
int biometry = BiometricManager.BIOMETRIC_ERROR_UNSUPPORTED;
if (VERSION.SDK_INT >= 30)
biometry = BiometricManager.from(context).canAuthenticate(Authenticators.BIOMETRIC_STRONG | Authenticators.BIOMETRIC_WEAK);
else
biometry = BiometricManager.from(context).canAuthenticate();
switch (biometry)
{
case BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE:
case BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED:
case BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE:
return (false);
case BiometricManager.BIOMETRIC_SUCCESS:
return true;
}
return (false);
}
/**
* biometricType()
*
* returns type of biometry supported
*/
public static BiometricType biometricType(Context context)
{
if (VERSION.SDK_INT < 23)
return BiometricType.NONE;
PackageManager packageManager = context.getPackageManager();
// SDK 29 adds FACE and IRIS authentication
if (VERSION.SDK_INT >= 29)
{
if (packageManager.hasSystemFeature(PackageManager.FEATURE_FACE))
return BiometricType.FACE;
if (packageManager.hasSystemFeature(PackageManager.FEATURE_IRIS))
return BiometricType.IRIS;
if (packageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT))
return BiometricType.FINGERPRINT;
return BiometricType.NONE;
}
// SDK 23-28 offer FINGERPRINT only
return (packageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT) ? BiometricType.FINGERPRINT : BiometricType.NONE);
}
}
This allows you to determine if a biometric authenticator is present (hasBiometricAuthenticator), and if so, return the type of authenticator as a BiometricType enum.
A device could theoretically have multiple authenticators, and biometricType() will return FACE, IRIS, then FINGERPRINT in that order of preference on API30+ devices.
Hopefully Google will expose better API in the future, but these tricks will at least help get appropriate prompts on the dialog
There is no mean of knowing this type of information for now, an issue had been opened last year to ask for it (https://issuetracker.google.com/issues/111315641). As Android tried to simplify the path for developer to implement authentication in their apps, there is a lack of options in the BiometricPrompt implementation (see the Android document for BiometricPrompt implementation).
In your case you can simply change your title String to "Biometric Authentication", and so with the other Strings. For an example see the blog posts pointed to below.
Your code might look as follows. But I'd also recommend you use the strings.xml resource file instead of hard-coding your Strings in the code. For example, in the future, you may want translation services.
biometricPrompt = new BiometricPrompt.Builder(context)
.setTitle("Biometric Authentication")
.setSubtitle("Login via biometrics")
.setDescription("Use the Biometrics Sensor")
.setNegativeButton("Cancel", context.getMainExecutor(),
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
Log.d(TAG,"Cancelled");
}
})
.build();
More broadly, the privacy implications must be evaluated before the API team makes a decision whether developers should know a user's biometric preferences. It's really not clear why a developer should need this information. Two blog posts have been published that touch on the subject of Strong vs Weak biometrics and how to implement them (blog one, blog two). Beyond that distinction (i.e. Strong vs Weak), what form-factors a user prefers or ends up using doesn't seem pertinent.
In Android R was added a method called setAllowedAuthenticators
public BiometricPrompt.Builder setAllowedAuthenticators (int authenticators)
Optional: Specifies the type(s) of authenticators that may be invoked by BiometricPrompt to authenticate the user. Available authenticator types are defined in Authenticators and can be combined via bitwise OR. Defaults to:
Authenticators#BIOMETRIC_WEAK for non-crypto authentication, or
Authenticators#BIOMETRIC_STRONG for crypto-based authentication.
If this method is used and no authenticator of any of the specified types is available at the time BiometricPrompt#authenticate(...) is called, authentication will be canceled and AuthenticationCallback#onAuthenticationError(int, CharSequence) will be invoked with an appropriate error code.
This method should be preferred over setDeviceCredentialAllowed(boolean) and overrides the latter if both are used. Using this method to enable device credential authentication (with Authenticators#DEVICE_CREDENTIAL) will replace the negative button on the prompt, making it an error to also call setNegativeButton(java.lang.CharSequence, java.util.concurrent.Executor, android.content.DialogInterface.OnClickListener).
authenticators
A bit field representing all valid authenticator types that may be
invoked by the prompt. Value is either 0 or a combination of
BiometricManager.Authenticators.BIOMETRIC_STRONG,
BiometricManager.Authenticators.BIOMETRIC_WEAK, and
BiometricManager.Authenticators.DEVICE_CREDENTIAL

Categories

Resources