深入理解PackageManagerService


http://blog.csdn.net/yujun411522/article/details/46226001

本文出自:【yujun411522的博客】

PackageManagerService负责管理系统的Package,包括APK的安装,卸载,信息的查询等等。它的功能非常的多,也非常的强大,所以要重点分析。 PMS(PackageManagerService)和java中其他系统服务一样,也是一个Service,它和它的Client的关系:
《深入理解PackageManagerService》

《深入理解PackageManagerService》 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的安装包的扫面等等,具体来说就是下面的历程:
《深入理解PackageManagerService》

《深入理解PackageManagerService》 其中ServerThread的启动之前已经讨论过了,这里直接看PackageManagerService的main函数 :   
[java] 
view plain
 copy

  1. public static final IPackageManager main(Context context, boolean factoryTest,  
  2.            boolean onlyCore) {          
  3.        PackageManagerService m = new PackageManagerService(context, factoryTest, onlyCore);  
  4.        ServiceManager.addService(“package”, m);//添加java系统服务的功能  
  5.        return m;  
  6.    }  

因为添加服务到ServiceManager在之前的PowerManagerService中已经讨论过,这里不做介绍,这里只看PackageManagerService的创建,它的构造函数中做了很多的工作,这也是android启动慢的一个主要原因,分段来看这个函数的执行。

8.1.1 Setting对象的创建和初始化 ….
[html] 
view plain
 copy

  1. mSettings = new Settings();  
  2.         mSettings.addSharedUserLPw(“android.uid.system”,  
  3.                 Process.SYSTEM_UID, ApplicationInfo.FLAG_SYSTEM);  
  4.         mSettings.addSharedUserLPw(“android.uid.phone”,  
  5.                 MULTIPLE_APPLICATION_UIDS  
  6.                         ? RADIO_UID : FIRST_APPLICATION_UID,  
  7.                 ApplicationInfo.FLAG_SYSTEM);  
  8.         mSettings.addSharedUserLPw(“android.uid.log”,  
  9.                 MULTIPLE_APPLICATION_UIDS  
  10.                         ? LOG_UID : FIRST_APPLICATION_UID,  
  11.                 ApplicationInfo.FLAG_SYSTEM);  
  12.         mSettings.addSharedUserLPw(“android.uid.nfc”,  
  13.                 MULTIPLE_APPLICATION_UIDS  
  14.                         ? NFC_UID : FIRST_APPLICATION_UID,  
  15.                 ApplicationInfo.FLAG_SYSTEM);  

这段代码做了两个工作: 1 构造Settings对象 ;2 调用addSharedUserLPw函数添加共享ID
1 构造Settings对象 Settings类在frameworks/base/services/java/com/android/server/pm中,Settings类的作用是管理android系统运行过程中的一些配置信息,它的构造函数: 
[html] 
view plain
 copy

  1. Settings() {  
  2.         File dataDir = Environment.getDataDirectory();  
  3.         File systemDir = new File(dataDir, “system”);  
  4.         systemDir.mkdirs();  
  5.         FileUtils.setPermissions(systemDir.toString(),  
  6.                 FileUtils.S_IRWXU|FileUtils.S_IRWXG  
  7.                 |FileUtils.S_IROTH|FileUtils.S_IXOTH,  
  8.                 -1, -1);  
  9.         //packages.xml 记录系统所有安装apk的信息  
  10.         mSettingsFilename = new File(systemDir, “packages.xml”);  
  11.     
  12.          //packages-backup.xml 是packages.xml备份文件,在安装或者卸载apk是更新packages.xml文件就会用backup备份  
  13.         mBackupSettingsFilename = new File(systemDir, “packages-backup.xml”);  
  14.   
  15.         //packages.list 所有已安装apk的简要信息  
  16.         mPackageListFilename = new File(systemDir, “packages.list”);  
  17.   
  18.         //  packages-stopped.xml  强制stop的apk信息  
  19.         mStoppedPackagesFilename = new File(systemDir, “packages-stopped.xml”);  
  20.   
  21.          //packages-stopped.xml的备份文件   
  22.         mBackupStoppedPackagesFilename = new File(systemDir, “packages-stopped-backup.xml”);  
  23.     }  

它的构造函数还是很简单的,初始化几个全局变量文件,这些文件变量后面解析过程中会用到。

