1 /*
2  * Copyright (C) 2014 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.mms.service;
18 
19 import android.content.BroadcastReceiver;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.IntentFilter;
23 import android.content.res.Configuration;
24 import android.telephony.SubscriptionInfo;
25 import android.telephony.SubscriptionManager;
26 import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
27 import android.util.ArrayMap;
28 import android.util.Log;
29 
30 import java.util.List;
31 import java.util.Map;
32 
33 import com.android.internal.telephony.IccCardConstants;
34 
35 /**
36  * This class manages cached copies of all the MMS configuration for each subscription ID.
37  * A subscription ID loosely corresponds to a particular SIM. See the
38  * {@link android.telephony.SubscriptionManager} for more details.
39  *
40  */
41 public class MmsConfigManager {
42     private static final String TAG = MmsService.TAG;
43 
44     private static volatile MmsConfigManager sInstance = new MmsConfigManager();
45 
getInstance()46     public static MmsConfigManager getInstance() {
47         return sInstance;
48     }
49 
50     // Map the various subIds to their corresponding MmsConfigs.
51     private final Map<Integer, MmsConfig> mSubIdConfigMap = new ArrayMap<Integer, MmsConfig>();
52     private Context mContext;
53     private SubscriptionManager mSubscriptionManager;
54 
55     /**
56      * This receiver listens for changes made to SubInfoRecords and for a broadcast telling us
57      * the TelephonyManager has loaded the information needed in order to get the mcc/mnc's for
58      * each subscription Id. When either of these broadcasts are received, we rebuild the
59      * MmsConfig table.
60      *
61      */
62     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
63         public void onReceive(Context context, Intent intent) {
64             String action = intent.getAction();
65             Log.i(TAG, "mReceiver action: " + action);
66             if (action.equals(IccCardConstants.INTENT_VALUE_ICC_LOADED)) {
67                 loadInBackground();
68             }
69         }
70     };
71 
72     private final OnSubscriptionsChangedListener mOnSubscriptionsChangedListener =
73             new OnSubscriptionsChangedListener() {
74         @Override
75         public void onSubscriptionsChanged() {
76             loadInBackground();
77         }
78     };
79 
80 
init(final Context context)81     public void init(final Context context) {
82         mContext = context;
83         mSubscriptionManager = SubscriptionManager.from(context);
84 
85         // TODO: When this object "finishes" we should unregister.
86         IntentFilter intentFilterLoaded =
87                 new IntentFilter(IccCardConstants.INTENT_VALUE_ICC_LOADED);
88         context.registerReceiver(mReceiver, intentFilterLoaded);
89 
90         // TODO: When this object "finishes" we should unregister by invoking
91         // SubscriptionManager.getInstance(mContext).unregister(mOnSubscriptionsChangedListener);
92         // This is not strictly necessary because it will be unregistered if the
93         // notification fails but it is good form.
94 
95         // Register for SubscriptionInfo list changes which is guaranteed
96         // to invoke onSubscriptionsChanged the first time.
97         SubscriptionManager.from(mContext).addOnSubscriptionsChangedListener(
98                 mOnSubscriptionsChangedListener);
99     }
100 
loadInBackground()101     private void loadInBackground() {
102         // TODO (ywen) - AsyncTask to avoid creating a new thread?
103         new Thread() {
104             @Override
105             public void run() {
106                 Configuration configuration = mContext.getResources().getConfiguration();
107                 // Always put the mnc/mcc in the log so we can tell which mms_config.xml
108                 // was loaded.
109                 Log.i(TAG, "MmsConfigManager.loadInBackground(): mcc/mnc: " +
110                         configuration.mcc + "/" + configuration.mnc);
111                 load(mContext);
112             }
113         }.start();
114     }
115 
116     /**
117      * Find and return the MmsConfig for a particular subscription id.
118      *
119      * @param subId Subscription id of the desired MmsConfig
120      * @return MmsConfig for the particular subscription id. This function can return null if
121      *         the MmsConfig cannot be found or if this function is called before the
122      *         TelephonyManager has setup the SIMs or if loadInBackground is still spawning a
123      *         thread after a recent LISTEN_SUBSCRIPTION_INFO_LIST_CHANGED event.
124      */
getMmsConfigBySubId(int subId)125     public MmsConfig getMmsConfigBySubId(int subId) {
126         MmsConfig mmsConfig;
127         synchronized(mSubIdConfigMap) {
128             mmsConfig = mSubIdConfigMap.get(subId);
129         }
130         Log.i(TAG, "getMmsConfigBySubId -- for sub: " + subId + " mmsConfig: " + mmsConfig);
131         return mmsConfig;
132     }
133 
134     /**
135      * This function goes through all the activated subscription ids (the actual SIMs in the
136      * device), builds a context with that SIM's mcc/mnc and loads the appropriate mms_config.xml
137      * file via the ResourceManager. With single-SIM devices, there will be a single subId.
138      *
139      */
load(Context context)140     private void load(Context context) {
141         List<SubscriptionInfo> subs = mSubscriptionManager.getActiveSubscriptionInfoList();
142         if (subs == null || subs.size() < 1) {
143             Log.e(TAG, "MmsConfigManager.load -- empty getActiveSubInfoList");
144             return;
145         }
146         // Load all the mms_config.xml files in a separate map and then swap with the
147         // real map at the end so we don't block anyone sync'd on the real map.
148         final Map<Integer, MmsConfig> newConfigMap = new ArrayMap<Integer, MmsConfig>();
149         for (SubscriptionInfo sub : subs) {
150             Configuration configuration = new Configuration();
151             if (sub.getMcc() == 0 && sub.getMnc() == 0) {
152                 Configuration config = mContext.getResources().getConfiguration();
153                 configuration.mcc = config.mcc;
154                 configuration.mnc = config.mnc;
155                 Log.i(TAG, "MmsConfigManager.load -- no mcc/mnc for sub: " + sub +
156                         " using mcc/mnc from main context: " + configuration.mcc + "/" +
157                                 configuration.mnc);
158             } else {
159                 Log.i(TAG, "MmsConfigManager.load -- mcc/mnc for sub: " + sub);
160 
161                 configuration.mcc = sub.getMcc();
162                 configuration.mnc = sub.getMnc();
163             }
164             Context subContext = context.createConfigurationContext(configuration);
165 
166             int subId = sub.getSubscriptionId();
167             newConfigMap.put(subId, new MmsConfig(subContext, subId));
168         }
169         synchronized(mSubIdConfigMap) {
170             mSubIdConfigMap.clear();
171             mSubIdConfigMap.putAll(newConfigMap);
172         }
173     }
174 
175 }
176