一、Stk应用简介
安卓源码packages/apps/Stk目录
Stk是全称Sim Tool Kit,即用户识别应用发展工具。该应用提供了用户与SIM卡功能交互的接口。Stk应用里面展示的所有功能和用户进行的所有操作都是与SIM卡运营商进行交互的,因为要走网络流程,所以响应一般比较慢。
注意只有在SIM卡可用的情况下才可以使用STK,例如手机在飞行模式或者没有驻网的情况下,STK应用是不可用的。
二、STK应用架构图
做了一张架构图,如下:

图比较大,可以下载下来观看。
三、STK功能代码详解
Stk应用是Telephony模块的CatService与用户交互的桥梁,功能都集中在StkAppService.java类。它实现了两个流程:
- 响应CatService发来的指令
- 响应用户操作,发送消息给CatService
3.1 Stk运行流程简述
- 在开机后,由BootCompeleteReceiver.java接收开机广播,启动StkAppService,并携带
OP_BOOT_COMPLETED
参数。StkService中由ServiceHandler处理,根据OP_BOOT_COMPLETED参数,检查SIM卡情况,再确定是否启用Stk组件 - 用户进入Stk主界面,启动StkLauncherActivity,接着启动StkAppService,携带OP_LAUNCH_APP参数,在StkAppService中由ServiceHandler处理,启动StkMenuActivity
- 用户进行一些操作,由ServiceHandler处理,通过CatResponseMessage向CatService发送消息
- CatService通过广播消息返回,由StkCmdReceiver接收,启动StkAppService,并携带广播中的参数,再加上OP_CMD参数,将由ServiceHandler进行处理
3.1 响应CatService发来的指令
以GET_INPUT命令为例,由StkCmdReceiver.java
接收
public class StkCmdReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (AppInterface.CAT_CMD_ACTION.equals(action)) {
handleAction(context, intent, StkAppService.OP_CMD);
}
...//后面还有其它的一些广播处理
}
private void handleAction(Context context, Intent intent, int op) {
Bundle args = new Bundle();
//获取卡槽slot_id
int slot_id = intent.getIntExtra(StkAppService.SLOT_ID, 0);
//传入op参数为OP_CMD,表示CAT发来的指令,本节中以GET_INPUT为例
args.putInt(StkAppService.OPCODE, op);
//传入slot_id
args.putInt(StkAppService.SLOT_ID, slot_id);
if (StkAppService.OP_CMD == op) {
//传入具体的command参数
args.putParcelable(StkAppService.CMD_MSG, intent.getParcelableExtra(StkAppService.STK_CMD));
}
...
//启动StkAppService,并传入参数
Intent toService = new Intent(context, StkAppService.class);
toService.putExtras(args);
context.startService(toService);
}
}
StkAppService.java
处理:
@Override
public void onStart(Intent intent, int startId) {
//发送消息由ServiceHandler处理
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = op;
msg.arg2 = slotId;
msg.obj = args.getParcelable(CMD_MSG);
mServiceHandler.sendMessage(msg);
}
ServiceHandler
处理消息:
private final class ServiceHandler extends Handler {
@Override
public void handleMessage(Message msg) {
if(null == msg) {
CatLog.d(LOG_TAG, "ServiceHandler handleMessage msg is null");
return;
}
int opcode = msg.arg1;
int slotId = msg.arg2;
// SPRD: Add this condition for slotId is invalid.
if (slotId < 0) {
CatLog.d(LOG_TAG, "ServiceHandler handleMessage slotId is invalid");
return;
}
CatLog.d(LOG_TAG, "handleMessage opcode[" + opcode + "], sim id[" + slotId + "]");
if (opcode == OP_CMD && msg.obj != null &&
((CatCmdMessage)msg.obj).getCmdType()!= null) {
CatLog.d(LOG_TAG, "cmdName[" + ((CatCmdMessage)msg.obj).getCmdType().name() + "]");
}
mStkContext[slotId].mOpCode = opcode;
switch (opcode) {
...
case OP_CMD:
CatLog.d(LOG_TAG, "[OP_CMD]");
CatCmdMessage cmdMsg = (CatCmdMessage) msg.obj;
/*
有两种类型的命令:
Interactive 交互命令,需要用户的交互操作
Informative 展示一条信息,不需要用户操作
交互命令不可以被覆盖,如果当前有交互命令正在执行,下一条命令需要等待 当前命令执行完成或者超时
非交互命令可以很快得得到响应
*/
if (!isCmdInteractive(cmdMsg)) {
//非交互命令由handleCmd处理
handleCmd(cmdMsg, slotId);
} else {
if (!mStkContext[slotId].mCmdInProgress) {
mStkContext[slotId].mCmdInProgress = true;
//交互命令,如果当前没有正在处理的命令,立即调用handleCmd处理
handleCmd((CatCmdMessage) msg.obj, slotId);
} else {
CatLog.d(LOG_TAG, "[Interactive][in progress]");
//交互命令,当前正在处理,则命令进入排队
mStkContext[slotId].mCmdsQ.addLast(new DelayedCmd(OP_CMD,
(CatCmdMessage) msg.obj, slotId));
}
}
break;
}
}
/**判断是否是交互命令
*/
private boolean isCmdInteractive(CatCmdMessage cmd) {
switch (cmd.getCmdType()) {
case SEND_DTMF:
case SEND_SMS:
case SEND_SS:
case SEND_USSD:
case SET_UP_IDLE_MODE_TEXT:
case SET_UP_MENU:
case CLOSE_CHANNEL:
case RECEIVE_DATA:
case SEND_DATA:
case SET_UP_EVENT_LIST:
case REFRESH:
return false;
}
return true;
}
下来继续来看handleCmd方法,以GET_INPUT为例:
private void handleCmd(CatCmdMessage cmdMsg, int slotId) {
if (cmdMsg == null) {
return;
}
...
switch (cmdMsg.getCmdType()) {
case GET_INPUT:
case GET_INKEY:
launchInputActivity(slotId); //接收到GET_INPUT指令启动InputActivity
break;
}
}
private static final String STK_INPUT_ACTIVITY_NAME = PACKAGE_NAME + ".StkInputActivity";
//启动StkInputActivity并传入参数
private void launchInputActivity(int slotId) {
Intent newIntent = new Intent(Intent.ACTION_VIEW);
String targetActivity = STK_INPUT_ACTIVITY_NAME;
String uriString = STK_INPUT_URI + System.currentTimeMillis();
//Set unique URI to create a new instance of activity for different slotId.
Uri uriData = Uri.parse(uriString);
CatLog.d(LOG_TAG, "launchInputActivity, slotId: " + slotId);
newIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| getFlagActivityNoUserAction(InitiatedByUserAction.unknown, slotId));
newIntent.setClassName(PACKAGE_NAME, targetActivity);
newIntent.putExtra("INPUT", mStkContext[slotId].mCurrentCmd.geInput());
newIntent.putExtra(SLOT_ID, slotId);
newIntent.setData(uriData);
mContext.startActivity(newIntent);
}
到此,GET_INPUT指令就执行完了。后面等待用户输入完成,点击确认后,就是用户发送响应的流程了,下面继续讲。
3.2 响应用户操作,发送消息给CatService
上面讲到了启动StkInputActivity,下面继续看:
我们直接跳到用户输入完成后,点击确认按键:
public void onClick(View v) {
String input = null;
if (!mAcceptUsersInput) {
CatLog.d(LOG_TAG, "mAcceptUsersInput:false");
return;
}
switch (v.getId()) {
case R.id.button_ok:
// Check that text entered is valid .
if (!verfiyTypedText()) {
CatLog.d(LOG_TAG, "handleClick, invalid text");
return;
}
mAcceptUsersInput = false;
//获取用户输入内容
input = mTextIn.getText().toString();
break;
}
CatLog.d(LOG_TAG, "handleClick, ready to response");
//调用sendResponse方法发送文本,响应消息类型为RES_ID_INPUT
sendResponse(StkAppService.RES_ID_INPUT, input, false);
}
//发送文本消息给CAT
void sendResponse(int resId, String input, boolean help) {
//传入参数
mIsResponseSent = true;
Bundle args = new Bundle();
//传入OPCODE类型为OP_RESPONSE
args.putInt(StkAppService.OPCODE, StkAppService.OP_RESPONSE);
args.putInt(StkAppService.SLOT_ID, mSlotId);
args.putInt(StkAppService.RES_ID, resId);
if (input != null) {
args.putString(StkAppService.INPUT, input);
}
args.putBoolean(StkAppService.HELP, help);
//启动StkAppService
mContext.startService(new Intent(mContext, StkAppService.class)
.putExtras(args));
}
原来发回复消息也是通过StkAppService的。
StkService的流程跟之前的相同,也是启动通过ServiceHandler来处理的,直接看ServiceHandler的处理方法:
private final class ServiceHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (opcode) {
case OP_RESPONSE: //OPCODE为OP_RESPONSE
handleCmdResponse((Bundle) msg.obj, slotId);
break;
}
}
//处理OP_RESPONSE的OPCODE
private void handleCmdResponse(Bundle args, int slotId) {
CatResponseMessage resMsg = new CatResponseMessage(mStkContext[slotId].mCurrentCmd);
switch(args.getInt(RES_ID)) {
case RES_ID_INPUT: //响应消息类型
CatLog.d(LOG_TAG, "RES_ID_INPUT");
String input = args.getString(INPUT);
resMsg.setResultCode(mStkContext[slotId].mCurrentCmd.hasIconLoadFailed()
ResultCode.PRFRMD_ICON_NOT_DISPLAYED : ResultCode.OK);
resMsg.setInput(input); //写消息内容
break;
}
onCmdResponse(resMsg, slotId);//通知CAT有response消息
}
private void onCmdResponse(CatResponseMessage resMsg, int slotId){
if(mStkService[slotId] == null){
CatLog.d(LOG_TAG, "mStkService[" + slotId + "] is null, reget it from CatServiceSprd");
mStkService[slotId] = CatService.getInstance(slotId);
}
if (mStkService[slotId] == null) {
// This should never happen (we should be responding only to a message
// that arrived from StkService). It has to exist by this time
CatLog.d(LOG_TAG, "Exception! mStkService is null when we need to send response.");
}else{
//最终调用CatService的onCmdResponse方法
mStkService[slotId].onCmdResponse(resMsg);
}
到此,用户响应消息也执行完成,后面的就是到framework了。从最后执行的代码看到,由CatService发出CatResponseMessage消息,该类位于frameworks/opt/telephony/src/java/com/android/internal/telephony/cat/CatResponseMessage.java
frameworks/opt/telephony/src/java/com/android/internal/telephony/cat/CatService.java
再下面的流程请看另一篇文章Android4.4 STK与CatService-framwork层代码详解
网友评论