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