Android Fk-PKMS(2) PackageManagerService之应用的安装与卸载

Android Fk-PKMS(2) PackageManagerService之应用的安装与卸载

原文:http://blog.csdn.net/taylorpotter/article/details/76166788

一、Apk安装卸载学习

1.应用的卸载概述

  android应用安装流程已经有很多大神分析过,而且写的相当详细精致,关于应用安装的详细流程可以看这篇博客,Android7.0 PackageManagerService (3) APK安装。博主结合代码流程图分析的相当详细,不乏经典之处,可以看出博主应该也是看过邓凡平的《深入理解Android》系列丛书,在邓凡平分析的基础上添加了自己的更深入的代码跟踪与理解,用来学习跟踪PKMS的安装流程相当好了;
  应用的安装方式主要有::
  1.应用市场下载进行静默安装;
  2.浏览器中下载点击安装,或者拷贝来apk点击安装
  3.adb install,adb shell pm install方式
  4.adb push/开机扫描安装方式
  5.三方应用调用PKMS安装接口进行安装
  
  其中前两种用户操作使用较多,后三种开发者使用的可能比较多些;
  应用市场的安装方式是采用了静默安装的方式,在应用市场中会调用种安装方式,如果第一种安装失败则使用第二种直至安装成功返回,而应用市场为达到静默安装的效果使用的安装方式无非就是使用脚本pm install的方式,或者调用pkms的静默安装接口,如果无法静默安装即无权限的话就只得调用系统自带的PackageInstaller进行安装,这种场景经常可以见到,比如手机Rom自带的市场就可以做到静默安装,而使用三方的市场就很大几率调出安装界面进行安装,用户体验的确差了很多,可见做平台厂商的优势;
  点击apk进行安装,调用的是PackageInstaller进行安装,浏览器下载apk完成有时为了提高用户体验也会主动去调用PackageInstaller进行安装,这种方式有安装界面;
  adb install 和adb shell pm install方式都是执行了system/bin/pm脚本进行安装;
  adb push多用于对系统应用的安装验证,需要开机重启才能完成安装生效;
  而调用PKMS接口进行安装就不多叙述了,应用市场也会使用这样的方式;
  事实上最终都需要调用PKMS的方法进行安装,只是到达PKMS的方式不同而已,下面将从PackageInstaller方式,adb install方式,adb push方式,及PKMS的通用安装过程来总结应用的安装流程。

2.应用卸载概述

  应用卸载方式主要分两类:
  1.普通三方应用的卸载,我们拖动桌面图标到垃圾桶即可以卸载;
  2.系统应用的卸载(即system/目录下的应用),当用户手动升级了系统应用,当用户觉得升级后的系统应用不好用时可以通过settings里的应用详情页上菜单按钮中的卸载还原进行系统应用的卸载,这个卸载操作菜单只在升级系统应用之后才会显示出来,并且卸载后会重新安装原始版本及ROM自带的该系统应用;
  
  本文主要基于Android 7.1.1的源码进行分析总结。

二、 APK安装总结

  首先应该如何定义apk的安装,自己理解,apk的安装就是需要将apk的执行文件拷贝到安装目录,然后解析apk,将apk中的组件信息统计起来,用于以后的组件调用管理,另外由于应用会产生用户数据,需要创建用户数据保存的地方,这样升级apk时只需要升级执行文件,用户数据还在。

1.调用PackageInstaller安装方式

1.1 从PKMS安装应用log开始

  查看将PKMS的log debug开关打开后打印的log,操作内容是将百度地图的apk拷贝到手机中,然后点击手机中的apk,调出PackageInstaller的安装界面进行安装:

