Android资源管理框架-------之OverlayManagerService简介及其数据的维护更新(二)

        我们知道在Android的低版本中,源生是不支持动态overlay的,也就是说,所有的overlay都是静态的,对于一个target package而言,它只要有overlay package,那么它肯定会被优先级最高的那个overlay package所覆盖,我们不能动态地去enable或者disable一个overlay package。不过,到了Android Oreo(不过我们的代码分析还是结合Android 10),Android引入了OverlayManagerService(后面简称OMS),正式支持了动态overlay。

OverlayManagerService的作用

        OverlayManagerService提供了一些API,我们通过这些API可以随时指定哪个overlay包生效,我们也可以随时查询一个包的状态,要详细了解OMS的功能,我们可以先看一下,相关接口的定义:

//frameworks/base/core/java/android/content/om/IOverlayManager.aidl
interface IOverlayManager {

    //获取overlay相关的信息,这是一组接口,只介绍其中两个
    
    /**
     * 返回指定user下,系统中已经安装的所有overlay package的信息,如果没有overlay package,则返回空。
     * @param userId 指定的userId.这个是我们手机概念中的userId,而非linux的uid
     * @return 返回的map中,key是一个一个的target包的包名,value是一个List,表示这个target包中所有
     *         overlay包的信息,list中的元素是OverlayInfo,它里面记录着Overlay相关的信息
     */
    Map getAllOverlays(in int userId);

    /**
     * 返回指定user下,某一个target pacakge的所有overlay package,如果没有,则返回空
     * @param targetPackageName 指定的target package
     * @param userId 指定的userId.这个是我们手机概念中的userId,而非linux的uid
     * @return 返回一个List,它里面的元素是OverlayInfo
     */
    List getOverlayInfosForTarget(in String targetPackageName, in int userId);
    

    /**动态enable或者disable overlay package的相关接口,也是一组,只介绍两个*/

    /**
     * 对某个用户,enable或者disable一个指定的overlay package
     * @param packageName 我们要enable或者disable的overlay package的包名
     * @param enable true or false enable or disable
     * @userId 指定的userId
     * @return 操作成功返回true,否则返回false
     */
    boolean setEnabled(in String packageName, in boolean enable, in int userId);
    
    /**
     * 对某个用户,enable或者disable一个指定的overlay package,同时对其它overlay package做反向操作
     * @param packageName 我们要enable或者disable的overlay package的包名
     * @param enable true or false enable or disable
     * @userId 指定的userId
     * @return 操作成功返回true,否则返回false
     */
    boolean setEnabledExclusive(in String packageName, in boolean enable, in int userId);

    
    //设置overlay package的优先级,也是一组接口,只介绍两个
    
    /**
     * 对于指定的用户,把一个overlay package的优先级设置为最高
     * @param packageName 要设置的overlay包的包名
     * @param userId 指定的userId
     * @return 操作成功则返回true,否则返回false
     */
    boolean setHighestPriority(in String packageName, in int userId);
    
    /**
     * 对于指定的用户,把一个overlay package的优先级设置为最低
     * @param packageName 要设置的overlay包的包名
     * @param userId 指定的userId
     * @return 操作成功则返回true,否则返回false
     */
    boolean setLowestPriority(in String packageName, in int userId);
}

        我们看到,这个接口中的方法主要分为三类:

  • 获取overlay包的信息。比如我们通过这些方法得到:一个target包有哪些overlay 包;
    某个overlay包的target包是哪个等等。

  • 设置一个overlay包的优先级。可以把优先级设定成最高,或者最低,也可以设置得比某个包高。

  • 动态enable或者disable一个overlay包。

            这里我们需要说明的是,disable一个overlay package和设置一个overlay package 都可能影响最终overlay的效果。但是一旦一个overlay package的被disable了,不论它的优先级有多高,它都不会生效;但是把一个overlay package的优先级设置到最低,它仍有生效的机会,比如对应的target package的overlay package只有它一个或者其它overlay package 都被disable了。
            另外,并不是所有的overlay pacakge都是可以被enable或者disable的。在Android 8之前,所有的overlay package 默认都是 enable 并且不能修改为disable的(因为Android 8之前还没有OMS)。在Android 8及以后的版本中,如果一个overlay package的AndroidManifest.xml中的overlay标签中有这个属性android:isStatic=“true” ,那么这个overlay package就不可以被enable或者disable,也就是说setEnabled组的那些方法都会失败。我们说这样的overlay包是一个静态的overlay包,否则我们就说这样的overlay包是一个动态的overlay包,我们可以动态enable或者disable它。其实Android还有一种静态overlay机制,它是指,oem厂商可以把自己定制的东西放到一个overlay目录下,这样在编译ROM的时候,编译系统会用这个目录下的文件来代替源生对应的文件来参与编译,这样就实现了oem厂商的代码和AOSP代码的隔离,注意和我们这里说的静态overlay package的区别,两者不是一个东西。

