Android的应用管理主要是通过PackageManagerService来完成的,PackageManagerService服务负责各种APK包的安装,卸载,优化和查询.
PackageManagerService在系统启动时会扫描所有APK文件和jar包,然后把它们的信息读取出来,保存在内存中,这样系统运行时就能迅速找到各种应用和组件的信息.扫描过程中如果遇到没有优化的文件,还要执行转换工作,将APP文件中的dex格式转换成oat格式(Android5.0之前是转换成odex格式);启动后,将提供安装包的信息查询服务及应用的安装和卸载服务.
一、安装涉及的目录介绍:
system/priv-app ---- 存放一些系统底层应用(Settings,SystemUI等,拥有的权限最高)
system/app ---- 存放系统级的应用(phone,Contacts等)
data/app ---- 存放用户安装的应用
data/data ---- 存放安装应用程序的数据
data/dalvik-cache ---- 存放大部分的apk文件和jar包的odex版本,odex是一种优化过的格式,执行速度比apk文件中的dex格式更快,如果系统运行在art模式下,这里保存的是oat格式的文件,这种文件格式是Linux的ELF格式的一种私有形式
data/system/packages.xml ---- 记录系统中所有安装的应用信息,包括基本信息、签名和权限
data/system/packages.list ---- 保存普通应用的数据目录和uid等信息
二、apk安装的方式:
- 1.安装系统APK和预置的APK(第一次开机时安装,没有安装界)
PackageManagerService的构造中会扫描对应目录下的apk,完成安装
- 2.网络下载应用安装――通过market应用完成,没有安装界面
调用PackageManager的installPackage方法执行安装
- 3.ADB工具安装,没有安装界面
调用:msm8976/repo/system/core/adb/commandline.cpp中install_app方法,该方法调用pm_command通过send_shell_command方法将数据发送到手机端的adbd守护进程中,adbd在收到PC中Console发来的数据之后启动一个Shell,然后执行pm脚本(pm位于/system/bin目录下).
pm脚本通过app_process执行pm.jar包的main函数(systemframeworkpm.jar)
对应源码: msm8976/repo/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
23
24
25
26
27
28
29
30
31
32
33private int runInstall() { ... ... LocalPackageInstallObserver obs = new LocalPackageInstallObserver(); try { VerificationParams verificationParams = new VerificationParams(verificationURI, originatingURI, referrerURI, VerificationParams.NO_UID, null); mPm.installPackageAsUser(apkFilePath, obs.getBinder(), installFlags, installerPackageName, verificationParams, abi, userId); synchronized (obs) { while (!obs.finished) { try { obs.wait(); } catch (InterruptedException e) { } } if (obs.result == PackageManager.INSTALL_SUCCEEDED) { System.out.println("Success"); return 0; } else { System.err.println("Failure [" + installFailureToString(obs) + "]"); return 1; } } } catch (RemoteException e) { System.err.println(e.toString()); System.err.println(PM_NOT_RUNNING_ERR); return 1; } }
调用PMS的installPackageAsUser完成安装
- 4.第三方应用安装,有安装界面
安装本地apk,有安装界面,由package/app/PackageInstaller应用处理安装及卸载过程的界面
以上方式都有PackageInstallObserver来监听安装是否成功
三、APK安装流程:
复制APK安装到data/app目录下;解析APK信息;解压安装包的dex文件,执行dexopt转换操作,把转换过的文件保存到dalvik-cache目录,更新apk安装后的一些信息完成安装
安装应用后,会在/data/system/packages.xml文件中写入app的信息.同理删除某个app时,也会从中删除该app的信息.尤其是内置应用,更需要将app的残留删除干净,不然再次安装app会报错:INSTALL_FAILED_UPDATE_INCOMPATIBLE
在应用中,我们通常是调用Context的getPackageManager()方法返回PackageManager对象,源码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14// msm8976/repo/frameworks/base/core/java/android/app/ContextImpl.java @Override public PackageManager getPackageManager() { if (mPackageManager != null) { return mPackageManager; } IPackageManager pm = ActivityThread.getPackageManager(); if (pm != null) { // Doesn't matter if we make more than one instance. return (mPackageManager = new ApplicationPackageManager(this, pm)); } return null; }
这里返回的实际是ApplicationPackageManager对象,这个对象创建时使用IPackageManager对象作为参数,IPackageManager是PackageManagerService实现的AIDL接口,通过binder来获取PackageManagerService对象,源码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13// msm8976/repo/frameworks/base/core/java/android/app/ActivityThread.java public static IPackageManager getPackageManager() { if (sPackageManager != null) { //Slog.v("PackageManager", "returning cur default = " + sPackageManager); return sPackageManager; } IBinder b = ServiceManager.getService("package"); //Slog.v("PackageManager", "default service binder = " + b); sPackageManager = IPackageManager.Stub.asInterface(b); //Slog.v("PackageManager", "default service = " + sPackageManager); return sPackageManager; }
因此ApplicationPackageManager是PackageManagerService的代理对象.ApplicationPackageManager继承自PackageManager,PackageManager中定义了可以操作PackageManagerService的接口.
在PackageManager中,实现对apk安装的方法是installExistingPackage和installPackageWithVerificationAndEncryption(前者是通过包名去升级应用,后者是安装新的应用),对应源码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18// msm8976/repo/packages/apps/PackageInstaller/src/com/android/packageinstaller/InstallAppProgress.java public void initView() { .... if ("package".equals(mPackageURI.getScheme())) { try { pm.installExistingPackage(mAppInfo.packageName); observer.packageInstalled(mAppInfo.packageName, PackageManager.INSTALL_SUCCEEDED); } catch (PackageManager.NameNotFoundException e) { observer.packageInstalled(mAppInfo.packageName, PackageManager.INSTALL_FAILED_INVALID_APK); } } else { pm.installPackageWithVerificationAndEncryption(mPackageURI, observer, installFlags, installerPackageName, verificationParams, null); } }
最终调用到PackageManagerService中的installExistingPackageAsUser和installPackage方法继续安装逻辑(这也就是传说中静默安装需要调用的方法)
PackageManagerService是处理应用的安装、卸载、管理等工作,开机时由systemServer启动.
PackageManagerService的构造中会扫描对应目录下的apk执行安装内置apk,同时会初始化两个重要的成员变量mInstaller和mInstallerService,mInstallerService是PackageInstallerService的实例,主要来管理安装会话,mInstaller是类Installer的实例,这个类比较简单,它有一个mInstaller的成员变量,这个变量是InstallerConnection的实例.InstallerConnection类中存在与守护进程Install通讯的Socket命令通道,实际上系统中进行APK文件格式转换等工作最后是由InstallID进程来完成;InstallerConnection中通过socket在系统installd进程中完成安装
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20//源码路径: msm8976/repo/frameworks/base/core/java/com/android/internal/os/InstallerConnection.java private boolean connect() { if (mSocket != null) { return true; } Slog.i(TAG, "connecting..."); try { mSocket = new LocalSocket(); LocalSocketAddress address = new LocalSocketAddress("installd", LocalSocketAddress.Namespace.RESERVED); mSocket.connect(address); mIn = mSocket.getInputStream(); mOut = mSocket.getOutputStream(); } catch (IOException ex) { disconnect(); return false; } return true; }
为什么需要installID进程?因为PMS运行在system_server中,是一个System用户,system用户并没有访问应用程序目录的权限,而InstallID的作用是处理root权限的操作.
installd源码路径:msm8976/repo/frameworks/native/cmds/installd
继续安装逻辑,PackageManagerService中installPackage会调用installPackageAsUser方法,该方法首先调用enforceCallingOrSelfPermission判断调用安装的程序是否申请了安装权限(所以要实现静默安装也需要申请该权限).这里判断安装来源,然后通过mHandler发送INIT_COPY的消息,这个mHandler运行在一个HandlerThread中
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@Override public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer, int installFlags, String installerPackageName, VerificationParams verificationParams, String packageAbiOverride, int userId) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null); final int callingUid = Binder.getCallingUid(); enforceCrossUserPermission(callingUid, userId, true, true, "installPackageAsUser"); if (isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) { try { if (observer != null) { observer.onPackageInstalled("", INSTALL_FAILED_USER_RESTRICTED, null, null); } } catch (RemoteException re) { } return; } if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) { installFlags |= PackageManager.INSTALL_FROM_ADB; } else { // Caller holds INSTALL_PACKAGES permission, so we're less strict // about installerPackageName. installFlags &= ~PackageManager.INSTALL_FROM_ADB; installFlags &= ~PackageManager.INSTALL_ALL_USERS; } UserHandle user; if ((installFlags & PackageManager.INSTALL_ALL_USERS) != 0) { user = UserHandle.ALL; } else { user = new UserHandle(userId); } // Only system components can circumvent runtime permissions when installing. if ((installFlags & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0 && mContext.checkCallingOrSelfPermission(Manifest.permission .INSTALL_GRANT_RUNTIME_PERMISSIONS) == PackageManager.PERMISSION_DENIED) { throw new SecurityException("You need the " + "android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS permission " + "to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag"); } verificationParams.setInstallerUid(callingUid); final File originFile = new File(originPath); final OriginInfo origin = OriginInfo.fromUntrustedFile(originFile); final Message msg = mHandler.obtainMessage(INIT_COPY); msg.obj = new InstallParams(origin, null, observer, installFlags, installerPackageName, null, verificationParams, user, packageAbiOverride, null); mHandler.sendMessage(msg); }
INIT_COPY主要是确保DefaultContainerService已bound,DefaultContainerService是一个应用服务,负责实现APK等相关资源文件在内部或外部存储器上的存储工作.而MCS_BOUND中则执行了
params.startCopy(),将apk文件复制到data/app目录下.复制的关键代码如下:
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
72
73
74
75
76
77
78
79
80
81
82
83
84/* * Invoke remote method to get package information and install * location values. Override install location based on default * policy if needed and then create install arguments based * on the install location. */ public void handleStartCopy() throws RemoteException { int ret = PackageManager.INSTALL_SUCCEEDED; ... ... final boolean onSd = (installFlags & PackageManager.INSTALL_EXTERNAL) != 0; final boolean onInt = (installFlags & PackageManager.INSTALL_INTERNAL) != 0; PackageInfoLite pkgLite = null; if (onInt && onSd) { // Check if both bits are set. Slog.w(TAG, "Conflicting flags specified for installing on both internal and external"); ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION; } else { pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags, packageAbiOverride); /* * If we have too little free space, try to free cache * before giving up. */ if (!origin.staged && pkgLite.recommendedInstallLocation == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) { // TODO: focus freeing disk space on the target device final StorageManager storage = StorageManager.from(mContext); final long lowThreshold = storage.getStorageLowBytes( Environment.getDataDirectory()); final long sizeBytes = mContainerService.calculateInstalledSize( origin.resolvedPath, isForwardLocked(), packageAbiOverride); if (mInstaller.freeCache(null, sizeBytes + lowThreshold) >= 0) { pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags, packageAbiOverride); } /* * The cache free must have deleted the file we * downloaded to install. * * TODO: fix the "freeCache" call to not delete * the file we care about. */ if (pkgLite.recommendedInstallLocation == PackageHelper.RECOMMEND_FAILED_INVALID_URI) { pkgLite.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE; } } } ... ... final InstallArgs args = createInstallArgs(this); mArgs = args; if (ret == PackageManager.INSTALL_SUCCEEDED) { /* * ADB installs appear as UserHandle.USER_ALL, and can only be performed by * UserHandle.USER_OWNER, so use the package verifier for UserHandle.USER_OWNER. */ int userIdentifier = getUser().getIdentifier(); if (userIdentifier == UserHandle.USER_ALL && ((installFlags & PackageManager.INSTALL_FROM_ADB) != 0)) { userIdentifier = UserHandle.USER_OWNER; } /* * Determine if we have any installed package verifiers. If we * do, then we'll defer to them to verify the packages. */ final int requiredUid = mRequiredVerifierPackage == null ? -1 : getPackageUid(mRequiredVerifierPackage, userIdentifier); ... { /* * No package verification is enabled, so immediately start * the remote call to initiate copy using temporary file. */ ret = args.copyApk(mContainerService, true); } } mRet = ret; }
handleStartCopy的核心是copyApk,其它是一些安全校验(存储空间检查,权限检查等)
startCopy方法走完copy apk的逻辑之后来到了handleReturnCode方法,然后调用processPendingInstall方法,这个方法调用installPackageLI来执行解析package等操作之后通过mHandler发送一个POST_INSTALL消息完成安装(发送Intent.ACTION_PACKAGE_ADDED广播)
installPackageLI首先解析package包,然后做大量签名和权限校验工作,该方法又会调用到installNewPackageLI,这个方法主要做了两件事: scanPackageLI负责安装,updateSettingLI完成安装后的设置信息更新
scanPackageLI调用scanPackageDirtyLI实现具体的逻辑,摘取该方法中一段
1
2
3
4
5
6
7
8
9if ((scanFlags & SCAN_NO_DEX) == 0) { int result = mPackageDexOptimizer.performDexOpt(pkg, null /* instruction sets */, forceDex, (scanFlags & SCAN_DEFER_DEX) != 0, false /* inclDependencies */, (scanFlags & SCAN_BOOTING) == 0); if (result == PackageDexOptimizer.DEX_OPT_FAILED) { throw new PackageManagerException(INSTALL_FAILED_DEXOPT, "scanPackageLI"); } }
执行mPackageDexOptimizer.performDexOpt(x,x,x)方法,继续跟代码最后调用到performDexOptLI方法执行dexopt操作,同PMS构造方法中一样,通过socket与installd进程通讯完成转换工作
Apk文件其实是一个压缩包,我们的代码最终都编译成了.dex文件,但为了提高运行性能,android系统并不会直接执行.dex,而是在安装过程中执行dexopt操作来优化.dex文件,最终系统运行的是优化后的’odex’文件(odex文件的后缀也是.dex,路径在data/dalvik-cache).对于dalvik虚拟机,dexopt就是优化操作,而对于art虚拟机,dexopt执行的则是dex2oat操作,将.dex文件翻译成oat文件.
以上源码基于Android6.0分析
对应流程图:
最后
以上就是俭朴棒球最近收集整理的关于APK的安装流程及PackageManagerService源码解析&静默安装的全部内容,更多相关APK内容请搜索靠谱客的其他文章。
发表评论 取消回复