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