SIM卡应用开发工具箱(STK)亦称SIM卡应用程序开发包,估计大家对它最直观的印象就是插入sim卡或usim卡开机时,会出现中国移动动感地带,神州行,中国联通这一类的提示信息,展示的方式可能字符串或一个对话框,这个就是STK的功劳了。简单说明一下,STK是sim/usim卡上的程序,含有STK功能的sim卡/usim卡就是一个单片机。在android手机上,在众多图标中会有一个sim卡应用图标,点击后进入会看到一个主菜单中,有类似品牌信息,发短信,网上营业厅选项等等,菜单内容根据运营商的不同也不完全一样。
这个STK涉及的东西还是蛮多的,想完全了解还是要花点力气,http://www.mobile52rd.com/?p=75这篇文章应该来说大概说明了一个学习思路,还不错,转过来分享下,在此先感谢下原作者的辛勤劳动。如果上述文章可看成是一个概述,本文将从一个具体实例来描述下STK的代码是什么样子的。还是回到开机显示的品牌信息吧,这个品牌信息是怎么出来的呢,一起看下相关的代码吧。先上图
先做点准备工作,开机在GsmPhone的构造函数里会获StkService的一个实例,
public GSMPhone (Context context, CommandsInterface ci, PhoneNotifier notifier, boolean unitTestMode) { super(notifier, context, ci, unitTestMode); mStkService = StkService.getInstance(mCM, mSIMRecords, mContext, (SIMFileHandler)mIccFileHandler, mSimCard); ......}
而在stkService的构造方方法里注册一系列的监听,等待从RIL传过来的消息进行处理。我们先关注这个mCmdIf.setOnStkProactiveCmd(this, MSG_ID_PROACTIVE_COMMAND, null);
public void setOnStkProactiveCmd(Handler h, int what, Object obj) { mStkProCmdRegistrant = new Registrant (h, what, obj); }
这里实例化mStkProCmdRegistrant对象,那么是谁在用呢,在RIL.java的代码,在这我在processUnsolicited ()方法的RIL _UNSOL_STK_PROACTIVE_COMMAND这个分支里,看到这个processUnsolicited()方法,可以知道这是一个主动上报的命令,
case RIL_UNSOL_STK_PROACTIVE_COMMAND: if (RILJ_LOGD) unsljLogRet(response, ret); if (mStkProCmdRegistrant != null) { mStkProCmdRegistrant.notifyRegistrant( new AsyncResult (null, ret, null)); }上面关注的是消息监听是怎么注册的,这个能解释我们为什么能收到消息,在这里有种逆向思维的感觉,下面看收到消息后我们是怎么处理的。
public void handleMessage(Message msg) { switch (msg.what) { case MSG_ID_PROACTIVE_COMMAND: StkLog.d(this, "ril message arrived");//RIL消息来了 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; } } } mMsgDecoder.sendStartDecodingMessageParams(new RilMessage(msg.what, data)); break;
public void sendStartDecodingMessageParams(RilMessage rilMsg) {//又是消息 Message msg = obtainMessage(CMD_START);//这个CMD_START很关键 msg.obj = rilMsg; sendMessage(msg); } private class StateStart extends HierarchicalState { @Override protected boolean processMessage(Message msg) { if (msg.what == CMD_START) { if (decodeMessageParams((RilMessage)msg.obj)) { transitionTo(mStateCmdParamsReady); } …省略无关代码 } return true; } }这里频繁使用了消息机制,设计的是很巧妙,不过找起来也有点费事,要注意好那些关键的TAG标志,在最一段代码里,decodeMessageParams((RilMessage)msg.obj)看起来要解码了,没错,具体的解码过程看Bertlv.java里的代码就好了。为什么要那么解,要看3gpp 11.14文档。解码结束后又回到stkservice.java的这个分支:
case MSG_ID_PROACTIVE_COMMAND: cmdParams = (CommandParams) rilMsg.mData; if (cmdParams != null) { if (rilMsg.mResCode == ResultCode.OK) { handleProactiveCommand(cmdParams); } ..省略次要代码.. } break;在这个handleProactiveCommand()方法最后的几行代码里会发送带参数的Intent
Intent intent = new Intent(AppInterface.STK_CMD_ACTION);//关注这个action intent.putExtra("STK CMD",cmdMsg); mContext.sendBroadcast(intent);
然后在StkCmdReceiver.java的onReceive()方法里接收,再启动StkAppService.java,先在onstart()方法做下简单的消息处理,然后转到内部类ServiceHandler
private final class ServiceHandler extends Handler { @Override public void handleMessage(Message msg) { int opcode = msg.arg1; //省略代码……。 switch (opcode) { case OP_CMD: //省略代码….. handleCmd((StkCmdMessage) msg.obj);
在handleCmd()里这个launchTextDialog()方法,终于看到Dialog了,里面的代码就是显示一个对话框的,没有什么特别的。这个就是品牌信息显示的对话框了,过程还是蛮曲折的。虽然这只是一个品牌信息的显示,但这里已经可以说明stk应用代码调用的基本流程了,可以借一斑以窥全豹了,要注意好消息的传递,不过STK细节很多,想完全掌握还是要花些功夫的