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