CatService.java revision b8127144de8d55c590ed537bdcb7a1afe5666b28
1/* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.internal.telephony.cat; 18 19import android.content.Context; 20import android.content.Intent; 21import android.content.pm.PackageManager; 22import android.content.pm.ResolveInfo; 23import android.os.AsyncResult; 24import android.os.Handler; 25import android.os.HandlerThread; 26import android.os.Message; 27import android.os.SystemProperties; 28 29import com.android.internal.telephony.CommandsInterface; 30import com.android.internal.telephony.uicc.IccFileHandler; 31import com.android.internal.telephony.uicc.IccRecords; 32import com.android.internal.telephony.uicc.IccUtils; 33import com.android.internal.telephony.uicc.UiccCard; 34import com.android.internal.telephony.uicc.UiccCardApplication; 35 36 37import java.io.ByteArrayOutputStream; 38import java.util.List; 39import java.util.Locale; 40 41class RilMessage { 42 int mId; 43 Object mData; 44 ResultCode mResCode; 45 46 RilMessage(int msgId, String rawData) { 47 mId = msgId; 48 mData = rawData; 49 } 50 51 RilMessage(RilMessage other) { 52 mId = other.mId; 53 mData = other.mData; 54 mResCode = other.mResCode; 55 } 56} 57 58/** 59 * Class that implements SIM Toolkit Telephony Service. Interacts with the RIL 60 * and application. 61 * 62 * {@hide} 63 */ 64public class CatService extends Handler implements AppInterface { 65 private static final boolean DBG = false; 66 67 // Class members 68 private static IccRecords mIccRecords; 69 private static UiccCardApplication mUiccApplication; 70 71 // Service members. 72 // Protects singleton instance lazy initialization. 73 private static final Object sInstanceLock = new Object(); 74 private static CatService sInstance; 75 private CommandsInterface mCmdIf; 76 private Context mContext; 77 private CatCmdMessage mCurrntCmd = null; 78 private CatCmdMessage mMenuCmd = null; 79 80 private RilMessageDecoder mMsgDecoder = null; 81 private boolean mStkAppInstalled = false; 82 83 // Service constants. 84 static final int MSG_ID_SESSION_END = 1; 85 static final int MSG_ID_PROACTIVE_COMMAND = 2; 86 static final int MSG_ID_EVENT_NOTIFY = 3; 87 static final int MSG_ID_CALL_SETUP = 4; 88 static final int MSG_ID_REFRESH = 5; 89 static final int MSG_ID_RESPONSE = 6; 90 static final int MSG_ID_SIM_READY = 7; 91 92 static final int MSG_ID_RIL_MSG_DECODED = 10; 93 94 // Events to signal SIM presence or absent in the device. 95 private static final int MSG_ID_ICC_RECORDS_LOADED = 20; 96 97 private static final int DEV_ID_KEYPAD = 0x01; 98 private static final int DEV_ID_UICC = 0x81; 99 private static final int DEV_ID_TERMINAL = 0x82; 100 private static final int DEV_ID_NETWORK = 0x83; 101 102 static final String STK_DEFAULT = "Default Message"; 103 104 /* Intentionally private for singleton */ 105 private CatService(CommandsInterface ci, UiccCardApplication ca, IccRecords ir, 106 Context context, IccFileHandler fh, UiccCard ic) { 107 if (ci == null || ca == null || ir == null || context == null || fh == null 108 || ic == null) { 109 throw new NullPointerException( 110 "Service: Input parameters must not be null"); 111 } 112 mCmdIf = ci; 113 mContext = context; 114 115 // Get the RilMessagesDecoder for decoding the messages. 116 mMsgDecoder = RilMessageDecoder.getInstance(this, fh); 117 118 // Register ril events handling. 119 mCmdIf.setOnCatSessionEnd(this, MSG_ID_SESSION_END, null); 120 mCmdIf.setOnCatProactiveCmd(this, MSG_ID_PROACTIVE_COMMAND, null); 121 mCmdIf.setOnCatEvent(this, MSG_ID_EVENT_NOTIFY, null); 122 mCmdIf.setOnCatCallSetUp(this, MSG_ID_CALL_SETUP, null); 123 //mCmdIf.setOnSimRefresh(this, MSG_ID_REFRESH, null); 124 125 mIccRecords = ir; 126 mUiccApplication = ca; 127 128 // Register for SIM ready event. 129 mUiccApplication.registerForReady(this, MSG_ID_SIM_READY, null); 130 mIccRecords.registerForRecordsLoaded(this, MSG_ID_ICC_RECORDS_LOADED, null); 131 132 // Check if STK application is availalbe 133 mStkAppInstalled = isStkAppInstalled(); 134 135 CatLog.d(this, "Running CAT service. STK app installed:" + mStkAppInstalled); 136 } 137 138 public void dispose() { 139 mIccRecords.unregisterForRecordsLoaded(this); 140 mCmdIf.unSetOnCatSessionEnd(this); 141 mCmdIf.unSetOnCatProactiveCmd(this); 142 mCmdIf.unSetOnCatEvent(this); 143 mCmdIf.unSetOnCatCallSetUp(this); 144 145 removeCallbacksAndMessages(null); 146 } 147 148 @Override 149 protected void finalize() { 150 CatLog.d(this, "Service finalized"); 151 } 152 153 private void handleRilMsg(RilMessage rilMsg) { 154 if (rilMsg == null) { 155 return; 156 } 157 158 // dispatch messages 159 CommandParams cmdParams = null; 160 switch (rilMsg.mId) { 161 case MSG_ID_EVENT_NOTIFY: 162 if (rilMsg.mResCode == ResultCode.OK) { 163 cmdParams = (CommandParams) rilMsg.mData; 164 if (cmdParams != null) { 165 handleCommand(cmdParams, false); 166 } 167 } 168 break; 169 case MSG_ID_PROACTIVE_COMMAND: 170 try { 171 cmdParams = (CommandParams) rilMsg.mData; 172 } catch (ClassCastException e) { 173 // for error handling : cast exception 174 CatLog.d(this, "Fail to parse proactive command"); 175 // Don't send Terminal Resp if command detail is not available 176 if (mCurrntCmd != null) { 177 sendTerminalResponse(mCurrntCmd.mCmdDet, ResultCode.CMD_DATA_NOT_UNDERSTOOD, 178 false, 0x00, null); 179 } 180 break; 181 } 182 if (cmdParams != null) { 183 if (rilMsg.mResCode == ResultCode.OK) { 184 handleCommand(cmdParams, true); 185 } else { 186 // for proactive commands that couldn't be decoded 187 // successfully respond with the code generated by the 188 // message decoder. 189 sendTerminalResponse(cmdParams.mCmdDet, rilMsg.mResCode, 190 false, 0, null); 191 } 192 } 193 break; 194 case MSG_ID_REFRESH: 195 cmdParams = (CommandParams) rilMsg.mData; 196 if (cmdParams != null) { 197 handleCommand(cmdParams, false); 198 } 199 break; 200 case MSG_ID_SESSION_END: 201 handleSessionEnd(); 202 break; 203 case MSG_ID_CALL_SETUP: 204 // prior event notify command supplied all the information 205 // needed for set up call processing. 206 break; 207 } 208 } 209 210 /** 211 * Handles RIL_UNSOL_STK_EVENT_NOTIFY or RIL_UNSOL_STK_PROACTIVE_COMMAND command 212 * from RIL. 213 * Sends valid proactive command data to the application using intents. 214 * RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE will be send back if the command is 215 * from RIL_UNSOL_STK_PROACTIVE_COMMAND. 216 */ 217 private void handleCommand(CommandParams cmdParams, boolean isProactiveCmd) { 218 CatLog.d(this, cmdParams.getCommandType().name()); 219 220 CharSequence message; 221 CatCmdMessage cmdMsg = new CatCmdMessage(cmdParams); 222 switch (cmdParams.getCommandType()) { 223 case SET_UP_MENU: 224 if (removeMenu(cmdMsg.getMenu())) { 225 mMenuCmd = null; 226 } else { 227 mMenuCmd = cmdMsg; 228 } 229 sendTerminalResponse(cmdParams.mCmdDet, ResultCode.OK, false, 0, null); 230 break; 231 case DISPLAY_TEXT: 232 // when application is not required to respond, send an immediate response. 233 if (!cmdMsg.geTextMessage().responseNeeded) { 234 sendTerminalResponse(cmdParams.mCmdDet, ResultCode.OK, false, 0, null); 235 } 236 break; 237 case REFRESH: 238 // ME side only handles refresh commands which meant to remove IDLE 239 // MODE TEXT. 240 cmdParams.mCmdDet.typeOfCommand = CommandType.SET_UP_IDLE_MODE_TEXT.value(); 241 break; 242 case SET_UP_IDLE_MODE_TEXT: 243 sendTerminalResponse(cmdParams.mCmdDet, ResultCode.OK, false, 0, null); 244 break; 245 case PROVIDE_LOCAL_INFORMATION: 246 ResponseData resp; 247 switch (cmdParams.mCmdDet.commandQualifier) { 248 case CommandParamsFactory.DTTZ_SETTING: 249 resp = new DTTZResponseData(null); 250 sendTerminalResponse(cmdParams.mCmdDet, ResultCode.OK, false, 0, resp); 251 break; 252 case CommandParamsFactory.LANGUAGE_SETTING: 253 resp = new LanguageResponseData(Locale.getDefault().getLanguage()); 254 sendTerminalResponse(cmdParams.mCmdDet, ResultCode.OK, false, 0, resp); 255 break; 256 default: 257 sendTerminalResponse(cmdParams.mCmdDet, ResultCode.OK, false, 0, null); 258 } 259 // No need to start STK app here. 260 return; 261 case LAUNCH_BROWSER: 262 if ((((LaunchBrowserParams) cmdParams).mConfirmMsg.text != null) 263 && (((LaunchBrowserParams) cmdParams).mConfirmMsg.text.equals(STK_DEFAULT))) { 264 message = mContext.getText(com.android.internal.R.string.launchBrowserDefault); 265 ((LaunchBrowserParams) cmdParams).mConfirmMsg.text = message.toString(); 266 } 267 break; 268 case SELECT_ITEM: 269 case GET_INPUT: 270 case GET_INKEY: 271 break; 272 case SEND_DTMF: 273 case SEND_SMS: 274 case SEND_SS: 275 case SEND_USSD: 276 if ((((DisplayTextParams)cmdParams).mTextMsg.text != null) 277 && (((DisplayTextParams)cmdParams).mTextMsg.text.equals(STK_DEFAULT))) { 278 message = mContext.getText(com.android.internal.R.string.sending); 279 ((DisplayTextParams)cmdParams).mTextMsg.text = message.toString(); 280 } 281 break; 282 case PLAY_TONE: 283 break; 284 case SET_UP_CALL: 285 if ((((CallSetupParams) cmdParams).mConfirmMsg.text != null) 286 && (((CallSetupParams) cmdParams).mConfirmMsg.text.equals(STK_DEFAULT))) { 287 message = mContext.getText(com.android.internal.R.string.SetupCallDefault); 288 ((CallSetupParams) cmdParams).mConfirmMsg.text = message.toString(); 289 } 290 break; 291 case OPEN_CHANNEL: 292 case CLOSE_CHANNEL: 293 case RECEIVE_DATA: 294 case SEND_DATA: 295 BIPClientParams cmd = (BIPClientParams) cmdParams; 296 if (cmd.mHasAlphaId && (cmd.mTextMsg.text == null)) { 297 CatLog.d(this, "cmd " + cmdParams.getCommandType() + " with null alpha id"); 298 // If alpha length is zero, we just respond with OK. 299 if (isProactiveCmd) { 300 sendTerminalResponse(cmdParams.mCmdDet, ResultCode.OK, false, 0, null); 301 } 302 return; 303 } 304 // Respond with permanent failure to avoid retry if STK app is not present. 305 if (!mStkAppInstalled) { 306 CatLog.d(this, "No STK application found."); 307 if (isProactiveCmd) { 308 sendTerminalResponse(cmdParams.mCmdDet, 309 ResultCode.BEYOND_TERMINAL_CAPABILITY, 310 false, 0, null); 311 return; 312 } 313 } 314 /* 315 * CLOSE_CHANNEL, RECEIVE_DATA and SEND_DATA can be delivered by 316 * either PROACTIVE_COMMAND or EVENT_NOTIFY. 317 * If PROACTIVE_COMMAND is used for those commands, send terminal 318 * response here. 319 */ 320 if (isProactiveCmd && 321 ((cmdParams.getCommandType() == CommandType.CLOSE_CHANNEL) || 322 (cmdParams.getCommandType() == CommandType.RECEIVE_DATA) || 323 (cmdParams.getCommandType() == CommandType.SEND_DATA))) { 324 sendTerminalResponse(cmdParams.mCmdDet, ResultCode.OK, false, 0, null); 325 } 326 break; 327 default: 328 CatLog.d(this, "Unsupported command"); 329 return; 330 } 331 mCurrntCmd = cmdMsg; 332 Intent intent = new Intent(AppInterface.CAT_CMD_ACTION); 333 intent.putExtra("STK CMD", cmdMsg); 334 mContext.sendBroadcast(intent); 335 } 336 337 /** 338 * Handles RIL_UNSOL_STK_SESSION_END unsolicited command from RIL. 339 * 340 */ 341 private void handleSessionEnd() { 342 CatLog.d(this, "SESSION END"); 343 344 mCurrntCmd = mMenuCmd; 345 Intent intent = new Intent(AppInterface.CAT_SESSION_END_ACTION); 346 mContext.sendBroadcast(intent); 347 } 348 349 private void sendTerminalResponse(CommandDetails cmdDet, 350 ResultCode resultCode, boolean includeAdditionalInfo, 351 int additionalInfo, ResponseData resp) { 352 353 if (cmdDet == null) { 354 return; 355 } 356 ByteArrayOutputStream buf = new ByteArrayOutputStream(); 357 358 Input cmdInput = null; 359 if (mCurrntCmd != null) { 360 cmdInput = mCurrntCmd.geInput(); 361 } 362 363 // command details 364 int tag = ComprehensionTlvTag.COMMAND_DETAILS.value(); 365 if (cmdDet.compRequired) { 366 tag |= 0x80; 367 } 368 buf.write(tag); 369 buf.write(0x03); // length 370 buf.write(cmdDet.commandNumber); 371 buf.write(cmdDet.typeOfCommand); 372 buf.write(cmdDet.commandQualifier); 373 374 // device identities 375 // According to TS102.223/TS31.111 section 6.8 Structure of 376 // TERMINAL RESPONSE, "For all SIMPLE-TLV objects with Min=N, 377 // the ME should set the CR(comprehension required) flag to 378 // comprehension not required.(CR=0)" 379 // Since DEVICE_IDENTITIES and DURATION TLVs have Min=N, 380 // the CR flag is not set. 381 tag = ComprehensionTlvTag.DEVICE_IDENTITIES.value(); 382 buf.write(tag); 383 buf.write(0x02); // length 384 buf.write(DEV_ID_TERMINAL); // source device id 385 buf.write(DEV_ID_UICC); // destination device id 386 387 // result 388 tag = 0x80 | ComprehensionTlvTag.RESULT.value(); 389 buf.write(tag); 390 int length = includeAdditionalInfo ? 2 : 1; 391 buf.write(length); 392 buf.write(resultCode.value()); 393 394 // additional info 395 if (includeAdditionalInfo) { 396 buf.write(additionalInfo); 397 } 398 399 // Fill optional data for each corresponding command 400 if (resp != null) { 401 resp.format(buf); 402 } else { 403 encodeOptionalTags(cmdDet, resultCode, cmdInput, buf); 404 } 405 406 byte[] rawData = buf.toByteArray(); 407 String hexString = IccUtils.bytesToHexString(rawData); 408 if (DBG) { 409 CatLog.d(this, "TERMINAL RESPONSE: " + hexString); 410 } 411 412 mCmdIf.sendTerminalResponse(hexString, null); 413 } 414 415 private void encodeOptionalTags(CommandDetails cmdDet, 416 ResultCode resultCode, Input cmdInput, ByteArrayOutputStream buf) { 417 CommandType cmdType = AppInterface.CommandType.fromInt(cmdDet.typeOfCommand); 418 if (cmdType != null) { 419 switch (cmdType) { 420 case GET_INKEY: 421 // ETSI TS 102 384,27.22.4.2.8.4.2. 422 // If it is a response for GET_INKEY command and the response timeout 423 // occured, then add DURATION TLV for variable timeout case. 424 if ((resultCode.value() == ResultCode.NO_RESPONSE_FROM_USER.value()) && 425 (cmdInput != null) && (cmdInput.duration != null)) { 426 getInKeyResponse(buf, cmdInput); 427 } 428 break; 429 case PROVIDE_LOCAL_INFORMATION: 430 if ((cmdDet.commandQualifier == CommandParamsFactory.LANGUAGE_SETTING) && 431 (resultCode.value() == ResultCode.OK.value())) { 432 getPliResponse(buf); 433 } 434 break; 435 default: 436 CatLog.d(this, "encodeOptionalTags() Unsupported Cmd details=" + cmdDet); 437 break; 438 } 439 } else { 440 CatLog.d(this, "encodeOptionalTags() bad Cmd details=" + cmdDet); 441 } 442 } 443 444 private void getInKeyResponse(ByteArrayOutputStream buf, Input cmdInput) { 445 int tag = ComprehensionTlvTag.DURATION.value(); 446 447 buf.write(tag); 448 buf.write(0x02); // length 449 buf.write(cmdInput.duration.timeUnit.SECOND.value()); // Time (Unit,Seconds) 450 buf.write(cmdInput.duration.timeInterval); // Time Duration 451 } 452 453 private void getPliResponse(ByteArrayOutputStream buf) { 454 455 // Locale Language Setting 456 String lang = SystemProperties.get("persist.sys.language"); 457 458 if (lang != null) { 459 // tag 460 int tag = ComprehensionTlvTag.LANGUAGE.value(); 461 buf.write(tag); 462 ResponseData.writeLength(buf, lang.length()); 463 buf.write(lang.getBytes(), 0, lang.length()); 464 } 465 } 466 467 private void sendMenuSelection(int menuId, boolean helpRequired) { 468 469 ByteArrayOutputStream buf = new ByteArrayOutputStream(); 470 471 // tag 472 int tag = BerTlv.BER_MENU_SELECTION_TAG; 473 buf.write(tag); 474 475 // length 476 buf.write(0x00); // place holder 477 478 // device identities 479 tag = 0x80 | ComprehensionTlvTag.DEVICE_IDENTITIES.value(); 480 buf.write(tag); 481 buf.write(0x02); // length 482 buf.write(DEV_ID_KEYPAD); // source device id 483 buf.write(DEV_ID_UICC); // destination device id 484 485 // item identifier 486 tag = 0x80 | ComprehensionTlvTag.ITEM_ID.value(); 487 buf.write(tag); 488 buf.write(0x01); // length 489 buf.write(menuId); // menu identifier chosen 490 491 // help request 492 if (helpRequired) { 493 tag = ComprehensionTlvTag.HELP_REQUEST.value(); 494 buf.write(tag); 495 buf.write(0x00); // length 496 } 497 498 byte[] rawData = buf.toByteArray(); 499 500 // write real length 501 int len = rawData.length - 2; // minus (tag + length) 502 rawData[1] = (byte) len; 503 504 String hexString = IccUtils.bytesToHexString(rawData); 505 506 mCmdIf.sendEnvelope(hexString, null); 507 } 508 509 /** 510 * Used for instantiating/updating the Service from the GsmPhone or CdmaPhone constructor. 511 * 512 * @param ci CommandsInterface object 513 * @param context phone app context 514 * @param ic Icc card 515 * @return The only Service object in the system 516 */ 517 public static CatService getInstance(CommandsInterface ci, 518 Context context, UiccCard ic) { 519 UiccCardApplication ca = null; 520 IccFileHandler fh = null; 521 IccRecords ir = null; 522 if (ic != null) { 523 /* Since Cat is not tied to any application, but rather is Uicc application 524 * in itself - just get first FileHandler and IccRecords object 525 */ 526 ca = ic.getApplicationIndex(0); 527 if (ca != null) { 528 fh = ca.getIccFileHandler(); 529 ir = ca.getIccRecords(); 530 } 531 } 532 synchronized (sInstanceLock) { 533 if (sInstance == null) { 534 if (ci == null || ca == null || ir == null || context == null || fh == null 535 || ic == null) { 536 return null; 537 } 538 HandlerThread thread = new HandlerThread("Cat Telephony service"); 539 thread.start(); 540 sInstance = new CatService(ci, ca, ir, context, fh, ic); 541 CatLog.d(sInstance, "NEW sInstance"); 542 } else if ((ir != null) && (mIccRecords != ir)) { 543 if (mIccRecords != null) { 544 mIccRecords.unregisterForRecordsLoaded(sInstance); 545 } 546 547 if (mUiccApplication != null) { 548 mUiccApplication.unregisterForReady(sInstance); 549 } 550 CatLog.d(sInstance, 551 "Reinitialize the Service with SIMRecords and UiccCardApplication"); 552 mIccRecords = ir; 553 mUiccApplication = ca; 554 555 // re-Register for SIM ready event. 556 mIccRecords.registerForRecordsLoaded(sInstance, MSG_ID_ICC_RECORDS_LOADED, null); 557 mUiccApplication.registerForReady(sInstance, MSG_ID_SIM_READY, null); 558 CatLog.d(sInstance, "sr changed reinitialize and return current sInstance"); 559 } else { 560 CatLog.d(sInstance, "Return current sInstance"); 561 } 562 return sInstance; 563 } 564 } 565 566 /** 567 * Used by application to get an AppInterface object. 568 * 569 * @return The only Service object in the system 570 */ 571 public static AppInterface getInstance() { 572 return getInstance(null, null, null); 573 } 574 575 @Override 576 public void handleMessage(Message msg) { 577 578 switch (msg.what) { 579 case MSG_ID_SESSION_END: 580 case MSG_ID_PROACTIVE_COMMAND: 581 case MSG_ID_EVENT_NOTIFY: 582 case MSG_ID_REFRESH: 583 CatLog.d(this, "ril message arrived"); 584 String data = null; 585 if (msg.obj != null) { 586 AsyncResult ar = (AsyncResult) msg.obj; 587 if (ar != null && ar.result != null) { 588 try { 589 data = (String) ar.result; 590 } catch (ClassCastException e) { 591 break; 592 } 593 } 594 } 595 mMsgDecoder.sendStartDecodingMessageParams(new RilMessage(msg.what, data)); 596 break; 597 case MSG_ID_CALL_SETUP: 598 mMsgDecoder.sendStartDecodingMessageParams(new RilMessage(msg.what, null)); 599 break; 600 case MSG_ID_ICC_RECORDS_LOADED: 601 break; 602 case MSG_ID_RIL_MSG_DECODED: 603 handleRilMsg((RilMessage) msg.obj); 604 break; 605 case MSG_ID_RESPONSE: 606 handleCmdResponse((CatResponseMessage) msg.obj); 607 break; 608 case MSG_ID_SIM_READY: 609 CatLog.d(this, "SIM ready. Reporting STK service running now..."); 610 mCmdIf.reportStkServiceIsRunning(null); 611 break; 612 default: 613 throw new AssertionError("Unrecognized CAT command: " + msg.what); 614 } 615 } 616 617 @Override 618 public synchronized void onCmdResponse(CatResponseMessage resMsg) { 619 if (resMsg == null) { 620 return; 621 } 622 // queue a response message. 623 Message msg = obtainMessage(MSG_ID_RESPONSE, resMsg); 624 msg.sendToTarget(); 625 } 626 627 private boolean validateResponse(CatResponseMessage resMsg) { 628 if (mCurrntCmd != null) { 629 return (resMsg.mCmdDet.compareTo(mCurrntCmd.mCmdDet)); 630 } 631 return false; 632 } 633 634 private boolean removeMenu(Menu menu) { 635 try { 636 if (menu.items.size() == 1 && menu.items.get(0) == null) { 637 return true; 638 } 639 } catch (NullPointerException e) { 640 CatLog.d(this, "Unable to get Menu's items size"); 641 return true; 642 } 643 return false; 644 } 645 646 private void handleCmdResponse(CatResponseMessage resMsg) { 647 // Make sure the response details match the last valid command. An invalid 648 // response is a one that doesn't have a corresponding proactive command 649 // and sending it can "confuse" the baseband/ril. 650 // One reason for out of order responses can be UI glitches. For example, 651 // if the application launch an activity, and that activity is stored 652 // by the framework inside the history stack. That activity will be 653 // available for relaunch using the latest application dialog 654 // (long press on the home button). Relaunching that activity can send 655 // the same command's result again to the CatService and can cause it to 656 // get out of sync with the SIM. 657 if (!validateResponse(resMsg)) { 658 return; 659 } 660 ResponseData resp = null; 661 boolean helpRequired = false; 662 CommandDetails cmdDet = resMsg.getCmdDetails(); 663 AppInterface.CommandType type = AppInterface.CommandType.fromInt(cmdDet.typeOfCommand); 664 665 switch (resMsg.mResCode) { 666 case HELP_INFO_REQUIRED: 667 helpRequired = true; 668 // fall through 669 case OK: 670 case PRFRMD_WITH_PARTIAL_COMPREHENSION: 671 case PRFRMD_WITH_MISSING_INFO: 672 case PRFRMD_WITH_ADDITIONAL_EFS_READ: 673 case PRFRMD_ICON_NOT_DISPLAYED: 674 case PRFRMD_MODIFIED_BY_NAA: 675 case PRFRMD_LIMITED_SERVICE: 676 case PRFRMD_WITH_MODIFICATION: 677 case PRFRMD_NAA_NOT_ACTIVE: 678 case PRFRMD_TONE_NOT_PLAYED: 679 case TERMINAL_CRNTLY_UNABLE_TO_PROCESS: 680 switch (type) { 681 case SET_UP_MENU: 682 helpRequired = resMsg.mResCode == ResultCode.HELP_INFO_REQUIRED; 683 sendMenuSelection(resMsg.mUsersMenuSelection, helpRequired); 684 return; 685 case SELECT_ITEM: 686 resp = new SelectItemResponseData(resMsg.mUsersMenuSelection); 687 break; 688 case GET_INPUT: 689 case GET_INKEY: 690 Input input = mCurrntCmd.geInput(); 691 if (!input.yesNo) { 692 // when help is requested there is no need to send the text 693 // string object. 694 if (!helpRequired) { 695 resp = new GetInkeyInputResponseData(resMsg.mUsersInput, 696 input.ucs2, input.packed); 697 } 698 } else { 699 resp = new GetInkeyInputResponseData( 700 resMsg.mUsersYesNoSelection); 701 } 702 break; 703 case DISPLAY_TEXT: 704 case LAUNCH_BROWSER: 705 break; 706 case SET_UP_CALL: 707 mCmdIf.handleCallSetupRequestFromSim(resMsg.mUsersConfirm, null); 708 // No need to send terminal response for SET UP CALL. The user's 709 // confirmation result is send back using a dedicated ril message 710 // invoked by the CommandInterface call above. 711 mCurrntCmd = null; 712 return; 713 default: 714 break; 715 } 716 break; 717 case BACKWARD_MOVE_BY_USER: 718 case USER_NOT_ACCEPT: 719 // if the user dismissed the alert dialog for a 720 // setup call/open channel, consider that as the user 721 // rejecting the call. Use dedicated API for this, rather than 722 // sending a terminal response. 723 if (type == CommandType.SET_UP_CALL || type == CommandType.OPEN_CHANNEL) { 724 mCmdIf.handleCallSetupRequestFromSim(false, null); 725 mCurrntCmd = null; 726 return; 727 } else { 728 resp = null; 729 } 730 break; 731 case NO_RESPONSE_FROM_USER: 732 case UICC_SESSION_TERM_BY_USER: 733 resp = null; 734 break; 735 default: 736 return; 737 } 738 sendTerminalResponse(cmdDet, resMsg.mResCode, resMsg.mIncludeAdditionalInfo, 739 resMsg.mAdditionalInfo, resp); 740 mCurrntCmd = null; 741 } 742 743 private boolean isStkAppInstalled() { 744 Intent intent = new Intent(AppInterface.CAT_CMD_ACTION); 745 PackageManager pm = mContext.getPackageManager(); 746 List<ResolveInfo> broadcastReceivers = 747 pm.queryBroadcastReceivers(intent, PackageManager.GET_META_DATA); 748 int numReceiver = broadcastReceivers == null ? 0 : broadcastReceivers.size(); 749 750 return (numReceiver > 0); 751 } 752} 753