07-30 10:33:44.409272  1646  1825 I PackageManager: init_copy idx=0: InstallParams{7ae63a2 file=/data/app/vmdl760150628.tmp cid=null}
07-30 10:33:44.775723  1646  1825 I PackageManager: mcs_bound
07-30 10:33:44.776727  1646  1825 I PackageManager: startCopy UserHandle{0}: InstallParams{7ae63a2 file=/data/app/vmdl760150628.tmp cid=null}
07-30 10:33:44.810103  1646  1825 D PackageManager: /data/app/vmdl760150628.tmp already staged; skipping copy
07-30 10:33:44.811681  1646  1825 D PackageManager: installPackageLI: path=/data/app/vmdl760150628.tmp
07-30 10:33:45.263513  1646  1825 D PackageManager: cluster install info.nativeLibraryRootDir:/data/app/vmdl760150628.tmp/lib
07-30 10:33:45.264690  1646  1825 D PackageManager: cluster install info.nativeLibraryDir:/data/app/vmdl760150628.tmp/lib/arm64
07-30 10:33:45.264996  1646  1825 D PackageManager: getPrimaryInstructionSetarm64
07-30 10:33:45.265373  1646  1825 D PackageManager: nativeLibraryRootStr:/data/app/vmdl760150628.tmp/lib
07-30 10:33:45.265727  1646  1825 D PackageManager: cpuAbiOverridenull
07-30 10:33:45.272850  1646  1825 D PackageManager: primaryCpuAbi already determined by nativeLibraryRootDir
07-30 10:33:45.274112  1646  1825 D PackageManager: cluster install info.nativeLibraryRootDir:/data/app/vmdl760150628.tmp/lib
07-30 10:33:45.275442  1646  1825 D PackageManager: cluster install info.nativeLibraryDir:/data/app/vmdl760150628.tmp/lib/arm
07-30 10:33:45.276030  1646  1825 D PackageManager: getPrimaryInstructionSetarm
07-30 10:34:07.310109  1646  1825 I PackageManager.DexOptimizer: DexoptNeeded for /data/app/vmdl760150628.tmp/base.apk@interpret-only is 1
07-30 10:34:07.359918  1646  1825 I PackageManager.DexOptimizer: Running dexopt (dex2oat) on: /data/app/vmdl760150628.tmp/base.apk pkg=com.baidu.BaiduMap isa=arm vmSafeMode=false debuggable=false target-filter=interpret-only oatDir = /data/app/vmdl760150628.tmp/oat sharedLibraries=null
07-30 10:35:18.565479  1646  1825 D PackageManager: Renaming /data/app/vmdl760150628.tmp to /data/app/com.baidu.BaiduMap-1
07-30 10:35:18.621789  1646  1825 D PackageManager: installNewPackageLI: Package{a3988b2 com.baidu.BaiduMap}
07-30 10:35:18.633012  1646  1825 D PackageManager: Scanning package com.baidu.BaiduMap
07-30 10:35:18.781099  1646  1825 D PackageManager: pkg.cpuAbiOverridenull
07-30 10:35:18.781787  1646  1825 D PackageManager: cluster install info.nativeLibraryRootDir:/data/app/com.baidu.BaiduMap-1/lib
07-30 10:35:18.782241  1646  1825 D PackageManager: cluster install info.nativeLibraryDir:/data/app/com.baidu.BaiduMap-1/lib/arm
07-30 10:35:18.782460  1646  1825 D PackageManager: getPrimaryInstructionSetarm
07-30 10:35:18.782792  1646  1825 D PackageManager: Resolved nativeLibraryRoot for com.baidu.BaiduMap to root=/data/app/com.baidu.BaiduMap-1/lib, isa=true
07-30 10:35:18.783156  1646  1825 D PackageManager: Abis for package[com.baidu.BaiduMap] are primary=armeabi-v7a secondary=null
07-30 10:35:18.830131  1646  1825 D PackageManager: Registered content provider: com.baidu.BaiduMap.bdpush, className = com.baidu.android.pushservice.PushInfoProvider, isSyncable = false
07-30 10:35:18.831063  1646  1825 D PackageManager: Registered content provider: com.baidu.lbs.offlinelocationprovider, className = com.baidu.location.OfflineLocationV1Provider, isSyncable = false
07-30 10:35:18.831458  1646  1825 D PackageManager:   Providers: com.baidu.android.pushservice.PushInfoProvider com.baidu.location.OfflineLocationV1Provider
07-30 10:35:18.838816  1646  1825 D PackageManager:   Services: com.baidu.baidunavis.ForegroundService com.baidu.baiduwalknavi.util.WbForegroundService com.baidu.mapframework.open.MapOpenService com.baidu.android.pushservice.CommandService com.baidu.sofire.MyService com.baidu.mapframework.sandbox.SandBoxService com.baidu.sapi2.share.ShareService com.baidu.location.f com.indooratlas.android.sdk.IALocationService com.baidu.location.wifihistory.SManager com.xiaomi.mipush.sdk.PushMessageHandler com.xiaomi.mipush.sdk.MessageHandleService com.baidu.android.pushservice.PushService com.baidu.mapframework.component3.update.ComUpdateService com.baidu.needle.loader.NeedleService com.baidu.needle.loader.NeedleService$InnerService com.baidu.baidumaps.route.RouteConditionService com.baidu.baidumaps.common.app.startup.StartMapService com.baidu.mapframework.favorite.database.FavDataService com.mapbox.telemetry.TelemetryService com.baidu.baiduwalknavi.routebook.database.RBDataService com.baidu.baidumaps.tra...
07-30 10:35:18.846468  1646  1825 D PackageManager:   Receivers: com.baidu.sofire.MyReceiver com.baidu.baidumaps.base.localmap.LMBroadcastReceiver com.baidu.mapframework.location.LocationStartListener com.baidu.baidumaps.push.BMPushMessageReceiver com.baidu.baidumaps.push.BMPushMainReceiver com.baidu.android.pushservice.PushPatchMessageReceiver com.baidu.android.pushservice.PushServiceReceiver com.baidu.android.pushservice.RegistrationReceiver com.baidu.mapframework.component3.update.ComUpdateReceiver com.baidu.needle.work.NeedleReceiver com.baidu.needle.work.PatchProcessReceiver com.baidu.baidumaps.track.receiver.CustomTrackReceiver com.baidu.baidumaps.route.util.ArrivalRemindReceiver com.baidu.baidumaps.alarm.AlarmReceiver com.baidu.mapframework.common.galaxy.GalaxyReceiver com.baidu.mecp.link.bc.server.MecpBcReceiver
07-30 10:35:18.868776  1646  1825 D PackageManager:   Activities: com.baidu.baidumaps.WelcomeScreen com.baidu.baidumaps.guide.NewGuideScreen com.baidu.scenedemo.demo.SceneActivity com.baidu.mapframework.common.video.BMLRRecordVideoActivity com.baidu.mapframework.common.video.BMLRVideoPlayActivity com.baidu.baidumaps.guide.NewUserGuide com.baidu.mapframework.ui.SinaWeiboTask com.baidu.baidumaps.MapsActivity com.baidu.baidunavis.ui.BNVoiceMainActivity com.baidu.baidunavis.ui.BNVoiceDetailActivity com.baidu.baidunavis.ui.BNVoiceSquareActivity com.baidu.baidunavis.ui.widget.WebShellActivity com.baidu.BaiduMap.wxapi.WXEntryActivity com.baidu.BaiduMap.wxapi.WXPayEntryActivity com.baidu.sapi2.ui.activity.LoginActivity com.baidu.sapi2.ui.activity.FillUnameActivity com.baidu.sapi2.ui.activity.ImagePickerActivity com.baidu.sapi2.ui.activity.ImageCropActivity com.baidu.sapi2.ui.activity.ImageRecommendActivity com.baidu.sapi2.ui.activity.UserProfileActivity com.baidu.sapi2.ui.activity.SmsLoginActivity ...
07-30 10:35:18.870555  1646  1825 D PackageManager:   Permissions: baidu.push.permission.WRITE_PUSHINFOPROVIDER.com.baidu.BaiduMap com.baidu.BaiduMap.CALL_MAIN_PROCESS_RECEIVER com.baidu.BaiduMap.permission.MIPUSH_RECEIVE com.baidu.BaiduMap.permission.SANDBOX_PERMISSION
07-30 10:35:24.430637  1646  1825 D PackageManager: New package installed in /data/app/com.baidu.BaiduMap-1
07-30 10:35:31.348520  1646  1825 V PackageManager: prepareAppData for com.baidu.BaiduMap u0 0x3
07-30 10:35:31.453326  1646  1825 V PackageManager: + starting restore round-trip 1
07-30 10:35:31.453696  1646  1825 V PackageManager: No restore - queue post-install for 1
07-30 10:35:31.454134  1646  1825 I PackageManager: mcs_unbind
07-30 10:35:31.454256  1646  1825 I PackageManager: calling disconnectService()
07-30 10:35:31.503445  1646  1825 D PackageManager: Checking for userId:0 if any IntentFilter from the 106 Activities needs verification ...
07-30 10:35:31.640530  1646  1825 D PackageManager: No filters or not all autoVerify for com.baidu.BaiduMap
07-30 10:35:31.641580  1646  1825 V PackageManager: Handling post-install for 1

  从上面的log中已经可以看出应用安装的大概过程:
