美文网首页
Android7.0指纹服务FingerprintService

Android7.0指纹服务FingerprintService

作者: Lonelyyy | 来源:发表于2018-01-05 00:25 被阅读0次

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

          本文链接:https://www.haomeiwen.com/subject/rnjxnxtx.html