OverlayManagerService的架构

OMS架构
        这个图是OverlayManagerService.java中的原图,它非常简明地描述了OMS的架构,OMS涉及到的核心类也就OverlayManagerServiceOverlayManagerServiceImplOverlayManagerSettingsIdmapManager四个,再加上OverlayManagerTests(严格来说这只是个测试类,说不上是OMS的核心模块),它们被分成了3个模块,图中已经用(1)(2)(3)标出来了:

  • 模块(1),Android Framework等上层模块,它们是OMS相关服务的正式使用者。需要说明的是,OverlayManagerService类只是一个空壳,它只不过是OMS相关服务在system_server中的一个接口类,实现了IOverlayManager.aidl中定义的接口。这个接口可以通过aidl为应用层提供服务。需要说明的是OMS除了被动被应用层调用外,也会监听一些系统广播,比如包信息变化的广播。当一个包被卸载了的时候,如果它有overlay package,OMS会删除OverlayManagerSettings中这个包的overlay设置信息,还会发一个action=Intent.ACTION_OVERLAY_CHANGED的广播,以通知感兴趣的组件这个包的overlay信息发生了改变;同样,当我们安装一个包的时候,如果系统中已经存在这个包的overlay包,那OMS会添加OverlayManagerSettings中overlay 包的设置信息,同时让overlay包生效,让overlay包生效的逻辑主要也在OverlayManagerService这个类中。
  • 模块(2),这部分才是OMS相关服务的真正实现者,OverlayManagerService类只是空壳,OverlayManagerServiceImpl类才是真正干活的。这个模块主要的工作就2个:更新OverlayManagerSettings中overlay包的信息以及调用IdmapManager动态生成idmap文件。我们知道,要想支持动态enable或者disable一个overlay包,我们就必须记录每一个overlay包的状态信息,并且还得把这些信息持久化,否则一重启机器,overlay包变了,那就太不合理了。所以,这个任务就落在了OverlayManagerSettings这个类身上,它负责管理这些信息,不仅仅会在内存中记录这些信息,还会把这些信息持久化到/data/system/overlays.xml这个文件中。IdmapManager则是负责idmap文件的生成,在Android 10中由于引入了idmap2,所以它还会决定到底使用idmap这个bin还是service_manager中对应的idmap服务来生成,关于idmap相关的我们后面会详说。
  • 模块(3),就是一个测试模块,在测试的时候,会直接和模块(2)交互;在实际的使用中,会被模块(1)取代。

OverlayManagerSettings

        OverlayManagerSettings这个类主要负责overlay包信息的维护,包括内存中的信息,以及持久化信息,并且保持两者同步。在说它之前,我先看一下持久化信息,也就是/data/system/overlays.xml:

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<overlays version="3">
    <item packageName="com.android.systemui.auto_generated_rro_vendor__"
        userId="0" 
        targetPackageName="com.android.systemui" 
        baseCodePath="/vendor/overlay/SystemUI__auto_generated_rro_vendor.apk"
        state="6" 
        isEnabled="true" 
        isStatic="true"
        priority="0" />
    
    ......

    <item packageName="com.android.theme.icon_pack.filled.systemui"
        userId="0"
        targetPackageName="com.android.systemui"
        baseCodePath="/product/overlay/IconPackFilledSystemUI/IconPackFilledSystemUIOverlay.apk"
        state="2"
        isEnabled="false"
        isStatic="false"
        priority="1"
        category="android.theme.customization.icon_pack.systemui" />

    ......

