android手机中指纹识别应用相关功能的讲解

现在很多手机厂商都加入了指纹芯片,相对应的就要开发一系列相配套的指纹相关功能,其中基本功能为应用锁,应用冻结,通过指纹关闭闹钟,通过指纹拍照,通过指纹接听电话,指纹作为密码对文件加密,自定义滑动指纹器件方向启动App,自定义多击打开应用等

一,应用锁

指纹应用锁功能逻辑流程图

应用锁的基本原理是一个应用启动的时候拿到该应用的componentName,在后台进行验证,对需要锁定的应用,在Intent中传入启动应用的packageName,通过Intent在其上启动一个Activity,在Activity中创建一层fragment实现遮挡锁定的效果。在密码验证成功后,覆盖Activity自身调用finish()方法移除自身,进入需要打开的应用。而如果在锁定界面返回,则重写覆盖Activity的onBackPressed()方法,获取ActivityManager将intent传来的packageName关闭。

应用锁的服务详解

android手机中指纹识别应用相关功能的讲解_第1张图片

本功能需要基于服务FPLockService,对打开的Activity进行验证。FPAppLockService服务需要开机启动,但是我们没有采用开机广播启动服务。首先我们可以看一下为什么不采用,如上是开机广播的简单流程。由于开机广播在launcher启动后发出,可能出现开机后未立即启动服务,无法立即锁定。我们在framework/base/services/core/java/android/server/am/ActivityManagerService.java中的startHomeActivityLocked方法中启动FPAppLockService,使得该服务比launcher更早启动

boolean startHomeActivityLocked(int userId, String reason){

    、、、代码省略、、、

    //在这里启动FpLockService服务
    Intent fpService = new Intent();
    fpService.setAction("com.rgk.fp.APP_LOCK_BOOT_SERVICE");
    fpService.setPackage("com.rgk.fpfeature");
    fpService.putExtra("app_lock_cmd",1001);
    mContext.startService(fpService);
}

FPAppLockService服务的基本工作是对当前用户启动的Activity进行判断,并作出响应。但是,怎样快速知道当前用户启动的Activity是什么是一个需要考虑的问题。之前采用的方式是监听Activity栈的变化,当栈顶Activity发生变化时,获取到栈顶Activity的ComponentName,并对ComponentName进行判断检验

private ComponentName getTopActivity(){
    ActivityManager am = (ActivityManager )getSystemService(Context.ACTIVITY_SERVICE);
    List runningTaskInfos = am.getRunningTask(10);
    RunningTaskInfo runningTaskInfo = runningTaskInfos.get(0);
    return runningTaskInfo.topActivity;
}

此方法存在一个弊端,由于只有当栈顶Activity发生变化时才去验证,这样会导致Activity已创建完成才会被发现,不够及时,在能修改到framework层文件时,不建议使用。
我们要尽可能在Activity创建时对Activity进行检验。于是我们找到了frameworks\base\services\core\java\com\android\server\am\ActivityStackSupervisor.java中的startActivityLocked和resumeTopActivitiesLocked方法,这两个方法分别在Activity的OnCreate和onResume方法调用时被执行。为了避免Activity以home键退出后用菜单键能浏览最近任务重新打开锁定应用,我们在frameworks\base\services\core\java\com\android\server\am\ActivityManagerService.java中创建sendResumingActivityBroadcast方法并在resumeTopActivitiesLocked中调用。为什么发送广播方法要在写在另一个类中?因为从ActivityManagerService中,我们能拿到当前正在被创建的Activity的所有信息,包括packageName。通过发送广播,在Intent中塞入packageName,我们可以立即在FPAppLockService中注册广播接收器获取当前创建Activity的packageName,并检测。
在7.0中resumeTopActivitiesLocked的API变为了resumeFocusedStackTopActivityLocked

ActivityStackSupervisor中的resumeTopActivitiesLocked方法
boolean resumeTopActivitiedLocked(ActivityStack targetStack, ActivityRecord target,
Bundle targetOption){
    if(targetStack == null){
        targetStack = mFocusedStack;
    }
    if(target == null){
        target = targetStack.topRunningActivityLocked(null,0);
    }
    mService.sendResumingActivityBroadcast(target == null?null : target.realActivity);


    、、、代码省略、、、

}
ActivityManagerService中的sendResumingActivityBroadcast方法
void sendResumingActivityBroadcast(ComponentName cmp){
    Slog.i(TAG,"sendResumingActivityBroadcast");
    if(cmp == null){
        return;
        }
    String packageName = cmp.getPackageName();
    if(packageName != null){
        Intent resumeIntent = new Intent();
        resumeIntent.setAction("android.intent.action.RESUMING.ACTIVITY");
        resumeIntent.putExtra("packageName",packageName);
        broadcastIntentLocked(null,null,resumeIntent,
            null,null,0,null,null,null,AppOpsManager.OP_NONE,
            null,false,false,MY_PID,Process.SYSTEM_UID,0);
    }
}

