Android 12系统源码_RRO机制(一)Runtime Resource Overlay机制实践

发布于:2024-12-06 ⋅ 阅读:(28) ⋅ 点赞:(0)

前言

Android的RRO(Runtime Resource Overlay)机制允许开发者在运行时替换或重写系统资源,例如布局、图标、字符串等。这个机制的目标是为了支持设备定制和主题化,特别是在不修改系统源代码的情况下。RRO通过在系统的资源上叠加一个额外的资源层,来实现个性化和品牌定制,而不需要修改原有的资源文件。通常,RRO被用于OEM厂商在其设备上定制UI和功能,或者在Android版本升级时保持兼容性。

RRO的工作原理是,通过在运行时将资源包(如APK文件)作为叠加层加载到系统中。这样,当应用请求某个资源时,Android首先检查叠加层中的资源,如果存在,则使用叠加层的版本,而不是默认的系统资源。RRO机制的优点是它减少了系统定制的复杂性,同时避免了对基础系统文件的修改。

这种机制最早是在Android 6.0(Marshmallow)中引入的,并且随着Android版本的升级得到了优化和增强。

一、RRO 换肤实践

1.1 创建目标应用

首先新建一个需要被换肤的应用工程RROApplication,该工程中只有一个简单的MainActivity,加载了一个activity_main.xml的布局文件,有三个TextView,引用了三个颜色资源,我们的目标是利用RRO机制替换这三个TextView的颜色。这个工程所产生的应用我们将其称为【目标应用】。
目标应用

1.2 创建资源包应用

1.2.1 新建资源包应用工程

再次新建一个独立的app工程,这个应用存在的意义就是放置【目标应用】的资源文件,它虽然是一个应用工程,但是不包含任何逻辑和业务,就只是用于存放资源文件。这个工程所产生的应用我们将其称为【资源包】。
资源包

1.2.2 配置 AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.rro.overlay">

    <overlay
        android:targetName="ThemeResources"
        android:priority="1"
        android:targetPackage="com.example.rro" />

    <application android:hasCode="false"/>

</manifest>

如果某个APK 的AndroidManifest.xml中包含 标记作为 标记的子项,该APK将被视为『资源包』 。

-【必需设定】android:targetPackage 用于指明 RRO 想要替换的『目标应用』。
-【必需设定】android:hasCode必须设定为 false。由于无法替换代码,因此 RRO 无法使用 DEX 文件。
-【非必需设定】android:targetName 用于指明 RRO 『目标应用』的可替换资源子集的名称。如果『目标应用』没有定义可替换资源集,此属性就不需要设定。

1.3 可替换资源集标签overlayable

1.3.1 目标应用使用overlayable标签标记允许被替换的资源

在 Android 10 或更高版本中,『目标应用』可以使用 标签公开一组允许 RRO 替换的资源,未被公开的资源,则不允许 RRO 替换。

<resources>
    <overlayable name="ThemeResources">
        <policy type="public">
            <item type="color" name="purple_200" />
            <item type="color" name="purple_500" />
            <item type="string" name="main_content" />
        </policy>
    </overlayable>
</resources>

一个 APK 可以定义多个overlayable 标签,但每个标签必须在该软件包中具有唯一的名称

1.3.2 资源包指定目标应用中允许被替换的标签

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.rro.overlay">

    <overlay
        android:targetName="ThemeResources"
        android:priority="1"
        android:targetPackage="com.example.rro" />

    <application android:hasCode="false"/>

</manifest>

当目标应用定义 标签的时候,资源包的需满足以下条件:

  • 必须指定 targetName
  • 只能替换 标记中列出的资源。
  • 只能定位到一个 名称。

1.3.3 限制策略

使用标记可以在目标应用中对可替换资源施加限制。type属性指定叠加层必须满足哪些政策的要求才能替换包含的资源。支持以下类型:

  • public:任何叠加层均可替换相应资源。
  • system:系统分区上的任何叠加层均可替换相应资源。
  • vendor:vendor 分区上的任何叠加层均可替换相应资源。
  • product:product 分区上的任何叠加层均可替换相应资源。
  • signature:使用与目标 APK 相同的签名进行签名的任何叠加层均可替换相应资源。
<overlayable name="ThemeResources">
   <policy type="vendor" >
         <item type="string" name="main_content" />
   </policy>
   <policy type="product|signature"  >
        <item type="color" name="purple_200" />
   </policy>
</overlayable>