Ⅰ.拷贝安装包至data/app下的临时文件夹下
  显示startcopy将数据拷贝到/data/app/,由于已经拷过跳过拷贝动作:

PackageManager: init_copy idx=0: InstallParams{7ae63a2 file=/data/app/vmdl760150628.tmp cid=null}
07-30 10:33:44.775723  1646  1825 I PackageManager: mcs_bound
07-30 10:33:44.776727  1646  1825 I PackageManager: startCopy UserHandle{0}: InstallParams{7ae63a2 file=/data/app/vmdl760150628.tmp cid=null}
07-30 10:33:44.810103  1646  1825 D PackageManager: /data/app/vmdl760150628.tmp already staged; skipping copy

  此时拷贝后的是vmdl965216119.tmp,它是拷贝生成中间文件夹,在”data/app”下,用来下一步的安装操作,至于为什么要使用这个名字这里有篇文章指出:

07-30 10:33:44.811681  1646  1825 D PackageManager: installPackageLI: path=/data/app/vmdl760150628.tmp

  我们可以看到这个文件夹中有拷贝过来的apk和抽取出来的lib文件夹:

《Android Fk-PKMS(2) PackageManagerService之应用的安装与卸载》

Ⅱ.为apk做dexopt优化

07-30 10:34:07.310109  1646  1825 I PackageManager.DexOptimizer: DexoptNeeded for /data/app/vmdl760150628.tmp/base.apk@interpret-only is 1
07-30 10:34:07.359918  1646  1825 I PackageManager.DexOptimizer: Running dexopt (dex2oat) on: /data/app/vmdl760150628.tmp/base.apk pkg=com.baidu.BaiduMap isa=arm vmSafeMode=false debuggable=false target-filter=interpret-only oatDir = /data/app/vmdl760150628.tmp/oat sharedLibraries=null

  通过log来看应该是生成在vmdl965216119.tmp/oat下,我们查看下文件,果然生成了优化后的odex文件:

《Android Fk-PKMS(2) PackageManagerService之应用的安装与卸载》

Ⅲ.临时文件夹重命名为包名
  然后就是重新命名了重新命名后vmdl965216119.tmp/变成/data/app/com.demo.mytest-1:

