GsmSMSDispatcher.java revision 1a87ab3d7170d618f048c4f5af8c7504a587aaa5
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.telephony.GsmAlphabet; 32import com.android.internal.telephony.ImsSMSDispatcher; 33import com.android.internal.telephony.InboundSmsHandler; 34import com.android.internal.telephony.Phone; 35import com.android.internal.telephony.SMSDispatcher; 36import com.android.internal.telephony.SmsConstants; 37import com.android.internal.telephony.SmsHeader; 38import com.android.internal.telephony.SmsUsageMonitor; 39import com.android.internal.telephony.uicc.IccRecords; 40import com.android.internal.telephony.uicc.IccUtils; 41import com.android.internal.telephony.uicc.UiccCardApplication; 42import com.android.internal.telephony.uicc.UiccController; 43 44import java.util.HashMap; 45import java.util.concurrent.atomic.AtomicBoolean; 46import java.util.concurrent.atomic.AtomicInteger; 47import java.util.concurrent.atomic.AtomicReference; 48 49public 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(Phone 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*/, true /*persistMessage*/); 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 boolean persistMessage) { 181 SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu( 182 scAddr, destAddr, text, (deliveryIntent != null)); 183 if (pdu != null) { 184 HashMap map = getSmsTrackerMap(destAddr, scAddr, text, pdu); 185 SmsTracker tracker = getSmsTracker(map, sentIntent, deliveryIntent, getFormat(), 186 messageUri, false /*isExpectMore*/, text /*fullMessageText*/, true /*isText*/, 187 persistMessage); 188 189 String carrierPackage = getCarrierAppPackageName(); 190 if (carrierPackage != null) { 191 Rlog.d(TAG, "Found carrier package."); 192 TextSmsSender smsSender = new TextSmsSender(tracker); 193 smsSender.sendSmsByCarrierApp(carrierPackage, new SmsSenderCallback(smsSender)); 194 } else { 195 Rlog.v(TAG, "No carrier package."); 196 sendRawPdu(tracker); 197 } 198 } else { 199 Rlog.e(TAG, "GsmSMSDispatcher.sendText(): getSubmitPdu() returned null"); 200 } 201 } 202 203 /** {@inheritDoc} */ 204 @Override 205 protected void injectSmsPdu(byte[] pdu, String format, PendingIntent receivedIntent) { 206 throw new IllegalStateException("This method must be called only on ImsSMSDispatcher"); 207 } 208 209 /** {@inheritDoc} */ 210 @Override 211 protected GsmAlphabet.TextEncodingDetails calculateLength(CharSequence messageBody, 212 boolean use7bitOnly) { 213 return SmsMessage.calculateLength(messageBody, use7bitOnly); 214 } 215 216 /** {@inheritDoc} */ 217 @Override 218 protected SmsTracker getNewSubmitPduTracker(String destinationAddress, String scAddress, 219 String message, SmsHeader smsHeader, int encoding, 220 PendingIntent sentIntent, PendingIntent deliveryIntent, boolean lastPart, 221 AtomicInteger unsentPartCount, AtomicBoolean anyPartFailed, Uri messageUri, 222 String fullMessageText) { 223 SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(scAddress, destinationAddress, 224 message, deliveryIntent != null, SmsHeader.toByteArray(smsHeader), 225 encoding, smsHeader.languageTable, smsHeader.languageShiftTable); 226 if (pdu != null) { 227 HashMap map = getSmsTrackerMap(destinationAddress, scAddress, 228 message, pdu); 229 return getSmsTracker(map, sentIntent, 230 deliveryIntent, getFormat(), unsentPartCount, anyPartFailed, messageUri, 231 smsHeader, !lastPart, fullMessageText, true /*isText*/, 232 false /*persistMessage*/); 233 } else { 234 Rlog.e(TAG, "GsmSMSDispatcher.sendNewSubmitPdu(): getSubmitPdu() returned null"); 235 return null; 236 } 237 } 238 239 @Override 240 protected void sendSubmitPdu(SmsTracker tracker) { 241 sendRawPdu(tracker); 242 } 243 244 /** {@inheritDoc} */ 245 @Override 246 protected void sendSms(SmsTracker tracker) { 247 HashMap<String, Object> map = tracker.mData; 248 249 byte pdu[] = (byte[]) map.get("pdu"); 250 251 if (tracker.mRetryCount > 0) { 252 Rlog.d(TAG, "sendSms: " 253 + " mRetryCount=" + tracker.mRetryCount 254 + " mMessageRef=" + tracker.mMessageRef 255 + " SS=" + mPhone.getServiceState().getState()); 256 257 // per TS 23.040 Section 9.2.3.6: If TP-MTI SMS-SUBMIT (0x01) type 258 // TP-RD (bit 2) is 1 for retry 259 // and TP-MR is set to previously failed sms TP-MR 260 if (((0x01 & pdu[0]) == 0x01)) { 261 pdu[0] |= 0x04; // TP-RD 262 pdu[1] = (byte) tracker.mMessageRef; // TP-MR 263 } 264 } 265 Rlog.d(TAG, "sendSms: " 266 + " isIms()=" + isIms() 267 + " mRetryCount=" + tracker.mRetryCount 268 + " mImsRetry=" + tracker.mImsRetry 269 + " mMessageRef=" + tracker.mMessageRef 270 + " SS=" + mPhone.getServiceState().getState()); 271 272 sendSmsByPstn(tracker); 273 } 274 275 /** {@inheritDoc} */ 276 @Override 277 protected void sendSmsByPstn(SmsTracker tracker) { 278 int ss = mPhone.getServiceState().getState(); 279 // if sms over IMS is not supported on data and voice is not available... 280 if (!isIms() && ss != ServiceState.STATE_IN_SERVICE) { 281 tracker.onFailed(mContext, getNotInServiceError(ss), 0/*errorCode*/); 282 return; 283 } 284 285 HashMap<String, Object> map = tracker.mData; 286 287 byte smsc[] = (byte[]) map.get("smsc"); 288 byte[] pdu = (byte[]) map.get("pdu"); 289 Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker); 290 291 // sms over gsm is used: 292 // if sms over IMS is not supported AND 293 // this is not a retry case after sms over IMS failed 294 // indicated by mImsRetry > 0 295 if (0 == tracker.mImsRetry && !isIms()) { 296 if (tracker.mRetryCount > 0) { 297 // per TS 23.040 Section 9.2.3.6: If TP-MTI SMS-SUBMIT (0x01) type 298 // TP-RD (bit 2) is 1 for retry 299 // and TP-MR is set to previously failed sms TP-MR 300 if (((0x01 & pdu[0]) == 0x01)) { 301 pdu[0] |= 0x04; // TP-RD 302 pdu[1] = (byte) tracker.mMessageRef; // TP-MR 303 } 304 } 305 if (tracker.mRetryCount == 0 && tracker.mExpectMore) { 306 mCi.sendSMSExpectMore(IccUtils.bytesToHexString(smsc), 307 IccUtils.bytesToHexString(pdu), reply); 308 } else { 309 mCi.sendSMS(IccUtils.bytesToHexString(smsc), 310 IccUtils.bytesToHexString(pdu), reply); 311 } 312 } else { 313 mCi.sendImsGsmSms(IccUtils.bytesToHexString(smsc), 314 IccUtils.bytesToHexString(pdu), tracker.mImsRetry, 315 tracker.mMessageRef, reply); 316 // increment it here, so in case of SMS_FAIL_RETRY over IMS 317 // next retry will be sent using IMS request again. 318 tracker.mImsRetry++; 319 } 320 } 321 322 protected UiccCardApplication getUiccCardApplication() { 323 Rlog.d(TAG, "GsmSMSDispatcher: subId = " + mPhone.getSubId() 324 + " slotId = " + mPhone.getPhoneId()); 325 return mUiccController.getUiccCardApplication(mPhone.getPhoneId(), 326 UiccController.APP_FAM_3GPP); 327 } 328 329 private void onUpdateIccAvailability() { 330 if (mUiccController == null ) { 331 return; 332 } 333 334 UiccCardApplication newUiccApplication = getUiccCardApplication(); 335 336 UiccCardApplication app = mUiccApplication.get(); 337 if (app != newUiccApplication) { 338 if (app != null) { 339 Rlog.d(TAG, "Removing stale icc objects."); 340 if (mIccRecords.get() != null) { 341 mIccRecords.get().unregisterForNewSms(this); 342 } 343 mIccRecords.set(null); 344 mUiccApplication.set(null); 345 } 346 if (newUiccApplication != null) { 347 Rlog.d(TAG, "New Uicc application found"); 348 mUiccApplication.set(newUiccApplication); 349 mIccRecords.set(newUiccApplication.getIccRecords()); 350 if (mIccRecords.get() != null) { 351 mIccRecords.get().registerForNewSms(this, EVENT_NEW_ICC_SMS, null); 352 } 353 } 354 } 355 } 356} 357