adb install 拦截案例分析
1.开发需求:
需要在用户通过 adb install 时做个拦截,用以提示用户是否继续安装该 Apk ,如果在一定时间内用户不做选择,系统就拒绝本次安装
2.开发过程中碰到的问题:
2.1 拦截功能:
第一次尝试在 installPackageAsUser 里面加上拦截流程,但在随后的开发中碰到一个问题:当弹框程序要获取 apk path 时(为了在对话框上显示 App Label ), selinux 不予许,而且该 selinux 权限被 neverallowed 了,如果改变的话, CTS 过不了,所以只能另辟蹊径了:
neverallow system_server { bluetooth_data_file nfc_data_file app_data_file }:file open; can not remove directly, it will cause cts failed
之后发现,PackageManagerService 在安装的时候肯定也需要解析这个 Apk ,那么它是怎么做的呢?顺着这个思路分析源码,发现 PackageManagerService 是先将要安装的 Apk 先暂存在 data/app 目录下(此时 system_server 是被允许访问的),然后再解析的,那么我们不妨将拦截流程放在这次临时的拷贝之后。
为了添加该流程,需要开启一个新的线程来负责拦截工作,目前所用框架如下:
LOOPER
Class used to run a message loop for a thread. Threads by default do not have a message loop associated with them; to create one, call prepare() in the thread that is to run the loop, and then loop() to have it process messages until the loop is stopped.
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
为此又引入了 lock ,condition ,signal 这些线程同步的概念,具体的可以去看实现,这里就不赘述了。
2.2 如何加载应用的 Icon
加载应用的 Icon 需要从要安装的 Apk 里面解析出来,通过2.1问题的解决,我们可以顺利地从临时拷贝的 Apk 里面解析出所需信息,然后获取应用的 Icon , Label 之类的信息,并把这些信息显示在弹出的对话框上面以告知用户这次安装的是什么应用。
ADB INSTALL 拦截笔记:
1.在PKMS.HandlerParams.startCopy里面进行监听;
2.private int PKMS.HandlerParams.mRetries = 0 是HandlerParams的一个成员变量,startCopy每次调用一次,+1,超过4次fail
3.用户 reject adb install 后,startCopy 也应该返回true,这样才能保证把mPendingInstalls中的第一项删除 ,不然会继续重复调用startCopy超过4次才删除。
4.用户选择reject后调用params.observer.onPackageInstalled 返回INSTALL_FAILED_USER_CANCELLED的安装结果。
5.获取apkPath 后,通过PM.getPackageArchiveInfo 获取PackInfo,得到packName 和 icon
PM.installPackage
---> PKMS.installPackageAsUser
1.检查callingUid 判断adb
2.初始化InstallParams对象
1.packageName
2.install_flags
---> PKMS.PackageHandler(msg == INIT_COPY)
---> PKMS.PackageHandler(msg == MCS_BOUND)
---> PKMS.InstallParams.startCopy
---> PKMS.InstallParams.handleStartCopy
.....> 获取APK_PATH
......> 通过PM.getPackageArchiveInfo(APK_PATH) 获取PackInfo
......>getPackageArchiveInfo 根据APK_PATH 生成PackageParser,返回PackageParser.generatePackageInfo
......>handleStartCopy return
......>【插入adb intsall 监听 功能】
----> startCopy 返回结果
***********************************************************************************************************************************
关于如何实现 用户控制 PKMS 安装流程:
1.HandlerParams 中新增 boolean mContinueToInstall = true;
2.仅仅在用户在AlertDialog 中选择了disagree 后,设置为false
3.以为 adb 拦截功能插入在handleStartCopy() 中,调用handleReturnCode() 返回结果前:
---如果mContinueToInstall =false(用户refuse),调用 mArgs.doPreInstall(PackageManager.INSTALL_FAILED_MISSING_FEATURE); 代表失败;
--- 如果mContinueToInstall =true (用户agree 或者 不是adb) ,走原流程;
@Override
void handleReturnCode() {
// If mArgs is null, then MCS couldn't be reached. When it
// reconnects, it will try again to install. At that point, this
// will succeed.
if (mArgs != null) {
if (mContinueToInstall) {
processPendingInstall(mArgs, mRet);
} else {
mArgs.doPreInstall(PackageManager.INSTALL_FAILED_MISSING_FEATURE);
}
}
}
*************************************************************************************************************************************
1.PKMS 是通过与DefaultContainerService通信获取apk 对应的packInfo,而DefaultContainerService 最终也是通过PackageParser 来解析apk_path 对应的XXX.apk 对应的信息