AndroidP RRO overlay (四)
应用重新创建之后,加载overlay资源
我们overlay原理二和三,分别说明了应用createIdmap和如何通知重启应用,这篇就紧接着分析,应用重启之后如何去调用overlay apk的资源。
我们这边假设havc.apk在res/values/colors.xml里面定义了一个color值为
<color name="havc_icon">#fff4b400</color>
然后在 havcOverlay.apk 的 res/values/colors.xml里面也定义了一个color值为
<color name="havc_icon">#0000ff</color>
,假设它们已经做过了createIdmap,然后havc.apk已被重启,那么在layout里面使用就会走以下流程。
frameworks/base/core/java/android/content/res/Resources.java
@ColorInt
@Deprecated
//通过调用getColor(R.id.xxxx),这里拿到一个havc_icon对应的id值
public int getColor(@ColorRes int id) throws NotFoundException {
int color = getColor(id, null);
return color;
}
/**
* Returns a themed color integer associated with a particular resource ID.
* If the resource holds a complex {@link ColorStateList}, then the default
* color from the set is returned.
*
* @param id The desired resource identifier, as generated by the aapt
* tool. This integer encodes the package, type, and resource
* entry. The value 0 is an invalid identifier.
* @param theme The theme used to style the color attributes, may be
* {@code null}.
*
* @throws NotFoundException Throws NotFoundException if the given ID does
* not exist.
*
* @return A single color value in the form 0xAARRGGBB.
*/
@ColorInt
public int getColor(@ColorRes int id, @Nullable Theme theme) throws NotFoundException {
final TypedValue value = obtainTempTypedValue();
try {
final ResourcesImpl impl = mResourcesImpl;
// 这里调到了 ResourcesImpl 里面,会将id对应的#0000ff值写到TypedValue对象的data里面
impl.getValue(id, value, true);
if (value.type >= TypedValue.TYPE_FIRST_INT
&& value.type <= TypedValue.TYPE_LAST_INT) {
return value.data;
} else if (value.type != TypedValue.TYPE_STRING) {
throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id)
+ " type #0x" + Integer.toHexString(value.type) + " is not valid");
}
final ColorStateList csl = impl.loadColorStateList(this, value, id, theme);
return csl.getDefaultColor();
} finally {
releaseTempTypedValue(value);
}
}
frameworks/base/core/java/android/content/res/ResourcesImpl.java
// 从Resources的getColor调到了这里。
// mAssets = new AssetManager()
void getValue(@AnyRes int id, TypedValue outValue, boolean resolveRefs)
throws NotFoundException {
boolean found = mAssets.getResourceValue(id, 0, outValue, resolveRefs);
if (found) {
return;
}
throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id));
}
frameworks/base/core/java/android/content/res/AssetManager.java
// resId = R.id.xxx
// densityDpi = 0;
// outValue 要返回的值
// resolveRefs = false
boolean getResourceValue(@AnyRes int resId, int densityDpi, @NonNull TypedValue outValue,
boolean resolveRefs) {
Preconditions.checkNotNull(outValue, "outValue");
synchronized (this) {
ensureValidLocked();
// 调到native层
final int cookie = nativeGetResourceValue(
mObject, resId, (short) densityDpi, outValue, resolveRefs);
if (cookie <= 0) {
return false;
}
// Convert the changing configurations flags populated by native code.
outValue.changingConfigurations = ActivityInfo.activityInfoConfigNativeToJava(
outValue.changingConfigurations);
if (outValue.type == TypedValue.TYPE_STRING) {
outValue.string = mApkAssets[cookie - 1].getStringFromPool(outValue.data);
}
return true;
}
}
// 定义在AssetManager.java实现在android_util_AssetManager.cpp
private static native int nativeGetResourceValue(long ptr, @AnyRes int resId, short density,
@NonNull TypedValue outValue, boolean resolveReferences);
frameworks/base/core/jni/android_util_AssetManager.cpp
static jint NativeGetResourceValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint resid,
jshort density, jobject typed_value,
jboolean resolve_references) {
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
Res_value value;
ResTable_config selected_config;
uint32_t flags;
// 调到了AssetManager2的GetResource
ApkAssetsCookie cookie =
assetmanager->GetResource(static_cast<uint32_t>(resid), false /*may_be_bag*/,
static_cast<uint16_t>(density), &value, &selected_config, &flags);
if (cookie == kInvalidCookie) {
return ApkAssetsCookieToJavaCookie(kInvalidCookie);
}
uint32_t ref = static_cast<uint32_t>(resid);
if (resolve_references) {
cookie = assetmanager->ResolveReference(cookie, &value, &selected_config, &flags, &ref);
if (cookie == kInvalidCookie) {
return ApkAssetsCookieToJavaCookie(kInvalidCookie);
}
}
return CopyValue(env, cookie, value, ref, flags, &selected_config, typed_value);
}
frameworks/base/libs/androidfw/AssetManager2.cpp
ApkAssetsCookie AssetManager2::GetResource(uint32_t resid, bool may_be_bag,
uint16_t density_override, Res_value* out_value,
ResTable_config* out_selected_config,
uint32_t* out_flags) const {
FindEntryResult entry;
// 这里是重点findEntry里面会找target apk对应的overlay apk资源
ApkAssetsCookie cookie =
FindEntry(resid, density_override, false /* stop_at_first_match */, &entry);
if (cookie == kInvalidCookie) {
return kInvalidCookie;
}
if (dtohs(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) {
if (!may_be_bag) {
LOG(ERROR) << base::StringPrintf("Resource %08x is a complex map type.", resid);
return kInvalidCookie;
}
// Create a reference since we can't represent this complex type as a Res_value.
out_value->dataType = Res_value::TYPE_REFERENCE;
out_value->data = resid;
*out_selected_config = entry.config;
*out_flags = entry.type_flags;
return cookie;
}
const Res_value* device_value = reinterpret_cast<const Res_value*>(
reinterpret_cast<const uint8_t*>(entry.entry) + dtohs(entry.entry->size));
// 这里会对具体的一些值进行赋值
out_value->copyFrom_dtoh(*device_value);
// Convert the package ID to the runtime assigned package ID.
entry.dynamic_ref_table->lookupResourceValue(out_value);
*out_selected_config = entry.config;
*out_flags = entry.type_flags;
return cookie;
}
ApkAssetsCookie AssetManager2::FindEntry(uint32_t resid, uint16_t density_override,
bool /*stop_at_first_match*/,
FindEntryResult* out_entry) const {
// Might use this if density_override != 0.
ResTable_config density_override_config;
// Select our configuration or generate a density override configuration.
const ResTable_config* desired_config = &configuration_;
if (density_override != 0 && density_override != configuration_.density) {
density_override_config = configuration_;
density_override_config.density = density_override;
desired_config = &density_override_config;
}
if (!is_valid_resid(resid)) {
LOG(ERROR) << base::StringPrintf("Invalid ID 0x%08x.", resid);
return kInvalidCookie;
}
const uint32_t package_id = get_package_id(resid);
const uint8_t type_idx = get_type_id(resid) - 1;
const uint16_t entry_idx = get_entry_id(resid);
const uint8_t package_idx = package_ids_[package_id];
// LOG(ERROR) << base::StringPrintf("lisiwei package ID %02x found for ID 0x%08x.", package_id, resid);
if (package_idx == 0xff) {
// LOG(ERROR) << base::StringPrintf("No package ID %02x found for ID 0x%08x.", package_id, resid);
return kInvalidCookie;
}
const PackageGroup& package_group = package_groups_[package_idx];
const size_t package_count = package_group.packages_.size();
// LOG(ERROR) << base::StringPrintf("lisiwei package_count = 0x%08x", package_count);
ApkAssetsCookie best_cookie = kInvalidCookie;
const LoadedPackage* best_package = nullptr;
const ResTable_type* best_type = nullptr;
const ResTable_config* best_config = nullptr;
ResTable_config best_config_copy;
uint32_t best_offset = 0u;
uint32_t type_flags = 0u;
// If desired_config is the same as the set configuration, then we can use our filtered list
// and we don't need to match the configurations, since they already matched.
const bool use_fast_path = desired_config == &configuration_;
// LOG(INFO) << "package_count = " << package_count;
for (size_t pi = 0; pi < package_count; pi++) {
const ConfiguredPackage& loaded_package_impl = package_group.packages_[pi];
const LoadedPackage* loaded_package = loaded_package_impl.loaded_package_;
ApkAssetsCookie cookie = package_group.cookies_[pi];
// If the type IDs are offset in this package, we need to take that into account when searching
// for a type.
const TypeSpec* type_spec = loaded_package->GetTypeSpecByTypeIndex(type_idx);
if (UNLIKELY(type_spec == nullptr)) {
continue;
}
uint16_t local_entry_idx = entry_idx;
// If there is an IDMAP supplied with this package, translate the entry ID.
if (type_spec->idmap_entries != nullptr) {
// 这里会根据target apk local_entry_idx去重新赋值为overlay apk的值
if (!LoadedIdmap::Lookup(type_spec->idmap_entries, local_entry_idx, &local_entry_idx)) {
// There is no mapping, so the resource is not meant to be in this overlay package.
continue;
}
}
type_flags |= type_spec->GetFlagsForEntryIndex(local_entry_idx);
// If the package is an overlay, then even configurations that are the same MUST be chosen.
const bool package_is_overlay = loaded_package->IsOverlay();
const FilteredConfigGroup& filtered_group = loaded_package_impl.filtered_configs_[type_idx];
if (use_fast_path) {
const std::vector<ResTable_config>& candidate_configs = filtered_group.configurations;
const size_t type_count = candidate_configs.size();
for (uint32_t i = 0; i < type_count; i++) {
const ResTable_config& this_config = candidate_configs[i];
// We can skip calling ResTable_config::match() because we know that all candidate
// configurations that do NOT match have been filtered-out.
if ((best_config == nullptr || this_config.isBetterThan(*best_config, desired_config)) ||
(package_is_overlay && this_config.compare(*best_config) == 0)) {
// The configuration matches and is better than the previous selection.
// Find the entry value if it exists for this configuration.
const ResTable_type* type_chunk = filtered_group.types[i];
const uint32_t offset = LoadedPackage::GetEntryOffset(type_chunk, local_entry_idx);
if (offset == ResTable_type::NO_ENTRY) {
continue;
}
best_cookie = cookie;
best_package = loaded_package;
best_type = type_chunk;
best_config = &this_config;
best_offset = offset;
}
}
} else {
// This is the slower path, which doesn't use the filtered list of configurations.
// Here we must read the ResTable_config from the mmapped APK, convert it to host endianness
// and fill in any new fields that did not exist when the APK was compiled.
// Furthermore when selecting configurations we can't just record the pointer to the
// ResTable_config, we must copy it.
const auto iter_end = type_spec->types + type_spec->type_count;
for (auto iter = type_spec->types; iter != iter_end; ++iter) {
ResTable_config this_config;
this_config.copyFromDtoH((*iter)->config);
if (this_config.match(*desired_config)) {
if ((best_config == nullptr || this_config.isBetterThan(*best_config, desired_config)) ||
(package_is_overlay && this_config.compare(*best_config) == 0)) {
// The configuration matches and is better than the previous selection.
// Find the entry value if it exists for this configuration.
const uint32_t offset = LoadedPackage::GetEntryOffset(*iter, local_entry_idx);
if (offset == ResTable_type::NO_ENTRY) {
continue;
}
best_cookie = cookie;
best_package = loaded_package;
best_type = *iter;
best_config_copy = this_config;
best_config = &best_config_copy;
best_offset = offset;
}
}
}
}
}
/home/aiways/worksapce/hikey/frameworks/base/libs/androidfw/ResourceTypes.cpp
// 在这里给out_value的data赋值
void Res_value::copyFrom_dtoh(const Res_value& src)
{
size = dtohs(src.size);
res0 = src.res0;
dataType = src.dataType;
data = dtohl(src.data);
}
我这里以上RRO原理二 三 四基本就讲完了,具体流程分为三部分,第一部分就是createIdmap,第二部分就是重启应用,第三部分就是应用重新加载被overlay的资源,可能有些细节讲的不够详细,但大体流程就这样了,有问题可以私信交流。