GsmSMSDispatcher.java revision b29851580bba4a13ddbf7a534d8b09295eb2c60f
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; 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*/); 161 sendRawPdu(tracker); 162 } else { 163 Rlog.e(TAG, "GsmSMSDispatcher.sendData(): getSubmitPdu() returned null"); 164 } 165 } 166 167 /** {@inheritDoc} */ 168 @Override 169 protected void sendText(String destAddr, String scAddr, String text, PendingIntent sentIntent, 170 PendingIntent deliveryIntent, Uri messageUri, String callingPkg) { 171 SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu( 172 scAddr, destAddr, text, (deliveryIntent != null)); 173 if (pdu != null) { 174 if (messageUri == null) { 175 messageUri = writeOutboxMessage( 176 getSubId(), 177 destAddr, 178 text, 179 deliveryIntent != null, 180 callingPkg); 181 } else { 182 moveToOutbox(getSubId(), messageUri, callingPkg); 183 } 184 HashMap map = getSmsTrackerMap(destAddr, scAddr, text, pdu); 185 SmsTracker tracker = getSmsTracker(map, sentIntent, deliveryIntent, getFormat(), 186 messageUri); 187 sendRawPdu(tracker); 188 } else { 189 Rlog.e(TAG, "GsmSMSDispatcher.sendText(): getSubmitPdu() returned null"); 190 } 191 } 192 193 /** {@inheritDoc} */ 194 @Override 195 protected void injectSmsPdu(byte[] pdu, String format, PendingIntent receivedIntent) { 196 throw new IllegalStateException("This method must be called only on ImsSMSDispatcher"); 197 } 198 199 /** {@inheritDoc} */ 200 @Override 201 protected GsmAlphabet.TextEncodingDetails calculateLength(CharSequence messageBody, 202 boolean use7bitOnly) { 203 return SmsMessage.calculateLength(messageBody, use7bitOnly); 204 } 205 206 /** {@inheritDoc} */ 207 @Override 208 protected void sendNewSubmitPdu(String destinationAddress, String scAddress, 209 String message, SmsHeader smsHeader, int encoding, 210 PendingIntent sentIntent, PendingIntent deliveryIntent, boolean lastPart, 211 AtomicInteger unsentPartCount, AtomicBoolean anyPartFailed, Uri messageUri) { 212 SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(scAddress, destinationAddress, 213 message, deliveryIntent != null, SmsHeader.toByteArray(smsHeader), 214 encoding, smsHeader.languageTable, smsHeader.languageShiftTable); 215 if (pdu != null) { 216 HashMap map = getSmsTrackerMap(destinationAddress, scAddress, 217 message, pdu); 218 SmsTracker tracker = getSmsTracker(map, sentIntent, 219 deliveryIntent, getFormat(), unsentPartCount, anyPartFailed, messageUri); 220 sendRawPdu(tracker); 221 } else { 222 Rlog.e(TAG, "GsmSMSDispatcher.sendNewSubmitPdu(): getSubmitPdu() returned null"); 223 } 224 } 225 226 /** {@inheritDoc} */ 227 @Override 228 protected void sendSms(SmsTracker tracker) { 229 HashMap<String, Object> map = tracker.mData; 230 231 byte pdu[] = (byte[]) map.get("pdu"); 232 233 if (tracker.mRetryCount > 0) { 234 Rlog.d(TAG, "sendSms: " 235 + " mRetryCount=" + tracker.mRetryCount 236 + " mMessageRef=" + tracker.mMessageRef 237 + " SS=" + mPhone.getServiceState().getState()); 238 239 // per TS 23.040 Section 9.2.3.6: If TP-MTI SMS-SUBMIT (0x01) type 240 // TP-RD (bit 2) is 1 for retry 241 // and TP-MR is set to previously failed sms TP-MR 242 if (((0x01 & pdu[0]) == 0x01)) { 243 pdu[0] |= 0x04; // TP-RD 244 pdu[1] = (byte) tracker.mMessageRef; // TP-MR 245 } 246 } 247 Rlog.d(TAG, "sendSms: " 248 + " isIms()=" + isIms() 249 + " mRetryCount=" + tracker.mRetryCount 250 + " mImsRetry=" + tracker.mImsRetry 251 + " mMessageRef=" + tracker.mMessageRef 252 + " SS=" + mPhone.getServiceState().getState()); 253 254 // FIX this once the carrier app and SIM restricted API is finalized. 255 // We should direct the intent to only the default carrier app. 256 257 // Send SMS via the carrier app. 258 BroadcastReceiver resultReceiver = new SMSDispatcherReceiver(tracker); 259 260 Intent intent = new Intent(Intents.SMS_SEND_ACTION); 261 intent.putExtra("pdu", pdu); 262 intent.putExtra("smsc", (byte[]) map.get("smsc")); 263 intent.putExtra("format", getFormat()); 264 intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT); 265 Rlog.d(TAG, "Sending SMS by carrier app."); 266 267 mContext.sendOrderedBroadcast(intent, android.Manifest.permission.RECEIVE_SMS, 268 AppOpsManager.OP_RECEIVE_SMS, resultReceiver, 269 null, Activity.RESULT_CANCELED, null, null); 270 } 271 272 /** {@inheritDoc} */ 273 @Override 274 protected void sendSmsByPstn(SmsTracker tracker) { 275 HashMap<String, Object> map = tracker.mData; 276 277 byte smsc[] = (byte[]) map.get("smsc"); 278 byte[] pdu = (byte[]) map.get("pdu"); 279 Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker); 280 281 // sms over gsm is used: 282 // if sms over IMS is not supported AND 283 // this is not a retry case after sms over IMS failed 284 // indicated by mImsRetry > 0 285 if (0 == tracker.mImsRetry && !isIms()) { 286 if (tracker.mRetryCount > 0) { 287 // per TS 23.040 Section 9.2.3.6: If TP-MTI SMS-SUBMIT (0x01) type 288 // TP-RD (bit 2) is 1 for retry 289 // and TP-MR is set to previously failed sms TP-MR 290 if (((0x01 & pdu[0]) == 0x01)) { 291 pdu[0] |= 0x04; // TP-RD 292 pdu[1] = (byte) tracker.mMessageRef; // TP-MR 293 } 294 } 295 mCi.sendSMS(IccUtils.bytesToHexString(smsc), 296 IccUtils.bytesToHexString(pdu), reply); 297 } else { 298 mCi.sendImsGsmSms(IccUtils.bytesToHexString(smsc), 299 IccUtils.bytesToHexString(pdu), tracker.mImsRetry, 300 tracker.mMessageRef, reply); 301 // increment it here, so in case of SMS_FAIL_RETRY over IMS 302 // next retry will be sent using IMS request again. 303 tracker.mImsRetry++; 304 } 305 } 306 307 /** {@inheritDoc} */ 308 @Override 309 protected void updateSmsSendStatus(int messageRef, boolean success) { 310 // This function should be defined in ImsDispatcher. 311 Rlog.e(TAG, "updateSmsSendStatus should never be called from here!"); 312 } 313 314 protected UiccCardApplication getUiccCardApplication() { 315 Rlog.d(TAG, "GsmSMSDispatcher: subId = " + mPhone.getSubId() 316 + " slotId = " + mPhone.getPhoneId()); 317 return mUiccController.getUiccCardApplication(mPhone.getPhoneId(), 318 UiccController.APP_FAM_3GPP); 319 } 320 321 private void onUpdateIccAvailability() { 322 if (mUiccController == null ) { 323 return; 324 } 325 326 UiccCardApplication newUiccApplication = getUiccCardApplication(); 327 328 UiccCardApplication app = mUiccApplication.get(); 329 if (app != newUiccApplication) { 330 if (app != null) { 331 Rlog.d(TAG, "Removing stale icc objects."); 332 if (mIccRecords.get() != null) { 333 mIccRecords.get().unregisterForNewSms(this); 334 } 335 mIccRecords.set(null); 336 mUiccApplication.set(null); 337 } 338 if (newUiccApplication != null) { 339 Rlog.d(TAG, "New Uicc application found"); 340 mUiccApplication.set(newUiccApplication); 341 mIccRecords.set(newUiccApplication.getIccRecords()); 342 if (mIccRecords.get() != null) { 343 mIccRecords.get().registerForNewSms(this, EVENT_NEW_ICC_SMS, null); 344 } 345 } 346 } 347 } 348} 349