CatService.java revision 8316a9897794e4b608e7917af701fafc34c9276e
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 = ComprehensionTlvTag.RESULT.value(); 389 if (cmdDet.compRequired) { 390 tag |= 0x80; 391 } 392 buf.write(tag); 393 int length = includeAdditionalInfo ? 2 : 1; 394 buf.write(length); 395 buf.write(resultCode.value()); 396 397 // additional info 398 if (includeAdditionalInfo) { 399 buf.write(additionalInfo); 400 } 401 402 // Fill optional data for each corresponding command 403 if (resp != null) { 404 resp.format(buf); 405 } else { 406 encodeOptionalTags(cmdDet, resultCode, cmdInput, buf); 407 } 408 409 byte[] rawData = buf.toByteArray(); 410 String hexString = IccUtils.bytesToHexString(rawData); 411 if (DBG) { 412 CatLog.d(this, "TERMINAL RESPONSE: " + hexString); 413 } 414 415 mCmdIf.sendTerminalResponse(hexString, null); 416 } 417 418 private void encodeOptionalTags(CommandDetails cmdDet, 419 ResultCode resultCode, Input cmdInput, ByteArrayOutputStream buf) { 420 CommandType cmdType = AppInterface.CommandType.fromInt(cmdDet.typeOfCommand); 421 if (cmdType != null) { 422 switch (cmdType) { 423 case GET_INKEY: 424 // ETSI TS 102 384,27.22.4.2.8.4.2. 425 // If it is a response for GET_INKEY command and the response timeout 426 // occured, then add DURATION TLV for variable timeout case. 427 if ((resultCode.value() == ResultCode.NO_RESPONSE_FROM_USER.value()) && 428 (cmdInput != null) && (cmdInput.duration != null)) { 429 getInKeyResponse(buf, cmdInput); 430 } 431 break; 432 case PROVIDE_LOCAL_INFORMATION: 433 if ((cmdDet.commandQualifier == CommandParamsFactory.LANGUAGE_SETTING) && 434 (resultCode.value() == ResultCode.OK.value())) { 435 getPliResponse(buf); 436 } 437 break; 438 default: 439 CatLog.d(this, "encodeOptionalTags() Unsupported Cmd details=" + cmdDet); 440 break; 441 } 442 } else { 443 CatLog.d(this, "encodeOptionalTags() bad Cmd details=" + cmdDet); 444 } 445 } 446 447 private void getInKeyResponse(ByteArrayOutputStream buf, Input cmdInput) { 448 int tag = ComprehensionTlvTag.DURATION.value(); 449 450 buf.write(tag); 451 buf.write(0x02); // length 452 buf.write(cmdInput.duration.timeUnit.SECOND.value()); // Time (Unit,Seconds) 453 buf.write(cmdInput.duration.timeInterval); // Time Duration 454 } 455 456 private void getPliResponse(ByteArrayOutputStream buf) { 457 458 // Locale Language Setting 459 String lang = SystemProperties.get("persist.sys.language"); 460 461 if (lang != null) { 462 // tag 463 int tag = ComprehensionTlvTag.LANGUAGE.value(); 464 buf.write(tag); 465 ResponseData.writeLength(buf, lang.length()); 466 buf.write(lang.getBytes(), 0, lang.length()); 467 } 468 } 469 470 private void sendMenuSelection(int menuId, boolean helpRequired) { 471 472 ByteArrayOutputStream buf = new ByteArrayOutputStream(); 473 474 // tag 475 int tag = BerTlv.BER_MENU_SELECTION_TAG; 476 buf.write(tag); 477 478 // length 479 buf.write(0x00); // place holder 480 481 // device identities 482 tag = 0x80 | ComprehensionTlvTag.DEVICE_IDENTITIES.value(); 483 buf.write(tag); 484 buf.write(0x02); // length 485 buf.write(DEV_ID_KEYPAD); // source device id 486 buf.write(DEV_ID_UICC); // destination device id 487 488 // item identifier 489 tag = 0x80 | ComprehensionTlvTag.ITEM_ID.value(); 490 buf.write(tag); 491 buf.write(0x01); // length 492 buf.write(menuId); // menu identifier chosen 493 494 // help request 495 if (helpRequired) { 496 tag = ComprehensionTlvTag.HELP_REQUEST.value(); 497 buf.write(tag); 498 buf.write(0x00); // length 499 } 500 501 byte[] rawData = buf.toByteArray(); 502 503 // write real length 504 int len = rawData.length - 2; // minus (tag + length) 505 rawData[1] = (byte) len; 506 507 String hexString = IccUtils.bytesToHexString(rawData); 508 509 mCmdIf.sendEnvelope(hexString, null); 510 } 511 512 /** 513 * Used for instantiating/updating the Service from the GsmPhone or CdmaPhone constructor. 514 * 515 * @param ci CommandsInterface object 516 * @param context phone app context 517 * @param ic Icc card 518 * @return The only Service object in the system 519 */ 520 public static CatService getInstance(CommandsInterface ci, 521 Context context, UiccCard ic) { 522 UiccCardApplication ca = null; 523 IccFileHandler fh = null; 524 IccRecords ir = null; 525 if (ic != null) { 526 /* Since Cat is not tied to any application, but rather is Uicc application 527 * in itself - just get first FileHandler and IccRecords object 528 */ 529 ca = ic.getApplicationIndex(0); 530 if (ca != null) { 531 fh = ca.getIccFileHandler(); 532 ir = ca.getIccRecords(); 533 } 534 } 535 synchronized (sInstanceLock) { 536 if (sInstance == null) { 537 if (ci == null || ca == null || ir == null || context == null || fh == null 538 || ic == null) { 539 return null; 540 } 541 HandlerThread thread = new HandlerThread("Cat Telephony service"); 542 thread.start(); 543 sInstance = new CatService(ci, ca, ir, context, fh, ic); 544 CatLog.d(sInstance, "NEW sInstance"); 545 } else if ((ir != null) && (mIccRecords != ir)) { 546 if (mIccRecords != null) { 547 mIccRecords.unregisterForRecordsLoaded(sInstance); 548 } 549 550 if (mUiccApplication != null) { 551 mUiccApplication.unregisterForReady(sInstance); 552 } 553 CatLog.d(sInstance, 554 "Reinitialize the Service with SIMRecords and UiccCardApplication"); 555 mIccRecords = ir; 556 mUiccApplication = ca; 557 558 // re-Register for SIM ready event. 559 mIccRecords.registerForRecordsLoaded(sInstance, MSG_ID_ICC_RECORDS_LOADED, null); 560 mUiccApplication.registerForReady(sInstance, MSG_ID_SIM_READY, null); 561 CatLog.d(sInstance, "sr changed reinitialize and return current sInstance"); 562 } else { 563 CatLog.d(sInstance, "Return current sInstance"); 564 } 565 return sInstance; 566 } 567 } 568 569 /** 570 * Used by application to get an AppInterface object. 571 * 572 * @return The only Service object in the system 573 */ 574 public static AppInterface getInstance() { 575 return getInstance(null, null, null); 576 } 577 578 @Override 579 public void handleMessage(Message msg) { 580 581 switch (msg.what) { 582 case MSG_ID_SESSION_END: 583 case MSG_ID_PROACTIVE_COMMAND: 584 case MSG_ID_EVENT_NOTIFY: 585 case MSG_ID_REFRESH: 586 CatLog.d(this, "ril message arrived"); 587 String data = null; 588 if (msg.obj != null) { 589 AsyncResult ar = (AsyncResult) msg.obj; 590 if (ar != null && ar.result != null) { 591 try { 592 data = (String) ar.result; 593 } catch (ClassCastException e) { 594 break; 595 } 596 } 597 } 598 mMsgDecoder.sendStartDecodingMessageParams(new RilMessage(msg.what, data)); 599 break; 600 case MSG_ID_CALL_SETUP: 601 mMsgDecoder.sendStartDecodingMessageParams(new RilMessage(msg.what, null)); 602 break; 603 case MSG_ID_ICC_RECORDS_LOADED: 604 break; 605 case MSG_ID_RIL_MSG_DECODED: 606 handleRilMsg((RilMessage) msg.obj); 607 break; 608 case MSG_ID_RESPONSE: 609 handleCmdResponse((CatResponseMessage) msg.obj); 610 break; 611 case MSG_ID_SIM_READY: 612 CatLog.d(this, "SIM ready. Reporting STK service running now..."); 613 mCmdIf.reportStkServiceIsRunning(null); 614 break; 615 default: 616 throw new AssertionError("Unrecognized CAT command: " + msg.what); 617 } 618 } 619 620 @Override 621 public synchronized void onCmdResponse(CatResponseMessage resMsg) { 622 if (resMsg == null) { 623 return; 624 } 625 // queue a response message. 626 Message msg = obtainMessage(MSG_ID_RESPONSE, resMsg); 627 msg.sendToTarget(); 628 } 629 630 private boolean validateResponse(CatResponseMessage resMsg) { 631 if (mCurrntCmd != null) { 632 return (resMsg.mCmdDet.compareTo(mCurrntCmd.mCmdDet)); 633 } 634 return false; 635 } 636 637 private boolean removeMenu(Menu menu) { 638 try { 639 if (menu.items.size() == 1 && menu.items.get(0) == null) { 640 return true; 641 } 642 } catch (NullPointerException e) { 643 CatLog.d(this, "Unable to get Menu's items size"); 644 return true; 645 } 646 return false; 647 } 648 649 private void handleCmdResponse(CatResponseMessage resMsg) { 650 // Make sure the response details match the last valid command. An invalid 651 // response is a one that doesn't have a corresponding proactive command 652 // and sending it can "confuse" the baseband/ril. 653 // One reason for out of order responses can be UI glitches. For example, 654 // if the application launch an activity, and that activity is stored 655 // by the framework inside the history stack. That activity will be 656 // available for relaunch using the latest application dialog 657 // (long press on the home button). Relaunching that activity can send 658 // the same command's result again to the CatService and can cause it to 659 // get out of sync with the SIM. 660 if (!validateResponse(resMsg)) { 661 return; 662 } 663 ResponseData resp = null; 664 boolean helpRequired = false; 665 CommandDetails cmdDet = resMsg.getCmdDetails(); 666 AppInterface.CommandType type = AppInterface.CommandType.fromInt(cmdDet.typeOfCommand); 667 668 switch (resMsg.mResCode) { 669 case HELP_INFO_REQUIRED: 670 helpRequired = true; 671 // fall through 672 case OK: 673 case PRFRMD_WITH_PARTIAL_COMPREHENSION: 674 case PRFRMD_WITH_MISSING_INFO: 675 case PRFRMD_WITH_ADDITIONAL_EFS_READ: 676 case PRFRMD_ICON_NOT_DISPLAYED: 677 case PRFRMD_MODIFIED_BY_NAA: 678 case PRFRMD_LIMITED_SERVICE: 679 case PRFRMD_WITH_MODIFICATION: 680 case PRFRMD_NAA_NOT_ACTIVE: 681 case PRFRMD_TONE_NOT_PLAYED: 682 case TERMINAL_CRNTLY_UNABLE_TO_PROCESS: 683 switch (type) { 684 case SET_UP_MENU: 685 helpRequired = resMsg.mResCode == ResultCode.HELP_INFO_REQUIRED; 686 sendMenuSelection(resMsg.mUsersMenuSelection, helpRequired); 687 return; 688 case SELECT_ITEM: 689 resp = new SelectItemResponseData(resMsg.mUsersMenuSelection); 690 break; 691 case GET_INPUT: 692 case GET_INKEY: 693 Input input = mCurrntCmd.geInput(); 694 if (!input.yesNo) { 695 // when help is requested there is no need to send the text 696 // string object. 697 if (!helpRequired) { 698 resp = new GetInkeyInputResponseData(resMsg.mUsersInput, 699 input.ucs2, input.packed); 700 } 701 } else { 702 resp = new GetInkeyInputResponseData( 703 resMsg.mUsersYesNoSelection); 704 } 705 break; 706 case DISPLAY_TEXT: 707 case LAUNCH_BROWSER: 708 break; 709 // 3GPP TS.102.223: Open Channel alpha confirmation should not send TR 710 case OPEN_CHANNEL: 711 case SET_UP_CALL: 712 mCmdIf.handleCallSetupRequestFromSim(resMsg.mUsersConfirm, null); 713 // No need to send terminal response for SET UP CALL. The user's 714 // confirmation result is send back using a dedicated ril message 715 // invoked by the CommandInterface call above. 716 mCurrntCmd = null; 717 return; 718 default: 719 break; 720 } 721 break; 722 case BACKWARD_MOVE_BY_USER: 723 case USER_NOT_ACCEPT: 724 // if the user dismissed the alert dialog for a 725 // setup call/open channel, consider that as the user 726 // rejecting the call. Use dedicated API for this, rather than 727 // sending a terminal response. 728 if (type == CommandType.SET_UP_CALL || type == CommandType.OPEN_CHANNEL) { 729 mCmdIf.handleCallSetupRequestFromSim(false, null); 730 mCurrntCmd = null; 731 return; 732 } else { 733 resp = null; 734 } 735 break; 736 case NO_RESPONSE_FROM_USER: 737 case UICC_SESSION_TERM_BY_USER: 738 resp = null; 739 break; 740 default: 741 return; 742 } 743 sendTerminalResponse(cmdDet, resMsg.mResCode, resMsg.mIncludeAdditionalInfo, 744 resMsg.mAdditionalInfo, resp); 745 mCurrntCmd = null; 746 } 747 748 private boolean isStkAppInstalled() { 749 Intent intent = new Intent(AppInterface.CAT_CMD_ACTION); 750 PackageManager pm = mContext.getPackageManager(); 751 List<ResolveInfo> broadcastReceivers = 752 pm.queryBroadcastReceivers(intent, PackageManager.GET_META_DATA); 753 int numReceiver = broadcastReceivers == null ? 0 : broadcastReceivers.size(); 754 755 return (numReceiver > 0); 756 } 757} 758