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 com.android.internal.telephony.IccSmsInterfaceManager.SMS_MESSAGE_PERIOD_NOT_SPECIFIED;
     20 import static com.android.internal.telephony.IccSmsInterfaceManager.SMS_MESSAGE_PRIORITY_NOT_SPECIFIED;
     21 
     22 import android.app.Activity;
     23 import android.app.PendingIntent;
     24 import android.app.PendingIntent.CanceledException;
     25 import android.content.Context;
     26 import android.content.Intent;
     27 import android.net.Uri;
     28 import android.os.AsyncResult;
     29 import android.os.Handler;
     30 import android.os.Message;
     31 import android.provider.Telephony.Sms;
     32 import android.provider.Telephony.Sms.Intents;
     33 import android.telephony.Rlog;
     34 import android.telephony.SmsManager;
     35 import android.telephony.SmsMessage;
     36 import android.util.Pair;
     37 
     38 import com.android.internal.annotations.VisibleForTesting;
     39 import com.android.internal.telephony.cdma.CdmaInboundSmsHandler;
     40 import com.android.internal.telephony.cdma.CdmaSMSDispatcher;
     41 import com.android.internal.telephony.gsm.GsmInboundSmsHandler;
     42 import com.android.internal.telephony.gsm.GsmSMSDispatcher;
     43 
     44 import java.util.ArrayList;
     45 import java.util.HashMap;
     46 
     47 /**
     48  *
     49  */
     50 public class SmsDispatchersController extends Handler {
     51     private static final String TAG = "SmsDispatchersController";
     52 
     53     /** Radio is ON */
     54     private static final int EVENT_RADIO_ON = 11;
     55 
     56     /** IMS registration/SMS format changed */
     57     private static final int EVENT_IMS_STATE_CHANGED = 12;
     58 
     59     /** Callback from RIL_REQUEST_IMS_REGISTRATION_STATE */
     60     private static final int EVENT_IMS_STATE_DONE = 13;
     61 
     62     private SMSDispatcher mCdmaDispatcher;
     63     private SMSDispatcher mGsmDispatcher;
     64     private ImsSmsDispatcher mImsSmsDispatcher;
     65 
     66     private GsmInboundSmsHandler mGsmInboundSmsHandler;
     67     private CdmaInboundSmsHandler mCdmaInboundSmsHandler;
     68 
     69     private Phone mPhone;
     70     /** Outgoing message counter. Shared by all dispatchers. */
     71     private final SmsUsageMonitor mUsageMonitor;
     72     private final CommandsInterface mCi;
     73     private final Context mContext;
     74 
     75     /** true if IMS is registered and sms is supported, false otherwise.*/
     76     private boolean mIms = false;
     77     private String mImsSmsFormat = SmsConstants.FORMAT_UNKNOWN;
     78 
     79     public SmsDispatchersController(Phone phone, SmsStorageMonitor storageMonitor,
     80             SmsUsageMonitor usageMonitor) {
     81         Rlog.d(TAG, "SmsDispatchersController created");
     82 
     83         mContext = phone.getContext();
     84         mUsageMonitor = usageMonitor;
     85         mCi = phone.mCi;
     86         mPhone = phone;
     87 
     88         // Create dispatchers, inbound SMS handlers and
     89         // broadcast undelivered messages in raw table.
     90         mImsSmsDispatcher = new ImsSmsDispatcher(phone, this);
     91         mCdmaDispatcher = new CdmaSMSDispatcher(phone, this);
     92         mGsmInboundSmsHandler = GsmInboundSmsHandler.makeInboundSmsHandler(phone.getContext(),
     93                 storageMonitor, phone);
     94         mCdmaInboundSmsHandler = CdmaInboundSmsHandler.makeInboundSmsHandler(phone.getContext(),
     95                 storageMonitor, phone, (CdmaSMSDispatcher) mCdmaDispatcher);
     96         mGsmDispatcher = new GsmSMSDispatcher(phone, this, mGsmInboundSmsHandler);
     97         SmsBroadcastUndelivered.initialize(phone.getContext(),
     98                 mGsmInboundSmsHandler, mCdmaInboundSmsHandler);
     99         InboundSmsHandler.registerNewMessageNotificationActionHandler(phone.getContext());
    100 
    101         mCi.registerForOn(this, EVENT_RADIO_ON, null);
    102         mCi.registerForImsNetworkStateChanged(this, EVENT_IMS_STATE_CHANGED, null);
    103     }
    104 
    105     /* Updates the phone object when there is a change */
    106     protected void updatePhoneObject(Phone phone) {
    107         Rlog.d(TAG, "In IMS updatePhoneObject ");
    108         mCdmaDispatcher.updatePhoneObject(phone);
    109         mGsmDispatcher.updatePhoneObject(phone);
    110         mGsmInboundSmsHandler.updatePhoneObject(phone);
    111         mCdmaInboundSmsHandler.updatePhoneObject(phone);
    112     }
    113 
    114     public void dispose() {
    115         mCi.unregisterForOn(this);
    116         mCi.unregisterForImsNetworkStateChanged(this);
    117         mGsmDispatcher.dispose();
    118         mCdmaDispatcher.dispose();
    119         mGsmInboundSmsHandler.dispose();
    120         mCdmaInboundSmsHandler.dispose();
    121     }
    122 
    123     /**
    124      * Handles events coming from the phone stack. Overridden from handler.
    125      *
    126      * @param msg the message to handle
    127      */
    128     @Override
    129     public void handleMessage(Message msg) {
    130         AsyncResult ar;
    131 
    132         switch (msg.what) {
    133             case EVENT_RADIO_ON:
    134             case EVENT_IMS_STATE_CHANGED: // received unsol
    135                 mCi.getImsRegistrationState(this.obtainMessage(EVENT_IMS_STATE_DONE));
    136                 break;
    137 
    138             case EVENT_IMS_STATE_DONE:
    139                 ar = (AsyncResult) msg.obj;
    140 
    141                 if (ar.exception == null) {
    142                     updateImsInfo(ar);
    143                 } else {
    144                     Rlog.e(TAG, "IMS State query failed with exp "
    145                             + ar.exception);
    146                 }
    147                 break;
    148 
    149             default:
    150                 if (isCdmaMo()) {
    151                     mCdmaDispatcher.handleMessage(msg);
    152                 } else {
    153                     mGsmDispatcher.handleMessage(msg);
    154                 }
    155         }
    156     }
    157 
    158     private void setImsSmsFormat(int format) {
    159         switch (format) {
    160             case PhoneConstants.PHONE_TYPE_GSM:
    161                 mImsSmsFormat = SmsConstants.FORMAT_3GPP;
    162                 break;
    163             case PhoneConstants.PHONE_TYPE_CDMA:
    164                 mImsSmsFormat = SmsConstants.FORMAT_3GPP2;
    165                 break;
    166             default:
    167                 mImsSmsFormat = SmsConstants.FORMAT_UNKNOWN;
    168                 break;
    169         }
    170     }
    171 
    172     private void updateImsInfo(AsyncResult ar) {
    173         int[] responseArray = (int[]) ar.result;
    174         setImsSmsFormat(responseArray[1]);
    175         mIms = responseArray[0] == 1 && !SmsConstants.FORMAT_UNKNOWN.equals(mImsSmsFormat);
    176         Rlog.d(TAG, "IMS registration state: " + mIms + " format: " + mImsSmsFormat);
    177     }
    178 
    179     /**
    180      * Inject an SMS PDU into the android platform only if it is class 1.
    181      *
    182      * @param pdu is the byte array of pdu to be injected into android telephony layer
    183      * @param format is the format of SMS pdu (3gpp or 3gpp2)
    184      * @param callback if not NULL this callback is triggered when the message is successfully
    185      *                 received by the android telephony layer. This callback is triggered at
    186      *                 the same time an SMS received from radio is responded back.
    187      */
    188     @VisibleForTesting
    189     public void injectSmsPdu(byte[] pdu, String format, SmsInjectionCallback callback) {
    190         // TODO We need to decide whether we should allow injecting GSM(3gpp)
    191         // SMS pdus when the phone is camping on CDMA(3gpp2) network and vice versa.
    192         android.telephony.SmsMessage msg =
    193                 android.telephony.SmsMessage.createFromPdu(pdu, format);
    194         injectSmsPdu(msg, format, callback, false /* ignoreClass */);
    195     }
    196 
    197     /**
    198      * Inject an SMS PDU into the android platform.
    199      *
    200      * @param msg is the {@link SmsMessage} to be injected into android telephony layer
    201      * @param format is the format of SMS pdu (3gpp or 3gpp2)
    202      * @param callback if not NULL this callback is triggered when the message is successfully
    203      *                 received by the android telephony layer. This callback is triggered at
    204      *                 the same time an SMS received from radio is responded back.
    205      * @param ignoreClass if set to false, this method will inject class 1 sms only.
    206      */
    207     @VisibleForTesting
    208     public void injectSmsPdu(SmsMessage msg, String format, SmsInjectionCallback callback,
    209             boolean ignoreClass) {
    210         Rlog.d(TAG, "SmsDispatchersController:injectSmsPdu");
    211         try {
    212             if (msg == null) {
    213                 Rlog.e(TAG, "injectSmsPdu: createFromPdu returned null");
    214                 callback.onSmsInjectedResult(Intents.RESULT_SMS_GENERIC_ERROR);
    215                 return;
    216             }
    217 
    218             if (!ignoreClass
    219                     && msg.getMessageClass() != android.telephony.SmsMessage.MessageClass.CLASS_1) {
    220                 Rlog.e(TAG, "injectSmsPdu: not class 1");
    221                 callback.onSmsInjectedResult(Intents.RESULT_SMS_GENERIC_ERROR);
    222                 return;
    223             }
    224 
    225             AsyncResult ar = new AsyncResult(callback, msg, null);
    226 
    227             if (format.equals(SmsConstants.FORMAT_3GPP)) {
    228                 Rlog.i(TAG, "SmsDispatchersController:injectSmsText Sending msg=" + msg
    229                         + ", format=" + format + "to mGsmInboundSmsHandler");
    230                 mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_INJECT_SMS, ar);
    231             } else if (format.equals(SmsConstants.FORMAT_3GPP2)) {
    232                 Rlog.i(TAG, "SmsDispatchersController:injectSmsText Sending msg=" + msg
    233                         + ", format=" + format + "to mCdmaInboundSmsHandler");
    234                 mCdmaInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_INJECT_SMS, ar);
    235             } else {
    236                 // Invalid pdu format.
    237                 Rlog.e(TAG, "Invalid pdu format: " + format);
    238                 callback.onSmsInjectedResult(Intents.RESULT_SMS_GENERIC_ERROR);
    239             }
    240         } catch (Exception e) {
    241             Rlog.e(TAG, "injectSmsPdu failed: ", e);
    242             callback.onSmsInjectedResult(Intents.RESULT_SMS_GENERIC_ERROR);
    243         }
    244     }
    245 
    246     /**
    247      * Retry the message along to the radio.
    248      *
    249      * @param tracker holds the SMS message to send
    250      */
    251     public void sendRetrySms(SMSDispatcher.SmsTracker tracker) {
    252         String oldFormat = tracker.mFormat;
    253 
    254         // newFormat will be based on voice technology
    255         String newFormat =
    256                 (PhoneConstants.PHONE_TYPE_CDMA == mPhone.getPhoneType())
    257                         ? mCdmaDispatcher.getFormat() : mGsmDispatcher.getFormat();
    258 
    259         // was previously sent sms format match with voice tech?
    260         if (oldFormat.equals(newFormat)) {
    261             if (isCdmaFormat(newFormat)) {
    262                 Rlog.d(TAG, "old format matched new format (cdma)");
    263                 mCdmaDispatcher.sendSms(tracker);
    264                 return;
    265             } else {
    266                 Rlog.d(TAG, "old format matched new format (gsm)");
    267                 mGsmDispatcher.sendSms(tracker);
    268                 return;
    269             }
    270         }
    271 
    272         // format didn't match, need to re-encode.
    273         HashMap map = tracker.getData();
    274 
    275         // to re-encode, fields needed are:  scAddr, destAddr, and
    276         //   text if originally sent as sendText or
    277         //   data and destPort if originally sent as sendData.
    278         if (!(map.containsKey("scAddr") && map.containsKey("destAddr")
    279                 && (map.containsKey("text")
    280                 || (map.containsKey("data") && map.containsKey("destPort"))))) {
    281             // should never come here...
    282             Rlog.e(TAG, "sendRetrySms failed to re-encode per missing fields!");
    283             tracker.onFailed(mContext, SmsManager.RESULT_ERROR_GENERIC_FAILURE, 0/*errorCode*/);
    284             return;
    285         }
    286         String scAddr = (String) map.get("scAddr");
    287         String destAddr = (String) map.get("destAddr");
    288 
    289         SmsMessageBase.SubmitPduBase pdu = null;
    290         //    figure out from tracker if this was sendText/Data
    291         if (map.containsKey("text")) {
    292             Rlog.d(TAG, "sms failed was text");
    293             String text = (String) map.get("text");
    294 
    295             if (isCdmaFormat(newFormat)) {
    296                 Rlog.d(TAG, "old format (gsm) ==> new format (cdma)");
    297                 pdu = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(
    298                         scAddr, destAddr, text, (tracker.mDeliveryIntent != null), null);
    299             } else {
    300                 Rlog.d(TAG, "old format (cdma) ==> new format (gsm)");
    301                 pdu = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(
    302                         scAddr, destAddr, text, (tracker.mDeliveryIntent != null), null);
    303             }
    304         } else if (map.containsKey("data")) {
    305             Rlog.d(TAG, "sms failed was data");
    306             byte[] data = (byte[]) map.get("data");
    307             Integer destPort = (Integer) map.get("destPort");
    308 
    309             if (isCdmaFormat(newFormat)) {
    310                 Rlog.d(TAG, "old format (gsm) ==> new format (cdma)");
    311                 pdu = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu(
    312                             scAddr, destAddr, destPort.intValue(), data,
    313                             (tracker.mDeliveryIntent != null));
    314             } else {
    315                 Rlog.d(TAG, "old format (cdma) ==> new format (gsm)");
    316                 pdu = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu(
    317                             scAddr, destAddr, destPort.intValue(), data,
    318                             (tracker.mDeliveryIntent != null));
    319             }
    320         }
    321 
    322         // replace old smsc and pdu with newly encoded ones
    323         map.put("smsc", pdu.encodedScAddress);
    324         map.put("pdu", pdu.encodedMessage);
    325 
    326         SMSDispatcher dispatcher = (isCdmaFormat(newFormat)) ? mCdmaDispatcher : mGsmDispatcher;
    327 
    328         tracker.mFormat = dispatcher.getFormat();
    329         dispatcher.sendSms(tracker);
    330     }
    331 
    332     public boolean isIms() {
    333         return mIms;
    334     }
    335 
    336     public String getImsSmsFormat() {
    337         return mImsSmsFormat;
    338     }
    339 
    340     /**
    341      * Determines whether or not to use CDMA format for MO SMS.
    342      * If SMS over IMS is supported, then format is based on IMS SMS format,
    343      * otherwise format is based on current phone type.
    344      *
    345      * @return true if Cdma format should be used for MO SMS, false otherwise.
    346      */
    347     protected boolean isCdmaMo() {
    348         if (!isIms()) {
    349             // IMS is not registered, use Voice technology to determine SMS format.
    350             return (PhoneConstants.PHONE_TYPE_CDMA == mPhone.getPhoneType());
    351         }
    352         // IMS is registered with SMS support
    353         return isCdmaFormat(mImsSmsFormat);
    354     }
    355 
    356     /**
    357      * Determines whether or not format given is CDMA format.
    358      *
    359      * @param format
    360      * @return true if format given is CDMA format, false otherwise.
    361      */
    362     public boolean isCdmaFormat(String format) {
    363         return (mCdmaDispatcher.getFormat().equals(format));
    364     }
    365 
    366     /**
    367      * Send a data based SMS to a specific application port.
    368      *
    369      * @param destAddr the address to send the message to
    370      * @param scAddr is the service center address or null to use
    371      *  the current default SMSC
    372      * @param destPort the port to deliver the message to
    373      * @param data the body of the message to send
    374      * @param sentIntent if not NULL this <code>PendingIntent</code> is
    375      *  broadcast when the message is successfully sent, or failed.
    376      *  The result code will be <code>Activity.RESULT_OK<code> for success,
    377      *  or one of these errors:<br>
    378      *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
    379      *  <code>RESULT_ERROR_RADIO_OFF</code><br>
    380      *  <code>RESULT_ERROR_NULL_PDU</code><br>
    381      *  <code>RESULT_ERROR_NO_SERVICE</code><br>.
    382      *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
    383      *  the extra "errorCode" containing a radio technology specific value,
    384      *  generally only useful for troubleshooting.<br>
    385      *  The per-application based SMS control checks sentIntent. If sentIntent
    386      *  is NULL the caller will be checked against all unknown applications,
    387      *  which cause smaller number of SMS to be sent in checking period.
    388      * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
    389      *  broadcast when the message is delivered to the recipient.  The
    390      *  raw pdu of the status report is in the extended data ("pdu").
    391      */
    392     protected void sendData(String destAddr, String scAddr, int destPort,
    393                             byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
    394         if (mImsSmsDispatcher.isAvailable()) {
    395             mImsSmsDispatcher.sendData(destAddr, scAddr, destPort, data, sentIntent,
    396                     deliveryIntent);
    397         } else if (isCdmaMo()) {
    398             mCdmaDispatcher.sendData(destAddr, scAddr, destPort, data, sentIntent, deliveryIntent);
    399         } else {
    400             mGsmDispatcher.sendData(destAddr, scAddr, destPort, data, sentIntent, deliveryIntent);
    401         }
    402     }
    403 
    404     /**
    405      * Send a text based SMS.
    406      *  @param destAddr the address to send the message to
    407      * @param scAddr is the service center address or null to use
    408      *  the current default SMSC
    409      * @param text the body of the message to send
    410      * @param sentIntent if not NULL this <code>PendingIntent</code> is
    411      *  broadcast when the message is successfully sent, or failed.
    412      *  The result code will be <code>Activity.RESULT_OK<code> for success,
    413      *  or one of these errors:<br>
    414      *  <code>RESULT_ERROR_GENERIC_FAILURE</code><br>
    415      *  <code>RESULT_ERROR_RADIO_OFF</code><br>
    416      *  <code>RESULT_ERROR_NULL_PDU</code><br>
    417      *  <code>RESULT_ERROR_NO_SERVICE</code><br>.
    418      *  For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include
    419      *  the extra "errorCode" containing a radio technology specific value,
    420      *  generally only useful for troubleshooting.<br>
    421      *  The per-application based SMS control checks sentIntent. If sentIntent
    422      *  is NULL the caller will be checked against all unknown applications,
    423      *  which cause smaller number of SMS to be sent in checking period.
    424      * @param deliveryIntent if not NULL this <code>PendingIntent</code> is
    425      *  broadcast when the message is delivered to the recipient.  The
    426      * @param messageUri optional URI of the message if it is already stored in the system
    427      * @param callingPkg the calling package name
    428      * @param persistMessage whether to save the sent message into SMS DB for a
    429      *   non-default SMS app.
    430      * @param priority Priority level of the message
    431      *  Refer specification See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1
    432      *  ---------------------------------
    433      *  PRIORITY      | Level of Priority
    434      *  ---------------------------------
    435      *      '00'      |     Normal
    436      *      '01'      |     Interactive
    437      *      '10'      |     Urgent
    438      *      '11'      |     Emergency
    439      *  ----------------------------------
    440      *  Any Other values included Negative considered as Invalid Priority Indicator of the message.
    441      * @param expectMore is a boolean to indicate the sending messages through same link or not.
    442      * @param validityPeriod Validity Period of the message in mins.
    443      *  Refer specification 3GPP TS 23.040 V6.8.1 section 9.2.3.12.1.
    444      *  Validity Period(Minimum) -> 5 mins
    445      *  Validity Period(Maximum) -> 635040 mins(i.e.63 weeks).
    446      *  Any Other values included Negative considered as Invalid Validity Period of the message.
    447      */
    448     public void sendText(String destAddr, String scAddr, String text,
    449                             PendingIntent sentIntent, PendingIntent deliveryIntent, Uri messageUri,
    450                             String callingPkg, boolean persistMessage, int priority,
    451                             boolean expectMore, int validityPeriod) {
    452         if (mImsSmsDispatcher.isAvailable()) {
    453             mImsSmsDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent,
    454                     messageUri, callingPkg, persistMessage, SMS_MESSAGE_PRIORITY_NOT_SPECIFIED,
    455                     false /*expectMore*/, SMS_MESSAGE_PERIOD_NOT_SPECIFIED);
    456         } else {
    457             if (isCdmaMo()) {
    458                 mCdmaDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent,
    459                         messageUri, callingPkg, persistMessage, priority, expectMore,
    460                         validityPeriod);
    461             } else {
    462                 mGsmDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent,
    463                         messageUri, callingPkg, persistMessage, priority, expectMore,
    464                         validityPeriod);
    465             }
    466         }
    467     }
    468 
    469     /**
    470      * Send a multi-part text based SMS.
    471      *  @param destAddr the address to send the message to
    472      * @param scAddr is the service center address or null to use
    473      *   the current default SMSC
    474      * @param parts an <code>ArrayList</code> of strings that, in order,
    475      *   comprise the original message
    476      * @param sentIntents if not null, an <code>ArrayList</code> of
    477      *   <code>PendingIntent</code>s (one for each message part) that is
    478      *   broadcast when the corresponding message part has been sent.
    479      *   The result code will be <code>Activity.RESULT_OK<code> for success,
    480      *   or one of these errors:
    481      *   <code>RESULT_ERROR_GENERIC_FAILURE</code>
    482      *   <code>RESULT_ERROR_RADIO_OFF</code>
    483      *   <code>RESULT_ERROR_NULL_PDU</code>
    484      *   <code>RESULT_ERROR_NO_SERVICE</code>.
    485      *  The per-application based SMS control checks sentIntent. If sentIntent
    486      *  is NULL the caller will be checked against all unknown applications,
    487      *  which cause smaller number of SMS to be sent in checking period.
    488      * @param deliveryIntents if not null, an <code>ArrayList</code> of
    489      *   <code>PendingIntent</code>s (one for each message part) that is
    490      *   broadcast when the corresponding message part has been delivered
    491      *   to the recipient.  The raw pdu of the status report is in the
    492      * @param messageUri optional URI of the message if it is already stored in the system
    493      * @param callingPkg the calling package name
    494      * @param persistMessage whether to save the sent message into SMS DB for a
    495      *   non-default SMS app.
    496      * @param priority Priority level of the message
    497      *  Refer specification See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1
    498      *  ---------------------------------
    499      *  PRIORITY      | Level of Priority
    500      *  ---------------------------------
    501      *      '00'      |     Normal
    502      *      '01'      |     Interactive
    503      *      '10'      |     Urgent
    504      *      '11'      |     Emergency
    505      *  ----------------------------------
    506      *  Any Other values included Negative considered as Invalid Priority Indicator of the message.
    507      * @param expectMore is a boolean to indicate the sending messages through same link or not.
    508      * @param validityPeriod Validity Period of the message in mins.
    509      *  Refer specification 3GPP TS 23.040 V6.8.1 section 9.2.3.12.1.
    510      *  Validity Period(Minimum) -> 5 mins
    511      *  Validity Period(Maximum) -> 635040 mins(i.e.63 weeks).
    512      *  Any Other values included Negative considered as Invalid Validity Period of the message.
    513 
    514      */
    515     protected void sendMultipartText(String destAddr, String scAddr,
    516             ArrayList<String> parts, ArrayList<PendingIntent> sentIntents,
    517             ArrayList<PendingIntent> deliveryIntents, Uri messageUri, String callingPkg,
    518             boolean persistMessage, int priority, boolean expectMore, int validityPeriod) {
    519         if (mImsSmsDispatcher.isAvailable()) {
    520             mImsSmsDispatcher.sendMultipartText(destAddr, scAddr, parts, sentIntents,
    521                     deliveryIntents, messageUri, callingPkg, persistMessage,
    522                     SMS_MESSAGE_PRIORITY_NOT_SPECIFIED,
    523                     false /*expectMore*/, SMS_MESSAGE_PERIOD_NOT_SPECIFIED);
    524         } else {
    525             if (isCdmaMo()) {
    526                 mCdmaDispatcher.sendMultipartText(destAddr, scAddr, parts, sentIntents,
    527                         deliveryIntents, messageUri, callingPkg, persistMessage, priority,
    528                         expectMore, validityPeriod);
    529             } else {
    530                 mGsmDispatcher.sendMultipartText(destAddr, scAddr, parts, sentIntents,
    531                         deliveryIntents, messageUri, callingPkg, persistMessage, priority,
    532                         expectMore, validityPeriod);
    533             }
    534         }
    535     }
    536 
    537     /**
    538      * Returns the premium SMS permission for the specified package. If the package has never
    539      * been seen before, the default {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ASK_USER}
    540      * will be returned.
    541      * @param packageName the name of the package to query permission
    542      * @return one of {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_UNKNOWN},
    543      *  {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ASK_USER},
    544      *  {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_NEVER_ALLOW}, or
    545      *  {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW}
    546      */
    547     public int getPremiumSmsPermission(String packageName) {
    548         return mUsageMonitor.getPremiumSmsPermission(packageName);
    549     }
    550 
    551     /**
    552      * Sets the premium SMS permission for the specified package and save the value asynchronously
    553      * to persistent storage.
    554      * @param packageName the name of the package to set permission
    555      * @param permission one of {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ASK_USER},
    556      *  {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_NEVER_ALLOW}, or
    557      *  {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW}
    558      */
    559     public void setPremiumSmsPermission(String packageName, int permission) {
    560         mUsageMonitor.setPremiumSmsPermission(packageName, permission);
    561     }
    562 
    563     public SmsUsageMonitor getUsageMonitor() {
    564         return mUsageMonitor;
    565     }
    566 
    567     /**
    568      * Triggers the correct method for handling the sms status report based on the format.
    569      *
    570      * @param tracker the sms tracker.
    571      * @param format the format.
    572      * @param pdu the pdu of the report.
    573      * @return a Pair in which the first boolean is whether the report was handled successfully
    574      *          or not and the second boolean is whether processing the sms is complete and the
    575      *          tracker no longer need to be kept track of, false if we should expect more callbacks
    576      *          and the tracker should be kept.
    577      */
    578     public Pair<Boolean, Boolean> handleSmsStatusReport(SMSDispatcher.SmsTracker tracker,
    579             String format, byte[] pdu) {
    580         if (isCdmaFormat(format)) {
    581             return handleCdmaStatusReport(tracker, format, pdu);
    582         } else {
    583             return handleGsmStatusReport(tracker, format, pdu);
    584         }
    585     }
    586 
    587     private Pair<Boolean, Boolean> handleCdmaStatusReport(SMSDispatcher.SmsTracker tracker,
    588             String format, byte[] pdu) {
    589         tracker.updateSentMessageStatus(mContext, Sms.STATUS_COMPLETE);
    590         boolean success = triggerDeliveryIntent(tracker, format, pdu);
    591         return new Pair(success, true /* complete */);
    592     }
    593 
    594     private Pair<Boolean, Boolean> handleGsmStatusReport(SMSDispatcher.SmsTracker tracker,
    595             String format, byte[] pdu) {
    596         com.android.internal.telephony.gsm.SmsMessage sms =
    597                 com.android.internal.telephony.gsm.SmsMessage.newFromCDS(pdu);
    598         boolean complete = false;
    599         boolean success = false;
    600         if (sms != null) {
    601             int tpStatus = sms.getStatus();
    602             if(tpStatus >= Sms.STATUS_FAILED || tpStatus < Sms.STATUS_PENDING ) {
    603                 // Update the message status (COMPLETE or FAILED)
    604                 tracker.updateSentMessageStatus(mContext, tpStatus);
    605                 complete = true;
    606             }
    607             success = triggerDeliveryIntent(tracker, format, pdu);
    608         }
    609         return new Pair(success, complete);
    610     }
    611 
    612     private boolean triggerDeliveryIntent(SMSDispatcher.SmsTracker tracker, String format,
    613                                           byte[] pdu) {
    614         PendingIntent intent = tracker.mDeliveryIntent;
    615         Intent fillIn = new Intent();
    616         fillIn.putExtra("pdu", pdu);
    617         fillIn.putExtra("format", format);
    618         try {
    619             intent.send(mContext, Activity.RESULT_OK, fillIn);
    620             return true;
    621         } catch (CanceledException ex) {
    622             return false;
    623         }
    624     }
    625 
    626 
    627     public interface SmsInjectionCallback {
    628         void onSmsInjectedResult(int result);
    629     }
    630 }
    631