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