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