2 调用addSharedUserLPw函数添加共享ID
[html] 
view plain
 copy

  1. //定义了一个HashMap存储sharedUsers信息  
  2. final HashMap<String, SharedUserSetting> mSharedUsers =  
  3.            new HashMap<String, SharedUserSetting>();  
  4.   
  5.    SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags) {  
  6.        //先在mSharedUsers中找有没有name对应的value。  
  7.        SharedUserSetting s = mSharedUsers.get(name);//  
  8.        if (s != null) {  
  9.            if (s.userId == uid) {//和传入的uid一样就直接返回  
  10.                return s;  
  11.            }  
  12.            ..如果s.userId != ui ,返回null  
  13.            return null;  
  14.        }  
  15.         
  16.        //如果没有在mSharedUsers 找到该name对应的value值,则创建一个新的SharedUserSetting 对象  
  17.        s = new SharedUserSetting(name, pkgFlags);  
  18.        s.userId = uid;  
  19.        //调用addUserIdLPw   
  20.        if (addUserIdLPw(uid, s, name)) {  
  21.            mSharedUsers.put(name, s);//添加到mSharedUsers  中  
  22.            return s;//返回创建的SharedUserSetting 对象   
  23.        }  
  24.        return null;//addUserIdLPw 添加失败就返回null  
  25.    }  

这里面又涉及到一个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等类,他们之间的关系如下:
《深入理解PackageManagerService》

《深入理解PackageManagerService》 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    
[html] 
view plain
 copy

  1. private boolean addUserIdLPw(int uid, Object obj, Object name) {  
  2.        // 三个参数分别为 uid=1000  
  3.        // objnew SharedUserSetting(“android.uid.system”,ApplicationInfo.FLAG_SYSTEM),   
  4.        // name = “android.uid.system”  
  5.   
  6.       //  PackageManagerService.FIRST_APPLICATION_UID值为 10000  
  7.        //PackageManagerService.MAX_APPLICATION_UIDS 值1000  
  8.        //这里不能uid不能超过10000+1000=11000  
  9.        //系统版本不一样这里的实现不一样  
  10.        if (uid >= PackageManagerService.FIRST_APPLICATION_UID + PackageManagerService.MAX_APPLICATION_UIDS) {  
  11.            return false;  
  12.        }  
  13.          
  14.        //uid>=10000时  
  15.        if (uid >= PackageManagerService.FIRST_APPLICATION_UID) {  
  16.            int N = mUserIds.size();  
  17.              
  18.            final int index = uid – PackageManagerService.FIRST_APPLICATION_UID;  
  19.            while (index >= N) {  
  20.                mUserIds.add(null);  
  21.                N++;  
  22.            }  
  23.            //可以看出mUserIds 中第i个位置,它存放的是uid=PackageManagerService.FIRST_APPLICATION_UID +i的SharedUserSetting 对象,如果没  
  24.            //有就设为null。  
  25.            if (mUserIds.get(index) != null) {//该位置已经设置过了  
  26.               return false;//返回false  
  27.            }  
  28.            mUserIds.set(index, obj);//该位置为null,可以插入,返回true  
  29.        } else {  
  30.            //uid<10000时   
  31.            if (mOtherUserIds.get(uid) != null) {同理这里也是只不过是存储小于10000的SharedUserSetting 对象            
  32.                return false;  
  33.            }  
  34.            mOtherUserIds.put(uid, obj);  
  35.        }  
  36.        return true;  
  37.    }  

所以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
[html] 
view plain
 copy

  1. mHandlerThread.start();  
  2. mHandler = new PackageHandler(mHandlerThread.getLooper());  

其中涉及到了HandlerThread、PackageHandler等类,它们之间的关系就是:
《深入理解PackageManagerService》

《深入理解PackageManagerService》 可以看出HandlerThread和PackageHandler分别代表系统通信模型中的Looper线程和Handler处理器,其中PackageHandler是PMS的内部类,它的handleMessage方法的实现实际调用了doHandleMessage方法: 
[html] 
view plain
 copy

  1. public void handleMessage(Message msg) {  
  2.             try {  
  3.                 doHandleMessage(msg);//调用了doHandleMessage方法  
  4.             } finally {  
  5.                 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);  
  6.             }  
  7.         }  
  8.  void doHandleMessage(Message msg) {  
  9.             switch (msg.what) {  
  10.           case INIT_COPY:  
  11.           ……  
  12.           case CHECK_PENDING_VERIFICATION:  
  13.            case PACKAGE_VERIFIED:  
  14.   
  15. }  
  16. }  

