Move attribute resolution from core/jni to libandroidfw
Without the entire JNI environment, testing the attribute
resolution code will be much easier and enable safer
refactoring.
Change-Id: I2815cc1e10a694a3b01bc37e191a0d5e9d0e6735
Test: Existing CTS tests pass
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index ef32afee..5a4348e 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -31,7 +31,7 @@
#include "androidfw/Asset.h"
#include "androidfw/AssetManager.h"
-#include "androidfw/AttributeFinder.h"
+#include "androidfw/AttributeResolution.h"
#include "androidfw/ResourceTypes.h"
#include "android_runtime/AndroidRuntime.h"
#include "android_util_Binder.h"
@@ -51,7 +51,6 @@
namespace android {
static const bool kThrowOnBadId = false;
-static const bool kDebugStyles = false;
// ----------------------------------------------------------------------------
@@ -1121,30 +1120,6 @@
theme->dumpToLog();
}
-class XmlAttributeFinder : public BackTrackingAttributeFinder<XmlAttributeFinder, jsize> {
-public:
- explicit XmlAttributeFinder(const ResXMLParser* parser)
- : BackTrackingAttributeFinder(0, parser != NULL ? parser->getAttributeCount() : 0)
- , mParser(parser) {}
-
- inline uint32_t getAttribute(jsize index) const {
- return mParser->getAttributeNameResID(index);
- }
-
-private:
- const ResXMLParser* mParser;
-};
-
-class BagAttributeFinder : public BackTrackingAttributeFinder<BagAttributeFinder, const ResTable::bag_entry*> {
-public:
- BagAttributeFinder(const ResTable::bag_entry* start, const ResTable::bag_entry* end)
- : BackTrackingAttributeFinder(start, end) {}
-
- inline uint32_t getAttribute(const ResTable::bag_entry* entry) const {
- return entry->map.name.ident;
- }
-};
-
static jboolean android_content_AssetManager_resolveAttrs(JNIEnv* env, jobject clazz,
jlong themeToken,
jint defStyleAttr,
@@ -1167,16 +1142,6 @@
return JNI_FALSE;
}
- if (kDebugStyles) {
- ALOGI("APPLY STYLE: theme=0x%" PRIx64 " defStyleAttr=0x%x "
- "defStyleRes=0x%x", themeToken, defStyleAttr, defStyleRes);
- }
-
- ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeToken);
- const ResTable& res = theme->getResTable();
- ResTable_config config;
- Res_value value;
-
const jsize NI = env->GetArrayLength(attrs);
const jsize NV = env->GetArrayLength(outValues);
if (NV < (NI*STYLE_NUM_ENTRIES)) {
@@ -1193,159 +1158,32 @@
const jsize NSV = srcValues == NULL ? 0 : env->GetArrayLength(inValues);
jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0);
- jint* dest = baseDest;
- if (dest == NULL) {
+ if (baseDest == NULL) {
env->ReleasePrimitiveArrayCritical(attrs, src, 0);
return JNI_FALSE;
}
jint* indices = NULL;
- int indicesIdx = 0;
if (outIndices != NULL) {
if (env->GetArrayLength(outIndices) > NI) {
indices = (jint*)env->GetPrimitiveArrayCritical(outIndices, 0);
}
}
- // Load default style from attribute, if specified...
- uint32_t defStyleBagTypeSetFlags = 0;
- if (defStyleAttr != 0) {
- Res_value value;
- if (theme->getAttribute(defStyleAttr, &value, &defStyleBagTypeSetFlags) >= 0) {
- if (value.dataType == Res_value::TYPE_REFERENCE) {
- defStyleRes = value.data;
- }
- }
- }
-
- // Now lock down the resource object and start pulling stuff from it.
- res.lock();
-
- // Retrieve the default style bag, if requested.
- const ResTable::bag_entry* defStyleStart = NULL;
- uint32_t defStyleTypeSetFlags = 0;
- ssize_t bagOff = defStyleRes != 0
- ? res.getBagLocked(defStyleRes, &defStyleStart, &defStyleTypeSetFlags) : -1;
- defStyleTypeSetFlags |= defStyleBagTypeSetFlags;
- const ResTable::bag_entry* const defStyleEnd = defStyleStart + (bagOff >= 0 ? bagOff : 0);
- BagAttributeFinder defStyleAttrFinder(defStyleStart, defStyleEnd);
-
- // Now iterate through all of the attributes that the client has requested,
- // filling in each with whatever data we can find.
- ssize_t block = 0;
- uint32_t typeSetFlags;
- for (jsize ii=0; ii<NI; ii++) {
- const uint32_t curIdent = (uint32_t)src[ii];
-
- if (kDebugStyles) {
- ALOGI("RETRIEVING ATTR 0x%08x...", curIdent);
- }
-
- // Try to find a value for this attribute... we prioritize values
- // coming from, first XML attributes, then XML style, then default
- // style, and finally the theme.
- value.dataType = Res_value::TYPE_NULL;
- value.data = Res_value::DATA_NULL_UNDEFINED;
- typeSetFlags = 0;
- config.density = 0;
-
- // Retrieve the current input value if available.
- if (NSV > 0 && srcValues[ii] != 0) {
- block = -1;
- value.dataType = Res_value::TYPE_ATTRIBUTE;
- value.data = srcValues[ii];
- if (kDebugStyles) {
- ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType, value.data);
- }
- }
-
- if (value.dataType == Res_value::TYPE_NULL) {
- const ResTable::bag_entry* const defStyleEntry = defStyleAttrFinder.find(curIdent);
- if (defStyleEntry != defStyleEnd) {
- block = defStyleEntry->stringBlock;
- typeSetFlags = defStyleTypeSetFlags;
- value = defStyleEntry->map.value;
- if (kDebugStyles) {
- ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data);
- }
- }
- }
-
- uint32_t resid = 0;
- if (value.dataType != Res_value::TYPE_NULL) {
- // Take care of resolving the found resource to its final value.
- ssize_t newBlock = theme->resolveAttributeReference(&value, block,
- &resid, &typeSetFlags, &config);
- if (newBlock >= 0) block = newBlock;
- if (kDebugStyles) {
- ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, value.data);
- }
- } else {
- // If we still don't have a value for this attribute, try to find
- // it in the theme!
- ssize_t newBlock = theme->getAttribute(curIdent, &value, &typeSetFlags);
- if (newBlock >= 0) {
- if (kDebugStyles) {
- ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data);
- }
- newBlock = res.resolveReference(&value, block, &resid,
- &typeSetFlags, &config);
- if (kThrowOnBadId) {
- if (newBlock == BAD_INDEX) {
- jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
- return JNI_FALSE;
- }
- }
- if (newBlock >= 0) block = newBlock;
- if (kDebugStyles) {
- ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, value.data);
- }
- }
- }
-
- // Deal with the special @null value -- it turns back to TYPE_NULL.
- if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
- if (kDebugStyles) {
- ALOGI("-> Setting to @null!");
- }
- value.dataType = Res_value::TYPE_NULL;
- value.data = Res_value::DATA_NULL_UNDEFINED;
- block = -1;
- }
-
- if (kDebugStyles) {
- ALOGI("Attribute 0x%08x: type=0x%x, data=0x%08x", curIdent, value.dataType,
- value.data);
- }
-
- // Write the final value back to Java.
- dest[STYLE_TYPE] = value.dataType;
- dest[STYLE_DATA] = value.data;
- dest[STYLE_ASSET_COOKIE] =
- block != -1 ? reinterpret_cast<jint>(res.getTableCookie(block)) : (jint)-1;
- dest[STYLE_RESOURCE_ID] = resid;
- dest[STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags;
- dest[STYLE_DENSITY] = config.density;
-
- if (indices != NULL && value.dataType != Res_value::TYPE_NULL) {
- indicesIdx++;
- indices[indicesIdx] = ii;
- }
-
- dest += STYLE_NUM_ENTRIES;
- }
-
- res.unlock();
+ ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeToken);
+ bool result = resolveAttrs(theme, defStyleAttr, defStyleRes,
+ (uint32_t*) srcValues, NSV,
+ (uint32_t*) src, NI,
+ (uint32_t*) baseDest,
+ (uint32_t*) indices);
if (indices != NULL) {
- indices[0] = indicesIdx;
env->ReleasePrimitiveArrayCritical(outIndices, indices, 0);
}
env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0);
env->ReleasePrimitiveArrayCritical(inValues, srcValues, 0);
env->ReleasePrimitiveArrayCritical(attrs, src, 0);
-
- return JNI_TRUE;
+ return result ? JNI_TRUE : JNI_FALSE;
}
static jboolean android_content_AssetManager_applyStyle(JNIEnv* env, jobject clazz,
@@ -1370,18 +1208,6 @@
return JNI_FALSE;
}
- if (kDebugStyles) {
- ALOGI("APPLY STYLE: theme=0x%" PRIx64 " defStyleAttr=0x%x defStyleRes=0x%x "
- "xml=0x%" PRIx64, themeToken, defStyleAttr, defStyleRes,
- xmlParserToken);
- }
-
- ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeToken);
- const ResTable& res = theme->getResTable();
- ResXMLParser* xmlParser = reinterpret_cast<ResXMLParser*>(xmlParserToken);
- ResTable_config config;
- Res_value value;
-
const jsize NI = env->GetArrayLength(attrs);
const jsize NV = env->GetArrayLength(outValues);
if (NV < (NI*STYLE_NUM_ENTRIES)) {
@@ -1395,211 +1221,32 @@
}
jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0);
- jint* dest = baseDest;
- if (dest == NULL) {
+ if (baseDest == NULL) {
env->ReleasePrimitiveArrayCritical(attrs, src, 0);
return JNI_FALSE;
}
jint* indices = NULL;
- int indicesIdx = 0;
if (outIndices != NULL) {
if (env->GetArrayLength(outIndices) > NI) {
indices = (jint*)env->GetPrimitiveArrayCritical(outIndices, 0);
}
}
- // Load default style from attribute, if specified...
- uint32_t defStyleBagTypeSetFlags = 0;
- if (defStyleAttr != 0) {
- Res_value value;
- if (theme->getAttribute(defStyleAttr, &value, &defStyleBagTypeSetFlags) >= 0) {
- if (value.dataType == Res_value::TYPE_REFERENCE) {
- defStyleRes = value.data;
- }
- }
- }
-
- // Retrieve the style class associated with the current XML tag.
- int style = 0;
- uint32_t styleBagTypeSetFlags = 0;
- if (xmlParser != NULL) {
- ssize_t idx = xmlParser->indexOfStyle();
- if (idx >= 0 && xmlParser->getAttributeValue(idx, &value) >= 0) {
- if (value.dataType == value.TYPE_ATTRIBUTE) {
- if (theme->getAttribute(value.data, &value, &styleBagTypeSetFlags) < 0) {
- value.dataType = Res_value::TYPE_NULL;
- }
- }
- if (value.dataType == value.TYPE_REFERENCE) {
- style = value.data;
- }
- }
- }
-
- // Now lock down the resource object and start pulling stuff from it.
- res.lock();
-
- // Retrieve the default style bag, if requested.
- const ResTable::bag_entry* defStyleAttrStart = NULL;
- uint32_t defStyleTypeSetFlags = 0;
- ssize_t bagOff = defStyleRes != 0
- ? res.getBagLocked(defStyleRes, &defStyleAttrStart, &defStyleTypeSetFlags) : -1;
- defStyleTypeSetFlags |= defStyleBagTypeSetFlags;
- const ResTable::bag_entry* const defStyleAttrEnd = defStyleAttrStart + (bagOff >= 0 ? bagOff : 0);
- BagAttributeFinder defStyleAttrFinder(defStyleAttrStart, defStyleAttrEnd);
-
- // Retrieve the style class bag, if requested.
- const ResTable::bag_entry* styleAttrStart = NULL;
- uint32_t styleTypeSetFlags = 0;
- bagOff = style != 0 ? res.getBagLocked(style, &styleAttrStart, &styleTypeSetFlags) : -1;
- styleTypeSetFlags |= styleBagTypeSetFlags;
- const ResTable::bag_entry* const styleAttrEnd = styleAttrStart + (bagOff >= 0 ? bagOff : 0);
- BagAttributeFinder styleAttrFinder(styleAttrStart, styleAttrEnd);
-
- // Retrieve the XML attributes, if requested.
- static const ssize_t kXmlBlock = 0x10000000;
- XmlAttributeFinder xmlAttrFinder(xmlParser);
- const jsize xmlAttrEnd = xmlParser != NULL ? xmlParser->getAttributeCount() : 0;
-
- // Now iterate through all of the attributes that the client has requested,
- // filling in each with whatever data we can find.
- ssize_t block = 0;
- uint32_t typeSetFlags;
- for (jsize ii = 0; ii < NI; ii++) {
- const uint32_t curIdent = (uint32_t)src[ii];
-
- if (kDebugStyles) {
- ALOGI("RETRIEVING ATTR 0x%08x...", curIdent);
- }
-
- // Try to find a value for this attribute... we prioritize values
- // coming from, first XML attributes, then XML style, then default
- // style, and finally the theme.
- value.dataType = Res_value::TYPE_NULL;
- value.data = Res_value::DATA_NULL_UNDEFINED;
- typeSetFlags = 0;
- config.density = 0;
-
- // Walk through the xml attributes looking for the requested attribute.
- const jsize xmlAttrIdx = xmlAttrFinder.find(curIdent);
- if (xmlAttrIdx != xmlAttrEnd) {
- // We found the attribute we were looking for.
- block = kXmlBlock;
- xmlParser->getAttributeValue(xmlAttrIdx, &value);
- if (kDebugStyles) {
- ALOGI("-> From XML: type=0x%x, data=0x%08x", value.dataType, value.data);
- }
- }
-
- if (value.dataType == Res_value::TYPE_NULL) {
- // Walk through the style class values looking for the requested attribute.
- const ResTable::bag_entry* const styleAttrEntry = styleAttrFinder.find(curIdent);
- if (styleAttrEntry != styleAttrEnd) {
- // We found the attribute we were looking for.
- block = styleAttrEntry->stringBlock;
- typeSetFlags = styleTypeSetFlags;
- value = styleAttrEntry->map.value;
- if (kDebugStyles) {
- ALOGI("-> From style: type=0x%x, data=0x%08x", value.dataType, value.data);
- }
- }
- }
-
- if (value.dataType == Res_value::TYPE_NULL) {
- // Walk through the default style values looking for the requested attribute.
- const ResTable::bag_entry* const defStyleAttrEntry = defStyleAttrFinder.find(curIdent);
- if (defStyleAttrEntry != defStyleAttrEnd) {
- // We found the attribute we were looking for.
- block = defStyleAttrEntry->stringBlock;
- typeSetFlags = styleTypeSetFlags;
- value = defStyleAttrEntry->map.value;
- if (kDebugStyles) {
- ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data);
- }
- }
- }
-
- uint32_t resid = 0;
- if (value.dataType != Res_value::TYPE_NULL) {
- // Take care of resolving the found resource to its final value.
- ssize_t newBlock = theme->resolveAttributeReference(&value, block,
- &resid, &typeSetFlags, &config);
- if (newBlock >= 0) {
- block = newBlock;
- }
-
- if (kDebugStyles) {
- ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, value.data);
- }
- } else {
- // If we still don't have a value for this attribute, try to find
- // it in the theme!
- ssize_t newBlock = theme->getAttribute(curIdent, &value, &typeSetFlags);
- if (newBlock >= 0) {
- if (kDebugStyles) {
- ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data);
- }
- newBlock = res.resolveReference(&value, block, &resid,
- &typeSetFlags, &config);
- if (kThrowOnBadId) {
- if (newBlock == BAD_INDEX) {
- jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
- return JNI_FALSE;
- }
- }
-
- if (newBlock >= 0) {
- block = newBlock;
- }
-
- if (kDebugStyles) {
- ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, value.data);
- }
- }
- }
-
- // Deal with the special @null value -- it turns back to TYPE_NULL.
- if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
- if (kDebugStyles) {
- ALOGI("-> Setting to @null!");
- }
- value.dataType = Res_value::TYPE_NULL;
- value.data = Res_value::DATA_NULL_UNDEFINED;
- block = kXmlBlock;
- }
-
- if (kDebugStyles) {
- ALOGI("Attribute 0x%08x: type=0x%x, data=0x%08x", curIdent, value.dataType, value.data);
- }
-
- // Write the final value back to Java.
- dest[STYLE_TYPE] = value.dataType;
- dest[STYLE_DATA] = value.data;
- dest[STYLE_ASSET_COOKIE] = block != kXmlBlock ?
- static_cast<jint>(res.getTableCookie(block)) : -1;
- dest[STYLE_RESOURCE_ID] = resid;
- dest[STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags;
- dest[STYLE_DENSITY] = config.density;
-
- if (indices != NULL && value.dataType != Res_value::TYPE_NULL) {
- indicesIdx++;
- indices[indicesIdx] = ii;
- }
-
- dest += STYLE_NUM_ENTRIES;
- }
-
- res.unlock();
+ ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeToken);
+ ResXMLParser* xmlParser = reinterpret_cast<ResXMLParser*>(xmlParserToken);
+ bool result = applyStyle(theme, xmlParser,
+ defStyleAttr, defStyleRes,
+ (uint32_t*) src, NI,
+ (uint32_t*) baseDest,
+ (uint32_t*) indices);
if (indices != NULL) {
- indices[0] = indicesIdx;
env->ReleasePrimitiveArrayCritical(outIndices, indices, 0);
}
env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0);
env->ReleasePrimitiveArrayCritical(attrs, src, 0);
-
- return JNI_TRUE;
+ return result ? JNI_TRUE : JNI_FALSE;
}
static jboolean android_content_AssetManager_retrieveAttributes(JNIEnv* env, jobject clazz,
@@ -1627,8 +1274,6 @@
}
const ResTable& res(am->getResources());
ResXMLParser* xmlParser = (ResXMLParser*)xmlParserToken;
- ResTable_config config;
- Res_value value;
const jsize NI = env->GetArrayLength(attrs);
const jsize NV = env->GetArrayLength(outValues);
@@ -1643,108 +1288,29 @@
}
jint* baseDest = (jint*)env->GetPrimitiveArrayCritical(outValues, 0);
- jint* dest = baseDest;
- if (dest == NULL) {
+ if (baseDest == NULL) {
env->ReleasePrimitiveArrayCritical(attrs, src, 0);
return JNI_FALSE;
}
jint* indices = NULL;
- int indicesIdx = 0;
if (outIndices != NULL) {
if (env->GetArrayLength(outIndices) > NI) {
indices = (jint*)env->GetPrimitiveArrayCritical(outIndices, 0);
}
}
- // Now lock down the resource object and start pulling stuff from it.
- res.lock();
-
- // Retrieve the XML attributes, if requested.
- const jsize NX = xmlParser->getAttributeCount();
- jsize ix=0;
- uint32_t curXmlAttr = xmlParser->getAttributeNameResID(ix);
-
- static const ssize_t kXmlBlock = 0x10000000;
-
- // Now iterate through all of the attributes that the client has requested,
- // filling in each with whatever data we can find.
- ssize_t block = 0;
- uint32_t typeSetFlags;
- for (jsize ii=0; ii<NI; ii++) {
- const uint32_t curIdent = (uint32_t)src[ii];
-
- // Try to find a value for this attribute...
- value.dataType = Res_value::TYPE_NULL;
- value.data = Res_value::DATA_NULL_UNDEFINED;
- typeSetFlags = 0;
- config.density = 0;
-
- // Skip through XML attributes until the end or the next possible match.
- while (ix < NX && curIdent > curXmlAttr) {
- ix++;
- curXmlAttr = xmlParser->getAttributeNameResID(ix);
- }
- // Retrieve the current XML attribute if it matches, and step to next.
- if (ix < NX && curIdent == curXmlAttr) {
- block = kXmlBlock;
- xmlParser->getAttributeValue(ix, &value);
- ix++;
- curXmlAttr = xmlParser->getAttributeNameResID(ix);
- }
-
- //printf("Attribute 0x%08x: type=0x%x, data=0x%08x\n", curIdent, value.dataType, value.data);
- uint32_t resid = 0;
- if (value.dataType != Res_value::TYPE_NULL) {
- // Take care of resolving the found resource to its final value.
- //printf("Resolving attribute reference\n");
- ssize_t newBlock = res.resolveReference(&value, block, &resid,
- &typeSetFlags, &config);
- if (kThrowOnBadId) {
- if (newBlock == BAD_INDEX) {
- jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
- return JNI_FALSE;
- }
- }
- if (newBlock >= 0) block = newBlock;
- }
-
- // Deal with the special @null value -- it turns back to TYPE_NULL.
- if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
- value.dataType = Res_value::TYPE_NULL;
- value.data = Res_value::DATA_NULL_UNDEFINED;
- }
-
- //printf("Attribute 0x%08x: final type=0x%x, data=0x%08x\n", curIdent, value.dataType, value.data);
-
- // Write the final value back to Java.
- dest[STYLE_TYPE] = value.dataType;
- dest[STYLE_DATA] = value.data;
- dest[STYLE_ASSET_COOKIE] =
- block != kXmlBlock ? reinterpret_cast<jint>(res.getTableCookie(block)) : (jint)-1;
- dest[STYLE_RESOURCE_ID] = resid;
- dest[STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags;
- dest[STYLE_DENSITY] = config.density;
-
- if (indices != NULL && value.dataType != Res_value::TYPE_NULL) {
- indicesIdx++;
- indices[indicesIdx] = ii;
- }
-
- dest += STYLE_NUM_ENTRIES;
- }
-
- res.unlock();
+ bool result = retrieveAttributes(&res, xmlParser,
+ (uint32_t*) src, NI,
+ (uint32_t*) baseDest,
+ (uint32_t*) indices);
if (indices != NULL) {
- indices[0] = indicesIdx;
env->ReleasePrimitiveArrayCritical(outIndices, indices, 0);
}
-
env->ReleasePrimitiveArrayCritical(outValues, baseDest, 0);
env->ReleasePrimitiveArrayCritical(attrs, src, 0);
-
- return JNI_TRUE;
+ return result ? JNI_TRUE : JNI_FALSE;
}
static jint android_content_AssetManager_getArraySize(JNIEnv* env, jobject clazz,
diff --git a/include/androidfw/AttributeResolution.h b/include/androidfw/AttributeResolution.h
new file mode 100644
index 0000000..2f60a1d
--- /dev/null
+++ b/include/androidfw/AttributeResolution.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROIDFW_ATTRIBUTERESOLUTION_H
+#define ANDROIDFW_ATTRIBUTERESOLUTION_H
+
+#include <androidfw/ResourceTypes.h>
+
+namespace android {
+
+// These are all variations of the same method. They each perform the exact same operation,
+// but on various data sources. I *think* they are re-written to avoid an extra branch
+// in the inner loop, but after one branch miss (some pointer != null), the branch predictor should
+// predict the rest of the iterations' branch correctly.
+// TODO(adamlesinski): Run performance tests against these methods and a new, single method
+// that uses all the sources and branches to the right ones within the inner loop.
+
+bool resolveAttrs(ResTable::Theme* theme,
+ uint32_t defStyleAttr,
+ uint32_t defStyleRes,
+ uint32_t* srcValues, size_t srcValuesLength,
+ uint32_t* attrs, size_t attrsLength,
+ uint32_t* outValues,
+ uint32_t* outIndices);
+
+bool applyStyle(ResTable::Theme* theme, ResXMLParser* xmlParser,
+ uint32_t defStyleAttr,
+ uint32_t defStyleRes,
+ uint32_t* attrs, size_t attrsLength,
+ uint32_t* outValues,
+ uint32_t* outIndices);
+
+bool retrieveAttributes(const ResTable* res, ResXMLParser* xmlParser,
+ uint32_t* attrs, size_t attrsLength,
+ uint32_t* outValues,
+ uint32_t* outIndices);
+
+} // namespace android
+
+#endif /* ANDROIDFW_ATTRIBUTERESOLUTION_H */
diff --git a/libs/androidfw/Android.mk b/libs/androidfw/Android.mk
index 6bbfcd2..7d7d42c7 100644
--- a/libs/androidfw/Android.mk
+++ b/libs/androidfw/Android.mk
@@ -21,6 +21,7 @@
Asset.cpp \
AssetDir.cpp \
AssetManager.cpp \
+ AttributeResolution.cpp \
LocaleData.cpp \
misc.cpp \
ObbFile.cpp \
diff --git a/libs/androidfw/AttributeResolution.cpp b/libs/androidfw/AttributeResolution.cpp
new file mode 100644
index 0000000..ad428a4
--- /dev/null
+++ b/libs/androidfw/AttributeResolution.cpp
@@ -0,0 +1,500 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "androidfw/AttributeFinder.h"
+#include "androidfw/AttributeResolution.h"
+#include "androidfw/ResourceTypes.h"
+
+#include <android/log.h>
+#include <cstdint>
+
+constexpr bool kDebugStyles = false;
+
+namespace android {
+
+enum {
+ STYLE_NUM_ENTRIES = 6,
+ STYLE_TYPE = 0,
+ STYLE_DATA = 1,
+ STYLE_ASSET_COOKIE = 2,
+ STYLE_RESOURCE_ID = 3,
+ STYLE_CHANGING_CONFIGURATIONS = 4,
+ STYLE_DENSITY = 5
+};
+
+class XmlAttributeFinder : public BackTrackingAttributeFinder<XmlAttributeFinder, size_t> {
+public:
+ explicit XmlAttributeFinder(const ResXMLParser* parser) :
+ BackTrackingAttributeFinder(0, parser != NULL ? parser->getAttributeCount() : 0),
+ mParser(parser) {
+ }
+
+ inline uint32_t getAttribute(size_t index) const {
+ return mParser->getAttributeNameResID(index);
+ }
+
+private:
+ const ResXMLParser* mParser;
+};
+
+class BagAttributeFinder :
+ public BackTrackingAttributeFinder<BagAttributeFinder, const ResTable::bag_entry*> {
+public:
+ BagAttributeFinder(const ResTable::bag_entry* start, const ResTable::bag_entry* end) :
+ BackTrackingAttributeFinder(start, end) {}
+
+ inline uint32_t getAttribute(const ResTable::bag_entry* entry) const {
+ return entry->map.name.ident;
+ }
+};
+
+bool resolveAttrs(ResTable::Theme* theme,
+ uint32_t defStyleAttr,
+ uint32_t defStyleRes,
+ uint32_t* srcValues, size_t srcValuesLength,
+ uint32_t* attrs, size_t attrsLength,
+ uint32_t* outValues,
+ uint32_t* outIndices) {
+ if (kDebugStyles) {
+ ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x "
+ "defStyleRes=0x%x", theme, defStyleAttr, defStyleRes);
+ }
+
+ const ResTable& res = theme->getResTable();
+ ResTable_config config;
+ Res_value value;
+
+ int indicesIdx = 0;
+
+ // Load default style from attribute, if specified...
+ uint32_t defStyleBagTypeSetFlags = 0;
+ if (defStyleAttr != 0) {
+ Res_value value;
+ if (theme->getAttribute(defStyleAttr, &value, &defStyleBagTypeSetFlags) >= 0) {
+ if (value.dataType == Res_value::TYPE_REFERENCE) {
+ defStyleRes = value.data;
+ }
+ }
+ }
+
+ // Now lock down the resource object and start pulling stuff from it.
+ res.lock();
+
+ // Retrieve the default style bag, if requested.
+ const ResTable::bag_entry* defStyleStart = NULL;
+ uint32_t defStyleTypeSetFlags = 0;
+ ssize_t bagOff = defStyleRes != 0
+ ? res.getBagLocked(defStyleRes, &defStyleStart, &defStyleTypeSetFlags) : -1;
+ defStyleTypeSetFlags |= defStyleBagTypeSetFlags;
+ const ResTable::bag_entry* const defStyleEnd = defStyleStart + (bagOff >= 0 ? bagOff : 0);
+ BagAttributeFinder defStyleAttrFinder(defStyleStart, defStyleEnd);
+
+ // Now iterate through all of the attributes that the client has requested,
+ // filling in each with whatever data we can find.
+ ssize_t block = 0;
+ uint32_t typeSetFlags;
+ for (size_t ii=0; ii<attrsLength; ii++) {
+ const uint32_t curIdent = attrs[ii];
+
+ if (kDebugStyles) {
+ ALOGI("RETRIEVING ATTR 0x%08x...", curIdent);
+ }
+
+ // Try to find a value for this attribute... we prioritize values
+ // coming from, first XML attributes, then XML style, then default
+ // style, and finally the theme.
+ value.dataType = Res_value::TYPE_NULL;
+ value.data = Res_value::DATA_NULL_UNDEFINED;
+ typeSetFlags = 0;
+ config.density = 0;
+
+ // Retrieve the current input value if available.
+ if (srcValuesLength > 0 && srcValues[ii] != 0) {
+ block = -1;
+ value.dataType = Res_value::TYPE_ATTRIBUTE;
+ value.data = srcValues[ii];
+ if (kDebugStyles) {
+ ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType, value.data);
+ }
+ }
+
+ if (value.dataType == Res_value::TYPE_NULL) {
+ const ResTable::bag_entry* const defStyleEntry = defStyleAttrFinder.find(curIdent);
+ if (defStyleEntry != defStyleEnd) {
+ block = defStyleEntry->stringBlock;
+ typeSetFlags = defStyleTypeSetFlags;
+ value = defStyleEntry->map.value;
+ if (kDebugStyles) {
+ ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data);
+ }
+ }
+ }
+
+ uint32_t resid = 0;
+ if (value.dataType != Res_value::TYPE_NULL) {
+ // Take care of resolving the found resource to its final value.
+ ssize_t newBlock = theme->resolveAttributeReference(&value, block,
+ &resid, &typeSetFlags, &config);
+ if (newBlock >= 0) block = newBlock;
+ if (kDebugStyles) {
+ ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, value.data);
+ }
+ } else {
+ // If we still don't have a value for this attribute, try to find
+ // it in the theme!
+ ssize_t newBlock = theme->getAttribute(curIdent, &value, &typeSetFlags);
+ if (newBlock >= 0) {
+ if (kDebugStyles) {
+ ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data);
+ }
+ newBlock = res.resolveReference(&value, block, &resid,
+ &typeSetFlags, &config);
+ if (newBlock >= 0) block = newBlock;
+ if (kDebugStyles) {
+ ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, value.data);
+ }
+ }
+ }
+
+ // Deal with the special @null value -- it turns back to TYPE_NULL.
+ if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
+ if (kDebugStyles) {
+ ALOGI("-> Setting to @null!");
+ }
+ value.dataType = Res_value::TYPE_NULL;
+ value.data = Res_value::DATA_NULL_UNDEFINED;
+ block = -1;
+ }
+
+ if (kDebugStyles) {
+ ALOGI("Attribute 0x%08x: type=0x%x, data=0x%08x", curIdent, value.dataType,
+ value.data);
+ }
+
+ // Write the final value back to Java.
+ outValues[STYLE_TYPE] = value.dataType;
+ outValues[STYLE_DATA] = value.data;
+ outValues[STYLE_ASSET_COOKIE] = block != -1
+ ? static_cast<uint32_t>(res.getTableCookie(block)) : static_cast<uint32_t>(-1);
+ outValues[STYLE_RESOURCE_ID] = resid;
+ outValues[STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags;
+ outValues[STYLE_DENSITY] = config.density;
+
+ if (outIndices != NULL && value.dataType != Res_value::TYPE_NULL) {
+ indicesIdx++;
+ outIndices[indicesIdx] = ii;
+ }
+
+ outValues += STYLE_NUM_ENTRIES;
+ }
+
+ res.unlock();
+
+ if (outIndices != NULL) {
+ outIndices[0] = indicesIdx;
+ }
+ return true;
+}
+
+bool applyStyle(ResTable::Theme* theme, ResXMLParser* xmlParser,
+ uint32_t defStyleAttr,
+ uint32_t defStyleRes,
+ uint32_t* attrs, size_t attrsLength,
+ uint32_t* outValues,
+ uint32_t* outIndices) {
+ if (kDebugStyles) {
+ ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p",
+ theme, defStyleAttr, defStyleRes, xmlParser);
+ }
+
+ const ResTable& res = theme->getResTable();
+ ResTable_config config;
+ Res_value value;
+
+ int indicesIdx = 0;
+
+ // Load default style from attribute, if specified...
+ uint32_t defStyleBagTypeSetFlags = 0;
+ if (defStyleAttr != 0) {
+ Res_value value;
+ if (theme->getAttribute(defStyleAttr, &value, &defStyleBagTypeSetFlags) >= 0) {
+ if (value.dataType == Res_value::TYPE_REFERENCE) {
+ defStyleRes = value.data;
+ }
+ }
+ }
+
+ // Retrieve the style class associated with the current XML tag.
+ int style = 0;
+ uint32_t styleBagTypeSetFlags = 0;
+ if (xmlParser != NULL) {
+ ssize_t idx = xmlParser->indexOfStyle();
+ if (idx >= 0 && xmlParser->getAttributeValue(idx, &value) >= 0) {
+ if (value.dataType == value.TYPE_ATTRIBUTE) {
+ if (theme->getAttribute(value.data, &value, &styleBagTypeSetFlags) < 0) {
+ value.dataType = Res_value::TYPE_NULL;
+ }
+ }
+ if (value.dataType == value.TYPE_REFERENCE) {
+ style = value.data;
+ }
+ }
+ }
+
+ // Now lock down the resource object and start pulling stuff from it.
+ res.lock();
+
+ // Retrieve the default style bag, if requested.
+ const ResTable::bag_entry* defStyleAttrStart = NULL;
+ uint32_t defStyleTypeSetFlags = 0;
+ ssize_t bagOff = defStyleRes != 0
+ ? res.getBagLocked(defStyleRes, &defStyleAttrStart, &defStyleTypeSetFlags) : -1;
+ defStyleTypeSetFlags |= defStyleBagTypeSetFlags;
+ const ResTable::bag_entry* const defStyleAttrEnd = defStyleAttrStart + (bagOff >= 0 ? bagOff : 0);
+ BagAttributeFinder defStyleAttrFinder(defStyleAttrStart, defStyleAttrEnd);
+
+ // Retrieve the style class bag, if requested.
+ const ResTable::bag_entry* styleAttrStart = NULL;
+ uint32_t styleTypeSetFlags = 0;
+ bagOff = style != 0 ? res.getBagLocked(style, &styleAttrStart, &styleTypeSetFlags) : -1;
+ styleTypeSetFlags |= styleBagTypeSetFlags;
+ const ResTable::bag_entry* const styleAttrEnd = styleAttrStart + (bagOff >= 0 ? bagOff : 0);
+ BagAttributeFinder styleAttrFinder(styleAttrStart, styleAttrEnd);
+
+ // Retrieve the XML attributes, if requested.
+ static const ssize_t kXmlBlock = 0x10000000;
+ XmlAttributeFinder xmlAttrFinder(xmlParser);
+ const size_t xmlAttrEnd = xmlParser != NULL ? xmlParser->getAttributeCount() : 0;
+
+ // Now iterate through all of the attributes that the client has requested,
+ // filling in each with whatever data we can find.
+ ssize_t block = 0;
+ uint32_t typeSetFlags;
+ for (size_t ii = 0; ii < attrsLength; ii++) {
+ const uint32_t curIdent = attrs[ii];
+
+ if (kDebugStyles) {
+ ALOGI("RETRIEVING ATTR 0x%08x...", curIdent);
+ }
+
+ // Try to find a value for this attribute... we prioritize values
+ // coming from, first XML attributes, then XML style, then default
+ // style, and finally the theme.
+ value.dataType = Res_value::TYPE_NULL;
+ value.data = Res_value::DATA_NULL_UNDEFINED;
+ typeSetFlags = 0;
+ config.density = 0;
+
+ // Walk through the xml attributes looking for the requested attribute.
+ const size_t xmlAttrIdx = xmlAttrFinder.find(curIdent);
+ if (xmlAttrIdx != xmlAttrEnd) {
+ // We found the attribute we were looking for.
+ block = kXmlBlock;
+ xmlParser->getAttributeValue(xmlAttrIdx, &value);
+ if (kDebugStyles) {
+ ALOGI("-> From XML: type=0x%x, data=0x%08x", value.dataType, value.data);
+ }
+ }
+
+ if (value.dataType == Res_value::TYPE_NULL) {
+ // Walk through the style class values looking for the requested attribute.
+ const ResTable::bag_entry* const styleAttrEntry = styleAttrFinder.find(curIdent);
+ if (styleAttrEntry != styleAttrEnd) {
+ // We found the attribute we were looking for.
+ block = styleAttrEntry->stringBlock;
+ typeSetFlags = styleTypeSetFlags;
+ value = styleAttrEntry->map.value;
+ if (kDebugStyles) {
+ ALOGI("-> From style: type=0x%x, data=0x%08x", value.dataType, value.data);
+ }
+ }
+ }
+
+ if (value.dataType == Res_value::TYPE_NULL) {
+ // Walk through the default style values looking for the requested attribute.
+ const ResTable::bag_entry* const defStyleAttrEntry = defStyleAttrFinder.find(curIdent);
+ if (defStyleAttrEntry != defStyleAttrEnd) {
+ // We found the attribute we were looking for.
+ block = defStyleAttrEntry->stringBlock;
+ typeSetFlags = styleTypeSetFlags;
+ value = defStyleAttrEntry->map.value;
+ if (kDebugStyles) {
+ ALOGI("-> From def style: type=0x%x, data=0x%08x", value.dataType, value.data);
+ }
+ }
+ }
+
+ uint32_t resid = 0;
+ if (value.dataType != Res_value::TYPE_NULL) {
+ // Take care of resolving the found resource to its final value.
+ ssize_t newBlock = theme->resolveAttributeReference(&value, block,
+ &resid, &typeSetFlags, &config);
+ if (newBlock >= 0) {
+ block = newBlock;
+ }
+
+ if (kDebugStyles) {
+ ALOGI("-> Resolved attr: type=0x%x, data=0x%08x", value.dataType, value.data);
+ }
+ } else {
+ // If we still don't have a value for this attribute, try to find
+ // it in the theme!
+ ssize_t newBlock = theme->getAttribute(curIdent, &value, &typeSetFlags);
+ if (newBlock >= 0) {
+ if (kDebugStyles) {
+ ALOGI("-> From theme: type=0x%x, data=0x%08x", value.dataType, value.data);
+ }
+ newBlock = res.resolveReference(&value, block, &resid,
+ &typeSetFlags, &config);
+
+ if (newBlock >= 0) {
+ block = newBlock;
+ }
+
+ if (kDebugStyles) {
+ ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, value.data);
+ }
+ }
+ }
+
+ // Deal with the special @null value -- it turns back to TYPE_NULL.
+ if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
+ if (kDebugStyles) {
+ ALOGI("-> Setting to @null!");
+ }
+ value.dataType = Res_value::TYPE_NULL;
+ value.data = Res_value::DATA_NULL_UNDEFINED;
+ block = kXmlBlock;
+ }
+
+ if (kDebugStyles) {
+ ALOGI("Attribute 0x%08x: type=0x%x, data=0x%08x", curIdent, value.dataType, value.data);
+ }
+
+ // Write the final value back to Java.
+ outValues[STYLE_TYPE] = value.dataType;
+ outValues[STYLE_DATA] = value.data;
+ outValues[STYLE_ASSET_COOKIE] = block != kXmlBlock ?
+ static_cast<uint32_t>(res.getTableCookie(block)) : static_cast<uint32_t>(-1);
+ outValues[STYLE_RESOURCE_ID] = resid;
+ outValues[STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags;
+ outValues[STYLE_DENSITY] = config.density;
+
+ if (outIndices != NULL && value.dataType != Res_value::TYPE_NULL) {
+ indicesIdx++;
+ outIndices[indicesIdx] = ii;
+ }
+
+ outValues += STYLE_NUM_ENTRIES;
+ }
+
+ res.unlock();
+
+ if (outIndices != NULL) {
+ outIndices[0] = indicesIdx;
+ }
+ return true;
+}
+
+bool retrieveAttributes(const ResTable* res, ResXMLParser* xmlParser,
+ uint32_t* attrs, size_t attrsLength,
+ uint32_t* outValues,
+ uint32_t* outIndices) {
+ ResTable_config config;
+ Res_value value;
+
+ int indicesIdx = 0;
+
+ // Now lock down the resource object and start pulling stuff from it.
+ res->lock();
+
+ // Retrieve the XML attributes, if requested.
+ const size_t NX = xmlParser->getAttributeCount();
+ size_t ix=0;
+ uint32_t curXmlAttr = xmlParser->getAttributeNameResID(ix);
+
+ static const ssize_t kXmlBlock = 0x10000000;
+
+ // Now iterate through all of the attributes that the client has requested,
+ // filling in each with whatever data we can find.
+ ssize_t block = 0;
+ uint32_t typeSetFlags;
+ for (size_t ii=0; ii<attrsLength; ii++) {
+ const uint32_t curIdent = attrs[ii];
+
+ // Try to find a value for this attribute...
+ value.dataType = Res_value::TYPE_NULL;
+ value.data = Res_value::DATA_NULL_UNDEFINED;
+ typeSetFlags = 0;
+ config.density = 0;
+
+ // Skip through XML attributes until the end or the next possible match.
+ while (ix < NX && curIdent > curXmlAttr) {
+ ix++;
+ curXmlAttr = xmlParser->getAttributeNameResID(ix);
+ }
+ // Retrieve the current XML attribute if it matches, and step to next.
+ if (ix < NX && curIdent == curXmlAttr) {
+ block = kXmlBlock;
+ xmlParser->getAttributeValue(ix, &value);
+ ix++;
+ curXmlAttr = xmlParser->getAttributeNameResID(ix);
+ }
+
+ //printf("Attribute 0x%08x: type=0x%x, data=0x%08x\n", curIdent, value.dataType, value.data);
+ uint32_t resid = 0;
+ if (value.dataType != Res_value::TYPE_NULL) {
+ // Take care of resolving the found resource to its final value.
+ //printf("Resolving attribute reference\n");
+ ssize_t newBlock = res->resolveReference(&value, block, &resid,
+ &typeSetFlags, &config);
+ if (newBlock >= 0) block = newBlock;
+ }
+
+ // Deal with the special @null value -- it turns back to TYPE_NULL.
+ if (value.dataType == Res_value::TYPE_REFERENCE && value.data == 0) {
+ value.dataType = Res_value::TYPE_NULL;
+ value.data = Res_value::DATA_NULL_UNDEFINED;
+ }
+
+ //printf("Attribute 0x%08x: final type=0x%x, data=0x%08x\n", curIdent, value.dataType, value.data);
+
+ // Write the final value back to Java.
+ outValues[STYLE_TYPE] = value.dataType;
+ outValues[STYLE_DATA] = value.data;
+ outValues[STYLE_ASSET_COOKIE] = block != kXmlBlock
+ ? static_cast<uint32_t>(res->getTableCookie(block)) : static_cast<uint32_t>(-1);
+ outValues[STYLE_RESOURCE_ID] = resid;
+ outValues[STYLE_CHANGING_CONFIGURATIONS] = typeSetFlags;
+ outValues[STYLE_DENSITY] = config.density;
+
+ if (outIndices != NULL && value.dataType != Res_value::TYPE_NULL) {
+ indicesIdx++;
+ outIndices[indicesIdx] = ii;
+ }
+
+ outValues += STYLE_NUM_ENTRIES;
+ }
+
+ res->unlock();
+
+ if (outIndices != NULL) {
+ outIndices[0] = indicesIdx;
+ }
+ return true;
+}
+
+} // namespace android