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