Android7.0指纹服务FingerprintService介绍

时间秒杀一切 提交于 2019-12-28 16:00:30

指纹服务是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;
    }



作者:Lonelyyy
链接:https://www.jianshu.com/p/6c625e4323a1
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!