现在很多手机厂商都加入了指纹芯片,相对应的就要开发一系列相配套的指纹相关功能,其中基本功能为应用锁,应用冻结,通过指纹关闭闹钟,通过指纹拍照,通过指纹接听电话,指纹作为密码对文件加密,自定义滑动指纹器件方向启动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 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
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);
、、、代码省略、、、
}
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信息及时进行检验。同时还注册了熄屏广播接收器,以重置所有应用已解锁标志位
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);
}
、、、代码省略、、、
}
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,达到锁定应用的目的
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;
}
}
}
@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;
}
};
至此,指纹中相关重要功能基本完毕