Implement compatibility support for WRITE_SDCARD permission.
Now old applications will automatically be granted it. Also renamed it from
SDCARD_WRITE to WRITE_SDCARD to be consistent with our other permissions,
and re-arranged how we do targetSdkVersion to actually be usuable for this
kind of stuff.
Note that right now this results in basically all apps being given the
WRITE_SDCARD permission, because their targetSdkVersion is not set. I will
be dealing with that in a future change.
diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h
index 2d8471b..216ece4 100644
--- a/tools/aapt/Bundle.h
+++ b/tools/aapt/Bundle.h
@@ -99,6 +99,17 @@
const android::Vector<const char*>& getNoCompressExtensions() const { return mNoCompressExtensions; }
void addNoCompressExtension(const char* ext) { mNoCompressExtensions.add(ext); }
+ const char* getMinSdkVersion() const { return mMinSdkVersion; }
+ void setMinSdkVersion(const char* val) { mMinSdkVersion = val; }
+ const char* getTargetSdkVersion() const { return mTargetSdkVersion; }
+ void setTargetSdkVersion(const char* val) { mTargetSdkVersion = val; }
+ const char* getMaxSdkVersion() const { return mMaxSdkVersion; }
+ void setMaxSdkVersion(const char* val) { mMaxSdkVersion = val; }
+ const char* getVersionCode() const { return mVersionCode; }
+ void setVersionCode(const char* val) { mVersionCode = val; }
+ const char* getVersionName() const { return mVersionName; }
+ void setVersionName(const char* val) { mVersionName = val; }
+
/*
* Set and get the file specification.
*
@@ -151,6 +162,12 @@
android::Vector<const char*> mNoCompressExtensions;
android::Vector<const char*> mResourceSourceDirs;
+ const char* mMinSdkVersion;
+ const char* mTargetSdkVersion;
+ const char* mMaxSdkVersion;
+ const char* mVersionCode;
+ const char* mVersionName;
+
/* file specification */
int mArgc;
char* const* mArgv;
diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp
index 71b1a3c..8bf2b07 100644
--- a/tools/aapt/Main.cpp
+++ b/tools/aapt/Main.cpp
@@ -54,9 +54,10 @@
" xmlstrings Print the strings of the given compiled xml assets.\n\n", gProgName);
fprintf(stderr,
" %s p[ackage] [-d][-f][-m][-u][-v][-x][-z][-M AndroidManifest.xml] \\\n"
- " [-0 extension [-0 extension ...]] \\\n"
- " [-g tolerance] \\\n"
- " [-j jarfile] \\\n"
+ " [-0 extension [-0 extension ...]] [-g tolerance] [-j jarfile] \\\n"
+ " [--min-sdk-version VAL] [--target-sdk-version VAL] \\\n"
+ " [--max-sdk-version VAL] [--app-version VAL] \\\n"
+ " [--app-version-name TEXT] \\\n"
" [-I base-package [-I base-package ...]] \\\n"
" [-A asset-source-dir] [-P public-definitions-file] \\\n"
" [-S resource-sources [-S resource-sources ...]] "
@@ -115,7 +116,17 @@
" and the first match found (left to right) will take precedence."
" -0 specifies an additional extension for which such files will not\n"
" be stored compressed in the .apk. An empty string means to not\n"
- " compress any files at all.\n");
+ " compress any files at all.\n"
+ " --min-sdk-version\n"
+ " inserts android:minSdkVersion in to manifest.\n"
+ " --target-sdk-version\n"
+ " inserts android:targetSdkVersion in to manifest.\n"
+ " --max-sdk-version\n"
+ " inserts android:maxSdkVersion in to manifest.\n"
+ " --version-code\n"
+ " inserts android:versionCode in to manifest.\n"
+ " --version-name\n"
+ " inserts android:versionName in to manifest.\n");
}
/*
@@ -339,6 +350,59 @@
bundle.setCompressionMethod(ZipEntry::kCompressStored);
}
break;
+ case '-':
+ if (strcmp(cp, "-min-sdk-version") == 0) {
+ argc--;
+ argv++;
+ if (!argc) {
+ fprintf(stderr, "ERROR: No argument supplied for '--min-sdk-version' option\n");
+ wantUsage = true;
+ goto bail;
+ }
+ bundle.setMinSdkVersion(argv[0]);
+ } else if (strcmp(cp, "-target-sdk-version") == 0) {
+ argc--;
+ argv++;
+ if (!argc) {
+ fprintf(stderr, "ERROR: No argument supplied for '--target-sdk-version' option\n");
+ wantUsage = true;
+ goto bail;
+ }
+ bundle.setTargetSdkVersion(argv[0]);
+ } else if (strcmp(cp, "-max-sdk-version") == 0) {
+ argc--;
+ argv++;
+ if (!argc) {
+ fprintf(stderr, "ERROR: No argument supplied for '--max-sdk-version' option\n");
+ wantUsage = true;
+ goto bail;
+ }
+ bundle.setMaxSdkVersion(argv[0]);
+ } else if (strcmp(cp, "-version-code") == 0) {
+ argc--;
+ argv++;
+ if (!argc) {
+ fprintf(stderr, "ERROR: No argument supplied for '--version-code' option\n");
+ wantUsage = true;
+ goto bail;
+ }
+ bundle.setVersionCode(argv[0]);
+ } else if (strcmp(cp, "-version-name") == 0) {
+ argc--;
+ argv++;
+ if (!argc) {
+ fprintf(stderr, "ERROR: No argument supplied for '--version-name' option\n");
+ wantUsage = true;
+ goto bail;
+ }
+ bundle.setVersionName(argv[0]);
+ } else {
+ fprintf(stderr, "ERROR: Unknown option '-%s'\n", cp);
+ wantUsage = true;
+ goto bail;
+ }
+ cp += strlen(cp) - 1;
+ break;
default:
fprintf(stderr, "ERROR: Unknown flag '-%c'\n", *cp);
wantUsage = true;
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 2e4d0a4..76b9d0a 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -488,6 +488,58 @@
return;
}
+void addTagAttribute(const sp<XMLNode>& node, const char* ns8,
+ const char* attr8, const char* value)
+{
+ if (value == NULL) {
+ return;
+ }
+
+ const String16 ns(ns8);
+ const String16 attr(attr8);
+
+ if (node->getAttribute(ns, attr) != NULL) {
+ fprintf(stderr, "Warning: AndroidManifest.xml already defines %s (in %s)\n",
+ String8(attr).string(), String8(ns).string());
+ return;
+ }
+
+ node->addAttribute(ns, attr, String16(value));
+}
+
+status_t massageManifest(Bundle* bundle, sp<XMLNode> root)
+{
+ root = root->searchElement(String16(), String16("manifest"));
+ if (root == NULL) {
+ fprintf(stderr, "No <manifest> tag.\n");
+ return UNKNOWN_ERROR;
+ }
+
+ addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionCode",
+ bundle->getVersionCode());
+ addTagAttribute(root, RESOURCES_ANDROID_NAMESPACE, "versionName",
+ bundle->getVersionName());
+
+ if (bundle->getMinSdkVersion() != NULL
+ || bundle->getTargetSdkVersion() != NULL
+ || bundle->getMaxSdkVersion() != NULL) {
+ sp<XMLNode> vers = root->getChildElement(String16(), String16("uses-sdk"));
+ if (vers == NULL) {
+ vers = XMLNode::newElement(root->getFilename(), String16(), String16("uses-sdk"));
+ root->insertChildAt(vers, 0);
+ }
+
+ addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "minSdkVersion",
+ bundle->getMinSdkVersion());
+ addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "targetSdkVersion",
+ bundle->getTargetSdkVersion());
+ addTagAttribute(vers, RESOURCES_ANDROID_NAMESPACE, "maxSdkVersion",
+ bundle->getMaxSdkVersion());
+ }
+
+ return NO_ERROR;
+}
+
#define ASSIGN_IT(n) \
do { \
ssize_t index = resources->indexOfKey(String8(#n)); \
@@ -1006,7 +1058,15 @@
// Generate final compiled manifest file.
manifestFile->clearData();
- err = compileXmlFile(assets, manifestFile, &table);
+ sp<XMLNode> manifestTree = XMLNode::parse(manifestFile);
+ if (manifestTree == NULL) {
+ return UNKNOWN_ERROR;
+ }
+ err = massageManifest(bundle, manifestTree);
+ if (err < NO_ERROR) {
+ return err;
+ }
+ err = compileXmlFile(assets, manifestTree, manifestFile, &table);
if (err < NO_ERROR) {
return err;
}
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index ef11a83..25ab147 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -23,6 +23,16 @@
if (root == NULL) {
return UNKNOWN_ERROR;
}
+
+ return compileXmlFile(assets, root, target, table, options);
+}
+
+status_t compileXmlFile(const sp<AaptAssets>& assets,
+ const sp<XMLNode>& root,
+ const sp<AaptFile>& target,
+ ResourceTable* table,
+ int options)
+{
if ((options&XML_COMPILE_STRIP_WHITESPACE) != 0) {
root->removeWhitespace(true, NULL);
} else if ((options&XML_COMPILE_COMPACT_WHITESPACE) != 0) {
@@ -1307,7 +1317,7 @@
} else if (id != 0) {
if (id == 127) {
if (mHaveAppPackage) {
- fprintf(stderr, "Included resource have two application packages!\n");
+ fprintf(stderr, "Included resources have two application packages!\n");
return UNKNOWN_ERROR;
}
mHaveAppPackage = true;
diff --git a/tools/aapt/ResourceTable.h b/tools/aapt/ResourceTable.h
index 74ba326..665232b 100644
--- a/tools/aapt/ResourceTable.h
+++ b/tools/aapt/ResourceTable.h
@@ -15,6 +15,7 @@
using namespace std;
+class XMLNode;
class ResourceTable;
enum {
@@ -34,6 +35,12 @@
ResourceTable* table,
int options = XML_COMPILE_STANDARD_RESOURCE);
+status_t compileXmlFile(const sp<AaptAssets>& assets,
+ const sp<XMLNode>& xmlTree,
+ const sp<AaptFile>& target,
+ ResourceTable* table,
+ int options = XML_COMPILE_STANDARD_RESOURCE);
+
status_t compileResourceFile(Bundle* bundle,
const sp<AaptAssets>& assets,
const sp<AaptFile>& in,
diff --git a/tools/aapt/XMLNode.cpp b/tools/aapt/XMLNode.cpp
index 50a2185..832ba6c 100644
--- a/tools/aapt/XMLNode.cpp
+++ b/tools/aapt/XMLNode.cpp
@@ -525,12 +525,30 @@
return mChildren;
}
+const String8& XMLNode::getFilename() const
+{
+ return mFilename;
+}
+
const Vector<XMLNode::attribute_entry>&
XMLNode::getAttributes() const
{
return mAttributes;
}
+const XMLNode::attribute_entry* XMLNode::getAttribute(const String16& ns,
+ const String16& name) const
+{
+ for (size_t i=0; i<mAttributes.size(); i++) {
+ const attribute_entry& ae(mAttributes.itemAt(i));
+ if (ae.ns == ns && ae.name == name) {
+ return &ae;
+ }
+ }
+
+ return NULL;
+}
+
const String16& XMLNode::getCData() const
{
return mChars;
@@ -551,6 +569,38 @@
return mEndLineNumber;
}
+sp<XMLNode> XMLNode::searchElement(const String16& tagNamespace, const String16& tagName)
+{
+ if (getType() == XMLNode::TYPE_ELEMENT
+ && mNamespaceUri == tagNamespace
+ && mElementName == tagName) {
+ return this;
+ }
+
+ for (size_t i=0; i<mChildren.size(); i++) {
+ sp<XMLNode> found = mChildren.itemAt(i)->searchElement(tagNamespace, tagName);
+ if (found != NULL) {
+ return found;
+ }
+ }
+
+ return NULL;
+}
+
+sp<XMLNode> XMLNode::getChildElement(const String16& tagNamespace, const String16& tagName)
+{
+ for (size_t i=0; i<mChildren.size(); i++) {
+ sp<XMLNode> child = mChildren.itemAt(i);
+ if (child->getType() == XMLNode::TYPE_ELEMENT
+ && child->mNamespaceUri == tagNamespace
+ && child->mElementName == tagName) {
+ return child;
+ }
+ }
+
+ return NULL;
+}
+
status_t XMLNode::addChild(const sp<XMLNode>& child)
{
if (getType() == TYPE_CDATA) {
@@ -562,6 +612,17 @@
return NO_ERROR;
}
+status_t XMLNode::insertChildAt(const sp<XMLNode>& child, size_t index)
+{
+ if (getType() == TYPE_CDATA) {
+ SourcePos(mFilename, child->getStartLineNumber()).error("Child to CDATA node.");
+ return UNKNOWN_ERROR;
+ }
+ //printf("Adding child %p to parent %p\n", child.get(), this);
+ mChildren.insertAt(child, index);
+ return NO_ERROR;
+}
+
status_t XMLNode::addAttribute(const String16& ns, const String16& name,
const String16& value)
{
diff --git a/tools/aapt/XMLNode.h b/tools/aapt/XMLNode.h
index 86548a2..a9bea43 100644
--- a/tools/aapt/XMLNode.h
+++ b/tools/aapt/XMLNode.h
@@ -68,6 +68,8 @@
const String16& getElementName() const;
const Vector<sp<XMLNode> >& getChildren() const;
+ const String8& getFilename() const;
+
struct attribute_entry {
attribute_entry() : index(~(uint32_t)0), nameResId(0)
{
@@ -91,6 +93,8 @@
const Vector<attribute_entry>& getAttributes() const;
+ const attribute_entry* getAttribute(const String16& ns, const String16& name) const;
+
const String16& getCData() const;
const String16& getComment() const;
@@ -98,8 +102,14 @@
int32_t getStartLineNumber() const;
int32_t getEndLineNumber() const;
+ sp<XMLNode> searchElement(const String16& tagNamespace, const String16& tagName);
+
+ sp<XMLNode> getChildElement(const String16& tagNamespace, const String16& tagName);
+
status_t addChild(const sp<XMLNode>& child);
+ status_t insertChildAt(const sp<XMLNode>& child, size_t index);
+
status_t addAttribute(const String16& ns, const String16& name,
const String16& value);