如需指定多个政策,请使用竖线 (|) 作为分隔符。 如果指定了多个政策,叠加层只需满足一个政策的要求即可替换 标记中列出的资源。

1.4 定义资源映射

1.4.1 Android 10或以下版本

在 Android 10 或更低版本中,系统是根据资源的名称进行资源替换,所以我们只需要在资源包中将需要替换的资源定义好即可。
Android11以前

1.4.2 Android 11或以上版本

在 Android 11 或更高版本中,Google推荐在『资源包』的res/xml 目录中创建一个文件overlays.xml,枚举出应覆盖的『目标应用』的资源值及其替换值。
Android11以后

注意,target标签中的color并没有带上@标记,他实际上仅仅是一个字符串而不是引用

然后在资源包的AndroidManifest.xml 中将android:resourcesMap 属性的值设置为资源映射文件。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.android.target.overlay.eleven">

    <overlay
        android:targetName="ThemeResources"
        android:targetPackage="com.android.target"
        android:resourcesMap="@xml/overlays"/>

    <application
        android:hasCode="false"/>

</manifest>

二、RRO换肤效果展示

2.1 构建安装包

在AndroidStudio中分别编译构建目标应用安装包和资源包应用安装包
安装包

2.2 安装目标应用

在我们安装完目标应用RROApplication-debug.apk之后,打开该应用,默认的显示效果如下所示。
目标应用默认显示效果

2.3 安装资源包应用

在我们安装完资源包应用RROResource-debug.apk之后,重新打开目标应用,会发现目标应用并不会发生什么变化,这是为什么呢?其实这是因为资源包应用的overlay功能开关默认没有被开启所导致的。

通过执行adb shelldumpsys overlay com.example.rro.overlay这条指令,可以得到以下关键信息。

com.example.rro.overlay:0 {
  mPackageName...........: com.example.rro.overlay
  mOverlayName...........: null
  mUserId................: 0
  mTargetPackageName.....: com.example.rro
  mTargetOverlayableName.: ThemeResources
  mBaseCodePath..........: /data/app/~~dxlUScJmLwQ1fBFif8-PNw==/com.example.rro.overlay-1r-PLB3eA9jUEf00J1vPYw==/base.apk
  mState.................: STATE_DISABLED
  mIsEnabled.............: false//注释1
  mIsMutable.............: true
  mPriority..............: 2147483647
  mCategory..............: null
  mIsFabricated..........: false
}
IDMAP OF com.example.rro.overlay
Paths:
    target path  : /data/app/~~B-NqptfXHZZHKfqYzQu-tg==/com.example.rro-8BHPMbRD_6ZPxdM6ZkYu3Q==/base.apk
    overlay path : /data/app/~~dxlUScJmLwQ1fBFif8-PNw==/com.example.rro.overlay-1r-PLB3eA9jUEf00J1vPYw==/base.apk
Debug info:
    W overlay '/data/app/~~dxlUScJmLwQ1fBFif8-PNw==/com.example.rro.overlay-1r-PLB3eA9jUEf00J1vPYw==/base.apk' is not allowed to overlay resource 'color/purple_700' in target: target resource has no overlayable declaration
    W overlay '/data/app/~~dxlUScJmLwQ1fBFif8-PNw==/com.example.rro.overlay-1r-PLB3eA9jUEf00J1vPYw==/base.apk' is not allowed to overlay resource 'color/teal_200' in target: target resource has no overlayable declaration
    W overlay '/data/app/~~dxlUScJmLwQ1fBFif8-PNw==/com.example.rro.overlay-1r-PLB3eA9jUEf00J1vPYw==/base.apk' is not allowed to overlay resource 'color/teal_700' in target: target resource has no overlayable declaration
    W overlay '/data/app/~~dxlUScJmLwQ1fBFif8-PNw==/com.example.rro.overlay-1r-PLB3eA9jUEf00J1vPYw==/base.apk' is not allowed to overlay resource 'color/black' in target: target resource has no overlayable declaration
    W overlay '/data/app/~~dxlUScJmLwQ1fBFif8-PNw==/com.example.rro.overlay-1r-PLB3eA9jUEf00J1vPYw==/base.apk' is not allowed to overlay resource 'color/white' in target: target resource has no overlayable declaration
Mapping://注释2
    0x7f05004a -> color 0xff000000 (color/purple_200)
    0x7f05004b -> color 0xff000000 (color/purple_500)
    0x7f0d001d -> string "RRO替换资源" (string/main_content)

