Android7.0指纹服务FingerprintService实例介绍

指纹服务是Android系统中一个较为简单的服务(相比于AMS,WMS等),也比较独立,功能上包括几点

  • 指纹的录入与删除
  • 指纹认证
  • 指纹的安全策略(错误次数判定)

和其他的system service 一样,应用程序通过FingerprintManager实现与FingerprintService的通信,除了上面所说的功能之外,FingerprintManager提供了一些别的的接口,重要的接口都会要求系统级别的权限,并且也不是公开的api(指纹的录入,删除,重命名,重置错误计数等)

 /**
  * Obtain the list of enrolled fingerprints templates.
  * @return list of current fingerprint items
  *
  * @hide
  */
 @RequiresPermission(USE_FINGERPRINT)
 public List<Fingerprint> getEnrolledFingerprints(int userId) {
  if (mService != null) try {
   return mService.getEnrolledFingerprints(userId, mContext.getOpPackageName());
  } catch (RemoteException e) {
   throw e.rethrowFromSystemServer();
  }
  return null;
 }
 /**
  * @hide
  */
 @RequiresPermission(allOf = {
   USE_FINGERPRINT,
   INTERACT_ACROSS_USERS})
 public boolean hasEnrolledFingerprints(int userId) {
  if (mService != null) try {
   return mService.hasEnrolledFingerprints(userId, mContext.getOpPackageName());
  } catch (RemoteException e) {
   throw e.rethrowFromSystemServer();
  }
  return false;
 }
 /**
  * Determine if fingerprint hardware is present and functional.
  *
  * @return true if hardware is present and functional, false otherwise.
  */
 @RequiresPermission(USE_FINGERPRINT)
 public boolean isHardwareDetected() {
  if (mService != null) {
   try {
    long deviceId = 0; /* TODO: plumb hardware id to FPMS */
    return mService.isHardwareDetected(deviceId, mContext.getOpPackageName());
   } catch (RemoteException e) {
    throw e.rethrowFromSystemServer();
   }
  } else {
   Log.w(TAG, "isFingerprintHardwareDetected(): Service not connected!");
  }
  return false;
 }

FingerprintService的启动过程

FingerprintService在system server中创建并初始化,当检测到手机支持指纹功能的时候就会启动这个service

...
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
  mSystemServiceManager.startService(FingerprintService.class);
 }
...

FingerprintService在初始化后会建立和HAL层的通信,即连接到fingerprintd,拿到用于通信的IFingerprintDaemon对象(binder)

public void onStart() {
  publishBinderService(Context.FINGERPRINT_SERVICE, new FingerprintServiceWrapper());
  IFingerprintDaemon daemon = getFingerprintDaemon();
  listenForUserSwitches();
 }
public IFingerprintDaemon getFingerprintDaemon() {
  if (mDaemon == null) {
   mDaemon = IFingerprintDaemon.Stub.asInterface(ServiceManager.getService(FINGERPRINTD));
   if (mDaemon != null) {
    try {
     mDaemon.asBinder().linkToDeath(this, 0);
     mDaemon.init(mDaemonCallback);
     mHalDeviceId = mDaemon.openHal();
     if (mHalDeviceId != 0) {
      updateActiveGroup(ActivityManager.getCurrentUser(), null);
     } else {
      Slog.w(TAG, "Failed to open Fingerprint HAL!");
      MetricsLogger.count(mContext, "fingerprintd_openhal_error", 1);
      mDaemon = null;
     }
    } catch (RemoteException e) {
     Slog.e(TAG, "Failed to open fingeprintd HAL", e);
     mDaemon = null; // try again later!
    }
   } else {
    Slog.w(TAG, "fingerprint service not available");
   }
  }
  return mDaemon;
 }

本质上来说,除去安全相关的策略外,指纹的功能是依赖硬件实现的,FingerprintService也只是充当了framework java层与native层的消息传递者罢了,所以指纹的识别,录入和监听都是向fingerprintd发送命令和获取相应的结果

指纹监听认证过程

以指纹认证为例,介绍这一过程,录入和删除的过程和认证类似,不重复描述

FingerprintManager