这里的作用就是启动mHandlerThread,不断从消息队列中取出来消息,交给PackageHandler处理。后面的apk安装会涉及到这部分的功能。

8.2.4 初始化UserManager
[html] 
view plain
 copy

  1. mInstaller = new Installer();  
  2. mUserAppDataDir = new File(dataDir, “user”);  
  3. mUserManager = new UserManager(mInstaller, mUserAppDataDir);  

创建UserManager对象,看它的构造方法: 
[html] 
view plain
 copy

  1. public UserManager(Installer installer, File baseUserPath) {  
  2.        this(Environment.getDataDirectory(), baseUserPath);  
  3.        mInstaller = installer;  
  4.    }  
  5.   
  6. UserManager(File dataDir, File baseUserPath) {  
  7.        //USER_INFO_DIR 为”system/users”;  
  8.        mUsersDir = new File(dataDir, USER_INFO_DIR);  
  9.        //创建/data/system/users 目录  
  10.        mUsersDir.mkdirs();  
  11.        mBaseUserPath = baseUserPath;  
  12.        FileUtils.setPermissions(mUsersDir.toString(),  
  13.                FileUtils.S_IRWXU|FileUtils.S_IRWXG  
  14.                |FileUtils.S_IROTH|FileUtils.S_IXOTH,  
  15.                -1, -1);  
  16.        // 指向/data/system/users userlist.xml文件  
  17.        mUserListFile = new File(mUsersDir, USER_LIST_FILENAME);  
  18.        readUserList();//调用readUserList函数  
  19.    }  

先创建了/data/system/users 目录 ,然后调用readUserList 函数

   
[html] 
view plain
 copy

  1. private void readUserList() {  
  2.        mUsers = new SparseArray<UserInfo>();       
  3.          //如果/data/system/users userlist.xml文件 不存在  
  4.        if (!mUserListFile.exists()) {  
  5.            fallbackToSingleUser();  
  6.            return;  
  7.        }  
  8.        FileInputStream fis = null;  
  9.        try {  
  10.            fis = new FileInputStream(mUserListFile);  
  11.            XmlPullParser parser = Xml.newPullParser();  
  12.            parser.setInput(fis, null);  
  13.            int type;  
  14.            while ((type = parser.next()) != XmlPullParser.START_TAG  
  15.                    && type != XmlPullParser.END_DOCUMENT) {  
  16.                ;  
  17.            }  
  18.   
  19.              while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {  
  20.               //TAG_USER 为”user”  
  21.                if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_USER)) {  
  22.                    //读取<user>标签中的id属性  
  23.                    String id = parser.getAttributeValue(null, ATTR_ID);  
  24.                    UserInfo user = readUser(Integer.parseInt(id));//根据id在读取xml构造成一个UserInfo实体  
  25.                    if (user != null) {  
  26.                        mUsers.put(user.id, user);//添加到mUsers   
  27.                    }  
  28.                }  
  29.            }  
  30.            updateUserIds();  
  31.        } catch (IOException ioe) {  
  32.            fallbackToSingleUser();  
  33.        } catch (XmlPullParserException pe) {  
  34.            fallbackToSingleUser();  
  35.        }  
  36.    }  

