diff options
Diffstat (limited to 'tools')
| -rw-r--r-- | tools/aapt/Command.cpp | 27 | ||||
| -rw-r--r-- | tools/aapt/Images.cpp | 6 | ||||
| -rw-r--r-- | tools/aapt/Resource.cpp | 30 | ||||
| -rw-r--r-- | tools/aapt/ResourceFilter.cpp | 58 | ||||
| -rw-r--r-- | tools/aapt/ResourceTable.cpp | 36 | ||||
| -rw-r--r-- | tools/aapt2/Android.mk | 1 | ||||
| -rw-r--r-- | tools/aapt2/Main.cpp | 5 | ||||
| -rw-r--r-- | tools/aapt2/ResourceParser.cpp | 12 | ||||
| -rw-r--r-- | tools/aapt2/ResourceTable.cpp | 11 | ||||
| -rw-r--r-- | tools/aapt2/ResourceTable.h | 4 | ||||
| -rw-r--r-- | tools/aapt2/ResourceUtils.cpp | 2 | ||||
| -rw-r--r-- | tools/aapt2/ResourceValues.cpp | 172 | ||||
| -rw-r--r-- | tools/aapt2/ResourceValues.h | 43 | ||||
| -rw-r--r-- | tools/aapt2/ValueVisitor.h | 6 | ||||
| -rw-r--r-- | tools/aapt2/compile/PseudolocaleGenerator.cpp | 41 | ||||
| -rw-r--r-- | tools/aapt2/diff/Diff.cpp | 411 | ||||
| -rw-r--r-- | tools/aapt2/link/ManifestFixer.cpp | 4 | ||||
| -rw-r--r-- | tools/aapt2/proto/TableProtoDeserializer.cpp | 2 | ||||
| -rwxr-xr-x | tools/fonts/fontchain_lint.py | 40 | ||||
| -rw-r--r-- | tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java | 16 | ||||
| -rw-r--r-- | tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java | 9 |
21 files changed, 842 insertions, 94 deletions
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp index 9976d00fa872..59da467a1cc3 100644 --- a/tools/aapt/Command.cpp +++ b/tools/aapt/Command.cpp @@ -529,6 +529,16 @@ struct FeatureGroup { int openGLESVersion; }; +static bool hasFeature(const char* name, const FeatureGroup& grp, + const KeyedVector<String8, ImpliedFeature>& implied) { + String8 name8(name); + ssize_t idx = grp.features.indexOfKey(name8); + if (idx < 0) { + idx = implied.indexOfKey(name8); + } + return idx >= 0; +} + static void addImpliedFeature(KeyedVector<String8, ImpliedFeature>* impliedFeatures, const char* name, const char* reason, bool sdk23) { String8 name8(name); @@ -616,9 +626,16 @@ static void addParentFeatures(FeatureGroup* grp, const String8& name) { } else if (name == "android.hardware.location.gps" || name == "android.hardware.location.network") { grp->features.add(String8("android.hardware.location"), Feature(true)); + } else if (name == "android.hardware.faketouch.multitouch") { + grp->features.add(String8("android.hardware.faketouch"), Feature(true)); + } else if (name == "android.hardware.faketouch.multitouch.distinct" || + name == "android.hardware.faketouch.multitouch.jazzhands") { + grp->features.add(String8("android.hardware.faketouch.multitouch"), Feature(true)); + grp->features.add(String8("android.hardware.faketouch"), Feature(true)); } else if (name == "android.hardware.touchscreen.multitouch") { grp->features.add(String8("android.hardware.touchscreen"), Feature(true)); - } else if (name == "android.hardware.touchscreen.multitouch.distinct") { + } else if (name == "android.hardware.touchscreen.multitouch.distinct" || + name == "android.hardware.touchscreen.multitouch.jazzhands") { grp->features.add(String8("android.hardware.touchscreen.multitouch"), Feature(true)); grp->features.add(String8("android.hardware.touchscreen"), Feature(true)); } else if (name == "android.hardware.opengles.aep") { @@ -2005,8 +2022,12 @@ int doDump(Bundle* bundle) } } - addImpliedFeature(&impliedFeatures, "android.hardware.touchscreen", - "default feature for all apps", false); + // If the app hasn't declared the touchscreen as a feature requirement (either + // directly or implied, required or not), then the faketouch feature is implied. + if (!hasFeature("android.hardware.touchscreen", commonFeatures, impliedFeatures)) { + addImpliedFeature(&impliedFeatures, "android.hardware.faketouch", + "default feature for all apps", false); + } const size_t numFeatureGroups = featureGroups.size(); if (numFeatureGroups == 0) { diff --git a/tools/aapt/Images.cpp b/tools/aapt/Images.cpp index 9939c188cdd9..aea16c715b95 100644 --- a/tools/aapt/Images.cpp +++ b/tools/aapt/Images.cpp @@ -808,13 +808,13 @@ static void checkNinePatchSerialization(Res_png_9patch* inPatch, void* data) assert(outPatch->paddingTop == inPatch->paddingTop); assert(outPatch->paddingBottom == inPatch->paddingBottom); for (int i = 0; i < outPatch->numXDivs; i++) { - assert(outPatch->xDivs[i] == inPatch->xDivs[i]); + assert(outPatch->getXDivs()[i] == inPatch->getXDivs()[i]); } for (int i = 0; i < outPatch->numYDivs; i++) { - assert(outPatch->yDivs[i] == inPatch->yDivs[i]); + assert(outPatch->getYDivs()[i] == inPatch->getYDivs()[i]); } for (int i = 0; i < outPatch->numColors; i++) { - assert(outPatch->colors[i] == inPatch->colors[i]); + assert(outPatch->getColors()[i] == inPatch->getColors()[i]); } free(newData); } diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp index e6407332bb90..5f91f17b05a3 100644 --- a/tools/aapt/Resource.cpp +++ b/tools/aapt/Resource.cpp @@ -326,13 +326,18 @@ static status_t makeFileResources(Bundle* bundle, const sp<AaptAssets>& assets, } String8 resPath = it.getPath(); resPath.convertToResPath(); - table->addEntry(SourcePos(it.getPath(), 0), String16(assets->getPackage()), + status_t result = table->addEntry(SourcePos(it.getPath(), 0), + String16(assets->getPackage()), type16, baseName, String16(resPath), NULL, &it.getParams()); - assets->addResource(it.getLeafName(), resPath, it.getFile(), type8); + if (result != NO_ERROR) { + hasErrors = true; + } else { + assets->addResource(it.getLeafName(), resPath, it.getFile(), type8); + } } return hasErrors ? STATUST(UNKNOWN_ERROR) : NO_ERROR; @@ -1028,7 +1033,6 @@ static ssize_t extractPlatformBuildVersion(AssetManager& assets, Bundle* bundle) return NO_ERROR; } - ResXMLTree tree; Asset* asset = assets.openNonAsset(cookie, "AndroidManifest.xml", Asset::ACCESS_STREAMING); if (asset == NULL) { fprintf(stderr, "ERROR: Platform AndroidManifest.xml not found\n"); @@ -1036,11 +1040,17 @@ static ssize_t extractPlatformBuildVersion(AssetManager& assets, Bundle* bundle) } ssize_t result = NO_ERROR; - if (tree.setTo(asset->getBuffer(true), asset->getLength()) != NO_ERROR) { - fprintf(stderr, "ERROR: Platform AndroidManifest.xml is corrupt\n"); - result = UNKNOWN_ERROR; - } else { - result = extractPlatformBuildVersion(tree, bundle); + + // Create a new scope so that ResXMLTree is destroyed before we delete the memory over + // which it iterates (asset). + { + ResXMLTree tree; + if (tree.setTo(asset->getBuffer(true), asset->getLength()) != NO_ERROR) { + fprintf(stderr, "ERROR: Platform AndroidManifest.xml is corrupt\n"); + result = UNKNOWN_ERROR; + } else { + result = extractPlatformBuildVersion(tree, bundle); + } } delete asset; @@ -1370,6 +1380,10 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets, sp<ApkBuil } } + if (hasErrors) { + return UNKNOWN_ERROR; + } + // -------------------------------------------------------------------- // Assignment of resource IDs and initial generation of resource table. // -------------------------------------------------------------------- diff --git a/tools/aapt/ResourceFilter.cpp b/tools/aapt/ResourceFilter.cpp index 8693999c7cc4..ed06f605eaeb 100644 --- a/tools/aapt/ResourceFilter.cpp +++ b/tools/aapt/ResourceFilter.cpp @@ -54,6 +54,46 @@ WeakResourceFilter::parse(const String8& str) return NO_ERROR; } +// Returns true if the locale script of the config should be considered matching +// the locale script of entry. +// +// If both the scripts are empty, the scripts are considered matching for +// backward compatibility reasons. +// +// If only one script is empty, we try to compute it based on the provided +// language and country. If we could not compute it, we assume it's either a +// new language we don't know about, or a private use language. We return true +// since we don't know any better and they might as well be a match. +// +// Finally, when we have two scripts (one of which could be computed), we return +// true if and only if they are an exact match. +inline bool +scriptsMatch(const ResTable_config& config, const ResTable_config& entry) { + const char* configScript = config.localeScript; + const char* entryScript = entry.localeScript; + if (configScript[0] == '\0' && entryScript[0] == '\0') { + return true; // both scripts are empty. We match for backward compatibility reasons. + } + + char scriptBuffer[sizeof(config.localeScript)]; + if (configScript[0] == '\0') { + localeDataComputeScript(scriptBuffer, config.language, config.country); + if (scriptBuffer[0] == '\0') { // We can't compute the script, so we match. + return true; + } + configScript = scriptBuffer; + } else if (entryScript[0] == '\0') { + localeDataComputeScript( + scriptBuffer, entry.language, entry.country); + if (scriptBuffer[0] == '\0') { // We can't compute the script, so we match. + return true; + } + entryScript = scriptBuffer; + } + return (memcmp(configScript, entryScript, sizeof(config.localeScript)) == 0); +} + + bool WeakResourceFilter::match(const ResTable_config& config) const { @@ -75,11 +115,19 @@ WeakResourceFilter::match(const ResTable_config& config) const // If the locales differ, but the languages are the same and // the locale we are matching only has a language specified, // we match. - if (config.language[0] && - memcmp(config.language, entry.first.language, sizeof(config.language)) == 0) { - if (config.country[0] == 0) { - matchedAxis |= ResTable_config::CONFIG_LOCALE; - } + // + // Exception: we won't match if a script is specified for at least + // one of the locales and it's different from the other locale's + // script. (We will compute the other script if at least one of the + // scripts were explicitly set. In cases we can't compute an script, + // we match.) + if (config.language[0] != '\0' && + config.country[0] == '\0' && + config.localeVariant[0] == '\0' && + config.language[0] == entry.first.language[0] && + config.language[1] == entry.first.language[1] && + scriptsMatch(config, entry.first)) { + matchedAxis |= ResTable_config::CONFIG_LOCALE; } } else if ((diff & entry.second) == ResTable_config::CONFIG_SMALLEST_SCREEN_SIZE) { // Special case if the smallest screen width doesn't match. We check that the diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp index 6a4b63789815..9cae00f778b4 100644 --- a/tools/aapt/ResourceTable.cpp +++ b/tools/aapt/ResourceTable.cpp @@ -2623,6 +2623,14 @@ status_t ResourceTable::assignResourceIds() const SourcePos unknown(String8("????"), 0); sp<Type> attr = p->getType(String16("attr"), unknown); + // Force creation of ID if we are building feature splits. + // Auto-generated ID resources won't apply the type ID offset correctly unless + // the offset is applied here first. + // b/30607637 + if (mPackageType == AppFeature && p->getName() == mAssetsPackage) { + sp<Type> id = p->getType(String16("id"), unknown); + } + // Assign indices... const size_t typeCount = p->getOrderedTypes().size(); for (size_t ti = 0; ti < typeCount; ti++) { @@ -4521,6 +4529,7 @@ bool ResourceTable::shouldGenerateVersionedResource( const ConfigDescription& sourceConfig, const int sdkVersionToGenerate) { assert(sdkVersionToGenerate > sourceConfig.sdkVersion); + assert(configList != NULL); const DefaultKeyedVector<ConfigDescription, sp<ResourceTable::Entry>>& entries = configList->getEntries(); ssize_t idx = entries.indexOfKey(sourceConfig); @@ -4851,24 +4860,39 @@ void ResourceTable::getDensityVaryingResources( const Vector<sp<Type> >& types = mOrderedPackages[p]->getOrderedTypes(); const size_t typeCount = types.size(); for (size_t t = 0; t < typeCount; t++) { - const Vector<sp<ConfigList> >& configs = types[t]->getOrderedConfigs(); + const sp<Type>& type = types[t]; + if (type == NULL) { + continue; + } + + const Vector<sp<ConfigList> >& configs = type->getOrderedConfigs(); const size_t configCount = configs.size(); for (size_t c = 0; c < configCount; c++) { + const sp<ConfigList>& configList = configs[c]; + if (configList == NULL) { + continue; + } + const DefaultKeyedVector<ConfigDescription, sp<Entry> >& configEntries - = configs[c]->getEntries(); + = configList->getEntries(); const size_t configEntryCount = configEntries.size(); for (size_t ce = 0; ce < configEntryCount; ce++) { + const sp<Entry>& entry = configEntries.valueAt(ce); + if (entry == NULL) { + continue; + } + const ConfigDescription& config = configEntries.keyAt(ce); if (AaptConfig::isDensityOnly(config)) { // This configuration only varies with regards to density. const Symbol symbol( mOrderedPackages[p]->getName(), - types[t]->getName(), - configs[c]->getName(), + type->getName(), + configList->getName(), getResId(mOrderedPackages[p], types[t], - configs[c]->getEntryIndex())); + configList->getEntryIndex())); + - const sp<Entry>& entry = configEntries.valueAt(ce); AaptUtil::appendValue(resources, symbol, SymbolDefinition(symbol, config, entry->getPos())); } diff --git a/tools/aapt2/Android.mk b/tools/aapt2/Android.mk index 3a1e2bb3bf08..4b5ea65d8fed 100644 --- a/tools/aapt2/Android.mk +++ b/tools/aapt2/Android.mk @@ -115,6 +115,7 @@ testSources := \ toolSources := \ compile/Compile.cpp \ + diff/Diff.cpp \ dump/Dump.cpp \ link/Link.cpp diff --git a/tools/aapt2/Main.cpp b/tools/aapt2/Main.cpp index a2fadd95db3f..00d8aaeeda55 100644 --- a/tools/aapt2/Main.cpp +++ b/tools/aapt2/Main.cpp @@ -24,6 +24,7 @@ namespace aapt { extern int compile(const std::vector<StringPiece>& args); extern int link(const std::vector<StringPiece>& args); extern int dump(const std::vector<StringPiece>& args); +extern int diff(const std::vector<StringPiece>& args); } // namespace aapt @@ -44,12 +45,14 @@ int main(int argc, char** argv) { return aapt::link(args); } else if (command == "dump" || command == "d") { return aapt::dump(args); + } else if (command == "diff") { + return aapt::diff(args); } std::cerr << "unknown command '" << command << "'\n"; } else { std::cerr << "no command specified\n"; } - std::cerr << "\nusage: aapt2 [compile|link|dump] ..." << std::endl; + std::cerr << "\nusage: aapt2 [compile|link|dump|diff] ..." << std::endl; return 1; } diff --git a/tools/aapt2/ResourceParser.cpp b/tools/aapt2/ResourceParser.cpp index 9704d97029b7..a84c306e2733 100644 --- a/tools/aapt2/ResourceParser.cpp +++ b/tools/aapt2/ResourceParser.cpp @@ -152,7 +152,7 @@ bool ResourceParser::flattenXmlSubtree(xml::XmlPullParser* parser, std::u16strin break; } - spanStack.back().lastChar = builder.str().size(); + spanStack.back().lastChar = builder.str().size() - 1; outStyleString->spans.push_back(spanStack.back()); spanStack.pop_back(); @@ -1058,6 +1058,16 @@ bool ResourceParser::parseArrayImpl(xml::XmlPullParser* parser, ParsedResource* std::unique_ptr<Array> array = util::make_unique<Array>(); + bool translateable = mOptions.translatable; + if (Maybe<StringPiece16> translateableAttr = xml::findAttribute(parser, u"translatable")) { + if (!ResourceUtils::tryParseBool(translateableAttr.value(), &translateable)) { + mDiag->error(DiagMessage(outResource->source) + << "invalid value for 'translatable'. Must be a boolean"); + return false; + } + } + array->setTranslateable(translateable); + bool error = false; const size_t depth = parser->getDepth(); while (xml::XmlPullParser::nextChildNode(parser, depth)) { diff --git a/tools/aapt2/ResourceTable.cpp b/tools/aapt2/ResourceTable.cpp index 8d734f3fc36d..e700ed98365a 100644 --- a/tools/aapt2/ResourceTable.cpp +++ b/tools/aapt2/ResourceTable.cpp @@ -189,6 +189,17 @@ std::vector<ResourceConfigValue*> ResourceEntry::findAllValues(const ConfigDescr return results; } +std::vector<ResourceConfigValue*> ResourceEntry::findValuesIf( + const std::function<bool(ResourceConfigValue*)>& f) { + std::vector<ResourceConfigValue*> results; + for (auto& configValue : values) { + if (f(configValue.get())) { + results.push_back(configValue.get()); + } + } + return results; +} + /** * The default handler for collisions. A return value of -1 means keep the * existing value, 0 means fail, and +1 means take the incoming value. diff --git a/tools/aapt2/ResourceTable.h b/tools/aapt2/ResourceTable.h index 7f5c2b8c0f37..5690ea6fa614 100644 --- a/tools/aapt2/ResourceTable.h +++ b/tools/aapt2/ResourceTable.h @@ -26,6 +26,7 @@ #include "io/File.h" #include <android-base/macros.h> +#include <functional> #include <map> #include <memory> #include <string> @@ -109,6 +110,9 @@ public: ResourceConfigValue* findOrCreateValue(const ConfigDescription& config, const StringPiece& product); std::vector<ResourceConfigValue*> findAllValues(const ConfigDescription& config); + std::vector<ResourceConfigValue*> findValuesIf( + const std::function<bool(ResourceConfigValue*)>& f); + private: DISALLOW_COPY_AND_ASSIGN(ResourceEntry); diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp index 74c48b0f8426..a0a7efc46476 100644 --- a/tools/aapt2/ResourceUtils.cpp +++ b/tools/aapt2/ResourceUtils.cpp @@ -289,7 +289,7 @@ std::unique_ptr<BinaryPrimitive> tryParseEnumSymbol(const Attribute* enumAttr, std::unique_ptr<BinaryPrimitive> tryParseFlagSymbol(const Attribute* flagAttr, const StringPiece16& str) { android::Res_value flags = { }; - flags.dataType = android::Res_value::TYPE_INT_DEC; + flags.dataType = android::Res_value::TYPE_INT_HEX; flags.data = 0u; if (util::trimWhitespace(str).empty()) { diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp index dd7ff013e524..c10b134cb36e 100644 --- a/tools/aapt2/ResourceValues.cpp +++ b/tools/aapt2/ResourceValues.cpp @@ -39,6 +39,14 @@ void BaseItem<Derived>::accept(RawValueVisitor* visitor) { RawString::RawString(const StringPool::Ref& ref) : value(ref) { } +bool RawString::equals(const Value* value) const { + const RawString* other = valueCast<RawString>(value); + if (!other) { + return false; + } + return *this->value == *other->value; +} + RawString* RawString::clone(StringPool* newPool) const { RawString* rs = new RawString(newPool->makeRef(*value)); rs->mComment = mComment; @@ -66,6 +74,15 @@ Reference::Reference(const ResourceNameRef& n, Type t) : Reference::Reference(const ResourceId& i, Type type) : id(i), referenceType(type) { } +bool Reference::equals(const Value* value) const { + const Reference* other = valueCast<Reference>(value); + if (!other) { + return false; + } + return referenceType == other->referenceType && privateReference == other->privateReference && + id == other->id && name == other->name; +} + bool Reference::flatten(android::Res_value* outValue) const { outValue->dataType = (referenceType == Reference::Type::kResource) ? android::Res_value::TYPE_REFERENCE : android::Res_value::TYPE_ATTRIBUTE; @@ -97,6 +114,10 @@ void Reference::print(std::ostream* out) const { } } +bool Id::equals(const Value* value) const { + return valueCast<Id>(value) != nullptr; +} + bool Id::flatten(android::Res_value* out) const { out->dataType = android::Res_value::TYPE_INT_BOOLEAN; out->data = util::hostToDevice32(0); @@ -111,15 +132,15 @@ void Id::print(std::ostream* out) const { *out << "(id)"; } -String::String(const StringPool::Ref& ref) : value(ref), mTranslateable(true) { -} - -void String::setTranslateable(bool val) { - mTranslateable = val; +String::String(const StringPool::Ref& ref) : value(ref) { } -bool String::isTranslateable() const { - return mTranslateable; +bool String::equals(const Value* value) const { + const String* other = valueCast<String>(value); + if (!other) { + return false; + } + return *this->value == *other->value; } bool String::flatten(android::Res_value* outValue) const { @@ -144,15 +165,24 @@ void String::print(std::ostream* out) const { *out << "(string) \"" << *value << "\""; } -StyledString::StyledString(const StringPool::StyleRef& ref) : value(ref), mTranslateable(true) { +StyledString::StyledString(const StringPool::StyleRef& ref) : value(ref) { } -void StyledString::setTranslateable(bool val) { - mTranslateable = val; -} +bool StyledString::equals(const Value* value) const { + const StyledString* other = valueCast<StyledString>(value); + if (!other) { + return false; + } -bool StyledString::isTranslateable() const { - return mTranslateable; + if (*this->value->str == *other->value->str) { + const std::vector<StringPool::Span>& spansA = this->value->spans; + const std::vector<StringPool::Span>& spansB = other->value->spans; + return std::equal(spansA.begin(), spansA.end(), spansB.begin(), + [](const StringPool::Span& a, const StringPool::Span& b) -> bool { + return *a.name == *b.name && a.firstChar == b.firstChar && a.lastChar == b.lastChar; + }); + } + return false; } bool StyledString::flatten(android::Res_value* outValue) const { @@ -174,11 +204,22 @@ StyledString* StyledString::clone(StringPool* newPool) const { void StyledString::print(std::ostream* out) const { *out << "(styled string) \"" << *value->str << "\""; + for (const StringPool::Span& span : value->spans) { + *out << " "<< *span.name << ":" << span.firstChar << "," << span.lastChar; + } } FileReference::FileReference(const StringPool::Ref& _path) : path(_path) { } +bool FileReference::equals(const Value* value) const { + const FileReference* other = valueCast<FileReference>(value); + if (!other) { + return false; + } + return *path == *other->path; +} + bool FileReference::flatten(android::Res_value* outValue) const { if (path.getIndex() > std::numeric_limits<uint32_t>::max()) { return false; @@ -209,6 +250,14 @@ BinaryPrimitive::BinaryPrimitive(uint8_t dataType, uint32_t data) { value.data = data; } +bool BinaryPrimitive::equals(const Value* value) const { + const BinaryPrimitive* other = valueCast<BinaryPrimitive>(value); + if (!other) { + return false; + } + return this->value.dataType == other->value.dataType && this->value.data == other->value.data; +} + bool BinaryPrimitive::flatten(android::Res_value* outValue) const { outValue->dataType = value.dataType; outValue->data = util::hostToDevice32(value.data); @@ -228,7 +277,7 @@ void BinaryPrimitive::print(std::ostream* out) const { *out << "(integer) " << static_cast<int32_t>(value.data); break; case android::Res_value::TYPE_INT_HEX: - *out << "(integer) " << std::hex << value.data << std::dec; + *out << "(integer) 0x" << std::hex << value.data << std::dec; break; case android::Res_value::TYPE_INT_BOOLEAN: *out << "(boolean) " << (value.data != 0 ? "true" : "false"); @@ -253,6 +302,21 @@ Attribute::Attribute(bool w, uint32_t t) : mWeak = w; } +bool Attribute::equals(const Value* value) const { + const Attribute* other = valueCast<Attribute>(value); + if (!other) { + return false; + } + + return this->typeMask == other->typeMask && this->minInt == other->minInt && + this->maxInt == other->maxInt && + std::equal(this->symbols.begin(), this->symbols.end(), + other->symbols.begin(), + [](const Symbol& a, const Symbol& b) -> bool { + return a.symbol.equals(&b.symbol) && a.value == b.value; + }); +} + Attribute* Attribute::clone(StringPool* /*newPool*/) const { return new Attribute(*this); } @@ -365,6 +429,14 @@ void Attribute::print(std::ostream* out) const { << "]"; } + if (minInt != std::numeric_limits<int32_t>::min()) { + *out << " min=" << minInt; + } + + if (maxInt != std::numeric_limits<int32_t>::max()) { + *out << " max=" << maxInt; + } + if (isWeak()) { *out << " [weak]"; } @@ -445,6 +517,21 @@ bool Attribute::matches(const Item* item, DiagMessage* outMsg) const { return true; } +bool Style::equals(const Value* value) const { + const Style* other = valueCast<Style>(value); + if (!other) { + return false; + } + if (bool(parent) != bool(other->parent) || + (parent && other->parent && !parent.value().equals(&other->parent.value()))) { + return false; + } + return std::equal(entries.begin(), entries.end(), other->entries.begin(), + [](const Entry& a, const Entry& b) -> bool { + return a.key.equals(&b.key) && a.value->equals(b.value.get()); + }); +} + Style* Style::clone(StringPool* newPool) const { Style* style = new Style(); style->parent = parent; @@ -484,6 +571,18 @@ static ::std::ostream& operator<<(::std::ostream& out, const Style::Entry& value return out; } +bool Array::equals(const Value* value) const { + const Array* other = valueCast<Array>(value); + if (!other) { + return false; + } + + return std::equal(items.begin(), items.end(), other->items.begin(), + [](const std::unique_ptr<Item>& a, const std::unique_ptr<Item>& b) -> bool { + return a->equals(b.get()); + }); +} + Array* Array::clone(StringPool* newPool) const { Array* array = new Array(); array->mComment = mComment; @@ -500,6 +599,21 @@ void Array::print(std::ostream* out) const { << "]"; } +bool Plural::equals(const Value* value) const { + const Plural* other = valueCast<Plural>(value); + if (!other) { + return false; + } + + return std::equal(values.begin(), values.end(), other->values.begin(), + [](const std::unique_ptr<Item>& a, const std::unique_ptr<Item>& b) -> bool { + if (bool(a) != bool(b)) { + return false; + } + return bool(a) == bool(b) || a->equals(b.get()); + }); +} + Plural* Plural::clone(StringPool* newPool) const { Plural* p = new Plural(); p->mComment = mComment; @@ -515,12 +629,42 @@ Plural* Plural::clone(StringPool* newPool) const { void Plural::print(std::ostream* out) const { *out << "(plural)"; + if (values[Zero]) { + *out << " zero=" << *values[Zero]; + } + + if (values[One]) { + *out << " one=" << *values[One]; + } + + if (values[Two]) { + *out << " two=" << *values[Two]; + } + + if (values[Few]) { + *out << " few=" << *values[Few]; + } + + if (values[Many]) { + *out << " many=" << *values[Many]; + } } static ::std::ostream& operator<<(::std::ostream& out, const std::unique_ptr<Item>& item) { return out << *item; } +bool Styleable::equals(const Value* value) const { + const Styleable* other = valueCast<Styleable>(value); + if (!other) { + return false; + } + return std::equal(entries.begin(), entries.end(), other->entries.begin(), + [](const Reference& a, const Reference& b) -> bool { + return a.equals(&b); + }); +} + Styleable* Styleable::clone(StringPool* /*newPool*/) const { return new Styleable(*this); } diff --git a/tools/aapt2/ResourceValues.h b/tools/aapt2/ResourceValues.h index 43354acf1d0b..aa1b550bdf09 100644 --- a/tools/aapt2/ResourceValues.h +++ b/tools/aapt2/ResourceValues.h @@ -54,6 +54,18 @@ struct Value { mWeak = val; } + // Whether the value is marked as translateable. + // This does not persist when flattened. + // It is only used during compilation phase. + void setTranslateable(bool val) { + mTranslateable = val; + } + + // Default true. + bool isTranslateable() const { + return mTranslateable; + } + /** * Returns the source where this value was defined. */ @@ -84,6 +96,8 @@ struct Value { mComment = std::move(str); } + virtual bool equals(const Value* value) const = 0; + /** * Calls the appropriate overload of ValueVisitor. */ @@ -103,6 +117,7 @@ protected: Source mSource; std::u16string mComment; bool mWeak = false; + bool mTranslateable = true; }; /** @@ -158,6 +173,7 @@ struct Reference : public BaseItem<Reference> { explicit Reference(const ResourceNameRef& n, Type type = Type::kResource); explicit Reference(const ResourceId& i, Type type = Type::kResource); + bool equals(const Value* value) const override; bool flatten(android::Res_value* outValue) const override; Reference* clone(StringPool* newPool) const override; void print(std::ostream* out) const override; @@ -168,6 +184,7 @@ struct Reference : public BaseItem<Reference> { */ struct Id : public BaseItem<Id> { Id() { mWeak = true; } + bool equals(const Value* value) const override; bool flatten(android::Res_value* out) const override; Id* clone(StringPool* newPool) const override; void print(std::ostream* out) const override; @@ -183,6 +200,7 @@ struct RawString : public BaseItem<RawString> { RawString(const StringPool::Ref& ref); + bool equals(const Value* value) const override; bool flatten(android::Res_value* outValue) const override; RawString* clone(StringPool* newPool) const override; void print(std::ostream* out) const override; @@ -193,17 +211,10 @@ struct String : public BaseItem<String> { String(const StringPool::Ref& ref); - // Whether the string is marked as translateable. This does not persist when flattened. - // It is only used during compilation phase. - void setTranslateable(bool val); - bool isTranslateable() const; - + bool equals(const Value* value) const override; bool flatten(android::Res_value* outValue) const override; String* clone(StringPool* newPool) const override; void print(std::ostream* out) const override; - -private: - bool mTranslateable; }; struct StyledString : public BaseItem<StyledString> { @@ -211,17 +222,10 @@ struct StyledString : public BaseItem<StyledString> { StyledString(const StringPool::StyleRef& ref); - // Whether the string is marked as translateable. This does not persist when flattened. - // It is only used during compilation phase. - void setTranslateable(bool val); - bool isTranslateable() const; - + bool equals(const Value* value) const override; bool flatten(android::Res_value* outValue) const override; StyledString* clone(StringPool* newPool) const override; void print(std::ostream* out) const override; - -private: - bool mTranslateable; }; struct FileReference : public BaseItem<FileReference> { @@ -235,6 +239,7 @@ struct FileReference : public BaseItem<FileReference> { FileReference() = default; FileReference(const StringPool::Ref& path); + bool equals(const Value* value) const override; bool flatten(android::Res_value* outValue) const override; FileReference* clone(StringPool* newPool) const override; void print(std::ostream* out) const override; @@ -250,6 +255,7 @@ struct BinaryPrimitive : public BaseItem<BinaryPrimitive> { BinaryPrimitive(const android::Res_value& val); BinaryPrimitive(uint8_t dataType, uint32_t data); + bool equals(const Value* value) const override; bool flatten(android::Res_value* outValue) const override; BinaryPrimitive* clone(StringPool* newPool) const override; void print(std::ostream* out) const override; @@ -268,6 +274,7 @@ struct Attribute : public BaseValue<Attribute> { Attribute(bool w, uint32_t t = 0u); + bool equals(const Value* value) const override; Attribute* clone(StringPool* newPool) const override; void printMask(std::ostream* out) const; void print(std::ostream* out) const override; @@ -290,6 +297,7 @@ struct Style : public BaseValue<Style> { std::vector<Entry> entries; + bool equals(const Value* value) const override; Style* clone(StringPool* newPool) const override; void print(std::ostream* out) const override; }; @@ -297,6 +305,7 @@ struct Style : public BaseValue<Style> { struct Array : public BaseValue<Array> { std::vector<std::unique_ptr<Item>> items; + bool equals(const Value* value) const override; Array* clone(StringPool* newPool) const override; void print(std::ostream* out) const override; }; @@ -314,6 +323,7 @@ struct Plural : public BaseValue<Plural> { std::array<std::unique_ptr<Item>, Count> values; + bool equals(const Value* value) const override; Plural* clone(StringPool* newPool) const override; void print(std::ostream* out) const override; }; @@ -321,6 +331,7 @@ struct Plural : public BaseValue<Plural> { struct Styleable : public BaseValue<Styleable> { std::vector<Reference> entries; + bool equals(const Value* value) const override; Styleable* clone(StringPool* newPool) const override; void print(std::ostream* out) const override; }; diff --git a/tools/aapt2/ValueVisitor.h b/tools/aapt2/ValueVisitor.h index ea2aa55764c1..b8bc5db7d6e4 100644 --- a/tools/aapt2/ValueVisitor.h +++ b/tools/aapt2/ValueVisitor.h @@ -127,6 +127,11 @@ struct DynCastVisitor<Item> : public RawValueVisitor { } }; +template <typename T> +const T* valueCast(const Value* value) { + return valueCast<T>(const_cast<Value*>(value)); +} + /** * Returns a valid pointer to T if the Value is of subtype T. * Otherwise, returns nullptr. @@ -141,7 +146,6 @@ T* valueCast(Value* value) { return visitor.value; } - inline void visitAllValuesInPackage(ResourceTablePackage* pkg, RawValueVisitor* visitor) { for (auto& type : pkg->types) { for (auto& entry : type->entries) { diff --git a/tools/aapt2/compile/PseudolocaleGenerator.cpp b/tools/aapt2/compile/PseudolocaleGenerator.cpp index 99c20778c816..d080e16c520b 100644 --- a/tools/aapt2/compile/PseudolocaleGenerator.cpp +++ b/tools/aapt2/compile/PseudolocaleGenerator.cpp @@ -128,23 +128,6 @@ struct Visitor : public RawValueVisitor { mPool(pool), mMethod(method), mLocalizer(method) { } - void visit(Array* array) override { - std::unique_ptr<Array> localized = util::make_unique<Array>(); - localized->items.resize(array->items.size()); - for (size_t i = 0; i < array->items.size(); i++) { - Visitor subVisitor(mPool, mMethod); - array->items[i]->accept(&subVisitor); - if (subVisitor.mItem) { - localized->items[i] = std::move(subVisitor.mItem); - } else { - localized->items[i] = std::unique_ptr<Item>(array->items[i]->clone(mPool)); - } - } - localized->setSource(array->getSource()); - localized->setWeak(true); - mValue = std::move(localized); - } - void visit(Plural* plural) override { std::unique_ptr<Plural> localized = util::make_unique<Plural>(); for (size_t i = 0; i < plural->values.size(); i++) { @@ -164,10 +147,6 @@ struct Visitor : public RawValueVisitor { } void visit(String* string) override { - if (!string->isTranslateable()) { - return; - } - std::u16string result = mLocalizer.start() + mLocalizer.text(*string->value) + mLocalizer.end(); std::unique_ptr<String> localized = util::make_unique<String>(mPool->makeRef(result)); @@ -177,10 +156,6 @@ struct Visitor : public RawValueVisitor { } void visit(StyledString* string) override { - if (!string->isTranslateable()) { - return; - } - mItem = pseudolocalizeStyledString(string, mMethod, mPool); mItem->setWeak(true); } @@ -238,14 +213,26 @@ void pseudolocalizeIfNeeded(const Pseudolocalizer::Method method, } } +/** + * A value is pseudolocalizable if it does not define a locale (or is the default locale) + * and is translateable. + */ +static bool isPseudolocalizable(ResourceConfigValue* configValue) { + const int diff = configValue->config.diff(ConfigDescription::defaultConfig()); + if (diff & ConfigDescription::CONFIG_LOCALE) { + return false; + } + return configValue->value->isTranslateable(); +} + } // namespace bool PseudolocaleGenerator::consume(IAaptContext* context, ResourceTable* table) { for (auto& package : table->packages) { for (auto& type : package->types) { for (auto& entry : type->entries) { - std::vector<ResourceConfigValue*> values = entry->findAllValues( - ConfigDescription::defaultConfig()); + std::vector<ResourceConfigValue*> values = entry->findValuesIf(isPseudolocalizable); + for (ResourceConfigValue* value : values) { pseudolocalizeIfNeeded(Pseudolocalizer::Method::kAccent, value, &table->stringPool, entry.get()); diff --git a/tools/aapt2/diff/Diff.cpp b/tools/aapt2/diff/Diff.cpp new file mode 100644 index 000000000000..20b7b59642ca --- /dev/null +++ b/tools/aapt2/diff/Diff.cpp @@ -0,0 +1,411 @@ +/* + * 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 "Flags.h" +#include "ResourceTable.h" +#include "io/ZipArchive.h" +#include "process/IResourceTableConsumer.h" +#include "process/SymbolTable.h" +#include "unflatten/BinaryResourceParser.h" + +#include <android-base/macros.h> + +namespace aapt { + +class DiffContext : public IAaptContext { +public: + const std::u16string& getCompilationPackage() override { + return mEmpty; + } + + uint8_t getPackageId() override { + return 0x0; + } + + IDiagnostics* getDiagnostics() override { + return &mDiagnostics; + } + + NameMangler* getNameMangler() override { + return &mNameMangler; + } + + SymbolTable* getExternalSymbols() override { + return &mSymbolTable; + } + + bool verbose() override { + return false; + } + +private: + std::u16string mEmpty; + StdErrDiagnostics mDiagnostics; + NameMangler mNameMangler = NameMangler(NameManglerPolicy{}); + SymbolTable mSymbolTable; +}; + +class LoadedApk { +public: + LoadedApk(const Source& source, std::unique_ptr<io::IFileCollection> apk, + std::unique_ptr<ResourceTable> table) : + mSource(source), mApk(std::move(apk)), mTable(std::move(table)) { + } + + io::IFileCollection* getFileCollection() { + return mApk.get(); + } + + ResourceTable* getResourceTable() { + return mTable.get(); + } + + const Source& getSource() { + return mSource; + } + +private: + Source mSource; + std::unique_ptr<io::IFileCollection> mApk; + std::unique_ptr<ResourceTable> mTable; + + DISALLOW_COPY_AND_ASSIGN(LoadedApk); +}; + +static std::unique_ptr<LoadedApk> loadApkFromPath(IAaptContext* context, const StringPiece& path) { + Source source(path); + std::string error; + std::unique_ptr<io::ZipFileCollection> apk = io::ZipFileCollection::create(path, &error); + if (!apk) { + context->getDiagnostics()->error(DiagMessage(source) << error); + return {}; + } + + io::IFile* file = apk->findFile("resources.arsc"); + if (!file) { + context->getDiagnostics()->error(DiagMessage(source) << "no resources.arsc found"); + return {}; + } + + std::unique_ptr<io::IData> data = file->openAsData(); + if (!data) { + context->getDiagnostics()->error(DiagMessage(source) << "could not open resources.arsc"); + return {}; + } + + std::unique_ptr<ResourceTable> table = util::make_unique<ResourceTable>(); + BinaryResourceParser parser(context, table.get(), source, data->data(), data->size()); + if (!parser.parse()) { + return {}; + } + + return util::make_unique<LoadedApk>(source, std::move(apk), std::move(table)); +} + +static void emitDiffLine(const Source& source, const StringPiece& message) { + std::cerr << source << ": " << message << "\n"; +} + +static bool isSymbolVisibilityDifferent(const Symbol& symbolA, const Symbol& symbolB) { + return symbolA.state != symbolB.state; +} + +template <typename Id> +static bool isIdDiff(const Symbol& symbolA, const Maybe<Id>& idA, + const Symbol& symbolB, const Maybe<Id>& idB) { + if (symbolA.state == SymbolState::kPublic || symbolB.state == SymbolState::kPublic) { + return idA != idB; + } + return false; +} + +static bool emitResourceConfigValueDiff(IAaptContext* context, + LoadedApk* apkA, + ResourceTablePackage* pkgA, + ResourceTableType* typeA, + ResourceEntry* entryA, + ResourceConfigValue* configValueA, + LoadedApk* apkB, + ResourceTablePackage* pkgB, + ResourceTableType* typeB, + ResourceEntry* entryB, + ResourceConfigValue* configValueB) { + Value* valueA = configValueA->value.get(); + Value* valueB = configValueB->value.get(); + if (!valueA->equals(valueB)) { + std::stringstream strStream; + strStream << "value " << pkgA->name << ":" << typeA->type << "/" << entryA->name + << " config=" << configValueA->config << " does not match:\n"; + valueA->print(&strStream); + strStream << "\n vs \n"; + valueB->print(&strStream); + emitDiffLine(apkB->getSource(), strStream.str()); + return true; + } + return false; +} + +static bool emitResourceEntryDiff(IAaptContext* context, + LoadedApk* apkA, + ResourceTablePackage* pkgA, + ResourceTableType* typeA, + ResourceEntry* entryA, + LoadedApk* apkB, + ResourceTablePackage* pkgB, + ResourceTableType* typeB, + ResourceEntry* entryB) { + bool diff = false; + for (std::unique_ptr<ResourceConfigValue>& configValueA : entryA->values) { + ResourceConfigValue* configValueB = entryB->findValue(configValueA->config); + if (!configValueB) { + std::stringstream strStream; + strStream << "missing " << pkgA->name << ":" << typeA->type << "/" << entryA->name + << " config=" << configValueA->config; + emitDiffLine(apkB->getSource(), strStream.str()); + diff = true; + } else { + diff |= emitResourceConfigValueDiff(context, apkA, pkgA, typeA, entryA, + configValueA.get(), apkB, pkgB, typeB, entryB, + configValueB); + } + } + + // Check for any newly added config values. + for (std::unique_ptr<ResourceConfigValue>& configValueB : entryB->values) { + ResourceConfigValue* configValueA = entryA->findValue(configValueB->config); + if (!configValueA) { + std::stringstream strStream; + strStream << "new config " << pkgB->name << ":" << typeB->type << "/" << entryB->name + << " config=" << configValueB->config; + emitDiffLine(apkB->getSource(), strStream.str()); + diff = true; + } + } + return false; +} + +static bool emitResourceTypeDiff(IAaptContext* context, + LoadedApk* apkA, + ResourceTablePackage* pkgA, + ResourceTableType* typeA, + LoadedApk* apkB, + ResourceTablePackage* pkgB, + ResourceTableType* typeB) { + bool diff = false; + for (std::unique_ptr<ResourceEntry>& entryA : typeA->entries) { + ResourceEntry* entryB = typeB->findEntry(entryA->name); + if (!entryB) { + std::stringstream strStream; + strStream << "missing " << pkgA->name << ":" << typeA->type << "/" << entryA->name; + emitDiffLine(apkB->getSource(), strStream.str()); + diff = true; + } else { + if (isSymbolVisibilityDifferent(entryA->symbolStatus, entryB->symbolStatus)) { + std::stringstream strStream; + strStream << pkgA->name << ":" << typeA->type << "/" << entryA->name + << " has different visibility ("; + if (entryB->symbolStatus.state == SymbolState::kPublic) { + strStream << "PUBLIC"; + } else { + strStream << "PRIVATE"; + } + strStream << " vs "; + if (entryA->symbolStatus.state == SymbolState::kPublic) { + strStream << "PUBLIC"; + } else { + strStream << "PRIVATE"; + } + strStream << ")"; + emitDiffLine(apkB->getSource(), strStream.str()); + diff = true; + } else if (isIdDiff(entryA->symbolStatus, entryA->id, + entryB->symbolStatus, entryB->id)) { + std::stringstream strStream; + strStream << pkgA->name << ":" << typeA->type << "/" << entryA->name + << " has different public ID ("; + if (entryB->id) { + strStream << "0x" << std::hex << entryB->id.value(); + } else { + strStream << "none"; + } + strStream << " vs "; + if (entryA->id) { + strStream << "0x " << std::hex << entryA->id.value(); + } else { + strStream << "none"; + } + strStream << ")"; + emitDiffLine(apkB->getSource(), strStream.str()); + diff = true; + } + diff |= emitResourceEntryDiff(context, apkA, pkgA, typeA, entryA.get(), + apkB, pkgB, typeB, entryB); + } + } + + // Check for any newly added entries. + for (std::unique_ptr<ResourceEntry>& entryB : typeB->entries) { + ResourceEntry* entryA = typeA->findEntry(entryB->name); + if (!entryA) { + std::stringstream strStream; + strStream << "new entry " << pkgB->name << ":" << typeB->type << "/" << entryB->name; + emitDiffLine(apkB->getSource(), strStream.str()); + diff = true; + } + } + return diff; +} + +static bool emitResourcePackageDiff(IAaptContext* context, LoadedApk* apkA, + ResourceTablePackage* pkgA, + LoadedApk* apkB, ResourceTablePackage* pkgB) { + bool diff = false; + for (std::unique_ptr<ResourceTableType>& typeA : pkgA->types) { + ResourceTableType* typeB = pkgB->findType(typeA->type); + if (!typeB) { + std::stringstream strStream; + strStream << "missing " << pkgA->name << ":" << typeA->type; + emitDiffLine(apkA->getSource(), strStream.str()); + diff = true; + } else { + if (isSymbolVisibilityDifferent(typeA->symbolStatus, typeB->symbolStatus)) { + std::stringstream strStream; + strStream << pkgA->name << ":" << typeA->type << " has different visibility ("; + if (typeB->symbolStatus.state == SymbolState::kPublic) { + strStream << "PUBLIC"; + } else { + strStream << "PRIVATE"; + } + strStream << " vs "; + if (typeA->symbolStatus.state == SymbolState::kPublic) { + strStream << "PUBLIC"; + } else { + strStream << "PRIVATE"; + } + strStream << ")"; + emitDiffLine(apkB->getSource(), strStream.str()); + diff = true; + } else if (isIdDiff(typeA->symbolStatus, typeA->id, typeB->symbolStatus, typeB->id)) { + std::stringstream strStream; + strStream << pkgA->name << ":" << typeA->type << " has different public ID ("; + if (typeB->id) { + strStream << "0x" << std::hex << typeB->id.value(); + } else { + strStream << "none"; + } + strStream << " vs "; + if (typeA->id) { + strStream << "0x " << std::hex << typeA->id.value(); + } else { + strStream << "none"; + } + strStream << ")"; + emitDiffLine(apkB->getSource(), strStream.str()); + diff = true; + } + diff |= emitResourceTypeDiff(context, apkA, pkgA, typeA.get(), apkB, pkgB, typeB); + } + } + + // Check for any newly added types. + for (std::unique_ptr<ResourceTableType>& typeB : pkgB->types) { + ResourceTableType* typeA = pkgA->findType(typeB->type); + if (!typeA) { + std::stringstream strStream; + strStream << "new type " << pkgB->name << ":" << typeB->type; + emitDiffLine(apkB->getSource(), strStream.str()); + diff = true; + } + } + return diff; +} + +static bool emitResourceTableDiff(IAaptContext* context, LoadedApk* apkA, LoadedApk* apkB) { + ResourceTable* tableA = apkA->getResourceTable(); + ResourceTable* tableB = apkB->getResourceTable(); + + bool diff = false; + for (std::unique_ptr<ResourceTablePackage>& pkgA : tableA->packages) { + ResourceTablePackage* pkgB = tableB->findPackage(pkgA->name); + if (!pkgB) { + std::stringstream strStream; + strStream << "missing package " << pkgA->name; + emitDiffLine(apkB->getSource(), strStream.str()); + diff = true; + } else { + if (pkgA->id != pkgB->id) { + std::stringstream strStream; + strStream << "package '" << pkgA->name << "' has different id ("; + if (pkgB->id) { + strStream << "0x" << std::hex << pkgB->id.value(); + } else { + strStream << "none"; + } + strStream << " vs "; + if (pkgA->id) { + strStream << "0x" << std::hex << pkgA->id.value(); + } else { + strStream << "none"; + } + strStream << ")"; + emitDiffLine(apkB->getSource(), strStream.str()); + diff = true; + } + diff |= emitResourcePackageDiff(context, apkA, pkgA.get(), apkB, pkgB); + } + } + + // Check for any newly added packages. + for (std::unique_ptr<ResourceTablePackage>& pkgB : tableB->packages) { + ResourceTablePackage* pkgA = tableA->findPackage(pkgB->name); + if (!pkgA) { + std::stringstream strStream; + strStream << "new package " << pkgB->name; + emitDiffLine(apkB->getSource(), strStream.str()); + diff = true; + } + } + return diff; +} + +int diff(const std::vector<StringPiece>& args) { + DiffContext context; + + Flags flags; + if (!flags.parse("aapt2 diff", args, &std::cerr)) { + return 1; + } + + if (flags.getArgs().size() != 2u) { + std::cerr << "must have two apks as arguments.\n\n"; + flags.usage("aapt2 diff", &std::cerr); + return 1; + } + + std::unique_ptr<LoadedApk> apkA = loadApkFromPath(&context, flags.getArgs()[0]); + std::unique_ptr<LoadedApk> apkB = loadApkFromPath(&context, flags.getArgs()[1]); + if (!apkA || !apkB) { + return 1; + } + + if (emitResourceTableDiff(&context, apkA.get(), apkB.get())) { + // We emitted a diff, so return 1 (failure). + return 1; + } + return 0; +} + +} // namespace aapt diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp index 953e87e104be..77a949f1339d 100644 --- a/tools/aapt2/link/ManifestFixer.cpp +++ b/tools/aapt2/link/ManifestFixer.cpp @@ -180,7 +180,6 @@ bool ManifestFixer::buildRules(xml::XmlActionExecutor* executor, IDiagnostics* d manifestAction[u"uses-configuration"]; manifestAction[u"uses-feature"]; - manifestAction[u"uses-library"]; manifestAction[u"supports-screens"]; manifestAction[u"compatible-screens"]; manifestAction[u"supports-gl-texture"]; @@ -189,6 +188,9 @@ bool ManifestFixer::buildRules(xml::XmlActionExecutor* executor, IDiagnostics* d xml::XmlNodeAction& applicationAction = (*executor)[u"manifest"][u"application"]; applicationAction.action(optionalNameIsJavaClassName); + // Uses library actions. + applicationAction[u"uses-library"]; + // Activity actions. applicationAction[u"activity"].action(requiredNameIsJavaClassName); applicationAction[u"activity"][u"intent-filter"] = intentFilterAction; diff --git a/tools/aapt2/proto/TableProtoDeserializer.cpp b/tools/aapt2/proto/TableProtoDeserializer.cpp index 82e4fb0146ab..1ec48f09f228 100644 --- a/tools/aapt2/proto/TableProtoDeserializer.cpp +++ b/tools/aapt2/proto/TableProtoDeserializer.cpp @@ -234,6 +234,8 @@ private: const pb::Attribute& pbAttr = pbCompoundValue.attr(); std::unique_ptr<Attribute> attr = util::make_unique<Attribute>(isWeak); attr->typeMask = pbAttr.format_flags(); + attr->minInt = pbAttr.min_int(); + attr->maxInt = pbAttr.max_int(); for (const pb::Attribute_Symbol& pbSymbol : pbAttr.symbols()) { Attribute::Symbol symbol; deserializeItemCommon(pbSymbol, &symbol.symbol); diff --git a/tools/fonts/fontchain_lint.py b/tools/fonts/fontchain_lint.py index ea36e2cb0d6a..2956d87f247c 100755 --- a/tools/fonts/fontchain_lint.py +++ b/tools/fonts/fontchain_lint.py @@ -256,23 +256,33 @@ def parse_fonts_xml(fonts_xml_path): def check_emoji_coverage(all_emoji, equivalent_emoji): + emoji_font = get_emoji_font() + check_emoji_font_coverage(emoji_font, all_emoji, equivalent_emoji) + + +def get_emoji_font(): emoji_fonts = [ record.font for record in _fallback_chain if 'Zsye' in record.scripts] assert len(emoji_fonts) == 1, 'There are %d emoji fonts.' % len(emoji_fonts) - emoji_font = emoji_fonts[0] - coverage = get_emoji_map(emoji_font) + return emoji_fonts[0] + +def check_emoji_font_coverage(emoji_font, all_emoji, equivalent_emoji): + coverage = get_emoji_map(emoji_font) for sequence in all_emoji: assert sequence in coverage, ( '%s is not supported in the emoji font.' % printable(sequence)) + # disable temporarily - we cover more than this + """ for sequence in coverage: if sequence in {0x0000, 0x000D, 0x0020}: # The font needs to support a few extra characters, which is OK continue assert sequence in all_emoji, ( 'Emoji font should not support %s.' % printable(sequence)) + """ for first, second in sorted(equivalent_emoji.items()): assert coverage[first] == coverage[second], ( @@ -280,6 +290,8 @@ def check_emoji_coverage(all_emoji, equivalent_emoji): printable(first), printable(second))) + # disable temporarily - some equivalent sequences we don't even know about + """ for glyph in set(coverage.values()): maps_to_glyph = [seq for seq in coverage if coverage[seq] == glyph] if len(maps_to_glyph) > 1: @@ -295,7 +307,7 @@ def check_emoji_coverage(all_emoji, equivalent_emoji): 'The sequences %s should not result in the same glyph %s' % ( printable(equivalent_seqs), glyph)) - + """ def check_emoji_defaults(default_emoji): missing_text_chars = _emoji_properties['Emoji'] - default_emoji @@ -427,6 +439,11 @@ def parse_ucd(ucd_path): _emoji_sequences = dict( (t, v) for (t, v) in _emoji_sequences.items() if not contains_excluded(t)) + # add in UN flag + UN_seq = flag_sequence('UN') + _emoji_sequences[UN_seq] = 'Emoji_Flag_Sequence' + + def flag_sequence(territory_code): return tuple(0x1F1E6 + ord(ch) - ord('A') for ch in territory_code) @@ -483,6 +500,11 @@ ZWJ_IDENTICALS = { (0x1F468, 0x200D, 0x1F469, 0x200D, 0x1F466): 0x1F46A, } + +def is_fitzpatrick_modifier(cp): + return 0x1f3fb <= cp <= 0x1f3ff + + def compute_expected_emoji(): equivalent_emoji = {} sequence_pieces = set() @@ -500,7 +522,15 @@ def compute_expected_emoji(): sequence_pieces.update(sequence) # Add reverse of all emoji ZWJ sequences, which are added to the fonts # as a workaround to get the sequences work in RTL text. - reversed_seq = tuple(reversed(sequence)) + reversed_seq = list(reversed(sequence)) + # if there are fitzpatrick modifiers in the sequence, keep them after + # the emoji they modify + for i in xrange(1, len(reversed_seq)): + if is_fitzpatrick_modifier(reversed_seq[i - 1]): + tmp = reversed_seq[i] + reversed_seq[i] = reversed_seq[i-1] + reversed_seq[i-1] = tmp + reversed_seq = tuple(reversed_seq) all_sequences.add(reversed_seq) equivalent_emoji[reversed_seq] = sequence @@ -536,8 +566,8 @@ def compute_expected_emoji(): def main(): - target_out = sys.argv[1] global _fonts_dir + target_out = sys.argv[1] _fonts_dir = path.join(target_out, 'fonts') fonts_xml_path = path.join(target_out, 'etc', 'fonts.xml') diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java index 04a59bcfb5ae..58df301b1cfc 100644 --- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java +++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java @@ -78,7 +78,8 @@ public class IWindowManagerImpl implements IWindowManager { @Override public void addAppToken(int arg0, IApplicationToken arg1, int arg2, int arg3, int arg4, boolean arg5, boolean arg6, int arg7, int arg8, boolean arg9, boolean arg10, - Rect arg11, Configuration arg12, int arg13, boolean arg14, boolean arg15, int arg16) + Rect arg11, Configuration arg12, int arg13, boolean arg14, boolean arg15, int arg16, + int arg17) throws RemoteException { // TODO Auto-generated method stub } @@ -350,7 +351,13 @@ public class IWindowManagerImpl implements IWindowManager { } @Override - public void notifyAppStopped(IBinder token, boolean stopped) throws RemoteException { + public void notifyAppResumed(IBinder token, boolean wasStopped, boolean allowSavedSurface) + throws RemoteException { + // TODO Auto-generated method stub + } + + @Override + public void notifyAppStopped(IBinder token) throws RemoteException { // TODO Auto-generated method stub } @@ -589,4 +596,9 @@ public class IWindowManagerImpl implements IWindowManager { @Override public void removeWallpaperInputConsumer() throws RemoteException {} + + @Override + public Bitmap screenshotWallpaper() throws RemoteException { + return null; + } } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java index 3f276c9375ba..ab73a8b820cd 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java @@ -16,11 +16,13 @@ package com.android.layoutlib.bridge.android; +import com.android.internal.inputmethod.IInputContentUriToken; import com.android.internal.view.IInputContext; import com.android.internal.view.IInputMethodClient; import com.android.internal.view.IInputMethodManager; import com.android.internal.view.InputBindResult; +import android.net.Uri; import android.os.IBinder; import android.os.RemoteException; import android.os.ResultReceiver; @@ -239,4 +241,11 @@ public class BridgeIInputMethodManager implements IInputMethodManager { // TODO Auto-generated method stub return null; } + + @Override + public IInputContentUriToken createInputContentUriToken(IBinder token, Uri contentUri, + String packageName) { + // TODO Auto-generated method stub + return null; + } } |