1231
for
(
int
i=
0
; i<; ...=
""
1243
=
""
1244
=
""
if
=
""
pre=
""
>接下来,监控/system/framework目录并扫描该目录。<PRE
class
=brush:java;>
1276
// Find base frameworks (resource packages without code).
1277
mFrameworkInstallObserver =
new
AppDirObserver(
1278
(), OBSERVER_EVENTS,
true
,
false
);</PRE>
这里用Observer模式来监视目录变动。它依赖于Linux kernel提供的Inotify机制。实现主要位于/frameworks/base/core/java/android/os/和/frameworks/base/core/jni/android_util_FileObserver.cpp。对于它的继承类(如AppDirObserver),只要实现onEvent()方法来处理文件或目录变动即可。onEvent()的实现比如/frameworks/base/services/java/com/android/server/pm/中:<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
(), );
...
6460
synchronized
(mPackages) {
6461
updatePermissionsLPw(, p,
6462
() >
0
? UPDATE_PERMISSIONS_ALL :
0
);
6463
}
...
6471
synchronized
(mPackages) {
6472
();
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
(), OBSERVER_EVENTS,
false
,
false
);
1382
();
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
();</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
pkg = (scanFile, scanPath, mMetrics, parseFlags);
assmgr =
new
AssetManager();
parser = (cookie, ANDROID_MANIFEST_FILENAME);
pkg = parsePackage(res, parser, flags, errorText);
// parse
...
scannedPkg = scanPackageLI(pkg, parseFlags, scanMode | SCAN_UPDATE_SIGNATURE, currentTime, user);</PRE>
可以看到scanPackageLI()和parsePackage()皆有重载版本。基本上内层的版本才是做事的。内层的parsePackage(res, ...)函数用于解析文件。实现在/frameworks/base/core/java/android/content/pm/。至于是apk中必不可少的配置文件。详见http:
///guide/topics/manifest/,没有的话Android压根不让你装。<BR>
<PRE
class
=brush:java;>
1034
String tagName = ();
1035
if
((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
(TAG, <MANIFEST> has more than one );
1043
(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
((keys)) {
1053
if
(!parseKeys(pkg, res, parser, attrs, outError)) {
1054
return
null
;
1055
}
1056
}
else
if
((permission-group)) {
1057
if
(parsePermissionGroup(pkg, flags, res, parser, attrs, outError) ==
null
) {
1058
return
null
;
1059
}
1060
}
else
if
((permission)) {
1061
if
(parsePermission(pkg, res, parser, attrs, outError) ==
null
) {
1062
return
null
;
1063
}
1064
}
else
if
((permission-tree)) {
1065
if
(parsePermissionTree(pkg, res, parser, attrs, outError) ==
null
) {
1066
return
null
;
1067
}
1068
}
else
if
((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)信息和库等信息。一顿解析后,返回结果对象pkg,这个类基本上就包含了里的信息。接下来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 = (name);
248
if
(s ==
null
) {
249
if
(!create) {
250
return
null
;
251
}
252
s =
new
SharedUserSetting(name, pkgFlags);
253
= newUserIdLPw(s);
254
(, New shared user + name + : id= + );
255
// < 0 means we couldn't assign a userid; fall out and return
256
// s, which is currently null
257
if
( >=
0
) {
258
(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 = (pkg, origPackage, realName, suid, destCodeFile,
4305
destResourceFile, ,
4306
, user,
false
);</PRE>
<P> </P>
<P>getPackageLPw()实现在/frameworks/base/services/java/com/android/server/pm/中:</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
= newUserIdLPw(p);</PRE>
<P> </P>
newUserIdLPw()函数为app分配uid。这样,该应用对应的uid就设置好了。<BR>
<P> </P>
<P>回到scanPackageLI()中,下面设置进程名,进程名默认就是包名。所以我们在ps里看到的都是包名。</P>
<P> </P>
<PRE
class
=brush:java;>
4422
= fixProcessName(
4423
,
4424
,
4425
);</PRE>
对于大部分全新安装的一般应用而言,接下来为应用创建数据目录:
<P> </P>
<P> </P>
<PRE
class
=brush:java;>
3987
private
int
createDataDirsLI(String packageName,
int
uid, String seinfo) {
3988
int
[] users = ();
3989
int
res = (packageName, uid, uid, seinfo);
...
3993
for
(
int
user : users) {
3994
if
(user !=
0
) {
3995
res = (packageName,
3996
(user, uid), user);</PRE>
Installer是一个代理类,它会和后台的installd通信并让installd完成具体工作。installd的实现位于/frameworks/
native
/cmds/installd/,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
( ==
null
) {
4557
setInternalAppNativeLibraryPath(pkg, pkgSetting);
4558
}
else
{
4559
= ;
4560
}
...
4577
if
( !=
null
) {
4578
try
{
4579
File nativeLibraryDir =
new
File();
...
4605
try
{
4606
if
(copyNativeLibrariesForInternalApp(scanFile, nativeLibraryDir) != PackageManager.INSTALL_SUCCEEDED) {
4607
(TAG, Unable to copy
native
libraries);
4608
mLastScanError = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
4609
return
null
;
</PRE>
这里调用copyNativeLibrariesForInternalApp(),它会调用()把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 value */
747
}</PRE>
前者适用于dalvik,将dex优化成odex文件。后者适用于art,将dex直接一步到位编译成oat文件(也就是可执行代码)了。由于下层是执行了/system/bin/dexopt或/system/bin/dex2oat文件,dexopt()将之放到子进程去做,自己作为父进程等待它结束。以art为例,/system/bin/dex2oat被调用(实现位于/art/dex2oat/)。输出的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(,
4744
, update pkg);</PRE>
之后将安装的app注册到PMS的mPackages中,mPackges是个Hash表,保存了从包名到的映射。注意Settings里也有个mPackages,那里保存的是包名到PackageSetting的映射。前者主要是app配置文件中的信息,而后者是安装过程中的信息。可以粗略理解为一个是静态信息,一个是动态信息。<BR>
<PRE
class
=brush:java;>
4763
(pkgSetting, pkg);
// PackageSetting <=
addPackageSettingLPw(p, , )
(name, p);
4764
// Add the new setting to mPackages
4765
(, pkg);</PRE>
下面把app中的组件信息(content provider, service, receiver, activity)记录到系统中。另外根据前面app配置文件中的权限信息进行初始化。
<P> </P>
<P> </P>
<P> </P>
<PRE
class
=brush:java;>
4811
int
N = ();
4812
StringBuilder r =
null
;
4813
int
i;
4814
for
(i=
0
; i<N; =
""
=
"fixProcessName(,"
p=
"(i);"
n=
"();"
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
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
( pkg : ()) {
5434
if
(pkg != pkgInfo) {
5435
grantPermissionsLPw(pkg, (flags&UPDATE_PERMISSIONS_REPLACE_ALL) !=
0
);
5436
}
5437
}
5438
}</PRE>
在中app会申请一些权限,比如读取位置信息,读取联系人,操作摄像头等等。中的格式如:<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( pkg,
boolean
replace) {
...
5467
final
int
N = ();
5468
for
(
int
i=
0
; i<N; ...=
""
pre=
""
if
=
""
=
"appendInts(,"
else
=
""
changedpermission=
"true;"
5534
=
""
5533
=
""
5532
=
""
5531
=
""
5530
=
""
5529
=
""
5528
=
""
>最后,writeLPr()将安装信息写回文件,这也是一开始readLPw()读的那个文件。这样下次启动时就可以按照这里边的信息重新安装了。<PRE
class
=brush:java;>
1261
void
writeLPr() {
...
1315
(
null
, permission-trees);
1316
for
(BasePermission bp : ()) {
1317
writePermissionLPr(serializer, bp);
1318
}
1319
(
null
, permission-trees);
1320
1321
(
null
, permissions);
1322
for
(BasePermission bp : ()) {
1323
writePermissionLPr(serializer, bp);
1324
}
1325
(
null
, permissions);
1326
1327
for
(
final
PackageSetting pkg : ()) {
1328
writePackageLPr(serializer, pkg);
1329
}</PRE>
总结下几个主要类的用途:<BR>
PackageManagerService: apk包安装服务<BR>
Settings: 管理app的安装信息<BR>
PackageSetting: app的动态安装信息<BR>
SharedUserSetting: 共享Linux用户<BR>
: app的静态配置信息。<BR>
Pm: pm命令实现类<BR>
Installer: installd daemon代理类<BR>
HandlerThread: PMS工作线程<BR>
它们之间的大致关系:<BR>
<IMG style=
"WIDTH: 630px; HEIGHT: 418px"
alt=\ src=
"http:///uploadfile/Collfiles/20140512/"
><BR>
<BR>
<STRONG>二、adb install安装</STRONG>
<P> </P>
<P> </P>
<P>通过adb install命令安装时流程略有不同,主要是scanPackageLI()之前的流程不同。host机上的adb从/system/core/adb/中的main()开始:main()->adb_commandline()->install_app()->pm_command()->send_shellcommand(),其中会把安装包从host传到guest上的临时目录。<BR>
接下来guest里的pm命令(/frameworks/base/cmds/pm/src/com/android/commands/pm/)接手,大体流程如下:</P>
<P> </P>
<PRE
class
=brush:java;>runInstall()
installPackageWithVerificationAndEncryption()
doHandleMessage()
// INIT_COPY
doHandleMessage()
// MCS_BOUND
startCopy()
()
InstallArgs args = createInstallArgs(
this
)
()
// FileInstallArgs或AsecInstallArgs,取决于是否是forward-lock或装在sd card上。
createCopyFile()
// 拷贝生成类似于/data/app/这样的临时文件,因为这时候包都没解析,不知道包名。
handleReturnCode()
processPendingInstall()
//异步方式安装,因为安装过程可能较长。
installPackageLI()</PRE>
这里用到了一开始提到的PMS工作线程,doHandleMessage()就是工作线程用于处理丢给它的消息的。<BR>
<P> </P>
<P><IMG style=
"WIDTH: 584px; HEIGHT: 354px"
alt=\ src=
"http:///uploadfile/Collfiles/20140512/"
></P>
<P>这里有几个设计模式值得学习的。首先,Pm中得到PMS的代理类,然后调用installPackageWithVerificationAndEncryption()进行安装。</P>
<P> </P>
<PRE
class
=brush:java;>
90
mPm = ((
package
));
...
957
(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:///uploadfile/Collfiles/20140512/"
><BR>
另外,HandlerParams和其继承类采用了Template method模式。其中基类中的startCopy()是模板函数,继承类实现handleStartCopy(),handleServiceError()和handleReturnCode()来完成不同工作。HandlerParams包含了要工作线程做的工作内容,工作线程只要取出HandlerParams对象,调用其startCopy()接口,因此这里也用了Command模式的思想。</P>
<P><IMG style=
"WIDTH: 508px; HEIGHT: 273px"
alt=\ src=
"http:///uploadfile/Collfiles/20140512/"
></P>
<P>这里的installPackageLI()做了很多前一种安装方式中scanPackageLI(file, ...)的工作,接着它会根据该app是否是全新安装调用replacePackageLI()或installNewPackageLI()。</P>
<P> </P>
<PRE
class
=brush:java;>
9061
if
(!(, pkgName, oldCodePath)) {
// 前面拷贝apk时是随机取了临时名字的,这里用doRename()函数为其“正名”。
...
9068
if
(replace) {
9069
replacePackageLI(pkg, parseFlags, scanMode, ,
9070
installerPackageName, res);
9071
}
else
{
9072
installNewPackageLI(pkg, parseFlags, scanMode, ,
9073
installerPackageName, res);
9074
}</PRE>
如果是全新的apk,调用installNewPackageLI()进行安装。它调用scanPackageLI()完成主要安装工作。<BR>
<PRE
class
=brush:java;>
8601
private
void
installNewPackageLI( pkg,
8602
int
parseFlags,
int
scanMode, UserHandle user,
8603
String installerPackageName, PackageInstalledInfo res) {
...
8630
newPackage = scanPackageLI(pkg, parseFlags, scanMode,
8631
(), user);</PRE>
从这开始就熟悉了吧,和启动时安装的流程差不多了,最后调用updateSettingsLI()来更新安装信息。
<P> </P>
<P> </P>
<P><STRONG>三、Google Play网络下载安装 </STRONG></P>
<P><BR>
Google Play的包名为。由于是闭源的,看不了源码。不过从反汇编粗略地看,应该是先把apk包下载到:<BR>
/data/data//cache/<BR>
然后调用installPackage()安装,类似于:</P>
<PRE
class
=brush:java;>PackageManager pm = ();
(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/中:</P>
<P> </P>
<PRE
class
=brush:java;>
284
(mPackageURI, observer, installFlags,
285
installerPackageName, verificationParams,
null
);</PRE>
后面的故事又都熟悉了吧。<BR>
<BR>
<P> </P>
</N;></PRE>
</USES-PERMISSION></N;></PRE>
</PACKAGE-NAME></PACKAGE-NAME></;>