07-30 10:35:18.565479  1646  1825 D PackageManager: Renaming /data/app/vmdl760150628.tmp to /data/app/com.baidu.BaiduMap-1

  再查看”data/app”下的文件,发现的确重新命名成了包名:

《Android Fk-PKMS(2) PackageManagerService之应用的安装与卸载》

Ⅳ.统计apk组件信息

  文件拷贝好了开始了组件信息的统计,通过解析apk的AndroidManifest.xml文件来进行组件的解析统计。这里统计到PKMS中的变量,也更新到settings中:

07-30 10:35:18.621789  1646  1825 D PackageManager: installNewPackageLI: Package{a3988b2 com.baidu.BaiduMap}
07-30 10:35:18.633012  1646  1825 D PackageManager: Scanning package com.baidu.BaiduMap
...
7-30 10:35:18.830131  1646  1825 D PackageManager: Registered content provider: com.baidu.BaiduMap.bdpush, className = com.baidu.android.pushservice.PushInfoProvider, isSyncable = false
07-30 10:35:18.831063  1646  1825 D PackageManager: Registered content provider: com.baidu.lbs.offlinelocationprovider, className = com.baidu.location.OfflineLocationV1Provider, isSyncable = false
07-30 10:35:18.831458  1646  1825 D PackageManager:   Providers: com.baidu.android.pushservice.PushInfoProvider com.baidu.location.OfflineLocationV1Provider
07-30 10:35:18.838816  1646  1825 D PackageManager:   Services: com.baidu.baidunavis.ForegroundService com.baidu.baiduwalknavi.util.WbForegroundService com.baidu.mapframework.open.MapOpenService com.baidu.android.pushservice.CommandService com.baidu.sofire.MyService com.baidu.mapframework.sandbox.SandBoxService com.baidu.sapi2.share.ShareService com.baidu.location.f com.indooratlas.android.sdk.IALocationService com.baidu.location.wifihistory.SManager com.xiaomi.mipush.sdk.PushMessageHandler com.xiaomi.mipush.sdk.MessageHandleService com.baidu.android.pushservice.PushService com.baidu.mapframework.component3.update.ComUpdateService com.baidu.needle.loader.NeedleService com.baidu.needle.loader.NeedleService$InnerService com.baidu.baidumaps.route.RouteConditionService com.baidu.baidumaps.common.app.startup.StartMapService com.baidu.mapframework.favorite.database.FavDataService com.mapbox.telemetry.TelemetryService com.baidu.baiduwalknavi.routebook.database.RBDataService com.baidu.baidumaps.tra...
07-30 10:35:18.846468  1646  1825 D PackageManager:   Receivers: com.baidu.sofire.MyReceiver com.baidu.baidumaps.base.localmap.LMBroadcastReceiver com.baidu.mapframework.location.LocationStartListener com.baidu.baidumaps.push.BMPushMessageReceiver com.baidu.baidumaps.push.BMPushMainReceiver com.baidu.android.pushservice.PushPatchMessageReceiver com.baidu.android.pushservice.PushServiceReceiver com.baidu.android.pushservice.RegistrationReceiver com.baidu.mapframework.component3.update.ComUpdateReceiver com.baidu.needle.work.NeedleReceiver com.baidu.needle.work.PatchProcessReceiver com.baidu.baidumaps.track.receiver.CustomTrackReceiver com.baidu.baidumaps.route.util.ArrivalRemindReceiver com.baidu.baidumaps.alarm.AlarmReceiver com.baidu.mapframework.common.galaxy.GalaxyReceiver com.baidu.mecp.link.bc.server.MecpBcReceiver
07-30 10:35:18.868776  1646  1825 D PackageManager:   Activities: com.baidu.baidumaps.WelcomeScreen com.baidu.baidumaps.guide.NewGuideScreen com.baidu.scenedemo.demo.SceneActivity com.baidu.mapframework.common.video.BMLRRecordVideoActivity com.baidu.mapframework.common.video.BMLRVideoPlayActivity com.baidu.baidumaps.guide.NewUserGuide com.baidu.mapframework.ui.SinaWeiboTask com.baidu.baidumaps.MapsActivity com.baidu.baidunavis.ui.BNVoiceMainActivity com.baidu.baidunavis.ui.BNVoiceDetailActivity com.baidu.baidunavis.ui.BNVoiceSquareActivity com.baidu.baidunavis.ui.widget.WebShellActivity com.baidu.BaiduMap.wxapi.WXEntryActivity com.baidu.BaiduMap.wxapi.WXPayEntryActivity com.baidu.sapi2.ui.activity.LoginActivity com.baidu.sapi2.ui.activity.FillUnameActivity com.baidu.sapi2.ui.activity.ImagePickerActivity com.baidu.sapi2.ui.activity.ImageCropActivity com.baidu.sapi2.ui.activity.ImageRecommendActivity com.baidu.sapi2.ui.activity.UserProfileActivity com.baidu.sapi2.ui.activity.SmsLoginActivity ...
07-30 10:35:18.870555  1646  1825 D PackageManager:   Permissions: baidu.push.permission.WRITE_PUSHINFOPROVIDER.com.baidu.BaiduMap com.baidu.BaiduMap.CALL_MAIN_PROCESS_RECEIVER com.baidu.BaiduMap.permission.MIPUSH_RECEIVE com.baidu.BaiduMap.permission.SANDBOX_PERMISSION

