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