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