其中涉及到了readUser,它的作用就是读取/data/system/users/id.xml文件,将xml文件转化为UserInfo实体类
[html] 
view plain
 copy

  1. private UserInfo readUser(int id) {  
  2.        int flags = 0;  
  3.        String name = null;  
  4.   
  5.        FileInputStream fis = null;  
  6.        try {  
  7.            File userFile = new File(mUsersDir, Integer.toString(id) + “.xml”);  
  8.            fis = new FileInputStream(userFile);  
  9.            XmlPullParser parser = Xml.newPullParser();  
  10.            parser.setInput(fis, null);  
  11.            int type;  
  12.            while ((type = parser.next()) != XmlPullParser.START_TAG  
  13.                    && type != XmlPullParser.END_DOCUMENT) {  
  14.                ;  
  15.            }  
  16.   
  17.            if (type != XmlPullParser.START_TAG) {  
  18.                Slog.e(LOG_TAG, “Unable to read user ” + id);  
  19.                return null;  
  20.            }  
  21.   
  22.            if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_USER)) {  
  23.                String storedId = parser.getAttributeValue(null, ATTR_ID);  
  24.                if (Integer.parseInt(storedId) != id) {  
  25.                    Slog.e(LOG_TAG, “User id does not match the file name”);  
  26.                    return null;  
  27.                }  
  28.                String flagString = parser.getAttributeValue(null, ATTR_FLAGS);  
  29.                flags = Integer.parseInt(flagString);  
  30.   
  31.                while ((type = parser.next()) != XmlPullParser.START_TAG  
  32.                        && type != XmlPullParser.END_DOCUMENT) {  
  33.                }  
  34.                if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_NAME)) {  
  35.                    type = parser.next();  
  36.                    if (type == XmlPullParser.TEXT) {  
  37.                        name = parser.getText();  
  38.                    }  
  39.                }  
  40.            }  
  41.            fis.close();  
  42.   
  43.            UserInfo userInfo = new UserInfo(id, name, flags);  
  44.            return userInfo;  
  45.   
  46.        } catch (IOException ioe) {  
  47.        } catch (XmlPullParserException pe) {  
  48.        }  
  49.        return null;  

看一下UserInfo实体类    
[html] 
view plain
 copy

  1. public class UserInfo implements Parcelable {  
  2.      public static final int FLAG_PRIMARY = 0x00000001;  
  3.   
  4.    public static final int FLAG_ADMIN   = 0x00000002;  
  5.  public static final int FLAG_GUEST   = 0x00000004;  
  6.   
  7.    public int id;//用户id  
  8.     public String name;//用户名称  
  9.     public int flags;//用户标记,是primary,admin,guest等等  
  10.   
  11.     public UserInfo(int id, String name, int flags) {  
  12.         this.id = id;  
  13.         this.name = name;  
  14.         this.flags = flags;  
  15.     }  
  16.   
  17.  public boolean isPrimary() {  
  18.         return (flags & FLAG_PRIMARY) == FLAG_PRIMARY;  
  19.     }  
  20.   
  21.     public boolean isAdmin() {  
  22.         return (flags & FLAG_ADMIN) == FLAG_ADMIN;  
  23.     }  
  24.   
  25.     public boolean isGuest() {  
  26.         return (flags & FLAG_GUEST) == FLAG_GUEST;  
  27.     }  
  28.  public int describeContents() {  
  29.         return 0;  
  30.     }  
  31.   
  32.     public void writeToParcel(Parcel dest, int parcelableFlags) {  
  33.         dest.writeInt(id);  
  34.         dest.writeString(name);  
  35.         dest.writeInt(flags);  
  36.     }  
  37.   
  38.     public static final Parcelable.Creator<UserInfo> CREATOR  
  39.             = new Parcelable.Creator<UserInfo>() {  
  40.         public UserInfo createFromParcel(Parcel source) {  
  41.             return new UserInfo(source);  
  42.         }  
  43.         public UserInfo[] newArray(int size) {  
  44.             return new UserInfo[size];  
  45.         }  
  46.     };  
  47.   
  48.     private UserInfo(Parcel source) {  
  49.         id = source.readInt();  
  50.         name = source.readString();  
  51.         flags = source.readInt();  
  52.     }  
  53. }  

实现了Parcelable 接口保存user的一些信息。 如果readUser(id)的结果不为null就将该结果加入到mUsers中。

8.2.5 解析permission readPermissions();函数调用的实现
   
[html] 
view plain
 copy

  1. void readPermissions() {  
  2.         //  指向etc/permissions 文件  
  3.         File libraryDir = new File(Environment.getRootDirectory(), “etc/permissions”);  
  4.          ..  
  5.   
  6.         // Iterate over the files in the directory and scan .xml files  
  7.         for (File f : libraryDir.listFiles()) {  
  8.             //不处理etc/permissions/platform.xml   
  9.             if (f.getPath().endsWith(“etc/permissions/platform.xml”)) {  
  10.                 continue;  
  11.             }  
  12.             //处理.xml文件  
  13.             if (!f.getPath().endsWith(“.xml”)) {  
  14.                  
  15.                 continue;  
  16.             }  
  17.             //处理刻度的  
  18.             if (!f.canRead()) {  
  19.                  
  20.                 continue;  
  21.             }  
  22.   
  23.             readPermissionsFromXml(f);  
  24.         }  
  25.   
  26.         // Read permissions from …/etc/permissions/platform.xml last so it will take precedence  
  27.           最后解析etc/permissions/platform.xm 文件  
  28.         final File permFile = new File(Environment.getRootDirectory(),”etc/permissions/platform.xml”);  
  29.         readPermissionsFromXml(permFile);  
  30.     }  

