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