1 /* 2 * Copyright (C) 2016 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.voicemail.impl; 18 19 import android.content.Context; 20 import android.os.PersistableBundle; 21 import android.support.annotation.Nullable; 22 import android.support.annotation.VisibleForTesting; 23 import android.util.ArrayMap; 24 import com.android.dialer.configprovider.ConfigProviderBindings; 25 import com.android.voicemail.impl.utils.XmlUtils; 26 import java.io.IOException; 27 import java.util.ArrayList; 28 import java.util.Map; 29 import java.util.Map.Entry; 30 import org.xmlpull.v1.XmlPullParser; 31 import org.xmlpull.v1.XmlPullParserException; 32 33 /** Load and caches telephony vvm config from res/xml/vvm_config.xml */ 34 public class TelephonyVvmConfigManager { 35 36 private static final String TAG = "TelephonyVvmCfgMgr"; 37 38 private static final boolean USE_DEBUG_CONFIG = false; 39 40 private static final String TAG_PERSISTABLEMAP = "pbundle_as_map"; 41 42 @VisibleForTesting static final String KEY_MCCMNC = "mccmnc"; 43 44 private static final String KEY_FEATURE_FLAG_NAME = "feature_flag_name"; 45 46 private static Map<String, PersistableBundle> sCachedConfigs; 47 48 private final Map<String, PersistableBundle> mConfigs; 49 TelephonyVvmConfigManager(Context context)50 public TelephonyVvmConfigManager(Context context) { 51 if (sCachedConfigs == null) { 52 sCachedConfigs = loadConfigs(context, context.getResources().getXml(R.xml.vvm_config)); 53 } 54 mConfigs = sCachedConfigs; 55 } 56 57 @VisibleForTesting TelephonyVvmConfigManager(Context context, XmlPullParser parser)58 TelephonyVvmConfigManager(Context context, XmlPullParser parser) { 59 mConfigs = loadConfigs(context, parser); 60 } 61 62 @Nullable getConfig(String mccMnc)63 public PersistableBundle getConfig(String mccMnc) { 64 if (USE_DEBUG_CONFIG) { 65 return mConfigs.get("TEST"); 66 } 67 return mConfigs.get(mccMnc); 68 } 69 loadConfigs(Context context, XmlPullParser parser)70 private static Map<String, PersistableBundle> loadConfigs(Context context, XmlPullParser parser) { 71 Map<String, PersistableBundle> configs = new ArrayMap<>(); 72 try { 73 ArrayList list = readBundleList(parser); 74 for (Object object : list) { 75 if (!(object instanceof PersistableBundle)) { 76 throw new IllegalArgumentException("PersistableBundle expected, got " + object); 77 } 78 PersistableBundle bundle = (PersistableBundle) object; 79 80 if (bundle.containsKey(KEY_FEATURE_FLAG_NAME) 81 && !ConfigProviderBindings.get(context) 82 .getBoolean(bundle.getString(KEY_FEATURE_FLAG_NAME), false)) { 83 continue; 84 } 85 86 String[] mccMncs = bundle.getStringArray(KEY_MCCMNC); 87 if (mccMncs == null) { 88 throw new IllegalArgumentException("MCCMNC is null"); 89 } 90 for (String mccMnc : mccMncs) { 91 configs.put(mccMnc, bundle); 92 } 93 } 94 } catch (IOException | XmlPullParserException e) { 95 throw new RuntimeException(e); 96 } 97 return configs; 98 } 99 100 @Nullable readBundleList(XmlPullParser in)101 public static ArrayList readBundleList(XmlPullParser in) 102 throws IOException, XmlPullParserException { 103 final int outerDepth = in.getDepth(); 104 int event; 105 while (((event = in.next()) != XmlPullParser.END_DOCUMENT) 106 && (event != XmlPullParser.END_TAG || in.getDepth() < outerDepth)) { 107 if (event == XmlPullParser.START_TAG) { 108 final String startTag = in.getName(); 109 final String[] tagName = new String[1]; 110 in.next(); 111 return XmlUtils.readThisListXml(in, startTag, tagName, new MyReadMapCallback(), false); 112 } 113 } 114 return null; 115 } 116 restoreFromXml(XmlPullParser in)117 public static PersistableBundle restoreFromXml(XmlPullParser in) 118 throws IOException, XmlPullParserException { 119 final int outerDepth = in.getDepth(); 120 final String startTag = in.getName(); 121 final String[] tagName = new String[1]; 122 int event; 123 while (((event = in.next()) != XmlPullParser.END_DOCUMENT) 124 && (event != XmlPullParser.END_TAG || in.getDepth() < outerDepth)) { 125 if (event == XmlPullParser.START_TAG) { 126 ArrayMap<String, ?> map = 127 XmlUtils.readThisArrayMapXml(in, startTag, tagName, new MyReadMapCallback()); 128 PersistableBundle result = new PersistableBundle(); 129 for (Entry<String, ?> entry : map.entrySet()) { 130 Object value = entry.getValue(); 131 if (value instanceof Integer) { 132 result.putInt(entry.getKey(), (int) value); 133 } else if (value instanceof Boolean) { 134 result.putBoolean(entry.getKey(), (boolean) value); 135 } else if (value instanceof String) { 136 result.putString(entry.getKey(), (String) value); 137 } else if (value instanceof String[]) { 138 result.putStringArray(entry.getKey(), (String[]) value); 139 } else if (value instanceof PersistableBundle) { 140 result.putPersistableBundle(entry.getKey(), (PersistableBundle) value); 141 } 142 } 143 return result; 144 } 145 } 146 return PersistableBundle.EMPTY; 147 } 148 149 static class MyReadMapCallback implements XmlUtils.ReadMapCallback { 150 151 @Override readThisUnknownObjectXml(XmlPullParser in, String tag)152 public Object readThisUnknownObjectXml(XmlPullParser in, String tag) 153 throws XmlPullParserException, IOException { 154 if (TAG_PERSISTABLEMAP.equals(tag)) { 155 return restoreFromXml(in); 156 } 157 throw new XmlPullParserException("Unknown tag=" + tag); 158 } 159 } 160 } 161