Android7.0 PackageManagerService (1) 通信结构、启动和main函数

一、概述
PackageManagerService是Android中比较重要的服务,它负责系统中Package的管理,应用程序的安装、卸载、信息查询等。

《Android7.0 PackageManagerService (1) 通信结构、启动和main函数》

上图主要展示了PackageManagerService及客户端的通信方式,以及相关类的继承关系。为了简化描述,下文中我们将PackageMangerService称为”PKMS”。

1、
PKMS的客户端,必须通过PackageManager才能发送请求给PKMS,即可以认为PackageManger是PKMS的代理对象。
PackageManager是一个抽象类,实际的实现类是ApplicationPackageManager。当客户端利用Context的getPacakgeManager函数获取PackageManger时,获取的就是ApplicationPacakgeManager。

2、
ApplicationPackageManager与PKMS的交互实际上是基于Binder通信的,只不过利用AIDL文件封装了实现细节。
我们看看ApplicationManager的构造函数:

ApplicationPackageManager(ContextImpl context, IPackageManager pm) { mContext = context; mPM = pm; }

容易看出,ApplicationPackageManger中持有了IPackageManger对象。

IPacakgeManager对象是怎么得到的?
我们以ActivityThread.java中handleBindApplication函数中的一小段代码为例:

.......
try {
    //getPackageManager函数将获取到IPackageManager对象
    ii = new ApplicationPackageManager(null, getPackageManager())
            .getInstrumentationInfo(data.instrumentationName, 0);
} catch (PackageManager.NameNotFoundException e) {
    .......
}
public static IPackageManager getPackageManager() {
    if (sPackageManager != null) {
        return sPackageManager;
    }

    //在之前的博客介绍Java层的Binder通信时,我们知道这里最终将会返回BinderProxy对象
    IBinder b = ServiceManager.getService("package");
    //将BinderProxy对象,转换成实际的业务代理对象
    sPackageManager = IPackageManager.Stub.asInterface(b);
}

现在我们知道了,IPackageManager是PKMS的业务代理,其中服务端和客户端通信的业务函数由
framework/base/core/java/android/content/pm/IPackageManager.aidl文件来定义。

如上图所示,PKMS继承自IPackageManager.Stub,其实上也是基于Binder的服务端。

二、PKMS的启动
PKMS由SystemService在开机的时候启动,在SystemServer.java的run方法中:

private void run() {
    .......
    try {
        .....
        startBootstrapServices();
        .....
        startOtherServices();
        .....
    } ......
}

private void startBootstrapServices() {
    // Wait for installd to finish starting up so that it has a chance to
    // create critical directories such as /data/user with the appropriate
    // permissions. We need this to complete before we initialize other services.
    Installer installer = mSystemServiceManager.startService(Installer.class);
    .........
    //根据系统属性,决定是否为加密设备加密
    String cryptState = SystemProperties.get("vold.decrypt");
    if (ENCRYPTING_STATE.equals(cryptState)) {
        Slog.w(TAG, "Detected encryption in progress - only parsing core apps");
        mOnlyCore = true;
    } else if (ENCRYPTED_STATE.equals(cryptState)) {
        Slog.w(TAG, "Device encrypted - only parsing core apps");
        mOnlyCore = true;
    }

    //调用PKMS的main函数
    mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
            mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);

    //判断是否为初次启动
    mFirstBoot = mPackageManagerService.isFirstBoot();
    mPackageManager = mSystemContext.getPackageManager();
    ..........
    // Manages A/B OTA dexopting. This is a bootstrap service as we need it to rename
    // A/B artifacts after boot, before anything else might touch/need them.
    // Note: this isn't needed during decryption (we don't have /data anyways).
    if (!mOnlyCore) {
        boolean disableOtaDexopt = SystemProperties.getBoolean("config.disable_otadexopt",
                false);
        if (!disableOtaDexopt) {
            try {
                //启动OtaDexoptService也需要PackageMangerService的参与
                OtaDexoptService.main(mSystemContext, mPackageManagerService);
            }......
        }
    }
}

