Android 4.4中apk包的安装过程

本文详细介绍Android中APK的安装流程,包括系统启动时安装、adb命令安装、Google Play下载安装和通过PackageInstaller安装等方式。文章深入剖析了安装过程中涉及的关键步骤和技术细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

其实对于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@.apk@classes.dex //优化或编译后的Java bytecode

 

 

一、启动时安装

 

Android启动时会把已有的app安装一遍,过程主要分三部分:读取安装信息,扫描安装,写回安装信息。读取和写回主要是针对于一坨安装信息文件。这些信息保证了启动后app与上一次的一致。关键步是扫描指定目录下的apk并安装。Android中apk主要分布在以下几个目录,意味着启动时要扫描的主要也是这几个目录:

系统核心应用:/system/priv-app
系统app:/system/app
非系统app:/data/app(安装于手机存储的一般app)或/mnt/asec//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中:

?
1
2
3
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:

 

?
1
2
3
4
5
6
7
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的构造函数中:

 

?
1
2
3
4
5
6
7
8
9
10
11
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的工作线程。它主要是用在其它安装方式中,因为启动时没什么用户交互,基本上不需要把工作交给后台。

 

?
1
2
262    final HandlerThread mHandlerThread = new HandlerThread(PackageManager,
263            Process.THREAD_PRIORITY_BACKGROUND);
权限信息在/etc/permissions目录中,由readPermissions()函数读取,保存在Settings中的mPermissions中。接下来在PMS构造函数中调用readLPw()来解析packages.xml文件得到上次的安装信息。

 

 

?
1
2
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调用相应的函数来读取信息:

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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结构大体如下:


...

其中readPackageLPw()调用addPackageLPw()注册app信息到变量mPackages中,uid信息到mUserIds/mOtherUserIds中,准确地说,应该是把这些信息从文件恢复回去。addPackageLPw()会创建PackageSetting,该类描述了该apk包的安装信息。
?
1
2
3
4
5
6
7
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中共享用户表示为:

...

readSharedUserLPw()用来处理app安装信息中的共享用户部分。它会调用addSharedUserLPw()来添加共享用户。addSharedUserLPw()为共享用户创建SharedUserSetting类(SharedUserSetting包含了一个PackageSetting的集合),再调用addUserIdLPw()函数注册到mUserIds中(或mOtherUserIds),成功的话另外还会写入mSharedUsers中。

 

?
1
2
3
4
5
6
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        }
综合上面的信息,就有了下面这样的结构:

 

\


我们知道packages.xml放了app在之前安装时的配置信息。这里可以有两点推论:当一个app卸载后packages.xml中该app的信息也被删除了。当卸载以后下一次安装同一个app时会重新生成,uid不会被保留。


回到readLPw()后,处理前面那些因为用了共享用户而待处理的app,也就是mPendingPackages里的那坨。完了再回到PackageManagerService()。mSharedLibraries里放的一些共享的java库,这里会调用dexopt()对它们进行优化。
?
1
2
3
4
5
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里的包和类进行优化。
?
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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
1231                for ( int i= 0 ; i<frameworkfiles.length; ...= "" 1243 = "" 1244 = "" if = "" pre= "" >接下来,监控/system/framework目录并扫描该目录。<PRE class =brush:java;> 1276            // Find base frameworks (resource packages without code).
1277            mFrameworkInstallObserver = new AppDirObserver(
1278                frameworkDir.getPath(), OBSERVER_EVENTS, true , false );</PRE>
这里用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中:<BR>
<PRE class =brush: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                }</PRE>
扫描该目录的目的是要安装里边的apk包。主要实现函数是scanDirLI():<BR>
<PRE class =brush:java;> 1280            scanDirLI(frameworkDir, PackageParser.PARSE_IS_SYSTEM
1281                    | PackageParser.PARSE_IS_SYSTEM_DIR
1282                    | PackageParser.PARSE_IS_PRIVILEGED,
1283                    scanMode | SCAN_NO_DEX, 0 );</PRE>
对于其它几个目录(/system/priv-app,/system/app,/vendor/app, /data/app, /data/app- private ),也是一样的:<BR>
<PRE class =brush:java;> 1380                mAppInstallObserver = new AppDirObserver(
1381                    mAppInstallDir.getPath(), OBSERVER_EVENTS, false , false );
1382                mAppInstallObserver.startWatching();
1383                scanDirLI(mAppInstallDir, 0 , scanMode, 0 );
…</PRE>
全安装好了就可以更新权限信息并且写回安装信息了。<BR>
<PRE class =brush:java;> 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();</PRE>
这样启动时安装主要工作就差不多完成了。下面回头看一下重头戏 - 目录的扫描和安装,也就是scanDirLI()函数:<BR>
<PRE class =brush:java;>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);</PRE>
可以看到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压根不让你装。<BR>
<PRE class =brush:java;> 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 ;
1039                        mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
1040                        return null ;
1041                    } else {
1042                        Slog.w(TAG, <MANIFEST> has more than one );
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                }
</APPLICATION></MANIFEST></APPLICATION></MANIFEST></PRE>
比较重要的如parseApplication()是解析application标签里的东西。application标签里包含Android四大组件(Activity, Receiver, Service, Content Provider)信息和库等信息。一顿解析后,返回结果PackageParser.Package对象pkg,这个类基本上就包含了AndroidManifest.xml里的信息。接下来scanPackageLI(pkg, ...)被调用,前面返回的解析结果pkg被当作参数传入。scanPackageLI(pkg, ...)干的事还挺多的。如处理共享用户,注册包信息,调用NativeLibraryHelper和Installer的相关函数进行安装等等。<BR>
<BR>
如果该app使用了共享用户,则调用getSharedUserLPw()函数获取该共享uid的SharedUserSetting,没有的话就新建,然后分配uid:<BR>
<P> </P>
<PRE class =brush:java;> 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    }</PRE>
在scanPackageLI(pkg, ...)函数中,下面调用getPackageLPw()得到该apk包的PackageSetting对象,有些在前面readPackageLPw()时就已经恢复好了,还没有的那些这儿就会新建:<BR>
<P> </P>
<PRE class =brush:java;> 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 );</PRE>
<P> </P>
<P>getPackageLPw()实现在/frameworks/base/services/java/com/android/server/pm/Settings.java中:</P>
<P> </P>
<PRE class =brush: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);</PRE>
<P> </P>
newUserIdLPw()函数为app分配uid。这样,该应用对应的uid就设置好了。<BR>
<P> </P>
<P>回到scanPackageLI()中,下面设置进程名,进程名默认就是包名。所以我们在ps里看到的都是包名。</P>
<P> </P>
<PRE class =brush:java;> 4422        pkg.applicationInfo.processName = fixProcessName(
4423                pkg.applicationInfo.packageName,
4424                pkg.applicationInfo.processName,
4425                pkg.applicationInfo.uid);</PRE>
对于大部分全新安装的一般应用而言,接下来为应用创建数据目录:
<P> </P>
<P> </P>
<PRE class =brush:java;> 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);</PRE>
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。
<P> </P>
<P> </P>
<P>下面设置原生库目录为/data/data/<PACKAGE- name= "" >/lib,它指向/data/app-lib。再把原生库文件(如有)解压到该目录下。</PACKAGE-></P>
<P> </P>
<PRE class =brush:java;> 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 ;
</PRE>
这里调用copyNativeLibrariesForInternalApp(),它会调用NativeLibraryHelper.copyNativeBinariesIfNeededLI()把apk里的原生库解压出来放到/data/app-lib的对应目录下。<BR>
<BR>
接下来PMS调用performDexOptLI()优化Java的bytecode,即dex文件。<BR>
<PRE class =brush:java;> 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        }</PRE>
真正的工作还是在后台installd进程中完成, installd中的dexopt()函数会根据当前是运行dalvik还是art虚拟机来选择调用run_dexopt()或run_dex2oat()。<BR>
<P> </P>
<P> </P>
<PRE class =brush:java;> 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        }</PRE>
前者适用于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下,这个就是可执行文件了。<BR>
<BR>
<P> </P>
<P>如果是更新已有的app,还要让ActivityManager调用killApplication()把进程杀掉。app都更新了,老的还留内存里跑,这不合适。</P>
<P> </P>
<PRE class =brush:java;> 4743            killApplication(pkg.applicationInfo.packageName,
4744                        pkg.applicationInfo.uid, update pkg);</PRE>
之后将安装的app注册到PMS的mPackages中,mPackges是个Hash表,保存了从包名到PackageParser.Package的映射。注意Settings里也有个mPackages,那里保存的是包名到PackageSetting的映射。前者主要是app配置文件中的信息,而后者是安装过程中的信息。可以粗略理解为一个是静态信息,一个是动态信息。<BR>
<PRE class =brush:java;> 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);</PRE>
下面把app中的组件信息(content provider, service, receiver, activity)记录到系统中。另外根据前面app配置文件中的权限信息进行初始化。
<P> </P>
<P> </P>
<P> </P>
<PRE class =brush:java;> 4811            int N = pkg.providers.size();
4812            StringBuilder r = null ;
4813            int i;
4814            for (i= 0 ; i<N; packageparser.provider= "" p.info.processname= "fixProcessName(pkg.applicationInfo.processName," p= "pkg.providers.get(i);" n= "pkg.services.size();" 4965 = "" 4931 = "" 4911 = "" 4891 = "" 4871 = "" 4818 = "" 4817 = "" 4816 = "" 4815 = "" ...= "" ><P> </P>
回到PMS构造函数中。下面就是收尾工作了。主要包括更新共享库信息,更新权限信息,以及写回安装信息。所有包都解析完了,意味着所有共享库信息都已解析,这儿就可以调用updateAllSharedLibrariesLPw()为那些使用动态库的app绑定动态库信息了。下面updatePermissionsLPw()函数用于赋予app相应权限:<P> </P><PRE class =brush:java;> 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        }</PRE>
在AndroidManifest.xml中app会申请一些权限,比如读取位置信息,读取联系人,操作摄像头等等。AndroidManifest.xml中的格式如:<BR>
<USES-PERMISSION android:name= "permission_name" ><BR>
这里的permission_name被放到requestedPermissions,代表该app申请该权限。经过grantPermissionsLPw()里判断能否给予相应权限,如果允许则授予权限(即把权限对应的gid加到app的gid列表中,因为权限在Linux中对应物就是group,由gid表示),并把该权限加到grantedPermissions中,代表已授予该权限。<BR>
<PRE class =brush:java;> 5445    private void grantPermissionsLPw(PackageParser.Package pkg, boolean replace) {
...
5467        final int N = pkg.requestedPermissions.size();
5468        for ( int i= 0 ; i<N; ...= "" pre= "" if = "" gp.gids= "appendInts(gp.gids," else = "" changedpermission= "true;" 5534 = "" 5533 = "" 5532 = "" 5531 = "" 5530 = "" 5529 = "" 5528 = "" >最后,writeLPr()将安装信息写回packages.xml文件,这也是一开始readLPw()读的那个文件。这样下次启动时就可以按照这里边的信息重新安装了。<PRE class =brush:java;> 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            }</PRE>
总结下几个主要类的用途:<BR>
PackageManagerService: apk包安装服务<BR>
Settings: 管理app的安装信息<BR>
PackageSetting: app的动态安装信息<BR>
SharedUserSetting: 共享Linux用户<BR>
PackageParser.Package: app的静态配置信息。<BR>
Pm: pm命令实现类<BR>
Installer: installd daemon代理类<BR>
HandlerThread: PMS工作线程<BR>
它们之间的大致关系:<BR>
<IMG style= "WIDTH: 630px; HEIGHT: 418px" alt=\ src= "http://www.2cto.com/uploadfile/Collfiles/20140512/2014051209105546.jpg" ><BR>
<BR>
<STRONG>二、adb install安装</STRONG>
<P> </P>
<P> </P>
<P>通过adb install命令安装时流程略有不同,主要是scanPackageLI()之前的流程不同。host机上的adb从/system/core/adb/adb.c中的main()开始:main()->adb_commandline()->install_app()->pm_command()->send_shellcommand(),其中会把安装包从host传到guest上的临时目录。<BR>
接下来guest里的pm命令(/frameworks/base/cmds/pm/src/com/android/commands/pm/Pm.java)接手,大体流程如下:</P>
<P> </P>
<PRE class =brush: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()</PRE>
这里用到了一开始提到的PMS工作线程,doHandleMessage()就是工作线程用于处理丢给它的消息的。<BR>
<P> </P>
<P><IMG style= "WIDTH: 584px; HEIGHT: 354px" alt=\ src= "http://www.2cto.com/uploadfile/Collfiles/20140512/2014051209105547.jpg" ></P>
<P>这里有几个设计模式值得学习的。首先,Pm中得到PMS的代理类,然后调用installPackageWithVerificationAndEncryption()进行安装。</P>
<P> </P>
<PRE class =brush:java;> 90        mPm = IPackageManager.Stub.asInterface(ServiceManager.getService( package ));
...
957            mPm.installPackageWithVerificationAndEncryption(apkURI, obs, installFlags,
958                    installerPackageName, verificationParams, encryptionParams);</PRE>
由于安装时间一般较长,这里的obs用了Observer模式来监视安装完成事件。
<P> </P>
<P> </P>
<P>其次,InstallArgs用了Strategy模式,而createInstallArgs()使用了简单工厂模式。在handleStartCopy()中,只要根据安装类型生成相应的InstallArgs对象,然后调用统一接口copyApk()等就行了。</P>
<P><IMG style= "WIDTH: 349px; HEIGHT: 240px" alt=\ src= "http://www.2cto.com/uploadfile/Collfiles/20140512/2014051209105548.jpg" ><BR>
另外,HandlerParams和其继承类采用了Template method模式。其中基类中的startCopy()是模板函数,继承类实现handleStartCopy(),handleServiceError()和handleReturnCode()来完成不同工作。HandlerParams包含了要工作线程做的工作内容,工作线程只要取出HandlerParams对象,调用其startCopy()接口,因此这里也用了Command模式的思想。</P>
<P><IMG style= "WIDTH: 508px; HEIGHT: 273px" alt=\ src= "http://www.2cto.com/uploadfile/Collfiles/20140512/2014051209105651.jpg" ></P>
<P>这里的installPackageLI()做了很多前一种安装方式中scanPackageLI(file, ...)的工作,接着它会根据该app是否是全新安装调用replacePackageLI()或installNewPackageLI()。</P>
<P> </P>
<PRE class =brush:java;> 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        }</PRE>
如果是全新的apk,调用installNewPackageLI()进行安装。它调用scanPackageLI()完成主要安装工作。<BR>
<PRE class =brush:java;> 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);</PRE>
从这开始就熟悉了吧,和启动时安装的流程差不多了,最后调用updateSettingsLI()来更新安装信息。
<P> </P>
<P> </P>
<P><STRONG>三、Google Play网络下载安装 </STRONG></P>
<P><BR>
Google Play的包名为com.android.vending。由于是闭源的,看不了源码。不过从反汇编粗略地看,应该是先把apk包下载到:<BR>
/data/data/com.android.providers.downloads/cache/downloadfile.apk<BR>
然后调用installPackage()安装,类似于:</P>
<PRE class =brush:java;>PackageManager pm = context.getPackageManager();
pm.installPackage(packageURI, observer, flags, null );</PRE>
接着就和上面一样了:installPackage()->installPackageWithVerification()->installPackageWithVerificationAndEncryption()。<BR>
<P> </P>
<P><BR>
<STRONG>四、点选apk文件安装</STRONG><BR>
<BR>
这种情况下,会通过PackageInstaller安装app。在/packages/apps/PackageInstaller/src/com/android/packageinstaller/InstallAppProgress.java中:</P>
<P> </P>
<PRE class =brush:java;> 284            pm.installPackageWithVerificationAndEncryption(mPackageURI, observer, installFlags,
285                    installerPackageName, verificationParams, null );</PRE>
后面的故事又都熟悉了吧。<BR>
<BR>
<P> </P>
</N;></PRE>
</USES-PERMISSION></N;></PRE>
</PACKAGE-NAME></PACKAGE-NAME></frameworkfiles.length;>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值