[Regulatory Labels] Load labels from overlays

When config_regulatory_info_overlay_package_name is configured, load
regulatory labels directly from overlay package.

Also fix RegulatoryInfoDisplayActivity missing dialog theme.

Bug: 283170651
Test: unit test
Test: manual - check regulatory labels
Change-Id: Ia1f6848abe0da9facf34f47abab09e88d57e74d6
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 2c3e7f3..a20c5e2 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -3759,6 +3759,7 @@
         <!-- Show regulatory info (from settings item or dialing "*#07#") -->
         <activity
             android:name="RegulatoryInfoDisplayActivity"
+            android:theme="@style/Theme.AlertDialog"
             android:label="@string/regulatory_labels"
             android:exported="true"
             android:enabled="@bool/config_show_regulatory_info">
diff --git a/res/values/config.xml b/res/values/config.xml
index 0c5c7a0..5ae0220 100755
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -345,6 +345,9 @@
     <bool name="config_show_manual">false</bool>
     <!-- Whether to show a preference item for regulatory information in About phone -->
     <bool name="config_show_regulatory_info">false</bool>
+    <!-- Package name of regulatory information overlay which provides mapping and contents.
+         Fetch resource from overlay package directly if this is set. -->
+    <string name="config_regulatory_info_overlay_package_name" translatable="false" />
 
     <!-- Whether to show a preference item for mobile plan -->
     <bool name="config_show_mobile_plan">true</bool>
