Extend 'phone cc set-value' to support bundle-valued carrier config

1. Created new command "set-values-from-xml" to allow setting config
   using an XML file that follows AOSP CarrierConfig format from stdin

E.g.

$ adb shell cmd phone cc set-values-from-xml < test.xml
Previous value:
inflate_signal_strength_bool
BOOLEAN         true
New value:
inflate_signal_strength_bool
BOOLEAN         true
Previous value:
opportunistic.5g_data_switch_hysteresis_time_long_bundle
PERSISTABLE_BUNDLE PersistableBundle[{}]
New value:
opportunistic.5g_data_switch_hysteresis_time_long_bundle
PERSISTABLE_BUNDLE PersistableBundle[{48=2000, 71=2000}]

2. Update "get-value" to support PersistableBundle type
3. Update "set-value" to notify user to use "set-values-from-xml" when
   handling PersistableBundle type config

Test: manual tests
Bug: 222332191
Change-Id: Id26fcbbd11a7486f87e3b034c0badf923c63b8f6
diff --git a/src/com/android/phone/TelephonyShellCommand.java b/src/com/android/phone/TelephonyShellCommand.java
index 2726855..ceaca25 100644
--- a/src/com/android/phone/TelephonyShellCommand.java
+++ b/src/com/android/phone/TelephonyShellCommand.java
@@ -57,6 +57,7 @@
 import com.android.modules.utils.BasicShellCommandHandler;
 import com.android.phone.callcomposer.CallComposerPictureManager;
 
+import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -113,6 +114,7 @@
 
     private static final String CC_GET_VALUE = "get-value";
     private static final String CC_SET_VALUE = "set-value";
+    private static final String CC_SET_VALUES_FROM_XML = "set-values-from-xml";
     private static final String CC_CLEAR_VALUES = "clear-values";
 
     private static final String GBA_SUBCOMMAND = "gba";
@@ -184,7 +186,7 @@
 
     private enum CcType {
         BOOLEAN, DOUBLE, DOUBLE_ARRAY, INT, INT_ARRAY, LONG, LONG_ARRAY, STRING,
-                STRING_ARRAY, UNKNOWN
+                STRING_ARRAY, PERSISTABLE_BUNDLE, UNKNOWN
     }
 
     private class CcOptionParseResult {
@@ -582,9 +584,17 @@
         pw.println("      used if NEW_VALUE is not set. Strings should be encapsulated with");
         pw.println("      quotation marks. Spaces needs to be escaped. Example: \"Hello\\ World\"");
         pw.println("      Separate items in arrays with space . Example: \"item1\" \"item2\"");
+        pw.println("  cc set-values-from-xml [-s SLOT_ID] [-p] < XML_FILE_PATH");
+        pw.println("    Set carrier config based on the contents of the XML_FILE. File must be");
+        pw.println("    provided through standard input and follow CarrierConfig XML format.");
+        pw.println("    Example: packages/apps/CarrierConfig/assets/*.xml");
+        pw.println("    Options are:");
+        pw.println("      -s: The SIM slot ID to set carrier config value for. If no option");
+        pw.println("          is specified, it will choose the default voice SIM slot.");
+        pw.println("      -p: Value will be stored persistent");
         pw.println("  cc clear-values [-s SLOT_ID]");
         pw.println("    Clear all carrier override values that has previously been set");
-        pw.println("    with set-value");
+        pw.println("    with set-value or set-values-from-xml");
         pw.println("    Options are:");
         pw.println("      -s: The SIM slot ID to clear carrier config values for. If no option");
         pw.println("          is specified, it will choose the default voice SIM slot.");
@@ -1503,6 +1513,9 @@
             case CC_SET_VALUE: {
                 return handleCcSetValue();
             }
+            case CC_SET_VALUES_FROM_XML: {
+                return handleCcSetValuesFromXml();
+            }
             case CC_CLEAR_VALUES: {
                 return handleCcClearValues();
             }
@@ -1520,7 +1533,7 @@
         String key = null;
 
         // Parse all options
-        CcOptionParseResult options =  parseCcOptions(tag, false);
+        CcOptionParseResult options = parseCcOptions(tag, false);
         if (options == null) {
             return -1;
         }
@@ -1560,7 +1573,7 @@
         String tag = CARRIER_CONFIG_SUBCOMMAND + " " + CC_SET_VALUE + ": ";
 
         // Parse all options
-        CcOptionParseResult options =  parseCcOptions(tag, true);
+        CcOptionParseResult options = parseCcOptions(tag, true);
         if (options == null) {
             return -1;
         }
@@ -1597,6 +1610,11 @@
             errPw.println(tag + "ERROR: Not possible to override key with unknown type.");
             return -1;
         }
+        if (type == CcType.PERSISTABLE_BUNDLE) {
+            errPw.println(tag + "ERROR: Overriding of persistable bundle type is not supported. "
+                    + "Use set-values-from-xml instead.");
+            return -1;
+        }
 
         // Create an override bundle containing the key and value that should be overriden.
         PersistableBundle overrideBundle = getOverrideBundle(tag, type, key, valueList);
