Android 4.4(KitKat)中apk包的安装过程
原文地址:http://blog.csdn.net/jinzhuojun/article/details/25542011
事实上对于apk包的安装。4.4和之前版本号没大的区别。
Android中app安装主要有下面几种情况:系统启动时安装,adb命令安装,Google
Play上下载安装和通过PackageInstaller安装。
安装的最核心方法是scanPackageLI()。以上几个安装方式最后都是调用这个函数完毕主要工作的,差别在于在此之前的处理过程不同。本文曾经两种为主,简要介绍这四种安装过程。
一个最一般的apk包(不是系统app,没有共享lib,不是装在sd上或是forward-lock的app)装完后内容会体如今这么几个文件夹:
/data/app // apk包
/data/app-lib// native lib
/data/data //数据文件夹,当中的lib文件夹指向上面的/data/app-lib文件夹
/data/dalvik-cache/data@app@<package-name>.apk@classes.dex //优化或编译后的Java bytecode
一、启动时安装
Android启动时会把已有的app安装一遍,过程主要分三部分:读取安装信息,扫描安装,写回安装信息。读取和写回主要是针对于一坨安装信息文件。这些信息保证了启动后app与上一次的一致。关键步是扫描指定文件夹下的apk并安装。Android中apk主要分布在下面几个文件夹,意味着启动时要扫描的主要也是这几个文件夹:
系统核心应用:/system/priv-app
系统app:/system/app
非系统app:/data/app(安装于手机存储的一般app)或/mnt/asec/<pkgname-number>/pkg.apk(sdcard或forward-locked)
受DRM保护app:/data/app-private
vendor-specific的app: /vendor/app
资源型app:/system/framework
整个启动时安装的流程大体例如以下:
几个同名函数一開始看得会有些混淆,内层scanPackageLI()比較复杂这里省略细节。
以下更加具体地分析下流程。故事从System Server開始,实如今/frameworks/base/services/java/com/android/server/SystemServer.java中:
241 pm = PackageManagerService.main(context, installer,
242 factoryTest != SystemServer.FACTORY_TEST_OFF,
243 onlyCore);
调用PMS的main()函数,事实上现位于/frameworks/base/services/java/com/android/server/pm/PackageManagerService.java:
1040 public static final IPackageManager main(Context context, Installer installer,
1041 boolean factoryTest, boolean onlyCore) {
1042 PackageManagerService m = new PackageManagerService(context, installer,
1043 factoryTest, onlyCore);
1044 ServiceManager.addService("package", m);
1045 return m;
1046 }
这里构造了PMS并加到ServiceManager中。这样其他的组件就能够用该服务了。PMS的构造函数中:
1084 mSettings = new Settings(context); // 用于存放和操作动态安装信息,详细地说,如uid, permission等。
...
1115 mInstaller = installer; // installd daemon的proxy类。
…
1124 mHandlerThread.start(); // 启动PMS的工作线程。
1125 mHandler = new PackageHandler(mHandlerThread.getLooper()); // PMS会通过mHandler丢活给工作线程。
...
1129 File dataDir = Environment.getDataDirectory(); // /data
1130 mAppDataDir = new File(dataDir, "data"); // /data/data
1131 mAppInstallDir = new File(dataDir, "app"); // /data/app
1132 mAppLibInstallDir = new File(dataDir, "app-lib"); // /data/app-lib
能够看到PMS除了主线程,还会有一个叫PackageManager的工作线程。它主要是用在其他安装方式中,由于启动时没什么用户交互,基本上不须要把工作交给后台。
262 final HandlerThread mHandlerThread = new HandlerThread("PackageManager",
263 Process.THREAD_PRIORITY_BACKGROUND);
权限信息在/etc/permissions文件夹中,由readPermissions()函数读取。保存在Settings中的mPermissions中。
接下来在PMS构造函数中调用readLPw()来解析packages.xml文件得到上次的安装信息。
1144 mRestoredSettings = mSettings.readLPw(this, sUserManager.getUsers(false),
1145 mSdkVersion, mOnlyCore);
Android启动时要扫描和安装现有app。
也许是为了防止corrupted信息的存在,也许是为了能在升级系统或更新系统属性后保持app也valid,亦或是由于有些信息(如uid)必须是动态生成和调整的。
总之,为了要还原现有app的安装信息,这些信息被放在/data/system/packages.xml里。由Settings管理。另外/data/system/packages.list记录了app的uid和数据路径等信息。readLPw()就是用于恢复这些信息,实现位于/frameworks/base/services/java/com/android/server/pm/Settings.java。readLPw()先打开packages.xml,再通过XmlPullParser类来解析其内容。它会依据不同的tag调用对应的函数来读取信息:
1712 String tagName = parser.getName();
1713 if (tagName.equals("package")) {
1714 readPackageLPw(parser);
1715 } else if (tagName.equals("permissions")) {
1716 readPermissionsLPw(mPermissions, parser);
1717 } else if (tagName.equals("permission-trees")) {
1718 readPermissionsLPw(mPermissionTrees, parser);
1719 } else if (tagName.equals("shared-user")) {
1720 readSharedUserLPw(parser);
1721 } else if (tagName.equals("preferred-packages")) {
1722 // no longer used.
1723 } else if (tagName.equals("preferred-activities")) {
1724 // Upgrading from old single-user implementation;
1725 // these are the preferred activities for user 0.
1726 readPreferredActivitiesLPw(parser, 0);
1727 } else if (tagName.equals("updated-package")) {
1728 readDisabledSysPackageLPw(parser);
packages.xml中的一个apk包相应的package结构大体例如以下:
<package name=""codePath="" nativeLibraryPath="" sharedUserId=""or userId="">
...
</package>
当中readPackageLPw()调用addPackageLPw()注冊app信息到变量mPackages中,uid信息到mUserIds/mOtherUserIds中,准确地说。应该是把这些信息从文件恢复回去。addPackageLPw()会创建PackageSetting,该类描写叙述了该apk包的安装信息。
325 p = new PackageSetting(name, realName, codePath, resourcePath, nativeLibraryPathString,
326 vc, pkgFlags);
327 p.appId = uid;
328 if (addUserIdLPw(uid, p, name)) {
329 mPackages.put(name, p);
330 return p;
331 }
注意Android中app的uid的范围区间是FIRST_APPLICATION_UID
~LAST_APPLICATION_UID,即10000 ~ 99999,FIRST_APPLICATION_UID之下的给系统应用。
碰到shared user就更麻烦了。由于有时多个app为了共享权限会共享一个uid。假设一个app要用共享uid,须要在apk的AndroidManifest.xml文件里申明android:sharedUserId="...",详见 http://developer.android.com/guide/topics/manifest/manifest-element.html。 这时就要先为其创建PendingPackage并放到mPendingPackages等共享uid部分搞定了再处理。在packages.xml中共享用户表示为:
<shared-user name="" userId="">
...
</shared-user>
readSharedUserLPw()用来处理app安装信息中的共享用户部分。
它会调用addSharedUserLPw()来加入共享用户。
addSharedUserLPw()为共享用户创建SharedUserSetting类(SharedUserSetting包括了一个PackageSetting的集合),再调用addUserIdLPw()函数注冊到mUserIds中(或mOtherUserIds),成功的话另外还会写入mSharedUsers中。
345 s = new SharedUserSetting(name, pkgFlags);
346 s.userId = uid;
347 if (addUserIdLPw(uid, s, name)) {
348 mSharedUsers.put(name, s);
349 return s;
350 }
综合上面的信息,就有了以下这种结构:
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvamluemh1b2p1bg==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">
我们知道packages.xml放了app在之前安装时的配置信息。这里能够有两点推论:当一个app卸载后packages.xml中该app的信息也被删除了。当卸载以后下一次安装同一个app时会又一次生成,uid不会被保留。
回到readLPw()后,处理前面那些由于用了共享用户而待处理的app,也就是mPendingPackages里的那坨。完了再回到PackageManagerService()。mSharedLibraries里放的一些共享的java库。这里会调用dexopt()对它们进行优化。
1199 if (dalvik.system.DexFile.isDexOptNeeded(lib)) {
1200 alreadyDexOpted.add(lib);
1201 mInstaller.dexopt(lib, Process.SYSTEM_UID, true);
1202 didDexOpt = true;
1203 }
alreadyDexOpted记录已经执行过dexopt的文件,像启动类和上面的共享库。以下是对framework里的包和类进行优化。
1231 for (int i=0; i<frameworkFiles.length; i++) {
...
1243 if (dalvik.system.DexFile.isDexOptNeeded(path)) {
1244 mInstaller.dexopt(path, Process.SYSTEM_UID, true);
接下来。监控/system/framework文件夹并扫描该文件夹。
1276 // Find base frameworks (resource packages without code).
1277 mFrameworkInstallObserver = new AppDirObserver(
1278 frameworkDir.getPath(), OBSERVER_EVENTS, true, false);
这里用Observer模式来监视文件夹变动。
它依赖于Linux kernel提供的Inotify机制。实现主要位于/frameworks/base/core/java/android/os/FileObserver.java和/frameworks/base/core/jni/android_util_FileObserver.cpp。对于它的继承类(如AppDirObserver),仅仅要实现onEvent()方法来处理文件或文件夹变动就可以。onEvent()的实现比方/frameworks/base/services/java/com/android/server/pm/PackageManagerService.java中:
6384 public void onEvent(int event, String path) {
...
6450 p = scanPackageLI(fullPath, flags,
6451 SCAN_MONITOR | SCAN_NO_PATHS | SCAN_UPDATE_TIME,
6452 System.currentTimeMillis(), UserHandle.ALL);
...
6460 synchronized (mPackages) {
6461 updatePermissionsLPw(p.packageName, p,
6462 p.permissions.size() > 0 ? UPDATE_PERMISSIONS_ALL : 0);
6463 }
...
6471 synchronized (mPackages) {
6472 mSettings.writeLPr();
6473 }
扫描该文件夹的目的是要安装里边的apk包。主要实现函数是scanDirLI():
1280 scanDirLI(frameworkDir, PackageParser.PARSE_IS_SYSTEM
1281 | PackageParser.PARSE_IS_SYSTEM_DIR
1282 | PackageParser.PARSE_IS_PRIVILEGED,
1283 scanMode | SCAN_NO_DEX, 0);
对于其他几个文件夹(/system/priv-app。/system/app。/vendor/app。 /data/app, /data/app-private),也是一样的:
1380 mAppInstallObserver = new AppDirObserver(
1381 mAppInstallDir.getPath(), OBSERVER_EVENTS, false, false);
1382 mAppInstallObserver.startWatching();
1383 scanDirLI(mAppInstallDir, 0, scanMode, 0);
…
全安装好了就能够更新权限信息而且写回安装信息了。
1446 updatePermissionsLPw(null, null, UPDATE_PERMISSIONS_ALL
1447 | (regrantPermissions
1448 ? (UPDATE_PERMISSIONS_REPLACE_PKG|UPDATE_PERMISSIONS_REPLACE_ALL)
1449 : 0));
...
1457 // can downgrade to reader
1458 mSettings.writeLPr();
这样启动时安装主要工作就差点儿相同完毕了。以下回头看一下重头戏 - 文件夹的扫描和安装。也就是scanDirLI()函数:
scanDirLI()
scanPackageLI(file, flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime, null);
PackageParser pp = new PackageParser(scanPath);
final PackageParser.Package pkg = pp.parsePackage(scanFile, scanPath, mMetrics, parseFlags);
assmgr = new AssetManager();
parser = assmgr.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
pkg = parsePackage(res, parser, flags, errorText); // parse AndroidManifest.xml
...
PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanMode | SCAN_UPDATE_SIGNATURE, currentTime, user);
能够看到scanPackageLI()和parsePackage()皆有重载版本号。基本上内层的版本号才是做事的。内层的parsePackage(res, ...)函数用于解析AndroidManifest.xml文件。实如今/frameworks/base/core/java/android/content/pm/PackageParser.java。
至于AndroidManifest.xml是apk中不可缺少的配置文件。
详见http://developer.android.com/guide/topics/manifest/manifest-intro.html。没有的话Android压根不让你装。
1034 String tagName = parser.getName();
1035 if (tagName.equals("application")) {
1036 if (foundApp) {
1037 if (RIGID_PARSER) {
1038 outError[0] = "<manifest> has more than one <application>";
1039 mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
1040 return null;
1041 } else {
1042 Slog.w(TAG, "<manifest> has more than one <application>");
1043 XmlUtils.skipCurrentTag(parser);
1044 continue;
1045 }
1046 }
1047
1048 foundApp = true;
1049 if (!parseApplication(pkg, res, parser, attrs, flags, outError)) {
1050 return null;
1051 }
1052 } else if (tagName.equals("keys")) {
1053 if (!parseKeys(pkg, res, parser, attrs, outError)) {
1054 return null;
1055 }
1056 } else if (tagName.equals("permission-group")) {
1057 if (parsePermissionGroup(pkg, flags, res, parser, attrs, outError) == null) {
1058 return null;
1059 }
1060 } else if (tagName.equals("permission")) {
1061 if (parsePermission(pkg, res, parser, attrs, outError) == null) {
1062 return null;
1063 }
1064 } else if (tagName.equals("permission-tree")) {
1065 if (parsePermissionTree(pkg, res, parser, attrs, outError) == null) {
1066 return null;
1067 }
1068 } else if (tagName.equals("uses-permission")) {
1069 if (!parseUsesPermission(pkg, res, parser, attrs, outError)) {
1070 return null;
1071 }
比較重要的如parseApplication()是解析application标签里的东西。application标签里包括Android四大组件(Activity, Receiver, Service, Content Provider)信息和库等信息。一顿解析后,返回结果PackageParser.Package对象pkg,这个类基本上就包括了AndroidManifest.xml里的信息。
接下来scanPackageLI(pkg, ...)被调用,前面返回的解析结果pkg被当作參数传入。scanPackageLI(pkg,
...)干的事还挺多的。如处理共享用户。注冊包信息。调用NativeLibraryHelper和Installer的相关函数进行安装等等。
假设该app使用了共享用户,则调用getSharedUserLPw()函数获取该共享uid的SharedUserSetting,没有的话就新建,然后分配uid:
245 SharedUserSetting getSharedUserLPw(String name,
246 int pkgFlags, boolean create) {
247 SharedUserSetting s = mSharedUsers.get(name);
248 if (s == null) {
249 if (!create) {
250 return null;
251 }
252 s = new SharedUserSetting(name, pkgFlags);
253 s.userId = newUserIdLPw(s);
254 Log.i(PackageManagerService.TAG, "New shared user " + name + ": id=" + s.userId);
255 // < 0 means we couldn't assign a userid; fall out and return
256 // s, which is currently null
257 if (s.userId >= 0) {
258 mSharedUsers.put(name, s);
259 }
260 }
261
262 return s;
263 }
在scanPackageLI(pkg, ...)函数中,以下调用getPackageLPw()得到该apk包的PackageSetting对象,有些在前面readPackageLPw()时就已经恢复好了。还没有的那些这儿就会新建:
4302 // Just create the setting, don't add it yet. For already existing packages
4303 // the PkgSetting exists already and doesn't have to be created.
4304 pkgSetting = mSettings.getPackageLPw(pkg, origPackage, realName, suid, destCodeFile,
4305 destResourceFile, pkg.applicationInfo.nativeLibraryDir,
4306 pkg.applicationInfo.flags, user, false);
getPackageLPw()实如今/frameworks/base/services/java/com/android/server/pm/Settings.java中:
392 private PackageSetting getPackageLPw(String name, PackageSetting origPackage,
393 String realName, SharedUserSetting sharedUser, File codePath, File resourcePath,
394 String nativeLibraryPathString, int vc, int pkgFlags,
395 UserHandle installUser, boolean add, boolean allowInstall) {
…
457 p = new PackageSetting(name, realName, codePath, resourcePath,
458 nativeLibraryPathString, vc, pkgFlags);
...
520 // Assign new user id
521 p.appId = newUserIdLPw(p);
newUserIdLPw()函数为app分配uid。
这样。该应用相应的uid就设置好了。
回到scanPackageLI()中。以下设置进程名,进程名默认就是包名。所以我们在ps里看到的都是包名。
4422 pkg.applicationInfo.processName = fixProcessName(
4423 pkg.applicationInfo.packageName,
4424 pkg.applicationInfo.processName,
4425 pkg.applicationInfo.uid);
对于大部分全新安装的一般应用而言。接下来为应用创建数据文件夹:
3987 private int createDataDirsLI(String packageName, int uid, String seinfo) {
3988 int[] users = sUserManager.getUserIds();
3989 int res = mInstaller.install(packageName, uid, uid, seinfo);
...
3993 for (int user : users) {
3994 if (user != 0) {
3995 res = mInstaller.createUserData(packageName,
3996 UserHandle.getUid(user, uid), user);
Installer是一个代理类,它会和后台的installd通信并让installd完毕详细工作。
installd的实现位于/frameworks/native/cmds/installd/commands.c,install()会创建app文件夹(/data/data/<package-name>),并作lib文件夹的软链接,如/data/data/xxx/lib -> /data/app-lib/xxx。
以下设置原生库文件夹为/data/data/<package- name>/lib。它指向/data/app-lib。
再把原生库文件(如有)解压到该文件夹下。
4556 if (pkgSetting.nativeLibraryPathString == null) {
4557 setInternalAppNativeLibraryPath(pkg, pkgSetting);
4558 } else {
4559 pkg.applicationInfo.nativeLibraryDir = pkgSetting.nativeLibraryPathString;
4560 }
...
4577 if (pkg.applicationInfo.nativeLibraryDir != null) {
4578 try {
4579 File nativeLibraryDir = new File(pkg.applicationInfo.nativeLibraryDir);
...
4605 try {
4606 if (copyNativeLibrariesForInternalApp(scanFile, nativeLibraryDir) != PackageManager.INSTALL_SUCCEEDED) {
4607 Slog.e(TAG, "Unable to copy native libraries");
4608 mLastScanError = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
4609 return null;
这里调用copyNativeLibrariesForInternalApp(),它会调用NativeLibraryHelper.copyNativeBinariesIfNeededLI()把apk里的原生库解压出来放到/data/app-lib的相应文件夹下。
接下来PMS调用performDexOptLI()优化Java的bytecode。即dex文件。
4638 if ((scanMode&SCAN_NO_DEX) == 0) {
4639 if (performDexOptLI(pkg, forceDex, (scanMode&SCAN_DEFER_DEX) != 0, false)
4640 == DEX_OPT_FAILED) {
4641 mLastScanError = PackageManager.INSTALL_FAILED_DEXOPT;
4642 return null;
4643 }
4644 }
真正的工作还是在后台installd进程中完毕, installd中的dexopt()函数会依据当前是执行dalvik还是art虚拟机来选择调用run_dexopt()或run_dex2oat()。
741 if (strncmp(persist_sys_dalvik_vm_lib, "libdvm", 6) == 0) {
742 run_dexopt(zip_fd, out_fd, apk_path, out_path, dexopt_flags);
743 } else if (strncmp(persist_sys_dalvik_vm_lib, "libart", 6) == 0) {
744 run_dex2oat(zip_fd, out_fd, apk_path, out_path, dexopt_flags);
745 } else {
746 exit(69); /* Unexpected persist.sys.dalvik.vm.lib value */
747 }
前者适用于dalvik,将dex优化成odex文件。
后者适用于art,将dex直接一步到位编译成oat文件(也就是可运行代码)了。因为下层是运行了/system/bin/dexopt或/system/bin/dex2oat文件,dexopt()将之放到子进程去做。自己作为父进程等待它结束。以art为例,/system/bin/dex2oat被调用(实现位于/art/dex2oat/dex2oat.cc)。输出的elf文件放在/data/dalvik-cache/data@app@<package-name>@classes.dex。
注意对于dalvik和art。这个文件名同样但性质截然不同。dalvik下。该文件为优化后的bytecode。而art下,这个就是可运行文件了。
假设是更新已有的app,还要让ActivityManager调用killApplication()把进程杀掉。app都更新了。老的还留内存里跑。这不合适。
4743 killApplication(pkg.applicationInfo.packageName,
4744 pkg.applicationInfo.uid, "update pkg");
之后将安装的app注冊到PMS的mPackages中,mPackges是个Hash表。保存了从包名到PackageParser.Package的映射。注意Settings里也有个mPackages。那里保存的是包名到PackageSetting的映射。前者主要是app配置文件里的信息,而后者是安装过程中的信息。
能够粗略理解为一个是静态信息,一个是动态信息。
4763 mSettings.insertPackageSettingLPw(pkgSetting, pkg);
// PackageSetting <= PackageParser.Package
addPackageSettingLPw(p, pkg.packageName, p.sharedUser)
mPackages.put(name, p);
4764 // Add the new setting to mPackages
4765 mPackages.put(pkg.applicationInfo.packageName, pkg);
以下把app中的组件信息(content provider, service, receiver, activity)记录到系统中。另外依据前面app配置文件里的权限信息进行初始化。
4811 int N = pkg.providers.size();
4812 StringBuilder r = null;
4813 int i;
4814 for (i=0; i<N; i++) {
4815 PackageParser.Provider p = pkg.providers.get(i);
4816 p.info.processName = fixProcessName(pkg.applicationInfo.processName,
4817 p.info.processName, pkg.applicationInfo.uid);
4818 mProviders.addProvider(p);
…
4871 N = pkg.services.size();
…
4891 N = pkg.receivers.size();
…
4911 N = pkg.activities.size();
…
4931 N = pkg.permissionGroups.size();
...
4965 N = pkg.permissions.size();
回到PMS构造函数中。以下就是收尾工作了。主要包含更新共享库信息。更新权限信息,以及写回安装信息。全部包都解析完了,意味着全部共享库信息都已解析。这儿就能够调用updateAllSharedLibrariesLPw()为那些使用动态库的app绑定动态库信息了。以下updatePermissionsLPw()函数用于赋予app对应权限:
5365 private void updatePermissionsLPw(String changingPkg,
5366 PackageParser.Package pkgInfo, int flags) {
...
5430 // Now update the permissions for all packages, in particular
5431 // replace the granted permissions of the system packages.
5432 if ((flags&UPDATE_PERMISSIONS_ALL) != 0) {
5433 for (PackageParser.Package pkg : mPackages.values()) {
5434 if (pkg != pkgInfo) {
5435 grantPermissionsLPw(pkg, (flags&UPDATE_PERMISSIONS_REPLACE_ALL) != 0);
5436 }
5437 }
5438 }
在AndroidManifest.xml中app会申请一些权限,比方读取位置信息,读取联系人。操作摄像头等等。
AndroidManifest.xml中的格式如:
<uses-permission android:name="permission_name">
这里的permission_name被放到requestedPermissions,代表该app申请该权限。经过grantPermissionsLPw()里推断是否能给予相应权限。假设同意则授予权限(即把权限相应的gid加到app的gid列表中。由于权限在Linux中相应物就是group,由gid表示)。并把该权限加到grantedPermissions中,代表已授予该权限。
5445 private void grantPermissionsLPw(PackageParser.Package pkg, boolean replace) {
...
5467 final int N = pkg.requestedPermissions.size();
5468 for (int i=0; i<N; i++) {
...
5528 if (!gp.grantedPermissions.contains(perm)) {
5529 changedPermission = true;
5530 gp.grantedPermissions.add(perm);
5531 gp.gids = appendInts(gp.gids, bp.gids);
5532 } else if (!ps.haveGids) {
5533 gp.gids = appendInts(gp.gids, bp.gids);
5534 }
最后,writeLPr()将安装信息写回packages.xml文件,这也是一開始readLPw()读的那个文件。这样下次启动时就能够依照这里边的信息又一次安装了。
1261 void writeLPr() {
...
1315 serializer.startTag(null, "permission-trees");
1316 for (BasePermission bp : mPermissionTrees.values()) {
1317 writePermissionLPr(serializer, bp);
1318 }
1319 serializer.endTag(null, "permission-trees");
1320
1321 serializer.startTag(null, "permissions");
1322 for (BasePermission bp : mPermissions.values()) {
1323 writePermissionLPr(serializer, bp);
1324 }
1325 serializer.endTag(null, "permissions");
1326
1327 for (final PackageSetting pkg : mPackages.values()) {
1328 writePackageLPr(serializer, pkg);
1329 }
总结下几个主要类的用途:
PackageManagerService: apk包安装服务
Settings: 管理app的安装信息
PackageSetting: app的动态安装信息
SharedUserSetting: 共享Linux用户
PackageParser.Package: app的静态配置信息。
Pm: pm命令实现类
Installer: installd daemon代理类
HandlerThread: PMS工作线程
它们之间的大致关系:
二、adb install安装
通过adb install命令安装时流程略有不同。主要是scanPackageLI()之前的流程不同。host机上的adb从/system/core/adb/adb.c中的main()開始:main()->adb_commandline()->install_app()->pm_command()->send_shellcommand(),当中会把安装包从host传到guest上的暂时文件夹。
接下来guest里的pm命令(/frameworks/base/cmds/pm/src/com/android/commands/pm/Pm.java)接手。大体流程例如以下:
runInstall()
installPackageWithVerificationAndEncryption()
doHandleMessage() // INIT_COPY
doHandleMessage() // MCS_BOUND
startCopy()
InstallParams.handleStartCopy()
InstallArgs args = createInstallArgs(this)
args.copyApk() // FileInstallArgs或AsecInstallArgs,取决于是否是forward-lock或装在sd card上。 createCopyFile() // 拷贝生成相似于/data/app/vmdl-842267127.tmp这种暂时文件。由于这时候包都没解析,不知道包名。
handleReturnCode()
processPendingInstall() //异步方式安装。由于安装过程可能较长。 installPackageLI()
这里用到了一開始提到的PMS工作线程,doHandleMessage()就是工作线程用于处理丢给它的消息的。
这里有几个设计模式值得学习的。首先。Pm中得到PMS的代理类,然后调用installPackageWithVerificationAndEncryption()进行安装。
90 mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
...
957 mPm.installPackageWithVerificationAndEncryption(apkURI, obs, installFlags,
958 installerPackageName, verificationParams, encryptionParams);
因为安装时间一般较长,这里的obs用了Observer模式来监视安装完毕事件。
其次,InstallArgs用了Strategy模式,而createInstallArgs()使用了简单工厂模式。在handleStartCopy()中。仅仅要依据安装类型生成对应的InstallArgs对象。然后调用统一接口copyApk()等即可了。
另外,HandlerParams和其继承类採用了Template method模式。当中基类中的startCopy()是模板函数。继承类实现handleStartCopy()。handleServiceError()和handleReturnCode()来完毕不同工作。HandlerParams包括了要工作线程做的工作内容。工作线程仅仅要取出HandlerParams对象,调用其startCopy()接口。因此这里也用了Command模式的思想。
将HandlerParams和InstallArgs组合起来看。它们又组成bridge pattern的变体。
这样把本来一棵层数更深的类树给压扁了,从而通过维度分解减少了复杂性,增强了灵活性。
这里的installPackageLI()做了非常多前一种安装方式中scanPackageLI(file, ...)的工作,接着它会依据该app是否是全新安装调用replacePackageLI()或installNewPackageLI()。
9061 if (!args.doRename(res.returnCode, pkgName, oldCodePath)) { // 前面拷贝apk时是随机取了暂时名字的。这里用doRename()函数为其“正名”。 ...
9068 if (replace) {
9069 replacePackageLI(pkg, parseFlags, scanMode, args.user,
9070 installerPackageName, res);
9071 } else {
9072 installNewPackageLI(pkg, parseFlags, scanMode, args.user,
9073 installerPackageName, res);
9074 }
假设是全新的apk,调用installNewPackageLI()进行安装。它调用scanPackageLI()完毕主要安装工作。
8601 private void installNewPackageLI(PackageParser.Package pkg,
8602 int parseFlags, int scanMode, UserHandle user,
8603 String installerPackageName, PackageInstalledInfo res) {
...
8630 PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanMode,
8631 System.currentTimeMillis(), user);
从这開始就熟悉了吧。和启动时安装的流程差点儿相同了,最后调用updateSettingsLI()来更新安装信息。
三、Google Play网络下载安装
Google Play的包名为com.android.vending。
因为是闭源的。看不了源代码。
只是从反汇编粗略地看,应该是先把apk包下载到:
/data/data/com.android.providers.downloads/cache/downloadfile.apk
然后调用installPackage()安装,类似于:
PackageManager pm = context.getPackageManager();
pm.installPackage(packageURI, observer, flags, null);
接着就和上面一样了:installPackage()->installPackageWithVerification()->installPackageWithVerificationAndEncryption()。
四、点选apk文件安装
这样的情况下。会通过PackageInstaller安装app。在/packages/apps/PackageInstaller/src/com/android/packageinstaller/InstallAppProgress.java中:
284 pm.installPackageWithVerificationAndEncryption(mPackageURI, observer, installFlags,
285 installerPackageName, verificationParams, null);
后面的故事又都熟悉了吧。
Android 4.4(KitKat)中apk包的安装过程的更多相关文章
- Android 使用 aapt 命令查看 apk 包名
一.aapt 是什么 aapt 即Android Asset Packaging Tool,在SDK的build-tools目录下.该工具可以查看,创建, 更新ZIP格式的文档附件(zip, jar, ...
- Android 4.4(KitKat)中VSync信号的虚拟化
原文地址:http://blog.csdn.net/jinzhuojun/article/details/17293325 Android 4.1(Jelly Bean)引入了Vsync(Vertic ...
- Android Studio修改项目中整体包名
莫名的需求,要把之前的apk分成三个不同的apk,还要在应用市场能够上线,麻麻滴这样一听那还不要各个apk包的包名不同以及apk签名文件也不同嘛(签名文件一般也用不同,为防止上线冲突嘛).所以就亲自尝 ...
- Android 演示 DownloadManager——Android 下载 apk 包并安装
本文内容 环境 项目结构 演示下载 参考资料 本文是 github 上 Trinea-Android-common 和 Trinea-Android-Demo 项目的一部分,将下载部分分离出来,看看如 ...
- 计算apk包的安装之后占用空间以及运行时占用内存
1.统计结果如下 计算apk安装占用空间大小方式 为了方式apk包运行时出现缓存数据等对空间计算造成影响.应该先进行安装,然后分别计算空间变化 所有apk包安装完毕后再运行 开启两个cmd窗口 第一个 ...
- APK文件结构和安装过程
APK文件结构Android应用是用Java编写的,利用Android SDK编译代码,并且把所有的数据和资源文件打包成一个APK (Android Package)文件,这是一个后缀名为.apk的压 ...
- Hadoop集群中pig工具的安装过程记录
在Hadoop环境中安装了pig工具,安装过程中碰到了一些问题,在此做一下记录: 主要安装流程参考:http://www.cnblogs.com/yanghuahui/p/3768270.html ...
- android 从服务器上获取APK并下载安装
简单的为新手做个分享. 网上有些资料,不过都是很零散,或是很乱的,有的人说看不懂. 一直有新手说 做到服务器更新APK时没有思路,这里做个简单的分享,希望有不同思路的可以讨论. 下 ...
- yum/dnf/rpm 等 查看rpm 包安装路径 (fedora 中 pygtk 包内容安装到哪里了)
有时候我们 使用 包管理工具,安装很方便,但我们还要能知道它们安装了什么文件,都把这些文件安装到哪里了? 我们以探究 pygtk 为例 在 fedora 28 之中 查找 pygtk: ➜ ~ rpm ...
随机推荐
- MailKit---如何知道文件夹下有多少封未读邮件
如果在mailkit中,文件夹已经选中并打开了的话,那直接使用ImapFolder.Unread属性就可以获取到有多少封未读邮件了. 如果文件夹没有打开,那么你还可以使用查询状态的方法来获取未读状态的 ...
- Android background tint颜色渲染
该篇文章主要是讲Android颜色渲染,首先先来看看PorterDuff,对绘图非常重要. PorterDuff的由来: 相信大多数人看到这个ProterDuff单词很奇怪了吧,这肿么个意思呢,然后就 ...
- python常见的编程错误
常见的编程错误 2.1 试图访问一个未赋值的变量,会产生运行时错误. 2.2 ==,!=, >=和<=这几个运算符的两个符号之间出现空格,会造成语法错误. 2.3 !=,<>, ...
- c#中的构造方法
c#基础--类的构造方法 当实例化一个类时,系统会自动对这个类的属性进行初始化 数字型初始化成0/0.0 string类型初始化成null char类型初始化成\0 构造器就是构造方法,能够被重载 ...
- textureView
textureView是用来访问texture的一部分的 cubemap 和 mipmap会用到这个 for(face =0;face<6;face++) setrendertarget(tex ...
- 查找文件命令find总结以及查找大文件
find / -name *** 示例如下: [dinpay@zk-spark-01 spark]$ find /home/ll -name slaves /home/ll/spark/conf/sl ...
- zabbix低级自动发现之mysql多实例
1.低级自动发现概述 zabbix的低级自动发现(LLD)适用于监控多实例,监控变化的数据(分区.网卡). 自动发现(LLD)提供了一种在计算机上为不同实体自动创建监控项,触发器和图形的方法.例如,Z ...
- 2016.6.20 tomcat端口始终被占用
我在使用tomcat7时,服务开启时,始终提示端口被占用. 但是从进程中又看不到开启的tomcat. 最后在资源监视器中,结束重复开启的tomcat. (注意是资源监视器,刚开始的时候看成管理器,怎么 ...
- zoj 3882 Help Bob(zoj 2015年7月月赛)
Help Bob Time Limit: 2 Seconds Memory Limit: 65536 KB There is a game very popular in ZJU at pr ...
- 线程阻塞工具类:LockSupport(读书笔记)
他可以在线程任意位置让线程阻塞, LockSupport的静态方法park()可以阻塞当前线程,类似的还有parkNanos() ParkUntil()等,他们实现了一个限时等待 public cl ...