public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
   int flags, @NonNull AuthenticationCallback callback, Handler handler, int userId) {
  if (callback == null) {
   throw new IllegalArgumentException("Must supply an authentication callback");
  }
  if (cancel != null) {
   if (cancel.isCanceled()) {
    Log.w(TAG, "authentication already canceled");
    return;
   } else {
    cancel.setOnCancelListener(new OnAuthenticationCancelListener(crypto));
   }
  }
  if (mService != null) try {
   useHandler(handler);
   mAuthenticationCallback = callback;
   mCryptoObject = crypto;
   long sessionId = crypto != null ? crypto.getOpId() : 0;
   mService.authenticate(mToken, sessionId, userId, mServiceReceiver, flags,
     mContext.getOpPackageName());
  } catch (RemoteException e) {
   Log.w(TAG, "Remote exception while authenticating: ", e);
   if (callback != null) {
    // Though this may not be a hardware issue, it will cause apps to give up or try
    // again later.
    callback.onAuthenticationError(FINGERPRINT_ERROR_HW_UNAVAILABLE,
      getErrorString(FINGERPRINT_ERROR_HW_UNAVAILABLE));
   }
  }
 }

可以看到,最终仍然是向FingerprintService发送消息,但是开启指纹认证的函数传入了两个比较重要的参数,一个是CancellationSignal对象,用于取消指纹认证,另一个是指纹认证的回调对象AuthenticationCallback

public static abstract class AuthenticationCallback {
  public void onAuthenticationError(int errorCode, CharSequence errString) { }
  public void onAuthenticationHelp(int helpCode, CharSequence helpString) { }
  public void onAuthenticationSucceeded(AuthenticationResult result) { }
  public void onAuthenticationFailed() { }
  public void onAuthenticationAcquired(int acquireInfo) {}
 };

看函数名称也能知道其功能,他们分别代表了指纹认证时的回调结果(成功,失败,检测到指纹,认证异常等),参数包含了具体的信息,这些信息在FingerprintManager中都有对应的常量定义,有兴趣可以查看代码

FingerprintService

public void authenticate(final IBinder token, final long opId, final int groupId,
    final IFingerprintServiceReceiver receiver, final int flags,
    final String opPackageName) {
   final int callingUid = Binder.getCallingUid();
   final int callingUserId = UserHandle.getCallingUserId();
   final int pid = Binder.getCallingPid();
   final boolean restricted = isRestricted();
   mHandler.post(new Runnable() {
    @Override
    public void run() {
     if (!canUseFingerprint(opPackageName, true /* foregroundOnly */,
       callingUid, pid)) {
      if (DEBUG) Slog.v(TAG, "authenticate(): reject " + opPackageName);
      return;
     }
     MetricsLogger.histogram(mContext, "fingerprint_token", opId != 0L ? 1 : 0);
     // Get performance stats object for this user.
     HashMap<Integer, PerformanceStats> pmap
       = (opId == 0) ? mPerformanceMap : mCryptoPerformanceMap;
     PerformanceStats stats = pmap.get(mCurrentUserId);
     if (stats == null) {
      stats = new PerformanceStats();
      pmap.put(mCurrentUserId, stats);
     }
     mPerformanceStats = stats;
     startAuthentication(token, opId, callingUserId, groupId, receiver,
       flags, restricted, opPackageName);
    }
   });
  }

前面会有对包名,userid以及应用进程是否在在前台的检查,继续看

private void startAuthentication(IBinder token, long opId, int callingUserId, int groupId,
    IFingerprintServiceReceiver receiver, int flags, boolean restricted,
    String opPackageName) {
  updateActiveGroup(groupId, opPackageName);
  if (DEBUG) Slog.v(TAG, "startAuthentication(" + opPackageName + ")");
  AuthenticationClient client = new AuthenticationClient(getContext(), mHalDeviceId, token,
    receiver, mCurrentUserId, groupId, opId, restricted, opPackageName) {
   @Override
   public boolean handleFailedAttempt() {
    mFailedAttempts++;
    if (mFailedAttempts == MAX_FAILED_ATTEMPTS) {
     mPerformanceStats.lockout++;
    }
    if (inLockoutMode()) {
     // Failing multiple times will continue to push out the lockout time.
     scheduleLockoutReset();
     return true;
    }
    return false;
   }
   @Override
   public void resetFailedAttempts() {
    FingerprintService.this.resetFailedAttempts();
   }
   @Override
   public void notifyUserActivity() {
    FingerprintService.this.userActivity();
   }
   @Override
   public IFingerprintDaemon getFingerprintDaemon() {
    return FingerprintService.this.getFingerprintDaemon();
   }
  };
  if (inLockoutMode()) {
   Slog.v(TAG, "In lockout mode; disallowing authentication");
   // Don't bother starting the client. Just send the error message.
   if (!client.onError(FingerprintManager.FINGERPRINT_ERROR_LOCKOUT)) {
    Slog.w(TAG, "Cannot send timeout message to client");
   }
   return;
  }
  startClient(client, true /* initiatedByClient */);
 }