可见就是解析各种/etc/permissions目录中的xml文件,最后才解析etc/permissions/platform.xml文件。不过我们先看etc/permissions/platform.xml文件,这是我手机上的platform.xml文件(一部分)
[html] 
view plain
 copy

  1. <permissions>  
  2. <!–为指定group 的gid分配相应的权限–>   
  3. <!–为指定gid为net_bt_admin分配 BLUETOOTH_ADMIN权限–>   
  4.  <permission name=“android.permission.BLUETOOTH_ADMIN” >  
  5.         <group gid=“net_bt_admin” />  
  6.     </permission>  
  7.   
  8.     <permission name=“android.permission.BLUETOOTH” >  
  9.         <group gid=“net_bt” />  
  10.     </permission>  
  11.   
  12.     <permission name=“android.permission.BLUETOOTH_STACK” >  
  13.         <group gid=“net_bt_stack” />  
  14.     </permission>  
  15.   
  16. <!–为指定的uid分配相应的权限–>   
  17. <!–为指定uid为  shell 分配WRITE_EXTERNAL_STORAGE权限–>    
  18.    <assign-permission name=“android.permission.WRITE_EXTERNAL_STORAGE” uid=“shell” />  
  19.     <assign-permission name=“android.permission.SEND_SMS” uid=“shell” />  
  20.     <assign-permission name=“android.permission.CALL_PHONE” uid=“shell” />  
  21.     <assign-permission name=“android.permission.READ_CONTACTS” uid=“shell” />  
  22.     <assign-permission name=“android.permission.WRITE_CONTACTS” uid=“shell” />  
  23.   
  24. <!–连接库–>  
  25.  <library name=“android.test.runner” file=“/system/framework/android.test.runner.jar” />  
  26.     <library name=“javax.obex” file=“/system/framework/javax.obex.jar”/>  
  27.   
  28. </permissions>  

解析platform.xml通过函数readPermissionsFromXml来实现,该函数的主要作用就是将permission 、group 、assign-permission,library 标签读出来并存储在相应的数据结构中。

上面的文件是platform.xml文件,还有一类文件是feature配置文件,包括硬件和软件 feature配置,先看硬件feature,这里android.hardware.wifi.xml为例:
[html] 
view plain
 copy

  1. <permissions>  
  2.     <feature name=“android.hardware.wifi” />  
  3. </permissions>  

说明当前设备可以支持wifi 再看软件 feature配置android.software.live_wallpaper.xml
[html] 
view plain
 copy

  1. permissions>  
  2.     <feature name=“android.software.live_wallpaper” />  
  3. </permissions>  

8.2.6 解析package文件 mSettings.readLPw()方法,它的作用就是解析之前的packagexxx.xml文件,包括package.xml,package-backup.xml,package-stop.xml,package-stopback.xml这些文件,主要历程如下:
《深入理解PackageManagerService》

主要历程在第二部分解析packages.xml文件,保存到相应的数据结构之中。

