1 /* 2 * Copyright (C) 2006 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.internal.telephony; 18 19 import android.app.Activity; 20 import android.app.AlertDialog; 21 import android.app.PendingIntent; 22 import android.app.PendingIntent.CanceledException; 23 import android.content.ContentResolver; 24 import android.content.ContentValues; 25 import android.content.Context; 26 import android.content.DialogInterface; 27 import android.content.Intent; 28 import android.content.pm.ApplicationInfo; 29 import android.content.pm.PackageInfo; 30 import android.content.pm.PackageManager; 31 import android.content.res.Resources; 32 import android.database.ContentObserver; 33 import android.database.sqlite.SqliteWrapper; 34 import android.net.Uri; 35 import android.os.AsyncResult; 36 import android.os.Binder; 37 import android.os.Handler; 38 import android.os.Message; 39 import android.os.SystemProperties; 40 import android.provider.Settings; 41 import android.provider.Telephony; 42 import android.provider.Telephony.Sms; 43 import android.telephony.PhoneNumberUtils; 44 import android.telephony.Rlog; 45 import android.telephony.ServiceState; 46 import android.telephony.TelephonyManager; 47 import android.text.Html; 48 import android.text.Spanned; 49 import android.util.EventLog; 50 import android.view.LayoutInflater; 51 import android.view.View; 52 import android.view.ViewGroup; 53 import android.view.WindowManager; 54 import android.widget.Button; 55 import android.widget.CheckBox; 56 import android.widget.CompoundButton; 57 import android.widget.TextView; 58 59 import com.android.internal.R; 60 import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails; 61 import com.android.internal.telephony.ImsSMSDispatcher; 62 63 import java.util.ArrayList; 64 import java.util.HashMap; 65 import java.util.Random; 66 import java.util.concurrent.atomic.AtomicInteger; 67 68 import static android.telephony.SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE; 69 import static android.telephony.SmsManager.RESULT_ERROR_GENERIC_FAILURE; 70 import static android.telephony.SmsManager.RESULT_ERROR_LIMIT_EXCEEDED; 71 import static android.telephony.SmsManager.RESULT_ERROR_NO_SERVICE; 72 import static android.telephony.SmsManager.RESULT_ERROR_NULL_PDU; 73 import static android.telephony.SmsManager.RESULT_ERROR_RADIO_OFF; 74 75 public abstract class SMSDispatcher extends Handler { 76 static final String TAG = "SMSDispatcher"; // accessed from inner class 77 static final boolean DBG = false; 78 private static final String SEND_NEXT_MSG_EXTRA = "SendNextMsg"; 79 80 /** Permission required to send SMS to short codes without user confirmation. */ 81 private static final String SEND_SMS_NO_CONFIRMATION_PERMISSION = 82 "android.permission.SEND_SMS_NO_CONFIRMATION"; 83 84 private static final int PREMIUM_RULE_USE_SIM = 1; 85 private static final int PREMIUM_RULE_USE_NETWORK = 2; 86 private static final int PREMIUM_RULE_USE_BOTH = 3; 87 private final AtomicInteger mPremiumSmsRule = new AtomicInteger(PREMIUM_RULE_USE_SIM); 88 private final SettingsObserver mSettingsObserver; 89 90 /** SMS send complete. */ 91 protected static final int EVENT_SEND_SMS_COMPLETE = 2; 92 93 /** Retry sending a previously failed SMS message */ 94 private static final int EVENT_SEND_RETRY = 3; 95 96 /** Confirmation required for sending a large number of messages. */ 97 private static final int EVENT_SEND_LIMIT_REACHED_CONFIRMATION = 4; 98 99 /** Send the user confirmed SMS */ 100 static final int EVENT_SEND_CONFIRMED_SMS = 5; // accessed from inner class 101 102 /** Don't send SMS (user did not confirm). */ 103 static final int EVENT_STOP_SENDING = 7; // accessed from inner class 104 105 /** Confirmation required for third-party apps sending to an SMS short code. */ 106 private static final int EVENT_CONFIRM_SEND_TO_POSSIBLE_PREMIUM_SHORT_CODE = 8; 107 108 /** Confirmation required for third-party apps sending to an SMS short code. */ 109 private static final int EVENT_CONFIRM_SEND_TO_PREMIUM_SHORT_CODE = 9; 110 111 /** Handle status report from {@code CdmaInboundSmsHandler}. */ 112 protected static final int EVENT_HANDLE_STATUS_REPORT = 10; 113 114 /** Radio is ON */ 115 protected static final int EVENT_RADIO_ON = 11; 116 117 /** IMS registration/SMS format changed */ 118 protected static final int EVENT_IMS_STATE_CHANGED = 12; 119 120 /** Callback from RIL_REQUEST_IMS_REGISTRATION_STATE */ 121 protected static final int EVENT_IMS_STATE_DONE = 13; 122 123 // other 124 protected static final int EVENT_NEW_ICC_SMS = 14; 125 protected static final int EVENT_ICC_CHANGED = 15; 126 127 protected PhoneBase mPhone; 128 protected final Context mContext; 129 protected final ContentResolver mResolver; 130 protected final CommandsInterface mCi; 131 protected final TelephonyManager mTelephonyManager; 132 133 /** Maximum number of times to retry sending a failed SMS. */ 134 private static final int MAX_SEND_RETRIES = 3; 135 /** Delay before next send attempt on a failed SMS, in milliseconds. */ 136 private static final int SEND_RETRY_DELAY = 2000; 137 /** single part SMS */ 138 private static final int SINGLE_PART_SMS = 1; 139 /** Message sending queue limit */ 140 private static final int MO_MSG_QUEUE_LIMIT = 5; 141 142 /** 143 * Message reference for a CONCATENATED_8_BIT_REFERENCE or 144 * CONCATENATED_16_BIT_REFERENCE message set. Should be 145 * incremented for each set of concatenated messages. 146 * Static field shared by all dispatcher objects. 147 */ 148 private static int sConcatenatedRef = new Random().nextInt(256); 149 150 /** Outgoing message counter. Shared by all dispatchers. */ 151 private SmsUsageMonitor mUsageMonitor; 152 153 private ImsSMSDispatcher mImsSMSDispatcher; 154 155 /** Number of outgoing SmsTrackers waiting for user confirmation. */ 156 private int mPendingTrackerCount; 157 158 /* Flags indicating whether the current device allows sms service */ 159 protected boolean mSmsCapable = true; 160 protected boolean mSmsSendDisabled; 161 162 protected int mRemainingMessages = -1; 163 164 protected static int getNextConcatenatedRef() { 165 sConcatenatedRef += 1; 166 return sConcatenatedRef; 167 } 168 169 /** 170 * Create a new SMS dispatcher. 171 * @param phone the Phone to use 172 * @param usageMonitor the SmsUsageMonitor to use 173 */ 174 protected SMSDispatcher(PhoneBase phone, SmsUsageMonitor usageMonitor, 175 ImsSMSDispatcher imsSMSDispatcher) { 176 mPhone = phone; 177 mImsSMSDispatcher = imsSMSDispatcher; 178 mContext = phone.getContext(); 179 mResolver = mContext.getContentResolver(); 180 mCi = phone.mCi; 181 mUsageMonitor = usageMonitor; 182 mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); 183 mSettingsObserver = new SettingsObserver(this, mPremiumSmsRule, mContext); 184 mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor( 185 Settings.Global.SMS_SHORT_CODE_RULE), false, mSettingsObserver); 186 187 mSmsCapable = mContext.getResources().getBoolean( 188 com.android.internal.R.bool.config_sms_capable); 189 mSmsSendDisabled = !SystemProperties.getBoolean( 190 TelephonyProperties.PROPERTY_SMS_SEND, mSmsCapable); 191 Rlog.d(TAG, "SMSDispatcher: ctor mSmsCapable=" + mSmsCapable + " format=" + getFormat() 192 + " mSmsSendDisabled=" + mSmsSendDisabled); 193 } 194 195 /** 196 * Observe the secure setting for updated premium sms determination rules 197 */ 198 private static class SettingsObserver extends ContentObserver { 199 private final AtomicInteger mPremiumSmsRule; 200 private final Context mContext; 201 SettingsObserver(Handler handler, AtomicInteger premiumSmsRule, Context context) { 202 super(handler); 203 mPremiumSmsRule = premiumSmsRule; 204 mContext = context; 205 onChange(false); // load initial value; 206 } 207 208 @Override 209 public void onChange(boolean selfChange) { 210 mPremiumSmsRule.set(Settings.Global.getInt(mContext.getContentResolver(), 211 Settings.Global.SMS_SHORT_CODE_RULE, PREMIUM_RULE_USE_SIM)); 212 } 213 } 214 215 protected void updatePhoneObject(PhoneBase phone) { 216 mPhone = phone; 217 mUsageMonitor = phone.mSmsUsageMonitor; 218 Rlog.d(TAG, "Active phone changed to " + mPhone.getPhoneName() ); 219 } 220 221 /** Unregister for incoming SMS events. */ 222 public void dispose() { 223 mContext.getContentResolver().unregisterContentObserver(mSettingsObserver); 224 } 225 226 /** 227 * The format of the message PDU in the associated broadcast intent. 228 * This will be either "3gpp" for GSM/UMTS/LTE messages in 3GPP format 229 * or "3gpp2" for CDMA/LTE messages in 3GPP2 format. 230 * 231 * Note: All applications which handle incoming SMS messages by processing the 232 * SMS_RECEIVED_ACTION broadcast intent MUST pass the "format" extra from the intent 233 * into the new methods in {@link android.telephony.SmsMessage} which take an 234 * extra format parameter. This is required in order to correctly decode the PDU on 235 * devices which require support for both 3GPP and 3GPP2 formats at the same time, 236 * such as CDMA/LTE devices and GSM/CDMA world phones. 237 * 238 * @return the format of the message PDU 239 */ 240 protected abstract String getFormat(); 241 242 /** 243 * Pass the Message object to subclass to handle. Currently used to pass CDMA status reports 244 * from {@link com.android.internal.telephony.cdma.CdmaInboundSmsHandler}. 245 * @param o the SmsMessage containing the status report 246 */ 247 protected void handleStatusReport(Object o) { 248 Rlog.d(TAG, "handleStatusReport() called with no subclass."); 249 } 250 251 /* TODO: Need to figure out how to keep track of status report routing in a 252 * persistent manner. If the phone process restarts (reboot or crash), 253 * we will lose this list and any status reports that come in after 254 * will be dropped. 255 */ 256 /** Sent messages awaiting a delivery status report. */ 257 protected final ArrayList<SmsTracker> deliveryPendingList = new ArrayList<SmsTracker>(); 258 259 /** 260 * Handles events coming from the phone stack. Overridden from handler. 261 * 262 * @param msg the message to handle 263 */ 264 @Override 265 public void handleMessage(Message msg) { 266 switch (msg.what) { 267 case EVENT_SEND_SMS_COMPLETE: 268 // An outbound SMS has been successfully transferred, or failed. 269 handleSendComplete((AsyncResult) msg.obj); 270 break; 271 272 case EVENT_SEND_RETRY: 273 Rlog.d(TAG, "SMS retry.."); 274 sendRetrySms((SmsTracker) msg.obj); 275 break; 276 277 case EVENT_SEND_LIMIT_REACHED_CONFIRMATION: 278 handleReachSentLimit((SmsTracker)(msg.obj)); 279 break; 280 281 case EVENT_CONFIRM_SEND_TO_POSSIBLE_PREMIUM_SHORT_CODE: 282 handleConfirmShortCode(false, (SmsTracker)(msg.obj)); 283 break; 284 285 case EVENT_CONFIRM_SEND_TO_PREMIUM_SHORT_CODE: 286 handleConfirmShortCode(true, (SmsTracker)(msg.obj)); 287 break; 288 289 case EVENT_SEND_CONFIRMED_SMS: 290 { 291 SmsTracker tracker = (SmsTracker) msg.obj; 292 if (tracker.isMultipart()) { 293 sendMultipartSms(tracker); 294 } else { 295 sendSms(tracker); 296 } 297 mPendingTrackerCount--; 298 break; 299 } 300 301 case EVENT_STOP_SENDING: 302 { 303 SmsTracker tracker = (SmsTracker) msg.obj; 304 if (tracker.mSentIntent != null) { 305 try { 306 tracker.mSentIntent.send(RESULT_ERROR_LIMIT_EXCEEDED); 307 } catch (CanceledException ex) { 308 Rlog.e(TAG, "failed to send RESULT_ERROR_LIMIT_EXCEEDED"); 309 } 310 } 311 mPendingTrackerCount--; 312 break; 313 } 314 315 case EVENT_HANDLE_STATUS_REPORT: 316 handleStatusReport(msg.obj); 317 break; 318 319 default: 320 Rlog.e(TAG, "handleMessage() ignoring message of unexpected type " + msg.what); 321 } 322 } 323 324 /** 325 * Called when SMS send completes. Broadcasts a sentIntent on success. 326 * On failure, either sets up retries or broadcasts a sentIntent with 327 * the failure in the result code. 328 * 329 * @param ar AsyncResult passed into the message handler. ar.result should 330 * an SmsResponse instance if send was successful. ar.userObj 331 * should be an SmsTracker instance. 332 */ 333 protected void handleSendComplete(AsyncResult ar) { 334 SmsTracker tracker = (SmsTracker) ar.userObj; 335 PendingIntent sentIntent = tracker.mSentIntent; 336 337 if (ar.result != null) { 338 tracker.mMessageRef = ((SmsResponse)ar.result).mMessageRef; 339 } else { 340 Rlog.d(TAG, "SmsResponse was null"); 341 } 342 343 if (ar.exception == null) { 344 if (DBG) Rlog.d(TAG, "SMS send complete. Broadcasting intent: " + sentIntent); 345 346 if (SmsApplication.shouldWriteMessageForPackage( 347 tracker.mAppInfo.applicationInfo.packageName, mContext)) { 348 // Persist it into the SMS database as a sent message 349 // so the user can see it in their default app. 350 tracker.writeSentMessage(mContext); 351 } 352 353 if (tracker.mDeliveryIntent != null) { 354 // Expecting a status report. Add it to the list. 355 deliveryPendingList.add(tracker); 356 } 357 358 if (sentIntent != null) { 359 try { 360 if (mRemainingMessages > -1) { 361 mRemainingMessages--; 362 } 363 364 if (mRemainingMessages == 0) { 365 Intent sendNext = new Intent(); 366 sendNext.putExtra(SEND_NEXT_MSG_EXTRA, true); 367 sentIntent.send(mContext, Activity.RESULT_OK, sendNext); 368 } else { 369 sentIntent.send(Activity.RESULT_OK); 370 } 371 } catch (CanceledException ex) {} 372 } 373 } else { 374 if (DBG) Rlog.d(TAG, "SMS send failed"); 375 376 int ss = mPhone.getServiceState().getState(); 377 378 if ( tracker.mImsRetry > 0 && ss != ServiceState.STATE_IN_SERVICE) { 379 // This is retry after failure over IMS but voice is not available. 380 // Set retry to max allowed, so no retry is sent and 381 // cause RESULT_ERROR_GENERIC_FAILURE to be returned to app. 382 tracker.mRetryCount = MAX_SEND_RETRIES; 383 384 Rlog.d(TAG, "handleSendComplete: Skipping retry: " 385 +" isIms()="+isIms() 386 +" mRetryCount="+tracker.mRetryCount 387 +" mImsRetry="+tracker.mImsRetry 388 +" mMessageRef="+tracker.mMessageRef 389 +" SS= "+mPhone.getServiceState().getState()); 390 } 391 392 // if sms over IMS is not supported on data and voice is not available... 393 if (!isIms() && ss != ServiceState.STATE_IN_SERVICE) { 394 handleNotInService(ss, tracker.mSentIntent); 395 } else if ((((CommandException)(ar.exception)).getCommandError() 396 == CommandException.Error.SMS_FAIL_RETRY) && 397 tracker.mRetryCount < MAX_SEND_RETRIES) { 398 // Retry after a delay if needed. 399 // TODO: According to TS 23.040, 9.2.3.6, we should resend 400 // with the same TP-MR as the failed message, and 401 // TP-RD set to 1. However, we don't have a means of 402 // knowing the MR for the failed message (EF_SMSstatus 403 // may or may not have the MR corresponding to this 404 // message, depending on the failure). Also, in some 405 // implementations this retry is handled by the baseband. 406 tracker.mRetryCount++; 407 Message retryMsg = obtainMessage(EVENT_SEND_RETRY, tracker); 408 sendMessageDelayed(retryMsg, SEND_RETRY_DELAY); 409 } else if (tracker.mSentIntent != null) { 410 int error = RESULT_ERROR_GENERIC_FAILURE; 411 412 if (((CommandException)(ar.exception)).getCommandError() 413 == CommandException.Error.FDN_CHECK_FAILURE) { 414 error = RESULT_ERROR_FDN_CHECK_FAILURE; 415 } 416 // Done retrying; return an error to the app. 417 try { 418 Intent fillIn = new Intent(); 419 if (ar.result != null) { 420 fillIn.putExtra("errorCode", ((SmsResponse)ar.result).mErrorCode); 421 } 422 if (mRemainingMessages > -1) { 423 mRemainingMessages--; 424 } 425 426 if (mRemainingMessages == 0) { 427 fillIn.putExtra(SEND_NEXT_MSG_EXTRA, true); 428 } 429 430 tracker.mSentIntent.send(mContext, error, fillIn); 431 } catch (CanceledException ex) {} 432 } 433 } 434 } 435 436 /** 437 * Handles outbound message when the phone is not in service. 438 * 439 * @param ss Current service state. Valid values are: 440 * OUT_OF_SERVICE 441 * EMERGENCY_ONLY 442 * POWER_OFF 443 * @param sentIntent the PendingIntent to send the error to 444 */ 445 protected static void handleNotInService(int ss, PendingIntent sentIntent) { 446 if (sentIntent != null) { 447 try { 448 if (ss == ServiceState.STATE_POWER_OFF) { 449 sentIntent.send(RESULT_ERROR_RADIO_OFF); 450 } else { 451 sentIntent.send(RESULT_ERROR_NO_SERVICE); 452 } 453 } catch (CanceledException ex) {} 454 } 455 } 456 457 /** 458 * Send a data based SMS to a specific application port. 459 * 460 * @param destAddr the address to send the message to 461 * @param scAddr is the service center address or null to use 462 * the current default SMSC 463 * @param destPort the port to deliver the message to 464 * @param data the body of the message to send 465 * @param sentIntent if not NULL this <code>PendingIntent</code> is 466 * broadcast when the message is successfully sent, or failed. 467 * The result code will be <code>Activity.RESULT_OK<code> for success, 468 * or one of these errors:<br> 469 * <code>RESULT_ERROR_GENERIC_FAILURE</code><br> 470 * <code>RESULT_ERROR_RADIO_OFF</code><br> 471 * <code>RESULT_ERROR_NULL_PDU</code><br> 472 * <code>RESULT_ERROR_NO_SERVICE</code><br>. 473 * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include 474 * the extra "errorCode" containing a radio technology specific value, 475 * generally only useful for troubleshooting.<br> 476 * The per-application based SMS control checks sentIntent. If sentIntent 477 * is NULL the caller will be checked against all unknown applications, 478 * which cause smaller number of SMS to be sent in checking period. 479 * @param deliveryIntent if not NULL this <code>PendingIntent</code> is 480 * broadcast when the message is delivered to the recipient. The 481 * raw pdu of the status report is in the extended data ("pdu"). 482 */ 483 protected abstract void sendData(String destAddr, String scAddr, int destPort, 484 byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent); 485 486 /** 487 * Send a text based SMS. 488 * 489 * @param destAddr the address to send the message to 490 * @param scAddr is the service center address or null to use 491 * the current default SMSC 492 * @param text the body of the message to send 493 * @param sentIntent if not NULL this <code>PendingIntent</code> is 494 * broadcast when the message is successfully sent, or failed. 495 * The result code will be <code>Activity.RESULT_OK<code> for success, 496 * or one of these errors:<br> 497 * <code>RESULT_ERROR_GENERIC_FAILURE</code><br> 498 * <code>RESULT_ERROR_RADIO_OFF</code><br> 499 * <code>RESULT_ERROR_NULL_PDU</code><br> 500 * <code>RESULT_ERROR_NO_SERVICE</code><br>. 501 * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include 502 * the extra "errorCode" containing a radio technology specific value, 503 * generally only useful for troubleshooting.<br> 504 * The per-application based SMS control checks sentIntent. If sentIntent 505 * is NULL the caller will be checked against all unknown applications, 506 * which cause smaller number of SMS to be sent in checking period. 507 * @param deliveryIntent if not NULL this <code>PendingIntent</code> is 508 * broadcast when the message is delivered to the recipient. The 509 * raw pdu of the status report is in the extended data ("pdu"). 510 */ 511 protected abstract void sendText(String destAddr, String scAddr, 512 String text, PendingIntent sentIntent, PendingIntent deliveryIntent); 513 514 /** 515 * Calculate the number of septets needed to encode the message. 516 * 517 * @param messageBody the message to encode 518 * @param use7bitOnly ignore (but still count) illegal characters if true 519 * @return TextEncodingDetails 520 */ 521 protected abstract TextEncodingDetails calculateLength(CharSequence messageBody, 522 boolean use7bitOnly); 523 524 /** 525 * Send a multi-part text based SMS. 526 * 527 * @param destAddr the address to send the message to 528 * @param scAddr is the service center address or null to use 529 * the current default SMSC 530 * @param parts an <code>ArrayList</code> of strings that, in order, 531 * comprise the original message 532 * @param sentIntents if not null, an <code>ArrayList</code> of 533 * <code>PendingIntent</code>s (one for each message part) that is 534 * broadcast when the corresponding message part has been sent. 535 * The result code will be <code>Activity.RESULT_OK<code> for success, 536 * or one of these errors: 537 * <code>RESULT_ERROR_GENERIC_FAILURE</code> 538 * <code>RESULT_ERROR_RADIO_OFF</code> 539 * <code>RESULT_ERROR_NULL_PDU</code> 540 * <code>RESULT_ERROR_NO_SERVICE</code>. 541 * The per-application based SMS control checks sentIntent. If sentIntent 542 * is NULL the caller will be checked against all unknown applications, 543 * which cause smaller number of SMS to be sent in checking period. 544 * @param deliveryIntents if not null, an <code>ArrayList</code> of 545 * <code>PendingIntent</code>s (one for each message part) that is 546 * broadcast when the corresponding message part has been delivered 547 * to the recipient. The raw pdu of the status report is in the 548 * extended data ("pdu"). 549 */ 550 protected void sendMultipartText(String destAddr, String scAddr, 551 ArrayList<String> parts, ArrayList<PendingIntent> sentIntents, 552 ArrayList<PendingIntent> deliveryIntents) { 553 554 int refNumber = getNextConcatenatedRef() & 0x00FF; 555 int msgCount = parts.size(); 556 int encoding = SmsConstants.ENCODING_UNKNOWN; 557 558 mRemainingMessages = msgCount; 559 560 TextEncodingDetails[] encodingForParts = new TextEncodingDetails[msgCount]; 561 for (int i = 0; i < msgCount; i++) { 562 TextEncodingDetails details = calculateLength(parts.get(i), false); 563 if (encoding != details.codeUnitSize 564 && (encoding == SmsConstants.ENCODING_UNKNOWN 565 || encoding == SmsConstants.ENCODING_7BIT)) { 566 encoding = details.codeUnitSize; 567 } 568 encodingForParts[i] = details; 569 } 570 571 for (int i = 0; i < msgCount; i++) { 572 SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef(); 573 concatRef.refNumber = refNumber; 574 concatRef.seqNumber = i + 1; // 1-based sequence 575 concatRef.msgCount = msgCount; 576 // TODO: We currently set this to true since our messaging app will never 577 // send more than 255 parts (it converts the message to MMS well before that). 578 // However, we should support 3rd party messaging apps that might need 16-bit 579 // references 580 // Note: It's not sufficient to just flip this bit to true; it will have 581 // ripple effects (several calculations assume 8-bit ref). 582 concatRef.isEightBits = true; 583 SmsHeader smsHeader = new SmsHeader(); 584 smsHeader.concatRef = concatRef; 585 586 // Set the national language tables for 3GPP 7-bit encoding, if enabled. 587 if (encoding == SmsConstants.ENCODING_7BIT) { 588 smsHeader.languageTable = encodingForParts[i].languageTable; 589 smsHeader.languageShiftTable = encodingForParts[i].languageShiftTable; 590 } 591 592 PendingIntent sentIntent = null; 593 if (sentIntents != null && sentIntents.size() > i) { 594 sentIntent = sentIntents.get(i); 595 } 596 597 PendingIntent deliveryIntent = null; 598 if (deliveryIntents != null && deliveryIntents.size() > i) { 599 deliveryIntent = deliveryIntents.get(i); 600 } 601 602 sendNewSubmitPdu(destAddr, scAddr, parts.get(i), smsHeader, encoding, 603 sentIntent, deliveryIntent, (i == (msgCount - 1))); 604 } 605 606 } 607 608 /** 609 * Create a new SubmitPdu and send it. 610 */ 611 protected abstract void sendNewSubmitPdu(String destinationAddress, String scAddress, 612 String message, SmsHeader smsHeader, int encoding, 613 PendingIntent sentIntent, PendingIntent deliveryIntent, boolean lastPart); 614 615 /** 616 * Send a SMS 617 * @param tracker will contain: 618 * -smsc the SMSC to send the message through, or NULL for the 619 * default SMSC 620 * -pdu the raw PDU to send 621 * -sentIntent if not NULL this <code>Intent</code> is 622 * broadcast when the message is successfully sent, or failed. 623 * The result code will be <code>Activity.RESULT_OK<code> for success, 624 * or one of these errors: 625 * <code>RESULT_ERROR_GENERIC_FAILURE</code> 626 * <code>RESULT_ERROR_RADIO_OFF</code> 627 * <code>RESULT_ERROR_NULL_PDU</code> 628 * <code>RESULT_ERROR_NO_SERVICE</code>. 629 * The per-application based SMS control checks sentIntent. If sentIntent 630 * is NULL the caller will be checked against all unknown applications, 631 * which cause smaller number of SMS to be sent in checking period. 632 * -deliveryIntent if not NULL this <code>Intent</code> is 633 * broadcast when the message is delivered to the recipient. The 634 * raw pdu of the status report is in the extended data ("pdu"). 635 * -param destAddr the destination phone number (for short code confirmation) 636 */ 637 protected void sendRawPdu(SmsTracker tracker) { 638 HashMap map = tracker.mData; 639 byte pdu[] = (byte[]) map.get("pdu"); 640 641 PendingIntent sentIntent = tracker.mSentIntent; 642 if (mSmsSendDisabled) { 643 if (sentIntent != null) { 644 try { 645 sentIntent.send(RESULT_ERROR_NO_SERVICE); 646 } catch (CanceledException ex) {} 647 } 648 Rlog.d(TAG, "Device does not support sending sms."); 649 return; 650 } 651 652 if (pdu == null) { 653 if (sentIntent != null) { 654 try { 655 sentIntent.send(RESULT_ERROR_NULL_PDU); 656 } catch (CanceledException ex) {} 657 } 658 return; 659 } 660 661 // Get calling app package name via UID from Binder call 662 PackageManager pm = mContext.getPackageManager(); 663 String[] packageNames = pm.getPackagesForUid(Binder.getCallingUid()); 664 665 if (packageNames == null || packageNames.length == 0) { 666 // Refuse to send SMS if we can't get the calling package name. 667 Rlog.e(TAG, "Can't get calling app package name: refusing to send SMS"); 668 if (sentIntent != null) { 669 try { 670 sentIntent.send(RESULT_ERROR_GENERIC_FAILURE); 671 } catch (CanceledException ex) { 672 Rlog.e(TAG, "failed to send error result"); 673 } 674 } 675 return; 676 } 677 678 // Get package info via packagemanager 679 PackageInfo appInfo; 680 try { 681 // XXX this is lossy- apps can share a UID 682 appInfo = pm.getPackageInfo(packageNames[0], PackageManager.GET_SIGNATURES); 683 } catch (PackageManager.NameNotFoundException e) { 684 Rlog.e(TAG, "Can't get calling app package info: refusing to send SMS"); 685 if (sentIntent != null) { 686 try { 687 sentIntent.send(RESULT_ERROR_GENERIC_FAILURE); 688 } catch (CanceledException ex) { 689 Rlog.e(TAG, "failed to send error result"); 690 } 691 } 692 return; 693 } 694 695 // checkDestination() returns true if the destination is not a premium short code or the 696 // sending app is approved to send to short codes. Otherwise, a message is sent to our 697 // handler with the SmsTracker to request user confirmation before sending. 698 if (checkDestination(tracker)) { 699 // check for excessive outgoing SMS usage by this app 700 if (!mUsageMonitor.check(appInfo.packageName, SINGLE_PART_SMS)) { 701 sendMessage(obtainMessage(EVENT_SEND_LIMIT_REACHED_CONFIRMATION, tracker)); 702 return; 703 } 704 705 int ss = mPhone.getServiceState().getState(); 706 707 // if sms over IMS is not supported on data and voice is not available... 708 if (!isIms() && ss != ServiceState.STATE_IN_SERVICE) { 709 handleNotInService(ss, tracker.mSentIntent); 710 } else { 711 sendSms(tracker); 712 } 713 } 714 } 715 716 /** 717 * Check if destination is a potential premium short code and sender is not pre-approved to 718 * send to short codes. 719 * 720 * @param tracker the tracker for the SMS to send 721 * @return true if the destination is approved; false if user confirmation event was sent 722 */ 723 boolean checkDestination(SmsTracker tracker) { 724 if (mContext.checkCallingOrSelfPermission(SEND_SMS_NO_CONFIRMATION_PERMISSION) 725 == PackageManager.PERMISSION_GRANTED) { 726 return true; // app is pre-approved to send to short codes 727 } else { 728 int rule = mPremiumSmsRule.get(); 729 int smsCategory = SmsUsageMonitor.CATEGORY_NOT_SHORT_CODE; 730 if (rule == PREMIUM_RULE_USE_SIM || rule == PREMIUM_RULE_USE_BOTH) { 731 String simCountryIso = mTelephonyManager.getSimCountryIso(); 732 if (simCountryIso == null || simCountryIso.length() != 2) { 733 Rlog.e(TAG, "Can't get SIM country Iso: trying network country Iso"); 734 simCountryIso = mTelephonyManager.getNetworkCountryIso(); 735 } 736 737 smsCategory = mUsageMonitor.checkDestination(tracker.mDestAddress, simCountryIso); 738 } 739 if (rule == PREMIUM_RULE_USE_NETWORK || rule == PREMIUM_RULE_USE_BOTH) { 740 String networkCountryIso = mTelephonyManager.getNetworkCountryIso(); 741 if (networkCountryIso == null || networkCountryIso.length() != 2) { 742 Rlog.e(TAG, "Can't get Network country Iso: trying SIM country Iso"); 743 networkCountryIso = mTelephonyManager.getSimCountryIso(); 744 } 745 746 smsCategory = SmsUsageMonitor.mergeShortCodeCategories(smsCategory, 747 mUsageMonitor.checkDestination(tracker.mDestAddress, networkCountryIso)); 748 } 749 750 if (smsCategory == SmsUsageMonitor.CATEGORY_NOT_SHORT_CODE 751 || smsCategory == SmsUsageMonitor.CATEGORY_FREE_SHORT_CODE 752 || smsCategory == SmsUsageMonitor.CATEGORY_STANDARD_SHORT_CODE) { 753 return true; // not a premium short code 754 } 755 756 // Wait for user confirmation unless the user has set permission to always allow/deny 757 int premiumSmsPermission = mUsageMonitor.getPremiumSmsPermission( 758 tracker.mAppInfo.packageName); 759 if (premiumSmsPermission == SmsUsageMonitor.PREMIUM_SMS_PERMISSION_UNKNOWN) { 760 // First time trying to send to premium SMS. 761 premiumSmsPermission = SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ASK_USER; 762 } 763 764 switch (premiumSmsPermission) { 765 case SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW: 766 Rlog.d(TAG, "User approved this app to send to premium SMS"); 767 return true; 768 769 case SmsUsageMonitor.PREMIUM_SMS_PERMISSION_NEVER_ALLOW: 770 Rlog.w(TAG, "User denied this app from sending to premium SMS"); 771 sendMessage(obtainMessage(EVENT_STOP_SENDING, tracker)); 772 return false; // reject this message 773 774 case SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ASK_USER: 775 default: 776 int event; 777 if (smsCategory == SmsUsageMonitor.CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE) { 778 event = EVENT_CONFIRM_SEND_TO_POSSIBLE_PREMIUM_SHORT_CODE; 779 } else { 780 event = EVENT_CONFIRM_SEND_TO_PREMIUM_SHORT_CODE; 781 } 782 sendMessage(obtainMessage(event, tracker)); 783 return false; // wait for user confirmation 784 } 785 } 786 } 787 788 /** 789 * Deny sending an SMS if the outgoing queue limit is reached. Used when the message 790 * must be confirmed by the user due to excessive usage or potential premium SMS detected. 791 * @param tracker the SmsTracker for the message to send 792 * @return true if the message was denied; false to continue with send confirmation 793 */ 794 private boolean denyIfQueueLimitReached(SmsTracker tracker) { 795 if (mPendingTrackerCount >= MO_MSG_QUEUE_LIMIT) { 796 // Deny sending message when the queue limit is reached. 797 try { 798 if (tracker.mSentIntent != null) { 799 tracker.mSentIntent.send(RESULT_ERROR_LIMIT_EXCEEDED); 800 } 801 } catch (CanceledException ex) { 802 Rlog.e(TAG, "failed to send back RESULT_ERROR_LIMIT_EXCEEDED"); 803 } 804 return true; 805 } 806 mPendingTrackerCount++; 807 return false; 808 } 809 810 /** 811 * Returns the label for the specified app package name. 812 * @param appPackage the package name of the app requesting to send an SMS 813 * @return the label for the specified app, or the package name if getApplicationInfo() fails 814 */ 815 private CharSequence getAppLabel(String appPackage) { 816 PackageManager pm = mContext.getPackageManager(); 817 try { 818 ApplicationInfo appInfo = pm.getApplicationInfo(appPackage, 0); 819 return appInfo.loadLabel(pm); 820 } catch (PackageManager.NameNotFoundException e) { 821 Rlog.e(TAG, "PackageManager Name Not Found for package " + appPackage); 822 return appPackage; // fall back to package name if we can't get app label 823 } 824 } 825 826 /** 827 * Post an alert when SMS needs confirmation due to excessive usage. 828 * @param tracker an SmsTracker for the current message. 829 */ 830 protected void handleReachSentLimit(SmsTracker tracker) { 831 if (denyIfQueueLimitReached(tracker)) { 832 return; // queue limit reached; error was returned to caller 833 } 834 835 CharSequence appLabel = getAppLabel(tracker.mAppInfo.packageName); 836 Resources r = Resources.getSystem(); 837 Spanned messageText = Html.fromHtml(r.getString(R.string.sms_control_message, appLabel)); 838 839 ConfirmDialogListener listener = new ConfirmDialogListener(tracker, null); 840 841 AlertDialog d = new AlertDialog.Builder(mContext) 842 .setTitle(R.string.sms_control_title) 843 .setIcon(R.drawable.stat_sys_warning) 844 .setMessage(messageText) 845 .setPositiveButton(r.getString(R.string.sms_control_yes), listener) 846 .setNegativeButton(r.getString(R.string.sms_control_no), listener) 847 .setOnCancelListener(listener) 848 .create(); 849 850 d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); 851 d.show(); 852 } 853 854 /** 855 * Post an alert for user confirmation when sending to a potential short code. 856 * @param isPremium true if the destination is known to be a premium short code 857 * @param tracker the SmsTracker for the current message. 858 */ 859 protected void handleConfirmShortCode(boolean isPremium, SmsTracker tracker) { 860 if (denyIfQueueLimitReached(tracker)) { 861 return; // queue limit reached; error was returned to caller 862 } 863 864 int detailsId; 865 if (isPremium) { 866 detailsId = R.string.sms_premium_short_code_details; 867 } else { 868 detailsId = R.string.sms_short_code_details; 869 } 870 871 CharSequence appLabel = getAppLabel(tracker.mAppInfo.packageName); 872 Resources r = Resources.getSystem(); 873 Spanned messageText = Html.fromHtml(r.getString(R.string.sms_short_code_confirm_message, 874 appLabel, tracker.mDestAddress)); 875 876 LayoutInflater inflater = (LayoutInflater) mContext.getSystemService( 877 Context.LAYOUT_INFLATER_SERVICE); 878 View layout = inflater.inflate(R.layout.sms_short_code_confirmation_dialog, null); 879 880 ConfirmDialogListener listener = new ConfirmDialogListener(tracker, 881 (TextView)layout.findViewById(R.id.sms_short_code_remember_undo_instruction)); 882 883 884 TextView messageView = (TextView) layout.findViewById(R.id.sms_short_code_confirm_message); 885 messageView.setText(messageText); 886 887 ViewGroup detailsLayout = (ViewGroup) layout.findViewById( 888 R.id.sms_short_code_detail_layout); 889 TextView detailsView = (TextView) detailsLayout.findViewById( 890 R.id.sms_short_code_detail_message); 891 detailsView.setText(detailsId); 892 893 CheckBox rememberChoice = (CheckBox) layout.findViewById( 894 R.id.sms_short_code_remember_choice_checkbox); 895 rememberChoice.setOnCheckedChangeListener(listener); 896 897 AlertDialog d = new AlertDialog.Builder(mContext) 898 .setView(layout) 899 .setPositiveButton(r.getString(R.string.sms_short_code_confirm_allow), listener) 900 .setNegativeButton(r.getString(R.string.sms_short_code_confirm_deny), listener) 901 .setOnCancelListener(listener) 902 .create(); 903 904 d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); 905 d.show(); 906 907 listener.setPositiveButton(d.getButton(DialogInterface.BUTTON_POSITIVE)); 908 listener.setNegativeButton(d.getButton(DialogInterface.BUTTON_NEGATIVE)); 909 } 910 911 /** 912 * Returns the premium SMS permission for the specified package. If the package has never 913 * been seen before, the default {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ASK_USER} 914 * will be returned. 915 * @param packageName the name of the package to query permission 916 * @return one of {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_UNKNOWN}, 917 * {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ASK_USER}, 918 * {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_NEVER_ALLOW}, or 919 * {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW} 920 */ 921 public int getPremiumSmsPermission(String packageName) { 922 return mUsageMonitor.getPremiumSmsPermission(packageName); 923 } 924 925 /** 926 * Sets the premium SMS permission for the specified package and save the value asynchronously 927 * to persistent storage. 928 * @param packageName the name of the package to set permission 929 * @param permission one of {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ASK_USER}, 930 * {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_NEVER_ALLOW}, or 931 * {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW} 932 */ 933 public void setPremiumSmsPermission(String packageName, int permission) { 934 mUsageMonitor.setPremiumSmsPermission(packageName, permission); 935 } 936 937 /** 938 * Send the message along to the radio. 939 * 940 * @param tracker holds the SMS message to send 941 */ 942 protected abstract void sendSms(SmsTracker tracker); 943 944 /** 945 * Retry the message along to the radio. 946 * 947 * @param tracker holds the SMS message to send 948 */ 949 public void sendRetrySms(SmsTracker tracker) { 950 // re-routing to ImsSMSDispatcher 951 if (mImsSMSDispatcher != null) { 952 mImsSMSDispatcher.sendRetrySms(tracker); 953 } else { 954 Rlog.e(TAG, mImsSMSDispatcher + " is null. Retry failed"); 955 } 956 } 957 958 /** 959 * Send the multi-part SMS based on multipart Sms tracker 960 * 961 * @param tracker holds the multipart Sms tracker ready to be sent 962 */ 963 private void sendMultipartSms(SmsTracker tracker) { 964 ArrayList<String> parts; 965 ArrayList<PendingIntent> sentIntents; 966 ArrayList<PendingIntent> deliveryIntents; 967 968 HashMap<String, Object> map = tracker.mData; 969 970 String destinationAddress = (String) map.get("destination"); 971 String scAddress = (String) map.get("scaddress"); 972 973 parts = (ArrayList<String>) map.get("parts"); 974 sentIntents = (ArrayList<PendingIntent>) map.get("sentIntents"); 975 deliveryIntents = (ArrayList<PendingIntent>) map.get("deliveryIntents"); 976 977 // check if in service 978 int ss = mPhone.getServiceState().getState(); 979 // if sms over IMS is not supported on data and voice is not available... 980 if (!isIms() && ss != ServiceState.STATE_IN_SERVICE) { 981 for (int i = 0, count = parts.size(); i < count; i++) { 982 PendingIntent sentIntent = null; 983 if (sentIntents != null && sentIntents.size() > i) { 984 sentIntent = sentIntents.get(i); 985 } 986 handleNotInService(ss, sentIntent); 987 } 988 return; 989 } 990 991 sendMultipartText(destinationAddress, scAddress, parts, sentIntents, deliveryIntents); 992 } 993 994 /** 995 * Keeps track of an SMS that has been sent to the RIL, until it has 996 * successfully been sent, or we're done trying. 997 */ 998 protected static final class SmsTracker { 999 // fields need to be public for derived SmsDispatchers 1000 public final HashMap<String, Object> mData; 1001 public int mRetryCount; 1002 public int mImsRetry; // nonzero indicates initial message was sent over Ims 1003 public int mMessageRef; 1004 String mFormat; 1005 1006 public final PendingIntent mSentIntent; 1007 public final PendingIntent mDeliveryIntent; 1008 1009 public final PackageInfo mAppInfo; 1010 public final String mDestAddress; 1011 1012 private long mTimestamp = System.currentTimeMillis(); 1013 private Uri mSentMessageUri; // Uri of persisted message if we wrote one 1014 1015 private SmsTracker(HashMap<String, Object> data, PendingIntent sentIntent, 1016 PendingIntent deliveryIntent, PackageInfo appInfo, String destAddr, String format) { 1017 mData = data; 1018 mSentIntent = sentIntent; 1019 mDeliveryIntent = deliveryIntent; 1020 mRetryCount = 0; 1021 mAppInfo = appInfo; 1022 mDestAddress = destAddr; 1023 mFormat = format; 1024 mImsRetry = 0; 1025 mMessageRef = 0; 1026 } 1027 1028 /** 1029 * Returns whether this tracker holds a multi-part SMS. 1030 * @return true if the tracker holds a multi-part SMS; false otherwise 1031 */ 1032 boolean isMultipart() { 1033 return mData.containsKey("parts"); 1034 } 1035 1036 /** 1037 * Persist this as a sent message 1038 */ 1039 void writeSentMessage(Context context) { 1040 String text = (String)mData.get("text"); 1041 if (text != null) { 1042 boolean deliveryReport = (mDeliveryIntent != null); 1043 // Using invalid threadId 0 here. When the message is inserted into the db, the 1044 // provider looks up the threadId based on the recipient(s). 1045 mSentMessageUri = Sms.addMessageToUri(context.getContentResolver(), 1046 Telephony.Sms.Sent.CONTENT_URI, 1047 mDestAddress, 1048 text /*body*/, 1049 null /*subject*/, 1050 mTimestamp /*date*/, 1051 true /*read*/, 1052 deliveryReport /*deliveryReport*/, 1053 0 /*threadId*/); 1054 } 1055 } 1056 1057 /** 1058 * Update the status of this message if we persisted it 1059 */ 1060 public void updateSentMessageStatus(Context context, int status) { 1061 if (mSentMessageUri != null) { 1062 // If we wrote this message in writeSentMessage, update it now 1063 ContentValues values = new ContentValues(1); 1064 values.put(Sms.STATUS, status); 1065 SqliteWrapper.update(context, context.getContentResolver(), 1066 mSentMessageUri, values, null, null); 1067 } 1068 } 1069 } 1070 1071 protected SmsTracker getSmsTracker(HashMap<String, Object> data, PendingIntent sentIntent, 1072 PendingIntent deliveryIntent, String format) { 1073 // Get calling app package name via UID from Binder call 1074 PackageManager pm = mContext.getPackageManager(); 1075 String[] packageNames = pm.getPackagesForUid(Binder.getCallingUid()); 1076 1077 // Get package info via packagemanager 1078 PackageInfo appInfo = null; 1079 if (packageNames != null && packageNames.length > 0) { 1080 try { 1081 // XXX this is lossy- apps can share a UID 1082 appInfo = pm.getPackageInfo(packageNames[0], PackageManager.GET_SIGNATURES); 1083 } catch (PackageManager.NameNotFoundException e) { 1084 // error will be logged in sendRawPdu 1085 } 1086 } 1087 // Strip non-digits from destination phone number before checking for short codes 1088 // and before displaying the number to the user if confirmation is required. 1089 String destAddr = PhoneNumberUtils.extractNetworkPortion((String) data.get("destAddr")); 1090 return new SmsTracker(data, sentIntent, deliveryIntent, appInfo, destAddr, format); 1091 } 1092 1093 protected HashMap<String, Object> getSmsTrackerMap(String destAddr, String scAddr, 1094 String text, SmsMessageBase.SubmitPduBase pdu) { 1095 HashMap<String, Object> map = new HashMap<String, Object>(); 1096 map.put("destAddr", destAddr); 1097 map.put("scAddr", scAddr); 1098 map.put("text", text); 1099 map.put("smsc", pdu.encodedScAddress); 1100 map.put("pdu", pdu.encodedMessage); 1101 return map; 1102 } 1103 1104 protected HashMap<String, Object> getSmsTrackerMap(String destAddr, String scAddr, 1105 int destPort, byte[] data, SmsMessageBase.SubmitPduBase pdu) { 1106 HashMap<String, Object> map = new HashMap<String, Object>(); 1107 map.put("destAddr", destAddr); 1108 map.put("scAddr", scAddr); 1109 map.put("destPort", destPort); 1110 map.put("data", data); 1111 map.put("smsc", pdu.encodedScAddress); 1112 map.put("pdu", pdu.encodedMessage); 1113 return map; 1114 } 1115 1116 /** 1117 * Dialog listener for SMS confirmation dialog. 1118 */ 1119 private final class ConfirmDialogListener 1120 implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener, 1121 CompoundButton.OnCheckedChangeListener { 1122 1123 private final SmsTracker mTracker; 1124 private Button mPositiveButton; 1125 private Button mNegativeButton; 1126 private boolean mRememberChoice; // default is unchecked 1127 private final TextView mRememberUndoInstruction; 1128 1129 ConfirmDialogListener(SmsTracker tracker, TextView textView) { 1130 mTracker = tracker; 1131 mRememberUndoInstruction = textView; 1132 } 1133 1134 void setPositiveButton(Button button) { 1135 mPositiveButton = button; 1136 } 1137 1138 void setNegativeButton(Button button) { 1139 mNegativeButton = button; 1140 } 1141 1142 @Override 1143 public void onClick(DialogInterface dialog, int which) { 1144 // Always set the SMS permission so that Settings will show a permission setting 1145 // for the app (it won't be shown until after the app tries to send to a short code). 1146 int newSmsPermission = SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ASK_USER; 1147 1148 if (which == DialogInterface.BUTTON_POSITIVE) { 1149 Rlog.d(TAG, "CONFIRM sending SMS"); 1150 // XXX this is lossy- apps can have more than one signature 1151 EventLog.writeEvent(EventLogTags.EXP_DET_SMS_SENT_BY_USER, 1152 mTracker.mAppInfo.applicationInfo == null ? 1153 -1 : mTracker.mAppInfo.applicationInfo.uid); 1154 sendMessage(obtainMessage(EVENT_SEND_CONFIRMED_SMS, mTracker)); 1155 if (mRememberChoice) { 1156 newSmsPermission = SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW; 1157 } 1158 } else if (which == DialogInterface.BUTTON_NEGATIVE) { 1159 Rlog.d(TAG, "DENY sending SMS"); 1160 // XXX this is lossy- apps can have more than one signature 1161 EventLog.writeEvent(EventLogTags.EXP_DET_SMS_DENIED_BY_USER, 1162 mTracker.mAppInfo.applicationInfo == null ? 1163 -1 : mTracker.mAppInfo.applicationInfo.uid); 1164 sendMessage(obtainMessage(EVENT_STOP_SENDING, mTracker)); 1165 if (mRememberChoice) { 1166 newSmsPermission = SmsUsageMonitor.PREMIUM_SMS_PERMISSION_NEVER_ALLOW; 1167 } 1168 } 1169 setPremiumSmsPermission(mTracker.mAppInfo.packageName, newSmsPermission); 1170 } 1171 1172 @Override 1173 public void onCancel(DialogInterface dialog) { 1174 Rlog.d(TAG, "dialog dismissed: don't send SMS"); 1175 sendMessage(obtainMessage(EVENT_STOP_SENDING, mTracker)); 1176 } 1177 1178 @Override 1179 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 1180 Rlog.d(TAG, "remember this choice: " + isChecked); 1181 mRememberChoice = isChecked; 1182 if (isChecked) { 1183 mPositiveButton.setText(R.string.sms_short_code_confirm_always_allow); 1184 mNegativeButton.setText(R.string.sms_short_code_confirm_never_allow); 1185 if (mRememberUndoInstruction != null) { 1186 mRememberUndoInstruction. 1187 setText(R.string.sms_short_code_remember_undo_instruction); 1188 mRememberUndoInstruction.setPadding(0,0,0,32); 1189 } 1190 } else { 1191 mPositiveButton.setText(R.string.sms_short_code_confirm_allow); 1192 mNegativeButton.setText(R.string.sms_short_code_confirm_deny); 1193 if (mRememberUndoInstruction != null) { 1194 mRememberUndoInstruction.setText(""); 1195 mRememberUndoInstruction.setPadding(0,0,0,0); 1196 } 1197 } 1198 } 1199 } 1200 1201 public boolean isIms() { 1202 if (mImsSMSDispatcher != null) { 1203 return mImsSMSDispatcher.isIms(); 1204 } else { 1205 Rlog.e(TAG, mImsSMSDispatcher + " is null"); 1206 return false; 1207 } 1208 } 1209 1210 public String getImsSmsFormat() { 1211 if (mImsSMSDispatcher != null) { 1212 return mImsSMSDispatcher.getImsSmsFormat(); 1213 } else { 1214 Rlog.e(TAG, mImsSMSDispatcher + " is null"); 1215 return null; 1216 } 1217 } 1218 } 1219