GsmSMSDispatcher.java revision 850665a367489cce0b83431fa0e6e543b24062e0
1/* 2 * Copyright (C) 2006 The Android Open Source Project 3 * Copyright (c) 2012, The Linux Foundation. All rights reserved. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18package com.android.internal.telephony.gsm; 19 20import android.app.Activity; 21import android.app.PendingIntent; 22import android.app.PendingIntent.CanceledException; 23import android.content.Intent; 24import android.os.AsyncResult; 25import android.os.Message; 26import android.os.SystemProperties; 27import android.provider.Telephony.Sms; 28import android.provider.Telephony.Sms.Intents; 29import android.telephony.PhoneNumberUtils; 30import android.telephony.SmsCbLocation; 31import android.telephony.SmsCbMessage; 32import android.telephony.SmsManager; 33import android.telephony.gsm.GsmCellLocation; 34import android.telephony.Rlog; 35 36import com.android.internal.telephony.CommandsInterface; 37import com.android.internal.telephony.GsmAlphabet; 38import com.android.internal.telephony.PhoneBase; 39import com.android.internal.telephony.SmsConstants; 40import com.android.internal.telephony.SMSDispatcher; 41import com.android.internal.telephony.ImsSMSDispatcher; 42import com.android.internal.telephony.SmsHeader; 43import com.android.internal.telephony.SmsMessageBase; 44import com.android.internal.telephony.SmsStorageMonitor; 45import com.android.internal.telephony.SmsUsageMonitor; 46import com.android.internal.telephony.TelephonyProperties; 47import com.android.internal.telephony.uicc.IccRecords; 48import com.android.internal.telephony.uicc.IccUtils; 49import com.android.internal.telephony.uicc.UiccCardApplication; 50import com.android.internal.telephony.uicc.UiccController; 51import com.android.internal.telephony.uicc.UsimServiceTable; 52 53import java.util.HashMap; 54import java.util.Iterator; 55import java.util.concurrent.atomic.AtomicReference; 56 57public final class GsmSMSDispatcher extends SMSDispatcher { 58 private static final String TAG = "GsmSMSDispatcher"; 59 private static final boolean VDBG = false; 60 private ImsSMSDispatcher mImsSMSDispatcher; 61 private UiccController mUiccController = null; 62 private AtomicReference<IccRecords> mIccRecords = new AtomicReference<IccRecords>(); 63 private AtomicReference<UiccCardApplication> mUiccApplication = 64 new AtomicReference<UiccCardApplication>(); 65 66 /** Status report received */ 67 private static final int EVENT_NEW_SMS_STATUS_REPORT = 100; 68 69 /** New broadcast SMS */ 70 private static final int EVENT_NEW_BROADCAST_SMS = 101; 71 72 /** Result of writing SM to UICC (when SMS-PP service is not available). */ 73 private static final int EVENT_WRITE_SMS_COMPLETE = 102; 74 75 /** Handler for SMS-PP data download messages to UICC. */ 76 private final UsimDataDownloadHandler mDataDownloadHandler; 77 78 public GsmSMSDispatcher(PhoneBase phone, SmsStorageMonitor storageMonitor, 79 SmsUsageMonitor usageMonitor, ImsSMSDispatcher imsSMSDispatcher) { 80 super(phone, storageMonitor, usageMonitor); 81 mDataDownloadHandler = new UsimDataDownloadHandler(mCi); 82 mCi.setOnNewGsmSms(this, EVENT_NEW_SMS, null); 83 mCi.setOnSmsStatus(this, EVENT_NEW_SMS_STATUS_REPORT, null); 84 mCi.setOnNewGsmBroadcastSms(this, EVENT_NEW_BROADCAST_SMS, null); 85 mImsSMSDispatcher = imsSMSDispatcher; 86 mUiccController = UiccController.getInstance(); 87 mUiccController.registerForIccChanged(this, EVENT_ICC_CHANGED, null); 88 Rlog.d(TAG, "GsmSMSDispatcher created"); 89 } 90 91 @Override 92 public void dispose() { 93 mCi.unSetOnNewGsmSms(this); 94 mCi.unSetOnSmsStatus(this); 95 mCi.unSetOnNewGsmBroadcastSms(this); 96 } 97 98 @Override 99 protected String getFormat() { 100 return SmsConstants.FORMAT_3GPP; 101 } 102 103 /** 104 * Handles 3GPP format-specific events coming from the phone stack. 105 * Other events are handled by {@link SMSDispatcher#handleMessage}. 106 * 107 * @param msg the message to handle 108 */ 109 @Override 110 public void handleMessage(Message msg) { 111 switch (msg.what) { 112 case EVENT_NEW_SMS_STATUS_REPORT: 113 handleStatusReport((AsyncResult) msg.obj); 114 break; 115 116 case EVENT_NEW_BROADCAST_SMS: 117 handleBroadcastSms((AsyncResult)msg.obj); 118 break; 119 120 case EVENT_WRITE_SMS_COMPLETE: 121 AsyncResult ar = (AsyncResult) msg.obj; 122 if (ar.exception == null) { 123 Rlog.d(TAG, "Successfully wrote SMS-PP message to UICC"); 124 mCi.acknowledgeLastIncomingGsmSms(true, 0, null); 125 } else { 126 Rlog.d(TAG, "Failed to write SMS-PP message to UICC", ar.exception); 127 mCi.acknowledgeLastIncomingGsmSms(false, 128 CommandsInterface.GSM_SMS_FAIL_CAUSE_UNSPECIFIED_ERROR, null); 129 } 130 break; 131 132 case EVENT_NEW_ICC_SMS: 133 ar = (AsyncResult)msg.obj; 134 dispatchMessage((SmsMessage)ar.result); 135 break; 136 137 case EVENT_ICC_CHANGED: 138 onUpdateIccAvailability(); 139 break; 140 141 default: 142 super.handleMessage(msg); 143 } 144 } 145 146 /** 147 * Called when a status report is received. This should correspond to 148 * a previously successful SEND. 149 * 150 * @param ar AsyncResult passed into the message handler. ar.result should 151 * be a String representing the status report PDU, as ASCII hex. 152 */ 153 private void handleStatusReport(AsyncResult ar) { 154 String pduString = (String) ar.result; 155 SmsMessage sms = SmsMessage.newFromCDS(pduString); 156 157 if (sms != null) { 158 int tpStatus = sms.getStatus(); 159 int messageRef = sms.mMessageRef; 160 for (int i = 0, count = deliveryPendingList.size(); i < count; i++) { 161 SmsTracker tracker = deliveryPendingList.get(i); 162 if (tracker.mMessageRef == messageRef) { 163 // Found it. Remove from list and broadcast. 164 if(tpStatus >= Sms.STATUS_FAILED || tpStatus < Sms.STATUS_PENDING ) { 165 deliveryPendingList.remove(i); 166 } 167 PendingIntent intent = tracker.mDeliveryIntent; 168 Intent fillIn = new Intent(); 169 fillIn.putExtra("pdu", IccUtils.hexStringToBytes(pduString)); 170 fillIn.putExtra("format", getFormat()); 171 try { 172 intent.send(mContext, Activity.RESULT_OK, fillIn); 173 } catch (CanceledException ex) {} 174 175 // Only expect to see one tracker matching this messageref 176 break; 177 } 178 } 179 } 180 acknowledgeLastIncomingSms(true, Intents.RESULT_SMS_HANDLED, null); 181 } 182 183 /** {@inheritDoc} */ 184 @Override 185 protected int dispatchMessage(SmsMessageBase smsb) { 186 187 // If sms is null, means there was a parsing error. 188 if (smsb == null) { 189 Rlog.e(TAG, "dispatchMessage: message is null"); 190 return Intents.RESULT_SMS_GENERIC_ERROR; 191 } 192 193 SmsMessage sms = (SmsMessage) smsb; 194 195 if (sms.isTypeZero()) { 196 // As per 3GPP TS 23.040 9.2.3.9, Type Zero messages should not be 197 // Displayed/Stored/Notified. They should only be acknowledged. 198 Rlog.d(TAG, "Received short message type 0, Don't display or store it. Send Ack"); 199 return Intents.RESULT_SMS_HANDLED; 200 } 201 202 // Send SMS-PP data download messages to UICC. See 3GPP TS 31.111 section 7.1.1. 203 if (sms.isUsimDataDownload()) { 204 UsimServiceTable ust = mPhone.getUsimServiceTable(); 205 // If we receive an SMS-PP message before the UsimServiceTable has been loaded, 206 // assume that the data download service is not present. This is very unlikely to 207 // happen because the IMS connection will not be established until after the ISIM 208 // records have been loaded, after the USIM service table has been loaded. 209 if (ust != null && ust.isAvailable( 210 UsimServiceTable.UsimService.DATA_DL_VIA_SMS_PP)) { 211 Rlog.d(TAG, "Received SMS-PP data download, sending to UICC."); 212 return mDataDownloadHandler.startDataDownload(sms); 213 } else { 214 Rlog.d(TAG, "DATA_DL_VIA_SMS_PP service not available, storing message to UICC."); 215 String smsc = IccUtils.bytesToHexString( 216 PhoneNumberUtils.networkPortionToCalledPartyBCDWithLength( 217 sms.getServiceCenterAddress())); 218 mCi.writeSmsToSim(SmsManager.STATUS_ON_ICC_UNREAD, smsc, 219 IccUtils.bytesToHexString(sms.getPdu()), 220 obtainMessage(EVENT_WRITE_SMS_COMPLETE)); 221 return Activity.RESULT_OK; // acknowledge after response from write to USIM 222 } 223 } 224 225 if (mSmsReceiveDisabled) { 226 // Device doesn't support SMS service, 227 Rlog.d(TAG, "Received short message on device which doesn't support " 228 + "SMS service. Ignored."); 229 return Intents.RESULT_SMS_HANDLED; 230 } 231 232 // Special case the message waiting indicator messages 233 boolean handled = false; 234 if (sms.isMWISetMessage()) { 235 mPhone.setVoiceMessageWaiting(1, -1); // line 1: unknown number of msgs waiting 236 handled = sms.isMwiDontStore(); 237 if (VDBG) { 238 Rlog.d(TAG, "Received voice mail indicator set SMS shouldStore=" + !handled); 239 } 240 } else if (sms.isMWIClearMessage()) { 241 mPhone.setVoiceMessageWaiting(1, 0); // line 1: no msgs waiting 242 handled = sms.isMwiDontStore(); 243 if (VDBG) { 244 Rlog.d(TAG, "Received voice mail indicator clear SMS shouldStore=" + !handled); 245 } 246 } 247 248 if (handled) { 249 return Intents.RESULT_SMS_HANDLED; 250 } 251 252 if (!mStorageMonitor.isStorageAvailable() && 253 sms.getMessageClass() != SmsConstants.MessageClass.CLASS_0) { 254 // It's a storable message and there's no storage available. Bail. 255 // (See TS 23.038 for a description of class 0 messages.) 256 return Intents.RESULT_SMS_OUT_OF_MEMORY; 257 } 258 259 return dispatchNormalMessage(smsb); 260 } 261 262 /** {@inheritDoc} */ 263 @Override 264 protected void sendData(String destAddr, String scAddr, int destPort, 265 byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) { 266 SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu( 267 scAddr, destAddr, destPort, data, (deliveryIntent != null)); 268 if (pdu != null) { 269 HashMap map = SmsTrackerMapFactory(destAddr, scAddr, destPort, data, pdu); 270 SmsTracker tracker = SmsTrackerFactory(map, sentIntent, deliveryIntent, 271 getFormat()); 272 sendRawPdu(tracker); 273 } else { 274 Rlog.e(TAG, "GsmSMSDispatcher.sendData(): getSubmitPdu() returned null"); 275 } 276 } 277 278 /** {@inheritDoc} */ 279 @Override 280 protected void sendText(String destAddr, String scAddr, String text, 281 PendingIntent sentIntent, PendingIntent deliveryIntent) { 282 SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu( 283 scAddr, destAddr, text, (deliveryIntent != null)); 284 if (pdu != null) { 285 HashMap map = SmsTrackerMapFactory(destAddr, scAddr, text, pdu); 286 SmsTracker tracker = SmsTrackerFactory(map, sentIntent, deliveryIntent, 287 getFormat()); 288 sendRawPdu(tracker); 289 } else { 290 Rlog.e(TAG, "GsmSMSDispatcher.sendText(): getSubmitPdu() returned null"); 291 } 292 } 293 294 /** {@inheritDoc} */ 295 @Override 296 protected GsmAlphabet.TextEncodingDetails calculateLength(CharSequence messageBody, 297 boolean use7bitOnly) { 298 return SmsMessage.calculateLength(messageBody, use7bitOnly); 299 } 300 301 /** {@inheritDoc} */ 302 @Override 303 protected void sendNewSubmitPdu(String destinationAddress, String scAddress, 304 String message, SmsHeader smsHeader, int encoding, 305 PendingIntent sentIntent, PendingIntent deliveryIntent, boolean lastPart) { 306 SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(scAddress, destinationAddress, 307 message, deliveryIntent != null, SmsHeader.toByteArray(smsHeader), 308 encoding, smsHeader.languageTable, smsHeader.languageShiftTable); 309 if (pdu != null) { 310 HashMap map = SmsTrackerMapFactory(destinationAddress, scAddress, 311 message, pdu); 312 SmsTracker tracker = SmsTrackerFactory(map, sentIntent, 313 deliveryIntent, getFormat()); 314 sendRawPdu(tracker); 315 } else { 316 Rlog.e(TAG, "GsmSMSDispatcher.sendNewSubmitPdu(): getSubmitPdu() returned null"); 317 } 318 } 319 320 /** {@inheritDoc} */ 321 @Override 322 protected void sendSms(SmsTracker tracker) { 323 HashMap<String, Object> map = tracker.mData; 324 325 byte smsc[] = (byte[]) map.get("smsc"); 326 byte pdu[] = (byte[]) map.get("pdu"); 327 328 Message reply = obtainMessage(EVENT_SEND_SMS_COMPLETE, tracker); 329 330 if (tracker.mRetryCount > 0) { 331 Rlog.d(TAG, "sendSms: " 332 + " mRetryCount=" + tracker.mRetryCount 333 + " mMessageRef=" + tracker.mMessageRef 334 + " SS=" + mPhone.getServiceState().getState()); 335 336 // per TS 23.040 Section 9.2.3.6: If TP-MTI SMS-SUBMIT (0x01) type 337 // TP-RD (bit 2) is 1 for retry 338 // and TP-MR is set to previously failed sms TP-MR 339 if (((0x01 & pdu[0]) == 0x01)) { 340 pdu[0] |= 0x04; // TP-RD 341 pdu[1] = (byte) tracker.mMessageRef; // TP-MR 342 } 343 } 344 Rlog.d(TAG, "sendSms: " 345 +" isIms()="+isIms() 346 +" mRetryCount="+tracker.mRetryCount 347 +" mImsRetry="+tracker.mImsRetry 348 +" mMessageRef="+tracker.mMessageRef 349 +" SS=" +mPhone.getServiceState().getState()); 350 351 // sms over gsm is used: 352 // if sms over IMS is not supported AND 353 // this is not a retry case after sms over IMS failed 354 // indicated by mImsRetry > 0 355 if (0 == tracker.mImsRetry && !isIms()) { 356 if (tracker.mRetryCount > 0) { 357 // per TS 23.040 Section 9.2.3.6: If TP-MTI SMS-SUBMIT (0x01) type 358 // TP-RD (bit 2) is 1 for retry 359 // and TP-MR is set to previously failed sms TP-MR 360 if (((0x01 & pdu[0]) == 0x01)) { 361 pdu[0] |= 0x04; // TP-RD 362 pdu[1] = (byte) tracker.mMessageRef; // TP-MR 363 } 364 } 365 mCi.sendSMS(IccUtils.bytesToHexString(smsc), 366 IccUtils.bytesToHexString(pdu), reply); 367 } else { 368 mCi.sendImsGsmSms(IccUtils.bytesToHexString(smsc), 369 IccUtils.bytesToHexString(pdu), tracker.mImsRetry, 370 tracker.mMessageRef, reply); 371 // increment it here, so in case of SMS_FAIL_RETRY over IMS 372 // next retry will be sent using IMS request again. 373 tracker.mImsRetry++; 374 } 375 } 376 377 @Override 378 public void sendRetrySms(SmsTracker tracker) { 379 //re-routing to ImsSMSDispatcher 380 mImsSMSDispatcher.sendRetrySms(tracker); 381 } 382 /** {@inheritDoc} */ 383 @Override 384 protected void acknowledgeLastIncomingSms(boolean success, int result, Message response) { 385 mCi.acknowledgeLastIncomingGsmSms(success, resultToCause(result), response); 386 } 387 388 private static int resultToCause(int rc) { 389 switch (rc) { 390 case Activity.RESULT_OK: 391 case Intents.RESULT_SMS_HANDLED: 392 // Cause code is ignored on success. 393 return 0; 394 case Intents.RESULT_SMS_OUT_OF_MEMORY: 395 return CommandsInterface.GSM_SMS_FAIL_CAUSE_MEMORY_CAPACITY_EXCEEDED; 396 case Intents.RESULT_SMS_GENERIC_ERROR: 397 default: 398 return CommandsInterface.GSM_SMS_FAIL_CAUSE_UNSPECIFIED_ERROR; 399 } 400 } 401 402 private void onUpdateIccAvailability() { 403 if (mUiccController == null ) { 404 return; 405 } 406 407 UiccCardApplication newUiccApplication = 408 mUiccController.getUiccCardApplication(UiccController.APP_FAM_3GPP); 409 410 UiccCardApplication app = mUiccApplication.get(); 411 if (app != newUiccApplication) { 412 if (app != null) { 413 Rlog.d(TAG, "Removing stale icc objects."); 414 if (mIccRecords.get() != null) { 415 mIccRecords.get().unregisterForNewSms(this); 416 } 417 mIccRecords.set(null); 418 mUiccApplication.set(null); 419 } 420 if (newUiccApplication != null) { 421 Rlog.d(TAG, "New Uicc application found"); 422 mUiccApplication.set(newUiccApplication); 423 mIccRecords.set(newUiccApplication.getIccRecords()); 424 if (mIccRecords.get() != null) { 425 mIccRecords.get().registerForNewSms(this, EVENT_NEW_ICC_SMS, null); 426 } 427 } 428 } 429 } 430 431 /** 432 * Holds all info about a message page needed to assemble a complete 433 * concatenated message 434 */ 435 private static final class SmsCbConcatInfo { 436 437 private final SmsCbHeader mHeader; 438 private final SmsCbLocation mLocation; 439 440 public SmsCbConcatInfo(SmsCbHeader header, SmsCbLocation location) { 441 mHeader = header; 442 mLocation = location; 443 } 444 445 @Override 446 public int hashCode() { 447 return (mHeader.getSerialNumber() * 31) + mLocation.hashCode(); 448 } 449 450 @Override 451 public boolean equals(Object obj) { 452 if (obj instanceof SmsCbConcatInfo) { 453 SmsCbConcatInfo other = (SmsCbConcatInfo)obj; 454 455 // Two pages match if they have the same serial number (which includes the 456 // geographical scope and update number), and both pages belong to the same 457 // location (PLMN, plus LAC and CID if these are part of the geographical scope). 458 return mHeader.getSerialNumber() == other.mHeader.getSerialNumber() 459 && mLocation.equals(other.mLocation); 460 } 461 462 return false; 463 } 464 465 /** 466 * Compare the location code for this message to the current location code. The match is 467 * relative to the geographical scope of the message, which determines whether the LAC 468 * and Cell ID are saved in mLocation or set to -1 to match all values. 469 * 470 * @param plmn the current PLMN 471 * @param lac the current Location Area (GSM) or Service Area (UMTS) 472 * @param cid the current Cell ID 473 * @return true if this message is valid for the current location; false otherwise 474 */ 475 public boolean matchesLocation(String plmn, int lac, int cid) { 476 return mLocation.isInLocationArea(plmn, lac, cid); 477 } 478 } 479 480 // This map holds incomplete concatenated messages waiting for assembly 481 private final HashMap<SmsCbConcatInfo, byte[][]> mSmsCbPageMap = 482 new HashMap<SmsCbConcatInfo, byte[][]>(); 483 484 /** 485 * Handle 3GPP format SMS-CB message. 486 * @param ar the AsyncResult containing the received PDUs 487 */ 488 private void handleBroadcastSms(AsyncResult ar) { 489 try { 490 byte[] receivedPdu = (byte[])ar.result; 491 492 if (VDBG) { 493 for (int i = 0; i < receivedPdu.length; i += 8) { 494 StringBuilder sb = new StringBuilder("SMS CB pdu data: "); 495 for (int j = i; j < i + 8 && j < receivedPdu.length; j++) { 496 int b = receivedPdu[j] & 0xff; 497 if (b < 0x10) { 498 sb.append('0'); 499 } 500 sb.append(Integer.toHexString(b)).append(' '); 501 } 502 Rlog.d(TAG, sb.toString()); 503 } 504 } 505 506 SmsCbHeader header = new SmsCbHeader(receivedPdu); 507 String plmn = SystemProperties.get(TelephonyProperties.PROPERTY_OPERATOR_NUMERIC); 508 int lac = -1; 509 int cid = -1; 510 android.telephony.CellLocation cl = mPhone.getCellLocation(); 511 // Check if cell location is GsmCellLocation. This is required to support 512 // dual-mode devices such as CDMA/LTE devices that require support for 513 // both 3GPP and 3GPP2 format messages 514 if (cl instanceof GsmCellLocation) { 515 GsmCellLocation cellLocation = (GsmCellLocation)cl; 516 lac = cellLocation.getLac(); 517 cid = cellLocation.getCid(); 518 } 519 520 SmsCbLocation location; 521 switch (header.getGeographicalScope()) { 522 case SmsCbMessage.GEOGRAPHICAL_SCOPE_LA_WIDE: 523 location = new SmsCbLocation(plmn, lac, -1); 524 break; 525 526 case SmsCbMessage.GEOGRAPHICAL_SCOPE_CELL_WIDE: 527 case SmsCbMessage.GEOGRAPHICAL_SCOPE_CELL_WIDE_IMMEDIATE: 528 location = new SmsCbLocation(plmn, lac, cid); 529 break; 530 531 case SmsCbMessage.GEOGRAPHICAL_SCOPE_PLMN_WIDE: 532 default: 533 location = new SmsCbLocation(plmn); 534 break; 535 } 536 537 byte[][] pdus; 538 int pageCount = header.getNumberOfPages(); 539 if (pageCount > 1) { 540 // Multi-page message 541 SmsCbConcatInfo concatInfo = new SmsCbConcatInfo(header, location); 542 543 // Try to find other pages of the same message 544 pdus = mSmsCbPageMap.get(concatInfo); 545 546 if (pdus == null) { 547 // This is the first page of this message, make room for all 548 // pages and keep until complete 549 pdus = new byte[pageCount][]; 550 551 mSmsCbPageMap.put(concatInfo, pdus); 552 } 553 554 // Page parameter is one-based 555 pdus[header.getPageIndex() - 1] = receivedPdu; 556 557 for (int i = 0; i < pdus.length; i++) { 558 if (pdus[i] == null) { 559 // Still missing pages, exit 560 return; 561 } 562 } 563 564 // Message complete, remove and dispatch 565 mSmsCbPageMap.remove(concatInfo); 566 } else { 567 // Single page message 568 pdus = new byte[1][]; 569 pdus[0] = receivedPdu; 570 } 571 572 SmsCbMessage message = GsmSmsCbMessage.createSmsCbMessage(header, location, pdus); 573 dispatchBroadcastMessage(message); 574 575 // Remove messages that are out of scope to prevent the map from 576 // growing indefinitely, containing incomplete messages that were 577 // never assembled 578 Iterator<SmsCbConcatInfo> iter = mSmsCbPageMap.keySet().iterator(); 579 580 while (iter.hasNext()) { 581 SmsCbConcatInfo info = iter.next(); 582 583 if (!info.matchesLocation(plmn, lac, cid)) { 584 iter.remove(); 585 } 586 } 587 } catch (RuntimeException e) { 588 Rlog.e(TAG, "Error in decoding SMS CB pdu", e); 589 } 590 } 591 592 @Override 593 public boolean isIms() { 594 return mImsSMSDispatcher.isIms(); 595 } 596 597 @Override 598 public String getImsSmsFormat() { 599 return mImsSMSDispatcher.getImsSmsFormat(); 600 } 601} 602