summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/aapt/AaptAssets.cpp5
-rw-r--r--tools/aapt/AaptAssets.h1
-rw-r--r--tools/aapt/AaptXml.cpp184
-rw-r--r--tools/aapt/AaptXml.h123
-rw-r--r--tools/aapt/Android.mk1
-rw-r--r--tools/aapt/Bundle.h6
-rw-r--r--tools/aapt/Command.cpp371
-rw-r--r--tools/aapt/Main.h3
-rw-r--r--tools/aapt/Resource.cpp135
-rw-r--r--tools/apilint/apilint.py540
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