最近在研究xposed,有个需求要做到清除其他应用数据,xposed功能足够强大,应该可以实现这个功能,下面是基于android4.4.4研究的思路。
google只能开放了清楚缓存的API,清除数据的API隐藏了,而调用清理数据的方法在“设置”中的“应用”里,找到设置功能的源码,代码位置 com.android.settings.applications.InstalledAppDetails ,点击事件在这
调用了initiateClearUserData,看看这里面怎么实现的
调用了ActivityManager的clearApplicationUserData,参数是该应用的包名和一个ClearUserDataObserver的实例
IPackageDataObserver是有AIDL生成的,位于android.content.pm包内,我们可以新建一个相同的包名,把AIDL放在这里。
(注:有些应用详情里该按钮不是清除数据,是管理空间,清除数据的按钮在应用自定义的界面里,但是最终实现清除数据还是调用的clearApplicationUserData)
clearApplicationUserData是隐藏方法,直接调用了ActivityManagerNative的方法
ActivityManagerNative:
这里直接使用反射或者Xposed的callmethod调用这两个方法都会报错,缺少权限
android.permission.CLEAR_APP_USER_DATA
这里并没有实现具体的方法,真正的实现在ActivityManagerService中,参考http://www.jianshu.com/p/18517a4ef8e1
ActivityManagerService:
该方法上面部分是获取pid、uid,给IPackageManager的对象赋值和权限检测,下面执行清除数据。最开始的思路是直接hook AppGlobals.getPackageManager()的clearApplicationUserData,但是仍旧报缺少权限错误。。。继续分析:
AppGlobals:
ActivityThread:
到这里遇到IPackageManager.aidl生成的Stub类,PackageManagerService继承了IPackageManager.Stub
PackageManagerService:
又是权限检测,跳过,进入clearApplicationUserDataLI
int retCode = mInstaller.clearUserData(packageName, userId);
进入Installer方法
该类大概是创建本地socket,执行linux命令行,进行各种应用的安装、移动、卸载、清除数据等
但是hook此方法,仍旧报错,空指针异常,而且hook connect也连接不上
Class installCls = XposedHelpers.findClass("com.android.server.pm.Installer",lpp.classLoader);
Object installObj = XposedHelpers.newInstance(installCls);
boolean lean = (boolean) XposedHelpers.callMethod(installObj,"ping");
boolean bo = (boolean) XposedHelpers.callMethod(installObj,"connect");
XposedBridge.log("lean:"+lean+"--bo:"+bo);
实例化Installer出的问题.查了资料,Installer在系统启动时由其他类调用,启动完成后用findAndHookMethod无法hook到。于是实现Xposed另一接口XposedHookZygoteInit
@Override
public void initZygote(StartupParam startupParam) throws Throwable {
Class installCls = XposedHelpers.findClass("com.android.server.pm.Installer",null);
XposedHelpers.findAndHookMethod(installCls, "writeCommand",String.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
String string = (String) param.args[0];
XposedBridge.log("string:"+string);
if(string.contains("com.google.android.gms"))
param.args[0] = "rmuserdata com.google.android.gms 0";
}
});
}
以google play service为例,这种情况下可以清除此app的数据。但是这种方法不可控,必须放在initZygote中,而且Installer不能通过反射实例化,只能另想办法。
郁闷两天后,想到既然从ActivityManager、PackageManagerService等类中不能调用是因为权限限制,那就hook检测权限的方法,让我的应用通过权限检测。最开始打算以外部调用的方式(在非Xposed中,例如activity,以反射的方式调用清除数据的方法,Xposed hook权限检测方法)从 ActivityManagerService入手,因为PackageManagerService的构造方法需要传多个参数,包括Installer的对象。ActivityManagerService里检测权限的是checkComponentPermission方法,但是通过反射获取ActivityManagerService对象时又报错,FileNotFoundException,引发异常的位置是mBatteryStatsService = new BatteryStatsService(new File( systemDir, "batterystats.bin").toString());
batterystats.bin文件位于/data/system/下,但即使我获得了root权限依然触发异常,不知道什么原因。这样只能在handleLoadPackage中调用清除数据,在Xposed外部设置个合适方法触发清理数据,比如获得IMEI、杀掉某个进程等,通过hook这些方法,然后调用清除数据的方法:
XposedHelpers.findAndHookMethod("android.app.ActivityManager", lpp.classLoader, "killBackgroundProcesses",String.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
String process = (String) param.args[0];
XposedBridge.log("process:"+process);
if("com.google.android.gsf".equals(process)){
ActivityManager manager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
XposedHelpers.callMethod(manager,"clearApplicationUserData",packageName,clearDataUserData);
}
}
});
这是以杀死com.google.android.gsf为例。
权限检测这里有两部分,就是ActivityManagerService 的checkComponentPermission了:
XposedHelpers.findAndHookMethod("com.android.server.am.ActivityManagerService", lpp.classLoader, "checkComponentPermission", String.class, int.class, int.class, int.class, boolean.class, new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
if(Manifest.permission.CLEAR_APP_USER_DATA.equals(param.args[0])) {
int re = (int) param.getResult();
XposedBridge.log("ActivityManagerService re:"+re);
param.setResult(0);//PackageManager.PERMISSION_GRANTED
}
}
});