8.2.7 dexopt优化
[html] 
view plain
 copy

  1.  int scanMode = SCAN_MONITOR | SCAN_NO_PATHS | SCAN_DEFER_DEX;  
  2.             if (mNoDexOpt) {                  
  3.                 scanMode |= SCAN_NO_DEX;//不需要DEX优化  
  4.             }  
  5.             final HashSet<String> libFiles = new HashSet<String>();  
  6.             mFrameworkDir = new File(Environment.getRootDirectory(), “framework”);  
  7.             mDalvikCacheDir = new File(dataDir, “dalvik-cache”);  
  8.             boolean didDexOpt = false;  
  9.               
  10.             String bootClassPath = System.getProperty(“java.boot.class.path”);  
  11.             if (bootClassPath != null) {  
  12.                 String[] paths = splitString(bootClassPath, ‘:’);  
  13.                 for (int i=0; i<paths.length; i++) {  
  14.                     try {  
  15.                          //判断是否需要dex优化,如果需要则加入到libFiles中  
  16.                         if (dalvik.system.DexFile.isDexOptNeeded(paths[i])) {  
  17.                             libFiles.add(paths[i]);  
  18.                             mInstaller.dexopt(paths[i], Process.SYSTEM_UID, true);  
  19.                             didDexOpt = true;  
  20.                         }  
  21.                     } catch (FileNotFoundException e) {  
  22.                           
  23.                     } catch (IOException e) {  
  24.                         
  25.                     }  
  26.                 }  
  27.             } else {  
  28.                 
  29.             }  
  30.   
  31.              //在platform.xml文件中存储library  标签的变量  
  32.             if (mSharedLibraries.size() > 0) {  
  33.                 Iterator<String> libs = mSharedLibraries.values().iterator();  
  34.                 while (libs.hasNext()) {  
  35.                     String lib = libs.next();  
  36.                     try {  
  37.                          //判断是否需要dex优化  
  38.                         if (dalvik.system.DexFile.isDexOptNeeded(lib)) {  
  39.                             libFiles.add(lib);  
  40.                             mInstaller.dexopt(lib, Process.SYSTEM_UID, true);  
  41.                             didDexOpt = true;  
  42.                         }  
  43.                     } catch (FileNotFoundException e) {  
  44.                            
  45.                     } catch (IOException e) {  
  46.                            
  47.                     }  
  48.                 }  
  49.             }  
  50.   
  51.   //对于framework-res.apk直接添加到libFiles中  
  52.  libFiles.add(mFrameworkDir.getPath() + “/framework-res.apk”);  
  53. //mFrameworkDir代表 /system/frameworks目录,优化里面的apk和jar文件  
  54. String[] frameworkFiles = mFrameworkDir.list();  
  55.             if (frameworkFiles != null) {  
  56.                 for (int i=0; i<frameworkFiles.length; i++) {  
  57.                  …..    
  58.                 }  
  59.             }  
  60.   
  61.             if (didDexOpt) {//如果didDexOpt为true,则说明优化成功        
  62.                //mDalvikCacheDir代表      /data/dalvik-cache    
  63.                 String[] files = mDalvikCacheDir.list();  
  64.                 if (files != null) {  
  65.                     for (int i=0; i<files.length; i++) {  
  66.                         String fn = files[i];  
  67.                          //删除data@app@ 或者data@app-private@的文件  
  68.                           if (fn.startsWith(“data@app@”)|| fn.startsWith(“data@app-private@”)) {                             
  69.                             (new File(mDalvikCacheDir, fn)).delete();//删除缓存数据  
  70.                         }  
  71.                     }  
  72.                 }  
  73.             }  

主要是对几个路径的apk和jar文件判断看是否需要dexopt优化,有java.boot.class.path、mSharedLibraries、/system/frameworks目录这几个目录下面机型操作,若有任意一个文件夹中有任意一个文件需要优化,删除 /data/dalvik-cache  目录下以data@app@或者data@app-private@文件

8.2.8 启动FileObserver监控APK文件目录
           
