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