Ⅴ.为该apk准备data的文件夹

07-30 10:35:31.348520  1646  1825 V PackageManager: prepareAppData for com.baidu.BaiduMap u0 0x3

  查看data/data下的生成了用户数据的文件夹:

《Android Fk-PKMS(2) PackageManagerService之应用的安装与卸载》

Ⅵ.安装完成执行post-install结束

07-30 10:35:31.453696  1646  1825 V PackageManager: No restore - queue post-install for 1
07-30 10:35:31.454134  1646  1825 I PackageManager: mcs_unbind
07-30 10:35:31.454256  1646  1825 I PackageManager: calling disconnectService()
07-30 10:35:31.641580  1646  1825 V PackageManager: Handling post-install for 1

  这其实已经就看到了大概的apk安装流程,知道安装过程中以下5个步骤是必不可少的重要步骤(顺序不一定是这个顺序)
    1.将安装包apk拷贝到安装目录
    2.提取安装包中的lib库文件至对应的lib目录下
    3.对apk做dexopt优化
    4.解析并统计apk中的组件信息
    5.创建用户数据目录

1.2 PackageInstaller安装方式的代码过程

1.2.1 点击应用调用PackageInstaller安装:

《Android Fk-PKMS(2) PackageManagerService之应用的安装与卸载》

  点击apk安装包后调起PackageInstaller主界面,点击确定安装后将调起InstallAppProgress的安装中的界面,在这个界面的activity中跨进程调用了PackageInstaller,创建了PackageInstallerSession对象,PackageInstallerSession是对安装通信过程的封装,本身也是一个远程服务;

  通过PackageInstaller和PackageInstallerSession主要完成了:

   a.拷贝安装包至”/data/app”下的临时文件夹下,类似于上面提到的”/data/app/vmdl760150628.tmp”

   b.抽取native的lib库至临时文件夹下的lib文件夹下

  之后调用到PKMS继续下面的安装过程;

1.2.2 PKMS安装过程:

《Android Fk-PKMS(2) PackageManagerService之应用的安装与卸载》

  从上图中看出主要完成安装的剩下的几个重要步骤:
   c. 给apk做odex优化,生在”/data/app/vmdl760150628.tmp/oat”下;
   d. 重命名临时文件夹为包名,”/data/app/com.baidu.BaiduMap-1”,其中后面的-1是覆盖安装过或升级过的次数,从1开始;
   e. 统计apk中的组件信息至PKMS及Settings,通过解析apk中的AndroidManifest.xml文件来实现;
   f. 创建应用的用户数据文件夹,生成位置:”/data/data/包名”,即”com.baidu.BaiduMap”,创建用户数据文件夹最终会调用Installd的方法完成;
   g. 发出完成安装的通知,通过广播和监听回调通知系统及其他监听者。

  即,点击apk调用PackageInstaller安装,最终调用到PKMS安装方法完成安装。

2.adb install安装方式

  “adb install”安装和”adb shell pm install”是一样的都是调用了位于system/bin/下的pm脚本去执行install的操作,pm脚本最终将调用到java层的Pm.java的main函数,接着由Pm调用到PKMS中的安装服务实现安装的过程;
  

2.1.adb到执行pm脚本

《Android Fk-PKMS(2) PackageManagerService之应用的安装与卸载》

  1.adb install执行到commandline.cpp中的adb_commandline()函数,在该函数中选择执行相应adb 后面携带的命令的方法,这里执行的是”install”命令最终调用到commandline.cpp中的install_app_legacy()函数:

//system/core/adb/commandline.cpp
static int install_app_legacy(TransportType transport, const char* serial, int argc, const char** argv) {
    //待安装的APK目前还在源机器上,现在需要把APK的文件复制到手机里
    //如果安装在手机内部存储,那么目的地址为DATA_DEST
    //如果安装在SD卡上,则目的地址为SD_DEST
    static const char *const DATA_DEST = "/data/local/tmp/%s";
    static const char *const SD_DEST = "/sdcard/tmp/%s";
    .........
    //默认安装到手机内部
    const char* where = DATA_DEST;
    for (i = 1; i < argc; i++) {
        //携带参数-s时,才安装到SD卡
        if (!strcmp(argv[i], "-s")) {
            where = SD_DEST;
        }
    }

    //解析参数,判断adb命令中是否携带了有效的apk文件名
    ...........

    //取出apk名
    std::vector<const char*> apk_file = {argv[last_apk]};
    //构造apk目的地址
    std::string apk_dest = android::base::StringPrintf(
            where, adb_basename(argv[last_apk]).c_str());

    //do_sync_push将此APK文件传输到手机的目标路径,失败的话将跳转到clenaup_apk
    if (!do_sync_push(apk_file, apk_dest.c_str())) goto cleanup_apk;

    //执行pm_command
    result = pm_command(transport, serial, argc, argv);

cleanup_apk:
    //删除刚才传输的文件
    //PKMS在安装过程中会将该APK复制一份到/data/app目录下,所有data/local/tmp目录下对应的文件可以删除
    delete_file(transport, serial, apk_dest);
    return result;
}

  从代码来看,传统的安装方式就是将源机器中的APK文件拷贝到目的手机的data/local/tmp目录下,然后调用pm_command进行处理。pm_command()函数将执行system/bin下的shell脚本pm,可以看下pm脚本的内容:

