1 /* 2 * Copyright (C) 2006 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.internal.telephony; 18 import android.app.Activity; 19 import android.app.AlertDialog; 20 import android.app.PendingIntent; 21 import android.app.PendingIntent.CanceledException; 22 import android.content.BroadcastReceiver; 23 import android.content.ContentResolver; 24 import android.content.ContentValues; 25 import android.content.Context; 26 import android.content.DialogInterface; 27 import android.content.Intent; 28 import android.content.pm.ApplicationInfo; 29 import android.content.pm.PackageInfo; 30 import android.content.pm.PackageManager; 31 import android.content.res.Resources; 32 import android.database.ContentObserver; 33 import android.database.sqlite.SqliteWrapper; 34 import android.net.Uri; 35 import android.os.AsyncResult; 36 import android.os.Binder; 37 import android.os.Bundle; 38 import android.os.Handler; 39 import android.os.Message; 40 import android.os.SystemProperties; 41 import android.provider.Settings; 42 import android.provider.Telephony; 43 import android.provider.Telephony.Sms; 44 import android.provider.Telephony.Sms.Intents; 45 import android.telephony.PhoneNumberUtils; 46 import android.telephony.Rlog; 47 import android.telephony.ServiceState; 48 import android.telephony.SmsManager; 49 import android.telephony.TelephonyManager; 50 import android.text.Html; 51 import android.text.Spanned; 52 import android.text.TextUtils; 53 import android.util.EventLog; 54 import android.view.LayoutInflater; 55 import android.view.View; 56 import android.view.ViewGroup; 57 import android.view.WindowManager; 58 import android.widget.Button; 59 import android.widget.CheckBox; 60 import android.widget.CompoundButton; 61 import android.widget.TextView; 62 63 import com.android.internal.R; 64 import com.android.internal.telephony.GsmAlphabet.TextEncodingDetails; 65 import com.android.internal.telephony.uicc.UiccCard; 66 import com.android.internal.telephony.uicc.UiccController; 67 68 import java.util.ArrayList; 69 import java.util.Collections; 70 import java.util.HashMap; 71 import java.util.List; 72 import java.util.Random; 73 import java.util.concurrent.atomic.AtomicBoolean; 74 import java.util.concurrent.atomic.AtomicInteger; 75 76 import static android.telephony.SmsManager.RESULT_ERROR_FDN_CHECK_FAILURE; 77 import static android.telephony.SmsManager.RESULT_ERROR_GENERIC_FAILURE; 78 import static android.telephony.SmsManager.RESULT_ERROR_LIMIT_EXCEEDED; 79 import static android.telephony.SmsManager.RESULT_ERROR_NO_SERVICE; 80 import static android.telephony.SmsManager.RESULT_ERROR_NULL_PDU; 81 import static android.telephony.SmsManager.RESULT_ERROR_RADIO_OFF; 82 83 public 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 = !SystemProperties.getBoolean( 196 TelephonyProperties.PROPERTY_SMS_SEND, 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 /** Outgoing messages being handled by the carrier app. */ 266 protected final List<SmsTracker> sendPendingList = 267 Collections.synchronizedList(new ArrayList<SmsTracker>()); 268 269 /** 270 * Handles events coming from the phone stack. Overridden from handler. 271 * 272 * @param msg the message to handle 273 */ 274 @Override 275 public void handleMessage(Message msg) { 276 switch (msg.what) { 277 case EVENT_SEND_SMS_COMPLETE: 278 // An outbound SMS has been successfully transferred, or failed. 279 handleSendComplete((AsyncResult) msg.obj); 280 break; 281 282 case EVENT_SEND_RETRY: 283 Rlog.d(TAG, "SMS retry.."); 284 sendRetrySms((SmsTracker) msg.obj); 285 break; 286 287 case EVENT_SEND_LIMIT_REACHED_CONFIRMATION: 288 handleReachSentLimit((SmsTracker)(msg.obj)); 289 break; 290 291 case EVENT_CONFIRM_SEND_TO_POSSIBLE_PREMIUM_SHORT_CODE: 292 handleConfirmShortCode(false, (SmsTracker)(msg.obj)); 293 break; 294 295 case EVENT_CONFIRM_SEND_TO_PREMIUM_SHORT_CODE: 296 handleConfirmShortCode(true, (SmsTracker)(msg.obj)); 297 break; 298 299 case EVENT_SEND_CONFIRMED_SMS: 300 { 301 SmsTracker tracker = (SmsTracker) msg.obj; 302 if (tracker.isMultipart()) { 303 sendMultipartSms(tracker); 304 } else { 305 if (mPendingTrackerCount > 1) { 306 tracker.mExpectMore = true; 307 } else { 308 tracker.mExpectMore = false; 309 } 310 sendSms(tracker); 311 } 312 mPendingTrackerCount--; 313 break; 314 } 315 316 case EVENT_STOP_SENDING: 317 { 318 SmsTracker tracker = (SmsTracker) msg.obj; 319 tracker.onFailed(mContext, RESULT_ERROR_LIMIT_EXCEEDED, 0/*errorCode*/); 320 mPendingTrackerCount--; 321 break; 322 } 323 324 case EVENT_HANDLE_STATUS_REPORT: 325 handleStatusReport(msg.obj); 326 break; 327 328 default: 329 Rlog.e(TAG, "handleMessage() ignoring message of unexpected type " + msg.what); 330 } 331 } 332 333 /** 334 * Called when SMS send completes. Broadcasts a sentIntent on success. 335 * On failure, either sets up retries or broadcasts a sentIntent with 336 * the failure in the result code. 337 * 338 * @param ar AsyncResult passed into the message handler. ar.result should 339 * an SmsResponse instance if send was successful. ar.userObj 340 * should be an SmsTracker instance. 341 */ 342 protected void handleSendComplete(AsyncResult ar) { 343 SmsTracker tracker = (SmsTracker) ar.userObj; 344 PendingIntent sentIntent = tracker.mSentIntent; 345 346 if (ar.result != null) { 347 tracker.mMessageRef = ((SmsResponse)ar.result).mMessageRef; 348 } else { 349 Rlog.d(TAG, "SmsResponse was null"); 350 } 351 352 if (ar.exception == null) { 353 if (DBG) Rlog.d(TAG, "SMS send complete. Broadcasting intent: " + sentIntent); 354 355 if (tracker.mDeliveryIntent != null) { 356 // Expecting a status report. Add it to the list. 357 deliveryPendingList.add(tracker); 358 } 359 tracker.onSent(mContext); 360 } else { 361 if (DBG) Rlog.d(TAG, "SMS send failed"); 362 363 int ss = mPhone.getServiceState().getState(); 364 365 if ( tracker.mImsRetry > 0 && ss != ServiceState.STATE_IN_SERVICE) { 366 // This is retry after failure over IMS but voice is not available. 367 // Set retry to max allowed, so no retry is sent and 368 // cause RESULT_ERROR_GENERIC_FAILURE to be returned to app. 369 tracker.mRetryCount = MAX_SEND_RETRIES; 370 371 Rlog.d(TAG, "handleSendComplete: Skipping retry: " 372 +" isIms()="+isIms() 373 +" mRetryCount="+tracker.mRetryCount 374 +" mImsRetry="+tracker.mImsRetry 375 +" mMessageRef="+tracker.mMessageRef 376 +" SS= "+mPhone.getServiceState().getState()); 377 } 378 379 // if sms over IMS is not supported on data and voice is not available... 380 if (!isIms() && ss != ServiceState.STATE_IN_SERVICE) { 381 tracker.onFailed(mContext, getNotInServiceError(ss), 0/*errorCode*/); 382 } else if ((((CommandException)(ar.exception)).getCommandError() 383 == CommandException.Error.SMS_FAIL_RETRY) && 384 tracker.mRetryCount < MAX_SEND_RETRIES) { 385 // Retry after a delay if needed. 386 // TODO: According to TS 23.040, 9.2.3.6, we should resend 387 // with the same TP-MR as the failed message, and 388 // TP-RD set to 1. However, we don't have a means of 389 // knowing the MR for the failed message (EF_SMSstatus 390 // may or may not have the MR corresponding to this 391 // message, depending on the failure). Also, in some 392 // implementations this retry is handled by the baseband. 393 tracker.mRetryCount++; 394 Message retryMsg = obtainMessage(EVENT_SEND_RETRY, tracker); 395 sendMessageDelayed(retryMsg, SEND_RETRY_DELAY); 396 } else { 397 int errorCode = 0; 398 if (ar.result != null) { 399 errorCode = ((SmsResponse)ar.result).mErrorCode; 400 } 401 int error = RESULT_ERROR_GENERIC_FAILURE; 402 if (((CommandException)(ar.exception)).getCommandError() 403 == CommandException.Error.FDN_CHECK_FAILURE) { 404 error = RESULT_ERROR_FDN_CHECK_FAILURE; 405 } 406 tracker.onFailed(mContext, error, errorCode); 407 } 408 } 409 } 410 411 /** 412 * Handles outbound message when the phone is not in service. 413 * 414 * @param ss Current service state. Valid values are: 415 * OUT_OF_SERVICE 416 * EMERGENCY_ONLY 417 * POWER_OFF 418 * @param sentIntent the PendingIntent to send the error to 419 */ 420 protected static void handleNotInService(int ss, PendingIntent sentIntent) { 421 if (sentIntent != null) { 422 try { 423 if (ss == ServiceState.STATE_POWER_OFF) { 424 sentIntent.send(RESULT_ERROR_RADIO_OFF); 425 } else { 426 sentIntent.send(RESULT_ERROR_NO_SERVICE); 427 } 428 } catch (CanceledException ex) {} 429 } 430 } 431 432 /** 433 * @param ss service state 434 * @return The result error based on input service state for not in service error 435 */ 436 protected static int getNotInServiceError(int ss) { 437 if (ss == ServiceState.STATE_POWER_OFF) { 438 return RESULT_ERROR_RADIO_OFF; 439 } 440 return RESULT_ERROR_NO_SERVICE; 441 } 442 443 /** 444 * Send a data based SMS to a specific application port. 445 * 446 * @param destAddr the address to send the message to 447 * @param scAddr is the service center address or null to use 448 * the current default SMSC 449 * @param destPort the port to deliver the message to 450 * @param data the body of the message to send 451 * @param sentIntent if not NULL this <code>PendingIntent</code> is 452 * broadcast when the message is successfully sent, or failed. 453 * The result code will be <code>Activity.RESULT_OK<code> for success, 454 * or one of these errors:<br> 455 * <code>RESULT_ERROR_GENERIC_FAILURE</code><br> 456 * <code>RESULT_ERROR_RADIO_OFF</code><br> 457 * <code>RESULT_ERROR_NULL_PDU</code><br> 458 * <code>RESULT_ERROR_NO_SERVICE</code><br>. 459 * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include 460 * the extra "errorCode" containing a radio technology specific value, 461 * generally only useful for troubleshooting.<br> 462 * The per-application based SMS control checks sentIntent. If sentIntent 463 * is NULL the caller will be checked against all unknown applications, 464 * which cause smaller number of SMS to be sent in checking period. 465 * @param deliveryIntent if not NULL this <code>PendingIntent</code> is 466 * broadcast when the message is delivered to the recipient. The 467 * raw pdu of the status report is in the extended data ("pdu"). 468 */ 469 protected abstract void sendData(String destAddr, String scAddr, int destPort, 470 byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent); 471 472 /** 473 * Send a text based SMS. 474 * @param destAddr the address to send the message to 475 * @param scAddr is the service center address or null to use 476 * the current default SMSC 477 * @param text the body of the message to send 478 * @param sentIntent if not NULL this <code>PendingIntent</code> is 479 * broadcast when the message is successfully sent, or failed. 480 * The result code will be <code>Activity.RESULT_OK<code> for success, 481 * or one of these errors:<br> 482 * <code>RESULT_ERROR_GENERIC_FAILURE</code><br> 483 * <code>RESULT_ERROR_RADIO_OFF</code><br> 484 * <code>RESULT_ERROR_NULL_PDU</code><br> 485 * <code>RESULT_ERROR_NO_SERVICE</code><br>. 486 * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include 487 * the extra "errorCode" containing a radio technology specific value, 488 * generally only useful for troubleshooting.<br> 489 * The per-application based SMS control checks sentIntent. If sentIntent 490 * is NULL the caller will be checked against all unknown applications, 491 * which cause smaller number of SMS to be sent in checking period. 492 * @param deliveryIntent if not NULL this <code>PendingIntent</code> is 493 * broadcast when the message is delivered to the recipient. The 494 * @param messageUri optional URI of the message if it is already stored in the system 495 * @param callingPkg the calling package name 496 */ 497 protected abstract void sendText(String destAddr, String scAddr, String text, 498 PendingIntent sentIntent, PendingIntent deliveryIntent, Uri messageUri, 499 String callingPkg); 500 501 /** 502 * Inject an SMS PDU into the android platform. 503 * 504 * @param pdu is the byte array of pdu to be injected into android telephony layer 505 * @param format is the format of SMS pdu (3gpp or 3gpp2) 506 * @param receivedIntent if not NULL this <code>PendingIntent</code> is 507 * broadcast when the message is successfully received by the 508 * android telephony layer. This intent is broadcasted at 509 * the same time an SMS received from radio is responded back. 510 */ 511 protected abstract void injectSmsPdu(byte[] pdu, String format, PendingIntent receivedIntent); 512 513 /** 514 * Calculate the number of septets needed to encode the message. 515 * 516 * @param messageBody the message to encode 517 * @param use7bitOnly ignore (but still count) illegal characters if true 518 * @return TextEncodingDetails 519 */ 520 protected abstract TextEncodingDetails calculateLength(CharSequence messageBody, 521 boolean use7bitOnly); 522 523 /** 524 * Update the status of a pending (send-by-IP) SMS message and resend by PSTN if necessary. 525 * This outbound message was handled by the carrier app. If the carrier app fails to send 526 * this message, it would be resent by PSTN. 527 * 528 * @param messageRef the reference number of the SMS message. 529 * @param success True if and only if the message was sent successfully. If its value is 530 * false, this message should be resent via PSTN. 531 */ 532 protected abstract void updateSmsSendStatus(int messageRef, boolean success); 533 534 /** 535 * Handler for a {@link GsmSMSDispatcher} or {@link CdmaSMSDispatcher} broadcast. 536 * If SMS sending is successfuly, sends EVENT_SEND_SMS_COMPLETE message. Otherwise, 537 * send the message via the GSM/CDMA network. 538 */ 539 protected final class SMSDispatcherReceiver extends BroadcastReceiver { 540 541 private final SmsTracker mTracker; 542 543 public SMSDispatcherReceiver(SmsTracker tracker) { 544 mTracker = tracker; 545 } 546 547 @Override 548 public void onReceive(Context context, Intent intent) { 549 String action = intent.getAction(); 550 if (action.equals(Intents.SMS_SEND_ACTION)) { 551 int rc = getResultCode(); 552 if (rc == Activity.RESULT_OK) { 553 Rlog.d(TAG, "Sending SMS by IP pending."); 554 Bundle resultExtras = getResultExtras(false); 555 if (resultExtras != null && resultExtras.containsKey("messageref")) { 556 mTracker.mMessageRef = resultExtras.getInt("messageref"); 557 Rlog.d(TAG, "messageref = " + mTracker.mMessageRef); 558 } else { 559 Rlog.e(TAG, "Can't find messageref in result extras."); 560 } 561 sendPendingList.add(mTracker); 562 } else { 563 Rlog.d(TAG, "Sending SMS by IP failed."); 564 sendSmsByPstn(mTracker); 565 } 566 } else { 567 Rlog.e(TAG, "unexpected BroadcastReceiver action: " + action); 568 } 569 } 570 } 571 572 /** 573 * Send a multi-part text based SMS. 574 * @param destAddr the address to send the message to 575 * @param scAddr is the service center address or null to use 576 * the current default SMSC 577 * @param parts an <code>ArrayList</code> of strings that, in order, 578 * comprise the original message 579 * @param sentIntents if not null, an <code>ArrayList</code> of 580 * <code>PendingIntent</code>s (one for each message part) that is 581 * broadcast when the corresponding message part has been sent. 582 * The result code will be <code>Activity.RESULT_OK<code> for success, 583 * or one of these errors: 584 * <code>RESULT_ERROR_GENERIC_FAILURE</code> 585 * <code>RESULT_ERROR_RADIO_OFF</code> 586 * <code>RESULT_ERROR_NULL_PDU</code> 587 * <code>RESULT_ERROR_NO_SERVICE</code>. 588 * The per-application based SMS control checks sentIntent. If sentIntent 589 * is NULL the caller will be checked against all unknown applications, 590 * which cause smaller number of SMS to be sent in checking period. 591 * @param deliveryIntents if not null, an <code>ArrayList</code> of 592 * <code>PendingIntent</code>s (one for each message part) that is 593 * broadcast when the corresponding message part has been delivered 594 * to the recipient. The raw pdu of the status report is in the 595 * @param messageUri optional URI of the message if it is already stored in the system 596 * @param callingPkg the calling package name 597 */ 598 protected void sendMultipartText(String destAddr, String scAddr, 599 ArrayList<String> parts, ArrayList<PendingIntent> sentIntents, 600 ArrayList<PendingIntent> deliveryIntents, Uri messageUri, String callingPkg) { 601 if (messageUri == null) { 602 if (SmsApplication.shouldWriteMessageForPackage(callingPkg, mContext)) { 603 messageUri = writeOutboxMessage( 604 getSubId(), 605 destAddr, 606 getMultipartMessageText(parts), 607 deliveryIntents != null && deliveryIntents.size() > 0, 608 callingPkg); 609 } 610 } else { 611 moveToOutbox(getSubId(), messageUri, callingPkg); 612 } 613 int refNumber = getNextConcatenatedRef() & 0x00FF; 614 int msgCount = parts.size(); 615 int encoding = SmsConstants.ENCODING_UNKNOWN; 616 617 TextEncodingDetails[] encodingForParts = new TextEncodingDetails[msgCount]; 618 for (int i = 0; i < msgCount; i++) { 619 TextEncodingDetails details = calculateLength(parts.get(i), false); 620 if (encoding != details.codeUnitSize 621 && (encoding == SmsConstants.ENCODING_UNKNOWN 622 || encoding == SmsConstants.ENCODING_7BIT)) { 623 encoding = details.codeUnitSize; 624 } 625 encodingForParts[i] = details; 626 } 627 628 // States to track at the message level (for all parts) 629 final AtomicInteger unsentPartCount = new AtomicInteger(msgCount); 630 final AtomicBoolean anyPartFailed = new AtomicBoolean(false); 631 632 for (int i = 0; i < msgCount; i++) { 633 SmsHeader.ConcatRef concatRef = new SmsHeader.ConcatRef(); 634 concatRef.refNumber = refNumber; 635 concatRef.seqNumber = i + 1; // 1-based sequence 636 concatRef.msgCount = msgCount; 637 // TODO: We currently set this to true since our messaging app will never 638 // send more than 255 parts (it converts the message to MMS well before that). 639 // However, we should support 3rd party messaging apps that might need 16-bit 640 // references 641 // Note: It's not sufficient to just flip this bit to true; it will have 642 // ripple effects (several calculations assume 8-bit ref). 643 concatRef.isEightBits = true; 644 SmsHeader smsHeader = new SmsHeader(); 645 smsHeader.concatRef = concatRef; 646 647 // Set the national language tables for 3GPP 7-bit encoding, if enabled. 648 if (encoding == SmsConstants.ENCODING_7BIT) { 649 smsHeader.languageTable = encodingForParts[i].languageTable; 650 smsHeader.languageShiftTable = encodingForParts[i].languageShiftTable; 651 } 652 653 PendingIntent sentIntent = null; 654 if (sentIntents != null && sentIntents.size() > i) { 655 sentIntent = sentIntents.get(i); 656 } 657 658 PendingIntent deliveryIntent = null; 659 if (deliveryIntents != null && deliveryIntents.size() > i) { 660 deliveryIntent = deliveryIntents.get(i); 661 } 662 663 sendNewSubmitPdu(destAddr, scAddr, parts.get(i), smsHeader, encoding, 664 sentIntent, deliveryIntent, (i == (msgCount - 1)), 665 unsentPartCount, anyPartFailed, messageUri); 666 } 667 } 668 669 /** 670 * Create a new SubmitPdu and send it. 671 */ 672 protected abstract void sendNewSubmitPdu(String destinationAddress, String scAddress, 673 String message, SmsHeader smsHeader, int encoding, 674 PendingIntent sentIntent, PendingIntent deliveryIntent, boolean lastPart, 675 AtomicInteger unsentPartCount, AtomicBoolean anyPartFailed, Uri messageUri); 676 677 /** 678 * Send a SMS 679 * @param tracker will contain: 680 * -smsc the SMSC to send the message through, or NULL for the 681 * default SMSC 682 * -pdu the raw PDU to send 683 * -sentIntent if not NULL this <code>Intent</code> is 684 * broadcast when the message is successfully sent, or failed. 685 * The result code will be <code>Activity.RESULT_OK<code> for success, 686 * or one of these errors: 687 * <code>RESULT_ERROR_GENERIC_FAILURE</code> 688 * <code>RESULT_ERROR_RADIO_OFF</code> 689 * <code>RESULT_ERROR_NULL_PDU</code> 690 * <code>RESULT_ERROR_NO_SERVICE</code>. 691 * The per-application based SMS control checks sentIntent. If sentIntent 692 * is NULL the caller will be checked against all unknown applications, 693 * which cause smaller number of SMS to be sent in checking period. 694 * -deliveryIntent if not NULL this <code>Intent</code> is 695 * broadcast when the message is delivered to the recipient. The 696 * raw pdu of the status report is in the extended data ("pdu"). 697 * -param destAddr the destination phone number (for short code confirmation) 698 */ 699 protected void sendRawPdu(SmsTracker tracker) { 700 HashMap map = tracker.mData; 701 byte pdu[] = (byte[]) map.get("pdu"); 702 703 if (mSmsSendDisabled) { 704 Rlog.e(TAG, "Device does not support sending sms."); 705 tracker.onFailed(mContext, RESULT_ERROR_NO_SERVICE, 0/*errorCode*/); 706 return; 707 } 708 709 if (pdu == null) { 710 Rlog.e(TAG, "Empty PDU"); 711 tracker.onFailed(mContext, RESULT_ERROR_NULL_PDU, 0/*errorCode*/); 712 return; 713 } 714 715 // Get calling app package name via UID from Binder call 716 PackageManager pm = mContext.getPackageManager(); 717 String[] packageNames = pm.getPackagesForUid(Binder.getCallingUid()); 718 719 if (packageNames == null || packageNames.length == 0) { 720 // Refuse to send SMS if we can't get the calling package name. 721 Rlog.e(TAG, "Can't get calling app package name: refusing to send SMS"); 722 tracker.onFailed(mContext, RESULT_ERROR_GENERIC_FAILURE, 0/*errorCode*/); 723 return; 724 } 725 726 // Get package info via packagemanager 727 PackageInfo appInfo; 728 try { 729 // XXX this is lossy- apps can share a UID 730 appInfo = pm.getPackageInfo(packageNames[0], PackageManager.GET_SIGNATURES); 731 } catch (PackageManager.NameNotFoundException e) { 732 Rlog.e(TAG, "Can't get calling app package info: refusing to send SMS"); 733 tracker.onFailed(mContext, RESULT_ERROR_GENERIC_FAILURE, 0/*errorCode*/); 734 return; 735 } 736 737 // checkDestination() returns true if the destination is not a premium short code or the 738 // sending app is approved to send to short codes. Otherwise, a message is sent to our 739 // handler with the SmsTracker to request user confirmation before sending. 740 if (checkDestination(tracker)) { 741 // check for excessive outgoing SMS usage by this app 742 if (!mUsageMonitor.check(appInfo.packageName, SINGLE_PART_SMS)) { 743 sendMessage(obtainMessage(EVENT_SEND_LIMIT_REACHED_CONFIRMATION, tracker)); 744 return; 745 } 746 747 sendSms(tracker); 748 } 749 } 750 751 /** 752 * Check if destination is a potential premium short code and sender is not pre-approved to 753 * send to short codes. 754 * 755 * @param tracker the tracker for the SMS to send 756 * @return true if the destination is approved; false if user confirmation event was sent 757 */ 758 boolean checkDestination(SmsTracker tracker) { 759 if (mContext.checkCallingOrSelfPermission(SEND_SMS_NO_CONFIRMATION_PERMISSION) 760 == PackageManager.PERMISSION_GRANTED) { 761 return true; // app is pre-approved to send to short codes 762 } else { 763 int rule = mPremiumSmsRule.get(); 764 int smsCategory = SmsUsageMonitor.CATEGORY_NOT_SHORT_CODE; 765 if (rule == PREMIUM_RULE_USE_SIM || rule == PREMIUM_RULE_USE_BOTH) { 766 String simCountryIso = mTelephonyManager.getSimCountryIso(); 767 if (simCountryIso == null || simCountryIso.length() != 2) { 768 Rlog.e(TAG, "Can't get SIM country Iso: trying network country Iso"); 769 simCountryIso = mTelephonyManager.getNetworkCountryIso(); 770 } 771 772 smsCategory = mUsageMonitor.checkDestination(tracker.mDestAddress, simCountryIso); 773 } 774 if (rule == PREMIUM_RULE_USE_NETWORK || rule == PREMIUM_RULE_USE_BOTH) { 775 String networkCountryIso = mTelephonyManager.getNetworkCountryIso(); 776 if (networkCountryIso == null || networkCountryIso.length() != 2) { 777 Rlog.e(TAG, "Can't get Network country Iso: trying SIM country Iso"); 778 networkCountryIso = mTelephonyManager.getSimCountryIso(); 779 } 780 781 smsCategory = SmsUsageMonitor.mergeShortCodeCategories(smsCategory, 782 mUsageMonitor.checkDestination(tracker.mDestAddress, networkCountryIso)); 783 } 784 785 if (smsCategory == SmsUsageMonitor.CATEGORY_NOT_SHORT_CODE 786 || smsCategory == SmsUsageMonitor.CATEGORY_FREE_SHORT_CODE 787 || smsCategory == SmsUsageMonitor.CATEGORY_STANDARD_SHORT_CODE) { 788 return true; // not a premium short code 789 } 790 791 // Wait for user confirmation unless the user has set permission to always allow/deny 792 int premiumSmsPermission = mUsageMonitor.getPremiumSmsPermission( 793 tracker.mAppInfo.packageName); 794 if (premiumSmsPermission == SmsUsageMonitor.PREMIUM_SMS_PERMISSION_UNKNOWN) { 795 // First time trying to send to premium SMS. 796 premiumSmsPermission = SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ASK_USER; 797 } 798 799 switch (premiumSmsPermission) { 800 case SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW: 801 Rlog.d(TAG, "User approved this app to send to premium SMS"); 802 return true; 803 804 case SmsUsageMonitor.PREMIUM_SMS_PERMISSION_NEVER_ALLOW: 805 Rlog.w(TAG, "User denied this app from sending to premium SMS"); 806 sendMessage(obtainMessage(EVENT_STOP_SENDING, tracker)); 807 return false; // reject this message 808 809 case SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ASK_USER: 810 default: 811 int event; 812 if (smsCategory == SmsUsageMonitor.CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE) { 813 event = EVENT_CONFIRM_SEND_TO_POSSIBLE_PREMIUM_SHORT_CODE; 814 } else { 815 event = EVENT_CONFIRM_SEND_TO_PREMIUM_SHORT_CODE; 816 } 817 sendMessage(obtainMessage(event, tracker)); 818 return false; // wait for user confirmation 819 } 820 } 821 } 822 823 /** 824 * Deny sending an SMS if the outgoing queue limit is reached. Used when the message 825 * must be confirmed by the user due to excessive usage or potential premium SMS detected. 826 * @param tracker the SmsTracker for the message to send 827 * @return true if the message was denied; false to continue with send confirmation 828 */ 829 private boolean denyIfQueueLimitReached(SmsTracker tracker) { 830 if (mPendingTrackerCount >= MO_MSG_QUEUE_LIMIT) { 831 // Deny sending message when the queue limit is reached. 832 Rlog.e(TAG, "Denied because queue limit reached"); 833 tracker.onFailed(mContext, RESULT_ERROR_LIMIT_EXCEEDED, 0/*errorCode*/); 834 return true; 835 } 836 mPendingTrackerCount++; 837 return false; 838 } 839 840 /** 841 * Returns the label for the specified app package name. 842 * @param appPackage the package name of the app requesting to send an SMS 843 * @return the label for the specified app, or the package name if getApplicationInfo() fails 844 */ 845 private CharSequence getAppLabel(String appPackage) { 846 PackageManager pm = mContext.getPackageManager(); 847 try { 848 ApplicationInfo appInfo = pm.getApplicationInfo(appPackage, 0); 849 return appInfo.loadLabel(pm); 850 } catch (PackageManager.NameNotFoundException e) { 851 Rlog.e(TAG, "PackageManager Name Not Found for package " + appPackage); 852 return appPackage; // fall back to package name if we can't get app label 853 } 854 } 855 856 /** 857 * Post an alert when SMS needs confirmation due to excessive usage. 858 * @param tracker an SmsTracker for the current message. 859 */ 860 protected void handleReachSentLimit(SmsTracker tracker) { 861 if (denyIfQueueLimitReached(tracker)) { 862 return; // queue limit reached; error was returned to caller 863 } 864 865 CharSequence appLabel = getAppLabel(tracker.mAppInfo.packageName); 866 Resources r = Resources.getSystem(); 867 Spanned messageText = Html.fromHtml(r.getString(R.string.sms_control_message, appLabel)); 868 869 ConfirmDialogListener listener = new ConfirmDialogListener(tracker, null); 870 871 AlertDialog d = new AlertDialog.Builder(mContext) 872 .setTitle(R.string.sms_control_title) 873 .setIcon(R.drawable.stat_sys_warning) 874 .setMessage(messageText) 875 .setPositiveButton(r.getString(R.string.sms_control_yes), listener) 876 .setNegativeButton(r.getString(R.string.sms_control_no), listener) 877 .setOnCancelListener(listener) 878 .create(); 879 880 d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); 881 d.show(); 882 } 883 884 /** 885 * Post an alert for user confirmation when sending to a potential short code. 886 * @param isPremium true if the destination is known to be a premium short code 887 * @param tracker the SmsTracker for the current message. 888 */ 889 protected void handleConfirmShortCode(boolean isPremium, SmsTracker tracker) { 890 if (denyIfQueueLimitReached(tracker)) { 891 return; // queue limit reached; error was returned to caller 892 } 893 894 int detailsId; 895 if (isPremium) { 896 detailsId = R.string.sms_premium_short_code_details; 897 } else { 898 detailsId = R.string.sms_short_code_details; 899 } 900 901 CharSequence appLabel = getAppLabel(tracker.mAppInfo.packageName); 902 Resources r = Resources.getSystem(); 903 Spanned messageText = Html.fromHtml(r.getString(R.string.sms_short_code_confirm_message, 904 appLabel, tracker.mDestAddress)); 905 906 LayoutInflater inflater = (LayoutInflater) mContext.getSystemService( 907 Context.LAYOUT_INFLATER_SERVICE); 908 View layout = inflater.inflate(R.layout.sms_short_code_confirmation_dialog, null); 909 910 ConfirmDialogListener listener = new ConfirmDialogListener(tracker, 911 (TextView)layout.findViewById(R.id.sms_short_code_remember_undo_instruction)); 912 913 914 TextView messageView = (TextView) layout.findViewById(R.id.sms_short_code_confirm_message); 915 messageView.setText(messageText); 916 917 ViewGroup detailsLayout = (ViewGroup) layout.findViewById( 918 R.id.sms_short_code_detail_layout); 919 TextView detailsView = (TextView) detailsLayout.findViewById( 920 R.id.sms_short_code_detail_message); 921 detailsView.setText(detailsId); 922 923 CheckBox rememberChoice = (CheckBox) layout.findViewById( 924 R.id.sms_short_code_remember_choice_checkbox); 925 rememberChoice.setOnCheckedChangeListener(listener); 926 927 AlertDialog d = new AlertDialog.Builder(mContext) 928 .setView(layout) 929 .setPositiveButton(r.getString(R.string.sms_short_code_confirm_allow), listener) 930 .setNegativeButton(r.getString(R.string.sms_short_code_confirm_deny), listener) 931 .setOnCancelListener(listener) 932 .create(); 933 934 d.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); 935 d.show(); 936 937 listener.setPositiveButton(d.getButton(DialogInterface.BUTTON_POSITIVE)); 938 listener.setNegativeButton(d.getButton(DialogInterface.BUTTON_NEGATIVE)); 939 } 940 941 /** 942 * Returns the premium SMS permission for the specified package. If the package has never 943 * been seen before, the default {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ASK_USER} 944 * will be returned. 945 * @param packageName the name of the package to query permission 946 * @return one of {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_UNKNOWN}, 947 * {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ASK_USER}, 948 * {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_NEVER_ALLOW}, or 949 * {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW} 950 */ 951 public int getPremiumSmsPermission(String packageName) { 952 return mUsageMonitor.getPremiumSmsPermission(packageName); 953 } 954 955 /** 956 * Sets the premium SMS permission for the specified package and save the value asynchronously 957 * to persistent storage. 958 * @param packageName the name of the package to set permission 959 * @param permission one of {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ASK_USER}, 960 * {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_NEVER_ALLOW}, or 961 * {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW} 962 */ 963 public void setPremiumSmsPermission(String packageName, int permission) { 964 mUsageMonitor.setPremiumSmsPermission(packageName, permission); 965 } 966 967 /** 968 * Send the message along to the radio. 969 * 970 * @param tracker holds the SMS message to send 971 */ 972 protected abstract void sendSms(SmsTracker tracker); 973 974 /** 975 * Send the SMS via the PSTN network. 976 * 977 * @param tracker holds the Sms tracker ready to be sent 978 */ 979 protected abstract void sendSmsByPstn(SmsTracker tracker); 980 981 /** 982 * Retry the message along to the radio. 983 * 984 * @param tracker holds the SMS message to send 985 */ 986 public void sendRetrySms(SmsTracker tracker) { 987 // re-routing to ImsSMSDispatcher 988 if (mImsSMSDispatcher != null) { 989 mImsSMSDispatcher.sendRetrySms(tracker); 990 } else { 991 Rlog.e(TAG, mImsSMSDispatcher + " is null. Retry failed"); 992 } 993 } 994 995 /** 996 * Send the multi-part SMS based on multipart Sms tracker 997 * 998 * @param tracker holds the multipart Sms tracker ready to be sent 999 */ 1000 private void sendMultipartSms(SmsTracker tracker) { 1001 ArrayList<String> parts; 1002 ArrayList<PendingIntent> sentIntents; 1003 ArrayList<PendingIntent> deliveryIntents; 1004 1005 HashMap<String, Object> map = tracker.mData; 1006 1007 String destinationAddress = (String) map.get("destination"); 1008 String scAddress = (String) map.get("scaddress"); 1009 1010 parts = (ArrayList<String>) map.get("parts"); 1011 sentIntents = (ArrayList<PendingIntent>) map.get("sentIntents"); 1012 deliveryIntents = (ArrayList<PendingIntent>) map.get("deliveryIntents"); 1013 1014 // check if in service 1015 int ss = mPhone.getServiceState().getState(); 1016 // if sms over IMS is not supported on data and voice is not available... 1017 if (!isIms() && ss != ServiceState.STATE_IN_SERVICE) { 1018 for (int i = 0, count = parts.size(); i < count; i++) { 1019 PendingIntent sentIntent = null; 1020 if (sentIntents != null && sentIntents.size() > i) { 1021 sentIntent = sentIntents.get(i); 1022 } 1023 handleNotInService(ss, sentIntent); 1024 } 1025 return; 1026 } 1027 1028 sendMultipartText(destinationAddress, scAddress, parts, sentIntents, deliveryIntents, 1029 null/*messageUri*/, null/*callingPkg*/); 1030 } 1031 1032 /** 1033 * Keeps track of an SMS that has been sent to the RIL, until it has 1034 * successfully been sent, or we're done trying. 1035 */ 1036 protected static final class SmsTracker { 1037 // fields need to be public for derived SmsDispatchers 1038 public final HashMap<String, Object> mData; 1039 public int mRetryCount; 1040 public int mImsRetry; // nonzero indicates initial message was sent over Ims 1041 public int mMessageRef; 1042 public boolean mExpectMore; 1043 String mFormat; 1044 1045 public final PendingIntent mSentIntent; 1046 public final PendingIntent mDeliveryIntent; 1047 1048 public final PackageInfo mAppInfo; 1049 public final String mDestAddress; 1050 1051 public final SmsHeader mSmsHeader; 1052 1053 private long mTimestamp = System.currentTimeMillis(); 1054 public Uri mMessageUri; // Uri of persisted message if we wrote one 1055 1056 // Reference to states of a multipart message that this part belongs to 1057 private AtomicInteger mUnsentPartCount; 1058 private AtomicBoolean mAnyPartFailed; 1059 1060 private SmsTracker(HashMap<String, Object> data, PendingIntent sentIntent, 1061 PendingIntent deliveryIntent, PackageInfo appInfo, String destAddr, String format, 1062 AtomicInteger unsentPartCount, AtomicBoolean anyPartFailed, Uri messageUri, 1063 SmsHeader smsHeader, boolean isExpectMore) { 1064 mData = data; 1065 mSentIntent = sentIntent; 1066 mDeliveryIntent = deliveryIntent; 1067 mRetryCount = 0; 1068 mAppInfo = appInfo; 1069 mDestAddress = destAddr; 1070 mFormat = format; 1071 mExpectMore = isExpectMore; 1072 mImsRetry = 0; 1073 mMessageRef = 0; 1074 mUnsentPartCount = unsentPartCount; 1075 mAnyPartFailed = anyPartFailed; 1076 mMessageUri = messageUri; 1077 mSmsHeader = smsHeader; 1078 } 1079 1080 /** 1081 * Returns whether this tracker holds a multi-part SMS. 1082 * @return true if the tracker holds a multi-part SMS; false otherwise 1083 */ 1084 boolean isMultipart() { 1085 return mData.containsKey("parts"); 1086 } 1087 1088 /** 1089 * Persist this as a sent message 1090 */ 1091 void writeSentMessage(Context context) { 1092 String text = (String)mData.get("text"); 1093 if (text != null) { 1094 boolean deliveryReport = (mDeliveryIntent != null); 1095 // Using invalid threadId 0 here. When the message is inserted into the db, the 1096 // provider looks up the threadId based on the recipient(s). 1097 mMessageUri = Sms.addMessageToUri(context.getContentResolver(), 1098 Telephony.Sms.Sent.CONTENT_URI, 1099 mDestAddress, 1100 text /*body*/, 1101 null /*subject*/, 1102 mTimestamp /*date*/, 1103 true /*read*/, 1104 deliveryReport /*deliveryReport*/, 1105 0 /*threadId*/); 1106 } 1107 } 1108 1109 /** 1110 * Update the status of this message if we persisted it 1111 */ 1112 public void updateSentMessageStatus(Context context, int status) { 1113 if (mMessageUri != null) { 1114 // If we wrote this message in writeSentMessage, update it now 1115 ContentValues values = new ContentValues(1); 1116 values.put(Sms.STATUS, status); 1117 SqliteWrapper.update(context, context.getContentResolver(), 1118 mMessageUri, values, null, null); 1119 } 1120 } 1121 1122 /** 1123 * Update the error_code column of a message 1124 * 1125 * @param context The Context 1126 * @param errorCode The error code 1127 */ 1128 private void updateMessageErrorCode(Context context, int errorCode) { 1129 if (mMessageUri == null) { 1130 return; 1131 } 1132 final ContentValues values = new ContentValues(1); 1133 values.put(Sms.ERROR_CODE, errorCode); 1134 final long identity = Binder.clearCallingIdentity(); 1135 try { 1136 if (SqliteWrapper.update(context, context.getContentResolver(), mMessageUri, values, 1137 null/*where*/, null/*selectionArgs*/) != 1) { 1138 Rlog.e(TAG, "Failed to update message error code"); 1139 } 1140 } finally { 1141 Binder.restoreCallingIdentity(identity); 1142 } 1143 } 1144 1145 /** 1146 * Set the final state of a message: FAILED or SENT 1147 * 1148 * @param context The Context 1149 * @param messageType The final message type 1150 */ 1151 private void setMessageFinalState(Context context, int messageType) { 1152 if (mMessageUri == null) { 1153 return; 1154 } 1155 final ContentValues values = new ContentValues(1); 1156 values.put(Sms.TYPE, messageType); 1157 final long identity = Binder.clearCallingIdentity(); 1158 try { 1159 if (SqliteWrapper.update(context, context.getContentResolver(), mMessageUri, values, 1160 null/*where*/, null/*selectionArgs*/) != 1) { 1161 Rlog.e(TAG, "Failed to move message to " + messageType); 1162 } 1163 } finally { 1164 Binder.restoreCallingIdentity(identity); 1165 } 1166 } 1167 1168 /** 1169 * Handle a failure of a single part message or a part of a multipart message 1170 * 1171 * @param context The Context 1172 * @param error The error to send back with 1173 * @param errorCode 1174 */ 1175 public void onFailed(Context context, int error, int errorCode) { 1176 if (mAnyPartFailed != null) { 1177 mAnyPartFailed.set(true); 1178 } 1179 // is single part or last part of multipart message 1180 boolean isSinglePartOrLastPart = true; 1181 if (mUnsentPartCount != null) { 1182 isSinglePartOrLastPart = mUnsentPartCount.decrementAndGet() == 0; 1183 } 1184 if (errorCode != 0) { 1185 updateMessageErrorCode(context, errorCode); 1186 } 1187 if (isSinglePartOrLastPart) { 1188 setMessageFinalState(context, Sms.MESSAGE_TYPE_FAILED); 1189 } 1190 if (mSentIntent != null) { 1191 try { 1192 // Extra information to send with the sent intent 1193 Intent fillIn = new Intent(); 1194 if (mMessageUri != null) { 1195 // Pass this to SMS apps so that they know where it is stored 1196 fillIn.putExtra("uri", mMessageUri.toString()); 1197 } 1198 if (errorCode != 0) { 1199 fillIn.putExtra("errorCode", errorCode); 1200 } 1201 if (mUnsentPartCount != null && isSinglePartOrLastPart) { 1202 // Is multipart and last part 1203 fillIn.putExtra(SEND_NEXT_MSG_EXTRA, true); 1204 } 1205 mSentIntent.send(context, error, fillIn); 1206 } catch (CanceledException ex) { 1207 Rlog.e(TAG, "Failed to send result"); 1208 } 1209 } 1210 } 1211 1212 /** 1213 * Handle the sent of a single part message or a part of a multipart message 1214 * 1215 * @param context The Context 1216 */ 1217 public void onSent(Context context) { 1218 // is single part or last part of multipart message 1219 boolean isSinglePartOrLastPart = true; 1220 if (mUnsentPartCount != null) { 1221 isSinglePartOrLastPart = mUnsentPartCount.decrementAndGet() == 0; 1222 } 1223 if (isSinglePartOrLastPart) { 1224 boolean success = true; 1225 if (mAnyPartFailed != null && mAnyPartFailed.get()) { 1226 success = false; 1227 } 1228 if (success) { 1229 setMessageFinalState(context, Sms.MESSAGE_TYPE_SENT); 1230 } else { 1231 setMessageFinalState(context, Sms.MESSAGE_TYPE_FAILED); 1232 } 1233 } 1234 if (mSentIntent != null) { 1235 try { 1236 // Extra information to send with the sent intent 1237 Intent fillIn = new Intent(); 1238 if (mMessageUri != null) { 1239 // Pass this to SMS apps so that they know where it is stored 1240 fillIn.putExtra("uri", mMessageUri.toString()); 1241 } 1242 if (mUnsentPartCount != null && isSinglePartOrLastPart) { 1243 // Is multipart and last part 1244 fillIn.putExtra(SEND_NEXT_MSG_EXTRA, true); 1245 } 1246 mSentIntent.send(context, Activity.RESULT_OK, fillIn); 1247 } catch (CanceledException ex) { 1248 Rlog.e(TAG, "Failed to send result"); 1249 } 1250 } 1251 } 1252 } 1253 1254 protected SmsTracker getSmsTracker(HashMap<String, Object> data, PendingIntent sentIntent, 1255 PendingIntent deliveryIntent, String format, AtomicInteger unsentPartCount, 1256 AtomicBoolean anyPartFailed, Uri messageUri, SmsHeader smsHeader, 1257 boolean isExpectMore) { 1258 // Get calling app package name via UID from Binder call 1259 PackageManager pm = mContext.getPackageManager(); 1260 String[] packageNames = pm.getPackagesForUid(Binder.getCallingUid()); 1261 1262 // Get package info via packagemanager 1263 PackageInfo appInfo = null; 1264 if (packageNames != null && packageNames.length > 0) { 1265 try { 1266 // XXX this is lossy- apps can share a UID 1267 appInfo = pm.getPackageInfo(packageNames[0], PackageManager.GET_SIGNATURES); 1268 } catch (PackageManager.NameNotFoundException e) { 1269 // error will be logged in sendRawPdu 1270 } 1271 } 1272 // Strip non-digits from destination phone number before checking for short codes 1273 // and before displaying the number to the user if confirmation is required. 1274 String destAddr = PhoneNumberUtils.extractNetworkPortion((String) data.get("destAddr")); 1275 return new SmsTracker(data, sentIntent, deliveryIntent, appInfo, destAddr, format, 1276 unsentPartCount, anyPartFailed, messageUri, smsHeader, isExpectMore); 1277 } 1278 1279 protected SmsTracker getSmsTracker(HashMap<String, Object> data, PendingIntent sentIntent, 1280 PendingIntent deliveryIntent, String format, Uri messageUri, boolean isExpectMore) { 1281 return getSmsTracker(data, sentIntent, deliveryIntent, format, null/*unsentPartCount*/, 1282 null/*anyPartFailed*/, messageUri, null/*smsHeader*/, isExpectMore); 1283 } 1284 1285 protected HashMap<String, Object> getSmsTrackerMap(String destAddr, String scAddr, 1286 String text, SmsMessageBase.SubmitPduBase pdu) { 1287 HashMap<String, Object> map = new HashMap<String, Object>(); 1288 map.put("destAddr", destAddr); 1289 map.put("scAddr", scAddr); 1290 map.put("text", text); 1291 map.put("smsc", pdu.encodedScAddress); 1292 map.put("pdu", pdu.encodedMessage); 1293 return map; 1294 } 1295 1296 protected HashMap<String, Object> getSmsTrackerMap(String destAddr, String scAddr, 1297 int destPort, byte[] data, SmsMessageBase.SubmitPduBase pdu) { 1298 HashMap<String, Object> map = new HashMap<String, Object>(); 1299 map.put("destAddr", destAddr); 1300 map.put("scAddr", scAddr); 1301 map.put("destPort", destPort); 1302 map.put("data", data); 1303 map.put("smsc", pdu.encodedScAddress); 1304 map.put("pdu", pdu.encodedMessage); 1305 return map; 1306 } 1307 1308 /** 1309 * Dialog listener for SMS confirmation dialog. 1310 */ 1311 private final class ConfirmDialogListener 1312 implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener, 1313 CompoundButton.OnCheckedChangeListener { 1314 1315 private final SmsTracker mTracker; 1316 private Button mPositiveButton; 1317 private Button mNegativeButton; 1318 private boolean mRememberChoice; // default is unchecked 1319 private final TextView mRememberUndoInstruction; 1320 1321 ConfirmDialogListener(SmsTracker tracker, TextView textView) { 1322 mTracker = tracker; 1323 mRememberUndoInstruction = textView; 1324 } 1325 1326 void setPositiveButton(Button button) { 1327 mPositiveButton = button; 1328 } 1329 1330 void setNegativeButton(Button button) { 1331 mNegativeButton = button; 1332 } 1333 1334 @Override 1335 public void onClick(DialogInterface dialog, int which) { 1336 // Always set the SMS permission so that Settings will show a permission setting 1337 // for the app (it won't be shown until after the app tries to send to a short code). 1338 int newSmsPermission = SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ASK_USER; 1339 1340 if (which == DialogInterface.BUTTON_POSITIVE) { 1341 Rlog.d(TAG, "CONFIRM sending SMS"); 1342 // XXX this is lossy- apps can have more than one signature 1343 EventLog.writeEvent(EventLogTags.EXP_DET_SMS_SENT_BY_USER, 1344 mTracker.mAppInfo.applicationInfo == null ? 1345 -1 : mTracker.mAppInfo.applicationInfo.uid); 1346 sendMessage(obtainMessage(EVENT_SEND_CONFIRMED_SMS, mTracker)); 1347 if (mRememberChoice) { 1348 newSmsPermission = SmsUsageMonitor.PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW; 1349 } 1350 } else if (which == DialogInterface.BUTTON_NEGATIVE) { 1351 Rlog.d(TAG, "DENY sending SMS"); 1352 // XXX this is lossy- apps can have more than one signature 1353 EventLog.writeEvent(EventLogTags.EXP_DET_SMS_DENIED_BY_USER, 1354 mTracker.mAppInfo.applicationInfo == null ? 1355 -1 : mTracker.mAppInfo.applicationInfo.uid); 1356 sendMessage(obtainMessage(EVENT_STOP_SENDING, mTracker)); 1357 if (mRememberChoice) { 1358 newSmsPermission = SmsUsageMonitor.PREMIUM_SMS_PERMISSION_NEVER_ALLOW; 1359 } 1360 } 1361 setPremiumSmsPermission(mTracker.mAppInfo.packageName, newSmsPermission); 1362 } 1363 1364 @Override 1365 public void onCancel(DialogInterface dialog) { 1366 Rlog.d(TAG, "dialog dismissed: don't send SMS"); 1367 sendMessage(obtainMessage(EVENT_STOP_SENDING, mTracker)); 1368 } 1369 1370 @Override 1371 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 1372 Rlog.d(TAG, "remember this choice: " + isChecked); 1373 mRememberChoice = isChecked; 1374 if (isChecked) { 1375 mPositiveButton.setText(R.string.sms_short_code_confirm_always_allow); 1376 mNegativeButton.setText(R.string.sms_short_code_confirm_never_allow); 1377 if (mRememberUndoInstruction != null) { 1378 mRememberUndoInstruction. 1379 setText(R.string.sms_short_code_remember_undo_instruction); 1380 mRememberUndoInstruction.setPadding(0,0,0,32); 1381 } 1382 } else { 1383 mPositiveButton.setText(R.string.sms_short_code_confirm_allow); 1384 mNegativeButton.setText(R.string.sms_short_code_confirm_deny); 1385 if (mRememberUndoInstruction != null) { 1386 mRememberUndoInstruction.setText(""); 1387 mRememberUndoInstruction.setPadding(0,0,0,0); 1388 } 1389 } 1390 } 1391 } 1392 1393 public boolean isIms() { 1394 if (mImsSMSDispatcher != null) { 1395 return mImsSMSDispatcher.isIms(); 1396 } else { 1397 Rlog.e(TAG, mImsSMSDispatcher + " is null"); 1398 return false; 1399 } 1400 } 1401 1402 public String getImsSmsFormat() { 1403 if (mImsSMSDispatcher != null) { 1404 return mImsSMSDispatcher.getImsSmsFormat(); 1405 } else { 1406 Rlog.e(TAG, mImsSMSDispatcher + " is null"); 1407 return null; 1408 } 1409 } 1410 1411 protected Uri writeOutboxMessage(long subId, String address, String text, 1412 boolean requireDeliveryReport, String creator) { 1413 final ContentValues values = new ContentValues(8); 1414 values.put(Telephony.Sms.SUB_ID, subId); 1415 values.put(Telephony.Sms.ADDRESS, address); 1416 values.put(Telephony.Sms.BODY, text); 1417 values.put(Telephony.Sms.DATE, System.currentTimeMillis()); // milliseconds 1418 values.put(Telephony.Sms.SEEN, 1); 1419 values.put(Telephony.Sms.READ, 1); 1420 if (!TextUtils.isEmpty(creator)) { 1421 values.put(Telephony.Sms.CREATOR, creator); 1422 } 1423 if (requireDeliveryReport) { 1424 values.put(Telephony.Sms.STATUS, Telephony.Sms.STATUS_PENDING); 1425 } 1426 final long identity = Binder.clearCallingIdentity(); 1427 try { 1428 final Uri uri = mContext.getContentResolver().insert( 1429 Telephony.Sms.Outbox.CONTENT_URI, values); 1430 return uri; 1431 } catch (Exception e) { 1432 Rlog.e(TAG, "writeOutboxMessage: Failed to persist outbox message", e); 1433 return null; 1434 } finally { 1435 Binder.restoreCallingIdentity(identity); 1436 } 1437 } 1438 1439 protected void moveToOutbox(long subId, Uri messageUri, String creator) { 1440 final ContentValues values = new ContentValues(4); 1441 values.put(Telephony.Sms.SUB_ID, subId); 1442 if (!TextUtils.isEmpty(creator)) { 1443 // Reset creator/sender 1444 values.put(Telephony.Sms.CREATOR, creator); 1445 } 1446 // Reset the timestamp 1447 values.put(Telephony.Sms.DATE, System.currentTimeMillis()); // milliseconds 1448 values.put(Telephony.Sms.TYPE, Telephony.Sms.MESSAGE_TYPE_OUTBOX); 1449 final long identity = Binder.clearCallingIdentity(); 1450 try { 1451 if (mContext.getContentResolver().update(messageUri, values, 1452 null/*where*/, null/*selectionArgs*/) != 1) { 1453 Rlog.e(TAG, "moveToOutbox: failed to update message " + messageUri); 1454 } 1455 } catch (Exception e) { 1456 Rlog.e(TAG, "moveToOutbox: Failed to update message", e); 1457 } finally { 1458 Binder.restoreCallingIdentity(identity); 1459 } 1460 } 1461 1462 private String getMultipartMessageText(ArrayList<String> parts) { 1463 final StringBuilder sb = new StringBuilder(); 1464 for (String part : parts) { 1465 if (part != null) { 1466 sb.append(part); 1467 } 1468 } 1469 return sb.toString(); 1470 } 1471 1472 protected String getCarrierAppPackageName(Intent intent) { 1473 UiccCard card = UiccController.getInstance().getUiccCard(); 1474 if (card == null) { 1475 return null; 1476 } 1477 1478 List<String> carrierPackages = card.getCarrierPackageNamesForIntent( 1479 mContext.getPackageManager(), intent); 1480 return (carrierPackages != null && carrierPackages.size() == 1) ? 1481 carrierPackages.get(0) : null; 1482 1483 } 1484 1485 protected long getSubId() { 1486 return SubscriptionController.getInstance().getSubIdUsingPhoneId(mPhone.mPhoneId); 1487 } 1488 } 1489