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