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 17 package com.android.internal.telephony.gsm; 18 19 import android.app.Activity; 20 import android.app.PendingIntent; 21 import android.app.PendingIntent.CanceledException; 22 import android.content.Intent; 23 import android.net.Uri; 24 import android.os.AsyncResult; 25 import android.os.Message; 26 import android.provider.Telephony.Sms; 27 import android.provider.Telephony.Sms.Intents; 28 import android.telephony.Rlog; 29 import android.telephony.ServiceState; 30 31 import com.android.internal.telephony.GsmAlphabet; 32 import com.android.internal.telephony.ImsSMSDispatcher; 33 import com.android.internal.telephony.InboundSmsHandler; 34 import com.android.internal.telephony.PhoneBase; 35 import com.android.internal.telephony.SMSDispatcher; 36 import com.android.internal.telephony.SmsConstants; 37 import com.android.internal.telephony.SmsHeader; 38 import com.android.internal.telephony.SmsUsageMonitor; 39 import com.android.internal.telephony.uicc.IccRecords; 40 import com.android.internal.telephony.uicc.IccUtils; 41 import com.android.internal.telephony.uicc.UiccCardApplication; 42 import com.android.internal.telephony.uicc.UiccController; 43 44 import java.util.HashMap; 45 import java.util.concurrent.atomic.AtomicBoolean; 46 import java.util.concurrent.atomic.AtomicInteger; 47 import java.util.concurrent.atomic.AtomicReference; 48 49 public 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(PhoneBase 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*/); 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*/, true /*isText*/); 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, true /*isText*/); 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