Home | History | Annotate | Download | only in service
      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 
     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 
     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 
    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      */
    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      */
    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