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 19 import static com.android.internal.telephony.IccSmsInterfaceManager.SMS_MESSAGE_PERIOD_NOT_SPECIFIED; 20 import static com.android.internal.telephony.IccSmsInterfaceManager.SMS_MESSAGE_PRIORITY_NOT_SPECIFIED; 21 22 import android.app.Activity; 23 import android.app.PendingIntent; 24 import android.app.PendingIntent.CanceledException; 25 import android.content.BroadcastReceiver; 26 import android.content.Context; 27 import android.content.Intent; 28 import android.content.IntentFilter; 29 import android.net.Uri; 30 import android.os.AsyncResult; 31 import android.os.Handler; 32 import android.os.Message; 33 import android.os.UserManager; 34 import android.provider.Telephony.Sms; 35 import android.provider.Telephony.Sms.Intents; 36 import android.telephony.Rlog; 37 import android.telephony.ServiceState; 38 import android.telephony.SmsManager; 39 import android.telephony.SmsMessage; 40 import android.util.Pair; 41 42 import com.android.internal.annotations.VisibleForTesting; 43 import com.android.internal.telephony.cdma.CdmaInboundSmsHandler; 44 import com.android.internal.telephony.cdma.CdmaSMSDispatcher; 45 import com.android.internal.telephony.gsm.GsmInboundSmsHandler; 46 import com.android.internal.telephony.gsm.GsmSMSDispatcher; 47 48 import java.io.FileDescriptor; 49 import java.io.PrintWriter; 50 import java.util.ArrayList; 51 import java.util.HashMap; 52 53 /** 54 * 55 */ 56 public class SmsDispatchersController extends Handler { 57 private static final String TAG = "SmsDispatchersController"; 58 private static final boolean VDBG = false; // STOPSHIP if true 59 60 /** Radio is ON */ 61 private static final int EVENT_RADIO_ON = 11; 62 63 /** IMS registration/SMS format changed */ 64 private static final int EVENT_IMS_STATE_CHANGED = 12; 65 66 /** Callback from RIL_REQUEST_IMS_REGISTRATION_STATE */ 67 private static final int EVENT_IMS_STATE_DONE = 13; 68 69 /** Service state changed */ 70 private static final int EVENT_SERVICE_STATE_CHANGED = 14; 71 72 /** Purge old message segments */ 73 private static final int EVENT_PARTIAL_SEGMENT_TIMER_EXPIRY = 15; 74 75 /** User unlocked the device */ 76 private static final int EVENT_USER_UNLOCKED = 16; 77 78 /** InboundSmsHandler exited WaitingState */ 79 protected static final int EVENT_SMS_HANDLER_EXITING_WAITING_STATE = 17; 80 81 /** Delete any partial message segments after being IN_SERVICE for 1 day. */ 82 private static final long PARTIAL_SEGMENT_WAIT_DURATION = (long) (60 * 60 * 1000) * 24; 83 /** Constant for invalid time */ 84 private static final long INVALID_TIME = -1; 85 /** Time at which last IN_SERVICE event was received */ 86 private long mLastInServiceTime = INVALID_TIME; 87 /** Current IN_SERVICE duration */ 88 private long mCurrentWaitElapsedDuration = 0; 89 /** Time at which the current PARTIAL_SEGMENT_WAIT_DURATION timer was started */ 90 private long mCurrentWaitStartTime = INVALID_TIME; 91 92 private SMSDispatcher mCdmaDispatcher; 93 private SMSDispatcher mGsmDispatcher; 94 private ImsSmsDispatcher mImsSmsDispatcher; 95 96 private GsmInboundSmsHandler mGsmInboundSmsHandler; 97 private CdmaInboundSmsHandler mCdmaInboundSmsHandler; 98 99 private Phone mPhone; 100 /** Outgoing message counter. Shared by all dispatchers. */ 101 private final SmsUsageMonitor mUsageMonitor; 102 private final CommandsInterface mCi; 103 private final Context mContext; 104 105 /** true if IMS is registered and sms is supported, false otherwise.*/ 106 private boolean mIms = false; 107 private String mImsSmsFormat = SmsConstants.FORMAT_UNKNOWN; 108 SmsDispatchersController(Phone phone, SmsStorageMonitor storageMonitor, SmsUsageMonitor usageMonitor)109 public SmsDispatchersController(Phone phone, SmsStorageMonitor storageMonitor, 110 SmsUsageMonitor usageMonitor) { 111 Rlog.d(TAG, "SmsDispatchersController created"); 112 113 mContext = phone.getContext(); 114 mUsageMonitor = usageMonitor; 115 mCi = phone.mCi; 116 mPhone = phone; 117 118 // Create dispatchers, inbound SMS handlers and 119 // broadcast undelivered messages in raw table. 120 mImsSmsDispatcher = new ImsSmsDispatcher(phone, this); 121 mCdmaDispatcher = new CdmaSMSDispatcher(phone, this); 122 mGsmInboundSmsHandler = GsmInboundSmsHandler.makeInboundSmsHandler(phone.getContext(), 123 storageMonitor, phone); 124 mCdmaInboundSmsHandler = CdmaInboundSmsHandler.makeInboundSmsHandler(phone.getContext(), 125 storageMonitor, phone, (CdmaSMSDispatcher) mCdmaDispatcher); 126 mGsmDispatcher = new GsmSMSDispatcher(phone, this, mGsmInboundSmsHandler); 127 SmsBroadcastUndelivered.initialize(phone.getContext(), 128 mGsmInboundSmsHandler, mCdmaInboundSmsHandler); 129 InboundSmsHandler.registerNewMessageNotificationActionHandler(phone.getContext()); 130 131 mCi.registerForOn(this, EVENT_RADIO_ON, null); 132 mCi.registerForImsNetworkStateChanged(this, EVENT_IMS_STATE_CHANGED, null); 133 134 UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); 135 if (userManager.isUserUnlocked()) { 136 if (VDBG) { 137 logd("SmsDispatchersController: user unlocked; registering for service" 138 + "state changed"); 139 } 140 mPhone.registerForServiceStateChanged(this, EVENT_SERVICE_STATE_CHANGED, null); 141 resetPartialSegmentWaitTimer(); 142 } else { 143 if (VDBG) { 144 logd("SmsDispatchersController: user locked; waiting for USER_UNLOCKED"); 145 } 146 IntentFilter userFilter = new IntentFilter(); 147 userFilter.addAction(Intent.ACTION_USER_UNLOCKED); 148 mContext.registerReceiver(mBroadcastReceiver, userFilter); 149 } 150 } 151 152 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 153 @Override 154 public void onReceive(final Context context, Intent intent) { 155 Rlog.d(TAG, "Received broadcast " + intent.getAction()); 156 if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) { 157 sendMessage(obtainMessage(EVENT_USER_UNLOCKED)); 158 } 159 } 160 }; 161 dispose()162 public void dispose() { 163 mCi.unregisterForOn(this); 164 mCi.unregisterForImsNetworkStateChanged(this); 165 mPhone.unregisterForServiceStateChanged(this); 166 mGsmDispatcher.dispose(); 167 mCdmaDispatcher.dispose(); 168 mGsmInboundSmsHandler.dispose(); 169 mCdmaInboundSmsHandler.dispose(); 170 } 171 172 /** 173 * Handles events coming from the phone stack. Overridden from handler. 174 * 175 * @param msg the message to handle 176 */ 177 @Override handleMessage(Message msg)178 public void handleMessage(Message msg) { 179 AsyncResult ar; 180 181 switch (msg.what) { 182 case EVENT_RADIO_ON: 183 case EVENT_IMS_STATE_CHANGED: // received unsol 184 mCi.getImsRegistrationState(this.obtainMessage(EVENT_IMS_STATE_DONE)); 185 break; 186 187 case EVENT_IMS_STATE_DONE: 188 ar = (AsyncResult) msg.obj; 189 190 if (ar.exception == null) { 191 updateImsInfo(ar); 192 } else { 193 Rlog.e(TAG, "IMS State query failed with exp " 194 + ar.exception); 195 } 196 break; 197 198 case EVENT_SERVICE_STATE_CHANGED: 199 case EVENT_SMS_HANDLER_EXITING_WAITING_STATE: 200 reevaluateTimerStatus(); 201 break; 202 203 case EVENT_PARTIAL_SEGMENT_TIMER_EXPIRY: 204 handlePartialSegmentTimerExpiry((Long) msg.obj); 205 break; 206 207 case EVENT_USER_UNLOCKED: 208 if (VDBG) { 209 logd("handleMessage: EVENT_USER_UNLOCKED"); 210 } 211 mPhone.registerForServiceStateChanged(this, EVENT_SERVICE_STATE_CHANGED, null); 212 resetPartialSegmentWaitTimer(); 213 break; 214 215 default: 216 if (isCdmaMo()) { 217 mCdmaDispatcher.handleMessage(msg); 218 } else { 219 mGsmDispatcher.handleMessage(msg); 220 } 221 } 222 } 223 reevaluateTimerStatus()224 private void reevaluateTimerStatus() { 225 long currentTime = System.currentTimeMillis(); 226 227 // Remove unhandled timer expiry message. A new message will be posted if needed. 228 removeMessages(EVENT_PARTIAL_SEGMENT_TIMER_EXPIRY); 229 // Update timer duration elapsed time (add time since last IN_SERVICE to now). 230 // This is needed for IN_SERVICE as well as OUT_OF_SERVICE because same events can be 231 // received back to back 232 if (mLastInServiceTime != INVALID_TIME) { 233 mCurrentWaitElapsedDuration += (currentTime - mLastInServiceTime); 234 } 235 236 if (VDBG) { 237 logd("reevaluateTimerStatus: currentTime: " + currentTime 238 + " mCurrentWaitElapsedDuration: " + mCurrentWaitElapsedDuration); 239 } 240 241 if (mCurrentWaitElapsedDuration > PARTIAL_SEGMENT_WAIT_DURATION) { 242 // handle this event as timer expiry 243 handlePartialSegmentTimerExpiry(mCurrentWaitStartTime); 244 } else { 245 if (isInService()) { 246 handleInService(currentTime); 247 } else { 248 handleOutOfService(currentTime); 249 } 250 } 251 } 252 handleInService(long currentTime)253 private void handleInService(long currentTime) { 254 if (VDBG) { 255 logd("handleInService: timer expiry in " 256 + (PARTIAL_SEGMENT_WAIT_DURATION - mCurrentWaitElapsedDuration) + "ms"); 257 } 258 259 // initialize mCurrentWaitStartTime if needed 260 if (mCurrentWaitStartTime == INVALID_TIME) mCurrentWaitStartTime = currentTime; 261 262 // Post a message for timer expiry time. mCurrentWaitElapsedDuration is the duration already 263 // elapsed from the timer. 264 sendMessageDelayed( 265 obtainMessage(EVENT_PARTIAL_SEGMENT_TIMER_EXPIRY, mCurrentWaitStartTime), 266 PARTIAL_SEGMENT_WAIT_DURATION - mCurrentWaitElapsedDuration); 267 268 // update mLastInServiceTime as the current time 269 mLastInServiceTime = currentTime; 270 } 271 handleOutOfService(long currentTime)272 private void handleOutOfService(long currentTime) { 273 if (VDBG) { 274 logd("handleOutOfService: currentTime: " + currentTime 275 + " mCurrentWaitElapsedDuration: " + mCurrentWaitElapsedDuration); 276 } 277 278 // mLastInServiceTime is not relevant now since state is OUT_OF_SERVICE; set it to INVALID 279 mLastInServiceTime = INVALID_TIME; 280 } 281 handlePartialSegmentTimerExpiry(long waitTimerStart)282 private void handlePartialSegmentTimerExpiry(long waitTimerStart) { 283 if (mGsmInboundSmsHandler.getCurrentState().getName().equals("WaitingState") 284 || mCdmaInboundSmsHandler.getCurrentState().getName().equals("WaitingState")) { 285 logd("handlePartialSegmentTimerExpiry: ignoring timer expiry as InboundSmsHandler is" 286 + " in WaitingState"); 287 return; 288 } 289 290 if (VDBG) { 291 logd("handlePartialSegmentTimerExpiry: calling scanRawTable()"); 292 } 293 // Timer expired. This indicates that device has been in service for 294 // PARTIAL_SEGMENT_WAIT_DURATION since waitTimerStart. Delete orphaned message segments 295 // older than waitTimerStart. 296 SmsBroadcastUndelivered.scanRawTable(mContext, mCdmaInboundSmsHandler, 297 mGsmInboundSmsHandler, waitTimerStart); 298 if (VDBG) { 299 logd("handlePartialSegmentTimerExpiry: scanRawTable() done"); 300 } 301 302 resetPartialSegmentWaitTimer(); 303 } 304 resetPartialSegmentWaitTimer()305 private void resetPartialSegmentWaitTimer() { 306 long currentTime = System.currentTimeMillis(); 307 308 removeMessages(EVENT_PARTIAL_SEGMENT_TIMER_EXPIRY); 309 if (isInService()) { 310 if (VDBG) { 311 logd("resetPartialSegmentWaitTimer: currentTime: " + currentTime 312 + " IN_SERVICE"); 313 } 314 mCurrentWaitStartTime = currentTime; 315 mLastInServiceTime = currentTime; 316 sendMessageDelayed( 317 obtainMessage(EVENT_PARTIAL_SEGMENT_TIMER_EXPIRY, mCurrentWaitStartTime), 318 PARTIAL_SEGMENT_WAIT_DURATION); 319 } else { 320 if (VDBG) { 321 logd("resetPartialSegmentWaitTimer: currentTime: " + currentTime 322 + " not IN_SERVICE"); 323 } 324 mCurrentWaitStartTime = INVALID_TIME; 325 mLastInServiceTime = INVALID_TIME; 326 } 327 328 mCurrentWaitElapsedDuration = 0; 329 } 330 isInService()331 private boolean isInService() { 332 ServiceState serviceState = mPhone.getServiceState(); 333 return serviceState != null && serviceState.getState() == ServiceState.STATE_IN_SERVICE; 334 } 335 setImsSmsFormat(int format)336 private void setImsSmsFormat(int format) { 337 switch (format) { 338 case PhoneConstants.PHONE_TYPE_GSM: 339 mImsSmsFormat = SmsConstants.FORMAT_3GPP; 340 break; 341 case PhoneConstants.PHONE_TYPE_CDMA: 342 mImsSmsFormat = SmsConstants.FORMAT_3GPP2; 343 break; 344 default: 345 mImsSmsFormat = SmsConstants.FORMAT_UNKNOWN; 346 break; 347 } 348 } 349 updateImsInfo(AsyncResult ar)350 private void updateImsInfo(AsyncResult ar) { 351 int[] responseArray = (int[]) ar.result; 352 setImsSmsFormat(responseArray[1]); 353 mIms = responseArray[0] == 1 && !SmsConstants.FORMAT_UNKNOWN.equals(mImsSmsFormat); 354 Rlog.d(TAG, "IMS registration state: " + mIms + " format: " + mImsSmsFormat); 355 } 356 357 /** 358 * Inject an SMS PDU into the android platform only if it is class 1. 359 * 360 * @param pdu is the byte array of pdu to be injected into android telephony layer 361 * @param format is the format of SMS pdu (3gpp or 3gpp2) 362 * @param callback if not NULL this callback is triggered when the message is successfully 363 * received by the android telephony layer. This callback is triggered at 364 * the same time an SMS received from radio is responded back. 365 */ 366 @VisibleForTesting injectSmsPdu(byte[] pdu, String format, SmsInjectionCallback callback)367 public void injectSmsPdu(byte[] pdu, String format, SmsInjectionCallback callback) { 368 // TODO We need to decide whether we should allow injecting GSM(3gpp) 369 // SMS pdus when the phone is camping on CDMA(3gpp2) network and vice versa. 370 android.telephony.SmsMessage msg = 371 android.telephony.SmsMessage.createFromPdu(pdu, format); 372 injectSmsPdu(msg, format, callback, false /* ignoreClass */); 373 } 374 375 /** 376 * Inject an SMS PDU into the android platform. 377 * 378 * @param msg is the {@link SmsMessage} to be injected into android telephony layer 379 * @param format is the format of SMS pdu (3gpp or 3gpp2) 380 * @param callback if not NULL this callback is triggered when the message is successfully 381 * received by the android telephony layer. This callback is triggered at 382 * the same time an SMS received from radio is responded back. 383 * @param ignoreClass if set to false, this method will inject class 1 sms only. 384 */ 385 @VisibleForTesting injectSmsPdu(SmsMessage msg, String format, SmsInjectionCallback callback, boolean ignoreClass)386 public void injectSmsPdu(SmsMessage msg, String format, SmsInjectionCallback callback, 387 boolean ignoreClass) { 388 Rlog.d(TAG, "SmsDispatchersController:injectSmsPdu"); 389 try { 390 if (msg == null) { 391 Rlog.e(TAG, "injectSmsPdu: createFromPdu returned null"); 392 callback.onSmsInjectedResult(Intents.RESULT_SMS_GENERIC_ERROR); 393 return; 394 } 395 396 if (!ignoreClass 397 && msg.getMessageClass() != android.telephony.SmsMessage.MessageClass.CLASS_1) { 398 Rlog.e(TAG, "injectSmsPdu: not class 1"); 399 callback.onSmsInjectedResult(Intents.RESULT_SMS_GENERIC_ERROR); 400 return; 401 } 402 403 AsyncResult ar = new AsyncResult(callback, msg, null); 404 405 if (format.equals(SmsConstants.FORMAT_3GPP)) { 406 Rlog.i(TAG, "SmsDispatchersController:injectSmsText Sending msg=" + msg 407 + ", format=" + format + "to mGsmInboundSmsHandler"); 408 mGsmInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_INJECT_SMS, ar); 409 } else if (format.equals(SmsConstants.FORMAT_3GPP2)) { 410 Rlog.i(TAG, "SmsDispatchersController:injectSmsText Sending msg=" + msg 411 + ", format=" + format + "to mCdmaInboundSmsHandler"); 412 mCdmaInboundSmsHandler.sendMessage(InboundSmsHandler.EVENT_INJECT_SMS, ar); 413 } else { 414 // Invalid pdu format. 415 Rlog.e(TAG, "Invalid pdu format: " + format); 416 callback.onSmsInjectedResult(Intents.RESULT_SMS_GENERIC_ERROR); 417 } 418 } catch (Exception e) { 419 Rlog.e(TAG, "injectSmsPdu failed: ", e); 420 callback.onSmsInjectedResult(Intents.RESULT_SMS_GENERIC_ERROR); 421 } 422 } 423 424 /** 425 * Retry the message along to the radio. 426 * 427 * @param tracker holds the SMS message to send 428 */ sendRetrySms(SMSDispatcher.SmsTracker tracker)429 public void sendRetrySms(SMSDispatcher.SmsTracker tracker) { 430 String oldFormat = tracker.mFormat; 431 432 // newFormat will be based on voice technology 433 String newFormat = 434 (PhoneConstants.PHONE_TYPE_CDMA == mPhone.getPhoneType()) 435 ? mCdmaDispatcher.getFormat() : mGsmDispatcher.getFormat(); 436 437 // was previously sent sms format match with voice tech? 438 if (oldFormat.equals(newFormat)) { 439 if (isCdmaFormat(newFormat)) { 440 Rlog.d(TAG, "old format matched new format (cdma)"); 441 mCdmaDispatcher.sendSms(tracker); 442 return; 443 } else { 444 Rlog.d(TAG, "old format matched new format (gsm)"); 445 mGsmDispatcher.sendSms(tracker); 446 return; 447 } 448 } 449 450 // format didn't match, need to re-encode. 451 HashMap map = tracker.getData(); 452 453 // to re-encode, fields needed are: scAddr, destAddr, and 454 // text if originally sent as sendText or 455 // data and destPort if originally sent as sendData. 456 if (!(map.containsKey("scAddr") && map.containsKey("destAddr") 457 && (map.containsKey("text") 458 || (map.containsKey("data") && map.containsKey("destPort"))))) { 459 // should never come here... 460 Rlog.e(TAG, "sendRetrySms failed to re-encode per missing fields!"); 461 tracker.onFailed(mContext, SmsManager.RESULT_ERROR_GENERIC_FAILURE, 0/*errorCode*/); 462 return; 463 } 464 String scAddr = (String) map.get("scAddr"); 465 String destAddr = (String) map.get("destAddr"); 466 467 SmsMessageBase.SubmitPduBase pdu = null; 468 // figure out from tracker if this was sendText/Data 469 if (map.containsKey("text")) { 470 Rlog.d(TAG, "sms failed was text"); 471 String text = (String) map.get("text"); 472 473 if (isCdmaFormat(newFormat)) { 474 Rlog.d(TAG, "old format (gsm) ==> new format (cdma)"); 475 pdu = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu( 476 scAddr, destAddr, text, (tracker.mDeliveryIntent != null), null); 477 } else { 478 Rlog.d(TAG, "old format (cdma) ==> new format (gsm)"); 479 pdu = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu( 480 scAddr, destAddr, text, (tracker.mDeliveryIntent != null), null); 481 } 482 } else if (map.containsKey("data")) { 483 Rlog.d(TAG, "sms failed was data"); 484 byte[] data = (byte[]) map.get("data"); 485 Integer destPort = (Integer) map.get("destPort"); 486 487 if (isCdmaFormat(newFormat)) { 488 Rlog.d(TAG, "old format (gsm) ==> new format (cdma)"); 489 pdu = com.android.internal.telephony.cdma.SmsMessage.getSubmitPdu( 490 scAddr, destAddr, destPort.intValue(), data, 491 (tracker.mDeliveryIntent != null)); 492 } else { 493 Rlog.d(TAG, "old format (cdma) ==> new format (gsm)"); 494 pdu = com.android.internal.telephony.gsm.SmsMessage.getSubmitPdu( 495 scAddr, destAddr, destPort.intValue(), data, 496 (tracker.mDeliveryIntent != null)); 497 } 498 } 499 500 // replace old smsc and pdu with newly encoded ones 501 map.put("smsc", pdu.encodedScAddress); 502 map.put("pdu", pdu.encodedMessage); 503 504 SMSDispatcher dispatcher = (isCdmaFormat(newFormat)) ? mCdmaDispatcher : mGsmDispatcher; 505 506 tracker.mFormat = dispatcher.getFormat(); 507 dispatcher.sendSms(tracker); 508 } 509 isIms()510 public boolean isIms() { 511 return mIms; 512 } 513 getImsSmsFormat()514 public String getImsSmsFormat() { 515 return mImsSmsFormat; 516 } 517 518 /** 519 * Determines whether or not to use CDMA format for MO SMS. 520 * If SMS over IMS is supported, then format is based on IMS SMS format, 521 * otherwise format is based on current phone type. 522 * 523 * @return true if Cdma format should be used for MO SMS, false otherwise. 524 */ isCdmaMo()525 protected boolean isCdmaMo() { 526 if (!isIms()) { 527 // IMS is not registered, use Voice technology to determine SMS format. 528 return (PhoneConstants.PHONE_TYPE_CDMA == mPhone.getPhoneType()); 529 } 530 // IMS is registered with SMS support 531 return isCdmaFormat(mImsSmsFormat); 532 } 533 534 /** 535 * Determines whether or not format given is CDMA format. 536 * 537 * @param format 538 * @return true if format given is CDMA format, false otherwise. 539 */ isCdmaFormat(String format)540 public boolean isCdmaFormat(String format) { 541 return (mCdmaDispatcher.getFormat().equals(format)); 542 } 543 544 /** 545 * Send a data based SMS to a specific application port. 546 * 547 * @param callingPackage the package name of the calling app 548 * @param destAddr the address to send the message to 549 * @param scAddr is the service center address or null to use 550 * the current default SMSC 551 * @param destPort the port to deliver the message to 552 * @param data the body of the message to send 553 * @param sentIntent if not NULL this <code>PendingIntent</code> is 554 * broadcast when the message is successfully sent, or failed. 555 * The result code will be <code>Activity.RESULT_OK<code> for success, 556 * or one of these errors:<br> 557 * <code>RESULT_ERROR_GENERIC_FAILURE</code><br> 558 * <code>RESULT_ERROR_RADIO_OFF</code><br> 559 * <code>RESULT_ERROR_NULL_PDU</code><br> 560 * <code>RESULT_ERROR_NO_SERVICE</code><br>. 561 * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include 562 * the extra "errorCode" containing a radio technology specific value, 563 * generally only useful for troubleshooting.<br> 564 * The per-application based SMS control checks sentIntent. If sentIntent 565 * is NULL the caller will be checked against all unknown applications, 566 * which cause smaller number of SMS to be sent in checking period. 567 * @param deliveryIntent if not NULL this <code>PendingIntent</code> is 568 * broadcast when the message is delivered to the recipient. The 569 * raw pdu of the status report is in the extended data ("pdu"). 570 */ sendData(String callingPackage, String destAddr, String scAddr, int destPort, byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent, boolean isForVvm)571 protected void sendData(String callingPackage, String destAddr, String scAddr, int destPort, 572 byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent, boolean isForVvm) { 573 if (mImsSmsDispatcher.isAvailable()) { 574 mImsSmsDispatcher.sendData(callingPackage, destAddr, scAddr, destPort, data, sentIntent, 575 deliveryIntent, isForVvm); 576 } else if (isCdmaMo()) { 577 mCdmaDispatcher.sendData(callingPackage, destAddr, scAddr, destPort, data, sentIntent, 578 deliveryIntent, isForVvm); 579 } else { 580 mGsmDispatcher.sendData(callingPackage, destAddr, scAddr, destPort, data, sentIntent, 581 deliveryIntent, isForVvm); 582 } 583 } 584 585 /** 586 * Send a text based SMS. 587 * @param destAddr the address to send the message to 588 * @param scAddr is the service center address or null to use 589 * the current default SMSC 590 * @param text the body of the message to send 591 * @param sentIntent if not NULL this <code>PendingIntent</code> is 592 * broadcast when the message is successfully sent, or failed. 593 * The result code will be <code>Activity.RESULT_OK<code> for success, 594 * or one of these errors:<br> 595 * <code>RESULT_ERROR_GENERIC_FAILURE</code><br> 596 * <code>RESULT_ERROR_RADIO_OFF</code><br> 597 * <code>RESULT_ERROR_NULL_PDU</code><br> 598 * <code>RESULT_ERROR_NO_SERVICE</code><br>. 599 * For <code>RESULT_ERROR_GENERIC_FAILURE</code> the sentIntent may include 600 * the extra "errorCode" containing a radio technology specific value, 601 * generally only useful for troubleshooting.<br> 602 * The per-application based SMS control checks sentIntent. If sentIntent 603 * is NULL the caller will be checked against all unknown applications, 604 * which cause smaller number of SMS to be sent in checking period. 605 * @param deliveryIntent if not NULL this <code>PendingIntent</code> is 606 * broadcast when the message is delivered to the recipient. The 607 * @param messageUri optional URI of the message if it is already stored in the system 608 * @param callingPkg the calling package name 609 * @param persistMessage whether to save the sent message into SMS DB for a 610 * non-default SMS app. 611 * @param priority Priority level of the message 612 * Refer specification See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1 613 * --------------------------------- 614 * PRIORITY | Level of Priority 615 * --------------------------------- 616 * '00' | Normal 617 * '01' | Interactive 618 * '10' | Urgent 619 * '11' | Emergency 620 * ---------------------------------- 621 * Any Other values included Negative considered as Invalid Priority Indicator of the message. 622 * @param expectMore is a boolean to indicate the sending messages through same link or not. 623 * @param validityPeriod Validity Period of the message in mins. 624 * Refer specification 3GPP TS 23.040 V6.8.1 section 9.2.3.12.1. 625 * Validity Period(Minimum) -> 5 mins 626 * Validity Period(Maximum) -> 635040 mins(i.e.63 weeks). 627 * Any Other values included Negative considered as Invalid Validity Period of the message. 628 */ sendText(String destAddr, String scAddr, String text, PendingIntent sentIntent, PendingIntent deliveryIntent, Uri messageUri, String callingPkg, boolean persistMessage, int priority, boolean expectMore, int validityPeriod, boolean isForVvm)629 public void sendText(String destAddr, String scAddr, String text, PendingIntent sentIntent, 630 PendingIntent deliveryIntent, Uri messageUri, String callingPkg, boolean persistMessage, 631 int priority, boolean expectMore, int validityPeriod, boolean isForVvm) { 632 if (mImsSmsDispatcher.isAvailable() || mImsSmsDispatcher.isEmergencySmsSupport(destAddr)) { 633 mImsSmsDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent, 634 messageUri, callingPkg, persistMessage, SMS_MESSAGE_PRIORITY_NOT_SPECIFIED, 635 false /*expectMore*/, SMS_MESSAGE_PERIOD_NOT_SPECIFIED, isForVvm); 636 } else { 637 if (isCdmaMo()) { 638 mCdmaDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent, 639 messageUri, callingPkg, persistMessage, priority, expectMore, 640 validityPeriod, isForVvm); 641 } else { 642 mGsmDispatcher.sendText(destAddr, scAddr, text, sentIntent, deliveryIntent, 643 messageUri, callingPkg, persistMessage, priority, expectMore, 644 validityPeriod, isForVvm); 645 } 646 } 647 } 648 649 /** 650 * Send a multi-part text based SMS. 651 * @param destAddr the address to send the message to 652 * @param scAddr is the service center address or null to use 653 * the current default SMSC 654 * @param parts an <code>ArrayList</code> of strings that, in order, 655 * comprise the original message 656 * @param sentIntents if not null, an <code>ArrayList</code> of 657 * <code>PendingIntent</code>s (one for each message part) that is 658 * broadcast when the corresponding message part has been sent. 659 * The result code will be <code>Activity.RESULT_OK<code> for success, 660 * or one of these errors: 661 * <code>RESULT_ERROR_GENERIC_FAILURE</code> 662 * <code>RESULT_ERROR_RADIO_OFF</code> 663 * <code>RESULT_ERROR_NULL_PDU</code> 664 * <code>RESULT_ERROR_NO_SERVICE</code>. 665 * The per-application based SMS control checks sentIntent. If sentIntent 666 * is NULL the caller will be checked against all unknown applications, 667 * which cause smaller number of SMS to be sent in checking period. 668 * @param deliveryIntents if not null, an <code>ArrayList</code> of 669 * <code>PendingIntent</code>s (one for each message part) that is 670 * broadcast when the corresponding message part has been delivered 671 * to the recipient. The raw pdu of the status report is in the 672 * @param messageUri optional URI of the message if it is already stored in the system 673 * @param callingPkg the calling package name 674 * @param persistMessage whether to save the sent message into SMS DB for a 675 * non-default SMS app. 676 * @param priority Priority level of the message 677 * Refer specification See 3GPP2 C.S0015-B, v2.0, table 4.5.9-1 678 * --------------------------------- 679 * PRIORITY | Level of Priority 680 * --------------------------------- 681 * '00' | Normal 682 * '01' | Interactive 683 * '10' | Urgent 684 * '11' | Emergency 685 * ---------------------------------- 686 * Any Other values included Negative considered as Invalid Priority Indicator of the message. 687 * @param expectMore is a boolean to indicate the sending messages through same link or not. 688 * @param validityPeriod Validity Period of the message in mins. 689 * Refer specification 3GPP TS 23.040 V6.8.1 section 9.2.3.12.1. 690 * Validity Period(Minimum) -> 5 mins 691 * Validity Period(Maximum) -> 635040 mins(i.e.63 weeks). 692 * Any Other values included Negative considered as Invalid Validity Period of the message. 693 694 */ sendMultipartText(String destAddr, String scAddr, ArrayList<String> parts, ArrayList<PendingIntent> sentIntents, ArrayList<PendingIntent> deliveryIntents, Uri messageUri, String callingPkg, boolean persistMessage, int priority, boolean expectMore, int validityPeriod)695 protected void sendMultipartText(String destAddr, String scAddr, 696 ArrayList<String> parts, ArrayList<PendingIntent> sentIntents, 697 ArrayList<PendingIntent> deliveryIntents, Uri messageUri, String callingPkg, 698 boolean persistMessage, int priority, boolean expectMore, int validityPeriod) { 699 if (mImsSmsDispatcher.isAvailable()) { 700 mImsSmsDispatcher.sendMultipartText(destAddr, scAddr, parts, sentIntents, 701 deliveryIntents, messageUri, callingPkg, persistMessage, 702 SMS_MESSAGE_PRIORITY_NOT_SPECIFIED, 703 false /*expectMore*/, SMS_MESSAGE_PERIOD_NOT_SPECIFIED); 704 } else { 705 if (isCdmaMo()) { 706 mCdmaDispatcher.sendMultipartText(destAddr, scAddr, parts, sentIntents, 707 deliveryIntents, messageUri, callingPkg, persistMessage, priority, 708 expectMore, validityPeriod); 709 } else { 710 mGsmDispatcher.sendMultipartText(destAddr, scAddr, parts, sentIntents, 711 deliveryIntents, messageUri, callingPkg, persistMessage, priority, 712 expectMore, validityPeriod); 713 } 714 } 715 } 716 717 /** 718 * Returns the premium SMS permission for the specified package. If the package has never 719 * been seen before, the default {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ASK_USER} 720 * will be returned. 721 * @param packageName the name of the package to query permission 722 * @return one of {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_UNKNOWN}, 723 * {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ASK_USER}, 724 * {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_NEVER_ALLOW}, or 725 * {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW} 726 */ getPremiumSmsPermission(String packageName)727 public int getPremiumSmsPermission(String packageName) { 728 return mUsageMonitor.getPremiumSmsPermission(packageName); 729 } 730 731 /** 732 * Sets the premium SMS permission for the specified package and save the value asynchronously 733 * to persistent storage. 734 * @param packageName the name of the package to set permission 735 * @param permission one of {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ASK_USER}, 736 * {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_NEVER_ALLOW}, or 737 * {@link SmsUsageMonitor#PREMIUM_SMS_PERMISSION_ALWAYS_ALLOW} 738 */ setPremiumSmsPermission(String packageName, int permission)739 public void setPremiumSmsPermission(String packageName, int permission) { 740 mUsageMonitor.setPremiumSmsPermission(packageName, permission); 741 } 742 getUsageMonitor()743 public SmsUsageMonitor getUsageMonitor() { 744 return mUsageMonitor; 745 } 746 747 /** 748 * Triggers the correct method for handling the sms status report based on the format. 749 * 750 * @param tracker the sms tracker. 751 * @param format the format. 752 * @param pdu the pdu of the report. 753 * @return a Pair in which the first boolean is whether the report was handled successfully 754 * or not and the second boolean is whether processing the sms is complete and the 755 * tracker no longer need to be kept track of, false if we should expect more callbacks 756 * and the tracker should be kept. 757 */ handleSmsStatusReport(SMSDispatcher.SmsTracker tracker, String format, byte[] pdu)758 public Pair<Boolean, Boolean> handleSmsStatusReport(SMSDispatcher.SmsTracker tracker, 759 String format, byte[] pdu) { 760 if (isCdmaFormat(format)) { 761 return handleCdmaStatusReport(tracker, format, pdu); 762 } else { 763 return handleGsmStatusReport(tracker, format, pdu); 764 } 765 } 766 handleCdmaStatusReport(SMSDispatcher.SmsTracker tracker, String format, byte[] pdu)767 private Pair<Boolean, Boolean> handleCdmaStatusReport(SMSDispatcher.SmsTracker tracker, 768 String format, byte[] pdu) { 769 tracker.updateSentMessageStatus(mContext, Sms.STATUS_COMPLETE); 770 boolean success = triggerDeliveryIntent(tracker, format, pdu); 771 return new Pair(success, true /* complete */); 772 } 773 handleGsmStatusReport(SMSDispatcher.SmsTracker tracker, String format, byte[] pdu)774 private Pair<Boolean, Boolean> handleGsmStatusReport(SMSDispatcher.SmsTracker tracker, 775 String format, byte[] pdu) { 776 com.android.internal.telephony.gsm.SmsMessage sms = 777 com.android.internal.telephony.gsm.SmsMessage.newFromCDS(pdu); 778 boolean complete = false; 779 boolean success = false; 780 if (sms != null) { 781 int tpStatus = sms.getStatus(); 782 if(tpStatus >= Sms.STATUS_FAILED || tpStatus < Sms.STATUS_PENDING ) { 783 // Update the message status (COMPLETE or FAILED) 784 tracker.updateSentMessageStatus(mContext, tpStatus); 785 complete = true; 786 } 787 success = triggerDeliveryIntent(tracker, format, pdu); 788 } 789 return new Pair(success, complete); 790 } 791 triggerDeliveryIntent(SMSDispatcher.SmsTracker tracker, String format, byte[] pdu)792 private boolean triggerDeliveryIntent(SMSDispatcher.SmsTracker tracker, String format, 793 byte[] pdu) { 794 PendingIntent intent = tracker.mDeliveryIntent; 795 Intent fillIn = new Intent(); 796 fillIn.putExtra("pdu", pdu); 797 fillIn.putExtra("format", format); 798 try { 799 intent.send(mContext, Activity.RESULT_OK, fillIn); 800 return true; 801 } catch (CanceledException ex) { 802 return false; 803 } 804 } 805 806 807 public interface SmsInjectionCallback { onSmsInjectedResult(int result)808 void onSmsInjectedResult(int result); 809 } 810 dump(FileDescriptor fd, PrintWriter pw, String[] args)811 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 812 mGsmInboundSmsHandler.dump(fd, pw, args); 813 mCdmaInboundSmsHandler.dump(fd, pw, args); 814 } 815 logd(String msg)816 private void logd(String msg) { 817 Rlog.d(TAG, msg); 818 } 819 } 820