最近工作上要用到应用锁(AppLocker),早上装了个试玩了一下,下午花了2个小时捣鼓出来一个demo (基于android 6.0),简单分享一下。

免责声明:这篇文章纯粹是个人YY,未参考或反编译任何商业app。

所谓应用锁,说白了就是在监测到目标app启动时额外起一个锁屏界面把它盖住。这种方式的缺点是显而易见的,无法100%保证盖住,有时候会先闪出一个app启动界面,然后才被盖住。最彻底的方式当然是在framework里拦,不过这种方式需要跟平台厂商合作,不在今天的讨论范围之内。

先看一下效果,有两种盖法,一种是起一个新的activity,还有一种是起一个悬浮窗。我没有比较过这两种方式的优劣,先采用了第一种。悬浮窗之前封装了一个类,后面有时间再写文章分享出来。

下面讨论实现细节:

在早期的android版本中,大家都是通过getRunningTasks()来获取前台进程的,但是出于安全考虑这个API已经被google阉割了,要用这个API必须要申请REAL_GET_TASKS权限,但是这个权限又是signature or system的,所以基本上是然并卵。。。

<permission android:name="android.permission.REAL_GET_TASKS"android:permissionGroup="android.permission-group.APP_INFO"android:protectionLevel="signature|system"android:label="@string/permlab_getTasks"android:description="@string/permdesc_getTasks" />

在android 6.0上,纯app层面,获取前台进程的唯一合法途径可能就是通过UsageStat的方式了,它原本是用来统计app使用情况的,但是可以根据时间戳获取最近使用的app来达到目的。需要在AndroidManifest.xml里添加PACKAGE_USAGE_STATS权限,同时要在Settings的“安全--> 有权查看使用情况的应用”里给你的应用打开这个权限。代码如下:

private String getForegroundApp() {
UsageStatsManager usageStatsManager = (UsageStatsManager) getApplicationContext().getSystemService(Context.USAGE_STATS_SERVICE);long ts = System.currentTimeMillis();List<UsageStats> queryUsageStats = usageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_BEST, 0, ts);if (queryUsageStats == null || queryUsageStats.isEmpty()) {return null;}UsageStats recentStats = null;for (UsageStats usageStats : queryUsageStats) {if(recentStats == null ||recentStats.getLastTimeUsed() < usageStats.getLastTimeUsed()) {recentStats = usageStats;}}if (DEBUG) Log.d(TAG, "getForegroundApp: " + recentStats.getPackageName());return recentStats.getPackageName();
}

queryUsageStats()方法里的两个参数0和ts表示统计从开机到当前时间所有app的使用情况,然后就是比较时间戳找到最近一次使用的app,返回它的package名称。

既然要持续监控前台进程,自然需要一个service,我们把它命名为AppLockerService。为避免阻塞主线程,这里创建一个新的HandlerThread,然后每隔300ms通过Handler发一个消息去检查前台进程,如果在监控列表中就启动锁屏界面(AppLockerActivity),通过在intent中添加一个APP_NAME字段表示被锁app的package名称。

另外有个需求是锁屏以后需要重新验证一次手势密码,因此需要通过KeyguardManager判断一下是否在锁屏,是的话就清楚掉解锁状态。代码如下:

private final int MONITOR_INTERVAL = 300;
HandlerThread mMonitorThread = new HandlerThread("AppLockerMonitorThread");
mMonitorThread.start();
Handler mHandler = new Handler(mMonitorThread.getLooper()) {@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case MSG_CHECK_FG_APP:if (mKeyguardManager.inKeyguardRestrictedInputMode()) {resetUnlockStatus();break;}String foregroundApp = getForegroundApp();if (shouldLock(foregroundApp)) {Intent intent = new Intent(AppLockerService.this, AppLockerActivity.class);intent.putExtra("APP_NAME", foregroundApp);intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);startActivity(intent);}break;}mHandler.sendEmptyMessageDelayed(MSG_CHECK_FG_APP, MONITOR_INTERVAL);}
};
mHandler.sendEmptyMessage(MSG_CHECK_FG_APP);

