http://blog.csdn.net/yujun411522/article/details/46226001
PackageManagerService负责管理系统的Package,包括APK的安装,卸载,信息的查询等等。它的功能非常的多,也非常的强大,所以要重点分析。
PMS(PackageManagerService)和java中其他系统服务一样,也是一个Service,它和它的Client的关系:
1 IPackageManager接口定义了server要提供的业务函数,其中子类Stub继承Binder且实现了IPackageManager接口
2 PMS继承Stub,所以可以作为Server与Binder通信
3 Stub中的一个内部类Proxy中有一个IBinder的成员变量mRemote,利用mRemote可以和Server端通信
4 client端在使用的时候是使用Context.getPackageManager函数返回的ApplicationPackageManager对象来处理,ApplicationPackageManager内部成员变量mPM指向Proxy类型的对象
可以看出java层的系统服务的模型都是一样的,换的仅仅是服务和服务的实现,关于java层系统服务,请看
java层系统服务在binder中的实现
8.1 PMS的启动
PMS的启动非常的复杂,涉及到Setting对象,属性系统,Installer系统,PackageHandler,系统权限,AndroidManifest.xml,Resouce,FileObserver已经APK的安装包的扫面等等,具体来说就是下面的历程:
其中ServerThread的启动之前已经讨论过了,这里直接看PackageManagerService的main函数 :
- public static final IPackageManager main(Context context, boolean factoryTest,
- boolean onlyCore) {
- PackageManagerService m = new PackageManagerService(context, factoryTest, onlyCore);
- ServiceManager.addService("package", m);//添加java系统服务的功能
- return m;
- }
因为添加服务到ServiceManager在之前的PowerManagerService中已经讨论过,这里不做介绍,这里只看PackageManagerService的创建,它的构造函数中做了很多的工作,这也是android启动慢的一个主要原因,分段来看这个函数的执行。
8.1.1 Setting对象的创建和初始化
....
- mSettings = new Settings();
- mSettings.addSharedUserLPw("android.uid.system",
- Process.SYSTEM_UID, ApplicationInfo.FLAG_SYSTEM);
- mSettings.addSharedUserLPw("android.uid.phone",
- MULTIPLE_APPLICATION_UIDS
- ? RADIO_UID : FIRST_APPLICATION_UID,
- ApplicationInfo.FLAG_SYSTEM);
- mSettings.addSharedUserLPw("android.uid.log",
- MULTIPLE_APPLICATION_UIDS
- ? LOG_UID : FIRST_APPLICATION_UID,
- ApplicationInfo.FLAG_SYSTEM);
- mSettings.addSharedUserLPw("android.uid.nfc",
- MULTIPLE_APPLICATION_UIDS
- ? NFC_UID : FIRST_APPLICATION_UID,
- ApplicationInfo.FLAG_SYSTEM);
这段代码做了两个工作: 1 构造Settings对象 ;2 调用addSharedUserLPw函数添加共享ID
1 构造Settings对象
Settings类在frameworks/base/services/java/com/android/server/pm中,Settings类的作用是管理android系统运行过程中的一些配置信息,它的构造函数:
- Settings() {
- File dataDir = Environment.getDataDirectory();
- File systemDir = new File(dataDir, "system");
- systemDir.mkdirs();
- FileUtils.setPermissions(systemDir.toString(),
- FileUtils.S_IRWXU|FileUtils.S_IRWXG
- |FileUtils.S_IROTH|FileUtils.S_IXOTH,
- -1, -1);
- //packages.xml 记录系统所有安装apk的信息
- mSettingsFilename = new File(systemDir, "packages.xml");
- //packages-backup.xml 是packages.xml备份文件,在安装或者卸载apk是更新packages.xml文件就会用backup备份
- mBackupSettingsFilename = new File(systemDir, "packages-backup.xml");
- //packages.list 所有已安装apk的简要信息
- mPackageListFilename = new File(systemDir, "packages.list");
- // packages-stopped.xml 强制stop的apk信息
- mStoppedPackagesFilename = new File(systemDir, "packages-stopped.xml");
- //packages-stopped.xml的备份文件
- mBackupStoppedPackagesFilename = new File(systemDir, "packages-stopped-backup.xml");
- }
它的构造函数还是很简单的,初始化几个全局变量文件,这些文件变量后面解析过程中会用到。
2 调用addSharedUserLPw函数添加共享ID
- //定义了一个HashMap存储sharedUsers信息
- final HashMap<String, SharedUserSetting> mSharedUsers =
- new HashMap<String, SharedUserSetting>();
- SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags) {
- //先在mSharedUsers中找有没有name对应的value。
- SharedUserSetting s = mSharedUsers.get(name);//
- if (s != null) {
- if (s.userId == uid) {//和传入的uid一样就直接返回
- return s;
- }
- ..如果s.userId != ui ,返回null
- return null;
- }
- //如果没有在mSharedUsers 找到该name对应的value值,则创建一个新的SharedUserSetting 对象
- s = new SharedUserSetting(name, pkgFlags);
- s.userId = uid;
- //调用addUserIdLPw
- if (addUserIdLPw(uid, s, name)) {
- mSharedUsers.put(name, s);//添加到mSharedUsers 中
- return s;//返回创建的SharedUserSetting 对象
- }
- return null;//addUserIdLPw 添加失败就返回null
- }
这里面又涉及到一个SharedUserSetting类,它的用途和AndroidManifest.xml文件中的一个标签相关android:sharedUserId
如android:sharedUserId="android.uid.system",如果在AndroidManifest.xml中这么配置的话,它有一下几个作用:1 有相同android:sharedUserId值的apk之间可以运行在同一个进程中 ;2 通过设置android:sharedUserId,该apk所在的进程的uid就是android:sharedUserId的uid,这里就是apk有system用户的权限。也即是这个进程现在运行在sharedUserId所在进程中,且有它的权限。
现在要存储某一个UID有哪些apk共享该UID需要以下三个字段:
1 String name:就是存储xml文件中android:sharedUserId的值,这里就是"android.uid.system"
2 int uid:在linux中uid是一个整数,这里对应的就是1000
3 HashSet<PackageSetting> packages:共享同一个UID的package很多 ,package设置就是通过PackageSetting类实现,它的超类是GrantedPermissions,它的pkgFlage就是用来存储package的标记。
这里涉及到Settings、GrantedPermissions、ShareUserSetting等类,他们之间的关系如下:
1.Settings类中有一个HashMap<String,SharedUserSetting>的成员变量mSharedUsers,其中key为String(比如 "android.uid.system")类型,它表示某一个共享用户name,value类型就是
SharedUserSetting类型,用来存储该共享该name的所有共享用户信息。
2.SharedUserSetting类继承GrantedPermissions,且内部维护一个HashSet<PackageSetting>的成员变量packages,表示有相同userId的package设置信息接着看addUserIdLPw
3.其中Settings中有两个成员变量ArrayList<Object>mUserIds,和SparseArray<Object> mOtherUserIds,其中mUserIds存储大于等于(10000)的SharedUserSetting对象,而mOtherUserIds存储小于10000的SharedUserSetting对象。下面会分析。
在addSharedUserLPw方法中调用了addUserIdLPw方法,这里以 mSettings.addSharedUserLPw("android.uid.system",Process.SYSTEM_UID, ApplicationInfo.FLAG_SYSTEM);为例,其中Process.SYSTEM_UID的值为1000
- private boolean addUserIdLPw(int uid, Object obj, Object name) {
- // 三个参数分别为 uid=1000
- // obj= new SharedUserSetting("android.uid.system",ApplicationInfo.FLAG_SYSTEM),
- // name = "android.uid.system"
- // PackageManagerService.FIRST_APPLICATION_UID值为 10000
- //PackageManagerService.MAX_APPLICATION_UIDS 值1000
- //这里不能uid不能超过10000+1000=11000
- //系统版本不一样这里的实现不一样
- if (uid >= PackageManagerService.FIRST_APPLICATION_UID + PackageManagerService.MAX_APPLICATION_UIDS) {
- return false;
- }
- //uid>=10000时
- if (uid >= PackageManagerService.FIRST_APPLICATION_UID) {
- int N = mUserIds.size();
- final int index = uid - PackageManagerService.FIRST_APPLICATION_UID;
- while (index >= N) {
- mUserIds.add(null);
- N++;
- }
- //可以看出mUserIds 中第i个位置,它存放的是uid=PackageManagerService.FIRST_APPLICATION_UID +i的SharedUserSetting 对象,如果没
- //有就设为null。
- if (mUserIds.get(index) != null) {//该位置已经设置过了
- return false;//返回false
- }
- mUserIds.set(index, obj);//该位置为null,可以插入,返回true
- } else {
- //uid<10000时
- if (mOtherUserIds.get(uid) != null) {同理这里也是只不过是存储小于10000的SharedUserSetting 对象
- return false;
- }
- mOtherUserIds.put(uid, obj);
- }
- return true;
- }
所以addUserIdLPw函数就是判断指定的uid能否按照一定的规则插入到mUserIds或者mOtherUserIds中。
如果这个方法返回true,按照key="mSharedUsers" value=new SharedUserSetting("android.uid.system",ApplicationInfo.FLAG_SYSTEM) put到Settings的成员变量mSharedUsers中。
到此为止Settting初始化和addSharedUserLPw分析完毕
8.1.2 获取系统默认设置
这里比较简单,读取"ro.build.type"和"debug.separate_processes"进行一些设置
8.1.3 启动HandlerThread
- mHandlerThread.start();
- mHandler = new PackageHandler(mHandlerThread.getLooper());
其中涉及到了HandlerThread、PackageHandler等类,它们之间的关系就是:
可以看出HandlerThread和PackageHandler分别代表系统通信模型中的Looper线程和Handler处理器,其中PackageHandler是PMS的内部类,它的handleMessage方法的实现实际调用了doHandleMessage方法:
- public void handleMessage(Message msg) {
- try {
- doHandleMessage(msg);//调用了doHandleMessage方法
- } finally {
- Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
- }
- }
- void doHandleMessage(Message msg) {
- switch (msg.what) {
- case INIT_COPY:
- ......
- case CHECK_PENDING_VERIFICATION:
- case PACKAGE_VERIFIED:
- }
- }
这里的作用就是启动mHandlerThread,不断从消息队列中取出来消息,交给PackageHandler处理。后面的apk安装会涉及到这部分的功能。
8.2.4 初始化UserManager
- mInstaller = new Installer();
- mUserAppDataDir = new File(dataDir, "user");
- mUserManager = new UserManager(mInstaller, mUserAppDataDir);
创建UserManager对象,看它的构造方法:
- public UserManager(Installer installer, File baseUserPath) {
- this(Environment.getDataDirectory(), baseUserPath);
- mInstaller = installer;
- }
- UserManager(File dataDir, File baseUserPath) {
- //USER_INFO_DIR 为"system/users";
- mUsersDir = new File(dataDir, USER_INFO_DIR);
- //创建/data/system/users 目录
- mUsersDir.mkdirs();
- mBaseUserPath = baseUserPath;
- FileUtils.setPermissions(mUsersDir.toString(),
- FileUtils.S_IRWXU|FileUtils.S_IRWXG
- |FileUtils.S_IROTH|FileUtils.S_IXOTH,
- -1, -1);
- // 指向/data/system/users userlist.xml文件
- mUserListFile = new File(mUsersDir, USER_LIST_FILENAME);
- readUserList();//调用readUserList函数
- }
先创建了/data/system/users 目录 ,然后调用readUserList 函数
- private void readUserList() {
- mUsers = new SparseArray<UserInfo>();
- //如果/data/system/users userlist.xml文件 不存在
- if (!mUserListFile.exists()) {
- fallbackToSingleUser();
- return;
- }
- FileInputStream fis = null;
- try {
- fis = new FileInputStream(mUserListFile);
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(fis, null);
- int type;
- while ((type = parser.next()) != XmlPullParser.START_TAG
- && type != XmlPullParser.END_DOCUMENT) {
- ;
- }
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
- //TAG_USER 为"user"
- if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_USER)) {
- //读取<user>标签中的id属性
- String id = parser.getAttributeValue(null, ATTR_ID);
- UserInfo user = readUser(Integer.parseInt(id));//根据id在读取xml构造成一个UserInfo实体
- if (user != null) {
- mUsers.put(user.id, user);//添加到mUsers
- }
- }
- }
- updateUserIds();
- } catch (IOException ioe) {
- fallbackToSingleUser();
- } catch (XmlPullParserException pe) {
- fallbackToSingleUser();
- }
- }
其中涉及到了readUser,它的作用就是读取/data/system/users/id.xml文件,将xml文件转化为UserInfo实体类
- private UserInfo readUser(int id) {
- int flags = 0;
- String name = null;
- FileInputStream fis = null;
- try {
- File userFile = new File(mUsersDir, Integer.toString(id) + ".xml");
- fis = new FileInputStream(userFile);
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(fis, null);
- int type;
- while ((type = parser.next()) != XmlPullParser.START_TAG
- && type != XmlPullParser.END_DOCUMENT) {
- ;
- }
- if (type != XmlPullParser.START_TAG) {
- Slog.e(LOG_TAG, "Unable to read user " + id);
- return null;
- }
- if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_USER)) {
- String storedId = parser.getAttributeValue(null, ATTR_ID);
- if (Integer.parseInt(storedId) != id) {
- Slog.e(LOG_TAG, "User id does not match the file name");
- return null;
- }
- String flagString = parser.getAttributeValue(null, ATTR_FLAGS);
- flags = Integer.parseInt(flagString);
- while ((type = parser.next()) != XmlPullParser.START_TAG
- && type != XmlPullParser.END_DOCUMENT) {
- }
- if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_NAME)) {
- type = parser.next();
- if (type == XmlPullParser.TEXT) {
- name = parser.getText();
- }
- }
- }
- fis.close();
- UserInfo userInfo = new UserInfo(id, name, flags);
- return userInfo;
- } catch (IOException ioe) {
- } catch (XmlPullParserException pe) {
- }
- return null;
看一下UserInfo实体类
- public class UserInfo implements Parcelable {
- public static final int FLAG_PRIMARY = 0x00000001;
- public static final int FLAG_ADMIN = 0x00000002;
- public static final int FLAG_GUEST = 0x00000004;
- public int id;//用户id
- public String name;//用户名称
- public int flags;//用户标记,是primary,admin,guest等等
- public UserInfo(int id, String name, int flags) {
- this.id = id;
- this.name = name;
- this.flags = flags;
- }
- public boolean isPrimary() {
- return (flags & FLAG_PRIMARY) == FLAG_PRIMARY;
- }
- public boolean isAdmin() {
- return (flags & FLAG_ADMIN) == FLAG_ADMIN;
- }
- public boolean isGuest() {
- return (flags & FLAG_GUEST) == FLAG_GUEST;
- }
- public int describeContents() {
- return 0;
- }
- public void writeToParcel(Parcel dest, int parcelableFlags) {
- dest.writeInt(id);
- dest.writeString(name);
- dest.writeInt(flags);
- }
- public static final Parcelable.Creator<UserInfo> CREATOR
- = new Parcelable.Creator<UserInfo>() {
- public UserInfo createFromParcel(Parcel source) {
- return new UserInfo(source);
- }
- public UserInfo[] newArray(int size) {
- return new UserInfo[size];
- }
- };
- private UserInfo(Parcel source) {
- id = source.readInt();
- name = source.readString();
- flags = source.readInt();
- }
- }
实现了Parcelable 接口保存user的一些信息。
如果readUser(id)的结果不为null就将该结果加入到mUsers中。
8.2.5 解析permission
readPermissions();函数调用的实现
- void readPermissions() {
- // 指向etc/permissions 文件
- File libraryDir = new File(Environment.getRootDirectory(), "etc/permissions");
- ..
- // Iterate over the files in the directory and scan .xml files
- for (File f : libraryDir.listFiles()) {
- //不处理etc/permissions/platform.xml
- if (f.getPath().endsWith("etc/permissions/platform.xml")) {
- continue;
- }
- //处理.xml文件
- if (!f.getPath().endsWith(".xml")) {
- continue;
- }
- //处理刻度的
- if (!f.canRead()) {
- continue;
- }
- readPermissionsFromXml(f);
- }
- // Read permissions from .../etc/permissions/platform.xml last so it will take precedence
- 最后解析etc/permissions/platform.xm 文件
- final File permFile = new File(Environment.getRootDirectory(),"etc/permissions/platform.xml");
- readPermissionsFromXml(permFile);
- }
可见就是解析各种/etc/permissions目录中的xml文件,最后才解析etc/permissions/platform.xml文件。不过我们先看etc/permissions/platform.xml文件,这是我手机上的platform.xml文件(一部分)
- <permissions>
- <!--为指定group 的gid分配相应的权限-->
- <!--为指定gid为net_bt_admin分配 BLUETOOTH_ADMIN权限-->
- <permission name="android.permission.BLUETOOTH_ADMIN" >
- <group gid="net_bt_admin" />
- </permission>
- <permission name="android.permission.BLUETOOTH" >
- <group gid="net_bt" />
- </permission>
- <permission name="android.permission.BLUETOOTH_STACK" >
- <group gid="net_bt_stack" />
- </permission>
- <!--为指定的uid分配相应的权限-->
- <!--为指定uid为 shell 分配WRITE_EXTERNAL_STORAGE权限-->
- <assign-permission name="android.permission.WRITE_EXTERNAL_STORAGE" uid="shell" />
- <assign-permission name="android.permission.SEND_SMS" uid="shell" />
- <assign-permission name="android.permission.CALL_PHONE" uid="shell" />
- <assign-permission name="android.permission.READ_CONTACTS" uid="shell" />
- <assign-permission name="android.permission.WRITE_CONTACTS" uid="shell" />
- <!--连接库-->
- <library name="android.test.runner" file="/system/framework/android.test.runner.jar" />
- <library name="javax.obex" file="/system/framework/javax.obex.jar"/>
- </permissions>
解析platform.xml通过函数readPermissionsFromXml来实现,该函数的主要作用就是将permission 、group 、assign-permission,library 标签读出来并存储在相应的数据结构中。
上面的文件是platform.xml文件,还有一类文件是feature配置文件,包括硬件和软件 feature配置,先看硬件feature,这里android.hardware.wifi.xml为例:
- <permissions>
- <feature name="android.hardware.wifi" />
- </permissions>
说明当前设备可以支持wifi
再看软件 feature配置android.software.live_wallpaper.xml
- permissions>
- <feature name="android.software.live_wallpaper" />
- </permissions>
8.2.6 解析package文件
mSettings.readLPw()方法,它的作用就是解析之前的packagexxx.xml文件,包括package.xml,package-backup.xml,package-stop.xml,package-stopback.xml这些文件,主要历程如下:
主要历程在第二部分解析packages.xml文件,保存到相应的数据结构之中。
8.2.7 dexopt优化
- int scanMode = SCAN_MONITOR | SCAN_NO_PATHS | SCAN_DEFER_DEX;
- if (mNoDexOpt) {
- scanMode |= SCAN_NO_DEX;//不需要DEX优化
- }
- final HashSet<String> libFiles = new HashSet<String>();
- mFrameworkDir = new File(Environment.getRootDirectory(), "framework");
- mDalvikCacheDir = new File(dataDir, "dalvik-cache");
- boolean didDexOpt = false;
- String bootClassPath = System.getProperty("java.boot.class.path");
- if (bootClassPath != null) {
- String[] paths = splitString(bootClassPath, ':');
- for (int i=0; i<paths.length; i++) {
- try {
- //判断是否需要dex优化,如果需要则加入到libFiles中
- if (dalvik.system.DexFile.isDexOptNeeded(paths[i])) {
- libFiles.add(paths[i]);
- mInstaller.dexopt(paths[i], Process.SYSTEM_UID, true);
- didDexOpt = true;
- }
- } catch (FileNotFoundException e) {
- } catch (IOException e) {
- }
- }
- } else {
- }
- //在platform.xml文件中存储library 标签的变量
- if (mSharedLibraries.size() > 0) {
- Iterator<String> libs = mSharedLibraries.values().iterator();
- while (libs.hasNext()) {
- String lib = libs.next();
- try {
- //判断是否需要dex优化
- if (dalvik.system.DexFile.isDexOptNeeded(lib)) {
- libFiles.add(lib);
- mInstaller.dexopt(lib, Process.SYSTEM_UID, true);
- didDexOpt = true;
- }
- } catch (FileNotFoundException e) {
- } catch (IOException e) {
- }
- }
- }
- //对于framework-res.apk直接添加到libFiles中
- libFiles.add(mFrameworkDir.getPath() + "/framework-res.apk");
- //mFrameworkDir代表 /system/frameworks目录,优化里面的apk和jar文件
- String[] frameworkFiles = mFrameworkDir.list();
- if (frameworkFiles != null) {
- for (int i=0; i<frameworkFiles.length; i++) {
- .....
- }
- }
- if (didDexOpt) {//如果didDexOpt为true,则说明优化成功
- //mDalvikCacheDir代表 /data/dalvik-cache
- String[] files = mDalvikCacheDir.list();
- if (files != null) {
- for (int i=0; i<files.length; i++) {
- String fn = files[i];
- //删除data@app@ 或者data@app-private@的文件
- if (fn.startsWith("data@app@")|| fn.startsWith("data@app-private@")) {
- (new File(mDalvikCacheDir, fn)).delete();//删除缓存数据
- }
- }
- }
- }
主要是对几个路径的apk和jar文件判断看是否需要dexopt优化,有java.boot.class.path、mSharedLibraries、/system/frameworks目录这几个目录下面机型操作,若有任意一个文件夹中有任意一个文件需要优化,删除 /data/dalvik-cache 目录下以data@app@或者data@app-private@文件
8.2.8 启动FileObserver监控APK文件目录
- <span style="white-space:pre"> </span> //监控 system/framework目录
- 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);
- //监控 system/app目录
- 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);
- //监控 /vendor/app目录
- 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);
- mInstaller.moveFiles();
- ...
- mAppInstallDir = new File(dataDir, "app");
- //look for any incomplete package installations
- ArrayList<PackageSetting> deletePkgsList = mSettings.getListOfIncompleteInstallPackagesLPr();
- //clean up list
- for(int i = 0; i < deletePkgsList.size(); i++) {
- cleanupInstallFailedPackage(deletePkgsList.get(i));
- }
- deleteTempPackageFiles();
- if (!mOnlyCore) {
- 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);
- } else {
- mAppInstallObserver = null;
- mDrmAppInstallObserver = null;
- }
利用FileObserver来监控几个文件夹下面的apk文件,可以监听文件或者目录的添加或者删除事件并作出响应。
8.2.9 调用scanDirLI方法扫描并安装apk文件
上面的过程中出现了一个函数scanDirLI函数,它的实现比较复杂后面会进行介绍
8.2.10更新package文件
mSettings.writeLPr();方法来将mSetting中存储的package信息保存到响应的文件之中
8.3 Installer和Installd
Installer是java层的接口,Installd是init启动的守护进行,两者是client和server的关系,installer的api会转化为installd的命令,他们之间的关系:
8.3.1 Installer
这是java层提供的Installer类,提供apk安装和卸载过程中的操作
安装的过程调用install方法:
安装的过程调用install方法:
- 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());
- }
先将name,uid,gid按照一定的规则生成一个字符串形式:"install name uid gid" 然后执行execute方法:
- private int execute(String cmd) {
- String res = transaction(cmd);//调用transaction 方法
- try {
- return Integer.parseInt(res);
- } catch (NumberFormatException ex) {
- return -1;
- }
- }
调用了transaction方法:
- private synchronized String transaction(String cmd) {
- if (!connect()) {//连接installd服务
- Slog.e(TAG, "connection failed");
- return "-1";
- }
- if (!writeCommand(cmd)) {//向installd发送命令
- 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";
- }
- }
三个过程: connect 进行连接;writeCommand 发送请求 ; readReply读取响应
可以看出来client端的工作仍然是由installd来完成,client将请求发送给installd来完成,真正的工作是installd
8.3.2 Installd服务
Intalld服务是init阶段启动的服务进行,在clieng发送一个命令之后会将该命令按照下面的对应方式进行映射:
- struct cmdinfo {
- const char *name; //命令名称
- unsigned numargs; //参数个数
- int (*func)(char **arg, char reply[REPLY_MAX]); //该命令对应的函数
- };
- struct cmdinfo cmds[] = {
- { "ping", 0, do_ping },
- { "install", 3, do_install },
- { "dexopt", 3, do_dexopt },
- { "movedex", 2, do_move_dex },
- { "rmdex", 1, do_rm_dex },
- { "remove", 2, do_remove },
- { "rename", 2, do_rename },
- { "freecache", 1, do_free_cache },
- { "rmcache", 1, do_rm_cache },
- { "protect", 2, do_protect },
- { "getsize", 4, do_get_size },
- { "rmuserdata", 2, do_rm_user_data },
- { "movefiles", 0, do_movefiles },
- { "linklib", 2, do_linklib },
- { "unlinklib", 1, do_unlinklib },
- { "mkuserdata", 3, do_mk_user_data },
- { "rmuser", 1, do_rm_user },
- };
这里的命令是install,对应的函数是do_install,它有调用Commands.c中的install方法:
- int install(const char *pkgname, uid_t uid, gid_t gid)
- {
- char pkgdir[PKG_PATH_MAX];
- char libdir[PKG_PATH_MAX];
- if ((uid < AID_SYSTEM) || (gid < AID_SYSTEM)) {
- LOGE("invalid uid/gid: %d %d\n", uid, gid);
- return -1;
- }
- if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, 0)) {
- LOGE("cannot create package path\n");
- return -1;
- }
- if (create_pkg_path(libdir, pkgname, PKG_LIB_POSTFIX, 0)) {
- LOGE("cannot create package lib path\n");
- return -1;
- }
- if (mkdir(pkgdir, 0751) < 0) {
- LOGE("cannot create dir '%s': %s\n", pkgdir, strerror(errno));
- return -errno;
- }
- if (chmod(pkgdir, 0751) < 0) {
- LOGE("cannot chmod dir '%s': %s\n", pkgdir, strerror(errno));
- unlink(pkgdir);
- return -errno;
- }
- if (chown(pkgdir, uid, gid) < 0) {
- LOGE("cannot chown dir '%s': %s\n", pkgdir, strerror(errno));
- unlink(pkgdir);
- return -errno;
- }
- if (mkdir(libdir, 0755) < 0) {
- LOGE("cannot create dir '%s': %s\n", libdir, strerror(errno));
- unlink(pkgdir);
- return -errno;
- }
- if (chmod(libdir, 0755) < 0) {
- LOGE("cannot chmod dir '%s': %s\n", libdir, strerror(errno));
- unlink(libdir);
- unlink(pkgdir);
- return -errno;
- }
- if (chown(libdir, AID_SYSTEM, AID_SYSTEM) < 0) {
- LOGE("cannot chown dir '%s': %s\n", libdir, strerror(errno));
- unlink(libdir);
- unlink(pkgdir);
- return -errno;
- }
- return 0;
- }
创建各种数据目录并对目录权限进行处理,这里只是创建了目录数据而已,APK文件的安装还是很复杂的,后面会分析。