android手机中指纹识别应用相关功能的讲解
2017-07-17 14:40
651 查看
现在很多手机厂商都加入了指纹芯片,相对应的就要开发一系列相配套的指纹相关功能,其中基本功能为应用锁,应用冻结,通过指纹关闭闹钟,通过指纹拍照,通过指纹接听电话,指纹作为密码对文件加密,自定义滑动指纹器件方向启动App,自定义多击打开应用等
应用锁的基本原理是一个应用启动的时候拿到该应用的componentName,在后台进行验证,对需要锁定的应用,在Intent中传入启动应用的packageName,通过Intent在其上启动一个Activity,在Activity中创建一层fragment实现遮挡锁定的效果。在密码验证成功后,覆盖Activity自身调用finish()方法移除自身,进入需要打开的应用。而如果在锁定界面返回,则重写覆盖Activity的onBackPressed()方法,获取ActivityManager将intent传来的packageName关闭。
[b]应用锁的服务详解[/b]
本功能需要基于服务FPLockService,对打开的Activity进行验证。FPAppLockService服务需要开机启动,但是我们没有采用开机广播启动服务。首先我们可以看一下为什么不采用,如上是开机广播的简单流程。由于开机广播在launcher启动后发出,可能出现开机后未立即启动服务,无法立即锁定。我们在framework/base/services/core/java/android/server/am/ActivityManagerService.java中的startHomeActivityLocked方法中启动FPAppLockService,使得该服务比launcher更早启动
FPAppLockService服务的基本工作是对当前用户启动的Activity进行判断,并作出响应。但是,怎样快速知道当前用户启动的Activity是什么是一个需要考虑的问题。之前采用的方式是监听Activity栈的变化,当栈顶Activity发生变化时,获取到栈顶Activity的ComponentName,并对ComponentName进行判断检验
此方法存在一个弊端,由于只有当栈顶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
[b]ActivityStackSupervisor中的resumeTopActivitiesLocked方法[/b]
[b]ActivityManagerService中的sendResumingActivityBroadcast方法[/b]
当Activity的onResume方法调用并在framework层发出广播之后,我们在FPAppLockService中注册广播接收器对当前Activity信息及时进行检验。同时还注册了熄屏广播接收器,以重置所有应用已解锁标志位
[b]FPAppLockService中的广播接收器[/b]
[b]FPAppLockService中的check方法[/b]
由于一个应用有多个Activity,但通常应用只有一个包名,所以我们只需检测包名,并在通过检测后及时更改是否需要锁定的标志位。上述代码中,当我们得知当前应用已经被解锁过,就do nothing。否则启动一个FPLockChooseActivity,并创建FPLo
d61b
ckFpFragment,达到锁定应用的目的
[b]在FPLockFpFragment中实现FingerprintManager.AuthenticationCallback,重写指纹验证回调监听[/b]
在FPLockChooseActivity中unlock()方法中finish自身,置定该应用已解锁标志位,在手机未熄屏之前,应用不再被锁定。同时根据intent传递的目的地,跳转到指定的Activity
[b]重写onBackPressed()方法实现返回键退出当前Activity[/b]
至此,APPLock 功能的基本流程便已实现
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实现应用的禁用,也就是冻结效果。
先要在framework层的SettingProvide设置好键值
然后就可以在控制的该类里这样获取键值
当allowFingerShutter当为0时关闭指纹拍照功能,为1时打开
该功能通过后台服务启动应用,关键在于怎么知道当前指纹被多击,指纹受方向滑动,在何处对这些进行监听?带着这些问题,我们想到手机已存在的熄屏手势启动应用功能,这个功能也存在同样的启动问题。因此我们找到了在framework层下的PhoneWindowManager类。frameworks\base\services\core\java\com\android\server\policy\PhoneWindowManager.java
对按键进行监听,监听中根据指纹方向启动DirectionStart服务,并在服务中根据数据库存储的数据启动相应的应用
监听中对指纹点击次数进行次数累计
对指纹点击次数通过handler机制处理,启动服务,根据数据库的数据启动相应的应用
一,应用锁
指纹应用锁功能逻辑流程图
应用锁的基本原理是一个应用启动的时候拿到该应用的componentName,在后台进行验证,对需要锁定的应用,在Intent中传入启动应用的packageName,通过Intent在其上启动一个Activity,在Activity中创建一层fragment实现遮挡锁定的效果。在密码验证成功后,覆盖Activity自身调用finish()方法移除自身,进入需要打开的应用。而如果在锁定界面返回,则重写覆盖Activity的onBackPressed()方法,获取ActivityManager将intent传来的packageName关闭。
[b]应用锁的服务详解[/b]
本功能需要基于服务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
[b]ActivityStackSupervisor中的resumeTopActivitiesLocked方法[/b]
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); 、、、代码省略、、、 }
[b]ActivityManagerService中的sendResumingActivityBroadcast方法[/b]
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信息及时进行检验。同时还注册了熄屏广播接收器,以重置所有应用已解锁标志位
[b]FPAppLockService中的广播接收器[/b]
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); } 、、、代码省略、、、 }
[b]FPAppLockService中的check方法[/b]
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,并创建FPLo
d61b
ckFpFragment,达到锁定应用的目的
[b]在FPLockFpFragment中实现FingerprintManager.AuthenticationCallback,重写指纹验证回调监听[/b]
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; } } }
[b]重写onBackPressed()方法实现返回键退出当前Activity[/b]
@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_DISNABLED
可用状态: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; } }; 至此,指纹中相关重要功能基本完毕
相关文章推荐
- Android调用手机中的应用市场,去评分的功能实现
- 如何在Android手机中开发QQ账户登陆功能的应用
- Android手机识别相关
- 如何在Android手机中开发QQ账户登陆功能的应用
- 看我七十二变----巧用Android手机指纹识别器扩充手势识别功能(一)
- [置顶] Android中高仿快牙实现Socket列表的展示,获取安卓手机系统安装的应用和自己安装的应用相关信息
- Android 手机应用开发经验之手势识别Gesture的研究
- 【Linux】Android手机在Ubuntu上无法被adb识别解决办法(权限相关)
- Chrome 将支持 Android 和 Mac 平台上指纹辨识的相关功能
- [置顶] Android中高仿快牙实现Socket列表的展示,获取安卓手机系统安装的应用和自己安装的应用相关信息
- Android 6.0指纹识别相关API
- Android调用手机中的应用市场,去评分的功能实现
- 指纹识别在智能手机上的应用前景分析
- Android实现打开手机淘宝并自动识别淘宝口令弹出商品信息功能
- 看我七十二变----巧用Android手机指纹识别器扩充手势识别功能(二)
- Android 6.0 指纹识别功能详细分析(郭元歆)
- Android指纹识别功能深入浅出分析到实战(6.0以下系统解决方案)
- Android 手机应用开发经验之手势识别Gesture的
- Android指纹识别功能
- Swift利用指纹识别或面部识别为应用添加私密保护功能