diff options
Diffstat (limited to 'tools')
| -rw-r--r-- | tools/aapt/AaptAssets.cpp | 5 | ||||
| -rw-r--r-- | tools/aapt/AaptAssets.h | 1 | ||||
| -rw-r--r-- | tools/aapt/AaptXml.cpp | 184 | ||||
| -rw-r--r-- | tools/aapt/AaptXml.h | 123 | ||||
| -rw-r--r-- | tools/aapt/Android.mk | 1 | ||||
| -rw-r--r-- | tools/aapt/Bundle.h | 6 | ||||
| -rw-r--r-- | tools/aapt/Command.cpp | 371 | ||||
| -rw-r--r-- | tools/aapt/Main.h | 3 | ||||
| -rw-r--r-- | tools/aapt/Resource.cpp | 135 | ||||
| -rw-r--r-- | tools/apilint/apilint.py | 540 |
10 files changed, 1118 insertions, 251 deletions
diff --git a/tools/aapt/AaptAssets.cpp b/tools/aapt/AaptAssets.cpp index b44e2d1..117fc24 100644 --- a/tools/aapt/AaptAssets.cpp +++ b/tools/aapt/AaptAssets.cpp @@ -1594,6 +1594,11 @@ const ResTable& AaptAssets::getIncludedResources() const return mIncludedAssets.getResources(false); } +AssetManager& AaptAssets::getAssetManager() +{ + return mIncludedAssets; +} + void AaptAssets::print(const String8& prefix) const { String8 innerPrefix(prefix); diff --git a/tools/aapt/AaptAssets.h b/tools/aapt/AaptAssets.h index 0c2576a..3fc9f81 100644 --- a/tools/aapt/AaptAssets.h +++ b/tools/aapt/AaptAssets.h @@ -561,6 +561,7 @@ public: status_t buildIncludedResources(Bundle* bundle); status_t addIncludedResources(const sp<AaptFile>& file); const ResTable& getIncludedResources() const; + AssetManager& getAssetManager(); void print(const String8& prefix) const; diff --git a/tools/aapt/AaptXml.cpp b/tools/aapt/AaptXml.cpp new file mode 100644 index 0000000..708e405 --- /dev/null +++ b/tools/aapt/AaptXml.cpp @@ -0,0 +1,184 @@ +/* + * 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. + */ + +#include <androidfw/ResourceTypes.h> +#include <utils/String8.h> + +#include "AaptXml.h" + +using namespace android; + +namespace AaptXml { + +static String8 getStringAttributeAtIndex(const ResXMLTree& tree, ssize_t attrIndex, + String8* outError) { + Res_value value; + if (tree.getAttributeValue(attrIndex, &value) < 0) { + if (outError != NULL) { + *outError = "could not find attribute at index"; + } + return String8(); + } + + if (value.dataType != Res_value::TYPE_STRING) { + if (outError != NULL) { + *outError = "attribute is not a string value"; + } + return String8(); + } + + size_t len; + const uint16_t* str = tree.getAttributeStringValue(attrIndex, &len); + return str ? String8(str, len) : String8(); +} + +static int32_t getIntegerAttributeAtIndex(const ResXMLTree& tree, ssize_t attrIndex, + int32_t defValue, String8* outError) { + Res_value value; + if (tree.getAttributeValue(attrIndex, &value) < 0) { + if (outError != NULL) { + *outError = "could not find attribute at index"; + } + return defValue; + } + + if (value.dataType < Res_value::TYPE_FIRST_INT + || value.dataType > Res_value::TYPE_LAST_INT) { + if (outError != NULL) { + *outError = "attribute is not an integer value"; + } + return defValue; + } + return value.data; +} + + +ssize_t indexOfAttribute(const ResXMLTree& tree, uint32_t attrRes) { + size_t attrCount = tree.getAttributeCount(); + for (size_t i = 0; i < attrCount; i++) { + if (tree.getAttributeNameResID(i) == attrRes) { + return (ssize_t)i; + } + } + return -1; +} + +String8 getAttribute(const ResXMLTree& tree, const char* ns, + const char* attr, String8* outError) { + ssize_t idx = tree.indexOfAttribute(ns, attr); + if (idx < 0) { + return String8(); + } + return getStringAttributeAtIndex(tree, idx, outError); +} + +String8 getAttribute(const ResXMLTree& tree, uint32_t attrRes, String8* outError) { + ssize_t idx = indexOfAttribute(tree, attrRes); + if (idx < 0) { + return String8(); + } + return getStringAttributeAtIndex(tree, idx, outError); +} + +String8 getResolvedAttribute(const ResTable& resTable, const ResXMLTree& tree, + uint32_t attrRes, String8* outError) { + ssize_t idx = indexOfAttribute(tree, attrRes); + if (idx < 0) { + return String8(); + } + Res_value value; + if (tree.getAttributeValue(idx, &value) != NO_ERROR) { + if (value.dataType == Res_value::TYPE_STRING) { + size_t len; + const uint16_t* str = tree.getAttributeStringValue(idx, &len); + return str ? String8(str, len) : String8(); + } + resTable.resolveReference(&value, 0); + if (value.dataType != Res_value::TYPE_STRING) { + if (outError != NULL) { + *outError = "attribute is not a string value"; + } + return String8(); + } + } + size_t len; + const Res_value* value2 = &value; + const char16_t* str = resTable.valueToString(value2, 0, NULL, &len); + return str ? String8(str, len) : String8(); +} + +int32_t getIntegerAttribute(const ResXMLTree& tree, const char* ns, + const char* attr, int32_t defValue, String8* outError) { + ssize_t idx = tree.indexOfAttribute(ns, attr); + if (idx < 0) { + return defValue; + } + return getIntegerAttributeAtIndex(tree, idx, defValue, outError); +} + +int32_t getIntegerAttribute(const ResXMLTree& tree, uint32_t attrRes, int32_t defValue, + String8* outError) { + ssize_t idx = indexOfAttribute(tree, attrRes); + if (idx < 0) { + return defValue; + } + return getIntegerAttributeAtIndex(tree, idx, defValue, outError); +} + +int32_t getResolvedIntegerAttribute(const ResTable& resTable, const ResXMLTree& tree, + uint32_t attrRes, int32_t defValue, String8* outError) { + ssize_t idx = indexOfAttribute(tree, attrRes); + if (idx < 0) { + return defValue; + } + Res_value value; + if (tree.getAttributeValue(idx, &value) != NO_ERROR) { + if (value.dataType == Res_value::TYPE_REFERENCE) { + resTable.resolveReference(&value, 0); + } + if (value.dataType < Res_value::TYPE_FIRST_INT + || value.dataType > Res_value::TYPE_LAST_INT) { + if (outError != NULL) { + *outError = "attribute is not an integer value"; + } + return defValue; + } + } + return value.data; +} + +void getResolvedResourceAttribute(const ResTable& resTable, const ResXMLTree& tree, + uint32_t attrRes, Res_value* outValue, String8* outError) { + ssize_t idx = indexOfAttribute(tree, attrRes); + if (idx < 0) { + if (outError != NULL) { + *outError = "attribute could not be found"; + } + return; + } + if (tree.getAttributeValue(idx, outValue) != NO_ERROR) { + if (outValue->dataType == Res_value::TYPE_REFERENCE) { + resTable.resolveReference(outValue, 0); + } + // The attribute was found and was resolved if need be. + return; + } + if (outError != NULL) { + *outError = "error getting resolved resource attribute"; + } +} + +} // namespace AaptXml diff --git a/tools/aapt/AaptXml.h b/tools/aapt/AaptXml.h new file mode 100644 index 0000000..16977f3 --- /dev/null +++ b/tools/aapt/AaptXml.h @@ -0,0 +1,123 @@ +/* + * 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 __AAPT_XML_H +#define __AAPT_XML_H + +#include <androidfw/ResourceTypes.h> +#include <utils/String8.h> + +/** + * Utility methods for dealing with ResXMLTree. + */ +namespace AaptXml { + +/** + * Returns the index of the attribute, or < 0 if it was not found. + */ +ssize_t indexOfAttribute(const android::ResXMLTree& tree, uint32_t attrRes); + +/** + * Returns the string value for the specified attribute. + * The string must be present in the ResXMLTree's string pool (inline in the XML). + */ +android::String8 getAttribute(const android::ResXMLTree& tree, const char* ns, + const char* attr, android::String8* outError = NULL); + +/** + * Returns the string value for the specified attribute, or an empty string + * if the attribute does not exist. + * The string must be present in the ResXMLTree's string pool (inline in the XML). + */ +android::String8 getAttribute(const android::ResXMLTree& tree, uint32_t attrRes, + android::String8* outError = NULL); + +/** + * Returns the integer value for the specified attribute, or the default value + * if the attribute does not exist. + * The integer must be declared inline in the XML. + */ +int32_t getIntegerAttribute(const android::ResXMLTree& tree, const char* ns, + const char* attr, int32_t defValue = -1, android::String8* outError = NULL); + +/** + * Returns the integer value for the specified attribute, or the default value + * if the attribute does not exist. + * The integer must be declared inline in the XML. + */ +inline int32_t getIntegerAttribute(const android::ResXMLTree& tree, const char* ns, + const char* attr, android::String8* outError) { + return getIntegerAttribute(tree, ns, attr, -1, outError); +} + +/** + * Returns the integer value for the specified attribute, or the default value + * if the attribute does not exist. + * The integer must be declared inline in the XML. + */ +int32_t getIntegerAttribute(const android::ResXMLTree& tree, uint32_t attrRes, + int32_t defValue = -1, android::String8* outError = NULL); + +/** + * Returns the integer value for the specified attribute, or the default value + * if the attribute does not exist. + * The integer must be declared inline in the XML. + */ +inline int32_t getIntegerAttribute(const android::ResXMLTree& tree, uint32_t attrRes, + android::String8* outError) { + return getIntegerAttribute(tree, attrRes, -1, outError); +} + +/** + * Returns the integer value for the specified attribute, or the default value + * if the attribute does not exist. + * The integer may be a resource in the supplied ResTable. + */ +int32_t getResolvedIntegerAttribute(const android::ResTable& resTable, + const android::ResXMLTree& tree, uint32_t attrRes, int32_t defValue = -1, + android::String8* outError = NULL); + +/** + * Returns the integer value for the specified attribute, or the default value + * if the attribute does not exist. + * The integer may be a resource in the supplied ResTable. + */ +inline int32_t getResolvedIntegerAttribute(const android::ResTable& resTable, + const android::ResXMLTree& tree, uint32_t attrRes, + android::String8* outError) { + return getResolvedIntegerAttribute(resTable, tree, attrRes, -1, outError); +} + +/** + * Returns the string value for the specified attribute, or an empty string + * if the attribute does not exist. + * The string may be a resource in the supplied ResTable. + */ +android::String8 getResolvedAttribute(const android::ResTable& resTable, + const android::ResXMLTree& tree, uint32_t attrRes, + android::String8* outError = NULL); + +/** + * Returns the resource for the specified attribute in the outValue parameter. + * The resource may be a resource in the supplied ResTable. + */ +void getResolvedResourceAttribute(const android::ResTable& resTable, + const android::ResXMLTree& tree, uint32_t attrRes, android::Res_value* outValue, + android::String8* outError = NULL); + +} // namespace AaptXml + +#endif // __AAPT_XML_H diff --git a/tools/aapt/Android.mk b/tools/aapt/Android.mk index 4ce5045..2cbabe1 100644 --- a/tools/aapt/Android.mk +++ b/tools/aapt/Android.mk @@ -28,6 +28,7 @@ aaptSources := \ AaptAssets.cpp \ AaptConfig.cpp \ AaptUtil.cpp \ + AaptXml.cpp \ ApkBuilder.cpp \ Command.cpp \ CrunchCache.cpp \ diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h index af49461..9bed899 100644 --- a/tools/aapt/Bundle.h +++ b/tools/aapt/Bundle.h @@ -130,6 +130,10 @@ public: void setErrorOnFailedInsert(bool val) { mErrorOnFailedInsert = val; } bool getErrorOnMissingConfigEntry() { return mErrorOnMissingConfigEntry; } void setErrorOnMissingConfigEntry(bool val) { mErrorOnMissingConfigEntry = val; } + const android::String8& getPlatformBuildVersionCode() { return mPlatformVersionCode; } + void setPlatformBuildVersionCode(const android::String8& code) { mPlatformVersionCode = code; } + const android::String8& getPlatformBuildVersionName() { return mPlatformVersionName; } + void setPlatformBuildVersionName(const android::String8& name) { mPlatformVersionName = name; } bool getUTF16StringsOption() { return mWantUTF16 || !isMinSdkAtLeast(SDK_FROYO); @@ -323,6 +327,8 @@ private: const char* mSingleCrunchInputFile; const char* mSingleCrunchOutputFile; bool mBuildSharedLibrary; + android::String8 mPlatformVersionCode; + android::String8 mPlatformVersionName; /* file specification */ int mArgc; diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp index a0f0a08..27e60f3 100644 --- a/tools/aapt/Command.cpp +++ b/tools/aapt/Command.cpp @@ -3,6 +3,7 @@ // // Android Asset Packaging Tool main entry point. // +#include "AaptXml.h" #include "ApkBuilder.h" #include "Bundle.h" #include "Images.h" @@ -241,162 +242,17 @@ bail: return result; } -static ssize_t indexOfAttribute(const ResXMLTree& tree, uint32_t attrRes) -{ - size_t N = tree.getAttributeCount(); - for (size_t i=0; i<N; i++) { - if (tree.getAttributeNameResID(i) == attrRes) { - return (ssize_t)i; - } - } - return -1; -} - -String8 getAttribute(const ResXMLTree& tree, const char* ns, - const char* attr, String8* outError) -{ - ssize_t idx = tree.indexOfAttribute(ns, attr); - if (idx < 0) { - return String8(); - } - Res_value value; - if (tree.getAttributeValue(idx, &value) != NO_ERROR) { - if (value.dataType != Res_value::TYPE_STRING) { - if (outError != NULL) { - *outError = "attribute is not a string value"; - } - return String8(); - } - } - size_t len; - const uint16_t* str = tree.getAttributeStringValue(idx, &len); - return str ? String8(str, len) : String8(); -} - -static String8 getAttribute(const ResXMLTree& tree, uint32_t attrRes, String8* outError) -{ - ssize_t idx = indexOfAttribute(tree, attrRes); - if (idx < 0) { - return String8(); - } - Res_value value; - if (tree.getAttributeValue(idx, &value) != NO_ERROR) { - if (value.dataType != Res_value::TYPE_STRING) { - if (outError != NULL) { - *outError = "attribute is not a string value"; - } - return String8(); - } - } - size_t len; - const uint16_t* str = tree.getAttributeStringValue(idx, &len); - return str ? String8(str, len) : String8(); -} - -static int32_t getIntegerAttribute(const ResXMLTree& tree, uint32_t attrRes, - String8* outError, int32_t defValue = -1) -{ - ssize_t idx = indexOfAttribute(tree, attrRes); - if (idx < 0) { - return defValue; - } - Res_value value; - if (tree.getAttributeValue(idx, &value) != NO_ERROR) { - if (value.dataType < Res_value::TYPE_FIRST_INT - || value.dataType > Res_value::TYPE_LAST_INT) { - if (outError != NULL) { - *outError = "attribute is not an integer value"; - } - return defValue; - } - } - return value.data; -} - -static int32_t getResolvedIntegerAttribute(const ResTable* resTable, const ResXMLTree& tree, - uint32_t attrRes, String8* outError, int32_t defValue = -1) -{ - ssize_t idx = indexOfAttribute(tree, attrRes); - if (idx < 0) { - return defValue; - } - Res_value value; - if (tree.getAttributeValue(idx, &value) != NO_ERROR) { - if (value.dataType == Res_value::TYPE_REFERENCE) { - resTable->resolveReference(&value, 0); - } - if (value.dataType < Res_value::TYPE_FIRST_INT - || value.dataType > Res_value::TYPE_LAST_INT) { - if (outError != NULL) { - *outError = "attribute is not an integer value"; - } - return defValue; - } - } - return value.data; -} - -static String8 getResolvedAttribute(const ResTable* resTable, const ResXMLTree& tree, - uint32_t attrRes, String8* outError) -{ - ssize_t idx = indexOfAttribute(tree, attrRes); - if (idx < 0) { - return String8(); - } - Res_value value; - if (tree.getAttributeValue(idx, &value) != NO_ERROR) { - if (value.dataType == Res_value::TYPE_STRING) { - size_t len; - const uint16_t* str = tree.getAttributeStringValue(idx, &len); - return str ? String8(str, len) : String8(); - } - resTable->resolveReference(&value, 0); - if (value.dataType != Res_value::TYPE_STRING) { - if (outError != NULL) { - *outError = "attribute is not a string value"; - } - return String8(); - } - } - size_t len; - const Res_value* value2 = &value; - const char16_t* str = const_cast<ResTable*>(resTable)->valueToString(value2, 0, NULL, &len); - return str ? String8(str, len) : String8(); -} - -static void getResolvedResourceAttribute(Res_value* value, const ResTable* resTable, - const ResXMLTree& tree, uint32_t attrRes, String8* outError) -{ - ssize_t idx = indexOfAttribute(tree, attrRes); - if (idx < 0) { - if (outError != NULL) { - *outError = "attribute could not be found"; - } - return; - } - if (tree.getAttributeValue(idx, value) != NO_ERROR) { - if (value->dataType == Res_value::TYPE_REFERENCE) { - resTable->resolveReference(value, 0); - } - // The attribute was found and was resolved if need be. - return; - } - if (outError != NULL) { - *outError = "error getting resolved resource attribute"; - } -} - -static void printResolvedResourceAttribute(const ResTable* resTable, const ResXMLTree& tree, +static void printResolvedResourceAttribute(const ResTable& resTable, const ResXMLTree& tree, uint32_t attrRes, String8 attrLabel, String8* outError) { Res_value value; - getResolvedResourceAttribute(&value, resTable, tree, attrRes, outError); + AaptXml::getResolvedResourceAttribute(resTable, tree, attrRes, &value, outError); if (*outError != "") { *outError = "error print resolved resource attribute"; return; } if (value.dataType == Res_value::TYPE_STRING) { - String8 result = getResolvedAttribute(resTable, tree, attrRes, outError); + String8 result = AaptXml::getResolvedAttribute(resTable, tree, attrRes, outError); printf("%s='%s'", attrLabel.string(), ResTable::normalizeForOutput(result.string()).string()); } else if (Res_value::TYPE_FIRST_INT <= value.dataType && @@ -488,10 +344,10 @@ static void printCompatibleScreens(ResXMLTree& tree, String8* outError) { } String8 tag(ctag16); if (tag == "screen") { - int32_t screenSize = getIntegerAttribute(tree, - SCREEN_SIZE_ATTR, NULL, -1); - int32_t screenDensity = getIntegerAttribute(tree, - SCREEN_DENSITY_ATTR, NULL, -1); + int32_t screenSize = AaptXml::getIntegerAttribute(tree, + SCREEN_SIZE_ATTR); + int32_t screenDensity = AaptXml::getIntegerAttribute(tree, + SCREEN_DENSITY_ATTR); if (screenSize > 0 && screenDensity > 0) { if (!first) { printf(","); @@ -577,7 +433,7 @@ Vector<String8> getNfcAidCategories(AssetManager& assets, String8 xmlPath, bool } } else if (depth == 2 && withinApduService) { if (tag == "aid-group") { - String8 category = getAttribute(tree, CATEGORY_ATTR, &error); + String8 category = AaptXml::getAttribute(tree, CATEGORY_ATTR, &error); if (error != "") { if (outError != NULL) *outError = error; return Vector<String8>(); @@ -876,11 +732,11 @@ int doDump(Bundle* bundle) fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n"); goto bail; } - String8 pkg = getAttribute(tree, NULL, "package", NULL); + String8 pkg = AaptXml::getAttribute(tree, NULL, "package", NULL); printf("package: %s\n", ResTable::normalizeForOutput(pkg.string()).string()); } else if (depth == 2 && tag == "permission") { String8 error; - String8 name = getAttribute(tree, NAME_ATTR, &error); + String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error); if (error != "") { fprintf(stderr, "ERROR: %s\n", error.string()); goto bail; @@ -889,14 +745,14 @@ int doDump(Bundle* bundle) ResTable::normalizeForOutput(name.string()).string()); } else if (depth == 2 && tag == "uses-permission") { String8 error; - String8 name = getAttribute(tree, NAME_ATTR, &error); + String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error); if (error != "") { fprintf(stderr, "ERROR: %s\n", error.string()); goto bail; } printUsesPermission(name, - getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1) == 0, - getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1)); + AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0, + AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR)); } } } else if (strcmp("badging", option) == 0) { @@ -1151,12 +1007,14 @@ int doDump(Bundle* bundle) fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n"); goto bail; } - pkg = getAttribute(tree, NULL, "package", NULL); + pkg = AaptXml::getAttribute(tree, NULL, "package", NULL); printf("package: name='%s' ", ResTable::normalizeForOutput(pkg.string()).string()); - int32_t versionCode = getIntegerAttribute(tree, VERSION_CODE_ATTR, &error); + int32_t versionCode = AaptXml::getIntegerAttribute(tree, VERSION_CODE_ATTR, + &error); if (error != "") { - fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", error.string()); + fprintf(stderr, "ERROR getting 'android:versionCode' attribute: %s\n", + error.string()); goto bail; } if (versionCode > 0) { @@ -1164,23 +1022,29 @@ int doDump(Bundle* bundle) } else { printf("versionCode='' "); } - String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error); + String8 versionName = AaptXml::getResolvedAttribute(res, tree, + VERSION_NAME_ATTR, &error); if (error != "") { - fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string()); + fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", + error.string()); goto bail; } printf("versionName='%s'", ResTable::normalizeForOutput(versionName.string()).string()); - String8 splitName = getAttribute(tree, NULL, "split", NULL); + String8 splitName = AaptXml::getAttribute(tree, NULL, "split"); if (!splitName.isEmpty()) { printf(" split='%s'", ResTable::normalizeForOutput( splitName.string()).string()); } + + String8 platformVersionName = AaptXml::getAttribute(tree, NULL, + "platformBuildVersionName"); + printf(" platformBuildVersionName='%s'", platformVersionName.string()); printf("\n"); - int32_t installLocation = getResolvedIntegerAttribute(&res, tree, - INSTALL_LOCATION_ATTR, &error, -1); + int32_t installLocation = AaptXml::getResolvedIntegerAttribute(res, tree, + INSTALL_LOCATION_ATTR, &error); if (error != "") { fprintf(stderr, "ERROR getting 'android:installLocation' attribute: %s\n", error.string()); @@ -1215,7 +1079,8 @@ int doDump(Bundle* bundle) for (size_t i=0; i<NL; i++) { const char* localeStr = locales[i].string(); assets.setLocale(localeStr != NULL ? localeStr : ""); - String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error); + String8 llabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR, + &error); if (llabel != "") { if (localeStr == NULL || strlen(localeStr) == 0) { label = llabel; @@ -1236,7 +1101,8 @@ int doDump(Bundle* bundle) for (size_t i=0; i<ND; i++) { tmpConfig.density = densities[i]; assets.setConfiguration(tmpConfig); - String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error); + String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR, + &error); if (icon != "") { printf("application-icon-%d:'%s'\n", densities[i], ResTable::normalizeForOutput(icon.string()).string()); @@ -1244,14 +1110,17 @@ int doDump(Bundle* bundle) } assets.setConfiguration(config); - String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error); + String8 icon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR, &error); if (error != "") { - fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string()); + fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", + error.string()); goto bail; } - int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0); + int32_t testOnly = AaptXml::getIntegerAttribute(tree, TEST_ONLY_ATTR, 0, + &error); if (error != "") { - fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string()); + fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", + error.string()); goto bail; } printf("application: label='%s' ", @@ -1261,9 +1130,11 @@ int doDump(Bundle* bundle) printf("testOnly='%d'\n", testOnly); } - int32_t debuggable = getResolvedIntegerAttribute(&res, tree, DEBUGGABLE_ATTR, &error, 0); + int32_t debuggable = AaptXml::getResolvedIntegerAttribute(res, tree, + DEBUGGABLE_ATTR, 0, &error); if (error != "") { - fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n", error.string()); + fprintf(stderr, "ERROR getting 'android:debuggable' attribute: %s\n", + error.string()); goto bail; } if (debuggable != 0) { @@ -1284,10 +1155,11 @@ int doDump(Bundle* bundle) } } } else if (tag == "uses-sdk") { - int32_t code = getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error); + int32_t code = AaptXml::getIntegerAttribute(tree, MIN_SDK_VERSION_ATTR, &error); if (error != "") { error = ""; - String8 name = getResolvedAttribute(&res, tree, MIN_SDK_VERSION_ATTR, &error); + String8 name = AaptXml::getResolvedAttribute(res, tree, + MIN_SDK_VERSION_ATTR, &error); if (error != "") { fprintf(stderr, "ERROR getting 'android:minSdkVersion' attribute: %s\n", error.string()); @@ -1300,14 +1172,15 @@ int doDump(Bundle* bundle) targetSdk = code; printf("sdkVersion:'%d'\n", code); } - code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1); + code = AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR); if (code != -1) { printf("maxSdkVersion:'%d'\n", code); } - code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error); + code = AaptXml::getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error); if (error != "") { error = ""; - String8 name = getResolvedAttribute(&res, tree, TARGET_SDK_VERSION_ATTR, &error); + String8 name = AaptXml::getResolvedAttribute(res, tree, + TARGET_SDK_VERSION_ATTR, &error); if (error != "") { fprintf(stderr, "ERROR getting 'android:targetSdkVersion' attribute: %s\n", error.string()); @@ -1323,16 +1196,16 @@ int doDump(Bundle* bundle) printf("targetSdkVersion:'%d'\n", code); } } else if (tag == "uses-configuration") { - int32_t reqTouchScreen = getIntegerAttribute(tree, - REQ_TOUCH_SCREEN_ATTR, NULL, 0); - int32_t reqKeyboardType = getIntegerAttribute(tree, - REQ_KEYBOARD_TYPE_ATTR, NULL, 0); - int32_t reqHardKeyboard = getIntegerAttribute(tree, - REQ_HARD_KEYBOARD_ATTR, NULL, 0); - int32_t reqNavigation = getIntegerAttribute(tree, - REQ_NAVIGATION_ATTR, NULL, 0); - int32_t reqFiveWayNav = getIntegerAttribute(tree, - REQ_FIVE_WAY_NAV_ATTR, NULL, 0); + int32_t reqTouchScreen = AaptXml::getIntegerAttribute(tree, + REQ_TOUCH_SCREEN_ATTR, 0); + int32_t reqKeyboardType = AaptXml::getIntegerAttribute(tree, + REQ_KEYBOARD_TYPE_ATTR, 0); + int32_t reqHardKeyboard = AaptXml::getIntegerAttribute(tree, + REQ_HARD_KEYBOARD_ATTR, 0); + int32_t reqNavigation = AaptXml::getIntegerAttribute(tree, + REQ_NAVIGATION_ATTR, 0); + int32_t reqFiveWayNav = AaptXml::getIntegerAttribute(tree, + REQ_FIVE_WAY_NAV_ATTR, 0); printf("uses-configuration:"); if (reqTouchScreen != 0) { printf(" reqTouchScreen='%d'", reqTouchScreen); @@ -1353,26 +1226,26 @@ int doDump(Bundle* bundle) } else if (tag == "supports-input") { withinSupportsInput = true; } else if (tag == "supports-screens") { - smallScreen = getIntegerAttribute(tree, - SMALL_SCREEN_ATTR, NULL, 1); - normalScreen = getIntegerAttribute(tree, - NORMAL_SCREEN_ATTR, NULL, 1); - largeScreen = getIntegerAttribute(tree, - LARGE_SCREEN_ATTR, NULL, 1); - xlargeScreen = getIntegerAttribute(tree, - XLARGE_SCREEN_ATTR, NULL, 1); - anyDensity = getIntegerAttribute(tree, - ANY_DENSITY_ATTR, NULL, 1); - requiresSmallestWidthDp = getIntegerAttribute(tree, - REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0); - compatibleWidthLimitDp = getIntegerAttribute(tree, - COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0); - largestWidthLimitDp = getIntegerAttribute(tree, - LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0); + smallScreen = AaptXml::getIntegerAttribute(tree, + SMALL_SCREEN_ATTR, 1); + normalScreen = AaptXml::getIntegerAttribute(tree, + NORMAL_SCREEN_ATTR, 1); + largeScreen = AaptXml::getIntegerAttribute(tree, + LARGE_SCREEN_ATTR, 1); + xlargeScreen = AaptXml::getIntegerAttribute(tree, + XLARGE_SCREEN_ATTR, 1); + anyDensity = AaptXml::getIntegerAttribute(tree, + ANY_DENSITY_ATTR, 1); + requiresSmallestWidthDp = AaptXml::getIntegerAttribute(tree, + REQUIRES_SMALLEST_WIDTH_DP_ATTR, 0); + compatibleWidthLimitDp = AaptXml::getIntegerAttribute(tree, + COMPATIBLE_WIDTH_LIMIT_DP_ATTR, 0); + largestWidthLimitDp = AaptXml::getIntegerAttribute(tree, + LARGEST_WIDTH_LIMIT_DP_ATTR, 0); } else if (tag == "feature-group") { withinFeatureGroup = true; FeatureGroup group; - group.label = getResolvedAttribute(&res, tree, LABEL_ATTR, &error); + group.label = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR, &error); if (error != "") { fprintf(stderr, "ERROR getting 'android:label' attribute:" " %s\n", error.string()); @@ -1381,17 +1254,17 @@ int doDump(Bundle* bundle) featureGroups.add(group); } else if (tag == "uses-feature") { - String8 name = getAttribute(tree, NAME_ATTR, &error); + String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error); if (name != "" && error == "") { - int req = getIntegerAttribute(tree, - REQUIRED_ATTR, NULL, 1); + int req = AaptXml::getIntegerAttribute(tree, + REQUIRED_ATTR, 1); commonFeatures.features.add(name, req); if (req) { addParentFeatures(&commonFeatures, name); } } else { - int vers = getIntegerAttribute(tree, + int vers = AaptXml::getIntegerAttribute(tree, GL_ES_VERSION_ATTR, &error); if (error == "") { if (vers > commonFeatures.openGLESVersion) { @@ -1400,7 +1273,7 @@ int doDump(Bundle* bundle) } } } else if (tag == "uses-permission") { - String8 name = getAttribute(tree, NAME_ATTR, &error); + String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error); if (name != "" && error == "") { if (name == "android.permission.CAMERA") { addImpliedFeature(&impliedFeatures, "android.hardware.camera", @@ -1478,15 +1351,15 @@ int doDump(Bundle* bundle) } printUsesPermission(name, - getIntegerAttribute(tree, REQUIRED_ATTR, NULL, 1) == 0, - getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1)); + AaptXml::getIntegerAttribute(tree, REQUIRED_ATTR, 1) == 0, + AaptXml::getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR)); } else { fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string()); goto bail; } } else if (tag == "uses-package") { - String8 name = getAttribute(tree, NAME_ATTR, &error); + String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error); if (name != "" && error == "") { printf("uses-package:'%s'\n", ResTable::normalizeForOutput(name.string()).string()); @@ -1496,7 +1369,7 @@ int doDump(Bundle* bundle) goto bail; } } else if (tag == "original-package") { - String8 name = getAttribute(tree, NAME_ATTR, &error); + String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error); if (name != "" && error == "") { printf("original-package:'%s'\n", ResTable::normalizeForOutput(name.string()).string()); @@ -1506,7 +1379,7 @@ int doDump(Bundle* bundle) goto bail; } } else if (tag == "supports-gl-texture") { - String8 name = getAttribute(tree, NAME_ATTR, &error); + String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error); if (name != "" && error == "") { printf("supports-gl-texture:'%s'\n", ResTable::normalizeForOutput(name.string()).string()); @@ -1524,9 +1397,9 @@ int doDump(Bundle* bundle) } depth--; } else if (tag == "package-verifier") { - String8 name = getAttribute(tree, NAME_ATTR, &error); + String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error); if (name != "" && error == "") { - String8 publicKey = getAttribute(tree, PUBLIC_KEY_ATTR, &error); + String8 publicKey = AaptXml::getAttribute(tree, PUBLIC_KEY_ATTR, &error); if (publicKey != "" && error == "") { printf("package-verifier: name='%s' publicKey='%s'\n", ResTable::normalizeForOutput(name.string()).string(), @@ -1553,35 +1426,38 @@ int doDump(Bundle* bundle) if (withinApplication) { if(tag == "activity") { withinActivity = true; - activityName = getAttribute(tree, NAME_ATTR, &error); + activityName = AaptXml::getAttribute(tree, NAME_ATTR, &error); if (error != "") { fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string()); goto bail; } - activityLabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error); + activityLabel = AaptXml::getResolvedAttribute(res, tree, LABEL_ATTR, + &error); if (error != "") { fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n", error.string()); goto bail; } - activityIcon = getResolvedAttribute(&res, tree, ICON_ATTR, &error); + activityIcon = AaptXml::getResolvedAttribute(res, tree, ICON_ATTR, + &error); if (error != "") { fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string()); goto bail; } - activityBanner = getResolvedAttribute(&res, tree, BANNER_ATTR, &error); + activityBanner = AaptXml::getResolvedAttribute(res, tree, BANNER_ATTR, + &error); if (error != "") { fprintf(stderr, "ERROR getting 'android:banner' attribute: %s\n", error.string()); goto bail; } - int32_t orien = getResolvedIntegerAttribute(&res, tree, + int32_t orien = AaptXml::getResolvedIntegerAttribute(res, tree, SCREEN_ORIENTATION_ATTR, &error); if (error == "") { if (orien == 0 || orien == 6 || orien == 8) { @@ -1595,21 +1471,21 @@ int doDump(Bundle* bundle) } } } else if (tag == "uses-library") { - String8 libraryName = getAttribute(tree, NAME_ATTR, &error); + String8 libraryName = AaptXml::getAttribute(tree, NAME_ATTR, &error); if (error != "") { fprintf(stderr, "ERROR getting 'android:name' attribute for uses-library" " %s\n", error.string()); goto bail; } - int req = getIntegerAttribute(tree, - REQUIRED_ATTR, NULL, 1); + int req = AaptXml::getIntegerAttribute(tree, + REQUIRED_ATTR, 1); printf("uses-library%s:'%s'\n", req ? "" : "-not-required", ResTable::normalizeForOutput( libraryName.string()).string()); } else if (tag == "receiver") { withinReceiver = true; - receiverName = getAttribute(tree, NAME_ATTR, &error); + receiverName = AaptXml::getAttribute(tree, NAME_ATTR, &error); if (error != "") { fprintf(stderr, @@ -1618,7 +1494,8 @@ int doDump(Bundle* bundle) goto bail; } - String8 permission = getAttribute(tree, PERMISSION_ATTR, &error); + String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR, + &error); if (error == "") { if (permission == "android.permission.BIND_DEVICE_ADMIN") { hasBindDeviceAdminPermission = true; @@ -1629,7 +1506,7 @@ int doDump(Bundle* bundle) } } else if (tag == "service") { withinService = true; - serviceName = getAttribute(tree, NAME_ATTR, &error); + serviceName = AaptXml::getAttribute(tree, NAME_ATTR, &error); if (error != "") { fprintf(stderr, "ERROR getting 'android:name' attribute for " @@ -1637,7 +1514,8 @@ int doDump(Bundle* bundle) goto bail; } - String8 permission = getAttribute(tree, PERMISSION_ATTR, &error); + String8 permission = AaptXml::getAttribute(tree, PERMISSION_ATTR, + &error); if (error == "") { if (permission == "android.permission.BIND_INPUT_METHOD") { hasBindInputMethodPermission = true; @@ -1659,22 +1537,24 @@ int doDump(Bundle* bundle) } else if (tag == "provider") { withinProvider = true; - bool exported = getResolvedIntegerAttribute(&res, tree, EXPORTED_ATTR, &error); + bool exported = AaptXml::getResolvedIntegerAttribute(res, tree, + EXPORTED_ATTR, &error); if (error != "") { fprintf(stderr, "ERROR getting 'android:exported' attribute for provider:" " %s\n", error.string()); goto bail; } - bool grantUriPermissions = getResolvedIntegerAttribute(&res, tree, - GRANT_URI_PERMISSIONS_ATTR, &error); + bool grantUriPermissions = AaptXml::getResolvedIntegerAttribute( + res, tree, GRANT_URI_PERMISSIONS_ATTR, &error); if (error != "") { fprintf(stderr, "ERROR getting 'android:grantUriPermissions' attribute for provider:" " %s\n", error.string()); goto bail; } - String8 permission = getResolvedAttribute(&res, tree, PERMISSION_ATTR, &error); + String8 permission = AaptXml::getResolvedAttribute(res, tree, + PERMISSION_ATTR, &error); if (error != "") { fprintf(stderr, "ERROR getting 'android:permission' attribute for provider:" " %s\n", error.string()); @@ -1685,7 +1565,8 @@ int doDump(Bundle* bundle) permission == "android.permission.MANAGE_DOCUMENTS"; } else if (bundle->getIncludeMetaData() && tag == "meta-data") { - String8 metaDataName = getResolvedAttribute(&res, tree, NAME_ATTR, &error); + String8 metaDataName = AaptXml::getResolvedAttribute(res, tree, + NAME_ATTR, &error); if (error != "") { fprintf(stderr, "ERROR getting 'android:name' attribute for " "meta-data:%s\n", error.string()); @@ -1693,12 +1574,12 @@ int doDump(Bundle* bundle) } printf("meta-data: name='%s' ", ResTable::normalizeForOutput(metaDataName.string()).string()); - printResolvedResourceAttribute(&res, tree, VALUE_ATTR, String8("value"), + printResolvedResourceAttribute(res, tree, VALUE_ATTR, String8("value"), &error); if (error != "") { // Try looking for a RESOURCE_ATTR error = ""; - printResolvedResourceAttribute(&res, tree, RESOURCE_ATTR, + printResolvedResourceAttribute(res, tree, RESOURCE_ATTR, String8("resource"), &error); if (error != "") { fprintf(stderr, "ERROR getting 'android:value' or " @@ -1709,7 +1590,7 @@ int doDump(Bundle* bundle) } printf("\n"); } else if (withinSupportsInput && tag == "input-type") { - String8 name = getAttribute(tree, NAME_ATTR, &error); + String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error); if (name != "" && error == "") { supportedInput.add(name); } else { @@ -1721,12 +1602,13 @@ int doDump(Bundle* bundle) } else if (withinFeatureGroup && tag == "uses-feature") { FeatureGroup& top = featureGroups.editTop(); - String8 name = getResolvedAttribute(&res, tree, NAME_ATTR, &error); + String8 name = AaptXml::getResolvedAttribute(res, tree, NAME_ATTR, &error); if (name != "" && error == "") { top.features.add(name, true); addParentFeatures(&top, name); } else { - int vers = getIntegerAttribute(tree, GL_ES_VERSION_ATTR, &error); + int vers = AaptXml::getIntegerAttribute(tree, GL_ES_VERSION_ATTR, + &error); if (error == "") { if (vers > top.openGLESVersion) { top.openGLESVersion = vers; @@ -1754,7 +1636,7 @@ int doDump(Bundle* bundle) actCameraSecure = false; catLauncher = false; } else if (withinService && tag == "meta-data") { - String8 name = getAttribute(tree, NAME_ATTR, &error); + String8 name = AaptXml::getAttribute(tree, NAME_ATTR, &error); if (error != "") { fprintf(stderr, "ERROR getting 'android:name' attribute for" " meta-data tag in service '%s': %s\n", serviceName.string(), error.string()); @@ -1768,7 +1650,8 @@ int doDump(Bundle* bundle) offHost = false; } - String8 xmlPath = getResolvedAttribute(&res, tree, RESOURCE_ATTR, &error); + String8 xmlPath = AaptXml::getResolvedAttribute(res, tree, + RESOURCE_ATTR, &error); if (error != "") { fprintf(stderr, "ERROR getting 'android:resource' attribute for" " meta-data tag in service '%s': %s\n", serviceName.string(), error.string()); @@ -1797,7 +1680,7 @@ int doDump(Bundle* bundle) } else if ((depth == 5) && withinIntentFilter) { String8 action; if (tag == "action") { - action = getAttribute(tree, NAME_ATTR, &error); + action = AaptXml::getAttribute(tree, NAME_ATTR, &error); if (error != "") { fprintf(stderr, "ERROR getting 'android:name' attribute: %s\n", error.string()); @@ -1849,7 +1732,7 @@ int doDump(Bundle* bundle) } if (tag == "category") { - String8 category = getAttribute(tree, NAME_ATTR, &error); + String8 category = AaptXml::getAttribute(tree, NAME_ATTR, &error); if (error != "") { fprintf(stderr, "ERROR getting 'name' attribute: %s\n", error.string()); diff --git a/tools/aapt/Main.h b/tools/aapt/Main.h index dd40b20..f24a023 100644 --- a/tools/aapt/Main.h +++ b/tools/aapt/Main.h @@ -60,9 +60,6 @@ extern status_t filterResources(Bundle* bundle, const sp<AaptAssets>& assets); int dumpResources(Bundle* bundle); -String8 getAttribute(const ResXMLTree& tree, const char* ns, - const char* attr, String8* outError); - status_t writeDependencyPreReqs(Bundle* bundle, const sp<AaptAssets>& assets, FILE* fp, bool includeRaw); #endif // __MAIN_H diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp index 7979a1d..5deeca2 100644 --- a/tools/aapt/Resource.cpp +++ b/tools/aapt/Resource.cpp @@ -4,6 +4,7 @@ // Build resource files from raw assets. // #include "AaptAssets.h" +#include "AaptXml.h" #include "CacheUpdater.h" #include "CrunchCache.h" #include "FileFinder.h" @@ -805,6 +806,20 @@ status_t massageManifest(Bundle* bundle, sp<XMLNode> root) } } + if (bundle->getPlatformBuildVersionCode() != "") { + if (!addTagAttribute(root, "", "platformBuildVersionCode", + bundle->getPlatformBuildVersionCode(), errorOnFailedInsert, true)) { + return UNKNOWN_ERROR; + } + } + + if (bundle->getPlatformBuildVersionName() != "") { + if (!addTagAttribute(root, "", "platformBuildVersionName", + bundle->getPlatformBuildVersionName(), errorOnFailedInsert, true)) { + return UNKNOWN_ERROR; + } + } + if (bundle->getDebugMode()) { sp<XMLNode> application = root->getChildElement(String16(), String16("application")); if (application != NULL) { @@ -881,6 +896,106 @@ status_t massageManifest(Bundle* bundle, sp<XMLNode> root) return NO_ERROR; } +static int32_t getPlatformAssetCookie(const AssetManager& assets) { + // Find the system package (0x01). AAPT always generates attributes + // with the type 0x01, so we're looking for the first attribute + // resource in the system package. + const ResTable& table = assets.getResources(true); + Res_value val; + ssize_t idx = table.getResource(0x01010000, &val, true); + if (idx != NO_ERROR) { + // Try as a bag. + const ResTable::bag_entry* entry; + ssize_t cnt = table.lockBag(0x01010000, &entry); + if (cnt >= 0) { + idx = entry->stringBlock; + } + table.unlockBag(entry); + } + + if (idx < 0) { + return 0; + } + return table.getTableCookie(idx); +} + +enum { + VERSION_CODE_ATTR = 0x0101021b, + VERSION_NAME_ATTR = 0x0101021c, +}; + +static ssize_t extractPlatformBuildVersion(ResXMLTree& tree, Bundle* bundle) { + size_t len; + ResXMLTree::event_code_t code; + while ((code = tree.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { + if (code != ResXMLTree::START_TAG) { + continue; + } + + const char16_t* ctag16 = tree.getElementName(&len); + if (ctag16 == NULL) { + fprintf(stderr, "ERROR: failed to get XML element name (bad string pool)\n"); + return UNKNOWN_ERROR; + } + + String8 tag(ctag16, len); + if (tag != "manifest") { + continue; + } + + String8 error; + int32_t versionCode = AaptXml::getIntegerAttribute(tree, VERSION_CODE_ATTR, &error); + if (error != "") { + fprintf(stderr, "ERROR: failed to get platform version code\n"); + return UNKNOWN_ERROR; + } + + if (versionCode >= 0 && bundle->getPlatformBuildVersionCode() == "") { + bundle->setPlatformBuildVersionCode(String8::format("%d", versionCode)); + } + + String8 versionName = AaptXml::getAttribute(tree, VERSION_NAME_ATTR, &error); + if (error != "") { + fprintf(stderr, "ERROR: failed to get platform version name\n"); + return UNKNOWN_ERROR; + } + + if (versionName != "" && bundle->getPlatformBuildVersionName() == "") { + bundle->setPlatformBuildVersionName(versionName); + } + return NO_ERROR; + } + + fprintf(stderr, "ERROR: no <manifest> tag found in platform AndroidManifest.xml\n"); + return UNKNOWN_ERROR; +} + +static ssize_t extractPlatformBuildVersion(AssetManager& assets, Bundle* bundle) { + int32_t cookie = getPlatformAssetCookie(assets); + if (cookie == 0) { + fprintf(stderr, "ERROR: Platform package not found\n"); + return UNKNOWN_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"); + return UNKNOWN_ERROR; + } + + 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); + } + + delete asset; + return result; +} + #define ASSIGN_IT(n) \ do { \ ssize_t index = resources->indexOfKey(String8(#n)); \ @@ -1356,6 +1471,17 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets, sp<ApkBuil return UNKNOWN_ERROR; } + // If we're not overriding the platform build versions, + // extract them from the platform APK. + if (packageType != ResourceTable::System && + (bundle->getPlatformBuildVersionCode() == "" || + bundle->getPlatformBuildVersionName() == "")) { + err = extractPlatformBuildVersion(assets->getAssetManager(), bundle); + if (err != NO_ERROR) { + return UNKNOWN_ERROR; + } + } + const sp<AaptFile> manifestFile(androidManifestFile->getFiles().valueAt(0)); String8 manifestPath(manifestFile->getPrintableSource()); @@ -2636,13 +2762,14 @@ writeProguardForAndroidManifest(ProguardKeepSet* keep, const sp<AaptAssets>& ass fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n"); return -1; } - pkg = getAttribute(tree, NULL, "package", NULL); + pkg = AaptXml::getAttribute(tree, NULL, "package"); } else if (depth == 2) { if (tag == "application") { inApplication = true; keepTag = true; - String8 agent = getAttribute(tree, "http://schemas.android.com/apk/res/android", + String8 agent = AaptXml::getAttribute(tree, + "http://schemas.android.com/apk/res/android", "backupAgent", &error); if (agent.length() > 0) { addProguardKeepRule(keep, agent, pkg.string(), @@ -2658,8 +2785,8 @@ writeProguardForAndroidManifest(ProguardKeepSet* keep, const sp<AaptAssets>& ass } } if (keepTag) { - String8 name = getAttribute(tree, "http://schemas.android.com/apk/res/android", - "name", &error); + String8 name = AaptXml::getAttribute(tree, + "http://schemas.android.com/apk/res/android", "name", &error); if (error != "") { fprintf(stderr, "ERROR: %s\n", error.string()); return -1; diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py new file mode 100644 index 0000000..fce4323 --- /dev/null +++ b/tools/apilint/apilint.py @@ -0,0 +1,540 @@ +#!/usr/bin/env python + +# 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. + +""" +Enforces common Android public API design patterns. It ignores lint messages from +a previous API level, if provided. + +Usage: apilint.py current.txt +Usage: apilint.py current.txt previous.txt +""" + +import re, sys + + +BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8) + +def format(fg=None, bg=None, bright=False, bold=False, dim=False, reset=False): + # manually derived from http://en.wikipedia.org/wiki/ANSI_escape_code#Codes + codes = [] + if reset: codes.append("0") + else: + if not fg is None: codes.append("3%d" % (fg)) + if not bg is None: + if not bright: codes.append("4%d" % (bg)) + else: codes.append("10%d" % (bg)) + if bold: codes.append("1") + elif dim: codes.append("2") + else: codes.append("22") + return "\033[%sm" % (";".join(codes)) + + +class Field(): + def __init__(self, clazz, raw): + self.clazz = clazz + self.raw = raw.strip(" {;") + + raw = raw.split() + self.split = list(raw) + + for r in ["field", "volatile", "transient", "public", "protected", "static", "final", "deprecated"]: + while r in raw: raw.remove(r) + + self.typ = raw[0] + self.name = raw[1].strip(";") + if len(raw) >= 4 and raw[2] == "=": + self.value = raw[3].strip(';"') + else: + self.value = None + + def __repr__(self): + return self.raw + + +class Method(): + def __init__(self, clazz, raw): + self.clazz = clazz + self.raw = raw.strip(" {;") + + raw = re.split("[\s(),;]+", raw) + for r in ["", ";"]: + while r in raw: raw.remove(r) + self.split = list(raw) + + for r in ["method", "public", "protected", "static", "final", "deprecated", "abstract"]: + while r in raw: raw.remove(r) + + self.typ = raw[0] + self.name = raw[1] + self.args = [] + for r in raw[2:]: + if r == "throws": break + self.args.append(r) + + def __repr__(self): + return self.raw + + +class Class(): + def __init__(self, pkg, raw): + self.pkg = pkg + self.raw = raw.strip(" {;") + self.ctors = [] + self.fields = [] + self.methods = [] + + raw = raw.split() + self.split = list(raw) + if "class" in raw: + self.fullname = raw[raw.index("class")+1] + elif "interface" in raw: + self.fullname = raw[raw.index("interface")+1] + + if "." in self.fullname: + self.name = self.fullname[self.fullname.rindex(".")+1:] + else: + self.name = self.fullname + + def __repr__(self): + return self.raw + + +class Package(): + def __init__(self, raw): + self.raw = raw.strip(" {;") + + raw = raw.split() + self.name = raw[raw.index("package")+1] + + def __repr__(self): + return self.raw + + +def parse_api(fn): + api = [] + pkg = None + clazz = None + + with open(fn) as f: + for raw in f.readlines(): + raw = raw.rstrip() + + if raw.startswith("package"): + pkg = Package(raw) + elif raw.startswith(" ") and raw.endswith("{"): + clazz = Class(pkg, raw) + api.append(clazz) + elif raw.startswith(" ctor"): + clazz.ctors.append(Method(clazz, raw)) + elif raw.startswith(" method"): + clazz.methods.append(Method(clazz, raw)) + elif raw.startswith(" field"): + clazz.fields.append(Field(clazz, raw)) + + return api + + +failures = [] + +def _fail(clazz, detail, msg): + """Records an API failure to be processed later.""" + global failures + + res = msg + if detail is not None: + res += "\n in " + repr(detail) + res += "\n in " + repr(clazz) + res += "\n in " + repr(clazz.pkg) + failures.append(res) + +def warn(clazz, detail, msg): + _fail(clazz, detail, "%sWarning:%s %s" % (format(fg=YELLOW, bg=BLACK), format(reset=True), msg)) + +def error(clazz, detail, msg): + _fail(clazz, detail, "%sError:%s %s" % (format(fg=RED, bg=BLACK), format(reset=True), msg)) + + +def verify_constants(clazz): + """All static final constants must be FOO_NAME style.""" + if re.match("R\.[a-z]+", clazz.fullname): return + + for f in clazz.fields: + if "static" in f.split and "final" in f.split: + if re.match("[A-Z0-9_]+", f.name) is None: + error(clazz, f, "Constant field names should be FOO_NAME") + + +def verify_enums(clazz): + """Enums are bad, mmkay?""" + if "extends java.lang.Enum" in clazz.raw: + error(clazz, None, "Enums are not allowed") + + +def verify_class_names(clazz): + """Try catching malformed class names like myMtp or MTPUser.""" + if re.search("[A-Z]{2,}", clazz.name) is not None: + warn(clazz, None, "Class name style should be Mtp not MTP") + if re.match("[^A-Z]", clazz.name): + error(clazz, None, "Class must start with uppercase char") + + +def verify_method_names(clazz): + """Try catching malformed method names, like Foo() or getMTU().""" + if clazz.pkg.name == "android.opengl": return + + for m in clazz.methods: + if re.search("[A-Z]{2,}", m.name) is not None: + warn(clazz, m, "Method name style should be getMtu() instead of getMTU()") + if re.match("[^a-z]", m.name): + error(clazz, None, "Method name must start with lowercase char") + + +def verify_callbacks(clazz): + """Verify Callback classes. + All callback classes must be abstract. + All methods must follow onFoo() naming style.""" + + if clazz.name.endswith("Callbacks"): + error(clazz, None, "Class must be named exactly Callback") + if clazz.name.endswith("Observer"): + warn(clazz, None, "Class should be named Callback") + + if clazz.name.endswith("Callback"): + if "interface" in clazz.split: + error(clazz, None, "Callback must be abstract class") + + for m in clazz.methods: + if not re.match("on[A-Z][a-z]*", m.name): + error(clazz, m, "Callback method names must be onFoo style") + + +def verify_listeners(clazz): + """Verify Listener classes. + All Listener classes must be interface. + All methods must follow onFoo() naming style. + If only a single method, it must match class name: + interface OnFooListener { void onFoo() }""" + + if clazz.name.endswith("Listener"): + if " abstract class " in clazz.raw: + error(clazz, None, "Listener should be interface") + + for m in clazz.methods: + if not re.match("on[A-Z][a-z]*", m.name): + error(clazz, m, "Listener method names must be onFoo style") + + if len(clazz.methods) == 1 and clazz.name.startswith("On"): + m = clazz.methods[0] + if (m.name + "Listener").lower() != clazz.name.lower(): + error(clazz, m, "Single method name should match class name") + + +def verify_actions(clazz): + """Verify intent actions. + All action names must be named ACTION_FOO. + All action values must be scoped by package and match name: + package android.foo { + String ACTION_BAR = "android.foo.action.BAR"; + }""" + for f in clazz.fields: + if f.value is None: continue + if f.name.startswith("EXTRA_"): continue + + if "static" in f.split and "final" in f.split and f.typ == "java.lang.String": + if "_ACTION" in f.name or "ACTION_" in f.name or ".action." in f.value.lower(): + if not f.name.startswith("ACTION_"): + error(clazz, f, "Intent action must be ACTION_FOO") + else: + if clazz.name == "Intent": + prefix = "android.intent.action" + elif clazz.name == "Settings": + prefix = "android.settings" + else: + prefix = clazz.pkg.name + ".action" + expected = prefix + "." + f.name[7:] + if f.value != expected: + error(clazz, f, "Inconsistent action value") + + +def verify_extras(clazz): + """Verify intent extras. + All extra names must be named EXTRA_FOO. + All extra values must be scoped by package and match name: + package android.foo { + String EXTRA_BAR = "android.foo.extra.BAR"; + }""" + for f in clazz.fields: + if f.value is None: continue + if f.name.startswith("ACTION_"): continue + + if "static" in f.split and "final" in f.split and f.typ == "java.lang.String": + if "_EXTRA" in f.name or "EXTRA_" in f.name or ".extra" in f.value.lower(): + if not f.name.startswith("EXTRA_"): + error(clazz, f, "Intent extra must be EXTRA_FOO") + else: + if clazz.name == "Intent": + prefix = "android.intent.extra" + else: + prefix = clazz.pkg.name + ".extra" + expected = prefix + "." + f.name[6:] + if f.value != expected: + error(clazz, f, "Inconsistent extra value") + + +def verify_equals(clazz): + """Verify that equals() and hashCode() must be overridden together.""" + methods = [ m.name for m in clazz.methods ] + eq = "equals" in methods + hc = "hashCode" in methods + if eq != hc: + error(clazz, None, "Must override both equals and hashCode") + + +def verify_parcelable(clazz): + """Verify that Parcelable objects aren't hiding required bits.""" + if "implements android.os.Parcelable" in clazz.raw: + creator = [ i for i in clazz.fields if i.name == "CREATOR" ] + write = [ i for i in clazz.methods if i.name == "writeToParcel" ] + describe = [ i for i in clazz.methods if i.name == "describeContents" ] + + if len(creator) == 0 or len(write) == 0 or len(describe) == 0: + error(clazz, None, "Parcelable requires CREATOR, writeToParcel, and describeContents") + + +def verify_protected(clazz): + """Verify that no protected methods are allowed.""" + for m in clazz.methods: + if "protected" in m.split: + error(clazz, m, "Protected method") + for f in clazz.fields: + if "protected" in f.split: + error(clazz, f, "Protected field") + + +def verify_fields(clazz): + """Verify that all exposed fields are final. + Exposed fields must follow myName style. + Catch internal mFoo objects being exposed.""" + for f in clazz.fields: + if not "final" in f.split: + error(clazz, f, "Bare fields must be final; consider adding accessors") + + if not "static" in f.split: + if not re.match("[a-z]([a-zA-Z]+)?", f.name): + error(clazz, f, "Non-static fields must be myName") + + if re.match("[m][A-Z]", f.name): + error(clazz, f, "Don't expose your internal objects") + + +def verify_register(clazz): + """Verify parity of registration methods. + Callback objects use register/unregister methods. + Listener objects use add/remove methods.""" + methods = [ m.name for m in clazz.methods ] + for m in clazz.methods: + if "Callback" in m.raw: + if m.name.startswith("register"): + other = "unregister" + m.name[8:] + if other not in methods: + error(clazz, m, "Missing unregister") + if m.name.startswith("unregister"): + other = "register" + m.name[10:] + if other not in methods: + error(clazz, m, "Missing register") + + if m.name.startswith("add") or m.name.startswith("remove"): + error(clazz, m, "Callback should be register/unregister") + + if "Listener" in m.raw: + if m.name.startswith("add"): + other = "remove" + m.name[3:] + if other not in methods: + error(clazz, m, "Missing remove") + if m.name.startswith("remove") and not m.name.startswith("removeAll"): + other = "add" + m.name[6:] + if other not in methods: + error(clazz, m, "Missing add") + + if m.name.startswith("register") or m.name.startswith("unregister"): + error(clazz, m, "Listener should be add/remove") + + +def verify_sync(clazz): + """Verify synchronized methods aren't exposed.""" + for m in clazz.methods: + if "synchronized" in m.split: + error(clazz, m, "Lock exposed") + + +def verify_intent_builder(clazz): + """Verify that Intent builders are createFooIntent() style.""" + if clazz.name == "Intent": return + + for m in clazz.methods: + if m.typ == "android.content.Intent": + if m.name.startswith("create") and m.name.endswith("Intent"): + pass + else: + warn(clazz, m, "Should be createFooIntent()") + + +def verify_helper_classes(clazz): + """Verify that helper classes are named consistently with what they extend.""" + if "extends android.app.Service" in clazz.raw: + if not clazz.name.endswith("Service"): + error(clazz, None, "Inconsistent class name") + if "extends android.content.ContentProvider" in clazz.raw: + if not clazz.name.endswith("Provider"): + error(clazz, None, "Inconsistent class name") + if "extends android.content.BroadcastReceiver" in clazz.raw: + if not clazz.name.endswith("Receiver"): + error(clazz, None, "Inconsistent class name") + + +def verify_builder(clazz): + """Verify builder classes. + Methods should return the builder to enable chaining.""" + if " extends " in clazz.raw: return + if not clazz.name.endswith("Builder"): return + + if clazz.name != "Builder": + warn(clazz, None, "Should be standalone Builder class") + + has_build = False + for m in clazz.methods: + if m.name == "build": + has_build = True + continue + + if m.name.startswith("get"): continue + if m.name.startswith("clear"): continue + + if not m.typ.endswith(clazz.fullname): + warn(clazz, m, "Should return the builder") + + if not has_build: + warn(clazz, None, "Missing build() method") + + +def verify_aidl(clazz): + """Catch people exposing raw AIDL.""" + if "extends android.os.Binder" in clazz.raw or "implements android.os.IInterface" in clazz.raw: + error(clazz, None, "Exposing raw AIDL interface") + + +def verify_internal(clazz): + """Catch people exposing internal classes.""" + if clazz.pkg.name.startswith("com.android"): + error(clazz, None, "Exposing internal class") + + +def verify_layering(clazz): + """Catch package layering violations. + For example, something in android.os depending on android.app.""" + ranking = [ + ["android.service","android.accessibilityservice","android.inputmethodservice","android.printservice","android.appwidget","android.webkit","android.preference","android.gesture","android.print"], + "android.app", + "android.widget", + "android.view", + "android.animation", + "android.provider", + "android.content", + "android.database", + "android.graphics", + "android.text", + "android.os", + "android.util" + ] + + def rank(p): + for i in range(len(ranking)): + if isinstance(ranking[i], list): + for j in ranking[i]: + if p.startswith(j): return i + else: + if p.startswith(ranking[i]): return i + + cr = rank(clazz.pkg.name) + if cr is None: return + + for f in clazz.fields: + ir = rank(f.typ) + if ir and ir < cr: + warn(clazz, f, "Field type violates package layering") + + for m in clazz.methods: + ir = rank(m.typ) + if ir and ir < cr: + warn(clazz, m, "Method return type violates package layering") + for arg in m.args: + ir = rank(arg) + if ir and ir < cr: + warn(clazz, m, "Method argument type violates package layering") + + +def verify_all(api): + global failures + + failures = [] + for clazz in api: + if clazz.pkg.name.startswith("java"): continue + if clazz.pkg.name.startswith("junit"): continue + if clazz.pkg.name.startswith("org.apache"): continue + if clazz.pkg.name.startswith("org.xml"): continue + if clazz.pkg.name.startswith("org.json"): continue + if clazz.pkg.name.startswith("org.w3c"): continue + + verify_constants(clazz) + verify_enums(clazz) + verify_class_names(clazz) + verify_method_names(clazz) + verify_callbacks(clazz) + verify_listeners(clazz) + verify_actions(clazz) + verify_extras(clazz) + verify_equals(clazz) + verify_parcelable(clazz) + verify_protected(clazz) + verify_fields(clazz) + verify_register(clazz) + verify_sync(clazz) + verify_intent_builder(clazz) + verify_helper_classes(clazz) + verify_builder(clazz) + verify_aidl(clazz) + verify_internal(clazz) + verify_layering(clazz) + + return failures + + +cur = parse_api(sys.argv[1]) +cur_fail = verify_all(cur) + +if len(sys.argv) > 2: + prev = parse_api(sys.argv[2]) + prev_fail = verify_all(prev) + + # ignore errors from previous API level + for p in prev_fail: + if p in cur_fail: + cur_fail.remove(p) + + +for f in cur_fail: + print f + print |
