PackageManagerService简介
源文档下载地址:http://download.csdn.net/download/zhaojigao/10165775
目录
2.2:SystemConfig.getInstance();函数分析...14
2.3:mSettings.readLPw();函数分析...18
1:介绍:
PackageManagerService,是Android系统中核心服务之一,管理着所有跟package相关的工作,常见的比如安装、卸载应用,这个Service我们在平时开发过程中会经常的遇到,所以了解了它的启动流程,对于我们项目的开发会有很大的帮助。
今天我们就从三个方面来分析PackageManagerService的具体功能以及功能的实现流程
1:PackageManagerService的启动以及启动中应用的安装流程
2:用户安装的三方应用的安装流程
备注:为了书写方便,后续将PackageManagerService简写成PMS
2:PMS的启动以及启动中应用的安装流程
根据前面我们学习的Android系统的启动流程可以知道,PMS是在SystemServer中启动的,下面我们就看下SystemServer中具体做了哪些准备工作,又是如何启动PMS的,
通过之前的学习,我们知道PMS是在SystemServer的startBootstrapServices函数中启动的,这里我们看下这个函数是如何启动的
private void startBootstrapServices() {
Installerinstaller = mSystemServiceManager.startService(Installer.class);
mPackageManagerService = PackageManagerService.main(mSystemContext,installer, mOnlyCore);
}
这个函数中首先启动了一个Installer.class的服务,然后将其对象作为参数传递个PMS,同时传递给一个SystemContext对象和一个Boolean类型的mOnlyCore,这个mOnlyCore一般情况都是false。这个Installer主要负责应用安装方面非常基础的操作,这里我们先看下Installer的具体实现。我们先看下Installer.java的具体实现。
private final InstallerConnectionmInstaller;
publicInstaller(Context context) {
super(context);
mInstaller = new InstallerConnection();
}
上面的代码显示,在服务的构造函数中初始化了一个InstallerConnection对象。而且这这类是使用的单例模式的。
public intinstall(String uuid, String name, int uid, int gid, String seinfo) {
public intdexopt(String apkPath, int uid, boolean isPublic, String pkgName,
public intidmap(String targetApkPath, String overlayApkPath, int uid) {
public intmovedex(String srcPath, String dstPath, String instructionSet) {
public intrmdex(String codePath, String instructionSet) {
****** 删除部分函数
public intgetSizeInfo(String pkgName, int persona, String apkPath, String libDirPath,
public intmoveFiles() {
public intlinkNativeLibraryDirectory(String uuid, String dataPath, StringnativeLibPath32,
publicboolean restoreconData(String uuid, String pkgName, String seinfo, int uid) {
public intcreateOatDir(String oatDir, String dexInstructionSet) {
public intlinkFile(String relativePath, String fromBase, String toBase) {
上面列举出了Installer.java里面的大部分函数,通过这些函数的名称,我们就能很清楚的看到Installer的具体功能,每个函数都是应用安装过程中,非常重要的函数。这些函数的具体功能的实现是在native层。每个函数最后都会调用mInstaller.execute("参数");继续执行,这里我们看下InstallerConnection的execute函数的具体实现。
public synchronized String transact(Stringcmd) {
if(!connect()) { // 链接native层的install服务
return "-1";
}
if(!writeCommand(cmd)) {// 向socket中写入参数
if(!connect() || !writeCommand(cmd)) {
return "-1";
}
}
final intreplyLength = readReply(); // 获取对端执行结果的返回值
if(replyLength > 0) {
String s = new String(buf, 0, replyLength);
return s;
} else {
return "-1";
}
}
上面的函数主要做了三件事情,首先调用connect进行链接,然后调用writeCommand将前面传递的参数进入socket,然后调用readReply获取对端的反馈。
private boolean connect() {
try {
mSocket = new LocalSocket();
LocalSocketAddress address = newLocalSocketAddress("installd",
LocalSocketAddress.Namespace.RESERVED);
mSocket.connect(address);//链接installd的本地socket
mIn =mSocket.getInputStream();//获取socket的输入输出
mOut= mSocket.getOutputStream();
} catch(IOException ex) {
disconnect();
return false;
}
returntrue;
}
private booleanwriteCommand(String cmdString) {
finalbyte[] cmd = cmdString.getBytes();
final intlen = cmd.length;
buf[0] =(byte) (len & 0xff);
buf[1] =(byte) ((len >> 8) & 0xff);
try {// 向socket中写入数据
mOut.write(buf, 0, 2);
mOut.write(cmd, 0, len);
} catch(IOException ex) {
return false;
}
returntrue;
}
connect这个函数链接本地的一个叫installd服务端,初始化mSocket 和mIn , mOut三个变量。而writeCommand函数就是根据前面的函数初始化的mOut参数将前面传递过来的参数写入socket传递给对端进行处理。这里我们比较疑惑,这个installd的本地socket具体是哪里来的。通过前面学习的Android系统的init进程我们知道这个socket是在init.rc中定义的,在init进程中启动的。
service installd /system/bin/installd
class main //执行文件是在/system/bin/installd下面,会创建一个名为installd的本地socket
socket installd stream 600 system system
源码的具体路径为:frameworks\native\cmds\installd\installd.cpp
下面我们看下installd.cpp的main函数
int main(const int argc __unused, char *argv[]) {
********
//初始化一些全局变量
if(initialize_globals() < 0) {
ALOGE("Could not initialize globals; exiting.\n");
exit(1);
}
//初始化安装目录
if(initialize_directories() < 0) {
ALOGE("Could not create directories; exiting.\n");
exit(1);
}
//取得installd套接字的句柄,
lsocket =android_get_control_socket(SOCKET_PATH);
if (lsocket< 0) {
ALOGE("Failed to get socket from environment: %s\n",strerror(errno));
exit(1);
}
//监听该socket
if(listen(lsocket, 5)) {
ALOGE("Listen on socket failed: %s\n", strerror(errno));
exit(1);
}
//修改该socket的属性
fcntl(lsocket, F_SETFD, FD_CLOEXEC);
//循环等待接收客户端的请求
for (;;) {
alen =sizeof(addr);
s =accept(lsocket, &addr, &alen);
//接收到客户端的请求后,修改客户端请求socket属性
fcntl(s,F_SETFD, FD_CLOEXEC);
for (;;){ //循环读取客户端socket中的内容,直到读取内容为空为止
unsigned short count;
//读取数据长度,读取成功返回0,反之返回-1
if(readx(s, &count, sizeof(count))) {
ALOGE("failed to read size\n");break;
}
//如果读取成功,但是读取的数据长度超出1024字节,同样停止读取
if((count < 1) || (count >= BUFFER_MAX)) {
ALOGE("invalid size %d\n", count); break;
}
//读取数据内容,读取成功返回0,反之返回-1
if(readx(s, buf, count)) {
break;
}
//执行客户端发送过来的命令
if(execute(s, buf)) break;
}
ALOGI("closing connection\n");
//处理完客户命令值,关闭连接
close(s);
}
return 0;
}
installd.cpp的main函数操作并不多,主要就是进行一些简单的初始化,然后创建本地socket进入循环,等待客户端的链接,接着循环读取客户端发送过来的数据,调用execute函数处理,处理完成之后,关闭连接,继续等待下一个连接。下面我们看下execute函数的实现。
struct cmdinfo cmds[] = {
{"ping", 0,do_ping },
{"install", 5,do_install },
{"dexopt", 9,do_dexopt },
{ "markbootcomplete", 1, do_mark_boot_complete },
{"movedex", 3,do_move_dex },
{"rmdex", 2,do_rm_dex },
{"remove", 3,do_remove },
{"rename", 2,do_rename },
{"fixuid", 4, do_fixuid },
{"freecache", 2,do_free_cache },
{"rmcache", 3,do_rm_cache },
{"rmcodecache", 3,do_rm_code_cache },
{"getsize", 8,do_get_size },
{"rmuserdata", 3, do_rm_user_data },
{"cpcompleteapp", 6,do_cp_complete_app },
{"movefiles", 0,do_movefiles },
{"linklib", 4,do_linklib },
{"mkuserdata", 5,do_mk_user_data },
{"mkuserconfig", 1,do_mk_user_config },
{"rmuser", 2,do_rm_user },
{"idmap", 3,do_idmap },
{"restorecondata", 4,do_restorecon_data },
{"createoatdir", 2,do_create_oat_dir },
{"rmpackagedir", 1,do_rm_package_dir },
{"linkfile", 3,do_link_file }
};
static int execute(int s, char cmd[BUFFER_MAX])
{
// 处理参数,将其规范化存储到arg参数中
arg[0] = cmd;
while (*cmd){
if(isspace(*cmd)) {
*cmd++ = 0;
n++;
arg[n] = cmd;
if (n== TOKEN_MAX) {
ALOGE("too many arguments\n");
goto done;
}
}
if (*cmd){
cmd++;
}
}
// 循环匹配,调用cmds表中对应的函数处理客户需要
for (i = 0; i< sizeof(cmds) / sizeof(cmds[0]); i++) {
if(!strcmp(cmds[i].name,arg[0])) {
if (n!= cmds[i].numargs) {
}else {
ret = cmds[i].func(arg + 1, reply);
}
gotodone;
}
}
// 将处理结果写入socket
if (writex(s,&count, sizeof(count))) return -1;
if (writex(s,cmd, count)) return -1;
return 0;
}
execute这个函数比较简单,就是对传入的参数进行处理,将其存入到arg变量中,然后根据前面对应的表格调用不同的函数处理客户端的需求,然后将处理结果写入到socket。返回给客户端。
installd就介绍到这里,下面我们看下PMS的main函数。
2.1 PMS的main函数
下面我们看下PMS的main函数。这个函数非常长,大约有六百行,这里我们根据其功能分四个部分说明。
1)初始化相关资源
2)对系统的库进行odex优化
3)扫描安装包
4)根据扫描结果对安装包进行处理
public static PackageManagerService main(Contextcontext, Installer installer, boolean factoryTest, boolean onlyCore) {
PackageManagerService m = new PackageManagerService(context, installer, factoryTest,onlyCore);
ServiceManager.addService("package", m);
return m;
}
main函数很简单,直接new一个PMS对象,然后将其添加到ServiceManager就直接返回了.
public PackageManagerService(Context context, Installerinstaller, boolean factoryTest, boolean onlyCore) {
mContext =context;
mFactoryTest = factoryTest;
mOnlyCore = onlyCore;
mLazyDexOpt ="eng".equals(SystemProperties.get("ro.build.type"));
//构造DisplayMetrics对象以便获取尺寸数据
mMetrics =new DisplayMetrics();
//构造Settings对象存储运行时的设置信息
mSettings = new Settings(mPackages);
//添加一些用户uid
mSettings.addSharedUserLPw("android.uid.system",Process.SYSTEM_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.phone",RADIO_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.log",LOG_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.nfc",NFC_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.bluetooth",BLUETOOTH_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw("android.uid.shell",SHELL_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
long dexOptLRUThresholdInMinutes;
if(mLazyDexOpt) {
dexOptLRUThresholdInMinutes = 30; // only last 30 minutes of apps foreng builds.
} else {
dexOptLRUThresholdInMinutes = 7 * 24 * 60; // apps used in the 7 daysfor users.
}
// 决定执行dex优化操作的时间阈
// 对于Eng版本,则只会对30分钟之内使用过的app执行dex优化;
// 对于非Eng版本,则会将用户最近一周内使用过的app执行dex优化;
mDexOptLRUThresholdInMills= dexOptLRUThresholdInMinutes * 60 * 1000;
//installer由SystemServer构造,这里通过该对象与底层进行通信,进行具体安装与卸载的操作
mInstaller =installer;
//创建PackageDexOptimizer,该类用于辅助进行dex优化
mPackageDexOptimizer = newPackageDexOptimizer(this);
//对应用权限变化进行监听
mOnPermissionChangeListeners = newOnPermissionChangeListeners(FgThread.get().getLooper());
// 获取屏幕尺寸信息
getDefaultDisplayMetrics(context, mMetrics);
// 初始化SystemConfig,在其初始化函数中,会读取系统配置文件。其中比较重要的两个是
//\system\etc\permissions\handheld_core_hardware.xml, 这里主要定义了硬件的支持情况,
//\system\etc\permissions\platform.xml 这里定义了一些基本的权限
SystemConfig systemConfig = SystemConfig.getInstance();
mGlobalGids= systemConfig.getGlobalGids();
mSystemPermissions = systemConfig.getSystemPermissions();
mAvailableFeatures =systemConfig.getAvailableFeatures();
synchronized (mInstallLock) {
synchronized (mPackages) {
//启动消息处理线程
mHandlerThread = new ServiceThread(TAG,Process.THREAD_PRIORITY_BACKGROUND, true);
mHandlerThread.start();
//通过消息处理线程的Looper对象构造一个处理消息的Handler对象
mHandler =new PackageHandler(mHandlerThread.getLooper());
//使用看门狗检测当前消息处理线程
Watchdog.getInstance().addThread(mHandler,WATCHDOG_TIMEOUT);
//获取当前的Data目录
File dataDir =Environment.getDataDirectory();
mAppDataDir =new File(dataDir, "data");
mAppInstallDir= new File(dataDir, "app");
mAppLib32InstallDir= new File(dataDir, "app-lib");
mAsecInternalPath= new File(dataDir, "app-asec").getPath();
mUserAppDataDir= new File(dataDir, "user");
mDrmAppPrivateInstallDir= new File(dataDir, "app-private");
//构造UserManagerService对象,创建用户管理服务
sUserManager= new UserManagerService(context, this, mInstallLock, mPackages);
//读取权限配置文件中的信息,保存到mPermissions这个ArrayMap中
// 这些权限是在SystemConfig对象初始化的时候从配置文件里面读取到的
ArrayMap<String,SystemConfig.PermissionEntry> permConfig = systemConfig.getPermissions();
for(int i=0; i<permConfig.size(); i++) {
SystemConfig.PermissionEntry perm = permConfig.valueAt(i);
BasePermission bp = mSettings.mPermissions.get(perm.name);
if(bp == null) {
bp = new BasePermission(perm.name, "android",BasePermission.TYPE_BUILTIN);
mSettings.mPermissions.put(perm.name, bp);
}
if(perm.gids != null) {
bp.setGids(perm.gids, perm.perUser);
}
}
// 获取SharedLibraries,这些是在systemConfig初始化的时候,从配置文件里面读取到的
ArrayMap<String,String> libConfig = systemConfig.getSharedLibraries();
for (inti=0; i<libConfig.size(); i++) {
mSharedLibraries.put(libConfig.keyAt(i),new SharedLibraryEntry(libConfig.valueAt(i), null));
}
// 初始化Selinux
mFoundPolicyFile =SELinuxMMAC.readInstallPolicy();
// 读取之前系统已经安装的应用信息,这非常重要,后面会详细分析
mRestoredSettings= mSettings.readLPw(this, sUserManager.getUsers(false), mSdkVersion,mOnlyCore);
上面这些代码主要是各种初始化,其中比较重要的有两个,第一个是SystemConfig,这个对象在初始化的时候,会读取系统的配置文件,获取到系统的基本权限,硬件支持情况,以及各个provider的定义信息,第二个是mSettings对象,这个对象非常重要,主要是存储系统安装的应用的各种信息,这里非常重要的函数就是readLPw,这个函数会获取系统启动之前安装的应用列表,以及手机在启动之前保存的权限,应用安装的所有信息。这两部分的具体实现会在后面详细介绍。这里继续看PMS的构造函数。
final ArraySet<String> alreadyDexOpted =new ArraySet<String>();
// 系统变量,这里将不需要进行odex优化的文件添加到alreadyDexOpted表格中
final StringbootClassPath = System.getenv("BOOTCLASSPATH");
final StringsystemServerClassPath = System.getenv("SYSTEMSERVERCLASSPATH");
if (bootClassPath!= null) {
String[] bootClassPathElements =splitString(bootClassPath, ':');
for (String element : bootClassPathElements) {
alreadyDexOpted.add(element);
}
}
if(systemServerClassPath != null) {
String[] systemServerClassPathElements =splitString(systemServerClassPath, ':');
for (String element : systemServerClassPathElements) {
alreadyDexOpted.add(element);
}
}
//allInstructionSets == arm 这里获取需要odex优化的类型,这里获取到的是arm说明仅仅是32位的。
finalList<String> allInstructionSets =InstructionSets.getAllInstructionSets();
final String[]dexCodeInstructionSets = getDexCodeInstructionSets(
allInstructionSets.toArray(new String[allInstructionSets.size()]));
// Ensure allexternal libraries have had dexopt run on them.
// 对所有的mSharedLibraries进行odex优化
if(mSharedLibraries.size() > 0) {
for (String dexCodeInstructionSet :dexCodeInstructionSets) {
for(SharedLibraryEntry libEntry : mSharedLibraries.values()) {
final String lib= libEntry.path;
if (lib ==null) { continue;}
try {// 进行判断,如果需要进行odex优化就调用mInstaller.dexopt进行优化
// 同时将次文件添加到alreadyDexOpted表格中
intdexoptNeeded = DexFile.getDexOptNeeded(lib, null, dexCodeInstructionSet,false);
if(dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
alreadyDexOpted.add(lib);
mInstaller.dexopt(lib, Process.SYSTEM_UID,true, dexCodeInstructionSet, dexoptNeeded);
}
}
}
}
}
File frameworkDir =new File(Environment.getRootDirectory(), "framework");
alreadyDexOpted.add(frameworkDir.getPath()+ "/framework-res.apk");
alreadyDexOpted.add(frameworkDir.getPath()+ "/core-libart.jar");
// 对/system/framework 下面的apk文件和jar文件进行odex优化
String[]frameworkFiles = frameworkDir.list();
if (frameworkFiles!= null) {
for (String dexCodeInstructionSet :dexCodeInstructionSets) {
for (int i=0; i<frameworkFiles.length; i++) {
File libPath =new File(frameworkDir, frameworkFiles[i]);
String path= libPath.getPath();
if(alreadyDexOpted.contains(path)) {continue; }
if (!path.endsWith(".apk")&& !path.endsWith(".jar")) { continue; }
try {
intdexoptNeeded = DexFile.getDexOptNeeded(path, null, dexCodeInstructionSet,false);
if(dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {
mInstaller.dexopt(path, Process.SYSTEM_UID, true, dexCodeInstructionSet,dexoptNeeded);
}
}
}
}
}
上面这部分主要是对系统各个jar包进行扫描,根据需要进行odex优化,这里代码比较简单,没有比较深的调用,下面我们继续。
FilevendorOverlayDir = new File(VENDOR_OVERLAY_DIR);
scanDirLI(vendorOverlayDir,PackageParser.PARSE_IS_SYSTEM
|PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_TRUSTED_OVERLAY, 0);
scanDirLI(frameworkDir,PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR
|PackageParser.PARSE_IS_PRIVILEGED, scanFlags | SCAN_NO_DEX, 0);
final FileprivilegedAppDir = new File(Environment.getRootDirectory(),"priv-app");
scanDirLI(privilegedAppDir,PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR
|PackageParser.PARSE_IS_PRIVILEGED, scanFlags, 0);
final FilesystemAppDir = new File(Environment.getRootDirectory(), "app");
scanDirLI(systemAppDir,PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags,0);
File vendorAppDir =new File("/vendor/app");
try {
vendorAppDir = vendorAppDir.getCanonicalFile();
} catch (IOException e) {}
scanDirLI(vendorAppDir,PackageParser.PARSE_IS_SYSTEM| PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags,0);
final FileoemAppDir = new File(Environment.getOemDirectory(), "app");
scanDirLI(oemAppDir,PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags,0);
上面这部分代码非常清晰,就是调用scanDirLI对各个应用安装目录进行扫描,scanDirLI这个函数首先会列举目录下面的文件,判断文件是否是apk文件,如果是apk文件,就对其进行解析,然后将解析到的应用保存到系统已安装应用的列表中,同时遍历其四大组件,将其添加到相应的数据结构中,使其公有化。scanDirLI 这个函数非常重要,后面我们会详细介绍。下面我们在看下应用安装完成之后的一些后续处理。
// 删除不在使用的系统app
final List<String>possiblyDeletedUpdatedSystemApps = new ArrayList<String>();
if (!mOnlyCore) {
Iterator<PackageSetting> psit =mSettings.mPackages.values().iterator();
while (psit.hasNext()) {
PackageSetting ps = psit.next();
//disable system app. 如果不是系统应用 则跳过
if ((ps.pkgFlags &ApplicationInfo.FLAG_SYSTEM) == 0) {
continue;
}
final PackageParser.Package scannedPkg =mPackages.get(ps.name);
// scannedPkg 不为空,说明这个应用在系统中依然存在
if (scannedPkg != null) {
//如果当前应用是之前已经被用户disable的应用,这里需要将此应用的数据从公有化的数据中删除
if(mSettings.isDisabledSystemPackageLPr(ps.name)) {
removePackageLI(ps, true);// 删除disable的应用数据
}
continue;
}
// 如果应用依然在系统中存在,那么上面的判断最终会调用continue进行下一轮循环,这里就不走了
// 如果走到这里,就说明之前保存起来的应用已经在升级过程中删除了。这里需要根据那个应用是不是disable状态进行不同的处理
// 如果应用之前没有被disable就删除之前应用安装的数据目录,。
// 如果之前已经被disable了就将此添加到possiblyDeletedUpdatedSystemApps里面进一步处理
if(!mSettings.isDisabledSystemPackageLPr(ps.name)) {
psit.remove();
removeDataDirsLI(null, ps.name);
} else {
final PackageSetting disabledPs =mSettings.getDisabledSystemPkgLPr(ps.name);
if (disabledPs.codePath == null ||!disabledPs.codePath.exists()) {
possiblyDeletedUpdatedSystemApps.add(ps.name);
}
}
}
}
// 删除安装失败的安装包 //look for any incomplete package installations
ArrayList<PackageSetting> deletePkgsList= mSettings.getListOfIncompleteInstallPackagesLPr();
for(int i = 0; i < deletePkgsList.size();i++) {
cleanupInstallFailedPackage(deletePkgsList.get(i));
}
// 删除缓存 //delete tmpfiles
deleteTempPackageFiles();
// Remove any shared userIDs that have noassociated packages
// 删除没有使用的shared userIDs
mSettings.pruneSharedUsersLPw();
if (!mOnlyCore) {
//扫描 data/app/ 目录
scanDirLI(mAppInstallDir, 0, scanFlags |SCAN_REQUIRE_KNOWN, 0);
// 扫描 data/app-private 目录
scanDirLI(mDrmAppPrivateInstallDir,PackageParser.PARSE_FORWARD_LOCK,
scanFlags | SCAN_REQUIRE_KNOWN, 0);
// 遍历possiblyDeletedUpdatedSystemApps,处理通过OTA更新和删除的APK文件
for (String deletedAppName :possiblyDeletedUpdatedSystemApps) {
PackageParser.Package deletedPkg =mPackages.get(deletedAppName);
mSettings.removeDisabledSystemPackageLPw(deletedAppName);
// deletedPkg 为空说明这个应用在data分区是不存在的,因此彻底删除应用的安装信息
if (deletedPkg == null) {
removeDataDirsLI(null,deletedAppName);
} else {
// 如果deletedPkg不为空说明当前应用在data分区存在,因此这里设置其标志,删除系统应用的标志位
deletedPkg.applicationInfo.flags&= ~ApplicationInfo.FLAG_SYSTEM;
PackageSetting deletedPs =mSettings.mPackages.get(deletedAppName);
deletedPs.pkgFlags &=~ApplicationInfo.FLAG_SYSTEM;
}
}
// Now that we know all of the sharedlibraries, update all clients to have
// the correct library paths.
//更新所有package的usesLibraryFiles数据,为什么要做更新?
// 因为package在manifest里头使用use-library描述要引用的library,
// 在系统mSharedLibraries里可能是未定义的,所以,需要做数据同步
updateAllSharedLibrariesLPw();
// 更新权限信息
updatePermissionsLPw(null, null,updateFlags);
// 检查默认的浏览器是否有变更
checkDefaultBrowser();
// can downgrade to reader
// 将扫描到的应用写入到文件
mSettings.writeLPr();
// 初始化PackageInstallerService对象
mInstallerService = new PackageInstallerService(context,this);
}}
// 进行一次内存回收
Runtime.getRuntime().gc();
}
PMS在进行后续处理中,主要是针对OTA升级导致的系统应用出现删除的情况进行处理,如果发现之前已经安装OK的应用在这次系统启动中没有找到,而且在扫描完data分区之后,依然没有,就确认此应用已经在OTA升级过程中被删除了,这里就会删除应用的安装信息。然后更新应用的权限信息,因为如果应用在OTA升级过程中进行了更新,那么他的权限需要与升级之前的应用的权限保持一致,然后清除应用扫描过程中的临时文件,最后调用writeLPr将本次开机过程中扫描到的应用信息写入到磁盘保存。在应用信息保存之后,初始化一个mInstallerService对象,用于用户安装三方应用。然后进行一次垃圾回收,至此PMS的初始化就走完了。
下面我们看下在前面的分析过程中遗留的几个重要的函数。
2.2:SystemConfig.getInstance();函数分析
前面说过,这个函数主要是读取手机的一些基本权限和SharedLibraries,这里我们就看下这个函数的具体实现.
public class SystemConfig {
staticSystemConfig sInstance;
public staticSystemConfig getInstance() {
synchronized(SystemConfig.class) {
if(sInstance == null) {
sInstance = new SystemConfig();
}
return sInstance;
}
}
从上面的代码很容易看出这里使用的是个单例模式.
SystemConfig的构造函数
SystemConfig() {
// 调用readPermissions函数分别对四个配置文件目录里面的文件进行解析
readPermissions(Environment.buildPath(Environment.getRootDirectory(),"etc","sysconfig"),false);
readPermissions(Environment.buildPath(Environment.getRootDirectory(),"etc","permissions"),false);
readPermissions(Environment.buildPath(Environment.getOemDirectory(),"etc","sysconfig"),true);
readPermissions(Environment.buildPath(Environment.getOemDirectory(),"etc","permissions"),true);
}
void readPermissions(File libraryDir, booleanonlyFeatures) {
if(!libraryDir.exists() || !libraryDir.isDirectory()) {return;}
if(!libraryDir.canRead()) {return;}
// 文件存在,而且是目录并且可以正常读取,要不然直接返回
for (File f :libraryDir.listFiles()) {
if(!f.getPath().endsWith(".xml")) {continue;}
if(!f.canRead()) {continue;}
// 跳过非xml文件,以及不可读取的文件,然后调用readPermissionsFromXml进行解析
readPermissionsFromXml(f, onlyFeatures);
}
}
/system/etc/sysconfig /system/etc/permissions
/oem/etc/sysconfig /oem/etc/permissions
在分析readPermissionsFromXml之前,我们先看下上面传递进来的四个目录具体是那四个,都有那些文件,通过打印log确认上面传进来的目录是前面四个,但是只有第二个目录是存在的,且里面有文件,其他的都是不存在的目录,因此我们这里先看下/system/etc/permissions目录下面的文件。
/system/etc/permissions目录下面的文件,前面五个都是SharedLibraries相关的配置文件,文件内容非常简单,这里就不贴出来了,这里我们需要看下后面的两个文件,handheld_core_hardware.xml和platform.xml,其中handheld_core_hardware.xml主要定义了硬件以及软件配置相关feature,platform.xml定义了权限和SharedLibraries.
handheld_core_hardware.xml 文件内容:
<?xml version="1.0" encoding="utf-8"?>
<permissions>
<featurename="android.hardware.audio.output" />
<featurename="android.hardware.camera" />
<featurename="android.hardware.location" />
******
<featurename="android.software.voice_recognizers"notLowRam="true"/>
<featurename="android.software.print" />
<featurename="android.software.device_admin" />
<featurename="android.software.managed_users" />
</permissions>
这个文件里面主要包含两部分,android.hardware主要是配置的系统硬件的支持情况,android.software是系统软件的支持情况。在这里没有配置的就表示系统不支持,目前市面上一些应用商店会去检查这些属性来判断应用的兼容情况,谷歌商店就是这么做的,如果手机上面没有配置GPS的硬件,那么需要使用GPS的软件就会提示不兼容而无法安装。这里我们需要关注下标红的这一行,后续会详细介绍。
platform.xml 文件的内容
<?xml version="1.0"encoding="utf-8"?>
<permissions>
// 一些基本的权限
<permissionname="android.permission.BLUETOOTH"><groupgid="net_bt" /></permission>
<permissionname="android.permission.NET_TUNNELING"><groupgid="vpn"/></permission>
<permissionname="android.permission.INTERNET"><group gid="inet"/></permission>
*******
<assign-permissionname="android.permission.UPDATE_DEVICE_STATS" uid="media"/>
<assign-permissionname="android.permission.UPDATE_APP_OPS_STATS" uid="media"/>
<assign-permissionname="android.permission.ACCESS_SURFACE_FLINGER"uid="graphics" />
//SharedLibraries
<libraryname="android.test.runner"file="/system/framework/android.test.runner.jar"/>
<libraryname="javax.obex"file="/system/framework/javax.obex.jar"/>
<libraryname="org.apache.http.legacy"file="/system/framework/org.apache.http.legacy.jar"/>
// 允许手机在省电模式下依然进行网络连接,即使这个应用在后台运行也可以
<allow-in-power-save-except-idlepackage="com.android.providers.downloads" />
</permissions>
platform.xml 文件的内容比较多,主要包含权限,链接库和一个特殊的应用程序,其中权限包含两部分,第一部分是根据gid来确认的权限,第二部分是根据uid来确认的权限。下面我们看下readPermissionsFromXml函数的实现。
private void readPermissionsFromXml(File permFile,boolean onlyFeatures) {
FileReaderpermReader = null;
try {
permReader =new FileReader(permFile);
} catch(FileNotFoundException e) {}
// 查看当前设备是不是LowRam设备,
// 谷歌官方建议在内存小于1GB的时候,应该开启此选项,大于1GB的时候,关闭此选项。
// 等于1GB的时候,可以开启也可以不开启,这个会影响到CTS/GTS,需要谷歌报备,默认不开启
final booleanlowRam = ActivityManager.isLowRamDeviceStatic();
try {//开始解析文件
XmlPullParserparser = Xml.newPullParser();
parser.setInput(permReader);
while (true) {
XmlUtils.nextElement(parser);
if(parser.getEventType() == XmlPullParser.END_DOCUMENT) {
break;// 如果已经到了文件结尾就退出循环
}
String name =parser.getName();
//获取当前标签的名称,然后根据名称进行匹配
// 如果标签是permission并且onlyFeatures为false就在这里解析
if("permission".equals(name) && !onlyFeatures) {
Stringperm = parser.getAttributeValue(null, "name");
if (perm== null) {// 如果name为空直接跳过
XmlUtils.skipCurrentTag(parser);
continue;
}
perm =perm.intern();
readPermission(parser, perm);//调用readPermission继续处理,
//这个函数比较简单就不进行详细分析了
// 处理assign-permission标签
} else if("assign-permission".equals(name) && !onlyFeatures) {
Stringperm = parser.getAttributeValue(null, "name");
StringuidStr = parser.getAttributeValue(null, "uid");
if (uidStr== null || perm == null) {
XmlUtils.skipCurrentTag(parser);
continue;
}//如果name或者uid有一个为空,就直接跳过
// 根据uidStr获取当前进程的uid,
int uid =Process.getUidForName(uidStr);
if (uid< 0) { // uid 无效
XmlUtils.skipCurrentTag(parser);
continue;
}
perm =perm.intern();
ArraySet<String> perms = mSystemPermissions.get(uid);
if (perms== null) {
perms= new ArraySet<String>();
// 将解析到的权限放到mSystemPermissions中
mSystemPermissions.put(uid, perms);
}
perms.add(perm);
XmlUtils.skipCurrentTag(parser);//跳过当前标签
// 处理library标签
} else if("library".equals(name) && !onlyFeatures) {
Stringlname = parser.getAttributeValue(null, "name");
Stringlfile = parser.getAttributeValue(null, "file");
if (lname != null && lfile != null) {
// 在名字和file都不为空的情况下将其添加到mSharedLibraries中
mSharedLibraries.put(lname, lfile);
}
XmlUtils.skipCurrentTag(parser);//跳过当前标签
continue;
// 处理feature标签
} else if("feature".equals(name)) {
Stringfname = parser.getAttributeValue(null, "name");
booleanallowed;
if(!lowRam) {
allowed = true;
} else {
StringnotLowRam = parser.getAttributeValue(null, "notLowRam");
allowed = !"true".equals(notLowRam);
}
// 上面主要是针对低内存设备进行判断,如果是低内存设备就要读取当前Features的notLowRam标签,如果这个标签是true就表示在低内存设备上面不启动这个服务项,这里我们可以看到handheld_core_hardware.xml里面的android.software.voice_recognizers项就有这个标签为true,因此在低内存的设备中,这个Feature是不支持的。这个Feature会影响到GTS测试中的一个与截图相关的测试项,如果当前设备是低内存设备,GTS需要找谷歌豁免。
if (fname != null && allowed) {
FeatureInfo fi = new FeatureInfo();
fi.name = fname;
mAvailableFeatures.put(fname, fi);
}
XmlUtils.skipCurrentTag(parser);
continue;
} else *****
}}}
2.3:mSettings.readLPw();函数分析
Android启动时要扫描和安装现有app。已安装应用的信息被放在/data/system/packages.xml里,由Settings管理。另外/data/system/packages.list记录了app的uid和数据路径等信息。readLPw()就是用于恢复这些信息,readLPw()先打开packages.xml,再通过XmlPullParser类来解析其内容。它会根据不同的tag调用相应的函数来读取信息。
在分析readLPw函数之前,我们先看下packages.xml的内容格式,这样在对照代码看就容易理解了。
<?xml version='1.0' encoding='utf-8'standalone='yes' ?>
<packages>
// 权限相关的部分,这部分记录系统中定义的所有权限,包括三方应用定义的权限
<permissions>
<itemname="android.permission.REAL_GET_TASKS" package="android"protection="18" />
<itemname="android.permission.REMOTE_AUDIO_PLAYBACK"package="android" protection="2" />
…………………………………………………………
<itemname="android.permission.MEDIA_CONTENT_CONTROL" package="android"protection="18" />
<itemname="android.permission.DELETE_PACKAGES" package="android"protection="18" />
<itemname="com.android.voicemail.permission.ADD_VOICEMAIL"package="android" protection="1" />
</permissions>
// 应用程序信息,包含应用的详细信息,报名,apk路径,so库路径,支持的CPU信息,安装时间,权限等等。
<packagename="com.mediatek.ims"codePath="/system/priv-app/ImsService"nativeLibraryPath="/system/priv-app/ImsService/lib"publicFlags="940097101" privateFlags="8"pkgFlagsEx="0" ft="15e55a24720" it="15e55a24720"ut="15e55a24720" version="23"sharedUserId="1001">
<sigscount="1"><cert index="0"key="308203e73……………………e5daaa0dfcebb0e" /></sigs>
<perms>
<itemname="android.permission.WRITE_SETTINGS" granted="true"flags="0" />
………………………………………………………………………
<itemname="android.permission.UPDATE_APP_OPS_STATS"granted="true" flags="0" />
</perms>
<proper-signing-keyset identifier="1" />
</package>
// shared-user信息
<shared-username="android.uid.shell" userId="2000">
<sigscount="1"><cert index="0"/></sigs>
<perms>
<itemname="android.permission.REAL_GET_TASKS" granted="true"flags="0" />
<itemname="android.permission.WRITE_SETTINGS" granted="true"flags="0" />
………………………………
<itemname="android.permission.SET_SCREEN_COMPATIBILITY"granted="true" flags="0" />
<itemname="android.permission.DELETE_PACKAGES" granted="true"flags="0" />
</perms>
</shared-user>
</packages>
mSystemDir = new File(dataDir, "system");
mBackupSettingsFilename = new File(mSystemDir,"packages-backup.xml");
mPackageListFilename = new File(mSystemDir,"packages.list");
上面的文件是在Setting对象初始化的时候建立的,下面我们就看下readLPw函数的具体内容
boolean readLPw(PackageManagerService service,………………,boolean onlyCore) {
FileInputStreamstr = null;
if(mBackupSettingsFilename.exists()) {
try {
str =new FileInputStream(mBackupSettingsFilename);
if(mSettingsFilename.exists()) {
mSettingsFilename.delete();
}
} catch(java.io.IOException e) {} }
这里会首先判断mBackupSettingsFilename是否存在,如果这个文件存在,就说明手机在上次正在写入文件的时候关机,因此mSettingsFilename有可能已经损坏,这里就直接使用mBackupSettingsFilename作为配置文件进行读取,同时删除mSettingsFilename文件
try {
if (str ==null) { //str位空说明没有备份文件
if(!mSettingsFilename.exists()) {
returnfalse; //刷机后首次开机这里会直接返回
}
str = newFileInputStream(mSettingsFilename);
}
XmlPullParserparser = Xml.newPullParser();
parser.setInput(str, StandardCharsets.UTF_8.name());
下面就是使用XmlPullParser对配置文件进行解析,根据不同的标签调用不同的函数进行处理。
int outerDepth= parser.getDepth();
while ((type =parser.next()) != XmlPullParser.END_DOCUMENT
&&(type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
String tagName= parser.getName();
if(tagName.equals("package")) {
readPackageLPw(parser);
} else if(tagName.equals("permissions")) {
readPermissionsLPw(mPermissions, parser);
} else if(tagName.equals("permission-trees")) {
readPermissionsLPw(mPermissionTrees, parser);
} else if(tagName.equals("shared-user")) {
readSharedUserLPw(parser);
} else if(tagName.equals("preferred-packages")) {
// nolonger used.
………………………………
} else if(tagName.equals("keyset-settings")) {
mKeySetManagerService.readKeySetsLPw(parser, mKeySetRefs);
} else if(TAG_VERSION.equals(tagName)) {
XmlUtils.skipCurrentTag(parser);
}
}
str.close();
}return true;
}
这个函数会首先判断备份文件是否存在,如果备份文件存在就说明在写入文件的过程中,手机关机,导致配置文件没有写入完毕,因此配置文件可能已经损坏,因此这里直接就使用备份的文件作为配置文件进行读取。然后就是使用XmlPullParser对配置文件进行解释,根据不同的标签调用不同的函数处理。
private void readPermissionsLPw(ArrayMap<String,BasePermission> out, XmlPullParser parser){
int outerDepth =parser.getDepth();
int type;
// 循环读完权限标签里面的每一项,
while ((type =parser.next()) != XmlPullParser.END_DOCUMENT
&&(type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
final StringtagName = parser.getName();
if(tagName.equals("item")) {
// 获取应用的名称和sourcePackage,ptype在默认情况下都是空
final Stringname = parser.getAttributeValue(null, "name");
final StringsourcePackage = parser.getAttributeValue(null, "package");
final Stringptype = parser.getAttributeValue(null, "type");
if (name !=null && sourcePackage != null) {
final booleandynamic = "dynamic".equals(ptype);
finalBasePermission bp = new BasePermission(name.intern(), sourcePackage,
dynamic ? BasePermission.TYPE_DYNAMIC : BasePermission.TYPE_NORMAL);
// 获取权限的保护等级
bp.protectionLevel = readInt(parser, null,"protection",PermissionInfo.PROTECTION_NORMAL);
bp.protectionLevel= PermissionInfo.fixProtectionLevel(bp.protectionLevel);
//将权限信息放入到mPermissions中
out.put(bp.name, bp);
} else {}
} else {}
XmlUtils.skipCurrentTag(parser);
}}
readPermissionsLPw这个函数就是循环遍历权限标签里面的每一个项,然后获取权限信息,根据这些信息建立BasePermission对象,然后将其添加到mPermissions变量中。
Setting中的readLPw主要就是读取系统配置文件,然后根据配置文件初始化各个数据,这里解析xml文件的具体函数代码流程都是类似的,这里就只介绍readPermissionsLPw。
2.4:scanDirLI函数分析
private void scanDirLI(File dir, int parseFlags, intscanFlags, long currentTime) {
final File[]files = dir.listFiles();
// 如果目录是空的直接返回
if(ArrayUtils.isEmpty(files)) {return;}
// 循环对目录里面的文件进行处理
for (File file :files) {
final booleanisPackage = (isApkFile(file) || file.isDirectory());
if (!isPackage){continue;} // 跳过非apk文件的目录
try { //调用scanPackageLI对应用进行安装
scanPackageLI(file, parseFlags | PackageParser.PARSE_MUST_BE_APK,
scanFlags, currentTime, null);
} catch(PackageManagerException e) {
//Deleteinvalid userdata apps data分区的apk,如果安装失败就删除apk文件
if((parseFlags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
e.error== PackageManager.INSTALL_FAILED_INVALID_APK) {
if(file.isDirectory()) {
mInstaller.rmPackageDir(file.getAbsolutePath());
} else {
file.delete();
}
}}}}
scanDirLI 这个函数就是对目录中的文件进行循环处理,判断是否是apk文件,对于apk文件调用scanPackageLI进行安装,如果安装失败且文件是data分区的,就删除安装失败的apk文件。
private PackageParser.Package scanPackageLI(FilescanFile,***** UserHandle user) {
// 构建PackageParser对象
PackageParser pp = new PackageParser();
pp.setSeparateProcesses(mSeparateProcesses);
pp.setOnlyCoreApps(mOnlyCore);
pp.setDisplayMetrics(mMetrics);
if((scanFlags & SCAN_TRUSTED_OVERLAY) != 0) {
parseFlags |= PackageParser.PARSE_TRUSTED_OVERLAY;
}
finalPackageParser.Package pkg;
try { // 调用parsePackage解析apk文件,获取apk的应用信息。
pkg =pp.parsePackage(scanFile, parseFlags);
} catch(PackageParserException e) {
}
//*****都是处理应用的更新情况 这部分具体原理后面文字说明即可 ****//
if((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
if (ps != null &&!ps.codePath.equals(ps.resourcePath)) {
parseFlags |= PackageParser.PARSE_FORWARD_LOCK;
}
}
pkg.applicationInfo.setCodePath(pkg.codePath);
pkg.applicationInfo.setBaseCodePath(pkg.baseCodePath);
pkg.applicationInfo.setSplitCodePaths(pkg.splitCodePaths);
pkg.applicationInfo.setResourcePath(resourcePath);
pkg.applicationInfo.setBaseResourcePath(baseResourcePath);
pkg.applicationInfo.setSplitResourcePaths(pkg.splitCodePaths);
// ********** 上面是完善pkg对象的信息**************************//
PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags,scanFlags
|SCAN_UPDATE_SIGNATURE, currentTime, user);
// 如果date分区有新的应用安装,这里直接disable不需要的系统应用
if (shouldHideSystemApp){
synchronized (mPackages){mSettings.disableSystemPackageLPw(pkg.packageName);
}
}
returnscannedPkg;
}
这个函数主要的功能就是解析apk文件,然后根据解析出来的apk内容,处理apk更新的问题,然后完善应用的信息,调用另一个scanPackageLI继续处理。
这个函数主要是处理应用自身的关系,包括解析获取应用的详细信息以及处理应用自身更新的问题,其中应用更新主要涉及到两方方面,首先会检查应用是否设置了original-package属性,这个属性是在应用更新的同时改变了应用的包名的时候使用的,目前实际使用中比较少,在此不做详细分析,其次就是应用可能在date分区中已经安装过了,对于这个情况系统会首先判断系统分区的应用与data分区应用的签名是否一致,如果不一致,就会直接删除data分区的应用,如果签名一致,会根据应用的版本进行判断,如果系统分区应用的版本号低于date分区的应用版本号,就需要disable掉系统分区的,如果系统分区的比较高,就说明系统分区的应用在OTA升级过程中有更新,这里就会删除data分区的应用。
这里我们看下另一个scanPackageLI函数的具体实现,这函数非常长,其中的任务也比较复杂,其主要是对之前解析出来的apk信息进行处理,使其公有化,这样其他应用就可以查询到所安装应用的相关信息。
private PackageParser.PackagescanPackageDirtyLI(PackageParser.Package pkg, int parseFlags,{
if ((parseFlags&PackageParser.PARSE_IS_SYSTEM)!= 0) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
} else{pkg.coreApp = false;}
if((parseFlags&PackageParser.PARSE_IS_PRIVILEGED) != 0) {
pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
}
// 对framework-res.apk 进行单独处理,这个应用是运行的SystemServer中的
if(pkg.packageName.equals("android")) {
synchronized(mPackages) {
mPlatformPackage = pkg;
pkg.mVersionCode = mSdkVersion;
mAndroidApplication = pkg.applicationInfo;
if(!mResolverReplaced) {
mResolveActivity.applicationInfo = mAndroidApplication;
mResolveActivity.name = ResolverActivity.class.getName();
mResolveActivity.packageName = mAndroidApplication.packageName;
mResolveActivity.processName = "system:ui";
mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
mResolveActivity.documentLaunchMode =ActivityInfo.DOCUMENT_LAUNCH_NEVER;
mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
mResolveActivity.theme = R.style.Theme_Holo_Dialog_Alert;
mResolveActivity.exported = true;
mResolveActivity.enabled = true;
mResolveInfo.activityInfo = mResolveActivity;
mResolveInfo.priority = 0;
mResolveInfo.preferredOrder= 0;
mResolveInfo.match = 0;
mResolveComponentName = new ComponentName(
mAndroidApplication.packageName, mResolveActivity.name);
}
}
}
// Initializepackage source and resource directories
FiledestCodeFile = new File(pkg.applicationInfo.getCodePath());
FiledestResourceFile = new File(pkg.applicationInfo.getResourcePath());
// 获取当前解析出来的应用之前的PackageSetting,如果之前的没有,
//就新创建一个PackageSetting对象,并使用解析出来的信息进行填充。
pkgSetting =mSettings.getPackageLPw(pkg, origPackage, realName, suid, destCodeFile,
destResourceFile, pkg.applicationInfo.nativeLibraryRootDir,
pkg.applicationInfo.primaryCpuAbi,
pkg.applicationInfo.secondaryCpuAbi,
pkg.applicationInfo.flags, pkg.applicationInfo.privateFlags,
user,false);
// 如果是新安装的应用,而且含有providers需要判断它的providers是否已经存在,如果已经存在则安装失败
if ((scanFlags & SCAN_NEW_INSTALL) != 0) {
final int N =pkg.providers.size();
int i;
for (i=0;i<N; i++) {
PackageParser.Provider p = pkg.providers.get(i);
if(p.info.authority != null) {
Stringnames[] = p.info.authority.split(";");
for (int j =0; j < names.length; j++) {
if(mProvidersByAuthority.containsKey(names[j])) {
// 抛出异常
}}}}}
//确定运行该package的进程名,一般用package作为进程名
pkg.applicationInfo.processName = fixProcessName(
pkg.applicationInfo.packageName,
pkg.applicationInfo.processName,
pkg.applicationInfo.uid);
//给应用在data/data 目录下面创建资源文件目录,目的就是初始化 pkg.applicationInfo.dataDir 变量
File dataPath;
//该函数返回data/data/packageName
dataPath =Environment.getDataUserPackageDirectory(pkg.volumeUuid,
UserHandle.USER_OWNER, pkg.packageName);
if(dataPath.exists()) {
intcurrentUid = 0;
try {
//使用os.stat是将文件的相关属性读出来,然后调用stat.st_uid获取当前目录应用的uid
StructStat stat = Os.stat(dataPath.getPath());
currentUid = stat.st_uid;
} catch(ErrnoException e) {}
//这里需要给刚刚扫描得到的应用创建data/data/资源目录,结果因为手机中已经有这个目录了,因此这里需要判断这个目录是否是这个应用之前创建的。
// 如果不是 这里就比较麻烦了,这里就是对这个目录所有者的问题处理
if(currentUid != pkg.applicationInfo.uid) {
} else {//如果目录不存在就调用createDataDirsLI创建一个
// 该方法调用installer发送install命令,其实就是在/data/data/目录下建立packageName目录
// 然后为系统所有的user安装此apk
int ret =createDataDirsLI(pkg.volumeUuid, pkgName, pkg.applicationInfo.uid,
pkg.applicationInfo.seinfo);
}
// 这里进行判断,如果应用是在data分区,就设置它的so库文件路径为 data/app/包名/lib/ 下面。
if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
// 创建lib库的路径,并将相应的lib文件复制过去,然后后面会调用Installd的方法,
// 在data/data/apk相关目录 下的lib 创建软链接到真正放lib的地方。
derivePackageAbi(pkg, scanFile,cpuAbiOverride, true /* extract libs */);
if(isSystemApp(pkg) && !pkg.isUpdatedSystemApp() &&
pkg.applicationInfo.primaryCpuAbi == null) {
setBundledAppAbisAndRoots(pkg, pkgSetting);
setNativeLibraryPaths(pkg);
}
}
final int[] userIds = sUserManager.getUserIds();
synchronized(mInstallLock) {
// 为所有的用户创建应用的数据目录
if(!TextUtils.isEmpty(pkg.volumeUuid)) {
for (intuserId : userIds) {
if (userId !=0) {
mInstaller.createUserData(pkg.volumeUuid, pkg.packageName,
UserHandle.getUid(userId, pkg.applicationInfo.uid), userId,
pkg.applicationInfo.seinfo);
}
}
}
}
// 默认对其进行odex优化
if ((scanFlags& SCAN_NO_DEX) == 0) {
//对该APK做dex优化
int result =mPackageDexOptimizer.performDexOpt(pkg, null,
forceDex,(scanFlags & SCAN_DEFER_DEX) != 0, false);
}
// writer
synchronized(mPackages) {
// 讲当前应用添加到mSettings中
mSettings.insertPackageSettingLPw(pkgSetting, pkg);
// Add the newsetting to mPackages
// 将当前应用添加到mPackages变量中
mPackages.put(pkg.applicationInfo.packageName, pkg);
}
// 将扫描到的应用的四大组件公有化
int N =pkg.providers.size();
StringBuilder r= null;
for (i=0;i<N; i++) {// 处理providers组件}
N =pkg.services.size();
for (i=0;i<N; i++) {// 处理应用的服务组件,使其公有化}
N =pkg.receivers.size();
for (i=0;i<N; i++) { // 处理广告组件,使其公有化}
N =pkg.activities.size();
for (i=0;i<N; i++) {//处理Activity}
N =pkg.permissionGroups.size();
for (i=0;i<N; i++) {//处理权限组}
N =pkg.permissions.size();
for (i=0;i<N; i++) {//处理权限问题}
return pkg;
}
这个函数代码量非常大,大约一千行左右,主要实现以下几个功能
1)处理包名为Android的应用
2)初始化Package的数据目录和资源目录;
3)验证新安装的APK中的Provider是否与系统中现有的Provider有冲突;
4)如果新安装APK需要使用其他Package的权限,则进行相应处理;
5)设置新安装应用的进程名称
6)调用createDataDirsLI()安装APK;
7)设置本地lib路径;
8)对应用进行odex优化
9)将Package信息更新到PMS和Setting相应的数据结构中;
10)设置APK安装时间;
11)公有化应的组件,使其添加到相应的组件信息列表中
该函数的主要工作便是将安装的APK的信息添加到PMS中,比如讲Provider、Activity、Service、Receiver等组件信息添加到相应的数据结构中,以便其他函数能够查询到。
在该函数中还对framework-res.apk进行特殊的信息处理。framework-res.apk中主要包含以下信息:
几个常用的Activity:ChooserActivity、ShutdownActivity、RingtonePckerActivity;
framework-res.apk与PMS联系紧密,其中PMS中的mPlatformPackage成员存储该Package信息;mAndroidApplicatioin保存该Package的ApplicationInfo信息;mResolveActivity表示ChooserActivity信息的ActivityInfo;mResolveInfo存储系统解析的Intent后得到的结果信息。
2.5:parsePackage函数分析
PackageParser类中的parsePackage是进行apk文件解析的最核心的函数,它通过对apk文件的解析,获取应用的所有信息,然后保存到Package变量中返回给调用者。这里我们就分析下parsePackage具体实现流程。
public PackageparsePackage(File packageFile, int flags) throws PackageParserException {
if (packageFile.isDirectory()) {
return parseClusterPackage(packageFile,flags);
} else {
return parseMonolithicPackage(packageFile,flags);
}}
这里进行判断,根据是不是目录,调用不用的函数,这里我们直接看parseMonolithicPackage这个函数,因为解析目录的函数最终还是会解析文件的。
public PackageparseMonolithicPackage(File apkFile, int flags) throws PackageParserException {
//创建AssetManager对象,
final AssetManager assets = newAssetManager();
try {// 调用parseBaseApk解析apk文件并返回Package对象
final Package pkg = parseBaseApk(apkFile,assets, flags);
pkg.codePath = apkFile.getAbsolutePath();
return pkg;
} finally {IoUtils.closeQuietly(assets);}
}
private PackageparseBaseApk(File apkFile, AssetManager assets, int flags){
final String apkPath =apkFile.getAbsolutePath();
mParseError = PackageManager.INSTALL_SUCCEEDED;
mArchiveSourcePath =apkFile.getAbsolutePath();
调用loadApkIntoAssetManager,执行assets.addAssetPath(apkPath)
final int cookie =loadApkIntoAssetManager(assets, apkPath, flags);
Resources res = null;
XmlResourceParser parser = null;
try {// 创建Resources对象
res = new Resources(assets, mMetrics,null);
assets.setConfiguration(0, 0, null, 0, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
Build.VERSION.RESOURCES_SDK_INT);
// 这里会调用到native的方法,来打开应用的AndroidManifest.xml配置文件
parser =assets.openXmlResourceParser(cookie,"AndroidManifest.xml");
final String[] outError = new String[1];
// 调用另一个版本的parseBaseApk进一步解析apk文件
final Package pkg = parseBaseApk(res,parser, flags, outError);
if (pkg == null) {throw newPackageParserException(***);}
return pkg;
}finally {IoUtils.closeQuietly(parser);}
}
这个函数执行AssetManager的方法,打开了应用的配置文件AndroidManifest.xml,然后调用另一个重载的parseBaseApk进一步解析apk文件。
private PackageparseBaseApk(Resources res, XmlResourceParser parser, int flags {
final boolean trustedOverlay = (flags &PARSE_TRUSTED_OVERLAY) != 0;
AttributeSet attrs = parser;
try {// 调用parsePackageSplitNames解析应用的包名
Pair<String, String> packageSplit =parsePackageSplitNames(parser, attrs, flags);
pkgName = packageSplit.first;
splitName = packageSplit.second;
} catch (PackageParserException e) {returnnull;}
// 获取应用的基本信息。然后填充到pkg变量中
TypedArray sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifest);
pkg.mVersionCode =pkg.applicationInfo.versionCode = sa.getInteger(
com.android.internal.R.styleable.AndroidManifest_versionCode, 0);
pkg.baseRevisionCode = sa.getInteger(
com.android.internal.R.styleable.AndroidManifest_revisionCode, 0);
pkg.mVersionName =sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifest_versionName, 0);
if (pkg.mVersionName != null){pkg.mVersionName = pkg.mVersionName.intern();}
pkg.installLocation = sa.getInteger(
com.android.internal.R.styleable.AndroidManifest_installLocation,
PARSE_DEFAULT_INSTALL_LOCATION);
pkg.applicationInfo.installLocation =pkg.installLocation;
pkg.coreApp =attrs.getAttributeBooleanValue(null, "coreApp", false);
sa.recycle();
while ((type = parser.next()) !=XmlPullParser.END_DOCUMENT
&& (type !=XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
String tagName = parser.getName();
if(tagName.equals("application")) {
if (!parseBaseApplication(pkg, res,parser, attrs, flags, outError)) {
return null;
}
} else if(tagName.equals("uses-permission")) {
if (!parseUsesPermission(pkg, res,parser, attrs)) {
return null;}
}
**********}
return pkg;
}
重载的parseBaseApk函数首先调用parsePackageSplitNames解析应用程序的包名,然后调用obtainAttributes对应用进行基本的解析获取应用的一些基本信息,然后就是通过whilse循环,循环检查"AndroidManifest.xml"中的每一个标签,然后对其进行解析。这里标签非常多删除了后面部分。这里我们主要看下解析application标签的这个函数。
private boolean parseBaseApplication(Packageowner, Resources res {
final ApplicationInfo ai =owner.applicationInfo;
final String pkgName =owner.applicationInfo.packageName;
TypedArray sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestApplication);
// 删除了部分代码,这部分主要是根据sa的信息对应用信息进一步进行完善
final int innerDepth = parser.getDepth();
int type;
while ((type = parser.next()) !=XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG|| parser.getDepth() > innerDepth)) {
if (type == XmlPullParser.END_TAG || type ==XmlPullParser.TEXT) {
continue;
}
String tagName = parser.getName();
if (tagName.equals("activity")) {//解析activity标签,然后将其添加到应用的activities中
Activity a = parseActivity(owner, res,parser, attrs, flags, outError, false,
owner.baseHardwareAccelerated);
owner.activities.add(a);
} else if(tagName.equals("receiver")) {//解析receiver
Activity a = parseActivity(owner, res,parser, attrs, flags, outError, true, false);
owner.receivers.add(a);
} else if(tagName.equals("service")) {// 解析service
Service s = parseService(owner, res, parser,attrs, flags, outError);
owner.services.add(s);
} else if(tagName.equals("provider")) {// 解析provider
Provider p = parseProvider(owner, res,parser, attrs, flags, outError);
owner.providers.add(p);
}
删除部分代码******}
return true;}
这部分代码首先对应用进行一些初步的初始化,然后使用while循环,分别对应用的application标签里面的每个组件进行解析,然后将其添加到应用信息的相应数据中。至此apk的解析部分就分析完了,这里只是apk解析部分的主要流程,其中很多细枝末叶的地方都未涉及到,这部分还请读者自行学习。
3: PMS的systemReady函数分析
前面我们分析系统启动的时候,提到在SystemServer的最后会回调很多服务的systemReady函数进行系统就绪前最后的设置工作,这里就包含了PMS的systemReady函数,这里我们就看下PMS的systemReady具体都做了哪些事情。
try{mPackageManagerService.systemReady();
} catch (Throwablee) {}
public voidsystemReady() {
mSystemReady = true;
int[] grantPermissionsUserIds =EMPTY_INT_ARRAY;
for (int userId :UserManagerService.getInstance().getUserIds()) {
if(!mSettings.areDefaultRuntimePermissionsGrantedLPr(userId)) {
grantPermissionsUserIds =ArrayUtils.appendInt(
grantPermissionsUserIds, userId);}
}
// 根据用户,设置各个应用的默认权限情况
for (int userId : grantPermissionsUserIds) {
mDefaultPermissionPolicy.grantDefaultPermissions(userId);
}
// 处理在初始化过程中保存的,需要在系统就绪之后才能处理的操作
if (mPostSystemReadyMessages != null) {
for (Message msg : mPostSystemReadyMessages){
msg.sendToTarget();}
mPostSystemReadyMessages = null;
}
// Watch for external volumes that come and goover time
// 注册存储器的监听,来监听外部存储的挂载
final StorageManager storage =mContext.getSystemService(StorageManager.class);
storage.registerListener(mStorageListener);
// 调用其他服务的systemReady
sUserManager.systemReady();
mInstallerService.systemReady();
mPackageDexOptimizer.systemReady();
}
PMS的systemReady函数首先检查用户情况,根据用户id调用grantDefaultPermissions函数,给各个用户下面的应用设置默认权限,然后处理在前期应用安装过程中遗留的需要处理的问题,这里主要是在应用安装过程中,删除安装失败的data分区的应用的操作,接着会注册一个监听,来监听外部存储设备的变化,最后调用其他函数的systemReady函数。这里我们着重看下grantDefaultPermissions函数数如何设置应用的默认权限的,这部分后续我们可能会经常用到,这个函数在DefaultPermissionGrantPolicy.java中。
public voidgrantDefaultPermissions(int userId) {
//针对系统组件和Private的应用做默认权限的处理
grantPermissionsToSysComponentsAndPrivApps(userId);
//对符合系统处理原则的模块进行默认权限的处理
grantDefaultSystemHandlerPermissions(userId);
}
这个函数中仅仅调用了两个函数,其中第一个函数主要处理系统核心应用的权限问题,因为有些系统核心的应用需要在系统启动过程中启动,而且在系统运行的整个过程中都在系统后台运行,因此这些应用的权限需要默认授予,使其能够正常运行。第二个函数主要是针对系统默认应用的权限授予,比如短信就默认拥有短信的权限,相机就默认拥有相机的权限等等,因为这些应用是手机某个功能的默认应用,因此需要根据应用的功能授予相应的权限,下面我们下看下第一个函数的具体实现。
private voidgrantPermissionsToSysComponentsAndPrivApps(int userId) {
synchronized (mService.mPackages) {
for (PackageParser.Package pkg :mService.mPackages.values()) {
// 如果这个应用是普通应用,或者不支持运行时权限,或者申请的权限为空都直接跳过
if(!isSysComponentOrPersistentPlatformSignedPrivAppLPr(pkg)
|| !doesPackageSupportRuntimePermissions(pkg)
|| pkg.requestedPermissions.isEmpty()) {
continue;
}
// 获取应用的权限信息,然后添加到变量 permissions
Set<String> permissions = newArraySet<>();
final int permissionCount =pkg.requestedPermissions.size();
for (int i = 0; i < permissionCount; i++){
String permission =pkg.requestedPermissions.get(i);
BasePermission bp =mService.mSettings.mPermissions.get(permission);
if (bp != null && bp.isRuntime()) {
permissions.add(permission);
}}
if (!permissions.isEmpty()) {
grantRuntimePermissionsLPw(pkg,permissions, true, userId);
}
}}}
这个函数直接对系统中的应用进行遍历,进行筛选,选出uid小于10000的应用,以及在system/priv-app目录下面,并且采用系统签名,需要一直在后台运行的,或者不支持动态权限的应用,然后将其所申请的权限读取出来,添加到permissions变量中,然后调用grantRuntimePermissionsLPw对应用进行权限授予。这里我们先看下对于核心应用的判断函数的实现,
// 如果是udi小于10000,或者是system/priv-app目录下面的并且是系统签名,就返回true,
private booleanisSysComponentOrPersistentPlatformSignedPrivAppLPr(PackageParser.Package pkg) {
// 如果app的UID小于10000,直接返回true
if (UserHandle.getAppId(pkg.applicationInfo.uid)< FIRST_APPLICATION_UID) {
return true;
}
// 除了system/priv-app 下面的应用,其他的都返回false
if (!pkg.isPrivilegedApp()) {return false;}
// 如果这个应用是disable的应用,返回false,如果应用不是一直在后台运行的应用,返回false
PackageSetting sysPkg = mService.mSettings.getDisabledSystemPkgLPr(pkg.packageName);
if (sysPkg != null) {
if ((sysPkg.pkg.applicationInfo.flags &ApplicationInfo.FLAG_PERSISTENT) == 0) {
return false;}
} else if ((pkg.applicationInfo.flags &ApplicationInfo.FLAG_PERSISTENT) == 0) {
return false;}
// 如果这个应用是system/priv-app目录下面的 而且是Platform签名就返回true。
returnPackageManagerService.compareSignatures(mService.mPlatformPackage.mSignatures,
pkg.mSignatures) ==PackageManager.SIGNATURE_MATCH;
}
上面这边函数的内容比较直接,首先判断应用的uid,如果uid小于10000,就直接返回true,如果不是就对其进行判断,只有在system/priv-app目录下面的而且是一直在后台运行的,使用了系统签名的应用才会返回true。
private voidgrantRuntimePermissionsLPw(PackageParser.Package pkg, Set<String>
permissions,boolean systemFixed, booleanoverrideUserChoice,int userId) {
List<String> requestedPermissions =pkg.requestedPermissions;
//获取应用所申请的权限的数量,然后使用循环申请每一个权限。
final int grantablePermissionCount =requestedPermissions.size();
for (int i = 0; i <grantablePermissionCount; i++) {
String permission =requestedPermissions.get(i);
if (permissions.contains(permission)) {
//调用PMS的grantRuntimePermission处理mService是在初始化的时候传递的PMS实例
mService.grantRuntimePermission(pkg.packageName, permission, userId);
mService.updatePermissionFlags(permission,pkg.packageName,
newFlags, newFlags, userId);
}}}}
这个函数获取应用所需要授予的权限数量循环调用PMS的grantRuntimePermission函数对应用当前的权限进行授予PMS的这个函数中最后会调用permissionsState.grantRuntimePermission函数,使得应用的相应权限被打开。
private voidgrantDefaultSystemHandlerPermissions(int userId) {
// 定义默认的应用信息
final PackagesProvider imePackagesProvider; //默认输入法
final PackagesProviderlocationPackagesProvider; // 定位服务
final PackagesProvidervoiceInteractionPackagesProvider;
final PackagesProvidersmsAppPackagesProvider; // 短信
final PackagesProviderdialerAppPackagesProvider; // 拨号盘
final PackagesProvidersimCallManagerPackagesProvider; // sim 卡管理
final SyncAdapterPackagesProvidersyncAdapterPackagesProvider; // 日历同步
// 初始化默认的应用信息
synchronized (mService.mPackages) {
imePackagesProvider = mImePackagesProvider;
locationPackagesProvider =mLocationPackagesProvider;
voiceInteractionPackagesProvider =mVoiceInteractionPackagesProvider;
smsAppPackagesProvider =mSmsAppPackagesProvider;
dialerAppPackagesProvider = mDialerAppPackagesProvider;
simCallManagerPackagesProvider =mSimCallManagerPackagesProvider;
syncAdapterPackagesProvider =mSyncAdapterPackagesProvider;
}
// 给系统中各个默认的应用分配权限
synchronized (mService.mPackages) {
// Installer 给PackageInstall默认授予读写存储设备的权限
PackageParser.Package installerPackage =getSystemPackageLPr(
mService.mRequiredInstallerPackage);
if (installerPackage != null
&&doesPackageSupportRuntimePermissions(installerPackage)) {
grantRuntimePermissionsLPw(installerPackage,STORAGE_PERMISSIONS, true, userId);
}
// SetupWizard 开机向导
Intent setupIntent = newIntent(Intent.ACTION_MAIN);
setupIntent.addCategory(Intent.CATEGORY_SETUP_WIZARD);
PackageParser.Package setupPackage =getDefaultSystemHandlerActivityPackageLPr(
setupIntent, userId);
if (setupPackage != null
&&doesPackageSupportRuntimePermissions(setupPackage)) {
grantRuntimePermissionsLPw(setupPackage,PHONE_PERMISSIONS, userId);
grantRuntimePermissionsLPw(setupPackage,CONTACTS_PERMISSIONS, userId);
}
// Camera 相机
Intent cameraIntent = newIntent(MediaStore.ACTION_IMAGE_CAPTURE);
PackageParser.Package cameraPackage =getDefaultSystemHandlerActivityPackageLPr(
cameraIntent, userId);
if (cameraPackage != null
&&doesPackageSupportRuntimePermissions(cameraPackage)) {
grantRuntimePermissionsLPw(cameraPackage, CAMERA_PERMISSIONS, userId);
grantRuntimePermissionsLPw(cameraPackage,MICROPHONE_PERMISSIONS, userId);
grantRuntimePermissionsLPw(cameraPackage, STORAGE_PERMISSIONS, userId);
}
后面的删除}
}
这个函数比较简单,首先就是记录系统中各个默认的应用,然后分别获取系统中各项功能的默认应用,给其分配相应的权限。
至此PMS的应用默认权限授予流程就分析完了。
4: 三方应用的安装流程梳理
通过分析PackageInstall的源码我们发现,用户安装应用,最终是在InstallAppProgress.java的initView函数中调用ApplicationPackageManager.java的installPackageWithVerificationAndEncryption函数进行用户应用的安装,下面我们就以这个函数为线索,分析下三方应用的安装过程。
InstallAppProgress.java的initView函数中,调用安装应用接口
pm.installPackageWithVerificationAndEncryption(mPackageURI,observer, installFlags,
installerPackageName, verificationParams,null);
installPackageWithVerificationAndEncryption这个函数的实现是在ApplicationPackageManager.java中
public voidinstallPackageWithVerificationAndEncryption(Uri packageURI,
IPackageInstallObserver observer, int flags,String installerPackageName,
VerificationParams verificationParams,ContainerEncryptionParams encryptionParams) {
installCommon(packageURI, newLegacyPackageInstallObserver(observer), flags,
installerPackageName,verificationParams, encryptionParams);
}
这里直接调用installCommon函数继续处理
private voidinstallCommon(Uri packageURI,****** encryptionParams) {
final String originPath =packageURI.getPath();
try {// 这里直接调用了PMS中的installPackage进行应用安装
mPM.installPackage(originPath,observer.getBinder(), flags, installerPackageName,
verificationParams, null);
} catch (RemoteException ignored) {
}
}
通过前面的调用流程,这里函数流程走到了PMS中,根据前面的流程,我们知道,这里调用了PMS的installPackage函数
public voidinstallPackage(String originPath, ********) {
installPackageAsUser(originPath, observer, ………UserHandle.getCallingUserId());
}
查看PMS的installPackage函数,它直接调用了installPackageAsUser函数
public voidinstallPackageAsUser(String originPath, IPackageInstallObserver2 observer,
int installFlags, StringinstallerPackageName, VerificationParams verificationParams,
String packageAbiOverride, int userId) {
//查看调用者是否具有应用安装权限
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES,null);
final int callingUid = Binder.getCallingUid();
enforceCrossUserPermission(callingUid, userId,true, true, "installPackageAsUser");
// 查看当前用是否是受限制的用户
if (isUserRestricted(userId,UserManager.DISALLOW_INSTALL_APPS)) {
try {//如果当前用户是受限制的用户,就无法安装应用,这里设置安装失败的信息之后返回
if (observer !=null){observer.onPackageInstalled("", INSTALL_FAILED_USER_RESTRICTED,null, null);}
} catch (RemoteException re) {}
return;
}
// 调整应用安装的installFlags,查看是否是从adb进行安装的
if ((callingUid == Process.SHELL_UID) ||(callingUid == Process.ROOT_UID)) {
installFlags |=PackageManager.INSTALL_FROM_ADB;
} else {installFlags &=~PackageManager.INSTALL_FROM_ADB;
installFlags &=~PackageManager.INSTALL_ALL_USERS;}
//查看是仅仅给当前用户安装,还是给所有用户安装
UserHandle user;
if ((installFlags &PackageManager.INSTALL_ALL_USERS) != 0) {
user = UserHandle.ALL;
} else {
user = new UserHandle(userId);
}
//构建应用安装需要的参数,然后发送INIT_COPY开始安装流程
verificationParams.setInstallerUid(callingUid);
final File originFile = new File(originPath);
final OriginInfo origin =OriginInfo.fromUntrustedFile(originFile);
final Message msg =mHandler.obtainMessage(INIT_COPY);
msg.obj = new InstallParams(origin, null,observer, installFlags, installerPackageName,
null, verificationParams, user,packageAbiOverride, null);
mHandler.sendMessage(msg);
}
installPackageAsUser函数的代码比较清晰,这里首先是权限检查,确保应用安装的安全性,然后确认应用是否是通过usb进行安装的,然后设置应用的安装范围,是仅仅给当前用户安装,还是给所有的用户都安装。完成以上检查之后,初始化必要的参数,然后发送INIT_COPY的异步消息,正式开始应用的安装。
void doHandleMessage(Messagemsg) {
switch (msg.what) {
case INIT_COPY: {//获取参数
HandlerParams params = (HandlerParams)msg.obj;
int idx = mPendingInstalls.size();// 获取等待安装应用的列表
if (!mBound) {//检查安装服务是否已经连接成功
//尝试连接服务,如果服务连接成功,会自动发送MCS_BOUND消息
if (!connectToService()) {
params.serviceError();return;
} else {
//将需要安装的应用添加到待安装应用列表
mPendingInstalls.add(idx, params);}
} else {
mPendingInstalls.add(idx, params);
if (idx == 0) {// 发送MCS_BOUND消息,开始安装
mHandler.sendEmptyMessage(MCS_BOUND);
}
}break;}
case MCS_BOUND: {
//这里对于mContainerService进行检查,如果这个服务没有进行链接,就等待其链接,如果这个服务链接失败,就直接循环,设置错误信息,并清除待安装的应用列表
if (mContainerService == null) {
if (!mBound) {
for (HandlerParams params :mPendingInstalls) {
params.serviceError();
}
mPendingInstalls.clear();
} else {
Slog.w(TAG, "Waiting to connect tomedia container service");
}
} else if (mPendingInstalls.size() > 0) {
HandlerParams params =mPendingInstalls.get(0);
if (params != null) {
// 从列表中获取待安装的应用信息,调用startCopy进行安装
if (params.startCopy()) {
// 当前应用安装完成之后,将其从待安装列表中删除
if (mPendingInstalls.size() > 0) {
mPendingInstalls.remove(0);
}
// 如果待安装列表为空,说明目前没有安装请求,发送十秒的延时,循环检查
if (mPendingInstalls.size() == 0) {
if (mBound) {
removeMessages(MCS_UNBIND);
Message ubmsg =obtainMessage(MCS_UNBIND);
sendMessageDelayed(ubmsg, 10000);}
} else {
// 如果待安装列表不为空,表示还有等待安装的应用,这里发送消息继续安装
mHandler.sendEmptyMessage(MCS_BOUND);
}
}
}} else {Slog.w(TAG, "Emptyqueue");}
break;}
上面贴出来的是doHandleMessage对于INIT_COPY和MCS_BOUND两个消息的处理,从消息的处理过程中,我们可以看到,这两个消息是紧密链接的。首先在INIT_COPY会获取前面传递的参数,然后检查mContainerService服务有没有绑定成功,如果没有,进进行绑定,并将需要安装的应用的所有参数信息添加到待安装的列表中。如果服务已经连接成功,就在将应用的安装信息添加到待安装列表之后直接发送MCS_BOUND消息开始安装。这里大家是不是会产生个疑问,如果服务没有绑定成功,是不是应用的安装过程就在这里停止了呢,其实不是的,如果服务没有绑定成功,那么在服务绑定成功的回调函数中,就会发送MCS_BOUND消息,进行应用的继续安装操作,因此这里是不会停止的。
下面我们看下MCS_BOUND消息,这个消息中首先是确保服务连接成功,如果这个服务没有进行链接,就等待其链接,如果这个服务连接失败,就直接循环,设置错误信息,并清除待安装的应用列表,如果服务绑定成功,就直接调用参数的startCopy函数进行应用的安装操作,待应用安装完成之后,将其从待安装应用的列表中删除,然后判断待安装应用的列表是否为空,如果为空,表示当前没有应用等待安装,就发送十秒的延时循环检查,如果不为空,表示还有应用等待安装,就直接发送MCS_BOUND消息进行下一轮的安装。
上面的两个消息中,都提到一个mContainerService服务,从代码流程来看,这个服务非常重要,直接影响到应用的安装,那么这个应用具体是干啥的呢,经过确认,这个应用的源码在frameworks\base\packages\DefaultContainerService下,是一个多媒体相关的后台服务,在三方应用的安装过程中,会调用这个服务的函数进行应用资源的复制操作。这个服务后面分析中还会遇到,遇到之后在详细分析其具体实现。
通过上面的分析,我们知道,应用的安装过程是调用了params.startCopy函数实现的,下面我们就看下这个函数的具体安装过程。
final boolean startCopy(){
boolean res;
try {// 检查当前同时安装的应用是否大于约定的任务,如果大于则放弃安装
if (++mRetries > MAX_RETRIES) {
mHandler.sendEmptyMessage(MCS_GIVE_UP);
handleServiceError();
return false;
} else {这里调用handleStartCopy进行安装
handleStartCopy();
res = true;
}
} catch (RemoteException e) {res = false;}
handleReturnCode();// 然后调用handleReturnCode继续安装
return res;
}
这个函数首先会检查当前正在进行安装的应用的数量,如果大于四个,这里直接返回,如果正常会首先调用handleStartCopy函数进行安装,完成之后,会接着调用handleReturnCode完成后续的安装过程。下面我们下看下handleStartCopy的执行过程。
public voidhandleStartCopy() throws RemoteException {
int ret = PackageManager.INSTALL_SUCCEEDED;
//根据参数决定是安装在手机内还是sdcard中,设置对应标志位
if (origin.staged) {
if (origin.file != null) {
installFlags |= PackageManager.INSTALL_INTERNAL;
installFlags &=~PackageManager.INSTALL_EXTERNAL;
} else if (origin.cid != null) {
installFlags |=PackageManager.INSTALL_EXTERNAL;
installFlags &=~PackageManager.INSTALL_INTERNAL;
} else {
throw newIllegalStateException("Invalid stage location");
}
}
final boolean onSd = (installFlags &PackageManager.INSTALL_EXTERNAL) != 0;
final boolean onInt = (installFlags &PackageManager.INSTALL_INTERNAL) != 0;
PackageInfoLite pkgLite = null;
if (onInt && onSd) {ret =PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
} else {//获取安装包包含有应用信息的PackageInfoLite对象
pkgLite =mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags,
packageAbiOverride);
// 如果空间不足,先释放缓存
if (!origin.staged &&pkgLite.recommendedInstallLocation
==PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
// 删除cache
if (mInstaller.freeCache(null, sizeBytes +lowThreshold) >= 0) {
pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath,
installFlags,packageAbiOverride);
}
if (pkgLite.recommendedInstallLocation
==PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
pkgLite.recommendedInstallLocation
= PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
}
}}
// 根据前面获取到的应用基本信息,进行检查,查看应用是否可以正常安装
if (ret == PackageManager.INSTALL_SUCCEEDED) {
int loc = pkgLite.recommendedInstallLocation;
if (loc ==PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION) {
ret =PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
} else if (loc ==PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS) {
ret =PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
} else if (loc ==PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
ret =PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
} else if (loc ==PackageHelper.RECOMMEND_FAILED_INVALID_APK) {
ret =PackageManager.INSTALL_FAILED_INVALID_APK;
} else if (loc ==PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
ret =PackageManager.INSTALL_FAILED_INVALID_URI;
} else if (loc ==PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE) {
ret =PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE;
} else {
loc = installLocationPolicy(pkgLite);
if (loc == PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE){
ret =PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
} else if (!onSd && !onInt) {
//如果应用没有指定安装位置,这里根据系统情况确定安装位置
if (loc ==PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {
installFlags |=PackageManager.INSTALL_EXTERNAL;
installFlags &=~PackageManager.INSTALL_INTERNAL;
} else {
installFlags |=PackageManager.INSTALL_INTERNAL;
installFlags &=~PackageManager.INSTALL_EXTERNAL;
}
}}}
//根据安装路径不同,创建不同的InstallArgs对象
final InstallArgs args =createInstallArgs(this);
mArgs = args;
if (ret == PackageManager.INSTALL_SUCCEEDED) {
ret = args.copyApk(mContainerService, true);
} } mRet = ret;
handleStartCopy函数是应用安装流程的一个很重要的函数,主要进行应用安装初期的相关检查,确保应用可以正常安装,它首先检查应用有没有设置默认的安装位置,确保默认安装位置是正确的,然后调用mContainerService服务的getMinimalPackageInfo函数,获取应用的基本信息,这个函数中会调用到PackageParser中的parsePackageLite函数对应用进行预解析,获取应用的基本信息。然后根据前面的信息,判断手机存储空间是否足够,如果手机存储空间不足就尝试调用install服务的freeCache清楚缓存,如果清楚缓存之后,空间足够,就继续安装。紧接着,会对应用的各种安装信息进行检查,确认无误之后,调用createInstallArgs函数创建InstallArgs对象,这个函数会根据应用的安装位置创建不同的InstallArgs对象。然后就是调用args.copyApk函数进行应用的安装。这里我们先看下createInstallArgs函数,然后在看copyApk函数。
private InstallArgscreateInstallArgs(InstallParams params) {
if (installOnExternalAsec(params.installFlags)|| params.isForwardLocked()) {
return new AsecInstallArgs(params); //安装在外部存储空间上
} else { return new FileInstallArgs(params);// 安装在内部存储空间 }
这个函数非常简单,就是根据应用的安装位置,创建不同的InstallArgs对象,这里我们重点分析下应用安装到内部存储的情况。下面我们看下FileInstallArgs的copyApk函数。
intcopyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
try {//在/data/app/下生成临时文件
final File tempDir =mInstallerService.allocateStageDirLegacy(volumeUuid);
codeFile = tempDir;resourceFile = tempDir;
} catch (IOException e) {
returnPackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;}
// 设置的回调函数,用来修改目录的权限
final IParcelFileDescriptorFactory target =new IParcelFileDescriptorFactory.Stub() {
@Override
public ParcelFileDescriptor open(Stringname, int mode) throws RemoteException {
try {
final File file = new File(codeFile,name);
final FileDescriptor fd =Os.open(file.getAbsolutePath(),
O_RDWR | O_CREAT, 0644);
Os.chmod(file.getAbsolutePath(), 0644);
return new ParcelFileDescriptor(fd);
} catch (ErrnoException e) {}
}};
int ret = PackageManager.INSTALL_SUCCEEDED;
//使用服务DefaultContainerService的copyPackage()方法复制文件
ret = imcs.copyPackage(origin.file.getAbsolutePath(),target);
if (ret != PackageManager.INSTALL_SUCCEEDED) {
return ret;}
final File libraryRoot = new File(codeFile,LIB_DIR_NAME);
NativeLibraryHelper.Handle handle = null;
try {//复制应用中自带的native的动态库
handle = NativeLibraryHelper.Handle.create(codeFile);
ret =NativeLibraryHelper.copyNativeBinariesWithOverride(handle,libraryRoot,abiOverride);
} catch (IOException e) {
Slog.e(TAG, "Copying native librariesfailed", e);
ret =PackageManager.INSTALL_FAILED_INTERNAL_ERROR;} finally {}
return ret;}
copyApk函数中主要进行了三个操作,首先调用mInstallerService服务的allocateStageDirLegacy,在data/app/目录下面创建一个vmdl+int型的sessionId.tmp目录。然后调用imcs.copyPackage函数对应用的资源进行复制,然后使用NativeLibraryHelper复制应用的so库文件。下面我们看下allocateStageDirLegacy的具体实现。
public File allocateStageDirLegacy(StringvolumeUuid) throws IOException {
synchronized (mSessions) {
try {
final int sessionId =allocateSessionIdLocked();
mLegacySessions.put(sessionId, true);
final File stageDir =buildStageDir(volumeUuid, sessionId);
prepareStageDir(stageDir);
return stageDir;
} catch (IllegalStateException e){throw newIOException(e);}
}}
这个函数首先调用allocateSessionIdLocked获取一个不大于int数字最大数的随机数,然后将其添加到mLegacySessions变量中,确保下次获取的数与这个不重复,然后调用buildStageDir创建一个/data/app/"vmdl" + sessionId +".tmp的目录,在调用prepareStageDir设置其目录的权限。
private intallocateSessionIdLocked() {
int n = 0;int sessionId;
do {sessionId =mRandom.nextInt(Integer.MAX_VALUE - 1) + 1;
if (mSessions.get(sessionId) == null&& mHistoricalSessions.get(sessionId) == null
&& !mLegacySessions.get(sessionId, false)) {
return sessionId;}
} while (n++ < 32);
}
循环获取一个小于int最大数字的随机变量,确保其没有重复的情况下返回一个随机数作为sessionId。
private FilebuildStageDir(String volumeUuid, int sessionId) {
final File stagingDir = Environment.getDataAppDirectory(volumeUuid);
return new File(stagingDir, "vmdl"+ sessionId + ".tmp");}
创建一个data/app/ 下面的vmdl" + sessionId + ".tmp文件。
static voidprepareStageDir(File stageDir) throws IOException {
try {
Os.mkdir(stageDir.getAbsolutePath(), 0755);
Os.chmod(stageDir.getAbsolutePath(), 0755);
} catch (ErrnoException e) {
throw new IOException("Failed toprepare session dir: " + stageDir, e);}
}
创建目录,然后设置目前权限为755
上面我们分析了下allocateStageDirLegacy函数的具体流程,这个函数最终会在data/app/目录下面创建一个临时的文件夹,后续会将应用的资源文件复制到这个文件夹下面。下面我们在看下imcs.copyPackage函数的具体实现。
public intcopyPackage(String packagePath, IParcelFileDescriptorFactory target) {
PackageLite pkg = null;
try {
final File packageFile = newFile(packagePath);
pkg =PackageParser.parsePackageLite(packageFile, 0);
return copyPackageInner(pkg, target);
} catch (PackageParserException | IOException| RemoteException e) {
returnPackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
}}
调用PackageParser的parsePackageLite对应用进行初步解析,然后调用copyPackageInner对应用的资源文件进行复制。
private intcopyPackageInner(PackageLite pkg, IParcelFileDescriptorFactory target){
copyFile(pkg.baseCodePath, target,"base.apk");
if (!ArrayUtils.isEmpty(pkg.splitNames)) {
for (int i = 0; i <pkg.splitNames.length; i++) {
copyFile(pkg.splitCodePaths[i], target,"split_" + pkg.splitNames[i] + ".apk");
}
}
returnPackageManager.INSTALL_SUCCEEDED;}
首先将原始的apk文件复制为base.apk文件,然后调用copyFile对其他资源文件进行复制。
private voidcopyFile(String sourcePath, IParcelFileDescriptorFactory target, StringtargetName)
throws IOException, RemoteException {
InputStream in = null;
OutputStream out = null;
try {in = new FileInputStream(sourcePath);
out = newParcelFileDescriptor.AutoCloseOutputStream(
target.open(targetName,ParcelFileDescriptor.MODE_READ_WRITE));
Streams.copy(in, out); //进行复制
} finally {}}
通过上面的流程,copyPackage函数的具体流程就分析完了,这里主要是将应用的资源文件复制到前面创建的目录下面。
到这里handleStartCopy函数就执行完了,这里主要是先判断应用安装的基本条件确保应用可以正常安装,然后根据应用的不同的安装路径,建立不同的InstallArgs对象,最后调用InstallArgs对象的copyApk函数,创建应用安装目录,并复制应用的跟apk文件以及so库文件。这些流程执行完成之后,调用流程会返回到前面的startCopy函数中,紧接着startCopy函数会调用handleReturnCode函数完成后续的安装。其实三方应用的安装过程主要有两个阶段,首先是进行各种排查,然后给需要安装的应用创建相应的目录,并将需要安装的应用程序资源复制到相应的目录下面。然后就是跟系统应用一样的安装过程,这个安装过程是通过handleReturnCode函数实现的,下面我们就分析下handleReturnCode函数的具体实现流程。
voidhandleReturnCode() {
if (mArgs != null) {// 直接调用processPendingInstall进行安装
processPendingInstall(mArgs, mRet);
}}
private voidprocessPendingInstall(final InstallArgs args, final int currentStatus) {
mHandler.post(new Runnable() {
public void run() {
mHandler.removeCallbacks(this);
PackageInstalledInfo res = newPackageInstalledInfo();
res.returnCode = currentStatus;
res.uid = -1;
res.pkg = null;
res.removedInfo = new PackageRemovedInfo();
if (res.returnCode ==PackageManager.INSTALL_SUCCEEDED) {
args.doPreInstall(res.returnCode);
synchronized (mInstallLock) {
installPackageLI(args, res);
}//如果installPackageLI安装失败,这里就删除前面复制的时候所建立的目录以及文件
args.doPostInstall(res.returnCode,res.uid);
}
// 根据安装情况,发送应用安装完成的广播
Message msg = mHandler.obtainMessage(POST_INSTALL,token, 0);
mHandler.sendMessage(msg);
}
});}
这个函数启动了一个异步的消息进行安装应用,首先创建PackageInstalledInfo参数,然后调用doPreInstall判断之前安装是否都是正确的,如果之前安装的结果是失败的就删除之前安装过程中创建的目录以及目录下面的文件,然后会调用installPackageLI函数进行应用的正式安装,安装完成之后,调用doPostInstall检查安装结果,如果安装失败,就删除之前安装过程中创建的目录,并返回安装失败的错误码。
intdoPreInstall(int status) {
if (status !=PackageManager.INSTALL_SUCCEEDED) {
cleanUp();//如果签名的安装返回状态不是成功,就删除之前创建的目录
}
return status;}
private booleancleanUp() {
if (codeFile == null || !codeFile.exists()) {
return false;
}
if (codeFile.isDirectory()) {
mInstaller.rmPackageDir(codeFile.getAbsolutePath());
} else {
codeFile.delete();
}
if (resourceFile != null &&!FileUtils.contains(codeFile, resourceFile)) {
resourceFile.delete();
}
return true;}
删除前面创建的文件。
通过前面的代码我们可以看到,在processPendingInstall里面,首先会确认之前的安装过程是否正常,如果之前的安装过程正常就调用installPackageLI继续安装,安装完成之后再调用doPostInstall检查安装过程是否正常,如果安装失败,就删除之前复制应用过程中创建的目录。最后根据安装结果,确定是否需要发送应用安装成功的广播。这里我们重点看下installPackageLI的安装过程。
private voidinstallPackageLI(InstallArgs args, PackageInstalledInfo res) {
手机创建各种参数,然后初始化一个PackageParser对象
PackageParser pp = new PackageParser();
pp.setSeparateProcesses(mSeparateProcesses);
pp.setDisplayMetrics(mMetrics);
final PackageParser.Package pkg;
try {//调用parsePackage函数对应用进行解析
pkg = pp.parsePackage(tmpPackageFile,parseFlags);
} catch (PackageParserException e){return;}
try {// 获取应用的签名信息
pp.collectCertificates(pkg,parseFlags);
pp.collectManifestDigest(pkg);
} catch (PackageParserException e){return;}
//如果PS不是空,就说明系统中之前已经安装了此应用,因此这里需要处老应用的安装信息
PackageSetting ps =mSettings.mPackages.get(pkgName);
if (ps != null) {
if (shouldCheckUpgradeKeySetLP(ps,scanFlags)) {
if (!checkUpgradeKeySetLP(ps, pkg)) {
return;
}
} else {
try {// 检查新旧两个应用的签名是否一致。如果不一致直接返回
verifySignaturesLP(ps, pkg);
} catch (PackageManagerException e){return;}
}
// 查看是否是核心应用
oldCodePath =mSettings.mPackages.get(pkgName).codePathString;
if (ps.pkg != null &&ps.pkg.applicationInfo != null) {
systemApp =(ps.pkg.applicationInfo.flags &
ApplicationInfo.FLAG_SYSTEM) !=0;
}
}
//检查新安装应用定义的权限,是否有定义新的权限
if (systemApp && onExternal) {
res.setError("Cannot install updatesto system apps on sdcard");
return;
}
try {// 处理应用的so库
derivePackageAbi(pkg, newFile(pkg.codePath), args.abiOverride,
true /* extract libs */);
} catch (PackageManagerException pme){return;}
// 对应用进行odex优化
int result = mPackageDexOptimizer
.performDexOpt(pkg, null,false,false,false);
if (result ==PackageDexOptimizer.DEX_OPT_FAILED) {
return;
}
// 更改data/app/下面的文件名称
if (!args.doRename(res.returnCode, pkg,oldCodePath)) {
res.setError(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failedrename");
return;
}
if (replace) {
replacePackageLI(pkg, parseFlags, scanFlags| SCAN_REPLACING, args.user,
installerPackageName, volumeUuid,res);
} else {
installNewPackageLI(pkg, parseFlags,scanFlags | SCAN_DELETE_DATA_ON_FAILURES,
args.user, installerPackageName,volumeUuid, res);
}
}
installPackageLI函数比较长,这里只贴出部分核心代码。它首先创建各种参数,然后初始化PackageParser对象,调用它的parsePackage函数对应用进行解析,然后调用collectCertificates函数获取应用的签名信息。完了之后处理应用的升级情况,看下升级前后两个应用的签名,查看升级的应用是否是核心应用,核心应用不允许安装到外置sd卡,完了之后处理应用的so库,对应用进程odex优化,最后根据前面是否是升级的应用,调用replacePackageLI或者installNewPackageLI函数进行继续安装,这里我们就看下replacePackageLI实现。
private voidreplacePackageLI(PackageParser.Package pkg,res) {
boolean sysPkg = (isSystemApp(oldPackage));
if (sysPkg) {// 移除系统中的应用
replaceSystemPackageLI(oldPackage, pkg,parseFlags, scanFlags,
user, allUsers, perUserInstalled,installerPackageName, volumeUuid, res);
} else {// 移除用户安装的应用
replaceNonSystemPackageLI(oldPackage, pkg,parseFlags, scanFlags,
user, allUsers, perUserInstalled,installerPackageName, volumeUuid, res);
}}
这个replacePackageLI首先获取原始应用的类型,根据原始应用的类型,调用不同的函数。
private voidreplaceSystemPackageLI(PackageParser.Package deletedPackage) {
//首先获取需要删除的系统应用的信息
//kill需要更新的应用
killApplication(packageName,oldPkg.applicationInfo.uid, "replace sys pkg");
res.removedInfo.uid = oldPkg.applicationInfo.uid;
res.removedInfo.removedPackage = packageName;
//删除应用的数据目录// Remove existing system package
removePackageLI(oldPkgSetting, true);
// writer
synchronized (mPackages) {
// 将需要删除的系统应用disable掉
disabledSystem = mSettings.disableSystemPackageLPw(packageName);
if (!disabledSystem &&deletedPackage != null) {
} else {
res.removedInfo.args = null;
}
}
// 删除应用的缓存目录
deleteCodeCacheDirsLI(pkg.volumeUuid,packageName);
res.returnCode = PackageManager.INSTALL_SUCCEEDED;
pkg.applicationInfo.flags |=ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
PackageParser.Package newPackage = null;
try {//安装新的应用
newPackage = scanPackageLI(pkg,parseFlags, scanFlags, 0, user);
// 将安装信息写入setting
if (res.returnCode ==PackageManager.INSTALL_SUCCEEDED) {
updateSettingsLI(newPackage,installerPackageName, volumeUuid, allUsers,
perUserInstalled, res, user);
updatedSettings = true;
}
}
if (res.returnCode !=PackageManager.INSTALL_SUCCEEDED) {
if (newPackage != null) {
removeInstalledPackageLI(newPackage,true);}
// Add back the old system package
try {
scanPackageLI(oldPkg, parseFlags,SCAN_UPDATE_SIGNATURE, 0, user);
} catch (PackageManagerException e) {
Slog.e(TAG, "Failed to restoreoriginal package: " + e.getMessage());
}
synchronized (mPackages) {
if (disabledSystem) {
mSettings.enableSystemPackageLPw(packageName);
}
if (updatedSettings) {
mSettings.setInstallerPackageName(packageName,
oldPkgSetting.installerPackageName);
}
mSettings.writeLPr();
}
}}
这个函数流程比较多,首先获取需要disable的系统应用新,然后终止这个应用的运行,在删除这个应用的安装目录以及缓存目录,完成之后调用scanPackageLI进行新应用的安装,待新应用安装完成之后,将新的应用写入到setting中。另外这个函数的最后会处理应用安装失败的情况,这种情况下,会首先删除新应用的目录,然后重新安装老的系统应用,完成之后恢复前面修改的setting数据。
关于scanPackageLI函数,在前面讲解PMS的main函数的时候,已经有详细的介绍,因此这里不做分析。至此三方应用的安装流程就分析完了。