Merge "Add tests for attribute resolution"

This commit is contained in:
TreeHugger Robot
2016-10-20 01:42:21 +00:00
committed by Android (Google) Code Review
19 changed files with 1082 additions and 766 deletions

View File

@@ -97,16 +97,6 @@ jclass g_stringClass = NULL;
// ----------------------------------------------------------------------------
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
};
static jint copyValue(JNIEnv* env, jobject outValue, const ResTable* table,
const Res_value& value, uint32_t ref, ssize_t block,
uint32_t typeSpecFlags, ResTable_config* config = NULL);
@@ -1170,7 +1160,7 @@ static jboolean android_content_AssetManager_resolveAttrs(JNIEnv* env, jobject c
}
ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeToken);
bool result = resolveAttrs(theme, defStyleAttr, defStyleRes,
bool result = ResolveAttrs(theme, defStyleAttr, defStyleRes,
(uint32_t*) srcValues, NSV,
(uint32_t*) src, NI,
(uint32_t*) baseDest,
@@ -1234,7 +1224,7 @@ static jboolean android_content_AssetManager_applyStyle(JNIEnv* env, jobject cla
ResTable::Theme* theme = reinterpret_cast<ResTable::Theme*>(themeToken);
ResXMLParser* xmlParser = reinterpret_cast<ResXMLParser*>(xmlParserToken);
bool result = applyStyle(theme, xmlParser,
bool result = ApplyStyle(theme, xmlParser,
defStyleAttr, defStyleRes,
(uint32_t*) src, NI,
(uint32_t*) baseDest,
@@ -1299,7 +1289,7 @@ static jboolean android_content_AssetManager_retrieveAttributes(JNIEnv* env, job
}
}
bool result = retrieveAttributes(&res, xmlParser,
bool result = RetrieveAttributes(&res, xmlParser,
(uint32_t*) src, NI,
(uint32_t*) baseDest,
(uint32_t*) indices);

View File

@@ -1,206 +0,0 @@
/*
* Copyright (C) 2014 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 H_ATTRIBUTE_FINDER
#define H_ATTRIBUTE_FINDER
#include <stdint.h>
#include <utils/KeyedVector.h>
namespace android {
static inline uint32_t getPackage(uint32_t attr) {
return attr >> 24;
}
/**
* A helper class to search linearly for the requested
* attribute, maintaining it's position and optimizing for
* the case that subsequent searches will involve an attribute with
* a higher attribute ID.
*
* In the case that a subsequent attribute has a different package ID,
* its resource ID may not be larger than the preceding search, so
* back tracking is supported for this case. This
* back tracking requirement is mainly for shared library
* resources, whose package IDs get assigned at runtime
* and thus attributes from a shared library may
* be out of order.
*
* We make two assumptions about the order of attributes:
* 1) The input has the same sorting rules applied to it as
* the attribute data contained by this class.
* 2) Attributes are grouped by package ID.
* 3) Among attributes with the same package ID, the attributes are
* sorted by increasing resource ID.
*
* Ex: 02010000, 02010001, 010100f4, 010100f5, 0x7f010001, 07f010003
*
* The total order of attributes (including package ID) can not be linear
* as shared libraries get assigned dynamic package IDs at runtime, which
* may break the sort order established at build time.
*/
template <typename Derived, typename Iterator>
class BackTrackingAttributeFinder {
public:
BackTrackingAttributeFinder(const Iterator& begin, const Iterator& end);
Iterator find(uint32_t attr);
private:
void jumpToClosestAttribute(uint32_t packageId);
void markCurrentPackageId(uint32_t packageId);
bool mFirstTime;
Iterator mBegin;
Iterator mEnd;
Iterator mCurrent;
Iterator mLargest;
uint32_t mLastPackageId;
uint32_t mCurrentAttr;
// Package Offsets (best-case, fast look-up).
Iterator mFrameworkStart;
Iterator mAppStart;
// Worst case, we have shared-library resources.
KeyedVector<uint32_t, Iterator> mPackageOffsets;
};
template <typename Derived, typename Iterator> inline
BackTrackingAttributeFinder<Derived, Iterator>::BackTrackingAttributeFinder(const Iterator& begin, const Iterator& end)
: mFirstTime(true)
, mBegin(begin)
, mEnd(end)
, mCurrent(begin)
, mLargest(begin)
, mLastPackageId(0)
, mCurrentAttr(0)
, mFrameworkStart(end)
, mAppStart(end) {
}
template <typename Derived, typename Iterator>
void BackTrackingAttributeFinder<Derived, Iterator>::jumpToClosestAttribute(const uint32_t packageId) {
switch (packageId) {
case 0x01:
mCurrent = mFrameworkStart;
break;
case 0x7f:
mCurrent = mAppStart;
break;
default: {
ssize_t idx = mPackageOffsets.indexOfKey(packageId);
if (idx >= 0) {
// We have seen this package ID before, so jump to the first
// attribute with this package ID.
mCurrent = mPackageOffsets[idx];
} else {
mCurrent = mEnd;
}
break;
}
}
// We have never seen this package ID yet, so jump to the
// latest/largest index we have processed so far.
if (mCurrent == mEnd) {
mCurrent = mLargest;
}
if (mCurrent != mEnd) {
mCurrentAttr = static_cast<const Derived*>(this)->getAttribute(mCurrent);
}
}
template <typename Derived, typename Iterator>
void BackTrackingAttributeFinder<Derived, Iterator>::markCurrentPackageId(const uint32_t packageId) {
switch (packageId) {
case 0x01:
mFrameworkStart = mCurrent;
break;
case 0x7f:
mAppStart = mCurrent;
break;
default:
mPackageOffsets.add(packageId, mCurrent);
break;
}
}
template <typename Derived, typename Iterator>
Iterator BackTrackingAttributeFinder<Derived, Iterator>::find(uint32_t attr) {
if (!(mBegin < mEnd)) {
return mEnd;
}
if (mFirstTime) {
// One-time initialization. We do this here instead of the constructor
// because the derived class we access in getAttribute() may not be
// fully constructed.
mFirstTime = false;
mCurrentAttr = static_cast<const Derived*>(this)->getAttribute(mBegin);
mLastPackageId = getPackage(mCurrentAttr);
markCurrentPackageId(mLastPackageId);
}
// Looking for the needle (attribute we're looking for)
// in the haystack (the attributes we're searching through)
const uint32_t needlePackageId = getPackage(attr);
if (mLastPackageId != needlePackageId) {
jumpToClosestAttribute(needlePackageId);
mLastPackageId = needlePackageId;
}
// Walk through the xml attributes looking for the requested attribute.
while (mCurrent != mEnd) {
const uint32_t haystackPackageId = getPackage(mCurrentAttr);
if (needlePackageId == haystackPackageId && attr < mCurrentAttr) {
// The attribute we are looking was not found.
break;
}
const uint32_t prevAttr = mCurrentAttr;
// Move to the next attribute in the XML.
++mCurrent;
if (mCurrent != mEnd) {
mCurrentAttr = static_cast<const Derived*>(this)->getAttribute(mCurrent);
const uint32_t newHaystackPackageId = getPackage(mCurrentAttr);
if (haystackPackageId != newHaystackPackageId) {
// We've moved to the next group of attributes
// with a new package ID, so we should record
// the offset of this new package ID.
markCurrentPackageId(newHaystackPackageId);
}
}
if (mCurrent > mLargest) {
// We've moved past the latest attribute we've
// seen.
mLargest = mCurrent;
}
if (attr == prevAttr) {
// We found the attribute we were looking for.
return mCurrent - 1;
}
}
return mEnd;
}
} // namespace android
#endif // H_ATTRIBUTE_FINDER

View File

@@ -21,6 +21,18 @@
namespace android {
// Offsets into the outValues array populated by the methods below. outValues is a uint32_t
// array, but each logical element takes up 6 uint32_t-sized physical elements.
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
};
// 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
@@ -28,26 +40,17 @@ namespace android {
// 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 ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, uint32_t def_style_res,
uint32_t* src_values, size_t src_values_length, uint32_t* attrs,
size_t attrs_length, uint32_t* out_values, uint32_t* out_indices);
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 ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr,
uint32_t def_style_res, uint32_t* attrs, size_t attrs_length, uint32_t* out_values,
uint32_t* out_indices);
bool retrieveAttributes(const ResTable* res, ResXMLParser* xmlParser,
uint32_t* attrs, size_t attrsLength,
uint32_t* outValues,
uint32_t* outIndices);
bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, uint32_t* attrs,
size_t attrs_length, uint32_t* out_values, uint32_t* out_indices);
} // namespace android
} // namespace android
#endif /* ANDROIDFW_ATTRIBUTERESOLUTION_H */

