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