功能有三部分
- 提供一个能够根据intent匹配到具体的Activity、Service、Provider。即当应用程序调用了startActivity(intent)时,能够把参数中指定的intent转换成一个具体的包含了程序包名称及具体Component(组件、构建、元件),以便java类加载器加载具体的Component。
- 进行权限检查。即当应用程序调用某个需要一定权限的函数调用时,系统能够判断调用者是否具备该权限,从而保证系统的安全。
- 提供安装、删除应用程序的接口。
--------------------------------------------------------------------
Framework中的包管理框架分为三层,应用程序层、PmS服务层、数据文件层
1.应用程序层
应用程序需要使用包管理服务时,调用ContextImpl类的getPackageManager()函数返回一个PackageManager对象,然后调用该对象所提供的各种API接口.(一般的服务调用是通过getSystemService("服务名")获得的,而PmS是提供了一个函数,但无本质区别).第一个目录是"/system/etc/permission",该目录下的所有XML文件用于permission的管理.包含两件事.一:定义了系统中包含哪些feature(特色,功能),应用程序可以在AndroidManifest.xml中使用<use-feature>标签声明程序需要哪些feature,比如:
1 <use-feature android:name="android.hardware.wifi" android:required="true" />
该标签的作用仅仅是"声明"或者"建议"设备是否具备指定的feature,而不会影响程序的安装,required(需要,必须的)的作用是如果没有改feature,程序不能运行.设备都包含了哪些feature就是在该目录下声明的,因此,如果用户给系统添加了GPS或者BlueTooth,则必须在该目录下声明相关的XML文件,以便Framework知晓.
例图 目录结构
代码结构
该目录下另外还有一个platform.xml文件,该文件中为一些特别的uid和gid分配了一些默认的权限.给uid分配权限使用<assign-permission>标签,给gid分配权限使用<permission>标签,如下

