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