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