Android之应用程序的安装过程源码分析

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.扫描目标文件夹之前的准备工作

先看下时序图:

Android之应用程序的安装过程源码分析_第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 mUserIds = new ArrayList();
private final SparseArray mOtherUserIds = new SparseArray();

private boolean addUserIdLPw(int uid, Object obj, Object name) {
    // LAST_APPLICATION_UID = 19999
    if (uid > Process.LAST_APPLICATION_UID) {
        return false;
    }

    // FIRST_APPLICATION_UID = 10000
    if (uid >= Process.FIRST_APPLICATION_UID) {
        // 获取数组的长度
        int N = mUserIds.size();
        // 计算目标索引值
        final int index = uid - Process.FIRST_APPLICATION_UID;
        // 如果目标索引值大于数组长度,则在数组索引值之前的位置都添加null的元素
        while (index >= N) {
            mUserIds.add(null);
            N++;
        }
        // 如果数组的目标索引值位置有不为null的值,说明已经添加过
        if (mUserIds.get(index) != null) {
            PackageManagerService.reportSettingsProblem(Log.ERROR,
                    "Adding duplicate user id: " + uid
                    + " name=" + name);
            return false;
        }
        // 把值放在数组的目标索引位置
        mUserIds.set(index, obj);
    } else {
        // 如果uid < 10000,则把对应的值保存在mOtherUserIds变量中
        if (mOtherUserIds.get(uid) != null) {
            PackageManagerService.reportSettingsProblem(Log.ERROR,
                    "Adding duplicate shared id: " + uid
                            + " name=" + name);
            return false;
        }
        mOtherUserIds.put(uid, obj);
    }
    return true;
}


至此,对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

你可能感兴趣的:(Android)