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