1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.compatibility.common.util;
18 
19 import com.android.tradefed.util.FileUtil;
20 
21 import org.json.JSONArray;
22 import org.json.JSONException;
23 import org.json.JSONObject;
24 import org.json.JSONTokener;
25 import org.xmlpull.v1.XmlPullParserException;
26 import org.xmlpull.v1.XmlPullParserFactory;
27 import org.xmlpull.v1.XmlSerializer;
28 
29 import java.io.File;
30 import java.io.FileOutputStream;
31 import java.io.IOException;
32 import java.io.OutputStream;
33 import java.util.ArrayList;
34 import java.util.Collections;
35 import java.util.HashMap;
36 import java.util.List;
37 import java.util.Map;
38 
39 public class DynamicConfigHandler {
40 
41     private final static String FILE_EXT = ".dynamic";
42     private static final String NS = null; //xml constant representing null namespace
43     private static final String ENCODING = "UTF-8";
44 
getMergedDynamicConfigFile(File localConfigFile, String apbsConfigJson, String moduleName)45     public static File getMergedDynamicConfigFile(File localConfigFile, String apbsConfigJson,
46             String moduleName) throws IOException, XmlPullParserException, JSONException {
47 
48         Map<String, List<String>> localConfig = DynamicConfig.createConfigMap(localConfigFile);
49         Map<String, List<String>> apbsConfig = parseJsonToConfigMap(apbsConfigJson);
50         localConfig.putAll(apbsConfig);
51         setRemoteConfigRetrieved(localConfig, apbsConfigJson != null);
52         return storeMergedConfigFile(localConfig, moduleName);
53     }
54 
setRemoteConfigRetrieved(Map<String, List<String>> config, boolean retrieved)55     private static void setRemoteConfigRetrieved(Map<String, List<String>> config,
56             boolean retrieved) {
57         List<String> val = Collections.singletonList(Boolean.toString(retrieved));
58         config.put(DynamicConfig.REMOTE_CONFIG_RETRIEVED_KEY, val);
59     }
60 
parseJsonToConfigMap(String apbsConfigJson)61     private static Map<String, List<String>> parseJsonToConfigMap(String apbsConfigJson)
62             throws JSONException {
63 
64         Map<String, List<String>> configMap = new HashMap<String, List<String>>();
65         if (apbsConfigJson == null) {
66             return configMap;
67         }
68 
69         JSONObject rootObj  = new JSONObject(new JSONTokener(apbsConfigJson));
70         JSONObject configObject  = null;
71         try {
72             configObject = rootObj.getJSONObject("dynamicConfigEntries");
73         } catch (JSONException e) {
74             // no config key-value(s) pairs have been defined remotely, return empty map
75             return configMap;
76         }
77         JSONArray keys = configObject.names();
78         for (int i = 0; i < keys.length(); i++) {
79             String key = keys.getString(i);
80             JSONArray jsonValues = configObject.getJSONObject(key).getJSONArray("configValues");
81             List<String> values = new ArrayList<>();
82             for (int j = 0; j < jsonValues.length(); j ++) {
83                 values.add(jsonValues.getString(j));
84             }
85             configMap.put(key, values);
86         }
87         return configMap;
88     }
89 
storeMergedConfigFile(Map<String, List<String>> configMap, String moduleName)90     private static File storeMergedConfigFile(Map<String, List<String>> configMap,
91             String moduleName) throws XmlPullParserException, IOException {
92 
93         File mergedConfigFile = FileUtil.createTempFile(moduleName, FILE_EXT);
94         OutputStream stream = new FileOutputStream(mergedConfigFile);
95         XmlSerializer serializer = XmlPullParserFactory.newInstance().newSerializer();
96         serializer.setOutput(stream, ENCODING);
97         serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
98         serializer.startDocument(ENCODING, false);
99 
100         serializer.startTag(NS, DynamicConfig.CONFIG_TAG);
101         for (String key : configMap.keySet()) {
102             serializer.startTag(NS, DynamicConfig.ENTRY_TAG);
103             serializer.attribute(NS, DynamicConfig.KEY_ATTR, key);
104             for (String value : configMap.get(key)) {
105                 serializer.startTag(NS, DynamicConfig.VALUE_TAG);
106                 serializer.text(value);
107                 serializer.endTag(NS, DynamicConfig.VALUE_TAG);
108             }
109             serializer.endTag(NS, DynamicConfig.ENTRY_TAG);
110         }
111         serializer.endTag(NS, DynamicConfig.CONFIG_TAG);
112         serializer.endDocument();
113         return mergedConfigFile;
114     }
115 }
116