</overlays>

        我们可以看到,每一个item,表示一个overlay信息,对应于OverlayManagerSettings.SettingItem类的一个实例。它描述了一个overlay的基本信息,比如overlay包是什么,target包是什么,这个overlay包的状态、优先级等等,我们结合SettingItem详说:

    //frameworks/base/services/core/java/com/android/server/om/OverlayManagerSettings.java
    private static final class SettingsItem {
        /**
         * overlay包要作用于系统的哪个用户
         */
        private final int mUserId;
        /**
         * overlay包的包名
         */
        private final String mPackageName;
        /**
         * target包的包名
         */
        private final String mTargetPackageName;
        /**
         * overlay包的目标overlayable,关于overlayable
         * 我们后面会详说,这个是Android 10 引入的,跟overlay策略有关。
         * 这里可以先不用太在意
         */
        private final String mTargetOverlayableName;
        /**
         * overlay包的路径
         */
        private String mBaseCodePath;
        /**
         * overlay包的状态,这里的状态包括是否有target包(考虑target包被卸载的时候)、
         * 是否已经生成了idmap文件、是否正在更新等
         */
        private @OverlayInfo.State int mState;
        /**
         * overlay包是否被enable了
         */
        private boolean mIsEnabled;
        
        /**
         * 用来将一个SettingItem转化为一个OverlayInfo
         * 为什么要有这么个转换呢?
         * 我的理解是SettingItem是private的,它是不对外公开的,
         * 这个当然是为了安全,防止应用直接接触SettingItem,进而有意
         * 或无意篡改/data/system/overlays.xml,也就是系统的overlay信息
         * 但OverlayInfo是公开的,应用对它的修改不会同步到SettingItem,进而
         * 影响/data/system/overlays.xml
         */
        private OverlayInfo mCache;
        /**
         * overlay包是否是Static的,一个overlay包如果是static的,那么它是不能
         * 被动态enable或者disable的
         */
        private boolean mIsStatic;
        /**
         * overlay包的优先级
         */
        private int mPriority;
        /**
         * overlay包的类别,目前要么是空,要么是CATEGORY_THEME,
         * Google用它来改变Android系统的主题,不过这个和国内
         * 手机厂商普遍的主题市场啥的没关系。
         */
        private String mCategory;
    }

        当我们动态enable或者disable一个overlay包的时候,OMS会通过OverlayManagerSettings的接口,修改里面的信息,并且会把这些信息持久化到/data/system/overlays.xml;当我们开机的时候,OMS会通过OverlayManagerSettings的接口,去读取/data/system/overlays.xml里的数据,构造出一个一个的SettingItem,我们就可以去查询overlay信息了。这两个过程的实现,是OverlayManagerSettings通过persistrestore两个方法实现的,这两个方法则是FastXmlSerializer来将我们的SettingItem转化为xml形式的数据,最终写入xml文件或者将xml文件中的数据解析出来,构造出一个一个的SettingItem,我们就不详说了,感兴趣的话,可以查看frameworks/base/services/core/java/com/android/server/om/OverlayManagerSettings.java

OverlayManagerServiceImpl

        OMS中相关服务的实现主要分为两部分,第一部分在OverlayManagerServiceImpl中,它主要负责维护overlay包的状态信息,生成idmap文件,我们以动态enable一个overlay package为例来走一下大概流程:

        OMS给应用端的接口封装在OverlayManager.java中,应用可以通过Context.getSystemService(Context.OVERLAY_SERVICE)方法得到其实例。

//frameworks/base/core/java/android/content/om/OverlayManager.java

/**
 * Updates OverlayManager state; gets information
 * about installed overlay packages.
 * @hide
 */
@SystemApi
@SystemService(Context.OVERLAY_SERVICE)
public class OverlayManager {

    /**
     * Binder代理,其实现为system_server进程中的OMS
     */
    private final IOverlayManager mService;
    
    private final Context mContext;


                            /*...省略部分代码...*/


    /**
     * 动态enable或者disable一个overlay package
     * @param packageName 要操作的overlay package的包名
     * @param enable true or false
     * @user 这个操作针对的是哪个用户
     */
    @SystemApi
    @RequiresPermission(anyOf = {
            "android.permission.INTERACT_ACROSS_USERS",
            "android.permission.INTERACT_ACROSS_USERS_FULL"
    })
    public void setEnabled(@NonNull final String packageName, final boolean enable,
            @NonNull UserHandle user) {
        try {
            if (!mService.setEnabled(packageName, enable, user.getIdentifier())) {
                throw new IllegalStateException("setEnabled failed");
            }
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
        return;
    }

                /*...省略部分代码...*/

}

