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.os.Bundle; 25 import android.os.PersistableBundle; 26 import android.telephony.CarrierConfigManager; 27 import android.telephony.SmsManager; 28 import android.telephony.SubscriptionInfo; 29 import android.telephony.SubscriptionManager; 30 import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener; 31 import android.util.ArrayMap; 32 33 import com.android.internal.telephony.IccCardConstants; 34 35 import java.util.List; 36 import java.util.Map; 37 38 /** 39 * This class manages cached copies of all the MMS configuration for each subscription ID. 40 * A subscription ID loosely corresponds to a particular SIM. See the 41 * {@link android.telephony.SubscriptionManager} for more details. 42 * 43 */ 44 public class MmsConfigManager { 45 private static volatile MmsConfigManager sInstance = new MmsConfigManager(); 46 47 public static MmsConfigManager getInstance() { 48 return sInstance; 49 } 50 51 // Map the various subIds to their corresponding MmsConfigs. 52 private final Map<Integer, Bundle> mSubIdConfigMap = new ArrayMap<Integer, Bundle>(); 53 private Context mContext; 54 private SubscriptionManager mSubscriptionManager; 55 56 /** 57 * This receiver listens for changes made to SubInfoRecords and for a broadcast telling us 58 * the TelephonyManager has loaded the information needed in order to get the mcc/mnc's for 59 * each subscription Id. When either of these broadcasts are received, we rebuild the 60 * MmsConfig table. 61 * 62 */ 63 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 64 public void onReceive(Context context, Intent intent) { 65 String action = intent.getAction(); 66 LogUtil.i("MmsConfigManager receiver action: " + action); 67 if (action.equals(IccCardConstants.INTENT_VALUE_ICC_LOADED)) { 68 loadInBackground(); 69 } 70 } 71 }; 72 73 private final OnSubscriptionsChangedListener mOnSubscriptionsChangedListener = 74 new OnSubscriptionsChangedListener() { 75 @Override 76 public void onSubscriptionsChanged() { 77 loadInBackground(); 78 } 79 }; 80 81 82 public void init(final Context context) { 83 mContext = context; 84 mSubscriptionManager = SubscriptionManager.from(context); 85 86 // TODO: When this object "finishes" we should unregister. 87 final IntentFilter intentFilterLoaded = 88 new IntentFilter(IccCardConstants.INTENT_VALUE_ICC_LOADED); 89 context.registerReceiver(mReceiver, intentFilterLoaded); 90 91 // TODO: When this object "finishes" we should unregister by invoking 92 // SubscriptionManager.getInstance(mContext).unregister(mOnSubscriptionsChangedListener); 93 // This is not strictly necessary because it will be unregistered if the 94 // notification fails but it is good form. 95 96 // Register for SubscriptionInfo list changes which is guaranteed 97 // to invoke onSubscriptionsChanged the first time. 98 SubscriptionManager.from(mContext).addOnSubscriptionsChangedListener( 99 mOnSubscriptionsChangedListener); 100 } 101 102 private void loadInBackground() { 103 // TODO (ywen) - AsyncTask to avoid creating a new thread? 104 new Thread() { 105 @Override 106 public void run() { 107 Configuration configuration = mContext.getResources().getConfiguration(); 108 // Always put the mnc/mcc in the log so we can tell which mms_config.xml 109 // was loaded. 110 LogUtil.i("MmsConfigManager loads in background mcc/mnc: " + 111 configuration.mcc + "/" + configuration.mnc); 112 load(mContext); 113 } 114 }.start(); 115 } 116 117 /** 118 * Find and return the MMS config for a particular subscription id. 119 * 120 * @param subId Subscription id of the desired MMS config bundle 121 * @return MMS config bundle for the particular subscription id. This function can return null 122 * if the MMS config cannot be found or if this function is called before the 123 * TelephonyManager has set up the SIMs, or if loadInBackground is still spawning a 124 * thread after a recent LISTEN_SUBSCRIPTION_INFO_LIST_CHANGED event. 125 */ 126 public Bundle getMmsConfigBySubId(int subId) { 127 Bundle mmsConfig; 128 synchronized(mSubIdConfigMap) { 129 mmsConfig = mSubIdConfigMap.get(subId); 130 } 131 LogUtil.i("mms config for sub " + subId + ": " + mmsConfig); 132 // Return a copy so that callers can mutate it. 133 if (mmsConfig != null) { 134 return new Bundle(mmsConfig); 135 } 136 return null; 137 } 138 139 /** 140 * This loads the MMS config for each active subscription. 141 * 142 * MMS config is fetched from CarrierConfigManager and filtered to only include MMS config 143 * variables. The resulting bundles are stored in mSubIdConfigMap. 144 */ 145 private void load(Context context) { 146 List<SubscriptionInfo> subs = mSubscriptionManager.getActiveSubscriptionInfoList(); 147 if (subs == null || subs.size() < 1) { 148 LogUtil.e(" Failed to load mms config: empty getActiveSubInfoList"); 149 return; 150 } 151 // Load all the config bundles into a new map and then swap it with the real map to avoid 152 // blocking. 153 final Map<Integer, Bundle> newConfigMap = new ArrayMap<Integer, Bundle>(); 154 final CarrierConfigManager configManager = 155 (CarrierConfigManager) context.getSystemService(Context.CARRIER_CONFIG_SERVICE); 156 for (SubscriptionInfo sub : subs) { 157 final int subId = sub.getSubscriptionId(); 158 PersistableBundle config = configManager.getConfigForSubId(subId); 159 newConfigMap.put(subId, SmsManager.getMmsConfig(config)); 160 } 161 synchronized(mSubIdConfigMap) { 162 mSubIdConfigMap.clear(); 163 mSubIdConfigMap.putAll(newConfigMap); 164 } 165 } 166 167 } 168