从源码角度解析Android中APK的安装过程
1. Android中APK简介
Android应用Apk的安装有如下四种方式:
1.1 系统应用安装
没有安装界面,在开机时自动完成
1.2 网络下载应用安装
没有安装界面,在应用市场完成
1.3 ADB命令安装
没有安装界面,通过命令直接安装
1.4 外部设备安装
有安装界面,通过SD卡等外部设备安装,由packageInstaller处理安装逻辑
2. APK安装涉及到的几个常用目录
-
system/app : 系统自带的应用程序,获得root权限才能删除
-
data/app : 用户程序安装目录,安装时会把apk文件复制到此目录下
-
data/data : 存放应用程序的数据
-
data/dalvik-cache : 将apk中的dex文件安装到该目录下(dex文件是dalvik虚拟机的可执行文件,大小约为原始apk的四分之一)
3. APK安装的四大步骤
(1)拷贝apk到指定的目录
默认情况下,用户安装的apk首先会拷贝到/data/app下,用户有访问/data/app目录的权限,但系统出厂的apk文件
会被放到/system分区下,包括/system/app,/system/vendor/app,以及/system/priv-app等。该分区需要
root权限的用户才能访问。
(2)加压apk、拷贝文件、创建应用的数据目录
为了加快APP的启动速度,apk在安装的时候,会首先将APP的可执行文件(dex)拷贝到/data/dalvik-cache目录
下,缓存起来。再在/data/data/目录下创建应用程序的数据目录(以应用包名命令),用来存放应用的数据库、xml
文件、cache、二进制的so动态库等。
(3)解析apk的AndroidManifest.xml文件
在安装apk的过程中,会解析apk的AndroidManifest.xml文件,将apk的权限、应用包名、apk的安装位置、版本、
userID等重要信息保存在/data/system/packages.xml文件中。这些操作都是在PackageManagerService中完成
的。
(4)显示icon图标
应用程序经过PMS中的逻辑处理后,相当于已经注册好了,如果想要在Android桌面上看到icon图标,则需要
Launcher将系统中已经安装的程序展现在桌面上。
4. APK安装的预备知识点
(1)PackageManagerService是由SystemServer启动,PMS负责应用的安装、卸载、权限检查等工作
(2)在/system/app和/data/app目录下的apk文件,PMS在启动过程中,都会扫描安装
(3)每次开机时,PMS都会在构造函数中对指定目录下的apk进行扫描,没有安装的apk就会触发安装。
(4) 本文的源码是基于Android6.0
5. 系统应用安装
在创建PackageManagerService实例时,会在PMS的构造函数中开始执行安装应用程序的逻辑。在PMS的构造函数中做了如下几点重要操作。
(1)创建Settings对象,添加shareUserId
mSettings = new Settings(mPackages);
mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.log", LOG_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
(2)通过PackageManagerService构造函数参数获取应用安装器Installer
mInstaller = installer;
(3)获取SystemConfig实例,读取“/system/etc/permissions/*.xml”资源文件
从资源文件中获取mGlobalsGids(Group-ids)、mSystemPermissions(系统权限)、mAvailableFeatures(系统支持的features)属性。
SystemConfig systemConfig = SystemConfig.getInstance();
mGlobalGids = systemConfig.getGlobalGids();
mSystemPermissions = systemConfig.getSystemPermissions();
mAvailableFeatures = systemConfig.getAvailableFeatures();
(4)创建系统消息处理线程
mHandlerThread = new ServiceThread(TAG,Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
mHandlerThread.start();
mHandler = new PackageHandler(mHandlerThread.getLooper());
Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
(5)执行com.android.server.pm.Settings中的readLPw方法,读取安装包中的信息,并解析成对应的数据结构。
File dataDir = Environment.getDataDirectory();
mAppDataDir = new File(dataDir, "data");
mAppInstallDir = new File(dataDir, "app");
mAppLib32InstallDir = new File(dataDir, "app-lib");
mAsecInternalPath = new File(dataDir, "app-asec").getPath();
mUserAppDataDir = new File(dataDir, "user");
mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
sUserManager = new UserManagerService(context, this,
mInstallLock, mPackages);
// Propagate permission configuration in to package manager.
ArrayMap<String, SystemConfig.PermissionEntry> permConfig
= systemConfig.getPermissions();
for (int i=0; i<permConfig.size(); i++) {
SystemConfig.PermissionEntry perm = permConfig.valueAt(i);
BasePermission bp = mSettings.mPermissions.get(perm.name);
if (bp == null) {
bp = new BasePermission(perm.name, "android", BasePermission.TYPE_BUILTIN);
mSettings.mPermissions.put(perm.name, bp);
}
if (perm.gids != null) {
bp.setGids(perm.gids, perm.perUser);
}
}
ArrayMap<String, String> libConfig = systemConfig.getSharedLibraries();
for (int i=0; i<libConfig.size(); i++) {
mSharedLibraries.put(libConfig.keyAt(i),
new SharedLibraryEntry(libConfig.valueAt(i), null));
}
mFoundPolicyFile = SELinuxMMAC.readInstallPolicy();
mRestoredSettings = mSettings.readLPw(this, sUserManager.getUsers(false),
mSdkVersion, mOnlyCore);
其中,读取的重要文件有如下几个:
-
packages.xml:记录了系统中所有安装应用的信息,包括基本信息、签名和权限。
-
packages-backup.xml:packages.xml的备份文件
-
packages.list:保存普通应用的数据目录和uid等信息
-
packages-stopped.xml:记录系统中被强制停止运行应用的信息。系统在强制停止某个应用时,会将应用的信息记录到该文件中
-
packages-stopped-backup.xml:packages-stopped.xml的备份文件
这几个目录在创建Settings对象时就已经被封装成了对应的File文件。
(6)执行PMS中的scanDirLI方法扫描系统安装目录和非系统apk信息
File vendorOverlayDir = new File(VENDOR_OVERLAY_DIR);
scanDirLI(vendorOverlayDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_TRUSTED_OVERLAY, 0);
// Find base frameworks (resource packages without code).
scanDirLI(frameworkDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR
| PackageParser.PARSE_IS_PRIVILEGED,
scanFlags | SCAN_NO_DEX, 0);
// Collected privileged system packages.
final File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
scanDirLI(privilegedAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR
| PackageParser.PARSE_IS_PRIVILEGED, scanFlags, 0);
// Collect ordinary system packages.
final File systemAppDir = new File(Environment.getRootDirectory(), "app");
scanDirLI(systemAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
// Collect all vendor packages.
File vendorAppDir = new File("/vendor/app");
try {
vendorAppDir = vendorAppDir.getCanonicalFile();
} catch (IOException e) {
// failed to look up canonical path, continue with original one
}
scanDirLI(vendorAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
// Collect all OEM packages.
final File oemAppDir = new File(Environment.getOemDirectory(), "app");
scanDirLI(oemAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);
其中,系统安装目录有:
-
/system/framework 系统库
-
/system/app 默认的系统应用
-
/vendor/app 厂商定制的应用
非系统apk信息目录:
-
/data/app/
-
/system/priv-app/
-
/data/app-private/
到此为止,PMS构造函数中主要的逻辑操作就介绍完了。
继续探究扫描安装过程:
(1)深入到PackageManagerService—>scanDirLI方法中
private void scanDirLI(File dir, int parseFlags, int scanFlags, long currentTime) {
final File[] files = dir.listFiles();
if (Arr