[html] 
view plain
 copy

  1. <span style=“white-space:pre”>    </span>    //监控 system/framework目录  
  2.            mFrameworkInstallObserver = new AppDirObserver(mFrameworkDir.getPath(), OBSERVER_EVENTS, true);  
  3.            mFrameworkInstallObserver.startWatching();  
  4.            scanDirLI(mFrameworkDir, PackageParser.PARSE_IS_SYSTEM| PackageParser.PARSE_IS_SYSTEM_DIR,scanMode | SCAN_NO_DEX, 0);  
  5.              
  6.           //监控 system/app目录   
  7.            mSystemAppDir = new File(Environment.getRootDirectory(), “app”);  
  8.            mSystemInstallObserver = new AppDirObserver(mSystemAppDir.getPath(), OBSERVER_EVENTS, true);  
  9.            mSystemInstallObserver.startWatching();  
  10.            scanDirLI(mSystemAppDir, PackageParser.PARSE_IS_SYSTEM| PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);  
  11.              
  12.             //监控 /vendor/app目录    
  13.            mVendorAppDir = new File(“/vendor/app”);  
  14.            mVendorInstallObserver = new AppDirObserver(mVendorAppDir.getPath(), OBSERVER_EVENTS, true);  
  15.            mVendorInstallObserver.startWatching();  
  16.            scanDirLI(mVendorAppDir, PackageParser.PARSE_IS_SYSTEM| PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);  
  17.   
  18.            mInstaller.moveFiles();  
  19.   
  20.            …              
  21.            mAppInstallDir = new File(dataDir, “app”);  
  22.            //look for any incomplete package installations  
  23.            ArrayList<PackageSetting> deletePkgsList = mSettings.getListOfIncompleteInstallPackagesLPr();  
  24.            //clean up list  
  25.            for(int i = 0; i < deletePkgsList.size(); i++) {                 
  26.                cleanupInstallFailedPackage(deletePkgsList.get(i));  
  27.            }  
  28.            
  29.            deleteTempPackageFiles();  
  30.   
  31.            if (!mOnlyCore) {  
  32.                   
  33.                mAppInstallObserver = new AppDirObserver(mAppInstallDir.getPath(), OBSERVER_EVENTS, false);  
  34.                mAppInstallObserver.startWatching();  
  35.                scanDirLI(mAppInstallDir, 0, scanMode, 0);  
  36.      
  37.                mDrmAppInstallObserver = new AppDirObserver(mDrmAppPrivateInstallDir.getPath(), OBSERVER_EVENTS, false);  
  38.                mDrmAppInstallObserver.startWatching();  
  39.                scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK,scanMode, 0);  
  40.            } else {  
  41.                mAppInstallObserver = null;  
  42.                mDrmAppInstallObserver = null;  
  43.            }  

利用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的命令,他们之间的关系:
《深入理解PackageManagerService》

8.3.1 Installer 这是java层提供的Installer类,提供apk安装和卸载过程中的操作

安装的过程调用install方法:
[html] 
view plain
 copy

  1. public int install(String name, int uid, int gid) {  
  2.        StringBuilder builder = new StringBuilder(“install”);  
  3.        builder.append(‘ ‘);  
  4.        builder.append(name);  
  5.        builder.append(‘ ‘);  
  6.        builder.append(uid);  
  7.        builder.append(‘ ‘);  
  8.        builder.append(gid);  
  9.        return execute(builder.toString());  
  10.    }  

先将name,uid,gid按照一定的规则生成一个字符串形式:”install  name   uid   gid” 然后执行execute方法:   
[html] 
view plain
 copy

  1. private int execute(String cmd) {  
  2.        String res = transaction(cmd);//调用transaction 方法  
  3.        try {  
  4.            return Integer.parseInt(res);  
  5.        } catch (NumberFormatException ex) {  
  6.            return -1;  
  7.        }  
  8.    }  

调用了transaction方法:  
[html] 
view plain
 copy

  1. private synchronized String transaction(String cmd) {  
  2.        if (!connect()) {//连接installd服务  
  3.            Slog.e(TAG, “connection failed”);  
  4.            return “-1”;  
  5.        }  
  6.   
  7.        if (!writeCommand(cmd)) {//向installd发送命令         
  8.            Slog.e(TAG, “write command failed? reconnect!”);  
  9.            if (!connect() || !writeCommand(cmd)) {  
  10.                return “-1”;  
  11.            }  
  12.        }  
  13.        if (LOCAL_DEBUG) {  
  14.            Slog.i(TAG, “send: ‘” + cmd + “‘”);  
  15.        }  
  16.        if (readReply()) {//读取返回的数据  
  17.            String s = new String(buf, 0, buflen);  
  18.            if (LOCAL_DEBUG) {  
  19.                Slog.i(TAG, “recv: ‘” + s + “‘”);  
  20.            }  
  21.            return s;  
  22.        } else {  
  23.            if (LOCAL_DEBUG) {  
  24.                Slog.i(TAG, “fail”);  
  25.            }  
  26.            return “-1”;  
  27.        }  
  28.    }  

三个过程: connect 进行连接;writeCommand 发送请求  ; readReply读取响应 可以看出来client端的工作仍然是由installd来完成,client将请求发送给installd来完成,真正的工作是installd

