InboundSmsHandler.java revision b29851580bba4a13ddbf7a534d8b09295eb2c60f
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.app.PendingIntent; 22import android.app.PendingIntent.CanceledException; 23import android.content.BroadcastReceiver; 24import android.content.ComponentName; 25import android.content.ContentResolver; 26import android.content.ContentUris; 27import android.content.ContentValues; 28import android.content.Context; 29import android.content.Intent; 30import android.database.Cursor; 31import android.database.SQLException; 32import android.net.Uri; 33import android.os.AsyncResult; 34import android.os.Binder; 35import android.os.Build; 36import android.os.Bundle; 37import android.os.Message; 38import android.os.PowerManager; 39import android.os.SystemProperties; 40import android.provider.Telephony; 41import android.provider.Telephony.Sms.Intents; 42import android.telephony.Rlog; 43import android.telephony.SmsMessage; 44import android.telephony.SubscriptionManager; 45import android.telephony.TelephonyManager; 46import android.text.TextUtils; 47 48import com.android.internal.util.HexDump; 49import com.android.internal.util.State; 50import com.android.internal.util.StateMachine; 51 52import java.io.ByteArrayOutputStream; 53import java.util.Arrays; 54 55import static android.telephony.TelephonyManager.PHONE_TYPE_CDMA; 56 57/** 58 * This class broadcasts incoming SMS messages to interested apps after storing them in 59 * the SmsProvider "raw" table and ACKing them to the SMSC. After each message has been 60 * broadcast, its parts are removed from the raw table. If the device crashes after ACKing 61 * but before the broadcast completes, the pending messages will be rebroadcast on the next boot. 62 * 63 * <p>The state machine starts in {@link IdleState} state. When the {@link SMSDispatcher} receives a 64 * new SMS from the radio, it calls {@link #dispatchNormalMessage}, 65 * which sends a message to the state machine, causing the wakelock to be acquired in 66 * {@link #haltedProcessMessage}, which transitions to {@link DeliveringState} state, where the message 67 * is saved to the raw table, then acknowledged via the {@link SMSDispatcher} which called us. 68 * 69 * <p>After saving the SMS, if the message is complete (either single-part or the final segment 70 * of a multi-part SMS), we broadcast the completed PDUs as an ordered broadcast, then transition to 71 * {@link WaitingState} state to wait for the broadcast to complete. When the local 72 * {@link BroadcastReceiver} is called with the result, it sends {@link #EVENT_BROADCAST_COMPLETE} 73 * to the state machine, causing us to either broadcast the next pending message (if one has 74 * arrived while waiting for the broadcast to complete), or to transition back to the halted state 75 * after all messages are processed. Then the wakelock is released and we wait for the next SMS. 76 */ 77public abstract class InboundSmsHandler extends StateMachine { 78 protected static final boolean DBG = true; 79 private static final boolean VDBG = false; // STOPSHIP if true, logs user data 80 81 /** Query projection for checking for duplicate message segments. */ 82 private static final String[] PDU_PROJECTION = { 83 "pdu" 84 }; 85 86 /** Query projection for combining concatenated message segments. */ 87 private static final String[] PDU_SEQUENCE_PORT_PROJECTION = { 88 "pdu", 89 "sequence", 90 "destination_port" 91 }; 92 93 static final int PDU_COLUMN = 0; 94 static final int SEQUENCE_COLUMN = 1; 95 static final int DESTINATION_PORT_COLUMN = 2; 96 static final int DATE_COLUMN = 3; 97 static final int REFERENCE_NUMBER_COLUMN = 4; 98 static final int COUNT_COLUMN = 5; 99 static final int ADDRESS_COLUMN = 6; 100 static final int ID_COLUMN = 7; 101 102 static final String SELECT_BY_ID = "_id=?"; 103 static final String SELECT_BY_REFERENCE = "address=? AND reference_number=? AND count=?"; 104 105 /** New SMS received as an AsyncResult. */ 106 public static final int EVENT_NEW_SMS = 1; 107 108 /** Message type containing a {@link InboundSmsTracker} ready to broadcast to listeners. */ 109 static final int EVENT_BROADCAST_SMS = 2; 110 111 /** Message from resultReceiver notifying {@link WaitingState} of a completed broadcast. */ 112 static final int EVENT_BROADCAST_COMPLETE = 3; 113 114 /** Sent on exit from {@link WaitingState} to return to idle after sending all broadcasts. */ 115 static final int EVENT_RETURN_TO_IDLE = 4; 116 117 /** Release wakelock after a short timeout when returning to idle state. */ 118 static final int EVENT_RELEASE_WAKELOCK = 5; 119 120 /** Sent by {@link SmsBroadcastUndelivered} after cleaning the raw table. */ 121 static final int EVENT_START_ACCEPTING_SMS = 6; 122 123 /** Update phone object */ 124 static final int EVENT_UPDATE_PHONE_OBJECT = 7; 125 126 /** New SMS received as an AsyncResult. */ 127 public static final int EVENT_INJECT_SMS = 8; 128 129 /** Wakelock release delay when returning to idle state. */ 130 private static final int WAKELOCK_TIMEOUT = 3000; 131 132 /** URI for raw table of SMS provider. */ 133 private static final Uri sRawUri = Uri.withAppendedPath(Telephony.Sms.CONTENT_URI, "raw"); 134 135 protected final Context mContext; 136 private final ContentResolver mResolver; 137 138 /** Special handler for WAP push messages. */ 139 private final WapPushOverSms mWapPush; 140 141 /** Wake lock to ensure device stays awake while dispatching the SMS intents. */ 142 final PowerManager.WakeLock mWakeLock; 143 144 /** DefaultState throws an exception or logs an error for unhandled message types. */ 145 final DefaultState mDefaultState = new DefaultState(); 146 147 /** Startup state. Waiting for {@link SmsBroadcastUndelivered} to complete. */ 148 final StartupState mStartupState = new StartupState(); 149 150 /** Idle state. Waiting for messages to process. */ 151 final IdleState mIdleState = new IdleState(); 152 153 /** Delivering state. Saves the PDU in the raw table and acknowledges to SMSC. */ 154 final DeliveringState mDeliveringState = new DeliveringState(); 155 156 /** Broadcasting state. Waits for current broadcast to complete before delivering next. */ 157 final WaitingState mWaitingState = new WaitingState(); 158 159 /** Helper class to check whether storage is available for incoming messages. */ 160 protected SmsStorageMonitor mStorageMonitor; 161 162 private final boolean mSmsReceiveDisabled; 163 164 protected PhoneBase mPhone; 165 166 protected CellBroadcastHandler mCellBroadcastHandler; 167 168 169 /** 170 * Create a new SMS broadcast helper. 171 * @param name the class name for logging 172 * @param context the context of the phone app 173 * @param storageMonitor the SmsStorageMonitor to check for storage availability 174 */ 175 protected InboundSmsHandler(String name, Context context, SmsStorageMonitor storageMonitor, 176 PhoneBase phone, CellBroadcastHandler cellBroadcastHandler) { 177 super(name); 178 179 mContext = context; 180 mStorageMonitor = storageMonitor; 181 mPhone = phone; 182 mCellBroadcastHandler = cellBroadcastHandler; 183 mResolver = context.getContentResolver(); 184 mWapPush = new WapPushOverSms(context); 185 186 boolean smsCapable = mContext.getResources().getBoolean( 187 com.android.internal.R.bool.config_sms_capable); 188 mSmsReceiveDisabled = !SystemProperties.getBoolean( 189 TelephonyProperties.PROPERTY_SMS_RECEIVE, smsCapable); 190 191 PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); 192 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, name); 193 mWakeLock.acquire(); // wake lock released after we enter idle state 194 195 addState(mDefaultState); 196 addState(mStartupState, mDefaultState); 197 addState(mIdleState, mDefaultState); 198 addState(mDeliveringState, mDefaultState); 199 addState(mWaitingState, mDeliveringState); 200 201 setInitialState(mStartupState); 202 if (DBG) log("created InboundSmsHandler"); 203 } 204 205 /** 206 * Tell the state machine to quit after processing all messages. 207 */ 208 public void dispose() { 209 quit(); 210 } 211 212 /** 213 * Update the phone object when it changes. 214 */ 215 public void updatePhoneObject(PhoneBase phone) { 216 sendMessage(EVENT_UPDATE_PHONE_OBJECT, phone); 217 } 218 219 /** 220 * Dispose of the WAP push object and release the wakelock. 221 */ 222 @Override 223 protected void onQuitting() { 224 mWapPush.dispose(); 225 226 while (mWakeLock.isHeld()) { 227 mWakeLock.release(); 228 } 229 } 230 231 // CAF_MSIM Is this used anywhere ? if not remove it 232 public PhoneBase getPhone() { 233 return mPhone; 234 } 235 236 /** 237 * This parent state throws an exception (for debug builds) or prints an error for unhandled 238 * message types. 239 */ 240 class DefaultState extends State { 241 @Override 242 public boolean processMessage(Message msg) { 243 switch (msg.what) { 244 case EVENT_UPDATE_PHONE_OBJECT: { 245 onUpdatePhoneObject((PhoneBase) msg.obj); 246 break; 247 } 248 default: { 249 String errorText = "processMessage: unhandled message type " + msg.what; 250 if (Build.IS_DEBUGGABLE) { 251 throw new RuntimeException(errorText); 252 } else { 253 loge(errorText); 254 } 255 break; 256 } 257 } 258 return HANDLED; 259 } 260 } 261 262 /** 263 * The Startup state waits for {@link SmsBroadcastUndelivered} to process the raw table and 264 * notify the state machine to broadcast any complete PDUs that might not have been broadcast. 265 */ 266 class StartupState extends State { 267 @Override 268 public boolean processMessage(Message msg) { 269 switch (msg.what) { 270 case EVENT_NEW_SMS: 271 case EVENT_INJECT_SMS: 272 case EVENT_BROADCAST_SMS: 273 deferMessage(msg); 274 return HANDLED; 275 276 case EVENT_START_ACCEPTING_SMS: 277 transitionTo(mIdleState); 278 return HANDLED; 279 280 case EVENT_BROADCAST_COMPLETE: 281 case EVENT_RETURN_TO_IDLE: 282 case EVENT_RELEASE_WAKELOCK: 283 default: 284 // let DefaultState handle these unexpected message types 285 return NOT_HANDLED; 286 } 287 } 288 } 289 290 /** 291 * In the idle state the wakelock is released until a new SM arrives, then we transition 292 * to Delivering mode to handle it, acquiring the wakelock on exit. 293 */ 294 class IdleState extends State { 295 @Override 296 public void enter() { 297 if (DBG) log("entering Idle state"); 298 sendMessageDelayed(EVENT_RELEASE_WAKELOCK, WAKELOCK_TIMEOUT); 299 } 300 301 @Override 302 public void exit() { 303 mWakeLock.acquire(); 304 if (DBG) log("acquired wakelock, leaving Idle state"); 305 } 306 307 @Override 308 public boolean processMessage(Message msg) { 309 if (DBG) log("Idle state processing message type " + msg.what); 310 switch (msg.what) { 311 case EVENT_NEW_SMS: 312 case EVENT_INJECT_SMS: 313 case EVENT_BROADCAST_SMS: 314 deferMessage(msg); 315 transitionTo(mDeliveringState); 316 return HANDLED; 317 318 case EVENT_RELEASE_WAKELOCK: 319 mWakeLock.release(); 320 if (DBG) { 321 if (mWakeLock.isHeld()) { 322 // this is okay as long as we call release() for every acquire() 323 log("mWakeLock is still held after release"); 324 } else { 325 log("mWakeLock released"); 326 } 327 } 328 return HANDLED; 329 330 case EVENT_RETURN_TO_IDLE: 331 // already in idle state; ignore 332 return HANDLED; 333 334 case EVENT_BROADCAST_COMPLETE: 335 case EVENT_START_ACCEPTING_SMS: 336 default: 337 // let DefaultState handle these unexpected message types 338 return NOT_HANDLED; 339 } 340 } 341 } 342 343 /** 344 * In the delivering state, the inbound SMS is processed and stored in the raw table. 345 * The message is acknowledged before we exit this state. If there is a message to broadcast, 346 * transition to {@link WaitingState} state to send the ordered broadcast and wait for the 347 * results. When all messages have been processed, the halting state will release the wakelock. 348 */ 349 class DeliveringState extends State { 350 @Override 351 public void enter() { 352 if (DBG) log("entering Delivering state"); 353 } 354 355 @Override 356 public void exit() { 357 if (DBG) log("leaving Delivering state"); 358 } 359 360 @Override 361 public boolean processMessage(Message msg) { 362 switch (msg.what) { 363 case EVENT_NEW_SMS: 364 // handle new SMS from RIL 365 handleNewSms((AsyncResult) msg.obj); 366 sendMessage(EVENT_RETURN_TO_IDLE); 367 return HANDLED; 368 369 case EVENT_INJECT_SMS: 370 // handle new injected SMS 371 handleInjectSms((AsyncResult) msg.obj); 372 sendMessage(EVENT_RETURN_TO_IDLE); 373 return HANDLED; 374 375 case EVENT_BROADCAST_SMS: 376 // if any broadcasts were sent, transition to waiting state 377 if (processMessagePart((InboundSmsTracker) msg.obj)) { 378 transitionTo(mWaitingState); 379 } 380 return HANDLED; 381 382 case EVENT_RETURN_TO_IDLE: 383 // return to idle after processing all other messages 384 transitionTo(mIdleState); 385 return HANDLED; 386 387 case EVENT_RELEASE_WAKELOCK: 388 mWakeLock.release(); // decrement wakelock from previous entry to Idle 389 if (!mWakeLock.isHeld()) { 390 // wakelock should still be held until 3 seconds after we enter Idle 391 loge("mWakeLock released while delivering/broadcasting!"); 392 } 393 return HANDLED; 394 395 // we shouldn't get this message type in this state, log error and halt. 396 case EVENT_BROADCAST_COMPLETE: 397 case EVENT_START_ACCEPTING_SMS: 398 default: 399 // let DefaultState handle these unexpected message types 400 return NOT_HANDLED; 401 } 402 } 403 } 404 405 /** 406 * The waiting state delegates handling of new SMS to parent {@link DeliveringState}, but 407 * defers handling of the {@link #EVENT_BROADCAST_SMS} phase until after the current 408 * result receiver sends {@link #EVENT_BROADCAST_COMPLETE}. Before transitioning to 409 * {@link DeliveringState}, {@link #EVENT_RETURN_TO_IDLE} is sent to transition to 410 * {@link IdleState} after any deferred {@link #EVENT_BROADCAST_SMS} messages are handled. 411 */ 412 class WaitingState extends State { 413 @Override 414 public boolean processMessage(Message msg) { 415 switch (msg.what) { 416 case EVENT_BROADCAST_SMS: 417 // defer until the current broadcast completes 418 deferMessage(msg); 419 return HANDLED; 420 421 case EVENT_BROADCAST_COMPLETE: 422 // return to idle after handling all deferred messages 423 sendMessage(EVENT_RETURN_TO_IDLE); 424 transitionTo(mDeliveringState); 425 return HANDLED; 426 427 case EVENT_RETURN_TO_IDLE: 428 // not ready to return to idle; ignore 429 return HANDLED; 430 431 default: 432 // parent state handles the other message types 433 return NOT_HANDLED; 434 } 435 } 436 } 437 438 void handleNewSms(AsyncResult ar) { 439 if (ar.exception != null) { 440 loge("Exception processing incoming SMS: " + ar.exception); 441 return; 442 } 443 444 int result; 445 try { 446 SmsMessage sms = (SmsMessage) ar.result; 447 result = dispatchMessage(sms.mWrappedSmsMessage); 448 } catch (RuntimeException ex) { 449 loge("Exception dispatching message", ex); 450 result = Intents.RESULT_SMS_GENERIC_ERROR; 451 } 452 453 // RESULT_OK means that the SMS will be acknowledged by special handling, 454 // e.g. for SMS-PP data download. Any other result, we should ack here. 455 if (result != Activity.RESULT_OK) { 456 boolean handled = (result == Intents.RESULT_SMS_HANDLED); 457 notifyAndAcknowledgeLastIncomingSms(handled, result, null); 458 } 459 } 460 461 /** 462 * This method is called when a new SMS PDU is injected into application framework. 463 * @param ar is the AsyncResult that has the SMS PDU to be injected. 464 */ 465 void handleInjectSms(AsyncResult ar) { 466 int result; 467 PendingIntent receivedIntent = null; 468 try { 469 receivedIntent = (PendingIntent) ar.userObj; 470 SmsMessage sms = (SmsMessage) ar.result; 471 if (sms == null) { 472 result = Intents.RESULT_SMS_GENERIC_ERROR; 473 } else { 474 result = dispatchMessage(sms.mWrappedSmsMessage); 475 } 476 } catch (RuntimeException ex) { 477 loge("Exception dispatching message", ex); 478 result = Intents.RESULT_SMS_GENERIC_ERROR; 479 } 480 481 if (receivedIntent != null) { 482 try { 483 receivedIntent.send(result); 484 } catch (CanceledException e) { } 485 } 486 } 487 488 /** 489 * Process an SMS message from the RIL, calling subclass methods to handle 3GPP and 490 * 3GPP2-specific message types. 491 * 492 * @param smsb the SmsMessageBase object from the RIL 493 * @return a result code from {@link android.provider.Telephony.Sms.Intents}, 494 * or {@link Activity#RESULT_OK} for delayed acknowledgment to SMSC 495 */ 496 public int dispatchMessage(SmsMessageBase smsb) { 497 // If sms is null, there was a parsing error. 498 if (smsb == null) { 499 loge("dispatchSmsMessage: message is null"); 500 return Intents.RESULT_SMS_GENERIC_ERROR; 501 } 502 503 if (mSmsReceiveDisabled) { 504 // Device doesn't support receiving SMS, 505 log("Received short message on device which doesn't support " 506 + "receiving SMS. Ignored."); 507 return Intents.RESULT_SMS_HANDLED; 508 } 509 510 return dispatchMessageRadioSpecific(smsb); 511 } 512 513 /** 514 * Process voicemail notification, SMS-PP data download, CDMA CMAS, CDMA WAP push, and other 515 * 3GPP/3GPP2-specific messages. Regular SMS messages are handled by calling the shared 516 * {@link #dispatchNormalMessage} from this class. 517 * 518 * @param smsb the SmsMessageBase object from the RIL 519 * @return a result code from {@link android.provider.Telephony.Sms.Intents}, 520 * or {@link Activity#RESULT_OK} for delayed acknowledgment to SMSC 521 */ 522 protected abstract int dispatchMessageRadioSpecific(SmsMessageBase smsb); 523 524 /** 525 * Send an acknowledge message to the SMSC. 526 * @param success indicates that last message was successfully received. 527 * @param result result code indicating any error 528 * @param response callback message sent when operation completes. 529 */ 530 protected abstract void acknowledgeLastIncomingSms(boolean success, 531 int result, Message response); 532 533 /** 534 * Called when the phone changes the default method updates mPhone 535 * mStorageMonitor and mCellBroadcastHandler.updatePhoneObject. 536 * Override if different or other behavior is desired. 537 * 538 * @param phone 539 */ 540 protected void onUpdatePhoneObject(PhoneBase phone) { 541 mPhone = phone; 542 mStorageMonitor = mPhone.mSmsStorageMonitor; 543 log("onUpdatePhoneObject: phone=" + mPhone.getClass().getSimpleName()); 544 } 545 546 /** 547 * Notify interested apps if the framework has rejected an incoming SMS, 548 * and send an acknowledge message to the network. 549 * @param success indicates that last message was successfully received. 550 * @param result result code indicating any error 551 * @param response callback message sent when operation completes. 552 */ 553 void notifyAndAcknowledgeLastIncomingSms(boolean success, 554 int result, Message response) { 555 if (!success) { 556 // broadcast SMS_REJECTED_ACTION intent 557 Intent intent = new Intent(Intents.SMS_REJECTED_ACTION); 558 intent.putExtra("result", result); 559 mContext.sendBroadcast(intent, android.Manifest.permission.RECEIVE_SMS); 560 } 561 acknowledgeLastIncomingSms(success, result, response); 562 } 563 564 /** 565 * Return true if this handler is for 3GPP2 messages; false for 3GPP format. 566 * @return true for the 3GPP2 handler; false for the 3GPP handler 567 */ 568 protected abstract boolean is3gpp2(); 569 570 /** 571 * Dispatch a normal incoming SMS. This is called from {@link #dispatchMessageRadioSpecific} 572 * if no format-specific handling was required. Saves the PDU to the SMS provider raw table, 573 * creates an {@link InboundSmsTracker}, then sends it to the state machine as an 574 * {@link #EVENT_BROADCAST_SMS}. Returns {@link Intents#RESULT_SMS_HANDLED} or an error value. 575 * 576 * @param sms the message to dispatch 577 * @return {@link Intents#RESULT_SMS_HANDLED} if the message was accepted, or an error status 578 */ 579 protected int dispatchNormalMessage(SmsMessageBase sms) { 580 SmsHeader smsHeader = sms.getUserDataHeader(); 581 InboundSmsTracker tracker; 582 583 if ((smsHeader == null) || (smsHeader.concatRef == null)) { 584 // Message is not concatenated. 585 int destPort = -1; 586 if (smsHeader != null && smsHeader.portAddrs != null) { 587 // The message was sent to a port. 588 destPort = smsHeader.portAddrs.destPort; 589 if (DBG) log("destination port: " + destPort); 590 } 591 592 tracker = new InboundSmsTracker(sms.getPdu(), sms.getTimestampMillis(), destPort, 593 is3gpp2(), false); 594 } else { 595 // Create a tracker for this message segment. 596 SmsHeader.ConcatRef concatRef = smsHeader.concatRef; 597 SmsHeader.PortAddrs portAddrs = smsHeader.portAddrs; 598 int destPort = (portAddrs != null ? portAddrs.destPort : -1); 599 600 tracker = new InboundSmsTracker(sms.getPdu(), sms.getTimestampMillis(), destPort, 601 is3gpp2(), sms.getOriginatingAddress(), concatRef.refNumber, 602 concatRef.seqNumber, concatRef.msgCount, false); 603 } 604 605 if (VDBG) log("created tracker: " + tracker); 606 return addTrackerToRawTableAndSendMessage(tracker); 607 } 608 609 /** 610 * Helper to add the tracker to the raw table and then send a message to broadcast it, if 611 * successful. Returns the SMS intent status to return to the SMSC. 612 * @param tracker the tracker to save to the raw table and then deliver 613 * @return {@link Intents#RESULT_SMS_HANDLED} or {@link Intents#RESULT_SMS_GENERIC_ERROR} 614 * or {@link Intents#RESULT_SMS_DUPLICATED} 615 */ 616 protected int addTrackerToRawTableAndSendMessage(InboundSmsTracker tracker) { 617 switch(addTrackerToRawTable(tracker)) { 618 case Intents.RESULT_SMS_HANDLED: 619 sendMessage(EVENT_BROADCAST_SMS, tracker); 620 return Intents.RESULT_SMS_HANDLED; 621 622 case Intents.RESULT_SMS_DUPLICATED: 623 return Intents.RESULT_SMS_HANDLED; 624 625 case Intents.RESULT_SMS_GENERIC_ERROR: 626 default: 627 return Intents.RESULT_SMS_GENERIC_ERROR; 628 } 629 } 630 631 /** 632 * Process the inbound SMS segment. If the message is complete, send it as an ordered 633 * broadcast to interested receivers and return true. If the message is a segment of an 634 * incomplete multi-part SMS, return false. 635 * @param tracker the tracker containing the message segment to process 636 * @return true if an ordered broadcast was sent; false if waiting for more message segments 637 */ 638 boolean processMessagePart(InboundSmsTracker tracker) { 639 int messageCount = tracker.getMessageCount(); 640 byte[][] pdus; 641 int destPort = tracker.getDestPort(); 642 643 if (messageCount == 1) { 644 // single-part message 645 pdus = new byte[][]{tracker.getPdu()}; 646 } else { 647 // multi-part message 648 Cursor cursor = null; 649 try { 650 // used by several query selection arguments 651 String address = tracker.getAddress(); 652 String refNumber = Integer.toString(tracker.getReferenceNumber()); 653 String count = Integer.toString(tracker.getMessageCount()); 654 655 // query for all segments and broadcast message if we have all the parts 656 String[] whereArgs = {address, refNumber, count}; 657 cursor = mResolver.query(sRawUri, PDU_SEQUENCE_PORT_PROJECTION, 658 SELECT_BY_REFERENCE, whereArgs, null); 659 660 int cursorCount = cursor.getCount(); 661 if (cursorCount < messageCount) { 662 // Wait for the other message parts to arrive. It's also possible for the last 663 // segment to arrive before processing the EVENT_BROADCAST_SMS for one of the 664 // earlier segments. In that case, the broadcast will be sent as soon as all 665 // segments are in the table, and any later EVENT_BROADCAST_SMS messages will 666 // get a row count of 0 and return. 667 return false; 668 } 669 670 // All the parts are in place, deal with them 671 pdus = new byte[messageCount][]; 672 while (cursor.moveToNext()) { 673 // subtract offset to convert sequence to 0-based array index 674 int index = cursor.getInt(SEQUENCE_COLUMN) - tracker.getIndexOffset(); 675 676 pdus[index] = HexDump.hexStringToByteArray(cursor.getString(PDU_COLUMN)); 677 678 // Read the destination port from the first segment (needed for CDMA WAP PDU). 679 // It's not a bad idea to prefer the port from the first segment in other cases. 680 if (index == 0 && !cursor.isNull(DESTINATION_PORT_COLUMN)) { 681 int port = cursor.getInt(DESTINATION_PORT_COLUMN); 682 // strip format flags and convert to real port number, or -1 683 port = InboundSmsTracker.getRealDestPort(port); 684 if (port != -1) { 685 destPort = port; 686 } 687 } 688 } 689 } catch (SQLException e) { 690 loge("Can't access multipart SMS database", e); 691 return false; 692 } finally { 693 if (cursor != null) { 694 cursor.close(); 695 } 696 } 697 } 698 699 BroadcastReceiver resultReceiver = new SmsBroadcastReceiver(tracker); 700 701 if (destPort == SmsHeader.PORT_WAP_PUSH) { 702 // Build up the data stream 703 ByteArrayOutputStream output = new ByteArrayOutputStream(); 704 for (byte[] pdu : pdus) { 705 // 3GPP needs to extract the User Data from the PDU; 3GPP2 has already done this 706 if (!tracker.is3gpp2()) { 707 SmsMessage msg = SmsMessage.createFromPdu(pdu, SmsConstants.FORMAT_3GPP); 708 pdu = msg.getUserData(); 709 } 710 output.write(pdu, 0, pdu.length); 711 } 712 int result = mWapPush.dispatchWapPdu(output.toByteArray(), resultReceiver, this); 713 if (DBG) log("dispatchWapPdu() returned " + result); 714 // result is Activity.RESULT_OK if an ordered broadcast was sent 715 return (result == Activity.RESULT_OK); 716 } 717 718 Intent intent = new Intent(Intents.SMS_FILTER_ACTION); 719 720 // FIX this once the carrier app API is finalized. 721 // We should direct the intent to only the default carrier app. 722 intent.putExtra("destport", destPort); 723 intent.putExtra("pdus", pdus); 724 intent.putExtra("format", tracker.getFormat()); 725 dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS, 726 AppOpsManager.OP_RECEIVE_SMS, resultReceiver); 727 return true; 728 } 729 730 /** 731 * Dispatch the intent with the specified permission, appOp, and result receiver, using 732 * this state machine's handler thread to run the result receiver. 733 * 734 * @param intent the intent to broadcast 735 * @param permission receivers are required to have this permission 736 * @param appOp app op that is being performed when dispatching to a receiver 737 */ 738 protected void dispatchIntent(Intent intent, String permission, int appOp, 739 BroadcastReceiver resultReceiver) { 740 intent.addFlags(Intent.FLAG_RECEIVER_NO_ABORT); 741 SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId()); 742 mContext.sendOrderedBroadcast(intent, permission, appOp, resultReceiver, 743 getHandler(), Activity.RESULT_OK, null, null); 744 } 745 746 /** 747 * Helper for {@link SmsBroadcastUndelivered} to delete an old message in the raw table. 748 */ 749 void deleteFromRawTable(String deleteWhere, String[] deleteWhereArgs) { 750 int rows = mResolver.delete(sRawUri, deleteWhere, deleteWhereArgs); 751 if (rows == 0) { 752 loge("No rows were deleted from raw table!"); 753 } else if (DBG) { 754 log("Deleted " + rows + " rows from raw table."); 755 } 756 } 757 758 /** 759 * Set the appropriate intent action and direct the intent to the default SMS app or the 760 * appropriate port. 761 * 762 * @param intent the intent to set and direct 763 * @param destPort the destination port 764 */ 765 void setAndDirectIntent(Intent intent, int destPort) { 766 if (destPort == -1) { 767 intent.setAction(Intents.SMS_DELIVER_ACTION); 768 769 // Direct the intent to only the default SMS app. If we can't find a default SMS app 770 // then sent it to all broadcast receivers. 771 ComponentName componentName = SmsApplication.getDefaultSmsApplication(mContext, true); 772 if (componentName != null) { 773 // Deliver SMS message only to this receiver. 774 intent.setComponent(componentName); 775 log("Delivering SMS to: " + componentName.getPackageName() + 776 " " + componentName.getClassName()); 777 } else { 778 intent.setComponent(null); 779 } 780 } else { 781 intent.setAction(Intents.DATA_SMS_RECEIVED_ACTION); 782 Uri uri = Uri.parse("sms://localhost:" + destPort); 783 intent.setData(uri); 784 intent.setComponent(null); 785 } 786 } 787 788 /** 789 * Insert a message PDU into the raw table so we can acknowledge it immediately. 790 * If the device crashes before the broadcast to listeners completes, it will be delivered 791 * from the raw table on the next device boot. For single-part messages, the deleteWhere 792 * and deleteWhereArgs fields of the tracker will be set to delete the correct row after 793 * the ordered broadcast completes. 794 * 795 * @param tracker the tracker to add to the raw table 796 * @return true on success; false on failure to write to database 797 */ 798 private int addTrackerToRawTable(InboundSmsTracker tracker) { 799 if (tracker.getMessageCount() != 1) { 800 // check for duplicate message segments 801 Cursor cursor = null; 802 try { 803 // sequence numbers are 1-based except for CDMA WAP, which is 0-based 804 int sequence = tracker.getSequenceNumber(); 805 806 // convert to strings for query 807 String address = tracker.getAddress(); 808 String refNumber = Integer.toString(tracker.getReferenceNumber()); 809 String count = Integer.toString(tracker.getMessageCount()); 810 811 String seqNumber = Integer.toString(sequence); 812 813 // set the delete selection args for multi-part message 814 String[] deleteWhereArgs = {address, refNumber, count}; 815 tracker.setDeleteWhere(SELECT_BY_REFERENCE, deleteWhereArgs); 816 817 // Check for duplicate message segments 818 cursor = mResolver.query(sRawUri, PDU_PROJECTION, 819 "address=? AND reference_number=? AND count=? AND sequence=?", 820 new String[] {address, refNumber, count, seqNumber}, null); 821 822 // moveToNext() returns false if no duplicates were found 823 if (cursor.moveToNext()) { 824 loge("Discarding duplicate message segment, refNumber=" + refNumber 825 + " seqNumber=" + seqNumber); 826 String oldPduString = cursor.getString(PDU_COLUMN); 827 byte[] pdu = tracker.getPdu(); 828 byte[] oldPdu = HexDump.hexStringToByteArray(oldPduString); 829 if (!Arrays.equals(oldPdu, tracker.getPdu())) { 830 loge("Warning: dup message segment PDU of length " + pdu.length 831 + " is different from existing PDU of length " + oldPdu.length); 832 } 833 return Intents.RESULT_SMS_DUPLICATED; // reject message 834 } 835 cursor.close(); 836 } catch (SQLException e) { 837 loge("Can't access multipart SMS database", e); 838 return Intents.RESULT_SMS_GENERIC_ERROR; // reject message 839 } finally { 840 if (cursor != null) { 841 cursor.close(); 842 } 843 } 844 } 845 846 ContentValues values = tracker.getContentValues(); 847 848 if (VDBG) log("adding content values to raw table: " + values.toString()); 849 Uri newUri = mResolver.insert(sRawUri, values); 850 if (DBG) log("URI of new row -> " + newUri); 851 852 try { 853 long rowId = ContentUris.parseId(newUri); 854 if (tracker.getMessageCount() == 1) { 855 // set the delete selection args for single-part message 856 tracker.setDeleteWhere(SELECT_BY_ID, new String[]{Long.toString(rowId)}); 857 } 858 return Intents.RESULT_SMS_HANDLED; 859 } catch (Exception e) { 860 loge("error parsing URI for new row: " + newUri, e); 861 return Intents.RESULT_SMS_GENERIC_ERROR; 862 } 863 } 864 865 /** 866 * Returns whether the default message format for the current radio technology is 3GPP2. 867 * @return true if the radio technology uses 3GPP2 format by default, false for 3GPP format 868 */ 869 static boolean isCurrentFormat3gpp2() { 870 int activePhone = TelephonyManager.getDefault().getCurrentPhoneType(); 871 return (PHONE_TYPE_CDMA == activePhone); 872 } 873 874 /** 875 * Handler for an {@link InboundSmsTracker} broadcast. Deletes PDUs from the raw table and 876 * logs the broadcast duration (as an error if the other receivers were especially slow). 877 */ 878 private final class SmsBroadcastReceiver extends BroadcastReceiver { 879 private final String mDeleteWhere; 880 private final String[] mDeleteWhereArgs; 881 private long mBroadcastTimeNano; 882 883 SmsBroadcastReceiver(InboundSmsTracker tracker) { 884 mDeleteWhere = tracker.getDeleteWhere(); 885 mDeleteWhereArgs = tracker.getDeleteWhereArgs(); 886 mBroadcastTimeNano = System.nanoTime(); 887 } 888 889 @Override 890 public void onReceive(Context context, Intent intent) { 891 String action = intent.getAction(); 892 if (action.equals(Intents.SMS_FILTER_ACTION)) { 893 int rc = getResultCode(); 894 if (rc == Activity.RESULT_OK) { 895 // Overwrite pdus data if the SMS filter has set it. 896 Bundle resultExtras = getResultExtras(false); 897 if (resultExtras != null && resultExtras.containsKey("pdus")) { 898 intent.putExtra("pdus", (byte[][]) resultExtras.get("pdus")); 899 } 900 if (intent.hasExtra("destport")) { 901 int destPort = intent.getIntExtra("destport", -1); 902 intent.removeExtra("destport"); 903 setAndDirectIntent(intent, destPort); 904 final Uri uri = writeInboxMessage(intent); 905 if (uri != null) { 906 // Pass this to SMS apps so that they know where it is stored 907 intent.putExtra("uri", uri.toString()); 908 } 909 dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS, 910 AppOpsManager.OP_RECEIVE_SMS, this); 911 } else { 912 loge("destport doesn't exist in the extras for SMS filter action."); 913 } 914 } else { 915 // Drop this SMS. 916 log("SMS filtered by result code " + rc); 917 deleteFromRawTable(mDeleteWhere, mDeleteWhereArgs); 918 sendMessage(EVENT_BROADCAST_COMPLETE); 919 } 920 } else if (action.equals(Intents.SMS_DELIVER_ACTION)) { 921 // Now dispatch the notification only intent 922 intent.setAction(Intents.SMS_RECEIVED_ACTION); 923 intent.setComponent(null); 924 dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS, 925 AppOpsManager.OP_RECEIVE_SMS, this); 926 } else if (action.equals(Intents.WAP_PUSH_DELIVER_ACTION)) { 927 // Now dispatch the notification only intent 928 intent.setAction(Intents.WAP_PUSH_RECEIVED_ACTION); 929 intent.setComponent(null); 930 dispatchIntent(intent, android.Manifest.permission.RECEIVE_SMS, 931 AppOpsManager.OP_RECEIVE_SMS, this); 932 } else { 933 // Now that the intents have been deleted we can clean up the PDU data. 934 if (!Intents.DATA_SMS_RECEIVED_ACTION.equals(action) 935 && !Intents.SMS_RECEIVED_ACTION.equals(action) 936 && !Intents.DATA_SMS_RECEIVED_ACTION.equals(action) 937 && !Intents.WAP_PUSH_RECEIVED_ACTION.equals(action)) { 938 loge("unexpected BroadcastReceiver action: " + action); 939 } 940 941 int rc = getResultCode(); 942 if ((rc != Activity.RESULT_OK) && (rc != Intents.RESULT_SMS_HANDLED)) { 943 loge("a broadcast receiver set the result code to " + rc 944 + ", deleting from raw table anyway!"); 945 } else if (DBG) { 946 log("successful broadcast, deleting from raw table."); 947 } 948 949 deleteFromRawTable(mDeleteWhere, mDeleteWhereArgs); 950 sendMessage(EVENT_BROADCAST_COMPLETE); 951 952 int durationMillis = (int) ((System.nanoTime() - mBroadcastTimeNano) / 1000000); 953 if (durationMillis >= 5000) { 954 loge("Slow ordered broadcast completion time: " + durationMillis + " ms"); 955 } else if (DBG) { 956 log("ordered broadcast completed in: " + durationMillis + " ms"); 957 } 958 } 959 } 960 } 961 962 /** 963 * Log with debug level. 964 * @param s the string to log 965 */ 966 @Override 967 protected void log(String s) { 968 Rlog.d(getName(), s); 969 } 970 971 /** 972 * Log with error level. 973 * @param s the string to log 974 */ 975 @Override 976 protected void loge(String s) { 977 Rlog.e(getName(), s); 978 } 979 980 /** 981 * Log with error level. 982 * @param s the string to log 983 * @param e is a Throwable which logs additional information. 984 */ 985 @Override 986 protected void loge(String s, Throwable e) { 987 Rlog.e(getName(), s, e); 988 } 989 990 /** 991 * Store a received SMS into Telephony provider 992 * 993 * @param intent The intent containing the received SMS 994 * @return The URI of written message 995 */ 996 private Uri writeInboxMessage(Intent intent) { 997 if (!Telephony.AUTO_PERSIST) { 998 // TODO(ywen): Temporarily only enable this with a flag so not to break existing apps 999 return null; 1000 } 1001 final SmsMessage[] messages = Telephony.Sms.Intents.getMessagesFromIntent(intent); 1002 if (messages == null || messages.length < 1) { 1003 loge("Failed to parse SMS pdu"); 1004 return null; 1005 } 1006 // Sometimes, SmsMessage.mWrappedSmsMessage is null causing NPE when we access 1007 // the methods on it although the SmsMessage itself is not null. So do this check 1008 // before we do anything on the parsed SmsMessages. 1009 for (final SmsMessage sms : messages) { 1010 try { 1011 sms.getDisplayMessageBody(); 1012 } catch (NullPointerException e) { 1013 loge("NPE inside SmsMessage"); 1014 return null; 1015 } 1016 } 1017 final ContentValues values = parseSmsMessage(messages); 1018 final long identity = Binder.clearCallingIdentity(); 1019 try { 1020 return mContext.getContentResolver().insert(Telephony.Sms.Inbox.CONTENT_URI, values); 1021 } catch (Exception e) { 1022 loge("Failed to persist inbox message", e); 1023 } finally { 1024 Binder.restoreCallingIdentity(identity); 1025 } 1026 return null; 1027 } 1028 1029 /** 1030 * Convert SmsMessage[] into SMS database schema columns 1031 * 1032 * @param msgs The SmsMessage array of the received SMS 1033 * @return ContentValues representing the columns of parsed SMS 1034 */ 1035 private static ContentValues parseSmsMessage(SmsMessage[] msgs) { 1036 final SmsMessage sms = msgs[0]; 1037 final ContentValues values = new ContentValues(); 1038 values.put(Telephony.Sms.Inbox.ADDRESS, sms.getDisplayOriginatingAddress()); 1039 values.put(Telephony.Sms.Inbox.BODY, buildMessageBodyFromPdus(msgs)); 1040 values.put(Telephony.Sms.Inbox.DATE_SENT, sms.getTimestampMillis()); 1041 values.put(Telephony.Sms.Inbox.DATE, System.currentTimeMillis()); 1042 values.put(Telephony.Sms.Inbox.PROTOCOL, sms.getProtocolIdentifier()); 1043 values.put(Telephony.Sms.Inbox.SEEN, 0); 1044 values.put(Telephony.Sms.Inbox.READ, 0); 1045 final String subject = sms.getPseudoSubject(); 1046 if (!TextUtils.isEmpty(subject)) { 1047 values.put(Telephony.Sms.Inbox.SUBJECT, subject); 1048 } 1049 values.put(Telephony.Sms.Inbox.REPLY_PATH_PRESENT, sms.isReplyPathPresent() ? 1 : 0); 1050 values.put(Telephony.Sms.Inbox.SERVICE_CENTER, sms.getServiceCenterAddress()); 1051 return values; 1052 } 1053 1054 /** 1055 * Build up the SMS message body from the SmsMessage array of received SMS 1056 * 1057 * @param msgs The SmsMessage array of the received SMS 1058 * @return The text message body 1059 */ 1060 private static String buildMessageBodyFromPdus(SmsMessage[] msgs) { 1061 if (msgs.length == 1) { 1062 // There is only one part, so grab the body directly. 1063 return replaceFormFeeds(msgs[0].getDisplayMessageBody()); 1064 } else { 1065 // Build up the body from the parts. 1066 StringBuilder body = new StringBuilder(); 1067 for (SmsMessage msg: msgs) { 1068 // getDisplayMessageBody() can NPE if mWrappedMessage inside is null. 1069 body.append(msg.getDisplayMessageBody()); 1070 } 1071 return replaceFormFeeds(body.toString()); 1072 } 1073 } 1074 1075 // Some providers send formfeeds in their messages. Convert those formfeeds to newlines. 1076 private static String replaceFormFeeds(String s) { 1077 return s == null ? "" : s.replace('\f', '\n'); 1078 } 1079} 1080