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

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

一,应用锁

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

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

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

#####应用锁的服务详解

应用锁服务启动流程

本功能需要基于服务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<RunningTaskInfo> 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,达到锁定应用的目的

LockView

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

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

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

指纹拍照逻辑流程图

指纹拍照

先要在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时打开

四,手势导航

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

手势导航

该功能通过后台服务启动应用,关键在于怎么知道当前指纹被多击,指纹受方向滑动,在何处对这些进行监听?带着这些问题,我们想到手机已存在的熄屏手势启动应用功能,这个功能也存在同样的启动问题。因此我们找到了在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;
		}
	};

至此,指纹中相关重要功能基本完毕
发布了45 篇原创文章 · 获赞 23 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/Easyhood/article/details/75215826