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