Load app resource as shared library.
- Added aapt command line flag --app-as-shared-lib to build app resources
that could be loaded as shared lib at runtime.
- Added new method AssetManager.addAssetPathAsSharedLibrary() to load an
app resource as shared library.
Bug 22487604
Change-Id: Ib9b33c35f9c2b7129f3ba205de03d4564623ea39
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index c248a9e..04c690b 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -610,14 +610,28 @@
* {@hide}
*/
public final int addAssetPath(String path) {
+ return addAssetPathInternal(path, false);
+ }
+
+ /**
+ * Add an application assets to the asset manager and loading it as shared library.
+ * This can be either a directory or ZIP file. Not for use by applications. Returns
+ * the cookie of the added asset, or 0 on failure.
+ * {@hide}
+ */
+ public final int addAssetPathAsSharedLibrary(String path) {
+ return addAssetPathInternal(path, true);
+ }
+
+ private final int addAssetPathInternal(String path, boolean appAsLib) {
synchronized (this) {
- int res = addAssetPathNative(path);
+ int res = addAssetPathNative(path, appAsLib);
makeStringBlocks(mStringBlocks);
return res;
}
}
- private native final int addAssetPathNative(String path);
+ private native final int addAssetPathNative(String path, boolean appAsLib);
/**
* Add a set of assets to overlay an already added set of assets.
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index 9aa544f..7ca0654 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -510,7 +510,7 @@
}
static jint android_content_AssetManager_addAssetPath(JNIEnv* env, jobject clazz,
- jstring path)
+ jstring path, jboolean appAsLib)
{
ScopedUtfChars path8(env, path);
if (path8.c_str() == NULL) {
@@ -523,7 +523,7 @@
}
int32_t cookie;
- bool res = am->addAssetPath(String8(path8.c_str()), &cookie);
+ bool res = am->addAssetPath(String8(path8.c_str()), &cookie, appAsLib);
return (res) ? static_cast<jint>(cookie) : 0;
}
@@ -2138,7 +2138,7 @@
(void*) android_content_AssetManager_getAssetLength },
{ "getAssetRemainingLength", "(J)J",
(void*) android_content_AssetManager_getAssetRemainingLength },
- { "addAssetPathNative", "(Ljava/lang/String;)I",
+ { "addAssetPathNative", "(Ljava/lang/String;Z)I",
(void*) android_content_AssetManager_addAssetPath },
{ "addOverlayPathNative", "(Ljava/lang/String;)I",
(void*) android_content_AssetManager_addOverlayPath },
diff --git a/include/androidfw/AssetManager.h b/include/androidfw/AssetManager.h
index 0cfd2b1..3d4e47d 100644
--- a/include/androidfw/AssetManager.h
+++ b/include/androidfw/AssetManager.h
@@ -93,13 +93,14 @@
* look in multiple places for assets. It can be either a directory (for
* finding assets as raw files on the disk) or a ZIP file. This newly
* added asset path will be examined first when searching for assets,
- * before any that were previously added.
+ * before any that were previously added, the assets are added as shared
+ * library if appAsLib is true.
*
* Returns "true" on success, "false" on failure. If 'cookie' is non-NULL,
* then on success, *cookie is set to the value corresponding to the
* newly-added asset source.
*/
- bool addAssetPath(const String8& path, int32_t* cookie);
+ bool addAssetPath(const String8& path, int32_t* cookie, bool appAsLib=false);
bool addOverlayPath(const String8& path, int32_t* cookie);
/*
@@ -280,7 +281,7 @@
const ResTable* getResTable(bool required = true) const;
void setLocaleLocked(const char* locale);
void updateResourceParamsLocked() const;
- bool appendPathToResTable(const asset_path& ap) const;
+ bool appendPathToResTable(const asset_path& ap, bool appAsLib=false) const;
Asset* openIdmapLocked(const struct asset_path& ap) const;
diff --git a/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h
index eff1f5f..49b6333 100644
--- a/include/androidfw/ResourceTypes.h
+++ b/include/androidfw/ResourceTypes.h
@@ -1505,7 +1505,7 @@
class DynamicRefTable
{
public:
- DynamicRefTable(uint8_t packageId);
+ DynamicRefTable(uint8_t packageId, bool appAsLib);
// Loads an unmapped reference table from the package.
status_t load(const ResTable_lib_header* const header);
@@ -1530,6 +1530,7 @@
const uint8_t mAssignedPackageId;
uint8_t mLookupTable[256];
KeyedVector<String16, uint8_t> mEntries;
+ bool mAppAsLib;
};
bool U16StringToInt(const char16_t* s, size_t len, Res_value* outValue);
@@ -1547,10 +1548,11 @@
status_t add(const void* data, size_t size, const int32_t cookie=-1, bool copyData=false);
status_t add(const void* data, size_t size, const void* idmapData, size_t idmapDataSize,
- const int32_t cookie=-1, bool copyData=false);
+ const int32_t cookie=-1, bool copyData=false, bool appAsLib=false);
status_t add(Asset* asset, const int32_t cookie=-1, bool copyData=false);
- status_t add(Asset* asset, Asset* idmapAsset, const int32_t cookie=-1, bool copyData=false);
+ status_t add(Asset* asset, Asset* idmapAsset, const int32_t cookie=-1, bool copyData=false,
+ bool appAsLib=false);
status_t add(ResTable* src);
status_t addEmpty(const int32_t cookie);
@@ -1858,7 +1860,7 @@
typedef Vector<Type*> TypeList;
status_t addInternal(const void* data, size_t size, const void* idmapData, size_t idmapDataSize,
- const int32_t cookie, bool copyData);
+ bool appAsLib, const int32_t cookie, bool copyData);
ssize_t getResourcePackageIndex(uint32_t resID) const;
@@ -1871,7 +1873,7 @@
size_t nameLen, uint32_t* outTypeSpecFlags) const;
status_t parsePackage(
- const ResTable_package* const pkg, const Header* const header);
+ const ResTable_package* const pkg, const Header* const header, bool appAsLib);
void print_value(const Package* pkg, const Res_value& value) const;
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index 623ea89..8a03b94 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -176,7 +176,7 @@
delete[] mVendor;
}
-bool AssetManager::addAssetPath(const String8& path, int32_t* cookie)
+bool AssetManager::addAssetPath(const String8& path, int32_t* cookie, bool appAsLib)
{
AutoMutex _l(mLock);
@@ -238,7 +238,7 @@
#endif
if (mResources != NULL) {
- appendPathToResTable(ap);
+ appendPathToResTable(ap, appAsLib);
}
return true;
@@ -610,7 +610,7 @@
return kFileTypeRegular;
}
-bool AssetManager::appendPathToResTable(const asset_path& ap) const {
+bool AssetManager::appendPathToResTable(const asset_path& ap, bool appAsLib) const {
// skip those ap's that correspond to system overlays
if (ap.isSystemOverlay) {
return true;
@@ -685,7 +685,7 @@
mResources->add(sharedRes);
} else {
ALOGV("Parsing resources for %s", ap.path.string());
- mResources->add(ass, idmap, nextEntryIdx + 1, !shared);
+ mResources->add(ass, idmap, nextEntryIdx + 1, !shared, appAsLib);
}
onlyEmptyResources = false;
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 37de89a..21b543e 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -3080,13 +3080,13 @@
// table that defined the package); the ones after are skins on top of it.
struct ResTable::PackageGroup
{
- PackageGroup(ResTable* _owner, const String16& _name, uint32_t _id)
+ PackageGroup(ResTable* _owner, const String16& _name, uint32_t _id, bool appAsLib)
: owner(_owner)
, name(_name)
, id(_id)
, largestTypeId(0)
, bags(NULL)
- , dynamicRefTable(static_cast<uint8_t>(_id))
+ , dynamicRefTable(static_cast<uint8_t>(_id), appAsLib)
{ }
~PackageGroup() {
@@ -3532,7 +3532,7 @@
{
memset(&mParams, 0, sizeof(mParams));
memset(mPackageMap, 0, sizeof(mPackageMap));
- addInternal(data, size, NULL, 0, cookie, copyData);
+ addInternal(data, size, NULL, 0, false, cookie, copyData);
LOG_FATAL_IF(mError != NO_ERROR, "Error parsing resource table");
if (kDebugTableSuperNoisy) {
ALOGI("Creating ResTable %p\n", this);
@@ -3553,12 +3553,12 @@
}
status_t ResTable::add(const void* data, size_t size, const int32_t cookie, bool copyData) {
- return addInternal(data, size, NULL, 0, cookie, copyData);
+ return addInternal(data, size, NULL, 0, false, cookie, copyData);
}
status_t ResTable::add(const void* data, size_t size, const void* idmapData, size_t idmapDataSize,
- const int32_t cookie, bool copyData) {
- return addInternal(data, size, idmapData, idmapDataSize, cookie, copyData);
+ const int32_t cookie, bool copyData, bool appAsLib) {
+ return addInternal(data, size, idmapData, idmapDataSize, appAsLib, cookie, copyData);
}
status_t ResTable::add(Asset* asset, const int32_t cookie, bool copyData) {
@@ -3568,10 +3568,12 @@
return UNKNOWN_ERROR;
}
- return addInternal(data, static_cast<size_t>(asset->getLength()), NULL, 0, cookie, copyData);
+ return addInternal(data, static_cast<size_t>(asset->getLength()), NULL, false, 0, cookie,
+ copyData);
}
-status_t ResTable::add(Asset* asset, Asset* idmapAsset, const int32_t cookie, bool copyData) {
+status_t ResTable::add(Asset* asset, Asset* idmapAsset, const int32_t cookie, bool copyData,
+ bool appAsLib) {
const void* data = asset->getBuffer(true);
if (data == NULL) {
ALOGW("Unable to get buffer of resource asset file");
@@ -3590,7 +3592,7 @@
}
return addInternal(data, static_cast<size_t>(asset->getLength()),
- idmapData, idmapSize, cookie, copyData);
+ idmapData, idmapSize, appAsLib, cookie, copyData);
}
status_t ResTable::add(ResTable* src)
@@ -3603,7 +3605,7 @@
for (size_t i=0; i<src->mPackageGroups.size(); i++) {
PackageGroup* srcPg = src->mPackageGroups[i];
- PackageGroup* pg = new PackageGroup(this, srcPg->name, srcPg->id);
+ PackageGroup* pg = new PackageGroup(this, srcPg->name, srcPg->id, false);
for (size_t j=0; j<srcPg->packages.size(); j++) {
pg->packages.add(srcPg->packages[j]);
}
@@ -3644,7 +3646,7 @@
}
status_t ResTable::addInternal(const void* data, size_t dataSize, const void* idmapData, size_t idmapDataSize,
- const int32_t cookie, bool copyData)
+ bool appAsLib, const int32_t cookie, bool copyData)
{
if (!data) {
return NO_ERROR;
@@ -3747,7 +3749,7 @@
return (mError=BAD_TYPE);
}
- if (parsePackage((ResTable_package*)chunk, header) != NO_ERROR) {
+ if (parsePackage((ResTable_package*)chunk, header, appAsLib) != NO_ERROR) {
return mError;
}
curPackage++;
@@ -5935,7 +5937,7 @@
}
status_t ResTable::parsePackage(const ResTable_package* const pkg,
- const Header* const header)
+ const Header* const header, bool appAsLib)
{
const uint8_t* base = (const uint8_t*)pkg;
status_t err = validate_chunk(&pkg->header, sizeof(*pkg) - sizeof(pkg->typeIdOffset),
@@ -5983,7 +5985,7 @@
if (id >= 256) {
LOG_ALWAYS_FATAL("Package id out of range");
return NO_ERROR;
- } else if (id == 0) {
+ } else if (id == 0 || appAsLib) {
// This is a library so assign an ID
id = mNextPackageId++;
}
@@ -6016,7 +6018,7 @@
char16_t tmpName[sizeof(pkg->name)/sizeof(pkg->name[0])];
strcpy16_dtoh(tmpName, pkg->name, sizeof(pkg->name)/sizeof(pkg->name[0]));
- group = new PackageGroup(this, String16(tmpName), id);
+ group = new PackageGroup(this, String16(tmpName), id, appAsLib);
if (group == NULL) {
delete package;
return (mError=NO_MEMORY);
@@ -6228,8 +6230,9 @@
return NO_ERROR;
}
-DynamicRefTable::DynamicRefTable(uint8_t packageId)
+DynamicRefTable::DynamicRefTable(uint8_t packageId, bool appAsLib)
: mAssignedPackageId(packageId)
+ , mAppAsLib(appAsLib)
{
memset(mLookupTable, 0, sizeof(mLookupTable));
@@ -6314,16 +6317,18 @@
uint32_t res = *resId;
size_t packageId = Res_GETPACKAGE(res) + 1;
- if (packageId == APP_PACKAGE_ID) {
+ if (packageId == APP_PACKAGE_ID && !mAppAsLib) {
// No lookup needs to be done, app package IDs are absolute.
return NO_ERROR;
}
- if (packageId == 0) {
+ if (packageId == 0 || (packageId == APP_PACKAGE_ID && mAppAsLib)) {
// The package ID is 0x00. That means that a shared library is accessing
- // its own local resource, so we fix up the resource with the calling
- // package ID.
- *resId |= ((uint32_t) mAssignedPackageId) << 24;
+ // its own local resource.
+ // Or if app resource is loaded as shared library, the resource which has
+ // app package Id is local resources.
+ // so we fix up those resources with the calling package ID.
+ *resId = (0xFFFFFF & (*resId)) | (((uint32_t) mAssignedPackageId) << 24);
return NO_ERROR;
}
@@ -6345,7 +6350,10 @@
}
status_t DynamicRefTable::lookupResourceValue(Res_value* value) const {
- if (value->dataType != Res_value::TYPE_DYNAMIC_REFERENCE) {
+ if (value->dataType != Res_value::TYPE_DYNAMIC_REFERENCE &&
+ (value->dataType != Res_value::TYPE_REFERENCE || !mAppAsLib)) {
+ // If the package is loaded as shared library, the resource reference
+ // also need to be fixed.
return NO_ERROR;
}
diff --git a/libs/androidfw/tests/Android.mk b/libs/androidfw/tests/Android.mk
index a353575..2bc026b7 100644
--- a/libs/androidfw/tests/Android.mk
+++ b/libs/androidfw/tests/Android.mk
@@ -21,6 +21,7 @@
LOCAL_PATH:= $(call my-dir)
testFiles := \
+ AppAsLib_test.cpp \
AttributeFinder_test.cpp \
ByteBucketArray_test.cpp \
Config_test.cpp \
diff --git a/libs/androidfw/tests/AppAsLib_test.cpp b/libs/androidfw/tests/AppAsLib_test.cpp
new file mode 100644
index 0000000..bdb0c3d
--- /dev/null
+++ b/libs/androidfw/tests/AppAsLib_test.cpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 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 "data/basic/R.h"
+#include "data/appaslib/R.h"
+
+#include <gtest/gtest.h>
+
+using namespace android;
+
+namespace {
+
+#include "data/basic/basic_arsc.h"
+
+TEST(AppAsLibTest, loadedAsApp) {
+ ResTable table;
+ ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+
+ Res_value val;
+ ssize_t block = table.getResource(base::R::integer::number2, &val);
+ ASSERT_GE(block, 0);
+ ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType);
+ ASSERT_EQ(base::R::array::integerArray1, val.data);
+}
+
+TEST(AppAsLibTest, loadedAsSharedLib) {
+ ResTable table;
+ // Load as shared library.
+ ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len, NULL, 0, -1, false, true));
+
+ Res_value val;
+ ssize_t block = table.getResource(appaslib::R::integer::number2, &val);
+ ASSERT_GE(block, 0);
+ ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType);
+ ASSERT_EQ(appaslib::R::array::integerArray1, val.data);
+}
+
+}
diff --git a/libs/androidfw/tests/data/appaslib/R.h b/libs/androidfw/tests/data/appaslib/R.h
new file mode 100644
index 0000000..f89d4bf
--- /dev/null
+++ b/libs/androidfw/tests/data/appaslib/R.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2015 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 __APPASLIB_R_H
+#define __APPASLIB_R_H
+
+namespace appaslib {
+namespace R {
+
+namespace integer {
+ enum {
+ number2 = 0x02040001, // default
+ };
+}
+
+namespace array {
+ enum {
+ integerArray1 = 0x02060000, // default
+ };
+}
+
+} // namespace R
+} // namespace appaslib
+
+#endif // __APPASLIB_R_H
diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h
index cbe7c5d..c29bb48 100644
--- a/tools/aapt/Bundle.h
+++ b/tools/aapt/Bundle.h
@@ -66,6 +66,7 @@
mErrorOnMissingConfigEntry(false), mOutputTextSymbols(NULL),
mSingleCrunchInputFile(NULL), mSingleCrunchOutputFile(NULL),
mBuildSharedLibrary(false),
+ mBuildAppAsSharedLibrary(false),
mArgc(0), mArgv(NULL)
{}
~Bundle(void) {}
@@ -206,6 +207,8 @@
void setSingleCrunchOutputFile(const char* val) { mSingleCrunchOutputFile = val; }
bool getBuildSharedLibrary() const { return mBuildSharedLibrary; }
void setBuildSharedLibrary(bool val) { mBuildSharedLibrary = val; }
+ bool getBuildAppAsSharedLibrary() const { return mBuildAppAsSharedLibrary; }
+ void setBuildAppAsSharedLibrary(bool val) { mBuildAppAsSharedLibrary = val; }
void setNoVersionVectors(bool val) { mNoVersionVectors = val; }
bool getNoVersionVectors() const { return mNoVersionVectors; }
@@ -327,6 +330,7 @@
const char* mSingleCrunchInputFile;
const char* mSingleCrunchOutputFile;
bool mBuildSharedLibrary;
+ bool mBuildAppAsSharedLibrary;
android::String8 mPlatformVersionCode;
android::String8 mPlatformVersionName;
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index d12ab3b..21f47bc2 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -2395,11 +2395,11 @@
// Write the R.java file into the appropriate class directory
// e.g. gen/com/foo/app/R.java
err = writeResourceSymbols(bundle, assets, assets->getPackage(), true,
- bundle->getBuildSharedLibrary());
+ bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
} else {
const String8 customPkg(bundle->getCustomPackage());
err = writeResourceSymbols(bundle, assets, customPkg, true,
- bundle->getBuildSharedLibrary());
+ bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
}
if (err < 0) {
goto bail;
@@ -2414,7 +2414,7 @@
while (packageString != NULL) {
// Write the R.java file out with the correct package name
err = writeResourceSymbols(bundle, assets, String8(packageString), true,
- bundle->getBuildSharedLibrary());
+ bundle->getBuildSharedLibrary() || bundle->getBuildAppAsSharedLibrary());
if (err < 0) {
goto bail;
}
diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp
index bcf0d5e..6411286 100644
--- a/tools/aapt/Main.cpp
+++ b/tools/aapt/Main.cpp
@@ -200,6 +200,9 @@
" --shared-lib\n"
" Make a shared library resource package that can be loaded by an application\n"
" at runtime to access the libraries resources. Implies --non-constant-id.\n"
+ " --app-as-shared-lib\n"
+ " Make an app resource package that also can be loaded as shared library at runtime.\n"
+ " Implies --non-constant-id.\n"
" --error-on-failed-insert\n"
" Forces aapt to return an error if it fails to insert values into the manifest\n"
" with --debug-mode, --min-sdk-version, --target-sdk-version --version-code\n"
@@ -668,6 +671,9 @@
} else if (strcmp(cp, "-shared-lib") == 0) {
bundle.setNonConstantId(true);
bundle.setBuildSharedLibrary(true);
+ } else if (strcmp(cp, "-app-as-shared-lib") == 0) {
+ bundle.setNonConstantId(true);
+ bundle.setBuildAppAsSharedLibrary(true);
} else if (strcmp(cp, "-no-crunch") == 0) {
bundle.setUseCrunchCache(true);
} else if (strcmp(cp, "-ignore-assets") == 0) {