这个锁屏的界面很简单,上面是一个被锁app的图标,下面是一个九宫格的锁。本来想自己写这个锁的,网上一搜有位仁兄已经封装好了,而且注释巨详细,拿来用就行了,在此向这位仁兄致敬!我们不生产代码,我们只是代码的搬运工,哈哈~~

每次手势完成以后会调用一个onGestureEvent()的回调函数,如果密码匹配的话就通知AppLockerService更新解锁状态并退出:

public void onGestureEvent(boolean matched) {if (matched) {Intent intent = new Intent(AppLockerActivity.this, AppLockerService.class);intent.putExtra("UNLOCK_APP", mAppName);startService(intent);finish();}
}

最后要写一下主界面,就是列举一下所有非系统的 app ,以及他们被锁的状态,放到一个 ListView 里显示。用户点击以后会更新图标,把设置保存到 SharedPreferences 里,还有通知 AppLockerService 更新 app 列表。

private void initAppList() {List<PackageInfo> packages = getPackageManager().getInstalledPackages(0);for (int i = 0; i < packages.size(); i++) {PackageInfo packageInfo = packages.get(i);if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) ==  0) {InstalledApp app =new InstalledApp(getApplicationContext(), packageInfo.packageName);mAppList.add(app);}}
}protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initAppList();ArrayAdapter<InstalledApp> adapter =new InstalledAppAdapter(getApplicationContext(), R.layout.app_item, mAppList);final ListView appListView = (ListView) findViewById(R.id.app_list);appListView.setAdapter(adapter);appListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {@Overridepublic void onItemClick(AdapterView<?> parent, View view, int position, long id) {InstalledApp app = mAppList.get(position);boolean lockStatus = app.isLocked();app.setLocked(!lockStatus);ImageView appLockStatus = (ImageView) view.findViewById(R.id.app_lock_status);appLockStatus.setImageDrawable(app.getLockStatusIcon());// notify AppLockerServiceIntent intent = new Intent(MainActivity.this, AppLockerService.class);intent.putExtra(app.isLocked() ? "ADD_APP" : "REMOVE_APP",    app.getPackageName());startService(intent);}});
}

差不多就这么多内容,还有一些小细节比如如何避免解锁后再次加锁,还有开机自启等等,具体看看代码就清楚了。

最后附上代码链接,欢迎大家交流分享!

源码下载

应用锁(AppLocker)原理及代码实现相关推荐

  1. AQS基础——多图详解CLH锁的原理与实现

    1 什么是自旋锁和互斥锁? 由于CLH锁是一种自旋锁,那么我们先来看看自旋锁是什么? 自旋锁说白了也是一种互斥锁,只不过没有抢到锁的线程会一直自旋等待锁的释放,处于busy-waiting的状态,此时 ...

  2. Redis进阶- Redisson分布式锁实现原理及源码解析

    文章目录 Pre 用法 Redisson分布式锁实现原理 Redisson分布式锁源码分析 redisson.getLock(lockKey) 的逻辑 redissonLock.lock()的逻辑 r ...

  3. Java 重入锁 ReentrantLock 原理分析

    1.简介 可重入锁ReentrantLock自 JDK 1.5 被引入,功能上与synchronized关键字类似.所谓的可重入是指,线程可对同一把锁进行重复加锁,而不会被阻塞住,这样可避免死锁的产生 ...

  4. zookeeper实现分布式锁的原理及具体使用案例

    zookeeper跟redis一样,也是基于内存的. 官网: http://zookeeper.apache.org/ zookeeper是分布式系统的协调服务,提供配置管理.分布式协同.命名的中心化 ...

  5. JUC多线程:synchronized锁机制原理 与 Lock锁机制

    前言: 线程安全是并发编程中的重要关注点,造成线程安全问题的主要原因有两点,一是存在共享数据(也称临界资源),二是存在多条线程共同操作共享数据.因此为了解决这个问题,我们可能需要这样一个方案,当存在多 ...

  6. 分布式锁实现原理与最佳实践

    分布式锁应用场景 很多应用场景是需要系统保证幂等性的(如api服务或消息消费者),并发情况下或消息重复很容易造成系统重入,那么分布式锁是保障幂等的一个重要手段. 另一方面,很多抢单场景或者叫交易撮合场 ...

  7. LoRa SX1278/76驱动原理 附代码

    LoRa SX1278/76驱动原理 附代码 原理解释 LoRa 关键参数说明 前导码: 报头: 显式报头模式: 隐式报头模式: LoRa 调制解调: 扩频因子: 编码率: 信号带宽: 代码说明 SP ...

  8. 对dpdk的rte_ring实现原理和代码分析

    对dpdk的rte_ring实现原理和代码分析 前言 dpdk的rte_ring是借鉴了linux内核的kfifo实现原理,这里统称为无锁环形缓冲队列. 环形缓冲区通常有一个读指针和一个写指针.读指针 ...

  9. java的markword_【转帖】Java工具结构与锁实现原理及MarkWord详解

    Java工具结构与锁实现原理及MarkWord详解 https://www.pianshen.com/article/2382167638/ 我们都知道,Java工具存储在堆(Heap)内存.那么一个 ...

  10. 老夫带你深度剖析Redisson实现分布式锁的原理

    Redis实现分布式锁的原理 前面讲了Redis在实际业务场景中的应用,那么下面再来了解一下Redisson功能性场景的应用,也就是大家经常使用的分布式锁的实现场景. 引入redisson依赖 < ...

最新文章

  1. 《Git in Practice》作者访谈:关于Git的八个问题
  2. 数据库计算机报告,外文数据库计算机检索报告实例.pdf
  3. linux多线程冗余,Linux  下的路径冗余
  4. 前端笔记-label标签的for属性
  5. python小型编程_学习Python编程的11个资源
  6. 编译自定义的主题theme
  7. bootstrap布局_如何使用LayoutIt构建HTML Bootstrap布局!
  8. 【中软杯国二开源】基于PaddleOCR和深度学习的企业实体识别
  9. 饭谈:失眠,还有梦魇,第二天要上班应该怎么办?
  10. 网络数据里的身份证实名认证接口,你了解多少?
  11. wps打开服务器文件很慢,wps打开速度慢怎么办-wps打开速度慢解决方法 - 河东软件园...
  12. Paper再现:MD+AI自动编码机探测蛋白变构(四):DIO的生成和聚类
  13. centos:gtk:No package ‘gdk-2.0‘ found
  14. Struts原理与实践(7)
  15. 预测:原理与实践(第二版)2021/05/01 第一次更新
  16. 2022年11月(下半年)信息系统项目管理师考试-案例分析真题及解析
  17. 产品机会:痛点,痒点,爽点
  18. 周杰伦等名人网站频被挂马 粉丝上网需警惕
  19. mysql中replace函数的用法
  20. 小番茄(Visual Assist X)常用快捷键

热门文章

  1. 文献阅读笔记怎么写?
  2. 从零开始写一个Jison解析器(7/10):解析器生成器 `parser generator` 的迭代式开发流程
  3. 2013.12.26 M-Learning
  4. 怎么逼自己 成为一个上进的人
  5. Log4J按照不同包名输出日志
  6. golang:同个包下不同文件不能互相调用函数
  7. 网卡收到一个数据包的时候,是如何传给应用层的
  8. 01百思不得其姐基本配置
  9. GhostXP_SP3电脑公司装机版v2011.04特别版
  10. 2021年N1叉车司机新版试题及N1叉车司机证考试