/** * This function validates the events in SETUP_EVENT_LIST which are currently supported by the * Android framework. In case of SETUP_EVENT_LIST has NULL events or no events, all the events * need to be reset. */ private boolean isSupportedSetupEventCommand(CatCmdMessage cmdMsg) { boolean flag = true; for (int eventVal : cmdMsg.getSetEventList().eventList) { CatLog.d(this, "Event: " + eventVal); switch (eventVal) { /* Currently android is supporting only the below events in SetupEventList * Idle Screen Available, * Language Selection and * HCI Connectivity. */ case IDLE_SCREEN_AVAILABLE_EVENT: case LANGUAGE_SELECTION_EVENT: case HCI_CONNECTIVITY_EVENT: break; default: flag = false; } } return flag; }
private void handleCmdResponse(CatResponseMessage resMsg) { // Make sure the response details match the last valid command. An invalid // response is a one that doesn't have a corresponding proactive command // and sending it can "confuse" the baseband/ril. // One reason for out of order responses can be UI glitches. For example, // if the application launch an activity, and that activity is stored // by the framework inside the history stack. That activity will be // available for relaunch using the latest application dialog // (long press on the home button). Relaunching that activity can send // the same command's result again to the CatService and can cause it to // get out of sync with the SIM. This can happen in case of // non-interactive type Setup Event List and SETUP_MENU proactive commands. // Stk framework would have already sent Terminal Response to Setup Event // List and SETUP_MENU proactive commands. After sometime Stk app will send // Envelope Command/Event Download. In which case, the response details doesn't // match with last valid command (which are not related). // However, we should allow Stk framework to send the message to ICC. if (!validateResponse(resMsg)) { return; } ResponseData resp = null; boolean helpRequired = false; CommandDetails cmdDet = resMsg.getCmdDetails(); AppInterface.CommandType type = AppInterface.CommandType.fromInt(cmdDet.typeOfCommand); switch (resMsg.mResCode) { case HELP_INFO_REQUIRED: helpRequired = true; // fall through case OK: case PRFRMD_WITH_PARTIAL_COMPREHENSION: case PRFRMD_WITH_MISSING_INFO: case PRFRMD_WITH_ADDITIONAL_EFS_READ: case PRFRMD_ICON_NOT_DISPLAYED: case PRFRMD_MODIFIED_BY_NAA: case PRFRMD_LIMITED_SERVICE: case PRFRMD_WITH_MODIFICATION: case PRFRMD_NAA_NOT_ACTIVE: case PRFRMD_TONE_NOT_PLAYED: case LAUNCH_BROWSER_ERROR: case TERMINAL_CRNTLY_UNABLE_TO_PROCESS: switch (type) { case SET_UP_MENU: helpRequired = resMsg.mResCode == ResultCode.HELP_INFO_REQUIRED; sendMenuSelection(resMsg.mUsersMenuSelection, helpRequired); return; case SELECT_ITEM: resp = new SelectItemResponseData(resMsg.mUsersMenuSelection); break; case GET_INPUT: case GET_INKEY: Input input = mCurrntCmd.geInput(); if (!input.yesNo) { // when help is requested there is no need to send the text // string object. if (!helpRequired) { resp = new GetInkeyInputResponseData(resMsg.mUsersInput, input.ucs2, input.packed); } } else { resp = new GetInkeyInputResponseData(resMsg.mUsersYesNoSelection); } break; case DISPLAY_TEXT: if (resMsg.mResCode == ResultCode.TERMINAL_CRNTLY_UNABLE_TO_PROCESS) { // For screenbusy case there will be addtional information in the terminal // response. And the value of the additional information byte is 0x01. resMsg.setAdditionalInfo(0x01); } else { resMsg.mIncludeAdditionalInfo = false; resMsg.mAdditionalInfo = 0; } break; case LAUNCH_BROWSER: break; // 3GPP TS.102.223: Open Channel alpha confirmation should not send TR case OPEN_CHANNEL: case SET_UP_CALL: mCmdIf.handleCallSetupRequestFromSim(resMsg.mUsersConfirm, null); // No need to send terminal response for SET UP CALL. The user's // confirmation result is send back using a dedicated ril message // invoked by the CommandInterface call above. mCurrntCmd = null; return; case SET_UP_EVENT_LIST: if (IDLE_SCREEN_AVAILABLE_EVENT == resMsg.mEventValue) { eventDownload( resMsg.mEventValue, DEV_ID_DISPLAY, DEV_ID_UICC, resMsg.mAddedInfo, false); } else { eventDownload( resMsg.mEventValue, DEV_ID_TERMINAL, DEV_ID_UICC, resMsg.mAddedInfo, false); } // No need to send the terminal response after event download. return; default: break; } break; case BACKWARD_MOVE_BY_USER: case USER_NOT_ACCEPT: // if the user dismissed the alert dialog for a // setup call/open channel, consider that as the user // rejecting the call. Use dedicated API for this, rather than // sending a terminal response. if (type == CommandType.SET_UP_CALL || type == CommandType.OPEN_CHANNEL) { mCmdIf.handleCallSetupRequestFromSim(false, null); mCurrntCmd = null; return; } else { resp = null; } break; case NO_RESPONSE_FROM_USER: // No need to send terminal response for SET UP CALL on user timeout. if (type == CommandType.SET_UP_CALL) { mCurrntCmd = null; return; } case UICC_SESSION_TERM_BY_USER: resp = null; break; default: return; } sendTerminalResponse( cmdDet, resMsg.mResCode, resMsg.mIncludeAdditionalInfo, resMsg.mAdditionalInfo, resp); mCurrntCmd = null; }
private void sendTerminalResponse( CommandDetails cmdDet, ResultCode resultCode, boolean includeAdditionalInfo, int additionalInfo, ResponseData resp) { if (cmdDet == null) { return; } ByteArrayOutputStream buf = new ByteArrayOutputStream(); Input cmdInput = null; if (mCurrntCmd != null) { cmdInput = mCurrntCmd.geInput(); } // command details int tag = ComprehensionTlvTag.COMMAND_DETAILS.value(); if (cmdDet.compRequired) { tag |= 0x80; } buf.write(tag); buf.write(0x03); // length buf.write(cmdDet.commandNumber); buf.write(cmdDet.typeOfCommand); buf.write(cmdDet.commandQualifier); // device identities // According to TS102.223/TS31.111 section 6.8 Structure of // TERMINAL RESPONSE, "For all SIMPLE-TLV objects with Min=N, // the ME should set the CR(comprehension required) flag to // comprehension not required.(CR=0)" // Since DEVICE_IDENTITIES and DURATION TLVs have Min=N, // the CR flag is not set. tag = ComprehensionTlvTag.DEVICE_IDENTITIES.value(); buf.write(tag); buf.write(0x02); // length buf.write(DEV_ID_TERMINAL); // source device id buf.write(DEV_ID_UICC); // destination device id // result tag = ComprehensionTlvTag.RESULT.value(); if (cmdDet.compRequired) { tag |= 0x80; } buf.write(tag); int length = includeAdditionalInfo ? 2 : 1; buf.write(length); buf.write(resultCode.value()); // additional info if (includeAdditionalInfo) { buf.write(additionalInfo); } // Fill optional data for each corresponding command if (resp != null) { resp.format(buf); } else { encodeOptionalTags(cmdDet, resultCode, cmdInput, buf); } byte[] rawData = buf.toByteArray(); String hexString = IccUtils.bytesToHexString(rawData); if (DBG) { CatLog.d(this, "TERMINAL RESPONSE: " + hexString); } mCmdIf.sendTerminalResponse(hexString, null); }
/** * Handles RIL_UNSOL_STK_EVENT_NOTIFY or RIL_UNSOL_STK_PROACTIVE_COMMAND command from RIL. Sends * valid proactive command data to the application using intents. * RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE will be send back if the command is from * RIL_UNSOL_STK_PROACTIVE_COMMAND. */ private void handleCommand(CommandParams cmdParams, boolean isProactiveCmd) { CatLog.d(this, cmdParams.getCommandType().name()); // Log all proactive commands. if (isProactiveCmd) { if (mUiccController != null) { mUiccController.addCardLog( "ProactiveCommand mSlotId=" + mSlotId + " cmdParams=" + cmdParams); } } CharSequence message; ResultCode resultCode; CatCmdMessage cmdMsg = new CatCmdMessage(cmdParams); switch (cmdParams.getCommandType()) { case SET_UP_MENU: if (removeMenu(cmdMsg.getMenu())) { mMenuCmd = null; } else { mMenuCmd = cmdMsg; } resultCode = cmdParams.mLoadIconFailed ? ResultCode.PRFRMD_ICON_NOT_DISPLAYED : ResultCode.OK; if (isProactiveCmd) { sendTerminalResponse(cmdParams.mCmdDet, resultCode, false, 0, null); } break; case DISPLAY_TEXT: break; case REFRESH: // Stk app service displays alpha id to user if it is present, nothing to do here CatLog.d(this, "Pass Refresh to Stk app"); break; case SET_UP_IDLE_MODE_TEXT: resultCode = cmdParams.mLoadIconFailed ? ResultCode.PRFRMD_ICON_NOT_DISPLAYED : ResultCode.OK; if (isProactiveCmd) { sendTerminalResponse(cmdParams.mCmdDet, resultCode, false, 0, null); } break; case SET_UP_EVENT_LIST: if (isProactiveCmd) { if (isSupportedSetupEventCommand(cmdMsg)) { sendTerminalResponse(cmdParams.mCmdDet, ResultCode.OK, false, 0, null); } else { sendTerminalResponse( cmdParams.mCmdDet, ResultCode.BEYOND_TERMINAL_CAPABILITY, false, 0, null); } } break; case PROVIDE_LOCAL_INFORMATION: ResponseData resp; switch (cmdParams.mCmdDet.commandQualifier) { case CommandParamsFactory.DTTZ_SETTING: resp = new DTTZResponseData(null); sendTerminalResponse(cmdParams.mCmdDet, ResultCode.OK, false, 0, resp); break; case CommandParamsFactory.LANGUAGE_SETTING: resp = new LanguageResponseData(Locale.getDefault().getLanguage()); sendTerminalResponse(cmdParams.mCmdDet, ResultCode.OK, false, 0, resp); break; default: sendTerminalResponse(cmdParams.mCmdDet, ResultCode.OK, false, 0, null); } // No need to start STK app here. return; case LAUNCH_BROWSER: if ((((LaunchBrowserParams) cmdParams).mConfirmMsg.text != null) && (((LaunchBrowserParams) cmdParams).mConfirmMsg.text.equals(STK_DEFAULT))) { message = mContext.getText(com.android.internal.R.string.launchBrowserDefault); ((LaunchBrowserParams) cmdParams).mConfirmMsg.text = message.toString(); } break; case SELECT_ITEM: case GET_INPUT: case GET_INKEY: break; case SEND_DTMF: case SEND_SMS: // Samsung STK if (cmdParams instanceof SendSMSParams) { handleProactiveCommandSendSMS((SendSMSParams) cmdParams); } // Fall through case SEND_SS: case SEND_USSD: // Samsung STK if (cmdParams instanceof SendUSSDParams) { handleProactiveCommandSendUSSD((SendUSSDParams) cmdParams); } if ((((DisplayTextParams) cmdParams).mTextMsg.text != null) && (((DisplayTextParams) cmdParams).mTextMsg.text.equals(STK_DEFAULT))) { message = mContext.getText(com.android.internal.R.string.sending); ((DisplayTextParams) cmdParams).mTextMsg.text = message.toString(); } break; case PLAY_TONE: break; case SET_UP_CALL: if ((((CallSetupParams) cmdParams).mConfirmMsg.text != null) && (((CallSetupParams) cmdParams).mConfirmMsg.text.equals(STK_DEFAULT))) { message = mContext.getText(com.android.internal.R.string.SetupCallDefault); ((CallSetupParams) cmdParams).mConfirmMsg.text = message.toString(); } break; case OPEN_CHANNEL: case CLOSE_CHANNEL: case RECEIVE_DATA: case SEND_DATA: BIPClientParams cmd = (BIPClientParams) cmdParams; /* Per 3GPP specification 102.223, * if the alpha identifier is not provided by the UICC, * the terminal MAY give information to the user * noAlphaUsrCnf defines if you need to show user confirmation or not */ boolean noAlphaUsrCnf = false; try { noAlphaUsrCnf = mContext .getResources() .getBoolean(com.android.internal.R.bool.config_stkNoAlphaUsrCnf); } catch (NotFoundException e) { noAlphaUsrCnf = false; } if ((cmd.mTextMsg.text == null) && (cmd.mHasAlphaId || noAlphaUsrCnf)) { CatLog.d(this, "cmd " + cmdParams.getCommandType() + " with null alpha id"); // If alpha length is zero, we just respond with OK. if (isProactiveCmd) { sendTerminalResponse(cmdParams.mCmdDet, ResultCode.OK, false, 0, null); } else if (cmdParams.getCommandType() == CommandType.OPEN_CHANNEL) { mCmdIf.handleCallSetupRequestFromSim(true, null); } return; } // Respond with permanent failure to avoid retry if STK app is not present. if (!mStkAppInstalled) { CatLog.d(this, "No STK application found."); if (isProactiveCmd) { sendTerminalResponse( cmdParams.mCmdDet, ResultCode.BEYOND_TERMINAL_CAPABILITY, false, 0, null); return; } } /* * CLOSE_CHANNEL, RECEIVE_DATA and SEND_DATA can be delivered by * either PROACTIVE_COMMAND or EVENT_NOTIFY. * If PROACTIVE_COMMAND is used for those commands, send terminal * response here. */ if (isProactiveCmd && ((cmdParams.getCommandType() == CommandType.CLOSE_CHANNEL) || (cmdParams.getCommandType() == CommandType.RECEIVE_DATA) || (cmdParams.getCommandType() == CommandType.SEND_DATA))) { sendTerminalResponse(cmdParams.mCmdDet, ResultCode.OK, false, 0, null); } break; case ACTIVATE: // TO DO: Retrieve the target of the ACTIVATE cmd from the cmd. // Target : '01' = UICC-CFL interface according to TS 102 613 [39]; // '00' and '02' to 'FF' = RFU (Reserved for Future Use). resultCode = ResultCode.OK; sendTerminalResponse(cmdParams.mCmdDet, resultCode, false, 0, null); break; default: CatLog.d(this, "Unsupported command"); return; } mCurrntCmd = cmdMsg; broadcastCatCmdIntent(cmdMsg); }
private void handleCmdResponse(CatResponseMessage resMsg) { // Make sure the response details match the last valid command. An invalid // response is a one that doesn't have a corresponding proactive command // and sending it can "confuse" the baseband/ril. // One reason for out of order responses can be UI glitches. For example, // if the application launch an activity, and that activity is stored // by the framework inside the history stack. That activity will be // available for relaunch using the latest application dialog // (long press on the home button). Relaunching that activity can send // the same command's result again to the CatService and can cause it to // get out of sync with the SIM. if (!validateResponse(resMsg)) { return; } ResponseData resp = null; boolean helpRequired = false; CommandDetails cmdDet = resMsg.getCmdDetails(); AppInterface.CommandType type = AppInterface.CommandType.fromInt(cmdDet.typeOfCommand); switch (resMsg.resCode) { case HELP_INFO_REQUIRED: helpRequired = true; // fall through case OK: case PRFRMD_WITH_PARTIAL_COMPREHENSION: case PRFRMD_WITH_MISSING_INFO: case PRFRMD_WITH_ADDITIONAL_EFS_READ: case PRFRMD_ICON_NOT_DISPLAYED: case PRFRMD_MODIFIED_BY_NAA: case PRFRMD_LIMITED_SERVICE: case PRFRMD_WITH_MODIFICATION: case PRFRMD_NAA_NOT_ACTIVE: case PRFRMD_TONE_NOT_PLAYED: case TERMINAL_CRNTLY_UNABLE_TO_PROCESS: switch (type) { case SET_UP_MENU: helpRequired = resMsg.resCode == ResultCode.HELP_INFO_REQUIRED; sendMenuSelection(resMsg.usersMenuSelection, helpRequired); return; case SELECT_ITEM: resp = new SelectItemResponseData(resMsg.usersMenuSelection); break; case GET_INPUT: case GET_INKEY: Input input = mCurrntCmd.geInput(); if (!input.yesNo) { // when help is requested there is no need to send the text // string object. if (!helpRequired) { resp = new GetInkeyInputResponseData(resMsg.usersInput, input.ucs2, input.packed); } } else { resp = new GetInkeyInputResponseData(resMsg.usersYesNoSelection); } break; case DISPLAY_TEXT: case LAUNCH_BROWSER: break; case SET_UP_CALL: mCmdIf.handleCallSetupRequestFromSim(resMsg.usersConfirm, null); // No need to send terminal response for SET UP CALL. The user's // confirmation result is send back using a dedicated ril message // invoked by the CommandInterface call above. mCurrntCmd = null; return; } break; case BACKWARD_MOVE_BY_USER: case USER_NOT_ACCEPT: // if the user dismissed the alert dialog for a // setup call/open channel, consider that as the user // rejecting the call. Use dedicated API for this, rather than // sending a terminal response. if (type == CommandType.SET_UP_CALL || type == CommandType.OPEN_CHANNEL) { mCmdIf.handleCallSetupRequestFromSim(false, null); mCurrntCmd = null; return; } else { resp = null; } break; case NO_RESPONSE_FROM_USER: case UICC_SESSION_TERM_BY_USER: resp = null; break; default: return; } sendTerminalResponse( cmdDet, resMsg.resCode, resMsg.includeAdditionalInfo, resMsg.additionalInfo, resp); mCurrntCmd = null; }
/** * Handles RIL_UNSOL_STK_EVENT_NOTIFY or RIL_UNSOL_STK_PROACTIVE_COMMAND command from RIL. Sends * valid proactive command data to the application using intents. * RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE will be send back if the command is from * RIL_UNSOL_STK_PROACTIVE_COMMAND. */ private void handleCommand(CommandParams cmdParams, boolean isProactiveCmd) { CatLog.d(this, cmdParams.getCommandType().name()); CharSequence message; CatCmdMessage cmdMsg = new CatCmdMessage(cmdParams); switch (cmdParams.getCommandType()) { case SET_UP_MENU: if (removeMenu(cmdMsg.getMenu())) { mMenuCmd = null; } else { mMenuCmd = cmdMsg; } sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null); break; case DISPLAY_TEXT: // when application is not required to respond, send an immediate response. if (!cmdMsg.geTextMessage().responseNeeded) { sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null); } break; case REFRESH: // ME side only handles refresh commands which meant to remove IDLE // MODE TEXT. cmdParams.cmdDet.typeOfCommand = CommandType.SET_UP_IDLE_MODE_TEXT.value(); break; case SET_UP_IDLE_MODE_TEXT: sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null); break; case PROVIDE_LOCAL_INFORMATION: ResponseData resp; switch (cmdParams.cmdDet.commandQualifier) { case CommandParamsFactory.DTTZ_SETTING: resp = new DTTZResponseData(null); sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, resp); break; case CommandParamsFactory.LANGUAGE_SETTING: resp = new LanguageResponseData(Locale.getDefault().getLanguage()); sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, resp); break; default: sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null); } // No need to start STK app here. return; case LAUNCH_BROWSER: if ((((LaunchBrowserParams) cmdParams).confirmMsg.text != null) && (((LaunchBrowserParams) cmdParams).confirmMsg.text.equals(STK_DEFAULT))) { message = mContext.getText(com.android.internal.R.string.launchBrowserDefault); ((LaunchBrowserParams) cmdParams).confirmMsg.text = message.toString(); } break; case SELECT_ITEM: case GET_INPUT: case GET_INKEY: break; case SEND_DTMF: case SEND_SMS: case SEND_SS: case SEND_USSD: if ((((DisplayTextParams) cmdParams).textMsg.text != null) && (((DisplayTextParams) cmdParams).textMsg.text.equals(STK_DEFAULT))) { message = mContext.getText(com.android.internal.R.string.sending); ((DisplayTextParams) cmdParams).textMsg.text = message.toString(); } break; case PLAY_TONE: break; case SET_UP_CALL: if ((((CallSetupParams) cmdParams).confirmMsg.text != null) && (((CallSetupParams) cmdParams).confirmMsg.text.equals(STK_DEFAULT))) { message = mContext.getText(com.android.internal.R.string.SetupCallDefault); ((CallSetupParams) cmdParams).confirmMsg.text = message.toString(); } break; case OPEN_CHANNEL: case CLOSE_CHANNEL: case RECEIVE_DATA: case SEND_DATA: BIPClientParams cmd = (BIPClientParams) cmdParams; /* * If the text mesg is null, need to send the response * back to the card in the following scenarios * - It has alpha ID tag with no Text Msg (or) * - If alphaUsrCnf is not set. In the above cases * there should be no UI indication given to the user. */ boolean alphaUsrCnf = SystemProperties.getBoolean(TelephonyProperties.PROPERTY_ALPHA_USRCNF, false); CatLog.d(this, "alphaUsrCnf: " + alphaUsrCnf + ", bHasAlphaId: " + cmd.bHasAlphaId); if ((cmd.textMsg.text == null) && (cmd.bHasAlphaId || !alphaUsrCnf)) { CatLog.d(this, "cmd " + cmdParams.getCommandType() + " with null alpha id"); // If alpha length is zero, we just respond with OK. if (isProactiveCmd) { sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null); } else if (cmdParams.getCommandType() == CommandType.OPEN_CHANNEL) { mCmdIf.handleCallSetupRequestFromSim(true, null); } return; } // Respond with permanent failure to avoid retry if STK app is not present. if (!mStkAppInstalled) { CatLog.d(this, "No STK application found."); if (isProactiveCmd) { sendTerminalResponse( cmdParams.cmdDet, ResultCode.BEYOND_TERMINAL_CAPABILITY, false, 0, null); return; } } /* * CLOSE_CHANNEL, RECEIVE_DATA and SEND_DATA can be delivered by * either PROACTIVE_COMMAND or EVENT_NOTIFY. * If PROACTIVE_COMMAND is used for those commands, send terminal * response here. */ if (isProactiveCmd && ((cmdParams.getCommandType() == CommandType.CLOSE_CHANNEL) || (cmdParams.getCommandType() == CommandType.RECEIVE_DATA) || (cmdParams.getCommandType() == CommandType.SEND_DATA))) { sendTerminalResponse(cmdParams.cmdDet, ResultCode.OK, false, 0, null); } break; default: CatLog.d(this, "Unsupported command"); return; } mCurrntCmd = cmdMsg; Intent intent = new Intent(AppInterface.CAT_CMD_ACTION); intent.putExtra("STK CMD", cmdMsg); mContext.sendBroadcast(intent); }