8.3.2 Installd服务 Intalld服务是init阶段启动的服务进行,在clieng发送一个命令之后会将该命令按照下面的对应方式进行映射:
[html] 
view plain
 copy

  1. struct cmdinfo {  
  2.     const char *name;  //命令名称  
  3.     unsigned numargs; //参数个数  
  4.     int (*func)(char **arg, char reply[REPLY_MAX]);  //该命令对应的函数  
  5. };  
  6.   
  7. struct cmdinfo cmds[] = {  
  8.     { “ping”,                 0, do_ping },  
  9.     { “install”,              3, do_install },  
  10.     { “dexopt”,               3, do_dexopt },  
  11.     { “movedex”,              2, do_move_dex },  
  12.     { “rmdex”,                1, do_rm_dex },  
  13.     { “remove”,               2, do_remove },  
  14.     { “rename”,               2, do_rename },  
  15.     { “freecache”,            1, do_free_cache },  
  16.     { “rmcache”,              1, do_rm_cache },  
  17.     { “protect”,              2, do_protect },  
  18.     { “getsize”,              4, do_get_size },  
  19.     { “rmuserdata”,           2, do_rm_user_data },  
  20.     { “movefiles”,            0, do_movefiles },  
  21.     { “linklib”,              2, do_linklib },  
  22.     { “unlinklib”,            1, do_unlinklib },  
  23.     { “mkuserdata”,           3, do_mk_user_data },  
  24.     { “rmuser”,               1, do_rm_user },  
  25. };  

这里的命令是install,对应的函数是do_install,它有调用Commands.c中的install方法:
[html] 
view plain
 copy

  1. int install(const char *pkgname, uid_t uid, gid_t gid)  
  2. {  
  3.     char pkgdir[PKG_PATH_MAX];  
  4.     char libdir[PKG_PATH_MAX];  
  5.   
  6.     if ((uid < AID_SYSTEM) || (gid < AID_SYSTEM)) {  
  7.         LOGE(“invalid uid/gid: %d %d\n”, uid, gid);  
  8.         return -1;  
  9.     }  
  10.   
  11.     if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, 0)) {  
  12.         LOGE(“cannot create package path\n”);  
  13.         return -1;  
  14.     }  
  15.   
  16.     if (create_pkg_path(libdir, pkgname, PKG_LIB_POSTFIX, 0)) {  
  17.         LOGE(“cannot create package lib path\n”);  
  18.         return -1;  
  19.     }  
  20.   
  21.     if (mkdir(pkgdir, 0751) < 0) {  
  22.         LOGE(“cannot create dir ‘%s’: %s\n”, pkgdir, strerror(errno));  
  23.         return -errno;  
  24.     }  
  25.     if (chmod(pkgdir, 0751) < 0) {  
  26.         LOGE(“cannot chmod dir ‘%s’: %s\n”, pkgdir, strerror(errno));  
  27.         unlink(pkgdir);  
  28.         return -errno;  
  29.     }  
  30.     if (chown(pkgdir, uid, gid) < 0) {  
  31.         LOGE(“cannot chown dir ‘%s’: %s\n”, pkgdir, strerror(errno));  
  32.         unlink(pkgdir);  
  33.         return -errno;  
  34.     }  
  35.     if (mkdir(libdir, 0755) < 0) {  
  36.         LOGE(“cannot create dir ‘%s’: %s\n”, libdir, strerror(errno));  
  37.         unlink(pkgdir);  
  38.         return -errno;  
  39.     }  
  40.     if (chmod(libdir, 0755) < 0) {  
  41.         LOGE(“cannot chmod dir ‘%s’: %s\n”, libdir, strerror(errno));  
  42.         unlink(libdir);  
  43.         unlink(pkgdir);  
  44.         return -errno;  
  45.     }  
  46.     if (chown(libdir, AID_SYSTEM, AID_SYSTEM) < 0) {  
  47.         LOGE(“cannot chown dir ‘%s’: %s\n”, libdir, strerror(errno));  
  48.         unlink(libdir);  
  49.         unlink(pkgdir);  
  50.         return -errno;  
  51.     }  
  52.     return 0;  
  53. }  

创建各种数据目录并对目录权限进行处理,这里只是创建了目录数据而已,APK文件的安装还是很复杂的,后面会分析。

    原文作者:大风在吹车
    原文地址: https://blog.csdn.net/wl0073921/article/details/50673527
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