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