Package管理服务PackageManagerService在安装一个应用程序的过程中,会对这个应用程序的配置文件AndroidManifest.xml进行解析,以便可以获得它的安装信息。
Android系统中每一个应用程序都有一个Linux用户ID,一个应用程序除了拥有一个Linux用户ID之外,还可以拥有若干个Linux用户组ID,以便可以在系统中获得更多的资源访问权限,如读取联系人信息、使用摄像头等权限。
PMS在安装一个应用程序时,如果发现它没有与其他应用程序共享同一个Linux用户ID,那么就会为它分配一个唯一的Linux用户ID,以便它可以在系统中获得合适的运行权限。如果发现它申请了一个特定的资源访问权限,那么就会为它分配一个相应的Linux用户组ID。
分析应用程序的安装过程,主要是关注它的组件信息的解析过程,以及Linux用户ID和Linux用户组ID的分配过程。
System进程在启动时,会调用PMS类的静态成员函数main方法将系统的PMS启动起来。由于PMS在启动过程中会对系统中的应用程序进行安装,因此,接下来就从PMS类的main方法开始分析应用程序的安装过程:
public static PackageManagerService main(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
// 实例化PMS
PackageManagerService m = new PackageManagerService(context, installer,
factoryTest, onlyCore);
// 把PMS添加到ServiceManager中
ServiceManager.addService("package", m);
return m;
}
PMS的构造方法完成的主要功能是,扫描Android系统中几个目标文件夹中的apk文件,从而建立合适的数据结构以管理诸如Package信息、四大组件信息、权限信息等各种信息。PMS的工作流程相对简单,复杂的是其中用于保存各种信息的数据结构和它们之间的关系,以及影响最终结果的策略控制。
由于代码量较大,采用分段的方法进行分析。
1.扫描目标文件夹之前的准备工作
先看下时序图:
final int mSdkVersion = Build.VERSION.SDK_INT;
final Context mContext;
final boolean mFactoryTest;
final boolean mOnlyCore;
final boolean mLazyDexOpt;
final DisplayMetrics mMetrics;
final Settings mSettings;
// Keys are String (package name), values are Package. This also serves
// as the lock for the global state. Methods that must be called with
// this lock held have the prefix "LP".
@GuardedBy("mPackages")
final ArrayMap mPackages =
new ArrayMap();
private static final int RADIO_UID = Process.PHONE_UID;
private static final int LOG_UID = Process.LOG_UID;
private static final int NFC_UID = Process.NFC_UID;
private static final int BLUETOOTH_UID = Process.BLUETOOTH_UID;
private static final int SHELL_UID = Process.SHELL_UID;
public PackageManagerService(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
// 编译的SDK版本,若没有定义则APK就无法知道自己运行在Android的哪个版本上
if (mSdkVersion <= 0) {
Slog.w(TAG, "**** ro.build.version.sdk not set!");
}
mContext = context;
// 是否运行在工厂模式下
mFactoryTest = factoryTest;
// 用于判断是否只扫描系统目录
mOnlyCore = onlyCore;
// 如果此系统是eng版本,则扫描Package后,不对其做dex优化
mLazyDexOpt = "eng".equals(SystemProperties.get("ro.build.type"));
// 用于存储与显示屏相关的一些属性,如屏幕的宽高、分辨率等信息
mMetrics = new DisplayMetrics();
// Settings是用来管理应用程序的安装信息的
mSettings = new Settings(mPackages);
// 添加共享用户
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);
. . .
}
刚进入构造函数就会遇到第一个较为复杂的数据结构Settings以及它的addSharedUserLPw方法。先看Settings类的构造方法,主要是创建相关的文件夹并设置文件夹的读写权限:
Settings(Object lock) {
// Environment.getDataDirectory()获取到的是/data目录
this(Environment.getDataDirectory(), lock);
}
private final Object mLock;
private final RuntimePermissionPersistence mRuntimePermissionsPersistence;
private final File mSystemDir;
private final File mSettingsFilename;
private final File mBackupSettingsFilename;
private final File mPackageListFilename;
private final File mStoppedPackagesFilename;
private final File mBackupStoppedPackagesFilename;
Settings(File dataDir, Object lock) {
mLock = lock;
mRuntimePermissionsPersistence = new RuntimePermissionPersistence(mLock);
mSystemDir = new File(dataDir, "system");
// 创建/data/system文件夹
mSystemDir.mkdirs();
// 设置/data/system文件夹的读写权限
FileUtils.setPermissions(mSystemDir.toString(),
FileUtils.S_IRWXU|FileUtils.S_IRWXG
|FileUtils.S_IROTH|FileUtils.S_IXOTH,
-1, -1);
mSettingsFilename = new File(mSystemDir, "packages.xml");
mBackupSettingsFilename = new File(mSystemDir, "packages-backup.xml");
mPackageListFilename = new File(mSystemDir, "packages.list");
FileUtils.setPermissions(mPackageListFilename, 0640, SYSTEM_UID, PACKAGE_INFO_GID);
// Deprecated: Needed for migration
mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml");
mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml");
}
Settings的构造方法会在data目录下创建system目录,用来保存多个系统文件。主要有:
packages.xml:记录系统中所有安装的应用信息,包括基本信息、签名和权限。
packages-backup.xml:packages.xml文件的备份。写文件之前会先备份,写成功后再删除掉备份文件,如果写的时候出现问题,则重启后再读取这两个文件时,如果发现备份文件存在,就会使用备份文件的内容,因为原文件可能已经损坏了。
packages.list:保存普通应用的数据目录和uid等信息。
packages-stopped.xml:记录系统中被强制停止运行的应用信息。系统在强制停止某个应用时,会将应用的信息记录在该文件中。
packages-stopped-backup.xml:packages-stopped.xml文件的备份文件。
final ArrayMap mSharedUsers = new ArrayMap();
// 添加共享用户
SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags, int pkgPrivateFlags) {
// 根据key从map中获取值
SharedUserSetting s = mSharedUsers.get(name);
// 如果值不为null并且保存的uid和传递过来的一致,就直接返回结果。uid不一致则返回null
if (s != null) {
if (s.userId == uid) {
return s;
}
PackageManagerService.reportSettingsProblem(Log.ERROR,
"Adding duplicate shared user, keeping first: " + name);
return null;
}
// 若s为null,则根据传递过来的参数新创建对象
s = new SharedUserSetting(name, pkgFlags, pkgPrivateFlags);
s.userId = uid;
// 在系统中保存值为uid的Linux用户ID,成功返回true
if (addUserIdLPw(uid, s, name)) {
// 保存到map中
mSharedUsers.put(name, s);
return s;
}
return null;
}
private final ArrayList
至此,对Settings的分析就告一段落了。下面继续分析PMS的构造方法:
private static final long WATCHDOG_TIMEOUT = 1000*60*10;
public PackageManagerService(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
. . .
// 应用安装器
mInstaller = installer;
// 实例化类优化器
mPackageDexOptimizer = new PackageDexOptimizer(this);
mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());
mOnPermissionChangeListeners = new OnPermissionChangeListeners(
FgThread.get().getLooper());
// 获取当前显示屏信息
getDefaultDisplayMetrics(context, mMetrics);
// 获取系统的初始化变量
SystemConfig systemConfig = SystemConfig.getInstance();
mGlobalGids = systemConfig.getGlobalGids();
mSystemPermissions = systemConfig.getSystemPermissions();
mAvailableFeatures = systemConfig.getAvailableFeatures();
// 创建一个HandlerThread子类的实例,主要处理应用的安装和卸载
mHandlerThread = new ServiceThread(TAG,
Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
mHandlerThread.start();
// 以mHandlerThread线程的looper创建的Handler实例,该Handler运行在mHandlerThread线程
mHandler = new PackageHandler(mHandlerThread.getLooper());
// 把mHandler加入到watchdog中
Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
// /data目录
File dataDir = Environment.getDataDirectory();
// /data/data目录
mAppDataDir = new File(dataDir, "data");
// /data/app目录,保存的是用户自己安装的app
mAppInstallDir = new File(dataDir, "app");
mAppLib32InstallDir = new File(dataDir, "app-lib");
mAsecInternalPath = new File(dataDir, "app-asec").getPath();
mUserAppDataDir = new File(dataDir, "user");
// /data/app-parivate目录,保存的是受DRM保护的私有app
mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
mRegionalizationAppInstallDir = new File(dataDir, "app-regional");
// 实例化用户管理服务,多用户时使用
sUserManager = new UserManagerService(context, this,
mInstallLock, mPackages);
// 通过systemConfig获取系统中定义的权限,这些权限保存在/system/etc/permissions目录下的文件中
ArrayMap permConfig
= systemConfig.getPermissions();
for (int i=0; i libConfig = systemConfig.getSharedLibraries();
for (int i=0; i