在注释1处可以发现该资源包的overlay功能并没有被开启,因此目标应用对应的UI样式才没有发生变化。
在注释2处可以发现该资源包所要替换的具体资源条目信息。

2.4 为资源包应用开启overlay功能开关

执行cmd overlay enable com.example.rro.overlay可以为资源包开启overlay功能开关
然后再执行adb shelldumpsys overlay com.example.rro.overlay这条指令,可以得到以下关键信息。

dumpsys overlay com.example.rro.overlay
com.example.rro.overlay:0 {
  mPackageName...........: com.example.rro.overlay
  mOverlayName...........: null
  mUserId................: 0
  mTargetPackageName.....: com.example.rro
  mTargetOverlayableName.: ThemeResources
  mBaseCodePath..........: /data/app/~~dxlUScJmLwQ1fBFif8-PNw==/com.example.rro.overlay-1r-PLB3eA9jUEf00J1vPYw==/base.apk
  mState.................: STATE_ENABLED
  mIsEnabled.............: true//注释1
  mIsMutable.............: true
  mPriority..............: 2147483647
  mCategory..............: null
  mIsFabricated..........: false
}
IDMAP OF com.example.rro.overlay
Paths:
    target path  : /data/app/~~B-NqptfXHZZHKfqYzQu-tg==/com.example.rro-8BHPMbRD_6ZPxdM6ZkYu3Q==/base.apk
    overlay path : /data/app/~~dxlUScJmLwQ1fBFif8-PNw==/com.example.rro.overlay-1r-PLB3eA9jUEf00J1vPYw==/base.apk
Debug info:
    W overlay '/data/app/~~dxlUScJmLwQ1fBFif8-PNw==/com.example.rro.overlay-1r-PLB3eA9jUEf00J1vPYw==/base.apk' is not allowed to overlay resource 'color/purple_700' in target: target resource has no overlayable declaration
    W overlay '/data/app/~~dxlUScJmLwQ1fBFif8-PNw==/com.example.rro.overlay-1r-PLB3eA9jUEf00J1vPYw==/base.apk' is not allowed to overlay resource 'color/teal_200' in target: target resource has no overlayable declaration
    W overlay '/data/app/~~dxlUScJmLwQ1fBFif8-PNw==/com.example.rro.overlay-1r-PLB3eA9jUEf00J1vPYw==/base.apk' is not allowed to overlay resource 'color/teal_700' in target: target resource has no overlayable declaration
    W overlay '/data/app/~~dxlUScJmLwQ1fBFif8-PNw==/com.example.rro.overlay-1r-PLB3eA9jUEf00J1vPYw==/base.apk' is not allowed to overlay resource 'color/black' in target: target resource has no overlayable declaration
    W overlay '/data/app/~~dxlUScJmLwQ1fBFif8-PNw==/com.example.rro.overlay-1r-PLB3eA9jUEf00J1vPYw==/base.apk' is not allowed to overlay resource 'color/white' in target: target resource has no overlayable declaration
Mapping:
    0x7f05004a -> color 0xff000000 (color/purple_200)
    0x7f05004b -> color 0xff000000 (color/purple_500)
    0x7f0d001d -> string "RRO替换资源" (string/main_content)

可以发现注释1处,该资源包的overlay功能开关已经被开启,此事目标应用的显示效果也会发生变化。
在这里插入图片描述

三、启用/停用RRO功能

3.1 使用adb指令

adb shell cmd overlay enable [com.example.rro.overlay] // 开启
adb shell cmd overlay disable [com.example.rro.overlay] // 关闭

使用以上指令可以为特定包名的资源包开启或者关闭overlay功能。

3.2 使用系统api

OverlayManager overlayManager = context.getSystemService(OverlayManager.class);
OverlayInfo overlayInfo = manager.getOverlayInfo("com.example.rro.overlay", UserHandle.CURRENT_OR_SELF);
if(overlayInfo != null){
	//true为开启,false为关闭
	overlayManager.setEnabled("com.example.rro.overlay", true, UserHandle.CURRENT_OR_SELF);
};

可以在系统源码中直接使用OverlayManager提供的API可以启用、停用RRO,但是OverlayManager在公开的Android SDK中并没有提供,如果是使用Gradle构建工程,需要额外使用AOSP源码编译一个framework.jar并引入才可以使用,同时应用签名也需要使用Android系统签名,让应用成为系统级应用。


网站公告

今日签到

点亮在社区的每一天
去签到