diff options
22 files changed, 1287 insertions, 1012 deletions
@@ -153,6 +153,7 @@ LOCAL_SRC_FILES += \ core/java/com/android/internal/app/IBatteryStats.aidl \ core/java/com/android/internal/app/IUsageStats.aidl \ core/java/com/android/internal/app/IMediaContainerService.aidl \ + core/java/com/android/internal/app/IAssetRedirectionManager.aidl \ core/java/com/android/internal/appwidget/IAppWidgetService.aidl \ core/java/com/android/internal/appwidget/IAppWidgetHost.aidl \ core/java/com/android/internal/backup/IBackupTransport.aidl \ diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 6955f41..650321e 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -17,6 +17,7 @@ package android.app; +import com.android.internal.app.IAssetRedirectionManager; import com.android.internal.os.BinderInternal; import com.android.internal.os.RuntimeInit; import com.android.internal.os.SamplingProfilerIntegration; @@ -41,11 +42,11 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.ProviderInfo; import android.content.pm.ServiceInfo; -import android.content.pm.ThemeInfo; import android.content.res.AssetManager; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.content.res.CustomTheme; +import android.content.res.PackageRedirectionMap; import android.content.res.Resources; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDebug; @@ -138,6 +139,7 @@ public final class ActivityThread { static ContextImpl mSystemContext = null; static IPackageManager sPackageManager; + static IAssetRedirectionManager sAssetRedirectionManager; final ApplicationThread mAppThread = new ApplicationThread(); final Looper mLooper = Looper.myLooper(); @@ -1145,6 +1147,18 @@ public final class ActivityThread { return sPackageManager; } + // NOTE: this method can return null if the SystemServer is still + // initializing (for example, of another SystemServer component is accessing + // a resources object) + public static IAssetRedirectionManager getAssetRedirectionManager() { + if (sAssetRedirectionManager != null) { + return sAssetRedirectionManager; + } + IBinder b = ServiceManager.getService("assetredirection"); + sAssetRedirectionManager = IAssetRedirectionManager.Stub.asInterface(b); + return sAssetRedirectionManager; + } + DisplayMetrics getDisplayMetricsLocked(boolean forceUpdate) { if (mDisplayMetrics != null && !forceUpdate) { return mDisplayMetrics; @@ -1236,7 +1250,18 @@ public final class ActivityThread { return r; } } - + + private void detachThemeAssets(AssetManager assets) { + String themePackageName = assets.getThemePackageName(); + int themeCookie = assets.getThemeCookie(); + if (!TextUtils.isEmpty(themePackageName) && themeCookie != 0) { + assets.removeAssetPath(themePackageName, themeCookie); + assets.setThemePackageName(null); + assets.setThemeCookie(0); + assets.clearRedirections(); + } + } + /** * Attach the necessary theme asset paths and meta information to convert an * AssetManager to being globally "theme-aware". @@ -1252,21 +1277,16 @@ public final class ActivityThread { * the framework default. */ private boolean attachThemeAssets(AssetManager assets, CustomTheme theme, boolean updating) { + IAssetRedirectionManager rm = getAssetRedirectionManager(); + if (rm == null) { + return false; + } PackageInfo pi = null; try { pi = getPackageManager().getPackageInfo(theme.getThemePackageName(), 0); } catch (RemoteException e) { } if (pi != null && pi.applicationInfo != null && pi.themeInfos != null) { - /* - * It's important that this is called before - * updateResourcesWithAssetPath as it depends on the result of - * getThemePackageName to figure out what to do with the resource - * redirection table. - */ - assets.setThemePackageInfo(theme.getThemePackageName(), - findThemeResourceId(pi.themeInfos, theme)); - String themeResDir = pi.applicationInfo.publicSourceDir; int cookie; if (updating) { @@ -1275,44 +1295,47 @@ public final class ActivityThread { cookie = assets.addAssetPath(themeResDir); } if (cookie != 0) { + String themePackageName = theme.getThemePackageName(); + String themeId = theme.getThemeId(); + int N = assets.getBasePackageCount(); + for (int i = 0; i < N; i++) { + String packageName = assets.getBasePackageName(i); + int packageId = assets.getBasePackageId(i); + + /* + * For now, we only consider redirections coming from the + * framework or regular android packages. This excludes + * themes and other specialty APKs we are not aware of. + */ + if (packageId != 0x01 && packageId != 0x7f) { + continue; + } + + try { + PackageRedirectionMap map = rm.getPackageRedirectionMap(themePackageName, themeId, + packageName); + if (map != null) { + assets.addRedirections(map); + } + } catch (RemoteException e) { + Log.e(TAG, "Failure accessing package redirection map, removing theme support."); + assets.removeAssetPath(themePackageName, cookie); + return false; + } + } + + assets.setThemePackageName(theme.getThemePackageName()); assets.setThemeCookie(cookie); return true; } else { Log.e(TAG, "Unable to " + (updating ? "update" : "add") + " theme assets at " + themeResDir); - - /* Roll back the theme package info. */ - assets.setThemePackageInfo(null, 0); } } return false; } /** - * Searches for the high-level theme resource id for the specific - * <theme> tag being applied. - * <p> - * An individual theme package can contain multiple <theme> tags, each - * representing a separate theme choice from the user's perspective, even - * though the most common case is for there to be only 1. - * - * @return The style resource id or 0 if no match was found. - */ - private int findThemeResourceId(ThemeInfo[] themeInfos, CustomTheme theme) { - String needle = theme.getThemeId(); - if (themeInfos != null && !TextUtils.isEmpty(needle)) { - int n = themeInfos.length; - for (int i = 0; i < n; i++) { - ThemeInfo info = themeInfos[i]; - if (needle.equals(info.themeId)) { - return info.styleResourceId; - } - } - } - return 0; - } - - /** * Creates the top level resources for the given package. */ Resources getTopLevelResources(String resDir, LoadedApk pkgInfo) { @@ -3060,20 +3083,8 @@ public final class ActivityThread { boolean themeChanged = (changes & ActivityInfo.CONFIG_THEME_RESOURCE) != 0; if (themeChanged) { AssetManager am = r.getAssets(); - /* - * Dynamically modify the AssetManager object to - * replace the old asset path with the new one. This - * is made possibly by native layer changes made by - * T-Mobile. - */ if (am.hasThemeSupport()) { - String oldThemePackage = am.getThemePackageName(); - int themeCookie = am.getThemeCookie(); - if (!TextUtils.isEmpty(oldThemePackage) && themeCookie != 0) { - am.setThemePackageInfo(null, 0); - am.removeAssetPath(oldThemePackage, themeCookie); - am.setThemeCookie(0); - } + detachThemeAssets(am); if (!TextUtils.isEmpty(config.customTheme.getThemePackageName())) { attachThemeAssets(am, config.customTheme, true); } diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index 5a7734c..0b8c3ab 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -20,6 +20,7 @@ package android.content.res; import android.os.ParcelFileDescriptor; import android.util.Config; import android.util.Log; +import android.util.SparseArray; import android.util.TypedValue; import java.io.FileNotFoundException; @@ -83,9 +84,17 @@ public final class AssetManager { private String mAppName; private boolean mThemeSupport; + private String mThemePackageName; private int mThemeCookie; /** + * Organize all added redirection maps using Java strong references to keep + * the native layer cleanup simple (that is, finalize() in Java will be + * responsible for delete in C++). + */ + private SparseArray<PackageRedirectionMap> mRedirections; + + /** * Create a new AssetManager containing only the basic system assets. * Applications will not generally use this method, instead retrieving the * appropriate asset manager with {@link Resources#getAssets}. Not for @@ -692,16 +701,30 @@ public final class AssetManager { } /** + * Apply a heuristic to match-up all attributes from the source style with + * attributes in the destination style. For each match, an entry in the + * package redirection map will be inserted. + * + * {@hide} + */ + public native final boolean generateStyleRedirections(int resMapNative, int sourceStyle, + int destStyle); + + /** * Get package name of current theme (may return null). * {@hide} */ - public native final String getThemePackageName(); + public String getThemePackageName() { + return mThemePackageName; + } /** * Sets package name and highest level style id for current theme (null, 0 is allowed). * {@hide} */ - public native final void setThemePackageInfo(String packageName, int styleId); + public void setThemePackageName(String packageName) { + mThemePackageName = packageName; + } /** * Get asset cookie for current theme (may return 0). @@ -720,6 +743,30 @@ public final class AssetManager { } /** + * Add a redirection map to the asset manager. All future resource lookups + * will consult this map. + * {@hide} + */ + public void addRedirections(PackageRedirectionMap map) { + if (mRedirections == null) { + mRedirections = new SparseArray<PackageRedirectionMap>(2); + } + mRedirections.append(map.getPackageId(), map); + addRedirectionsNative(map.getNativePointer()); + } + + /** + * Clear redirection map for the asset manager. + * {@hide} + */ + public void clearRedirections() { + if (mRedirections != null) { + mRedirections.clear(); + } + clearRedirectionsNative(); + } + + /** * Determine whether the state in this asset manager is up-to-date with * the files on the filesystem. If false is returned, you need to * instantiate a new AssetManager class to see the new data. @@ -837,6 +884,24 @@ public final class AssetManager { private native final int splitThemePackage(String srcFileName, String dstFileName, String [] drmProtectedAssetNames); + /** + * {@hide} + */ + public native final int getBasePackageCount(); + + /** + * {@hide} + */ + public native final String getBasePackageName(int index); + + /** + * {@hide} + */ + public native final int getBasePackageId(int index); + + private native final void addRedirectionsNative(int redirectionMapNativePointer); + private native final void clearRedirectionsNative(); + private native final void init(); private native final void destroy(); diff --git a/core/java/android/content/res/PackageRedirectionMap.aidl b/core/java/android/content/res/PackageRedirectionMap.aidl new file mode 100644 index 0000000..4f47525 --- /dev/null +++ b/core/java/android/content/res/PackageRedirectionMap.aidl @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2011, 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. + */ + +package android.content.res; + +/** + * @hide + */ +parcelable PackageRedirectionMap; diff --git a/core/java/android/content/res/PackageRedirectionMap.java b/core/java/android/content/res/PackageRedirectionMap.java new file mode 100644 index 0000000..55c4282 --- /dev/null +++ b/core/java/android/content/res/PackageRedirectionMap.java @@ -0,0 +1,90 @@ +package android.content.res; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Native transport for package asset redirection information coming from the + * AssetRedirectionManagerService. + * + * @hide + */ +public class PackageRedirectionMap implements Parcelable { + private final int mNativePointer; + + public static final Parcelable.Creator<PackageRedirectionMap> CREATOR + = new Parcelable.Creator<PackageRedirectionMap>() { + public PackageRedirectionMap createFromParcel(Parcel in) { + return new PackageRedirectionMap(in); + } + + public PackageRedirectionMap[] newArray(int size) { + return new PackageRedirectionMap[size]; + } + }; + + public PackageRedirectionMap() { + this(nativeConstructor()); + } + + private PackageRedirectionMap(Parcel in) { + this(nativeCreateFromParcel(in)); + } + + private PackageRedirectionMap(int nativePointer) { + if (nativePointer == 0) { + throw new RuntimeException(); + } + mNativePointer = nativePointer; + } + + @Override + protected void finalize() throws Throwable { + nativeDestructor(mNativePointer); + } + + public int getNativePointer() { + return mNativePointer; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + if (!nativeWriteToParcel(mNativePointer, dest)) { + throw new RuntimeException(); + } + } + + public int getPackageId() { + return nativeGetPackageId(mNativePointer); + } + + public void addRedirection(int fromIdent, int toIdent) { + nativeAddRedirection(mNativePointer, fromIdent, toIdent); + } + + // Used for debugging purposes only. + public int[] getRedirectionKeys() { + return nativeGetRedirectionKeys(mNativePointer); + } + + // Used for debugging purposes only. + public int lookupRedirection(int fromIdent) { + return nativeLookupRedirection(mNativePointer, fromIdent); + } + + private static native int nativeConstructor(); + private static native void nativeDestructor(int nativePointer); + + private static native int nativeCreateFromParcel(Parcel p); + private static native boolean nativeWriteToParcel(int nativePointer, Parcel p); + + private native void nativeAddRedirection(int nativePointer, int fromIdent, int toIdent); + private native int nativeGetPackageId(int nativePointer); + private native int[] nativeGetRedirectionKeys(int nativePointer); + private native int nativeLookupRedirection(int nativePointer, int fromIdent); +} diff --git a/core/java/com/android/internal/app/IAssetRedirectionManager.aidl b/core/java/com/android/internal/app/IAssetRedirectionManager.aidl new file mode 100644 index 0000000..8b47f0b --- /dev/null +++ b/core/java/com/android/internal/app/IAssetRedirectionManager.aidl @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2011, 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. + */ + +package com.android.internal.app; + +import android.content.res.PackageRedirectionMap; + +/** + * Interface used to interact with the AssetRedirectionManagerService. + */ +interface IAssetRedirectionManager { + /** + * Access the package redirection map for the supplied package name given a + * particular theme. + */ + PackageRedirectionMap getPackageRedirectionMap(in String themePackageName, + String themeId, in String targetPackageName); + + /** + * Clear all redirection maps for the given theme. + */ + void clearRedirectionMapsByTheme(in String themePackageName, + in String themeId); + + /** + * Clear all redirection maps for the given target package. + */ + void clearPackageRedirectionMap(in String targetPackageName); +} diff --git a/core/jni/Android.mk b/core/jni/Android.mk index c3f393d..69cb928 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -80,6 +80,7 @@ LOCAL_SRC_FILES:= \ android_util_Process.cpp \ android_util_StringBlock.cpp \ android_util_XmlBlock.cpp \ + android_util_PackageRedirectionMap.cpp \ android/graphics/AutoDecodeCancel.cpp \ android/graphics/Bitmap.cpp \ android/graphics/BitmapFactory.cpp \ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 648d93f..b22e611 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -170,6 +170,7 @@ extern int register_android_view_KeyEvent(JNIEnv* env); extern int register_android_view_MotionEvent(JNIEnv* env); extern int register_android_content_res_ObbScanner(JNIEnv* env); extern int register_android_content_res_Configuration(JNIEnv* env); +extern int register_android_content_res_PackageRedirectionMap(JNIEnv* env); static AndroidRuntime* gCurRuntime = NULL; @@ -1289,6 +1290,8 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_content_res_ObbScanner), REG_JNI(register_android_content_res_Configuration), + + REG_JNI(register_android_content_res_PackageRedirectionMap), }; /* diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index c9f0ca7..b9ceeb7 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -33,6 +33,7 @@ #include <utils/Asset.h> #include <utils/AssetManager.h> #include <utils/ResourceTypes.h> +#include <utils/PackageRedirectionMap.h> #include <utils/ZipFile.h> #include <stdio.h> @@ -1802,36 +1803,128 @@ static jint android_content_AssetManager_getGlobalAssetManagerCount(JNIEnv* env, return AssetManager::getGlobalCount(); } -static void android_content_AssetManager_setThemePackageInfo(JNIEnv* env, jobject clazz, - jstring packageName, jint styleId) +static jint android_content_AssetManager_getBasePackageCount(JNIEnv* env, jobject clazz) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return JNI_FALSE; + } + + return am->getResources().getBasePackageCount(); +} + +static jstring android_content_AssetManager_getBasePackageName(JNIEnv* env, jobject clazz, jint index) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return JNI_FALSE; + } + + String16 packageName(am->getResources().getBasePackageName(index)); + return env->NewString((const jchar*)packageName.string(), packageName.size()); +} + +static jint android_content_AssetManager_getBasePackageId(JNIEnv* env, jobject clazz, jint index) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return JNI_FALSE; + } + + return am->getResources().getBasePackageId(index); +} + +static void android_content_AssetManager_addRedirectionsNative(JNIEnv* env, jobject clazz, + PackageRedirectionMap* resMap) { AssetManager* am = assetManagerForJavaObject(env, clazz); if (am == NULL) { return; } - if (packageName != NULL) { - const char* packageName8 = env->GetStringUTFChars(packageName, NULL); - am->setThemePackageInfo(packageName8, styleId); - env->ReleaseStringUTFChars(packageName, packageName8); - } else { - am->setThemePackageInfo(NULL, styleId); + am->addRedirections(resMap); +} + +static void android_content_AssetManager_clearRedirectionsNative(JNIEnv* env, jobject clazz) +{ + AssetManager* am = assetManagerForJavaObject(env, clazz); + if (am == NULL) { + return; } + + am->clearRedirections(); } -static jstring android_content_AssetManager_getThemePackageName(JNIEnv* env, jobject clazz) +static jboolean android_content_AssetManager_generateStyleRedirections(JNIEnv* env, jobject clazz, + PackageRedirectionMap* resMap, jint sourceStyle, jint destStyle) { AssetManager* am = assetManagerForJavaObject(env, clazz); if (am == NULL) { - return NULL; + return JNI_FALSE; } - const char* packageName = am->getThemePackageName(); - if (packageName == NULL) { - return NULL; + const ResTable& res(am->getResources()); + + res.lock(); + + // Load up a bag for the user-supplied theme. + const ResTable::bag_entry* themeEnt = NULL; + ssize_t N = res.getBagLocked(destStyle, &themeEnt); + const ResTable::bag_entry* endThemeEnt = themeEnt + (N >= 0 ? N : 0); + + // ...and a bag for the framework default. + const ResTable::bag_entry* frameworkEnt = NULL; + N = res.getBagLocked(sourceStyle, &frameworkEnt); + const ResTable::bag_entry* endFrameworkEnt = frameworkEnt + (N >= 0 ? N : 0); + + // Add the source => dest style redirection first. + jboolean ret = JNI_FALSE; + if (themeEnt < endThemeEnt && frameworkEnt < endFrameworkEnt) { + resMap->addRedirection(sourceStyle, destStyle); + ret = JNI_TRUE; + } + + // 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 0x%08x and 0x%08x:\n", sourceStyle, destStyle)); + 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) { + uint32_t fromIdent = frameworkEnt->map.value.data; + uint32_t toIdent = themeEnt->map.value.data; + REDIRECT_NOISY(LOGW(" generated mapping from 0x%08x => 0x%08x (by attr 0x%08x)\n", + fromIdent, toIdent, curIdent)); + resMap->addRedirection(fromIdent, toIdent); + } + themeEnt++; + } + + // Exhausted the theme, bail early. + if (themeEnt >= endThemeEnt) { + break; + } } - return env->NewStringUTF(packageName); + res.unlock(); + + return ret; } static jboolean android_content_AssetManager_removeAssetPath(JNIEnv* env, jobject clazz, @@ -2001,16 +2094,24 @@ static JNINativeMethod gAssetManagerMethods[] = { (void*) android_content_AssetManager_splitThemePackage }, // Dynamic theme package support. - { "setThemePackageInfo", "(Ljava/lang/String;I)V", - (void*) android_content_AssetManager_setThemePackageInfo }, - { "getThemePackageName", "()Ljava/lang/String;", - (void*) android_content_AssetManager_getThemePackageName }, { "removeAssetPath", "(Ljava/lang/String;I)Z", (void*) android_content_AssetManager_removeAssetPath }, { "updateResourcesWithAssetPath", "(Ljava/lang/String;)I", (void*) android_content_AssetManager_updateResourcesWithAssetPath }, { "dumpResources", "()V", (void*) android_content_AssetManager_dumpRes }, + { "getBasePackageCount", "()I", + (void*) android_content_AssetManager_getBasePackageCount }, + { "getBasePackageName", "(I)Ljava/lang/String;", + (void*) android_content_AssetManager_getBasePackageName }, + { "getBasePackageId", "(I)I", + (void*) android_content_AssetManager_getBasePackageId }, + { "addRedirectionsNative", "(I)V", + (void*) android_content_AssetManager_addRedirectionsNative }, + { "clearRedirectionsNative", "()V", + (void*) android_content_AssetManager_clearRedirectionsNative }, + { "generateStyleRedirections", "(III)Z", + (void*) android_content_AssetManager_generateStyleRedirections }, }; int register_android_content_AssetManager(JNIEnv* env) diff --git a/core/jni/android_util_PackageRedirectionMap.cpp b/core/jni/android_util_PackageRedirectionMap.cpp new file mode 100644 index 0000000..2391edb --- /dev/null +++ b/core/jni/android_util_PackageRedirectionMap.cpp @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2011, 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. + */ + +#include <utils/PackageRedirectionMap.h> + +#include "jni.h" +#include "JNIHelp.h" +#include <utils/misc.h> +#include <android_runtime/AndroidRuntime.h> + +#include "android_util_Binder.h" +#include <binder/Parcel.h> + +#include <utils/ResourceTypes.h> + +#include <stdio.h> + +namespace android { + +// ---------------------------------------------------------------------------- + +static PackageRedirectionMap* PackageRedirectionMap_constructor(JNIEnv* env, jobject clazz) +{ + return new PackageRedirectionMap; +} + +static void PackageRedirectionMap_destructor(JNIEnv* env, jobject clazz, + PackageRedirectionMap* resMap) +{ + delete resMap; +} + +static PackageRedirectionMap* PackageRedirectionMap_createFromParcel(JNIEnv* env, jobject clazz, + jobject parcel) +{ + if (parcel == NULL) { + return NULL; + } + + Parcel* p = parcelForJavaObject(env, parcel); + PackageRedirectionMap* resMap = new PackageRedirectionMap; + + int32_t entryCount = p->readInt32(); + while (entryCount-- > 0) { + uint32_t fromIdent = (uint32_t)p->readInt32(); + uint32_t toIdent = (uint32_t)p->readInt32(); + resMap->addRedirection(fromIdent, toIdent); + } + + return resMap; +} + +static jboolean PackageRedirectionMap_writeToParcel(JNIEnv* env, jobject clazz, + PackageRedirectionMap* resMap, jobject parcel) +{ + if (parcel == NULL) { + return JNI_FALSE; + } + + Parcel* p = parcelForJavaObject(env, parcel); + + int package = resMap->getPackage(); + size_t nTypes = resMap->getNumberOfTypes(); + size_t entryCount = 0; + for (size_t type=0; type<nTypes; type++) { + entryCount += resMap->getNumberOfUsedEntries(type); + } + p->writeInt32(entryCount); + for (size_t type=0; type<nTypes; type++) { + size_t nEntries = resMap->getNumberOfEntries(type); + for (size_t entry=0; entry<nEntries; entry++) { + uint32_t toIdent = resMap->getEntry(type, entry); + if (toIdent != 0) { + uint32_t fromIdent = Res_MAKEID(package-1, type, entry); + p->writeInt32(fromIdent); + p->writeInt32(toIdent); + } + } + } + + return JNI_TRUE; +} + +static void PackageRedirectionMap_addRedirection(JNIEnv* env, jobject clazz, + PackageRedirectionMap* resMap, jint fromIdent, jint toIdent) +{ + resMap->addRedirection(fromIdent, toIdent); +} + +static jint PackageRedirectionMap_getPackageId(JNIEnv* env, jobject clazz, + PackageRedirectionMap* resMap) +{ + return resMap->getPackage(); +} + +static jint PackageRedirectionMap_lookupRedirection(JNIEnv* env, jobject clazz, + PackageRedirectionMap* resMap, jint fromIdent) +{ + return resMap->lookupRedirection(fromIdent); +} + +static jintArray PackageRedirectionMap_getRedirectionKeys(JNIEnv* env, jobject clazz, + PackageRedirectionMap* resMap) +{ + int package = resMap->getPackage(); + size_t nTypes = resMap->getNumberOfTypes(); + size_t entryCount = 0; + for (size_t type=0; type<nTypes; type++) { + size_t usedEntries = resMap->getNumberOfUsedEntries(type); + entryCount += usedEntries; + } + jintArray array = env->NewIntArray(entryCount); + if (array == NULL) { + jniThrowException(env, "java/lang/OutOfMemoryError", ""); + return NULL; + } + jsize index = 0; + for (size_t type=0; type<nTypes; type++) { + size_t nEntries = resMap->getNumberOfEntries(type); + for (size_t entry=0; entry<nEntries; entry++) { + uint32_t toIdent = resMap->getEntry(type, entry); + if (toIdent != 0) { + jint fromIdent = (jint)Res_MAKEID(package-1, type, entry); + env->SetIntArrayRegion(array, index++, 1, &fromIdent); + } + } + } + return array; +} + +// ---------------------------------------------------------------------------- + +/* + * JNI registration. + */ +static JNINativeMethod gPackageRedirectionMapMethods[] = { + { "nativeConstructor", "()I", + (void*) PackageRedirectionMap_constructor }, + { "nativeDestructor", "(I)V", + (void*) PackageRedirectionMap_destructor }, + { "nativeCreateFromParcel", "(Landroid/os/Parcel;)I", + (void*) PackageRedirectionMap_createFromParcel }, + { "nativeWriteToParcel", "(ILandroid/os/Parcel;)Z", + (void*) PackageRedirectionMap_writeToParcel }, + { "nativeAddRedirection", "(III)V", + (void*) PackageRedirectionMap_addRedirection }, + { "nativeGetPackageId", "(I)I", + (void*) PackageRedirectionMap_getPackageId }, + { "nativeLookupRedirection", "(II)I", + (void*) PackageRedirectionMap_lookupRedirection }, + { "nativeGetRedirectionKeys", "(I)[I", + (void*) PackageRedirectionMap_getRedirectionKeys }, +}; + +int register_android_content_res_PackageRedirectionMap(JNIEnv* env) +{ + return AndroidRuntime::registerNativeMethods(env, + "android/content/res/PackageRedirectionMap", + gPackageRedirectionMapMethods, + NELEM(gPackageRedirectionMapMethods)); +} + +}; // namespace android diff --git a/include/utils/AssetManager.h b/include/utils/AssetManager.h index 01179ed..f374cae 100644 --- a/include/utils/AssetManager.h +++ b/include/utils/AssetManager.h @@ -23,6 +23,7 @@ #include <utils/Asset.h> #include <utils/AssetDir.h> +#include <utils/PackageRedirectionMap.h> #include <utils/KeyedVector.h> #include <utils/String8.h> #include <utils/Vector.h> @@ -218,9 +219,6 @@ public: */ void getLocales(Vector<String8>* locales) const; - void setThemePackageInfo(const char* packageName, uint32_t styleId); - const char* getThemePackageName(); - /* * Remove existing source for assets. * @@ -231,6 +229,8 @@ public: bool removeAssetPath(const String8 &packageName, void *cookie); bool updateWithAssetPath(const String8& path, void** cookie); void dumpRes(); + void addRedirections(PackageRedirectionMap* resMap); + void clearRedirections(); private: struct asset_path @@ -239,12 +239,6 @@ private: FileType type; }; - SharedBuffer* generateRedirections(SharedBuffer* entriesByTypeBuf, ResTable* rt, - const char* themePackageName, const char16_t* resPackageName); - bool generateAndWriteRedirections(ResTable* rt, const char* themePackageName, - uint32_t themeStyleId, 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); @@ -362,11 +356,6 @@ 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; - uint32_t mThemeStyleId; - mutable ResTable* mResources; ResTable_config* mConfig; diff --git a/include/utils/FileLock.h b/include/utils/FileLock.h deleted file mode 100644 index 2253cc2..0000000 --- a/include/utils/FileLock.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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_RDWR, 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/PackageRedirectionMap.h b/include/utils/PackageRedirectionMap.h new file mode 100644 index 0000000..9e6435b --- /dev/null +++ b/include/utils/PackageRedirectionMap.h @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2005 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 ANDROID_PACKAGEREDIRECTIONMAP_H +#define ANDROID_PACKAGEREDIRECTIONMAP_H + +#include <binder/Parcel.h> + +// --------------------------------------------------------------------------- + +namespace android { + +class PackageRedirectionMap +{ +public: + PackageRedirectionMap(); + ~PackageRedirectionMap(); + + bool addRedirection(uint32_t fromIdent, uint32_t toIdent); + uint32_t lookupRedirection(uint32_t fromIdent); + + // If there are no redirections present in this map, this method will + // return -1. + int getPackage(); + + // Usage of the following methods is intended to be used only by the JNI + // methods for the purpose of parceling. + size_t getNumberOfTypes(); + size_t getNumberOfUsedTypes(); + + size_t getNumberOfEntries(int type); + size_t getNumberOfUsedEntries(int type); + + // Similar to lookupRedirection, but with no sanity checking. + uint32_t getEntry(int type, int entry); + +private: + int mPackage; + + /* + * Sparse array organized into two layers: first by type, then by entry. + * The result of each lookup will be a qualified resource ID in the theme + * package scope. + * + * Underneath each layer is a SharedBuffer which + * indicates the array size. + */ + uint32_t** mEntriesByType; +}; + +} // namespace android + +// --------------------------------------------------------------------------- + +#endif // ANDROID_PACKAGEREDIRECTIONMAP_H diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index abdc00b..bae6b92 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -24,6 +24,7 @@ #include <utils/Asset.h> #include <utils/ByteOrder.h> #include <utils/Errors.h> +#include <utils/PackageRedirectionMap.h> #include <utils/String16.h> #include <utils/Vector.h> @@ -1741,7 +1742,7 @@ public: bool copyData=false); status_t add(ResTable* src); - status_t addRedirections(int package, const char* dataPath); + void addRedirections(PackageRedirectionMap* resMap); void clearRedirections(); status_t getError() const; @@ -2031,43 +2032,10 @@ private: // 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 + // one). Resources requested which are found in this map will be // automatically redirected to the appropriate themed value. - Vector<PackageResMap*> mRedirectionMap; + Vector<PackageRedirectionMap*> mRedirectionMap; }; } // namespace android diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index d077fec..2726cf2 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -25,10 +25,10 @@ commonSources:= \ CallStack.cpp \ Debug.cpp \ FileMap.cpp \ - FileLock.cpp \ Flattenable.cpp \ ObbFile.cpp \ Pool.cpp \ + PackageRedirectionMap.cpp \ RefBase.cpp \ ResourceTypes.cpp \ SharedBuffer.cpp \ diff --git a/libs/utils/AssetManager.cpp b/libs/utils/AssetManager.cpp index 28dc6c0..4513a06 100644 --- a/libs/utils/AssetManager.cpp +++ b/libs/utils/AssetManager.cpp @@ -33,7 +33,6 @@ #include <utils/Log.h> #include <utils/Timers.h> #include <utils/threads.h> -#include <utils/FileLock.h> #include <dirent.h> #include <sys/stat.h> @@ -41,8 +40,6 @@ #include <errno.h> #include <assert.h> -#define REDIRECT_NOISY(x) //x - using namespace android; /* @@ -77,7 +74,6 @@ int32_t AssetManager::getGlobalCount() AssetManager::AssetManager(CacheMode cacheMode) : mLocale(NULL), mVendor(NULL), - mThemePackageName(NULL), mResources(NULL), mConfig(new ResTable_config), mCacheMode(cacheMode), mCacheValid(false) { @@ -97,10 +93,6 @@ 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) @@ -438,365 +430,6 @@ static SharedBuffer* addToEntriesByTypeBuffer(SharedBuffer* buf, uint32_t from, 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, uint32_t styleId, const char* redirPath) -{ - REDIRECT_NOISY(LOGW("generateFrameworkRedirections: themePackageName=%s, styleId=0x%08x\n", themePackageName, styleId)); - - rt->lock(); - - // Load up a bag for the user-supplied theme. - const ResTable::bag_entry* themeEnt = NULL; - ssize_t N = rt->getBagLocked(styleId, &themeEnt); - const ResTable::bag_entry* endThemeEnt = themeEnt + (N >= 0 ? N : 0); - - // ...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 >= 0 ? N : 0); - - // The first entry should be for the theme itself. - entriesByTypeBuf = addToEntriesByTypeBuffer(entriesByTypeBuf, 0x01030005, styleId); - - // 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", styleId)); - 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, uint32_t themeStyleId, - 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 && themeStyleId != 0) { - // Special framework theme heuristic... - buf = generateFrameworkRedirections(buf, rt, themePackageName, - themeStyleId, 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(), 0777, &statbuf); - basePath.appendPath(mThemePackageName); - createDirIfNecessary(basePath.string(), 0777, &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, - mThemeStyleId, 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, - mThemeStyleId, String16("android").string(), - frameworkRedirPath.string(), true); - } - - rt->addRedirections(0x01, frameworkRedirPath.string()); - } - - themeDirLock->unlock(); - } -} - const ResTable* AssetManager::getResTable(bool required) const { ResTable* rt = mResources; @@ -827,7 +460,6 @@ 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"); @@ -2195,28 +1827,6 @@ int AssetManager::ZipSet::getIndex(const String8& zip) const return mZipPath.size()-1; } -/* - * Set the currently applied theme package name and the high-level theme style - * identifier (the one to replace @android:style/Theme). May be set to NULL, 0 - * to indicate that this AssetManager does not have an added theme package. - * - * This information is used when constructing the ResTable's resource - * redirection map. - */ -void AssetManager::setThemePackageInfo(const char* packageName, uint32_t styleId) -{ - if (mThemePackageName != NULL) { - delete[] mThemePackageName; - } - mThemePackageName = strdupNew(packageName); - mThemeStyleId = styleId; -} - -const char* AssetManager::getThemePackageName() -{ - return mThemePackageName; -} - bool AssetManager::updateWithAssetPath(const String8& path, void** cookie) { bool res = addAssetPath(path, cookie); @@ -2225,7 +1835,6 @@ bool AssetManager::updateWithAssetPath(const String8& path, void** cookie) AutoMutex _l(mLock); const asset_path& ap = mAssetPaths.itemAt((size_t)*cookie - 1); updateResTableFromAssetPath(rt, ap, *cookie); - loadRedirectionMappings(rt); } return res; } @@ -2240,7 +1849,6 @@ bool AssetManager::removeAssetPath(const String8 &packageName, void* cookie) return false; } - rt->clearRedirections(); rt->removeAssetsByCookie(packageName, (void *)cookie); return true; @@ -2255,3 +1863,17 @@ void AssetManager::dumpRes() } rt->dump(); } + +void AssetManager::addRedirections(PackageRedirectionMap* resMap) +{ + getResources(); + ResTable* rt = mResources; + rt->addRedirections(resMap); +} + +void AssetManager::clearRedirections() +{ + getResources(); + ResTable* rt = mResources; + rt->clearRedirections(); +} diff --git a/libs/utils/FileLock.cpp b/libs/utils/FileLock.cpp deleted file mode 100644 index a25a0e6..0000000 --- a/libs/utils/FileLock.cpp +++ /dev/null @@ -1,96 +0,0 @@ -/* - * 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); -} - -static struct flock fullfileLock(short lockType) -{ - struct flock lock; - memset(&lock, 0, sizeof(lock)); - - lock.l_type = lockType; - lock.l_whence = SEEK_SET; - lock.l_start = 0; - lock.l_len = 0; - - return lock; -} - -/* - * Destructor. - */ -FileLock::~FileLock(void) -{ - assert(mRefCount == 0); - - if (mFd >= 0) { - struct flock lock(fullfileLock(F_UNLCK)); - if (fcntl(mFd, F_SETLK, &lock) == -1) { - LOGE("error releasing write lock on %s: %s\n", mFileName, strerror(errno)); - } - if (close(mFd) != 0) { - LOGE("close(%s) failed: %s\n", mFileName, strerror(errno)); - } - } - if (mFileName != NULL) { - free(mFileName); - } -} - -bool FileLock::doLock(int openFlags, mode_t fileCreateMode) -{ - int fd = open(mFileName, openFlags | O_CREAT, fileCreateMode); - if (fd == -1) { - return false; - } - - struct flock lock(fullfileLock(F_WRLCK)); - while (fcntl(fd, F_SETLKW, &lock) == -1) { - if (errno != EINTR) { - LOGE("error acquiring write lock on %s: %s\n", mFileName, strerror(errno)); - close(fd); - return false; - } - } - - mFd = fd; - return true; -} diff --git a/libs/utils/PackageRedirectionMap.cpp b/libs/utils/PackageRedirectionMap.cpp new file mode 100644 index 0000000..bf1062a --- /dev/null +++ b/libs/utils/PackageRedirectionMap.cpp @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2006 The Android Open Source Project + * This code has been modified. Portions 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. + */ + +// +// Provide access to read-only assets. +// + +#define LOG_TAG "packageresmap" + +#include <utils/PackageRedirectionMap.h> +#include <utils/ResourceTypes.h> +#include <utils/misc.h> + +using namespace android; + +PackageRedirectionMap::PackageRedirectionMap() + : mPackage(-1), mEntriesByType(NULL) +{ +} + +static void clearEntriesByType(uint32_t** entriesByType) +{ + SharedBuffer* buf = SharedBuffer::bufferFromData(entriesByType); + const size_t N = buf->size() / sizeof(entriesByType[0]); + for (size_t i = 0; i < N; i++) { + uint32_t* entries = entriesByType[i]; + if (entries != NULL) { + SharedBuffer::bufferFromData(entries)->release(); + } + } + buf->release(); +} + +PackageRedirectionMap::~PackageRedirectionMap() +{ + if (mEntriesByType != NULL) { + clearEntriesByType(mEntriesByType); + } +} + +static void* ensureCapacity(void* data, size_t nmemb, size_t size) +{ + SharedBuffer* buf; + size_t currentSize; + + if (data != NULL) { + buf = SharedBuffer::bufferFromData(data); + currentSize = buf->size(); + } else { + buf = NULL; + currentSize = 0; + } + + size_t minSize = nmemb * size; + if (minSize > currentSize) { + unsigned int requestSize = roundUpPower2(minSize); + if (buf == NULL) { + buf = SharedBuffer::alloc(requestSize); + } else { + buf = buf->editResize(requestSize); + } + memset((unsigned char*)buf->data()+currentSize, 0, requestSize - currentSize); + } + + return buf->data(); +} + +bool PackageRedirectionMap::addRedirection(uint32_t fromIdent, uint32_t toIdent) +{ + const int package = Res_GETPACKAGE(fromIdent); + const int type = Res_GETTYPE(fromIdent); + const int entry = Res_GETENTRY(fromIdent); + + // The first time we add a redirection we can infer the package for all + // future redirections. + if (mPackage == -1) { + mPackage = package+1; + } else if (mPackage != (package+1)) { + LOGW("cannot add redirection for conflicting package 0x%02x (expecting package 0x%02x)\n", package+1, mPackage); + return false; + } + + mEntriesByType = (uint32_t**)ensureCapacity(mEntriesByType, type + 1, sizeof(uint32_t*)); + uint32_t* entries = mEntriesByType[type]; + entries = (uint32_t*)ensureCapacity(entries, entry + 1, sizeof(uint32_t)); + entries[entry] = toIdent; + mEntriesByType[type] = entries; + + return true; +} + +uint32_t PackageRedirectionMap::lookupRedirection(uint32_t fromIdent) +{ + if (mPackage == -1 || mEntriesByType == NULL || fromIdent == 0) { + return 0; + } + + const int package = Res_GETPACKAGE(fromIdent); + const int type = Res_GETTYPE(fromIdent); + const int entry = Res_GETENTRY(fromIdent); + + if (package+1 != mPackage) { + return 0; + } + + size_t nTypes = getNumberOfTypes(); + if (type < 0 || type >= nTypes) { + return 0; + } + uint32_t* entries = mEntriesByType[type]; + if (entries == NULL) { + return 0; + } + size_t nEntries = getNumberOfEntries(type); + if (entry < 0 || entry >= nEntries) { + return 0; + } + return entries[entry]; +} + +int PackageRedirectionMap::getPackage() +{ + return mPackage; +} + +size_t PackageRedirectionMap::getNumberOfTypes() +{ + if (mEntriesByType == NULL) { + return 0; + } else { + return SharedBuffer::bufferFromData(mEntriesByType)->size() / + sizeof(mEntriesByType[0]); + } +} + +size_t PackageRedirectionMap::getNumberOfUsedTypes() +{ + uint32_t** entriesByType = mEntriesByType; + size_t N = getNumberOfTypes(); + size_t count = 0; + for (size_t i=0; i<N; i++) { + if (entriesByType[i] != NULL) { + count++; + } + } + return count; +} + +size_t PackageRedirectionMap::getNumberOfEntries(int type) +{ + uint32_t* entries = mEntriesByType[type]; + if (entries == NULL) { + return 0; + } else { + return SharedBuffer::bufferFromData(entries)->size() / + sizeof(entries[0]); + } +} + +size_t PackageRedirectionMap::getNumberOfUsedEntries(int type) +{ + size_t N = getNumberOfEntries(type); + uint32_t* entries = mEntriesByType[type]; + size_t count = 0; + for (size_t i=0; i<N; i++) { + if (entries[i] != 0) { + count++; + } + } + return count; +} + +uint32_t PackageRedirectionMap::getEntry(int type, int entry) +{ + uint32_t* entries = mEntriesByType[type]; + return entries[entry]; +} diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index f7bee02..d9e5012 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -2057,14 +2057,12 @@ uint32_t ResTable::lookupRedirectionMap(uint32_t resID) const } 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); + PackageRedirectionMap* resMap = mRedirectionMap[i]; + if (resMap->getPackage() == p) { + return resMap->lookupRedirection(resID); } } return 0; @@ -4096,127 +4094,6 @@ 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. * @@ -4226,130 +4103,18 @@ bool ResTable::PackageResMap::parseMap(const unsigned char* basePtr, * 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) +void ResTable::addRedirections(PackageRedirectionMap* resMap) { - 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; - } + // TODO: Replace an existing entry matching the same package. + mRedirectionMap.add(resMap); } void ResTable::clearRedirections() { - const size_t N = mRedirectionMap.size(); - for (size_t i=0; i<N; i++) { - PackageResMap* resMap = mRedirectionMap[i]; - delete resMap; - } + /* This memory is being managed by strong references at the Java layer. */ 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 diff --git a/services/java/com/android/server/AssetRedirectionManagerService.java b/services/java/com/android/server/AssetRedirectionManagerService.java new file mode 100644 index 0000000..820c45c --- /dev/null +++ b/services/java/com/android/server/AssetRedirectionManagerService.java @@ -0,0 +1,376 @@ +package com.android.server; + +import com.android.internal.app.IAssetRedirectionManager; +import com.android.internal.util.XmlUtils; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.ThemeInfo; +import android.content.res.AssetManager; +import android.content.res.PackageRedirectionMap; +import android.content.res.Resources; +import android.os.RemoteException; +import android.text.TextUtils; +import android.util.Log; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +public class AssetRedirectionManagerService extends IAssetRedirectionManager.Stub { + private static final String TAG = "AssetRedirectionManager"; + + private final Context mContext; + + /* + * TODO: This data structure should have some way to expire very old cache + * entries. Would be nice to optimize for the removal path as well. + */ + private final HashMap<RedirectionKey, PackageRedirectionMap> mRedirections = + new HashMap<RedirectionKey, PackageRedirectionMap>(); + + public AssetRedirectionManagerService(Context context) { + mContext = context; + } + + @Override + public void clearRedirectionMapsByTheme(String themePackageName, String themeId) + throws RemoteException { + synchronized (mRedirections) { + Set<RedirectionKey> keys = mRedirections.keySet(); + Iterator<RedirectionKey> iter = keys.iterator(); + while (iter.hasNext()) { + RedirectionKey key = iter.next(); + if (themePackageName.equals(key.themePackageName) && + (themeId == null || themeId.equals(key.themeId))) { + iter.remove(); + } + } + } + } + + @Override + public void clearPackageRedirectionMap(String targetPackageName) throws RemoteException { + synchronized (mRedirections) { + Set<RedirectionKey> keys = mRedirections.keySet(); + Iterator<RedirectionKey> iter = keys.iterator(); + while (iter.hasNext()) { + RedirectionKey key = iter.next(); + if (targetPackageName.equals(key.targetPackageName)) { + iter.remove(); + } + } + } + } + + @Override + public PackageRedirectionMap getPackageRedirectionMap(String themePackageName, + String themeId, String targetPackageName) throws RemoteException { + synchronized (mRedirections) { + RedirectionKey key = new RedirectionKey(); + key.themePackageName = themePackageName; + key.themeId = themeId; + key.targetPackageName = targetPackageName; + + PackageRedirectionMap map = mRedirections.get(key); + if (map != null) { + return map; + } else { + map = generatePackageRedirectionMap(key); + if (map != null) { + mRedirections.put(key, map); + } + return map; + } + } + } + + private PackageRedirectionMap generatePackageRedirectionMap(RedirectionKey key) { + AssetManager assets = new AssetManager(); + + boolean frameworkAssets = key.targetPackageName.equals("android"); + + if (!frameworkAssets) { + PackageInfo pi = getPackageInfo(mContext, key.targetPackageName); + if (pi == null || pi.applicationInfo == null || + assets.addAssetPath(pi.applicationInfo.publicSourceDir) == 0) { + Log.w(TAG, "Unable to attach target package assets for " + key.targetPackageName); + return null; + } + } + + PackageInfo pi = getPackageInfo(mContext, key.themePackageName); + if (pi == null || pi.applicationInfo == null || pi.themeInfos == null || + assets.addAssetPath(pi.applicationInfo.publicSourceDir) == 0) { + Log.w(TAG, "Unable to attach theme package assets from " + key.themePackageName); + return null; + } + + PackageRedirectionMap resMap = new PackageRedirectionMap(); + + /* + * Apply a special redirection hack for the highest level <style> + * replacing @android:style/Theme. + */ + if (frameworkAssets) { + int themeResourceId = findThemeResourceId(pi.themeInfos, key.themeId); + assets.generateStyleRedirections(resMap.getNativePointer(), android.R.style.Theme, + themeResourceId); + } + + Resources res = new Resources(assets, null, null); + generateExplicitRedirections(resMap, res, key.themePackageName, key.targetPackageName); + + return resMap; + } + + private void generateExplicitRedirections(PackageRedirectionMap resMap, Resources res, + String themePackageName, String targetPackageName) { + /* + * XXX: We should be parsing the <theme> tag's <meta-data>! Instead, + * we're just assuming that res/xml/<package>.xml exists and describes + * the redirects we want! + */ + String redirectXmlName = targetPackageName.replace('.', '_'); + int redirectXmlResId = res.getIdentifier(redirectXmlName, "xml", themePackageName); + if (redirectXmlResId == 0) { + return; + } + + ResourceRedirectionsProcessor processor = new ResourceRedirectionsProcessor(res, + redirectXmlResId, themePackageName, targetPackageName, resMap); + processor.process(); + } + + private static PackageInfo getPackageInfo(Context context, String packageName) { + try { + return context.getPackageManager().getPackageInfo(packageName, 0); + } catch (NameNotFoundException e) { + return null; + } + } + + /** + * Searches for the high-level theme resource id for the specific + * <theme> tag being applied. + * <p> + * An individual theme package can contain multiple <theme> tags, each + * representing a separate theme choice from the user's perspective, even + * though the most common case is for there to be only 1. + * + * @return The style resource id or 0 if no match was found. + */ + private static int findThemeResourceId(ThemeInfo[] themeInfos, String needle) { + if (themeInfos != null && !TextUtils.isEmpty(needle)) { + int n = themeInfos.length; + for (int i = 0; i < n; i++) { + ThemeInfo info = themeInfos[i]; + if (needle.equals(info.themeId)) { + return info.styleResourceId; + } + } + } + return 0; + } + + @Override + protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + synchronized (mRedirections) { + final ArrayList<RedirectionKey> filteredKeySet = new ArrayList<RedirectionKey>(); + for (Map.Entry<RedirectionKey, PackageRedirectionMap> entry: mRedirections.entrySet()) { + PackageRedirectionMap map = entry.getValue(); + if (map != null && map.getPackageId() != -1) { + filteredKeySet.add(entry.getKey()); + } + } + Collections.sort(filteredKeySet, new Comparator<RedirectionKey>() { + @Override + public int compare(RedirectionKey a, RedirectionKey b) { + int comp = a.themePackageName.compareTo(b.themePackageName); + if (comp != 0) { + return comp; + } + comp = a.themeId.compareTo(b.themeId); + if (comp != 0) { + return comp; + } + return a.targetPackageName.compareTo(b.targetPackageName); + } + }); + + pw.println("Theme asset redirections:"); + String lastPackageName = null; + String lastId = null; + for (RedirectionKey key: filteredKeySet) { + if (lastPackageName == null || !lastPackageName.equals(key.themePackageName)) { + pw.println("* Theme package " + key.themePackageName + ":"); + lastPackageName = key.themePackageName; + } + if (lastId == null || !lastId.equals(key.themeId)) { + pw.println(" theme id #" + key.themeId + ":"); + lastId = key.themeId; + } + pw.println(" " + key.targetPackageName + ":"); + PackageRedirectionMap resMap = mRedirections.get(key); + int[] fromIdents = resMap.getRedirectionKeys(); + int N = fromIdents.length; + for (int i = 0; i < N; i++) { + int fromIdent = fromIdents[i]; + pw.println(String.format(" 0x%08x => 0x%08x", fromIdent, + resMap.lookupRedirection(fromIdent))); + } + } + } + } + + private static class RedirectionKey { + public String themePackageName; + public String themeId; + public String targetPackageName; + + @Override + public boolean equals(Object o) { + if (o == this) return true; + if (!(o instanceof RedirectionKey)) return false; + final RedirectionKey oo = (RedirectionKey)o; + if (!nullSafeEquals(themePackageName, oo.themePackageName)) { + return false; + } + if (!nullSafeEquals(themeId, oo.themeId)) { + return false; + } + if (!nullSafeEquals(targetPackageName, oo.targetPackageName)) { + return false; + } + return true; + } + + @Override + public int hashCode() { + return themePackageName.hashCode() + + themeId.hashCode() + + targetPackageName.hashCode(); + } + + private static boolean nullSafeEquals(Object a, Object b) { + if (a == null) { + return b == a; + } else if (b == null) { + return false; + } else { + return a.equals(b); + } + } + } + + /** + * Parses and processes explicit redirection XML files. + */ + private static class ResourceRedirectionsProcessor { + private final Resources mResources; + private final XmlPullParser mParser; + private final int mResourceId; + private final String mThemePackageName; + private final String mTargetPackageName; + private final PackageRedirectionMap mResMap; + + public ResourceRedirectionsProcessor(Resources res, int resourceId, + String themePackageName, String targetPackageName, + PackageRedirectionMap outMap) { + mResources = res; + mParser = res.getXml(resourceId); + mResourceId = resourceId; + mThemePackageName = themePackageName; + mTargetPackageName = targetPackageName; + mResMap = outMap; + } + + public void process() { + XmlPullParser parser = mParser; + int type; + try { + while ((type = parser.next()) != XmlPullParser.START_TAG + && type != XmlPullParser.END_DOCUMENT) { + // just loop... + } + + String tagName = parser.getName(); + if (parser.getName().equals("resource-redirections")) { + processResourceRedirectionsTag(); + } else { + Log.w(TAG, "Unknown root element: " + tagName + " at " + getResourceLabel() + " " + + parser.getPositionDescription()); + } + } catch (XmlPullParserException e) { + Log.w(TAG, "Malformed theme redirection meta at " + getResourceLabel()); + } catch (IOException e) { + Log.w(TAG, "Unknown error reading redirection meta at " + getResourceLabel()); + } + } + + private void processResourceRedirectionsTag() throws XmlPullParserException, IOException { + XmlPullParser parser = mParser; + int type; + final int innerDepth = parser.getDepth(); + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && + (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + + String tagName = parser.getName(); + if (tagName.equals("item")) { + processItemTag(); + } else { + Log.w(TAG, "Unknown element under <resource-redirections>: " + tagName + + " at " + getResourceLabel() + " " + + parser.getPositionDescription()); + XmlUtils.skipCurrentTag(parser); + continue; + } + } + } + + private void processItemTag() throws XmlPullParserException, IOException { + XmlPullParser parser = mParser; + String fromName = parser.getAttributeValue(null, "name"); + if (TextUtils.isEmpty(fromName)) { + Log.w(TAG, "Missing android:name attribute on <item> tag at " + getResourceLabel() + " " + + parser.getPositionDescription()); + return; + } + String toName = parser.nextText(); + if (TextUtils.isEmpty(toName)) { + Log.w(TAG, "Missing <item> text at " + getResourceLabel() + " " + + parser.getPositionDescription()); + return; + } + int fromIdent = mResources.getIdentifier(fromName, null, mTargetPackageName); + if (fromIdent == 0) { + Log.w(TAG, "No such resource found for " + mTargetPackageName + ":" + fromName); + return; + } + int toIdent = mResources.getIdentifier(toName, null, mThemePackageName); + if (toIdent == 0) { + Log.w(TAG, "No such resource found for " + mThemePackageName + ":" + toName); + return; + } + mResMap.addRedirection(fromIdent, toIdent); + } + + private String getResourceLabel() { + return "resource #0x" + Integer.toHexString(mResourceId); + } + } +} diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java index 79c8677..79f96bd 100644 --- a/services/java/com/android/server/PackageManagerService.java +++ b/services/java/com/android/server/PackageManagerService.java @@ -17,6 +17,7 @@ package com.android.server; +import com.android.internal.app.IAssetRedirectionManager; import com.android.internal.app.IMediaContainerService; import com.android.internal.app.ResolverActivity; import com.android.internal.content.NativeLibraryHelper; @@ -103,9 +104,6 @@ import java.io.FilenameFilter; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; -import java.io.RandomAccessFile; -import java.nio.channels.FileChannel; -import java.nio.channels.FileLock; import java.security.NoSuchAlgorithmException; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -351,6 +349,8 @@ class PackageManagerService extends IPackageManager.Stub { final ResolveInfo mResolveInfo = new ResolveInfo(); ComponentName mResolveComponentName; PackageParser.Package mPlatformPackage; + + IAssetRedirectionManager mAssetRedirectionManager; // Set of pending broadcasts for aggregating enable/disable of components. final HashMap<String, ArrayList<String>> mPendingBroadcasts @@ -3682,16 +3682,38 @@ class PackageManagerService extends IPackageManager.Stub { } } + // NOTE: this method can return null if the SystemServer is still + // initializing + public IAssetRedirectionManager getAssetRedirectionManager() { + if (mAssetRedirectionManager != null) { + return mAssetRedirectionManager; + } + IBinder b = ServiceManager.getService("assetredirection"); + mAssetRedirectionManager = IAssetRedirectionManager.Stub.asInterface(b); + return mAssetRedirectionManager; + } + + private void cleanAssetRedirections(PackageParser.Package pkg) { + IAssetRedirectionManager rm = getAssetRedirectionManager(); + if (rm == null) { + return; + } + try { + if (pkg.mIsThemeApk) { + rm.clearRedirectionMapsByTheme(pkg.packageName, null); + } else { + rm.clearPackageRedirectionMap(pkg.packageName); + } + } catch (RemoteException e) { + } + } + void removePackageLI(PackageParser.Package pkg, boolean chatty) { if (chatty && Config.LOGD) Log.d( TAG, "Removing package " + pkg.applicationInfo.packageName ); synchronized (mPackages) { - if (pkg.mIsThemeApk) { - deleteThemeResourceCache(pkg.packageName); - } else { - deleteThemeResourceCacheForApp(pkg.packageName); - } + cleanAssetRedirections(pkg); clearPackagePreferredActivitiesLP(pkg.packageName); @@ -5848,12 +5870,10 @@ class PackageManagerService extends IPackageManager.Stub { Log.d(TAG, "New package installed in " + newPackage.mPath); } - // DBS -- this changed a lot, hope it works - if (newPackage.mIsThemeApk) { - // Even though the package is still around, we'll delete the cache - // and have it regenerated on demand by the AssetManager. - deleteThemeResourceCache(newPackage.packageName); + cleanAssetRedirections(newPackage); + if (newPackage.mIsThemeApk) { + /* DBS-TODO boolean isThemePackageDrmProtected = false; int N = newPackage.mThemeInfos.size(); for (int i = 0; i < N; i++) { @@ -5862,13 +5882,10 @@ class PackageManagerService extends IPackageManager.Stub { break; } } - /* DBS-TODO if (isThemePackageDrmProtected) { splitThemePackage(newPackage.mPath); } */ - } else { - deleteThemeResourceCacheForApp(newPackage.packageName); } synchronized (mPackages) { @@ -5885,71 +5902,6 @@ class PackageManagerService extends IPackageManager.Stub { } } - /* - * We currently synchronize with consumers of the theme redirections - * cache by taking a file lock that is shared by that - * implementation. This should ideally be replaced by centralizing - * access through a service component that can rely on a simple - * monitor lock to synchronize properly. - */ - private FileLock lockThemeResourceDir(String themePackageName) throws IOException { - RandomAccessFile lockFile = new RandomAccessFile(new File(mThemeResCacheDir, - themePackageName + ".lck"), "rw"); - return lockFile.getChannel().lock(); - } - - private void deleteThemeResourceCache(String packageName) { - Log.v(TAG, "deleteThemeResourceCache: packageName=" + packageName); - try { - FileLock lock = lockThemeResourceDir(packageName); - try { - File themeResDir = new File(mThemeResCacheDir, packageName); - File[] files = themeResDir.listFiles(); - int n = files != null ? files.length : 0; - Log.v(TAG, n + " files from " + themeResDir); - for (int i = 0; i < n; i++) { - File file = files[i]; - if (!file.delete()) { - throw new IOException("Cannot delete " + file); - } - } - if (themeResDir.exists() && !themeResDir.delete()) { - throw new IOException("Cannot delete " + themeResDir); - } - } finally { - lock.release(); - } - } catch (IOException e) { - Log.w(TAG, "Couldn't delete resource cache for theme package " + packageName + - ":" + e.getMessage()); - } - } - - private void deleteThemeResourceCacheForApp(String appPackageName) { - Log.v(TAG, "deleteThemeResourceCacheForApp: appPackageName=" + appPackageName); - - // Unfortunately we must loop through all installed themes to make sure - // we clear this package cache for each. Very lame. - List<PackageInfo> themes = getInstalledThemePackages(); - for (PackageInfo theme: themes) { - try { - FileLock lock = lockThemeResourceDir(theme.packageName); - try { - File resCache = new File(new File(mThemeResCacheDir, theme.packageName), - appPackageName); - if (resCache.exists() && !resCache.delete()) { - throw new IOException("Cannot delete " + resCache); - } - } finally { - lock.release(); - } - } catch (IOException e) { - Log.w(TAG, "Couldn't delete app resource cache for " + appPackageName + - " from theme " + theme.packageName); - } - } - } - private void deleteLockedZipFileIfExists(String originalPackagePath) { String lockedZipFilePath = PackageParser.getLockedZipFilePath(originalPackagePath); File zipFile = new File(lockedZipFilePath); diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index cfd5311..73587b2 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -443,6 +443,13 @@ class ServerThread extends Thread { } catch (Throwable e) { Slog.e(TAG, "Failure starting DiskStats Service", e); } + + try { + Slog.i(TAG, "AssetRedirectionManager Service"); + ServiceManager.addService("assetredirection", new AssetRedirectionManagerService(context)); + } catch (Throwable e) { + Slog.e(TAG, "Failure starting AssetRedirectionManager Service", e); + } } // make sure the ADB_ENABLED setting value matches the secure property value |