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.AlertDialog; 21import android.app.PendingIntent; 22import android.app.PendingIntent.CanceledException; 23import android.content.BroadcastReceiver; 24import android.content.ContentResolver; 25import android.content.ContentValues; 26import android.content.Context; 27import android.content.DialogInterface; 28import android.content.Intent; 29import android.content.pm.ApplicationInfo; 30import android.content.pm.PackageInfo; 31import android.content.pm.PackageManager; 32import android.content.res.Resources; 33import android.database.Cursor; 34import android.database.ContentObserver; 35import android.database.SQLException; 36import android.net.Uri; 37import android.os.AsyncResult; 38import android.os.Binder; 39import android.os.Handler; 40import android.os.Message; 41import android.os.PowerManager; 42import android.os.SystemProperties; 43import android.provider.Settings; 44import android.provider.Telephony; 45import android.provider.Telephony.Sms.Intents; 46import android.telephony.PhoneNumberUtils; 47import android.telephony.ServiceState; 48import android.telephony.SmsCbMessage; 49import android.telephony.SmsMessage; 50import android.telephony.TelephonyManager; 51import android.text.Html; 52import android.text.Spanned; 53import android.util.EventLog; 54import android.util.Log; 55import android.view.LayoutInflater; 56import android.view.View; 57import android.view.ViewGroup; 58import android.view.WindowManager; 59import android.widget.Button; 60import android.widget.CheckBox; 61import android.widget.CompoundButton; 62import android.widget.LinearLayout; 63import android.widget.TextView; 64 65import com.android.internal.R; 66import com.android.internal.telephony.EventLogTags; 67import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails; 68import com.android.internal.util.HexDump; 69 70import java.io.ByteArrayOutputStream; 71import java.util.ArrayList; 72import java.util.Arrays; 73import java.util.concurrent.atomic.AtomicInteger; 74import java.util.HashMap; 75import java.util.Random; 76 77import static android.telephony.SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE; 78import static android.telephony.SmsManager.RESULT_ERROR_GENERIC_FAILURE; 79import static android.telephony.SmsManager.RESULT_ERROR_LIMIT_EXCEEDED; 80import static android.telephony.SmsManager.RESULT_ERROR_NO_SERVICE; 81import static android.telephony.SmsManager.RESULT_ERROR_NULL_PDU; 82import static android.telephony.SmsManager.RESULT_ERROR_RADIO_OFF; 83 84public abstract class SMSDispatcher extends Handler { 85 static final String TAG = "SMS"; // accessed from inner class 86 private static final String SEND_NEXT_MSG_EXTRA = "SendNextMsg"; 87 88 /** Permission required to receive SMS and SMS-CB messages. */ 89 public static final String RECEIVE_SMS_PERMISSION = "android.permission.RECEIVE_SMS"; 90 91 /** Permission required to receive ETWS and CMAS emergency broadcasts. */ 92 public static final String RECEIVE_EMERGENCY_BROADCAST_PERMISSION = 93 "android.permission.RECEIVE_EMERGENCY_BROADCAST"; 94 95 /** Permission required to send SMS to short codes without user confirmation. */ 96 private static final String SEND_SMS_NO_CONFIRMATION_PERMISSION = 97 "android.permission.SEND_SMS_NO_CONFIRMATION"; 98 99 /** Query projection for checking for duplicate message segments. */ 100 private static final String[] PDU_PROJECTION = new String[] { 101 "pdu" 102 }; 103 104 /** Query projection for combining concatenated message segments. */ 105 private static final String[] PDU_SEQUENCE_PORT_PROJECTION = new String[] { 106 "pdu", 107 "sequence", 108 "destination_port" 109 }; 110 111 private static final int PDU_COLUMN = 0; 112 private static final int SEQUENCE_COLUMN = 1; 113 private static final int DESTINATION_PORT_COLUMN = 2; 114 115 private static final int PREMIUM_RULE_USE_SIM = 1; 116 private static final int PREMIUM_RULE_USE_NETWORK = 2; 117 private static final int PREMIUM_RULE_USE_BOTH = 3; 118 private final AtomicInteger mPremiumSmsRule = new AtomicInteger(PREMIUM_RULE_USE_SIM); 119 private final SettingsObserver mSettingsObserver; 120 121 /** New SMS received. */ 122 protected static final int EVENT_NEW_SMS = 1; 123 124 /** SMS send complete. */ 125 protected static final int EVENT_SEND_SMS_COMPLETE = 2; 126 127 /** Retry sending a previously failed SMS message */ 128 private static final int EVENT_SEND_RETRY = 3; 129 130 /** Confirmation required for sending a large number of messages. */ 131 private static final int EVENT_SEND_LIMIT_REACHED_CONFIRMATION = 4; 132 133 /** Send the user confirmed SMS */ 134 static final int EVENT_SEND_CONFIRMED_SMS = 5; // accessed from inner class 135 136 /** Don't send SMS (user did not confirm). */ 137 static final int EVENT_STOP_SENDING = 7; // accessed from inner class 138 139 /** Confirmation required for third-party apps sending to an SMS short code. */ 140 private static final int EVENT_CONFIRM_SEND_TO_POSSIBLE_PREMIUM_SHORT_CODE = 8; 141 142 /** Confirmation required for third-party apps sending to an SMS short code. */ 143 private static final int EVENT_CONFIRM_SEND_TO_PREMIUM_SHORT_CODE = 9; 144 145 protected final Phone mPhone; 146 protected final Context mContext; 147 protected final ContentResolver mResolver; 148 protected final CommandsInterface mCm; 149 protected final SmsStorageMonitor mStorageMonitor; 150 protected final TelephonyManager mTelephonyManager; 151 152 protected final WapPushOverSms mWapPush; 153 154 protected static final Uri mRawUri = Uri.withAppendedPath(Telephony.Sms.CONTENT_URI, "raw"); 155 156 /** Maximum number of times to retry sending a failed SMS. */ 157 private static final int MAX_SEND_RETRIES = 3; 158 /** Delay before next send attempt on a failed SMS, in milliseconds. */ 159 private static final int SEND_RETRY_DELAY = 2000; 160 /** single part SMS */ 161 private static final int SINGLE_PART_SMS = 1; 162 /** Message sending queue limit */ 163 private static final int MO_MSG_QUEUE_LIMIT = 5; 164 165 /** 166 * Message reference for a CONCATENATED_8_BIT_REFERENCE or 167 * CONCATENATED_16_BIT_REFERENCE message set. Should be 168 * incremented for each set of concatenated messages. 169 * Static field shared by all dispatcher objects. 170 */ 171 private static int sConcatenatedRef = new Random().nextInt(256); 172 173 /** Outgoing message counter. Shared by all dispatchers. */ 174 private final SmsUsageMonitor mUsageMonitor; 175 176 /** Number of outgoing SmsTrackers waiting for user confirmation. */ 177 private int mPendingTrackerCount; 178 179 /** Wake lock to ensure device stays awake while dispatching the SMS intent. */ 180 private PowerManager.WakeLock mWakeLock; 181 182 /** 183 * Hold the wake lock for 5 seconds, which should be enough time for 184 * any receiver(s) to grab its own wake lock. 185 */ 186 private static final int WAKE_LOCK_TIMEOUT = 5000; 187 188 /* Flags indicating whether the current device allows sms service */ 189 protected boolean mSmsCapable = true; 190 protected boolean mSmsReceiveDisabled; 191 protected boolean mSmsSendDisabled; 192 193 protected int mRemainingMessages = -1; 194 195 protected static int getNextConcatenatedRef() { 196 sConcatenatedRef += 1; 197 return sConcatenatedRef; 198 } 199 200 /** 201 * Create a new SMS dispatcher. 202 * @param phone the Phone to use 203 * @param storageMonitor the SmsStorageMonitor to use 204 * @param usageMonitor the SmsUsageMonitor to use 205 */ 206 protected SMSDispatcher(PhoneBase phone, SmsStorageMonitor storageMonitor, 207 SmsUsageMonitor usageMonitor) { 208 mPhone = phone; 209 mWapPush = new WapPushOverSms(phone, this); 210 mContext = phone.getContext(); 211 mResolver = mContext.getContentResolver(); 212 mCm = phone.mCM; 213 mStorageMonitor = storageMonitor; 214 mUsageMonitor = usageMonitor; 215 mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); 216 mSettingsObserver = new SettingsObserver(this, mPremiumSmsRule, mContext); 217 mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor( 218 Settings.Global.SMS_SHORT_CODE_RULE), false, mSettingsObserver); 219 220 createWakelock(); 221 222 mSmsCapable = mContext.getResources().getBoolean( 223 com.android.internal.R.bool.config_sms_capable); 224 mSmsReceiveDisabled = !SystemProperties.getBoolean( 225 TelephonyProperties.PROPERTY_SMS_RECEIVE, mSmsCapable); 226 mSmsSendDisabled = !SystemProperties.getBoolean( 227 TelephonyProperties.PROPERTY_SMS_SEND, mSmsCapable); 228 Log.d(TAG, "SMSDispatcher: ctor mSmsCapable=" + mSmsCapable + " format=" + getFormat() 229 + " mSmsReceiveDisabled=" + mSmsReceiveDisabled 230 + " mSmsSendDisabled=" + mSmsSendDisabled); 231 } 232 233 /** 234 * Observe the secure setting for updated premium sms determination rules 235 */ 236 private static class SettingsObserver extends ContentObserver { 237 private final AtomicInteger mPremiumSmsRule; 238 private final Context mContext; 239 SettingsObserver(Handler handler, AtomicInteger premiumSmsRule, Context context) { 240 super(handler); 241 mPremiumSmsRule = premiumSmsRule; 242 mContext = context; 243 onChange(false); // load initial value; 244 } 245 246 @Override 247 public void onChange(boolean selfChange) { 248 mPremiumSmsRule.set(Settings.Global.getInt(mContext.getContentResolver(), 249 Settings.Global.SMS_SHORT_CODE_RULE, PREMIUM_RULE_USE_SIM)); 250 } 251 } 252 253 /** Unregister for incoming SMS events. */ 254 public abstract void dispose(); 255 256 /** 257 * The format of the message PDU in the associated broadcast intent. 258 * This will be either "3gpp" for GSM/UMTS/LTE messages in 3GPP format 259 * or "3gpp2" for CDMA/LTE messages in 3GPP2 format. 260 * 261 * Note: All applications which handle incoming SMS messages by processing the 262 * SMS_RECEIVED_ACTION broadcast intent MUST pass the "format" extra from the intent 263 * into the new methods in {@link android.telephony.SmsMessage} which take an 264 * extra format parameter. This is required in order to correctly decode the PDU on 265 * devices which require support for both 3GPP and 3GPP2 formats at the same time, 266 * such as CDMA/LTE devices and GSM/CDMA world phones. 267 * 268 * @return the format of the message PDU 269 */ 270 protected abstract String getFormat(); 271 272 @Override 273 protected void finalize() { 274 Log.d(TAG, "SMSDispatcher finalized"); 275 } 276 277 278 /* TODO: Need to figure out how to keep track of status report routing in a 279 * persistent manner. If the phone process restarts (reboot or crash), 280 * we will lose this list and any status reports that come in after 281 * will be dropped. 282 */ 283 /** Sent messages awaiting a delivery status report. */ 284 protected final ArrayList<SmsTracker> deliveryPendingList = new ArrayList<SmsTracker>(); 285 286 /** 287 * Handles events coming from the phone stack. Overridden from handler. 288 * 289 * @param msg the message to handle 290 */ 291 @Override 292 public void handleMessage(Message msg) { 293 AsyncResult ar; 294 295 switch (msg.what) { 296 case EVENT_NEW_SMS: 297 // A new SMS has been received by the device 298 if (false) { 299 Log.d(TAG, "New SMS Message Received"); 300 } 301 302 SmsMessage sms; 303 304 ar = (AsyncResult) msg.obj; 305 306 if (ar.exception != null) { 307 Log.e(TAG, "Exception processing incoming SMS. Exception:" + ar.exception); 308 return; 309 } 310 311 sms = (SmsMessage) ar.result; 312 try { 313 int result = dispatchMessage(sms.mWrappedSmsMessage); 314 if (result != Activity.RESULT_OK) { 315 // RESULT_OK means that message was broadcast for app(s) to handle. 316 // Any other result, we should ack here. 317 boolean handled = (result == Intents.RESULT_SMS_HANDLED); 318 notifyAndAcknowledgeLastIncomingSms(handled, result, null); 319 } 320 } catch (RuntimeException ex) { 321 Log.e(TAG, "Exception dispatching message", ex); 322 notifyAndAcknowledgeLastIncomingSms(false, Intents.RESULT_SMS_GENERIC_ERROR, null); 323 } 324 325 break; 326 327 case EVENT_SEND_SMS_COMPLETE: 328 // An outbound SMS has been successfully transferred, or failed. 329 handleSendComplete((AsyncResult) msg.obj); 330 break; 331 332 case EVENT_SEND_RETRY: 333 sendSms((SmsTracker) msg.obj); 334 break; 335 336 case EVENT_SEND_LIMIT_REACHED_CONFIRMATION: 337 handleReachSentLimit((SmsTracker)(msg.obj)); 338 break; 339 340 case EVENT_CONFIRM_SEND_TO_POSSIBLE_PREMIUM_SHORT_CODE: 341 handleConfirmShortCode(false, (SmsTracker)(msg.obj)); 342 break; 343 344 case EVENT_CONFIRM_SEND_TO_PREMIUM_SHORT_CODE: 345 handleConfirmShortCode(true, (SmsTracker)(msg.obj)); 346 break; 347 348 case EVENT_SEND_CONFIRMED_SMS: 349 { 350 SmsTracker tracker = (SmsTracker) msg.obj; 351 if (tracker.isMultipart()) { 352 sendMultipartSms(tracker); 353 } else { 354 sendSms(tracker); 355 } 356 mPendingTrackerCount--; 357 break; 358 } 359 360 case EVENT_STOP_SENDING: 361 { 362 SmsTracker tracker = (SmsTracker) msg.obj; 363 if (tracker.mSentIntent != null) { 364 try { 365 tracker.mSentIntent.send(RESULT_ERROR_LIMIT_EXCEEDED); 366 } catch (CanceledException ex) { 367 Log.e(TAG, "failed to send RESULT_ERROR_LIMIT_EXCEEDED"); 368 } 369 } 370 mPendingTrackerCount--; 371 break; 372 } 373 } 374 } 375 376 private void createWakelock() { 377 PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); 378 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "SMSDispatcher"); 379 mWakeLock.setReferenceCounted(true); 380 } 381 382 /** 383 * Grabs a wake lock and sends intent as an ordered broadcast. 384 * The resultReceiver will check for errors and ACK/NACK back 385 * to the RIL. 386 * 387 * @param intent intent to broadcast 388 * @param permission Receivers are required to have this permission 389 */ 390 public void dispatch(Intent intent, String permission) { 391 // Hold a wake lock for WAKE_LOCK_TIMEOUT seconds, enough to give any 392 // receivers time to take their own wake locks. 393 mWakeLock.acquire(WAKE_LOCK_TIMEOUT); 394 mContext.sendOrderedBroadcast(intent, permission, mResultReceiver, 395 this, Activity.RESULT_OK, null, null); 396 } 397 398 /** 399 * Grabs a wake lock and sends intent as an ordered broadcast. 400 * Used for setting a custom result receiver for CDMA SCPD. 401 * 402 * @param intent intent to broadcast 403 * @param permission Receivers are required to have this permission 404 * @param resultReceiver the result receiver to use 405 */ 406 public void dispatch(Intent intent, String permission, BroadcastReceiver resultReceiver) { 407 // Hold a wake lock for WAKE_LOCK_TIMEOUT seconds, enough to give any 408 // receivers time to take their own wake locks. 409 mWakeLock.acquire(WAKE_LOCK_TIMEOUT); 410 mContext.sendOrderedBroadcast(intent, permission, resultReceiver, 411 this, Activity.RESULT_OK, null, null); 412 } 413 414 /** 415 * Called when SMS send completes. Broadcasts a sentIntent on success. 416 * On failure, either sets up retries or broadcasts a sentIntent with 417 * the failure in the result code. 418 * 419 * @param ar AsyncResult passed into the message handler. ar.result should 420 * an SmsResponse instance if send was successful. ar.userObj 421 * should be an SmsTracker instance. 422 */ 423 protected void handleSendComplete(AsyncResult ar) { 424 SmsTracker tracker = (SmsTracker) ar.userObj; 425 PendingIntent sentIntent = tracker.mSentIntent; 426 427 if (ar.exception == null) { 428 if (false) { 429 Log.d(TAG, "SMS send complete. Broadcasting " 430 + "intent: " + sentIntent); 431 } 432 433 if (tracker.mDeliveryIntent != null) { 434 // Expecting a status report. Add it to the list. 435 int messageRef = ((SmsResponse)ar.result).messageRef; 436 tracker.mMessageRef = messageRef; 437 deliveryPendingList.add(tracker); 438 } 439 440 if (sentIntent != null) { 441 try { 442 if (mRemainingMessages > -1) { 443 mRemainingMessages--; 444 } 445 446 if (mRemainingMessages == 0) { 447 Intent sendNext = new Intent(); 448 sendNext.putExtra(SEND_NEXT_MSG_EXTRA, true); 449 sentIntent.send(mContext, Activity.RESULT_OK, sendNext); 450 } else { 451 sentIntent.send(Activity.RESULT_OK); 452 } 453 } catch (CanceledException ex) {} 454 } 455 } else { 456 if (false) { 457 Log.d(TAG, "SMS send failed"); 458 } 459 460 int ss = mPhone.getServiceState().getState(); 461 462 if (ss != ServiceState.STATE_IN_SERVICE) { 463 handleNotInService(ss, tracker.mSentIntent); 464 } else if ((((CommandException)(ar.exception)).getCommandError() 465 == CommandException.Error.SMS_FAIL_RETRY) && 466 tracker.mRetryCount < MAX_SEND_RETRIES) { 467 // Retry after a delay if needed. 468 // TODO: According to TS 23.040, 9.2.3.6, we should resend 469 // with the same TP-MR as the failed message, and 470 // TP-RD set to 1. However, we don't have a means of 471 // knowing the MR for the failed message (EF_SMSstatus 472 // may or may not have the MR corresponding to this 473 // message, depending on the failure). Also, in some 474 // implementations this retry is handled by the baseband. 475 tracker.mRetryCount++; 476 Message retryMsg = obtainMessage(EVENT_SEND_RETRY, tracker); 477 sendMessageDelayed(retryMsg, SEND_RETRY_DELAY); 478 } else if (tracker.mSentIntent != null) { 479 int error = RESULT_ERROR_GENERIC_FAILURE; 480 481 if (((CommandException)(ar.exception)).getCommandError() 482 == CommandException.Error.FDN_CHECK_FAILURE) { 483 error = RESULT_ERROR_FDN_CHECK_FAILURE; 484 } 485 // Done retrying; return an error to the app. 486 try { 487 Intent fillIn = new Intent(); 488 if (ar.result != null) { 489 fillIn.putExtra("errorCode", ((SmsResponse)ar.result).errorCode); 490 } 491 if (mRemainingMessages > -1) { 492 mRemainingMessages--; 493 } 494 495 if (mRemainingMessages == 0) { 496 fillIn.putExtra(SEND_NEXT_MSG_EXTRA, true); 497 } 498 499 tracker.mSentIntent.send(mContext, error, fillIn); 500 } catch (CanceledException ex) {} 501 } 502 } 503 } 504 505 /** 506 * Handles outbound message when the phone is not in service. 507 * 508 * @param ss Current service state. Valid values are: 509 * OUT_OF_SERVICE 510 * EMERGENCY_ONLY 511 * POWER_OFF 512 * @param sentIntent the PendingIntent to send the error to 513 */ 514 protected static void handleNotInService(int ss, PendingIntent sentIntent) { 515 if (sentIntent != null) { 516 try { 517 if (ss == ServiceState.STATE_POWER_OFF) { 518 sentIntent.send(RESULT_ERROR_RADIO_OFF); 519 } else { 520 sentIntent.send(RESULT_ERROR_NO_SERVICE); 521 } 522 } catch (CanceledException ex) {} 523 } 524 } 525 526 /** 527 * Dispatches an incoming SMS messages. 528 * 529 * @param sms the incoming message from the phone 530 * @return a result code from {@link Telephony.Sms.Intents}, or 531 * {@link Activity#RESULT_OK} if the message has been broadcast 532 * to applications 533 */ 534 public abstract int dispatchMessage(SmsMessageBase sms); 535 536 /** 537 * Dispatch a normal incoming SMS. This is called from the format-specific 538 * {@link #dispatchMessage(SmsMessageBase)} if no format-specific handling is required. 539 * 540 * @param sms 541 * @return 542 */ 543 protected int dispatchNormalMessage(SmsMessageBase sms) { 544 SmsHeader smsHeader = sms.getUserDataHeader(); 545 546 // See if message is partial or port addressed. 547 if ((smsHeader == null) || (smsHeader.concatRef == null)) { 548 // Message is not partial (not part of concatenated sequence). 549 byte[][] pdus = new byte[1][]; 550 pdus[0] = sms.getPdu(); 551 552 if (smsHeader != null && smsHeader.portAddrs != null) { 553 if (smsHeader.portAddrs.destPort == SmsHeader.PORT_WAP_PUSH) { 554 // GSM-style WAP indication 555 return mWapPush.dispatchWapPdu(sms.getUserData()); 556 } else { 557 // The message was sent to a port, so concoct a URI for it. 558 dispatchPortAddressedPdus(pdus, smsHeader.portAddrs.destPort); 559 } 560 } else { 561 // Normal short and non-port-addressed message, dispatch it. 562 dispatchPdus(pdus); 563 } 564 return Activity.RESULT_OK; 565 } else { 566 // Process the message part. 567 SmsHeader.ConcatRef concatRef = smsHeader.concatRef; 568 SmsHeader.PortAddrs portAddrs = smsHeader.portAddrs; 569 return processMessagePart(sms.getPdu(), sms.getOriginatingAddress(), 570 concatRef.refNumber, concatRef.seqNumber, concatRef.msgCount, 571 sms.getTimestampMillis(), (portAddrs != null ? portAddrs.destPort : -1), false); 572 } 573 } 574 575 /** 576 * If this is the last part send the parts out to the application, otherwise 577 * the part is stored for later processing. Handles both 3GPP concatenated messages 578 * as well as 3GPP2 format WAP push messages processed by 579 * {@link com.android.internal.telephony.cdma.CdmaSMSDispatcher#processCdmaWapPdu}. 580 * 581 * @param pdu the message PDU, or the datagram portion of a CDMA WDP datagram segment 582 * @param address the originating address 583 * @param referenceNumber distinguishes concatenated messages from the same sender 584 * @param sequenceNumber the order of this segment in the message 585 * (starting at 0 for CDMA WDP datagrams and 1 for concatenated messages). 586 * @param messageCount the number of segments in the message 587 * @param timestamp the service center timestamp in millis 588 * @param destPort the destination port for the message, or -1 for no destination port 589 * @param isCdmaWapPush true if pdu is a CDMA WDP datagram segment and not an SM PDU 590 * 591 * @return a result code from {@link Telephony.Sms.Intents}, or 592 * {@link Activity#RESULT_OK} if the message has been broadcast 593 * to applications 594 */ 595 protected int processMessagePart(byte[] pdu, String address, int referenceNumber, 596 int sequenceNumber, int messageCount, long timestamp, int destPort, 597 boolean isCdmaWapPush) { 598 byte[][] pdus = null; 599 Cursor cursor = null; 600 try { 601 // used by several query selection arguments 602 String refNumber = Integer.toString(referenceNumber); 603 String seqNumber = Integer.toString(sequenceNumber); 604 605 // Check for duplicate message segment 606 cursor = mResolver.query(mRawUri, PDU_PROJECTION, 607 "address=? AND reference_number=? AND sequence=?", 608 new String[] {address, refNumber, seqNumber}, null); 609 610 // moveToNext() returns false if no duplicates were found 611 if (cursor.moveToNext()) { 612 Log.w(TAG, "Discarding duplicate message segment from address=" + address 613 + " refNumber=" + refNumber + " seqNumber=" + seqNumber); 614 String oldPduString = cursor.getString(PDU_COLUMN); 615 byte[] oldPdu = HexDump.hexStringToByteArray(oldPduString); 616 if (!Arrays.equals(oldPdu, pdu)) { 617 Log.e(TAG, "Warning: dup message segment PDU of length " + pdu.length 618 + " is different from existing PDU of length " + oldPdu.length); 619 } 620 return Intents.RESULT_SMS_HANDLED; 621 } 622 cursor.close(); 623 624 // not a dup, query for all other segments of this concatenated message 625 String where = "address=? AND reference_number=?"; 626 String[] whereArgs = new String[] {address, refNumber}; 627 cursor = mResolver.query(mRawUri, PDU_SEQUENCE_PORT_PROJECTION, where, whereArgs, null); 628 629 int cursorCount = cursor.getCount(); 630 if (cursorCount != messageCount - 1) { 631 // We don't have all the parts yet, store this one away 632 ContentValues values = new ContentValues(); 633 values.put("date", timestamp); 634 values.put("pdu", HexDump.toHexString(pdu)); 635 values.put("address", address); 636 values.put("reference_number", referenceNumber); 637 values.put("count", messageCount); 638 values.put("sequence", sequenceNumber); 639 if (destPort != -1) { 640 values.put("destination_port", destPort); 641 } 642 mResolver.insert(mRawUri, values); 643 return Intents.RESULT_SMS_HANDLED; 644 } 645 646 // All the parts are in place, deal with them 647 pdus = new byte[messageCount][]; 648 for (int i = 0; i < cursorCount; i++) { 649 cursor.moveToNext(); 650 int cursorSequence = cursor.getInt(SEQUENCE_COLUMN); 651 // GSM sequence numbers start at 1; CDMA WDP datagram sequence numbers start at 0 652 if (!isCdmaWapPush) { 653 cursorSequence--; 654 } 655 pdus[cursorSequence] = HexDump.hexStringToByteArray( 656 cursor.getString(PDU_COLUMN)); 657 658 // Read the destination port from the first segment (needed for CDMA WAP PDU). 659 // It's not a bad idea to prefer the port from the first segment for 3GPP as well. 660 if (cursorSequence == 0 && !cursor.isNull(DESTINATION_PORT_COLUMN)) { 661 destPort = cursor.getInt(DESTINATION_PORT_COLUMN); 662 } 663 } 664 // This one isn't in the DB, so add it 665 // GSM sequence numbers start at 1; CDMA WDP datagram sequence numbers start at 0 666 if (isCdmaWapPush) { 667 pdus[sequenceNumber] = pdu; 668 } else { 669 pdus[sequenceNumber - 1] = pdu; 670 } 671 672 // Remove the parts from the database 673 mResolver.delete(mRawUri, where, whereArgs); 674 } catch (SQLException e) { 675 Log.e(TAG, "Can't access multipart SMS database", e); 676 return Intents.RESULT_SMS_GENERIC_ERROR; 677 } finally { 678 if (cursor != null) cursor.close(); 679 } 680 681 // Special handling for CDMA WDP datagrams 682 if (isCdmaWapPush) { 683 // Build up the data stream 684 ByteArrayOutputStream output = new ByteArrayOutputStream(); 685 for (int i = 0; i < messageCount; i++) { 686 // reassemble the (WSP-)pdu 687 output.write(pdus[i], 0, pdus[i].length); 688 } 689 byte[] datagram = output.toByteArray(); 690 691 // Dispatch the PDU to applications 692 if (destPort == SmsHeader.PORT_WAP_PUSH) { 693 // Handle the PUSH 694 return mWapPush.dispatchWapPdu(datagram); 695 } else { 696 pdus = new byte[1][]; 697 pdus[0] = datagram; 698 // The messages were sent to any other WAP port 699 dispatchPortAddressedPdus(pdus, destPort); 700 return Activity.RESULT_OK; 701 } 702 } 703 704 // Dispatch the PDUs to applications 705 if (destPort != -1) { 706 if (destPort == SmsHeader.PORT_WAP_PUSH) { 707 // Build up the data stream 708 ByteArrayOutputStream output = new ByteArrayOutputStream(); 709 for (int i = 0; i < messageCount; i++) { 710 SmsMessage msg = SmsMessage.createFromPdu(pdus[i], getFormat()); 711 byte[] data = msg.getUserData(); 712 output.write(data, 0, data.length); 713 } 714 // Handle the PUSH 715 return mWapPush.dispatchWapPdu(output.toByteArray()); 716 } else { 717 // The messages were sent to a port, so concoct a URI for it 718 dispatchPortAddressedPdus(pdus, destPort); 719 } 720 } else { 721 // The messages were not sent to a port 722 dispatchPdus(pdus); 723 } 724 return Activity.RESULT_OK; 725 } 726 727 /** 728 * Dispatches standard PDUs to interested applications 729 * 730 * @param pdus The raw PDUs making up the message 731 */ 732 protected void dispatchPdus(byte[][] pdus) { 733 Intent intent = new Intent(Intents.SMS_RECEIVED_ACTION); 734 intent.putExtra("pdus", pdus); 735 intent.putExtra("format", getFormat()); 736 dispatch(intent, RECEIVE_SMS_PERMISSION); 737 } 738 739 /** 740 * Dispatches port addressed PDUs to interested applications 741 * 742 * @param pdus The raw PDUs making up the message 743 * @param port The destination port of the messages 744 */ 745 protected void dispatchPortAddressedPdus(byte[][] pdus, int port) { 746 Uri uri = Uri.parse("sms://localhost:" + port); 747 Intent intent = new Intent(Intents.DATA_SMS_RECEIVED_ACTION, uri); 748 intent.putExtra("pdus", pdus); 749 intent.putExtra("format", getFormat()); 750 dispatch(intent, RECEIVE_SMS_PERMISSION); 751 } 752 753 /** 754 * Send a data based SMS to a specific application port. 755 * 756 * @param destAddr the address to send the message to 757 * @param scAddr is the service center address or null to use 758 * the current default SMSC 759 * @param destPort the port to deliver the message to 760 * @param data the body of the message to send 761 * @param sentIntent if not NULL this <code>PendingIntent</code> is 762 * broadcast when the message is successfully sent, or failed. 763 * The result code will be <code>Activity.RESULT_OK<code> for success, 764 * or one of these errors:<br> 765 * <code>RESULT_ERROR_GENERIC_FAILURE</code><br> 766 * <code>RESULT_ERROR_RADIO_OFF</code><br> 767 * <code>RESULT_ERROR_NULL_PDU</code><br> 768 * <code>RESULT_ERROR_NO_SERVICE</code><br>. 769 * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include 770 * the extra "errorCode" containing a radio technology specific value, 771 * generally only useful for troubleshooting.<br> 772 * The per-application based SMS control checks sentIntent. If sentIntent 773 * is NULL the caller will be checked against all unknown applications, 774 * which cause smaller number of SMS to be sent in checking period. 775 * @param deliveryIntent if not NULL this <code>PendingIntent</code> is 776 * broadcast when the message is delivered to the recipient. The 777 * raw pdu of the status report is in the extended data ("pdu"). 778 */ 779 protected abstract void sendData(String destAddr, String scAddr, int destPort, 780 byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent); 781 782 /** 783 * Send a text based SMS. 784 * 785 * @param destAddr the address to send the message to 786 * @param scAddr is the service center address or null to use 787 * the current default SMSC 788 * @param text the body of the message to send 789 * @param sentIntent if not NULL this <code>PendingIntent</code> is 790 * broadcast when the message is successfully sent, or failed. 791 * The result code will be <code>Activity.RESULT_OK<code> for success, 792 * or one of these errors:<br> 793 * <code>RESULT_ERROR_GENERIC_FAILURE</code><br> 794 * <code>RESULT_ERROR_RADIO_OFF</code><br> 795 * <code>RESULT_ERROR_NULL_PDU</code><br> 796 * <code>RESULT_ERROR_NO_SERVICE</code><br>. 797 * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include 798 * the extra "errorCode" containing a radio technology specific value, 799 * generally only useful for troubleshooting.<br> 800 * The per-application based SMS control checks sentIntent. If sentIntent 801 * is NULL the caller will be checked against all unknown applications, 802 * which cause smaller number of SMS to be sent in checking period. 803 * @param deliveryIntent if not NULL this <code>PendingIntent</code> is 804 * broadcast when the message is delivered to the recipient. The 805 * raw pdu of the status report is in the extended data ("pdu"). 806 */ 807 protected abstract void sendText(String destAddr, String scAddr, 808 String text, PendingIntent sentIntent, PendingIntent deliveryIntent); 809 810 /** 811 * Calculate the number of septets needed to encode the message. 812 * 813 * @param messageBody the message to encode 814 * @param use7bitOnly ignore (but still count) illegal characters if true 815 * @return TextEncodingDetails 816 */ 817 protected abstract TextEncodingDetails calculateLength(CharSequence messageBody, 818 boolean use7bitOnly); 819 820 /** 821 * Send a multi-part text based SMS. 822 * 823 * @param destAddr the address to send the message to 824 * @param scAddr is the service center address or null to use 825 * the current default SMSC 826 * @param parts an <code>ArrayList</code> of strings that, in order, 827 * comprise the original message 828 * @param sentIntents if not null, an <code>ArrayList</code> of 829 * <code>PendingIntent</code>s (one for each message part) that is 830 * broadcast when the corresponding message part has been sent. 831 * The result code will be <code>Activity.RESULT_OK<code> for success, 832 * or one of these errors: 833 * <code>RESULT_ERROR_GENERIC_FAILURE</code> 834 * <code>RESULT_ERROR_RADIO_OFF</code> 835 * <code>RESULT_ERROR_NULL_PDU</code> 836 * <code>RESULT_ERROR_NO_SERVICE</code>. 837 * The per-application based SMS control checks sentIntent. If sentIntent 838 * is NULL the caller will be checked against all unknown applications, 839 * which cause smaller number of SMS to be sent in checking period. 840 * @param deliveryIntents if not null, an <code>ArrayList</code> of 841 * <code>PendingIntent</code>s (one for each message part) that is 842 * broadcast when the corresponding message part has been delivered 843 * to the recipient. The raw pdu of the status report is in the 844 * extended data ("pdu"). 845 */ 846 protected void sendMultipartText(String destAddr, String scAddr, 847 ArrayList<String> parts, ArrayList<PendingIntent> sentIntents, 848 ArrayList<PendingIntent> deliveryIntents) { 849 850 int refNumber = getNextConcatenatedRef() & 0x00FF; 851 int msgCount = parts.size(); 852 int encoding = SmsConstants.ENCODING_UNKNOWN; 853 854 mRemainingMessages = msgCount; 855 856 TextEncodingDetails[] encodingForParts = new TextEncodingDetails[msgCount]; 857 for (int i = 0; i < msgCount; i++) { 858 TextEncodingDetails details = calculateLength(parts.get(i), false); 859 if (encoding != details.codeUnitSize 860 && (encoding == SmsConstants.ENCODING_UNKNOWN 861 || encoding == SmsConstants.ENCODING_7BIT)) { 862 encoding = details.codeUnitSize; 863 } 864 encodingForParts[i] = details; 865 } 866 867 for (int i = 0; i < msgCount; i++) { 868 SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef(); 869 concatRef.refNumber = refNumber; 870 concatRef.seqNumber = i + 1; // 1-based sequence 871 concatRef.msgCount = msgCount; 872 // TODO: We currently set this to true since our messaging app will never 873 // send more than 255 parts (it converts the message to MMS well before that). 874 // However, we should support 3rd party messaging apps that might need 16-bit 875 // references 876 // Note: It's not sufficient to just flip this bit to true; it will have 877 // ripple effects (several calculations assume 8-bit ref). 878 concatRef.isEightBits = true; 879 SmsHeader smsHeader = new SmsHeader(); 880 smsHeader.concatRef = concatRef; 881 882 // Set the national language tables for 3GPP 7-bit encoding, if enabled. 883 if (encoding == SmsConstants.ENCODING_7BIT) { 884 smsHeader.languageTable = encodingForParts[i].languageTable; 885 smsHeader.languageShiftTable = encodingForParts[i].languageShiftTable; 886 } 887 888 PendingIntent sentIntent = null; 889 if (sentIntents != null && sentIntents.size() > i) { 890 sentIntent = sentIntents.get(i); 891 } 892 893 PendingIntent deliveryIntent = null; 894 if (deliveryIntents != null && deliveryIntents.size() > i) { 895 deliveryIntent = deliveryIntents.get(i); 896 } 897 898 sendNewSubmitPdu(destAddr, scAddr, parts.get(i), smsHeader, encoding, 899 sentIntent, deliveryIntent, (i == (msgCount - 1))); 900 } 901 902 } 903 904 /** 905 * Create a new SubmitPdu and send it. 906 */ 907 protected abstract void sendNewSubmitPdu(String destinationAddress, String scAddress, 908 String message, SmsHeader smsHeader, int encoding, 909 PendingIntent sentIntent, PendingIntent deliveryIntent, boolean lastPart); 910 911 /** 912 * Send a SMS 913 * 914 * @param smsc the SMSC to send the message through, or NULL for the 915 * default SMSC 916 * @param pdu the raw PDU to send 917 * @param sentIntent if not NULL this <code>Intent</code> is 918 * broadcast when the message is successfully sent, or failed. 919 * The result code will be <code>Activity.RESULT_OK<code> for success, 920 * or one of these errors: 921 * <code>RESULT_ERROR_GENERIC_FAILURE</code> 922 * <code>RESULT_ERROR_RADIO_OFF</code> 923 * <code>RESULT_ERROR_NULL_PDU</code> 924 * <code>RESULT_ERROR_NO_SERVICE</code>. 925 * The per-application based SMS control checks sentIntent. If sentIntent 926 * is NULL the caller will be checked against all unknown applications, 927 * which cause smaller number of SMS to be sent in checking period. 928 * @param deliveryIntent if not NULL this <code>Intent</code> is 929 * broadcast when the message is delivered to the recipient. The 930 * raw pdu of the status report is in the extended data ("pdu"). 931 * @param destAddr the destination phone number (for short code confirmation) 932 */ 933 protected void sendRawPdu(byte[] smsc, byte[] pdu, PendingIntent sentIntent, 934 PendingIntent deliveryIntent, String destAddr) { 935 if (mSmsSendDisabled) { 936 if (sentIntent != null) { 937 try { 938 sentIntent.send(RESULT_ERROR_NO_SERVICE); 939 } catch (CanceledException ex) {} 940 } 941 Log.d(TAG, "Device does not support sending sms."); 942 return; 943 } 944 945 if (pdu == null) { 946 if (sentIntent != null) { 947 try { 948 sentIntent.send(RESULT_ERROR_NULL_PDU); 949 } catch (CanceledException ex) {} 950 } 951 return; 952 } 953 954 HashMap<String, Object> map = new HashMap<String, Object>(); 955 map.put("smsc", smsc); 956 map.put("pdu", pdu); 957 958 // Get calling app package name via UID from Binder call 959 PackageManager pm = mContext.getPackageManager(); 960 String[] packageNames = pm.getPackagesForUid(Binder.getCallingUid()); 961 962 if (packageNames == null || packageNames.length == 0) { 963 // Refuse to send SMS if we can't get the calling package name. 964 Log.e(TAG, "Can't get calling app package name: refusing to send SMS"); 965 if (sentIntent != null) { 966 try { 967 sentIntent.send(RESULT_ERROR_GENERIC_FAILURE); 968 } catch (CanceledException ex) { 969 Log.e(TAG, "failed to send error result"); 970 } 971 } 972 return; 973 } 974 975 // Get package info via packagemanager 976 PackageInfo appInfo = null; 977 try { 978 // XXX this is lossy- apps can share a UID 979 appInfo = pm.getPackageInfo(packageNames[0], PackageManager.GET_SIGNATURES); 980 } catch (PackageManager.NameNotFoundException e) { 981 Log.e(TAG, "Can't get calling app package info: refusing to send SMS"); 982 if (sentIntent != null) { 983 try { 984 sentIntent.send(RESULT_ERROR_GENERIC_FAILURE); 985 } catch (CanceledException ex) { 986 Log.e(TAG, "failed to send error result"); 987 } 988 } 989 return; 990 } 991 992 // Strip non-digits from destination phone number before checking for short codes 993 // and before displaying the number to the user if confirmation is required. 994 SmsTracker tracker = new SmsTracker(map, sentIntent, deliveryIntent, appInfo, 995 PhoneNumberUtils.extractNetworkPortion(destAddr)); 996 997 // checkDestination() returns true if the destination is not a premium short code or the 998 // sending app is approved to send to short codes. Otherwise, a message is sent to our 999 // handler with the SmsTracker to request user confirmation before sending. 1000 if (checkDestination(tracker)) { 1001 // check for excessive outgoing SMS usage by this app 1002 if (!mUsageMonitor.check(appInfo.packageName, SINGLE_PART_SMS)) { 1003 sendMessage(obtainMessage(EVENT_SEND_LIMIT_REACHED_CONFIRMATION, tracker)); 1004 return; 1005 } 1006 1007 int ss = mPhone.getServiceState().getState(); 1008 1009 if (ss != ServiceState.STATE_IN_SERVICE) { 1010 handleNotInService(ss, tracker.mSentIntent); 1011 } else { 1012 sendSms(tracker); 1013 } 1014 } 1015 } 1016 1017 /** 1018 * Check if destination is a potential premium short code and sender is not pre-approved to 1019 * send to short codes. 1020 * 1021 * @param tracker the tracker for the SMS to send 1022 * @return true if the destination is approved; false if user confirmation event was sent 1023 */ 1024 boolean checkDestination(SmsTracker tracker) { 1025 if (mContext.checkCallingOrSelfPermission(SEND_SMS_NO_CONFIRMATION_PERMISSION) 1026 == PackageManager.PERMISSION_GRANTED) { 1027 return true; // app is pre-approved to send to short codes 1028 } else { 1029 int rule = mPremiumSmsRule.get(); 1030 int smsCategory = SmsUsageMonitor.CATEGORY_NOT_SHORT_CODE; 1031 if (rule == PREMIUM_RULE_USE_SIM || rule == PREMIUM_RULE_USE_BOTH) { 1032 String simCountryIso = mTelephonyManager.getSimCountryIso(); 1033 if (simCountryIso == null || simCountryIso.length() != 2) { 1034 Log.e(TAG, "Can't get SIM country Iso: trying network country Iso"); 1035 simCountryIso = mTelephonyManager.getNetworkCountryIso(); 1036 } 1037 1038 smsCategory = mUsageMonitor.checkDestination(tracker.mDestAddress, simCountryIso); 1039 } 1040 if (rule == PREMIUM_RULE_USE_NETWORK || rule == PREMIUM_RULE_USE_BOTH) { 1041 String networkCountryIso = mTelephonyManager.getNetworkCountryIso(); 1042 if (networkCountryIso == null || networkCountryIso.length() != 2) { 1043 Log.e(TAG, "Can't get Network country Iso: trying SIM country Iso"); 1044 networkCountryIso = mTelephonyManager.getSimCountryIso(); 1045 } 1046 1047 smsCategory = mUsageMonitor.mergeShortCodeCategories(smsCategory, 1048 mUsageMonitor.checkDestination(tracker.mDestAddress, networkCountryIso)); 1049 } 1050 1051 if (smsCategory == SmsUsageMonitor.CATEGORY_NOT_SHORT_CODE 1052 || smsCategory == SmsUsageMonitor.CATEGORY_FREE_SHORT_CODE 1053 || smsCategory == SmsUsageMonitor.CATEGORY_STANDARD_SHORT_CODE) { 1054 return true; // not a premium short code 1055 } 1056 1057 // Wait for user confirmation unless the user has set permission to always allow/deny 1058 int premiumSmsPermission = mUsageMonitor.getPremiumSmsPermission( 1059 tracker.mAppInfo.packageName); 1060 if (premiumSmsPermission == SmsUsageMonitor.PREMIUM_SMS_PERMISSION_UNKNOWN) { 1061 // First time trying to send to premium SMS. 1062 premiumSmsPermission = SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ASK_USER; 1063 } 1064 1065 switch (premiumSmsPermission) { 1066 case SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW: 1067 Log.d(TAG, "User approved this app to send to premium SMS"); 1068 return true; 1069 1070 case SmsUsageMonitor.PREMIUM_SMS_PERMISSION_NEVER_ALLOW: 1071 Log.w(TAG, "User denied this app from sending to premium SMS"); 1072 sendMessage(obtainMessage(EVENT_STOP_SENDING, tracker)); 1073 return false; // reject this message 1074 1075 case SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ASK_USER: 1076 default: 1077 int event; 1078 if (smsCategory == SmsUsageMonitor.CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE) { 1079 event = EVENT_CONFIRM_SEND_TO_POSSIBLE_PREMIUM_SHORT_CODE; 1080 } else { 1081 event = EVENT_CONFIRM_SEND_TO_PREMIUM_SHORT_CODE; 1082 } 1083 sendMessage(obtainMessage(event, tracker)); 1084 return false; // wait for user confirmation 1085 } 1086 } 1087 } 1088 1089 /** 1090 * Deny sending an SMS if the outgoing queue limit is reached. Used when the message 1091 * must be confirmed by the user due to excessive usage or potential premium SMS detected. 1092 * @param tracker the SmsTracker for the message to send 1093 * @return true if the message was denied; false to continue with send confirmation 1094 */ 1095 private boolean denyIfQueueLimitReached(SmsTracker tracker) { 1096 if (mPendingTrackerCount >= MO_MSG_QUEUE_LIMIT) { 1097 // Deny sending message when the queue limit is reached. 1098 try { 1099 tracker.mSentIntent.send(RESULT_ERROR_LIMIT_EXCEEDED); 1100 } catch (CanceledException ex) { 1101 Log.e(TAG, "failed to send back RESULT_ERROR_LIMIT_EXCEEDED"); 1102 } 1103 return true; 1104 } 1105 mPendingTrackerCount++; 1106 return false; 1107 } 1108 1109 /** 1110 * Returns the label for the specified app package name. 1111 * @param appPackage the package name of the app requesting to send an SMS 1112 * @return the label for the specified app, or the package name if getApplicationInfo() fails 1113 */ 1114 private CharSequence getAppLabel(String appPackage) { 1115 PackageManager pm = mContext.getPackageManager(); 1116 try { 1117 ApplicationInfo appInfo = pm.getApplicationInfo(appPackage, 0); 1118 return appInfo.loadLabel(pm); 1119 } catch (PackageManager.NameNotFoundException e) { 1120 Log.e(TAG, "PackageManager Name Not Found for package " + appPackage); 1121 return appPackage; // fall back to package name if we can't get app label 1122 } 1123 } 1124 1125 /** 1126 * Post an alert when SMS needs confirmation due to excessive usage. 1127 * @param tracker an SmsTracker for the current message. 1128 */ 1129 protected void handleReachSentLimit(SmsTracker tracker) { 1130 if (denyIfQueueLimitReached(tracker)) { 1131 return; // queue limit reached; error was returned to caller 1132 } 1133 1134 CharSequence appLabel = getAppLabel(tracker.mAppInfo.packageName); 1135 Resources r = Resources.getSystem(); 1136 Spanned messageText = Html.fromHtml(r.getString(R.string.sms_control_message, appLabel)); 1137 1138 ConfirmDialogListener listener = new ConfirmDialogListener(tracker, null); 1139 1140 AlertDialog d = new AlertDialog.Builder(mContext) 1141 .setTitle(R.string.sms_control_title) 1142 .setIcon(R.drawable.stat_sys_warning) 1143 .setMessage(messageText) 1144 .setPositiveButton(r.getString(R.string.sms_control_yes), listener) 1145 .setNegativeButton(r.getString(R.string.sms_control_no), listener) 1146 .setOnCancelListener(listener) 1147 .create(); 1148 1149 d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); 1150 d.show(); 1151 } 1152 1153 /** 1154 * Post an alert for user confirmation when sending to a potential short code. 1155 * @param isPremium true if the destination is known to be a premium short code 1156 * @param tracker the SmsTracker for the current message. 1157 */ 1158 protected void handleConfirmShortCode(boolean isPremium, SmsTracker tracker) { 1159 if (denyIfQueueLimitReached(tracker)) { 1160 return; // queue limit reached; error was returned to caller 1161 } 1162 1163 int detailsId; 1164 if (isPremium) { 1165 detailsId = R.string.sms_premium_short_code_details; 1166 } else { 1167 detailsId = R.string.sms_short_code_details; 1168 } 1169 1170 CharSequence appLabel = getAppLabel(tracker.mAppInfo.packageName); 1171 Resources r = Resources.getSystem(); 1172 Spanned messageText = Html.fromHtml(r.getString(R.string.sms_short_code_confirm_message, 1173 appLabel, tracker.mDestAddress)); 1174 1175 LayoutInflater inflater = (LayoutInflater) mContext.getSystemService( 1176 Context.LAYOUT_INFLATER_SERVICE); 1177 View layout = inflater.inflate(R.layout.sms_short_code_confirmation_dialog, null); 1178 1179 ConfirmDialogListener listener = new ConfirmDialogListener(tracker, 1180 (TextView)layout.findViewById(R.id.sms_short_code_remember_undo_instruction)); 1181 1182 1183 TextView messageView = (TextView) layout.findViewById(R.id.sms_short_code_confirm_message); 1184 messageView.setText(messageText); 1185 1186 ViewGroup detailsLayout = (ViewGroup) layout.findViewById( 1187 R.id.sms_short_code_detail_layout); 1188 TextView detailsView = (TextView) detailsLayout.findViewById( 1189 R.id.sms_short_code_detail_message); 1190 detailsView.setText(detailsId); 1191 1192 CheckBox rememberChoice = (CheckBox) layout.findViewById( 1193 R.id.sms_short_code_remember_choice_checkbox); 1194 rememberChoice.setOnCheckedChangeListener(listener); 1195 1196 AlertDialog d = new AlertDialog.Builder(mContext) 1197 .setView(layout) 1198 .setPositiveButton(r.getString(R.string.sms_short_code_confirm_allow), listener) 1199 .setNegativeButton(r.getString(R.string.sms_short_code_confirm_deny), listener) 1200 .setOnCancelListener(listener) 1201 .create(); 1202 1203 d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); 1204 d.show(); 1205 1206 listener.setPositiveButton(d.getButton(DialogInterface.BUTTON_POSITIVE)); 1207 listener.setNegativeButton(d.getButton(DialogInterface.BUTTON_NEGATIVE)); 1208 } 1209 1210 /** 1211 * Returns the premium SMS permission for the specified package. If the package has never 1212 * been seen before, the default {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ASK_USER} 1213 * will be returned. 1214 * @param packageName the name of the package to query permission 1215 * @return one of {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_UNKNOWN}, 1216 * {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ASK_USER}, 1217 * {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_NEVER_ALLOW}, or 1218 * {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW} 1219 */ 1220 public int getPremiumSmsPermission(String packageName) { 1221 return mUsageMonitor.getPremiumSmsPermission(packageName); 1222 } 1223 1224 /** 1225 * Sets the premium SMS permission for the specified package and save the value asynchronously 1226 * to persistent storage. 1227 * @param packageName the name of the package to set permission 1228 * @param permission one of {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ASK_USER}, 1229 * {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_NEVER_ALLOW}, or 1230 * {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW} 1231 */ 1232 public void setPremiumSmsPermission(String packageName, int permission) { 1233 mUsageMonitor.setPremiumSmsPermission(packageName, permission); 1234 } 1235 1236 /** 1237 * Send the message along to the radio. 1238 * 1239 * @param tracker holds the SMS message to send 1240 */ 1241 protected abstract void sendSms(SmsTracker tracker); 1242 1243 /** 1244 * Send the multi-part SMS based on multipart Sms tracker 1245 * 1246 * @param tracker holds the multipart Sms tracker ready to be sent 1247 */ 1248 private void sendMultipartSms(SmsTracker tracker) { 1249 ArrayList<String> parts; 1250 ArrayList<PendingIntent> sentIntents; 1251 ArrayList<PendingIntent> deliveryIntents; 1252 1253 HashMap<String, Object> map = tracker.mData; 1254 1255 String destinationAddress = (String) map.get("destination"); 1256 String scAddress = (String) map.get("scaddress"); 1257 1258 parts = (ArrayList<String>) map.get("parts"); 1259 sentIntents = (ArrayList<PendingIntent>) map.get("sentIntents"); 1260 deliveryIntents = (ArrayList<PendingIntent>) map.get("deliveryIntents"); 1261 1262 // check if in service 1263 int ss = mPhone.getServiceState().getState(); 1264 if (ss != ServiceState.STATE_IN_SERVICE) { 1265 for (int i = 0, count = parts.size(); i < count; i++) { 1266 PendingIntent sentIntent = null; 1267 if (sentIntents != null && sentIntents.size() > i) { 1268 sentIntent = sentIntents.get(i); 1269 } 1270 handleNotInService(ss, sentIntent); 1271 } 1272 return; 1273 } 1274 1275 sendMultipartText(destinationAddress, scAddress, parts, sentIntents, deliveryIntents); 1276 } 1277 1278 /** 1279 * Send an acknowledge message. 1280 * @param success indicates that last message was successfully received. 1281 * @param result result code indicating any error 1282 * @param response callback message sent when operation completes. 1283 */ 1284 protected abstract void acknowledgeLastIncomingSms(boolean success, 1285 int result, Message response); 1286 1287 /** 1288 * Notify interested apps if the framework has rejected an incoming SMS, 1289 * and send an acknowledge message to the network. 1290 * @param success indicates that last message was successfully received. 1291 * @param result result code indicating any error 1292 * @param response callback message sent when operation completes. 1293 */ 1294 private void notifyAndAcknowledgeLastIncomingSms(boolean success, 1295 int result, Message response) { 1296 if (!success) { 1297 // broadcast SMS_REJECTED_ACTION intent 1298 Intent intent = new Intent(Intents.SMS_REJECTED_ACTION); 1299 intent.putExtra("result", result); 1300 mWakeLock.acquire(WAKE_LOCK_TIMEOUT); 1301 mContext.sendBroadcast(intent, "android.permission.RECEIVE_SMS"); 1302 } 1303 acknowledgeLastIncomingSms(success, result, response); 1304 } 1305 1306 /** 1307 * Keeps track of an SMS that has been sent to the RIL, until it has 1308 * successfully been sent, or we're done trying. 1309 * 1310 */ 1311 protected static final class SmsTracker { 1312 // fields need to be public for derived SmsDispatchers 1313 public final HashMap<String, Object> mData; 1314 public int mRetryCount; 1315 public int mMessageRef; 1316 1317 public final PendingIntent mSentIntent; 1318 public final PendingIntent mDeliveryIntent; 1319 1320 public final PackageInfo mAppInfo; 1321 public final String mDestAddress; 1322 1323 public SmsTracker(HashMap<String, Object> data, PendingIntent sentIntent, 1324 PendingIntent deliveryIntent, PackageInfo appInfo, String destAddr) { 1325 mData = data; 1326 mSentIntent = sentIntent; 1327 mDeliveryIntent = deliveryIntent; 1328 mRetryCount = 0; 1329 mAppInfo = appInfo; 1330 mDestAddress = destAddr; 1331 } 1332 1333 /** 1334 * Returns whether this tracker holds a multi-part SMS. 1335 * @return true if the tracker holds a multi-part SMS; false otherwise 1336 */ 1337 protected boolean isMultipart() { 1338 HashMap map = mData; 1339 return map.containsKey("parts"); 1340 } 1341 } 1342 1343 /** 1344 * Dialog listener for SMS confirmation dialog. 1345 */ 1346 private final class ConfirmDialogListener 1347 implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener, 1348 CompoundButton.OnCheckedChangeListener { 1349 1350 private final SmsTracker mTracker; 1351 private Button mPositiveButton; 1352 private Button mNegativeButton; 1353 private boolean mRememberChoice; // default is unchecked 1354 private final TextView mRememberUndoInstruction; 1355 1356 ConfirmDialogListener(SmsTracker tracker, TextView textView) { 1357 mTracker = tracker; 1358 mRememberUndoInstruction = textView; 1359 } 1360 1361 void setPositiveButton(Button button) { 1362 mPositiveButton = button; 1363 } 1364 1365 void setNegativeButton(Button button) { 1366 mNegativeButton = button; 1367 } 1368 1369 @Override 1370 public void onClick(DialogInterface dialog, int which) { 1371 // Always set the SMS permission so that Settings will show a permission setting 1372 // for the app (it won't be shown until after the app tries to send to a short code). 1373 int newSmsPermission = SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ASK_USER; 1374 1375 if (which == DialogInterface.BUTTON_POSITIVE) { 1376 Log.d(TAG, "CONFIRM sending SMS"); 1377 // XXX this is lossy- apps can have more than one signature 1378 EventLog.writeEvent(EventLogTags.SMS_SENT_BY_USER, 1379 mTracker.mAppInfo.signatures[0].toCharsString()); 1380 sendMessage(obtainMessage(EVENT_SEND_CONFIRMED_SMS, mTracker)); 1381 if (mRememberChoice) { 1382 newSmsPermission = SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW; 1383 } 1384 } else if (which == DialogInterface.BUTTON_NEGATIVE) { 1385 Log.d(TAG, "DENY sending SMS"); 1386 // XXX this is lossy- apps can have more than one signature 1387 EventLog.writeEvent(EventLogTags.SMS_DENIED_BY_USER, 1388 mTracker.mAppInfo.signatures[0].toCharsString()); 1389 sendMessage(obtainMessage(EVENT_STOP_SENDING, mTracker)); 1390 if (mRememberChoice) { 1391 newSmsPermission = SmsUsageMonitor.PREMIUM_SMS_PERMISSION_NEVER_ALLOW; 1392 } 1393 } 1394 setPremiumSmsPermission(mTracker.mAppInfo.packageName, newSmsPermission); 1395 } 1396 1397 @Override 1398 public void onCancel(DialogInterface dialog) { 1399 Log.d(TAG, "dialog dismissed: don't send SMS"); 1400 sendMessage(obtainMessage(EVENT_STOP_SENDING, mTracker)); 1401 } 1402 1403 @Override 1404 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 1405 Log.d(TAG, "remember this choice: " + isChecked); 1406 mRememberChoice = isChecked; 1407 if (isChecked) { 1408 mPositiveButton.setText(R.string.sms_short_code_confirm_always_allow); 1409 mNegativeButton.setText(R.string.sms_short_code_confirm_never_allow); 1410 if (mRememberUndoInstruction != null) { 1411 mRememberUndoInstruction. 1412 setText(R.string.sms_short_code_remember_undo_instruction); 1413 mRememberUndoInstruction.setPadding(0,0,0,32); 1414 } 1415 } else { 1416 mPositiveButton.setText(R.string.sms_short_code_confirm_allow); 1417 mNegativeButton.setText(R.string.sms_short_code_confirm_deny); 1418 if (mRememberUndoInstruction != null) { 1419 mRememberUndoInstruction.setText(""); 1420 mRememberUndoInstruction.setPadding(0,0,0,0); 1421 } 1422 } 1423 } 1424 } 1425 1426 private final BroadcastReceiver mResultReceiver = new BroadcastReceiver() { 1427 @Override 1428 public void onReceive(Context context, Intent intent) { 1429 // Assume the intent is one of the SMS receive intents that 1430 // was sent as an ordered broadcast. Check result and ACK. 1431 int rc = getResultCode(); 1432 boolean success = (rc == Activity.RESULT_OK) 1433 || (rc == Intents.RESULT_SMS_HANDLED); 1434 1435 // For a multi-part message, this only ACKs the last part. 1436 // Previous parts were ACK'd as they were received. 1437 acknowledgeLastIncomingSms(success, rc, null); 1438 } 1439 }; 1440 1441 protected void dispatchBroadcastMessage(SmsCbMessage message) { 1442 if (message.isEmergencyMessage()) { 1443 Intent intent = new Intent(Intents.SMS_EMERGENCY_CB_RECEIVED_ACTION); 1444 intent.putExtra("message", message); 1445 Log.d(TAG, "Dispatching emergency SMS CB"); 1446 dispatch(intent, RECEIVE_EMERGENCY_BROADCAST_PERMISSION); 1447 } else { 1448 Intent intent = new Intent(Intents.SMS_CB_RECEIVED_ACTION); 1449 intent.putExtra("message", message); 1450 Log.d(TAG, "Dispatching SMS CB"); 1451 dispatch(intent, RECEIVE_SMS_PERMISSION); 1452 } 1453 } 1454} 1455