Android通过PackageManagerService(后面简称Pms)进行包管理,其主要功能包括:用户ID分配、包解析、包的安装卸载等。本文不对Pms进行分析,主要目的是探讨一下包安装。在本文中主要探讨包安装的相关操作,卸载作为安装的逆过程,实现类似,不再赘述。
Android中APK的安装方式
在Android中APK的安装有三种方式:
1、开机Pms初始化时,扫描包安装目录。
@/frameworks/base/services/java/com/android/server/SystemServer.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
@/frameworks/base/services/java/com/android/server/pm/PackageManagerService.java
1 2 3 4 5 6 7 |
|
下面是Pms构造函数的实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
|
通过Pms的构造函数可以看出,Pms在初始化时会扫描/system/app、vender/app、/data/app、/data/app-private四个应用安装目录,然后调用sanDirLI方法进行安装。Pms通过AppDirObserver对这四个应用安装目录进行监控,一旦发现APK格式的文件则会调用scanPackageLI进行安装。
2、通过包安装器PackageInstaller安装
Android提供了一个默认的包安装器,位于/package/app/PackageInstaller目录。通过其Manifest文件可以看出,PackageInstaller会对我们安装应用发出的Intent进行处理,这里PackageInstaller提供了两种处理方式,分别是:file方式和package方式。
@/package/app/PackageInstaller/AndroidManifest.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
@/package/app/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
1 2 3 4 5 6 7 8 |
|
1 2 3 4 5 |
|
在startInstallConfirm方法中点击“确认”后,会发出一个Intent,接收者为InstallAppProgress。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
@/package/app/PackageInstaller/src/com/android/packageinstaller/InstallAppProgress.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
|
InstallAppProgress即应用安装过程中的进度条界面。通过上面的代码可以看到在initView方法的最后会调用Pms的installPackageWithVerificationAndEncryption方法进行安装。
3、通过adb命令安装
adb命令pm是Pms的Shell客户端,通过pm可以进行包相关的一些操作,包括安装和卸载。pm命令的用法如下:
pm的代码实现在Pm.java中,如下:
@/frameworks/base/cmds/pm/src/com/android/commands/pm/Pm.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
在run方法中初始化了一个Pms的客户端代理对象mPm,后续的相关操作将有mPm完成。下面看一下Pm中负责安装的方法runInstall的代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
|
可以看出runInstall最终会调用Pms的installPackageWithVerificationAndEncryption方法进行安装。通过pm安装时,安装成功的返回信息为“Success”,安装失败的返回信息为”Failure[失败信息]"。
静默安装实现
在了解了Android中包安装的方式后,接下来探讨一些如何实现”静默安装“。所谓静默安装即跳过安装界面和进度条,在不被用户察觉的情况下载后台安装。下面针对上面的三种安装方式分别来分析如何实现静默安装。
1、push安装包到应用安装目录的方式
在Pms初始化时安装包的流程中,我们知道Pms会监控/system/app、vender/app、/data/app、/data/app-private这四个应用安装目录。因此如果能够将APK文件push进应用安装目录不就可以触发AppDirObserver中的包安装逻辑了了吗?所以这种思路理论上是行得通的,但有两个局限:
- 局限一:如下图所示,/system/app的访问权限为root,这就要求在push到/system/app目录时必须具有root权限。
而/data/app的访问权限为system。要获得system权限就要求使用这种方式的应用程序必须签名为platform并且sharedUserId制定为“android.uid.system”。
- 局限二:系统应用(/system/app)与普通应用(/data/app)的安装方式是不同的,对于系统应用,所有资源都包含在apk这个zip包中,而且其在/system/app不必以包名命名(理论上可以随便起名)。
而对于普通应用安装后,它的dex、lib、资源文件(安装包)分别存放在不同的目录,并且安装后以packagename-x.apk的形式保存在/data/app目录下。?
那这种安装方式是不是就没有用了呢?非也。
网上有些电子市场或管家类软件实现的”秒装“功能应该就是安装这个思路实现的,当然这里只是猜测,需要进一步研究。
2、调用Pm隐藏API
Android实现了一个应用安装器的APK负责包的安装工作,在上面的分析中我们知道,PackageInstaller的工作实际上只是安装界面、权限确认、进度显示等,真正的安装工作依然是调用Pms实现的。到这里我们就有了第二种思路,能不能绕过安装界面,直接调用Pms里面的相应方法呢?当然可以,PackageManager类中就提供了这样的方法:
@/frameworks/base/core/java/android/content/pm/PackageManager.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
可以看出,这个方法是hide的,因此在应用开发时如果要使用,必须通过反射。
这里的IPackageInstallObserver是installPackage方法的一个回调接口通知,其实现在IPackageInstallObserver.aidl中,如下:
@/frameworks/base/core/java/com/android/content/pm/IPackageInstallObserver.aidl
1 2 3 4 5 6 7 8 9 |
|
使用Android内置未公开API有两种方法:一种是通过反射的方式实现;另一种是在工程目录下建立与所引用系统类相同的类和方法,这里只要求类和方法名相同,不需要实现,只保证编译时不报错就可以了,根据Java的类加载机制,在运行时,会去加载系统类。下面是采用第二种方法时的两段示例代码:
实现接口回调的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
|
调用PackageManager.java隐藏方法,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
|
这种方法也有一定的限制:
首先,要在AndroidManifest.xml中声明”android.permission.INSTALL_PACKAGES”权限;
其次,应用需要system权限。
3、调用pm命令进行安装
在adb窗口通过pm install安装包本来就是没有安装界面的,这不正是我们想要的吗?通过pm的安装方式需要取得root或system权限。
pm的安装方式有两种,一种需要root权限,示例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
|
另一钟需要system权限,示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
|
关于system权限的获取在介绍push方式的安装时已做介绍。上面的代码只给出了比较核心的部分,在实际实现中,对返回结果的处理同样重要。
Android应用安装