Add SKU filter for CarrierConfig feature

Add SKU filter for CarrierConfig feature to allow to control by new
resouces config("sku_filter").

Currently, carrier features are customizable by SIM or device.
This new filter helps OEM to control carrier feature in special case
that any feature is required regardless of both SIM and device.

Test: manual - Checked that setting can read regardless of SIM when
setting sku.
Test: auto - Passed CarrierConfigTest.
Bug: 117527344

Change-Id: I102375886778add648d4556738c5ae214977a304
Merged-In: I102375886778add648d4556738c5ae214977a304
(cherry picked from commit 007e9c791bf164effe690a42624232fc0d6b27ed)
diff --git a/res/values/config.xml b/res/values/config.xml
new file mode 100644
index 0000000..d742a9a
--- /dev/null
+++ b/res/values/config.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+
+<resources>
+    <!-- Setting for SKU filter to be customizable. -->
+    <string name="sku_filter" translatable="false"></string>
+</resources>
\ No newline at end of file
diff --git a/src/com/android/carrierconfig/DefaultCarrierConfigService.java b/src/com/android/carrierconfig/DefaultCarrierConfigService.java
index 5227956..13a5bd1 100644
--- a/src/com/android/carrierconfig/DefaultCarrierConfigService.java
+++ b/src/com/android/carrierconfig/DefaultCarrierConfigService.java
@@ -92,18 +92,19 @@
 
     PersistableBundle loadConfig(XmlPullParser parser, @Nullable CarrierIdentifier id) {
         PersistableBundle config = new PersistableBundle();
+        String sku = getApplicationContext().getResources().getString(R.string.sku_filter);
 
         if (id == null) {
             try {
                 // Load no SIM config if carrier id is not set.
                 parser.setInput(getApplicationContext().getAssets().open(
                         NO_SIM_CONFIG_FILE_NAME), "utf-8");
-                config = readConfigFromXml(parser, null);
+                config = readConfigFromXml(parser, null, sku);
 
                 // Treat vendor_no_sim.xml as if it were appended to the no sim config file.
                 XmlPullParser vendorInput =
                         getApplicationContext().getResources().getXml(R.xml.vendor_no_sim);
-                PersistableBundle vendorConfig = readConfigFromXml(vendorInput, null);
+                PersistableBundle vendorConfig = readConfigFromXml(vendorInput, null, sku);
                 config.putAll(vendorConfig);
             }
             catch (IOException|XmlPullParserException e) {
@@ -125,14 +126,14 @@
                 for (String file : getApplicationContext().getAssets().list("")) {
                     if (file.startsWith(CARRIER_ID_PREFIX + id.getSpecificCarrierId() + "_")) {
                         parser.setInput(getApplicationContext().getAssets().open(file), "utf-8");
-                        configBySpecificCarrierId = readConfigFromXml(parser, null);
+                        configBySpecificCarrierId = readConfigFromXml(parser, null, sku);
                         break;
                     } else if (file.startsWith(CARRIER_ID_PREFIX + id.getCarrierId() + "_")) {
                         parser.setInput(getApplicationContext().getAssets().open(file), "utf-8");
-                        configByCarrierId = readConfigFromXml(parser, null);
+                        configByCarrierId = readConfigFromXml(parser, null, sku);
                     } else if (file.startsWith(CARRIER_ID_PREFIX + mccmncCarrierId + "_")) {
                         parser.setInput(getApplicationContext().getAssets().open(file), "utf-8");
-                        configByMccMncFallBackCarrierId = readConfigFromXml(parser, null);
+                        configByMccMncFallBackCarrierId = readConfigFromXml(parser, null, sku);
                     }
                 }
 
@@ -149,7 +150,7 @@
                 // fallback to use mccmnc.xml when there is no carrier id named config found.
                 parser.setInput(getApplicationContext().getAssets().open(
                         MCCMNC_PREFIX + id.getMcc() + id.getMnc() + ".xml"), "utf-8");
-                config = readConfigFromXml(parser, id);
+                config = readConfigFromXml(parser, id, sku);
             }
         }
         catch (IOException | XmlPullParserException e) {
@@ -161,7 +162,7 @@
         // Treat vendor.xml as if it were appended to the carrier config file we read.
         XmlPullParser vendorInput = getApplicationContext().getResources().getXml(R.xml.vendor);
         try {
-            PersistableBundle vendorConfig = readConfigFromXml(vendorInput, id);
+            PersistableBundle vendorConfig = readConfigFromXml(vendorInput, id, sku);
             config.putAll(vendorConfig);
         }
         catch (IOException | XmlPullParserException e) {
@@ -210,10 +211,11 @@
      * @param id the details of the SIM operator used to filter parts of the document. If read from
      *           files named after carrier id, this will be set to {@null code} as no filter match
      *           needed.
+     * @param sku a filter to be customizable.
      * @return a possibly empty PersistableBundle containing the config values.
      */
-    static PersistableBundle readConfigFromXml(XmlPullParser parser, @Nullable CarrierIdentifier id)
-            throws IOException, XmlPullParserException {
+    static PersistableBundle readConfigFromXml(XmlPullParser parser, @Nullable CarrierIdentifier id,
+            String sku) throws IOException, XmlPullParserException {
         PersistableBundle config = new PersistableBundle();
 
         if (parser == null) {
@@ -226,7 +228,7 @@
         while (((event = parser.next()) != XmlPullParser.END_DOCUMENT)) {
             if (event == XmlPullParser.START_TAG && "carrier_config".equals(parser.getName())) {
                 // Skip this fragment if it has filters that don't match.
-                if (id != null && !checkFilters(parser, id)) {
+                if (!checkFilters(parser, id, sku)) {
                     continue;
                 }
                 PersistableBundle configFragment = PersistableBundle.restoreFromXml(parser);
@@ -241,9 +243,9 @@
      * Checks to see if an XML node matches carrier filters.
      *
      * <p>This iterates over the attributes of the current tag pointed to by {@code parser} and
-     * checks each one against {@code id} or {@link Build.DEVICE}. Attributes that are not specified
-     * in the node will not be checked, so a node with no attributes will always return true. The
-     * supported filter attributes are,
+     * checks each one against {@code id} or {@link Build.DEVICE} or {@link R.string#sku_filter}.
+     * Attributes that are not specified in the node will not be checked, so a node with no
+     * attributes will always return true. The supported filter attributes are,
      * <ul>
      *   <li>mcc: {@link CarrierIdentifier#getMcc}</li>
      *   <li>mnc: {@link CarrierIdentifier#getMnc}</li>
@@ -254,6 +256,7 @@
      *   <li>device: {@link Build.DEVICE}</li>
      *   <li>cid: {@link CarrierIdentifier#getCarrierId()}
      *   or {@link CarrierIdentifier#getSpecificCarrierId()}</li>
+     *   <li>sku: {@link R.string#sku_filter} "sku_filter" that OEM customizable filter</li>
      * </ul>
      * </p>
      *
@@ -265,42 +268,46 @@
      *
      * @param parser an XmlPullParser pointing at a START_TAG with the attributes to check.
      * @param id the carrier details to check against.
+     * @param sku a filter to be customizable.
      * @return false if any XML attribute does not match the corresponding value.
      */
-    static boolean checkFilters(XmlPullParser parser, CarrierIdentifier id) {
+    static boolean checkFilters(XmlPullParser parser, @Nullable CarrierIdentifier id, String sku) {
         boolean result = true;
         for (int i = 0; i < parser.getAttributeCount(); ++i) {
             String attribute = parser.getAttributeName(i);
             String value = parser.getAttributeValue(i);
             switch (attribute) {
                 case "mcc":
-                    result = result && value.equals(id.getMcc());
+                    result = result && (id == null || value.equals(id.getMcc()));
                     break;
                 case "mnc":
-                    result = result && value.equals(id.getMnc());
+                    result = result && (id == null || value.equals(id.getMnc()));
                     break;
                 case "gid1":
-                    result = result && value.equalsIgnoreCase(id.getGid1());
+                    result = result && (id == null || value.equalsIgnoreCase(id.getGid1()));
                     break;
                 case "gid2":
-                    result = result && value.equalsIgnoreCase(id.getGid2());
+                    result = result && (id == null || value.equalsIgnoreCase(id.getGid2()));
                     break;
                 case "spn":
-                    result = result && matchOnSP(value, id);
+                    result = result && (id == null || matchOnSP(value, id));
                     break;
                 case "imsi":
-                    result = result && matchOnImsi(value, id);
+                    result = result && (id == null || matchOnImsi(value, id));
                     break;
                 case "device":
                     result = result && value.equalsIgnoreCase(Build.DEVICE);
                     break;
                 case "cid":
-                    result = result && ((Integer.parseInt(value) == id.getCarrierId())
+                    result = result && (id == null || (Integer.parseInt(value) == id.getCarrierId())
                             || (Integer.parseInt(value) == id.getSpecificCarrierId()));
                     break;
                 case "name":
                     // name is used together with cid for readability. ignore for filter.
                     break;
+                case "sku":
+                    result = result && value.equalsIgnoreCase(sku);
+                    break;
                 default:
                     Log.e(TAG, "Unknown attribute " + attribute + "=" + value);
                     result = false;
diff --git a/tests/src/com/android/carrierconfig/CarrierConfigTest.java b/tests/src/com/android/carrierconfig/CarrierConfigTest.java
index 4b78fd6..4c9720d 100644
--- a/tests/src/com/android/carrierconfig/CarrierConfigTest.java
+++ b/tests/src/com/android/carrierconfig/CarrierConfigTest.java
@@ -40,7 +40,7 @@
             public void check(XmlPullParser parser, String mccmnc) throws XmlPullParserException,
                     IOException {
                 PersistableBundle b = DefaultCarrierConfigService.readConfigFromXml(parser,
-                        new CarrierIdentifier("001", "001", "Test", "001001123456789", "", ""));
+                        new CarrierIdentifier("001", "001", "Test", "001001123456789", "", ""), "");
                 assertNotNull("got null bundle", b);
             }
         });
@@ -70,6 +70,7 @@
                                 case "device":
                                 case "cid":
                                 case "name":
+                                case "sku":
                                     break;
                                 default:
                                     fail("Unknown attribute '" + attribute