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