Home | History | Annotate | Download | only in telephony
      1 /*
      2  * Copyright (C) 2006 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.internal.telephony;
     18 
     19 import static android.telephony.SmsManager.RESULT_ERROR_GENERIC_FAILURE;
     20 
     21 import java.util.ArrayList;
     22 import java.util.HashMap;
     23 
     24 import android.app.PendingIntent;
     25 import android.app.PendingIntent.CanceledException;
     26 import android.os.AsyncResult;
     27 import android.os.Message;
     28 import android.provider.Telephony.Sms.Intents;
     29 import android.telephony.Rlog;
     30 
     31 import com.android.internal.telephony.cdma.CdmaSMSDispatcher;
     32 import com.android.internal.telephony.gsm.GsmSMSDispatcher;
     33 import com.android.internal.telephony.InboundSmsHandler;
     34 import com.android.internal.telephony.gsm.GsmInboundSmsHandler;
     35 import com.android.internal.telephony.cdma.CdmaInboundSmsHandler;
     36 import com.android.internal.telephony.SmsBroadcastUndelivered;
     37 
     38 public final class ImsSMSDispatcher extends SMSDispatcher {
     39     private static final String TAG = "RIL_ImsSms";
     40 
     41     private SMSDispatcher mCdmaDispatcher;
     42     private SMSDispatcher mGsmDispatcher;
     43 
     44     private GsmInboundSmsHandler mGsmInboundSmsHandler;
     45     private CdmaInboundSmsHandler mCdmaInboundSmsHandler;
     46 
     47 
     48     /** true if IMS is registered and sms is supported, false otherwise.*/
     49     private boolean mIms = false;
     50     private String mImsSmsFormat = SmsConstants.FORMAT_UNKNOWN;
     51 
     52     public ImsSMSDispatcher(PhoneBase phone, SmsStorageMonitor storageMonitor,
     53             SmsUsageMonitor usageMonitor) {
     54         super(phone, usageMonitor, null);
     55         Rlog.d(TAG, "ImsSMSDispatcher created");
     56 
     57         // Create dispatchers, inbound SMS handlers and
     58         // broadcast undelivered messages in raw table.
     59         mCdmaDispatcher = new CdmaSMSDispatcher(phone, usageMonitor, this);
     60         mGsmInboundSmsHandler = GsmInboundSmsHandler.makeInboundSmsHandler(phone.getContext(),
     61                 storageMonitor, phone);
     62         mCdmaInboundSmsHandler = CdmaInboundSmsHandler.makeInboundSmsHandler(phone.getContext(),
     63                 storageMonitor, phone, (CdmaSMSDispatcher) mCdmaDispatcher);
     64         mGsmDispatcher = new GsmSMSDispatcher(phone, usageMonitor, this, mGsmInboundSmsHandler);
     65         Thread broadcastThread = new Thread(new SmsBroadcastUndelivered(phone.getContext(),
     66                 mGsmInboundSmsHandler, mCdmaInboundSmsHandler));
     67         broadcastThread.start();
     68 
     69         mCi.registerForOn(this, EVENT_RADIO_ON, null);
     70         mCi.registerForImsNetworkStateChanged(this, EVENT_IMS_STATE_CHANGED, null);
     71     }
     72 
     73     /* Updates the phone object when there is a change */
     74     @Override
     75     protected void updatePhoneObject(PhoneBase phone) {
     76         Rlog.d(TAG, "In IMS updatePhoneObject ");
     77         super.updatePhoneObject(phone);
     78         mCdmaDispatcher.updatePhoneObject(phone);
     79         mGsmDispatcher.updatePhoneObject(phone);
     80         mGsmInboundSmsHandler.updatePhoneObject(phone);
     81         mCdmaInboundSmsHandler.updatePhoneObject(phone);
     82     }
     83 
     84     public void dispose() {
     85         mCi.unregisterForOn(this);
     86         mCi.unregisterForImsNetworkStateChanged(this);
     87         mGsmDispatcher.dispose();
     88         mCdmaDispatcher.dispose();
     89         mGsmInboundSmsHandler.dispose();
     90         mCdmaInboundSmsHandler.dispose();
     91     }
     92 
     93     /**
     94      * Handles events coming from the phone stack. Overridden from handler.
     95      *
     96      * @param msg the message to handle
     97      */
     98     @Override
     99     public void handleMessage(Message msg) {
    100         AsyncResult ar;
    101 
    102         switch (msg.what) {
    103         case EVENT_RADIO_ON:
    104         case EVENT_IMS_STATE_CHANGED: // received unsol
    105             mCi.getImsRegistrationState(this.obtainMessage(EVENT_IMS_STATE_DONE));
    106             break;
    107 
    108         case EVENT_IMS_STATE_DONE:
    109             ar = (AsyncResult) msg.obj;
    110 
    111             if (ar.exception == null) {
    112                 updateImsInfo(ar);
    113             } else {
    114                 Rlog.e(TAG, "IMS State query failed with exp "
    115                         + ar.exception);
    116             }
    117             break;
    118 
    119         default:
    120             super.handleMessage(msg);
    121         }
    122     }
    123 
    124     private void setImsSmsFormat(int format) {
    125         // valid format?
    126         switch (format) {
    127             case PhoneConstants.PHONE_TYPE_GSM:
    128                 mImsSmsFormat = "3gpp";
    129                 break;
    130             case PhoneConstants.PHONE_TYPE_CDMA:
    131                 mImsSmsFormat = "3gpp2";
    132                 break;
    133             default:
    134                 mImsSmsFormat = "unknown";
    135                 break;
    136         }
    137     }
    138 
    139     private void updateImsInfo(AsyncResult ar) {
    140         int[] responseArray = (int[])ar.result;
    141 
    142         mIms = false;
    143         if (responseArray[0] == 1) {  // IMS is registered
    144             Rlog.d(TAG, "IMS is registered!");
    145             mIms = true;
    146         } else {
    147             Rlog.d(TAG, "IMS is NOT registered!");
    148         }
    149 
    150         setImsSmsFormat(responseArray[1]);
    151 
    152         if (("unknown".equals(mImsSmsFormat))) {
    153             Rlog.e(TAG, "IMS format was unknown!");
    154             // failed to retrieve valid IMS SMS format info, set IMS to unregistered
    155             mIms = false;
    156         }
    157     }
    158 
    159     @Override
    160     protected void sendData(String destAddr, String scAddr, int destPort,
    161             byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
    162         if (isCdmaMo()) {
    163             mCdmaDispatcher.sendData(destAddr, scAddr, destPort,
    164                     data, sentIntent, deliveryIntent);
    165         } else {
    166             mGsmDispatcher.sendData(destAddr, scAddr, destPort,
    167                     data, sentIntent, deliveryIntent);
    168         }
    169     }
    170 
    171     @Override
    172     protected void sendMultipartText(String destAddr, String scAddr,
    173             ArrayList<String> parts, ArrayList<PendingIntent> sentIntents,
    174             ArrayList<PendingIntent> deliveryIntents) {
    175         if (isCdmaMo()) {
    176             mCdmaDispatcher.sendMultipartText(destAddr, scAddr,
    177                     parts, sentIntents, deliveryIntents);
    178         } else {
    179             mGsmDispatcher.sendMultipartText(destAddr, scAddr,
    180                     parts, sentIntents, deliveryIntents);
    181         }
    182     }
    183 
    184     @Override
    185     protected void sendSms(SmsTracker tracker) {
    186         //  sendSms is a helper function to other send functions, sendText/Data...
    187         //  it is not part of ISms.stub
    188         Rlog.e(TAG, "sendSms should never be called from here!");
    189     }
    190 
    191     @Override
    192     protected void sendText(String destAddr, String scAddr, String text,
    193             PendingIntent sentIntent, PendingIntent deliveryIntent) {
    194         Rlog.d(TAG, "sendText");
    195         if (isCdmaMo()) {
    196             mCdmaDispatcher.sendText(destAddr, scAddr,
    197                     text, sentIntent, deliveryIntent);
    198         } else {
    199             mGsmDispatcher.sendText(destAddr, scAddr,
    200                     text, sentIntent, deliveryIntent);
    201         }
    202     }
    203 
    204     @Override
    205     public void sendRetrySms(SmsTracker tracker) {
    206         String oldFormat = tracker.mFormat;
    207 
    208         // newFormat will be based on voice technology
    209         String newFormat =
    210             (PhoneConstants.PHONE_TYPE_CDMA == mPhone.getPhoneType()) ?
    211                     mCdmaDispatcher.getFormat() :
    212                         mGsmDispatcher.getFormat();
    213 
    214         // was previously sent sms format match with voice tech?
    215         if (oldFormat.equals(newFormat)) {
    216             if (isCdmaFormat(newFormat)) {
    217                 Rlog.d(TAG, "old format matched new format (cdma)");
    218                 mCdmaDispatcher.sendSms(tracker);
    219                 return;
    220             } else {
    221                 Rlog.d(TAG, "old format matched new format (gsm)");
    222                 mGsmDispatcher.sendSms(tracker);
    223                 return;
    224             }
    225         }
    226 
    227         // format didn't match, need to re-encode.
    228         HashMap map = tracker.mData;
    229 
    230         // to re-encode, fields needed are:  scAddr, destAddr, and
    231         //   text if originally sent as sendText or
    232         //   data and destPort if originally sent as sendData.
    233         if (!( map.containsKey("scAddr") && map.containsKey("destAddr") &&
    234                ( map.containsKey("text") ||
    235                        (map.containsKey("data") && map.containsKey("destPort"))))) {
    236             // should never come here...
    237             Rlog.e(TAG, "sendRetrySms failed to re-encode per missing fields!");
    238             if (tracker.mSentIntent != null) {
    239                 int error = RESULT_ERROR_GENERIC_FAILURE;
    240                 // Done retrying; return an error to the app.
    241                 try {
    242                     tracker.mSentIntent.send(mContext, error, null);
    243                 } catch (CanceledException ex) {}
    244             }
    245             return;
    246         }
    247         String scAddr = (String)map.get("scAddr");
    248         String destAddr = (String)map.get("destAddr");
    249 
    250         SmsMessageBase.SubmitPduBase pdu = null;
    251         //    figure out from tracker if this was sendText/Data
    252         if (map.containsKey("text")) {
    253             Rlog.d(TAG, "sms failed was text");
    254             String text = (String)map.get("text");
    255 
    256             if (isCdmaFormat(newFormat)) {
    257                 Rlog.d(TAG, "old format (gsm) ==> new format (cdma)");
    258                 pdu = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(
    259                         scAddr, destAddr, text, (tracker.mDeliveryIntent != null), null);
    260             } else {
    261                 Rlog.d(TAG, "old format (cdma) ==> new format (gsm)");
    262                 pdu = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(
    263                         scAddr, destAddr, text, (tracker.mDeliveryIntent != null), null);
    264             }
    265         } else if (map.containsKey("data")) {
    266             Rlog.d(TAG, "sms failed was data");
    267             byte[] data = (byte[])map.get("data");
    268             Integer destPort = (Integer)map.get("destPort");
    269 
    270             if (isCdmaFormat(newFormat)) {
    271                 Rlog.d(TAG, "old format (gsm) ==> new format (cdma)");
    272                 pdu = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(
    273                             scAddr, destAddr, destPort.intValue(), data,
    274                             (tracker.mDeliveryIntent != null));
    275             } else {
    276                 Rlog.d(TAG, "old format (cdma) ==> new format (gsm)");
    277                 pdu = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(
    278                             scAddr, destAddr, destPort.intValue(), data,
    279                             (tracker.mDeliveryIntent != null));
    280             }
    281         }
    282 
    283         // replace old smsc and pdu with newly encoded ones
    284         map.put("smsc", pdu.encodedScAddress);
    285         map.put("pdu", pdu.encodedMessage);
    286 
    287         SMSDispatcher dispatcher = (isCdmaFormat(newFormat)) ?
    288                 mCdmaDispatcher : mGsmDispatcher;
    289 
    290         tracker.mFormat = dispatcher.getFormat();
    291         dispatcher.sendSms(tracker);
    292     }
    293 
    294     @Override
    295     protected String getFormat() {
    296         // this function should be defined in Gsm/CdmaDispatcher.
    297         Rlog.e(TAG, "getFormat should never be called from here!");
    298         return "unknown";
    299     }
    300 
    301     @Override
    302     protected GsmAlphabet.TextEncodingDetails calculateLength(
    303             CharSequence messageBody, boolean use7bitOnly) {
    304         Rlog.e(TAG, "Error! Not implemented for IMS.");
    305         return null;
    306     }
    307 
    308     @Override
    309     protected void sendNewSubmitPdu(String destinationAddress, String scAddress, String message,
    310             SmsHeader smsHeader, int format, PendingIntent sentIntent,
    311             PendingIntent deliveryIntent, boolean lastPart) {
    312         Rlog.e(TAG, "Error! Not implemented for IMS.");
    313     }
    314 
    315     @Override
    316     public boolean isIms() {
    317         return mIms;
    318     }
    319 
    320     @Override
    321     public String getImsSmsFormat() {
    322         return mImsSmsFormat;
    323     }
    324 
    325     /**
    326      * Determines whether or not to use CDMA format for MO SMS.
    327      * If SMS over IMS is supported, then format is based on IMS SMS format,
    328      * otherwise format is based on current phone type.
    329      *
    330      * @return true if Cdma format should be used for MO SMS, false otherwise.
    331      */
    332     private boolean isCdmaMo() {
    333         if (!isIms()) {
    334             // IMS is not registered, use Voice technology to determine SMS format.
    335             return (PhoneConstants.PHONE_TYPE_CDMA == mPhone.getPhoneType());
    336         }
    337         // IMS is registered with SMS support
    338         return isCdmaFormat(mImsSmsFormat);
    339     }
    340 
    341     /**
    342      * Determines whether or not format given is CDMA format.
    343      *
    344      * @param format
    345      * @return true if format given is CDMA format, false otherwise.
    346      */
    347     private boolean isCdmaFormat(String format) {
    348         return (mCdmaDispatcher.getFormat().equals(format));
    349     }
    350 }
    351