从上面的代码,大致可以看出SystemServer在startBootstrapServices函数中启动PKMS,在非加密机器中PKMS还将参与到OtaDexoptService中。

在SystemServer的startOtherService中:

private void startOtherServices() {
    ......
    if (!mOnlyCore) {
        ........
        try {
            //将调用performDexOpt:Performs dexopt on the set of packages
            mPackageManagerService.updatePackagesIfNeeded();
        }.......
        ........
        try {
            //执行Fstrim,执行磁盘维护操作,未看到详细的资料
            //可能类似于TRIM技术,将标记为删除的文件,彻底从硬盘上移除
            //而不是等到写入时再移除,目的是提高写入时效率
            mPackageManagerService.performFstrimIfNeeded();
        }.........
        .......
        try {
            mPackageManagerService.systemReady();
        }........
        .......
    }
}

从上面的代码可以看出,PKMS启动后将参与一些系统优化的工作,然后调用SystemReady函数通知系统进入就绪状态。

三、PKMS的main函数分析
现在我们重点看看PKMS的main函数。

public static PackageManagerService main(Context context, Installer installer,
        boolean factoryTest, boolean onlyCore) {
    // Self-check for initial settings.
    //此处主要检查系统属性
    PackageManagerServiceCompilerMapping.checkProperties();

    //调用构造函数,其中factoryTest决定是否是测试版本,onlyCore决定是否只解析系统目录,我们先假定均为false
    PackageManagerService m = new PackageManagerService(context, installer,
            factoryTest, onlyCore);

    //根据条件,enable一些app,针对多用户场景
    m.enableSystemUserPackages();

    // Disable any carrier apps. We do this very early in boot to prevent the apps from being
    // disabled after already being started.
    //关闭一些运营商应用,直到被授权
    CarrierAppUtils.disableCarrierAppsUntilPrivileged(context.getOpPackageName(), m,
            UserHandle.USER_SYSTEM);

    //利用Binder通信,将自己注册到ServiceManager进程中
    ServiceManager.addService("package", m);
    return m;
}

1、检查系统属性
我们先看看检查系统属性进行了哪些操作。

........ PackageManagerServiceCompilerMapping.checkProperties(); ........
// Check that the properties are set and valid.
static void checkProperties() {
    // We're gonna check all properties and collect the exceptions, so we can give a general
    // overview. Store the exceptions here.
    RuntimeException toThrow = null;

    //reason包括REASON_FIRST_BOOT、REASON_BOOT等
    for (int reason = 0; reason <= PackageManagerService.REASON_LAST; reason++) {
        try {
            // Check that the system property name is legal.
            //将reason转化为"pm.dexopt.first-boot"等,定义于PackageManagerServiceCompilerMapping.java中
            String sysPropName = getSystemPropertyName(reason);
            if (sysPropName == null ||
                    sysPropName.isEmpty() ||
                    sysPropName.length() > SystemProperties.PROP_NAME_MAX) {
                throw.......
            }

            // Check validity, ignore result.
            getAndCheckValidity(reason);
         } catch (Exception exc) {
            if (toThrow == null) {
                toThrow = new IllegalStateException("PMS compiler filter settings are bad.");
            }
            //搜集每一个异常
            toThrow.addSuppressed(exc);
         }
    }

    if (toThrow != null) {
        throw toThrow;
    }
}

我们跟进getAndCheckValidity:

