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