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

Commit ce30ccce authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Restore device locale from backup."

parents e9685cab 0f19cc77
Loading
Loading
Loading
Loading
+90 −33
Original line number Diff line number Diff line
@@ -16,6 +16,11 @@

package com.android.providers.settings;

import com.android.internal.R;
import com.android.internal.app.LocalePicker;
import com.android.internal.annotations.VisibleForTesting;

import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.IActivityManager;
import android.app.backup.IBackupManager;
@@ -24,11 +29,13 @@ import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.icu.util.ULocale;
import android.location.LocationManager;
import android.media.AudioManager;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.IPowerManager;
import android.os.LocaleList;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
@@ -39,6 +46,9 @@ import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Slog;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;

public class SettingsHelper {
@@ -305,59 +315,106 @@ public class SettingsHelper {
        }
    }

    byte[] getLocaleData() {
    /* package */ byte[] getLocaleData() {
        Configuration conf = mContext.getResources().getConfiguration();
        final Locale loc = conf.locale;
        String localeString = loc.getLanguage();
        String country = loc.getCountry();
        if (!TextUtils.isEmpty(country)) {
            localeString += "-" + country;
        return conf.getLocales().toLanguageTags().getBytes();
    }

    private static Locale toFullLocale(@NonNull Locale locale) {
        if (locale.getScript().isEmpty() || locale.getCountry().isEmpty()) {
            return ULocale.addLikelySubtags(ULocale.forLocale(locale)).toLocale();
        }
        return localeString.getBytes();
        return locale;
    }

    /**
     * Sets the locale specified. Input data is the byte representation of a
     * BCP-47 language tag. For backwards compatibility, strings of the form
     * Merging the locale came from backup server and current device locale.
     *
     * Merge works with following rules.
     * - The backup locales are appended to the current locale with keeping order.
     *   e.g. current locale "en-US,zh-CN" and backup locale "ja-JP,ko-KR" are merged to
     *   "en-US,zh-CH,ja-JP,ko-KR".
     *
     * - Duplicated locales are dropped.
     *   e.g. current locale "en-US,zh-CN" and backup locale "ja-JP,zh-Hans-CN,en-US" are merged to
     *   "en-US,zh-CN,ja-JP".
     *
     * - Unsupported locales are dropped.
     *   e.g. current locale "en-US" and backup locale "ja-JP,zh-CN" but the supported locales
     *   are "en-US,zh-CN", the merged locale list is "en-US,zh-CN".
     *
     * - The final result locale list only contains the supported locales.
     *   e.g. current locale "en-US" and backup locale "zh-Hans-CN" and supported locales are
     *   "en-US,zh-CN", the merged locale list is "en-US,zh-CN".
     *
     * @param restore The locale list that came from backup server.
     * @param current The device's locale setting.
     * @param supportedLocales The list of language tags supported by this device.
     */
    @VisibleForTesting
    public static LocaleList resolveLocales(LocaleList restore, LocaleList current,
            String[] supportedLocales) {
        final HashMap<Locale, Locale> allLocales = new HashMap<>(supportedLocales.length);
        for (String supportedLocaleStr : supportedLocales) {
            final Locale locale = Locale.forLanguageTag(supportedLocaleStr);
            allLocales.put(toFullLocale(locale), locale);
        }

        final ArrayList<Locale> filtered = new ArrayList<>(current.size());
        for (int i = 0; i < current.size(); i++) {
            final Locale locale = current.get(i);
            allLocales.remove(toFullLocale(locale));
            filtered.add(locale);
        }

        for (int i = 0; i < restore.size(); i++) {
            final Locale locale = allLocales.remove(toFullLocale(restore.get(i)));
            if (locale != null) {
                filtered.add(locale);
            }
        }

        if (filtered.size() == current.size()) {
            return current;  // Nothing added to current locale list.
        }

        return new LocaleList(filtered.toArray(new Locale[filtered.size()]));
    }

    /**
     * Sets the locale specified. Input data is the byte representation of comma separated
     * multiple BCP-47 language tags. For backwards compatibility, strings of the form
     * {@code ll_CC} are also accepted, where {@code ll} is a two letter language
     * code and {@code CC} is a two letter country code.
     *
     * @param data the locale string in bytes.
     * @param data the comma separated BCP-47 language tags in bytes.
     */
    void setLocaleData(byte[] data, int size) {
        // Check if locale was set by the user:
        final ContentResolver cr = mContext.getContentResolver();
        final boolean userSetLocale = mContext.getResources().getConfiguration().userSetLocale;
        final boolean provisioned = Settings.Global.getInt(cr,
                Settings.Global.DEVICE_PROVISIONED, 0) != 0;
        if (userSetLocale || provisioned) {
            // Don't change if user set it in the SetupWizard, or if this is a post-setup
            // deferred restore operation
            Slog.i(TAG, "Not applying restored locale; "
                    + (userSetLocale ? "user already specified" : "device already provisioned"));
            return;
        }
    /* package */ void setLocaleData(byte[] data, int size) {
        final Configuration conf = mContext.getResources().getConfiguration();

        final String[] availableLocales = mContext.getAssets().getLocales();
        // Replace "_" with "-" to deal with older backups.
        String localeCode = new String(data, 0, size).replace('_', '-');
        Locale loc = null;
        for (int i = 0; i < availableLocales.length; i++) {
            if (availableLocales[i].equals(localeCode)) {
                loc = Locale.forLanguageTag(localeCode);
                break;
        final String localeCodes = new String(data, 0, size).replace('_', '-');
        final LocaleList localeList = LocaleList.forLanguageTags(localeCodes);
        if (localeList.isEmpty()) {
            return;
        }

        final String[] supportedLocales = LocalePicker.getSupportedLocales(mContext);
        final LocaleList currentLocales = conf.getLocales();

        final LocaleList merged = resolveLocales(localeList, currentLocales, supportedLocales);
        if (merged.equals(currentLocales)) {
            return;
        }
        if (loc == null) return; // Couldn't find the saved locale in this version of the software

        try {
            IActivityManager am = ActivityManager.getService();
            Configuration config = am.getConfiguration();
            config.locale = loc;
            config.setLocales(merged);
            // indicate this isn't some passing default - the user wants this remembered
            config.userSetLocale = true;

            am.updateConfiguration(config);
            am.updatePersistentConfiguration(config);
        } catch (RemoteException e) {
            // Intentionally left blank
        }
+3 −2
Original line number Diff line number Diff line
@@ -4,10 +4,11 @@ include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := tests

# Note we statically link SettingsState to do some unit tests.  It's not accessible otherwise
# Note we statically link several classes to do some unit tests.  It's not accessible otherwise
# because this test is not an instrumentation test. (because the target runs in the system process.)
LOCAL_SRC_FILES := $(call all-subdir-java-files) \
    ../src/com/android/providers/settings/SettingsState.java
    ../src/com/android/providers/settings/SettingsState.java \
    ../src/com/android/providers/settings/SettingsHelper.java

LOCAL_STATIC_JAVA_LIBRARIES := android-support-test

+135 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 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.
 */

package com.android.providers.settings;

import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertSame;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.fail;

import com.android.internal.app.LocalePicker;
import com.android.providers.settings.SettingsHelper;

import android.os.LocaleList;
import android.support.test.runner.AndroidJUnit4;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

import org.junit.Test;
import org.junit.runner.RunWith;

/**
 * Tests for the SettingsHelperTest
 */
@RunWith(AndroidJUnit4.class)
public class SettingsHelperTest {
    @Test
    public void testResolveLocales() throws Exception {
        // Empty string from backup server
        assertEquals(LocaleList.forLanguageTags("en-US"),
                SettingsHelper.resolveLocales(
                        LocaleList.forLanguageTags(""),  // restore
                        LocaleList.forLanguageTags("en-US"),  // current
                        new String[] { "en-US" }));  // supported

        // Same as current settings
        assertEquals(LocaleList.forLanguageTags("en-US"),
                SettingsHelper.resolveLocales(
                        LocaleList.forLanguageTags("en-US"),  // restore
                        LocaleList.forLanguageTags("en-US"),  // current
                        new String[] { "en-US" }));  // supported

        assertEquals(LocaleList.forLanguageTags("en-US,ja-JP"),
                SettingsHelper.resolveLocales(
                        LocaleList.forLanguageTags("en-US,ja-JP"),  // restore
                        LocaleList.forLanguageTags("en-US,ja-JP"),  // current
                        new String[] { "en-US", "ja-JP" }));  // supported

        // Current locale must be kept at the first place.
        assertEquals(LocaleList.forLanguageTags("ja-JP,en-US"),
                SettingsHelper.resolveLocales(
                        LocaleList.forLanguageTags("en-US"),  // restore
                        LocaleList.forLanguageTags("ja-JP"),  // current
                        new String[] { "en-US", "ja-JP" }));  // supported

        assertEquals(LocaleList.forLanguageTags("ja-JP,ko-KR,en-US"),
                SettingsHelper.resolveLocales(
                        LocaleList.forLanguageTags("en-US"),  // restore
                        LocaleList.forLanguageTags("ja-JP,ko-KR"),  // current
                        new String[] { "en-US", "ja-JP", "ko-KR" }));  // supported

        assertEquals(LocaleList.forLanguageTags("ja-JP,en-US,ko-KR"),
                SettingsHelper.resolveLocales(
                        LocaleList.forLanguageTags("en-US,ko-KR"),  // restore
                        LocaleList.forLanguageTags("ja-JP"),  // current
                        new String[] { "en-US", "ja-JP", "ko-KR" }));  // supported

        // Duplicated entries must be removed.
        assertEquals(LocaleList.forLanguageTags("ja-JP,ko-KR,en-US"),
                SettingsHelper.resolveLocales(
                        LocaleList.forLanguageTags("en-US,ko-KR"),  // restore
                        LocaleList.forLanguageTags("ja-JP,ko-KR"),  // current
                        new String[] { "en-US", "ja-JP", "ko-KR" }));  // supported

        // Drop unsupported locales.
        assertEquals(LocaleList.forLanguageTags("en-US"),
                SettingsHelper.resolveLocales(
                        LocaleList.forLanguageTags("en-US,zh-CN"),  // restore
                        LocaleList.forLanguageTags("en-US"),  // current
                        new String[] { "en-US" }));  // supported

        // Comparison happens on fully-expanded locale.
        assertEquals(LocaleList.forLanguageTags("en-US,sr-Latn-SR,sr-Cryl-SR"),
                SettingsHelper.resolveLocales(
                        LocaleList.forLanguageTags("sr-Cryl-SR"),  // restore
                        LocaleList.forLanguageTags("en-US,sr-Latn-SR"),  // current
                        new String[] { "en-US", "sr-Latn-SR", "sr-Cryl-SR" }));  // supported

        assertEquals(LocaleList.forLanguageTags("en-US"),
                SettingsHelper.resolveLocales(
                        LocaleList.forLanguageTags("kk-Cryl-KZ"),  // restore
                        LocaleList.forLanguageTags("en-US"),  // current
                        new String[] { "en-US", "kk-Latn-KZ" }));  // supported

        assertEquals(LocaleList.forLanguageTags("en-US,kk-Cryl-KZ"),
                SettingsHelper.resolveLocales(
                        LocaleList.forLanguageTags("kk-Cryl-KZ"),  // restore
                        LocaleList.forLanguageTags("en-US"),  // current
                        new String[] { "en-US", "kk-Cryl-KZ" }));  // supported

        assertEquals(LocaleList.forLanguageTags("en-US,zh-CN"),
                SettingsHelper.resolveLocales(
                        LocaleList.forLanguageTags("zh-Hans-CN"),  // restore
                        LocaleList.forLanguageTags("en-US"),  // current
                        new String[] { "en-US", "zh-CN" }));  // supported

        assertEquals(LocaleList.forLanguageTags("en-US,zh-Hans-CN"),
                SettingsHelper.resolveLocales(
                        LocaleList.forLanguageTags("zh-CN"),  // restore
                        LocaleList.forLanguageTags("en-US"),  // current
                        new String[] { "en-US", "zh-Hans-CN" }));  // supported

        // Old langauge code should be updated.
        assertEquals(LocaleList.forLanguageTags("en-US,he-IL,id-ID,yi"),
                SettingsHelper.resolveLocales(
                        LocaleList.forLanguageTags("iw-IL,in-ID,ji"),  // restore
                        LocaleList.forLanguageTags("en-US"),  // current
                        new String[] { "he-IL", "id-ID", "yi" }));  // supported
    }
}