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