1. 程序锁原理
1.1 实现效果:
在用户打开一个应用时,若此应用是我们业务内的逻辑拦截目标,那就在开启应用之后,弹出一个输入密码的界面,输入密码正确则进入目标应用。若不输入直接按返回键,则直接返回桌面。
1.2 实现原理:
实时检测栈顶Activity的包名,如果和我们预置的包名相符(可用SQLite数据库对要进行匹配的包名进行信息存储),则新开一个Activity任务栈,将拦截画面置于用户面前。只有在用户输入密码,并且验证成功后,才“放行”。本文原创,转载请注明出处:http://blog.csdn.net/seu_calvin/article/details/51933690
2. 程序锁实现
1. 代码实现比较简单,获取到topActivity的包名即可进行程序锁的逻辑判断。
注意,当判断某个应用需要保护时,因为服务是没有任务栈信息的,所以在服务里开启Activity,需要指定这个Activity运行的任务栈。这里指定Flag为Intent.FLAG_ACTIVITY_NEW_TASK。这时候就会有一个Activity挡在被打开的应用前面,完成程序锁的功能。
当然,监听到用户开启QQ时,跳出一个我们“自定义”的登录界面,这就是所谓的钓鱼了,可以拿到用户的用户名和密码。实现起来还是比较简单的。不过需要申请这个权限<uses-permission android:name="android.permission.GET_TASKS" />
while (flag){ List<ActivityManager.RunningTaskInfo> runningTasks = am.getRunningTasks(1); //最近操作的任务栈runningTasks.get(0) //topActivity栈顶 baseActivity栈底 String packageName = runningTasks.get(0).topActivity.getPackageName(); if(protectPacknames.contains(packageName)){ //判断应用是否需要临时停止保护 if(packageName.equals(tempStopEnterPwPackageName)){ }else{ Intent intent = new Intent(getApplicationContext(),EnterAppLockPwActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.putExtra("packageName",packageName);//用户在拦截界面展示被保护的应用信息 startActivity(intent); } } try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } }
2. tempStopEnterPwPackageName的概念。
为了防止用户验证成功,进入被锁应用后,继续弹出登录验证界面,(因为我们做了死循环),那么就该在合适的时机跳出循环,在本例中肯定是当用户验证成功时,我们采用发送自定义广播的方式来解决,在服务里代码动态注册此广播的Broadcast Receiver,从而使该应用成为tempStopEnterPwPackageName,暂时处于不被锁的状态即可。自定义广播发送时,将该应用的包名放入Extra中进行传递即可。
Intent intent = new Intent(); intent.setAction("com.example.user.mobilesafe.tempstop"); intent.putExtra("packageName",packageName); sendBroadcast(intent); finish();
3.最后需要处理的是,在EnterAppLockPwActivity界面进行Back键的屏蔽操作,原因很简单,就不用多说了。这里实现Back键回桌面,因为不会执行OnDestroy方法,但是会执行onStop方法,因此我们把finish()放在onStop方法中实现。
@Override public void onBackPressed() { Intent intent = new Intent(); intent.setAction("android.intent.action.MAIN"); intent.addCategory("android.intent.category.HOME"); intent.addCategory("android.intent.category.DEFAULT"); intent.addCategory("android.intent.category.MONKEY"); startActivity(intent); } @Override protected void onStop() { super.onStop(); finish(); }
3. BUG处理
(2)同时第二个BUG是,用户打开程序锁所在应用的其他界面A,按Home键,返回桌面,再打开被保护的应用,不输入密码直接按Back键,长按Home键,弹出用户最近打开过的Activity列表(Activity可能已经被关闭了),不代表应用操作的应用。用户认为从该列表点开进入的就应该是应用,那么用户想再次打开程序锁应用时,很显然,会弹出我们的EnterAppLockPwActivity界面(因为这个界面本身就属于我们的程序,从Activity列表里看起来就像是程序锁本身这个应用),这就给用户带来了困扰。
针对上述两个BUG,可以通过指定Activity启动模式和屏蔽应用在Activity列表里显示来解决。
<activity android:name=".EnterAppLockPwActivity" android:launchMode="singleInstance" android:excludeFromRecents="true"/>
(3)我们有时需要方便用户开启和关闭服务,而设置一个开关,为了防止开关显示开启但服务实际上没有Alive,无论什么时候需要展示这个开关设置界面时,我们都进行此服务“是死是活”的判断。再修改界面控件显示即可。
public class IsServiceAliveUtils { public static boolean isServiceRunning(Context context,String serviceName){ ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); List<RunningServiceInfo> infos = am.getRunningServices(100); for(RunningServiceInfo info : infos){ String name = info.service.getClassName(); if(serviceName.equals(name)){ return true; } } return false; } }