@@ -1623,13 +1641,79 @@
         return 0;
     }
 
+    // cc set-values-from-xml
+    private int handleCcSetValuesFromXml() {
+        PrintWriter errPw = getErrPrintWriter();
+        String tag = CARRIER_CONFIG_SUBCOMMAND + " " + CC_SET_VALUES_FROM_XML + ": ";
+
+        // Parse all options
+        CcOptionParseResult options = parseCcOptions(tag, false);
+        if (options == null) {
+            return -1;
+        }
+
+        // Get bundle containing all current carrier configuration values.
+        PersistableBundle originalValues = mCarrierConfigManager.getConfigForSubId(options.mSubId);
+        if (originalValues == null) {
+            errPw.println(tag + "No carrier config values found for subId " + options.mSubId + ".");
+            return -1;
+        }
+
+        PersistableBundle overrideBundle = readPersistableBundleFromXml(tag);
+        if (overrideBundle == null) {
+            return -1;
+        }
+
+        // Verify all values are valid types
+        for (String key : overrideBundle.keySet()) {
+            CcType type = getType(tag, key, originalValues);
+            if (type == CcType.UNKNOWN) {
+                errPw.println(tag + "ERROR: Not possible to override key with unknown type.");
+                return -1;
+            }
+        }
+
+        // Override the value
+        mCarrierConfigManager.overrideConfig(options.mSubId, overrideBundle, options.mPersistent);
+
+        // Find bundle containing all new carrier configuration values after the override.
+        PersistableBundle newValues = mCarrierConfigManager.getConfigForSubId(options.mSubId);
+        if (newValues == null) {
+            errPw.println(tag + "No carrier config values found for subId " + options.mSubId + ".");
+            return -1;
+        }
+
+        // Print the original and new values
+        overrideBundle.keySet().forEach(key -> {
+            CcType type = getType(tag, key, originalValues);
+            String originalValueString = ccValueToString(key, type, originalValues);
+            String newValueString = ccValueToString(key, type, newValues);
+            getOutPrintWriter().println("Previous value: \n" + originalValueString);
+            getOutPrintWriter().println("New value: \n" + newValueString);
+        });
+
+        return 0;
+    }
+
+    private PersistableBundle readPersistableBundleFromXml(String tag) {
+        PersistableBundle subIdBundles;
+        try {
+            subIdBundles = PersistableBundle.readFromStream(getRawInputStream());
+        } catch (IOException | RuntimeException e) {
+            PrintWriter errPw = getErrPrintWriter();
+            errPw.println(tag + e);
+            return null;
+        }
+
+        return subIdBundles;
+    }
+
     // cc clear-values
     private int handleCcClearValues() {
-        PrintWriter errPw = getErrPrintWriter();
         String tag = CARRIER_CONFIG_SUBCOMMAND + " " + CC_CLEAR_VALUES + ": ";
 
         // Parse all options
-        CcOptionParseResult options =  parseCcOptions(tag, false);
+        CcOptionParseResult options = parseCcOptions(tag, false);
         if (options == null) {
             return -1;
         }
@@ -1650,23 +1734,34 @@
         } else if (value != null) {
             if (value instanceof Boolean) {
                 return CcType.BOOLEAN;
-            } else if (value instanceof Double) {
+            }
+            if (value instanceof Double) {
                 return CcType.DOUBLE;
-            } else if (value instanceof double[]) {
+            }
+            if (value instanceof double[]) {
                 return CcType.DOUBLE_ARRAY;
-            } else if (value instanceof Integer) {
+            }
+            if (value instanceof Integer) {
                 return CcType.INT;
-            } else if (value instanceof int[]) {
+            }
+            if (value instanceof int[]) {
                 return CcType.INT_ARRAY;
-            } else if (value instanceof Long) {
+            }
+            if (value instanceof Long) {
                 return CcType.LONG;
-            } else if (value instanceof long[]) {
+            }
+            if (value instanceof long[]) {
                 return CcType.LONG_ARRAY;
-            } else if (value instanceof String) {
+            }
+            if (value instanceof String) {
                 return CcType.STRING;
-            } else if (value instanceof String[]) {
+            }
+            if (value instanceof String[]) {
                 return CcType.STRING_ARRAY;
             }
+            if (value instanceof PersistableBundle) {
+                return CcType.PERSISTABLE_BUNDLE;
+            }
         } else {
             // Current value was null and can therefore not be used in order to find the type.
             // Check the name of the key to infer the type. This check is not needed for primitive
@@ -1686,6 +1781,9 @@
             if (key.endsWith("string_array") || key.endsWith("strings")) {
                 return CcType.STRING_ARRAY;
             }
+            if (key.endsWith("bundle")) {
+                return CcType.PERSISTABLE_BUNDLE;
+            }
         }
 
         // Not possible to infer the type by looking at the current value or the key.