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.os.AsyncResult;
     20 import android.os.Message;
     21 import android.provider.Telephony.Sms.Intents;
     22 import android.telephony.Rlog;
     23 import android.telephony.ServiceState;
     24 import android.util.Pair;
     25 
     26 import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails;
     27 import com.android.internal.telephony.InboundSmsHandler;
     28 import com.android.internal.telephony.Phone;
     29 import com.android.internal.telephony.SmsConstants;
     30 import com.android.internal.telephony.SmsDispatchersController;
     31 import com.android.internal.telephony.SMSDispatcher;
     32 import com.android.internal.telephony.SmsHeader;
     33 import com.android.internal.telephony.SmsMessageBase;
     34 import com.android.internal.telephony.uicc.IccRecords;
     35 import com.android.internal.telephony.uicc.IccUtils;
     36 import com.android.internal.telephony.uicc.UiccCardApplication;
     37 import com.android.internal.telephony.uicc.UiccController;
     38 import com.android.internal.telephony.util.SMSDispatcherUtil;
     39 
     40 import java.util.HashMap;
     41 import java.util.concurrent.atomic.AtomicReference;
     42 
     43 public final class GsmSMSDispatcher extends SMSDispatcher {
     44     private static final String TAG = "GsmSMSDispatcher";
     45     protected UiccController mUiccController = null;
     46     private AtomicReference<IccRecords> mIccRecords = new AtomicReference<IccRecords>();
     47     private AtomicReference<UiccCardApplication> mUiccApplication =
     48             new AtomicReference<UiccCardApplication>();
     49     private GsmInboundSmsHandler mGsmInboundSmsHandler;
     50 
     51     /** Status report received */
     52     private static final int EVENT_NEW_SMS_STATUS_REPORT = 100;
     53 
     54     public GsmSMSDispatcher(Phone phone, SmsDispatchersController smsDispatchersController,
     55             GsmInboundSmsHandler gsmInboundSmsHandler) {
     56         super(phone, smsDispatchersController);
     57         mCi.setOnSmsStatus(this, EVENT_NEW_SMS_STATUS_REPORT, null);
     58         mGsmInboundSmsHandler = gsmInboundSmsHandler;
     59         mUiccController = UiccController.getInstance();
     60         mUiccController.registerForIccChanged(this, EVENT_ICC_CHANGED, null);
     61         Rlog.d(TAG, "GsmSMSDispatcher created");
     62     }
     63 
     64     @Override
     65     public void dispose() {
     66         super.dispose();
     67         mCi.unSetOnSmsStatus(this);
     68         mUiccController.unregisterForIccChanged(this);
     69     }
     70 
     71     @Override
     72     protected String getFormat() {
     73         return SmsConstants.FORMAT_3GPP;
     74     }
     75 
     76     /**
     77      * Handles 3GPP format-specific events coming from the phone stack.
     78      * Other events are handled by {@link SMSDispatcher#handleMessage}.
     79      *
     80      * @param msg the message to handle
     81      */
     82     @Override
     83     public void handleMessage(Message msg) {
     84         switch (msg.what) {
     85         case EVENT_NEW_SMS_STATUS_REPORT:
     86             handleStatusReport((AsyncResult) msg.obj);
     87             break;
     88 
     89         case EVENT_NEW_ICC_SMS:
     90         // pass to InboundSmsHandler to process
     91         mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_NEW_SMS, msg.obj);
     92         break;
     93 
     94         case EVENT_ICC_CHANGED:
     95             onUpdateIccAvailability();
     96             break;
     97 
     98         default:
     99             super.handleMessage(msg);
    100         }
    101     }
    102 
    103     @Override
    104     protected boolean shouldBlockSmsForEcbm() {
    105         // There is no such thing as ECBM for GSM. This only applies to CDMA.
    106         return false;
    107     }
    108 
    109     @Override
    110     protected SmsMessageBase.SubmitPduBase getSubmitPdu(String scAddr, String destAddr,
    111             String message, boolean statusReportRequested, SmsHeader smsHeader, int priority,
    112             int validityPeriod) {
    113         return SMSDispatcherUtil.getSubmitPduGsm(scAddr, destAddr, message, statusReportRequested,
    114                 validityPeriod);
    115     }
    116 
    117     @Override
    118     protected SmsMessageBase.SubmitPduBase getSubmitPdu(String scAddr, String destAddr,
    119             int destPort, byte[] message, boolean statusReportRequested) {
    120         return SMSDispatcherUtil.getSubmitPduGsm(scAddr, destAddr, destPort, message,
    121                 statusReportRequested);
    122     }
    123 
    124     @Override
    125     protected TextEncodingDetails calculateLength(CharSequence messageBody, boolean use7bitOnly) {
    126         return SMSDispatcherUtil.calculateLengthGsm(messageBody, use7bitOnly);
    127     }
    128 
    129     /**
    130      * Called when a status report is received.  This should correspond to
    131      * a previously successful SEND.
    132      *
    133      * @param ar AsyncResult passed into the message handler.  ar.result should
    134      *           be a String representing the status report PDU, as ASCII hex.
    135      */
    136     private void handleStatusReport(AsyncResult ar) {
    137         byte[] pdu = (byte[]) ar.result;
    138         SmsMessage sms = SmsMessage.newFromCDS(pdu);
    139 
    140         if (sms != null) {
    141             int messageRef = sms.mMessageRef;
    142             for (int i = 0, count = deliveryPendingList.size(); i < count; i++) {
    143                 SmsTracker tracker = deliveryPendingList.get(i);
    144                 if (tracker.mMessageRef == messageRef) {
    145                     Pair<Boolean, Boolean> result = mSmsDispatchersController.handleSmsStatusReport(
    146                             tracker,
    147                             getFormat(),
    148                             pdu);
    149                     if (result.second) {
    150                         deliveryPendingList.remove(i);
    151                     }
    152                     // Only expect to see one tracker matching this messageref
    153                     break;
    154                 }
    155             }
    156         }
    157         mCi.acknowledgeLastIncomingGsmSms(true, Intents.RESULT_SMS_HANDLED, null);
    158     }
    159 
    160     /** {@inheritDoc} */
    161     @Override
    162     protected void sendSms(SmsTracker tracker) {
    163         HashMap<String, Object> map = tracker.getData();
    164 
    165         byte pdu[] = (byte[]) map.get("pdu");
    166 
    167         if (tracker.mRetryCount > 0) {
    168             Rlog.d(TAG, "sendSms: "
    169                     + " mRetryCount=" + tracker.mRetryCount
    170                     + " mMessageRef=" + tracker.mMessageRef
    171                     + " SS=" + mPhone.getServiceState().getState());
    172 
    173             // per TS 23.040 Section 9.2.3.6:  If TP-MTI SMS-SUBMIT (0x01) type
    174             //   TP-RD (bit 2) is 1 for retry
    175             //   and TP-MR is set to previously failed sms TP-MR
    176             if (((0x01 & pdu[0]) == 0x01)) {
    177                 pdu[0] |= 0x04; // TP-RD
    178                 pdu[1] = (byte) tracker.mMessageRef; // TP-MR
    179             }
    180         }
    181         Rlog.d(TAG, "sendSms: "
    182                 + " isIms()=" + isIms()
    183                 + " mRetryCount=" + tracker.mRetryCount
    184                 + " mImsRetry=" + tracker.mImsRetry
    185                 + " mMessageRef=" + tracker.mMessageRef
    186                 + " mUsesImsServiceForIms=" + tracker.mUsesImsServiceForIms
    187                 + " SS=" + mPhone.getServiceState().getState());
    188 
    189         int ss = mPhone.getServiceState().getState();
    190         // if sms over IMS is not supported on data and voice is not available...
    191         if (!isIms() && ss != ServiceState.STATE_IN_SERVICE) {
    192             tracker.onFailed(mContext, getNotInServiceError(ss), 0/*errorCode*/);
    193             return;
    194         }
    195 
    196         byte smsc[] = (byte[]) map.get("smsc");
    197         Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker);
    198 
    199         // sms over gsm is used:
    200         //   if sms over IMS is not supported AND
    201         //   this is not a retry case after sms over IMS failed
    202         //     indicated by mImsRetry > 0 OR
    203         //   this tracker uses ImsSmsDispatcher to handle SMS over IMS. This dispatcher has received
    204         //     this message because the ImsSmsDispatcher has indicated that the message needs to
    205         //     fall back to sending over CS.
    206         if (0 == tracker.mImsRetry && !isIms() || tracker.mUsesImsServiceForIms) {
    207             if (tracker.mRetryCount == 0 && tracker.mExpectMore) {
    208                 mCi.sendSMSExpectMore(IccUtils.bytesToHexString(smsc),
    209                         IccUtils.bytesToHexString(pdu), reply);
    210             } else {
    211                 mCi.sendSMS(IccUtils.bytesToHexString(smsc),
    212                         IccUtils.bytesToHexString(pdu), reply);
    213             }
    214         } else {
    215             mCi.sendImsGsmSms(IccUtils.bytesToHexString(smsc),
    216                     IccUtils.bytesToHexString(pdu), tracker.mImsRetry,
    217                     tracker.mMessageRef, reply);
    218             // increment it here, so in case of SMS_FAIL_RETRY over IMS
    219             // next retry will be sent using IMS request again.
    220             tracker.mImsRetry++;
    221         }
    222     }
    223 
    224     protected UiccCardApplication getUiccCardApplication() {
    225             Rlog.d(TAG, "GsmSMSDispatcher: subId = " + mPhone.getSubId()
    226                     + " slotId = " + mPhone.getPhoneId());
    227                 return mUiccController.getUiccCardApplication(mPhone.getPhoneId(),
    228                         UiccController.APP_FAM_3GPP);
    229     }
    230 
    231     private void onUpdateIccAvailability() {
    232         if (mUiccController == null ) {
    233             return;
    234         }
    235 
    236         UiccCardApplication newUiccApplication = getUiccCardApplication();
    237 
    238         UiccCardApplication app = mUiccApplication.get();
    239         if (app != newUiccApplication) {
    240             if (app != null) {
    241                 Rlog.d(TAG, "Removing stale icc objects.");
    242                 if (mIccRecords.get() != null) {
    243                     mIccRecords.get().unregisterForNewSms(this);
    244                 }
    245                 mIccRecords.set(null);
    246                 mUiccApplication.set(null);
    247             }
    248             if (newUiccApplication != null) {
    249                 Rlog.d(TAG, "New Uicc application found");
    250                 mUiccApplication.set(newUiccApplication);
    251                 mIccRecords.set(newUiccApplication.getIccRecords());
    252                 if (mIccRecords.get() != null) {
    253                     mIccRecords.get().registerForNewSms(this, EVENT_NEW_ICC_SMS, null);
    254                 }
    255             }
    256         }
    257     }
    258 }
    259