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