CatService主要负责STK菜单的相关事宜,本节我们就来分析该对象。
@UiccCard.java public void update(Context c, CommandsInterface ci, IccCardStatus ics) { synchronized (mLock) { if (mUiccApplications.length > 0 && mUiccApplications[0] != null) { //创建CatService mCatService = CatService.getInstance(mCi, mContext, this); } else { if (mCatService != null) { mCatService.dispose(); } mCatService = null; } } }然后看具体的初始化流程:
@CatService.java public static CatService getInstance(CommandsInterface ci, Context context, UiccCard ic) { UiccCardApplication ca = null; IccFileHandler fh = null; IccRecords ir = null; if (ic != null) { //获取UiccCardApplication、IccFileHandler、IccRecords等对象 ca = ic.getApplicationIndex(0); if (ca != null) { fh = ca.getIccFileHandler(); ir = ca.getIccRecords(); } } synchronized (sInstanceLock) { if (sInstance == null) { if (ci == null || ca == null || ir == null || context == null || fh == null || ic == null) { return null; } //创建CatService的消息处理线程 HandlerThread thread = new HandlerThread("Cat Telephony service"); thread.start(); //创建CatService实例对象 sInstance = new CatService(ci, ca, ir, context, fh, ic); } else if ((ir != null) && (mIccRecords != ir)) { //CatService已经被创建过,只需要更新其监听器 if (mIccRecords != null) { mIccRecords.unregisterForRecordsLoaded(sInstance); } if (mUiccApplication != null) { mUiccApplication.unregisterForReady(sInstance); } mIccRecords = ir; mUiccApplication = ca; mIccRecords.registerForRecordsLoaded(sInstance, MSG_ID_ICC_RECORDS_LOADED, null); mUiccApplication.registerForReady(sInstance, MSG_ID_SIM_READY, null); } else { CatLog.d(sInstance, "Return current sInstance"); } return sInstance; } }看他的构造函数:
private CatService(CommandsInterface ci, UiccCardApplication ca, IccRecords ir, Context context, IccFileHandler fh, UiccCard ic) { if (ci == null || ca == null || ir == null || context == null || fh == null || ic == null) { throw new NullPointerException( "Service: Input parameters must not be null"); } mCmdIf = ci; mContext = context; //获取RilMessageDecoder对象 mMsgDecoder = RilMessageDecoder.getInstance(this, fh); //向RIL注册监听器 mCmdIf.setOnCatSessionEnd(this, MSG_ID_SESSION_END, null); mCmdIf.setOnCatProactiveCmd(this, MSG_ID_PROACTIVE_COMMAND, null); mCmdIf.setOnCatEvent(this, MSG_ID_EVENT_NOTIFY, null); mCmdIf.setOnCatCallSetUp(this, MSG_ID_CALL_SETUP, null); mIccRecords = ir; mUiccApplication = ca; //向UiccCardApplication对象注册SIM Ready的监听器 //向IccRecords对象注册Icc Record Loaded的监听器 mUiccApplication.registerForReady(this, MSG_ID_SIM_READY, null); mIccRecords.registerForRecordsLoaded(this, MSG_ID_ICC_RECORDS_LOADED, null); mStkAppInstalled = isStkAppInstalled(); }我们看到在CatService的初始化过程中主要完成了一下三个任务:
3、向UiccCardApplication、IccRecords注册SIM卡和Record的监听;
下面我们主要介绍以上四个消息的处理流程。
public void handleMessage(Message msg) { switch (msg.what) { case MSG_ID_SIM_READY: //向RIL发送消息 mCmdIf.reportStkServiceIsRunning(null); break; default: throw new AssertionError("Unrecognized CAT command: " + msg.what); } }
此时的RIL将会向Modem发送RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING的消息,从而告诉Modem,CatService已经准备好接受STK的数据了。
public void handleMessage(Message msg) { switch (msg.what) { case MSG_ID_SESSION_END: case MSG_ID_PROACTIVE_COMMAND: case MSG_ID_EVENT_NOTIFY: case MSG_ID_REFRESH: String data = null; if (msg.obj != null) { AsyncResult ar = (AsyncResult) msg.obj; if (ar != null && ar.result != null) { try { data = (String) ar.result; } catch (ClassCastException e) { break; } } } //通过RilMessageDecoder去解析数据 mMsgDecoder.sendStartDecodingMessageParams(new RilMessage(msg.what, data)); break; default: throw new AssertionError("Unrecognized CAT command: " + msg.what); } }我们看到,对于拿到的数据时通过mMsgDecoder去解析的,也就是RilMessageDecoder的对象,这个对象是在CatService的构造函数中被创建的。我们来看具体的解析数据的过程:
@RilMessageDecoder.java public void sendStartDecodingMessageParams(RilMessage rilMsg) { Message msg = obtainMessage(CMD_START); msg.obj = rilMsg; sendMessage(msg); }在RilMessageDecoder中又将消息封装后发送给自己,并在StateStart中被处理:
private class StateStart extends State { @Override public boolean processMessage(Message msg) { if (msg.what == CMD_START) { if (decodeMessageParams((RilMessage)msg.obj)) { transitionTo(mStateCmdParamsReady); } } else { CatLog.d(this, "StateStart unexpected expecting START=" + CMD_START + " got " + msg.what); } return true; } }然后在decodeMessageParams()方法中被解析:
private boolean decodeMessageParams(RilMessage rilMsg) { boolean decodingStarted; mCurrentRilMessage = rilMsg; switch(rilMsg.mId) { case CatService.MSG_ID_PROACTIVE_COMMAND: case CatService.MSG_ID_EVENT_NOTIFY: case CatService.MSG_ID_REFRESH: byte[] rawData = null; try { rawData = IccUtils.hexStringToBytes((String) rilMsg.mData); } catch (Exception e) { } try { // Start asynch parsing of the command parameters. mCmdParamsFactory.make(BerTlv.decode(rawData)); decodingStarted = true; } catch (ResultException e) { } break; default: decodingStarted = false; break; } return decodingStarted; }在decodeMessageParams()中先通过hexStringToBytes()方法将得到的Modem数据转换为byte类型的数组数据rawData,然后将此数据交给CommandParamsFactory类的make()方法去解析,经过make的解析就可以得到每项STK菜单的图标、菜单文本以及需要的控件等信息,其解析过程为:
@CommandParamsFactory.java void make(BerTlv berTlv) { mCmdParams = null; mIconLoadState = LOAD_NO_ICON; boolean cmdPending = false; List<ComprehensionTlv> ctlvs = berTlv.getComprehensionTlvs(); CommandDetails cmdDet = processCommandDetails(ctlvs); //得到当前的命令类型 AppInterface.CommandType cmdType = AppInterface.CommandType .fromInt(cmdDet.typeOfCommand); try { switch (cmdType) { case SET_UP_MENU: cmdPending = processSelectItem(cmdDet, ctlvs); break; case SELECT_ITEM: cmdPending = processSelectItem(cmdDet, ctlvs); break; case DISPLAY_TEXT: cmdPending = processDisplayText(cmdDet, ctlvs); break; case SET_UP_IDLE_MODE_TEXT: cmdPending = processSetUpIdleModeText(cmdDet, ctlvs); break; case GET_INKEY: cmdPending = processGetInkey(cmdDet, ctlvs); break; case GET_INPUT: cmdPending = processGetInput(cmdDet, ctlvs); break; case SEND_DTMF: case SEND_SMS: case SEND_SS: case SEND_USSD: cmdPending = processEventNotify(cmdDet, ctlvs); break; case GET_CHANNEL_STATUS: case SET_UP_CALL: cmdPending = processSetupCall(cmdDet, ctlvs); break; case REFRESH: processRefresh(cmdDet, ctlvs); cmdPending = false; break; case LAUNCH_BROWSER: cmdPending = processLaunchBrowser(cmdDet, ctlvs); break; case PLAY_TONE: cmdPending = processPlayTone(cmdDet, ctlvs); break; case PROVIDE_LOCAL_INFORMATION: cmdPending = processProvideLocalInfo(cmdDet, ctlvs); break; case OPEN_CHANNEL: case CLOSE_CHANNEL: case RECEIVE_DATA: case SEND_DATA: cmdPending = processBIPClient(cmdDet, ctlvs); break; default: // unsupported proactive commands mCmdParams = new CommandParams(cmdDet); sendCmdParams(ResultCode.BEYOND_TERMINAL_CAPABILITY); return; } } catch (ResultException e) { } if (!cmdPending) { sendCmdParams(ResultCode.OK); } }从make()方法看出,解析的主要类型分为以下几类:
我们分别来看以上几个事件的处理流程:
private boolean processSelectItem(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs) throws ResultException { Menu menu = new Menu(); IconId titleIconId = null; ItemsIconId itemsIconId = null; Iterator<ComprehensionTlv> iter = ctlvs.iterator(); //搜索并解析该STK应用的名字,比如“神州行天地” ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.ALPHA_ID, ctlvs); if (ctlv != null) { menu.title = ValueParser.retrieveAlphaId(ctlv); } //循环解析该STK应用的所有根目录菜单 //比如对于神州行的卡,就会有“轻松问候”、“短信群发”、“优惠快讯”、“业务精选”等菜单 while (true) { ctlv = searchForNextTag(ComprehensionTlvTag.ITEM, iter); if (ctlv != null) { menu.items.add(ValueParser.retrieveItem(ctlv)); } else { break; } } if (menu.items.size() == 0) { throw new ResultException(ResultCode.REQUIRED_VALUES_MISSING); } //搜索ITEM_ID ctlv = searchForTag(ComprehensionTlvTag.ITEM_ID, ctlvs); if (ctlv != null) { menu.defaultItem = ValueParser.retrieveItemId(ctlv) - 1; } //搜索ICON_ID ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs); if (ctlv != null) { mIconLoadState = LOAD_SINGLE_ICON; titleIconId = ValueParser.retrieveIconId(ctlv); menu.titleIconSelfExplanatory = titleIconId.selfExplanatory; } ctlv = searchForTag(ComprehensionTlvTag.ITEM_ICON_ID_LIST, ctlvs); if (ctlv != null) { mIconLoadState = LOAD_MULTI_ICONS; itemsIconId = ValueParser.retrieveItemsIconId(ctlv); menu.itemsIconSelfExplanatory = itemsIconId.selfExplanatory; } boolean presentTypeSpecified = (cmdDet.commandQualifier & 0x01) != 0; if (presentTypeSpecified) { if ((cmdDet.commandQualifier & 0x02) == 0) { menu.presentationType = PresentationType.DATA_VALUES; } else { menu.presentationType = PresentationType.NAVIGATION_OPTIONS; } } menu.softKeyPreferred = (cmdDet.commandQualifier & 0x04) != 0; menu.helpAvailable = (cmdDet.commandQualifier & 0x80) != 0; //用解析的菜单项构建mCmdParams对象 mCmdParams = new SelectItemParams(cmdDet, menu, titleIconId != null); //处理图标的载入 switch(mIconLoadState) { case LOAD_NO_ICON: return false; case LOAD_SINGLE_ICON: mIconLoader.loadIcon(titleIconId.recordNumber, this .obtainMessage(MSG_ID_LOAD_ICON_DONE)); break; case LOAD_MULTI_ICONS: int[] recordNumbers = itemsIconId.recordNumbers; if (titleIconId != null) { // Create a new array for all the icons (title and items). recordNumbers = new int[itemsIconId.recordNumbers.length + 1]; recordNumbers[0] = titleIconId.recordNumber; System.arraycopy(itemsIconId.recordNumbers, 0, recordNumbers, 1, itemsIconId.recordNumbers.length); } mIconLoader.loadIcons(recordNumbers, this .obtainMessage(MSG_ID_LOAD_ICON_DONE)); break; } return true; }
在上面这个过程中,processSelectItem()会对STK的菜单项的文本、ID、图标等进行解析,最终把解析完的数据放入mCmdParams中,并通过CatService把数据发送给StkAppService并显示相应的菜单项。
SELECT_ITEM的事件也是通过processSelectItem()来处理的,他的主要作用就是当用户点击某个STK菜单时,对下一级菜单进行解析,并在StkAppService中进行显示。
private boolean processDisplayText(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs) throws ResultException { TextMessage textMsg = new TextMessage(); IconId iconId = null; //得到显示的文本,比如开机后的运营商欢迎语 ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.TEXT_STRING, ctlvs); if (ctlv != null) { textMsg.text = ValueParser.retrieveTextString(ctlv); } if (textMsg.text == null) { throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD); } ctlv = searchForTag(ComprehensionTlvTag.IMMEDIATE_RESPONSE, ctlvs); if (ctlv != null) { textMsg.responseNeeded = false; } //图标 ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs); if (ctlv != null) { iconId = ValueParser.retrieveIconId(ctlv); textMsg.iconSelfExplanatory = iconId.selfExplanatory; } //显示的持续时间 ctlv = searchForTag(ComprehensionTlvTag.DURATION, ctlvs); if (ctlv != null) { textMsg.duration = ValueParser.retrieveDuration(ctlv); } textMsg.isHighPriority = (cmdDet.commandQualifier & 0x01) != 0; textMsg.userClear = (cmdDet.commandQualifier & 0x80) != 0; //构建mCmdParams mCmdParams = new DisplayTextParams(cmdDet, textMsg); if (iconId != null) { mIconLoadState = LOAD_SINGLE_ICON; mIconLoader.loadIcon(iconId.recordNumber, this .obtainMessage(MSG_ID_LOAD_ICON_DONE)); return true; } return false; }
我们看到,在processDisplayText()的过程中,主要还是对要显示数据进行解析,得到显示的文本和图标,最终将解析结果放入mCmdParams中并发送给StkAppService。
private boolean processGetInput(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs) throws ResultException { Input input = new Input(); IconId iconId = null; //要显示的提示字串,比如“输入内容” ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.TEXT_STRING, ctlvs); if (ctlv != null) { input.text = ValueParser.retrieveTextString(ctlv); } else { throw new ResultException(ResultCode.REQUIRED_VALUES_MISSING); } //得到输入框的输入最大长度 ctlv = searchForTag(ComprehensionTlvTag.RESPONSE_LENGTH, ctlvs); if (ctlv != null) { try { byte[] rawValue = ctlv.getRawValue(); int valueIndex = ctlv.getValueIndex(); input.minLen = rawValue[valueIndex] & 0xff; input.maxLen = rawValue[valueIndex + 1] & 0xff; } catch (IndexOutOfBoundsException e) { throw new ResultException(ResultCode.CMD_DATA_NOT_UNDERSTOOD); } } else { throw new ResultException(ResultCode.REQUIRED_VALUES_MISSING); } ctlv = searchForTag(ComprehensionTlvTag.DEFAULT_TEXT, ctlvs); if (ctlv != null) { input.defaultText = ValueParser.retrieveTextString(ctlv); } // parse icon identifier ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs); if (ctlv != null) { iconId = ValueParser.retrieveIconId(ctlv); } input.digitOnly = (cmdDet.commandQualifier & 0x01) == 0; input.ucs2 = (cmdDet.commandQualifier & 0x02) != 0; input.echo = (cmdDet.commandQualifier & 0x04) == 0; input.packed = (cmdDet.commandQualifier & 0x08) != 0; input.helpAvailable = (cmdDet.commandQualifier & 0x80) != 0; mCmdParams = new GetInputParams(cmdDet, input); if (iconId != null) { mIconLoadState = LOAD_SINGLE_ICON; mIconLoader.loadIcon(iconId.recordNumber, this .obtainMessage(MSG_ID_LOAD_ICON_DONE)); return true; } return false; }
和上面两个消息类似,最终会把解析得到的编辑框信息发送给StkAppService来显示。
private boolean processEventNotify(CommandDetails cmdDet, List<ComprehensionTlv> ctlvs) throws ResultException { TextMessage textMsg = new TextMessage(); IconId iconId = null; //发送短信时的提示语,比如“正在发送消息…” ComprehensionTlv ctlv = searchForTag(ComprehensionTlvTag.ALPHA_ID, ctlvs); textMsg.text = ValueParser.retrieveAlphaId(ctlv); ctlv = searchForTag(ComprehensionTlvTag.ICON_ID, ctlvs); if (ctlv != null) { iconId = ValueParser.retrieveIconId(ctlv); textMsg.iconSelfExplanatory = iconId.selfExplanatory; } textMsg.responseNeeded = false; mCmdParams = new DisplayTextParams(cmdDet, textMsg); if (iconId != null) { mIconLoadState = LOAD_SINGLE_ICON; mIconLoader.loadIcon(iconId.recordNumber, this .obtainMessage(MSG_ID_LOAD_ICON_DONE)); return true; } return false; }
和上面的消息类似,最终会把解析得到的信息发送给StkAppService来显示。
void make(BerTlv berTlv) { try { switch (cmdType) { case SET_UP_MENU: case SELECT_ITEM: case DISPLAY_TEXT: case SET_UP_IDLE_MODE_TEXT: case GET_INKEY: case GET_INPUT: case SEND_DTMF: case SEND_SMS: case SEND_SS: case SEND_USSD: case GET_CHANNEL_STATUS: case SET_UP_CALL: case REFRESH: case LAUNCH_BROWSER: case PLAY_TONE: case PROVIDE_LOCAL_INFORMATION: case OPEN_CHANNEL: case CLOSE_CHANNEL: case RECEIVE_DATA: case SEND_DATA: default: } } catch (ResultException e) { } if (!cmdPending) { //将解析的mCmdParams发送给RilMessageDecoder sendCmdParams(ResultCode.OK); } } private void sendCmdParams(ResultCode resCode) { mCaller.sendMsgParamsDecoded(resCode, mCmdParams); }这里的mCaller就是CommandParamsFactory初始化时传递进来的RilMessageDecoder:
@RilMessageDecoder.java public void sendMsgParamsDecoded(ResultCode resCode, CommandParams cmdParams) { Message msg = obtainMessage(RilMessageDecoder.CMD_PARAMS_READY); msg.arg1 = resCode.value(); msg.obj = cmdParams; sendMessage(msg); }至此,CommandParamsFactory的任务已经完成, 其主要作用体现在:1、解析SIM卡上报的STK数据;2、将解析后的数据发送给RilMessageDecoder。
private class StateCmdParamsReady extends State { @Override public boolean processMessage(Message msg) { if (msg.what == CMD_PARAMS_READY) { //接收到消息 mCurrentRilMessage.mResCode = ResultCode.fromInt(msg.arg1); mCurrentRilMessage.mData = msg.obj; sendCmdForExecution(mCurrentRilMessage); transitionTo(mStateStart); } else { CatLog.d(this, "StateCmdParamsReady expecting CMD_PARAMS_READY=" + CMD_PARAMS_READY + " got " + msg.what); deferMessage(msg); } return true; } }然后通过sendCmdForExecution()把解析结果重新发送给CatService:
private void sendCmdForExecution(RilMessage rilMsg) { Message msg = mCaller.obtainMessage(CatService.MSG_ID_RIL_MSG_DECODED, new RilMessage(rilMsg)); msg.sendToTarget(); }
然后在CatService中将会收到MSG_ID_RIL_MSG_DECODED的消息。
@CatService.java public void handleMessage(Message msg) { switch (msg.what) { case MSG_ID_RIL_MSG_DECODED: handleRilMsg((RilMessage) msg.obj); break; default: throw new AssertionError("Unrecognized CAT command: " + msg.what); } }接着往下看:
private void handleRilMsg(RilMessage rilMsg) { CommandParams cmdParams = null; switch (rilMsg.mId) { case MSG_ID_PROACTIVE_COMMAND: try { //拿到解析后的数据 cmdParams = (CommandParams) rilMsg.mData; } catch (ClassCastException e) { } if (cmdParams != null) { if (rilMsg.mResCode == ResultCode.OK) { //处理当前的数据 handleCommand(cmdParams, true); } else { sendTerminalResponse(cmdParams.mCmdDet, rilMsg.mResCode, false, 0, null); } } break; } } private void handleCommand(CommandParams cmdParams, boolean isProactiveCmd) { 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.mCmdDet, ResultCode.OK, false, 0, null); break; case DISPLAY_TEXT: if (!cmdMsg.geTextMessage().responseNeeded) { sendTerminalResponse(cmdParams.mCmdDet, ResultCode.OK, false, 0, null); } 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).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; 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); }
我们看到,在handleCommand()中,对于主要的几种数据类型,比如SET_UP_MENU、DISPLAY_TEXT的最后都有sendTerminalResponse()的操作,而这个操作的作用就是将数据封装后发送给RIL。并且在handleCommand()的最后,通过广播的形式将当前解析的结果发送出来,发送之后就有STK应用的StkAppService.java负责接收,并构建STK菜单。
@CatService.java public void handleMessage(Message msg) { switch (msg.what) { case MSG_ID_SESSION_END: case MSG_ID_PROACTIVE_COMMAND: case MSG_ID_EVENT_NOTIFY: case MSG_ID_REFRESH: CatLog.d(this, "ril message arrived"); String data = null; if (msg.obj != null) { AsyncResult ar = (AsyncResult) msg.obj; if (ar != null && ar.result != null) { try { data = (String) ar.result; } catch (ClassCastException e) { break; } } } //让RilMessageDecoder去处理 mMsgDecoder.sendStartDecodingMessageParams(new RilMessage(msg.what, data)); break; } }然后就交给RilMessageDecoder中的decodeMessageParams()处理:
private boolean decodeMessageParams(RilMessage rilMsg) { boolean decodingStarted; mCurrentRilMessage = rilMsg; switch(rilMsg.mId) { case CatService.MSG_ID_SESSION_END: case CatService.MSG_ID_CALL_SETUP: mCurrentRilMessage.mResCode = ResultCode.OK; sendCmdForExecution(mCurrentRilMessage); decodingStarted = false; break; default: decodingStarted = false; break; } return decodingStarted; }这里和MSG_ID_PROACTIVE_COMMAND的区别就是,不再进入CommandParamsFactory中解析数据,而是直接通过sendCmdForExecution()将OK的ResultCode发回给CatService,并且带回的返回码也是MSG_ID_RIL_MSG_DECODED消息。
接下来的流程和MSG_ID_PROACTIVE_COMMAND相同,就是将结果发送给RIL和StkAppService.java。
现在,我们可以来简要分析CatService的作用了。
从以上的分析中得知, CatService的作用主要体现在接收并解析RIL层发来的STK相关原始数据,并把解析后的数据同时传回给RIL和发送给StkAppService。但是我们发现,CatService虽然对数据进行了解析,但是并没有显示菜单、弹出提示框的动作,而真正将数据转化为控件是在StkAppService中完成的,这是一个上层的app,主要作用就是拿到CatService解析的数据并进行显示,以及处理用户在相应菜单上进行的点击操作。
在前面的2.3中我们知道,当CatService完成RIL上报的数据解析后,就会收到MSG_ID_RIL_MSG_DECODED消息,然后CatService就会将解析的结果发送给RIL,同时发送Intent出来:
Intent intent = new Intent(AppInterface.CAT_CMD_ACTION); intent.putExtra("STK CMD", cmdMsg); mContext.sendBroadcast(intent);这里发送的Intent,就会被STK模块的StkCmdReceiver接收到,而且该Intent中的"STK CMD"中存放的就是当前的解析结果。
@StkCmdReceiver.java public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (action.equals(AppInterface.CAT_CMD_ACTION)) { handleCommandMessage(context, intent); } else if (action.equals(AppInterface.CAT_SESSION_END_ACTION)) { handleSessionEnd(context, intent); } }然后就会在handleCommandMessage()中启动StkAppService:
private void handleCommandMessage(Context context, Intent intent) { Bundle args = new Bundle(); args.putInt(StkAppService.OPCODE, StkAppService.OP_CMD); args.putParcelable(StkAppService.CMD_MSG, intent .getParcelableExtra("STK CMD")); context.startService(new Intent(context, StkAppService.class) .putExtras(args)); }请注意,在startService时,args里面放了两个数据:1、StkAppService.OPCODE里面存的是StkAppService.OP_CMD;2、StkAppService.CMD_MSG里面存的是CatService解析获取的数据,比如是SET_UP_MENU还是DISPLAY_TEXT等。
@StkAppService.java public void onStart(Intent intent, int startId) { mStkService = com.android.internal.telephony.cat.CatService .getInstance(); if (mStkService == null) { } Bundle args = intent.getExtras(); if (args == null) { return; } Message msg = mServiceHandler.obtainMessage(); msg.arg1 = args.getInt(OPCODE); switch(msg.arg1) { case OP_CMD: msg.obj = args.getParcelable(CMD_MSG); break; case OP_RESPONSE: msg.obj = args; case OP_LAUNCH_APP: case OP_END_SESSION: case OP_BOOT_COMPLETED: break; default: return; } mServiceHandler.sendMessage(msg); }然后在ServiceHandler中处理:
private final class ServiceHandler extends Handler { @Override public void handleMessage(Message msg) { int opcode = msg.arg1; switch (opcode) { case OP_LAUNCH_APP: break; case OP_CMD: CatCmdMessage cmdMsg = (CatCmdMessage) msg.obj; if (!isCmdInteractive(cmdMsg)) { handleCmd(cmdMsg); } else { if (!mCmdInProgress) { mCmdInProgress = true; handleCmd((CatCmdMessage) msg.obj); } else { mCmdsQ.addLast(new DelayedCmd(OP_CMD, (CatCmdMessage) msg.obj)); } } break; case OP_RESPONSE: break; case OP_END_SESSION: break; case OP_BOOT_COMPLETED: break; case OP_DELAYED_MSG: break; } } }由于当前的opcode是OP_CMD,那么在该case中进行其他的处理,这里会根据isCmdInteractive()来判断当前的请求,不过对于SET_UP_MENU、DISPLAY_TEXT等消息,最终还是要通过handleCmd()去处理:
private void handleCmd(CatCmdMessage cmdMsg) { if (cmdMsg == null) { return; } switch (cmdMsg.getCmdType()) { case DISPLAY_TEXT: //弹出提示框 TextMessage msg = cmdMsg.geTextMessage(); responseNeeded = msg.responseNeeded; waitForUsersResponse = msg.responseNeeded; if (lastSelectedItem != null) { msg.title = lastSelectedItem; } else if (mMainCmd != null){ msg.title = mMainCmd.getMenu().title; } else { msg.title = ""; } launchTextDialog(); break; case SELECT_ITEM: //载入下一级菜单 mCurrentMenu = cmdMsg.getMenu(); launchMenuActivity(cmdMsg.getMenu()); break; case SET_UP_MENU: //显示列表 mMainCmd = mCurrentCmd; mCurrentMenu = cmdMsg.getMenu(); if (removeMenu()) { CatLog.d(this, "Uninstall App"); mCurrentMenu = null; StkAppInstaller.unInstall(mContext); } else { CatLog.d(this, "Install App"); StkAppInstaller.install(mContext); } if (mMenuIsVisibile) { launchMenuActivity(null); } break; case GET_INPUT: case GET_INKEY: //显示输入框 launchInputActivity(); break; case SET_UP_IDLE_MODE_TEXT: waitForUsersResponse = false; launchIdleText(); break; case SEND_DTMF: case SEND_SMS: case SEND_SS: case SEND_USSD: //发送信息的提示 waitForUsersResponse = false; launchEventMessage(); break; case LAUNCH_BROWSER: //载入浏览器 launchConfirmationDialog(mCurrentCmd.geTextMessage()); break; case SET_UP_CALL: //呼叫 launchConfirmationDialog(mCurrentCmd.getCallSettings().confirmMsg); break; case PLAY_TONE: //播放铃声 launchToneDialog(); break; case OPEN_CHANNEL: launchOpenChannelDialog(); break; case CLOSE_CHANNEL: case RECEIVE_DATA: case SEND_DATA: TextMessage m = mCurrentCmd.geTextMessage(); if ((m != null) && (m.text == null)) { switch(cmdMsg.getCmdType()) { case CLOSE_CHANNEL: m.text = getResources().getString(R.string.default_close_channel_msg); break; case RECEIVE_DATA: m.text = getResources().getString(R.string.default_receive_data_msg); break; case SEND_DATA: m.text = getResources().getString(R.string.default_send_data_msg); break; } } launchEventMessage(); break; } if (!waitForUsersResponse) { if (mCmdsQ.size() != 0) { callDelayedMsg(); } else { mCmdInProgress = false; } } }接下来,就会对当前的请求做UI层的处理,比如当需要弹出对话框时(DISPLAY_TEXT)就会通过launchTextDialog()方法弹出对话框,当需要弹出菜单时(SELECT_ITEM/SET_UP_MENU)就会通过launchMenuActivity()方法来实现,当需要一个编辑框(GET_INPUT)时,就会通过launchInputActivity()来实现。