AuthenticationClient继承自ClientMonitor,用于处理指纹认证相关的功能事务,ClientMonitor的其他子类如RemovalMonior,EnrollMonitor也是如此,ClientMonitor会直接与fingerprintd通信,其核心是调用其start()或stop()方法,
对于AuthenticationClient而言

private void startClient(ClientMonitor newClient, boolean initiatedByClient) {
  ClientMonitor currentClient = mCurrentClient;
  if (currentClient != null) {
   if (DEBUG) Slog.v(TAG, "request stop current client " + currentClient.getOwnerString());
   currentClient.stop(initiatedByClient);
   mPendingClient = newClient;
   mHandler.removeCallbacks(mResetClientState);
   mHandler.postDelayed(mResetClientState, CANCEL_TIMEOUT_LIMIT);
  } else if (newClient != null) {
   mCurrentClient = newClient;
   if (DEBUG) Slog.v(TAG, "starting client "
     + newClient.getClass().getSuperclass().getSimpleName()
     + "(" + newClient.getOwnerString() + ")"
     + ", initiatedByClient = " + initiatedByClient + ")");
   newClient.start();
  }
 }
public int start() {
  IFingerprintDaemon daemon = getFingerprintDaemon();
  if (daemon == null) {
   Slog.w(TAG, "start authentication: no fingeprintd!");
   return ERROR_ESRCH;
  }
  try {
   final int result = daemon.authenticate(mOpId, getGroupId());
   if (result != 0) {
    Slog.w(TAG, "startAuthentication failed, result=" + result);
    MetricsLogger.histogram(getContext(), "fingeprintd_auth_start_error", result);
    onError(FingerprintManager.FINGERPRINT_ERROR_HW_UNAVAILABLE);
    return result;
   }
   if (DEBUG) Slog.w(TAG, "client " + getOwnerString() + " is authenticating...");
  } catch (RemoteException e) {
   Slog.e(TAG, "startAuthentication failed", e);
   return ERROR_ESRCH;
  }
  return 0; // success
 }

向底层发送认证命令后就只需要等待认证结果就可以了,前面我们说到在初始化的时候会建立与fingerprintd的通信,其核心是下面这行代码

mDaemon.init(mDaemonCallback);

mDaemonCallback是一个binder对象,接受来自底层的结果,然后通过FingerprintService和FingerManager一层层把结果发送到应用程序中去。

8.0的一些变化

8.0上的fingerprintd变化很大,甚至都不叫fingerprintd了,当然这是native层的东西,这里不讨论,对于FingerprintService而言,一个显著的变化是安全策略的调整

  • 8.0之前,指纹只能错误5次,达到5次时会禁止指纹认证,同时开启30秒倒计时,等待结束后重置错误计数,继续认证
  • 8.0之后,依然是每错误5次就会倒计时30秒,然而30秒结束后错误计数并不会被清空,8.0上加入了最大20次的限制,累计错误20次之后就无法使用指纹认证功能了,只能用密码的方式才能重置错误计数
private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED = 5;
private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT = 20;
private int getLockoutMode() {
  if (mFailedAttempts >= MAX_FAILED_ATTEMPTS_LOCKOUT_PERMANENT) {
   return AuthenticationClient.LOCKOUT_PERMANENT;
  } else if (mFailedAttempts > 0 && mTimedLockoutCleared == false &&
    (mFailedAttempts % MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED == 0)) {
   return AuthenticationClient.LOCKOUT_TIMED;
  }
  return AuthenticationClient.LOCKOUT_NONE;
 }