View File

@@ -0,0 +1,2 @@
BasedOnStyle: Google
ColumnLimit: 100

View File

@@ -0,0 +1,206 @@
/*
* 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_ATTRIBUTE_FINDER_H
#define ANDROIDFW_ATTRIBUTE_FINDER_H
#include <utils/KeyedVector.h>
#include <stdint.h>
namespace android {
static inline uint32_t get_package(uint32_t attr) { return attr >> 24; }
/**
* A helper class to search linearly for the requested
* attribute, maintaining it's position and optimizing for
* the case that subsequent searches will involve an attribute with
* a higher attribute ID.
*
* In the case that a subsequent attribute has a different package ID,
* its resource ID may not be larger than the preceding search, so
* back tracking is supported for this case. This
* back tracking requirement is mainly for shared library
* resources, whose package IDs get assigned at runtime
* and thus attributes from a shared library may
* be out of order.
*
* We make two assumptions about the order of attributes:
* 1) The input has the same sorting rules applied to it as
* the attribute data contained by this class.
* 2) Attributes are grouped by package ID.
* 3) Among attributes with the same package ID, the attributes are
* sorted by increasing resource ID.
*
* Ex: 02010000, 02010001, 010100f4, 010100f5, 0x7f010001, 07f010003
*
* The total order of attributes (including package ID) can not be linear
* as shared libraries get assigned dynamic package IDs at runtime, which
* may break the sort order established at build time.
*/
template <typename Derived, typename Iterator>
class BackTrackingAttributeFinder {
public:
BackTrackingAttributeFinder(const Iterator& begin, const Iterator& end);
Iterator Find(uint32_t attr);
private:
void JumpToClosestAttribute(uint32_t package_id);
void MarkCurrentPackageId(uint32_t package_id);
bool first_time_;
Iterator begin_;
Iterator end_;
Iterator current_;
Iterator largest_;
uint32_t last_package_id_;
uint32_t current_attr_;
// Package offsets (best-case, fast look-up).
Iterator framework_start_;
Iterator app_start_;
// Worst case, we have shared-library resources.
KeyedVector<uint32_t, Iterator> package_offsets_;
};
template <typename Derived, typename Iterator>
inline BackTrackingAttributeFinder<Derived, Iterator>::BackTrackingAttributeFinder(
const Iterator& begin, const Iterator& end)
: first_time_(true),
begin_(begin),
end_(end),
current_(begin),
largest_(begin),
last_package_id_(0),
current_attr_(0),
framework_start_(end),
app_start_(end) {}
template <typename Derived, typename Iterator>
void BackTrackingAttributeFinder<Derived, Iterator>::JumpToClosestAttribute(
const uint32_t package_id) {
switch (package_id) {
case 0x01u:
current_ = framework_start_;
break;
case 0x7fu:
current_ = app_start_;
break;
default: {
ssize_t idx = package_offsets_.indexOfKey(package_id);
if (idx >= 0) {
// We have seen this package ID before, so jump to the first
// attribute with this package ID.
current_ = package_offsets_[idx];
} else {
current_ = end_;
}
break;
}
}
// We have never seen this package ID yet, so jump to the
// latest/largest index we have processed so far.
if (current_ == end_) {
current_ = largest_;
}
if (current_ != end_) {
current_attr_ = static_cast<const Derived*>(this)->GetAttribute(current_);
}
}
template <typename Derived, typename Iterator>
void BackTrackingAttributeFinder<Derived, Iterator>::MarkCurrentPackageId(
const uint32_t package_id) {
switch (package_id) {
case 0x01u:
framework_start_ = current_;
break;
case 0x7fu:
app_start_ = current_;
break;
default:
package_offsets_.add(package_id, current_);
break;
}
}
template <typename Derived, typename Iterator>
Iterator BackTrackingAttributeFinder<Derived, Iterator>::Find(uint32_t attr) {
if (!(begin_ < end_)) {
return end_;
}
if (first_time_) {
// One-time initialization. We do this here instead of the constructor
// because the derived class we access in getAttribute() may not be
// fully constructed.
first_time_ = false;
current_attr_ = static_cast<const Derived*>(this)->GetAttribute(begin_);
last_package_id_ = get_package(current_attr_);
MarkCurrentPackageId(last_package_id_);
}
// Looking for the needle (attribute we're looking for)
// in the haystack (the attributes we're searching through)
const uint32_t needle_package_id = get_package(attr);
if (last_package_id_ != needle_package_id) {
JumpToClosestAttribute(needle_package_id);
last_package_id_ = needle_package_id;
}
// Walk through the xml attributes looking for the requested attribute.
while (current_ != end_) {
const uint32_t haystack_package_id = get_package(current_attr_);
if (needle_package_id == haystack_package_id && attr < current_attr_) {
// The attribute we are looking was not found.
break;
}
const uint32_t prev_attr = current_attr_;
// Move to the next attribute in the XML.
++current_;
if (current_ != end_) {
current_attr_ = static_cast<const Derived*>(this)->GetAttribute(current_);
const uint32_t new_haystack_package_id = get_package(current_attr_);
if (haystack_package_id != new_haystack_package_id) {
// We've moved to the next group of attributes
// with a new package ID, so we should record
// the offset of this new package ID.
MarkCurrentPackageId(new_haystack_package_id);
}
}
if (current_ > largest_) {
// We've moved past the latest attribute we've seen.
largest_ = current_;
}
if (attr == prev_attr) {
// We found the attribute we were looking for.
return current_ - 1;
}
}
return end_;
}
} // namespace android
#endif // ANDROIDFW_ATTRIBUTE_FINDER_H