// Load the property for the given reason and check for validity. This will throw an
// exception in case the reason or value are invalid.
private static String getAndCheckValidity(int reason) {
    //读取对应的系统属性,native函数从系统属性空间中读取
    String sysPropValue = SystemProperties.get(getSystemPropertyName(reason));
    if (sysPropValue == null || sysPropValue.isEmpty() ||
            !DexFile.isValidCompilerFilter(sysPropValue)) {
        throw......
    }

    // Ensure that some reasons are not mapped to profile-guided filters.
    switch (reason) {
        case PackageManagerService.REASON_SHARED_APK:
        case PackageManagerService.REASON_FORCED_DEXOPT:
            if (DexFile.isProfileGuidedCompilerFilter(sysPropValue)) {
                throw......
            }
            break;
    }

    return sysPropValue;
}

从上面的代码可以看出,这些属性可能和编译器有关,实际的用途目前还需要进一步了解。

2、调用PKMS的构造函数
PKMS的构造函数很长,主要功能是:扫描Android系统中几个目标文件夹中的APK,从而建立合适的数据结构以管理诸如Package信息、四大组件信息、权限信息等各种信息。
我们将在之后的博客再深入分析。

3、针对系统用户,enable一些App

/** * @hide * @return Whether the device is running with split system user. It means the system user and * primary user are two separate users. Previously system user and primary user are combined as * a single owner user. see @link {android.os.UserHandle#USER_OWNER} */
//这里仅作了解即可,我看目前的手机好像还没有配置这个属性
public static boolean isSplitSystemUser() {
    return SystemProperties.getBoolean("ro.fw.system_user_split", false);
}


private void enableSystemUserPackages() {
    //system和primary未分离,则退出
    //当前手机应该都会退出
    if (!UserManager.isSplitSystemUser()) {
        return;
    }

    // For system user, enable apps based on the following conditions:
    // - app is whitelisted or belong to one of these groups:
    // -- system app which has no launcher icons
    // -- system app which has INTERACT_ACROSS_USERS permission
    // -- system IME app
    // - app is not in the blacklist
    AppsQueryHelper queryHelper = new AppsQueryHelper(this);
    Set<String> enableApps = new ArraySet<>();
    //按照条件,增加可用的app
    enableApps.addAll(queryHelper.queryApps(AppsQueryHelper.GET_NON_LAUNCHABLE_APPS
            | AppsQueryHelper.GET_APPS_WITH_INTERACT_ACROSS_USERS_PERM
            | AppsQueryHelper.GET_IMES, /* systemAppsOnly */ true, UserHandle.SYSTEM));

    //增加白名单里的应用,移除黑名单里的应用
    ArraySet<String> wlApps = SystemConfig.getInstance().getSystemUserWhitelistedApps();
    enableApps.addAll(wlApps);
    enableApps.addAll(queryHelper.queryApps(AppsQueryHelper.GET_REQUIRED_FOR_SYSTEM_USER,
            /* systemAppsOnly */ false, UserHandle.SYSTEM));
    ArraySet<String> blApps = SystemConfig.getInstance().getSystemUserBlacklistedApps();
    enableApps.removeAll(blApps);

    //得到system user已安装的应用
    Log.i(TAG, "Applications installed for system user: " + enableApps);
    List<String> allAps = queryHelper.queryApps(0, /* systemAppsOnly */ false,
            UserHandle.SYSTEM);

    synchronized (mPackages) {
        for (int i = 0; i < allAppsSize; i++) {
            String pName = allAps.get(i);
            PackageSetting pkgSetting = mSettings.mPackages.get(pName);
            // Should not happen, but we shouldn't be failing if it does
            if (pkgSetting == null) {
                continue;
            }
            //从已安装中筛选出可使用的
            boolean install = enableApps.contains(pName);
            if (pkgSetting.getInstalled(UserHandle.USER_SYSTEM) != install) {
                Log.i(TAG, (install ? "Installing " : "Uninstalling ") + pName
                        + " for system user");
                pkgSetting.setInstalled(install, UserHandle.USER_SYSTEM);
            }
        }
    }
}

从上面的代码可以看出,enableSystemUserPackages针对的是system user和primary user分离的特殊场景,按条件为system user安装一些应用。

