Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1c686f2c authored by Roozbeh Pournader's avatar Roozbeh Pournader
Browse files

Avoid matching system locales in locale negotiation

Also:
1. Add AssetManager method for finding non-system locales: This is
used in per-app locale negotiation. (Normally,
AssetManager#getLocales() returns both system and non-system
locales.)

2. Match pseudolocales correctly in locale negotiation.

Bug: 25800576
Bug: 26236938
Change-Id: I116caf3a91c290deb4ad68b291c65b7035b18dd4
parent c1f9e7c4
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -698,6 +698,18 @@ public final class AssetManager implements AutoCloseable {
     */
    public native final String[] getLocales();

    /**
     * Same as getLocales(), except that locales that are only provided by the system (i.e. those
     * present in framework-res.apk or its overlays) will not be listed.
     *
     * For example, if the "system" assets support English, French, and German, and the additional
     * assets support Cherokee and French, getLocales() would return
     * [Cherokee, English, French, German], while getNonSystemLocales() would return
     * [Cherokee, French].
     * {@hide}
     */
    public native final String[] getNonSystemLocales();

    /** {@hide} */
    public native final Configuration[] getSizeConfigurations();

+15 −1
Original line number Diff line number Diff line
@@ -1976,7 +1976,21 @@ public class Resources {

            if (setLocalesToDefault || mResolvedLocale == null
                    || (configChanges & Configuration.NATIVE_CONFIG_LOCALE) != 0) {
                mResolvedLocale = locales.getFirstMatch(mAssets.getLocales());
                if (locales.size() == 1) {
                    // This is an optimization to avoid the JNI call(s) when the result of
                    // getFirstMatch() does not depend on the supported locales.
                    mResolvedLocale = locales.getPrimary();
                } else {
                    String[] supportedLocales = mAssets.getNonSystemLocales();
                    if (LocaleList.isPseudoLocalesOnly(supportedLocales)) {
                        // We fallback to all locales (including system locales) if there was no
                        // locale specifically supported by the assets. This is to properly support
                        // apps that only rely on the shared system assets and don't need assets of
                        // their own.
                        supportedLocales = mAssets.getLocales();
                    }
                    mResolvedLocale = locales.getFirstMatch(supportedLocales);
                }
            }
            mAssets.setConfiguration(mConfiguration.mcc, mConfiguration.mnc,
                    adjustLanguageTag(mResolvedLocale.toLanguageTag()),
+40 −1
Original line number Diff line number Diff line
@@ -219,6 +219,20 @@ public final class LocaleList implements Parcelable {
        }
    }

    private static final String STRING_EN_XA = "en-XA";
    private static final String STRING_AR_XB = "ar-XB";
    private static final Locale LOCALE_EN_XA = new Locale("en", "XA");
    private static final Locale LOCALE_AR_XB = new Locale("ar", "XB");
    private static final int NUM_PSEUDO_LOCALES = 2;

    private static boolean isPseudoLocale(String locale) {
        return STRING_EN_XA.equals(locale) || STRING_AR_XB.equals(locale);
    }

    private static boolean isPseudoLocale(Locale locale) {
        return LOCALE_EN_XA.equals(locale) || LOCALE_AR_XB.equals(locale);
    }

    private static int matchScore(Locale supported, Locale desired) {
        if (supported.equals(desired)) {
            return 1;  // return early so we don't do unnecessary computation
@@ -226,6 +240,11 @@ public final class LocaleList implements Parcelable {
        if (!supported.getLanguage().equals(desired.getLanguage())) {
            return 0;
        }
        if (isPseudoLocale(supported) || isPseudoLocale(desired)) {
            // The locales are not the same, but the languages are the same, and one of the locales
            // is a pseudo-locale. So this is not a match.
            return 0;
        }
        // There is no match if the two locales use different scripts. This will most imporantly
        // take care of traditional vs simplified Chinese.
        final String supportedScr = getLikelyScript(supported);
@@ -247,7 +266,6 @@ public final class LocaleList implements Parcelable {
        if (mList.length == 0) {  // empty locale list
            return null;
        }
        // TODO: Figure out what to if en-XA or ar-XB are in the locale list
        int bestIndex = Integer.MAX_VALUE;
        for (String tag : supportedLocales) {
            final Locale supportedLocale = Locale.forLanguageTag(tag);
@@ -271,6 +289,27 @@ public final class LocaleList implements Parcelable {
        }
    }

    /**
     * Returns true if the array of locale tags only contains empty locales and pseudolocales.
     * Assumes that there is no repetition in the input.
     * {@hide}
     */
    public static boolean isPseudoLocalesOnly(String[] supportedLocales) {
        if (supportedLocales.length > NUM_PSEUDO_LOCALES + 1) {
            // This is for optimization. Since there's no repetition in the input, if we have more
            // than the number of pseudo-locales plus one for the empty string, it's guaranteed
            // that we have some meaninful locale in the list, so the list is not "practically
            // empty".
            return false;
        }
        for (String locale : supportedLocales) {
            if (!locale.isEmpty() && !isPseudoLocale(locale)) {
                return false;
            }
        }
        return true;
    }

    private final static Object sLock = new Object();

    @GuardedBy("sLock")
+14 −2
Original line number Diff line number Diff line
@@ -578,7 +578,7 @@ static jboolean android_content_AssetManager_isUpToDate(JNIEnv* env, jobject cla
    return am->isUpToDate() ? JNI_TRUE : JNI_FALSE;
}

static jobjectArray android_content_AssetManager_getLocales(JNIEnv* env, jobject clazz)
static jobjectArray getLocales(JNIEnv* env, jobject clazz, bool includeSystemLocales)
{
    Vector<String8> locales;

@@ -587,7 +587,7 @@ static jobjectArray android_content_AssetManager_getLocales(JNIEnv* env, jobject
        return NULL;
    }

    am->getLocales(&locales);
    am->getLocales(&locales, includeSystemLocales);

    const int N = locales.size();

@@ -608,6 +608,16 @@ static jobjectArray android_content_AssetManager_getLocales(JNIEnv* env, jobject
    return result;
}

static jobjectArray android_content_AssetManager_getLocales(JNIEnv* env, jobject clazz)
{
    return getLocales(env, clazz, true /* include system locales */);
}

static jobjectArray android_content_AssetManager_getNonSystemLocales(JNIEnv* env, jobject clazz)
{
    return getLocales(env, clazz, false /* don't include system locales */);
}

static jobject constructConfigurationObject(JNIEnv* env, const ResTable_config& config) {
    jobject result = env->NewObject(gConfigurationOffsets.classObject,
            gConfigurationOffsets.constructor);
@@ -2154,6 +2164,8 @@ static const JNINativeMethod gAssetManagerMethods[] = {
    // Resources.
    { "getLocales",      "()[Ljava/lang/String;",
        (void*) android_content_AssetManager_getLocales },
    { "getNonSystemLocales", "()[Ljava/lang/String;",
        (void*) android_content_AssetManager_getNonSystemLocales },
    { "getSizeConfigurations", "()[Landroid/content/res/Configuration;",
        (void*) android_content_AssetManager_getSizeConfigurations },
    { "setConfiguration", "!(IILjava/lang/String;IIIIIIIIIIIIII)V",
+10 −7
Original line number Diff line number Diff line
@@ -100,7 +100,8 @@ public:
     * then on success, *cookie is set to the value corresponding to the
     * newly-added asset source.
     */
    bool addAssetPath(const String8& path, int32_t* cookie, bool appAsLib=false);
    bool addAssetPath(const String8& path, int32_t* cookie,
        bool appAsLib=false, bool isSystemAsset=false);
    bool addOverlayPath(const String8& path, int32_t* cookie);

    /*
@@ -225,7 +226,7 @@ public:
    /**
     * Get the known locales for this asset manager object.
     */
    void getLocales(Vector<String8>* locales) const;
    void getLocales(Vector<String8>* locales, bool includeSystemLocales=true) const;

    /**
     * Generate idmap data to translate resources IDs between a package and a
@@ -237,11 +238,13 @@ public:
private:
    struct asset_path
    {
        asset_path() : path(""), type(kFileTypeRegular), idmap(""), isSystemOverlay(false) {}
        asset_path() : path(""), type(kFileTypeRegular), idmap(""),
                       isSystemOverlay(false), isSystemAsset(false) {}
        String8 path;
        FileType type;
        String8 idmap;
        bool isSystemOverlay;
        bool isSystemAsset;
    };

    Asset* openInPathLocked(const char* fileName, AccessMode mode,
Loading