SMSDispatcher.java revision 7d259626d009a45195ec341646ce06658b842e40
1/* 2 * Copyright (C) 2006 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.PendingIntent; 21import android.app.AlertDialog; 22import android.app.PendingIntent.CanceledException; 23import android.content.BroadcastReceiver; 24import android.content.ContentResolver; 25import android.content.ContentValues; 26import android.content.Context; 27import android.content.Intent; 28import android.content.DialogInterface; 29import android.content.IntentFilter; 30import android.content.res.Resources; 31import android.database.Cursor; 32import android.database.SQLException; 33import android.net.Uri; 34import android.os.AsyncResult; 35import android.os.Handler; 36import android.os.Message; 37import android.os.PowerManager; 38import android.os.SystemProperties; 39import android.provider.Telephony; 40import android.provider.Telephony.Sms.Intents; 41import android.provider.Settings; 42import android.telephony.SmsMessage; 43import android.telephony.ServiceState; 44import android.util.Config; 45import android.util.Log; 46import android.view.WindowManager; 47 48import com.android.internal.util.HexDump; 49 50import java.io.ByteArrayOutputStream; 51import java.util.ArrayList; 52import java.util.HashMap; 53import java.util.Random; 54 55import com.android.internal.R; 56 57import static android.telephony.SmsManager.RESULT_ERROR_GENERIC_FAILURE; 58import static android.telephony.SmsManager.RESULT_ERROR_NO_SERVICE; 59import static android.telephony.SmsManager.RESULT_ERROR_NULL_PDU; 60import static android.telephony.SmsManager.RESULT_ERROR_RADIO_OFF; 61import static android.telephony.SmsManager.RESULT_ERROR_LIMIT_EXCEEDED; 62import static android.telephony.SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE; 63 64 65public abstract class SMSDispatcher extends Handler { 66 private static final String TAG = "SMS"; 67 private static final String SEND_NEXT_MSG_EXTRA = "SendNextMsg"; 68 69 /** Default checking period for SMS sent without user permit */ 70 private static final int DEFAULT_SMS_CHECK_PERIOD = 3600000; 71 72 /** Default number of SMS sent in checking period without user permit */ 73 private static final int DEFAULT_SMS_MAX_COUNT = 100; 74 75 /** Default timeout for SMS sent query */ 76 private static final int DEFAULT_SMS_TIMEOUT = 6000; 77 78 protected static final String[] RAW_PROJECTION = new String[] { 79 "pdu", 80 "sequence", 81 "destination_port", 82 }; 83 84 static final protected int EVENT_NEW_SMS = 1; 85 86 static final protected int EVENT_SEND_SMS_COMPLETE = 2; 87 88 /** Retry sending a previously failed SMS message */ 89 static final protected int EVENT_SEND_RETRY = 3; 90 91 /** Status report received */ 92 static final protected int EVENT_NEW_SMS_STATUS_REPORT = 5; 93 94 /** SIM/RUIM storage is full */ 95 static final protected int EVENT_ICC_FULL = 6; 96 97 /** SMS confirm required */ 98 static final protected int EVENT_POST_ALERT = 7; 99 100 /** Send the user confirmed SMS */ 101 static final protected int EVENT_SEND_CONFIRMED_SMS = 8; 102 103 /** Alert is timeout */ 104 static final protected int EVENT_ALERT_TIMEOUT = 9; 105 106 /** Stop the sending */ 107 static final protected int EVENT_STOP_SENDING = 10; 108 109 /** Memory status reporting is acknowledged by RIL */ 110 static final protected int EVENT_REPORT_MEMORY_STATUS_DONE = 11; 111 112 /** Radio is ON */ 113 static final protected int EVENT_RADIO_ON = 12; 114 115 /** New broadcast SMS */ 116 static final protected int EVENT_NEW_BROADCAST_SMS = 13; 117 118 protected Phone mPhone; 119 protected Context mContext; 120 protected ContentResolver mResolver; 121 protected CommandsInterface mCm; 122 123 protected final WapPushOverSms mWapPush; 124 125 protected final Uri mRawUri = Uri.withAppendedPath(Telephony.Sms.CONTENT_URI, "raw"); 126 127 /** Maximum number of times to retry sending a failed SMS. */ 128 private static final int MAX_SEND_RETRIES = 3; 129 /** Delay before next send attempt on a failed SMS, in milliseconds. */ 130 private static final int SEND_RETRY_DELAY = 2000; 131 /** single part SMS */ 132 private static final int SINGLE_PART_SMS = 1; 133 /** Message sending queue limit */ 134 private static final int MO_MSG_QUEUE_LIMIT = 5; 135 136 /** 137 * Message reference for a CONCATENATED_8_BIT_REFERENCE or 138 * CONCATENATED_16_BIT_REFERENCE message set. Should be 139 * incremented for each set of concatenated messages. 140 */ 141 private static int sConcatenatedRef; 142 143 private SmsCounter mCounter; 144 145 private ArrayList<SmsTracker> mSTrackers = new ArrayList<SmsTracker>(MO_MSG_QUEUE_LIMIT); 146 147 /** Wake lock to ensure device stays awake while dispatching the SMS intent. */ 148 private PowerManager.WakeLock mWakeLock; 149 150 /** 151 * Hold the wake lock for 5 seconds, which should be enough time for 152 * any receiver(s) to grab its own wake lock. 153 */ 154 private final int WAKE_LOCK_TIMEOUT = 5000; 155 156 protected boolean mStorageAvailable = true; 157 protected boolean mReportMemoryStatusPending = false; 158 159 /* Flags indicating whether the current device allows sms service */ 160 protected boolean mSmsCapable = true; 161 protected boolean mSmsReceiveDisabled; 162 protected boolean mSmsSendDisabled; 163 164 protected static int mRemainingMessages = -1; 165 166 protected static int getNextConcatenatedRef() { 167 sConcatenatedRef += 1; 168 return sConcatenatedRef; 169 } 170 171 /** 172 * Implement the per-application based SMS control, which only allows 173 * a limit on the number of SMS/MMS messages an app can send in checking 174 * period. 175 */ 176 private class SmsCounter { 177 private int mCheckPeriod; 178 private int mMaxAllowed; 179 private HashMap<String, ArrayList<Long>> mSmsStamp; 180 181 /** 182 * Create SmsCounter 183 * @param mMax is the number of SMS allowed without user permit 184 * @param mPeriod is the checking period 185 */ 186 SmsCounter(int mMax, int mPeriod) { 187 mMaxAllowed = mMax; 188 mCheckPeriod = mPeriod; 189 mSmsStamp = new HashMap<String, ArrayList<Long>> (); 190 } 191 192 /** 193 * Check to see if an application allow to send new SMS messages 194 * 195 * @param appName is the application sending sms 196 * @param smsWaiting is the number of new sms wants to be sent 197 * @return true if application is allowed to send the requested number 198 * of new sms messages 199 */ 200 boolean check(String appName, int smsWaiting) { 201 if (!mSmsStamp.containsKey(appName)) { 202 mSmsStamp.put(appName, new ArrayList<Long>()); 203 } 204 205 return isUnderLimit(mSmsStamp.get(appName), smsWaiting); 206 } 207 208 private boolean isUnderLimit(ArrayList<Long> sent, int smsWaiting) { 209 Long ct = System.currentTimeMillis(); 210 211 Log.d(TAG, "SMS send size=" + sent.size() + "time=" + ct); 212 213 while (sent.size() > 0 && (ct - sent.get(0)) > mCheckPeriod ) { 214 sent.remove(0); 215 } 216 217 218 if ( (sent.size() + smsWaiting) <= mMaxAllowed) { 219 for (int i = 0; i < smsWaiting; i++ ) { 220 sent.add(ct); 221 } 222 return true; 223 } 224 return false; 225 } 226 } 227 228 protected SMSDispatcher(PhoneBase phone) { 229 mPhone = phone; 230 mWapPush = new WapPushOverSms(phone, this); 231 mContext = phone.getContext(); 232 mResolver = mContext.getContentResolver(); 233 mCm = phone.mCM; 234 235 createWakelock(); 236 237 int check_period = Settings.Secure.getInt(mResolver, 238 Settings.Secure.SMS_OUTGOING_CHECK_INTERVAL_MS, 239 DEFAULT_SMS_CHECK_PERIOD); 240 int max_count = Settings.Secure.getInt(mResolver, 241 Settings.Secure.SMS_OUTGOING_CHECK_MAX_COUNT, 242 DEFAULT_SMS_MAX_COUNT); 243 mCounter = new SmsCounter(max_count, check_period); 244 245 mCm.setOnNewSMS(this, EVENT_NEW_SMS, null); 246 mCm.setOnSmsStatus(this, EVENT_NEW_SMS_STATUS_REPORT, null); 247 mCm.setOnIccSmsFull(this, EVENT_ICC_FULL, null); 248 mCm.registerForOn(this, EVENT_RADIO_ON, null); 249 250 // Don't always start message ref at 0. 251 sConcatenatedRef = new Random().nextInt(256); 252 253 // Register for device storage intents. Use these to notify the RIL 254 // that storage for SMS is or is not available. 255 IntentFilter filter = new IntentFilter(); 256 filter.addAction(Intent.ACTION_DEVICE_STORAGE_FULL); 257 filter.addAction(Intent.ACTION_DEVICE_STORAGE_NOT_FULL); 258 mContext.registerReceiver(mResultReceiver, filter); 259 260 mSmsCapable = mContext.getResources().getBoolean( 261 com.android.internal.R.bool.config_sms_capable); 262 mSmsReceiveDisabled = !SystemProperties.getBoolean( 263 TelephonyProperties.PROPERTY_SMS_RECEIVE, mSmsCapable); 264 mSmsSendDisabled = !SystemProperties.getBoolean( 265 TelephonyProperties.PROPERTY_SMS_SEND, mSmsCapable); 266 Log.d(TAG, "SMSDispatcher: ctor mSmsCapable=" + mSmsCapable 267 + " mSmsReceiveDisabled=" + mSmsReceiveDisabled 268 + " mSmsSendDisabled=" + mSmsSendDisabled); 269 } 270 271 public void dispose() { 272 mCm.unSetOnNewSMS(this); 273 mCm.unSetOnSmsStatus(this); 274 mCm.unSetOnIccSmsFull(this); 275 mCm.unregisterForOn(this); 276 } 277 278 @Override 279 protected void finalize() { 280 Log.d(TAG, "SMSDispatcher finalized"); 281 } 282 283 284 /* TODO: Need to figure out how to keep track of status report routing in a 285 * persistent manner. If the phone process restarts (reboot or crash), 286 * we will lose this list and any status reports that come in after 287 * will be dropped. 288 */ 289 /** Sent messages awaiting a delivery status report. */ 290 protected final ArrayList<SmsTracker> deliveryPendingList = new ArrayList<SmsTracker>(); 291 292 /** 293 * Handles events coming from the phone stack. Overridden from handler. 294 * 295 * @param msg the message to handle 296 */ 297 @Override 298 public void handleMessage(Message msg) { 299 AsyncResult ar; 300 301 switch (msg.what) { 302 case EVENT_NEW_SMS: 303 // A new SMS has been received by the device 304 if (Config.LOGD) { 305 Log.d(TAG, "New SMS Message Received"); 306 } 307 308 SmsMessage sms; 309 310 ar = (AsyncResult) msg.obj; 311 312 if (ar.exception != null) { 313 Log.e(TAG, "Exception processing incoming SMS. Exception:" + ar.exception); 314 return; 315 } 316 317 sms = (SmsMessage) ar.result; 318 try { 319 int result = dispatchMessage(sms.mWrappedSmsMessage); 320 if (result != Activity.RESULT_OK) { 321 // RESULT_OK means that message was broadcast for app(s) to handle. 322 // Any other result, we should ack here. 323 boolean handled = (result == Intents.RESULT_SMS_HANDLED); 324 notifyAndAcknowledgeLastIncomingSms(handled, result, null); 325 } 326 } catch (RuntimeException ex) { 327 Log.e(TAG, "Exception dispatching message", ex); 328 notifyAndAcknowledgeLastIncomingSms(false, Intents.RESULT_SMS_GENERIC_ERROR, null); 329 } 330 331 break; 332 333 case EVENT_SEND_SMS_COMPLETE: 334 // An outbound SMS has been successfully transferred, or failed. 335 handleSendComplete((AsyncResult) msg.obj); 336 break; 337 338 case EVENT_SEND_RETRY: 339 sendSms((SmsTracker) msg.obj); 340 break; 341 342 case EVENT_NEW_SMS_STATUS_REPORT: 343 handleStatusReport((AsyncResult)msg.obj); 344 break; 345 346 case EVENT_ICC_FULL: 347 handleIccFull(); 348 break; 349 350 case EVENT_POST_ALERT: 351 handleReachSentLimit((SmsTracker)(msg.obj)); 352 break; 353 354 case EVENT_ALERT_TIMEOUT: 355 ((AlertDialog)(msg.obj)).dismiss(); 356 msg.obj = null; 357 if (mSTrackers.isEmpty() == false) { 358 try { 359 SmsTracker sTracker = mSTrackers.remove(0); 360 sTracker.mSentIntent.send(RESULT_ERROR_LIMIT_EXCEEDED); 361 } catch (CanceledException ex) { 362 Log.e(TAG, "failed to send back RESULT_ERROR_LIMIT_EXCEEDED"); 363 } 364 } 365 if (Config.LOGD) { 366 Log.d(TAG, "EVENT_ALERT_TIMEOUT, message stop sending"); 367 } 368 break; 369 370 case EVENT_SEND_CONFIRMED_SMS: 371 if (mSTrackers.isEmpty() == false) { 372 SmsTracker sTracker = mSTrackers.remove(mSTrackers.size() - 1); 373 if (isMultipartTracker(sTracker)) { 374 sendMultipartSms(sTracker); 375 } else { 376 sendSms(sTracker); 377 } 378 removeMessages(EVENT_ALERT_TIMEOUT, msg.obj); 379 } 380 break; 381 382 case EVENT_STOP_SENDING: 383 if (mSTrackers.isEmpty() == false) { 384 // Remove the latest one. 385 try { 386 SmsTracker sTracker = mSTrackers.remove(mSTrackers.size() - 1); 387 sTracker.mSentIntent.send(RESULT_ERROR_LIMIT_EXCEEDED); 388 } catch (CanceledException ex) { 389 Log.e(TAG, "failed to send back RESULT_ERROR_LIMIT_EXCEEDED"); 390 } 391 removeMessages(EVENT_ALERT_TIMEOUT, msg.obj); 392 } 393 break; 394 395 case EVENT_REPORT_MEMORY_STATUS_DONE: 396 ar = (AsyncResult)msg.obj; 397 if (ar.exception != null) { 398 mReportMemoryStatusPending = true; 399 Log.v(TAG, "Memory status report to modem pending : mStorageAvailable = " 400 + mStorageAvailable); 401 } else { 402 mReportMemoryStatusPending = false; 403 } 404 break; 405 406 case EVENT_RADIO_ON: 407 if (mReportMemoryStatusPending) { 408 Log.v(TAG, "Sending pending memory status report : mStorageAvailable = " 409 + mStorageAvailable); 410 mCm.reportSmsMemoryStatus(mStorageAvailable, 411 obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE)); 412 } 413 break; 414 415 case EVENT_NEW_BROADCAST_SMS: 416 handleBroadcastSms((AsyncResult)msg.obj); 417 break; 418 } 419 } 420 421 private void createWakelock() { 422 PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); 423 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "SMSDispatcher"); 424 mWakeLock.setReferenceCounted(true); 425 } 426 427 /** 428 * Grabs a wake lock and sends intent as an ordered broadcast. 429 * The resultReceiver will check for errors and ACK/NACK back 430 * to the RIL. 431 * 432 * @param intent intent to broadcast 433 * @param permission Receivers are required to have this permission 434 */ 435 void dispatch(Intent intent, String permission) { 436 // Hold a wake lock for WAKE_LOCK_TIMEOUT seconds, enough to give any 437 // receivers time to take their own wake locks. 438 mWakeLock.acquire(WAKE_LOCK_TIMEOUT); 439 mContext.sendOrderedBroadcast(intent, permission, mResultReceiver, 440 this, Activity.RESULT_OK, null, null); 441 } 442 443 /** 444 * Called when SIM_FULL message is received from the RIL. Notifies interested 445 * parties that SIM storage for SMS messages is full. 446 */ 447 private void handleIccFull(){ 448 // broadcast SIM_FULL intent 449 Intent intent = new Intent(Intents.SIM_FULL_ACTION); 450 mWakeLock.acquire(WAKE_LOCK_TIMEOUT); 451 mContext.sendBroadcast(intent, "android.permission.RECEIVE_SMS"); 452 } 453 454 /** 455 * Called when a status report is received. This should correspond to 456 * a previously successful SEND. 457 * 458 * @param ar AsyncResult passed into the message handler. ar.result should 459 * be a String representing the status report PDU, as ASCII hex. 460 */ 461 protected abstract void handleStatusReport(AsyncResult ar); 462 463 /** 464 * Called when SMS send completes. Broadcasts a sentIntent on success. 465 * On failure, either sets up retries or broadcasts a sentIntent with 466 * the failure in the result code. 467 * 468 * @param ar AsyncResult passed into the message handler. ar.result should 469 * an SmsResponse instance if send was successful. ar.userObj 470 * should be an SmsTracker instance. 471 */ 472 protected void handleSendComplete(AsyncResult ar) { 473 SmsTracker tracker = (SmsTracker) ar.userObj; 474 PendingIntent sentIntent = tracker.mSentIntent; 475 476 if (ar.exception == null) { 477 if (Config.LOGD) { 478 Log.d(TAG, "SMS send complete. Broadcasting " 479 + "intent: " + sentIntent); 480 } 481 482 if (tracker.mDeliveryIntent != null) { 483 // Expecting a status report. Add it to the list. 484 int messageRef = ((SmsResponse)ar.result).messageRef; 485 tracker.mMessageRef = messageRef; 486 deliveryPendingList.add(tracker); 487 } 488 489 if (sentIntent != null) { 490 try { 491 if (mRemainingMessages > -1) { 492 mRemainingMessages--; 493 } 494 495 if (mRemainingMessages == 0) { 496 Intent sendNext = new Intent(); 497 sendNext.putExtra(SEND_NEXT_MSG_EXTRA, true); 498 sentIntent.send(mContext, Activity.RESULT_OK, sendNext); 499 } else { 500 sentIntent.send(Activity.RESULT_OK); 501 } 502 } catch (CanceledException ex) {} 503 } 504 } else { 505 if (Config.LOGD) { 506 Log.d(TAG, "SMS send failed"); 507 } 508 509 int ss = mPhone.getServiceState().getState(); 510 511 if (ss != ServiceState.STATE_IN_SERVICE) { 512 handleNotInService(ss, tracker); 513 } else if ((((CommandException)(ar.exception)).getCommandError() 514 == CommandException.Error.SMS_FAIL_RETRY) && 515 tracker.mRetryCount < MAX_SEND_RETRIES) { 516 // Retry after a delay if needed. 517 // TODO: According to TS 23.040, 9.2.3.6, we should resend 518 // with the same TP-MR as the failed message, and 519 // TP-RD set to 1. However, we don't have a means of 520 // knowing the MR for the failed message (EF_SMSstatus 521 // may or may not have the MR corresponding to this 522 // message, depending on the failure). Also, in some 523 // implementations this retry is handled by the baseband. 524 tracker.mRetryCount++; 525 Message retryMsg = obtainMessage(EVENT_SEND_RETRY, tracker); 526 sendMessageDelayed(retryMsg, SEND_RETRY_DELAY); 527 } else if (tracker.mSentIntent != null) { 528 int error = RESULT_ERROR_GENERIC_FAILURE; 529 530 if (((CommandException)(ar.exception)).getCommandError() 531 == CommandException.Error.FDN_CHECK_FAILURE) { 532 error = RESULT_ERROR_FDN_CHECK_FAILURE; 533 } 534 // Done retrying; return an error to the app. 535 try { 536 Intent fillIn = new Intent(); 537 if (ar.result != null) { 538 fillIn.putExtra("errorCode", ((SmsResponse)ar.result).errorCode); 539 } 540 if (mRemainingMessages > -1) { 541 mRemainingMessages--; 542 } 543 544 if (mRemainingMessages == 0) { 545 fillIn.putExtra(SEND_NEXT_MSG_EXTRA, true); 546 } 547 548 tracker.mSentIntent.send(mContext, error, fillIn); 549 } catch (CanceledException ex) {} 550 } 551 } 552 } 553 554 /** 555 * Handles outbound message when the phone is not in service. 556 * 557 * @param ss Current service state. Valid values are: 558 * OUT_OF_SERVICE 559 * EMERGENCY_ONLY 560 * POWER_OFF 561 * @param tracker An SmsTracker for the current message. 562 */ 563 protected void handleNotInService(int ss, SmsTracker tracker) { 564 if (tracker.mSentIntent != null) { 565 try { 566 if (ss == ServiceState.STATE_POWER_OFF) { 567 tracker.mSentIntent.send(RESULT_ERROR_RADIO_OFF); 568 } else { 569 tracker.mSentIntent.send(RESULT_ERROR_NO_SERVICE); 570 } 571 } catch (CanceledException ex) {} 572 } 573 } 574 575 /** 576 * Dispatches an incoming SMS messages. 577 * 578 * @param sms the incoming message from the phone 579 * @return a result code from {@link Telephony.Sms.Intents}, or 580 * {@link Activity#RESULT_OK} if the message has been broadcast 581 * to applications 582 */ 583 public abstract int dispatchMessage(SmsMessageBase sms); 584 585 586 /** 587 * If this is the last part send the parts out to the application, otherwise 588 * the part is stored for later processing. 589 * 590 * NOTE: concatRef (naturally) needs to be non-null, but portAddrs can be null. 591 * @return a result code from {@link Telephony.Sms.Intents}, or 592 * {@link Activity#RESULT_OK} if the message has been broadcast 593 * to applications 594 */ 595 protected int processMessagePart(SmsMessageBase sms, 596 SmsHeader.ConcatRef concatRef, SmsHeader.PortAddrs portAddrs) { 597 598 // Lookup all other related parts 599 StringBuilder where = new StringBuilder("reference_number ="); 600 where.append(concatRef.refNumber); 601 where.append(" AND address = ?"); 602 String[] whereArgs = new String[] {sms.getOriginatingAddress()}; 603 604 byte[][] pdus = null; 605 Cursor cursor = null; 606 try { 607 cursor = mResolver.query(mRawUri, RAW_PROJECTION, where.toString(), whereArgs, null); 608 int cursorCount = cursor.getCount(); 609 if (cursorCount != concatRef.msgCount - 1) { 610 // We don't have all the parts yet, store this one away 611 ContentValues values = new ContentValues(); 612 values.put("date", new Long(sms.getTimestampMillis())); 613 values.put("pdu", HexDump.toHexString(sms.getPdu())); 614 values.put("address", sms.getOriginatingAddress()); 615 values.put("reference_number", concatRef.refNumber); 616 values.put("count", concatRef.msgCount); 617 values.put("sequence", concatRef.seqNumber); 618 if (portAddrs != null) { 619 values.put("destination_port", portAddrs.destPort); 620 } 621 mResolver.insert(mRawUri, values); 622 return Intents.RESULT_SMS_HANDLED; 623 } 624 625 // All the parts are in place, deal with them 626 int pduColumn = cursor.getColumnIndex("pdu"); 627 int sequenceColumn = cursor.getColumnIndex("sequence"); 628 629 pdus = new byte[concatRef.msgCount][]; 630 for (int i = 0; i < cursorCount; i++) { 631 cursor.moveToNext(); 632 int cursorSequence = (int)cursor.getLong(sequenceColumn); 633 pdus[cursorSequence - 1] = HexDump.hexStringToByteArray( 634 cursor.getString(pduColumn)); 635 } 636 // This one isn't in the DB, so add it 637 pdus[concatRef.seqNumber - 1] = sms.getPdu(); 638 639 // Remove the parts from the database 640 mResolver.delete(mRawUri, where.toString(), whereArgs); 641 } catch (SQLException e) { 642 Log.e(TAG, "Can't access multipart SMS database", e); 643 // TODO: Would OUT_OF_MEMORY be more appropriate? 644 return Intents.RESULT_SMS_GENERIC_ERROR; 645 } finally { 646 if (cursor != null) cursor.close(); 647 } 648 649 /** 650 * TODO(cleanup): The following code has duplicated logic with 651 * the radio-specific dispatchMessage code, which is fragile, 652 * in addition to being redundant. Instead, if this method 653 * maybe returned the reassembled message (or just contents), 654 * the following code (which is not really related to 655 * reconstruction) could be better consolidated. 656 */ 657 658 // Dispatch the PDUs to applications 659 if (portAddrs != null) { 660 if (portAddrs.destPort == SmsHeader.PORT_WAP_PUSH) { 661 // Build up the data stream 662 ByteArrayOutputStream output = new ByteArrayOutputStream(); 663 for (int i = 0; i < concatRef.msgCount; i++) { 664 SmsMessage msg = SmsMessage.createFromPdu(pdus[i]); 665 byte[] data = msg.getUserData(); 666 output.write(data, 0, data.length); 667 } 668 // Handle the PUSH 669 return mWapPush.dispatchWapPdu(output.toByteArray()); 670 } else { 671 // The messages were sent to a port, so concoct a URI for it 672 dispatchPortAddressedPdus(pdus, portAddrs.destPort); 673 } 674 } else { 675 // The messages were not sent to a port 676 dispatchPdus(pdus); 677 } 678 return Activity.RESULT_OK; 679 } 680 681 /** 682 * Dispatches standard PDUs to interested applications 683 * 684 * @param pdus The raw PDUs making up the message 685 */ 686 protected void dispatchPdus(byte[][] pdus) { 687 Intent intent = new Intent(Intents.SMS_RECEIVED_ACTION); 688 intent.putExtra("pdus", pdus); 689 dispatch(intent, "android.permission.RECEIVE_SMS"); 690 } 691 692 /** 693 * Dispatches port addressed PDUs to interested applications 694 * 695 * @param pdus The raw PDUs making up the message 696 * @param port The destination port of the messages 697 */ 698 protected void dispatchPortAddressedPdus(byte[][] pdus, int port) { 699 Uri uri = Uri.parse("sms://localhost:" + port); 700 Intent intent = new Intent(Intents.DATA_SMS_RECEIVED_ACTION, uri); 701 intent.putExtra("pdus", pdus); 702 dispatch(intent, "android.permission.RECEIVE_SMS"); 703 } 704 705 /** 706 * Send a data based SMS to a specific application port. 707 * 708 * @param destAddr the address to send the message to 709 * @param scAddr is the service center address or null to use 710 * the current default SMSC 711 * @param destPort the port to deliver the message to 712 * @param data the body of the message to send 713 * @param sentIntent if not NULL this <code>PendingIntent</code> is 714 * broadcast when the message is successfully sent, or failed. 715 * The result code will be <code>Activity.RESULT_OK<code> for success, 716 * or one of these errors:<br> 717 * <code>RESULT_ERROR_GENERIC_FAILURE</code><br> 718 * <code>RESULT_ERROR_RADIO_OFF</code><br> 719 * <code>RESULT_ERROR_NULL_PDU</code><br> 720 * <code>RESULT_ERROR_NO_SERVICE</code><br>. 721 * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include 722 * the extra "errorCode" containing a radio technology specific value, 723 * generally only useful for troubleshooting.<br> 724 * The per-application based SMS control checks sentIntent. If sentIntent 725 * is NULL the caller will be checked against all unknown applications, 726 * which cause smaller number of SMS to be sent in checking period. 727 * @param deliveryIntent if not NULL this <code>PendingIntent</code> is 728 * broadcast when the message is delivered to the recipient. The 729 * raw pdu of the status report is in the extended data ("pdu"). 730 */ 731 protected abstract void sendData(String destAddr, String scAddr, int destPort, 732 byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent); 733 734 /** 735 * Send a text based SMS. 736 * 737 * @param destAddr the address to send the message to 738 * @param scAddr is the service center address or null to use 739 * the current default SMSC 740 * @param text the body of the message to send 741 * @param sentIntent if not NULL this <code>PendingIntent</code> is 742 * broadcast when the message is successfully sent, or failed. 743 * The result code will be <code>Activity.RESULT_OK<code> for success, 744 * or one of these errors:<br> 745 * <code>RESULT_ERROR_GENERIC_FAILURE</code><br> 746 * <code>RESULT_ERROR_RADIO_OFF</code><br> 747 * <code>RESULT_ERROR_NULL_PDU</code><br> 748 * <code>RESULT_ERROR_NO_SERVICE</code><br>. 749 * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include 750 * the extra "errorCode" containing a radio technology specific value, 751 * generally only useful for troubleshooting.<br> 752 * The per-application based SMS control checks sentIntent. If sentIntent 753 * is NULL the caller will be checked against all unknown applications, 754 * which cause smaller number of SMS to be sent in checking period. 755 * @param deliveryIntent if not NULL this <code>PendingIntent</code> is 756 * broadcast when the message is delivered to the recipient. The 757 * raw pdu of the status report is in the extended data ("pdu"). 758 */ 759 protected abstract void sendText(String destAddr, String scAddr, 760 String text, PendingIntent sentIntent, PendingIntent deliveryIntent); 761 762 /** 763 * Send a multi-part text based SMS. 764 * 765 * @param destAddr the address to send the message to 766 * @param scAddr is the service center address or null to use 767 * the current default SMSC 768 * @param parts an <code>ArrayList</code> of strings that, in order, 769 * comprise the original message 770 * @param sentIntents if not null, an <code>ArrayList</code> of 771 * <code>PendingIntent</code>s (one for each message part) that is 772 * broadcast when the corresponding message part has been sent. 773 * The result code will be <code>Activity.RESULT_OK<code> for success, 774 * or one of these errors: 775 * <code>RESULT_ERROR_GENERIC_FAILURE</code> 776 * <code>RESULT_ERROR_RADIO_OFF</code> 777 * <code>RESULT_ERROR_NULL_PDU</code> 778 * <code>RESULT_ERROR_NO_SERVICE</code>. 779 * The per-application based SMS control checks sentIntent. If sentIntent 780 * is NULL the caller will be checked against all unknown applications, 781 * which cause smaller number of SMS to be sent in checking period. 782 * @param deliveryIntents if not null, an <code>ArrayList</code> of 783 * <code>PendingIntent</code>s (one for each message part) that is 784 * broadcast when the corresponding message part has been delivered 785 * to the recipient. The raw pdu of the status report is in the 786 * extended data ("pdu"). 787 */ 788 protected abstract void sendMultipartText(String destAddr, String scAddr, 789 ArrayList<String> parts, ArrayList<PendingIntent> sentIntents, 790 ArrayList<PendingIntent> deliveryIntents); 791 792 /** 793 * Send a SMS 794 * 795 * @param smsc the SMSC to send the message through, or NULL for the 796 * default SMSC 797 * @param pdu the raw PDU to send 798 * @param sentIntent if not NULL this <code>Intent</code> is 799 * broadcast when the message is successfully sent, or failed. 800 * The result code will be <code>Activity.RESULT_OK<code> for success, 801 * or one of these errors: 802 * <code>RESULT_ERROR_GENERIC_FAILURE</code> 803 * <code>RESULT_ERROR_RADIO_OFF</code> 804 * <code>RESULT_ERROR_NULL_PDU</code> 805 * <code>RESULT_ERROR_NO_SERVICE</code>. 806 * The per-application based SMS control checks sentIntent. If sentIntent 807 * is NULL the caller will be checked against all unknown applications, 808 * which cause smaller number of SMS to be sent in checking period. 809 * @param deliveryIntent if not NULL this <code>Intent</code> is 810 * broadcast when the message is delivered to the recipient. The 811 * raw pdu of the status report is in the extended data ("pdu"). 812 */ 813 protected void sendRawPdu(byte[] smsc, byte[] pdu, PendingIntent sentIntent, 814 PendingIntent deliveryIntent) { 815 if (mSmsSendDisabled) { 816 if (sentIntent != null) { 817 try { 818 sentIntent.send(RESULT_ERROR_NO_SERVICE); 819 } catch (CanceledException ex) {} 820 } 821 Log.d(TAG, "Device does not support sending sms."); 822 return; 823 } 824 825 if (pdu == null) { 826 if (sentIntent != null) { 827 try { 828 sentIntent.send(RESULT_ERROR_NULL_PDU); 829 } catch (CanceledException ex) {} 830 } 831 return; 832 } 833 834 HashMap<String, Object> map = new HashMap<String, Object>(); 835 map.put("smsc", smsc); 836 map.put("pdu", pdu); 837 838 SmsTracker tracker = new SmsTracker(map, sentIntent, 839 deliveryIntent); 840 int ss = mPhone.getServiceState().getState(); 841 842 if (ss != ServiceState.STATE_IN_SERVICE) { 843 handleNotInService(ss, tracker); 844 } else { 845 String appName = getAppNameByIntent(sentIntent); 846 if (mCounter.check(appName, SINGLE_PART_SMS)) { 847 sendSms(tracker); 848 } else { 849 sendMessage(obtainMessage(EVENT_POST_ALERT, tracker)); 850 } 851 } 852 } 853 854 /** 855 * Post an alert while SMS needs user confirm. 856 * 857 * An SmsTracker for the current message. 858 */ 859 protected void handleReachSentLimit(SmsTracker tracker) { 860 if (mSTrackers.size() >= MO_MSG_QUEUE_LIMIT) { 861 // Deny the sending when the queue limit is reached. 862 try { 863 tracker.mSentIntent.send(RESULT_ERROR_LIMIT_EXCEEDED); 864 } catch (CanceledException ex) { 865 Log.e(TAG, "failed to send back RESULT_ERROR_LIMIT_EXCEEDED"); 866 } 867 return; 868 } 869 870 Resources r = Resources.getSystem(); 871 872 String appName = getAppNameByIntent(tracker.mSentIntent); 873 874 AlertDialog d = new AlertDialog.Builder(mContext) 875 .setTitle(r.getString(R.string.sms_control_title)) 876 .setMessage(appName + " " + r.getString(R.string.sms_control_message)) 877 .setPositiveButton(r.getString(R.string.sms_control_yes), mListener) 878 .setNegativeButton(r.getString(R.string.sms_control_no), mListener) 879 .create(); 880 881 d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); 882 d.show(); 883 884 mSTrackers.add(tracker); 885 sendMessageDelayed ( obtainMessage(EVENT_ALERT_TIMEOUT, d), 886 DEFAULT_SMS_TIMEOUT); 887 } 888 889 protected String getAppNameByIntent(PendingIntent intent) { 890 Resources r = Resources.getSystem(); 891 return (intent != null) ? intent.getTargetPackage() 892 : r.getString(R.string.sms_control_default_app_name); 893 } 894 895 /** 896 * Send the message along to the radio. 897 * 898 * @param tracker holds the SMS message to send 899 */ 900 protected abstract void sendSms(SmsTracker tracker); 901 902 /** 903 * Send the multi-part SMS based on multipart Sms tracker 904 * 905 * @param tracker holds the multipart Sms tracker ready to be sent 906 */ 907 protected abstract void sendMultipartSms (SmsTracker tracker); 908 909 /** 910 * Send an acknowledge message. 911 * @param success indicates that last message was successfully received. 912 * @param result result code indicating any error 913 * @param response callback message sent when operation completes. 914 */ 915 protected abstract void acknowledgeLastIncomingSms(boolean success, 916 int result, Message response); 917 918 /** 919 * Notify interested apps if the framework has rejected an incoming SMS, 920 * and send an acknowledge message to the network. 921 * @param success indicates that last message was successfully received. 922 * @param result result code indicating any error 923 * @param response callback message sent when operation completes. 924 */ 925 private void notifyAndAcknowledgeLastIncomingSms(boolean success, 926 int result, Message response) { 927 if (!success) { 928 // broadcast SMS_REJECTED_ACTION intent 929 Intent intent = new Intent(Intents.SMS_REJECTED_ACTION); 930 intent.putExtra("result", result); 931 mWakeLock.acquire(WAKE_LOCK_TIMEOUT); 932 mContext.sendBroadcast(intent, "android.permission.RECEIVE_SMS"); 933 } 934 acknowledgeLastIncomingSms(success, result, response); 935 } 936 937 /** 938 * Check if a SmsTracker holds multi-part Sms 939 * 940 * @param tracker a SmsTracker could hold a multi-part Sms 941 * @return true for tracker holds Multi-parts Sms 942 */ 943 private boolean isMultipartTracker (SmsTracker tracker) { 944 HashMap map = tracker.mData; 945 return ( map.get("parts") != null); 946 } 947 948 /** 949 * Keeps track of an SMS that has been sent to the RIL, until it has 950 * successfully been sent, or we're done trying. 951 * 952 */ 953 static protected class SmsTracker { 954 // fields need to be public for derived SmsDispatchers 955 public HashMap<String, Object> mData; 956 public int mRetryCount; 957 public int mMessageRef; 958 959 public PendingIntent mSentIntent; 960 public PendingIntent mDeliveryIntent; 961 962 SmsTracker(HashMap<String, Object> data, PendingIntent sentIntent, 963 PendingIntent deliveryIntent) { 964 mData = data; 965 mSentIntent = sentIntent; 966 mDeliveryIntent = deliveryIntent; 967 mRetryCount = 0; 968 } 969 } 970 971 protected SmsTracker SmsTrackerFactory(HashMap<String, Object> data, PendingIntent sentIntent, 972 PendingIntent deliveryIntent) { 973 return new SmsTracker(data, sentIntent, deliveryIntent); 974 } 975 976 public void initSipStack(boolean isObg) { 977 // This function should be overridden by the classes that support 978 // switching modes such as the CdmaSMSDispatcher. 979 // Not implemented in GsmSMSDispatcher. 980 Log.e(TAG, "Error! This function should never be executed."); 981 } 982 983 public void switchToCdma() { 984 // This function should be overridden by the classes that support 985 // switching modes such as the CdmaSMSDispatcher. 986 // Not implemented in GsmSMSDispatcher. 987 Log.e(TAG, "Error! This function should never be executed."); 988 } 989 990 public void switchToGsm() { 991 // This function should be overridden by the classes that support 992 // switching modes such as the CdmaSMSDispatcher. 993 // Not implemented in GsmSMSDispatcher. 994 Log.e(TAG, "Error! This function should never be executed."); 995 } 996 997 private DialogInterface.OnClickListener mListener = 998 new DialogInterface.OnClickListener() { 999 1000 public void onClick(DialogInterface dialog, int which) { 1001 if (which == DialogInterface.BUTTON_POSITIVE) { 1002 Log.d(TAG, "click YES to send out sms"); 1003 sendMessage(obtainMessage(EVENT_SEND_CONFIRMED_SMS)); 1004 } else if (which == DialogInterface.BUTTON_NEGATIVE) { 1005 Log.d(TAG, "click NO to stop sending"); 1006 sendMessage(obtainMessage(EVENT_STOP_SENDING)); 1007 } 1008 } 1009 }; 1010 1011 private BroadcastReceiver mResultReceiver = new BroadcastReceiver() { 1012 @Override 1013 public void onReceive(Context context, Intent intent) { 1014 if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_FULL)) { 1015 mStorageAvailable = false; 1016 mCm.reportSmsMemoryStatus(false, obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE)); 1017 } else if (intent.getAction().equals(Intent.ACTION_DEVICE_STORAGE_NOT_FULL)) { 1018 mStorageAvailable = true; 1019 mCm.reportSmsMemoryStatus(true, obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE)); 1020 } else { 1021 // Assume the intent is one of the SMS receive intents that 1022 // was sent as an ordered broadcast. Check result and ACK. 1023 int rc = getResultCode(); 1024 boolean success = (rc == Activity.RESULT_OK) 1025 || (rc == Intents.RESULT_SMS_HANDLED); 1026 1027 // For a multi-part message, this only ACKs the last part. 1028 // Previous parts were ACK'd as they were received. 1029 acknowledgeLastIncomingSms(success, rc, null); 1030 } 1031 } 1032 }; 1033 1034 protected abstract void handleBroadcastSms(AsyncResult ar); 1035 1036 protected void dispatchBroadcastPdus(byte[][] pdus, boolean isEmergencyMessage) { 1037 if (isEmergencyMessage) { 1038 Intent intent = new Intent(Intents.SMS_EMERGENCY_CB_RECEIVED_ACTION); 1039 intent.putExtra("pdus", pdus); 1040 if (Config.LOGD) 1041 Log.d(TAG, "Dispatching " + pdus.length + " emergency SMS CB pdus"); 1042 1043 dispatch(intent, "android.permission.RECEIVE_EMERGENCY_BROADCAST"); 1044 } else { 1045 Intent intent = new Intent(Intents.SMS_CB_RECEIVED_ACTION); 1046 intent.putExtra("pdus", pdus); 1047 if (Config.LOGD) 1048 Log.d(TAG, "Dispatching " + pdus.length + " SMS CB pdus"); 1049 1050 dispatch(intent, "android.permission.RECEIVE_SMS"); 1051 } 1052 } 1053} 1054