Home | History | Annotate | Download | only in gsm
      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.gsm;
     18 
     19 import android.app.Activity;
     20 import android.app.PendingIntent;
     21 import android.app.PendingIntent.CanceledException;
     22 import android.content.Intent;
     23 import android.net.Uri;
     24 import android.os.AsyncResult;
     25 import android.os.Message;
     26 import android.provider.Telephony.Sms;
     27 import android.provider.Telephony.Sms.Intents;
     28 import android.telephony.Rlog;
     29 import android.telephony.ServiceState;
     30 
     31 import com.android.internal.telephony.GsmAlphabet;
     32 import com.android.internal.telephony.ImsSMSDispatcher;
     33 import com.android.internal.telephony.InboundSmsHandler;
     34 import com.android.internal.telephony.PhoneBase;
     35 import com.android.internal.telephony.SMSDispatcher;
     36 import com.android.internal.telephony.SmsConstants;
     37 import com.android.internal.telephony.SmsHeader;
     38 import com.android.internal.telephony.SmsUsageMonitor;
     39 import com.android.internal.telephony.uicc.IccRecords;
     40 import com.android.internal.telephony.uicc.IccUtils;
     41 import com.android.internal.telephony.uicc.UiccCardApplication;
     42 import com.android.internal.telephony.uicc.UiccController;
     43 
     44 import java.util.HashMap;
     45 import java.util.concurrent.atomic.AtomicBoolean;
     46 import java.util.concurrent.atomic.AtomicInteger;
     47 import java.util.concurrent.atomic.AtomicReference;
     48 
     49 public final class GsmSMSDispatcher extends SMSDispatcher {
     50     private static final String TAG = "GsmSMSDispatcher";
     51     private static final boolean VDBG = false;
     52     protected UiccController mUiccController = null;
     53     private AtomicReference<IccRecords> mIccRecords = new AtomicReference<IccRecords>();
     54     private AtomicReference<UiccCardApplication> mUiccApplication =
     55             new AtomicReference<UiccCardApplication>();
     56     private GsmInboundSmsHandler mGsmInboundSmsHandler;
     57 
     58     /** Status report received */
     59     private static final int EVENT_NEW_SMS_STATUS_REPORT = 100;
     60 
     61     public GsmSMSDispatcher(PhoneBase phone, SmsUsageMonitor usageMonitor,
     62             ImsSMSDispatcher imsSMSDispatcher,
     63             GsmInboundSmsHandler gsmInboundSmsHandler) {
     64         super(phone, usageMonitor, imsSMSDispatcher);
     65         mCi.setOnSmsStatus(this, EVENT_NEW_SMS_STATUS_REPORT, null);
     66         mGsmInboundSmsHandler = gsmInboundSmsHandler;
     67         mUiccController = UiccController.getInstance();
     68         mUiccController.registerForIccChanged(this, EVENT_ICC_CHANGED, null);
     69         Rlog.d(TAG, "GsmSMSDispatcher created");
     70     }
     71 
     72     @Override
     73     public void dispose() {
     74         super.dispose();
     75         mCi.unSetOnSmsStatus(this);
     76         mUiccController.unregisterForIccChanged(this);
     77     }
     78 
     79     @Override
     80     protected String getFormat() {
     81         return SmsConstants.FORMAT_3GPP;
     82     }
     83 
     84     /**
     85      * Handles 3GPP format-specific events coming from the phone stack.
     86      * Other events are handled by {@link SMSDispatcher#handleMessage}.
     87      *
     88      * @param msg the message to handle
     89      */
     90     @Override
     91     public void handleMessage(Message msg) {
     92         switch (msg.what) {
     93         case EVENT_NEW_SMS_STATUS_REPORT:
     94             handleStatusReport((AsyncResult) msg.obj);
     95             break;
     96 
     97         case EVENT_NEW_ICC_SMS:
     98         // pass to InboundSmsHandler to process
     99         mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, msg.obj);
    100         break;
    101 
    102         case EVENT_ICC_CHANGED:
    103             onUpdateIccAvailability();
    104             break;
    105 
    106         default:
    107             super.handleMessage(msg);
    108         }
    109     }
    110 
    111     /**
    112      * Called when a status report is received.  This should correspond to
    113      * a previously successful SEND.
    114      *
    115      * @param ar AsyncResult passed into the message handler.  ar.result should
    116      *           be a String representing the status report PDU, as ASCII hex.
    117      */
    118     private void handleStatusReport(AsyncResult ar) {
    119         String pduString = (String) ar.result;
    120         SmsMessage sms = SmsMessage.newFromCDS(pduString);
    121 
    122         if (sms != null) {
    123             int tpStatus = sms.getStatus();
    124             int messageRef = sms.mMessageRef;
    125             for (int i = 0, count = deliveryPendingList.size(); i < count; i++) {
    126                 SmsTracker tracker = deliveryPendingList.get(i);
    127                 if (tracker.mMessageRef == messageRef) {
    128                     // Found it.  Remove from list and broadcast.
    129                     if(tpStatus >= Sms.STATUS_FAILED || tpStatus < Sms.STATUS_PENDING ) {
    130                        deliveryPendingList.remove(i);
    131                        // Update the message status (COMPLETE or FAILED)
    132                        tracker.updateSentMessageStatus(mContext, tpStatus);
    133                     }
    134                     PendingIntent intent = tracker.mDeliveryIntent;
    135                     Intent fillIn = new Intent();
    136                     fillIn.putExtra("pdu", IccUtils.hexStringToBytes(pduString));
    137                     fillIn.putExtra("format", getFormat());
    138                     try {
    139                         intent.send(mContext, Activity.RESULT_OK, fillIn);
    140                     } catch (CanceledException ex) {}
    141 
    142                     // Only expect to see one tracker matching this messageref
    143                     break;
    144                 }
    145             }
    146         }
    147         mCi.acknowledgeLastIncomingGsmSms(true, Intents.RESULT_SMS_HANDLED, null);
    148     }
    149 
    150     /** {@inheritDoc} */
    151     @Override
    152     protected void sendData(String destAddr, String scAddr, int destPort,
    153             byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) {
    154         SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(
    155                 scAddr, destAddr, destPort, data, (deliveryIntent != null));
    156         if (pdu != null) {
    157             HashMap map = getSmsTrackerMap(destAddr, scAddr, destPort, data, pdu);
    158             SmsTracker tracker = getSmsTracker(map, sentIntent, deliveryIntent, getFormat(),
    159                     null /*messageUri*/, false /*isExpectMore*/, null /*fullMessageText*/,
    160                     false /*isText*/);
    161 
    162             String carrierPackage = getCarrierAppPackageName();
    163             if (carrierPackage != null) {
    164                 Rlog.d(TAG, "Found carrier package.");
    165                 DataSmsSender smsSender = new DataSmsSender(tracker);
    166                 smsSender.sendSmsByCarrierApp(carrierPackage, new SmsSenderCallback(smsSender));
    167             } else {
    168                 Rlog.v(TAG, "No carrier package.");
    169                 sendRawPdu(tracker);
    170             }
    171         } else {
    172             Rlog.e(TAG, "GsmSMSDispatcher.sendData(): getSubmitPdu() returned null");
    173         }
    174     }
    175 
    176     /** {@inheritDoc} */
    177     @Override
    178     protected void sendText(String destAddr, String scAddr, String text, PendingIntent sentIntent,
    179             PendingIntent deliveryIntent, Uri messageUri, String callingPkg) {
    180         SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(
    181                 scAddr, destAddr, text, (deliveryIntent != null));
    182         if (pdu != null) {
    183             HashMap map = getSmsTrackerMap(destAddr, scAddr, text, pdu);
    184             SmsTracker tracker = getSmsTracker(map, sentIntent, deliveryIntent, getFormat(),
    185                     messageUri, false /*isExpectMore*/, text /*fullMessageText*/, true /*isText*/);
    186 
    187             String carrierPackage = getCarrierAppPackageName();
    188             if (carrierPackage != null) {
    189                 Rlog.d(TAG, "Found carrier package.");
    190                 TextSmsSender smsSender = new TextSmsSender(tracker);
    191                 smsSender.sendSmsByCarrierApp(carrierPackage, new SmsSenderCallback(smsSender));
    192             } else {
    193                 Rlog.v(TAG, "No carrier package.");
    194                 sendRawPdu(tracker);
    195             }
    196         } else {
    197             Rlog.e(TAG, "GsmSMSDispatcher.sendText(): getSubmitPdu() returned null");
    198         }
    199     }
    200 
    201     /** {@inheritDoc} */
    202     @Override
    203     protected void injectSmsPdu(byte[] pdu, String format, PendingIntent receivedIntent) {
    204         throw new IllegalStateException("This method must be called only on ImsSMSDispatcher");
    205     }
    206 
    207     /** {@inheritDoc} */
    208     @Override
    209     protected GsmAlphabet.TextEncodingDetails calculateLength(CharSequence messageBody,
    210             boolean use7bitOnly) {
    211         return SmsMessage.calculateLength(messageBody, use7bitOnly);
    212     }
    213 
    214     /** {@inheritDoc} */
    215     @Override
    216     protected SmsTracker getNewSubmitPduTracker(String destinationAddress, String scAddress,
    217             String message, SmsHeader smsHeader, int encoding,
    218             PendingIntent sentIntent, PendingIntent deliveryIntent, boolean lastPart,
    219             AtomicInteger unsentPartCount, AtomicBoolean anyPartFailed, Uri messageUri,
    220             String fullMessageText) {
    221         SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(scAddress, destinationAddress,
    222                 message, deliveryIntent != null, SmsHeader.toByteArray(smsHeader),
    223                 encoding, smsHeader.languageTable, smsHeader.languageShiftTable);
    224         if (pdu != null) {
    225             HashMap map =  getSmsTrackerMap(destinationAddress, scAddress,
    226                     message, pdu);
    227             return getSmsTracker(map, sentIntent,
    228                     deliveryIntent, getFormat(), unsentPartCount, anyPartFailed, messageUri,
    229                     smsHeader, !lastPart, fullMessageText, true /*isText*/);
    230         } else {
    231             Rlog.e(TAG, "GsmSMSDispatcher.sendNewSubmitPdu(): getSubmitPdu() returned null");
    232             return null;
    233         }
    234     }
    235 
    236     @Override
    237     protected void sendSubmitPdu(SmsTracker tracker) {
    238         sendRawPdu(tracker);
    239     }
    240 
    241     /** {@inheritDoc} */
    242     @Override
    243     protected void sendSms(SmsTracker tracker) {
    244         HashMap<String, Object> map = tracker.mData;
    245 
    246         byte pdu[] = (byte[]) map.get("pdu");
    247 
    248         if (tracker.mRetryCount > 0) {
    249             Rlog.d(TAG, "sendSms: "
    250                     + " mRetryCount=" + tracker.mRetryCount
    251                     + " mMessageRef=" + tracker.mMessageRef
    252                     + " SS=" + mPhone.getServiceState().getState());
    253 
    254             // per TS 23.040 Section 9.2.3.6:  If TP-MTI SMS-SUBMIT (0x01) type
    255             //   TP-RD (bit 2) is 1 for retry
    256             //   and TP-MR is set to previously failed sms TP-MR
    257             if (((0x01 & pdu[0]) == 0x01)) {
    258                 pdu[0] |= 0x04; // TP-RD
    259                 pdu[1] = (byte) tracker.mMessageRef; // TP-MR
    260             }
    261         }
    262         Rlog.d(TAG, "sendSms: "
    263                 + " isIms()=" + isIms()
    264                 + " mRetryCount=" + tracker.mRetryCount
    265                 + " mImsRetry=" + tracker.mImsRetry
    266                 + " mMessageRef=" + tracker.mMessageRef
    267                 + " SS=" + mPhone.getServiceState().getState());
    268 
    269         sendSmsByPstn(tracker);
    270     }
    271 
    272     /** {@inheritDoc} */
    273     @Override
    274     protected void sendSmsByPstn(SmsTracker tracker) {
    275         int ss = mPhone.getServiceState().getState();
    276         // if sms over IMS is not supported on data and voice is not available...
    277         if (!isIms() && ss != ServiceState.STATE_IN_SERVICE) {
    278             tracker.onFailed(mContext, getNotInServiceError(ss), 0/*errorCode*/);
    279             return;
    280         }
    281 
    282         HashMap<String, Object> map = tracker.mData;
    283 
    284         byte smsc[] = (byte[]) map.get("smsc");
    285         byte[] pdu = (byte[]) map.get("pdu");
    286         Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker);
    287 
    288         // sms over gsm is used:
    289         //   if sms over IMS is not supported AND
    290         //   this is not a retry case after sms over IMS failed
    291         //     indicated by mImsRetry > 0
    292         if (0 == tracker.mImsRetry && !isIms()) {
    293             if (tracker.mRetryCount > 0) {
    294                 // per TS 23.040 Section 9.2.3.6:  If TP-MTI SMS-SUBMIT (0x01) type
    295                 //   TP-RD (bit 2) is 1 for retry
    296                 //   and TP-MR is set to previously failed sms TP-MR
    297                 if (((0x01 & pdu[0]) == 0x01)) {
    298                     pdu[0] |= 0x04; // TP-RD
    299                     pdu[1] = (byte) tracker.mMessageRef; // TP-MR
    300                 }
    301             }
    302             if (tracker.mRetryCount == 0 && tracker.mExpectMore) {
    303                 mCi.sendSMSExpectMore(IccUtils.bytesToHexString(smsc),
    304                         IccUtils.bytesToHexString(pdu), reply);
    305             } else {
    306                 mCi.sendSMS(IccUtils.bytesToHexString(smsc),
    307                         IccUtils.bytesToHexString(pdu), reply);
    308             }
    309         } else {
    310             mCi.sendImsGsmSms(IccUtils.bytesToHexString(smsc),
    311                     IccUtils.bytesToHexString(pdu), tracker.mImsRetry,
    312                     tracker.mMessageRef, reply);
    313             // increment it here, so in case of SMS_FAIL_RETRY over IMS
    314             // next retry will be sent using IMS request again.
    315             tracker.mImsRetry++;
    316         }
    317     }
    318 
    319     protected UiccCardApplication getUiccCardApplication() {
    320             Rlog.d(TAG, "GsmSMSDispatcher: subId = " + mPhone.getSubId()
    321                     + " slotId = " + mPhone.getPhoneId());
    322                 return mUiccController.getUiccCardApplication(mPhone.getPhoneId(),
    323                         UiccController.APP_FAM_3GPP);
    324     }
    325 
    326     private void onUpdateIccAvailability() {
    327         if (mUiccController == null ) {
    328             return;
    329         }
    330 
    331         UiccCardApplication newUiccApplication = getUiccCardApplication();
    332 
    333         UiccCardApplication app = mUiccApplication.get();
    334         if (app != newUiccApplication) {
    335             if (app != null) {
    336                 Rlog.d(TAG, "Removing stale icc objects.");
    337                 if (mIccRecords.get() != null) {
    338                     mIccRecords.get().unregisterForNewSms(this);
    339                 }
    340                 mIccRecords.set(null);
    341                 mUiccApplication.set(null);
    342             }
    343             if (newUiccApplication != null) {
    344                 Rlog.d(TAG, "New Uicc application found");
    345                 mUiccApplication.set(newUiccApplication);
    346                 mIccRecords.set(newUiccApplication.getIccRecords());
    347                 if (mIccRecords.get() != null) {
    348                     mIccRecords.get().registerForNewSms(this, EVENT_NEW_ICC_SMS, null);
    349                 }
    350             }
    351         }
    352     }
    353 }
    354