当Activity的onResume方法调用并在framework层发出广播之后,我们在FPAppLockService中注册广播接收器对当前Activity信息及时进行检验。同时还注册了熄屏广播接收器,以重置所有应用已解锁标志位

FPAppLockService中的广播接收器
    private BroadcastReceiver mReceiver = new BroadcastReceiver() {

    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if(ACTION_ACTIVITY_RUSUME.equals(action)){
            if(mKeyguardManager.isKeyguardLocked()){    
                //do nothing if isKeyguardLocked
            }else{
                mHandler.sendEmptyMessage(MSG_CHECK);
                setIntent(intent);
            }
            、、、代码省略、、、
        }
FPAppLockService中的check方法
private void check() {
    if (mFingerprintManager == null) {
        mFingerprintManager = (FingerprintManager) getSystemService(Context.FINGERPRINT_SERVICE);
        if (mFingerprintManager.hasEnrolledFingerprints() || mKeyguardManager.isKeyguardSecure()) {
        ComponentName topActivity ;
        String packageName=getIntent().getStringExtra("packageName");
        //Log.d(TAG, "topActivity=" + topActivity);
        Log.d(TAG, "pre-topActivity=" + preTopActivity);
        Log.d(TAG, "lockedApp number:" + lockedApps.size());
        if (preTopActivity == null) {
        }
        for (LockApp app : lockedApps) {
            if (app.packageName.equals(packageName)) {
                Log.d(TAG, "state:"+app.state);
                if (app.state == State.UNLOCKED /*|| isFingerprintSetting(topActivity)
                        || isSettingConfirmLock(topActivity)*/) {
                    mHandler.sendEmptyMessage(MSG_DISMISS);
                } else if (app.state == State.LOCKED) {
                    Log.i(TAG, "lock");
                     Intent intent = new Intent();
                    Log.d(TAG, "show when screen locked:" + lusaifengLock);
                    if (mKeyguardManager.isKeyguardLocked()) {
                    Log.i(TAG, "lock if ");
                    if (lusaifengLock) {                            intent.setClassName("com.rgk.fpfeature",FPLockPreActivity.class.getName());
                            ActivityHandle.getInstance().setCurrentTopActivity(app.packageName);
                            intent.putExtra("isKeyguardLocked", true);
                            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK
                                    | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
    }

由于一个应用有多个Activity,但通常应用只有一个包名,所以我们只需检测包名,并在通过检测后及时更改是否需要锁定的标志位。上述代码中,当我们得知当前应用已经被解锁过,就do nothing。否则启动一个FPLockChooseActivity,并创建FPLockFpFragment,达到锁定应用的目的

android手机中指纹识别应用相关功能的讲解_第2张图片

在FPLockFpFragment中实现FingerprintManager.AuthenticationCallback,重写指纹验证回调监听
class MyCallBack extends FingerprintManager.AuthenticationCallback {
    boolean mSelfCancelled;
    private FingerprintManager fingerPrintManager;
    private CancellationSignal mCancellationSignal;
    TextView prompt;
    Activity mActivity;

    public MyCallBack(FingerprintManager mFingerPrintManager, TextView prompt, Activity mActivity) {
        this.fingerPrintManager = mFingerPrintManager;
        this.prompt = prompt;
        this.mActivity = mActivity;
    }
    public boolean isFingerprintAuthAvailable() {
        return fingerPrintManager.isHardwareDetected()
                && fingerPrintManager.hasEnrolledFingerprints();
    }
    public void startListening(FingerprintManager.CryptoObject cryptoObject) {
        if (!isFingerprintAuthAvailable()) {
            return;
        }
        mCancellationSignal = new CancellationSignal();
        mSelfCancelled = false;
        prompt.setText("");
        fingerPrintManager
                .authenticate(cryptoObject, mCancellationSignal, 0 /* flags */, this, null);
    }
    public void stopListening() {
        if (mCancellationSignal != null) {
            mSelfCancelled = true;
            mCancellationSignal.cancel();
            mCancellationSignal = null;
        }
    }
    @Override
    public void onAuthenticationError(int errorCode, CharSequence errString) {
        super.onAuthenticationError(errorCode, errString);
        Log.d(TAG, "onAuthenticationError:"+errString);
    }
    @Override
    public void onAuthenticationFailed() {
        super.onAuthenticationFailed();
        Log.d(TAG, "onAuthenticationFailed");
        prompt.setText(R.string.call_back_authentication_failed);
    }
    @Override
    public void onAuthenticationSucceeded(AuthenticationResult result) {
        super.onAuthenticationSucceeded(result);
        Log.d(TAG, "onAuthenticationSucceeded");
        prompt.setText("");
        if (getActivity() instanceof FPLockChooseActivity) {
            ((FPLockChooseActivity)getActivity()).unlock();
        } else if (getActivity() instanceof FPRecognizeActivity) {
            ((FPRecognizeActivity)getActivity()).unlock();
        }
    }
    @Override
    public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
        super.onAuthenticationHelp(helpCode, helpString);
        Log.d(TAG, "onAuthenticationHelp:"+helpString);
        }
    }

在FPLockChooseActivity中unlock()方法中finish自身,置定该应用已解锁标志位,在手机未熄屏之前,应用不再被锁定。同时根据intent传递的目的地,跳转到指定的Activity

private void unlock() {
        mHandler.sendEmptyMessage(MSG_DISMISS);
        String needUnlockActivity = ActivityHandle.getInstance().getCurrentTopActivity();
        Log.d(TAG, "unlock, packageName=" + needUnlockActivity);
        if (null != needUnlockActivity && !needUnlockActivity.isEmpty()) {
            for (LockApp app : FPAppLockViewService.lockedApps) {
            if (app.packageName.equals(needUnlockActivity)) {
                    app.state = State.UNLOCKED;
                    ActivityHandle.getInstance().setCurrentTopActivity("");
                    break;
                }
            }
        }
    }


private void dismissLockScreen() {
    Log.d(TAG, "dismissLockScreen");
    if (mLockView != null) {
        if (mWindowManager == null) {
            mWindowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
        }   
        if (isLockScreenShow) {
            mWindowManager.removeView(mLockView);
            mLockView = null;
            isLockScreenShow = false;
        }       
    }
}
重写onBackPressed()方法实现返回键退出当前Activity
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
    switch (event.getKeyCode()) {
    case KeyEvent.KEYCODE_BACK:
        Log.d(TAG, "KEYCODE_BACK"+event);
        ActivityHandle.getInstance().removeAll();
        Intent resumeIntent = new Intent();
        resumeIntent.setAction("com.rgk.fp.APP_LOCK_BOOT_SERVICE");
        resumeIntent.setPackage("com.rgk.fpfeature");
        resumeIntent.putExtra("app_lock_cmd", 1004);
        getContext().startService(resumeIntent);

        Intent backHome = new Intent(Intent.ACTION_MAIN);
        backHome.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        backHome.addCategory(Intent.CATEGORY_HOME);
        getContext().startActivity(backHome);
        int action = event.getAction();
        if (action == KeyEvent.ACTION_UP) {
            if (mOnBackKeyPressedListener != null) {
                mOnBackKeyPressedListener.pressed();
            }
        }
        break;
    default:
        break;
        }
    return super.dispatchKeyEvent(event);
}

至此,APPLock 功能的基本流程便已实现

二,应用冻结

应用冻结功能逻辑流程图

android手机中指纹识别应用相关功能的讲解_第3张图片

Android:setComponetEnabledSetting 组件禁用和隐藏启动图标
public abstract void setApplicationEnabledSetting(String packageName,int newState,int flags)
packageName:应用包名
newState:组件的新状态,可以设置为三个值,如下:
不可用状态:COMPONENT_ENABLED_STATE_DISNABLED
可用状态:COMPONENT_ENABLED_STATE_ENABLED
默认状态:COMPONENT_ENABLED_STATE_DEFAULT
flags:行为标签,值可以是DONT_KILL_APP或者0。0说明杀死包含该组件的APP。
通过PackageManager调用接口方法setApplicationEnabledSetting实现应用的禁用,也就是冻结效果。

三,指纹拍照、关闭闹钟、接听电话

三个功能内容相似,以指纹拍照为例

指纹拍照逻辑流程图

android手机中指纹识别应用相关功能的讲解_第4张图片

先要在framework层的SettingProvide设置好键值

public static final String FP_CAMERA = "fp_camera";

然后就可以在控制的该类里这样获取键值

int allowFingerShutter =    Settings.System.getInt(mContext.getContentResolve,
Settings.System.FP_CAMERA,0);

当allowFingerShutter当为0时关闭指纹拍照功能,为1时打开

四,手势导航

手势导航分为 多击/方向滑动 打开相应的应用

android手机中指纹识别应用相关功能的讲解_第5张图片

该功能通过后台服务启动应用,关键在于怎么知道当前指纹被多击,指纹受方向滑动,在何处对这些进行监听?带着这些问题,我们想到手机已存在的熄屏手势启动应用功能,这个功能也存在同样的启动问题。因此我们找到了在framework层下的PhoneWindowManager类。frameworks\base\services\core\java\com\android\server\policy\PhoneWindowManager.java

对按键进行监听,监听中根据指纹方向启动DirectionStart服务,并在服务中根据数据库存储的数据启动相应的应用

private static final int MSG_DOWN = 1001;
private static final int MSG_SEND = 1002;
private static final long MULTI_CLICK_BUFFER = 500L;

private int clickF11count = 0;
private long clickF11Time = 0l;

、、、代码省略、、、
@Override
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
、、、代码省略、、、
final int keyCode = event.getKeyCode();
 switch (keyCode) {
        case KeyEvent.KEYCODE_DPAD_UP:
        case KeyEvent.KEYCODE_DPAD_DOWN:
        case KeyEvent.KEYCODE_DPAD_LEFT:
        case KeyEvent.KEYCODE_DPAD_RIGHT:
        //*don't* pass this key thru to the current activity
        result &= ~ACTION_PASS_TO_USER;
        startDirectionApp(keyCode, down);
        break;
        、、、代码省略、、、
        }
  }

、、、代码省略、、、
private void startDirectionApp(int keyCode, boolean down) {
    Log.d("Fp.frame", "startDirectionApp, " + down);

    if (!hasFingerprint()) {
        return;
    }

    if (!down) {
        return;
    }
    Intent intent = new Intent(); 
    intent.setAction("com.xxx.directoin.DIRECTION_KEY_DOWN");
    // MyUI, fix EWWJLJ-786
    //intent.setPackage("com.xxx.fingerprint");
    intent.setPackage("com.xxx.fpfeature");
    switch (keyCode) {
        case KeyEvent.KEYCODE_DPAD_UP:
        intent.putExtra("cmd", 1001);
        break;   
        case KeyEvent.KEYCODE_DPAD_DOWN:
        intent.putExtra("cmd", 1002);
        break;
        case KeyEvent.KEYCODE_DPAD_LEFT:
        intent.putExtra("cmd", 1003);
        break;
        case KeyEvent.KEYCODE_DPAD_RIGHT:
        intent.putExtra("cmd", 1004);
        break;
        }
        mContext.startService(intent);
}

监听中对指纹点击次数进行次数累计

private void ClickCount() {
    if (!hasFingerprint()) {
        return;
    }
    long currentTime = System.currentTimeMillis();
    if (currentTime - clickF11Time <= MULTI_CLICK_BUFFER) {
        gclHandler.removeMessages(MSG_SEND);
        clickF11count++;
    } else {
        gclHandler.removeMessages(MSG_SEND);
        clickF11count = 1;
    }
    clickF11Time = currentTime;
    Log.d("RgkFp.frame", "ClickCount, " + clickF11count);
    if (clickF11count > 1) {
        gclHandler.sendEmptyMessage(MSG_DOWN);
    }

}

对指纹点击次数通过handler机制处理,启动服务,根据数据库的数据启动相应的应用

Handler gclHandler = new Handler() {
    public void handleMessage(android.os.Message msg) {
        switch (msg.what) {
        case MSG_DOWN:
            sendEmptyMessageDelayed(MSG_SEND, MULTI_CLICK_BUFFER+100);
            break;
        case MSG_SEND:
            Log.d("Fp.frame", "count = " + clickF11count);
            if (clickF11count > 1) {
            Intent intent = new Intent();
                intent.setAction("com.xxx.fp.CLICK_COUNT");
                intent.setPackage("com.xxx.fpfeature");
                intent.putExtra("clickCount", clickF11count);
                mContext.startService(intent);
            }
            break;
        default:
            break;
        }
    };

至此,指纹中相关重要功能基本完毕

你可能感兴趣的:(技术总结)