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