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