View File

@@ -14,7 +14,8 @@
* limitations under the License.
*/
#include "androidfw/AttributeFinder.h"
#include "AttributeFinder.h"
#include "androidfw/AttributeResolution.h"
#include "androidfw/ResourceTypes.h"
@@ -25,476 +26,452 @@ 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) {
}
public:
explicit XmlAttributeFinder(const ResXMLParser* parser)
: BackTrackingAttributeFinder(0, parser != NULL ? parser->getAttributeCount() : 0),
parser_(parser) {}
inline uint32_t getAttribute(size_t index) const {
return mParser->getAttributeNameResID(index);
}
inline uint32_t GetAttribute(size_t index) const { return parser_->getAttributeNameResID(index); }
private:
const ResXMLParser* mParser;
private:
const ResXMLParser* parser_;
};
class BagAttributeFinder :
public BackTrackingAttributeFinder<BagAttributeFinder, const ResTable::bag_entry*> {
public:
BagAttributeFinder(const ResTable::bag_entry* start, const ResTable::bag_entry* end) :
BackTrackingAttributeFinder(start, end) {}
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;
}
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) {
bool ResolveAttrs(ResTable::Theme* theme, uint32_t def_style_attr, uint32_t def_style_res,
uint32_t* src_values, size_t src_values_length, uint32_t* attrs,
size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) {
if (kDebugStyles) {
ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x", theme, def_style_attr,
def_style_res);
}
const ResTable& res = theme->getResTable();
ResTable_config config;
Res_value value;
int indices_idx = 0;
// Load default style from attribute, if specified...
uint32_t def_style_bag_type_set_flags = 0;
if (def_style_attr != 0) {
Res_value value;
if (theme->getAttribute(def_style_attr, &value, &def_style_bag_type_set_flags) >= 0) {
if (value.dataType == Res_value::TYPE_REFERENCE) {
def_style_res = 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* def_style_start = NULL;
uint32_t def_style_type_set_flags = 0;
ssize_t bag_off =
def_style_res != 0
? res.getBagLocked(def_style_res, &def_style_start, &def_style_type_set_flags)
: -1;
def_style_type_set_flags |= def_style_bag_type_set_flags;
const ResTable::bag_entry* const def_style_end = def_style_start + (bag_off >= 0 ? bag_off : 0);
BagAttributeFinder def_style_attr_finder(def_style_start, def_style_end);
// Now iterate through all of the attributes that the client has requested,
// filling in each with whatever data we can find.
for (size_t ii = 0; ii < attrs_length; ii++) {
const uint32_t cur_ident = attrs[ii];
if (kDebugStyles) {
ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x "
"defStyleRes=0x%x", theme, defStyleAttr, defStyleRes);
ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident);
}
const ResTable& res = theme->getResTable();
ResTable_config config;
Res_value value;
ssize_t block = -1;
uint32_t type_set_flags = 0;
int indicesIdx = 0;
value.dataType = Res_value::TYPE_NULL;
value.data = Res_value::DATA_NULL_UNDEFINED;
config.density = 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;
}
}
// 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.
// Retrieve the current input value if available.
if (src_values_length > 0 && src_values[ii] != 0) {
value.dataType = Res_value::TYPE_ATTRIBUTE;
value.data = src_values[ii];
if (kDebugStyles) {
ALOGI("-> From values: type=0x%x, data=0x%08x", value.dataType, 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 (value.dataType == Res_value::TYPE_NULL) {
const ResTable::bag_entry* const def_style_entry = def_style_attr_finder.Find(cur_ident);
if (def_style_entry != def_style_end) {
block = def_style_entry->stringBlock;
type_set_flags = def_style_type_set_flags;
value = def_style_entry->map.value;
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;
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 new_block =
theme->resolveAttributeReference(&value, block, &resid, &type_set_flags, &config);
if (new_block >= 0) block = new_block;
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 new_block = theme->getAttribute(cur_ident, &value, &type_set_flags);
if (new_block >= 0) {
if (kDebugStyles) {
ALOGI("Attribute 0x%08x: type=0x%x, data=0x%08x", curIdent, value.dataType,
value.data);
ALOGI("-> From theme: type=0x%x, data=0x%08x", 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;
new_block = res.resolveReference(&value, new_block, &resid, &type_set_flags, &config);
if (new_block >= 0) block = new_block;
if (kDebugStyles) {
ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", value.dataType, value.data);
}
outValues += STYLE_NUM_ENTRIES;
}
}
res.unlock();
if (outIndices != NULL) {
outIndices[0] = indicesIdx;
// 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;
}
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);
ALOGI("Attribute 0x%08x: type=0x%x, data=0x%08x", cur_ident, value.dataType, value.data);
}
const ResTable& res = theme->getResTable();
ResTable_config config;
// Write the final value back to Java.
out_values[STYLE_TYPE] = value.dataType;
out_values[STYLE_DATA] = value.data;
out_values[STYLE_ASSET_COOKIE] =
block != -1 ? static_cast<uint32_t>(res.getTableCookie(block)) : static_cast<uint32_t>(-1);
out_values[STYLE_RESOURCE_ID] = resid;
out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags;
out_values[STYLE_DENSITY] = config.density;
if (out_indices != NULL && value.dataType != Res_value::TYPE_NULL) {
indices_idx++;
out_indices[indices_idx] = ii;
}
out_values += STYLE_NUM_ENTRIES;
}
res.unlock();
if (out_indices != NULL) {
out_indices[0] = indices_idx;
}
return true;
}
bool ApplyStyle(ResTable::Theme* theme, ResXMLParser* xml_parser, uint32_t def_style_attr,
uint32_t def_style_res, uint32_t* attrs, size_t attrs_length, uint32_t* out_values,
uint32_t* out_indices) {
if (kDebugStyles) {
ALOGI("APPLY STYLE: theme=0x%p defStyleAttr=0x%x defStyleRes=0x%x xml=0x%p", theme,
def_style_attr, def_style_res, xml_parser);
}
const ResTable& res = theme->getResTable();
ResTable_config config;
Res_value value;
int indices_idx = 0;
// Load default style from attribute, if specified...
uint32_t def_style_bag_type_set_flags = 0;
if (def_style_attr != 0) {
Res_value value;
if (theme->getAttribute(def_style_attr, &value, &def_style_bag_type_set_flags) >= 0) {
if (value.dataType == Res_value::TYPE_REFERENCE) {
def_style_res = value.data;
}
}
}
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 style_bag_type_set_flags = 0;
if (xml_parser != NULL) {
ssize_t idx = xml_parser->indexOfStyle();
if (idx >= 0 && xml_parser->getAttributeValue(idx, &value) >= 0) {
if (value.dataType == value.TYPE_ATTRIBUTE) {
if (theme->getAttribute(value.data, &value, &style_bag_type_set_flags) < 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* def_style_attr_start = NULL;
uint32_t def_style_type_set_flags = 0;
ssize_t bag_off =
def_style_res != 0
? res.getBagLocked(def_style_res, &def_style_attr_start, &def_style_type_set_flags)
: -1;
def_style_type_set_flags |= def_style_bag_type_set_flags;
const ResTable::bag_entry* const def_style_attr_end =
def_style_attr_start + (bag_off >= 0 ? bag_off : 0);
BagAttributeFinder def_style_attr_finder(def_style_attr_start, def_style_attr_end);
// Retrieve the style class bag, if requested.
const ResTable::bag_entry* style_attr_start = NULL;
uint32_t style_type_set_flags = 0;
bag_off = style != 0 ? res.getBagLocked(style, &style_attr_start, &style_type_set_flags) : -1;
style_type_set_flags |= style_bag_type_set_flags;
const ResTable::bag_entry* const style_attr_end = style_attr_start + (bag_off >= 0 ? bag_off : 0);
BagAttributeFinder style_attr_finder(style_attr_start, style_attr_end);
// Retrieve the XML attributes, if requested.
static const ssize_t kXmlBlock = 0x10000000;
XmlAttributeFinder xml_attr_finder(xml_parser);
const size_t xml_attr_end = xml_parser != NULL ? xml_parser->getAttributeCount() : 0;
// Now iterate through all of the attributes that the client has requested,
// filling in each with whatever data we can find.
for (size_t ii = 0; ii < attrs_length; ii++) {
const uint32_t cur_ident = attrs[ii];
if (kDebugStyles) {
ALOGI("RETRIEVING ATTR 0x%08x...", cur_ident);
}
// 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;
}
}
ssize_t block = kXmlBlock;
uint32_t type_set_flags = 0;
value.dataType = Res_value::TYPE_NULL;
value.data = Res_value::DATA_NULL_UNDEFINED;
config.density = 0;
// 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.
// Walk through the xml attributes looking for the requested attribute.
const size_t xml_attr_idx = xml_attr_finder.Find(cur_ident);
if (xml_attr_idx != xml_attr_end) {
// We found the attribute we were looking for.
xml_parser->getAttributeValue(xml_attr_idx, &value);
if (kDebugStyles) {
ALOGI("-> From XML: type=0x%x, data=0x%08x", value.dataType, 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 (value.dataType == Res_value::TYPE_NULL) {
// Walk through the style class values looking for the requested attribute.
const ResTable::bag_entry* const style_attr_entry = style_attr_finder.Find(cur_ident);
if (style_attr_entry != style_attr_end) {
// We found the attribute we were looking for.
block = style_attr_entry->stringBlock;
type_set_flags = style_type_set_flags;
value = style_attr_entry->map.value;
if (kDebugStyles) {
ALOGI("RETRIEVING ATTR 0x%08x...", curIdent);
ALOGI("-> From style: type=0x%x, data=0x%08x", value.dataType, value.data);
}
}
}
// 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 default style values looking for the requested attribute.
const ResTable::bag_entry* const def_style_attr_entry = def_style_attr_finder.Find(cur_ident);
if (def_style_attr_entry != def_style_attr_end) {
// We found the attribute we were looking for.
block = def_style_attr_entry->stringBlock;
type_set_flags = style_type_set_flags;
value = def_style_attr_entry->map.value;
if (kDebugStyles) {
ALOGI("-> From def style: 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);
}
}
uint32_t resid = 0;
if (value.dataType != Res_value::TYPE_NULL) {
// Take care of resolving the found resource to its final value.
ssize_t new_block =
theme->resolveAttributeReference(&value, block, &resid, &type_set_flags, &config);
if (new_block >= 0) {
block = new_block;
}
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 new_block = theme->getAttribute(cur_ident, &value, &type_set_flags);
if (new_block >= 0) {
if (kDebugStyles) {
ALOGI("-> From theme: 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;
new_block = res.resolveReference(&value, new_block, &resid, &type_set_flags, &config);
if (new_block >= 0) {
block = new_block;
}
if (kDebugStyles) {
ALOGI("Attribute 0x%08x: type=0x%x, data=0x%08x", curIdent, value.dataType, value.data);
ALOGI("-> Resolved theme: type=0x%x, data=0x%08x", 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;
// 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;
}
return true;
if (kDebugStyles) {
ALOGI("Attribute 0x%08x: type=0x%x, data=0x%08x", cur_ident, value.dataType, value.data);
}
// Write the final value back to Java.
out_values[STYLE_TYPE] = value.dataType;
out_values[STYLE_DATA] = value.data;
out_values[STYLE_ASSET_COOKIE] = block != kXmlBlock
? static_cast<uint32_t>(res.getTableCookie(block))
: static_cast<uint32_t>(-1);
out_values[STYLE_RESOURCE_ID] = resid;
out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags;
out_values[STYLE_DENSITY] = config.density;
if (out_indices != NULL && value.dataType != Res_value::TYPE_NULL) {
indices_idx++;
out_indices[indices_idx] = ii;
}
out_values += STYLE_NUM_ENTRIES;
}
res.unlock();
if (out_indices != NULL) {
out_indices[0] = indices_idx;
}
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;
bool RetrieveAttributes(const ResTable* res, ResXMLParser* xml_parser, uint32_t* attrs,
size_t attrs_length, uint32_t* out_values, uint32_t* out_indices) {
ResTable_config config;
Res_value value;
int indicesIdx = 0;
int indices_idx = 0;
// Now lock down the resource object and start pulling stuff from it.
res->lock();
// 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);
// Retrieve the XML attributes, if requested.
const size_t xml_attr_count = xml_parser->getAttributeCount();
size_t ix = 0;
uint32_t cur_xml_attr = xml_parser->getAttributeNameResID(ix);
static const ssize_t kXmlBlock = 0x10000000;
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];
// Now iterate through all of the attributes that the client has requested,
// filling in each with whatever data we can find.
for (size_t ii = 0; ii < attrs_length; ii++) {
const uint32_t cur_ident = attrs[ii];
ssize_t block = kXmlBlock;
uint32_t type_set_flags = 0;
// 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;
value.dataType = Res_value::TYPE_NULL;
value.data = Res_value::DATA_NULL_UNDEFINED;
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;
// Try to find a value for this attribute...
// Skip through XML attributes until the end or the next possible match.
while (ix < xml_attr_count && cur_ident > cur_xml_attr) {
ix++;
cur_xml_attr = xml_parser->getAttributeNameResID(ix);
}
// Retrieve the current XML attribute if it matches, and step to next.
if (ix < xml_attr_count && cur_ident == cur_xml_attr) {
xml_parser->getAttributeValue(ix, &value);
ix++;
cur_xml_attr = xml_parser->getAttributeNameResID(ix);
}
res->unlock();
if (outIndices != NULL) {
outIndices[0] = indicesIdx;
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 new_block = res->resolveReference(&value, block, &resid, &type_set_flags, &config);
if (new_block >= 0) block = new_block;
}
return true;
// 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;
block = kXmlBlock;
}
// Write the final value back to Java.
out_values[STYLE_TYPE] = value.dataType;
out_values[STYLE_DATA] = value.data;
out_values[STYLE_ASSET_COOKIE] = block != kXmlBlock
? static_cast<uint32_t>(res->getTableCookie(block))
: static_cast<uint32_t>(-1);
out_values[STYLE_RESOURCE_ID] = resid;
out_values[STYLE_CHANGING_CONFIGURATIONS] = type_set_flags;
out_values[STYLE_DENSITY] = config.density;
if (out_indices != NULL && value.dataType != Res_value::TYPE_NULL) {
indices_idx++;
out_indices[indices_idx] = ii;
}
out_values += STYLE_NUM_ENTRIES;
}
res->unlock();
if (out_indices != NULL) {
out_indices[0] = indices_idx;
}
return true;
}
} // namespace android
} // namespace android

View File

@@ -40,7 +40,7 @@ androidfw_test_cflags := \
-Werror \
-Wunused \
-Wunreachable-code \
-Wno-missing-field-initializers \
-Wno-missing-field-initializers
# gtest is broken.
androidfw_test_cflags += -Wno-unnamed-type-template-args
@@ -52,9 +52,10 @@ include $(CLEAR_VARS)
LOCAL_MODULE := libandroidfw_tests
LOCAL_CFLAGS := $(androidfw_test_cflags)
LOCAL_SRC_FILES := $(testFiles)
LOCAL_SRC_FILES := $(testFiles) AttributeResolution_test.cpp
LOCAL_STATIC_LIBRARIES := \
libandroidfw \
libbase \
libutils \
libcutils \
liblog \
@@ -76,6 +77,7 @@ LOCAL_SRC_FILES := $(testFiles) \
LOCAL_SHARED_LIBRARIES := \
libandroidfw \
libbase \
libcutils \
libutils \
libui \

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2014 The Android Open Source Project
* 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.
@@ -14,115 +14,105 @@
* limitations under the License.
*/
#include <androidfw/AttributeFinder.h>
#include "../AttributeFinder.h"
#include <android-base/macros.h>
#include <gtest/gtest.h>
using android::BackTrackingAttributeFinder;
class MockAttributeFinder : public BackTrackingAttributeFinder<MockAttributeFinder, int> {
public:
MockAttributeFinder(const uint32_t* attrs, int len)
: BackTrackingAttributeFinder(0, len) {
mAttrs = new uint32_t[len];
memcpy(mAttrs, attrs, sizeof(*attrs) * len);
}
public:
MockAttributeFinder(const uint32_t* attrs, int len) : BackTrackingAttributeFinder(0, len) {
attrs_ = new uint32_t[len];
memcpy(attrs_, attrs, sizeof(*attrs) * len);
}
~MockAttributeFinder() {
delete mAttrs;
}
~MockAttributeFinder() { delete attrs_; }
inline uint32_t getAttribute(const int index) const {
return mAttrs[index];
}
inline uint32_t GetAttribute(const int index) const { return attrs_[index]; }
private:
uint32_t* mAttrs;
private:
uint32_t* attrs_;
};
static const uint32_t sortedAttributes[] = {
0x01010000, 0x01010001, 0x01010002, 0x01010004,
0x02010001, 0x02010010, 0x7f010001
};
static const uint32_t kSortedAttributes[] = {0x01010000, 0x01010001, 0x01010002, 0x01010004,
0x02010001, 0x02010010, 0x7f010001};
static const uint32_t packageUnsortedAttributes[] = {
0x02010001, 0x02010010, 0x01010000, 0x01010001,
0x01010002, 0x01010004, 0x7f010001
};
static const uint32_t kPackageUnsortedAttributes[] = {
0x02010001, 0x02010010, 0x01010000, 0x01010001, 0x01010002, 0x01010004, 0x7f010001};
static const uint32_t singlePackageAttributes[] = {
0x7f010007, 0x7f01000a, 0x7f01000d, 0x00000000
};
static const uint32_t kSinglePackageAttributes[] = {0x7f010007, 0x7f01000a, 0x7f01000d, 0x00000000};
TEST(AttributeFinderTest, IteratesSequentially) {
const int end = sizeof(sortedAttributes) / sizeof(*sortedAttributes);
MockAttributeFinder finder(sortedAttributes, end);
const int end = arraysize(kSortedAttributes);
MockAttributeFinder finder(kSortedAttributes, end);
EXPECT_EQ(0, finder.find(0x01010000));
EXPECT_EQ(1, finder.find(0x01010001));
EXPECT_EQ(2, finder.find(0x01010002));
EXPECT_EQ(3, finder.find(0x01010004));
EXPECT_EQ(4, finder.find(0x02010001));
EXPECT_EQ(5, finder.find(0x02010010));
EXPECT_EQ(6, finder.find(0x7f010001));
EXPECT_EQ(end, finder.find(0x7f010002));
EXPECT_EQ(0, finder.Find(0x01010000));
EXPECT_EQ(1, finder.Find(0x01010001));
EXPECT_EQ(2, finder.Find(0x01010002));
EXPECT_EQ(3, finder.Find(0x01010004));
EXPECT_EQ(4, finder.Find(0x02010001));
EXPECT_EQ(5, finder.Find(0x02010010));
EXPECT_EQ(6, finder.Find(0x7f010001));
EXPECT_EQ(end, finder.Find(0x7f010002));
}
TEST(AttributeFinderTest, PackagesAreOutOfOrder) {
const int end = sizeof(sortedAttributes) / sizeof(*sortedAttributes);
MockAttributeFinder finder(sortedAttributes, end);
const int end = arraysize(kSortedAttributes);
MockAttributeFinder finder(kSortedAttributes, end);
EXPECT_EQ(6, finder.find(0x7f010001));
EXPECT_EQ(end, finder.find(0x7f010002));
EXPECT_EQ(4, finder.find(0x02010001));
EXPECT_EQ(5, finder.find(0x02010010));
EXPECT_EQ(0, finder.find(0x01010000));
EXPECT_EQ(1, finder.find(0x01010001));
EXPECT_EQ(2, finder.find(0x01010002));
EXPECT_EQ(3, finder.find(0x01010004));
EXPECT_EQ(6, finder.Find(0x7f010001));
EXPECT_EQ(end, finder.Find(0x7f010002));
EXPECT_EQ(4, finder.Find(0x02010001));
EXPECT_EQ(5, finder.Find(0x02010010));
EXPECT_EQ(0, finder.Find(0x01010000));
EXPECT_EQ(1, finder.Find(0x01010001));
EXPECT_EQ(2, finder.Find(0x01010002));
EXPECT_EQ(3, finder.Find(0x01010004));
}
TEST(AttributeFinderTest, SomeAttributesAreNotFound) {
const int end = sizeof(sortedAttributes) / sizeof(*sortedAttributes);
MockAttributeFinder finder(sortedAttributes, end);
const int end = arraysize(kSortedAttributes);
MockAttributeFinder finder(kSortedAttributes, end);
EXPECT_EQ(0, finder.find(0x01010000));
EXPECT_EQ(1, finder.find(0x01010001));
EXPECT_EQ(2, finder.find(0x01010002));
EXPECT_EQ(end, finder.find(0x01010003));
EXPECT_EQ(3, finder.find(0x01010004));
EXPECT_EQ(end, finder.find(0x01010005));
EXPECT_EQ(end, finder.find(0x01010006));
EXPECT_EQ(4, finder.find(0x02010001));
EXPECT_EQ(end, finder.find(0x02010002));
EXPECT_EQ(0, finder.Find(0x01010000));
EXPECT_EQ(1, finder.Find(0x01010001));
EXPECT_EQ(2, finder.Find(0x01010002));
EXPECT_EQ(end, finder.Find(0x01010003));
EXPECT_EQ(3, finder.Find(0x01010004));
EXPECT_EQ(end, finder.Find(0x01010005));
EXPECT_EQ(end, finder.Find(0x01010006));
EXPECT_EQ(4, finder.Find(0x02010001));
EXPECT_EQ(end, finder.Find(0x02010002));
}
TEST(AttributeFinderTest, FindAttributesInPackageUnsortedAttributeList) {
const int end = sizeof(packageUnsortedAttributes) / sizeof(*packageUnsortedAttributes);
MockAttributeFinder finder(packageUnsortedAttributes, end);
const int end = arraysize(kPackageUnsortedAttributes);
MockAttributeFinder finder(kPackageUnsortedAttributes, end);
EXPECT_EQ(2, finder.find(0x01010000));
EXPECT_EQ(3, finder.find(0x01010001));
EXPECT_EQ(4, finder.find(0x01010002));
EXPECT_EQ(end, finder.find(0x01010003));
EXPECT_EQ(5, finder.find(0x01010004));
EXPECT_EQ(end, finder.find(0x01010005));
EXPECT_EQ(end, finder.find(0x01010006));
EXPECT_EQ(0, finder.find(0x02010001));
EXPECT_EQ(end, finder.find(0x02010002));
EXPECT_EQ(1, finder.find(0x02010010));
EXPECT_EQ(6, finder.find(0x7f010001));
EXPECT_EQ(2, finder.Find(0x01010000));
EXPECT_EQ(3, finder.Find(0x01010001));
EXPECT_EQ(4, finder.Find(0x01010002));
EXPECT_EQ(end, finder.Find(0x01010003));
EXPECT_EQ(5, finder.Find(0x01010004));
EXPECT_EQ(end, finder.Find(0x01010005));
EXPECT_EQ(end, finder.Find(0x01010006));
EXPECT_EQ(0, finder.Find(0x02010001));
EXPECT_EQ(end, finder.Find(0x02010002));
EXPECT_EQ(1, finder.Find(0x02010010));
EXPECT_EQ(6, finder.Find(0x7f010001));
}
TEST(AttributeFinderTest, FindAttributesInSinglePackageAttributeList) {
const int end = sizeof(singlePackageAttributes) / sizeof(*singlePackageAttributes);
MockAttributeFinder finder(singlePackageAttributes, end);
const int end = arraysize(kSinglePackageAttributes);
MockAttributeFinder finder(kSinglePackageAttributes, end);
EXPECT_EQ(end, finder.find(0x010100f4));
EXPECT_EQ(end, finder.find(0x010100f5));
EXPECT_EQ(end, finder.find(0x010100f6));
EXPECT_EQ(end, finder.find(0x010100f7));
EXPECT_EQ(end, finder.find(0x010100f8));
EXPECT_EQ(end, finder.find(0x010100fa));
EXPECT_EQ(0, finder.find(0x7f010007));
EXPECT_EQ(end, finder.Find(0x010100f4));
EXPECT_EQ(end, finder.Find(0x010100f5));
EXPECT_EQ(end, finder.Find(0x010100f6));
EXPECT_EQ(end, finder.Find(0x010100f7));
EXPECT_EQ(end, finder.Find(0x010100f8));
EXPECT_EQ(end, finder.Find(0x010100fa));
EXPECT_EQ(0, finder.Find(0x7f010007));
}

View File

@@ -0,0 +1,201 @@
/*
* 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/AttributeResolution.h"
#include "TestHelpers.h"
#include "data/styles/R.h"
#include <android-base/file.h>
#include <android-base/macros.h>
using namespace android;
using android::base::ReadFileToString;
using com::android::app::R;
class AttributeResolutionTest : public ::testing::Test {
public:
virtual void SetUp() override {
std::string test_source_dir = TestSourceDir();
std::string contents;
LOG_ALWAYS_FATAL_IF(!ReadFileToString(test_source_dir + "/styles/resources.arsc", &contents));
LOG_ALWAYS_FATAL_IF(
table_.add(contents.data(), contents.size(), 1 /*cookie*/, true /*copyData*/) != NO_ERROR);
}
protected:
ResTable table_;
};
class AttributeResolutionXmlTest : public AttributeResolutionTest {
public:
virtual void SetUp() override {
AttributeResolutionTest::SetUp();
std::string test_source_dir = TestSourceDir();
std::string contents;
LOG_ALWAYS_FATAL_IF(!ReadFileToString(test_source_dir + "/styles/layout.xml", &contents));
LOG_ALWAYS_FATAL_IF(xml_parser_.setTo(contents.data(), contents.size(), true /*copyData*/) !=
NO_ERROR);
// Skip to the first tag.
while (xml_parser_.next() != ResXMLParser::START_TAG) {
}
}
protected:
ResXMLTree xml_parser_;
};
TEST_F(AttributeResolutionTest, Theme) {
ResTable::Theme theme(table_);
ASSERT_EQ(NO_ERROR, theme.applyStyle(R::style::StyleTwo));
uint32_t attrs[] = {R::attr::attr_one, R::attr::attr_two, R::attr::attr_three,
R::attr::attr_four};
std::vector<uint32_t> values;
values.resize(arraysize(attrs) * 6);
ASSERT_TRUE(ResolveAttrs(&theme, 0 /*def_style_attr*/, 0 /*def_style_res*/,
nullptr /*src_values*/, 0 /*src_values_length*/, attrs, arraysize(attrs),
values.data(), nullptr /*out_indices*/));
const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC;
const uint32_t* values_cursor = values.data();
EXPECT_EQ(Res_value::TYPE_INT_DEC, values_cursor[STYLE_TYPE]);
EXPECT_EQ(1u, values_cursor[STYLE_DATA]);
EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
EXPECT_EQ(1u, values_cursor[STYLE_ASSET_COOKIE]);
EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
values_cursor += STYLE_NUM_ENTRIES;
EXPECT_EQ(Res_value::TYPE_STRING, values_cursor[STYLE_TYPE]);
EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
EXPECT_EQ(1u, values_cursor[STYLE_ASSET_COOKIE]);
EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
values_cursor += STYLE_NUM_ENTRIES;
EXPECT_EQ(Res_value::TYPE_INT_DEC, values_cursor[STYLE_TYPE]);
EXPECT_EQ(3u, values_cursor[STYLE_DATA]);
EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
EXPECT_EQ(1u, values_cursor[STYLE_ASSET_COOKIE]);
EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
values_cursor += STYLE_NUM_ENTRIES;
EXPECT_EQ(Res_value::TYPE_NULL, values_cursor[STYLE_TYPE]);
EXPECT_EQ(Res_value::DATA_NULL_UNDEFINED, values_cursor[STYLE_DATA]);
EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
EXPECT_EQ(uint32_t(-1), values_cursor[STYLE_ASSET_COOKIE]);
EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
EXPECT_EQ(0u, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
}
TEST_F(AttributeResolutionXmlTest, XmlParser) {
uint32_t attrs[] = {R::attr::attr_one, R::attr::attr_two, R::attr::attr_three,
R::attr::attr_four};
std::vector<uint32_t> values;
values.resize(arraysize(attrs) * 6);
ASSERT_TRUE(RetrieveAttributes(&table_, &xml_parser_, attrs, arraysize(attrs), values.data(),
nullptr /*out_indices*/));
uint32_t* values_cursor = values.data();
EXPECT_EQ(Res_value::TYPE_NULL, values_cursor[STYLE_TYPE]);
EXPECT_EQ(0u, values_cursor[STYLE_DATA]);
EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
EXPECT_EQ(uint32_t(-1), values_cursor[STYLE_ASSET_COOKIE]);
EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
EXPECT_EQ(0u, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
values_cursor += STYLE_NUM_ENTRIES;
EXPECT_EQ(Res_value::TYPE_NULL, values_cursor[STYLE_TYPE]);
EXPECT_EQ(0u, values_cursor[STYLE_DATA]);
EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
EXPECT_EQ(uint32_t(-1), values_cursor[STYLE_ASSET_COOKIE]);
EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
EXPECT_EQ(0u, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
values_cursor += STYLE_NUM_ENTRIES;
EXPECT_EQ(Res_value::TYPE_INT_DEC, values_cursor[STYLE_TYPE]);
EXPECT_EQ(10u, values_cursor[STYLE_DATA]);
EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
EXPECT_EQ(uint32_t(-1), values_cursor[STYLE_ASSET_COOKIE]);
EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
EXPECT_EQ(0u, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
values_cursor += STYLE_NUM_ENTRIES;
EXPECT_EQ(Res_value::TYPE_ATTRIBUTE, values_cursor[STYLE_TYPE]);
EXPECT_EQ(R::attr::attr_indirect, values_cursor[STYLE_DATA]);
EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
EXPECT_EQ(uint32_t(-1), values_cursor[STYLE_ASSET_COOKIE]);
EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
EXPECT_EQ(0u, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
}
TEST_F(AttributeResolutionXmlTest, ThemeAndXmlParser) {
ResTable::Theme theme(table_);
ASSERT_EQ(NO_ERROR, theme.applyStyle(R::style::StyleTwo));
uint32_t attrs[] = {R::attr::attr_one, R::attr::attr_two, R::attr::attr_three, R::attr::attr_four,
R::attr::attr_five};
std::vector<uint32_t> values;
values.resize(arraysize(attrs) * 6);
ASSERT_TRUE(ApplyStyle(&theme, &xml_parser_, 0 /*def_style_attr*/, 0 /*def_style_res*/, attrs,
arraysize(attrs), values.data(), nullptr /*out_indices*/));
const uint32_t public_flag = ResTable_typeSpec::SPEC_PUBLIC;
uint32_t* values_cursor = values.data();
EXPECT_EQ(Res_value::TYPE_INT_DEC, values_cursor[STYLE_TYPE]);
EXPECT_EQ(1u, values_cursor[STYLE_DATA]);
EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
EXPECT_EQ(1u, values_cursor[STYLE_ASSET_COOKIE]);
EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
values_cursor += STYLE_NUM_ENTRIES;
EXPECT_EQ(Res_value::TYPE_STRING, values_cursor[STYLE_TYPE]);
EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
EXPECT_EQ(1u, values_cursor[STYLE_ASSET_COOKIE]);
EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
values_cursor += STYLE_NUM_ENTRIES;
EXPECT_EQ(Res_value::TYPE_INT_DEC, values_cursor[STYLE_TYPE]);
EXPECT_EQ(10u, values_cursor[STYLE_DATA]);
EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
EXPECT_EQ(uint32_t(-1), values_cursor[STYLE_ASSET_COOKIE]);
EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
EXPECT_EQ(0u, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
values_cursor += STYLE_NUM_ENTRIES;
EXPECT_EQ(Res_value::TYPE_INT_DEC, values_cursor[STYLE_TYPE]);
EXPECT_EQ(3u, values_cursor[STYLE_DATA]);
EXPECT_EQ(0u, values_cursor[STYLE_RESOURCE_ID]);
EXPECT_EQ(1u, values_cursor[STYLE_ASSET_COOKIE]);
EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
values_cursor += STYLE_NUM_ENTRIES;
EXPECT_EQ(Res_value::TYPE_STRING, values_cursor[STYLE_TYPE]);
EXPECT_EQ(R::string::string_one, values_cursor[STYLE_RESOURCE_ID]);
EXPECT_EQ(1u, values_cursor[STYLE_ASSET_COOKIE]);
EXPECT_EQ(0u, values_cursor[STYLE_DENSITY]);
EXPECT_EQ(public_flag, values_cursor[STYLE_CHANGING_CONFIGURATIONS]);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2014 The Android Open Source Project
* 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.
@@ -17,32 +17,46 @@
#include "TestHelpers.h"
#include <androidfw/ResourceTypes.h>
#include <utils/String8.h>
#include <gtest/gtest.h>
#include <unistd.h>
#include <utils/String8.h>
std::string TestSourceDir() {
const char* dir = getenv("ANDROID_BUILD_TOP");
LOG_ALWAYS_FATAL_IF(dir == nullptr, "Environment variable ANDROID_BUILD_TOP must be set");
std::string testdir = std::string(dir) + "/frameworks/base/libs/androidfw/tests/data";
// Check that the directory exists.
struct stat filestat;
LOG_ALWAYS_FATAL_IF(stat(testdir.c_str(), &filestat) != 0, "test data path '%s' does not exist",
testdir.c_str());
return testdir;
}
namespace android {
::testing::AssertionResult IsStringEqual(const ResTable& table, uint32_t resourceId, const char* expectedStr) {
Res_value val;
ssize_t block = table.getResource(resourceId, &val, MAY_NOT_BE_BAG);
if (block < 0) {
return ::testing::AssertionFailure() << "could not find resource";
}
::testing::AssertionResult IsStringEqual(const ResTable& table, uint32_t resource_id,
const char* expected_str) {
Res_value val;
ssize_t block = table.getResource(resource_id, &val, MAY_NOT_BE_BAG);
if (block < 0) {
return ::testing::AssertionFailure() << "could not find resource";
}
if (val.dataType != Res_value::TYPE_STRING) {
return ::testing::AssertionFailure() << "resource is not a string";
}
if (val.dataType != Res_value::TYPE_STRING) {
return ::testing::AssertionFailure() << "resource is not a string";
}
const ResStringPool* pool = table.getTableStringBlock(block);
if (pool == NULL) {
return ::testing::AssertionFailure() << "table has no string pool for block " << block;
}
const ResStringPool* pool = table.getTableStringBlock(block);
if (pool == NULL) {
return ::testing::AssertionFailure() << "table has no string pool for block " << block;
}
const String8 actual = pool->string8ObjectAt(val.data);
if (String8(expectedStr) != actual) {
return ::testing::AssertionFailure() << actual.string();
}
return ::testing::AssertionSuccess() << actual.string();
const String8 actual_str = pool->string8ObjectAt(val.data);
if (String8(expected_str) != actual_str) {
return ::testing::AssertionFailure() << actual_str.string();
}
return ::testing::AssertionSuccess() << actual_str.string();
}
} // namespace android
} // namespace android

View File

@@ -1,35 +1,56 @@
/*
* 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 __TEST_HELPERS_H
#define __TEST_HELPERS_H
#include <ostream>
#include <androidfw/ResourceTypes.h>
#include <utils/String8.h>
#include <utils/String16.h>
#include <gtest/gtest.h>
#include <utils/String16.h>
#include <utils/String8.h>
#include <ostream>
#include <string>
std::string TestSourceDir();
static inline ::std::ostream& operator<<(::std::ostream& out, const android::String8& str) {
return out << str.string();
return out << str.string();
}
static inline ::std::ostream& operator<<(::std::ostream& out, const android::String16& str) {
return out << android::String8(str).string();
return out << android::String8(str).string();
}
namespace android {
enum { MAY_NOT_BE_BAG = false };
static inline bool operator==(const android::ResTable_config& a, const android::ResTable_config& b) {
return a.compare(b) == 0;
static inline bool operator==(const android::ResTable_config& a,
const android::ResTable_config& b) {
return a.compare(b) == 0;
}
static inline ::std::ostream& operator<<(::std::ostream& out, const android::ResTable_config& c) {
return out << c.toString().string();
return out << c.toString().string();
}
::testing::AssertionResult IsStringEqual(const ResTable& table, uint32_t resourceId, const char* expectedStr);
::testing::AssertionResult IsStringEqual(const ResTable& table, uint32_t resource_id,
const char* expected_str);
} // namespace android
} // namespace android
#endif // __TEST_HELPERS_H
#endif // __TEST_HELPERS_H

View File

@@ -1,2 +0,0 @@
*.apk
*.arsc

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.app">
</manifest>

View File

@@ -0,0 +1,35 @@
#include <cstdint>
namespace com {
namespace android {
namespace app {
struct R {
struct attr {
enum : uint32_t {
attr_one = 0x7f010000u,
attr_two = 0x7f010001u,
attr_three = 0x7f010002u,
attr_four = 0x7f010003u,
attr_five = 0x7f010004u,
attr_indirect = 0x7f010005u,
};
};
struct string {
enum : uint32_t {
string_one = 0x7f030000u,
};
};
struct style {
enum : uint32_t {
StyleOne = 0x7f020000u,
StyleTwo = 0x7f020001u,
};
};
};
} // namespace app
} // namespace android
} // namespace com

View File

@@ -0,0 +1,7 @@
#!/bin/bash
set -e
aapt package -F package.apk -M AndroidManifest.xml -S res
unzip -j package.apk resources.arsc res/layout/layout.xml
rm package.apk

Binary file not shown.

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<View xmlns:app="http://schemas.android.com/apk/res-auto"
app:attr_four="?attr/attr_indirect"
app:attr_three="10" />

View File

@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2014 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.
-->
<resources>
<public type="attr" name="attr_one" id="0x7f010000" />
<attr name="attr_one" />
<public type="attr" name="attr_two" id="0x7f010001" />
<attr name="attr_two" />
<public type="attr" name="attr_three" id="0x7f010002" />
<attr name="attr_three" />
<public type="attr" name="attr_four" id="0x7f010003" />
<attr name="attr_four" />
<public type="attr" name="attr_five" id="0x7f010004" />
<attr name="attr_five" />
<public type="attr" name="attr_indirect" id="0x7f010005" />
<attr name="attr_indirect" />
<public type="string" name="string_one" id="0x7f030000" />
<string name="string_one">Hi</string>
<public type="style" name="StyleOne" id="0x7f020000" />
<style name="StyleOne">
<item name="attr_one">1</item>
</style>
<public type="style" name="StyleTwo" id="0x7f020001" />
<style name="StyleTwo" parent="@style/StyleOne">
<item name="attr_indirect">3</item>
<item name="attr_two">"string"</item>
<item name="attr_three">?attr/attr_indirect</item>
<item name="attr_five">@string/string_one</item>
</style>
</resources>

Binary file not shown.