        这个逻辑非常简单,就是OverlayManager通过mService这个binder代理,去访问OMS完成具体的操作。不过这里需要说明的是,虽然IOverlayManager.aidl里定义了很多接口方法,但是OverlayManager所提供的却非常少,所以如果我们想要访问更多的接口方法,建议使用如下方式来访问:

IOverlayManager mService = ServiceManager.getService(Context.OVERLAY_SERVICE)

        我们直接使用mService这个代理,就可以访问IOverlayManager.aidl里定义的所有接口方法了。另外,我们也看到了,OverlayManager是隐藏的系统API,一般应用是不能直接访问的。setEnabled方法在OMS中的实现如下:

        //frameworks/base/services/core/java/com/android/server/om/OverlayManagerService.java
        @Override
        public boolean setEnabled(@Nullable final String packageName, final boolean enable,
                int userId) throws RemoteException {
            try {
                /**trace、权限检查、合法性检查相关的代码,略去**/

                final long ident = Binder.clearCallingIdentity();
                try {
                    synchronized (mLock) {
                        //调用了mImpl的对应方法,mImpl是OverlayManagerServiceImpl类的实例
                        return mImpl.setEnabled(packageName, enable, userId);
                    }
                } finally {
                    Binder.restoreCallingIdentity(ident);
                }
            } finally {
                traceEnd(TRACE_TAG_RRO);
            }
        }

        这个逻辑也很简单,就是调用OMS实现类,不过我们看到在调用实现类对象的对应方法前后,有final long ident = Binder.clearCallingIdentity();Binder.restoreCallingIdentity(ident);这两行代码。这种现象在Android Framework的代码中是非常常见的,为什么要有这两行代码呢?我们假设这个setEnable方法是从某个应用通过binder调到OMS的,也就是说,这个setEnable方法,是在binder线程中执行的binder方法。那么我们通过Binder.getCallingUid()方法(这个方法会在OMS去获取包信息,调用PMS的时候,被PMS用来获取请求方的uid)得到到肯定是这个应用的uid,那这时候使用这个uid有可能就通不过PMS的权限检查。所以,要通过final long ident = Binder.clearCallingIdentity();这行代码,把Binder里面的mCallingPidmCallingUid设置为当前进程,也就是system_server的pid和uid,这样就不会有问题了(这样也是合理的,毕竟这时候已经是OMS代替应用去访问PMS了,而不是应用进程去访问的)。当然,访问结束后还要记得恢复,这就是Binder.restoreCallingIdentity(ident);

        接着,我们看OverlayManagerServiceImpl的实现:

    //framework/base/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
    boolean setEnabled(@NonNull final String packageName, final boolean enable,
            final int userId) {
        //通过PM拿到overlay包的包信息
        final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
        if (overlayPackage == null) {
            return false;
        }

        /**
         * 如果这个overlay包是static的,也就是说AndroidManifest.xml的overlay标签中
         * 设置了android:isStatic="true",那么这个overlay包是不能被动态enable或者disable
         * 的,直接return false,操作失败
         */
        if (overlayPackage.isStaticOverlayPackage()) {
            return false;
        }

        try {
            //获取这个包的overlay信息,mSettings是OverlayManagerSettings的实例
            final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
            /**
             * 设置mSettings中对应SettingItem的mIsEnabled字段
             * 如果要设置的值和里面的值一样,或者这个overlay包是static的
             * 则返回false,否则modified为true
             */
            boolean modified = mSettings.setEnabled(packageName, userId, enable);
            //更新overlay包的状态
            modified |= updateState(oi.targetPackageName, oi.packageName, userId, 0);
            /**
             * 如果这个overlay包的信息有变动,则通知OverlayManagerService
             * OverlayManagerService会负责让这个变动生效
             */
            if (modified) {
                mListener.onOverlaysChanged(oi.targetPackageName, userId);
            }
            return true;
        } catch (OverlayManagerSettings.BadKeyException e) {
            return false;
        }
    }

        这个方法的核心是两个操作:更改OverlayManagerSettings中的overlay信息以及让这个改动生效。先看信息的改动:

    //framework/base/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java

