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