resource.arsc二进制内容解析 之 Dynamic package reference
目录
1、加载Theme出错
2、aapt中的特殊处理
3、RES_TABLE_LIBRARY_TYPE
4、dynamicRefTable位置
5、验证dynamicRefTable
6、总结
1、加载Theme出错
这是一篇补充文章,在做动态替换resId的过程中,我发现bag类型的ResTable_entry在使用过程中存在问题。比如style,其parent解析一直有问题,日志如下:
W/ResourceType: Failed resolving bag parent id 0x7d090062W/ResourceType: Attempt to retrieve bag 0x7d090114 which is invalid or in a cycle.
经过一些粗暴的尝试,发现解决不了问题,看来需要祭出绝招了——看源码。
ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag,
uint32_t* outTypeSpecFlags) const
{...status_t err = grp->dynamicRefTable.lookupResourceId(&resolvedParent);if (err != NO_ERROR) {ALOGE("Failed resolving bag parent id 0x%08x", parent);return UNKNOWN_ERROR;}...
}
status_t DynamicRefTable::lookupResourceId(uint32_t* resId) const {uint32_t res = *resId;size_t packageId = Res_GETPACKAGE(res) + 1;if (packageId == APP_PACKAGE_ID && !mAppAsLib) {// No lookup needs to be done, app package IDs are absolute.return NO_ERROR;}if (packageId == 0 || (packageId == APP_PACKAGE_ID && mAppAsLib)) {*resId = (0xFFFFFF & (*resId)) | (((uint32_t) mAssignedPackageId) << 24);return NO_ERROR;}// Do a proper lookup.uint8_t translatedId = mLookupTable[packageId];if (translatedId == 0) {ALOGW("DynamicRefTable(0x%02x): No mapping for build-time package ID 0x%02x.",(uint8_t)mAssignedPackageId, (uint8_t)packageId);for (size_t i = 0; i < 256; i++) {if (mLookupTable[i] != 0) {ALOGW("e[0x%02x] -> 0x%02x", (uint8_t)i, mLookupTable[i]);}}return UNKNOWN_ERROR;}*resId = (res & 0x00ffffff) | (((uint32_t) translatedId) << 24);return NO_ERROR;
}
2、aapt中的特殊处理
https://github.com/aosp-mirror/platform_frameworks_base/blob/master/tools/aapt/ResourceTable.cpp
status_t ResourceTable::flatten(Bundle* bundle, const sp<const ResourceFilter>& filter,const sp<AaptFile>& dest,const bool isBase)
{...// The libraries this table references.Vector<sp<Package> > libraryPackages;const ResTable& table = mAssets->getIncludedResources();const size_t basePackageCount = table.getBasePackageCount();for (size_t i = 0; i < basePackageCount; i++) {size_t packageId = table.getBasePackageId(i);String16 packageName(table.getBasePackageName(i));if (packageId > 0x01 && packageId != 0x7f &&packageName != String16("android")) {libraryPackages.add(sp<Package>(new Package(packageName, packageId)));}}...if (isBase) {status_t err = flattenLibraryTable(data, libraryPackages);if (err != NO_ERROR) {fprintf(stderr, "ERROR: failed to write library table\n");return err;}}...}
可以看到当packageId不是0x7F,会新建一个Package结构存入libraryPackages,那么这个libraryPackages有啥用?
我们继续往下看发现flattenLibraryTable函数使用了它,这个函数源码如下:
status_t ResourceTable::flattenLibraryTable(const sp<AaptFile>& dest, const Vector<sp<Package> >& libs) {// Write out the library table if necessaryif (libs.size() > 0) {if (kIsDebug) {fprintf(stderr, "Writing library reference table\n");}const size_t libStart = dest->getSize();const size_t count = libs.size();ResTable_lib_header* libHeader = (ResTable_lib_header*) dest->editDataInRange(libStart, sizeof(ResTable_lib_header));memset(libHeader, 0, sizeof(*libHeader));libHeader->header.type = htods(RES_TABLE_LIBRARY_TYPE);libHeader->header.headerSize = htods(sizeof(*libHeader));libHeader->header.size = htodl(sizeof(*libHeader) + (sizeof(ResTable_lib_entry) * count));libHeader->count = htodl(count);// Write the library entriesfor (size_t i = 0; i < count; i++) {const size_t entryStart = dest->getSize();sp<Package> libPackage = libs[i];if (kIsDebug) {fprintf(stderr, " Entry %s -> 0x%02x\n",String8(libPackage->getName()).string(),(uint8_t)libPackage->getAssignedId());}ResTable_lib_entry* entry = (ResTable_lib_entry*) dest->editDataInRange(entryStart, sizeof(ResTable_lib_entry));memset(entry, 0, sizeof(*entry));entry->packageId = htodl(libPackage->getAssignedId());strcpy16_htod(entry->packageName, libPackage->getName().string());}}return NO_ERROR;
}
可以看到向resource.arsc文件中写入了一个RES_TABLE_LIBRARY_TYPE类型的数据结构。
3、RES_TABLE_LIBRARY_TYPE
chunk | type | note |
---|---|---|
Table header | RES_TABLE_TYPE = 0x002 | |
Resource strings | RES_STRING_POOL_TYPE = 0x001 | e.g. 'Hello World!' |
Package header | RES_TABLE_PACKAGE_TYPE = 0x200 | Rewrite entry: package id |
Type strings | RES_STRING_POOL_TYPE = 0x001 | e.g. 'attr', 'layout', etc. |
Key strings | RES_STRING_POOL_TYPE = 0x001 | e.g. 'activity_main', etc. |
DynamicRefTable | RES_TABLE_LIBRARY_TYPE = 0x0203 | Insert entry for Android 5.0+ |
Type spec | RES_TABLE_TYPE_SPEC_TYPE = 0x0202 | |
Type info | RES_TABLE_TYPE_TYPE = 0x0201 | Rewrite entry: resource entry value |
In Android 5.0+ needs to set the dynamicRefTable for lookup bag parent.
5.0以上需要对二进制文件加入“资源包id映射”数据段,以使得能正确查找到主题等bag的parent。
根据上面的我们大概知道这个“资源包id映射”是5.0之后才加入的,所以网上那么比较陈旧的文章中没有提及。它的作用就是正确的查找bag类型Entry数据的parent。
(至于什么是bag类型,请阅读《resource.arsc二进制内容解析 之 RES_TABLE_TYPE_TYPE》)
这样我们就知道了,如果resId使用默认的0x7F,则不存在问题;如果不使用默认的0x7F,而且还缺少dynamicRefTable这个映射,则查找parent会出问题。
同时,当用aapt编译资源时,如果resId的packageId不是0x7F,才会添加dynamicRefTable。
由于我们是在resource.arsc生产后才去修改,所以aapt编译时并未加入dynamicRefTable,这就是问题所在,我们需要手动加入它。
那么我们就需要知道它的结构,如下:
struct ResTable_lib_header
{struct ResChunk_header header;// The number of shared libraries linked in this resource table.uint32_t count;
};
ResChunk_header结构体如下:
struct ResChunk_header { //当前这个chunk的类型 uint16_t type; //当前这个chunk的头部大小 uint16_t headerSize; //当前这个chunk的大小 uint32_t size; };
这个结构体就不细说了,其中type就是RES_TABLE_LIBRARY_TYPE
紧接着header,就是映射表了,每一个映射都是一个ResTable_lib_entry结构,一共有count个
struct ResTable_lib_entry
{// The package-id this shared library was assigned at build time.// We use a uint32 to keep the structure aligned on a uint32 boundary.uint32_t packageId;// The package name of the shared library. \0 terminated.char16_t packageName[128];
};
映射结构也很简单,packageId+packageName,但是注意packageId占固定4byte大小,而packageName则占固定256byte大小(2byte一个字符,一共128个字符),当然一般packageName都不会这么长,多余的用0补充。
4、dynamicRefTable位置
上面我们知道了dynamicRefTable的结构,那么它在文件中处于什么位置呢?又与哪些数据有联系?
根据网上的神图我重新做了一张更完整更准确的结构图,补充了dynamicRefTable,如下:
这样看到比较清晰了,dynamicRefTable是整个Package结构中的一部分,与Package中的其他结构是并列的,所以不会影响。但是加入dynamicRefTable会影响Package头部中的块大小,以及文件header中的文件大小。
那么如果下面还有另外一个Package数据块,则会有影响么?
答案是不会,因为与位置相关的数据,比如偏移量,一般都是相对于该数据块头部的。
5、验证dynamicRefTable
这样dynamicRefTable的结构就分析完了。那么我们如何知道resource.arsc文件中是否存在这种结构?
我们可以使用aapt反编译一下打包好的apk
./aapt d resources /Users/.../app-debug.apk
会得到如下数据:
Package Groups (1)
Package Group 0 id=0x6d packageCount=1 name=com.example.bennu.testapp
DynamicRefTable entryCount=1:
0x6d -> com.example.bennu.testapp
Package 0 id=0x6d name=com.example.bennu.testapp
type 1 configCount=2 entryCount=12
spec resource 0x6d020000 com.example.bennu.testapp:drawable/arrow: flags=0x00000000
spec resource 0x6d020001 com.example.bennu.testapp:drawable/fio: flags=0x00000000
spec resource 0x6d020002 com.example.bennu.testapp:drawable/fit: flags=0x00000000
spec resource 0x6d020003 com.example.bennu.testapp:drawable/fith: flags=0x00000000
spec resource 0x6d020004 com.example.bennu.testapp:drawable/fo: flags=0x00000000
spec resource 0x6d020005 com.example.bennu.testapp:drawable/ft: flags=0x00000000
spec resource 0x6d020006 com.example.bennu.testapp:drawable/fth: flags=0x00000000
spec resource 0x6d020007 com.example.bennu.testapp:drawable/test_bg_java: flags=0x00000000
spec resource 0x6d020008 com.example.bennu.testapp:drawable/test_bg_xml: flags=0x00000000
spec resource 0x6d020009 com.example.bennu.testapp:drawable/xo: flags=0x00000000
spec resource 0x6d02000a com.example.bennu.testapp:drawable/xt: flags=0x00000000
spec resource 0x6d02000b com.example.bennu.testapp:drawable/xth: flags=0x00000000
config (default):
resource 0x6d020007 com.example.bennu.testapp:drawable/test_bg_java: t=0x03 d=0x00000000 (s=0x0008 r=0x00)
resource 0x6d020008 com.example.bennu.testapp:drawable/test_bg_xml: t=0x03 d=0x00000001 (s=0x0008 r=0x00)
config xhdpi-v4:
resource 0x6d020000 com.example.bennu.testapp:drawable/arrow: t=0x03 d=0x00000003 (s=0x0008 r=0x00)
resource 0x6d020001 com.example.bennu.testapp:drawable/fio: t=0x03 d=0x00000004 (s=0x0008 r=0x00)
resource 0x6d020002 com.example.bennu.testapp:drawable/fit: t=0x03 d=0x00000005 (s=0x0008 r=0x00)
resource 0x6d020003 com.example.bennu.testapp:drawable/fith: t=0x03 d=0x00000006 (s=0x0008 r=0x00)
resource 0x6d020004 com.example.bennu.testapp:drawable/fo: t=0x03 d=0x00000007 (s=0x0008 r=0x00)
resource 0x6d020005 com.example.bennu.testapp:drawable/ft: t=0x03 d=0x00000008 (s=0x0008 r=0x00)
resource 0x6d020006 com.example.bennu.testapp:drawable/fth: t=0x03 d=0x00000009 (s=0x0008 r=0x00)
resource 0x6d020009 com.example.bennu.testapp:drawable/xo: t=0x03 d=0x0000000a (s=0x0008 r=0x00)
resource 0x6d02000a com.example.bennu.testapp:drawable/xt: t=0x03 d=0x0000000b (s=0x0008 r=0x00)
resource 0x6d02000b com.example.bennu.testapp:drawable/xth: t=0x03 d=0x0000000c (s=0x0008 r=0x00)
可以看到有dynamicRefTable的相关数据,如果没有则表示不存在这类数据。
6、总结
dynamicRefTable的存在很重要,影响了bag类数据的parent部分。但是有一个前提,即资源索引不使用默认的0x7F。所以在正常编译时我们不需要特别去注意它,但是如果我们手动干预或后期修改了资源索引,就不得不考虑它了。
resource.arsc二进制内容解析 之 Dynamic package reference相关推荐
- resource.arsc二进制内容解析 之 RES_TABLE_TYPE_TYPE (Config List)
目录 1.resource.arsc结构 2.RES_TABLE_TYPE_TYPE 3.ResTable_type 4.ResTable_entry偏移数组 5.ResTable_entry(非ba ...
- Resource.arsc文件格式解析
Resource.arsc文件格式 借用了互联网一些资料,记录在此. 准备工作 我们在使用apktool工具进行反编译的时候,会发现有一个:res/values/public.xml这个文件: 我们查 ...
- 一文读懂resource.arsc文件结构
resource.arsc文件结构 概述 arsc文件作用 chunk arsc文件结构 Chunk头结构 ResTable_header StringPool Package `ResTable_t ...
- 关于Redis 二进制内容的 可视化尝试
关于Redis 二进制内容的 可视化尝试 二进制内容的 能否可视化? 网上的资料比较少啊! ----------------------------------------------------- ...
- linux 可执行文件_linux中ELF二进制程序解析
0. 简介 在Linux系统的可执行文件(ELF文件)中,开头是一个文件头,用来描述程序的布局,整个文件的属性等信息,包括文件是否可执行.静态还是动态链接及入口地址等信息:如下图所示: 程序文件中包含 ...
- 修改MP4文件二进制内容,实现安卓Camera2旋转录制视频画面功能
Camera2比起Camera的自定义程度更高一点,比如可以同时输出多个视频流分别用于显示预览画面和录制视频.但是!不同于Camera可以直接使用setOrientation方法直接设置视频旋转角度, ...
- No resource identifier found for attribute 'headerLayout' in package错误解决方法
我在使用NavigationView这个控件的时候出现了以下的错误: Error:(2) No resource identifier found for attribute 'headerLayou ...
- Vert.x + Protobuf二进制协议解析
这一期介绍如何解析二进制私有协议. 先说几句题外话,就是绝大多数情况下,可能根本用不着使用私有二进制协议,除非你的业务对性能极其敏感,否则HTTP足矣. 协议 我们的协议非常简单,先是一个4字节的整数 ...
- 从站寄存器EEPROM内容解析之SII(Slave Information Interface)
从站寄存器EEPROM内容解析之SII(Slave Information Interface) 一.EEPROM 的构造如下表所示,ESI 使用字编址.这里特别注意是字编址,那么实际上一个16进制的 ...
最新文章
- 解题报告(一)快速沃尔什变换FWT(ACM / OI)超高质量题解
- 814. Binary Tree Pruning
- 如何在VB例程中接收自定义消息
- 【原】关于AdaBoost的一些再思考
- c/c++连接mysql数据库设置及乱码问题(vs2013连接mysql数据库,使用Mysql API操作数据库)...
- 图嵌入综述 (arxiv 1709.07604) 译文第三章
- WPF教程尝试(修正部分格式)
- Mahout的一些推荐算法
- py thon 多线程(转一篇好文章)
- 二进制转十进制 十进制转二进制
- Kaldi AMI数据集脚本学习5---AMI mono phone文件 40.mdl分析
- Node.js之Stream可读流readable
- 【实用技巧】文件MD5修改方法
- 华硕怎么安装linux系统教程,华硕Eee PC下安装Puppy Linux系统(图)
- open gl太阳系简单实现
- 智源社区AI周刊No.101:DeepMind推出AlphaTensor登Nature封面;stateof.ai发布AI情况报告...
- ubuntu如何降级到之前的版本
- 什么是SQL注入式攻击!如何防范SQL注入式攻击?
- ICPLAZA凭BFT+POS快速“出圈” 打造更繁荣的生态系统
- uniapp开发APP和微信小程序——使用高德实现定位
热门文章
- Large-Scale Named Entity Disambiguation Based on Wikipedia Data
- mybatis 模糊查询
- C#中窗体的close,dispose,以及application.exit()的区别
- .NET访问PI数据库
- 2018 浅谈前端面试那些事
- 【bzoj1304】[CQOI2009]叶子的染色 树形dp
- PAT-乙级-1020. 月饼 (25)
- UVa OJ 120
- vue-router 在项目中的使用
- Docker系列06—基于容器制作镜像并上传到Docker Registry