diff --git a/src/com/android/settings/RegulatoryInfoDisplayActivity.java b/src/com/android/settings/RegulatoryInfoDisplayActivity.java
deleted file mode 100644
index 8f65051..0000000
--- a/src/com/android/settings/RegulatoryInfoDisplayActivity.java
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * Copyright (C) 2013 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.settings;
-
-import android.app.Activity;
-import android.content.DialogInterface;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.os.SystemProperties;
-import android.text.TextUtils;
-import android.view.Gravity;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import androidx.annotation.VisibleForTesting;
-import androidx.appcompat.app.AlertDialog;
-
-import java.util.Locale;
-
-/**
- * {@link Activity} that displays regulatory information for the "Regulatory information"
- * preference item, and when "*#07#" is dialed on the Phone keypad. To enable this feature,
- * set the "config_show_regulatory_info" boolean to true in a device overlay resource, and in the
- * same overlay, either add a drawable named "regulatory_info.png" containing a graphical version
- * of the required regulatory info (If ro.bootloader.hardware.sku property is set use
- * "regulatory_info_<sku>.png where sku is ro.bootloader.hardware.sku property value in lowercase"),
- * or add a string resource named "regulatory_info_text" with an HTML version of the required
- * information (text will be centered in the dialog).
- */
-public class RegulatoryInfoDisplayActivity extends Activity implements
-        DialogInterface.OnDismissListener {
-
-    private final String REGULATORY_INFO_RESOURCE = "regulatory_info";
-    private static final String DEFAULT_REGULATORY_INFO_FILEPATH =
-            "/data/misc/elabel/regulatory_info.png";
-    private static final String REGULATORY_INFO_FILEPATH_TEMPLATE =
-            "/data/misc/elabel/regulatory_info_%s.png";
-
-    /**
-     * Display the regulatory info graphic in a dialog window.
-     */
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        AlertDialog.Builder builder = new AlertDialog.Builder(this)
-                .setTitle(R.string.regulatory_labels)
-                .setOnDismissListener(this)
-                .setPositiveButton(android.R.string.ok, null /* onClickListener */);
-
-        boolean regulatoryInfoDrawableExists = false;
-
-        final String regulatoryInfoFile = getRegulatoryInfoImageFileName();
-        final Bitmap regulatoryInfoBitmap = BitmapFactory.decodeFile(regulatoryInfoFile);
-
-        if (regulatoryInfoBitmap != null) {
-            regulatoryInfoDrawableExists = true;
-        }
-
-        int resId = 0;
-        if (!regulatoryInfoDrawableExists) {
-            resId = getResourceId();
-        }
-        if (resId != 0) {
-            try {
-                Drawable d = getDrawable(resId);
-                // set to false if the width or height is <= 2
-                // (missing PNG can return an empty 2x2 pixel Drawable)
-                regulatoryInfoDrawableExists = (d.getIntrinsicWidth() > 2
-                        && d.getIntrinsicHeight() > 2);
-            } catch (Resources.NotFoundException ignored) {
-                regulatoryInfoDrawableExists = false;
-            }
-        }
-
-        CharSequence regulatoryText = getResources()
-                .getText(R.string.regulatory_info_text);
-
-        if (regulatoryInfoDrawableExists) {
-            View view = getLayoutInflater().inflate(R.layout.regulatory_info, null);
-            ImageView image = view.findViewById(R.id.regulatoryInfo);
-            if (regulatoryInfoBitmap != null) {
-                image.setImageBitmap(regulatoryInfoBitmap);
-            } else {
-                image.setImageResource(resId);
-            }
-            builder.setView(view);
-            builder.show();
-        } else if (regulatoryText.length() > 0) {
-            builder.setMessage(regulatoryText);
-            AlertDialog dialog = builder.show();
-            // we have to show the dialog first, or the setGravity() call will throw a NPE
-            TextView messageText = (TextView) dialog.findViewById(android.R.id.message);
-            messageText.setGravity(Gravity.CENTER);
-        } else {
-            // neither drawable nor text resource exists, finish activity
-            finish();
-        }
-    }
-
-    @VisibleForTesting
-    int getResourceId() {
-        // Use regulatory_info by default.
-        int resId = getResources().getIdentifier(
-                REGULATORY_INFO_RESOURCE, "drawable", getPackageName());
-
-        // When hardware sku property exists, use regulatory_info_<sku> resource if valid.
-        final String sku = getSku();
-        if (!TextUtils.isEmpty(sku)) {
-            String regulatory_info_res = REGULATORY_INFO_RESOURCE + "_" + sku.toLowerCase();
-            int id = getResources().getIdentifier(
-                    regulatory_info_res, "drawable", getPackageName());
-            if (id != 0) {
-                resId = id;
-            }
-        }
-
-        // When hardware coo property exists, use regulatory_info_<sku>_<coo> resource if valid.
-        final String coo = getCoo();
-        if (!TextUtils.isEmpty(coo) && !TextUtils.isEmpty(sku)) {
-            final String regulatory_info_coo_res =
-                    REGULATORY_INFO_RESOURCE + "_" + sku.toLowerCase() + "_" + coo.toLowerCase();
-            final int id = getResources().getIdentifier(
-                    regulatory_info_coo_res, "drawable", getPackageName());
-            if (id != 0) {
-                resId = id;
-            }
-        }
-        return resId;
-    }
-
-    @Override
-    public void onDismiss(DialogInterface dialog) {
-        finish();   // close the activity
-    }
-
-    private String getCoo() {
-        return SystemProperties.get("ro.boot.hardware.coo", "");
-    }
-
-    private String getSku() {
-        return SystemProperties.get("ro.boot.hardware.sku", "");
-    }
-
-    private String getRegulatoryInfoImageFileName() {
-        final String sku = getSku();
-        if (TextUtils.isEmpty(sku)) {
-            return DEFAULT_REGULATORY_INFO_FILEPATH;
-        } else {
-            return String.format(Locale.US, REGULATORY_INFO_FILEPATH_TEMPLATE,
-                    sku.toLowerCase());
-        }
-    }
-}
diff --git a/src/com/android/settings/RegulatoryInfoDisplayActivity.kt b/src/com/android/settings/RegulatoryInfoDisplayActivity.kt
new file mode 100644
index 0000000..ffacb9c
--- /dev/null
+++ b/src/com/android/settings/RegulatoryInfoDisplayActivity.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2023 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.settings
+
+import android.app.Activity
+import android.os.Bundle
+import android.view.Gravity
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.appcompat.app.AlertDialog
+import com.android.settings.deviceinfo.regulatory.RegulatoryInfo.getRegulatoryInfo
+
+/**
+ * [Activity] that displays regulatory information for the "Regulatory information"
+ * preference item, and when "*#07#" is dialed on the Phone keypad. To enable this feature,
+ * set the "config_show_regulatory_info" boolean to true in a device overlay resource, and in the
+ * same overlay, either add a drawable named "regulatory_info.png" containing a graphical version
+ * of the required regulatory info (If ro.bootloader.hardware.sku property is set use
+ * "regulatory_info_<sku>.png where sku is ro.bootloader.hardware.sku property value in lowercase"),
+ * or add a string resource named "regulatory_info_text" with an HTML version of the required
+ * information (text will be centered in the dialog).
+ */
+class RegulatoryInfoDisplayActivity : Activity() {
+
+    /** Display the regulatory info graphic in a dialog window. */
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        val builder = AlertDialog.Builder(this)
+            .setTitle(R.string.regulatory_labels)
+            .setOnDismissListener { finish() }  // close the activity
+            .setPositiveButton(android.R.string.ok, null)
+
+        getRegulatoryInfo()?.let {
+            val view = layoutInflater.inflate(R.layout.regulatory_info, null)
+            val image = view.findViewById<ImageView>(R.id.regulatoryInfo)
+            image.setImageDrawable(it)
+            builder.setView(view)
+            builder.show()
+            return
+        }
+
+        val regulatoryText = resources.getText(R.string.regulatory_info_text)
+        if (regulatoryText.isNotEmpty()) {
+            builder.setMessage(regulatoryText)
+            val dialog = builder.show()
+            // we have to show the dialog first, or the setGravity() call will throw a NPE
+            dialog.findViewById<TextView>(android.R.id.message)?.gravity = Gravity.CENTER
+        } else {
+            // neither drawable nor text resource exists, finish activity
+            finish()
+        }
+    }
+}
diff --git a/src/com/android/settings/deviceinfo/regulatory/RegulatoryInfo.kt b/src/com/android/settings/deviceinfo/regulatory/RegulatoryInfo.kt
new file mode 100644
index 0000000..e26e061
--- /dev/null
+++ b/src/com/android/settings/deviceinfo/regulatory/RegulatoryInfo.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2023 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.settings.deviceinfo.regulatory
+
+import android.content.Context
+import android.content.res.Resources
+import android.graphics.drawable.Drawable
+import android.os.SystemProperties
+import androidx.annotation.DrawableRes
+import androidx.annotation.VisibleForTesting
+import com.android.settings.R
+
+
+
+/** To load Regulatory Info from device. */
+object RegulatoryInfo {
+    private const val REGULATORY_INFO_RESOURCE = "regulatory_info"
+
+    @VisibleForTesting
+    const val KEY_COO = "ro.boot.hardware.coo"
+
+    @VisibleForTesting
+    const val KEY_SKU = "ro.boot.hardware.sku"
+
+    /** Gets the regulatory drawable. */
+    fun Context.getRegulatoryInfo(): Drawable? {
+        val sku = getSku()
+        if (sku.isNotBlank()) {
+            // When hardware coo property exists, use regulatory_info_<sku>_<coo> resource if valid.
+            val coo = getCoo()
+            if (coo.isNotBlank()) {
+                getRegulatoryInfo("${REGULATORY_INFO_RESOURCE}_${sku}_$coo")?.let { return it }
+            }
+            // Use regulatory_info_<sku> resource if valid.
+            getRegulatoryInfo("${REGULATORY_INFO_RESOURCE}_$sku")?.let { return it }
+        }
+        return getRegulatoryInfo(REGULATORY_INFO_RESOURCE)
+    }
+
+    private fun getCoo(): String = SystemProperties.get(KEY_COO).lowercase()
+
+    private fun getSku(): String = SystemProperties.get(KEY_SKU).lowercase()
+
+    private fun Context.getRegulatoryInfo(fileName: String): Drawable? {
+        val overlayPackageName =
+            resources.getString(R.string.config_regulatory_info_overlay_package_name)
+                .ifBlank { packageName }
+        val resources = packageManager.getResourcesForApplication(overlayPackageName)
+        val id = resources.getIdentifier(fileName, "drawable", overlayPackageName)
+        return if (id > 0) resources.getRegulatoryInfo(id) else null
+    }
+
+    private fun Resources.getRegulatoryInfo(@DrawableRes resId: Int): Drawable? = try {
+        getDrawable(resId, null).takeIf {
+            // Ignore the placeholder image
+            it.intrinsicWidth > 10 && it.intrinsicHeight > 10
+        }
+    } catch (_: Resources.NotFoundException) {
+        null
+    }
+}
diff --git a/tests/robotests/res/drawable/regulatory_info.png b/tests/robotests/res/drawable/regulatory_info.png
deleted file mode 100644
index 65de26c..0000000
--- a/tests/robotests/res/drawable/regulatory_info.png
+++ /dev/null
Binary files differ
diff --git a/tests/robotests/res/drawable/regulatory_info_sku.png b/tests/robotests/res/drawable/regulatory_info_sku.png
deleted file mode 100644
index 65de26c..0000000
--- a/tests/robotests/res/drawable/regulatory_info_sku.png
+++ /dev/null
Binary files differ
diff --git a/tests/robotests/res/drawable/regulatory_info_sku1_coo.png b/tests/robotests/res/drawable/regulatory_info_sku1_coo.png
deleted file mode 100644
index 65de26c..0000000
--- a/tests/robotests/res/drawable/regulatory_info_sku1_coo.png
+++ /dev/null
Binary files differ
diff --git a/tests/robotests/src/com/android/settings/RegulatoryInfoDisplayActivityTest.java b/tests/robotests/src/com/android/settings/RegulatoryInfoDisplayActivityTest.java
deleted file mode 100644
index d05d5d7..0000000
--- a/tests/robotests/src/com/android/settings/RegulatoryInfoDisplayActivityTest.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2019 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.settings;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.os.SystemProperties;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.Robolectric;
-import org.robolectric.RobolectricTestRunner;
-
-
-@RunWith(RobolectricTestRunner.class)
-public class RegulatoryInfoDisplayActivityTest {
-
-    private static final String SKU_PROP_KEY = "ro.boot.hardware.sku";
-    private static final String COO_PROP_KEY = "ro.boot.hardware.coo";
-
-    private RegulatoryInfoDisplayActivity mRegulatoryInfoDisplayActivity;
-
-    @Before
-    public void setUp() {
-        mRegulatoryInfoDisplayActivity = Robolectric.buildActivity(
-                RegulatoryInfoDisplayActivity.class).create().get();
-    }
-
-    @Test
-    public void getResourceId_noSkuProperty_shouldReturnDefaultLabel() {
-        SystemProperties.set(SKU_PROP_KEY, "");
-
-        final int expectedResId = getResourceId("regulatory_info");
-        assertThat(mRegulatoryInfoDisplayActivity.getResourceId()).isEqualTo(expectedResId);
-    }
-
-    @Test
-    public void getResourceId_noCooProperty_shouldReturnSkuLabel() {
-        SystemProperties.set(SKU_PROP_KEY, "sku");
-        SystemProperties.set(COO_PROP_KEY, "");
-
-        final int expectedResId = getResourceId("regulatory_info_sku");
-        assertThat(mRegulatoryInfoDisplayActivity.getResourceId()).isEqualTo(expectedResId);
-    }
-
-    @Test
-    public void getResourceId_hasSkuAndCooProperties_shouldReturnCooLabel() {
-        SystemProperties.set(SKU_PROP_KEY, "sku1");
-        SystemProperties.set(COO_PROP_KEY, "coo");
-
-        final int expectedResId = getResourceId("regulatory_info_sku1_coo");
-        assertThat(mRegulatoryInfoDisplayActivity.getResourceId()).isEqualTo(expectedResId);
-    }
-
-    @Test
-    public void getResourceId_noCorrespondingCooLabel_shouldReturnSkuLabel() {
-        SystemProperties.set(SKU_PROP_KEY, "sku");
-        SystemProperties.set(COO_PROP_KEY, "unknown");
-
-        final int expectedResId = getResourceId("regulatory_info_sku");
-        assertThat(mRegulatoryInfoDisplayActivity.getResourceId()).isEqualTo(expectedResId);
-    }
-
-    private int getResourceId(String resourceName) {
-        return mRegulatoryInfoDisplayActivity.getResources().getIdentifier(resourceName, "drawable",
-                mRegulatoryInfoDisplayActivity.getPackageName());
-    }
-}
diff --git a/tests/spa_unit/res/drawable/regulatory_info.xml b/tests/spa_unit/res/drawable/regulatory_info.xml
new file mode 100644
index 0000000..7cc3803
--- /dev/null
+++ b/tests/spa_unit/res/drawable/regulatory_info.xml
@@ -0,0 +1,20 @@
+<!--
+  ~ Copyright (C) 2023 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.
+  -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="#ff0000"/>
+    <size android:width="24dp" android:height="24dp" />
+</shape>
diff --git a/tests/spa_unit/res/drawable/regulatory_info_sku.xml b/tests/spa_unit/res/drawable/regulatory_info_sku.xml
new file mode 100644
index 0000000..634e55e
--- /dev/null
+++ b/tests/spa_unit/res/drawable/regulatory_info_sku.xml
@@ -0,0 +1,21 @@
+<!--
+  ~ Copyright (C) 2023 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.
+  -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="#00ff00"/>
+    <size android:width="24dp" android:height="24dp" />
+</shape>
+
diff --git a/tests/spa_unit/res/drawable/regulatory_info_sku1_coo.xml b/tests/spa_unit/res/drawable/regulatory_info_sku1_coo.xml
new file mode 100644
index 0000000..7e6b9ef
--- /dev/null
+++ b/tests/spa_unit/res/drawable/regulatory_info_sku1_coo.xml
@@ -0,0 +1,20 @@
+<!--
+  ~ Copyright (C) 2023 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.
+  -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="#0000ff"/>
+    <size android:width="24dp" android:height="24dp" />
+</shape>
diff --git a/tests/spa_unit/src/com/android/settings/deviceinfo/regulatory/RegulatoryInfoTest.kt b/tests/spa_unit/src/com/android/settings/deviceinfo/regulatory/RegulatoryInfoTest.kt
new file mode 100644
index 0000000..f1e18fc
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/deviceinfo/regulatory/RegulatoryInfoTest.kt
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2023 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.settings.deviceinfo.regulatory
+
+import android.content.Context
+import android.graphics.drawable.Drawable
+import android.os.SystemProperties
+import androidx.annotation.DrawableRes
+import androidx.core.graphics.drawable.toBitmap
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.dx.mockito.inline.extended.ExtendedMockito
+import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
+import com.android.settings.deviceinfo.regulatory.RegulatoryInfo.KEY_COO
+import com.android.settings.deviceinfo.regulatory.RegulatoryInfo.KEY_SKU
+import com.android.settings.deviceinfo.regulatory.RegulatoryInfo.getRegulatoryInfo
+import com.android.settings.tests.spa_unit.R
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.MockitoSession
+import org.mockito.Spy
+import org.mockito.quality.Strictness
+
+@RunWith(AndroidJUnit4::class)
+class RegulatoryInfoTest {
+    private lateinit var mockSession: MockitoSession
+
+    @Spy
+    private val context: Context = ApplicationProvider.getApplicationContext()
+
+    @Before
+    fun setUp() {
+        mockSession = ExtendedMockito.mockitoSession()
+            .initMocks(this)
+            .mockStatic(SystemProperties::class.java)
+            .strictness(Strictness.LENIENT)
+            .startMocking()
+    }
+
+    @After
+    fun tearDown() {
+        mockSession.finishMocking()
+    }
+
+    @Test
+    fun getRegulatoryInfo_noSkuProperty_shouldReturnDefaultLabel() {
+        doReturn("").`when` { SystemProperties.get(KEY_SKU) }
+
+        val regulatoryInfo = context.getRegulatoryInfo()
+
+        assertDrawableSameAs(regulatoryInfo, R.drawable.regulatory_info)
+    }
+
+    @Test
+    fun getResourceId_noCooProperty_shouldReturnSkuLabel() {
+        doReturn("sku").`when` { SystemProperties.get(KEY_SKU) }
+        doReturn("").`when` { SystemProperties.get(KEY_COO) }
+
+        val regulatoryInfo = context.getRegulatoryInfo()
+
+        assertDrawableSameAs(regulatoryInfo, R.drawable.regulatory_info_sku)
+    }
+
+    @Test
+    fun getResourceId_hasSkuAndCooProperties_shouldReturnCooLabel() {
+        doReturn("sku1").`when` { SystemProperties.get(KEY_SKU) }
+        doReturn("coo").`when` { SystemProperties.get(KEY_COO) }
+
+        val regulatoryInfo = context.getRegulatoryInfo()
+
+        assertDrawableSameAs(regulatoryInfo, R.drawable.regulatory_info_sku1_coo)
+    }
+
+    @Test
+    fun getResourceId_noCorrespondingCooLabel_shouldReturnSkuLabel() {
+        doReturn("sku").`when` { SystemProperties.get(KEY_SKU) }
+        doReturn("unknown").`when` { SystemProperties.get(KEY_COO) }
+
+        val regulatoryInfo = context.getRegulatoryInfo()
+
+        assertDrawableSameAs(regulatoryInfo, R.drawable.regulatory_info_sku)
+    }
+
+    private fun assertDrawableSameAs(drawable: Drawable?, @DrawableRes resId: Int) {
+        val expected = context.getDrawable(resId)!!.toBitmap()
+        assertThat(drawable!!.toBitmap().sameAs(expected)).isTrue()
+    }
+}