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