4、禁止运营商应用

/** * This prevents a potential race condition on first boot - since the app's default state is * enabled, we will initially disable it when the telephony stack is first initialized as it has * not yet read the carrier privilege rules. However, since telephony is initialized later on * late in boot, the app being disabled may have already been started in response to certain * broadcasts. The app will continue to run (briefly) after being disabled, before the Package * Manager can kill it, and this can lead to crashes as the app is in an unexpected state. */
public synchronized static void disableCarrierAppsUntilPrivileged(String callingPackage,
        IPackageManager packageManager, int userId) {
    //读取将被禁用的App名称
    String[] systemCarrierAppsDisabledUntilUsed = Resources.getSystem().getStringArray(
            com.android.internal.R.array.config_disabledUntilUsedPreinstalledCarrierApps);

    disableCarrierAppsUntilPrivileged(callingPackage, packageManager,
            null /* telephonyManager */, userId, systemCarrierAppsDisabledUntilUsed);
}

@VisibleForTesting
public static void disableCarrierAppsUntilPrivileged(String callingPackage,
        IPackageManager packageManager, @Nullable TelephonyManager telephonyManager, int userId,
        String[] systemCarrierAppsDisabledUntilUsed) {
    //根据被禁用的App的名称,取出对应的ApplicationInfo
    List<ApplicationInfo> candidates = getDefaultCarrierAppCandidatesHelper(packageManager,
        userId, systemCarrierAppsDisabledUntilUsed);
    .........
    List<String> enabledCarrierPackages = new ArrayList<>();

    try {
        for (ApplicationInfo ai : candidates) {
            String packageName = ai.packageName;
            //PKMS中传入的telephonyManager变量为null,因此不检查被禁用的app是否有特权
            boolean hasPrivileges = telephonyManager != null &&
                    telephonyManager.checkCarrierPrivilegesForPackageAnyPhone(packageName) ==
                            TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;

            // Only update enabled state for the app on /system. Once it has been updated we
            // shouldn't touch it.
            if (!ai.isUpdatedSystemApp()) {
                //根据是否有特权及application中的信息,修改PKMS对被禁用app的管理策略
                //PKMS禁用运营商应用时,不进入该分支
                if (hasPrivileges
                        && (ai.enabledSetting == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
                        || ai.enabledSetting ==
                        PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED)) {
                    packageManager.setApplicationEnabledSetting(packageName,
                            PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
                            PackageManager.DONT_KILL_APP, userId, callingPackage);
                } else if (!hasPrivileges
                        && ai.enabledSetting ==
                        PackageManager.COMPONENT_ENABLED_STATE_DEFAULT){
                    //被禁用App的管理策略变为COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
                    packageManager.setApplicationEnabledSetting(packageName,
                            PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED, 0,
                            userId, callingPackage);
                }
            }

            // Always re-grant default permissions to carrier apps w/ privileges.
            //PKMS禁用的App无privileges
            if (hasPrivileges) {
                enabledCarrierPackages.add(ai.packageName);
            }
        }

        //PKMS禁用运营商App时不会进入该分支
        if (!enabledCarrierPackages.isEmpty()) {
            // Since we enabled at least one app, ensure we grant default permissions to those
            // apps.
            String[] packageNames = new String[enabledCarrierPackages.size()];
            enabledCarrierPackages.toArray(packageNames);
            packageManager.grantDefaultPermissionsToEnabledCarrierApps(packageNames, userId);
        }
    } catch(RemoteException e) {
        ......
    }   
}

以上就是PKMS中main函数的主要内容,代码比较简单,但执行时间较长,主要是PKMS的构造函数比较耗时。
虽然没有分析PKMS的构造函数,但我们大致上可以知道PKMS其实就是管理手机中APK信息的,因此它的主要工作就是解析这些APK信息,并用相应的数据结构管理解析出来的内容。

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