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