Integrate system language filtering functionality

Filter system languages based on the configuration provided via
the corresponding system property.

Bug: 154133013
Test: manual; atest LocalizationTest
Change-Id: I7e4694f4b381ab81d0e8ceab97bad68bb266aaee
diff --git a/tests/LocalizationTest/Android.bp b/tests/LocalizationTest/Android.bp
new file mode 100644
index 0000000..c4bfcb1
--- /dev/null
+++ b/tests/LocalizationTest/Android.bp
@@ -0,0 +1,41 @@
+// Copyright (C) 2020 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.
+
+android_test {
+    name: "LocalizationTest",
+    srcs: ["java/**/*.kt"],
+    libs: [
+        "android.test.runner",
+        "android.test.base",
+        "android.test.mock",
+    ],
+    static_libs: [
+        "androidx.test.core",
+        "androidx.test.ext.junit",
+        "androidx.test.rules",
+        "mockito-target-extended-minus-junit4",
+        "truth-prebuilt",
+    ],
+    jni_libs: [
+        // For mockito extended
+        "libdexmakerjvmtiagent",
+        "libstaticjvmtiagent",
+    ],
+    certificate: "platform",
+    platform_apis: true,
+    test_suites: ["device-tests"],
+    optimize: {
+        enabled: false,
+    },
+}
diff --git a/tests/LocalizationTest/AndroidManifest.xml b/tests/LocalizationTest/AndroidManifest.xml
new file mode 100644
index 0000000..b135443
--- /dev/null
+++ b/tests/LocalizationTest/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.android.internal.app">
+
+    <application android:debuggable="true"  android:testOnly="true">
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation
+        android:name="androidx.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.android.internal.app"
+        android:label="Localization Tests" />
+
+</manifest>
diff --git a/tests/LocalizationTest/AndroidTest.xml b/tests/LocalizationTest/AndroidTest.xml
new file mode 100644
index 0000000..8309b4f
--- /dev/null
+++ b/tests/LocalizationTest/AndroidTest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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.
+  -->
+<configuration description="Localization Tests.">
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="apct-instrumentation" />
+
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="install-arg" value="-t" />
+        <option name="test-file-name" value="LocalizationTest.apk" />
+    </target_preparer>
+
+    <option name="test-tag" value="LocalizationTest" />
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="com.android.android.internal.app" />
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+        <option name="hidden-api-checks" value="false"/>
+    </test>
+</configuration>
\ No newline at end of file
diff --git a/tests/LocalizationTest/java/com/android/internal/app/LocalizationTest.kt b/tests/LocalizationTest/java/com/android/internal/app/LocalizationTest.kt
new file mode 100644
index 0000000..22ea971
--- /dev/null
+++ b/tests/LocalizationTest/java/com/android/internal/app/LocalizationTest.kt
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2020 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.android.internal.app
+
+import android.os.SystemProperties
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
+import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
+import com.android.internal.R
+import com.android.internal.app.LocalePicker
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.After
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.MockitoSession
+
+@RunWith(AndroidJUnit4::class)
+class LocalizationTest {
+    private val mContext = InstrumentationRegistry.getInstrumentation().context
+    private val mUnfilteredLocales =
+            mContext.getResources().getStringArray(R.array.supported_locales)
+
+    private lateinit var mMockitoSession: MockitoSession
+
+    @Before
+    fun setUp() {
+        mMockitoSession = mockitoSession()
+                .initMocks(this)
+                .spyStatic(SystemProperties::class.java)
+                .startMocking()
+    }
+
+    @After
+    fun tearDown() {
+        mMockitoSession.finishMocking()
+    }
+
+    @Test
+    fun testGetSupportedLocales_noFilter() {
+        // Filter not set.
+        setTestLocaleFilter(null)
+
+        val locales1 = LocalePicker.getSupportedLocales(mContext)
+
+        assertThat(locales1).isEqualTo(mUnfilteredLocales)
+
+        // Empty filter.
+        setTestLocaleFilter("")
+
+        val locales2 = LocalePicker.getSupportedLocales(mContext)
+
+        assertThat(locales2).isEqualTo(mUnfilteredLocales)
+    }
+
+    @Test
+    fun testGetSupportedLocales_invalidFilter() {
+        setTestLocaleFilter("**")
+
+        val locales = LocalePicker.getSupportedLocales(mContext)
+
+        assertThat(locales).isEqualTo(mUnfilteredLocales)
+    }
+
+    @Test
+    fun testGetSupportedLocales_inclusiveFilter() {
+        setTestLocaleFilter("^(de-AT|de-DE|en|ru).*")
+
+        val locales = LocalePicker.getSupportedLocales(mContext)
+
+        assertThat(locales).isEqualTo(
+                mUnfilteredLocales
+                        .filter { it.startsWithAnyOf("de-AT", "de-DE", "en", "ru") }
+                        .toTypedArray()
+        )
+    }
+
+    @Test
+    fun testGetSupportedLocales_exclusiveFilter() {
+        setTestLocaleFilter("^(?!de-IT|es|fr).*")
+
+        val locales = LocalePicker.getSupportedLocales(mContext)
+
+        assertThat(locales).isEqualTo(
+                mUnfilteredLocales
+                        .filter { !it.startsWithAnyOf("de-IT", "es", "fr") }
+                        .toTypedArray()
+        )
+    }
+
+    private fun setTestLocaleFilter(localeFilter: String?) {
+        doReturn(localeFilter).`when` { SystemProperties.get(eq("ro.localization.locale_filter")) }
+    }
+
+    private fun String.startsWithAnyOf(vararg prefixes: String): Boolean {
+        prefixes.forEach {
+            if (startsWith(it)) return true
+        }
+
+        return false
+    }
+}