Add support for mandatory remote configs in DynamicConfig feature

Also patch issue in which config with no key-value(s) pairs throws
JSONException on parsing.

bug: 77447857
Test: run gts-dev -m GtsOsTestCases
Change-Id: I213a60d1c39bd20e0e73d6a2e7016d9c5656d12f
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/util/DynamicConfigFileReader.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/util/DynamicConfigFileReader.java
index f2b7f3c..bf392d7 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/util/DynamicConfigFileReader.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/util/DynamicConfigFileReader.java
@@ -42,13 +42,9 @@
      */
     public static String getValueFromConfig(File file, String key)
             throws XmlPullParserException, IOException {
-        Map<String, List<String>> configMap = DynamicConfig.createConfigMap(file);
-        List<String> singleValue = configMap.get(key);
-        if (singleValue == null || singleValue.size() == 0 || singleValue.size() > 1) {
-            // key must exist in the map, and map to a list containing exactly one string
-            return null;
-        }
-        return singleValue.get(0);
+        DynamicConfig config = new DynamicConfig();
+        config.initializeConfig(file);
+        return config.getValue(key);
     }
 
     /**
@@ -61,7 +57,9 @@
      */
     public static List<String> getValuesFromConfig(File file, String key)
             throws XmlPullParserException, IOException {
-        return DynamicConfig.createConfigMap(file).get(key);
+        DynamicConfig config = new DynamicConfig();
+        config.initializeConfig(file);
+        return config.getValues(key);
     }
 
     /**
diff --git a/common/host-side/util/src/com/android/compatibility/common/util/DynamicConfigHandler.java b/common/host-side/util/src/com/android/compatibility/common/util/DynamicConfigHandler.java
index 58840fd..4d79b30 100644
--- a/common/host-side/util/src/com/android/compatibility/common/util/DynamicConfigHandler.java
+++ b/common/host-side/util/src/com/android/compatibility/common/util/DynamicConfigHandler.java
@@ -31,6 +31,7 @@
 import java.io.IOException;
 import java.io.OutputStream;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -47,9 +48,16 @@
         Map<String, List<String>> localConfig = DynamicConfig.createConfigMap(localConfigFile);
         Map<String, List<String>> apbsConfig = parseJsonToConfigMap(apbsConfigJson);
         localConfig.putAll(apbsConfig);
+        setRemoteConfigRetrieved(localConfig, apbsConfigJson != null);
         return storeMergedConfigFile(localConfig, moduleName);
     }
 
+    private static void setRemoteConfigRetrieved(Map<String, List<String>> config,
+            boolean retrieved) {
+        List<String> val = Collections.singletonList(Boolean.toString(retrieved));
+        config.put(DynamicConfig.REMOTE_CONFIG_RETRIEVED_KEY, val);
+    }
+
     private static Map<String, List<String>> parseJsonToConfigMap(String apbsConfigJson)
             throws JSONException {
 
@@ -59,7 +67,13 @@
         }
 
         JSONObject rootObj  = new JSONObject(new JSONTokener(apbsConfigJson));
-        JSONObject configObject = rootObj.getJSONObject("dynamicConfigEntries");
+        JSONObject configObject  = null;
+        try {
+            configObject = rootObj.getJSONObject("dynamicConfigEntries");
+        } catch (JSONException e) {
+            // no config key-value(s) pairs have been defined remotely, return empty map
+            return configMap;
+        }
         JSONArray keys = configObject.names();
         for (int i = 0; i < keys.length(); i++) {
             String key = keys.getString(i);
diff --git a/common/util/src/com/android/compatibility/common/util/DynamicConfig.java b/common/util/src/com/android/compatibility/common/util/DynamicConfig.java
index 5c92ef4..d73cbc5 100644
--- a/common/util/src/com/android/compatibility/common/util/DynamicConfig.java
+++ b/common/util/src/com/android/compatibility/common/util/DynamicConfig.java
@@ -16,6 +16,8 @@
 
 package com.android.compatibility.common.util;
 
+import static org.junit.Assert.assertTrue;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlPullParserFactory;
@@ -42,15 +44,19 @@
     public static final String ENTRY_TAG = "entry";
     public static final String VALUE_TAG = "value";
     public static final String KEY_ATTR = "key";
-    public final static String CONFIG_FOLDER_ON_DEVICE = "/sdcard/dynamic-config-files/";
+
+    public static final String REMOTE_CONFIG_REQUIRED_KEY = "remote_config_required";
+    public static final String REMOTE_CONFIG_RETRIEVED_KEY = "remote_config_retrieved";
+    public static final String CONFIG_FOLDER_ON_DEVICE = "/sdcard/dynamic-config-files/";
 
     protected Map<String, List<String>> mDynamicConfigMap = new HashMap<String, List<String>>();
 
-    protected void initializeConfig(File file) throws XmlPullParserException, IOException {
+    public void initializeConfig(File file) throws XmlPullParserException, IOException {
         mDynamicConfigMap = createConfigMap(file);
     }
 
     public String getValue(String key) {
+        assertRemoteConfigRequirementMet();
         List<String> singleValue = mDynamicConfigMap.get(key);
         if (singleValue == null || singleValue.size() == 0 || singleValue.size() > 1) {
             // key must exist in the map, and map to a list containing exactly one string
@@ -60,13 +66,34 @@
     }
 
     public List<String> getValues(String key) {
+        assertRemoteConfigRequirementMet();
         return mDynamicConfigMap.get(key);
     }
 
     public Set<String> keySet() {
+        assertRemoteConfigRequirementMet();
         return mDynamicConfigMap.keySet();
     }
 
+    public boolean remoteConfigRequired() {
+        if (mDynamicConfigMap.containsKey(REMOTE_CONFIG_REQUIRED_KEY)) {
+            String val = mDynamicConfigMap.get(REMOTE_CONFIG_REQUIRED_KEY).get(0);
+            return Boolean.parseBoolean(val);
+        }
+        return false;
+    }
+
+    public boolean remoteConfigRetrieved() {
+        // assume config will always contain exactly one value, populated by DynamicConfigHandler
+        String val = mDynamicConfigMap.get(REMOTE_CONFIG_RETRIEVED_KEY).get(0);
+        return Boolean.parseBoolean(val);
+    }
+
+    public void assertRemoteConfigRequirementMet() {
+        assertTrue("Remote connection to DynamicConfigService required for this test",
+                !remoteConfigRequired() || remoteConfigRetrieved());
+    }
+
     public static File getConfigFile(File configFolder, String moduleName)
             throws FileNotFoundException {
         File config = getConfigFileUnchecked(configFolder, moduleName);