总结

以上所述是小编给大家介绍的Android7.0指纹服务FingerprintService实例介绍,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

您可能感兴趣的文章:

  • Android 获取系统语言的实例(兼容7.0)
  • Android安装apk文件并适配Android 7.0详解
  • Android 7.0中新签名对多渠道打包的影响详解
  • 详解Android 7.0 Settings 加载选项
  • Android 7.0中拍照和图片裁剪适配的问题详解
(0)

相关推荐

  • Android安装apk文件并适配Android 7.0详解

    Android安装apk文件并适配Android 7.0详解 首先在AndroidManifest.xml文件,activity同级节点注册provider: <provider android:name="android.support.v4.content.FileProvider" android:authorities="${applicationId}.file_provider" android:exported="false"

  • Android 7.0中新签名对多渠道打包的影响详解

    老签名多渠道打包原理 前言 由于Android7.0发布了新的签名机制,加强了签名的加固,导致在新的签名机制下无法通过美团式的方式再继续打多渠道包了.不过在说新的签名机制对打包方案的 影响和为什么会影响我们原有的打包机制之前,需要先简单理解下打包原理和签名在整个打包过程中的作用. Android打包流程 Android打包过程大致如图所示,整个流程就是将Java代码,资源文件以及第三方库整合成一个Apk文件,并对整合后的文件进行签名和优化对齐.整个过程可以简 单分为以下几个步骤: 资源预编译 为

  • 详解Android 7.0 Settings 加载选项

    先写在前面,这说的Settings加载选项是指Settings这个应用显示在主界面的选项,这个修改需要对系统源码进行修改. Android 7.0 Settings顶部多了一个建议选项,多了个侧边栏,操作更加便捷了.       原生7.0主界面                                                          原生7.0侧边栏 Android 6.0 之前做Android 6.0开发的,都会了解到6.0的Settings加载选项是通过加载dash

  • Android 获取系统语言的实例(兼容7.0)

    前言 获取系统当前语言是一个比较常用的功能,在 Android 7.0 系统上旧函数获取到的当前系统语言并不正确,或者说从 Android 7.0 起,Android 系统语言的规则变了. 下面是未适配 Android 7.0 的代码: //获取 Locale 的方式有二 //方式一 Locale locale = getResources().getConfiguration().locale; //方式二 Locale locale = Locale.getDefault(); //获取当前

  • Android 7.0中拍照和图片裁剪适配的问题详解

    前言 Android 7.0系统发布后,拿到能升级的nexus 6P,就开始了7.0的适配.发现在Android 7.0以上,在相机拍照和图片裁剪上,可能会碰到以下一些错误: Process: com.yuyh.imgsel, PID: 22995 // 错误1 android.os.FileUriExposedException: file:///storage/emulated/0/Android/data/com.yuyh.imgsel/cache/1486438962645.jpg ex

  • Android7.0指纹服务FingerprintService实例介绍

    指纹服务是Android系统中一个较为简单的服务(相比于AMS,WMS等),也比较独立,功能上包括几点 指纹的录入与删除 指纹认证 指纹的安全策略(错误次数判定) 和其他的system service 一样,应用程序通过FingerprintManager实现与FingerprintService的通信,除了上面所说的功能之外,FingerprintManager提供了一些别的的接口,重要的接口都会要求系统级别的权限,并且也不是公开的api(指纹的录入,删除,重命名,重置错误计数等) /** *

  • Android6.0指纹识别开发实例详解

    Android6.0指纹识别开发实例详解 最近在做android指纹相关的功能,谷歌在android6.0及以上版本对指纹识别进行了官方支持.当时在FingerprintManager和FingerprintManagerCompat这两个之间纠结,其中使用FingerprintManager要引入com.android.support:appcompat-v7包,考虑到包的大小,决定使用v4兼容包FingerprintManagerCompat来实现. 主要实现的工具类FingerprintU

  • 使用C#创建Windows服务的实例代码

    本文介绍了使用C#创建Windows服务的实例代码,分享给大家 一.开发环境 操作系统:Windows 10 X64 开发环境:VS2015 编程语言:C# .NET版本:.NET Framework 4.0 目标平台:X86 二.创建Windows Service 1.新建一个Windows Service,并将项目名称改为"MyWindowsService",如下图所示: 2.在解决方案资源管理器内将Service1.cs改为MyService1.cs后并点击"查看代码&

  • SpringBoot2.0整合WebSocket代码实例

    这篇文章主要介绍了SpringBoot2.0整合WebSocket代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 之前公司的某个系统为了实现推送技术,所用的技术都是Ajax轮询,这种方式浏览器需要不断的向服务器发出请求,显然这样会浪费很多的带宽等资源,所以研究了下WebSocket,本文将详细介绍下. 一.什么是WebSocket? WebSocket是HTML5开始提供的一种在单个TCP连接上进行全双工通讯的协议,能更好的节省服务器资

  • MySQL8.0.20单机多实例部署步骤

    目录 0.环境需要 1.安装步骤 1.下载解压安装的mysql安装包文件 2.上传解压缩(我这里的上传为:xhell,当然也可使用其他方式) 3.在mysql目录创建创建数据文件存放路径并赋权 4.配置my.cnf文件 5.初始化各实例数据库 6.设置msyql环境变量 7.启动与查看 mysql 服务(需指定配置文件) 8.设置远程访问密码 9.防火墙开启访问端口(获取关闭防火墙) 0.环境需要 1.准备Linux环境(系统:CentOS7)2.准备MySQL安装包(版本:8.0.20)3.安

  • Spring Cloud OpenFeign实例介绍使用方法

    目录 一. OpenFeign概述 二. 使用步骤 2.1 feign接口模块 2.1.1依赖配置 2.1.2编写FeignClient的接口, 并加@FeignCleint 注解 2.2 消费端使用fegin接口 2.2.1在消费者端添加feign接口依赖 2.2.2在消费者端配置文件中添加 feign.client.url 2.2.3在消费者端启动类中添加@EnableFeignClients 2.2.4在消费者端使用fegin接口 2.3 测试 一. OpenFeign概述 OpenFei

  • Android传感器使用实例介绍

    目录 传感器 磁场传感器 加速度传感器 方向传感器 传感器 1.mainActivity 实现SensorEventListerner 方法 2. 定义:SensorManage 对象 3. 在重写的onResum 方法中 为重力和光线传感器注册监听器 sensorManage.registerListener() 4. 在实现的onSensorChanged 方法中 获取传感器的类型 5. 借助硬件 从软件接口上使用光线传感器,根据外界屏幕亮度 修改屏幕亮度 package com.mingr

  • 通过PHP简单实例介绍文件上传

    php文件上传的简单例子,获取文件名称.类型.大小等相关信息,完成文件的上传,供大家学习参考. 1.上传文件的代码: code <?php //判断临时文件存放路径是否包含用户上传的文件 if(is_uploaded_file($_FILES["uploadfile"]["tmp_name"])){ //为了更高效,将信息存放在变量中 $upfile=$_FILES["uploadfile"];//用一个数组类型的字符串存放上传文件的信息

  • Android7.0 工具类:DiffUtil详解

    一 概述 DiffUtil是support-v7:24.2.0中的新工具类,它用来比较两个数据集,寻找出旧数据集->新数据集的最小变化量. 说到数据集,相信大家知道它是和谁相关的了,就是我的最爱,RecyclerView. 就我使用的这几天来看,它最大的用处就是在RecyclerView刷新时,不再无脑mAdapter.notifyDataSetChanged(). 以前无脑mAdapter.notifyDataSetChanged()有两个缺点: 1.不会触发RecyclerView的动画(删

  • Android7.0上某些PopuWindow出现显示位置不正确问题的解决方法

    本文实例讲述了Android7.0上某些PopuWindow出现显示位置不正确问题的解决方法.分享给大家供大家参考,具体如下: 情景描述: 在andorid7.0及以上系统,点击某个view,本来期待有一个Popuwindow在该view下面弹出(调用PopuWindow.showAsDropDown(view)方法)但结果PopuWindow却弹出在view上方,顶在系统状态栏下面. 原因分析: 在android7.0上,如果不主动约束PopuWindow的大小,比如,设置布局大小为 MATC

随机推荐