您的位置:首页 > 其它

深入理解PackageManagerService

2015-10-20 16:19 411 查看


转载请标明出处: 

http://blog.csdn.net/yujun411522/article/details/46226001
本文出自:【yujun411522的博客】

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函数 :   

[java]
view plaincopyprint?

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;  
   }  

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对象的创建和初始化
....

[html]
view plaincopyprint?

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);  

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系统运行过程中的一些配置信息,它的构造函数: 

[html]
view plaincopyprint?

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");  
    }  

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

[html]
view plaincopyprint?

//定义了一个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  
   }  

//定义了一个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
   

[html]
view plaincopyprint?

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;  
   }  

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

[html]
view plaincopyprint?

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

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

其中涉及到了HandlerThread、PackageHandler等类,它们之间的关系就是:



可以看出HandlerThread和PackageHandler分别代表系统通信模型中的Looper线程和Handler处理器,其中PackageHandler是PMS的内部类,它的handleMessage方法的实现实际调用了doHandleMessage方法: 

[html]
view plaincopyprint?

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:  
  
}  
}  

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

[html]
view plaincopyprint?

mInstaller = new Installer();  
mUserAppDataDir = new File(dataDir, "user");  
mUserManager = new UserManager(mInstaller, mUserAppDataDir);  

mInstaller = new Installer();
mUserAppDataDir = new File(dataDir, "user");
mUserManager = new UserManager(mInstaller, mUserAppDataDir);

创建UserManager对象,看它的构造方法: 

[html]
view plaincopyprint?

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函数  
   }  

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 函数

   

[html]
view plaincopyprint?

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();  
       }  
   }  

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实体类

[html]
view plaincopyprint?

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;  

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实体类    

[html]
view plaincopyprint?

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();  
    }  
}  

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();函数调用的实现
   

[html]
view plaincopyprint?

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);  
    }  

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文件(一部分)

[html]
view plaincopyprint?

<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>  

<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为例:

[html]
view plaincopyprint?

<permissions>  
    <feature name="android.hardware.wifi" />  
</permissions>  

<permissions>
<feature name="android.hardware.wifi" />
</permissions>

说明当前设备可以支持wifi
再看软件 feature配置android.software.live_wallpaper.xml

[html]
view plaincopyprint?

permissions>  
    <feature name="android.software.live_wallpaper" />  
</permissions>  

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优化

[html]
view plaincopyprint?

 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();//删除缓存数据  
                        }  
                    }  
                }  
            }  

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文件目录
           

[html]
view plaincopyprint?

<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;  
           }  

<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方法:

[html]
view plaincopyprint?

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 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方法:   

[html]
view plaincopyprint?

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

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

调用了transaction方法:  

[html]
view plaincopyprint?

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";  
       }  
   }  

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发送一个命令之后会将该命令按照下面的对应方式进行映射:

[html]
view plaincopyprint?

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 },  
};  

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方法:

[html]
view plaincopyprint?

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;  
}  

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文件的安装还是很复杂的,后面会分析。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Package