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