Time for action – using the fingerprint sensor

In order to support the fingerprint sensor, the Android platform has introduced a new system service, which is called the Finger Print Service, and it can be accessed using the instance of FingerprintManager. It provides all the necessary APIs to use the fingerprint sensor. In the following example, we will use the fingerprint sensor to authenticate the user. In order to make this example work, the Android device should have a fingerprint sensor, and it also should have set up or enrolled the user's fingerprint using the security settings. We also need to use two components of security (Keystore and Cipher) to use the fingerprint authentication API. Fingerprint sensor APIs require install time permission in the AndroidManifest.xml file (android.permission.USE_FINGERPRINT) and also runtime permission before using them. Now, let's look at the implementation details:

  1. Inside the onCreate() method of FingerPrintActivity, we initiated the object of FingerprintManager using getSystemService(FINGERPRINT_SERVICE). The checkFingerPrintConditions() method is responsible for checking the necessary conditions for the Fingerprint sensor to work. This method is invoked from onCreate() and also from the initiateFingerPrintSensor() method. We will discuss the checkFingerPrintConditions() method in the next section:

            public class FingerPrintActivity extends Activity { 
     
              private static final int FINGERPRINT_PERMISSION_REQUEST_CODE = 0; 
              private FingerprintManager mFingerprintManager; 
              //Alias for our key in the Android Key Store 
              private static final String KEY_NAME = "my_key"; 
              private KeyStore mKeyStore; 
              private KeyGenerator mKeyGenerator; 
              private Cipher mCipher; 
              private CancellationSignal mCancellationSignal; 
              private Dialog mFingerPrintDialog; 
     
              @Override 
              protected void onCreate(Bundle savedInstanceState) { 
                super.onCreate(savedInstanceState); 
                setContentView(R.layout.fingerprint_layout); 
                mFingerprintManager = (FingerprintManager)getSystemService
                (FINGERPRINT_SERVICE); 
                //As soon as Activity starts, check for the finger print 
                conditions 
                checkFingerPrintConditions() 
              } 
     
              public void initiateFingerPrintSensor(View v) { 
                //Called from Layout button 
                checkFingerPrintConditions(); 
              } 
    
  2. There are three mandatory conditions for the fingerprint sensor to work; they are checked inside the checkFingerPrintConditions() method. The first condition is to check if the fingerprint sensor hardware is present on the phone, which is done by the isHardwareDetected() method of FingerprintManager. The second condition is to check whether the user has enrolled or set up his fingerprint; this is done by using the hasEnrolledFingerprints() method. The third and final condition is to check whether the user has given runtime permission to use the fingerprint sensor. This is done using the checkSelfPermission() method of the ContextCompat class. If the user has not given the runtime permission, then we ask for it using the requestPermissions() method. This will open a runtime permission dialog, and when the user allows it, we will receive a callback in the onRequestPermissionsResult() method. Once all the conditions are satisfied and we have the required permission, we invoke the showFingerPrintDialog() method, which will initiate the fingerprint authentication process explained in the next section. If any of the conditions fail, we show the relevant message using the showAlertDialog() method. This method simply presents the user with the passed title and message:

            public void checkFingerPrintConditions() { 
     
              if(mFingerprintManager.isHardwareDetected()) { 
                if(mFingerprintManager.hasEnrolledFingerprints()) { 
                  if(ContextCompat.checkSelfPermission(this, 
                  Manifest.permission.USE_FINGERPRINT)!= 
                  PackageManager.PERMISSION_GRANTED) { 
                    //Requesting runtime finger print permission 
                    requestPermissions(new String[]
                    {Manifest.permission.USE_FINGERPRINT}, 
                    FINGERPRINT_PERMISSION_REQUEST_CODE); 
                  } else { 
                    //After all 3 conditions are met, then show FingerPrint 
                    Dialog 
                    showFingerPrintDialog(); 
                  } 
                } else { 
                  showAlertDialog("Finger Print Not Registered!", "Go to 
                  'Settings -> Security -> Fingerprint' and register at least
                  one fingerprint"); 
                } 
              } else { 
                showAlertDialog("Finger Print Sensor Not Found!", "Finger Print 
                Sensor could not be found on your phone."); 
              } 
            } 
     
            @Override 
            public void onRequestPermissionsResult(int requestCode, String[] 
            permissions, int[] state) { 
     
              //show FingerPrint Dialog, when runtime permission is granted 
              if (requestCode == FINGERPRINT_PERMISSION_REQUEST_CODE 
              && state[0] == PackageManager.PERMISSION_GRANTED) { 
     
                showFingerPrintDialog(); 
              } 
            } 
     
            public void showAlertDialog(String title, String message){ 
              new android.app.AlertDialog.Builder(this).setTitle(title)     
              .setMessage(message).setIcon(android.R.drawable.ic_dialog_alert) 
              .setPositiveButton("Cancel", new DialogInterface
              .OnClickListener()                
              { 
                 public void onClick(DialogInterface dialog, int whichButton) 
                 { 
                    dialog.dismiss(); 
                 }}) 
                .show(); 
              } 
    
  3. The showFingerPrintDialog() method performs two major tasks: the first task is to initiate the required fingerprint APIs and settings, which is done by calling the initFingerPrintSettings() method, and the second task is to show a custom dialog, which asks the user to place their finger on the fingerprint sensor for authentication. If any of the required APIs or settings fail, then we show the relevant error message to the user. The initFingerPrintSettings() method initiates the KeystoreCipher, and object of the CancellationSignal class. We will discuss Keystore and Cipher in the next section. 

    The cancel() method of the CancellationSignal class instructs the authentication API to stop sensing for the fingerprint. Once all three (Keystore,Cipher, and CancellationSignal) are initiated successfully, we invoke the authenticate() method of FingerprintManager. This wakes up the fingerprint sensor hardware, and it starts sensing for a fingerprint. This is the time that the user has to place his finger on the fingerprint sensor for authentication. Generally, the fingerprint sensor declares the authentication result within a second. The result callback of fingerprint authentication is done through the object of the FingerprintManager.AuthenticationCallback class, which is discussed in the last section of this example:

            public void showFingerPrintDialog() { 
              //First Initialize the FingerPrint Settings 
              if(initFingerPrintSettings()) 
              { 
                //Init Custom FingerPrint Dialog from xml 
                mFingerPrintDialog = new Dialog(this); 
                View view = LayoutInflater.from(this).inflate
                (R.layout.fingerpring_dialog, null, false); 
                mFingerPrintDialog.setContentView(view); 
                Button cancel = (Button) view.findViewById(R.id.cancelbutton); 
                cancel.setOnClickListener(new View.OnClickListener() { 
                  @Override 
                  public void onClick(View arg0) { 
                    mCancellationSignal.cancel(); 
                    mFingerPrintDialog.dismiss(); 
                  } 
                }); 
     
                //Stops the cancelling of the fingerprint dialog 
                //by back press or touching accidentally on screen 
                mFingerPrintDialog.setCanceledOnTouchOutside(false); 
                mFingerPrintDialog.setCancelable(false); 
                mFingerPrintDialog.show(); 
              } 
              else 
              { 
                showAlertDialog("Error!", "Error in initiating Finger Print 
                Cipher or Key!"); 
              } 
            } 
     
            public boolean initFingerPrintSettings() { 
     
              //CancellationSignal requests authenticate api to stop scanning 
              mCancellationSignal = new CancellationSignal(); 
              if(initKey() && initCipher()) { 
                mFingerprintManager.authenticate(new 
                FingerprintManager.CryptoObject(mCipher),
                mCancellationSignal, 0, new AuthenticationListener(), null); 
                return true; 
              } else { 
                return false; 
              } 
            } 
    
  4. Inside the initCipher() method, we initialize the Cipher instance with the created key in the initkey() method. It returns true if the initialization was successful and returns false if the lock screen is disabled or reset after the key was generated, or if a fingerprint got enrolled after the key was generated. The initkey() method creates a symmetric key in the Android key store, which can only be used after the user has been authenticated by a fingerprint. The detailed discussion of Cipher and Keystore belongs to security and is out of the scope of this book:

            public boolean initKey() { 
              try { 
                mKeyStore = KeyStore.getInstance("AndroidKeyStore"); 
                mKeyStore.load(null); 
                mKeyGenerator = KeyGenerator.getInstance
                (KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore"); 
                mKeyGenerator.init(new KeyGenParameterSpec.Builder(KEY_NAME, 
                KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT) 
                .setBlockModes(KeyProperties.BLOCK_MODE_CBC) 
                .setUserAuthenticationRequired(true)                 
                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7) 
                .build()); 
                mKeyGenerator.generateKey(); 
                return true; 
              } catch (Exception e) { 
                return false; 
              } 
            } 
     
            public boolean initCipher() { 
              try { 
                mKeyStore.load(null); 
                SecretKey key = (SecretKey) mKeyStore.getKey(KEY_NAME, null); 
                mCipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + 
                "/" + KeyProperties.BLOCK_MODE_CBC + "/" + 
                KeyProperties.ENCRYPTION_PADDING_PKCS7); 
                mCipher.init(Cipher.ENCRYPT_MODE, key); 
                return true; 
              } catch (KeyStoreException | CertificateException | 
              UnrecoverableKeyException | IOException | 
              NoSuchAlgorithmException | InvalidKeyException | 
              NoSuchPaddingException e) { 
                return false; 
              } 
            } 
    
  5. The AuthenticationListener class is responsible for passing the result callback from the FingerprintManager authentication API. Once the authentication is done, the relevant method is called depending on the result of authentication. If the authentication is successful, which means that the recognized fingerprint matches with the original enrolled fingerprint, then we show a success message to the user via the Toast API. In case of failure, which means that the recognized fingerprint doesn't match the originally enrolled fingerprint, we show the relevant failure message. In case of error, which means that there was an error in reading the fingerprint, we show the relevant error message:

            class AuthenticationListener extends 
            FingerprintManager.AuthenticationCallback{ 
     
              @Override 
              public void onAuthenticationError(int errMsgId, CharSequence 
              errString) { 
     
                Toast.makeText(getApplicationContext(), "Authentication 
                Error!", Toast.LENGTH_LONG).show(); 
              } 
     
              @Override 
              public void onAuthenticationHelp(int helpMsgId, CharSequence 
              helpString) { 
              }
     
              @Override 
              public void onAuthenticationFailed() { 
     
                Toast.makeText(getApplicationContext(), "Authentication 
                Failed!", Toast.LENGTH_LONG).show(); 
              } 
     
              @Override 
              public void onAuthenticationSucceeded
              (FingerprintManager.AuthenticationResult result) { 
                 Toast.makeText(getApplicationContext(), "Authentication 
                Success!", Toast.LENGTH_LONG).show(); 
                mFingerPrintDialog.dismiss(); 
              } 
            } 
    
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset