Persistent storage in Carrier Config debug shell commands

Add support for Persistent storage in Carrier Config shell debug
commands. Adds an option "-p" in the command "cc set-config" to control
that the new value should be stored persistent.

The syntax for "cc set-value" then becomes:
cc set-value [-s SLOT_ID] [-p] KEY [NEW_VALUE]

Bug: 144544956
Test: atest TeleServiceTests
Change-Id: I775503c32974d658dace68ea88cdbeb564ca7c19
diff --git a/src/com/android/phone/CarrierConfigLoader.java b/src/com/android/phone/CarrierConfigLoader.java
index 9841a82..13dcaa7 100644
--- a/src/com/android/phone/CarrierConfigLoader.java
+++ b/src/com/android/phone/CarrierConfigLoader.java
@@ -20,6 +20,7 @@
 import static android.service.carrier.CarrierService.ICarrierServiceWrapper.RESULT_ERROR;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -94,6 +95,8 @@
     private PersistableBundle[] mConfigFromDefaultApp;
     // Carrier configs from privileged carrier config app, indexed by phoneID.
     private PersistableBundle[] mConfigFromCarrierApp;
+    // Persistent Carrier configs that are provided via the override test API, indexed by phone ID.
+    private PersistableBundle[] mPersistentOverrideConfigs;
     // Carrier configs that are provided via the override test API, indexed by phone ID.
     private PersistableBundle[] mOverrideConfigs;
     // Service connection for binding to config app.
@@ -151,6 +154,8 @@
     private static final String TAG_VERSION = "package_version";
     private static final String TAG_BUNDLE = "bundle_data";
 
+    private static final String OVERRIDE_PACKAGE_ADDITION = "-override";
+
     // SharedPreferences key for last known build fingerprint.
     private static final String KEY_FINGERPRINT = "build_fingerprint";
 
@@ -220,8 +225,17 @@
 
                 case EVENT_DO_FETCH_DEFAULT:
                 {
-                    final PersistableBundle config =
-                            restoreConfigFromXml(mPlatformCarrierConfigPackage, phoneId);
+                    // Restore persistent override values.
+                    PersistableBundle config = restoreConfigFromXml(
+                            mPlatformCarrierConfigPackage, OVERRIDE_PACKAGE_ADDITION, phoneId);
+                    if (config != null) {
+                        log("Loaded persistent override config from XML. package="
+                                + mPlatformCarrierConfigPackage
+                                + " phoneId=" + phoneId);
+                        mPersistentOverrideConfigs[phoneId] = config;
+                    }
+
+                    config = restoreConfigFromXml(mPlatformCarrierConfigPackage, "", phoneId);
                     if (config != null) {
                         log(
                                 "Loaded config from XML. package="
@@ -283,7 +297,7 @@
                                     }
                                     PersistableBundle config =
                                             resultData.getParcelable(KEY_CONFIG_BUNDLE);
-                                    saveConfigToXml(mPlatformCarrierConfigPackage, phoneId,
+                                    saveConfigToXml(mPlatformCarrierConfigPackage, "", phoneId,
                                         carrierId, config);
                                     mConfigFromDefaultApp[phoneId] = config;
                                     sendMessage(
@@ -351,7 +365,7 @@
                 {
                     final String carrierPackageName = getCarrierPackageForPhoneId(phoneId);
                     final PersistableBundle config =
-                            restoreConfigFromXml(carrierPackageName, phoneId);
+                            restoreConfigFromXml(carrierPackageName, "", phoneId);
                     if (config != null) {
                         log(
                                 "Loaded config from XML. package="
@@ -414,8 +428,8 @@
                                     }
                                     PersistableBundle config =
                                             resultData.getParcelable(KEY_CONFIG_BUNDLE);
-                                    saveConfigToXml(getCarrierPackageForPhoneId(phoneId), phoneId,
-                                        carrierId, config);
+                                    saveConfigToXml(getCarrierPackageForPhoneId(phoneId), "",
+                                            phoneId, carrierId, config);
                                     mConfigFromCarrierApp[phoneId] = config;
                                     sendMessage(
                                             obtainMessage(
@@ -528,6 +542,7 @@
         int numPhones = TelephonyManager.from(context).getSupportedModemCount();
         mConfigFromDefaultApp = new PersistableBundle[numPhones];
         mConfigFromCarrierApp = new PersistableBundle[numPhones];
+        mPersistentOverrideConfigs = new PersistableBundle[numPhones];
         mOverrideConfigs = new PersistableBundle[numPhones];
         mServiceConnection = new CarrierServiceConnection[numPhones];
         mHasSentConfigChange = new boolean[numPhones];
@@ -737,12 +752,13 @@
      * In case of errors or invalid input, no file will be written.
      *
      * @param packageName the name of the package from which we fetched this bundle.
+     * @param extraString An extra string to be used in the XML file name.
      * @param phoneId the phone ID.
      * @param carrierId contains all carrier-identifying information.
      * @param config the bundle to be written. Null will be treated as an empty bundle.
      */
-    private void saveConfigToXml(String packageName, int phoneId, CarrierIdentifier carrierId,
-        PersistableBundle config) {
+    private void saveConfigToXml(String packageName, @NonNull String extraString, int phoneId,
+            CarrierIdentifier carrierId, PersistableBundle config) {
         if (SubscriptionManager.getSimStateForSlotIndex(phoneId)
                 != TelephonyManager.SIM_STATE_LOADED) {
             loge("Skip save config because SIM records are not loaded.");
@@ -776,7 +792,7 @@
         try {
             outFile = new FileOutputStream(
                     new File(mContext.getFilesDir(),
-                            getFilenameForConfig(packageName, iccid, cid)));
+                            getFilenameForConfig(packageName, extraString, iccid, cid)));
             FastXmlSerializer out = new FastXmlSerializer();
             out.setOutput(outFile, "utf-8");
             out.startDocument("utf-8", true);
@@ -810,11 +826,13 @@
      * current version, then null will be returned.
      *
      * @param packageName the name of the package from which we fetched this bundle.
+     * @param extraString An extra string to be used in the XML file name.
      * @param phoneId the phone ID.
      * @return the bundle from the XML file. Returns null if there is no saved config, the saved
      *         version does not match, or reading config fails.
      */
-    private PersistableBundle restoreConfigFromXml(String packageName, int phoneId) {
+    private PersistableBundle restoreConfigFromXml(String packageName, @NonNull String extraString,
+            int phoneId) {
         final String version = getPackageVersion(packageName);
         if (version == null) {
             loge("Failed to get package version for: " + packageName);
@@ -834,11 +852,12 @@
         }
 
         PersistableBundle restoredBundle = null;
+        File file = null;
         FileInputStream inFile = null;
         try {
-            inFile = new FileInputStream(
-                    new File(mContext.getFilesDir(),
-                            getFilenameForConfig(packageName, iccid, cid)));
+            file = new File(mContext.getFilesDir(),
+                    getFilenameForConfig(packageName, extraString, iccid, cid));
+            inFile = new FileInputStream(file);
             XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
             parser.setInput(inFile, "utf-8");
 
@@ -860,7 +879,9 @@
             inFile.close();
         }
         catch (FileNotFoundException e) {
-            loge(e.toString());
+            // Missing file is normal occurrence that might occur with a new sim or when restoring
+            // an override file during boot and should not be treated as an error.
+            if (file != null) log("File not found: " + file.getPath());
         }
         catch (XmlPullParserException e) {
             loge(e.toString());
@@ -901,13 +922,13 @@
     }
 
     /** Builds a canonical file name for a config file. */
-    private String getFilenameForConfig(@NonNull String packageName, @NonNull String iccid,
-                                        int cid) {
+    private String getFilenameForConfig(@NonNull String packageName, @NonNull String extraString,
+                                        @NonNull String iccid, int cid) {
         // the same carrier should have a single copy of XML file named after carrier id.
         // However, it's still possible that platform doesn't recognize the current sim carrier,
         // we will use iccid + carrierid as the canonical file name. carrierid can also handle the
         // cases SIM OTA resolves to different carrier while iccid remains the same.
-        return "carrierconfig-" + packageName + "-" + iccid + "-" + cid + ".xml";
+        return "carrierconfig-" + packageName + extraString + "-" + iccid + "-" + cid + ".xml";
     }
 
     /** Return the current version code of a package, or null if the name is not found. */
@@ -973,6 +994,11 @@
                 retConfig.putAll(config);
                 retConfig.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
             }
+            config = mPersistentOverrideConfigs[phoneId];
+            if (config != null) {
+                retConfig.putAll(config);
+                retConfig.putBoolean(CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL, true);
+            }
             config = mOverrideConfigs[phoneId];
             if (config != null) {
                 retConfig.putAll(config);
@@ -982,7 +1008,8 @@
     }
 
     @Override
-    public void overrideConfig(int subscriptionId, PersistableBundle overrides) {
+    public void overrideConfig(int subscriptionId, @Nullable PersistableBundle overrides,
+            boolean persistent) {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.MODIFY_PHONE_STATE, null);
         //TODO: Also check for SHELL UID to restrict this method to testing only (b/131326259)
@@ -991,18 +1018,38 @@
             log("Ignore invalid phoneId: " + phoneId + " for subId: " + subscriptionId);
             return;
         }
+        overrideConfig(mOverrideConfigs, phoneId, overrides);
 
-        if (overrides == null) {
-            mOverrideConfigs[phoneId] = new PersistableBundle();
-        } else if (mOverrideConfigs[phoneId] == null) {
-            mOverrideConfigs[phoneId] = overrides;
-        } else {
-            mOverrideConfigs[phoneId].putAll(overrides);
+        if (persistent) {
+            overrideConfig(mPersistentOverrideConfigs, phoneId, overrides);
+
+            if (overrides != null) {
+                final CarrierIdentifier carrierId = getCarrierIdentifierForPhoneId(phoneId);
+                saveConfigToXml(mPlatformCarrierConfigPackage, OVERRIDE_PACKAGE_ADDITION, phoneId,
+                        carrierId, mPersistentOverrideConfigs[phoneId]);
+            } else {
+                final String iccid = getIccIdForPhoneId(phoneId);
+                final int cid = getSpecificCarrierIdForPhoneId(phoneId);
+                String fileName = getFilenameForConfig(mPlatformCarrierConfigPackage,
+                        OVERRIDE_PACKAGE_ADDITION, iccid, cid);
+                File fileToDelete = new File(mContext.getFilesDir(), fileName);
+                fileToDelete.delete();
+            }
         }
-
         notifySubscriptionInfoUpdater(phoneId);
     }
 
+    private void overrideConfig(@NonNull PersistableBundle[] currentOverrides, int phoneId,
+            @Nullable PersistableBundle overrides) {
+        if (overrides == null) {
+            currentOverrides[phoneId] = new PersistableBundle();
+        } else if (currentOverrides[phoneId] == null) {
+            currentOverrides[phoneId] = overrides;
+        } else {
+            currentOverrides[phoneId].putAll(overrides);
+        }
+    }
+
     @Override
     public void notifyConfigChangedForSubId(int subId) {
         int phoneId = SubscriptionManager.getPhoneId(subId);
@@ -1074,6 +1121,8 @@
             // display ConfigFromCarrierApp
             printConfig(mConfigFromCarrierApp[i], pw, "mConfigFromCarrierApp");
             pw.println("");
+            printConfig(mPersistentOverrideConfigs[i], pw, "mPersistentOverrideConfigs");
+            pw.println("");
             printConfig(mOverrideConfigs[i], pw, "mOverrideConfigs");
         }