第二个目录是"/data/system/packages.xml",该文件保存了所有安装程序的基本包信息,有点像系统的"注册表".详细信息后面介绍.
除了PmS服务外,还有两个辅助系统服务用于程序安装.一个是DefaultContainerService,该服务主要用于把安装程序复制到程序目录中;另一个是Installer服务,该服务实际上并不是一个Binder,而是一个Socket客户端,PmS直接和该Socket客户端交互.Socket的服务端主要完成程序的文件解压工作及数据目录创建,比如从APK文件中提取出dex文件,删除dalvik-cache目录下的dex文件,创建程序专属的数据目录等.
3.数据应用层
android程序中的程序文件分为三部分:
第一部分,程序文件.所有的系统程序保存在/system/app目录下,所有的第三方应用程序保存在/data/app目录下.对于非系统程序在安装前可以保存在任意目录下,但在安装后,PmS会把APK文件存放到/data/app目录下.它与原始的APK文件唯一的区别是文件名不同,原始的APK可以任意命名,而该目录下的APK是以包名进行命名,并自动增加一个"-x"后缀,比如com.android.xxx.text-1.apk.当同样一个程序第二次安装时,就会变成com.android.xxx.text-2.apk.第三次安装时又会变成com.android.xxx.text-1.apk.
/data/dalvik-cache目录保存了程序中执行代码.一个APK实际上是一个jar压缩类型的文件,压缩包中包含了各种资源文件,资源索引文件,AndroidManifest文件及程序文件,当应用程序运行前,PmS会从APK文件中提取代码文件,也就是所谓的dex文件,并将该文件存储在该目录下,以便以后能够快速运行该程序.
第二部分,framework库文件.这些库文件存在于/system/framework目录下,库文件类型是APK或者Jar,系统开机后,dalvik虚拟机会加载这些库文件,而在PmS启动时,如果这些Jar或者APK文件还没有转换成dex文件,则PmS会将这些库文件转换成dex文件,并保存到/data/dalvik-cache目录下.
第三部分,应用程序所使用的数据文件.应用程序可以使用三种数据保存方式,分别为参数存储,数据库存储,文件存储.这三种存储方式对应的数据文件一般都保存在/data/data/xxx目录下,xxx代表程序包名.在2.2之后,系统还提供了额外三个目录,其中/data/secure用于保存加密数据,Framework将提供一种加密接口,所有的加密文件将存放到该目录下./data/drm目录主要用于存放包含有数字版权的数据文件,同样,Framework层将提供一种与Drm相关的类接口用于间接访问该目录中的文件./data/app-private目录目前和/data/drm的作用相同,Framework中似乎没有使用/data/drm,er临时使用/data/app-private代替.
--------------------------------------------------------------------
packages.xml文件格式
packagers.xml在/data/system目录下,该文件记录了系统汇总所有应用程序的包管理相关信息,PmS将根据这些信息进行包管理的各种操作.last-platform-version:该标签记录了系统最后一次的版本号,一般和SDK版本号相同.标签中包含两个属性,一个是internal(内部的),另一个是external(外部的),internal是内部存储区上的程序被更新前的系统版本号,external是外部存储区程序更新前的系统版本号,一般情况下两者相同.(所谓程序更新,不是指应用程序有了新版本的更新,而是指系统升级后,需要重新为所有自己安装的程序设置访问权限,即重新建立包管理信息所需的XML文件)permission:保存系统中所有的权限列表,包含Framework定义的权限以及应用程序自定义的权限,Framework中定义的权限在android/framework/base/core/res/rs/AndroidManifest.xml中.每个permission都有一个item标签标识,item标签中分别包含三个属性.- name:权限名称.系统权限一般都以android.permission.开头,应用程序中自定义权限一般都以所属的包名开头,权限名称必须全局唯一.
- package:权限所在的包名,Framework对应的包名为android.
- protectionLevel:保护级别.一共有四种保护级别,分别为普通的,危险的,签名的,签名或系统的.具体参见http://developer.android.com/guide/topics/manifest/permission-element.html
cert:代表certifacation,即"证书"的意思,一个应用程序一般只包含一个证书.cert包含两个属性,分别是index和key.- index:在PmS内部保存了所有的证书,每个证书对应一个索引,index表示的就是索引.
- key:该属性不是必需的,如果index对应的证书已经存在,则该key子标签可以没有,而如果index是首次出现,则key的值必须指定,其值为2390个16进制值,来源于对APK签名的证书文件.
从表面上可以修改cert的标签值来改变应用程序的签名.但实际是不可行的.因为在PmS每次启动时同时解析packages.xml和packages.list文件内容.如果发现uid不一致,会直接卸载程序,并重新扫描安装目录,根据读取信息重写packages.xml和packages.list文件.sigs:代表signature,即"签名",一个应用程序只能有一个签名,但一个签名可以包含多个证书.签名标签只有一个count属性,表示包含多少个证书,sigs标签中必须包含count个cert子标签,用于表示具体的证书.perms:代表permission,即"权限".一个应用程序可以申请多个权限,perms标签包含了所有这些权限的列表.perms标签中可以包含item子标签,用于表示每一个具体的权限.注意区分perms标签和permission标签,perms标签用于表示一个具体应用程序所使用的权限列表,该标签一般包含在package标签或者shared-user标签中,而permission标签却是表示系统中所有定义的权限列表,该列表是全局的.package:包含一个应用程序包的相关信息,有如下属性- name:程序包名称,比如com.android.xxx.xxx
- codePath:程序包所在路径,比如/system/app/xxx.apk,/data/app/com.xxx.xxx-1.apk.
- nativeLibrayPath:该程序所使用的native库文件路径.在一般情况下native库文件会被安装到程序数据文件路径下的lib子目录中,比如/data/data/com.xxx.xxx/lib.
- flags:用于标识应用程序类型,这些类型在ApplicationInfo.java中有定义,包括FLAG_SYSTEM,FLAG_DEBUGGABLE,FLAG_PERSISTENT等.
- it,ut:分别代表"首次安装的时间install time"和"最后升级时间update time".
- ft,ts:这两个标签的含义相同,代表最后一次修改该记录的时间,一般情况下,这个时间等于ut标签值.
- installStatus:改属性仅在packages-backup.xml文件中存在,其值一般为false,当值为true时,该属性一般不会再写到文件中.
- userId和sharedUserId:前者代表应用程序对应的Linux用户id值,后者代表该应用程序共享的Linux用户id,这两个属性是互斥的,即只能有一个属性.当存在sharedUserId时,该应用程序将以共享id的身份运行,但这并不是说所有共享id的程序运行在同一个进程中.
- installer:安装器的名称,一般是值应用程序调用PackageManager的installPackage()函数时,参数installerPackageName的值.
package标签中可以包含sigs和perms子标签,分别表示该应用程序所使用的签名信息及权限信息.如下1 <package name="com.android.xxx" codePath="/system/app/xxx.apk" nativeLibraryPath="/data/data.com.android.xxx/lib" flags="1" ft="12dabe97198" it="12d805b2cd8" ut="12dabe97198" version="9" userId="10086">2 <sigs count="1">3 <cert index="0" key="3082
"/>4 </sigs>5 </package>
shared-user:系统为每一个应用程序分配一个Linux用户id,一些native进程的用户id从1000开始,比如shell进程,log进程,phone进程等,java应用程序对应的用户id从10000开始.从某种角度讲,一个应用程序就是一个用户id,当然,应用程序申请使用共享用户id除外.shared-user标签定义了共享用户id对应的签名和权限,它和package标签的含义本质是相同的.shared-user的属性包括name和userId,其意义和package标签的属性相同,包含的子标签也和package相同,包括sigs和perms标签.--------------------------------------------------------------------
各主要功能类的关系Settings类:在PmS中,Settings基本上包含了所需的全部信息,该类主要包含几类变量.1.包属性信息 1 /*配置文件名称,指的就是packages.xml*/
2 private final File mSettingsFilename;
3 /*配置文件可以有一个backup文件,该backup文件用于系统以外关机后和原始配置文件进行对比,以检查系统的完整性*/
4 private final File mBackupSettingsFilename;
5 /*值得就是packages.list文件,保存了所有的应用程序列表,该文件中每一行对应一个应用程序,比如com.android.haiii.debugjar 10032 1 /data/data/com.android.haiii.debugjar
6
7 在该记录中,第一项表示应用程序包名称;第二项代表该应用程序的Linux用户id,如果应用程序使用的共享用户id,则指的是共享用户id;第三项数字1代表该应用程序可以被debug,0代表不可以.第四项代表应用程序的数据文件的目录*/
8 private final File mPackageListFilename;
9 /*指的是文件packages-stopped.xml,这个文件记录的是系统中强制停止运行的Package*/
10 private final File mStoppedPackagesFilename;
11 /*packages-stopped.xml文件的备份*/
12 private final File mBackupStoppedPackagesFilename;
13 /*在PmS启动后,该变量将被填充为包管理信息,这些信息来源于pacaages.xml.注意该变量和PmS中的mPackages的区别,前者是从packages.xml读取的记录信息,而后者则是直接扫描程序目录下所有APK文件而生成的信息,并且前者的类型是PackageSetting,后者是PackageParse.Package*/
14 final HashMap<String, PackageSetting> mPackages =new HashMap<String, PackageSetting>();
15 /*该变量保存那些没有通过标准卸载方法删除的程序列表.如果使用标准卸载方法,PmS在卸载程序后,会清除该程序在packages.xml中对应的记录,而如果通过adb或者其他文件操作删除APK文件,那么packages.xml中对应的记录将不会被删除.每次系统启动时,PmS会检查packages.xml文件,并检查相应程序目录下的文件,从而判断是否被以外删除.如果删除,如果删除,则把相应的包信息存放到mDisabledSysPackage列表中,该变量的Sys不是指系统程序,指的是应用程序*/
16 private final HashMap<String, PackageSetting> mDisabledSysPackages = new HashMap<String, PackageSetting>();
17 /*保存packages.xml中last-platform-version标签中的值*/
18 int mInternalSdkPlatform;
19 /*同上*/
20 int mExternalSdkPlatform;
用户id相关信息/*该变量保存了所有共享id的信息,来源于packages.xml中的<shared-user>标签*/
final HashMap<String, SharedUserSetting> mSharedUsers = new HashMap<String, SharedUserSetting>();
/*所有用户id*/
private final ArrayList<Object> mUserIds = new ArrayList<Object>();
/*当PmS解析packages.xml文件时,如果发现某个package标签中使用的是sharedUserId,则暂时把该package添加到mPendingPackages列表中,直到最后解析完毕shared-user标签后,在完善原来的package标签中的相关信息*/
private final ArrayList<PendingPackage> mPendingPackages = new ArrayList<PendingPackage>();
权限相关信息
/*保存了所有签名*/
private final ArrayList<Signature> mPastSignatures = new ArrayList<Signature>();
/*保存了所有的权限,该Map是从permission名称到permission信息的一个映射*/
final HashMap<String, BasePermission> mPermissions = new HashMap<String, BasePermission>();
/*对应packages.xml文件中的permission-tree标签.*/
final HashMap<String, BasePermission> mPermissionTrees = new HashMap<String, BasePermission>();
删除信息/*保存的是packages.xml中的cleaning-package标签中包含的package列表,该列表来源于那些已经被卸载的应用程序,但其所包含的数据目录由于保存在外部存储区而没有被删除.因为PmS卸载程序时,如果该程序的数据保存在外部存储空间中,则其数据目录默认不会被删除.*/
final ArrayList<PackageCleanItem> mPackagesToBeCleaned = new ArrayList<PackageCleanItem>();
PmS类中包含的重要变量 1 /*保存所有程序包信息,来源于扫描程序目录下所有程序文件*/
2 final HashMap<String, PackageParser.Package> mPackages = new HashMap<String, PackageParser.Package>();
3 /*Settings类*/
4 final Settings mSettings
5 /*一个feature本质上只是一段字符串描述,比如"android.hardwar.wifi"*/
6 final HashMap<String, FeatureInfo> mAvailableFeatures = new HashMap<String, FeatureInfo>();
7 /*系统中所有Linux用户id*/
8 int[] mGlobalGids;
9 /*系统中所有的权限名称*/
10 final SparseArray<HashSet<String>> mSystemPermissions = new SparseArray<HashSet<String>>();
11 /*系统所以来的共享Java库,其值来源于platform.xml中library标签中定义的值*/
12 final HashMap<String, String> mSharedLibraries = new HashMap<String, String>();
13
14 // All available activities, for your resolving pleasure.
15 final ActivityIntentResolver mActivities = new ActivityIntentResolver();
16
17 // All available receivers, for your resolving pleasure.
18 final ActivityIntentResolver mReceivers = new ActivityIntentResolver();
19
20 // All available services, for your resolving pleasure.
21 final ServiceIntentResolver mServices = new ServiceIntentResolver();
22
23 /*以上上个变量用于进行Intent-filter的匹配,并分别匹配到Activity,Receiver,Service对象.在PmS初始化时会便利程序目录下的全部的程序,并从其中包含的AndroidManifest.xml文件中提取所有的intent-filter数据,并将其保存到以上三个变量中.系统运行时,应用程序调用PackageManager的 queryIntentXXX()函数时,其内部正式通过以上三个变量查询相关的目标对象信息的*/
--------------------------------------------------------------------
PmS主体启动过程
主体启动过程就是指PmS构造函数内部的启动流程.1.创建一个Settings对象,在该对象的构造函数中,给成员变量静态赋值./*创建Settings对象*/
mSettings = new Settings(context);
/*Settings对象构造函数*/
Settings(Context context, File dataDir) {
mContext = context;
mSystemDir = new File(dataDir, "system");
mSystemDir.mkdirs();
FileUtils.setPermissions(mSystemDir.toString(),
FileUtils.S_IRWXU|FileUtils.S_IRWXG
|FileUtils.S_IROTH|FileUtils.S_IXOTH,
-1, -1);
// 主要的三个变量
mSettingsFilename = new File(mSystemDir, "packages.xml");
mBackupSettingsFilename = new File(mSystemDir, "packages-backup.xml");
mPackageListFilename = new File(mSystemDir, "packages.list");
// Deprecated: Needed for migration 如下变量还没有使用
mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml");
mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml");
}
2.调用mSettings.addSharedUserLP()添加共享id mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID, ApplicationInfo.FLAG_SYSTEM);
mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID, ApplicationInfo.FLAG_SYSTEM);
mSettings.addSharedUserLPw("android.uid.log", LOG_UID, ApplicationInfo.FLAG_SYSTEM);
mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID, ApplicationInfo.FLAG_SYSTEM);
mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID, ApplicationInfo.FLAG_SYSTEM);
3.传递一个Installer对象,该对象将辅助程序的安装,比如完成从APK文件提取出dex文件,删除dex文件等操作.Installer对象内部其实包含一个LocalSocket对象,所有Installer提供的API接口内部其实会通过该LocalSocket发送一系列参数值给远程的SocketServer对象.public final class Installer {
private static final String TAG = "Installer";
private static final boolean LOCAL_DEBUG =
false;
InputStream mIn;
OutputStream mOut;
LocalSocket mSocket;
byte buf[] =
new byte[1024];
int buflen = 0;
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;
}
private void disconnect() {
Slog.i(TAG, "disconnecting

");
try {
if (mSocket !=
null)
mSocket.close();
}
catch (IOException ex) {
}
try {
if (mIn !=
null)
mIn.close();
}
catch (IOException ex) {
}
try {
if (mOut !=
null)
mOut.close();
}
catch (IOException ex) {
}
mSocket =
null;
mIn =
null;
mOut =
null;
}
private boolean readBytes(
byte buffer[],
int len) {
int off = 0, count;
if (len < 0)
return false;
while (off != len) {
try {
count = mIn.read(buffer, off, len - off);
if (count <= 0) {
Slog.e(TAG, "read error " + count);
break;
}
off += count;
}
catch (IOException ex) {
Slog.e(TAG, "read exception");
break;
}
}
if (LOCAL_DEBUG) {
Slog.i(TAG, "read " + len + " bytes");
}
if (off == len)
return true;
disconnect();
return false;
}
private boolean readReply() {
int len;
buflen = 0;
if (!readBytes(buf, 2))
return false;
len = (((
int) buf[0]) & 0xff) | ((((
int) buf[1]) & 0xff) << 8);
if ((len < 1) || (len > 1024)) {
Slog.e(TAG, "invalid reply length (" + len + ")");
disconnect();
return false;
}
if (!readBytes(buf, len))
return false;
buflen = len;
return true;
}
private boolean writeCommand(String _cmd) {
byte[] cmd = _cmd.getBytes();
int len = cmd.length;
if ((len < 1) || (len > 1024))
return false;
buf[0] = (
byte) (len & 0xff);
buf[1] = (
byte) ((len >> 8) & 0xff);
try {
mOut.write(buf, 0, 2);
mOut.write(cmd, 0, len);
}
catch (IOException ex) {
Slog.e(TAG, "write error");
disconnect();
return false;
}
return true;
}
private synchronized String transaction(String cmd) {
if (!connect()) {
Slog.e(TAG, "connection failed");
return "-1";
}
if (!writeCommand(cmd)) {
/*
* If installd died and restarted in the background (unlikely but
* possible) we'll fail on the next write (this one). Try to
* reconnect and write the command one more time before giving up.
*/ Slog.e(TAG, "write command failed? reconnect!");
if (!connect() || !writeCommand(cmd)) {
return "-1";
}
}
if (LOCAL_DEBUG) {
Slog.i(TAG, "send: '" + cmd + "'");
}
if (readReply()) {
String s =
new String(buf, 0, buflen);
if (LOCAL_DEBUG) {
Slog.i(TAG, "recv: '" + s + "'");
}
return s;
}
else {
if (LOCAL_DEBUG) {
Slog.i(TAG, "fail");
}
return "-1";
}
}
private int execute(String cmd) {
String res = transaction(cmd);
try {
return Integer.parseInt(res);
}
catch (NumberFormatException ex) {
return -1;
}
}
public int install(String name,
int uid,
int gid) {
StringBuilder builder =
new StringBuilder("install");
builder.append(' ');
builder.append(name);
builder.append(' ');
builder.append(uid);
builder.append(' ');
builder.append(gid);
return execute(builder.toString());
}
public int dexopt(String apkPath,
int uid,
boolean isPublic) {
StringBuilder builder =
new StringBuilder("dexopt");
builder.append(' ');
builder.append(apkPath);
builder.append(' ');
builder.append(uid);
builder.append(isPublic ? " 1" : " 0");
return execute(builder.toString());
}
public int movedex(String srcPath, String dstPath) {
StringBuilder builder =
new StringBuilder("movedex");
builder.append(' ');
builder.append(srcPath);
builder.append(' ');
builder.append(dstPath);
return execute(builder.toString());
}
public int rmdex(String codePath) {
StringBuilder builder =
new StringBuilder("rmdex");
builder.append(' ');
builder.append(codePath);
return execute(builder.toString());
}
public int remove(String name,
int userId) {
StringBuilder builder =
new StringBuilder("remove");
builder.append(' ');
builder.append(name);
builder.append(' ');
builder.append(userId);
return execute(builder.toString());
}
public int rename(String oldname, String newname) {
StringBuilder builder =
new StringBuilder("rename");
builder.append(' ');
builder.append(oldname);
builder.append(' ');
builder.append(newname);
return execute(builder.toString());
}
public int fixUid(String name,
int uid,
int gid) {
StringBuilder builder =
new StringBuilder("fixuid");
builder.append(' ');
builder.append(name);
builder.append(' ');
builder.append(uid);
builder.append(' ');
builder.append(gid);
return execute(builder.toString());
}
public int deleteCacheFiles(String name,
int userId) {
StringBuilder builder =
new StringBuilder("rmcache");
builder.append(' ');
builder.append(name);
builder.append(' ');
builder.append(userId);
return execute(builder.toString());
}
public int createUserData(String name,
int uid,
int userId) {
StringBuilder builder =
new StringBuilder("mkuserdata");
builder.append(' ');
builder.append(name);
builder.append(' ');
builder.append(uid);
builder.append(' ');
builder.append(userId);
return execute(builder.toString());
}
public int removeUserDataDirs(
int userId) {
StringBuilder builder =
new StringBuilder("rmuser");
builder.append(' ');
builder.append(userId);
return execute(builder.toString());
}
public int clearUserData(String name,
int userId) {
StringBuilder builder =
new StringBuilder("rmuserdata");
builder.append(' ');
builder.append(name);
builder.append(' ');
builder.append(userId);
return execute(builder.toString());
}
/**
* Clone all the package data directories from srcUserId to targetUserId. If copyData is true,
* some of the data is also copied, otherwise just empty directories are created with the
* correct access rights.
* @param srcUserId user to copy the data directories from
* @param targetUserId user to copy the data directories to
* @param copyData whether the data itself is to be copied. If false, empty directories are
* created.
* @return success/error code
*/ public int cloneUserData(
int srcUserId,
int targetUserId,
boolean copyData) {
StringBuilder builder =
new StringBuilder("cloneuserdata");
builder.append(' ');
builder.append(srcUserId);
builder.append(' ');
builder.append(targetUserId);
builder.append(' ');
builder.append(copyData ? '1' : '0');
return execute(builder.toString());
}
public boolean ping() {
if (execute("ping") < 0) {
return false;
}
else {
return true;
}
}
public int freeCache(
long freeStorageSize) {
StringBuilder builder =
new StringBuilder("freecache");
builder.append(' ');
builder.append(String.valueOf(freeStorageSize));
return execute(builder.toString());
}
public int getSizeInfo(String pkgName,
int persona, String apkPath, String fwdLockApkPath,
String asecPath, PackageStats pStats) {
StringBuilder builder =
new StringBuilder("getsize");
builder.append(' ');
builder.append(pkgName);
builder.append(' ');
builder.append(persona);
builder.append(' ');
builder.append(apkPath);
builder.append(' ');
builder.append(fwdLockApkPath !=
null ? fwdLockApkPath : "!");
builder.append(' ');
builder.append(asecPath !=
null ? asecPath : "!");
String s = transaction(builder.toString());
String res[] = s.split(" ");
if ((res ==
null) || (res.length != 5)) {
return -1;
}
try {
pStats.codeSize = Long.parseLong(res[1]);
pStats.dataSize = Long.parseLong(res[2]);
pStats.cacheSize = Long.parseLong(res[3]);
pStats.externalCodeSize = Long.parseLong(res[4]);
return Integer.parseInt(res[0]);
}
catch (NumberFormatException e) {
return -1;
}
}
public int moveFiles() {
return execute("movefiles");
}
/**
* Links the native library directory in an application's directory to its
* real location.
*
* @param dataPath data directory where the application is
* @param nativeLibPath target native library path
* @return -1 on error
*/ public int linkNativeLibraryDirectory(String dataPath, String nativeLibPath,
int userId) {
if (dataPath ==
null) {
Slog.e(TAG, "linkNativeLibraryDirectory dataPath is null");
return -1;
}
else if (nativeLibPath ==
null) {
Slog.e(TAG, "linkNativeLibraryDirectory nativeLibPath is null");
return -1;
}
StringBuilder builder =
new StringBuilder("linklib ");
builder.append(dataPath);
builder.append(' ');
builder.append(nativeLibPath);
builder.append(' ');
builder.append(userId);
return execute(builder.toString());
}
}
4.给以下几个数据文件路径赋值 File dataDir = Environment.getDataDirectory();
mAppDataDir = new File(dataDir, "data");
mAppInstallDir = new File(dataDir, "app");
mAppLibInstallDir = new File(dataDir, "app-lib");
mAsecInternalPath = new File(dataDir, "app-asec").getPath();
mUserAppDataDir = new File(dataDir, "user");
mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
5.调用readPermission()函数,从/system/etc/permissions目录下读取全部XML文件,这些文件主要定义了两部分系统属性.第一是系统中所有的feature,第二是为一些native系统进程分配了一些特定的权限,读取出的属性值将保存到mSettings.mPermissions变量中.6.调用mSettings对象的readLP()函数从/data/system/packages.xml文件中读取所有应用程序中和包管理相关的信息,这些信息将保存到mSettings.mPackages变量中.7.对Java系统中的库文件进行dex提取(转换).在Android系统中,Java源码编译后的Class文件不能直接被执行,Android中的编译器会将程序中的多个Class文件转换成一个dex文件.转换后的文件和原始的Class文件相比有几点优势,比如节省了字节码,优化了内存分配,重新组织函数映射表等.而APK或者Jar文件本身是一个zip压缩包,其内部包含了真的dex文件,因此,在程序执行前,必须先从这些Jar/APK文件中提取出相应的dex文件.当然并不是说系统每次开机都会进行dex提取,它会调用dalvik.system.DexFile.isDexOptNeeded()函数进行判断,该函数内部会判断dex是否存在,并且内部版本号和原始APK/Jar是否相同.如果是,就不进行dex提取,否则,系统认为原来的dex文件已经过期,于是就会重新提取.Java系统中的库文件包含以下三个部分.- Java Boot路径下的所有文件,Boot路径是指在Terminal下调用getProp("java.boot.class.path")获取的路径,获取的路径以冒号为分隔符.
- 共享库路径:该路径是在platform.xml中使用library标签定义的Jar文件.
- Frameworks目录下的所有APK和Jar文件,除了framework-res.apk之外,因为该文件内部不包含代码,所以不存在dex文件.
// Set flag to monitor and not change apk file paths when
// scanning install directories.
int scanMode = SCAN_MONITOR | SCAN_NO_PATHS | SCAN_DEFER_DEX | SCAN_BOOTING;
if (mNoDexOpt) {
Slog.w(TAG, "Running ENG build: no pre-dexopt!");
scanMode |= SCAN_NO_DEX;
}
final HashSet<String> libFiles = new HashSet<String>();
mFrameworkDir = new File(Environment.getRootDirectory(), "framework");
mDalvikCacheDir = new File(dataDir, "dalvik-cache");
boolean didDexOpt = false;
/**
* Out of paranoia, ensure that everything in the boot class
* path has been dexed.
*/
String bootClassPath = System.getProperty("java.boot.class.path");
if (bootClassPath != null) {
String[] paths = splitString(bootClassPath, ':');
for (int i=0; i<paths.length; i++) {
try {
if (dalvik.system.DexFile.isDexOptNeeded(paths[i])) {
libFiles.add(paths[i]);
mInstaller.dexopt(paths[i], Process.SYSTEM_UID, true);
didDexOpt = true;
}
} catch (FileNotFoundException e) {
Slog.w(TAG, "Boot class path not found: " + paths[i]);
} catch (IOException e) {
Slog.w(TAG, "Cannot dexopt " + paths[i] + "; is it an APK or JAR? "
+ e.getMessage());
}
}
} else {
Slog.w(TAG, "No BOOTCLASSPATH found!");
}
/**
* Also ensure all external libraries have had dexopt run on them.
*/
if (mSharedLibraries.size() > 0) {
Iterator<String> libs = mSharedLibraries.values().iterator();
while (libs.hasNext()) {
String lib = libs.next();
try {
if (dalvik.system.DexFile.isDexOptNeeded(lib)) {
libFiles.add(lib);
mInstaller.dexopt(lib, Process.SYSTEM_UID, true);
didDexOpt = true;
}
} catch (FileNotFoundException e) {
Slog.w(TAG, "Library not found: " + lib);
} catch (IOException e) {
Slog.w(TAG, "Cannot dexopt " + lib + "; is it an APK or JAR? "
+ e.getMessage());
}
}
}
// Gross hack for now: we know this file doesn't contain any
// code, so don't dexopt it to avoid the resulting log spew.
libFiles.add(mFrameworkDir.getPath() + "/framework-res.apk");
/**
* And there are a number of commands implemented in Java, which
* we currently need to do the dexopt on so that they can be
* run from a non-root shell.
*/
String[] frameworkFiles = mFrameworkDir.list();
if (frameworkFiles != null) {
for (int i=0; i<frameworkFiles.length; i++) {
File libPath = new File(mFrameworkDir, frameworkFiles[i]);
String path = libPath.getPath();
// Skip the file if we alrady did it.
if (libFiles.contains(path)) {
continue;
}
// Skip the file if it is not a type we want to dexopt.
if (!path.endsWith(".apk") && !path.endsWith(".jar")) {
continue;
}
try {
if (dalvik.system.DexFile.isDexOptNeeded(path)) {
mInstaller.dexopt(path, Process.SYSTEM_UID, true);
didDexOpt = true;
}
} catch (FileNotFoundException e) {
Slog.w(TAG, "Jar not found: " + path);
} catch (IOException e) {
Slog.w(TAG, "Exception reading jar: " + path, e);
}
}
}
if (didDexOpt) {
// If we had to do a dexopt of one of the previous
// things, then something on the system has changed.
// Consider this significant, and wipe away all other
// existing dexopt files to ensure we don't leave any
// dangling around.
String[] files = mDalvikCacheDir.list();
if (files != null) {
for (int i=0; i<files.length; i++) {
String fn = files[i];
if (fn.startsWith("data@app@")
|| fn.startsWith("data@app-private@")) {
Slog.i(TAG, "Pruning dalvik file: " + fn);
(new File(mDalvikCacheDir, fn)).delete();
}
}
}
}
8.为三个程序目录分别创建一个FileObserver,FileObserver对象内部会检测目录中添加、删除文件的事件,并当事件发生时执行相应的操作.比如,当目录中添加文件时,就会调用scanPackageLI(file,...)函数扫描添加的文件.这三个目录如下- /system/framework:该目录保存了Framework内核相关的程序.
- /system/app:系统程序,一般是指Android中自带的程序.
- /vendor/app:第三方程序,一般是厂商开发的自定义程序.
9.调用scanDirLI()扫描(解析程序中AndroidManifest.xml)以上三个目录中的所有程序文件,并将扫描结果保存到PmS中的mPackages变量中. // Find base frameworks (resource packages without code).
mFrameworkInstallObserver = new AppDirObserver(
mFrameworkDir.getPath(), OBSERVER_EVENTS, true);
mFrameworkInstallObserver.startWatching();
scanDirLI(mFrameworkDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR,
scanMode | SCAN_NO_DEX, 0);
// Collect all system packages.
mSystemAppDir = new File(Environment.getRootDirectory(), "app");
mSystemInstallObserver = new AppDirObserver(
mSystemAppDir.getPath(), OBSERVER_EVENTS, true);
mSystemInstallObserver.startWatching();
scanDirLI(mSystemAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);
// Collect all vendor packages.
mVendorAppDir = new File("/vendor/app");
mVendorInstallObserver = new AppDirObserver(
mVendorAppDir.getPath(), OBSERVER_EVENTS, true);
mVendorInstallObserver.startWatching();
scanDirLI(mVendorAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);
if (DEBUG_UPGRADE) Log.v(TAG, "Running installd update commands");
mInstaller.moveFiles();
private void scanDirLI(File dir, int flags, int scanMode, long currentTime) {
String[] files = dir.list();
if (files == null) {
Log.d(TAG, "No files in app dir " + dir);
return;
}
if (DEBUG_PACKAGE_SCANNING) {
Log.d(TAG, "Scanning app dir " + dir);
}
int i;
for (i=0; i<files.length; i++) {
File file = new File(dir, files[i]);
if (!isPackageFilename(files[i])) {
// Ignore entries which are not apk's
continue;
}
PackageParser.Package pkg = scanPackageLI(file,
flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime, null);
// Don't mess around with apps in system partition.
if (pkg == null && (flags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
mLastScanError == PackageManager.INSTALL_FAILED_INVALID_APK) {
// Delete the apk
Slog.w(TAG, "Cleaning up failed install of " + file);
file.delete();
}
}
}
private PackageParser.Package scanPackageLI(PackageParser.Package pkg,
int parseFlags,
int scanMode,
long currentTime, UserHandle user) {
File scanFile =
new File(pkg.mScanPath);
if (scanFile ==
null || pkg.applicationInfo.sourceDir ==
null ||
pkg.applicationInfo.publicSourceDir ==
null) {
// Bail out. The resource and code paths haven't been set.
Slog.w(TAG, " Code and resource paths haven't been set correctly");
mLastScanError = PackageManager.INSTALL_FAILED_INVALID_APK;
return null;
}
mScanningPath = scanFile;
if ((parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
}
if (pkg.packageName.equals("android")) {
synchronized (mPackages) {
if (mAndroidApplication !=
null) {
Slog.w(TAG, "*************************************************");
Slog.w(TAG, "Core android package being redefined. Skipping.");
Slog.w(TAG, " file=" + mScanningPath);
Slog.w(TAG, "*************************************************");
mLastScanError = PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
return null;
}
// Set up information for our fall-back user intent resolution
// activity.
mPlatformPackage = pkg;
pkg.mVersionCode = mSdkVersion;
mAndroidApplication = pkg.applicationInfo;
mResolveActivity.applicationInfo = mAndroidApplication;
mResolveActivity.name = ResolverActivity.
class.getName();
mResolveActivity.packageName = mAndroidApplication.packageName;
mResolveActivity.processName = "system:ui";
mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
mResolveActivity.theme = com.android.internal.R.style.Theme_Holo_Dialog_Alert;
mResolveActivity.exported =
true;
mResolveActivity.enabled =
true;
mResolveInfo.activityInfo = mResolveActivity;
mResolveInfo.priority = 0;
mResolveInfo.preferredOrder = 0;
mResolveInfo.match = 0;
mResolveComponentName =
new ComponentName(
mAndroidApplication.packageName, mResolveActivity.name);
}
}
if (DEBUG_PACKAGE_SCANNING) {
if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)
Log.d(TAG, "Scanning package " + pkg.packageName);
}
if (mPackages.containsKey(pkg.packageName)
|| mSharedLibraries.containsKey(pkg.packageName)) {
Slog.w(TAG, "Application package " + pkg.packageName
+ " already installed. Skipping duplicate.");
mLastScanError = PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE;
return null;
}
// Initialize package source and resource directories
File destCodeFile =
new File(pkg.applicationInfo.sourceDir);
File destResourceFile =
new File(pkg.applicationInfo.publicSourceDir);
SharedUserSetting suid =
null;
PackageSetting pkgSetting =
null;
if (!isSystemApp(pkg)) {
// Only system apps can use these features.
pkg.mOriginalPackages =
null;
pkg.mRealPackage =
null;
pkg.mAdoptPermissions =
null;
}
// writer
synchronized (mPackages) {
// Check all shared libraries and map to their actual file path.
if (pkg.usesLibraries !=
null || pkg.usesOptionalLibraries !=
null) {
if (mTmpSharedLibraries ==
null ||
mTmpSharedLibraries.length < mSharedLibraries.size()) {
mTmpSharedLibraries =
new String[mSharedLibraries.size()];
}
int num = 0;
int N = pkg.usesLibraries !=
null ? pkg.usesLibraries.size() : 0;
for (
int i=0; i<N; i++) {
final String file = mSharedLibraries.get(pkg.usesLibraries.get(i));
if (file ==
null) {
Slog.e(TAG, "Package " + pkg.packageName
+ " requires unavailable shared library "
+ pkg.usesLibraries.get(i) + "; failing!");
mLastScanError = PackageManager.INSTALL_FAILED_MISSING_SHARED_LIBRARY;
return null;
}
mTmpSharedLibraries[num] = file;
num++;
}
N = pkg.usesOptionalLibraries !=
null ? pkg.usesOptionalLibraries.size() : 0;
for (
int i=0; i<N; i++) {
final String file = mSharedLibraries.get(pkg.usesOptionalLibraries.get(i));
if (file ==
null) {
Slog.w(TAG, "Package " + pkg.packageName
+ " desires unavailable shared library "
+ pkg.usesOptionalLibraries.get(i) + "; ignoring!");
}
else {
mTmpSharedLibraries[num] = file;
num++;
}
}
if (num > 0) {
pkg.usesLibraryFiles =
new String[num];
System.arraycopy(mTmpSharedLibraries, 0,
pkg.usesLibraryFiles, 0, num);
}
}
if (pkg.mSharedUserId !=
null) {
suid = mSettings.getSharedUserLPw(pkg.mSharedUserId,
pkg.applicationInfo.flags,
true);
if (suid ==
null) {
Slog.w(TAG, "Creating application package " + pkg.packageName
+ " for shared user failed");
mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
return null;
}
if (DEBUG_PACKAGE_SCANNING) {
if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)
Log.d(TAG, "Shared UserID " + pkg.mSharedUserId + " (uid=" + suid.userId
+ "): packages=" + suid.packages);
}
}
// Check if we are renaming from an original package name.
PackageSetting origPackage =
null;
String realName =
null;
if (pkg.mOriginalPackages !=
null) {
// This package may need to be renamed to a previously
// installed name. Let's check on that
final String renamed = mSettings.mRenamedPackages.get(pkg.mRealPackage);
if (pkg.mOriginalPackages.contains(renamed)) {
// This package had originally been installed as the
// original name, and we have already taken care of
// transitioning to the new one. Just update the new
// one to continue using the old name.
realName = pkg.mRealPackage;
if (!pkg.packageName.equals(renamed)) {
// Callers into this function may have already taken
// care of renaming the package; only do it here if
// it is not already done.
pkg.setPackageName(renamed);
}
}
else {
for (
int i=pkg.mOriginalPackages.size()-1; i>=0; i--) {
if ((origPackage = mSettings.peekPackageLPr(
pkg.mOriginalPackages.get(i))) !=
null) {
// We do have the package already installed under its
// original name
should we use it?
if (!verifyPackageUpdateLPr(origPackage, pkg)) {
// New package is not compatible with original.
origPackage =
null;
continue;
}
else if (origPackage.sharedUser !=
null) {
// Make sure uid is compatible between packages.
if (!origPackage.sharedUser.name.equals(pkg.mSharedUserId)) {
Slog.w(TAG, "Unable to migrate data from " + origPackage.name
+ " to " + pkg.packageName + ": old uid "
+ origPackage.sharedUser.name
+ " differs from " + pkg.mSharedUserId);
origPackage =
null;
continue;
}
}
else {
if (DEBUG_UPGRADE) Log.v(TAG, "Renaming new package "
+ pkg.packageName + " to old name " + origPackage.name);
}
break;
}
}
}
}
if (mTransferedPackages.contains(pkg.packageName)) {
Slog.w(TAG, "Package " + pkg.packageName
+ " was transferred to another, but its .apk remains");
}
// Just create the setting, don't add it yet. For already existing packages
// the PkgSetting exists already and doesn't have to be created.
pkgSetting = mSettings.getPackageLPw(pkg, origPackage, realName, suid, destCodeFile,
destResourceFile, pkg.applicationInfo.nativeLibraryDir,
pkg.applicationInfo.flags, user,
false);
if (pkgSetting ==
null) {
Slog.w(TAG, "Creating application package " + pkg.packageName + " failed");
mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
return null;
}
if (pkgSetting.origPackage !=
null) {
// If we are first transitioning from an original package,
// fix up the new package's name now. We need to do this after
// looking up the package under its new name, so getPackageLP
// can take care of fiddling things correctly.
pkg.setPackageName(origPackage.name);
// File a report about this.
String msg = "New package " + pkgSetting.realName
+ " renamed to replace old package " + pkgSetting.name;
reportSettingsProblem(Log.WARN, msg);
// Make a note of it.
mTransferedPackages.add(origPackage.name);
// No longer need to retain this.
pkgSetting.origPackage =
null;
}
if (realName !=
null) {
// Make a note of it.
mTransferedPackages.add(pkg.packageName);
}
if (mSettings.isDisabledSystemPackageLPr(pkg.packageName)) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
}
pkg.applicationInfo.uid = pkgSetting.appId;
pkg.mExtras = pkgSetting;
if (!verifySignaturesLP(pkgSetting, pkg)) {
if ((parseFlags&PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
return null;
}
// The signature has changed, but this package is in the system
// image
let's recover!
pkgSetting.signatures.mSignatures = pkg.mSignatures;
// However
if this package is part of a shared user, but it
// doesn't match the signature of the shared user, let's fail.
// What this means is that you can't change the signatures
// associated with an overall shared user, which doesn't seem all
// that unreasonable.
if (pkgSetting.sharedUser !=
null) {
if (compareSignatures(pkgSetting.sharedUser.signatures.mSignatures,
pkg.mSignatures) != PackageManager.SIGNATURE_MATCH) {
Log.w(TAG, "Signature mismatch for shared user : " + pkgSetting.sharedUser);
mLastScanError = PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
return null;
}
}
// File a report about this.
String msg = "System package " + pkg.packageName
+ " signature changed; retaining data.";
reportSettingsProblem(Log.WARN, msg);
}
// Verify that this new package doesn't have any content providers
// that conflict with existing packages. Only do this if the
// package isn't already installed, since we don't want to break
// things that are installed.
if ((scanMode&SCAN_NEW_INSTALL) != 0) {
final int N = pkg.providers.size();
int i;
for (i=0; i<N; i++) {
PackageParser.Provider p = pkg.providers.get(i);
if (p.info.authority !=
null) {
String names[] = p.info.authority.split(";");
for (
int j = 0; j < names.length; j++) {
if (mProviders.containsKey(names[j])) {
PackageParser.Provider other = mProviders.get(names[j]);
Slog.w(TAG, "Can't install because provider name " + names[j] +
" (in package " + pkg.applicationInfo.packageName +
") is already used by "
+ ((other !=
null && other.getComponentName() !=
null)
? other.getComponentName().getPackageName() : "?"));
mLastScanError = PackageManager.INSTALL_FAILED_CONFLICTING_PROVIDER;
return null;
}
}
}
}
}
if (pkg.mAdoptPermissions !=
null) {
// This package wants to adopt ownership of permissions from
// another package.
for (
int i = pkg.mAdoptPermissions.size() - 1; i >= 0; i--) {
final String origName = pkg.mAdoptPermissions.get(i);
final PackageSetting orig = mSettings.peekPackageLPr(origName);
if (orig !=
null) {
if (verifyPackageUpdateLPr(orig, pkg)) {
Slog.i(TAG, "Adopting permissions from " + origName + " to "
+ pkg.packageName);
mSettings.transferPermissionsLPw(origName, pkg.packageName);
}
}
}
}
}
final String pkgName = pkg.packageName;
final long scanFileTime = scanFile.lastModified();
final boolean forceDex = (scanMode&SCAN_FORCE_DEX) != 0;
pkg.applicationInfo.processName = fixProcessName(
pkg.applicationInfo.packageName,
pkg.applicationInfo.processName,
pkg.applicationInfo.uid);
File dataPath;
if (mPlatformPackage == pkg) {
// The system package is special.
dataPath =
new File (Environment.getDataDirectory(), "system");
pkg.applicationInfo.dataDir = dataPath.getPath();
}
else {
// This is a normal package, need to make its data directory.
dataPath = getDataPathForPackage(pkg.packageName, 0);
boolean uidError =
false;
if (dataPath.exists()) {
int currentUid = 0;
try {
StructStat stat = Libcore.os.stat(dataPath.getPath());
currentUid = stat.st_uid;
}
catch (ErrnoException e) {
Slog.e(TAG, "Couldn't stat path " + dataPath.getPath(), e);
}
// If we have mismatched owners for the data path, we have a problem.
if (currentUid != pkg.applicationInfo.uid) {
boolean recovered =
false;
if (currentUid == 0) {
// The directory somehow became owned by root. Wow.
// This is probably because the system was stopped while
// installd was in the middle of messing with its libs
// directory. Ask installd to fix that.
int ret = mInstaller.fixUid(pkgName, pkg.applicationInfo.uid,
pkg.applicationInfo.uid);
if (ret >= 0) {
recovered =
true;
String msg = "Package " + pkg.packageName
+ " unexpectedly changed to uid 0; recovered to " +
+ pkg.applicationInfo.uid;
reportSettingsProblem(Log.WARN, msg);
}
}
if (!recovered && ((parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0
|| (scanMode&SCAN_BOOTING) != 0)) {
// If this is a system app, we can at least delete its
// current data so the application will still work.
int ret = removeDataDirsLI(pkgName);
if (ret >= 0) {
// TODO: Kill the processes first
// Old data gone!
String prefix = (parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0
? "System package " : "Third party package ";
String msg = prefix + pkg.packageName
+ " has changed from uid: "
+ currentUid + " to "
+ pkg.applicationInfo.uid + "; old data erased";
reportSettingsProblem(Log.WARN, msg);
recovered =
true;
// And now re-install the app.
ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid);
if (ret == -1) {
// Ack should not happen!
msg = prefix + pkg.packageName
+ " could not have data directory re-created after delete.";
reportSettingsProblem(Log.WARN, msg);
mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
return null;
}
}
if (!recovered) {
mHasSystemUidErrors =
true;
}
}
else if (!recovered) {
// If we allow this install to proceed, we will be broken.
// Abort, abort!
mLastScanError = PackageManager.INSTALL_FAILED_UID_CHANGED;
return null;
}
if (!recovered) {
pkg.applicationInfo.dataDir = "/mismatched_uid/settings_"
+ pkg.applicationInfo.uid + "/fs_"
+ currentUid;
pkg.applicationInfo.nativeLibraryDir = pkg.applicationInfo.dataDir;
String msg = "Package " + pkg.packageName
+ " has mismatched uid: "
+ currentUid + " on disk, "
+ pkg.applicationInfo.uid + " in settings";
// writer
synchronized (mPackages) {
mSettings.mReadMessages.append(msg);
mSettings.mReadMessages.append('\n');
uidError =
true;
if (!pkgSetting.uidError) {
reportSettingsProblem(Log.ERROR, msg);
}
}
}
}
pkg.applicationInfo.dataDir = dataPath.getPath();
}
else {
if (DEBUG_PACKAGE_SCANNING) {
if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)
Log.v(TAG, "Want this data dir: " + dataPath);
}
//invoke installer to do the actual installation
int ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid);
if (ret < 0) {
// Error from installer
mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
return null;
}
if (dataPath.exists()) {
pkg.applicationInfo.dataDir = dataPath.getPath();
}
else {
Slog.w(TAG, "Unable to create data directory: " + dataPath);
pkg.applicationInfo.dataDir =
null;
}
}
/*
* Set the data dir to the default "/data/data/<package name>/lib"
* if we got here without anyone telling us different (e.g., apps
* stored on SD card have their native libraries stored in the ASEC
* container with the APK).
*
* This happens during an upgrade from a package settings file that
* doesn't have a native library path attribute at all.
*/ if (pkg.applicationInfo.nativeLibraryDir ==
null && pkg.applicationInfo.dataDir !=
null) {
if (pkgSetting.nativeLibraryPathString ==
null) {
setInternalAppNativeLibraryPath(pkg, pkgSetting);
}
else {
pkg.applicationInfo.nativeLibraryDir = pkgSetting.nativeLibraryPathString;
}
}
pkgSetting.uidError = uidError;
}
String path = scanFile.getPath();
/* Note: We don't want to unpack the native binaries for
* system applications, unless they have been updated
* (the binaries are already under /system/lib).
* Also, don't unpack libs for apps on the external card
* since they should have their libraries in the ASEC
* container already.
*
* In other words, we're going to unpack the binaries
* only for non-system apps and system app upgrades.
*/ if (pkg.applicationInfo.nativeLibraryDir !=
null) {
try {
File nativeLibraryDir =
new File(pkg.applicationInfo.nativeLibraryDir);
final String dataPathString = dataPath.getCanonicalPath();
if (isSystemApp(pkg) && !isUpdatedSystemApp(pkg)) {
/*
* Upgrading from a previous version of the OS sometimes
* leaves native libraries in the /data/data/<app>/lib
* directory for system apps even when they shouldn't be.
* Recent changes in the JNI library search path
* necessitates we remove those to match previous behavior.
*/ if (NativeLibraryHelper.removeNativeBinariesFromDirLI(nativeLibraryDir)) {
Log.i(TAG, "removed obsolete native libraries for system package "
+ path);
}
}
else {
if (!isForwardLocked(pkg) && !isExternal(pkg)) {
/*
* Update native library dir if it starts with
* /data/data
*/ if (nativeLibraryDir.getPath().startsWith(dataPathString)) {
setInternalAppNativeLibraryPath(pkg, pkgSetting);
nativeLibraryDir =
new File(pkg.applicationInfo.nativeLibraryDir);
}
try {
if (copyNativeLibrariesForInternalApp(scanFile, nativeLibraryDir) != PackageManager.INSTALL_SUCCEEDED) {
Slog.e(TAG, "Unable to copy native libraries");
mLastScanError = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
return null;
}
}
catch (IOException e) {
Slog.e(TAG, "Unable to copy native libraries", e);
mLastScanError = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
return null;
}
}
if (DEBUG_INSTALL) Slog.i(TAG, "Linking native library dir for " + path);
final int[] userIds = sUserManager.getUserIds();
synchronized (mInstallLock) {
for (
int userId : userIds) {
if (mInstaller.linkNativeLibraryDirectory(pkg.packageName,
pkg.applicationInfo.nativeLibraryDir, userId) < 0) {
Slog.w(TAG, "Failed linking native library dir (user=" + userId
+ ")");
mLastScanError = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
return null;
}
}
}
}
}
catch (IOException ioe) {
Slog.e(TAG, "Unable to get canonical file " + ioe.toString());
}
}
pkg.mScanPath = path;
if ((scanMode&SCAN_NO_DEX) == 0) {
if (performDexOptLI(pkg, forceDex, (scanMode&SCAN_DEFER_DEX) != 0)
== DEX_OPT_FAILED) {
mLastScanError = PackageManager.INSTALL_FAILED_DEXOPT;
return null;
}
}
if (mFactoryTest && pkg.requestedPermissions.contains(
android.Manifest.permission.FACTORY_TEST)) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_FACTORY_TEST;
}
// Request the ActivityManager to kill the process(only for existing packages)
// so that we do not end up in a confused state while the user is still using the older
// version of the application while the new one gets installed.
if ((parseFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
killApplication(pkg.applicationInfo.packageName,
pkg.applicationInfo.uid);
}
// writer
synchronized (mPackages) {
// We don't expect installation to fail beyond this point,
if ((scanMode&SCAN_MONITOR) != 0) {
mAppDirs.put(pkg.mPath, pkg);
}
// Add the new setting to mSettings
mSettings.insertPackageSettingLPw(pkgSetting, pkg);
// Add the new setting to mPackages
mPackages.put(pkg.applicationInfo.packageName, pkg);
// Make sure we don't accidentally delete its data.
final Iterator<PackageCleanItem> iter = mSettings.mPackagesToBeCleaned.iterator();
while (iter.hasNext()) {
PackageCleanItem item = iter.next();
if (pkgName.equals(item.packageName)) {
iter.remove();
}
}
// Take care of first install / last update times.
if (currentTime != 0) {
if (pkgSetting.firstInstallTime == 0) {
pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = currentTime;
}
else if ((scanMode&SCAN_UPDATE_TIME) != 0) {
pkgSetting.lastUpdateTime = currentTime;
}
}
else if (pkgSetting.firstInstallTime == 0) {
// We need *something*. Take time time stamp of the file.
pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = scanFileTime;
}
else if ((parseFlags&PackageParser.PARSE_IS_SYSTEM_DIR) != 0) {
if (scanFileTime != pkgSetting.timeStamp) {
// A package on the system image has changed; consider this
// to be an update.
pkgSetting.lastUpdateTime = scanFileTime;
}
}
int N = pkg.providers.size();
StringBuilder r =
null;
int i;
for (i=0; i<N; i++) {
PackageParser.Provider p = pkg.providers.get(i);
p.info.processName = fixProcessName(pkg.applicationInfo.processName,
p.info.processName, pkg.applicationInfo.uid);
mProvidersByComponent.put(
new ComponentName(p.info.packageName,
p.info.name), p);
p.syncable = p.info.isSyncable;
if (p.info.authority !=
null) {
String names[] = p.info.authority.split(";");
p.info.authority =
null;
for (
int j = 0; j < names.length; j++) {
if (j == 1 && p.syncable) {
// We only want the first authority for a provider to possibly be
// syncable, so if we already added this provider using a different
// authority clear the syncable flag. We copy the provider before
// changing it because the mProviders object contains a reference
// to a provider that we don't want to change.
// Only do this for the second authority since the resulting provider
// object can be the same for all future authorities for this provider.
p =
new PackageParser.Provider(p);
p.syncable =
false;
}
if (!mProviders.containsKey(names[j])) {
mProviders.put(names[j], p);
if (p.info.authority ==
null) {
p.info.authority = names[j];
}
else {
p.info.authority = p.info.authority + ";" + names[j];
}
if (DEBUG_PACKAGE_SCANNING) {
if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)
Log.d(TAG, "Registered content provider: " + names[j]
+ ", className = " + p.info.name + ", isSyncable = "
+ p.info.isSyncable);
}
}
else {
PackageParser.Provider other = mProviders.get(names[j]);
Slog.w(TAG, "Skipping provider name " + names[j] +
" (in package " + pkg.applicationInfo.packageName +
"): name already used by "
+ ((other !=
null && other.getComponentName() !=
null)
? other.getComponentName().getPackageName() : "?"));
}
}
}
if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
if (r ==
null) {
r =
new StringBuilder(256);
}
else {
r.append(' ');
}
r.append(p.info.name);
}
}
if (r !=
null) {
if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Providers: " + r);
}
N = pkg.services.size();
r =
null;
for (i=0; i<N; i++) {
PackageParser.Service s = pkg.services.get(i);
s.info.processName = fixProcessName(pkg.applicationInfo.processName,
s.info.processName, pkg.applicationInfo.uid);
mServices.addService(s);
if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
if (r ==
null) {
r =
new StringBuilder(256);
}
else {
r.append(' ');
}
r.append(s.info.name);
}
}
if (r !=
null) {
if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Services: " + r);
}
N = pkg.receivers.size();
r =
null;
for (i=0; i<N; i++) {
PackageParser.Activity a = pkg.receivers.get(i);
a.info.processName = fixProcessName(pkg.applicationInfo.processName,
a.info.processName, pkg.applicationInfo.uid);
mReceivers.addActivity(a, "receiver");
if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
if (r ==
null) {
r =
new StringBuilder(256);
}
else {
r.append(' ');
}
r.append(a.info.name);
}
}
if (r !=
null) {
if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Receivers: " + r);
}
N = pkg.activities.size();
r =
null;
for (i=0; i<N; i++) {
PackageParser.Activity a = pkg.activities.get(i);
a.info.processName = fixProcessName(pkg.applicationInfo.processName,
a.info.processName, pkg.applicationInfo.uid);
mActivities.addActivity(a, "activity");
if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
if (r ==
null) {
r =
new StringBuilder(256);
}
else {
r.append(' ');
}
r.append(a.info.name);
}
}
if (r !=
null) {
if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Activities: " + r);
}
N = pkg.permissionGroups.size();
r =
null;
for (i=0; i<N; i++) {
PackageParser.PermissionGroup pg = pkg.permissionGroups.get(i);
PackageParser.PermissionGroup cur = mPermissionGroups.get(pg.info.name);
if (cur ==
null) {
mPermissionGroups.put(pg.info.name, pg);
if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
if (r ==
null) {
r =
new StringBuilder(256);
}
else {
r.append(' ');
}
r.append(pg.info.name);
}
}
else {
Slog.w(TAG, "Permission group " + pg.info.name + " from package "
+ pg.info.packageName + " ignored: original from "
+ cur.info.packageName);
if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
if (r ==
null) {
r =
new StringBuilder(256);
}
else {
r.append(' ');
}
r.append("DUP:");
r.append(pg.info.name);
}
}
}
if (r !=
null) {
if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Permission Groups: " + r);
}
N = pkg.permissions.size();
r =
null;
for (i=0; i<N; i++) {
PackageParser.Permission p = pkg.permissions.get(i);
HashMap<String, BasePermission> permissionMap =
p.tree ? mSettings.mPermissionTrees
: mSettings.mPermissions;
p.group = mPermissionGroups.get(p.info.group);
if (p.info.group ==
null || p.group !=
null) {
BasePermission bp = permissionMap.get(p.info.name);
if (bp ==
null) {
bp =
new BasePermission(p.info.name, p.info.packageName,
BasePermission.TYPE_NORMAL);
permissionMap.put(p.info.name, bp);
}
if (bp.perm ==
null) {
if (bp.sourcePackage ==
null || bp.sourcePackage.equals(p.info.packageName)) {
BasePermission tree = findPermissionTreeLP(p.info.name);
if (tree ==
null || tree.sourcePackage.equals(p.info.packageName)) {
bp.packageSetting = pkgSetting;
bp.perm = p;
bp.uid = pkg.applicationInfo.uid;
if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
if (r ==
null) {
r =
new StringBuilder(256);
}
else {
r.append(' ');
}
r.append(p.info.name);
}
}
else {
Slog.w(TAG, "Permission " + p.info.name + " from package "
+ p.info.packageName + " ignored: base tree "
+ tree.name + " is from package "
+ tree.sourcePackage);
}
}
else {
Slog.w(TAG, "Permission " + p.info.name + " from package "
+ p.info.packageName + " ignored: original from "
+ bp.sourcePackage);
}
}
else if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
if (r ==
null) {
r =
new StringBuilder(256);
}
else {
r.append(' ');
}
r.append("DUP:");
r.append(p.info.name);
}
if (bp.perm == p) {
bp.protectionLevel = p.info.protectionLevel;
}
}
else {
Slog.w(TAG, "Permission " + p.info.name + " from package "
+ p.info.packageName + " ignored: no group "
+ p.group);
}
}
if (r !=
null) {
if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Permissions: " + r);
}
N = pkg.instrumentation.size();
r =
null;
for (i=0; i<N; i++) {
PackageParser.Instrumentation a = pkg.instrumentation.get(i);
a.info.packageName = pkg.applicationInfo.packageName;
a.info.sourceDir = pkg.applicationInfo.sourceDir;
a.info.publicSourceDir = pkg.applicationInfo.publicSourceDir;
a.info.dataDir = pkg.applicationInfo.dataDir;
a.info.nativeLibraryDir = pkg.applicationInfo.nativeLibraryDir;
mInstrumentation.put(a.getComponentName(), a);
if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
if (r ==
null) {
r =
new StringBuilder(256);
}
else {
r.append(' ');
}
r.append(a.info.name);
}
}
if (r !=
null) {
if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, " Instrumentation: " + r);
}
if (pkg.protectedBroadcasts !=
null) {
N = pkg.protectedBroadcasts.size();
for (i=0; i<N; i++) {
mProtectedBroadcasts.add(pkg.protectedBroadcasts.get(i));
}
}
pkgSetting.setTimeStamp(scanFileTime);
}
return pkg;
}
10.删除已经不存在程序对应的数据记录.mPackages保存了以上三个目录下程序文件的列表,mSettings.mPackages中保存了安装后的数据记录,因此,如果mPackages.contain()函数返回false,则意味着以上三个目录下的某个程序已经被手工删除,于是会删除对应的记录.// Prune any system packages that no longer exist.
final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<String>();
if (!mOnlyCore) {
Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();
while (psit.hasNext()) {
PackageSetting ps = psit.next();
/*
* If this is not a system app, it can't be a
* disable system app.
*/
if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {
continue;
}
/*
* If the package is scanned, it's not erased.
*/
final PackageParser.Package scannedPkg = mPackages.get(ps.name);
if (scannedPkg != null) {
/*
* If the system app is both scanned and in the
* disabled packages list, then it must have been
* added via OTA. Remove it from the currently
* scanned package so the previously user-installed
* application can be scanned.
*/
if (mSettings.isDisabledSystemPackageLPr(ps.name)) {
Slog.i(TAG, "Expecting better updatd system app for " + ps.name
+ "; removing system app");
removePackageLI(ps, true);
}
continue;
}
if (!mSettings.isDisabledSystemPackageLPr(ps.name)) {
psit.remove();
String msg = "System package " + ps.name
+ " no longer exists; wiping its data";
reportSettingsProblem(Log.WARN, msg);
removeDataDirsLI(ps.name);
} else {
final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(ps.name);
if (disabledPs.codePath == null || !disabledPs.codePath.exists()) {
possiblyDeletedUpdatedSystemApps.add(ps.name);
}
}
}
}
11.清除没有安装成功的数据记录.调用mSettings的getListOfIncompleteInstallPackages()函数获取没有安装成功的程序包列表,然后使用for循环,调用cleanupInstallFailedPackage()逐个清除这些记录.
//look for any incomplete package installations
ArrayList<PackageSetting> deletePkgsList = mSettings.getListOfIncompleteInstallPackagesLPr();
//clean up list
for(int i = 0; i < deletePkgsList.size(); i++) {
//clean up here
cleanupInstallFailedPackage(deletePkgsList.get(i));
}
//delete tmp files
deleteTempPackageFiles();
1 void cleanupInstallFailedPackage(PackageSetting ps) {
2 Slog.i(TAG, "Cleaning up incompletely installed app: " + ps.name);
3 removeDataDirsLI(ps.name);
4 if (ps.codePath != null) {
5 if (!ps.codePath.delete()) {
6 Slog.w(TAG, "Unable to remove old code file: " + ps.codePath);
7 }
8 }
9 if (ps.resourcePath != null) {
10 if (!ps.resourcePath.delete() && !ps.resourcePath.equals(ps.codePath)) {
11 Slog.w(TAG, "Unable to remove old code file: " + ps.resourcePath);
12 }
13 }
14 mSettings.removePackageLPw(ps.name);
15 }
12.调用deleteTemppackageFiles()删除/data/app目录下的以vmdl开头以及.tmp结尾的文件.
private void deleteTempPackageFiles() {
final FilenameFilter filter = new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.startsWith("vmdl") && name.endsWith(".tmp");
}
};
deleteTempPackageFilesInDirectory(mAppInstallDir, filter);
deleteTempPackageFilesInDirectory(mDrmAppPrivateInstallDir, filter);
}
private static final void deleteTempPackageFilesInDirectory(File directory,
FilenameFilter filter) {
final String[] tmpFilesList = directory.list(filter);
if (tmpFilesList == null) {
return;
}
for (int i = 0; i < tmpFilesList.length; i++) {
final File tmpFile = new File(directory, tmpFilesList[i]);
tmpFile.delete();
}
}
13.为以下两个第三方程序目录分别添加FileObserver,并调用scanDirLI()解析目录下的所有应用程序,解析结果将保存到mPackages变量中.
/data/app:普通应用程序目录.
/data/app-private:暂时没有被使用
if (!mOnlyCore) {
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
SystemClock.uptimeMillis());
mAppInstallObserver = new AppDirObserver(
mAppInstallDir.getPath(), OBSERVER_EVENTS, false);
mAppInstallObserver.startWatching();
scanDirLI(mAppInstallDir, 0, scanMode, 0);
mDrmAppInstallObserver = new AppDirObserver(
mDrmAppPrivateInstallDir.getPath(), OBSERVER_EVENTS, false);
mDrmAppInstallObserver.startWatching();
scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK,
scanMode, 0);
/**
* Remove disable package settings for any updated system
* apps that were removed via an OTA. If they're not a
* previously-updated app, remove them completely.
* Otherwise, just revoke their system-level permissions.
*/
for (String deletedAppName : possiblyDeletedUpdatedSystemApps) {
PackageParser.Package deletedPkg = mPackages.get(deletedAppName);
mSettings.removeDisabledSystemPackageLPw(deletedAppName);
String msg;
if (deletedPkg == null) {
msg = "Updated system package " + deletedAppName
+ " no longer exists; wiping its data";
removeDataDirsLI(deletedAppName);
} else {
msg = "Updated system app + " + deletedAppName
+ " no longer present; removing system privileges for "
+ deletedAppName;
deletedPkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM;
PackageSetting deletedPs = mSettings.mPackages.get(deletedAppName);
deletedPs.pkgFlags &= ~ApplicationInfo.FLAG_SYSTEM;
}
reportSettingsProblem(Log.WARN, msg);
}
} else {
mAppInstallObserver = null;
mDrmAppInstallObserver = null;
}
14.如果系统版本升级,调用upatePermissionLPw()重新为应用程序赋予权限.原因是升级可能会定义一些新的权限,为了保证已经安装过的程序保持原有权限,则需要重新赋予新的权限.
15.调用mSettings.writeLPr()将mSettings.mPackages中的数据重新写入packages.xml文件中.
// If the platform SDK has changed since the last time we booted,
// we need to re-grant app permission to catch any new ones that
// appear. This is really a hack, and means that apps can in some
// cases get permissions that the user didn't initially explicitly
// allow
it would be nice to have some better way to handle
// this situation.
final boolean regrantPermissions = mSettings.mInternalSdkPlatform
!= mSdkVersion;
if (regrantPermissions) Slog.i(TAG, "Platform changed from "
+ mSettings.mInternalSdkPlatform + " to " + mSdkVersion
+ "; regranting permissions for internal storage");
mSettings.mInternalSdkPlatform = mSdkVersion;
updatePermissionsLPw(
null,
null, UPDATE_PERMISSIONS_ALL
| (regrantPermissions
? (UPDATE_PERMISSIONS_REPLACE_PKG|UPDATE_PERMISSIONS_REPLACE_ALL)
: 0));
// can downgrade to reader
mSettings.writeLPr();
部分函数内部详情
readPermissions()
流程图

代码
void readPermissions() {
// Read permissions from
/etc/permission directory.
File libraryDir =
new File(Environment.getRootDirectory(), "etc/permissions");
if (!libraryDir.exists() || !libraryDir.isDirectory()) {
Slog.w(TAG, "No directory " + libraryDir + ", skipping");
return;
}
if (!libraryDir.canRead()) {
Slog.w(TAG, "Directory " + libraryDir + " cannot be read");
return;
}
// Iterate over the files in the directory and scan .xml files
for (File f : libraryDir.listFiles()) {
// We'll read platform.xml last
if (f.getPath().endsWith("etc/permissions/platform.xml")) {
continue;
}
if (!f.getPath().endsWith(".xml")) {
Slog.i(TAG, "Non-xml file " + f + " in " + libraryDir + " directory, ignoring");
continue;
}
if (!f.canRead()) {
Slog.w(TAG, "Permissions library file " + f + " cannot be read");
continue;
}
readPermissionsFromXml(f);
}
private void readPermissionsFromXml(File permFile) {
FileReader permReader = null;
try {
permReader = new FileReader(permFile);
} catch (FileNotFoundException e) {
Slog.w(TAG, "Couldn't find or open permissions file " + permFile);
return;
}
try {
XmlPullParser parser = Xml.newPullParser();
parser.setInput(permReader);
XmlUtils.beginDocument(parser, "permissions");
while (true) {
XmlUtils.nextElement(parser);
if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {
break;
}
String name = parser.getName();
if ("group".equals(name)) {
String gidStr = parser.getAttributeValue(null, "gid");
if (gidStr != null) {
int gid = Integer.parseInt(gidStr);
mGlobalGids = appendInt(mGlobalGids, gid);
} else {
Slog.w(TAG, "<group> without gid at "
+ parser.getPositionDescription());
}
XmlUtils.skipCurrentTag(parser);
continue;
} else if ("permission".equals(name)) {
String perm = parser.getAttributeValue(null, "name");
if (perm == null) {
Slog.w(TAG, "<permission> without name at "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
}
perm = perm.intern();
readPermission(parser, perm);
} else if ("assign-permission".equals(name)) {
String perm = parser.getAttributeValue(null, "name");
if (perm == null) {
Slog.w(TAG, "<assign-permission> without name at "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
}
String uidStr = parser.getAttributeValue(null, "uid");
if (uidStr == null) {
Slog.w(TAG, "<assign-permission> without uid at "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
}
int uid = Process.getUidForName(uidStr);
if (uid < 0) {
Slog.w(TAG, "<assign-permission> with unknown uid \""
+ uidStr + "\" at "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
}
perm = perm.intern();
HashSet<String> perms = mSystemPermissions.get(uid);
if (perms == null) {
perms = new HashSet<String>();
mSystemPermissions.put(uid, perms);
}
perms.add(perm);
XmlUtils.skipCurrentTag(parser);
} else if ("library".equals(name)) {
String lname = parser.getAttributeValue(null, "name");
String lfile = parser.getAttributeValue(null, "file");
if (lname == null) {
Slog.w(TAG, "<library> without name at "
+ parser.getPositionDescription());
} else if (lfile == null) {
Slog.w(TAG, "<library> without file at "
+ parser.getPositionDescription());
} else {
//Log.i(TAG, "Got library " + lname + " in " + lfile);
mSharedLibraries.put(lname, lfile);
}
XmlUtils.skipCurrentTag(parser);
continue;
} else if ("feature".equals(name)) {
String fname = parser.getAttributeValue(null, "name");
if (fname == null) {
Slog.w(TAG, "<feature> without name at "
+ parser.getPositionDescription());
} else {
//Log.i(TAG, "Got feature " + fname);
FeatureInfo fi = new FeatureInfo();
fi.name = fname;
mAvailableFeatures.put(fname, fi);
}
XmlUtils.skipCurrentTag(parser);
continue;
} else {
XmlUtils.skipCurrentTag(parser);
continue;
}
}
permReader.close();
} catch (XmlPullParserException e) {
Slog.w(TAG, "Got execption parsing permissions.", e);
} catch (IOException e) {
Slog.w(TAG, "Got execption parsing permissions.", e);
}
}
1.确保/ststem/etc/permissions目录存在
2.使用for()循环,读取目录下的每一个XML文件,并调用readPermissionFromXml()函数逐个解析每一个XML文件,for()循环读取中跳过platform.xml文件.一般情况下,除了platform.xml文件外,其他XML文件仅仅定义了一些系统feature,包括features.xml、handleheld_core_hardware.xml、android.hardware.wifi.xml等,平台设计人员可以在该目录下添加任意命名的XML文件描述平台所拥有的feature.这些feature的名称原理上是任意的,但是Android的Market会解析应用程序中所包含的feature,因此,feature命名建议遵守共同的约定.比如,以下为几个常见的feature名称.
备注:本文章中的代码仅为参考,版本为4.2.2,代码会与3.0之前的版本有所不同.功能没有太大变化,有部分新功能的加入,具体内容请遵照对应版本
参考资料
http://www.educity.cn/wenda/179359.html