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