summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/ActivityThread.java28
-rw-r--r--core/java/android/app/ContextImpl.java42
-rw-r--r--core/java/android/content/pm/ActivityInfo.java63
-rw-r--r--core/java/android/content/pm/ApplicationInfo.java2
-rw-r--r--core/java/android/content/pm/PackageParser.java4
-rw-r--r--core/java/android/content/res/AssetManager.java9
-rw-r--r--core/java/android/view/ContextThemeWrapper.java10
-rw-r--r--core/jni/android_util_AssetManager.cpp85
-rw-r--r--include/utils/AssetManager.h14
-rw-r--r--include/utils/FileLock.h79
-rw-r--r--include/utils/ResourceTypes.h45
-rw-r--r--libs/utils/Android.mk1
-rw-r--r--libs/utils/AssetManager.cpp459
-rw-r--r--libs/utils/FileLock.cpp79
-rw-r--r--libs/utils/ResourceTypes.cpp299
15 files changed, 1085 insertions, 134 deletions
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 2bb412e..c30c442 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -226,6 +226,8 @@ public final class ActivityThread {
if (assets.addAssetPath(resDir) == 0) {
return null;
}
+
+ /* Attach theme information to the resulting AssetManager when appropriate. */
Configuration config = getConfiguration();
if (isThemable && config != null) {
if (config.customTheme == null) {
@@ -233,14 +235,8 @@ public final class ActivityThread {
}
if (!TextUtils.isEmpty(config.customTheme.getThemePackageName())) {
- PackageInfo pi = getPackageInfo(config.customTheme.getThemePackageName(), 0);
- if (pi != null) {
- String themeResDir = pi.getResDir();
- if (assets.addAssetPath(themeResDir) != 0) {
- assets.setThemePackageName(config.customTheme.getThemePackageName());
- } else {
- Log.e(TAG, "Unable to add theme resdir=" + themeResDir);
- }
+ if (!attachThemeAssets(assets, resDir, config.customTheme)) {
+ Log.e(TAG, "Failed to attach theme " + config.customTheme + " to resource '" + resDir + "'");
}
}
}
@@ -253,7 +249,7 @@ public final class ActivityThread {
+ r.getConfiguration() + " appScale="
+ r.getCompatibilityInfo().applicationScale);
}
-
+
synchronized (mPackages) {
WeakReference<Resources> wr = mActiveResources.get(key);
Resources existing = wr != null ? wr.get() : null;
@@ -270,6 +266,20 @@ public final class ActivityThread {
}
}
+ private boolean attachThemeAssets(AssetManager assets, String resDir, CustomTheme theme) {
+ PackageInfo pi = getPackageInfo(theme.getThemePackageName(), 0);
+ if (pi != null) {
+ String themeResDir = pi.getResDir();
+ if (assets.addAssetPath(themeResDir) != 0) {
+ assets.setThemePackageName(theme.getThemePackageName());
+ return true;
+ } else {
+ Log.e(TAG, "Unable to add theme resdir=" + themeResDir);
+ }
+ }
+ return false;
+ }
+
/**
* Creates the top level resources for the given package.
*
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 1633ca0..5c4d803 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -291,53 +291,15 @@ class ContextImpl extends Context {
mThemeResource = resid;
}
- private int determineDefaultThemeResource() {
- if (getResources() != Resources.getSystem() && mPackageInfo.mApplicationInfo.isThemeable) {
- try {
- Configuration config = ActivityManagerNative.getDefault().getConfiguration();
- if (config.customTheme != null) {
- int themeId = CustomTheme.getStyleId(this,
- config.customTheme.getThemePackageName(),
- config.customTheme.getThemeId());
- if (themeId == -1) {
- CustomTheme defaultTheme = CustomTheme.getDefault();
- if (config.customTheme.equals(defaultTheme)) {
- return com.android.internal.R.style.Theme;
- } else {
- themeId = CustomTheme.getStyleId(this,
- defaultTheme.getThemePackageName(),
- defaultTheme.getThemeId());
- if (themeId == -1) {
- return com.android.internal.R.style.Theme;
- } else {
- return themeId;
- }
- }
- } else {
- return themeId;
- }
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Unable to access configuration, reverting to original system default theme", e);
- }
- }
-
- /* Fallback... */
- return com.android.internal.R.style.Theme;
- }
-
@Override
public Resources.Theme getTheme() {
if (mTheme == null) {
- int themeId;
if (mThemeResource == 0) {
- themeId = determineDefaultThemeResource();
- } else {
- themeId = mThemeResource;
+ mThemeResource = com.android.internal.R.style.Theme;
}
mTheme = mResources.newTheme();
- mTheme.applyStyle(themeId, true);
+ mTheme.applyStyle(mThemeResource, true);
}
return mTheme;
}
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index ef62bc9..3257ebe 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -307,48 +307,6 @@ public class ActivityInfo extends ComponentInfo
*/
public int softInputMode;
- /**
- * isThemeable flag is not explicitly set - use isThemeable value from ApllicationInfo.
- */
- private static final int ISTHEMEABLE_INHERITED = 0;
-
- /**
- * isThemeable flag is explicitly set to false.
- */
- private static final int ISTHEMEABLE_FALSE = 1;
-
- /**
- * isThemeable flag is explicitly set to true.
- */
- private static final int ISTHEMEABLE_TRUE = 2;
-
- /**
- * Is given activity theme agnostic, i.e. behaves properly when default theme is changed.
- *
- * @deprecated Not fully supported; do not use.
- * @hide
- */
- private int isThemeable = ISTHEMEABLE_INHERITED;
-
- /**
- * @deprecated Not fully supported; do not use.
- * @hide
- */
- public boolean getIsThemeable() {
- if (isThemeable == ISTHEMEABLE_INHERITED) {
- return applicationInfo != null && applicationInfo.isThemeable;
- }
- return isThemeable != ISTHEMEABLE_FALSE;
- }
-
- /**
- * @deprecated Not fully supported; do not use.
- * @hide
- */
- public void setIsThemeable(boolean value) {
- isThemeable = value? ISTHEMEABLE_TRUE : ISTHEMEABLE_FALSE;
- }
-
public ActivityInfo() {
}
@@ -363,7 +321,6 @@ public class ActivityInfo extends ComponentInfo
screenOrientation = orig.screenOrientation;
configChanges = orig.configChanges;
softInputMode = orig.softInputMode;
- isThemeable = orig.isThemeable;
}
/**
@@ -377,23 +334,6 @@ public class ActivityInfo extends ComponentInfo
return theme != 0 ? theme : applicationInfo.theme;
}
- /**
- * @deprecated Not fully supported; do not use.
- * @hide
- */
- public final boolean isThemeable() {
- switch (isThemeable) {
- case ISTHEMEABLE_TRUE:
- return true;
-
- case ISTHEMEABLE_INHERITED:
- return applicationInfo.isThemeable;
-
- default:
- return false;
- }
- }
-
public void dump(Printer pw, String prefix) {
super.dumpFront(pw, prefix);
if (permission != null) {
@@ -412,7 +352,6 @@ public class ActivityInfo extends ComponentInfo
+ " configChanges=0x" + Integer.toHexString(configChanges)
+ " softInputMode=0x" + Integer.toHexString(softInputMode));
}
- pw.println(prefix + "isThemeable=" + isThemeable);
super.dumpBack(pw, prefix);
}
@@ -437,7 +376,6 @@ public class ActivityInfo extends ComponentInfo
dest.writeInt(screenOrientation);
dest.writeInt(configChanges);
dest.writeInt(softInputMode);
- dest.writeInt(isThemeable);
}
public static final Parcelable.Creator<ActivityInfo> CREATOR
@@ -461,6 +399,5 @@ public class ActivityInfo extends ComponentInfo
screenOrientation = source.readInt();
configChanges = source.readInt();
softInputMode = source.readInt();
- isThemeable = source.readInt();
}
}
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 978ff73..a76c523 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -353,7 +353,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
* Is given application theme agnostic, i.e. behaves properly when default theme is changed.
* {@hide}
*/
- public boolean isThemeable = false;
+ public boolean isThemeable = true;
private static final String PLUTO_SCHEMA = "http://www.w3.org/2001/pluto.html";
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 6006ac0..b77ea32 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1472,9 +1472,7 @@ public class PackageParser {
continue;
}
String attrName = attrs.getAttributeName(i);
- if (attrName.equalsIgnoreCase(ApplicationInfo.PLUTO_ISTHEMEABLE_ATTRIBUTE_NAME)) {
- ai.setIsThemeable(attrs.getAttributeBooleanValue(i, false));
- } else if (attrName.equalsIgnoreCase(ApplicationInfo.PLUTO_HANDLE_THEME_CONFIG_CHANGES_ATTRIBUTE_NAME)) {
+ if (attrName.equalsIgnoreCase(ApplicationInfo.PLUTO_HANDLE_THEME_CONFIG_CHANGES_ATTRIBUTE_NAME)) {
ai.configChanges |= ActivityInfo.CONFIG_THEME_RESOURCE;
}
}
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 4b5d769..56b5cde 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -82,7 +82,6 @@ public final class AssetManager {
private String mAppName;
private boolean mThemeSupport;
- private String mThemePackageName;
/**
* Create a new AssetManager containing only the basic system assets.
@@ -695,17 +694,13 @@ public final class AssetManager {
* Get package name of current theme (may return null).
* {@hide}
*/
- public final String getThemePackageName() {
- return mThemePackageName;
- }
+ public native final String getThemePackageName();
/**
* Sets package name for current theme (null is allowed).
* {@hide}
*/
- public final void setThemePackageName(String packageName) {
- mThemePackageName = packageName;
- }
+ public native final void setThemePackageName(String packageName);
/**
* Determine whether the state in this asset manager is up-to-date with
diff --git a/core/java/android/view/ContextThemeWrapper.java b/core/java/android/view/ContextThemeWrapper.java
index e82af80..3c05d05 100644
--- a/core/java/android/view/ContextThemeWrapper.java
+++ b/core/java/android/view/ContextThemeWrapper.java
@@ -124,11 +124,11 @@ public class ContextThemeWrapper extends ContextWrapper {
}
if (mThemeResource == 0) {
- return mBase.getTheme();
- } else {
- initializeTheme();
- return mTheme;
+ mThemeResource = com.android.internal.R.style.Theme;
}
+ initializeTheme();
+
+ return mTheme;
}
@Override
@@ -158,7 +158,7 @@ public class ContextThemeWrapper extends ContextWrapper {
}
private void initializeTheme() {
- final boolean first = (mTheme == null);
+ final boolean first = mTheme == null;
if (first) {
mTheme = getResources().newTheme();
Resources.Theme theme = mBase.getTheme();
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index c8f4599..24ff670 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -37,6 +37,8 @@
#include <stdio.h>
+#define REDIRECT_NOISY(x) //x
+
namespace android {
// ----------------------------------------------------------------------------
@@ -718,17 +720,23 @@ static jint android_content_AssetManager_loadResourceValue(JNIEnv* env, jobject
}
const ResTable& res(am->getResources());
+ uint32_t ref = res.lookupRedirectionMap(ident);
+ if (ref == 0) {
+ ref = ident;
+ } else {
+ REDIRECT_NOISY(LOGW("PERFORMED REDIRECT OF ident=0x%08x FOR ref=0x%08x\n", ident, ref));
+ }
+
Res_value value;
ResTable_config config;
uint32_t typeSpecFlags;
- ssize_t block = res.getResource(ident, &value, false, &typeSpecFlags, &config);
+ ssize_t block = res.getResource(ref, &value, false, &typeSpecFlags, &config);
#if THROW_ON_BAD_ID
if (block == BAD_INDEX) {
jniThrowException(env, "java/lang/IllegalStateException", "Bad resource!");
return 0;
}
#endif
- uint32_t ref = ident;
if (resolve) {
block = res.resolveReference(&value, block, &ref);
#if THROW_ON_BAD_ID
@@ -993,6 +1001,20 @@ static jboolean android_content_AssetManager_applyStyle(JNIEnv* env, jobject cla
// Now lock down the resource object and start pulling stuff from it.
res.lock();
+ // Apply theme redirections to the referenced styles.
+ if (defStyleRes != 0) {
+ uint32_t ref = res.lookupRedirectionMap(defStyleRes);
+ if (ref != 0) {
+ defStyleRes = ref;
+ }
+ }
+ if (style != 0) {
+ uint32_t ref = res.lookupRedirectionMap(style);
+ if (ref != 0) {
+ style = ref;
+ }
+ }
+
// Retrieve the default style bag, if requested.
const ResTable::bag_entry* defStyleEnt = NULL;
uint32_t defStyleTypeSetFlags = 0;
@@ -1116,6 +1138,17 @@ static jboolean android_content_AssetManager_applyStyle(JNIEnv* env, jobject cla
value.dataType = Res_value::TYPE_NULL;
}
+ // One final test for a resource redirection from the applied theme.
+ if (resid != 0) {
+ uint32_t redirect = res.lookupRedirectionMap(resid);
+ if (redirect != 0) {
+ REDIRECT_NOISY(LOGW("deep REDIRECT FROM resid=0x%08x TO redirect=0x%08x\n", resid, redirect));
+ block = res.getResource(redirect, &value, false, &typeSetFlags, &config);
+ block = res.resolveReference(&value, block, &redirect);
+ resid = redirect;
+ }
+ }
+
DEBUG_STYLES(LOGI("Attribute 0x%08x: type=0x%x, data=0x%08x",
curIdent, value.dataType, value.data));
@@ -1367,6 +1400,17 @@ static jint android_content_AssetManager_retrieveArray(JNIEnv* env, jobject claz
value.dataType = Res_value::TYPE_NULL;
}
+ // One final test for a resource redirection from the applied theme.
+ if (resid != 0) {
+ uint32_t redirect = res.lookupRedirectionMap(resid);
+ if (redirect != 0) {
+ REDIRECT_NOISY(LOGW("array REDIRECT FROM resid=0x%08x TO redirect=0x%08x\n", resid, redirect));
+ block = res.getResource(redirect, &value, false, &typeSetFlags, &config);
+ block = res.resolveReference(&value, block, &redirect);
+ resid = redirect;
+ }
+ }
+
//printf("Attribute 0x%08x: final type=0x%x, data=0x%08x\n", curIdent, value.dataType, value.data);
// Write the final value back to Java.
@@ -1723,6 +1767,38 @@ static jint android_content_AssetManager_getGlobalAssetManagerCount(JNIEnv* env,
return AssetManager::getGlobalCount();
}
+static void android_content_AssetManager_setThemePackageName(JNIEnv* env, jobject clazz,
+ jstring packageName)
+{
+ AssetManager* am = assetManagerForJavaObject(env, clazz);
+ if (am == NULL) {
+ return;
+ }
+
+ if (packageName != NULL) {
+ const char* packageName8 = env->GetStringUTFChars(packageName, NULL);
+ am->setThemePackageName(packageName8);
+ env->ReleaseStringUTFChars(packageName, packageName8);
+ } else {
+ am->setThemePackageName(NULL);
+ }
+}
+
+static jstring android_content_AssetManager_getThemePackageName(JNIEnv* env, jobject clazz)
+{
+ AssetManager* am = assetManagerForJavaObject(env, clazz);
+ if (am == NULL) {
+ return NULL;
+ }
+
+ const char* packageName = am->getThemePackageName();
+ if (packageName == NULL) {
+ return NULL;
+ }
+
+ return env->NewStringUTF(packageName);
+}
+
static jboolean android_content_AssetManager_removeAssetPath(JNIEnv* env, jobject clazz,
jstring packageName, jstring path)
{
@@ -1891,6 +1967,11 @@ static JNINativeMethod gAssetManagerMethods[] = {
{ "splitThemePackage","(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;)I",
(void*) android_content_AssetManager_splitThemePackage },
+ // Dynamic theme package support.
+ { "setThemePackageName", "(Ljava/lang/String;)V",
+ (void*) android_content_AssetManager_setThemePackageName },
+ { "getThemePackageName", "()Ljava/lang/String;",
+ (void*) android_content_AssetManager_getThemePackageName },
{ "removeAssetPath", "(Ljava/lang/String;Ljava/lang/String;)Z",
(void*) android_content_AssetManager_removeAssetPath },
{ "updateResourcesWithAssetPath", "(Ljava/lang/String;)I",
diff --git a/include/utils/AssetManager.h b/include/utils/AssetManager.h
index e1a11e1..57d6ef2 100644
--- a/include/utils/AssetManager.h
+++ b/include/utils/AssetManager.h
@@ -198,6 +198,9 @@ public:
*/
void getLocales(Vector<String8>* locales) const;
+ void setThemePackageName(const char* packageName);
+ const char* getThemePackageName();
+
/*
* Remove existing source for assets. It can be either a directory (for
* deleting assets as raw files on the disk) or a ZIP file.
@@ -216,7 +219,12 @@ private:
FileType type;
};
- void updateResTableFromAssetPath(ResTable *rt, const asset_path& ap, void *cookie) const;
+ SharedBuffer* generateRedirections(SharedBuffer* entriesByTypeBuf, ResTable* rt,
+ const char* themePackageName, const char16_t* resPackageName);
+ bool generateAndWriteRedirections(ResTable* rt, const char* themePackageName,
+ const char16_t* resPackageName, const char* redirPath, bool isFramework) const;
+ void loadRedirectionMappings(ResTable* rt) const;
+ void updateResTableFromAssetPath(ResTable* rt, const asset_path& ap, void* cookie) const;
Asset* openInPathLocked(const char* fileName, AccessMode mode,
const asset_path& path);
Asset* openNonAssetInPathLocked(const char* fileName, AccessMode mode,
@@ -333,6 +341,10 @@ private:
char* mLocale;
char* mVendor;
+ // If non-null, represents the theme package from which to construct the
+ // resource redirection map used by ResTable.
+ char* mThemePackageName;
+
mutable ResTable* mResources;
ResTable_config* mConfig;
diff --git a/include/utils/FileLock.h b/include/utils/FileLock.h
new file mode 100644
index 0000000..fde5938
--- /dev/null
+++ b/include/utils/FileLock.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2010, T-Mobile USA, Inc.
+ *
+ * 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 __LIBS_FILE_LOCK_H
+#define __LIBS_FILE_LOCK_H
+
+#include <fcntl.h>
+
+namespace android {
+
+/*
+ * Object oriented interface for flock. Implements reference counting so that
+ * multiple levels of locks on the same object instance is possible.
+ */
+class FileLock {
+public:
+ FileLock(const char* fileName);
+
+ /*
+ * Lock the file. A balanced call to unlock is required even if the lock
+ * fails.
+ */
+ bool lock(int openFlags=O_RDONLY, mode_t fileCreateMode=0755) {
+ mRefCount++;
+ if (mFd == -1) {
+ return doLock(openFlags, fileCreateMode);
+ } else {
+ return true;
+ }
+ }
+
+ /*
+ * Call this when mapping is no longer needed.
+ */
+ void unlock(void) {
+ if (--mRefCount <= 0) {
+ delete this;
+ }
+ }
+
+ /*
+ * Return the name of the file this map came from, if known.
+ */
+ const char* getFileName(void) const { return mFileName; }
+
+ /*
+ * Return the open file descriptor, if locked; -1 otherwise.
+ */
+ int getFileDescriptor(void) const { return mFd; }
+
+protected:
+ // don't delete objects; call unlock()
+ ~FileLock(void);
+
+ bool doLock(int openFlags, mode_t fileCreateMode);
+
+private:
+
+ int mRefCount; // reference count
+ int mFd; // file descriptor, if locked
+ char* mFileName; // original file name, if known
+};
+
+}; // namespace android
+
+#endif // __LIBS_FILE_LOCK_H
diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h
index 0d47cfd..b2ad4e1 100644
--- a/include/utils/ResourceTypes.h
+++ b/include/utils/ResourceTypes.h
@@ -1711,6 +1711,9 @@ public:
bool copyData=false);
status_t add(ResTable* src);
+ status_t addRedirections(int package, const char* dataPath);
+ void clearRedirections();
+
status_t getError() const;
void uninit();
@@ -1756,6 +1759,8 @@ public:
uint32_t* inoutTypeSpecFlags = NULL,
ResTable_config* outConfig = NULL) const;
+ uint32_t lookupRedirectionMap(uint32_t resID) const;
+
enum {
TMP_BUFFER_SIZE = 16
};
@@ -1979,7 +1984,7 @@ private:
const ResTable_package* const pkg, const Header* const header);
void print_value(const Package* pkg, const Res_value& value) const;
-
+
mutable Mutex mLock;
status_t mError;
@@ -1995,6 +2000,44 @@ private:
// Mapping from resource package IDs to indices into the internal
// package array.
uint8_t mPackageMap[256];
+
+ // Represents the resource lookup table for a specific package.
+ class PackageResMap {
+ public:
+ ~PackageResMap();
+
+ int package;
+
+ // Do the heavy lifting to read from a specific package resource map
+ // file.
+ static PackageResMap* createFromCache(int package, const char* cachePath);
+
+ // Returns 0 if the lookup finds no mapping.
+ uint32_t lookup(int type, int entry);
+
+ // Inserts a SharedBuffer array into the res map structure.
+ void insert(int type, const uint32_t* entries);
+
+ private:
+ PackageResMap();
+
+ bool parseMap(const unsigned char* basePtr,
+ const unsigned char* endPtr);
+
+ uint32_t* parseMapType(const unsigned char* basePtr,
+ const unsigned char* endPtr);
+
+ // Sparse array representing all entries, organized into two layers:
+ // first by type, then by entry id. The result of each lookup will be
+ // a qualified resource ID in the theme package scope. Underneath is a
+ // SharedBuffer on both layers which indicates the size.
+ uint32_t** mEntriesByType;
+ };
+
+ // Resource redirection mapping provided by the applied theme (if there is
+ // any). Resources requested which are found in this map will be
+ // automatically redirected to the appropriate themed value.
+ Vector<PackageResMap*> mRedirectionMap;
};
} // namespace android
diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk
index ae9b06a..24cb9a2 100644
--- a/libs/utils/Android.mk
+++ b/libs/utils/Android.mk
@@ -25,6 +25,7 @@ commonSources:= \
CallStack.cpp \
Debug.cpp \
FileMap.cpp \
+ FileLock.cpp \
Flattenable.cpp \
RefBase.cpp \
ResourceTypes.cpp \
diff --git a/libs/utils/AssetManager.cpp b/libs/utils/AssetManager.cpp
index 4591f3e..eeebe05 100644
--- a/libs/utils/AssetManager.cpp
+++ b/libs/utils/AssetManager.cpp
@@ -33,11 +33,16 @@
#include <utils/Log.h>
#include <utils/Timers.h>
#include <utils/threads.h>
+#include <utils/FileLock.h>
#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/types.h>
#include <errno.h>
#include <assert.h>
+#define REDIRECT_NOISY(x) //x
+
using namespace android;
/*
@@ -52,6 +57,8 @@ static const char* kSystemAssets = "framework/framework-res.apk";
static const char* kExcludeExtension = ".EXCLUDE";
+static const char* kThemeResCacheDir = "res-cache/";
+
static Asset* const kExcludedAsset = (Asset*) 0xd000000d;
static volatile int32_t gCount = 0;
@@ -70,6 +77,7 @@ int32_t AssetManager::getGlobalCount()
AssetManager::AssetManager(CacheMode cacheMode)
: mLocale(NULL), mVendor(NULL),
+ mThemePackageName(NULL),
mResources(NULL), mConfig(new ResTable_config),
mCacheMode(cacheMode), mCacheValid(false)
{
@@ -89,6 +97,10 @@ AssetManager::~AssetManager(void)
// don't have a String class yet, so make sure we clean up
delete[] mLocale;
delete[] mVendor;
+
+ if (mThemePackageName != NULL) {
+ delete[] mThemePackageName;
+ }
}
bool AssetManager::addAssetPath(const String8& path, void** cookie)
@@ -371,6 +383,429 @@ FileType AssetManager::getFileType(const char* fileName)
return kFileTypeRegular;
}
+static void createDirIfNecessary(const char* path, mode_t mode, struct stat *statbuf)
+{
+ if (lstat(path, statbuf) != 0) {
+ if (mkdir(path, mode) != 0) {
+ LOGE("mkdir(%s,%04o) failed: %s\n", path, (int)mode, strerror(errno));
+ }
+ }
+}
+
+static SharedBuffer* addToEntriesByTypeBuffer(SharedBuffer* buf, uint32_t from, uint32_t to)
+{
+ size_t currentSize = (buf != NULL) ? buf->size() : 0;
+
+ int type = Res_GETTYPE(from)+1;
+ int entry = Res_GETENTRY(from);
+
+ size_t typeSize = (type+1) * sizeof(uint32_t*);
+ unsigned int requestSize = roundUpPower2(typeSize);
+ if (typeSize > currentSize) {
+ unsigned int requestSize = roundUpPower2(typeSize);
+ if (buf == NULL) {
+ buf = SharedBuffer::alloc(requestSize);
+ } else {
+ buf = buf->editResize(requestSize);
+ }
+ memset((unsigned char*)buf->data()+currentSize, 0, requestSize-currentSize);
+ }
+
+ uint32_t** entriesByType = (uint32_t**)buf->data();
+ uint32_t* entries = entriesByType[type];
+ SharedBuffer* entriesBuf = (entries != NULL) ? SharedBuffer::bufferFromData(entries) : NULL;
+ currentSize = (entriesBuf != NULL) ? entriesBuf->size() : 0;
+ size_t entrySize = (entry+1) * sizeof(uint32_t);
+ if (entrySize > currentSize) {
+ unsigned int requestSize = roundUpPower2(entrySize);
+ if (entriesBuf == NULL) {
+ entriesBuf = SharedBuffer::alloc(requestSize);
+ } else {
+ entriesBuf = entriesBuf->editResize(requestSize);
+ }
+ memset((unsigned char*)entriesBuf->data()+currentSize, 0, requestSize-currentSize);
+ entriesByType[type] = (uint32_t*)entriesBuf->data();
+ }
+ entries = (uint32_t*)entriesBuf->data();
+ entries[entry] = to;
+
+ return buf;
+}
+
+// TODO: Terrible, terrible I/O error handling here!
+static bool writeRedirections(const char* redirPath, SharedBuffer* entriesByTypeBuf)
+{
+ REDIRECT_NOISY(LOGW("writing %s\n", redirPath));
+
+ FILE* fp = fopen(redirPath, "w");
+ if (!fp) {
+ LOGE("fopen(%s,r) failed: %s\n", redirPath, strerror(errno));
+ return false;
+ }
+
+ uint16_t version = 1;
+ fwrite(&version, sizeof(version), 1, fp);
+
+ uint16_t totalTypes = 0;
+ size_t typeSize = (entriesByTypeBuf != NULL) ? entriesByTypeBuf->size() / sizeof(uint32_t*) : 0;
+ uint32_t** entriesByType = (uint32_t**)entriesByTypeBuf->data();
+ for (size_t i=0; i<typeSize; i++) {
+ uint32_t* entries = entriesByType[i];
+ if (entries != NULL) {
+ totalTypes++;
+ }
+ }
+
+ REDIRECT_NOISY(LOGW("writing %d total types\n", (int)totalTypes));
+ fwrite(&totalTypes, sizeof(totalTypes), 1, fp);
+
+ // Start offset for the first type table.
+ uint32_t typeSectionOffset = 4 + (9 * totalTypes);
+
+ for (size_t i=0; i<typeSize; i++) {
+ uint32_t* entries = entriesByType[i];
+ if (entries != NULL) {
+ uint8_t type = i;
+ fwrite(&type, sizeof(type), 1, fp);
+ size_t entrySize = SharedBuffer::bufferFromData(entries)->size() / sizeof(uint32_t);
+ size_t numEntries = 0;
+ for (size_t j=0; j<entrySize; j++) {
+ if (entries[j] != 0) {
+ numEntries++;
+ }
+ }
+ REDIRECT_NOISY(LOGW("%d entries for type %d\n", (int)numEntries, (int)type));
+ fwrite(&typeSectionOffset, sizeof(typeSectionOffset), 1, fp);
+ uint32_t typeSectionLength = numEntries * 6;
+ fwrite(&typeSectionLength, sizeof(typeSectionLength), 1, fp);
+ typeSectionOffset += typeSectionLength;
+ }
+ }
+
+ for (size_t i=0; i<typeSize; i++) {
+ uint32_t* entries = entriesByType[i];
+ if (entries != NULL) {
+ REDIRECT_NOISY(LOGW("writing for type %d...\n", i));
+ size_t entrySize = SharedBuffer::bufferFromData(entries)->size() / sizeof(uint32_t);
+ for (size_t j=0; j<entrySize; j++) {
+ uint32_t resID = entries[j];
+ if (resID != 0) {
+ uint16_t entryIndex = j;
+ REDIRECT_NOISY(LOGW("writing 0x%04x => 0x%08x\n", entryIndex, resID));
+ fwrite(&entryIndex, sizeof(entryIndex), 1, fp);
+ fwrite(&resID, sizeof(resID), 1, fp);
+ }
+ }
+ SharedBuffer::bufferFromData(entries)->release();
+ }
+ }
+
+ if (entriesByTypeBuf != NULL) {
+ entriesByTypeBuf->release();
+ }
+
+ fclose(fp);
+
+ REDIRECT_NOISY(LOGW("written...\n"));
+
+ return true;
+}
+
+// Crude, lame brute force way to generate the initial framework redirections
+// for testing. This code should be generalized and follow a much better OO
+// structure.
+static SharedBuffer* generateFrameworkRedirections(SharedBuffer* entriesByTypeBuf, ResTable* rt,
+ const char* themePackageName, const char* redirPath)
+{
+ REDIRECT_NOISY(LOGW("generateFrameworkRedirections: themePackageName=%s\n", themePackageName));
+
+ // HACK HACK HACK
+ if (strcmp(themePackageName, "com.tmobile.theme.Androidian") != 0) {
+ LOGW("EEP, UNEXPECTED PACKAGE!");
+ return entriesByTypeBuf;
+ }
+
+ rt->lock();
+
+ // Load up a bag for the user-supplied theme.
+ String16 type("style");
+ String16 name("Androidian");
+ String16 package(themePackageName);
+ uint32_t ident = rt->identifierForName(name.string(), name.size(), type.string(), type.size(),
+ package.string(), package.size());
+ if (ident == 0) {
+ LOGW("unable to locate theme identifier %s:%s/%s\n", String8(package).string(),
+ String8(type).string(), String8(name).string());
+ rt->unlock();
+ return entriesByTypeBuf;
+ }
+
+ const ResTable::bag_entry* themeEnt = NULL;
+ ssize_t N = rt->getBagLocked(ident, &themeEnt);
+ const ResTable::bag_entry* endThemeEnt = themeEnt + N;
+
+ // ...and a bag for the framework default.
+ const ResTable::bag_entry* frameworkEnt = NULL;
+ N = rt->getBagLocked(0x01030005, &frameworkEnt);
+ const ResTable::bag_entry* endFrameworkEnt = frameworkEnt + N;
+
+ // The first entry should be for the theme itself.
+ entriesByTypeBuf = addToEntriesByTypeBuffer(entriesByTypeBuf, 0x01030005, ident);
+
+ // Now compare them and infer resource redirections for attributes that
+ // remap to different styles. This works by essentially lining up all the
+ // sorted attributes from each theme and detected TYPE_REFERENCE entries
+ // that point to different resources. When we find such a mismatch, we'll
+ // create a resource redirection from the original framework resource ID to
+ // the one in the theme. This lets us do things like automatically find
+ // redirections for @android:style/Widget.Button by looking at how the
+ // theme overrides the android:attr/buttonStyle attribute.
+ REDIRECT_NOISY(LOGW("delta between 0x01030005 and 0x%08x:\n", ident));
+ for (; frameworkEnt < endFrameworkEnt; frameworkEnt++) {
+ if (frameworkEnt->map.value.dataType != Res_value::TYPE_REFERENCE) {
+ continue;
+ }
+
+ uint32_t curIdent = frameworkEnt->map.name.ident;
+
+ // Walk along the theme entry looking for a match.
+ while (themeEnt < endThemeEnt && curIdent > themeEnt->map.name.ident) {
+ themeEnt++;
+ }
+ // Match found, compare the references.
+ if (themeEnt < endThemeEnt && curIdent == themeEnt->map.name.ident) {
+ if (themeEnt->map.value.data != frameworkEnt->map.value.data) {
+ entriesByTypeBuf = addToEntriesByTypeBuffer(entriesByTypeBuf,
+ frameworkEnt->map.value.data, themeEnt->map.value.data);
+ REDIRECT_NOISY(LOGW(" generated mapping from 0x%08x => 0x%08x (by attr 0x%08x)\n", frameworkEnt->map.value.data,
+ themeEnt->map.value.data, curIdent));
+ }
+ themeEnt++;
+ }
+
+ // Exhausted the theme, bail early.
+ if (themeEnt >= endThemeEnt) {
+ break;
+ }
+ }
+
+ rt->unlock();
+
+ return entriesByTypeBuf;
+}
+
+static SharedBuffer* parseRedirections(SharedBuffer* buf, ResTable* rt,
+ ResXMLTree& xml, String16& themePackage, String16& resPackage)
+{
+ ResXMLTree::event_code_t eventType = xml.getEventType();
+ REDIRECT_NOISY(LOGW("initial eventType=%d\n", (int)eventType));
+ size_t len;
+ while (eventType != ResXMLTree::END_DOCUMENT) {
+ if (eventType == ResXMLTree::START_TAG) {
+ String8 outerTag(xml.getElementName(&len));
+ if (outerTag == "resource-redirections") {
+ REDIRECT_NOISY(LOGW("got resource-redirections tag\n"));
+ xml.next();
+ while ((eventType = xml.getEventType()) != ResXMLTree::END_TAG) {
+ if (eventType == ResXMLTree::START_TAG) {
+ String8 itemTag(xml.getElementName(&len));
+ if (itemTag == "item") {
+ REDIRECT_NOISY(LOGW("got item tag\n"));
+ ssize_t nameIdx = xml.indexOfAttribute(NULL, "name");
+ size_t fromLen;
+ const char16_t* fromName = NULL;
+ size_t toLen;
+ const char16_t* toName = NULL;
+ if (nameIdx >= 0) {
+ fromName = xml.getAttributeStringValue(nameIdx, &fromLen);
+ REDIRECT_NOISY(LOGW(" got from %s\n", String8(fromName).string()));
+ }
+ if (xml.next() == ResXMLTree::TEXT) {
+ toName = xml.getText(&toLen);
+ REDIRECT_NOISY(LOGW(" got to %s\n", String8(toName).string()));
+ xml.next();
+ }
+ if (toName != NULL && fromName != NULL) {
+ // fromName should look "drawable/foo", so we'll
+ // let identifierForName parse that part of it, but
+ // make sure to provide the background ourselves.
+ // TODO: we should check that the package isn't
+ // already in the string and error out if it is...
+ uint32_t fromIdent = rt->identifierForName(fromName, fromLen, NULL, 0,
+ resPackage.string(), resPackage.size());
+ if (fromIdent == 0) {
+ LOGW("Failed to locate identifier for resource %s:%s\n",
+ String8(fromName, fromLen).string(), String8(resPackage).string());
+ } else {
+ uint32_t toIdent = rt->identifierForName(toName, toLen, NULL, 0,
+ themePackage.string(), themePackage.size());
+ if (toIdent == 0) {
+ LOGW("Failed to locate identifier for resource %s:%s\n",
+ String8(toName, toLen).string(), String8(themePackage).string());
+ } else {
+ REDIRECT_NOISY(LOGW("adding fromIdent=0x%08x to toIdent=0x%08x\n", fromIdent, toIdent));
+ buf = addToEntriesByTypeBuffer(buf, fromIdent, toIdent);
+ }
+ }
+ }
+ } else {
+ REDIRECT_NOISY(LOGW("unexpected tag %s\n", itemTag.string()));
+ }
+ } else if (eventType == ResXMLTree::END_DOCUMENT) {
+ return buf;
+ }
+ xml.next();
+ }
+ }
+ }
+
+ eventType = xml.next();
+ }
+
+ return buf;
+}
+
+// Generate redirections from XML meta data. This code should be moved into
+// the Java space at some point, generated by a special service.
+SharedBuffer* AssetManager::generateRedirections(SharedBuffer* entriesByTypeBuf,
+ ResTable* rt, const char* themePackageName,
+ const char16_t* resPackageName)
+{
+ REDIRECT_NOISY(LOGW("generateRedirections: themePackageName=%s; resPackageName=%s\n",
+ themePackageName, String8(resPackageName).string()));
+
+ String16 type("xml");
+ String16 name(resPackageName);
+ name.replaceAll('.', '_');
+ String16 package(themePackageName);
+ uint32_t xmlIdent = rt->identifierForName(name.string(), name.size(), type.string(), type.size(),
+ package.string(), package.size());
+ REDIRECT_NOISY(LOGW("xmlIdent=0x%08x from %s:%s/%s\n", xmlIdent,
+ String8(package).string(), String8(type).string(), String8(name).string()));
+ if (xmlIdent != 0) {
+ // All this junk is being simulated from the Java side implementation.
+ // This is very clumsy and poorly thought through/tested. This code
+ // will eventually be merged into the Java layer.
+ Res_value value;
+ ssize_t block = rt->getResource(xmlIdent, &value);
+ block = rt->resolveReference(&value, block, &xmlIdent);
+ if (block < 0 || value.dataType != Res_value::TYPE_STRING) {
+ LOGE("Bad redirection XML resource #0x%08x\n", xmlIdent);
+ } else {
+ size_t len;
+ const char16_t* str = rt->valueToString(&value, block, NULL, &len);
+ void* cookie = rt->getTableCookie(block);
+ const size_t whichAssetPath = ((size_t)cookie)-1;
+ Asset* asset = openNonAssetInPathLocked(
+ String8(str).string(), Asset::ACCESS_BUFFER,
+ mAssetPaths.itemAt(whichAssetPath));
+ if (asset == NULL || asset == kExcludedAsset) {
+ LOGE("XML resource %s not found in package\n", String8(str).string());
+ } else {
+ ResXMLTree xml;
+ status_t err = xml.setTo(asset->getBuffer(true), asset->getLength());
+
+ xml.restart();
+
+ String16 resPackage(resPackageName);
+ entriesByTypeBuf = parseRedirections(entriesByTypeBuf, rt, xml,
+ package, resPackage);
+
+ xml.uninit();
+
+ asset->close();
+ delete asset;
+ }
+ }
+ }
+
+ return entriesByTypeBuf;
+}
+
+bool AssetManager::generateAndWriteRedirections(ResTable* rt,
+ const char* themePackageName, const char16_t* resPackageName,
+ const char* redirPath, bool isFramework) const
+{
+ // FIXME: the const is a lie!!!
+ AssetManager* am = (AssetManager*)this;
+
+ SharedBuffer* buf = NULL;
+ if (isFramework) {
+ // Special framework theme heuristic...
+ buf = generateFrameworkRedirections(buf, rt, themePackageName, redirPath);
+ }
+ // Generate redirections from the package XML.
+ buf = am->generateRedirections(buf, rt, themePackageName, resPackageName);
+
+ return writeRedirections(redirPath, buf);
+}
+
+void AssetManager::loadRedirectionMappings(ResTable* rt) const
+{
+ rt->clearRedirections();
+
+ if (mThemePackageName != NULL) {
+ const char* data = getenv("ANDROID_DATA");
+ LOG_ALWAYS_FATAL_IF(data == NULL, "ANDROID_DATA not set");
+
+ // Create the basic directory structure on demand.
+ struct stat statbuf;
+ String8 basePath(data);
+ basePath.appendPath(kThemeResCacheDir);
+ createDirIfNecessary(basePath.string(), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, &statbuf);
+ basePath.appendPath(mThemePackageName);
+ createDirIfNecessary(basePath.string(), S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, &statbuf);
+
+ String8 themeDirLockPath(basePath);
+ themeDirLockPath.append(".lck");
+
+ FileLock* themeDirLock = new FileLock(themeDirLockPath.string());
+ themeDirLock->lock();
+
+ // Load (generating if necessary) the cache files for each installed
+ // package in this ResTable, excluding the framework's "android"
+ // package.
+ bool hasFramework = false;
+ const size_t N = rt->getBasePackageCount();
+ for (size_t i=0; i<N; i++) {
+ uint32_t packageId = rt->getBasePackageId(i);
+
+ // No need to regenerate the 0x01 framework resources.
+ if (packageId == 0x7f) {
+ String8 redirPath(basePath);
+ const char16_t* resPackageName = rt->getBasePackageName(i);
+ redirPath.appendPath(String8(resPackageName));
+
+ if (lstat(redirPath.string(), &statbuf) != 0) {
+ generateAndWriteRedirections(rt, mThemePackageName,
+ resPackageName, redirPath.string(), false);
+ }
+
+ rt->addRedirections(packageId, redirPath.string());
+ } else if (packageId == 0x01) {
+ hasFramework = true;
+ }
+ }
+
+ // Handle the "android" package space as a special case using some
+ // fancy heuristics.
+ if (hasFramework) {
+ String8 frameworkRedirPath(basePath);
+ frameworkRedirPath.appendPath("android");
+
+ if (lstat(frameworkRedirPath.string(), &statbuf) != 0) {
+ generateAndWriteRedirections(rt, mThemePackageName,
+ String16("android").string(),
+ frameworkRedirPath.string(), true);
+ }
+
+ rt->addRedirections(0x01, frameworkRedirPath.string());
+ }
+
+ themeDirLock->unlock();
+ }
+}
+
const ResTable* AssetManager::getResTable(bool required) const
{
ResTable* rt = mResources;
@@ -401,6 +836,7 @@ const ResTable* AssetManager::getResTable(bool required) const
const asset_path& ap = mAssetPaths.itemAt(i);
updateResTableFromAssetPath(rt, ap, (void*)(i+1));
}
+ loadRedirectionMappings(rt);
}
if (required && !rt) LOGW("Unable to find resources file resources.arsc");
@@ -1768,14 +2204,34 @@ int AssetManager::ZipSet::getIndex(const String8& zip) const
return mZipPath.size()-1;
}
+/*
+ * Set the currently applied theme package name.
+ *
+ * This information is used when constructing the ResTable's resource
+ * redirection map.
+ */
+void AssetManager::setThemePackageName(const char* packageName)
+{
+ if (mThemePackageName != NULL) {
+ delete[] mThemePackageName;
+ }
+ mThemePackageName = strdupNew(packageName);
+}
+
+const char* AssetManager::getThemePackageName()
+{
+ return mThemePackageName;
+}
+
bool AssetManager::updateWithAssetPath(const String8& path, void** cookie)
{
bool res = addAssetPath(path, cookie);
ResTable* rt = mResources;
- if (res && rt != NULL && ((size_t)*cookie == mAssetPaths.size())) {
+ if (res && rt != NULL && ((size_t)*cookie == mAssetPaths.size())) {
AutoMutex _l(mLock);
const asset_path& ap = mAssetPaths.itemAt((size_t)*cookie - 1);
updateResTableFromAssetPath(rt, ap, *cookie);
+ loadRedirectionMappings(rt);
}
return res;
}
@@ -1809,6 +2265,7 @@ bool AssetManager::removeAssetPath(const String8 &packageName, const String8 &as
return false;
}
+ rt->clearRedirections();
rt->removeAssetsByCookie(packageName, (void *)cookie);
return true;
diff --git a/libs/utils/FileLock.cpp b/libs/utils/FileLock.cpp
new file mode 100644
index 0000000..c279b86
--- /dev/null
+++ b/libs/utils/FileLock.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2010, T-Mobile USA, Inc.
+ *
+ * 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.
+ */
+
+//
+// File locking utility
+//
+
+#define LOG_TAG "filelock"
+
+#include <utils/FileLock.h>
+#include <utils/Log.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/file.h>
+#include <errno.h>
+#include <assert.h>
+
+using namespace android;
+
+/*
+ * Constructor. Create an unlocked object.
+ */
+FileLock::FileLock(const char* fileName)
+ : mRefCount(0), mFd(-1), mFileName(strdup(fileName))
+{
+ assert(mFileName != NULL);
+}
+
+/*
+ * Destructor.
+ */
+FileLock::~FileLock(void)
+{
+ assert(mRefCount == 0);
+
+ if (mFileName != NULL) {
+ free(mFileName);
+ }
+ if (mFd >= 0) {
+ if (flock(mFd, LOCK_UN) != 0) {
+ LOGE("flock(%s,LOCK_UN) failed: %s\n", mFileName, strerror(errno));
+ }
+ if (close(mFd) != 0) {
+ LOGE("close(%s) failed: %s\n", mFileName, strerror(errno));
+ }
+ }
+}
+
+bool FileLock::doLock(int openFlags, mode_t fileCreateMode)
+{
+ int fd = open(mFileName, openFlags | O_CREAT, fileCreateMode);
+ if (fd == -1) {
+ return false;
+ }
+
+ if (flock(fd, LOCK_EX) != 0) {
+ LOGE("flock(%s,LOCK_EX) failed: %s\n", mFileName, strerror(errno));
+ close(fd);
+ return false;
+ }
+
+ mFd = fd;
+ return true;
+}
diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp
index e1ce1c2..319b8d9 100644
--- a/libs/utils/ResourceTypes.cpp
+++ b/libs/utils/ResourceTypes.cpp
@@ -26,6 +26,7 @@
#include <utils/String8.h>
#include <utils/TextOutput.h>
#include <utils/Log.h>
+#include <utils/misc.h>
#include <stdlib.h>
#include <string.h>
@@ -44,6 +45,7 @@
#define TABLE_SUPER_NOISY(x) //x
#define LOAD_TABLE_NOISY(x) //x
#define TABLE_THEME(x) //x
+#define REDIRECT_NOISY(x) //x
namespace android {
@@ -1411,6 +1413,13 @@ status_t ResTable::Theme::applyStyle(uint32_t resID, bool force)
const bag_entry* bag;
uint32_t bagTypeSpecFlags = 0;
mTable.lock();
+ uint32_t redirect = mTable.lookupRedirectionMap(resID);
+ if (redirect != 0 || resID == 0x01030005) {
+ REDIRECT_NOISY(LOGW("applyStyle: PERFORMED REDIRECT OF ident=0x%08x FOR redirect=0x%08x\n", resID, redirect));
+ }
+ if (redirect != 0) {
+ resID = redirect;
+ }
const ssize_t N = mTable.getBagLocked(resID, &bag, &bagTypeSpecFlags);
TABLE_NOISY(LOGV("Applying style 0x%08x to theme %p, count=%d", resID, this, N));
if (N < 0) {
@@ -1836,6 +1845,8 @@ void ResTable::uninit()
mPackageGroups.clear();
mHeaders.clear();
+
+ clearRedirections();
}
bool ResTable::getResourceName(uint32_t resID, resource_name* outName) const
@@ -2039,6 +2050,26 @@ ssize_t ResTable::resolveReference(Res_value* value, ssize_t blockIndex,
return blockIndex;
}
+uint32_t ResTable::lookupRedirectionMap(uint32_t resID) const
+{
+ if (mError != NO_ERROR) {
+ return 0;
+ }
+
+ const int p = Res_GETPACKAGE(resID)+1;
+ const int t = Res_GETTYPE(resID)+1;
+ const int e = Res_GETENTRY(resID);
+
+ const size_t N = mRedirectionMap.size();
+ for (size_t i=0; i<N; i++) {
+ PackageResMap* resMap = mRedirectionMap[i];
+ if (resMap->package == p) {
+ return resMap->lookup(t, e);
+ }
+ }
+ return 0;
+}
+
const char16_t* ResTable::valueToString(
const Res_value* value, size_t stringBlock,
char16_t tmpBuffer[TMP_BUFFER_SIZE], size_t* outLen)
@@ -2214,7 +2245,19 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag,
if (parent) {
const bag_entry* parentBag;
uint32_t parentTypeSpecFlags = 0;
- const ssize_t NP = getBagLocked(parent, &parentBag, &parentTypeSpecFlags);
+ uint32_t parentRedirect = lookupRedirectionMap(parent);
+ uint32_t parentActual = parent;
+ if (parentRedirect != 0 || parent == 0x01030005) {
+ if (parentRedirect == resID) {
+ REDIRECT_NOISY(LOGW("applyStyle(parent): ignoring circular redirect from parent=0x%08x to parentRedirect=0x%08x\n", parent, parentRedirect));
+ } else {
+ REDIRECT_NOISY(LOGW("applyStyle(parent): PERFORMED REDIRECT OF parent=0x%08x FOR parentRedirect=0x%08x\n", parent, parentRedirect));
+ if (parentRedirect != 0) {
+ parentActual = parentRedirect;
+ }
+ }
+ }
+ const ssize_t NP = getBagLocked(parentActual, &parentBag, &parentTypeSpecFlags);
const size_t NT = ((NP >= 0) ? NP : 0) + N;
set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*NT);
if (set == NULL) {
@@ -4053,6 +4096,260 @@ void ResTable::removeAssetsByCookie(const String8 &packageName, void* cookie)
}
}
+ResTable::PackageResMap::PackageResMap()
+ : mEntriesByType(NULL)
+{
+}
+
+ResTable::PackageResMap::~PackageResMap()
+{
+ if (mEntriesByType != NULL) {
+ SharedBuffer* buf = SharedBuffer::bufferFromData(mEntriesByType);
+ const size_t N = buf->size() / sizeof(mEntriesByType[0]);
+ for (size_t i = 0; i < N; i++) {
+ uint32_t* entries = mEntriesByType[i];
+ if (entries != NULL) {
+ SharedBuffer::bufferFromData(entries)->release();
+ }
+ }
+ buf->release();
+ }
+}
+
+uint32_t* ResTable::PackageResMap::parseMapType(const unsigned char* ptr, const unsigned char* end)
+{
+ SharedBuffer* buf = NULL;
+ for (; ptr + 6 <= end; ptr += 6) {
+ uint16_t entry = *(uint16_t*)ptr;
+ uint32_t resID = *(uint32_t*)(ptr + 2);
+ REDIRECT_NOISY(LOGW("map type got entry=0x%04x, resID=0x%08x\n", entry, resID));
+ size_t currentSize = (buf != NULL) ? buf->size() : 0;
+ size_t entrySize = (entry+1) * sizeof(resID);
+ if (entrySize > currentSize) {
+ unsigned int requestSize = roundUpPower2(entrySize);
+ REDIRECT_NOISY(LOGW("allocating buffer (%p) at requestSize=%d\n", buf, (int)requestSize));
+ if (buf == NULL) {
+ buf = SharedBuffer::alloc(requestSize);
+ } else {
+ buf = buf->editResize(requestSize);
+ }
+ memset((unsigned char*)buf->data()+currentSize, 0, requestSize - currentSize);
+ REDIRECT_NOISY(LOGW("allocated buf=%p\n", buf));
+ }
+ uint32_t* entries = (uint32_t*)buf->data();
+ entries[entry] = resID;
+ }
+ return (buf != NULL) ? (uint32_t*)buf->data() : NULL;
+}
+
+/*
+ * Parse the resource mappings file.
+ *
+ * The format of this file begins with a simple header identifying the number
+ * of types inside, followed by a table mapping each type to an offset further
+ * in the file. At each offset mentioned is a set of resource ID mappings to
+ * be parsed out and applied to a sparse array that is ultimately used to
+ * lookup resource redirections (the indices are entries in the associated
+ * package space and the values are the replacement resID's from the theme
+ * itself)
+ *
+ * Detailed information of each section:
+ *
+ * | bytes | description
+ * |-------+-----------------------
+ * | 2 | file format version (first version is 1)
+ * | 2 | number of resource types (think Res_GETTYPE) in the mapping
+ *
+ * For each resource type:
+ *
+ * +-------+-----------------------
+ * | 1 | type identifier
+ * | 4 | file offset containing the entry mapping
+ * | 4 | length of the entry mapping section for this type
+ * ...
+ *
+ * At each file offset mentioned in the type header:
+ *
+ * +-------+-----------------------
+ * | 2 | entry id to be replaced (combined with the type and package this
+ * | | forms a resID)
+ * | 4 | resID in the theme space which is to replace the previous entry
+ * ...
+ */
+bool ResTable::PackageResMap::parseMap(const unsigned char* basePtr,
+ const unsigned char* endPtr)
+{
+ LOG_FATAL_IF(mEntriesByType != NULL, "parseMap must only be called once");
+
+ if (basePtr + 4 > endPtr) {
+ return false;
+ }
+ uint16_t version = *(uint16_t*)basePtr;
+ uint16_t numTypes = *(uint16_t*)(basePtr + 2);
+ const unsigned char* headerPtr = basePtr + 4;
+
+ REDIRECT_NOISY(LOGW("file version=%d\n", version));
+ REDIRECT_NOISY(LOGW("read %d numTypes\n", numTypes));
+
+ while (numTypes-- > 0) {
+ uint8_t type;
+ uint32_t* entries = NULL;
+ if (headerPtr + 9 < endPtr) {
+ type = *(uint8_t*)headerPtr;
+ uint32_t offset = *(uint32_t*)(headerPtr + 1);
+ uint32_t length = *(uint32_t*)(headerPtr + 5);
+ headerPtr += 9;
+ REDIRECT_NOISY(LOGW("got type=0x%02x\n", type));
+ if (basePtr + offset + length <= endPtr) {
+ REDIRECT_NOISY(LOGW("parsing type...\n"));
+ const unsigned char* entryStartPtr = basePtr + offset;
+ const unsigned char* entryEndPtr = entryStartPtr + length;
+ entries = parseMapType(entryStartPtr, entryEndPtr);
+ }
+ }
+ if (entries == NULL) {
+ return false;
+ }
+ REDIRECT_NOISY(LOGW("inserting type 0x%02x with %p\n", type, entries));
+ insert(type, entries);
+ }
+
+ return true;
+}
+
+/*
+ * Load the redirection map from the supplied map path.
+ *
+ * The path is expected to be a directory containing individual map cache files
+ * for each package that is to have resources redirected. Only those packages
+ * that are included in this ResTable will be loaded into the redirection map.
+ * For this reason, this method should be called only after all resource
+ * bundles have been added to the table.
+ */
+status_t ResTable::addRedirections(int package, const char* cachePath)
+{
+ LOGV("Adding redirections for package 0x%02x at %s\n", package, cachePath);
+
+ if (package != 0x01 && package != 0x7f) {
+ REDIRECT_NOISY(LOGW("invalid package 0x%02x: should be either 0x01 (android) or 0x7f (application)\n", package));
+ return BAD_TYPE;
+ }
+
+ ResTable::PackageResMap* resMap = ResTable::PackageResMap::createFromCache(package, cachePath);
+ if (resMap != NULL) {
+ REDIRECT_NOISY(LOGW("loaded cache stuff for cachePath=%s (package=%d)\n", cachePath, package));
+ mRedirectionMap.add(resMap);
+ return NO_ERROR;
+ } else {
+ REDIRECT_NOISY(LOGW("failed to parse redirection path at %s\n", cachePath));
+ return BAD_TYPE;
+ }
+}
+
+void ResTable::clearRedirections()
+{
+ const size_t N = mRedirectionMap.size();
+ for (size_t i=0; i<N; i++) {
+ PackageResMap* resMap = mRedirectionMap[i];
+ delete resMap;
+ }
+ mRedirectionMap.clear();
+}
+
+ResTable::PackageResMap* ResTable::PackageResMap::createFromCache(int package, const char* cachePath)
+{
+ FILE* cacheFile = fopen(cachePath, "r");
+ if (cacheFile == NULL) {
+ REDIRECT_NOISY(LOGW("unable to open resource mapping path %s: %s\n", cachePath, strerror(errno)));
+ return NULL;
+ }
+
+ if (fseek(cacheFile, 0, SEEK_END) != 0) {
+ fclose(cacheFile);
+ return NULL;
+ }
+
+ long length = ftell(cacheFile);
+ REDIRECT_NOISY(LOGW("file length is %d\n", (int)length));
+ if (length < 4) {
+ fclose(cacheFile);
+ return NULL;
+ }
+
+ ResTable::PackageResMap* resMap = NULL;
+ FileMap* fileMap = new FileMap;
+ if (fileMap != NULL) {
+ if (fileMap->create(cachePath, fileno(cacheFile), 0, length, true)) {
+ REDIRECT_NOISY(LOGW("successfully mapped %s\n", cachePath));
+ resMap = new ResTable::PackageResMap();
+ if (resMap != NULL) {
+ resMap->package = package;
+ const unsigned char* ptr = (const unsigned char*)fileMap->getDataPtr();
+ if (!resMap->parseMap(ptr, ptr + length)) {
+ REDIRECT_NOISY(LOGW("failed to parse map!\n"));
+ delete resMap;
+ resMap = NULL;
+ }
+ }
+ } else {
+ REDIRECT_NOISY(LOGW("unable to map '%s': %s\n", cachePath, strerror(errno)));
+ }
+
+ fileMap->release();
+ }
+
+ fclose(cacheFile);
+
+ return resMap;
+}
+
+uint32_t ResTable::PackageResMap::lookup(int type, int entry)
+{
+ if (mEntriesByType == NULL) {
+ return 0;
+ }
+ size_t maxTypes = SharedBuffer::bufferFromData(mEntriesByType)->size() /
+ sizeof(mEntriesByType[0]);
+ if (type < 0 || type >= maxTypes) {
+ return 0;
+ }
+ uint32_t* entries = mEntriesByType[type];
+ if (entries == NULL) {
+ return 0;
+ }
+ size_t maxEntries = SharedBuffer::bufferFromData(entries)->size() /
+ sizeof(entries[0]);
+ if (entry < 0 || entry >= maxEntries) {
+ return 0;
+ }
+ return entries[entry];
+}
+
+void ResTable::PackageResMap::insert(int type, const uint32_t* entries)
+{
+ SharedBuffer* buf = NULL;
+ size_t currentSize = 0;
+ if (mEntriesByType != NULL) {
+ buf = SharedBuffer::bufferFromData(mEntriesByType);
+ currentSize = buf->size();
+ }
+ size_t typeSize = (type+1) * sizeof(uint32_t*);
+ if (typeSize > currentSize) {
+ unsigned int requestSize = roundUpPower2(typeSize);
+ REDIRECT_NOISY(LOGW("allocating new type buffer (%p) at size requestSize=%d\n", buf, requestSize));
+ if (buf == NULL) {
+ buf = SharedBuffer::alloc(requestSize);
+ } else {
+ buf = buf->editResize(requestSize);
+ }
+ memset((unsigned char*)buf->data()+currentSize, 0, requestSize - currentSize);
+ REDIRECT_NOISY(LOGW("allocated new type buffer %p\n", buf));
+ }
+ uint32_t** entriesByType = (uint32_t**)buf->data();
+ entriesByType[type] = (uint32_t*)entries;
+ mEntriesByType = entriesByType;
+}
+
#define CHAR16_TO_CSTR(c16, len) (String8(String16(c16,len)).string())
#ifndef HAVE_ANDROID_OS