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