需求:做一个系统服务或者应用;可以根据后台配置的黑白名单,禁用应用启动。如果是黑名单应用,则点击了,也无法启动。
这里不做后台拉取黑名单的动作。只做 如何检测其他应用启动。检测到了,就可以禁止启动了。
具体如下。
这里作为非系统app来进行说明,系统app要简单一些,但差别不大。
知识点及介绍参考文章如下:
使用IActivityController监听所有app启动的状态。
如下:
public class MyActivityController extends IActivityController.Stub {
public boolean activityStarting(Intent intent, String pkg) {
Log.d(TAG, "activityStarting: " + pkg + ", intent= " + intent);
//retrun false;//false 则不会启动,直接返回。
return true;
}
public boolean activityResuming(String pkg) {
Log.d(TAG, "activityResuming: " + pkg);
return true;
}
public int appEarlyNotResponding(String processName, int pid,
String annotation) {
return 0;
}
public boolean appCrashed(String processName, int pid, String shortMsg,
String longMsg, long timeMillis, String stackTrace) {
return false;
}
public int appNotResponding(String processName, int pid,
String processStats) {
return 0;
}
public int systemNotResponding(String msg) {
return 1;
}
}
但是正常情况下,是无法使用的,这个是系统级别的。所以需要权限、反射、系统签名才能使用。
检测其他app启动情况就如上面函数。
在activityStarting回调中进行处理,如果是黑名单则return false,则不会启动,retrun true,正常启动。
如何使用系统级别的函数呢,在2中介绍。
1中是监听,则需要在某个地方,把这个监听设置一下。
伪代码:ActivityManagerNative.setActivityController(new MyActivityController())
区别5.1和7.1,其他版本setActivityController
里参数是否有差异不确定,如果需要兼容,则根据函数的参数进行一下处理即可。反射参考推荐文章。
反射获取相关类及方法。实际代码如下:
private void setActivityController() {
Log.d(TAG, "setActivityController: ...........");
try {
Class<?> cActivityManagerNative = Class
.forName("android.app.ActivityManagerNative");
Method mGetDefault = cActivityManagerNative.getMethod("getDefault",
null);
Object oActivityManagerNative = mGetDefault.invoke(null, null);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
Method mSetActivityController = cActivityManagerNative.getMethod(
"setActivityController",
Class.forName("android.app.IActivityController"), Boolean.TYPE);
mSetActivityController.invoke(oActivityManagerNative,
new ActivityController(), true);
} else {
Method mSetActivityController = cActivityManagerNative.getMethod(
"setActivityController",
Class.forName("android.app.IActivityController"));
mSetActivityController.invoke(oActivityManagerNative,
new ActivityController());
}
Log.d(TAG, "setActivityController: ....................end");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
直接这么写肯定会报错的,所以需要配置一下。
这里需要个源码中间产物:classes.jar。在源码目录:图中绿色部分。
可以简单参考下2中给出的参考超链接。
通过classes.jar既可以在编译的时候不报错。具体原理参考推荐参考文章。
AndroidStudio中配置:
将classes.jar放到相应目录,并在gradle中配置一下。如下图:
使用setActivityController
需要系统权限:SET_ACTIVITY_WATCHER
在menifest中配置一下。这个权限是系统级别的,所以三方app配置后也无法生效,需要增加android:sharedUserId="android.uid.system"
,而这个shareduid需要是系统级别app,否则无法安装,所谓系统级别,就是有系统的签名。
参考文章即可,一般如果给其他厂商用,厂商会要求你把apk发过去,他们进行apk签名,然后可以安装到设备上。签名文件一般不会给你。
如果是自己公司开发,有自己的系统,则可以拿到签名文件自己签名,或者生成as所需要的签名文件,在as打包时自动签名。
具体签名及生成签名文件的方法参考推荐文章。
在as真配置一下生成好的签名文件:
这样便可以安装到设备上,进行调试了。
否则app因没签名,但使用android:sharedUserId="android.uid.system"
导致无法安装。
相关demo放到了gtihub上。其中签名文件被我删除了,一个是放上去,一般也用不了,需要根据具体设备而定。
无论怎样,都需要有签名或者源码的环境才可行。