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