Merge "Pass all preferred locales to AssetManager" into main

This commit is contained in:
Jeremy Meyer
2023-07-07 16:11:40 +00:00
committed by Android (Google) Code Review
17 changed files with 441 additions and 271 deletions

View File

@@ -40,7 +40,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
@@ -195,7 +195,8 @@ public class LocaleConfig implements Parcelable {
XmlUtils.beginDocument(parser, TAG_LOCALE_CONFIG);
int outerDepth = parser.getDepth();
AttributeSet attrs = Xml.asAttributeSet(parser);
Set<String> localeNames = new HashSet<String>();
// LinkedHashSet to preserve insertion order
Set<String> localeNames = new LinkedHashSet<>();
while (XmlUtils.nextElementWithin(parser, outerDepth)) {
if (TAG_LOCALE.equals(parser.getName())) {
final TypedArray attributes = res.obtainAttributes(

View File

@@ -1456,8 +1456,8 @@ public class PackageParser {
private static AssetManager newConfiguredAssetManager() {
AssetManager assetManager = new AssetManager();
assetManager.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
Build.VERSION.RESOURCES_SDK_INT);
assetManager.setConfiguration(0, 0, null, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, Build.VERSION.RESOURCES_SDK_INT);
return assetManager;
}
@@ -9011,8 +9011,8 @@ public class PackageParser {
}
AssetManager assets = new AssetManager();
assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
Build.VERSION.RESOURCES_SDK_INT);
assets.setConfiguration(0, 0, null, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, Build.VERSION.RESOURCES_SDK_INT);
assets.setApkAssets(apkAssets, false /*invalidateCaches*/);
mCachedAssetManager = assets;
@@ -9086,8 +9086,8 @@ public class PackageParser {
private static AssetManager createAssetManagerWithAssets(ApkAssets[] apkAssets) {
final AssetManager assets = new AssetManager();
assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
Build.VERSION.RESOURCES_SDK_INT);
assets.setConfiguration(0, 0, null, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, Build.VERSION.RESOURCES_SDK_INT);
assets.setApkAssets(apkAssets, false /*invalidateCaches*/);
return assets;
}

View File

@@ -1480,15 +1480,38 @@ public final class AssetManager implements AutoCloseable {
int screenWidth, int screenHeight, int smallestScreenWidthDp, int screenWidthDp,
int screenHeightDp, int screenLayout, int uiMode, int colorMode, int grammaticalGender,
int majorVersion) {
synchronized (this) {
ensureValidLocked();
nativeSetConfiguration(mObject, mcc, mnc, locale, orientation, touchscreen, density,
if (locale != null) {
setConfiguration(mcc, mnc, null, new String[]{locale}, orientation, touchscreen,
density, keyboard, keyboardHidden, navigation, screenWidth, screenHeight,
smallestScreenWidthDp, screenWidthDp, screenHeightDp, screenLayout, uiMode,
colorMode, grammaticalGender, majorVersion);
} else {
setConfiguration(mcc, mnc, null, null, orientation, touchscreen, density,
keyboard, keyboardHidden, navigation, screenWidth, screenHeight,
smallestScreenWidthDp, screenWidthDp, screenHeightDp, screenLayout, uiMode,
colorMode, grammaticalGender, majorVersion);
}
}
/**
* Change the configuration used when retrieving resources. Not for use by
* applications.
* @hide
*/
public void setConfiguration(int mcc, int mnc, String defaultLocale, String[] locales,
int orientation, int touchscreen, int density, int keyboard, int keyboardHidden,
int navigation, int screenWidth, int screenHeight, int smallestScreenWidthDp,
int screenWidthDp, int screenHeightDp, int screenLayout, int uiMode, int colorMode,
int grammaticalGender, int majorVersion) {
synchronized (this) {
ensureValidLocked();
nativeSetConfiguration(mObject, mcc, mnc, defaultLocale, locales, orientation,
touchscreen, density, keyboard, keyboardHidden, navigation, screenWidth,
screenHeight, smallestScreenWidthDp, screenWidthDp, screenHeightDp,
screenLayout, uiMode, colorMode, grammaticalGender, majorVersion);
}
}
/**
* @hide
*/
@@ -1572,10 +1595,11 @@ public final class AssetManager implements AutoCloseable {
private static native void nativeSetApkAssets(long ptr, @NonNull ApkAssets[] apkAssets,
boolean invalidateCaches);
private static native void nativeSetConfiguration(long ptr, int mcc, int mnc,
@Nullable String locale, int orientation, int touchscreen, int density, int keyboard,
int keyboardHidden, int navigation, int screenWidth, int screenHeight,
int smallestScreenWidthDp, int screenWidthDp, int screenHeightDp, int screenLayout,
int uiMode, int colorMode, int grammaticalGender, int majorVersion);
@Nullable String defaultLocale, @NonNull String[] locales, int orientation,
int touchscreen, int density, int keyboard, int keyboardHidden, int navigation,
int screenWidth, int screenHeight, int smallestScreenWidthDp, int screenWidthDp,
int screenHeightDp, int screenLayout, int uiMode, int colorMode, int grammaticalGender,
int majorVersion);
private static native @NonNull SparseArray<String> nativeGetAssignedPackageIdentifiers(
long ptr, boolean includeOverlays, boolean includeLoaders);

View File

@@ -407,14 +407,12 @@ public class ResourcesImpl {
mConfiguration.setLocales(locales);
}
String[] selectedLocales = null;
String defaultLocale = null;
if ((configChanges & ActivityInfo.CONFIG_LOCALE) != 0) {
if (locales.size() > 1) {
String[] availableLocales;
LocaleList localeList = ResourcesManager.getInstance().getLocaleList();
if (!localeList.isEmpty()) {
availableLocales = localeList.toLanguageTags().split(",");
} else {
if (ResourcesManager.getInstance().getLocaleList().isEmpty()) {
// The LocaleList has changed. We must query the AssetManager's
// available Locales and figure out the best matching Locale in the new
// LocaleList.
@@ -426,16 +424,32 @@ public class ResourcesImpl {
availableLocales = null;
}
}
}
if (availableLocales != null) {
final Locale bestLocale = locales.getFirstMatchWithEnglishSupported(
availableLocales);
if (bestLocale != null && bestLocale != locales.get(0)) {
mConfiguration.setLocales(new LocaleList(bestLocale, locales));
if (availableLocales != null) {
final Locale bestLocale = locales.getFirstMatchWithEnglishSupported(
availableLocales);
if (bestLocale != null) {
selectedLocales = new String[]{
adjustLanguageTag(bestLocale.toLanguageTag())};
if (!bestLocale.equals(locales.get(0))) {
mConfiguration.setLocales(
new LocaleList(bestLocale, locales));
}
}
}
} else {
selectedLocales = locales.getIntersection(
ResourcesManager.getInstance().getLocaleList());
defaultLocale = ResourcesManager.getInstance()
.getLocaleList().get(0).toLanguageTag();
}
}
}
if (selectedLocales == null) {
selectedLocales = new String[locales.size()];
for (int i = 0; i < locales.size(); i++) {
selectedLocales[i] = adjustLanguageTag(locales.get(i).toLanguageTag());
}
}
if (mConfiguration.densityDpi != Configuration.DENSITY_DPI_UNDEFINED) {
mMetrics.densityDpi = mConfiguration.densityDpi;
@@ -470,7 +484,8 @@ public class ResourcesImpl {
}
mAssets.setConfiguration(mConfiguration.mcc, mConfiguration.mnc,
adjustLanguageTag(mConfiguration.getLocales().get(0).toLanguageTag()),
defaultLocale,
selectedLocales,
mConfiguration.orientation,
mConfiguration.touchscreen,
mConfiguration.densityDpi, mConfiguration.keyboard,

View File

@@ -30,6 +30,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
/**
@@ -150,6 +151,25 @@ public final class LocaleList implements Parcelable {
return mStringRepresentation;
}
/**
* Find the intersection between this LocaleList and another
* @return a String array of the Locales in both LocaleLists
* {@hide}
*/
@NonNull
public String[] getIntersection(@NonNull LocaleList other) {
List<String> intersection = new ArrayList<>();
for (Locale l1 : mList) {
for (Locale l2 : other.mList) {
if (matchesLanguageAndScript(l2, l1)) {
intersection.add(l1.toLanguageTag());
break;
}
}
}
return intersection.toArray(new String[0]);
}
/**
* Creates a new {@link LocaleList}.
*

View File

@@ -347,14 +347,23 @@ static void NativeSetApkAssets(JNIEnv* env, jclass /*clazz*/, jlong ptr,
}
static void NativeSetConfiguration(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint mcc, jint mnc,
jstring locale, jint orientation, jint touchscreen, jint density,
jint keyboard, jint keyboard_hidden, jint navigation,
jint screen_width, jint screen_height,
jint smallest_screen_width_dp, jint screen_width_dp,
jint screen_height_dp, jint screen_layout, jint ui_mode,
jint color_mode, jint grammatical_gender, jint major_version) {
jstring default_locale, jobjectArray locales, jint orientation,
jint touchscreen, jint density, jint keyboard,
jint keyboard_hidden, jint navigation, jint screen_width,
jint screen_height, jint smallest_screen_width_dp,
jint screen_width_dp, jint screen_height_dp, jint screen_layout,
jint ui_mode, jint color_mode, jint grammatical_gender,
jint major_version) {
ATRACE_NAME("AssetManager::SetConfiguration");
const jsize locale_count = (locales == NULL) ? 0 : env->GetArrayLength(locales);
// Constants duplicated from Java class android.content.res.Configuration.
static const jint kScreenLayoutRoundMask = 0x300;
static const jint kScreenLayoutRoundShift = 8;
std::vector<ResTable_config> configs;
ResTable_config configuration;
memset(&configuration, 0, sizeof(configuration));
configuration.mcc = static_cast<uint16_t>(mcc);
@@ -375,25 +384,37 @@ static void NativeSetConfiguration(JNIEnv* env, jclass /*clazz*/, jlong ptr, jin
configuration.colorMode = static_cast<uint8_t>(color_mode);
configuration.grammaticalInflection = static_cast<uint8_t>(grammatical_gender);
configuration.sdkVersion = static_cast<uint16_t>(major_version);
if (locale != nullptr) {
ScopedUtfChars locale_utf8(env, locale);
CHECK(locale_utf8.c_str() != nullptr);
configuration.setBcp47Locale(locale_utf8.c_str());
}
// Constants duplicated from Java class android.content.res.Configuration.
static const jint kScreenLayoutRoundMask = 0x300;
static const jint kScreenLayoutRoundShift = 8;
// In Java, we use a 32bit integer for screenLayout, while we only use an 8bit integer
// in C++. We must extract the round qualifier out of the Java screenLayout and put it
// into screenLayout2.
configuration.screenLayout2 =
static_cast<uint8_t>((screen_layout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift);
static_cast<uint8_t>((screen_layout & kScreenLayoutRoundMask) >> kScreenLayoutRoundShift);
if (locale_count > 0) {
configs.resize(locale_count, configuration);
for (int i = 0; i < locale_count; i++) {
jstring locale = (jstring)(env->GetObjectArrayElement(locales, i));
ScopedUtfChars locale_utf8(env, locale);
CHECK(locale_utf8.c_str() != nullptr);
configs[i].setBcp47Locale(locale_utf8.c_str());
}
} else {
configs.push_back(configuration);
}
uint32_t default_locale_int = 0;
if (default_locale != nullptr) {
ResTable_config config;
static_assert(std::is_same_v<decltype(config.locale), decltype(default_locale_int)>);
ScopedUtfChars locale_utf8(env, default_locale);
CHECK(locale_utf8.c_str() != nullptr);
config.setBcp47Locale(locale_utf8.c_str());
default_locale_int = config.locale;
}
auto assetmanager = LockAndStartAssetManager(ptr);
assetmanager->SetConfiguration(configuration);
assetmanager->SetConfigurations(configs);
assetmanager->SetDefaultLocale(default_locale_int);
}
static jobject NativeGetAssignedPackageIdentifiers(JNIEnv* env, jclass /*clazz*/, jlong ptr,
@@ -1498,94 +1519,97 @@ static jlong NativeAssetGetRemainingLength(JNIEnv* /*env*/, jclass /*clazz*/, jl
// JNI registration.
static const JNINativeMethod gAssetManagerMethods[] = {
// AssetManager setup methods.
{"nativeCreate", "()J", (void*)NativeCreate},
{"nativeDestroy", "(J)V", (void*)NativeDestroy},
{"nativeSetApkAssets", "(J[Landroid/content/res/ApkAssets;Z)V", (void*)NativeSetApkAssets},
{"nativeSetConfiguration", "(JIILjava/lang/String;IIIIIIIIIIIIIIII)V",
(void*)NativeSetConfiguration},
{"nativeGetAssignedPackageIdentifiers", "(JZZ)Landroid/util/SparseArray;",
(void*)NativeGetAssignedPackageIdentifiers},
// AssetManager setup methods.
{"nativeCreate", "()J", (void*)NativeCreate},
{"nativeDestroy", "(J)V", (void*)NativeDestroy},
{"nativeSetApkAssets", "(J[Landroid/content/res/ApkAssets;Z)V", (void*)NativeSetApkAssets},
{"nativeSetConfiguration", "(JIILjava/lang/String;[Ljava/lang/String;IIIIIIIIIIIIIIII)V",
(void*)NativeSetConfiguration},
{"nativeGetAssignedPackageIdentifiers", "(JZZ)Landroid/util/SparseArray;",
(void*)NativeGetAssignedPackageIdentifiers},
// AssetManager file methods.
{"nativeContainsAllocatedTable", "(J)Z", (void*)ContainsAllocatedTable},
{"nativeList", "(JLjava/lang/String;)[Ljava/lang/String;", (void*)NativeList},
{"nativeOpenAsset", "(JLjava/lang/String;I)J", (void*)NativeOpenAsset},
{"nativeOpenAssetFd", "(JLjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;",
(void*)NativeOpenAssetFd},
{"nativeOpenNonAsset", "(JILjava/lang/String;I)J", (void*)NativeOpenNonAsset},
{"nativeOpenNonAssetFd", "(JILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;",
(void*)NativeOpenNonAssetFd},
{"nativeOpenXmlAsset", "(JILjava/lang/String;)J", (void*)NativeOpenXmlAsset},
{"nativeOpenXmlAssetFd", "(JILjava/io/FileDescriptor;)J", (void*)NativeOpenXmlAssetFd},
// AssetManager file methods.
{"nativeContainsAllocatedTable", "(J)Z", (void*)ContainsAllocatedTable},
{"nativeList", "(JLjava/lang/String;)[Ljava/lang/String;", (void*)NativeList},
{"nativeOpenAsset", "(JLjava/lang/String;I)J", (void*)NativeOpenAsset},
{"nativeOpenAssetFd", "(JLjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;",
(void*)NativeOpenAssetFd},
{"nativeOpenNonAsset", "(JILjava/lang/String;I)J", (void*)NativeOpenNonAsset},
{"nativeOpenNonAssetFd", "(JILjava/lang/String;[J)Landroid/os/ParcelFileDescriptor;",
(void*)NativeOpenNonAssetFd},
{"nativeOpenXmlAsset", "(JILjava/lang/String;)J", (void*)NativeOpenXmlAsset},
{"nativeOpenXmlAssetFd", "(JILjava/io/FileDescriptor;)J", (void*)NativeOpenXmlAssetFd},
// AssetManager resource methods.
{"nativeGetResourceValue", "(JISLandroid/util/TypedValue;Z)I", (void*)NativeGetResourceValue},
{"nativeGetResourceBagValue", "(JIILandroid/util/TypedValue;)I",
(void*)NativeGetResourceBagValue},
{"nativeGetStyleAttributes", "(JI)[I", (void*)NativeGetStyleAttributes},
{"nativeGetResourceStringArray", "(JI)[Ljava/lang/String;",
(void*)NativeGetResourceStringArray},
{"nativeGetResourceStringArrayInfo", "(JI)[I", (void*)NativeGetResourceStringArrayInfo},
{"nativeGetResourceIntArray", "(JI)[I", (void*)NativeGetResourceIntArray},
{"nativeGetResourceArraySize", "(JI)I", (void*)NativeGetResourceArraySize},
{"nativeGetResourceArray", "(JI[I)I", (void*)NativeGetResourceArray},
{"nativeGetParentThemeIdentifier", "(JI)I",
(void*)NativeGetParentThemeIdentifier},
// AssetManager resource methods.
{"nativeGetResourceValue", "(JISLandroid/util/TypedValue;Z)I",
(void*)NativeGetResourceValue},
{"nativeGetResourceBagValue", "(JIILandroid/util/TypedValue;)I",
(void*)NativeGetResourceBagValue},
{"nativeGetStyleAttributes", "(JI)[I", (void*)NativeGetStyleAttributes},
{"nativeGetResourceStringArray", "(JI)[Ljava/lang/String;",
(void*)NativeGetResourceStringArray},
{"nativeGetResourceStringArrayInfo", "(JI)[I", (void*)NativeGetResourceStringArrayInfo},
{"nativeGetResourceIntArray", "(JI)[I", (void*)NativeGetResourceIntArray},
{"nativeGetResourceArraySize", "(JI)I", (void*)NativeGetResourceArraySize},
{"nativeGetResourceArray", "(JI[I)I", (void*)NativeGetResourceArray},
{"nativeGetParentThemeIdentifier", "(JI)I", (void*)NativeGetParentThemeIdentifier},
// AssetManager resource name/ID methods.
{"nativeGetResourceIdentifier", "(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
(void*)NativeGetResourceIdentifier},
{"nativeGetResourceName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceName},
{"nativeGetResourcePackageName", "(JI)Ljava/lang/String;", (void*)NativeGetResourcePackageName},
{"nativeGetResourceTypeName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceTypeName},
{"nativeGetResourceEntryName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceEntryName},
{"nativeSetResourceResolutionLoggingEnabled", "(JZ)V",
(void*) NativeSetResourceResolutionLoggingEnabled},
{"nativeGetLastResourceResolution", "(J)Ljava/lang/String;",
(void*) NativeGetLastResourceResolution},
{"nativeGetLocales", "(JZ)[Ljava/lang/String;", (void*)NativeGetLocales},
{"nativeGetSizeConfigurations", "(J)[Landroid/content/res/Configuration;",
(void*)NativeGetSizeConfigurations},
{"nativeGetSizeAndUiModeConfigurations", "(J)[Landroid/content/res/Configuration;",
(void*)NativeGetSizeAndUiModeConfigurations},
// AssetManager resource name/ID methods.
{"nativeGetResourceIdentifier",
"(JLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
(void*)NativeGetResourceIdentifier},
{"nativeGetResourceName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceName},
{"nativeGetResourcePackageName", "(JI)Ljava/lang/String;",
(void*)NativeGetResourcePackageName},
{"nativeGetResourceTypeName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceTypeName},
{"nativeGetResourceEntryName", "(JI)Ljava/lang/String;", (void*)NativeGetResourceEntryName},
{"nativeSetResourceResolutionLoggingEnabled", "(JZ)V",
(void*)NativeSetResourceResolutionLoggingEnabled},
{"nativeGetLastResourceResolution", "(J)Ljava/lang/String;",
(void*)NativeGetLastResourceResolution},
{"nativeGetLocales", "(JZ)[Ljava/lang/String;", (void*)NativeGetLocales},
{"nativeGetSizeConfigurations", "(J)[Landroid/content/res/Configuration;",
(void*)NativeGetSizeConfigurations},
{"nativeGetSizeAndUiModeConfigurations", "(J)[Landroid/content/res/Configuration;",
(void*)NativeGetSizeAndUiModeConfigurations},
// Style attribute related methods.
{"nativeAttributeResolutionStack", "(JJIII)[I", (void*)NativeAttributeResolutionStack},
{"nativeApplyStyle", "(JJIIJ[IJJ)V", (void*)NativeApplyStyle},
{"nativeResolveAttrs", "(JJII[I[I[I[I)Z", (void*)NativeResolveAttrs},
{"nativeRetrieveAttributes", "(JJ[I[I[I)Z", (void*)NativeRetrieveAttributes},
// Style attribute related methods.
{"nativeAttributeResolutionStack", "(JJIII)[I", (void*)NativeAttributeResolutionStack},
{"nativeApplyStyle", "(JJIIJ[IJJ)V", (void*)NativeApplyStyle},
{"nativeResolveAttrs", "(JJII[I[I[I[I)Z", (void*)NativeResolveAttrs},
{"nativeRetrieveAttributes", "(JJ[I[I[I)Z", (void*)NativeRetrieveAttributes},
// Theme related methods.
{"nativeThemeCreate", "(J)J", (void*)NativeThemeCreate},
{"nativeGetThemeFreeFunction", "()J", (void*)NativeGetThemeFreeFunction},
{"nativeThemeApplyStyle", "(JJIZ)V", (void*)NativeThemeApplyStyle},
{"nativeThemeRebase", "(JJ[I[ZI)V", (void*)NativeThemeRebase},
// Theme related methods.
{"nativeThemeCreate", "(J)J", (void*)NativeThemeCreate},
{"nativeGetThemeFreeFunction", "()J", (void*)NativeGetThemeFreeFunction},
{"nativeThemeApplyStyle", "(JJIZ)V", (void*)NativeThemeApplyStyle},
{"nativeThemeRebase", "(JJ[I[ZI)V", (void*)NativeThemeRebase},
{"nativeThemeCopy", "(JJJJ)V", (void*)NativeThemeCopy},
{"nativeThemeGetAttributeValue", "(JJILandroid/util/TypedValue;Z)I",
(void*)NativeThemeGetAttributeValue},
{"nativeThemeDump", "(JJILjava/lang/String;Ljava/lang/String;)V", (void*)NativeThemeDump},
{"nativeThemeGetChangingConfigurations", "(J)I", (void*)NativeThemeGetChangingConfigurations},
{"nativeThemeCopy", "(JJJJ)V", (void*)NativeThemeCopy},
{"nativeThemeGetAttributeValue", "(JJILandroid/util/TypedValue;Z)I",
(void*)NativeThemeGetAttributeValue},
{"nativeThemeDump", "(JJILjava/lang/String;Ljava/lang/String;)V", (void*)NativeThemeDump},
{"nativeThemeGetChangingConfigurations", "(J)I",
(void*)NativeThemeGetChangingConfigurations},
// AssetInputStream methods.
{"nativeAssetDestroy", "(J)V", (void*)NativeAssetDestroy},
{"nativeAssetReadChar", "(J)I", (void*)NativeAssetReadChar},
{"nativeAssetRead", "(J[BII)I", (void*)NativeAssetRead},
{"nativeAssetSeek", "(JJI)J", (void*)NativeAssetSeek},
{"nativeAssetGetLength", "(J)J", (void*)NativeAssetGetLength},
{"nativeAssetGetRemainingLength", "(J)J", (void*)NativeAssetGetRemainingLength},
// AssetInputStream methods.
{"nativeAssetDestroy", "(J)V", (void*)NativeAssetDestroy},
{"nativeAssetReadChar", "(J)I", (void*)NativeAssetReadChar},
{"nativeAssetRead", "(J[BII)I", (void*)NativeAssetRead},
{"nativeAssetSeek", "(JJI)J", (void*)NativeAssetSeek},
{"nativeAssetGetLength", "(J)J", (void*)NativeAssetGetLength},
{"nativeAssetGetRemainingLength", "(J)J", (void*)NativeAssetGetRemainingLength},
// System/idmap related methods.
{"nativeGetOverlayableMap", "(JLjava/lang/String;)Ljava/util/Map;",
(void*)NativeGetOverlayableMap},
{"nativeGetOverlayablesToString", "(JLjava/lang/String;)Ljava/lang/String;",
(void*)NativeGetOverlayablesToString},
// System/idmap related methods.
{"nativeGetOverlayableMap", "(JLjava/lang/String;)Ljava/util/Map;",
(void*)NativeGetOverlayableMap},
{"nativeGetOverlayablesToString", "(JLjava/lang/String;)Ljava/lang/String;",
(void*)NativeGetOverlayablesToString},
// Global management/debug methods.
{"getGlobalAssetCount", "()I", (void*)NativeGetGlobalAssetCount},
{"getAssetAllocations", "()Ljava/lang/String;", (void*)NativeGetAssetAllocations},
{"getGlobalAssetManagerCount", "()I", (void*)NativeGetGlobalAssetManagerCount},
// Global management/debug methods.
{"getGlobalAssetCount", "()I", (void*)NativeGetGlobalAssetCount},
{"getAssetAllocations", "()Ljava/lang/String;", (void*)NativeGetAssetAllocations},
{"getGlobalAssetManagerCount", "()I", (void*)NativeGetGlobalAssetManagerCount},
};
int register_android_content_AssetManager(JNIEnv* env) {

View File

@@ -91,12 +91,17 @@ struct FindEntryResult {
StringPoolRef entry_string_ref;
};
AssetManager2::AssetManager2(ApkAssetsList apk_assets, const ResTable_config& configuration)
: configuration_(configuration) {
AssetManager2::AssetManager2(ApkAssetsList apk_assets, const ResTable_config& configuration) {
configurations_.push_back(configuration);
// Don't invalidate caches here as there's nothing cached yet.
SetApkAssets(apk_assets, false);
}
AssetManager2::AssetManager2() {
configurations_.resize(1);
}
bool AssetManager2::SetApkAssets(ApkAssetsList apk_assets, bool invalidate_caches) {
BuildDynamicRefTable(apk_assets);
RebuildFilterList();
@@ -421,9 +426,16 @@ bool AssetManager2::ContainsAllocatedTable() const {
return false;
}
void AssetManager2::SetConfiguration(const ResTable_config& configuration) {
const int diff = configuration_.diff(configuration);
configuration_ = configuration;
void AssetManager2::SetConfigurations(std::vector<ResTable_config> configurations) {
int diff = 0;
if (configurations_.size() != configurations.size()) {
diff = -1;
} else {
for (int i = 0; i < configurations_.size(); i++) {
diff |= configurations_[i].diff(configurations[i]);
}
}
configurations_ = std::move(configurations);
if (diff) {
RebuildFilterList();
@@ -620,16 +632,6 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntry(
auto op = StartOperation();
// Might use this if density_override != 0.
ResTable_config density_override_config;
// Select our configuration or generate a density override configuration.
const ResTable_config* desired_config = &configuration_;
if (density_override != 0 && density_override != configuration_.density) {
density_override_config = configuration_;
density_override_config.density = density_override;
desired_config = &density_override_config;
}
// Retrieve the package group from the package id of the resource id.
if (UNLIKELY(!is_valid_resid(resid))) {
@@ -648,119 +650,160 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntry(
}
const PackageGroup& package_group = package_groups_[package_idx];
auto result = FindEntryInternal(package_group, type_idx, entry_idx, *desired_config,
stop_at_first_match, ignore_configuration);
if (UNLIKELY(!result.has_value())) {
return base::unexpected(result.error());
}
std::optional<FindEntryResult> final_result;
bool final_has_locale = false;
bool final_overlaid = false;
for (auto & config : configurations_) {
// Might use this if density_override != 0.
ResTable_config density_override_config;
bool overlaid = false;
if (!stop_at_first_match && !ignore_configuration) {
const auto& assets = GetApkAssets(result->cookie);
if (!assets) {
ALOGE("Found expired ApkAssets #%d for resource ID 0x%08x.", result->cookie, resid);
return base::unexpected(std::nullopt);
// Select our configuration or generate a density override configuration.
const ResTable_config* desired_config = &config;
if (density_override != 0 && density_override != config.density) {
density_override_config = config;
density_override_config.density = density_override;
desired_config = &density_override_config;
}
if (!assets->IsLoader()) {
for (const auto& id_map : package_group.overlays_) {
auto overlay_entry = id_map.overlay_res_maps_.Lookup(resid);
if (!overlay_entry) {
// No id map entry exists for this target resource.
continue;
}
if (overlay_entry.IsInlineValue()) {
// The target resource is overlaid by an inline value not represented by a resource.
ConfigDescription best_frro_config;
Res_value best_frro_value;
bool frro_found = false;
for( const auto& [config, value] : overlay_entry.GetInlineValue()) {
if ((!frro_found || config.isBetterThan(best_frro_config, desired_config))
&& config.match(*desired_config)) {
frro_found = true;
best_frro_config = config;
best_frro_value = value;
}
}
if (!frro_found) {
auto result = FindEntryInternal(package_group, type_idx, entry_idx, *desired_config,
stop_at_first_match, ignore_configuration);
if (UNLIKELY(!result.has_value())) {
return base::unexpected(result.error());
}
bool overlaid = false;
if (!stop_at_first_match && !ignore_configuration) {
const auto& assets = GetApkAssets(result->cookie);
if (!assets) {
ALOGE("Found expired ApkAssets #%d for resource ID 0x%08x.", result->cookie, resid);
return base::unexpected(std::nullopt);
}
if (!assets->IsLoader()) {
for (const auto& id_map : package_group.overlays_) {
auto overlay_entry = id_map.overlay_res_maps_.Lookup(resid);
if (!overlay_entry) {
// No id map entry exists for this target resource.
continue;
}
result->entry = best_frro_value;
if (overlay_entry.IsInlineValue()) {
// The target resource is overlaid by an inline value not represented by a resource.
ConfigDescription best_frro_config;
Res_value best_frro_value;
bool frro_found = false;
for( const auto& [config, value] : overlay_entry.GetInlineValue()) {
if ((!frro_found || config.isBetterThan(best_frro_config, desired_config))
&& config.match(*desired_config)) {
frro_found = true;
best_frro_config = config;
best_frro_value = value;
}
}
if (!frro_found) {
continue;
}
result->entry = best_frro_value;
result->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable();
result->cookie = id_map.cookie;
if (UNLIKELY(logging_enabled)) {
last_resolution_.steps.push_back(Resolution::Step{
Resolution::Step::Type::OVERLAID_INLINE, result->cookie, String8()});
if (auto path = assets->GetPath()) {
const std::string overlay_path = path->data();
if (IsFabricatedOverlay(overlay_path)) {
// FRRO don't have package name so we use the creating package here.
String8 frro_name = String8("FRRO");
// Get the first part of it since the expected one should be like
// {overlayPackageName}-{overlayName}-{4 alphanumeric chars}.frro
// under /data/resource-cache/.
const std::string name = overlay_path.substr(overlay_path.rfind('/') + 1);
const size_t end = name.find('-');
if (frro_name.size() != overlay_path.size() && end != std::string::npos) {
frro_name.append(base::StringPrintf(" created by %s",
name.substr(0 /* pos */,
end).c_str()).c_str());
}
last_resolution_.best_package_name = frro_name;
} else {
last_resolution_.best_package_name = result->package_name->c_str();
}
}
overlaid = true;
}
continue;
}
auto overlay_result = FindEntry(overlay_entry.GetResourceId(), density_override,
false /* stop_at_first_match */,
false /* ignore_configuration */);
if (UNLIKELY(IsIOError(overlay_result))) {
return base::unexpected(overlay_result.error());
}
if (!overlay_result.has_value()) {
continue;
}
if (!overlay_result->config.isBetterThan(result->config, desired_config)
&& overlay_result->config.compare(result->config) != 0) {
// The configuration of the entry for the overlay must be equal to or better than the
// target configuration to be chosen as the better value.
continue;
}
result->cookie = overlay_result->cookie;
result->entry = overlay_result->entry;
result->config = overlay_result->config;
result->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable();
result->cookie = id_map.cookie;
if (UNLIKELY(logging_enabled)) {
last_resolution_.steps.push_back(
Resolution::Step{Resolution::Step::Type::OVERLAID_INLINE, result->cookie, String8()});
if (auto path = assets->GetPath()) {
const std::string overlay_path = path->data();
if (IsFabricatedOverlay(overlay_path)) {
// FRRO don't have package name so we use the creating package here.
String8 frro_name = String8("FRRO");
// Get the first part of it since the expected one should be like
// {overlayPackageName}-{overlayName}-{4 alphanumeric chars}.frro
// under /data/resource-cache/.
const std::string name = overlay_path.substr(overlay_path.rfind('/') + 1);
const size_t end = name.find('-');
if (frro_name.size() != overlay_path.size() && end != std::string::npos) {
frro_name.append(base::StringPrintf(" created by %s",
name.substr(0 /* pos */,
end).c_str()).c_str());
}
last_resolution_.best_package_name = frro_name;
} else {
last_resolution_.best_package_name = result->package_name->c_str();
}
}
Resolution::Step{Resolution::Step::Type::OVERLAID, overlay_result->cookie,
overlay_result->config.toString()});
last_resolution_.best_package_name =
overlay_result->package_name->c_str();
overlaid = true;
}
continue;
}
auto overlay_result = FindEntry(overlay_entry.GetResourceId(), density_override,
false /* stop_at_first_match */,
false /* ignore_configuration */);
if (UNLIKELY(IsIOError(overlay_result))) {
return base::unexpected(overlay_result.error());
}
if (!overlay_result.has_value()) {
continue;
}
if (!overlay_result->config.isBetterThan(result->config, desired_config)
&& overlay_result->config.compare(result->config) != 0) {
// The configuration of the entry for the overlay must be equal to or better than the target
// configuration to be chosen as the better value.
continue;
}
result->cookie = overlay_result->cookie;
result->entry = overlay_result->entry;
result->config = overlay_result->config;
result->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable();
if (UNLIKELY(logging_enabled)) {
last_resolution_.steps.push_back(
Resolution::Step{Resolution::Step::Type::OVERLAID, overlay_result->cookie,
overlay_result->config.toString()});
last_resolution_.best_package_name =
overlay_result->package_name->c_str();
overlaid = true;
}
}
}
bool has_locale = false;
if (result->config.locale == 0) {
if (default_locale_ != 0) {
ResTable_config conf;
conf.locale = default_locale_;
// Since we know conf has a locale and only a locale, match will tell us if that locale
// matches
has_locale = conf.match(config);
}
} else {
has_locale = true;
}
// if we don't have a result yet
if (!final_result ||
// or this config is better before the locale than the existing result
result->config.isBetterThanBeforeLocale(final_result->config, desired_config) ||
// or the existing config isn't better before locale and this one specifies a locale
// whereas the existing one doesn't
(!final_result->config.isBetterThanBeforeLocale(result->config, desired_config)
&& has_locale && !final_has_locale)) {
final_result = result.value();
final_overlaid = overlaid;
final_has_locale = has_locale;
}
}
if (UNLIKELY(logging_enabled)) {
last_resolution_.cookie = result->cookie;
last_resolution_.type_string_ref = result->type_string_ref;
last_resolution_.entry_string_ref = result->entry_string_ref;
last_resolution_.best_config_name = result->config.toString();
if (!overlaid) {
last_resolution_.best_package_name = result->package_name->c_str();
last_resolution_.cookie = final_result->cookie;
last_resolution_.type_string_ref = final_result->type_string_ref;
last_resolution_.entry_string_ref = final_result->entry_string_ref;
last_resolution_.best_config_name = final_result->config.toString();
if (!final_overlaid) {
last_resolution_.best_package_name = final_result->package_name->c_str();
}
}
return result;
return *final_result;
}
base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntryInternal(
@@ -778,8 +821,10 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntryInternal(
// If `desired_config` is not the same as the set configuration or the caller will accept a value
// from any configuration, then we cannot use our filtered list of types since it only it contains
// types matched to the set configuration.
const bool use_filtered = !ignore_configuration && &desired_config == &configuration_;
const bool use_filtered = !ignore_configuration && std::find_if(
configurations_.begin(), configurations_.end(),
[&desired_config](auto& value) { return &desired_config == &value; })
!= configurations_.end();
const size_t package_count = package_group.packages_.size();
for (size_t pi = 0; pi < package_count; pi++) {
const ConfiguredPackage& loaded_package_impl = package_group.packages_[pi];
@@ -934,10 +979,22 @@ std::string AssetManager2::GetLastResourceResolution() const {
}
std::stringstream log_stream;
log_stream << base::StringPrintf("Resolution for 0x%08x %s\n"
"\tFor config - %s", resid, resource_name_string.c_str(),
configuration_.toString().c_str());
if (configurations_.size() == 1) {
log_stream << base::StringPrintf("Resolution for 0x%08x %s\n"
"\tFor config - %s", resid, resource_name_string.c_str(),
configurations_[0].toString().c_str());
} else {
ResTable_config conf = configurations_[0];
conf.clearLocale();
log_stream << base::StringPrintf("Resolution for 0x%08x %s\n\tFor config - %s and locales",
resid, resource_name_string.c_str(), conf.toString().c_str());
char str[40];
str[0] = '\0';
for(auto iter = configurations_.begin(); iter < configurations_.end(); iter++) {
iter->getBcp47Locale(str);
log_stream << base::StringPrintf(" %s%s", str, iter < configurations_.end() ? "," : "");
}
}
for (const Resolution::Step& step : last_resolution_.steps) {
constexpr static std::array kStepStrings = {
"Found initial",
@@ -1427,11 +1484,14 @@ void AssetManager2::RebuildFilterList() {
package.loaded_package_->ForEachTypeSpec([&](const TypeSpec& type_spec, uint8_t type_id) {
FilteredConfigGroup* group = nullptr;
for (const auto& type_entry : type_spec.type_entries) {
if (type_entry.config.match(configuration_)) {
if (!group) {
group = &package.filtered_configs_.editItemAt(type_id - 1);
for (auto & config : configurations_) {
if (type_entry.config.match(config)) {
if (!group) {
group = &package.filtered_configs_.editItemAt(type_id - 1);
}
group->type_entries.push_back(&type_entry);
break;
}
group->type_entries.push_back(&type_entry);
}
}
});

View File

@@ -2568,6 +2568,22 @@ bool ResTable_config::isLocaleBetterThan(const ResTable_config& o,
return false;
}
bool ResTable_config::isBetterThanBeforeLocale(const ResTable_config& o,
const ResTable_config* requested) const {
if (requested) {
if (imsi || o.imsi) {
if ((mcc != o.mcc) && requested->mcc) {
return (mcc);
}
if ((mnc != o.mnc) && requested->mnc) {
return (mnc);
}
}
}
return false;
}
bool ResTable_config::isBetterThan(const ResTable_config& o,
const ResTable_config* requested) const {
if (requested) {

View File

@@ -100,7 +100,7 @@ class AssetManager2 {
using ApkAssetsWPtr = wp<const ApkAssets>;
using ApkAssetsList = std::span<const ApkAssetsPtr>;
AssetManager2() = default;
AssetManager2();
explicit AssetManager2(AssetManager2&& other) = default;
AssetManager2(ApkAssetsList apk_assets, const ResTable_config& configuration);
@@ -156,10 +156,14 @@ class AssetManager2 {
// Sets/resets the configuration for this AssetManager. This will cause all
// caches that are related to the configuration change to be invalidated.
void SetConfiguration(const ResTable_config& configuration);
void SetConfigurations(std::vector<ResTable_config> configurations);
inline const ResTable_config& GetConfiguration() const {
return configuration_;
inline const std::vector<ResTable_config>& GetConfigurations() const {
return configurations_;
}
inline void SetDefaultLocale(uint32_t default_locale) {
default_locale_ = default_locale;
}
// Returns all configurations for which there are resources defined, or an I/O error if reading
@@ -465,9 +469,11 @@ class AssetManager2 {
// without taking too much memory.
std::array<uint8_t, std::numeric_limits<uint8_t>::max() + 1> package_ids_;
// The current configuration set for this AssetManager. When this changes, cached resources
uint32_t default_locale_;
// The current configurations set for this AssetManager. When this changes, cached resources
// may need to be purged.
ResTable_config configuration_ = {};
std::vector<ResTable_config> configurations_;
// Cached set of bags. These are cached because they can inherit keys from parent bags,
// which involves some calculation.

View File

@@ -1375,6 +1375,8 @@ struct ResTable_config
// match the requested configuration at all.
bool isLocaleBetterThan(const ResTable_config& o, const ResTable_config* requested) const;
bool isBetterThanBeforeLocale(const ResTable_config& o, const ResTable_config* requested) const;
String8 toString() const;
};

View File

@@ -228,10 +228,12 @@ static void BM_AssetManagerSetConfigurationFramework(benchmark::State& state) {
ResTable_config config;
memset(&config, 0, sizeof(config));
std::vector<ResTable_config> configs;
configs.push_back(config);
while (state.KeepRunning()) {
config.sdkVersion = ~config.sdkVersion;
assets.SetConfiguration(config);
configs[0].sdkVersion = ~configs[0].sdkVersion;
assets.SetConfigurations(configs);
}
}
BENCHMARK(BM_AssetManagerSetConfigurationFramework);

View File

@@ -113,7 +113,7 @@ TEST_F(AssetManager2Test, FindsResourceFromSingleApkAssets) {
desired_config.language[1] = 'e';
AssetManager2 assetmanager;
assetmanager.SetConfiguration(desired_config);
assetmanager.SetConfigurations({desired_config});
assetmanager.SetApkAssets({basic_assets_});
auto value = assetmanager.GetResource(basic::R::string::test1);
@@ -137,7 +137,7 @@ TEST_F(AssetManager2Test, FindsResourceFromMultipleApkAssets) {
desired_config.language[1] = 'e';
AssetManager2 assetmanager;
assetmanager.SetConfiguration(desired_config);
assetmanager.SetConfigurations({desired_config});
assetmanager.SetApkAssets({basic_assets_, basic_de_fr_assets_});
auto value = assetmanager.GetResource(basic::R::string::test1);
@@ -466,10 +466,10 @@ TEST_F(AssetManager2Test, ResolveDeepIdReference) {
TEST_F(AssetManager2Test, DensityOverride) {
AssetManager2 assetmanager;
assetmanager.SetApkAssets({basic_assets_, basic_xhdpi_assets_, basic_xxhdpi_assets_});
assetmanager.SetConfiguration({
assetmanager.SetConfigurations({{
.density = ResTable_config::DENSITY_XHIGH,
.sdkVersion = 21,
});
}});
auto value = assetmanager.GetResource(basic::R::string::density, false /*may_be_bag*/);
ASSERT_TRUE(value.has_value());
@@ -721,7 +721,7 @@ TEST_F(AssetManager2Test, GetLastPathWithoutEnablingReturnsEmpty) {
ResTable_config desired_config;
AssetManager2 assetmanager;
assetmanager.SetConfiguration(desired_config);
assetmanager.SetConfigurations({desired_config});
assetmanager.SetApkAssets({basic_assets_});
assetmanager.SetResourceResolutionLoggingEnabled(false);
@@ -736,7 +736,7 @@ TEST_F(AssetManager2Test, GetLastPathWithoutResolutionReturnsEmpty) {
ResTable_config desired_config;
AssetManager2 assetmanager;
assetmanager.SetConfiguration(desired_config);
assetmanager.SetConfigurations({desired_config});
assetmanager.SetApkAssets({basic_assets_});
auto result = assetmanager.GetLastResourceResolution();
@@ -751,7 +751,7 @@ TEST_F(AssetManager2Test, GetLastPathWithSingleApkAssets) {
AssetManager2 assetmanager;
assetmanager.SetResourceResolutionLoggingEnabled(true);
assetmanager.SetConfiguration(desired_config);
assetmanager.SetConfigurations({desired_config});
assetmanager.SetApkAssets({basic_assets_});
auto value = assetmanager.GetResource(basic::R::string::test1);
@@ -774,7 +774,7 @@ TEST_F(AssetManager2Test, GetLastPathWithMultipleApkAssets) {
AssetManager2 assetmanager;
assetmanager.SetResourceResolutionLoggingEnabled(true);
assetmanager.SetConfiguration(desired_config);
assetmanager.SetConfigurations({desired_config});
assetmanager.SetApkAssets({basic_assets_, basic_de_fr_assets_});
auto value = assetmanager.GetResource(basic::R::string::test1);
@@ -796,7 +796,7 @@ TEST_F(AssetManager2Test, GetLastPathAfterDisablingReturnsEmpty) {
AssetManager2 assetmanager;
assetmanager.SetResourceResolutionLoggingEnabled(true);
assetmanager.SetConfiguration(desired_config);
assetmanager.SetConfigurations({desired_config});
assetmanager.SetApkAssets({basic_assets_});
auto value = assetmanager.GetResource(basic::R::string::test1);
@@ -817,7 +817,7 @@ TEST_F(AssetManager2Test, GetOverlayablesToString) {
AssetManager2 assetmanager;
assetmanager.SetResourceResolutionLoggingEnabled(true);
assetmanager.SetConfiguration(desired_config);
assetmanager.SetConfigurations({desired_config});
assetmanager.SetApkAssets({overlayable_assets_});
const auto map = assetmanager.GetOverlayableMapForPackage(0x7f);

View File

@@ -66,7 +66,7 @@ void GetResourceBenchmark(const std::vector<std::string>& paths, const ResTable_
AssetManager2 assetmanager;
assetmanager.SetApkAssets(apk_assets);
if (config != nullptr) {
assetmanager.SetConfiguration(*config);
assetmanager.SetConfigurations({*config});
}
while (state.KeepRunning()) {

View File

@@ -260,7 +260,7 @@ TEST_F(ThemeTest, ThemeRebase) {
ResTable_config night{};
night.uiMode = ResTable_config::UI_MODE_NIGHT_YES;
night.version = 8u;
am_night.SetConfiguration(night);
am_night.SetConfigurations({night});
auto theme = am.NewTheme();
{

View File

@@ -36,7 +36,7 @@ void AConfiguration_delete(AConfiguration* config) {
void AConfiguration_fromAssetManager(AConfiguration* out, AAssetManager* am) {
ScopedLock<AssetManager2> locked_mgr(*AssetManagerForNdkAssetManager(am));
ResTable_config config = locked_mgr->GetConfiguration();
ResTable_config config = locked_mgr->GetConfigurations()[0];
// AConfiguration is not a virtual subclass, so we can memcpy.
memcpy(out, &config, sizeof(config));

View File

@@ -17,13 +17,13 @@ package com.android.server.pm.split;
import android.content.pm.parsing.ApkLiteParseUtils;
import android.content.pm.parsing.PackageLite;
import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
import com.android.server.pm.pkg.parsing.ParsingPackageUtils.ParseFlags;
import android.content.res.ApkAssets;
import android.content.res.AssetManager;
import android.os.Build;
import com.android.internal.util.ArrayUtils;
import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
import com.android.server.pm.pkg.parsing.ParsingPackageUtils.ParseFlags;
import libcore.io.IoUtils;
@@ -82,8 +82,8 @@ public class DefaultSplitAssetLoader implements SplitAssetLoader {
}
AssetManager assets = new AssetManager();
assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
Build.VERSION.RESOURCES_SDK_INT);
assets.setConfiguration(0, 0, null, new String[0], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, Build.VERSION.RESOURCES_SDK_INT);
assets.setApkAssets(apkAssets, false /*invalidateCaches*/);
mCachedAssetManager = assets;

View File

@@ -80,8 +80,8 @@ public class SplitAssetDependencyLoader extends SplitDependencyLoader<IllegalArg
private static AssetManager createAssetManagerWithAssets(ApkAssets[] apkAssets) {
final AssetManager assets = new AssetManager();
assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
Build.VERSION.RESOURCES_SDK_INT);
assets.setConfiguration(0, 0, null, new String[0], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, Build.VERSION.RESOURCES_SDK_INT);
assets.setApkAssets(apkAssets, false /*invalidateCaches*/);
return assets;
}