这段时间在给公司的产品做CTA认证,公司的产品声明不允许第三方软件安装,所以需要禁止掉APK的安装功能。一开始我把Packageinstaller.apk从系统里面删了,试了一下,放一个APK到SD卡,点击安装,确实安装不了,我以为就成功了,心想还挺简单么!!
不过,CTA认证实验室说,不行,还是可以安装第三方软件!仔细问了一下,他们是通过PC端类似于“手机助手”的软件安装上去的。自己赶紧试了一下,确实可以安装,又试了一下adb install命令,也可以安装!!
得,还是问问软件开发的同事吧,给个patch也行呀,谁知道人家太忙,没时间搞,但是这事情分给我干了,还是硬着头皮搞吧。不过,对于一个搞驱动的,碰到这种应用的问题,半天都没思路呀!!不过,功夫不负有心人,百度了一天,终于搞定了。
啰嗦完了,说正事!!
通过网上大神们对于Android系统APK安装过程的分析,系统安装APK主要的源码在文件“frameworks\base\services\core\java\com\android\server\pm\PackageManagerService.java”里,顺便说一下,我用的是Android 5.0系统。
Android系统安装APK有4种方式:
1. 开机时系统应用的安装;
2. 手机APP下载应用安装;
3. ADB命令安装;
4. 点击APK文件安装;(我之前删除的Packageinstaller.apk就是负责这种安装)
第1种安装方式函数调用结构大致如下:
PackageManagerService
->scanDirLI
–>scanPackageLI(File scanFile, …)
—>(省略)
—->mInstaller.install
剩下3种安装方式,虽然开始是从不同接口进入,但是最后统一的调用如下:
installPackage
->scanPackageLI(PackageParser.Package pkg, …)
–>scanPackageDirtyLI
—>createDataDirsLI
—->mInstaller.install
由于不是很懂JAVA语法规则,所以,一开始我以为,第1个“scanPackageLI”函数应该不会像下面的“scanPackageLI”函数一样调用“createDataDirsLI”函数,所以我做如下修改“createDataDirsLI”函数:
private int createDataDirsLI(String packageName, int uid, String seinfo) {
int[] users = sUserManager.getUserIds();
//直接返回失败,禁止安装APK
return PackageManager.INSTALL_FAILED_INVALID_APK;
/*
int res = mInstaller.install(packageName, uid, uid, seinfo);
if (res < 0) {
return res;
}
for (int user : users) {
if (user != 0) {
res = mInstaller.createUserData(packageName,
UserHandle.getUid(user, uid), user, seinfo);
if (res < 0) {
return res;
}
}
}
return res;
*/
}
编译运行之后发现,系统卡在开机动画那里不动了!!
所以,我估计第1种安装方式的函数“scanPackageLI”一样调用了“createDataDirsLI”函数,这个时候就麻烦了,系统APK的安装和其他方式APK安装调用一样的函数!!
然后又去百度,看了大神的关于Android系统启动时APK安装过程的分析后,想到一个办法,定义一个标志位,用来标记系统启动时APK是否安装完成,当系统APK安装没有完成时,“createDataDirsLI”函数正常工作,当系统启动完成,让“createDataDirsLI”函数失效。
系统启动时安装APK调用的函数为上面第1种安装方式的“PackageManagerService”,然后调用“scanDirLI”函数完成安装APK,安装完成后,“调用updatePermissionsLPw来申请为了特定的资源访问权限的apk分配相应的linux用户组ID”,这是文章原话。
所以先定义一个标志位:private static boolean APK_install_finish = false;
初始化为“false”代表系统APK安装还没有完成。
找到“PackageManagerService”函数内“updatePermissionsLPw”函数的调用处,发现只有一处。
在“updatePermissionsLPw”函数调用之前,“scanDirLI”函数调用之后,改变“APK_install_finish ”变量的值,表示系统APK安装完成,代码如下:
...
Slog.i(TAG, "Time to scan packages: "
+ ((SystemClock.uptimeMillis()-startTime)/1000f)
+ " seconds");
//系统APK安装完成
APK_install_finish = true;
// If the platform SDK has changed since the last time we booted,
// we need to re-grant app permission to catch any new ones that
// appear. This is really a hack, and means that apps can in some
// cases get permissions that the user didn't initially explicitly
// allow... it would be nice to have some better way to handle
// this situation.
final boolean regrantPermissions = mSettings.mInternalSdkPlatform
!= mSdkVersion;
if (regrantPermissions) Slog.i(TAG, "Platform changed from "
+ mSettings.mInternalSdkPlatform + " to " + mSdkVersion
+ "; regranting permissions for internal storage");
mSettings.mInternalSdkPlatform = mSdkVersion;
updatePermissionsLPw(null, null, UPDATE_PERMISSIONS_ALL
| (regrantPermissions
? (UPDATE_PERMISSIONS_REPLACE_PKG|UPDATE_PERMISSIONS_REPLACE_ALL)
: 0));
...
然后重新修改“createDataDirsLI”函数:
private int createDataDirsLI(String packageName, int uid, String seinfo) {
int[] users = sUserManager.getUserIds();
//如果系统APK安装完成,则禁止安装任何APK
if(APK_install_finish)
{
return PackageManager.INSTALL_FAILED_INVALID_APK;
}
int res = mInstaller.install(packageName, uid, uid, seinfo);
if (res < 0) {
return res;
}
for (int user : users) {
if (user != 0) {
res = mInstaller.createUserData(packageName,
UserHandle.getUid(user, uid), user, seinfo);
if (res < 0) {
return res;
}
}
}
return res;
}
只有当系统APK安装完成之后,才让“createDataDirsLI”函数失效。
编译下载运行,可以正常开机,开机之后,先使用ADB安装APK,失败;然后点击APK装包,安装失败;再使用PC端“手机助手”安装APK,依然失败!!
至此,大功告成~
相关参考文章链接:
http://blog.csdn.net/hdhd588/article/details/6739281
https://www.2cto.com/kf/201607/528367.html
http://blog.csdn.net/dkbdkbdkb/article/details/45313911