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