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