SMSDispatcher.java revision e70617d81dcd42350a737b11c25532e1d43df4ff
1/* 2 * Copyright (C) 2006 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.internal.telephony; 18import android.app.Activity; 19import android.app.AlertDialog; 20import android.app.PendingIntent; 21import android.app.PendingIntent.CanceledException; 22import android.content.ContentResolver; 23import android.content.ContentValues; 24import android.content.Context; 25import android.content.DialogInterface; 26import android.content.Intent; 27import android.content.pm.ApplicationInfo; 28import android.content.pm.PackageInfo; 29import android.content.pm.PackageManager; 30import android.content.res.Resources; 31import android.database.ContentObserver; 32import android.database.sqlite.SqliteWrapper; 33import android.net.Uri; 34import android.os.AsyncResult; 35import android.os.Binder; 36import android.os.Handler; 37import android.os.Message; 38import android.os.RemoteException; 39import android.os.SystemProperties; 40import android.provider.Settings; 41import android.provider.Telephony; 42import android.provider.Telephony.Sms; 43import android.service.carrier.CarrierMessagingService; 44import android.service.carrier.ICarrierMessagingCallback; 45import android.service.carrier.ICarrierMessagingService; 46import android.telephony.CarrierMessagingServiceManager; 47import android.telephony.PhoneNumberUtils; 48import android.telephony.Rlog; 49import android.telephony.ServiceState; 50import android.telephony.TelephonyManager; 51import android.text.Html; 52import android.text.Spanned; 53import android.text.TextUtils; 54import android.util.EventLog; 55import android.view.LayoutInflater; 56import android.view.View; 57import android.view.ViewGroup; 58import android.view.WindowManager; 59import android.widget.Button; 60import android.widget.CheckBox; 61import android.widget.CompoundButton; 62import android.widget.TextView; 63 64import com.android.internal.R; 65import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails; 66import com.android.internal.telephony.uicc.UiccCard; 67import com.android.internal.telephony.uicc.UiccController; 68 69import java.util.ArrayList; 70import java.util.HashMap; 71import java.util.List; 72import java.util.Random; 73import java.util.concurrent.atomic.AtomicBoolean; 74import java.util.concurrent.atomic.AtomicInteger; 75 76import static android.telephony.SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE; 77import static android.telephony.SmsManager.RESULT_ERROR_GENERIC_FAILURE; 78import static android.telephony.SmsManager.RESULT_ERROR_LIMIT_EXCEEDED; 79import static android.telephony.SmsManager.RESULT_ERROR_NO_SERVICE; 80import static android.telephony.SmsManager.RESULT_ERROR_NULL_PDU; 81import static android.telephony.SmsManager.RESULT_ERROR_RADIO_OFF; 82 83public abstract class SMSDispatcher extends Handler { 84 static final String TAG = "SMSDispatcher"; // accessed from inner class 85 static final boolean DBG = false; 86 private static final String SEND_NEXT_MSG_EXTRA = "SendNextMsg"; 87 88 /** Permission required to send SMS to short codes without user confirmation. */ 89 private static final String SEND_SMS_NO_CONFIRMATION_PERMISSION = 90 "android.permission.SEND_SMS_NO_CONFIRMATION"; 91 92 private static final int PREMIUM_RULE_USE_SIM = 1; 93 private static final int PREMIUM_RULE_USE_NETWORK = 2; 94 private static final int PREMIUM_RULE_USE_BOTH = 3; 95 private final AtomicInteger mPremiumSmsRule = new AtomicInteger(PREMIUM_RULE_USE_SIM); 96 private final SettingsObserver mSettingsObserver; 97 98 /** SMS send complete. */ 99 protected static final int EVENT_SEND_SMS_COMPLETE = 2; 100 101 /** Retry sending a previously failed SMS message */ 102 private static final int EVENT_SEND_RETRY = 3; 103 104 /** Confirmation required for sending a large number of messages. */ 105 private static final int EVENT_SEND_LIMIT_REACHED_CONFIRMATION = 4; 106 107 /** Send the user confirmed SMS */ 108 static final int EVENT_SEND_CONFIRMED_SMS = 5; // accessed from inner class 109 110 /** Don't send SMS (user did not confirm). */ 111 static final int EVENT_STOP_SENDING = 7; // accessed from inner class 112 113 /** Confirmation required for third-party apps sending to an SMS short code. */ 114 private static final int EVENT_CONFIRM_SEND_TO_POSSIBLE_PREMIUM_SHORT_CODE = 8; 115 116 /** Confirmation required for third-party apps sending to an SMS short code. */ 117 private static final int EVENT_CONFIRM_SEND_TO_PREMIUM_SHORT_CODE = 9; 118 119 /** Handle status report from {@code CdmaInboundSmsHandler}. */ 120 protected static final int EVENT_HANDLE_STATUS_REPORT = 10; 121 122 /** Radio is ON */ 123 protected static final int EVENT_RADIO_ON = 11; 124 125 /** IMS registration/SMS format changed */ 126 protected static final int EVENT_IMS_STATE_CHANGED = 12; 127 128 /** Callback from RIL_REQUEST_IMS_REGISTRATION_STATE */ 129 protected static final int EVENT_IMS_STATE_DONE = 13; 130 131 // other 132 protected static final int EVENT_NEW_ICC_SMS = 14; 133 protected static final int EVENT_ICC_CHANGED = 15; 134 135 protected PhoneBase mPhone; 136 protected final Context mContext; 137 protected final ContentResolver mResolver; 138 protected final CommandsInterface mCi; 139 protected final TelephonyManager mTelephonyManager; 140 141 /** Maximum number of times to retry sending a failed SMS. */ 142 private static final int MAX_SEND_RETRIES = 3; 143 /** Delay before next send attempt on a failed SMS, in milliseconds. */ 144 private static final int SEND_RETRY_DELAY = 2000; 145 /** single part SMS */ 146 private static final int SINGLE_PART_SMS = 1; 147 /** Message sending queue limit */ 148 private static final int MO_MSG_QUEUE_LIMIT = 5; 149 150 /** 151 * Message reference for a CONCATENATED_8_BIT_REFERENCE or 152 * CONCATENATED_16_BIT_REFERENCE message set. Should be 153 * incremented for each set of concatenated messages. 154 * Static field shared by all dispatcher objects. 155 */ 156 private static int sConcatenatedRef = new Random().nextInt(256); 157 158 /** Outgoing message counter. Shared by all dispatchers. */ 159 private SmsUsageMonitor mUsageMonitor; 160 161 private ImsSMSDispatcher mImsSMSDispatcher; 162 163 /** Number of outgoing SmsTrackers waiting for user confirmation. */ 164 private int mPendingTrackerCount; 165 166 /* Flags indicating whether the current device allows sms service */ 167 protected boolean mSmsCapable = true; 168 protected boolean mSmsSendDisabled; 169 170 protected static int getNextConcatenatedRef() { 171 sConcatenatedRef += 1; 172 return sConcatenatedRef; 173 } 174 175 /** 176 * Create a new SMS dispatcher. 177 * @param phone the Phone to use 178 * @param usageMonitor the SmsUsageMonitor to use 179 */ 180 protected SMSDispatcher(PhoneBase phone, SmsUsageMonitor usageMonitor, 181 ImsSMSDispatcher imsSMSDispatcher) { 182 mPhone = phone; 183 mImsSMSDispatcher = imsSMSDispatcher; 184 mContext = phone.getContext(); 185 mResolver = mContext.getContentResolver(); 186 mCi = phone.mCi; 187 mUsageMonitor = usageMonitor; 188 mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); 189 mSettingsObserver = new SettingsObserver(this, mPremiumSmsRule, mContext); 190 mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor( 191 Settings.Global.SMS_SHORT_CODE_RULE), false, mSettingsObserver); 192 193 mSmsCapable = mContext.getResources().getBoolean( 194 com.android.internal.R.bool.config_sms_capable); 195 mSmsSendDisabled = !mTelephonyManager.getSmsSendCapableForPhone( 196 mPhone.getPhoneId(), mSmsCapable); 197 Rlog.d(TAG, "SMSDispatcher: ctor mSmsCapable=" + mSmsCapable + " format=" + getFormat() 198 + " mSmsSendDisabled=" + mSmsSendDisabled); 199 } 200 201 /** 202 * Observe the secure setting for updated premium sms determination rules 203 */ 204 private static class SettingsObserver extends ContentObserver { 205 private final AtomicInteger mPremiumSmsRule; 206 private final Context mContext; 207 SettingsObserver(Handler handler, AtomicInteger premiumSmsRule, Context context) { 208 super(handler); 209 mPremiumSmsRule = premiumSmsRule; 210 mContext = context; 211 onChange(false); // load initial value; 212 } 213 214 @Override 215 public void onChange(boolean selfChange) { 216 mPremiumSmsRule.set(Settings.Global.getInt(mContext.getContentResolver(), 217 Settings.Global.SMS_SHORT_CODE_RULE, PREMIUM_RULE_USE_SIM)); 218 } 219 } 220 221 protected void updatePhoneObject(PhoneBase phone) { 222 mPhone = phone; 223 mUsageMonitor = phone.mSmsUsageMonitor; 224 Rlog.d(TAG, "Active phone changed to " + mPhone.getPhoneName() ); 225 } 226 227 /** Unregister for incoming SMS events. */ 228 public void dispose() { 229 mContext.getContentResolver().unregisterContentObserver(mSettingsObserver); 230 } 231 232 /** 233 * The format of the message PDU in the associated broadcast intent. 234 * This will be either "3gpp" for GSM/UMTS/LTE messages in 3GPP format 235 * or "3gpp2" for CDMA/LTE messages in 3GPP2 format. 236 * 237 * Note: All applications which handle incoming SMS messages by processing the 238 * SMS_RECEIVED_ACTION broadcast intent MUST pass the "format" extra from the intent 239 * into the new methods in {@link android.telephony.SmsMessage} which take an 240 * extra format parameter. This is required in order to correctly decode the PDU on 241 * devices which require support for both 3GPP and 3GPP2 formats at the same time, 242 * such as CDMA/LTE devices and GSM/CDMA world phones. 243 * 244 * @return the format of the message PDU 245 */ 246 protected abstract String getFormat(); 247 248 /** 249 * Pass the Message object to subclass to handle. Currently used to pass CDMA status reports 250 * from {@link com.android.internal.telephony.cdma.CdmaInboundSmsHandler}. 251 * @param o the SmsMessage containing the status report 252 */ 253 protected void handleStatusReport(Object o) { 254 Rlog.d(TAG, "handleStatusReport() called with no subclass."); 255 } 256 257 /* TODO: Need to figure out how to keep track of status report routing in a 258 * persistent manner. If the phone process restarts (reboot or crash), 259 * we will lose this list and any status reports that come in after 260 * will be dropped. 261 */ 262 /** Sent messages awaiting a delivery status report. */ 263 protected final ArrayList<SmsTracker> deliveryPendingList = new ArrayList<SmsTracker>(); 264 265 /** 266 * Handles events coming from the phone stack. Overridden from handler. 267 * 268 * @param msg the message to handle 269 */ 270 @Override 271 public void handleMessage(Message msg) { 272 switch (msg.what) { 273 case EVENT_SEND_SMS_COMPLETE: 274 // An outbound SMS has been successfully transferred, or failed. 275 handleSendComplete((AsyncResult) msg.obj); 276 break; 277 278 case EVENT_SEND_RETRY: 279 Rlog.d(TAG, "SMS retry.."); 280 sendRetrySms((SmsTracker) msg.obj); 281 break; 282 283 case EVENT_SEND_LIMIT_REACHED_CONFIRMATION: 284 handleReachSentLimit((SmsTracker)(msg.obj)); 285 break; 286 287 case EVENT_CONFIRM_SEND_TO_POSSIBLE_PREMIUM_SHORT_CODE: 288 handleConfirmShortCode(false, (SmsTracker)(msg.obj)); 289 break; 290 291 case EVENT_CONFIRM_SEND_TO_PREMIUM_SHORT_CODE: 292 handleConfirmShortCode(true, (SmsTracker)(msg.obj)); 293 break; 294 295 case EVENT_SEND_CONFIRMED_SMS: 296 { 297 SmsTracker tracker = (SmsTracker) msg.obj; 298 if (tracker.isMultipart()) { 299 sendMultipartSms(tracker); 300 } else { 301 if (mPendingTrackerCount > 1) { 302 tracker.mExpectMore = true; 303 } else { 304 tracker.mExpectMore = false; 305 } 306 sendSms(tracker); 307 } 308 mPendingTrackerCount--; 309 break; 310 } 311 312 case EVENT_STOP_SENDING: 313 { 314 SmsTracker tracker = (SmsTracker) msg.obj; 315 tracker.onFailed(mContext, RESULT_ERROR_LIMIT_EXCEEDED, 0/*errorCode*/); 316 mPendingTrackerCount--; 317 break; 318 } 319 320 case EVENT_HANDLE_STATUS_REPORT: 321 handleStatusReport(msg.obj); 322 break; 323 324 default: 325 Rlog.e(TAG, "handleMessage() ignoring message of unexpected type " + msg.what); 326 } 327 } 328 329 /** 330 * Use the carrier messaging service to send a data or text SMS. 331 */ 332 protected abstract class SmsSender extends CarrierMessagingServiceManager { 333 protected final SmsTracker mTracker; 334 // Initialized in sendSmsByCarrierApp 335 protected volatile SmsSenderCallback mSenderCallback; 336 337 protected SmsSender(SmsTracker tracker) { 338 mTracker = tracker; 339 } 340 341 public void sendSmsByCarrierApp(String carrierPackageName, 342 SmsSenderCallback senderCallback) { 343 mSenderCallback = senderCallback; 344 if (!bindToCarrierMessagingService(mContext, carrierPackageName)) { 345 Rlog.e(TAG, "bindService() for carrier messaging service failed"); 346 mSenderCallback.onSendSmsComplete( 347 CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK, 348 0 /* messageRef */); 349 } else { 350 Rlog.d(TAG, "bindService() for carrier messaging service succeeded"); 351 } 352 } 353 } 354 355 /** 356 * Use the carrier messaging service to send a text SMS. 357 */ 358 protected final class TextSmsSender extends SmsSender { 359 public TextSmsSender(SmsTracker tracker) { 360 super(tracker); 361 } 362 363 @Override 364 protected void onServiceReady(ICarrierMessagingService carrierMessagingService) { 365 HashMap<String, Object> map = mTracker.mData; 366 String text = (String) map.get("text"); 367 368 if (text != null) { 369 try { 370 carrierMessagingService.sendTextSms(text, getSubId(), 371 mTracker.mDestAddress, mSenderCallback); 372 } catch (RemoteException e) { 373 Rlog.e(TAG, "Exception sending the SMS: " + e); 374 mSenderCallback.onSendSmsComplete( 375 CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK, 376 0 /* messageRef */); 377 } 378 } else { 379 mSenderCallback.onSendSmsComplete( 380 CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK, 381 0 /* messageRef */); 382 } 383 } 384 } 385 386 /** 387 * Use the carrier messaging service to send a data SMS. 388 */ 389 protected final class DataSmsSender extends SmsSender { 390 public DataSmsSender(SmsTracker tracker) { 391 super(tracker); 392 } 393 394 @Override 395 protected void onServiceReady(ICarrierMessagingService carrierMessagingService) { 396 HashMap<String, Object> map = mTracker.mData; 397 byte[] data = (byte[]) map.get("data"); 398 int destPort = (int) map.get("destPort"); 399 400 if (data != null) { 401 try { 402 carrierMessagingService.sendDataSms(data, getSubId(), 403 mTracker.mDestAddress, destPort, mSenderCallback); 404 } catch (RemoteException e) { 405 Rlog.e(TAG, "Exception sending the SMS: " + e); 406 mSenderCallback.onSendSmsComplete( 407 CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK, 408 0 /* messageRef */); 409 } 410 } else { 411 mSenderCallback.onSendSmsComplete( 412 CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK, 413 0 /* messageRef */); 414 } 415 } 416 } 417 418 /** 419 * Callback for TextSmsSender and DataSmsSender from the carrier messaging service. 420 * Once the result is ready, the carrier messaging service connection is disposed. 421 */ 422 protected final class SmsSenderCallback extends ICarrierMessagingCallback.Stub { 423 private final SmsSender mSmsSender; 424 425 public SmsSenderCallback(SmsSender smsSender) { 426 mSmsSender = smsSender; 427 } 428 429 /** 430 * This method should be called only once. 431 */ 432 @Override 433 public void onSendSmsComplete(int result, int messageRef) { 434 mSmsSender.disposeConnection(mContext); 435 processSendSmsResponse(mSmsSender.mTracker, result, messageRef); 436 } 437 438 @Override 439 public void onSendMultipartSmsComplete(int result, int[] messageRefs) { 440 Rlog.e(TAG, "Unexpected onSendMultipartSmsComplete call with result: " + result); 441 } 442 443 @Override 444 public void onFilterComplete(boolean keepMessage) { 445 Rlog.e(TAG, "Unexpected onFilterComplete call with result: " + keepMessage); 446 } 447 448 @Override 449 public void onSendMmsComplete(int result, byte[] sendConfPdu) { 450 Rlog.e(TAG, "Unexpected onSendMmsComplete call with result: " + result); 451 } 452 453 @Override 454 public void onDownloadMmsComplete(int result) { 455 Rlog.e(TAG, "Unexpected onDownloadMmsComplete call with result: " + result); 456 } 457 } 458 459 private void processSendSmsResponse(SmsTracker tracker, int result, int messageRef) { 460 if (tracker == null) { 461 Rlog.e(TAG, "processSendSmsResponse: null tracker"); 462 return; 463 } 464 465 SmsResponse smsResponse = new SmsResponse( 466 messageRef, null /* ackPdu */, -1 /* unknown error code */); 467 468 switch (result) { 469 case CarrierMessagingService.SEND_STATUS_OK: 470 Rlog.d(TAG, "Sending SMS by IP succeeded."); 471 sendMessage(obtainMessage(EVENT_SEND_SMS_COMPLETE, 472 new AsyncResult(tracker, 473 smsResponse, 474 null /* exception*/ ))); 475 break; 476 case CarrierMessagingService.SEND_STATUS_ERROR: 477 Rlog.d(TAG, "Sending SMS by IP failed."); 478 sendMessage(obtainMessage(EVENT_SEND_SMS_COMPLETE, 479 new AsyncResult(tracker, smsResponse, 480 new CommandException(CommandException.Error.GENERIC_FAILURE)))); 481 break; 482 case CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK: 483 Rlog.d(TAG, "Sending SMS by IP failed. Retry on carrier network."); 484 sendSubmitPdu(tracker); 485 break; 486 default: 487 Rlog.d(TAG, "Unknown result " + result + " Retry on carrier network."); 488 sendSubmitPdu(tracker); 489 } 490 } 491 492 /** 493 * Use the carrier messaging service to send a multipart text SMS. 494 */ 495 private final class MultipartSmsSender extends CarrierMessagingServiceManager { 496 private final List<String> mParts; 497 public final SmsTracker[] mTrackers; 498 // Initialized in sendSmsByCarrierApp 499 private volatile MultipartSmsSenderCallback mSenderCallback; 500 501 MultipartSmsSender(ArrayList<String> parts, SmsTracker[] trackers) { 502 mParts = parts; 503 mTrackers = trackers; 504 } 505 506 void sendSmsByCarrierApp(String carrierPackageName, 507 MultipartSmsSenderCallback senderCallback) { 508 mSenderCallback = senderCallback; 509 if (!bindToCarrierMessagingService(mContext, carrierPackageName)) { 510 Rlog.e(TAG, "bindService() for carrier messaging service failed"); 511 mSenderCallback.onSendMultipartSmsComplete( 512 CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK, 513 null /* smsResponse */); 514 } else { 515 Rlog.d(TAG, "bindService() for carrier messaging service succeeded"); 516 } 517 } 518 519 @Override 520 protected void onServiceReady(ICarrierMessagingService carrierMessagingService) { 521 try { 522 carrierMessagingService.sendMultipartTextSms( 523 mParts, getSubId(), mTrackers[0].mDestAddress, mSenderCallback); 524 } catch (RemoteException e) { 525 Rlog.e(TAG, "Exception sending the SMS: " + e); 526 mSenderCallback.onSendMultipartSmsComplete( 527 CarrierMessagingService.SEND_STATUS_RETRY_ON_CARRIER_NETWORK, 528 null /* smsResponse */); 529 } 530 } 531 } 532 533 /** 534 * Callback for MultipartSmsSender from the carrier messaging service. 535 * Once the result is ready, the carrier messaging service connection is disposed. 536 */ 537 private final class MultipartSmsSenderCallback extends ICarrierMessagingCallback.Stub { 538 private final MultipartSmsSender mSmsSender; 539 540 MultipartSmsSenderCallback(MultipartSmsSender smsSender) { 541 mSmsSender = smsSender; 542 } 543 544 @Override 545 public void onSendSmsComplete(int result, int messageRef) { 546 Rlog.e(TAG, "Unexpected onSendSmsComplete call with result: " + result); 547 } 548 549 /** 550 * This method should be called only once. 551 */ 552 @Override 553 public void onSendMultipartSmsComplete(int result, int[] messageRefs) { 554 mSmsSender.disposeConnection(mContext); 555 556 if (mSmsSender.mTrackers == null) { 557 Rlog.e(TAG, "Unexpected onSendMultipartSmsComplete call with null trackers."); 558 return; 559 } 560 561 for (int i = 0; i < mSmsSender.mTrackers.length; i++) { 562 int messageRef = 0; 563 if (messageRefs != null && messageRefs.length > i) { 564 messageRef = messageRefs[i]; 565 } 566 processSendSmsResponse(mSmsSender.mTrackers[i], result, messageRef); 567 } 568 } 569 570 @Override 571 public void onFilterComplete(boolean keepMessage) { 572 Rlog.e(TAG, "Unexpected onFilterComplete call with result: " + keepMessage); 573 } 574 575 @Override 576 public void onSendMmsComplete(int result, byte[] sendConfPdu) { 577 Rlog.e(TAG, "Unexpected onSendMmsComplete call with result: " + result); 578 } 579 580 @Override 581 public void onDownloadMmsComplete(int result) { 582 Rlog.e(TAG, "Unexpected onDownloadMmsComplete call with result: " + result); 583 } 584 } 585 586 /** 587 * Send an SMS PDU. Usually just calls {@link sendRawPdu}. 588 */ 589 protected abstract void sendSubmitPdu(SmsTracker tracker); 590 591 /** 592 * Called when SMS send completes. Broadcasts a sentIntent on success. 593 * On failure, either sets up retries or broadcasts a sentIntent with 594 * the failure in the result code. 595 * 596 * @param ar AsyncResult passed into the message handler. ar.result should 597 * an SmsResponse instance if send was successful. ar.userObj 598 * should be an SmsTracker instance. 599 */ 600 protected void handleSendComplete(AsyncResult ar) { 601 SmsTracker tracker = (SmsTracker) ar.userObj; 602 PendingIntent sentIntent = tracker.mSentIntent; 603 604 if (ar.result != null) { 605 tracker.mMessageRef = ((SmsResponse)ar.result).mMessageRef; 606 } else { 607 Rlog.d(TAG, "SmsResponse was null"); 608 } 609 610 if (ar.exception == null) { 611 if (DBG) Rlog.d(TAG, "SMS send complete. Broadcasting intent: " + sentIntent); 612 613 if (tracker.mDeliveryIntent != null) { 614 // Expecting a status report. Add it to the list. 615 deliveryPendingList.add(tracker); 616 } 617 tracker.onSent(mContext); 618 } else { 619 if (DBG) Rlog.d(TAG, "SMS send failed"); 620 621 int ss = mPhone.getServiceState().getState(); 622 623 if ( tracker.mImsRetry > 0 && ss != ServiceState.STATE_IN_SERVICE) { 624 // This is retry after failure over IMS but voice is not available. 625 // Set retry to max allowed, so no retry is sent and 626 // cause RESULT_ERROR_GENERIC_FAILURE to be returned to app. 627 tracker.mRetryCount = MAX_SEND_RETRIES; 628 629 Rlog.d(TAG, "handleSendComplete: Skipping retry: " 630 +" isIms()="+isIms() 631 +" mRetryCount="+tracker.mRetryCount 632 +" mImsRetry="+tracker.mImsRetry 633 +" mMessageRef="+tracker.mMessageRef 634 +" SS= "+mPhone.getServiceState().getState()); 635 } 636 637 // if sms over IMS is not supported on data and voice is not available... 638 if (!isIms() && ss != ServiceState.STATE_IN_SERVICE) { 639 tracker.onFailed(mContext, getNotInServiceError(ss), 0/*errorCode*/); 640 } else if ((((CommandException)(ar.exception)).getCommandError() 641 == CommandException.Error.SMS_FAIL_RETRY) && 642 tracker.mRetryCount < MAX_SEND_RETRIES) { 643 // Retry after a delay if needed. 644 // TODO: According to TS 23.040, 9.2.3.6, we should resend 645 // with the same TP-MR as the failed message, and 646 // TP-RD set to 1. However, we don't have a means of 647 // knowing the MR for the failed message (EF_SMSstatus 648 // may or may not have the MR corresponding to this 649 // message, depending on the failure). Also, in some 650 // implementations this retry is handled by the baseband. 651 tracker.mRetryCount++; 652 Message retryMsg = obtainMessage(EVENT_SEND_RETRY, tracker); 653 sendMessageDelayed(retryMsg, SEND_RETRY_DELAY); 654 } else { 655 int errorCode = 0; 656 if (ar.result != null) { 657 errorCode = ((SmsResponse)ar.result).mErrorCode; 658 } 659 int error = RESULT_ERROR_GENERIC_FAILURE; 660 if (((CommandException)(ar.exception)).getCommandError() 661 == CommandException.Error.FDN_CHECK_FAILURE) { 662 error = RESULT_ERROR_FDN_CHECK_FAILURE; 663 } 664 tracker.onFailed(mContext, error, errorCode); 665 } 666 } 667 } 668 669 /** 670 * Handles outbound message when the phone is not in service. 671 * 672 * @param ss Current service state. Valid values are: 673 * OUT_OF_SERVICE 674 * EMERGENCY_ONLY 675 * POWER_OFF 676 * @param sentIntent the PendingIntent to send the error to 677 */ 678 protected static void handleNotInService(int ss, PendingIntent sentIntent) { 679 if (sentIntent != null) { 680 try { 681 if (ss == ServiceState.STATE_POWER_OFF) { 682 sentIntent.send(RESULT_ERROR_RADIO_OFF); 683 } else { 684 sentIntent.send(RESULT_ERROR_NO_SERVICE); 685 } 686 } catch (CanceledException ex) {} 687 } 688 } 689 690 /** 691 * @param ss service state 692 * @return The result error based on input service state for not in service error 693 */ 694 protected static int getNotInServiceError(int ss) { 695 if (ss == ServiceState.STATE_POWER_OFF) { 696 return RESULT_ERROR_RADIO_OFF; 697 } 698 return RESULT_ERROR_NO_SERVICE; 699 } 700 701 /** 702 * Send a data based SMS to a specific application port. 703 * 704 * @param destAddr the address to send the message to 705 * @param scAddr is the service center address or null to use 706 * the current default SMSC 707 * @param destPort the port to deliver the message to 708 * @param data the body of the message to send 709 * @param sentIntent if not NULL this <code>PendingIntent</code> is 710 * broadcast when the message is successfully sent, or failed. 711 * The result code will be <code>Activity.RESULT_OK<code> for success, 712 * or one of these errors:<br> 713 * <code>RESULT_ERROR_GENERIC_FAILURE</code><br> 714 * <code>RESULT_ERROR_RADIO_OFF</code><br> 715 * <code>RESULT_ERROR_NULL_PDU</code><br> 716 * <code>RESULT_ERROR_NO_SERVICE</code><br>. 717 * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include 718 * the extra "errorCode" containing a radio technology specific value, 719 * generally only useful for troubleshooting.<br> 720 * The per-application based SMS control checks sentIntent. If sentIntent 721 * is NULL the caller will be checked against all unknown applications, 722 * which cause smaller number of SMS to be sent in checking period. 723 * @param deliveryIntent if not NULL this <code>PendingIntent</code> is 724 * broadcast when the message is delivered to the recipient. The 725 * raw pdu of the status report is in the extended data ("pdu"). 726 */ 727 protected abstract void sendData(String destAddr, String scAddr, int destPort, 728 byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent); 729 730 /** 731 * Send a text based SMS. 732 * @param destAddr the address to send the message to 733 * @param scAddr is the service center address or null to use 734 * the current default SMSC 735 * @param text the body of the message to send 736 * @param sentIntent if not NULL this <code>PendingIntent</code> is 737 * broadcast when the message is successfully sent, or failed. 738 * The result code will be <code>Activity.RESULT_OK<code> for success, 739 * or one of these errors:<br> 740 * <code>RESULT_ERROR_GENERIC_FAILURE</code><br> 741 * <code>RESULT_ERROR_RADIO_OFF</code><br> 742 * <code>RESULT_ERROR_NULL_PDU</code><br> 743 * <code>RESULT_ERROR_NO_SERVICE</code><br>. 744 * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include 745 * the extra "errorCode" containing a radio technology specific value, 746 * generally only useful for troubleshooting.<br> 747 * The per-application based SMS control checks sentIntent. If sentIntent 748 * is NULL the caller will be checked against all unknown applications, 749 * which cause smaller number of SMS to be sent in checking period. 750 * @param deliveryIntent if not NULL this <code>PendingIntent</code> is 751 * broadcast when the message is delivered to the recipient. The 752 * @param messageUri optional URI of the message if it is already stored in the system 753 * @param callingPkg the calling package name 754 */ 755 protected abstract void sendText(String destAddr, String scAddr, String text, 756 PendingIntent sentIntent, PendingIntent deliveryIntent, Uri messageUri, 757 String callingPkg); 758 759 /** 760 * Inject an SMS PDU into the android platform. 761 * 762 * @param pdu is the byte array of pdu to be injected into android telephony layer 763 * @param format is the format of SMS pdu (3gpp or 3gpp2) 764 * @param receivedIntent if not NULL this <code>PendingIntent</code> is 765 * broadcast when the message is successfully received by the 766 * android telephony layer. This intent is broadcasted at 767 * the same time an SMS received from radio is responded back. 768 */ 769 protected abstract void injectSmsPdu(byte[] pdu, String format, PendingIntent receivedIntent); 770 771 /** 772 * Calculate the number of septets needed to encode the message. 773 * 774 * @param messageBody the message to encode 775 * @param use7bitOnly ignore (but still count) illegal characters if true 776 * @return TextEncodingDetails 777 */ 778 protected abstract TextEncodingDetails calculateLength(CharSequence messageBody, 779 boolean use7bitOnly); 780 781 /** 782 * Send a multi-part text based SMS. 783 * @param destAddr the address to send the message to 784 * @param scAddr is the service center address or null to use 785 * the current default SMSC 786 * @param parts an <code>ArrayList</code> of strings that, in order, 787 * comprise the original message 788 * @param sentIntents if not null, an <code>ArrayList</code> of 789 * <code>PendingIntent</code>s (one for each message part) that is 790 * broadcast when the corresponding message part has been sent. 791 * The result code will be <code>Activity.RESULT_OK<code> for success, 792 * or one of these errors: 793 * <code>RESULT_ERROR_GENERIC_FAILURE</code> 794 * <code>RESULT_ERROR_RADIO_OFF</code> 795 * <code>RESULT_ERROR_NULL_PDU</code> 796 * <code>RESULT_ERROR_NO_SERVICE</code>. 797 * The per-application based SMS control checks sentIntent. If sentIntent 798 * is NULL the caller will be checked against all unknown applications, 799 * which cause smaller number of SMS to be sent in checking period. 800 * @param deliveryIntents if not null, an <code>ArrayList</code> of 801 * <code>PendingIntent</code>s (one for each message part) that is 802 * broadcast when the corresponding message part has been delivered 803 * to the recipient. The raw pdu of the status report is in the 804 * @param messageUri optional URI of the message if it is already stored in the system 805 * @param callingPkg the calling package name 806 */ 807 protected void sendMultipartText(String destAddr, String scAddr, 808 ArrayList<String> parts, ArrayList<PendingIntent> sentIntents, 809 ArrayList<PendingIntent> deliveryIntents, Uri messageUri, String callingPkg) { 810 final String fullMessageText = getMultipartMessageText(parts); 811 int refNumber = getNextConcatenatedRef() & 0x00FF; 812 int msgCount = parts.size(); 813 int encoding = SmsConstants.ENCODING_UNKNOWN; 814 815 TextEncodingDetails[] encodingForParts = new TextEncodingDetails[msgCount]; 816 for (int i = 0; i < msgCount; i++) { 817 TextEncodingDetails details = calculateLength(parts.get(i), false); 818 if (encoding != details.codeUnitSize 819 && (encoding == SmsConstants.ENCODING_UNKNOWN 820 || encoding == SmsConstants.ENCODING_7BIT)) { 821 encoding = details.codeUnitSize; 822 } 823 encodingForParts[i] = details; 824 } 825 826 SmsTracker[] trackers = new SmsTracker[msgCount]; 827 828 // States to track at the message level (for all parts) 829 final AtomicInteger unsentPartCount = new AtomicInteger(msgCount); 830 final AtomicBoolean anyPartFailed = new AtomicBoolean(false); 831 832 for (int i = 0; i < msgCount; i++) { 833 SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef(); 834 concatRef.refNumber = refNumber; 835 concatRef.seqNumber = i + 1; // 1-based sequence 836 concatRef.msgCount = msgCount; 837 // TODO: We currently set this to true since our messaging app will never 838 // send more than 255 parts (it converts the message to MMS well before that). 839 // However, we should support 3rd party messaging apps that might need 16-bit 840 // references 841 // Note: It's not sufficient to just flip this bit to true; it will have 842 // ripple effects (several calculations assume 8-bit ref). 843 concatRef.isEightBits = true; 844 SmsHeader smsHeader = new SmsHeader(); 845 smsHeader.concatRef = concatRef; 846 847 // Set the national language tables for 3GPP 7-bit encoding, if enabled. 848 if (encoding == SmsConstants.ENCODING_7BIT) { 849 smsHeader.languageTable = encodingForParts[i].languageTable; 850 smsHeader.languageShiftTable = encodingForParts[i].languageShiftTable; 851 } 852 853 PendingIntent sentIntent = null; 854 if (sentIntents != null && sentIntents.size() > i) { 855 sentIntent = sentIntents.get(i); 856 } 857 858 PendingIntent deliveryIntent = null; 859 if (deliveryIntents != null && deliveryIntents.size() > i) { 860 deliveryIntent = deliveryIntents.get(i); 861 } 862 863 trackers[i] = 864 getNewSubmitPduTracker(destAddr, scAddr, parts.get(i), smsHeader, encoding, 865 sentIntent, deliveryIntent, (i == (msgCount - 1)), 866 unsentPartCount, anyPartFailed, messageUri, fullMessageText); 867 } 868 869 if (parts == null || trackers == null || trackers.length == 0 870 || trackers[0] == null) { 871 Rlog.e(TAG, "Cannot send multipart text. parts=" + parts + " trackers=" + trackers); 872 return; 873 } 874 875 String carrierPackage = getCarrierAppPackageName(); 876 if (carrierPackage != null) { 877 Rlog.d(TAG, "Found carrier package."); 878 MultipartSmsSender smsSender = new MultipartSmsSender(parts, trackers); 879 smsSender.sendSmsByCarrierApp(carrierPackage, new MultipartSmsSenderCallback(smsSender)); 880 } else { 881 Rlog.v(TAG, "No carrier package."); 882 for (SmsTracker tracker : trackers) { 883 if (tracker != null) { 884 sendSubmitPdu(tracker); 885 } else { 886 Rlog.e(TAG, "Null tracker."); 887 } 888 } 889 } 890 } 891 892 /** 893 * Create a new SubmitPdu and return the SMS tracker. 894 */ 895 protected abstract SmsTracker getNewSubmitPduTracker(String destinationAddress, String scAddress, 896 String message, SmsHeader smsHeader, int encoding, 897 PendingIntent sentIntent, PendingIntent deliveryIntent, boolean lastPart, 898 AtomicInteger unsentPartCount, AtomicBoolean anyPartFailed, Uri messageUri, 899 String fullMessageText); 900 901 /** 902 * Send an SMS 903 * @param tracker will contain: 904 * -smsc the SMSC to send the message through, or NULL for the 905 * default SMSC 906 * -pdu the raw PDU to send 907 * -sentIntent if not NULL this <code>Intent</code> is 908 * broadcast when the message is successfully sent, or failed. 909 * The result code will be <code>Activity.RESULT_OK<code> for success, 910 * or one of these errors: 911 * <code>RESULT_ERROR_GENERIC_FAILURE</code> 912 * <code>RESULT_ERROR_RADIO_OFF</code> 913 * <code>RESULT_ERROR_NULL_PDU</code> 914 * <code>RESULT_ERROR_NO_SERVICE</code>. 915 * The per-application based SMS control checks sentIntent. If sentIntent 916 * is NULL the caller will be checked against all unknown applications, 917 * which cause smaller number of SMS to be sent in checking period. 918 * -deliveryIntent if not NULL this <code>Intent</code> is 919 * broadcast when the message is delivered to the recipient. The 920 * raw pdu of the status report is in the extended data ("pdu"). 921 * -param destAddr the destination phone number (for short code confirmation) 922 */ 923 protected void sendRawPdu(SmsTracker tracker) { 924 HashMap map = tracker.mData; 925 byte pdu[] = (byte[]) map.get("pdu"); 926 927 if (mSmsSendDisabled) { 928 Rlog.e(TAG, "Device does not support sending sms."); 929 tracker.onFailed(mContext, RESULT_ERROR_NO_SERVICE, 0/*errorCode*/); 930 return; 931 } 932 933 if (pdu == null) { 934 Rlog.e(TAG, "Empty PDU"); 935 tracker.onFailed(mContext, RESULT_ERROR_NULL_PDU, 0/*errorCode*/); 936 return; 937 } 938 939 // Get calling app package name via UID from Binder call 940 PackageManager pm = mContext.getPackageManager(); 941 String[] packageNames = pm.getPackagesForUid(Binder.getCallingUid()); 942 943 if (packageNames == null || packageNames.length == 0) { 944 // Refuse to send SMS if we can't get the calling package name. 945 Rlog.e(TAG, "Can't get calling app package name: refusing to send SMS"); 946 tracker.onFailed(mContext, RESULT_ERROR_GENERIC_FAILURE, 0/*errorCode*/); 947 return; 948 } 949 950 // Get package info via packagemanager 951 PackageInfo appInfo; 952 try { 953 // XXX this is lossy- apps can share a UID 954 appInfo = pm.getPackageInfo(packageNames[0], PackageManager.GET_SIGNATURES); 955 } catch (PackageManager.NameNotFoundException e) { 956 Rlog.e(TAG, "Can't get calling app package info: refusing to send SMS"); 957 tracker.onFailed(mContext, RESULT_ERROR_GENERIC_FAILURE, 0/*errorCode*/); 958 return; 959 } 960 961 // checkDestination() returns true if the destination is not a premium short code or the 962 // sending app is approved to send to short codes. Otherwise, a message is sent to our 963 // handler with the SmsTracker to request user confirmation before sending. 964 if (checkDestination(tracker)) { 965 // check for excessive outgoing SMS usage by this app 966 if (!mUsageMonitor.check(appInfo.packageName, SINGLE_PART_SMS)) { 967 sendMessage(obtainMessage(EVENT_SEND_LIMIT_REACHED_CONFIRMATION, tracker)); 968 return; 969 } 970 971 sendSms(tracker); 972 } 973 } 974 975 /** 976 * Check if destination is a potential premium short code and sender is not pre-approved to 977 * send to short codes. 978 * 979 * @param tracker the tracker for the SMS to send 980 * @return true if the destination is approved; false if user confirmation event was sent 981 */ 982 boolean checkDestination(SmsTracker tracker) { 983 if (mContext.checkCallingOrSelfPermission(SEND_SMS_NO_CONFIRMATION_PERMISSION) 984 == PackageManager.PERMISSION_GRANTED) { 985 return true; // app is pre-approved to send to short codes 986 } else { 987 int rule = mPremiumSmsRule.get(); 988 int smsCategory = SmsUsageMonitor.CATEGORY_NOT_SHORT_CODE; 989 if (rule == PREMIUM_RULE_USE_SIM || rule == PREMIUM_RULE_USE_BOTH) { 990 String simCountryIso = mTelephonyManager.getSimCountryIso(); 991 if (simCountryIso == null || simCountryIso.length() != 2) { 992 Rlog.e(TAG, "Can't get SIM country Iso: trying network country Iso"); 993 simCountryIso = mTelephonyManager.getNetworkCountryIso(); 994 } 995 996 smsCategory = mUsageMonitor.checkDestination(tracker.mDestAddress, simCountryIso); 997 } 998 if (rule == PREMIUM_RULE_USE_NETWORK || rule == PREMIUM_RULE_USE_BOTH) { 999 String networkCountryIso = mTelephonyManager.getNetworkCountryIso(); 1000 if (networkCountryIso == null || networkCountryIso.length() != 2) { 1001 Rlog.e(TAG, "Can't get Network country Iso: trying SIM country Iso"); 1002 networkCountryIso = mTelephonyManager.getSimCountryIso(); 1003 } 1004 1005 smsCategory = SmsUsageMonitor.mergeShortCodeCategories(smsCategory, 1006 mUsageMonitor.checkDestination(tracker.mDestAddress, networkCountryIso)); 1007 } 1008 1009 if (smsCategory == SmsUsageMonitor.CATEGORY_NOT_SHORT_CODE 1010 || smsCategory == SmsUsageMonitor.CATEGORY_FREE_SHORT_CODE 1011 || smsCategory == SmsUsageMonitor.CATEGORY_STANDARD_SHORT_CODE) { 1012 return true; // not a premium short code 1013 } 1014 1015 // Wait for user confirmation unless the user has set permission to always allow/deny 1016 int premiumSmsPermission = mUsageMonitor.getPremiumSmsPermission( 1017 tracker.mAppInfo.packageName); 1018 if (premiumSmsPermission == SmsUsageMonitor.PREMIUM_SMS_PERMISSION_UNKNOWN) { 1019 // First time trying to send to premium SMS. 1020 premiumSmsPermission = SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ASK_USER; 1021 } 1022 1023 switch (premiumSmsPermission) { 1024 case SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW: 1025 Rlog.d(TAG, "User approved this app to send to premium SMS"); 1026 return true; 1027 1028 case SmsUsageMonitor.PREMIUM_SMS_PERMISSION_NEVER_ALLOW: 1029 Rlog.w(TAG, "User denied this app from sending to premium SMS"); 1030 sendMessage(obtainMessage(EVENT_STOP_SENDING, tracker)); 1031 return false; // reject this message 1032 1033 case SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ASK_USER: 1034 default: 1035 int event; 1036 if (smsCategory == SmsUsageMonitor.CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE) { 1037 event = EVENT_CONFIRM_SEND_TO_POSSIBLE_PREMIUM_SHORT_CODE; 1038 } else { 1039 event = EVENT_CONFIRM_SEND_TO_PREMIUM_SHORT_CODE; 1040 } 1041 sendMessage(obtainMessage(event, tracker)); 1042 return false; // wait for user confirmation 1043 } 1044 } 1045 } 1046 1047 /** 1048 * Deny sending an SMS if the outgoing queue limit is reached. Used when the message 1049 * must be confirmed by the user due to excessive usage or potential premium SMS detected. 1050 * @param tracker the SmsTracker for the message to send 1051 * @return true if the message was denied; false to continue with send confirmation 1052 */ 1053 private boolean denyIfQueueLimitReached(SmsTracker tracker) { 1054 if (mPendingTrackerCount >= MO_MSG_QUEUE_LIMIT) { 1055 // Deny sending message when the queue limit is reached. 1056 Rlog.e(TAG, "Denied because queue limit reached"); 1057 tracker.onFailed(mContext, RESULT_ERROR_LIMIT_EXCEEDED, 0/*errorCode*/); 1058 return true; 1059 } 1060 mPendingTrackerCount++; 1061 return false; 1062 } 1063 1064 /** 1065 * Returns the label for the specified app package name. 1066 * @param appPackage the package name of the app requesting to send an SMS 1067 * @return the label for the specified app, or the package name if getApplicationInfo() fails 1068 */ 1069 private CharSequence getAppLabel(String appPackage) { 1070 PackageManager pm = mContext.getPackageManager(); 1071 try { 1072 ApplicationInfo appInfo = pm.getApplicationInfo(appPackage, 0); 1073 return appInfo.loadLabel(pm); 1074 } catch (PackageManager.NameNotFoundException e) { 1075 Rlog.e(TAG, "PackageManager Name Not Found for package " + appPackage); 1076 return appPackage; // fall back to package name if we can't get app label 1077 } 1078 } 1079 1080 /** 1081 * Post an alert when SMS needs confirmation due to excessive usage. 1082 * @param tracker an SmsTracker for the current message. 1083 */ 1084 protected void handleReachSentLimit(SmsTracker tracker) { 1085 if (denyIfQueueLimitReached(tracker)) { 1086 return; // queue limit reached; error was returned to caller 1087 } 1088 1089 CharSequence appLabel = getAppLabel(tracker.mAppInfo.packageName); 1090 Resources r = Resources.getSystem(); 1091 Spanned messageText = Html.fromHtml(r.getString(R.string.sms_control_message, appLabel)); 1092 1093 ConfirmDialogListener listener = new ConfirmDialogListener(tracker, null); 1094 1095 AlertDialog d = new AlertDialog.Builder(mContext) 1096 .setTitle(R.string.sms_control_title) 1097 .setIcon(R.drawable.stat_sys_warning) 1098 .setMessage(messageText) 1099 .setPositiveButton(r.getString(R.string.sms_control_yes), listener) 1100 .setNegativeButton(r.getString(R.string.sms_control_no), listener) 1101 .setOnCancelListener(listener) 1102 .create(); 1103 1104 d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); 1105 d.show(); 1106 } 1107 1108 /** 1109 * Post an alert for user confirmation when sending to a potential short code. 1110 * @param isPremium true if the destination is known to be a premium short code 1111 * @param tracker the SmsTracker for the current message. 1112 */ 1113 protected void handleConfirmShortCode(boolean isPremium, SmsTracker tracker) { 1114 if (denyIfQueueLimitReached(tracker)) { 1115 return; // queue limit reached; error was returned to caller 1116 } 1117 1118 int detailsId; 1119 if (isPremium) { 1120 detailsId = R.string.sms_premium_short_code_details; 1121 } else { 1122 detailsId = R.string.sms_short_code_details; 1123 } 1124 1125 CharSequence appLabel = getAppLabel(tracker.mAppInfo.packageName); 1126 Resources r = Resources.getSystem(); 1127 Spanned messageText = Html.fromHtml(r.getString(R.string.sms_short_code_confirm_message, 1128 appLabel, tracker.mDestAddress)); 1129 1130 LayoutInflater inflater = (LayoutInflater) mContext.getSystemService( 1131 Context.LAYOUT_INFLATER_SERVICE); 1132 View layout = inflater.inflate(R.layout.sms_short_code_confirmation_dialog, null); 1133 1134 ConfirmDialogListener listener = new ConfirmDialogListener(tracker, 1135 (TextView)layout.findViewById(R.id.sms_short_code_remember_undo_instruction)); 1136 1137 1138 TextView messageView = (TextView) layout.findViewById(R.id.sms_short_code_confirm_message); 1139 messageView.setText(messageText); 1140 1141 ViewGroup detailsLayout = (ViewGroup) layout.findViewById( 1142 R.id.sms_short_code_detail_layout); 1143 TextView detailsView = (TextView) detailsLayout.findViewById( 1144 R.id.sms_short_code_detail_message); 1145 detailsView.setText(detailsId); 1146 1147 CheckBox rememberChoice = (CheckBox) layout.findViewById( 1148 R.id.sms_short_code_remember_choice_checkbox); 1149 rememberChoice.setOnCheckedChangeListener(listener); 1150 1151 AlertDialog d = new AlertDialog.Builder(mContext) 1152 .setView(layout) 1153 .setPositiveButton(r.getString(R.string.sms_short_code_confirm_allow), listener) 1154 .setNegativeButton(r.getString(R.string.sms_short_code_confirm_deny), listener) 1155 .setOnCancelListener(listener) 1156 .create(); 1157 1158 d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); 1159 d.show(); 1160 1161 listener.setPositiveButton(d.getButton(DialogInterface.BUTTON_POSITIVE)); 1162 listener.setNegativeButton(d.getButton(DialogInterface.BUTTON_NEGATIVE)); 1163 } 1164 1165 /** 1166 * Returns the premium SMS permission for the specified package. If the package has never 1167 * been seen before, the default {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ASK_USER} 1168 * will be returned. 1169 * @param packageName the name of the package to query permission 1170 * @return one of {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_UNKNOWN}, 1171 * {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ASK_USER}, 1172 * {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_NEVER_ALLOW}, or 1173 * {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW} 1174 */ 1175 public int getPremiumSmsPermission(String packageName) { 1176 return mUsageMonitor.getPremiumSmsPermission(packageName); 1177 } 1178 1179 /** 1180 * Sets the premium SMS permission for the specified package and save the value asynchronously 1181 * to persistent storage. 1182 * @param packageName the name of the package to set permission 1183 * @param permission one of {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ASK_USER}, 1184 * {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_NEVER_ALLOW}, or 1185 * {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW} 1186 */ 1187 public void setPremiumSmsPermission(String packageName, int permission) { 1188 mUsageMonitor.setPremiumSmsPermission(packageName, permission); 1189 } 1190 1191 /** 1192 * Send the message along to the radio. 1193 * 1194 * @param tracker holds the SMS message to send 1195 */ 1196 protected abstract void sendSms(SmsTracker tracker); 1197 1198 /** 1199 * Send the SMS via the PSTN network. 1200 * 1201 * @param tracker holds the Sms tracker ready to be sent 1202 */ 1203 protected abstract void sendSmsByPstn(SmsTracker tracker); 1204 1205 /** 1206 * Retry the message along to the radio. 1207 * 1208 * @param tracker holds the SMS message to send 1209 */ 1210 public void sendRetrySms(SmsTracker tracker) { 1211 // re-routing to ImsSMSDispatcher 1212 if (mImsSMSDispatcher != null) { 1213 mImsSMSDispatcher.sendRetrySms(tracker); 1214 } else { 1215 Rlog.e(TAG, mImsSMSDispatcher + " is null. Retry failed"); 1216 } 1217 } 1218 1219 /** 1220 * Send the multi-part SMS based on multipart Sms tracker 1221 * 1222 * @param tracker holds the multipart Sms tracker ready to be sent 1223 */ 1224 private void sendMultipartSms(SmsTracker tracker) { 1225 ArrayList<String> parts; 1226 ArrayList<PendingIntent> sentIntents; 1227 ArrayList<PendingIntent> deliveryIntents; 1228 1229 HashMap<String, Object> map = tracker.mData; 1230 1231 String destinationAddress = (String) map.get("destination"); 1232 String scAddress = (String) map.get("scaddress"); 1233 1234 parts = (ArrayList<String>) map.get("parts"); 1235 sentIntents = (ArrayList<PendingIntent>) map.get("sentIntents"); 1236 deliveryIntents = (ArrayList<PendingIntent>) map.get("deliveryIntents"); 1237 1238 // check if in service 1239 int ss = mPhone.getServiceState().getState(); 1240 // if sms over IMS is not supported on data and voice is not available... 1241 if (!isIms() && ss != ServiceState.STATE_IN_SERVICE) { 1242 for (int i = 0, count = parts.size(); i < count; i++) { 1243 PendingIntent sentIntent = null; 1244 if (sentIntents != null && sentIntents.size() > i) { 1245 sentIntent = sentIntents.get(i); 1246 } 1247 handleNotInService(ss, sentIntent); 1248 } 1249 return; 1250 } 1251 1252 sendMultipartText(destinationAddress, scAddress, parts, sentIntents, deliveryIntents, 1253 null/*messageUri*/, null/*callingPkg*/); 1254 } 1255 1256 /** 1257 * Keeps track of an SMS that has been sent to the RIL, until it has 1258 * successfully been sent, or we're done trying. 1259 */ 1260 protected static final class SmsTracker { 1261 // fields need to be public for derived SmsDispatchers 1262 public final HashMap<String, Object> mData; 1263 public int mRetryCount; 1264 public int mImsRetry; // nonzero indicates initial message was sent over Ims 1265 public int mMessageRef; 1266 public boolean mExpectMore; 1267 String mFormat; 1268 1269 public final PendingIntent mSentIntent; 1270 public final PendingIntent mDeliveryIntent; 1271 1272 public final PackageInfo mAppInfo; 1273 public final String mDestAddress; 1274 1275 public final SmsHeader mSmsHeader; 1276 1277 private long mTimestamp = System.currentTimeMillis(); 1278 public Uri mMessageUri; // Uri of persisted message if we wrote one 1279 1280 // Reference to states of a multipart message that this part belongs to 1281 private AtomicInteger mUnsentPartCount; 1282 private AtomicBoolean mAnyPartFailed; 1283 // The full message content of a single part message 1284 // or a multipart message that this part belongs to 1285 private String mFullMessageText; 1286 1287 private int mSubId; 1288 1289 // If this is a text message (instead of data message) 1290 private boolean mIsText; 1291 1292 private SmsTracker(HashMap<String, Object> data, PendingIntent sentIntent, 1293 PendingIntent deliveryIntent, PackageInfo appInfo, String destAddr, String format, 1294 AtomicInteger unsentPartCount, AtomicBoolean anyPartFailed, Uri messageUri, 1295 SmsHeader smsHeader, boolean isExpectMore, String fullMessageText, int subId, 1296 boolean isText) { 1297 mData = data; 1298 mSentIntent = sentIntent; 1299 mDeliveryIntent = deliveryIntent; 1300 mRetryCount = 0; 1301 mAppInfo = appInfo; 1302 mDestAddress = destAddr; 1303 mFormat = format; 1304 mExpectMore = isExpectMore; 1305 mImsRetry = 0; 1306 mMessageRef = 0; 1307 mUnsentPartCount = unsentPartCount; 1308 mAnyPartFailed = anyPartFailed; 1309 mMessageUri = messageUri; 1310 mSmsHeader = smsHeader; 1311 mFullMessageText = fullMessageText; 1312 mSubId = subId; 1313 mIsText = isText; 1314 } 1315 1316 /** 1317 * Returns whether this tracker holds a multi-part SMS. 1318 * @return true if the tracker holds a multi-part SMS; false otherwise 1319 */ 1320 boolean isMultipart() { 1321 return mData.containsKey("parts"); 1322 } 1323 1324 /** 1325 * Update the status of this message if we persisted it 1326 */ 1327 public void updateSentMessageStatus(Context context, int status) { 1328 if (mMessageUri != null) { 1329 // If we wrote this message in writeSentMessage, update it now 1330 ContentValues values = new ContentValues(1); 1331 values.put(Sms.STATUS, status); 1332 SqliteWrapper.update(context, context.getContentResolver(), 1333 mMessageUri, values, null, null); 1334 } 1335 } 1336 1337 /** 1338 * Set the final state of a message: FAILED or SENT 1339 * 1340 * @param context The Context 1341 * @param messageType The final message type 1342 * @param errorCode The error code 1343 */ 1344 private void updateMessageState(Context context, int messageType, int errorCode) { 1345 if (mMessageUri == null) { 1346 return; 1347 } 1348 final ContentValues values = new ContentValues(2); 1349 values.put(Sms.TYPE, messageType); 1350 values.put(Sms.ERROR_CODE, errorCode); 1351 final long identity = Binder.clearCallingIdentity(); 1352 try { 1353 if (SqliteWrapper.update(context, context.getContentResolver(), mMessageUri, values, 1354 null/*where*/, null/*selectionArgs*/) != 1) { 1355 Rlog.e(TAG, "Failed to move message to " + messageType); 1356 } 1357 } finally { 1358 Binder.restoreCallingIdentity(identity); 1359 } 1360 } 1361 1362 /** 1363 * Persist a sent SMS if required: 1364 * 1. It is a text message 1365 * 2. SmsApplication tells us to persist: sent from apps that are not default-SMS app or 1366 * bluetooth 1367 * 1368 * @param context 1369 * @param messageType The folder to store (FAILED or SENT) 1370 * @param errorCode The current error code for this SMS or SMS part 1371 * @return The telephony provider URI if stored 1372 */ 1373 private Uri persistSentMessageIfRequired(Context context, int messageType, int errorCode) { 1374 if (!mIsText || 1375 !SmsApplication.shouldWriteMessageForPackage(mAppInfo.packageName, context)) { 1376 return null; 1377 } 1378 Rlog.d(TAG, "Persist SMS into " 1379 + (messageType == Sms.MESSAGE_TYPE_FAILED ? "FAILED" : "SENT")); 1380 final ContentValues values = new ContentValues(); 1381 values.put(Sms.SUBSCRIPTION_ID, mSubId); 1382 values.put(Sms.ADDRESS, mDestAddress); 1383 values.put(Sms.BODY, mFullMessageText); 1384 values.put(Sms.DATE, System.currentTimeMillis()); // milliseconds 1385 values.put(Sms.SEEN, 1); 1386 values.put(Sms.READ, 1); 1387 final String creator = mAppInfo != null ? mAppInfo.packageName : null; 1388 if (!TextUtils.isEmpty(creator)) { 1389 values.put(Sms.CREATOR, creator); 1390 } 1391 if (mDeliveryIntent != null) { 1392 values.put(Sms.STATUS, Telephony.Sms.STATUS_PENDING); 1393 } 1394 if (errorCode != 0) { 1395 values.put(Sms.ERROR_CODE, errorCode); 1396 } 1397 final long identity = Binder.clearCallingIdentity(); 1398 final ContentResolver resolver = context.getContentResolver(); 1399 try { 1400 final Uri uri = resolver.insert(Telephony.Sms.Sent.CONTENT_URI, values); 1401 if (uri != null && messageType == Sms.MESSAGE_TYPE_FAILED) { 1402 // Since we can't persist a message directly into FAILED box, 1403 // we have to update the column after we persist it into SENT box. 1404 // The gap between the state change is tiny so I would not expect 1405 // it to cause any serious problem 1406 // TODO: we should add a "failed" URI for this in SmsProvider? 1407 final ContentValues updateValues = new ContentValues(1); 1408 updateValues.put(Sms.TYPE, Sms.MESSAGE_TYPE_FAILED); 1409 resolver.update(uri, updateValues, null/*where*/, null/*selectionArgs*/); 1410 } 1411 return uri; 1412 } catch (Exception e) { 1413 Rlog.e(TAG, "writeOutboxMessage: Failed to persist outbox message", e); 1414 return null; 1415 } finally { 1416 Binder.restoreCallingIdentity(identity); 1417 } 1418 } 1419 1420 /** 1421 * Persist or update an SMS depending on if we send a new message or a stored message 1422 * 1423 * @param context 1424 * @param messageType The message folder for this SMS, FAILED or SENT 1425 * @param errorCode The current error code for this SMS or SMS part 1426 */ 1427 private void persistOrUpdateMessage(Context context, int messageType, int errorCode) { 1428 if (mMessageUri != null) { 1429 updateMessageState(context, messageType, errorCode); 1430 } else { 1431 mMessageUri = persistSentMessageIfRequired(context, messageType, errorCode); 1432 } 1433 } 1434 1435 /** 1436 * Handle a failure of a single part message or a part of a multipart message 1437 * 1438 * @param context The Context 1439 * @param error The error to send back with 1440 * @param errorCode 1441 */ 1442 public void onFailed(Context context, int error, int errorCode) { 1443 if (mAnyPartFailed != null) { 1444 mAnyPartFailed.set(true); 1445 } 1446 // is single part or last part of multipart message 1447 boolean isSinglePartOrLastPart = true; 1448 if (mUnsentPartCount != null) { 1449 isSinglePartOrLastPart = mUnsentPartCount.decrementAndGet() == 0; 1450 } 1451 if (isSinglePartOrLastPart) { 1452 persistOrUpdateMessage(context, Sms.MESSAGE_TYPE_FAILED, errorCode); 1453 } 1454 if (mSentIntent != null) { 1455 try { 1456 // Extra information to send with the sent intent 1457 Intent fillIn = new Intent(); 1458 if (mMessageUri != null) { 1459 // Pass this to SMS apps so that they know where it is stored 1460 fillIn.putExtra("uri", mMessageUri.toString()); 1461 } 1462 if (errorCode != 0) { 1463 fillIn.putExtra("errorCode", errorCode); 1464 } 1465 if (mUnsentPartCount != null && isSinglePartOrLastPart) { 1466 // Is multipart and last part 1467 fillIn.putExtra(SEND_NEXT_MSG_EXTRA, true); 1468 } 1469 mSentIntent.send(context, error, fillIn); 1470 } catch (CanceledException ex) { 1471 Rlog.e(TAG, "Failed to send result"); 1472 } 1473 } 1474 } 1475 1476 /** 1477 * Handle the sent of a single part message or a part of a multipart message 1478 * 1479 * @param context The Context 1480 */ 1481 public void onSent(Context context) { 1482 // is single part or last part of multipart message 1483 boolean isSinglePartOrLastPart = true; 1484 if (mUnsentPartCount != null) { 1485 isSinglePartOrLastPart = mUnsentPartCount.decrementAndGet() == 0; 1486 } 1487 if (isSinglePartOrLastPart) { 1488 int messageType = Sms.MESSAGE_TYPE_SENT; 1489 if (mAnyPartFailed != null && mAnyPartFailed.get()) { 1490 messageType = Sms.MESSAGE_TYPE_FAILED; 1491 } 1492 persistOrUpdateMessage(context, messageType, 0/*errorCode*/); 1493 } 1494 if (mSentIntent != null) { 1495 try { 1496 // Extra information to send with the sent intent 1497 Intent fillIn = new Intent(); 1498 if (mMessageUri != null) { 1499 // Pass this to SMS apps so that they know where it is stored 1500 fillIn.putExtra("uri", mMessageUri.toString()); 1501 } 1502 if (mUnsentPartCount != null && isSinglePartOrLastPart) { 1503 // Is multipart and last part 1504 fillIn.putExtra(SEND_NEXT_MSG_EXTRA, true); 1505 } 1506 mSentIntent.send(context, Activity.RESULT_OK, fillIn); 1507 } catch (CanceledException ex) { 1508 Rlog.e(TAG, "Failed to send result"); 1509 } 1510 } 1511 } 1512 } 1513 1514 protected SmsTracker getSmsTracker(HashMap<String, Object> data, PendingIntent sentIntent, 1515 PendingIntent deliveryIntent, String format, AtomicInteger unsentPartCount, 1516 AtomicBoolean anyPartFailed, Uri messageUri, SmsHeader smsHeader, 1517 boolean isExpectMore, String fullMessageText, boolean isText) { 1518 // Get calling app package name via UID from Binder call 1519 PackageManager pm = mContext.getPackageManager(); 1520 String[] packageNames = pm.getPackagesForUid(Binder.getCallingUid()); 1521 1522 // Get package info via packagemanager 1523 PackageInfo appInfo = null; 1524 if (packageNames != null && packageNames.length > 0) { 1525 try { 1526 // XXX this is lossy- apps can share a UID 1527 appInfo = pm.getPackageInfo(packageNames[0], PackageManager.GET_SIGNATURES); 1528 } catch (PackageManager.NameNotFoundException e) { 1529 // error will be logged in sendRawPdu 1530 } 1531 } 1532 // Strip non-digits from destination phone number before checking for short codes 1533 // and before displaying the number to the user if confirmation is required. 1534 String destAddr = PhoneNumberUtils.extractNetworkPortion((String) data.get("destAddr")); 1535 return new SmsTracker(data, sentIntent, deliveryIntent, appInfo, destAddr, format, 1536 unsentPartCount, anyPartFailed, messageUri, smsHeader, isExpectMore, 1537 fullMessageText, getSubId(), isText); 1538 } 1539 1540 protected SmsTracker getSmsTracker(HashMap<String, Object> data, PendingIntent sentIntent, 1541 PendingIntent deliveryIntent, String format, Uri messageUri, boolean isExpectMore, 1542 String fullMessageText, boolean isText) { 1543 return getSmsTracker(data, sentIntent, deliveryIntent, format, null/*unsentPartCount*/, 1544 null/*anyPartFailed*/, messageUri, null/*smsHeader*/, isExpectMore, 1545 fullMessageText, isText); 1546 } 1547 1548 protected HashMap<String, Object> getSmsTrackerMap(String destAddr, String scAddr, 1549 String text, SmsMessageBase.SubmitPduBase pdu) { 1550 HashMap<String, Object> map = new HashMap<String, Object>(); 1551 map.put("destAddr", destAddr); 1552 map.put("scAddr", scAddr); 1553 map.put("text", text); 1554 map.put("smsc", pdu.encodedScAddress); 1555 map.put("pdu", pdu.encodedMessage); 1556 return map; 1557 } 1558 1559 protected HashMap<String, Object> getSmsTrackerMap(String destAddr, String scAddr, 1560 int destPort, byte[] data, SmsMessageBase.SubmitPduBase pdu) { 1561 HashMap<String, Object> map = new HashMap<String, Object>(); 1562 map.put("destAddr", destAddr); 1563 map.put("scAddr", scAddr); 1564 map.put("destPort", destPort); 1565 map.put("data", data); 1566 map.put("smsc", pdu.encodedScAddress); 1567 map.put("pdu", pdu.encodedMessage); 1568 return map; 1569 } 1570 1571 /** 1572 * Dialog listener for SMS confirmation dialog. 1573 */ 1574 private final class ConfirmDialogListener 1575 implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener, 1576 CompoundButton.OnCheckedChangeListener { 1577 1578 private final SmsTracker mTracker; 1579 private Button mPositiveButton; 1580 private Button mNegativeButton; 1581 private boolean mRememberChoice; // default is unchecked 1582 private final TextView mRememberUndoInstruction; 1583 1584 ConfirmDialogListener(SmsTracker tracker, TextView textView) { 1585 mTracker = tracker; 1586 mRememberUndoInstruction = textView; 1587 } 1588 1589 void setPositiveButton(Button button) { 1590 mPositiveButton = button; 1591 } 1592 1593 void setNegativeButton(Button button) { 1594 mNegativeButton = button; 1595 } 1596 1597 @Override 1598 public void onClick(DialogInterface dialog, int which) { 1599 // Always set the SMS permission so that Settings will show a permission setting 1600 // for the app (it won't be shown until after the app tries to send to a short code). 1601 int newSmsPermission = SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ASK_USER; 1602 1603 if (which == DialogInterface.BUTTON_POSITIVE) { 1604 Rlog.d(TAG, "CONFIRM sending SMS"); 1605 // XXX this is lossy- apps can have more than one signature 1606 EventLog.writeEvent(EventLogTags.EXP_DET_SMS_SENT_BY_USER, 1607 mTracker.mAppInfo.applicationInfo == null ? 1608 -1 : mTracker.mAppInfo.applicationInfo.uid); 1609 sendMessage(obtainMessage(EVENT_SEND_CONFIRMED_SMS, mTracker)); 1610 if (mRememberChoice) { 1611 newSmsPermission = SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW; 1612 } 1613 } else if (which == DialogInterface.BUTTON_NEGATIVE) { 1614 Rlog.d(TAG, "DENY sending SMS"); 1615 // XXX this is lossy- apps can have more than one signature 1616 EventLog.writeEvent(EventLogTags.EXP_DET_SMS_DENIED_BY_USER, 1617 mTracker.mAppInfo.applicationInfo == null ? 1618 -1 : mTracker.mAppInfo.applicationInfo.uid); 1619 sendMessage(obtainMessage(EVENT_STOP_SENDING, mTracker)); 1620 if (mRememberChoice) { 1621 newSmsPermission = SmsUsageMonitor.PREMIUM_SMS_PERMISSION_NEVER_ALLOW; 1622 } 1623 } 1624 setPremiumSmsPermission(mTracker.mAppInfo.packageName, newSmsPermission); 1625 } 1626 1627 @Override 1628 public void onCancel(DialogInterface dialog) { 1629 Rlog.d(TAG, "dialog dismissed: don't send SMS"); 1630 sendMessage(obtainMessage(EVENT_STOP_SENDING, mTracker)); 1631 } 1632 1633 @Override 1634 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 1635 Rlog.d(TAG, "remember this choice: " + isChecked); 1636 mRememberChoice = isChecked; 1637 if (isChecked) { 1638 mPositiveButton.setText(R.string.sms_short_code_confirm_always_allow); 1639 mNegativeButton.setText(R.string.sms_short_code_confirm_never_allow); 1640 if (mRememberUndoInstruction != null) { 1641 mRememberUndoInstruction. 1642 setText(R.string.sms_short_code_remember_undo_instruction); 1643 mRememberUndoInstruction.setPadding(0,0,0,32); 1644 } 1645 } else { 1646 mPositiveButton.setText(R.string.sms_short_code_confirm_allow); 1647 mNegativeButton.setText(R.string.sms_short_code_confirm_deny); 1648 if (mRememberUndoInstruction != null) { 1649 mRememberUndoInstruction.setText(""); 1650 mRememberUndoInstruction.setPadding(0,0,0,0); 1651 } 1652 } 1653 } 1654 } 1655 1656 public boolean isIms() { 1657 if (mImsSMSDispatcher != null) { 1658 return mImsSMSDispatcher.isIms(); 1659 } else { 1660 Rlog.e(TAG, mImsSMSDispatcher + " is null"); 1661 return false; 1662 } 1663 } 1664 1665 public String getImsSmsFormat() { 1666 if (mImsSMSDispatcher != null) { 1667 return mImsSMSDispatcher.getImsSmsFormat(); 1668 } else { 1669 Rlog.e(TAG, mImsSMSDispatcher + " is null"); 1670 return null; 1671 } 1672 } 1673 1674 private String getMultipartMessageText(ArrayList<String> parts) { 1675 final StringBuilder sb = new StringBuilder(); 1676 for (String part : parts) { 1677 if (part != null) { 1678 sb.append(part); 1679 } 1680 } 1681 return sb.toString(); 1682 } 1683 1684 protected String getCarrierAppPackageName() { 1685 UiccCard card = UiccController.getInstance().getUiccCard(mPhone.getPhoneId()); 1686 if (card == null) { 1687 return null; 1688 } 1689 1690 List<String> carrierPackages = card.getCarrierPackageNamesForIntent( 1691 mContext.getPackageManager(), new Intent(CarrierMessagingService.SERVICE_INTERFACE)); 1692 return (carrierPackages != null && carrierPackages.size() == 1) ? 1693 carrierPackages.get(0) : null; 1694 } 1695 1696 protected int getSubId() { 1697 return SubscriptionController.getInstance().getSubIdUsingPhoneId(mPhone.mPhoneId); 1698 } 1699} 1700