InboundSmsHandler.java revision 8b61e10b480d3a7bae41025a97f3bd27ee5ed610
1/* 2 * Copyright (C) 2013 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.AppOpsManager; 21import android.content.BroadcastReceiver; 22import android.content.ContentResolver; 23import android.content.ContentUris; 24import android.content.ContentValues; 25import android.content.Context; 26import android.content.Intent; 27import android.database.Cursor; 28import android.database.SQLException; 29import android.net.Uri; 30import android.os.AsyncResult; 31import android.os.Build; 32import android.os.Message; 33import android.os.PowerManager; 34import android.os.SystemProperties; 35import android.provider.Telephony; 36import android.provider.Telephony.Sms.Intents; 37import android.telephony.Rlog; 38import android.telephony.SmsMessage; 39import android.telephony.TelephonyManager; 40 41import com.android.internal.util.HexDump; 42import com.android.internal.util.State; 43import com.android.internal.util.StateMachine; 44 45import java.io.ByteArrayOutputStream; 46import java.util.Arrays; 47 48import static android.telephony.TelephonyManager.PHONE_TYPE_CDMA; 49 50/** 51 * This class broadcasts incoming SMS messages to interested apps after storing them in 52 * the SmsProvider "raw" table and ACKing them to the SMSC. After each message has been 53 * broadcast, its parts are removed from the raw table. If the device crashes after ACKing 54 * but before the broadcast completes, the pending messages will be rebroadcast on the next boot. 55 * 56 * <p>The state machine starts in {@link IdleState} state. When the {@link SMSDispatcher} receives a 57 * new SMS from the radio, it calls {@link #dispatchNormalMessage}, 58 * which sends a message to the state machine, causing the wakelock to be acquired in 59 * {@link #haltedProcessMessage}, which transitions to {@link DeliveringState} state, where the message 60 * is saved to the raw table, then acknowledged via the {@link SMSDispatcher} which called us. 61 * 62 * <p>After saving the SMS, if the message is complete (either single-part or the final segment 63 * of a multi-part SMS), we broadcast the completed PDUs as an ordered broadcast, then transition to 64 * {@link WaitingState} state to wait for the broadcast to complete. When the local 65 * {@link BroadcastReceiver} is called with the result, it sends {@link #EVENT_BROADCAST_COMPLETE} 66 * to the state machine, causing us to either broadcast the next pending message (if one has 67 * arrived while waiting for the broadcast to complete), or to transition back to the halted state 68 * after all messages are processed. Then the wakelock is released and we wait for the next SMS. 69 */ 70public abstract class InboundSmsHandler extends StateMachine { 71 protected static final boolean DBG = true; 72 private static final boolean VDBG = false; // STOPSHIP if true, logs user data 73 74 /** Query projection for checking for duplicate message segments. */ 75 private static final String[] PDU_PROJECTION = { 76 "pdu" 77 }; 78 79 /** Query projection for combining concatenated message segments. */ 80 private static final String[] PDU_SEQUENCE_PORT_PROJECTION = { 81 "pdu", 82 "sequence", 83 "destination_port" 84 }; 85 86 static final int PDU_COLUMN = 0; 87 static final int SEQUENCE_COLUMN = 1; 88 static final int DESTINATION_PORT_COLUMN = 2; 89 static final int DATE_COLUMN = 3; 90 static final int REFERENCE_NUMBER_COLUMN = 4; 91 static final int COUNT_COLUMN = 5; 92 static final int ADDRESS_COLUMN = 6; 93 static final int ID_COLUMN = 7; 94 95 static final String SELECT_BY_ID = "_id=?"; 96 static final String SELECT_BY_REFERENCE = "address=? AND reference_number=? AND count=?"; 97 98 /** New SMS received as an AsyncResult. */ 99 public static final int EVENT_NEW_SMS = 1; 100 101 /** Message type containing a {@link InboundSmsTracker} ready to broadcast to listeners. */ 102 static final int EVENT_BROADCAST_SMS = 2; 103 104 /** Message from resultReceiver notifying {@link WaitingState} of a completed broadcast. */ 105 static final int EVENT_BROADCAST_COMPLETE = 3; 106 107 /** Sent on exit from {@link WaitingState} to return to idle after sending all broadcasts. */ 108 static final int EVENT_RETURN_TO_IDLE = 4; 109 110 /** Release wakelock after a short timeout when returning to idle state. */ 111 static final int EVENT_RELEASE_WAKELOCK = 5; 112 113 /** Sent by {@link SmsBroadcastUndelivered} after cleaning the raw table. */ 114 static final int EVENT_START_ACCEPTING_SMS = 6; 115 116 /** Wakelock release delay when returning to idle state. */ 117 private static final int WAKELOCK_TIMEOUT = 3000; 118 119 /** URI for raw table of SMS provider. */ 120 private static final Uri sRawUri = Uri.withAppendedPath(Telephony.Sms.CONTENT_URI, "raw"); 121 122 protected final Context mContext; 123 private final ContentResolver mResolver; 124 125 /** Special handler for WAP push messages. */ 126 private final WapPushOverSms mWapPush; 127 128 /** Wake lock to ensure device stays awake while dispatching the SMS intents. */ 129 final PowerManager.WakeLock mWakeLock; 130 131 /** DefaultState throws an exception or logs an error for unhandled message types. */ 132 final DefaultState mDefaultState = new DefaultState(); 133 134 /** Startup state. Waiting for {@link SmsBroadcastUndelivered} to complete. */ 135 final StartupState mStartupState = new StartupState(); 136 137 /** Idle state. Waiting for messages to process. */ 138 final IdleState mIdleState = new IdleState(); 139 140 /** Delivering state. Saves the PDU in the raw table and acknowledges to SMSC. */ 141 final DeliveringState mDeliveringState = new DeliveringState(); 142 143 /** Broadcasting state. Waits for current broadcast to complete before delivering next. */ 144 final WaitingState mWaitingState = new WaitingState(); 145 146 /** Helper class to check whether storage is available for incoming messages. */ 147 protected final SmsStorageMonitor mStorageMonitor; 148 149 private final boolean mSmsReceiveDisabled; 150 151 /** 152 * Create a new SMS broadcast helper. 153 * @param name the class name for logging 154 * @param context the context of the phone app 155 * @param storageMonitor the SmsStorageMonitor to check for storage availability 156 */ 157 protected InboundSmsHandler(String name, Context context, SmsStorageMonitor storageMonitor) { 158 super(name); 159 160 mContext = context; 161 mStorageMonitor = storageMonitor; 162 mResolver = context.getContentResolver(); 163 mWapPush = new WapPushOverSms(context); 164 165 boolean smsCapable = mContext.getResources().getBoolean( 166 com.android.internal.R.bool.config_sms_capable); 167 mSmsReceiveDisabled = !SystemProperties.getBoolean( 168 TelephonyProperties.PROPERTY_SMS_RECEIVE, smsCapable); 169 170 PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); 171 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, name); 172 mWakeLock.acquire(); // wake lock released after we enter idle state 173 174 addState(mDefaultState); 175 addState(mStartupState, mDefaultState); 176 addState(mIdleState, mDefaultState); 177 addState(mDeliveringState, mDefaultState); 178 addState(mWaitingState, mDeliveringState); 179 180 setInitialState(mStartupState); 181 if (DBG) log("created InboundSmsHandler"); 182 } 183 184 /** 185 * Tell the state machine to quit after processing all messages. 186 */ 187 public void dispose() { 188 quit(); 189 } 190 191 /** 192 * Dispose of the WAP push object and release the wakelock. 193 */ 194 @Override 195 protected void onQuitting() { 196 mWapPush.dispose(); 197 198 while (mWakeLock.isHeld()) { 199 mWakeLock.release(); 200 } 201 } 202 203 /** 204 * This parent state throws an exception (for debug builds) or prints an error for unhandled 205 * message types. 206 */ 207 class DefaultState extends State { 208 @Override 209 public boolean processMessage(Message msg) { 210 String errorText = "processMessage: unhandled message type " + msg.what; 211 if (Build.IS_DEBUGGABLE) { 212 throw new RuntimeException(errorText); 213 } else { 214 loge(errorText); 215 return HANDLED; 216 } 217 } 218 } 219 220 /** 221 * The Startup state waits for {@link SmsBroadcastUndelivered} to process the raw table and 222 * notify the state machine to broadcast any complete PDUs that might not have been broadcast. 223 */ 224 class StartupState extends State { 225 @Override 226 public boolean processMessage(Message msg) { 227 switch (msg.what) { 228 case EVENT_NEW_SMS: 229 case EVENT_BROADCAST_SMS: 230 deferMessage(msg); 231 return HANDLED; 232 233 case EVENT_START_ACCEPTING_SMS: 234 transitionTo(mIdleState); 235 return HANDLED; 236 237 case EVENT_BROADCAST_COMPLETE: 238 case EVENT_RETURN_TO_IDLE: 239 case EVENT_RELEASE_WAKELOCK: 240 default: 241 // let DefaultState handle these unexpected message types 242 return NOT_HANDLED; 243 } 244 } 245 } 246 247 /** 248 * In the idle state the wakelock is released until a new SM arrives, then we transition 249 * to Delivering mode to handle it, acquiring the wakelock on exit. 250 */ 251 class IdleState extends State { 252 @Override 253 public void enter() { 254 if (DBG) log("entering Idle state"); 255 sendMessageDelayed(EVENT_RELEASE_WAKELOCK, WAKELOCK_TIMEOUT); 256 } 257 258 @Override 259 public void exit() { 260 mWakeLock.acquire(); 261 if (DBG) log("acquired wakelock, leaving Idle state"); 262 } 263 264 @Override 265 public boolean processMessage(Message msg) { 266 if (DBG) log("Idle state processing message type " + msg.what); 267 switch (msg.what) { 268 case EVENT_NEW_SMS: 269 case EVENT_BROADCAST_SMS: 270 deferMessage(msg); 271 transitionTo(mDeliveringState); 272 return HANDLED; 273 274 case EVENT_RELEASE_WAKELOCK: 275 mWakeLock.release(); 276 if (DBG) { 277 if (mWakeLock.isHeld()) { 278 // this is okay as long as we call release() for every acquire() 279 log("mWakeLock is still held after release"); 280 } else { 281 log("mWakeLock released"); 282 } 283 } 284 return HANDLED; 285 286 case EVENT_RETURN_TO_IDLE: 287 // already in idle state; ignore 288 return HANDLED; 289 290 case EVENT_BROADCAST_COMPLETE: 291 case EVENT_START_ACCEPTING_SMS: 292 default: 293 // let DefaultState handle these unexpected message types 294 return NOT_HANDLED; 295 } 296 } 297 } 298 299 /** 300 * In the delivering state, the inbound SMS is processed and stored in the raw table. 301 * The message is acknowledged before we exit this state. If there is a message to broadcast, 302 * transition to {@link WaitingState} state to send the ordered broadcast and wait for the 303 * results. When all messages have been processed, the halting state will release the wakelock. 304 */ 305 class DeliveringState extends State { 306 @Override 307 public void enter() { 308 if (DBG) log("entering Delivering state"); 309 } 310 311 @Override 312 public void exit() { 313 if (DBG) log("leaving Delivering state"); 314 } 315 316 @Override 317 public boolean processMessage(Message msg) { 318 switch (msg.what) { 319 case EVENT_NEW_SMS: 320 // handle new SMS from RIL 321 handleNewSms((AsyncResult) msg.obj); 322 sendMessage(EVENT_RETURN_TO_IDLE); 323 return HANDLED; 324 325 case EVENT_BROADCAST_SMS: 326 // if any broadcasts were sent, transition to waiting state 327 if (processMessagePart((InboundSmsTracker) msg.obj)) { 328 transitionTo(mWaitingState); 329 } 330 return HANDLED; 331 332 case EVENT_RETURN_TO_IDLE: 333 // return to idle after processing all other messages 334 transitionTo(mIdleState); 335 return HANDLED; 336 337 case EVENT_RELEASE_WAKELOCK: 338 mWakeLock.release(); // decrement wakelock from previous entry to Idle 339 if (!mWakeLock.isHeld()) { 340 // wakelock should still be held until 3 seconds after we enter Idle 341 loge("mWakeLock released while delivering/broadcasting!"); 342 } 343 return HANDLED; 344 345 // we shouldn't get this message type in this state, log error and halt. 346 case EVENT_BROADCAST_COMPLETE: 347 case EVENT_START_ACCEPTING_SMS: 348 default: 349 // let DefaultState handle these unexpected message types 350 return NOT_HANDLED; 351 } 352 } 353 } 354 355 /** 356 * The waiting state delegates handling of new SMS to parent {@link DeliveringState}, but 357 * defers handling of the {@link #EVENT_BROADCAST_SMS} phase until after the current 358 * result receiver sends {@link #EVENT_BROADCAST_COMPLETE}. Before transitioning to 359 * {@link DeliveringState}, {@link #EVENT_RETURN_TO_IDLE} is sent to transition to 360 * {@link IdleState} after any deferred {@link #EVENT_BROADCAST_SMS} messages are handled. 361 */ 362 class WaitingState extends State { 363 @Override 364 public boolean processMessage(Message msg) { 365 switch (msg.what) { 366 case EVENT_BROADCAST_SMS: 367 // defer until the current broadcast completes 368 deferMessage(msg); 369 return HANDLED; 370 371 case EVENT_BROADCAST_COMPLETE: 372 // return to idle after handling all deferred messages 373 sendMessage(EVENT_RETURN_TO_IDLE); 374 transitionTo(mDeliveringState); 375 return HANDLED; 376 377 case EVENT_RETURN_TO_IDLE: 378 // not ready to return to idle; ignore 379 return HANDLED; 380 381 default: 382 // parent state handles the other message types 383 return NOT_HANDLED; 384 } 385 } 386 } 387 388 void handleNewSms(AsyncResult ar) { 389 if (ar.exception != null) { 390 loge("Exception processing incoming SMS: " + ar.exception); 391 return; 392 } 393 394 int result; 395 try { 396 SmsMessage sms = (SmsMessage) ar.result; 397 result = dispatchMessage(sms.mWrappedSmsMessage); 398 } catch (RuntimeException ex) { 399 loge("Exception dispatching message", ex); 400 result = Intents.RESULT_SMS_GENERIC_ERROR; 401 } 402 403 // RESULT_OK means that the SMS will be acknowledged by special handling, 404 // e.g. for SMS-PP data download. Any other result, we should ack here. 405 if (result != Activity.RESULT_OK) { 406 boolean handled = (result == Intents.RESULT_SMS_HANDLED); 407 notifyAndAcknowledgeLastIncomingSms(handled, result, null); 408 } 409 } 410 411 /** 412 * Process an SMS message from the RIL, calling subclass methods to handle 3GPP and 413 * 3GPP2-specific message types. 414 * 415 * @param smsb the SmsMessageBase object from the RIL 416 * @return a result code from {@link android.provider.Telephony.Sms.Intents}, 417 * or {@link Activity#RESULT_OK} for delayed acknowledgment to SMSC 418 */ 419 public int dispatchMessage(SmsMessageBase smsb) { 420 // If sms is null, there was a parsing error. 421 if (smsb == null) { 422 loge("dispatchSmsMessage: message is null"); 423 return Intents.RESULT_SMS_GENERIC_ERROR; 424 } 425 426 if (mSmsReceiveDisabled) { 427 // Device doesn't support receiving SMS, 428 log("Received short message on device which doesn't support " 429 + "receiving SMS. Ignored."); 430 return Intents.RESULT_SMS_HANDLED; 431 } 432 433 return dispatchMessageRadioSpecific(smsb); 434 } 435 436 /** 437 * Process voicemail notification, SMS-PP data download, CDMA CMAS, CDMA WAP push, and other 438 * 3GPP/3GPP2-specific messages. Regular SMS messages are handled by calling the shared 439 * {@link #dispatchNormalMessage} from this class. 440 * 441 * @param smsb the SmsMessageBase object from the RIL 442 * @return a result code from {@link android.provider.Telephony.Sms.Intents}, 443 * or {@link Activity#RESULT_OK} for delayed acknowledgment to SMSC 444 */ 445 protected abstract int dispatchMessageRadioSpecific(SmsMessageBase smsb); 446 447 /** 448 * Send an acknowledge message to the SMSC. 449 * @param success indicates that last message was successfully received. 450 * @param result result code indicating any error 451 * @param response callback message sent when operation completes. 452 */ 453 protected abstract void acknowledgeLastIncomingSms(boolean success, 454 int result, Message response); 455 456 /** 457 * Notify interested apps if the framework has rejected an incoming SMS, 458 * and send an acknowledge message to the network. 459 * @param success indicates that last message was successfully received. 460 * @param result result code indicating any error 461 * @param response callback message sent when operation completes. 462 */ 463 void notifyAndAcknowledgeLastIncomingSms(boolean success, 464 int result, Message response) { 465 if (!success) { 466 // broadcast SMS_REJECTED_ACTION intent 467 Intent intent = new Intent(Intents.SMS_REJECTED_ACTION); 468 intent.putExtra("result", result); 469 mContext.sendBroadcast(intent, android.Manifest.permission.RECEIVE_SMS); 470 } 471 acknowledgeLastIncomingSms(success, result, response); 472 } 473 474 /** 475 * Return true if this handler is for 3GPP2 messages; false for 3GPP format. 476 * @return true for the 3GPP2 handler; false for the 3GPP handler 477 */ 478 protected abstract boolean is3gpp2(); 479 480 /** 481 * Dispatch a normal incoming SMS. This is called from {@link #dispatchMessageRadioSpecific} 482 * if no format-specific handling was required. Saves the PDU to the SMS provider raw table, 483 * creates an {@link InboundSmsTracker}, then sends it to the state machine as an 484 * {@link #EVENT_BROADCAST_SMS}. Returns {@link Intents#RESULT_SMS_HANDLED} or an error value. 485 * 486 * @param sms the message to dispatch 487 * @return {@link Intents#RESULT_SMS_HANDLED} if the message was accepted, or an error status 488 */ 489 protected int dispatchNormalMessage(SmsMessageBase sms) { 490 SmsHeader smsHeader = sms.getUserDataHeader(); 491 InboundSmsTracker tracker; 492 493 if ((smsHeader == null) || (smsHeader.concatRef == null)) { 494 // Message is not concatenated. 495 int destPort = -1; 496 if (smsHeader != null && smsHeader.portAddrs != null) { 497 // The message was sent to a port. 498 destPort = smsHeader.portAddrs.destPort; 499 if (DBG) log("destination port: " + destPort); 500 } 501 502 tracker = new InboundSmsTracker(sms.getPdu(), sms.getTimestampMillis(), destPort, 503 is3gpp2(), false); 504 } else { 505 // Create a tracker for this message segment. 506 SmsHeader.ConcatRef concatRef = smsHeader.concatRef; 507 SmsHeader.PortAddrs portAddrs = smsHeader.portAddrs; 508 int destPort = (portAddrs != null ? portAddrs.destPort : -1); 509 510 tracker = new InboundSmsTracker(sms.getPdu(), sms.getTimestampMillis(), destPort, 511 is3gpp2(), sms.getOriginatingAddress(), concatRef.refNumber, 512 concatRef.seqNumber, concatRef.msgCount, false); 513 } 514 515 if (VDBG) log("created tracker: " + tracker); 516 return addTrackerToRawTableAndSendMessage(tracker); 517 } 518 519 /** 520 * Helper to add the tracker to the raw table and then send a message to broadcast it, if 521 * successful. Returns the SMS intent status to return to the SMSC. 522 * @param tracker the tracker to save to the raw table and then deliver 523 * @return {@link Intents#RESULT_SMS_HANDLED} or {@link Intents#RESULT_SMS_GENERIC_ERROR} 524 * or {@link Intents#RESULT_SMS_DUPLICATED} 525 */ 526 protected int addTrackerToRawTableAndSendMessage(InboundSmsTracker tracker) { 527 switch(addTrackerToRawTable(tracker)) { 528 case Intents.RESULT_SMS_HANDLED: 529 sendMessage(EVENT_BROADCAST_SMS, tracker); 530 return Intents.RESULT_SMS_HANDLED; 531 532 case Intents.RESULT_SMS_DUPLICATED: 533 return Intents.RESULT_SMS_HANDLED; 534 535 case Intents.RESULT_SMS_GENERIC_ERROR: 536 default: 537 return Intents.RESULT_SMS_GENERIC_ERROR; 538 } 539 } 540 541 /** 542 * Process the inbound SMS segment. If the message is complete, send it as an ordered 543 * broadcast to interested receivers and return true. If the message is a segment of an 544 * incomplete multi-part SMS, return false. 545 * @param tracker the tracker containing the message segment to process 546 * @return true if an ordered broadcast was sent; false if waiting for more message segments 547 */ 548 boolean processMessagePart(InboundSmsTracker tracker) { 549 int messageCount = tracker.getMessageCount(); 550 byte[][] pdus; 551 int destPort = tracker.getDestPort(); 552 553 if (messageCount == 1) { 554 // single-part message 555 pdus = new byte[][]{tracker.getPdu()}; 556 } else { 557 // multi-part message 558 Cursor cursor = null; 559 try { 560 // used by several query selection arguments 561 String address = tracker.getAddress(); 562 String refNumber = Integer.toString(tracker.getReferenceNumber()); 563 String count = Integer.toString(tracker.getMessageCount()); 564 565 // query for all segments and broadcast message if we have all the parts 566 String[] whereArgs = {address, refNumber, count}; 567 cursor = mResolver.query(sRawUri, PDU_SEQUENCE_PORT_PROJECTION, 568 SELECT_BY_REFERENCE, whereArgs, null); 569 570 int cursorCount = cursor.getCount(); 571 if (cursorCount < messageCount) { 572 // Wait for the other message parts to arrive. It's also possible for the last 573 // segment to arrive before processing the EVENT_BROADCAST_SMS for one of the 574 // earlier segments. In that case, the broadcast will be sent as soon as all 575 // segments are in the table, and any later EVENT_BROADCAST_SMS messages will 576 // get a row count of 0 and return. 577 return false; 578 } 579 580 // All the parts are in place, deal with them 581 pdus = new byte[messageCount][]; 582 while (cursor.moveToNext()) { 583 // subtract offset to convert sequence to 0-based array index 584 int index = cursor.getInt(SEQUENCE_COLUMN) - tracker.getIndexOffset(); 585 586 pdus[index] = HexDump.hexStringToByteArray(cursor.getString(PDU_COLUMN)); 587 588 // Read the destination port from the first segment (needed for CDMA WAP PDU). 589 // It's not a bad idea to prefer the port from the first segment in other cases. 590 if (index == 0 && !cursor.isNull(DESTINATION_PORT_COLUMN)) { 591 int port = cursor.getInt(DESTINATION_PORT_COLUMN); 592 // strip format flags and convert to real port number, or -1 593 port = InboundSmsTracker.getRealDestPort(port); 594 if (port != -1) { 595 destPort = port; 596 } 597 } 598 } 599 } catch (SQLException e) { 600 loge("Can't access multipart SMS database", e); 601 return false; 602 } finally { 603 if (cursor != null) { 604 cursor.close(); 605 } 606 } 607 } 608 609 BroadcastReceiver resultReceiver = new SmsBroadcastReceiver(tracker); 610 611 if (destPort == SmsHeader.PORT_WAP_PUSH) { 612 // Build up the data stream 613 ByteArrayOutputStream output = new ByteArrayOutputStream(); 614 for (byte[] pdu : pdus) { 615 // 3GPP needs to extract the User Data from the PDU; 3GPP2 has already done this 616 if (!tracker.is3gpp2()) { 617 SmsMessage msg = SmsMessage.createFromPdu(pdu, SmsConstants.FORMAT_3GPP); 618 pdu = msg.getUserData(); 619 } 620 output.write(pdu, 0, pdu.length); 621 } 622 int result = mWapPush.dispatchWapPdu(output.toByteArray(), resultReceiver, this); 623 if (DBG) log("dispatchWapPdu() returned " + result); 624 // result is Activity.RESULT_OK if an ordered broadcast was sent 625 return (result == Activity.RESULT_OK); 626 } 627 628 Intent intent; 629 if (destPort == -1) { 630 intent = new Intent(Intents.SMS_RECEIVED_ACTION); 631 } else { 632 Uri uri = Uri.parse("sms://localhost:" + destPort); 633 intent = new Intent(Intents.DATA_SMS_RECEIVED_ACTION, uri); 634 } 635 intent.putExtra("pdus", pdus); 636 intent.putExtra("format", tracker.getFormat()); 637 dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS, 638 AppOpsManager.OP_RECEIVE_SMS, resultReceiver); 639 return true; 640 } 641 642 /** 643 * Dispatch the intent with the specified permission, appOp, and result receiver, using 644 * this state machine's handler thread to run the result receiver. 645 * 646 * @param intent the intent to broadcast 647 * @param permission receivers are required to have this permission 648 * @param appOp app op that is being performed when dispatching to a receiver 649 */ 650 void dispatchIntent(Intent intent, String permission, int appOp, 651 BroadcastReceiver resultReceiver) { 652 mContext.sendOrderedBroadcast(intent, permission, appOp, resultReceiver, 653 getHandler(), Activity.RESULT_OK, null, null); 654 } 655 656 /** 657 * Helper for {@link SmsBroadcastUndelivered} to delete an old message in the raw table. 658 */ 659 void deleteFromRawTable(String deleteWhere, String[] deleteWhereArgs) { 660 int rows = mResolver.delete(sRawUri, deleteWhere, deleteWhereArgs); 661 if (rows == 0) { 662 loge("No rows were deleted from raw table!"); 663 } else if (DBG) { 664 log("Deleted " + rows + " rows from raw table."); 665 } 666 } 667 668 /** 669 * Insert a message PDU into the raw table so we can acknowledge it immediately. 670 * If the device crashes before the broadcast to listeners completes, it will be delivered 671 * from the raw table on the next device boot. For single-part messages, the deleteWhere 672 * and deleteWhereArgs fields of the tracker will be set to delete the correct row after 673 * the ordered broadcast completes. 674 * 675 * @param tracker the tracker to add to the raw table 676 * @return true on success; false on failure to write to database 677 */ 678 private int addTrackerToRawTable(InboundSmsTracker tracker) { 679 if (tracker.getMessageCount() != 1) { 680 // check for duplicate message segments 681 Cursor cursor = null; 682 try { 683 // sequence numbers are 1-based except for CDMA WAP, which is 0-based 684 int sequence = tracker.getSequenceNumber(); 685 686 // convert to strings for query 687 String address = tracker.getAddress(); 688 String refNumber = Integer.toString(tracker.getReferenceNumber()); 689 String count = Integer.toString(tracker.getMessageCount()); 690 691 String seqNumber = Integer.toString(sequence); 692 693 // set the delete selection args for multi-part message 694 String[] deleteWhereArgs = {address, refNumber, count}; 695 tracker.setDeleteWhere(SELECT_BY_REFERENCE, deleteWhereArgs); 696 697 // Check for duplicate message segments 698 cursor = mResolver.query(sRawUri, PDU_PROJECTION, 699 "address=? AND reference_number=? AND count=? AND sequence=?", 700 new String[] {address, refNumber, count, seqNumber}, null); 701 702 // moveToNext() returns false if no duplicates were found 703 if (cursor.moveToNext()) { 704 loge("Discarding duplicate message segment, refNumber=" + refNumber 705 + " seqNumber=" + seqNumber); 706 String oldPduString = cursor.getString(PDU_COLUMN); 707 byte[] pdu = tracker.getPdu(); 708 byte[] oldPdu = HexDump.hexStringToByteArray(oldPduString); 709 if (!Arrays.equals(oldPdu, tracker.getPdu())) { 710 loge("Warning: dup message segment PDU of length " + pdu.length 711 + " is different from existing PDU of length " + oldPdu.length); 712 } 713 return Intents.RESULT_SMS_DUPLICATED; // reject message 714 } 715 cursor.close(); 716 } catch (SQLException e) { 717 loge("Can't access multipart SMS database", e); 718 return Intents.RESULT_SMS_GENERIC_ERROR; // reject message 719 } finally { 720 if (cursor != null) { 721 cursor.close(); 722 } 723 } 724 } 725 726 ContentValues values = tracker.getContentValues(); 727 728 if (VDBG) log("adding content values to raw table: " + values.toString()); 729 Uri newUri = mResolver.insert(sRawUri, values); 730 if (DBG) log("URI of new row -> " + newUri); 731 732 try { 733 long rowId = ContentUris.parseId(newUri); 734 if (tracker.getMessageCount() == 1) { 735 // set the delete selection args for single-part message 736 tracker.setDeleteWhere(SELECT_BY_ID, new String[]{Long.toString(rowId)}); 737 } 738 return Intents.RESULT_SMS_HANDLED; 739 } catch (Exception e) { 740 loge("error parsing URI for new row: " + newUri, e); 741 return Intents.RESULT_SMS_GENERIC_ERROR; 742 } 743 } 744 745 /** 746 * Returns whether the default message format for the current radio technology is 3GPP2. 747 * @return true if the radio technology uses 3GPP2 format by default, false for 3GPP format 748 */ 749 static boolean isCurrentFormat3gpp2() { 750 int activePhone = TelephonyManager.getDefault().getCurrentPhoneType(); 751 return (PHONE_TYPE_CDMA == activePhone); 752 } 753 754 /** 755 * Handler for an {@link InboundSmsTracker} broadcast. Deletes PDUs from the raw table and 756 * logs the broadcast duration (as an error if the other receivers were especially slow). 757 */ 758 private final class SmsBroadcastReceiver extends BroadcastReceiver { 759 private final String mDeleteWhere; 760 private final String[] mDeleteWhereArgs; 761 private long mBroadcastTimeNano; 762 763 SmsBroadcastReceiver(InboundSmsTracker tracker) { 764 mDeleteWhere = tracker.getDeleteWhere(); 765 mDeleteWhereArgs = tracker.getDeleteWhereArgs(); 766 mBroadcastTimeNano = System.nanoTime(); 767 } 768 769 @Override 770 public void onReceive(Context context, Intent intent) { 771 String action = intent.getAction(); 772 if (!Intents.SMS_RECEIVED_ACTION.equals(action) 773 && !Intents.DATA_SMS_RECEIVED_ACTION.equals(action)) { 774 loge("unexpected BroadcastReceiver action: " + action); 775 } 776 777 int rc = getResultCode(); 778 if ((rc != Activity.RESULT_OK) && (rc != Intents.RESULT_SMS_HANDLED)) { 779 loge("a broadcast receiver set the result code to " + rc 780 + ", deleting from raw table anyway!"); 781 } else if (DBG) { 782 log("successful broadcast, deleting from raw table."); 783 } 784 785 deleteFromRawTable(mDeleteWhere, mDeleteWhereArgs); 786 sendMessage(EVENT_BROADCAST_COMPLETE); 787 788 int durationMillis = (int) ((System.nanoTime() - mBroadcastTimeNano) / 1000000); 789 if (durationMillis >= 5000) { 790 loge("Slow ordered broadcast completion time: " + durationMillis + " ms"); 791 } else if (DBG) { 792 log("ordered broadcast completed in: " + durationMillis + " ms"); 793 } 794 } 795 } 796 797 /** 798 * Log with debug level. 799 * @param s the string to log 800 */ 801 @Override 802 protected void log(String s) { 803 Rlog.d(getName(), s); 804 } 805 806 /** 807 * Log with error level. 808 * @param s the string to log 809 */ 810 @Override 811 protected void loge(String s) { 812 Rlog.e(getName(), s); 813 } 814 815 /** 816 * Log with error level. 817 * @param s the string to log 818 * @param e is a Throwable which logs additional information. 819 */ 820 @Override 821 protected void loge(String s, Throwable e) { 822 Rlog.e(getName(), s, e); 823 } 824} 825