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