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