    /**
     * 只有当真正有信息改动的时候才return true
     */
    private boolean updateState(@NonNull final String targetPackageName,
            @NonNull final String overlayPackageName, final int userId, final int flags)
            throws OverlayManagerSettings.BadKeyException {
        //拿到target package和overlay package的包信息
        final PackageInfo targetPackage = mPackageManager.getPackageInfo(targetPackageName, userId);
        final PackageInfo overlayPackage = mPackageManager.getPackageInfo(overlayPackageName,
                userId);

        /**
         * 生成idmap文件,这里要说明的是,为什么不对framework-res.apk的静态overlay包生成idmap
         * 文件。因为它会在native层生成,我们之前的文章中有介绍,这里不再多说
         */
        if (targetPackage != null && overlayPackage != null
                && !("android".equals(targetPackageName)
                        && overlayPackage.isStaticOverlayPackage())) {
            mIdmapManager.createIdmap(targetPackage, overlayPackage, userId);
        }

        boolean modified = false;
        //将包信息更新到OverlayManagerSettings里面去
        if (overlayPackage != null) {
            modified |= mSettings.setBaseCodePath(overlayPackageName, userId,
                    overlayPackage.applicationInfo.getBaseCodePath());
            modified |= mSettings.setCategory(overlayPackageName, userId,
                    overlayPackage.overlayCategory);
        }
        //获OverlayManagerSettings里的状态
        final @OverlayInfo.State int currentState = mSettings.getState(overlayPackageName, userId);
        //计算新的状态
        final @OverlayInfo.State int newState = calculateNewState(targetPackage, overlayPackage,
                userId, flags);
        //状态不一样的话,则更新之
        if (currentState != newState) {
            modified |= mSettings.setState(overlayPackageName, userId, newState);
        }
        return modified;
    }

    //计算overlay包的状态
    private @OverlayInfo.State int calculateNewState(@Nullable final PackageInfo targetPackage,
         @Nullable final PackageInfo overlayPackage, final int userId, final int flags)
         throws OverlayManagerSettings.BadKeyException {/* flags = 0 */

        if ((flags & FLAG_TARGET_IS_BEING_REPLACED) != 0) {
            return STATE_TARGET_IS_BEING_REPLACED;
        }

        if ((flags & FLAG_OVERLAY_IS_BEING_REPLACED) != 0) {
            return STATE_OVERLAY_IS_BEING_REPLACED;
        }

        // assert expectation on overlay package: can only be null if the flags are used
        if (DEBUG && overlayPackage == null) {
            throw new IllegalArgumentException("null overlay package not compatible with no flags");
        }
        // target pacakge 不存在
        if (targetPackage == null) {
            return STATE_MISSING_TARGET;
        }
        //idmap文件没有生成
        if (!mIdmapManager.idmapExists(overlayPackage, userId)) {
            return STATE_NO_IDMAP;
        }
        /**
         * 一般情况下如果一个overlay package 是static的
         * 那么它也应该是enabled,否则它将永远是disable的
         * 这是没有意义的
         */
        if (overlayPackage.isSytaticOverlayPackage()) {
            return STATE_ENABLED_STATIC;
        }
        //以上检查都没问题,才会看它是否是enabled,这个状态的优先级是最低的
        final boolean enabled = mSettings.getEnabled(overlayPackage.packageName, userId);
        return enabled ? STATE_ENABLED : STATE_DISABLED;
    }

        对于我们的setEnabled流程而言,信息的改动其实就是修改OverlayManagerSettings中对应item的信息,然后计算并更新它的状态。需要说明的是,当overlay包或者target包改变的时候(比如overlay包或者target包被删除、改变、增加等),都会触发updateState方法,所以它的实现相对复杂些。另外,在这个过程中还涉及到了idmap文件的生成和更新,关于idmap我们后面会详说。

        然后就是让这个变动生效了,我们有了overlay包和target包,还有idmap文件,这个时候从原理上来说,我们已经万事俱备了。但是到目前为止,我们操作的都是设置状态信息和生成idmap文件,target包所对应的进程(如果这个包已经在运行了)的AssetManager2还没有重新加载新的overlay包,也就是说,target包所对应的进程的显示效果还是以前的老效果,怎么让它生效呢?答案就在mListener.onOverlaysChanged(oi.targetPackageName, userId);这行代码,mListenerOverlayManagerService在构造OverlayManagerServiceImpl的时候传过来的,它里面的逻辑我们下篇再说。

  • 13
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值