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.messaging.sms;
18 
19 import android.content.Context;
20 import android.content.res.Configuration;
21 import android.content.res.Resources;
22 import android.content.res.XmlResourceParser;
23 import android.os.Bundle;
24 import androidx.appcompat.mms.CarrierConfigValuesLoader;
25 import android.util.SparseArray;
26 
27 import com.android.messaging.R;
28 import com.android.messaging.util.LogUtil;
29 import com.android.messaging.util.OsUtil;
30 import com.android.messaging.util.PhoneUtils;
31 
32 /**
33  * Carrier configuration loader
34  *
35  * Loader tries to load from resources. If there is MMS API available, also
36  * load from system.
37  */
38 public class BugleCarrierConfigValuesLoader implements CarrierConfigValuesLoader {
39     /*
40      * Key types
41      */
42     public static final String KEY_TYPE_INT = "int";
43     public static final String KEY_TYPE_BOOL = "bool";
44     public static final String KEY_TYPE_STRING = "string";
45 
46     private final Context mContext;
47 
48     // Cached values for subIds
49     private final SparseArray<Bundle> mValuesCache;
50 
BugleCarrierConfigValuesLoader(final Context context)51     public BugleCarrierConfigValuesLoader(final Context context) {
52         mContext = context;
53         mValuesCache = new SparseArray<>();
54     }
55 
56     @Override
get(int subId)57     public Bundle get(int subId) {
58         subId = PhoneUtils.getDefault().getEffectiveSubId(subId);
59         Bundle values;
60         String loadSource = null;
61         synchronized (this) {
62             values = mValuesCache.get(subId);
63             if (values == null) {
64                 values = new Bundle();
65                 mValuesCache.put(subId, values);
66                 loadSource = loadLocked(subId, values);
67             }
68         }
69         if (loadSource != null) {
70             LogUtil.i(LogUtil.BUGLE_TAG, "Carrier configs loaded: " + values
71                     + " from " + loadSource + " for subId=" + subId);
72         }
73         return values;
74     }
75 
76     /**
77      * Clear the cache for reloading
78      */
reset()79     public void reset() {
80         synchronized (this) {
81             mValuesCache.clear();
82         }
83     }
84 
85     /**
86      * Loading carrier config values
87      *
88      * @param subId which SIM to load for
89      * @param values the result to add to
90      * @return the source of the config, could be "resources" or "resources+system"
91      */
loadLocked(final int subId, final Bundle values)92     private String loadLocked(final int subId, final Bundle values) {
93         // Load from resources in earlier platform
94         loadFromResources(subId, values);
95         if (OsUtil.isAtLeastL()) {
96             // Load from system to override if system API exists
97             loadFromSystem(subId, values);
98             return "resources+system";
99         }
100         return "resources";
101     }
102 
103     /**
104      * Load from system, using MMS API
105      *
106      * @param subId which SIM to load for
107      * @param values the result to add to
108      */
loadFromSystem(final int subId, final Bundle values)109     private static void loadFromSystem(final int subId, final Bundle values) {
110         try {
111             final Bundle systemValues =
112                     PhoneUtils.get(subId).getSmsManager().getCarrierConfigValues();
113             if (systemValues != null) {
114                 values.putAll(systemValues);
115             }
116         } catch (final Exception e) {
117             LogUtil.w(LogUtil.BUGLE_TAG, "Calling system getCarrierConfigValues exception", e);
118         }
119     }
120 
121     /**
122      * Load from SIM-dependent resources
123      *
124      * @param subId which SIM to load for
125      * @param values the result to add to
126      */
loadFromResources(final int subId, final Bundle values)127     private void loadFromResources(final int subId, final Bundle values) {
128         // Get a subscription-dependent context for loading the mms_config.xml
129         final Context subContext = getSubDepContext(mContext, subId);
130         // Load and parse the XML
131         XmlResourceParser parser = null;
132         try {
133             parser = subContext.getResources().getXml(R.xml.mms_config);
134             final ApnsXmlProcessor processor = ApnsXmlProcessor.get(parser);
135             processor.setMmsConfigHandler(new ApnsXmlProcessor.MmsConfigHandler() {
136                 @Override
137                 public void process(final String mccMnc, final String key, final String value,
138                         final String type) {
139                     update(values, type, key, value);
140                 }
141             });
142             processor.process();
143         } catch (final Resources.NotFoundException e) {
144             LogUtil.w(LogUtil.BUGLE_TAG, "Can not find mms_config.xml");
145         } finally {
146             if (parser != null) {
147                 parser.close();
148             }
149         }
150     }
151 
152     /**
153      * Get a subscription's Context so we can load resources from it
154      *
155      * @param context the sub-independent Context
156      * @param subId the SIM's subId
157      * @return the sub-dependent Context
158      */
getSubDepContext(final Context context, final int subId)159     private static Context getSubDepContext(final Context context, final int subId) {
160         if (!OsUtil.isAtLeastL_MR1()) {
161             return context;
162         }
163         final int[] mccMnc = PhoneUtils.get(subId).getMccMnc();
164         final int mcc = mccMnc[0];
165         final int mnc = mccMnc[1];
166         final Configuration subConfig = new Configuration();
167         if (mcc == 0 && mnc == 0) {
168             Configuration config = context.getResources().getConfiguration();
169             subConfig.mcc = config.mcc;
170             subConfig.mnc = config.mnc;
171         } else {
172             subConfig.mcc = mcc;
173             subConfig.mnc = mnc;
174         }
175         return context.createConfigurationContext(subConfig);
176     }
177 
178     /**
179      * Add or update a carrier config key/value pair to the Bundle
180      *
181      * @param values the result Bundle to add to
182      * @param type the value type
183      * @param key the key
184      * @param value the value
185      */
update(final Bundle values, final String type, final String key, final String value)186     public static void update(final Bundle values, final String type, final String key,
187             final String value) {
188         try {
189             if (KEY_TYPE_INT.equals(type)) {
190                 values.putInt(key, Integer.parseInt(value));
191             } else if (KEY_TYPE_BOOL.equals(type)) {
192                 values.putBoolean(key, Boolean.parseBoolean(value));
193             } else if (KEY_TYPE_STRING.equals(type)){
194                 values.putString(key, value);
195             }
196         } catch (final NumberFormatException e) {
197             LogUtil.w(LogUtil.BUGLE_TAG, "Add carrier values: "
198                     + "invalid " + key + "," + value + "," + type);
199         }
200     }
201 }
202