SMSDispatcher.java revision b8a13d300b245e0080aa01275e232f54d5d0e09c
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; 18 19import android.app.Activity; 20import android.app.AlertDialog; 21import android.app.PendingIntent; 22import android.app.PendingIntent.CanceledException; 23import android.content.BroadcastReceiver; 24import android.content.ContentResolver; 25import android.content.ContentValues; 26import android.content.Context; 27import android.content.DialogInterface; 28import android.content.Intent; 29import android.content.pm.ApplicationInfo; 30import android.content.pm.PackageManager; 31import android.content.res.Resources; 32import android.database.Cursor; 33import android.database.SQLException; 34import android.net.Uri; 35import android.os.AsyncResult; 36import android.os.Binder; 37import android.os.Handler; 38import android.os.Message; 39import android.os.PowerManager; 40import android.os.SystemProperties; 41import android.provider.Telephony; 42import android.provider.Telephony.Sms.Intents; 43import android.telephony.PhoneNumberUtils; 44import android.telephony.ServiceState; 45import android.telephony.SmsCbMessage; 46import android.telephony.SmsMessage; 47import android.telephony.TelephonyManager; 48import android.text.Html; 49import android.text.Spanned; 50import android.util.Log; 51import android.view.WindowManager; 52 53import com.android.internal.R; 54import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails; 55import com.android.internal.telephony.SmsConstants; 56import com.android.internal.util.HexDump; 57 58import java.io.ByteArrayOutputStream; 59import java.util.ArrayList; 60import java.util.Arrays; 61import java.util.HashMap; 62import java.util.Random; 63 64import static android.telephony.SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE; 65import static android.telephony.SmsManager.RESULT_ERROR_GENERIC_FAILURE; 66import static android.telephony.SmsManager.RESULT_ERROR_LIMIT_EXCEEDED; 67import static android.telephony.SmsManager.RESULT_ERROR_NO_SERVICE; 68import static android.telephony.SmsManager.RESULT_ERROR_NULL_PDU; 69import static android.telephony.SmsManager.RESULT_ERROR_RADIO_OFF; 70 71public abstract class SMSDispatcher extends Handler { 72 static final String TAG = "SMS"; // accessed from inner class 73 private static final String SEND_NEXT_MSG_EXTRA = "SendNextMsg"; 74 75 /** Permission required to receive SMS and SMS-CB messages. */ 76 public static final String RECEIVE_SMS_PERMISSION = "android.permission.RECEIVE_SMS"; 77 78 /** Permission required to receive ETWS and CMAS emergency broadcasts. */ 79 public static final String RECEIVE_EMERGENCY_BROADCAST_PERMISSION = 80 "android.permission.RECEIVE_EMERGENCY_BROADCAST"; 81 82 /** Permission required to send SMS to short codes without user confirmation. */ 83 private static final String SEND_SMS_NO_CONFIRMATION_PERMISSION = 84 "android.permission.SEND_SMS_NO_CONFIRMATION"; 85 86 /** Query projection for checking for duplicate message segments. */ 87 private static final String[] PDU_PROJECTION = new String[] { 88 "pdu" 89 }; 90 91 /** Query projection for combining concatenated message segments. */ 92 private static final String[] PDU_SEQUENCE_PORT_PROJECTION = new String[] { 93 "pdu", 94 "sequence", 95 "destination_port" 96 }; 97 98 private static final int PDU_COLUMN = 0; 99 private static final int SEQUENCE_COLUMN = 1; 100 private static final int DESTINATION_PORT_COLUMN = 2; 101 102 /** New SMS received. */ 103 protected static final int EVENT_NEW_SMS = 1; 104 105 /** SMS send complete. */ 106 protected static final int EVENT_SEND_SMS_COMPLETE = 2; 107 108 /** Retry sending a previously failed SMS message */ 109 private static final int EVENT_SEND_RETRY = 3; 110 111 /** Confirmation required for sending a large number of messages. */ 112 private static final int EVENT_SEND_LIMIT_REACHED_CONFIRMATION = 4; 113 114 /** Send the user confirmed SMS */ 115 static final int EVENT_SEND_CONFIRMED_SMS = 5; // accessed from inner class 116 117 /** Don't send SMS (user did not confirm). */ 118 static final int EVENT_STOP_SENDING = 7; // accessed from inner class 119 120 /** Confirmation required for third-party apps sending to an SMS short code. */ 121 private static final int EVENT_CONFIRM_SEND_TO_POSSIBLE_PREMIUM_SHORT_CODE = 8; 122 123 /** Confirmation required for third-party apps sending to an SMS short code. */ 124 private static final int EVENT_CONFIRM_SEND_TO_PREMIUM_SHORT_CODE = 9; 125 126 protected final Phone mPhone; 127 protected final Context mContext; 128 protected final ContentResolver mResolver; 129 protected final CommandsInterface mCm; 130 protected final SmsStorageMonitor mStorageMonitor; 131 protected final TelephonyManager mTelephonyManager; 132 133 protected final WapPushOverSms mWapPush; 134 135 protected static final Uri mRawUri = Uri.withAppendedPath(Telephony.Sms.CONTENT_URI, "raw"); 136 137 /** Maximum number of times to retry sending a failed SMS. */ 138 private static final int MAX_SEND_RETRIES = 3; 139 /** Delay before next send attempt on a failed SMS, in milliseconds. */ 140 private static final int SEND_RETRY_DELAY = 2000; 141 /** single part SMS */ 142 private static final int SINGLE_PART_SMS = 1; 143 /** Message sending queue limit */ 144 private static final int MO_MSG_QUEUE_LIMIT = 5; 145 146 /** 147 * Message reference for a CONCATENATED_8_BIT_REFERENCE or 148 * CONCATENATED_16_BIT_REFERENCE message set. Should be 149 * incremented for each set of concatenated messages. 150 * Static field shared by all dispatcher objects. 151 */ 152 private static int sConcatenatedRef = new Random().nextInt(256); 153 154 /** Outgoing message counter. Shared by all dispatchers. */ 155 private final SmsUsageMonitor mUsageMonitor; 156 157 /** Number of outgoing SmsTrackers waiting for user confirmation. */ 158 private int mPendingTrackerCount; 159 160 /** Wake lock to ensure device stays awake while dispatching the SMS intent. */ 161 private PowerManager.WakeLock mWakeLock; 162 163 /** 164 * Hold the wake lock for 5 seconds, which should be enough time for 165 * any receiver(s) to grab its own wake lock. 166 */ 167 private static final int WAKE_LOCK_TIMEOUT = 5000; 168 169 /* Flags indicating whether the current device allows sms service */ 170 protected boolean mSmsCapable = true; 171 protected boolean mSmsReceiveDisabled; 172 protected boolean mSmsSendDisabled; 173 174 protected int mRemainingMessages = -1; 175 176 protected static int getNextConcatenatedRef() { 177 sConcatenatedRef += 1; 178 return sConcatenatedRef; 179 } 180 181 /** 182 * Create a new SMS dispatcher. 183 * @param phone the Phone to use 184 * @param storageMonitor the SmsStorageMonitor to use 185 * @param usageMonitor the SmsUsageMonitor to use 186 */ 187 protected SMSDispatcher(PhoneBase phone, SmsStorageMonitor storageMonitor, 188 SmsUsageMonitor usageMonitor) { 189 mPhone = phone; 190 mWapPush = new WapPushOverSms(phone, this); 191 mContext = phone.getContext(); 192 mResolver = mContext.getContentResolver(); 193 mCm = phone.mCM; 194 mStorageMonitor = storageMonitor; 195 mUsageMonitor = usageMonitor; 196 mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); 197 198 createWakelock(); 199 200 mSmsCapable = mContext.getResources().getBoolean( 201 com.android.internal.R.bool.config_sms_capable); 202 mSmsReceiveDisabled = !SystemProperties.getBoolean( 203 TelephonyProperties.PROPERTY_SMS_RECEIVE, mSmsCapable); 204 mSmsSendDisabled = !SystemProperties.getBoolean( 205 TelephonyProperties.PROPERTY_SMS_SEND, mSmsCapable); 206 Log.d(TAG, "SMSDispatcher: ctor mSmsCapable=" + mSmsCapable + " format=" + getFormat() 207 + " mSmsReceiveDisabled=" + mSmsReceiveDisabled 208 + " mSmsSendDisabled=" + mSmsSendDisabled); 209 } 210 211 /** Unregister for incoming SMS events. */ 212 public abstract void dispose(); 213 214 /** 215 * The format of the message PDU in the associated broadcast intent. 216 * This will be either "3gpp" for GSM/UMTS/LTE messages in 3GPP format 217 * or "3gpp2" for CDMA/LTE messages in 3GPP2 format. 218 * 219 * Note: All applications which handle incoming SMS messages by processing the 220 * SMS_RECEIVED_ACTION broadcast intent MUST pass the "format" extra from the intent 221 * into the new methods in {@link android.telephony.SmsMessage} which take an 222 * extra format parameter. This is required in order to correctly decode the PDU on 223 * devices which require support for both 3GPP and 3GPP2 formats at the same time, 224 * such as CDMA/LTE devices and GSM/CDMA world phones. 225 * 226 * @return the format of the message PDU 227 */ 228 protected abstract String getFormat(); 229 230 @Override 231 protected void finalize() { 232 Log.d(TAG, "SMSDispatcher finalized"); 233 } 234 235 236 /* TODO: Need to figure out how to keep track of status report routing in a 237 * persistent manner. If the phone process restarts (reboot or crash), 238 * we will lose this list and any status reports that come in after 239 * will be dropped. 240 */ 241 /** Sent messages awaiting a delivery status report. */ 242 protected final ArrayList<SmsTracker> deliveryPendingList = new ArrayList<SmsTracker>(); 243 244 /** 245 * Handles events coming from the phone stack. Overridden from handler. 246 * 247 * @param msg the message to handle 248 */ 249 @Override 250 public void handleMessage(Message msg) { 251 AsyncResult ar; 252 253 switch (msg.what) { 254 case EVENT_NEW_SMS: 255 // A new SMS has been received by the device 256 if (false) { 257 Log.d(TAG, "New SMS Message Received"); 258 } 259 260 SmsMessage sms; 261 262 ar = (AsyncResult) msg.obj; 263 264 if (ar.exception != null) { 265 Log.e(TAG, "Exception processing incoming SMS. Exception:" + ar.exception); 266 return; 267 } 268 269 sms = (SmsMessage) ar.result; 270 try { 271 int result = dispatchMessage(sms.mWrappedSmsMessage); 272 if (result != Activity.RESULT_OK) { 273 // RESULT_OK means that message was broadcast for app(s) to handle. 274 // Any other result, we should ack here. 275 boolean handled = (result == Intents.RESULT_SMS_HANDLED); 276 notifyAndAcknowledgeLastIncomingSms(handled, result, null); 277 } 278 } catch (RuntimeException ex) { 279 Log.e(TAG, "Exception dispatching message", ex); 280 notifyAndAcknowledgeLastIncomingSms(false, Intents.RESULT_SMS_GENERIC_ERROR, null); 281 } 282 283 break; 284 285 case EVENT_SEND_SMS_COMPLETE: 286 // An outbound SMS has been successfully transferred, or failed. 287 handleSendComplete((AsyncResult) msg.obj); 288 break; 289 290 case EVENT_SEND_RETRY: 291 sendSms((SmsTracker) msg.obj); 292 break; 293 294 case EVENT_SEND_LIMIT_REACHED_CONFIRMATION: 295 handleReachSentLimit((SmsTracker)(msg.obj)); 296 break; 297 298 case EVENT_CONFIRM_SEND_TO_POSSIBLE_PREMIUM_SHORT_CODE: 299 handleConfirmShortCode(false, (SmsTracker)(msg.obj)); 300 break; 301 302 case EVENT_CONFIRM_SEND_TO_PREMIUM_SHORT_CODE: 303 handleConfirmShortCode(true, (SmsTracker)(msg.obj)); 304 break; 305 306 case EVENT_SEND_CONFIRMED_SMS: 307 { 308 SmsTracker tracker = (SmsTracker) msg.obj; 309 if (tracker.isMultipart()) { 310 sendMultipartSms(tracker); 311 } else { 312 sendSms(tracker); 313 } 314 mPendingTrackerCount--; 315 break; 316 } 317 318 case EVENT_STOP_SENDING: 319 { 320 SmsTracker tracker = (SmsTracker) msg.obj; 321 if (tracker.mSentIntent != null) { 322 try { 323 tracker.mSentIntent.send(RESULT_ERROR_LIMIT_EXCEEDED); 324 } catch (CanceledException ex) { 325 Log.e(TAG, "failed to send RESULT_ERROR_LIMIT_EXCEEDED"); 326 } 327 } 328 mPendingTrackerCount--; 329 break; 330 } 331 } 332 } 333 334 private void createWakelock() { 335 PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); 336 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "SMSDispatcher"); 337 mWakeLock.setReferenceCounted(true); 338 } 339 340 /** 341 * Grabs a wake lock and sends intent as an ordered broadcast. 342 * The resultReceiver will check for errors and ACK/NACK back 343 * to the RIL. 344 * 345 * @param intent intent to broadcast 346 * @param permission Receivers are required to have this permission 347 */ 348 public void dispatch(Intent intent, String permission) { 349 // Hold a wake lock for WAKE_LOCK_TIMEOUT seconds, enough to give any 350 // receivers time to take their own wake locks. 351 mWakeLock.acquire(WAKE_LOCK_TIMEOUT); 352 mContext.sendOrderedBroadcast(intent, permission, mResultReceiver, 353 this, Activity.RESULT_OK, null, null); 354 } 355 356 /** 357 * Grabs a wake lock and sends intent as an ordered broadcast. 358 * Used for setting a custom result receiver for CDMA SCPD. 359 * 360 * @param intent intent to broadcast 361 * @param permission Receivers are required to have this permission 362 * @param resultReceiver the result receiver to use 363 */ 364 public void dispatch(Intent intent, String permission, BroadcastReceiver resultReceiver) { 365 // Hold a wake lock for WAKE_LOCK_TIMEOUT seconds, enough to give any 366 // receivers time to take their own wake locks. 367 mWakeLock.acquire(WAKE_LOCK_TIMEOUT); 368 mContext.sendOrderedBroadcast(intent, permission, resultReceiver, 369 this, Activity.RESULT_OK, null, null); 370 } 371 372 /** 373 * Called when SMS send completes. Broadcasts a sentIntent on success. 374 * On failure, either sets up retries or broadcasts a sentIntent with 375 * the failure in the result code. 376 * 377 * @param ar AsyncResult passed into the message handler. ar.result should 378 * an SmsResponse instance if send was successful. ar.userObj 379 * should be an SmsTracker instance. 380 */ 381 protected void handleSendComplete(AsyncResult ar) { 382 SmsTracker tracker = (SmsTracker) ar.userObj; 383 PendingIntent sentIntent = tracker.mSentIntent; 384 385 if (ar.exception == null) { 386 if (false) { 387 Log.d(TAG, "SMS send complete. Broadcasting " 388 + "intent: " + sentIntent); 389 } 390 391 if (tracker.mDeliveryIntent != null) { 392 // Expecting a status report. Add it to the list. 393 int messageRef = ((SmsResponse)ar.result).messageRef; 394 tracker.mMessageRef = messageRef; 395 deliveryPendingList.add(tracker); 396 } 397 398 if (sentIntent != null) { 399 try { 400 if (mRemainingMessages > -1) { 401 mRemainingMessages--; 402 } 403 404 if (mRemainingMessages == 0) { 405 Intent sendNext = new Intent(); 406 sendNext.putExtra(SEND_NEXT_MSG_EXTRA, true); 407 sentIntent.send(mContext, Activity.RESULT_OK, sendNext); 408 } else { 409 sentIntent.send(Activity.RESULT_OK); 410 } 411 } catch (CanceledException ex) {} 412 } 413 } else { 414 if (false) { 415 Log.d(TAG, "SMS send failed"); 416 } 417 418 int ss = mPhone.getServiceState().getState(); 419 420 if (ss != ServiceState.STATE_IN_SERVICE) { 421 handleNotInService(ss, tracker.mSentIntent); 422 } else if ((((CommandException)(ar.exception)).getCommandError() 423 == CommandException.Error.SMS_FAIL_RETRY) && 424 tracker.mRetryCount < MAX_SEND_RETRIES) { 425 // Retry after a delay if needed. 426 // TODO: According to TS 23.040, 9.2.3.6, we should resend 427 // with the same TP-MR as the failed message, and 428 // TP-RD set to 1. However, we don't have a means of 429 // knowing the MR for the failed message (EF_SMSstatus 430 // may or may not have the MR corresponding to this 431 // message, depending on the failure). Also, in some 432 // implementations this retry is handled by the baseband. 433 tracker.mRetryCount++; 434 Message retryMsg = obtainMessage(EVENT_SEND_RETRY, tracker); 435 sendMessageDelayed(retryMsg, SEND_RETRY_DELAY); 436 } else if (tracker.mSentIntent != null) { 437 int error = RESULT_ERROR_GENERIC_FAILURE; 438 439 if (((CommandException)(ar.exception)).getCommandError() 440 == CommandException.Error.FDN_CHECK_FAILURE) { 441 error = RESULT_ERROR_FDN_CHECK_FAILURE; 442 } 443 // Done retrying; return an error to the app. 444 try { 445 Intent fillIn = new Intent(); 446 if (ar.result != null) { 447 fillIn.putExtra("errorCode", ((SmsResponse)ar.result).errorCode); 448 } 449 if (mRemainingMessages > -1) { 450 mRemainingMessages--; 451 } 452 453 if (mRemainingMessages == 0) { 454 fillIn.putExtra(SEND_NEXT_MSG_EXTRA, true); 455 } 456 457 tracker.mSentIntent.send(mContext, error, fillIn); 458 } catch (CanceledException ex) {} 459 } 460 } 461 } 462 463 /** 464 * Handles outbound message when the phone is not in service. 465 * 466 * @param ss Current service state. Valid values are: 467 * OUT_OF_SERVICE 468 * EMERGENCY_ONLY 469 * POWER_OFF 470 * @param sentIntent the PendingIntent to send the error to 471 */ 472 protected static void handleNotInService(int ss, PendingIntent sentIntent) { 473 if (sentIntent != null) { 474 try { 475 if (ss == ServiceState.STATE_POWER_OFF) { 476 sentIntent.send(RESULT_ERROR_RADIO_OFF); 477 } else { 478 sentIntent.send(RESULT_ERROR_NO_SERVICE); 479 } 480 } catch (CanceledException ex) {} 481 } 482 } 483 484 /** 485 * Dispatches an incoming SMS messages. 486 * 487 * @param sms the incoming message from the phone 488 * @return a result code from {@link Telephony.Sms.Intents}, or 489 * {@link Activity#RESULT_OK} if the message has been broadcast 490 * to applications 491 */ 492 public abstract int dispatchMessage(SmsMessageBase sms); 493 494 /** 495 * Dispatch a normal incoming SMS. This is called from the format-specific 496 * {@link #dispatchMessage(SmsMessageBase)} if no format-specific handling is required. 497 * 498 * @param sms 499 * @return 500 */ 501 protected int dispatchNormalMessage(SmsMessageBase sms) { 502 SmsHeader smsHeader = sms.getUserDataHeader(); 503 504 // See if message is partial or port addressed. 505 if ((smsHeader == null) || (smsHeader.concatRef == null)) { 506 // Message is not partial (not part of concatenated sequence). 507 byte[][] pdus = new byte[1][]; 508 pdus[0] = sms.getPdu(); 509 510 if (smsHeader != null && smsHeader.portAddrs != null) { 511 if (smsHeader.portAddrs.destPort == SmsHeader.PORT_WAP_PUSH) { 512 // GSM-style WAP indication 513 return mWapPush.dispatchWapPdu(sms.getUserData()); 514 } else { 515 // The message was sent to a port, so concoct a URI for it. 516 dispatchPortAddressedPdus(pdus, smsHeader.portAddrs.destPort); 517 } 518 } else { 519 // Normal short and non-port-addressed message, dispatch it. 520 dispatchPdus(pdus); 521 } 522 return Activity.RESULT_OK; 523 } else { 524 // Process the message part. 525 SmsHeader.ConcatRef concatRef = smsHeader.concatRef; 526 SmsHeader.PortAddrs portAddrs = smsHeader.portAddrs; 527 return processMessagePart(sms.getPdu(), sms.getOriginatingAddress(), 528 concatRef.refNumber, concatRef.seqNumber, concatRef.msgCount, 529 sms.getTimestampMillis(), (portAddrs != null ? portAddrs.destPort : -1), false); 530 } 531 } 532 533 /** 534 * If this is the last part send the parts out to the application, otherwise 535 * the part is stored for later processing. Handles both 3GPP concatenated messages 536 * as well as 3GPP2 format WAP push messages processed by 537 * {@link com.android.internal.telephony.cdma.CdmaSMSDispatcher#processCdmaWapPdu}. 538 * 539 * @param pdu the message PDU, or the datagram portion of a CDMA WDP datagram segment 540 * @param address the originating address 541 * @param referenceNumber distinguishes concatenated messages from the same sender 542 * @param sequenceNumber the order of this segment in the message 543 * (starting at 0 for CDMA WDP datagrams and 1 for concatenated messages). 544 * @param messageCount the number of segments in the message 545 * @param timestamp the service center timestamp in millis 546 * @param destPort the destination port for the message, or -1 for no destination port 547 * @param isCdmaWapPush true if pdu is a CDMA WDP datagram segment and not an SM PDU 548 * 549 * @return a result code from {@link Telephony.Sms.Intents}, or 550 * {@link Activity#RESULT_OK} if the message has been broadcast 551 * to applications 552 */ 553 protected int processMessagePart(byte[] pdu, String address, int referenceNumber, 554 int sequenceNumber, int messageCount, long timestamp, int destPort, 555 boolean isCdmaWapPush) { 556 byte[][] pdus = null; 557 Cursor cursor = null; 558 try { 559 // used by several query selection arguments 560 String refNumber = Integer.toString(referenceNumber); 561 String seqNumber = Integer.toString(sequenceNumber); 562 563 // Check for duplicate message segment 564 cursor = mResolver.query(mRawUri, PDU_PROJECTION, 565 "address=? AND reference_number=? AND sequence=?", 566 new String[] {address, refNumber, seqNumber}, null); 567 568 // moveToNext() returns false if no duplicates were found 569 if (cursor.moveToNext()) { 570 Log.w(TAG, "Discarding duplicate message segment from address=" + address 571 + " refNumber=" + refNumber + " seqNumber=" + seqNumber); 572 String oldPduString = cursor.getString(PDU_COLUMN); 573 byte[] oldPdu = HexDump.hexStringToByteArray(oldPduString); 574 if (!Arrays.equals(oldPdu, pdu)) { 575 Log.e(TAG, "Warning: dup message segment PDU of length " + pdu.length 576 + " is different from existing PDU of length " + oldPdu.length); 577 } 578 return Intents.RESULT_SMS_HANDLED; 579 } 580 cursor.close(); 581 582 // not a dup, query for all other segments of this concatenated message 583 String where = "address=? AND reference_number=?"; 584 String[] whereArgs = new String[] {address, refNumber}; 585 cursor = mResolver.query(mRawUri, PDU_SEQUENCE_PORT_PROJECTION, where, whereArgs, null); 586 587 int cursorCount = cursor.getCount(); 588 if (cursorCount != messageCount - 1) { 589 // We don't have all the parts yet, store this one away 590 ContentValues values = new ContentValues(); 591 values.put("date", timestamp); 592 values.put("pdu", HexDump.toHexString(pdu)); 593 values.put("address", address); 594 values.put("reference_number", referenceNumber); 595 values.put("count", messageCount); 596 values.put("sequence", sequenceNumber); 597 if (destPort != -1) { 598 values.put("destination_port", destPort); 599 } 600 mResolver.insert(mRawUri, values); 601 return Intents.RESULT_SMS_HANDLED; 602 } 603 604 // All the parts are in place, deal with them 605 pdus = new byte[messageCount][]; 606 for (int i = 0; i < cursorCount; i++) { 607 cursor.moveToNext(); 608 int cursorSequence = cursor.getInt(SEQUENCE_COLUMN); 609 // GSM sequence numbers start at 1; CDMA WDP datagram sequence numbers start at 0 610 if (!isCdmaWapPush) { 611 cursorSequence--; 612 } 613 pdus[cursorSequence] = HexDump.hexStringToByteArray( 614 cursor.getString(PDU_COLUMN)); 615 616 // Read the destination port from the first segment (needed for CDMA WAP PDU). 617 // It's not a bad idea to prefer the port from the first segment for 3GPP as well. 618 if (cursorSequence == 0 && !cursor.isNull(DESTINATION_PORT_COLUMN)) { 619 destPort = cursor.getInt(DESTINATION_PORT_COLUMN); 620 } 621 } 622 // This one isn't in the DB, so add it 623 // GSM sequence numbers start at 1; CDMA WDP datagram sequence numbers start at 0 624 if (isCdmaWapPush) { 625 pdus[sequenceNumber] = pdu; 626 } else { 627 pdus[sequenceNumber - 1] = pdu; 628 } 629 630 // Remove the parts from the database 631 mResolver.delete(mRawUri, where, whereArgs); 632 } catch (SQLException e) { 633 Log.e(TAG, "Can't access multipart SMS database", e); 634 return Intents.RESULT_SMS_GENERIC_ERROR; 635 } finally { 636 if (cursor != null) cursor.close(); 637 } 638 639 // Special handling for CDMA WDP datagrams 640 if (isCdmaWapPush) { 641 // Build up the data stream 642 ByteArrayOutputStream output = new ByteArrayOutputStream(); 643 for (int i = 0; i < messageCount; i++) { 644 // reassemble the (WSP-)pdu 645 output.write(pdus[i], 0, pdus[i].length); 646 } 647 byte[] datagram = output.toByteArray(); 648 649 // Dispatch the PDU to applications 650 if (destPort == SmsHeader.PORT_WAP_PUSH) { 651 // Handle the PUSH 652 return mWapPush.dispatchWapPdu(datagram); 653 } else { 654 pdus = new byte[1][]; 655 pdus[0] = datagram; 656 // The messages were sent to any other WAP port 657 dispatchPortAddressedPdus(pdus, destPort); 658 return Activity.RESULT_OK; 659 } 660 } 661 662 // Dispatch the PDUs to applications 663 if (destPort != -1) { 664 if (destPort == SmsHeader.PORT_WAP_PUSH) { 665 // Build up the data stream 666 ByteArrayOutputStream output = new ByteArrayOutputStream(); 667 for (int i = 0; i < messageCount; i++) { 668 SmsMessage msg = SmsMessage.createFromPdu(pdus[i], getFormat()); 669 byte[] data = msg.getUserData(); 670 output.write(data, 0, data.length); 671 } 672 // Handle the PUSH 673 return mWapPush.dispatchWapPdu(output.toByteArray()); 674 } else { 675 // The messages were sent to a port, so concoct a URI for it 676 dispatchPortAddressedPdus(pdus, destPort); 677 } 678 } else { 679 // The messages were not sent to a port 680 dispatchPdus(pdus); 681 } 682 return Activity.RESULT_OK; 683 } 684 685 /** 686 * Dispatches standard PDUs to interested applications 687 * 688 * @param pdus The raw PDUs making up the message 689 */ 690 protected void dispatchPdus(byte[][] pdus) { 691 Intent intent = new Intent(Intents.SMS_RECEIVED_ACTION); 692 intent.putExtra("pdus", pdus); 693 intent.putExtra("format", getFormat()); 694 dispatch(intent, RECEIVE_SMS_PERMISSION); 695 } 696 697 /** 698 * Dispatches port addressed PDUs to interested applications 699 * 700 * @param pdus The raw PDUs making up the message 701 * @param port The destination port of the messages 702 */ 703 protected void dispatchPortAddressedPdus(byte[][] pdus, int port) { 704 Uri uri = Uri.parse("sms://localhost:" + port); 705 Intent intent = new Intent(Intents.DATA_SMS_RECEIVED_ACTION, uri); 706 intent.putExtra("pdus", pdus); 707 intent.putExtra("format", getFormat()); 708 dispatch(intent, RECEIVE_SMS_PERMISSION); 709 } 710 711 /** 712 * Send a data based SMS to a specific application port. 713 * 714 * @param destAddr the address to send the message to 715 * @param scAddr is the service center address or null to use 716 * the current default SMSC 717 * @param destPort the port to deliver the message to 718 * @param data the body of the message to send 719 * @param sentIntent if not NULL this <code>PendingIntent</code> is 720 * broadcast when the message is successfully sent, or failed. 721 * The result code will be <code>Activity.RESULT_OK<code> for success, 722 * or one of these errors:<br> 723 * <code>RESULT_ERROR_GENERIC_FAILURE</code><br> 724 * <code>RESULT_ERROR_RADIO_OFF</code><br> 725 * <code>RESULT_ERROR_NULL_PDU</code><br> 726 * <code>RESULT_ERROR_NO_SERVICE</code><br>. 727 * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include 728 * the extra "errorCode" containing a radio technology specific value, 729 * generally only useful for troubleshooting.<br> 730 * The per-application based SMS control checks sentIntent. If sentIntent 731 * is NULL the caller will be checked against all unknown applications, 732 * which cause smaller number of SMS to be sent in checking period. 733 * @param deliveryIntent if not NULL this <code>PendingIntent</code> is 734 * broadcast when the message is delivered to the recipient. The 735 * raw pdu of the status report is in the extended data ("pdu"). 736 */ 737 protected abstract void sendData(String destAddr, String scAddr, int destPort, 738 byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent); 739 740 /** 741 * Send a text based SMS. 742 * 743 * @param destAddr the address to send the message to 744 * @param scAddr is the service center address or null to use 745 * the current default SMSC 746 * @param text the body of the message to send 747 * @param sentIntent if not NULL this <code>PendingIntent</code> is 748 * broadcast when the message is successfully sent, or failed. 749 * The result code will be <code>Activity.RESULT_OK<code> for success, 750 * or one of these errors:<br> 751 * <code>RESULT_ERROR_GENERIC_FAILURE</code><br> 752 * <code>RESULT_ERROR_RADIO_OFF</code><br> 753 * <code>RESULT_ERROR_NULL_PDU</code><br> 754 * <code>RESULT_ERROR_NO_SERVICE</code><br>. 755 * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include 756 * the extra "errorCode" containing a radio technology specific value, 757 * generally only useful for troubleshooting.<br> 758 * The per-application based SMS control checks sentIntent. If sentIntent 759 * is NULL the caller will be checked against all unknown applications, 760 * which cause smaller number of SMS to be sent in checking period. 761 * @param deliveryIntent if not NULL this <code>PendingIntent</code> is 762 * broadcast when the message is delivered to the recipient. The 763 * raw pdu of the status report is in the extended data ("pdu"). 764 */ 765 protected abstract void sendText(String destAddr, String scAddr, 766 String text, PendingIntent sentIntent, PendingIntent deliveryIntent); 767 768 /** 769 * Calculate the number of septets needed to encode the message. 770 * 771 * @param messageBody the message to encode 772 * @param use7bitOnly ignore (but still count) illegal characters if true 773 * @return TextEncodingDetails 774 */ 775 protected abstract TextEncodingDetails calculateLength(CharSequence messageBody, 776 boolean use7bitOnly); 777 778 /** 779 * Send a multi-part text based SMS. 780 * 781 * @param destAddr the address to send the message to 782 * @param scAddr is the service center address or null to use 783 * the current default SMSC 784 * @param parts an <code>ArrayList</code> of strings that, in order, 785 * comprise the original message 786 * @param sentIntents if not null, an <code>ArrayList</code> of 787 * <code>PendingIntent</code>s (one for each message part) that is 788 * broadcast when the corresponding message part has been sent. 789 * The result code will be <code>Activity.RESULT_OK<code> for success, 790 * or one of these errors: 791 * <code>RESULT_ERROR_GENERIC_FAILURE</code> 792 * <code>RESULT_ERROR_RADIO_OFF</code> 793 * <code>RESULT_ERROR_NULL_PDU</code> 794 * <code>RESULT_ERROR_NO_SERVICE</code>. 795 * The per-application based SMS control checks sentIntent. If sentIntent 796 * is NULL the caller will be checked against all unknown applications, 797 * which cause smaller number of SMS to be sent in checking period. 798 * @param deliveryIntents if not null, an <code>ArrayList</code> of 799 * <code>PendingIntent</code>s (one for each message part) that is 800 * broadcast when the corresponding message part has been delivered 801 * to the recipient. The raw pdu of the status report is in the 802 * extended data ("pdu"). 803 */ 804 protected void sendMultipartText(String destAddr, String scAddr, 805 ArrayList<String> parts, ArrayList<PendingIntent> sentIntents, 806 ArrayList<PendingIntent> deliveryIntents) { 807 808 int refNumber = getNextConcatenatedRef() & 0x00FF; 809 int msgCount = parts.size(); 810 int encoding = SmsConstants.ENCODING_UNKNOWN; 811 812 mRemainingMessages = msgCount; 813 814 TextEncodingDetails[] encodingForParts = new TextEncodingDetails[msgCount]; 815 for (int i = 0; i < msgCount; i++) { 816 TextEncodingDetails details = calculateLength(parts.get(i), false); 817 if (encoding != details.codeUnitSize 818 && (encoding == SmsConstants.ENCODING_UNKNOWN 819 || encoding == SmsConstants.ENCODING_7BIT)) { 820 encoding = details.codeUnitSize; 821 } 822 encodingForParts[i] = details; 823 } 824 825 for (int i = 0; i < msgCount; i++) { 826 SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef(); 827 concatRef.refNumber = refNumber; 828 concatRef.seqNumber = i + 1; // 1-based sequence 829 concatRef.msgCount = msgCount; 830 // TODO: We currently set this to true since our messaging app will never 831 // send more than 255 parts (it converts the message to MMS well before that). 832 // However, we should support 3rd party messaging apps that might need 16-bit 833 // references 834 // Note: It's not sufficient to just flip this bit to true; it will have 835 // ripple effects (several calculations assume 8-bit ref). 836 concatRef.isEightBits = true; 837 SmsHeader smsHeader = new SmsHeader(); 838 smsHeader.concatRef = concatRef; 839 840 // Set the national language tables for 3GPP 7-bit encoding, if enabled. 841 if (encoding == SmsConstants.ENCODING_7BIT) { 842 smsHeader.languageTable = encodingForParts[i].languageTable; 843 smsHeader.languageShiftTable = encodingForParts[i].languageShiftTable; 844 } 845 846 PendingIntent sentIntent = null; 847 if (sentIntents != null && sentIntents.size() > i) { 848 sentIntent = sentIntents.get(i); 849 } 850 851 PendingIntent deliveryIntent = null; 852 if (deliveryIntents != null && deliveryIntents.size() > i) { 853 deliveryIntent = deliveryIntents.get(i); 854 } 855 856 sendNewSubmitPdu(destAddr, scAddr, parts.get(i), smsHeader, encoding, 857 sentIntent, deliveryIntent, (i == (msgCount - 1))); 858 } 859 860 } 861 862 /** 863 * Create a new SubmitPdu and send it. 864 */ 865 protected abstract void sendNewSubmitPdu(String destinationAddress, String scAddress, 866 String message, SmsHeader smsHeader, int encoding, 867 PendingIntent sentIntent, PendingIntent deliveryIntent, boolean lastPart); 868 869 /** 870 * Send a SMS 871 * 872 * @param smsc the SMSC to send the message through, or NULL for the 873 * default SMSC 874 * @param pdu the raw PDU to send 875 * @param sentIntent if not NULL this <code>Intent</code> is 876 * broadcast when the message is successfully sent, or failed. 877 * The result code will be <code>Activity.RESULT_OK<code> for success, 878 * or one of these errors: 879 * <code>RESULT_ERROR_GENERIC_FAILURE</code> 880 * <code>RESULT_ERROR_RADIO_OFF</code> 881 * <code>RESULT_ERROR_NULL_PDU</code> 882 * <code>RESULT_ERROR_NO_SERVICE</code>. 883 * The per-application based SMS control checks sentIntent. If sentIntent 884 * is NULL the caller will be checked against all unknown applications, 885 * which cause smaller number of SMS to be sent in checking period. 886 * @param deliveryIntent if not NULL this <code>Intent</code> is 887 * broadcast when the message is delivered to the recipient. The 888 * raw pdu of the status report is in the extended data ("pdu"). 889 * @param destAddr the destination phone number (for short code confirmation) 890 */ 891 protected void sendRawPdu(byte[] smsc, byte[] pdu, PendingIntent sentIntent, 892 PendingIntent deliveryIntent, String destAddr) { 893 if (mSmsSendDisabled) { 894 if (sentIntent != null) { 895 try { 896 sentIntent.send(RESULT_ERROR_NO_SERVICE); 897 } catch (CanceledException ex) {} 898 } 899 Log.d(TAG, "Device does not support sending sms."); 900 return; 901 } 902 903 if (pdu == null) { 904 if (sentIntent != null) { 905 try { 906 sentIntent.send(RESULT_ERROR_NULL_PDU); 907 } catch (CanceledException ex) {} 908 } 909 return; 910 } 911 912 HashMap<String, Object> map = new HashMap<String, Object>(); 913 map.put("smsc", smsc); 914 map.put("pdu", pdu); 915 916 // Get calling app package name via UID from Binder call 917 PackageManager pm = mContext.getPackageManager(); 918 String[] packageNames = pm.getPackagesForUid(Binder.getCallingUid()); 919 920 if (packageNames == null || packageNames.length == 0) { 921 // Refuse to send SMS if we can't get the calling package name. 922 Log.e(TAG, "Can't get calling app package name: refusing to send SMS"); 923 if (sentIntent != null) { 924 try { 925 sentIntent.send(RESULT_ERROR_GENERIC_FAILURE); 926 } catch (CanceledException ex) { 927 Log.e(TAG, "failed to send error result"); 928 } 929 } 930 return; 931 } 932 933 String appPackage = packageNames[0]; 934 935 // Strip non-digits from destination phone number before checking for short codes 936 // and before displaying the number to the user if confirmation is required. 937 SmsTracker tracker = new SmsTracker(map, sentIntent, deliveryIntent, appPackage, 938 PhoneNumberUtils.extractNetworkPortion(destAddr)); 939 940 // checkDestination() returns true if the destination is not a premium short code or the 941 // sending app is approved to send to short codes. Otherwise, a message is sent to our 942 // handler with the SmsTracker to request user confirmation before sending. 943 if (checkDestination(tracker)) { 944 // check for excessive outgoing SMS usage by this app 945 if (!mUsageMonitor.check(appPackage, SINGLE_PART_SMS)) { 946 sendMessage(obtainMessage(EVENT_SEND_LIMIT_REACHED_CONFIRMATION, tracker)); 947 return; 948 } 949 950 int ss = mPhone.getServiceState().getState(); 951 952 if (ss != ServiceState.STATE_IN_SERVICE) { 953 handleNotInService(ss, tracker.mSentIntent); 954 } else { 955 sendSms(tracker); 956 } 957 } 958 } 959 960 /** 961 * Check if destination is a potential premium short code and sender is not pre-approved to 962 * send to short codes. 963 * 964 * @param tracker the tracker for the SMS to send 965 * @return true if the destination is approved; false if user confirmation event was sent 966 */ 967 boolean checkDestination(SmsTracker tracker) { 968 if (mContext.checkCallingOrSelfPermission(SEND_SMS_NO_CONFIRMATION_PERMISSION) 969 == PackageManager.PERMISSION_GRANTED) { 970 return true; // app is pre-approved to send to short codes 971 } else { 972 String countryIso = mTelephonyManager.getSimCountryIso(); 973 if (countryIso == null || countryIso.length() != 2) { 974 Log.e(TAG, "Can't get SIM country code: trying network country code"); 975 countryIso = mTelephonyManager.getNetworkCountryIso(); 976 } 977 978 switch (mUsageMonitor.checkDestination(tracker.mDestAddress, countryIso)) { 979 case SmsUsageMonitor.CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE: 980 sendMessage(obtainMessage(EVENT_CONFIRM_SEND_TO_POSSIBLE_PREMIUM_SHORT_CODE, 981 tracker)); 982 return false; // wait for user confirmation before sending 983 984 case SmsUsageMonitor.CATEGORY_PREMIUM_SHORT_CODE: 985 sendMessage(obtainMessage(EVENT_CONFIRM_SEND_TO_PREMIUM_SHORT_CODE, 986 tracker)); 987 return false; // wait for user confirmation before sending 988 989 case SmsUsageMonitor.CATEGORY_NOT_SHORT_CODE: 990 case SmsUsageMonitor.CATEGORY_FREE_SHORT_CODE: 991 case SmsUsageMonitor.CATEGORY_STANDARD_SHORT_CODE: 992 default: 993 return true; // destination is not a premium short code 994 } 995 } 996 } 997 998 /** 999 * Deny sending an SMS if the outgoing queue limit is reached. Used when the message 1000 * must be confirmed by the user due to excessive usage or potential premium SMS detected. 1001 * @param tracker the SmsTracker for the message to send 1002 * @return true if the message was denied; false to continue with send confirmation 1003 */ 1004 private boolean denyIfQueueLimitReached(SmsTracker tracker) { 1005 if (mPendingTrackerCount >= MO_MSG_QUEUE_LIMIT) { 1006 // Deny sending message when the queue limit is reached. 1007 try { 1008 tracker.mSentIntent.send(RESULT_ERROR_LIMIT_EXCEEDED); 1009 } catch (CanceledException ex) { 1010 Log.e(TAG, "failed to send back RESULT_ERROR_LIMIT_EXCEEDED"); 1011 } 1012 return true; 1013 } 1014 mPendingTrackerCount++; 1015 return false; 1016 } 1017 1018 /** 1019 * Returns the label for the specified app package name. 1020 * @param appPackage the package name of the app requesting to send an SMS 1021 * @return the label for the specified app, or the package name if getApplicationInfo() fails 1022 */ 1023 private CharSequence getAppLabel(String appPackage) { 1024 PackageManager pm = mContext.getPackageManager(); 1025 try { 1026 ApplicationInfo appInfo = pm.getApplicationInfo(appPackage, 0); 1027 return appInfo.loadLabel(pm); 1028 } catch (PackageManager.NameNotFoundException e) { 1029 Log.e(TAG, "PackageManager Name Not Found for package " + appPackage); 1030 return appPackage; // fall back to package name if we can't get app label 1031 } 1032 } 1033 1034 /** 1035 * Post an alert when SMS needs confirmation due to excessive usage. 1036 * @param tracker an SmsTracker for the current message. 1037 */ 1038 protected void handleReachSentLimit(SmsTracker tracker) { 1039 if (denyIfQueueLimitReached(tracker)) { 1040 return; // queue limit reached; error was returned to caller 1041 } 1042 1043 CharSequence appLabel = getAppLabel(tracker.mAppPackage); 1044 Resources r = Resources.getSystem(); 1045 Spanned messageText = Html.fromHtml(r.getString(R.string.sms_control_message, appLabel)); 1046 1047 ConfirmDialogListener listener = new ConfirmDialogListener(tracker); 1048 1049 AlertDialog d = new AlertDialog.Builder(mContext) 1050 .setTitle(R.string.sms_control_title) 1051 .setIcon(R.drawable.stat_sys_warning) 1052 .setMessage(messageText) 1053 .setPositiveButton(r.getString(R.string.sms_control_yes), listener) 1054 .setNegativeButton(r.getString(R.string.sms_control_no), listener) 1055 .setOnCancelListener(listener) 1056 .create(); 1057 1058 d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); 1059 d.show(); 1060 } 1061 1062 /** 1063 * Post an alert for user confirmation when sending to a potential short code. 1064 * @param isPremium true if the destination is known to be a premium short code 1065 * @param tracker the SmsTracker for the current message. 1066 */ 1067 protected void handleConfirmShortCode(boolean isPremium, SmsTracker tracker) { 1068 if (denyIfQueueLimitReached(tracker)) { 1069 return; // queue limit reached; error was returned to caller 1070 } 1071 1072 int messageId; 1073 int titleId; 1074 if (isPremium) { 1075 messageId = R.string.sms_premium_short_code_confirm_message; 1076 titleId = R.string.sms_premium_short_code_confirm_title; 1077 } else { 1078 messageId = R.string.sms_short_code_confirm_message; 1079 titleId = R.string.sms_short_code_confirm_title; 1080 } 1081 1082 CharSequence appLabel = getAppLabel(tracker.mAppPackage); 1083 Resources r = Resources.getSystem(); 1084 Spanned messageText = Html.fromHtml(r.getString(messageId, appLabel, tracker.mDestAddress)); 1085 1086 ConfirmDialogListener listener = new ConfirmDialogListener(tracker); 1087 1088 AlertDialog d = new AlertDialog.Builder(mContext) 1089 .setTitle(titleId) 1090 .setIcon(R.drawable.stat_sys_warning) 1091 .setMessage(messageText) 1092 .setPositiveButton(r.getString(R.string.sms_short_code_confirm_allow), listener) 1093 .setNegativeButton(r.getString(R.string.sms_short_code_confirm_deny), listener) 1094// TODO: add third button for "Report malicious app" feature 1095// .setNeutralButton(r.getString(R.string.sms_short_code_confirm_report), listener) 1096 .setOnCancelListener(listener) 1097 .create(); 1098 1099 d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); 1100 d.show(); 1101 } 1102 1103 /** 1104 * Send the message along to the radio. 1105 * 1106 * @param tracker holds the SMS message to send 1107 */ 1108 protected abstract void sendSms(SmsTracker tracker); 1109 1110 /** 1111 * Send the multi-part SMS based on multipart Sms tracker 1112 * 1113 * @param tracker holds the multipart Sms tracker ready to be sent 1114 */ 1115 private void sendMultipartSms(SmsTracker tracker) { 1116 ArrayList<String> parts; 1117 ArrayList<PendingIntent> sentIntents; 1118 ArrayList<PendingIntent> deliveryIntents; 1119 1120 HashMap<String, Object> map = tracker.mData; 1121 1122 String destinationAddress = (String) map.get("destination"); 1123 String scAddress = (String) map.get("scaddress"); 1124 1125 parts = (ArrayList<String>) map.get("parts"); 1126 sentIntents = (ArrayList<PendingIntent>) map.get("sentIntents"); 1127 deliveryIntents = (ArrayList<PendingIntent>) map.get("deliveryIntents"); 1128 1129 // check if in service 1130 int ss = mPhone.getServiceState().getState(); 1131 if (ss != ServiceState.STATE_IN_SERVICE) { 1132 for (int i = 0, count = parts.size(); i < count; i++) { 1133 PendingIntent sentIntent = null; 1134 if (sentIntents != null && sentIntents.size() > i) { 1135 sentIntent = sentIntents.get(i); 1136 } 1137 handleNotInService(ss, sentIntent); 1138 } 1139 return; 1140 } 1141 1142 sendMultipartText(destinationAddress, scAddress, parts, sentIntents, deliveryIntents); 1143 } 1144 1145 /** 1146 * Send an acknowledge message. 1147 * @param success indicates that last message was successfully received. 1148 * @param result result code indicating any error 1149 * @param response callback message sent when operation completes. 1150 */ 1151 protected abstract void acknowledgeLastIncomingSms(boolean success, 1152 int result, Message response); 1153 1154 /** 1155 * Notify interested apps if the framework has rejected an incoming SMS, 1156 * and send an acknowledge message to the network. 1157 * @param success indicates that last message was successfully received. 1158 * @param result result code indicating any error 1159 * @param response callback message sent when operation completes. 1160 */ 1161 private void notifyAndAcknowledgeLastIncomingSms(boolean success, 1162 int result, Message response) { 1163 if (!success) { 1164 // broadcast SMS_REJECTED_ACTION intent 1165 Intent intent = new Intent(Intents.SMS_REJECTED_ACTION); 1166 intent.putExtra("result", result); 1167 mWakeLock.acquire(WAKE_LOCK_TIMEOUT); 1168 mContext.sendBroadcast(intent, "android.permission.RECEIVE_SMS"); 1169 } 1170 acknowledgeLastIncomingSms(success, result, response); 1171 } 1172 1173 /** 1174 * Keeps track of an SMS that has been sent to the RIL, until it has 1175 * successfully been sent, or we're done trying. 1176 * 1177 */ 1178 protected static final class SmsTracker { 1179 // fields need to be public for derived SmsDispatchers 1180 public final HashMap<String, Object> mData; 1181 public int mRetryCount; 1182 public int mMessageRef; 1183 1184 public final PendingIntent mSentIntent; 1185 public final PendingIntent mDeliveryIntent; 1186 1187 public final String mAppPackage; 1188 public final String mDestAddress; 1189 1190 public SmsTracker(HashMap<String, Object> data, PendingIntent sentIntent, 1191 PendingIntent deliveryIntent, String appPackage, String destAddr) { 1192 mData = data; 1193 mSentIntent = sentIntent; 1194 mDeliveryIntent = deliveryIntent; 1195 mRetryCount = 0; 1196 mAppPackage = appPackage; 1197 mDestAddress = destAddr; 1198 } 1199 1200 /** 1201 * Returns whether this tracker holds a multi-part SMS. 1202 * @return true if the tracker holds a multi-part SMS; false otherwise 1203 */ 1204 protected boolean isMultipart() { 1205 HashMap map = mData; 1206 return map.containsKey("parts"); 1207 } 1208 } 1209 1210 /** 1211 * Dialog listener for SMS confirmation dialog. 1212 */ 1213 private final class ConfirmDialogListener 1214 implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener { 1215 1216 private final SmsTracker mTracker; 1217 1218 ConfirmDialogListener(SmsTracker tracker) { 1219 mTracker = tracker; 1220 } 1221 1222 @Override 1223 public void onClick(DialogInterface dialog, int which) { 1224 if (which == DialogInterface.BUTTON_POSITIVE) { 1225 Log.d(TAG, "CONFIRM sending SMS"); 1226 sendMessage(obtainMessage(EVENT_SEND_CONFIRMED_SMS, mTracker)); 1227 } else if (which == DialogInterface.BUTTON_NEGATIVE) { 1228 Log.d(TAG, "DENY sending SMS"); 1229 sendMessage(obtainMessage(EVENT_STOP_SENDING, mTracker)); 1230 } 1231 } 1232 1233 @Override 1234 public void onCancel(DialogInterface dialog) { 1235 Log.d(TAG, "dialog dismissed: don't send SMS"); 1236 sendMessage(obtainMessage(EVENT_STOP_SENDING, mTracker)); 1237 } 1238 } 1239 1240 private final BroadcastReceiver mResultReceiver = new BroadcastReceiver() { 1241 @Override 1242 public void onReceive(Context context, Intent intent) { 1243 // Assume the intent is one of the SMS receive intents that 1244 // was sent as an ordered broadcast. Check result and ACK. 1245 int rc = getResultCode(); 1246 boolean success = (rc == Activity.RESULT_OK) 1247 || (rc == Intents.RESULT_SMS_HANDLED); 1248 1249 // For a multi-part message, this only ACKs the last part. 1250 // Previous parts were ACK'd as they were received. 1251 acknowledgeLastIncomingSms(success, rc, null); 1252 } 1253 }; 1254 1255 protected void dispatchBroadcastMessage(SmsCbMessage message) { 1256 if (message.isEmergencyMessage()) { 1257 Intent intent = new Intent(Intents.SMS_EMERGENCY_CB_RECEIVED_ACTION); 1258 intent.putExtra("message", message); 1259 Log.d(TAG, "Dispatching emergency SMS CB"); 1260 dispatch(intent, RECEIVE_EMERGENCY_BROADCAST_PERMISSION); 1261 } else { 1262 Intent intent = new Intent(Intents.SMS_CB_RECEIVED_ACTION); 1263 intent.putExtra("message", message); 1264 Log.d(TAG, "Dispatching SMS CB"); 1265 dispatch(intent, RECEIVE_SMS_PERMISSION); 1266 } 1267 } 1268} 1269