《Android Fk-PKMS(2) PackageManagerService之应用的安装与卸载》

  2.pm脚本执行app_process中的main函数,传入Pm的类名及终端输入的参数

  3.app_process将通过jni调用到java层的Pm.jar中的Pm.java的main函数

  (如
这篇博客中关于pm脚本的解析,这种从终端调用到系统中java层的函数的方式的确很牛逼的感觉,可以尝试仿写自己的需要的功能;)

  在Pm.java的main函数中调用Pm的run()函数

//frameworks/base/cmds/pm/src/com/android/commands/pm/Pm.java
 public static void main(String[] args) {
        try {
            exitCode = new Pm().run(args);
        } catch (Exception e) { }
        }
    }

  Pm的run()函数中:

//frameworks/base/cmds/pm/src/com/android/commands/pm/Pm.java
  public int run(String[] args) throws RemoteException {
        boolean validCommand = false;
        mAm = IAccountManager.Stub.asInterface(ServiceManager.getService(Context.ACCOUNT_SERVICE));
        mUm = IUserManager.Stub.asInterface(ServiceManager.getService(Context.USER_SERVICE));
        mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));

        mInstaller = mPm.getPackageInstaller();
        mArgs = args;
        String op = args[0];
        mNextArg = 1;
        if ("list".equals(op)) { return runList();}
        ...
        if ("install".equals(op)) {return runInstall();}
        if ("uninstall".equals(op)) {return runUninstall();}
    ...
    }

  从代码中可以知Pm提供了多种包管理的操作,如常用的list,uninstall等,根据不同指令走不同实现方法,这里当然是走runInstall()方法;
 

2.2Pm到PKMS

《Android Fk-PKMS(2) PackageManagerService之应用的安装与卸载》

  从Pm.java到PKMS的过程中,和PackageInstaller有点类似,同样是利用PackageInstaller构造一个PackageInstallSession的安装会话工具,通过对PackageInstallerSession的操作,在调用到PKMS方法之前完成了:

  a.文件从”/local/tmp/”下拷贝到”data/app/”下的类似于”vmdl760150628.tmp”的临时文件夹;

  b.完成了lib库的抽取工作,抽取到临时文件下对应的lib文件夹下;

然后调用PKMS完成下面的安装操作;

2.3PKMS的安装

  PKMS的安装过程就和第一种PackageInstaller安装时相同了,如上调用了installStage()接口完成:
   c. 给apk做dexopt优化操作
   d. 更改临时文件夹名为应用包名
   e. 解析统计安装包中的组件信息
   f. 创建用户数据文件夹
   g. 发出安装完成的通知
 即,adb install,adb shell pm install,都是调用system/bin/pm脚本,反射调用到Pm.jar再调用到PKMS完成安装。
  

3.adb push/开机扫描安装方式

3.1 首次开机系统应用的安装:

  由于编译生成system.img之后就已经将system的apk文件拷贝到了对应的system目录下,如”system/app/”,”system/priv-app”,也就是说开机安装系统app可以省略拷贝apk的动作
  从上一篇学习PKMS初始化的过程中知道PKMS在开机初始化时会调用在scanDirTracedLI()方法去扫描system/app等下面的目录,最终调用scanPackageDirtyLI()方法,
  由上面分析知道scanPackageDirtyLI()函数中完成扫描统计apk中的组件信息的操作;
  而在PKMS初始化的构造函数中,在scanDirTracedLI()函数之后我们看到有对system下的应用进行dexopt的操作:

//frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
public PackageManagerService(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) {
        ...
         final File systemAppDir = new File(Environment.getRootDirectory(), "app");
                    scanDirTracedLI(systemAppDir, mDefParseFlags
                            | PackageParser.PARSE_IS_SYSTEM
                            | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
        ...
         if ((isFirstBoot() || isUpgrade() || VMRuntime.didPruneDalvikCache()) && !onlyCore) {
                List<PackageParser.Package> coreApps = new ArrayList<>();
                for (PackageParser.Package pkg : mPackages.values()) {
                    if (pkg.coreApp) {//scanPackageDirtyLI()函数一开始就将pkg.coreApp设置为系统应用为true
                        coreApps.add(pkg);
                    }
                }
                //执行dexopt操作
                int[] stats = performDexOptUpgrade(coreApps, false,
                        getCompilerFilterForReason(REASON_CORE_APP));
          }
         ...
}

  在系统编译时有mk文件中可以选择是否生成odex文件,具体可以参考这边博客。因此在非eng版本上(编译时做odex优化)系统apk会在编译时在system下的apk目录下生成dexopt后生成的odex文件;

《Android Fk-PKMS(2) PackageManagerService之应用的安装与卸载》

  如果编译时没有生成odex文件,将在第一次开机时做dexopt优化,此时dexopt优化后的文件放在/data/dalvik-cache目录下。

  经过开机的PKMS的初始化,即完成了系统应用的开机安装过程。

3.2 adb push方式

  通常我们通过adb push方式验证系统应用,(这里指的系统应用是指system目录下的应用apk),从上面知道使用adb install的方式和packageInstaller的方式会将apk安装到”data/app”目录下,而android对于data目录和system目录的权限是不同的,解析时解析AndroidManifest.xml的原则也是不同的,因此系统应用使用adb push到system对应的app目录下才能正确的获取对应的权限;
  当然使用adb push方式将apk push到”data/app”下也是可以的,但需要重启完成扫描安装;
  
  即,使用adp push的方式需要进行重启操作完成安装。

三、 APK卸载总结

  同样,先来看下如何理解卸载的过程,自己的理解,卸载就是要清理该应用的在设备上的数据,这些数据有应用的安装目录下的文件,使用时产生的数据文件,当然还有统计在PKMS,Settings等中相关的应用组件配置信息等;(对于使用产生的用户数据也可以选择不清除,毕竟用户数据还是很重要的,通过PackageManager.DELETE_KEEP_DATA这个flag进行判断,具体细节就不赘述了);
  从Launcher上拖动卸载apk的PKMS的log:

07-31 14:04:12.885  1641  3056 D PackageManager: deletePackageAsUser: pkg=com.baidu.BaiduMap user=0 deleteAllUsers: false
07-31 14:04:12.887  1641  1940 D PackageManager: deletePackageX: pkg=com.baidu.BaiduMap user=0
07-31 14:04:12.888  1641  1940 D PackageManager: deletePackageLI: com.baidu.BaiduMap user UserHandle{0}
07-31 14:04:12.888  1641  1940 D PackageManager: Marking package:com.baidu.BaiduMap uninstalled for user:0
07-31 14:04:12.888  1641  1940 D PackageManager: Not installed by other users, full delete
07-31 14:04:12.888  1641  1940 D PackageManager: Removing non-system package: com.baidu.BaiduMap
07-31 14:04:12.888  1641  1940 D PackageManager: removePackageDataLI: PackageSetting{33cf7ce com.baidu.BaiduMap/10103}
07-31 14:04:12.889  1641  1940 D PackageManager: Removing package com.baidu.BaiduMap
07-31 14:04:12.889  1641  1940 D PackageManager: Unregistered content provider: com.baidu.BaiduMap.bdpush, className = com.baidu.android.pushservice.PushInfoProvider, isSyncable = false
07-31 14:04:12.889  1641  1940 D PackageManager: Unregistered content provider: com.baidu.lbs.offlinelocationprovider, className = com.baidu.location.OfflineLocationV1Provider, isSyncable = false
07-31 14:04:12.889  1641  1940 D PackageManager:   Providers: com.baidu.android.pushservice.PushInfoProvider com.baidu.location.OfflineLocationV1Provider
07-31 14:04:12.889  1641  1940 D PackageManager:   Services: com.baidu.baidunavis.ForegroundService com.baidu.baiduwalknavi.util.WbForegroundService com.baidu.mapframework.open.MapOpenService com.baidu.android.pushservice.CommandService com.baidu.sofire.MyService com.baidu.mapframework.sandbox.SandBoxService com.baidu.sapi2.share.ShareService com.baidu.location.f com.indooratlas.android.sdk.IALocationService com.baidu.location.wifihistory.SManager com.xiaomi.mipush.sdk.PushMessageHandler com.xiaomi.mipush.sdk.MessageHandleService com.baidu.android.pushservice.PushService com.baidu.mapframework.component3.update.ComUpdateService com.baidu.needle.loader.NeedleService com.baidu.needle.loader.NeedleService$InnerService com.baidu.baidumaps.route.RouteConditionService com.baidu.baidumaps.common.app.startup.StartMapService com.baidu.mapframework.favorite.database.FavDataService com.mapbox.telemetry.TelemetryService com.baidu.baiduwalknavi.routebook.database.RBDataService com.baidu.baidumaps.track....
07-31 14:04:12.890  1641  1940 D PackageManager:   Receivers: com.baidu.sofire.MyReceiver com.baidu.baidumaps.base.localmap.LMBroadcastReceiver com.baidu.mapframework.location.LocationStartListener com.baidu.baidumaps.push.BMPushMessageReceiver com.baidu.baidumaps.push.BMPushMainReceiver com.baidu.android.pushservice.PushPatchMessageReceiver com.baidu.android.pushservice.PushServiceReceiver com.baidu.android.pushservice.RegistrationReceiver com.baidu.mapframework.component3.update.ComUpdateReceiver com.baidu.needle.work.NeedleReceiver com.baidu.needle.work.PatchProcessReceiver com.baidu.baidumaps.track.receiver.CustomTrackReceiver com.baidu.baidumaps.route.util.ArrivalRemindReceiver com.baidu.baidumaps.alarm.AlarmReceiver com.baidu.mapframework.common.galaxy.GalaxyReceiver com.baidu.mecp.link.bc.server.MecpBcReceiver
07-31 14:04:12.892  1641  1940 D PackageManager:   Activities: com.baidu.baidumaps.WelcomeScreen com.baidu.baidumaps.guide.NewGuideScreen com.baidu.scenedemo.demo.SceneActivity com.baidu.mapframework.common.video.BMLRRecordVideoActivity com.baidu.mapframework.common.video.BMLRVideoPlayActivity com.baidu.baidumaps.guide.NewUserGuide com.baidu.mapframework.ui.SinaWeiboTask com.baidu.baidumaps.MapsActivity com.baidu.baidunavis.ui.BNVoiceMainActivity com.baidu.baidunavis.ui.BNVoiceDetailActivity com.baidu.baidunavis.ui.BNVoiceSquareActivity com.baidu.baidunavis.ui.widget.WebShellActivity com.baidu.BaiduMap.wxapi.WXEntryActivity com.baidu.BaiduMap.wxapi.WXPayEntryActivity com.baidu.sapi2.ui.activity.LoginActivity com.baidu.sapi2.ui.activity.FillUnameActivity com.baidu.sapi2.ui.activity.ImagePickerActivity com.baidu.sapi2.ui.activity.ImageCropActivity com.baidu.sapi2.ui.activity.ImageRecommendActivity com.baidu.sapi2.ui.activity.UserProfileActivity com.baidu.sapi2.ui.activity.SmsLoginActivity com...
07-31 14:04:12.892  1641  1940 D PackageManager:   Permissions: baidu.push.permission.WRITE_PUSHINFOPROVIDER.com.baidu.BaiduMap com.baidu.BaiduMap.CALL_MAIN_PROCESS_RECEIVER com.baidu.BaiduMap.permission.MIPUSH_RECEIVE com.baidu.BaiduMap.permission.SANDBOX_PERMISSION
07-31 14:04:12.904  1641  1940 I PackageManager: Removing old permission: baidu.push.permission.WRITE_PUSHINFOPROVIDER.com.baidu.BaiduMap from package com.baidu.BaiduMap
07-31 14:04:12.904  1641  1940 I PackageManager: Removing old permission: com.baidu.BaiduMap.CALL_MAIN_PROCESS_RECEIVER from package com.baidu.BaiduMap
07-31 14:04:12.904  1641  1940 I PackageManager: Removing old permission: com.baidu.BaiduMap.permission.SANDBOX_PERMISSION from package com.baidu.BaiduMap
07-31 14:04:12.904  1641  1940 I PackageManager: Removing old permission: com.baidu.BaiduMap.permission.MIPUSH_RECEIVE from package com.baidu.BaiduMap

  基本上也就看出了卸载的具体过程:
   1.删掉安装的package
   2.删掉该package的data数据文件夹
   3.更新PKMS的组件信息,更新Permission信息

《Android Fk-PKMS(2) PackageManagerService之应用的安装与卸载》

  卸载的某些操作还需要通过installd来完成,关于卸载过程暂时不详细叙述,卸载也是个很大的过程,主要注意以下几个问题,以后遇到实际问题再做深入学习更新:

   1.多用户应用卸载问题;

   2.是否有其他apk依赖该卸载的应用,比如该卸载的A应用含有其他应用使用的sharelibrary库等(这时候卸载A会导致致使用该库的应用或应用强制被kill掉);

   3.是否是系统应用,在settings里有一个系统应用还原的功能,卸载系统应用会重新安装初始版本的应用,
这里有一个具体的系统应用卸载问题可以参考阅读;

四、本篇总结

  1. 本篇仅仅讲述了大概的应用的安装过程卸载过程,这其中还是有很多细节需要平时再看的,比如安装时的签名检查过程,应用的升级过程,安装包安全检查问题等还有安装卸载过程中很多实现需要借助installd的方法,tag一下,日后继续学习;

  2. 附件是本人自己跟踪PKMS的时序图标注了一些重点,其实太大了应该就没人想看了,不过平时找问题查询还是可以的,顺便将uml的源码也奉上,方便发现有问题或者发现新的重点也可以自己进行修改添加;
附件:
Ⅰ.adb install方式从adb到PKMS:
点击查看大图

《Android Fk-PKMS(2) PackageManagerService之应用的安装与卸载》

Ⅱ.PackageInstaller安装到PKMS的过程:

点击查看大图

《Android Fk-PKMS(2) PackageManagerService之应用的安装与卸载》

Ⅲ.PKMS中的具体安装过程:

点击查看大图

《Android Fk-PKMS(2) PackageManagerService之应用的安装与卸载》

Ⅳ.PKMS中卸载的具体过程:

点击查看大图

《Android Fk-PKMS(2) PackageManagerService之应用的安装与卸载》

    原文作者:TaylorPotter
    原文地址: https://blog.csdn.net/taylorpotter/article/details/76166788
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