1. STK整体工作流程的结构图
从图中可以得到,查看方向,由MODEM向JAVA查看,MODEM会使用两种方式给RIL.JAVA上报数据,分别是Unsolicited and Terminalresponse Solicited(request/response) . 在RIL.JAVA中常用的有
solicited response
RIL_REQUEST_STK_GET_PROFILE 用来GET PROFILE
RIL_REQUEST_STK_SET_PROFILE 设置PROFILE
RIL_REQUEST_STK_SEND_ENVELOPE_COMMAND 用于发送SELECT ITEM 等
RIL_REQUEST_STK_SEND_TERMINAL_RESPONSE 发送STK TERMINAL RESPONSE
RIL_REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM 能于从STK打CALL,通知JAVA界面,相关CALL的消息。
Unsolicited response
RIL_UNSOL_STK_SESSION_END 结束会话
RIL_UNSOL_STK_PROACTIVE_COMMAND SETUP MENU 显示STK的主菜单
RIL_UNSOL_STK_EVENT_NOTIFY 主要用于EVENTDOWNLOAD
RIL_UNSOL_STK_CALL_SETUP 打CALL后的相关通知消息。
上图中的Baseband 一般由平台厂商进行开发,根据ANDROID的开源默认的机制,可以使用AT COMMAND 来操作MODEM,也有其它的厂商会进行自己的MODEM开发,来达到提高MODEM的速度和效率。
但对于RIL.java这个抽象层来说,不管采用哪种类型的MODEM,上层都是一样的,这样便于更好的移植.
2. 应用层与telephony层交互流程图
请求发到StkService.java(即CatService.java),它是telephony service,将二进制的流进行解析,得到对应的类的结构,并将请求发送给StkAppService.java.
大部分STK的协议是在MODEM这边实现的,比如TermianlProfile, USSD,SS,而在ANDROID上层,只对部分的协议进行了支持,主要是和用户有交互的操作,比如DISPLAYTEXT。
(备注:上面1,2点内容,出自博客http://blog.youkuaiyun.com/guoleimail/article/details/6683458,借用来分析STK工作原理)
3. STK应用的启动
应用目录:mediatek/packages/apps/Stk1/
(1). 设备开机,STK应用的安装
文件路径:com.android.stk.BootCompleteReceiver
//每次设备开机完成后,系统广播Intent.ACTION_BOOT_COMPLETED,STK应用接到广播进行STK启动,现以单卡情况下进行分析STK流程
public void onReceive(Context context,Intent intent) {
String action = intent.getAction();
//初始化应用安装者实例,应用服务实例
StkAppInstaller appInstaller =StkAppInstaller.getInstance();
StkAppService appService =StkAppService.getInstance();
//(a).开机完成广播.
if(action.equals(Intent.ACTION_BOOT_COMPLETED)) {
……
//开启STK后台应用服务,用于UI和Telephony之间交互
context.startService(newIntent(context, StkAppService.class).putExtras(args));
……
//根据当前是单卡,或者是双卡,或者是多卡,进行STK应用的卸载
……
mHasBootComplete = true;
} else if(action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
//(b).从基带芯片开始上电,然后SIM卡上电,再协议栈开启,再基带初始化完成,以致基带正常工作起来,这过程中伴随SIM STATE的改变
……
//判断STK应用安装状态,若没安装,则进行安装
int app_state =appInstaller.getIsInstalled(SIMID);
if (app_state == -1)
{
appInstaller.install(context, SIMID);
//如果基带工作非正常,以及处于飞行模式,则卸载STK应用
if (checkSimRadioState(context, SIMID)!= true || true == isOnFlightMode(context))
{
/* The SIM card is off souninstall it */
SystemClock.sleep(100);
appInstaller.unInstall(context,SIMID);
}
}
……
//检查SIM卡不同状态下,STK安装情况,然后进行正确安装
……
}
}
(2). 单击启动STK,到显示视图菜单
文件路径:AndroidManifest.xml
<activityandroid:name="StkLauncherActivity"
android:excludeFromRecents="true"
android:label="@string/appI_name"
android:theme="@android:style/Theme.NoDisplay"
android:enabled="false"
android:taskAffinity="android.task.stk.StkLauncherActivity">
<intent-filter>
<actionandroid:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
文件路径:com.android.stk.StkLauncherActivity
protected void onCreate(Bundle savedInstanceState) {
……
//操作类型为StkAppService.OP_LAUNCH_APP,并启动StkAppService进行启动处理
Bundle args = newBundle();
int[] op = new int[2];
op[0] = StkAppService.OP_LAUNCH_APP;
/* TODO: GEMINI+ */
op[1] =PhoneConstants.GEMINI_SIM_1;
args.putIntArray(StkAppService.OPCODE, op);
args.putInt(StkAppService.CMD_SIM_ID, PhoneConstants.GEMINI_SIM_1);
startService(newIntent(this, StkAppService.class).putExtras(args));
finish();
}
文件路径:com.android.stk.StkAppService
public void onStart(Intentintent, int startId) {
……
//对启动Service的Intent参数的检查,合法则继续往下进行
……
//对Intent参数作判断,区分STK操作类型(操作:命令,下载,响应,启动app,结束会话,开机,拔出SIM卡),然后交给ServiceHandler进行处理
Message msg =mServiceHandler.obtainMessage();
msg.arg1 = op[0];
msg.arg2 = sim_id;
switch(msg.arg1) {
case OP_CMD:
msg.obj =args.getParcelable(CMD_MSG);
break;
caseOP_EVENT_DOWNLOAD:
msg.obj = args;
break;
case OP_RESPONSE:
msg.obj = args;
/* falls through*/
case OP_LAUNCH_APP:
case OP_END_SESSION:
caseOP_BOOT_COMPLETED:
case OP_REMOVE_STM:
break;
default:
return;
}
mServiceHandler.sendMessage(msg);
}
//服务处理Handler,对不同操作进行处理
private finalclass ServiceHandler extends Handler {
public voidhandleMessage(Message msg) {
switch(opcode) {
case OP_LAUNCH_APP:
if(mStkContext[sim_id].mCurrentMenu ==mStkContext[sim_id].mMainCmd.getMenu() || mStkContext[sim_id].mCurrentMenu == null) {
//当前无菜单时,则加载STK根菜单
launchMenuActivity(null, sim_id);
} else {
//当前有菜单时,则加载其子菜单
launchMenuActivity(mStkContext[sim_id].mCurrentMenu,sim_id);
}
//设置STK应用为可访问状态
setUserAccessState(true, sim_id);
break;
}
}
}
private void launchMenuActivity(Menu menu, int sim_id) {
IntentnewIntent = new Intent(Intent.ACTION_VIEW);
String targetActivity =STK1_MENU_ACTIVITY_NAME;
……
//其中STK1_MENU_ACTIVITY_NAME为StkMenuActivity
newIntent.setClassName(PACKAGE_NAME, targetActivity);
……
mContext.startActivity(newIntent);
}
文件路径:com.android.stk.StkMenuActivity
//菜单视图的构建
publicvoid onCreate(Bundle icicle) {
……
setContentView(R.layout.stk_menu_list);
mTitleTextView = (TextView)findViewById(R.id.title_text);
mTitleIconView =(ImageView) findViewById(R.id.title_icon);
mProgressView =(ProgressBar) findViewById(R.id.progress_bar);
……
}
(3). 单击菜单视图中一项
文件路径:com.android.stk.StkMenuActivity
//单击事件的处理
protectedvoid onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l,v, position, id);
mMenuInstance.handleListItemClick(position,mProgressView);
}
文件路径:com.android.stk.StkMenuInstance
publicvoid handleListItemClick(int position, ProgressBar bar)
{
……
sendResponse(StkAppService.RES_ID_MENU_SELECTION,item.id, false);
……
}
//把单击菜单项信息封装,传给StkAppService处理
voidsendResponse(int resId, int itemId, boolean help) {
……
Bundle args = newBundle();
int[]op = new int[2];
op[0] =StkAppService.OP_RESPONSE;
op[1] = mSimId;
args.putIntArray(StkAppService.OPCODE,op);
args.putInt(StkAppService.RES_ID,resId);
args.putInt(StkAppService.MENU_SELECTION,itemId);
args.putBoolean(StkAppService.HELP,help);
mContext.startService(newIntent(mContext, StkAppService.class).putExtras(args));
}
文件路径:com.android.stk.Stk.StkAppService
private final class ServiceHandler extends Handler {
public voidhandleMessage(Message msg) {
switch(opcode) {
caseOP_RESPONSE:
if (mStkContext[sim_id].responseNeeded) {
handleCmdResponse((Bundle) msg.obj, sim_id);
}
……
}
}
}
//处理有响应的命令
private void handleCmdResponse(Bundle args, int sim_id) {
……
CatResponseMessageresMsg = new CatResponseMessage(mStkContext[sim_id].mCurrentCmd);
……
switch(args.getInt(RES_ID)) {
caseRES_ID_MENU_SELECTION:
……
intmenuSelection = args.getInt(MENU_SELECTION);
switch(mStkContext[sim_id].mCurrentMenuCmd.getCmdType()) {
caseSET_UP_MENU:
//have already handled setup menu
mStkContext[sim_id].mSetUpMenuHandled = true;
caseSELECT_ITEM:
resMsg = new CatResponseMessage(mStkContext[sim_id].mCurrentMenuCmd);
mStkContext[sim_id].lastSelectedItem= getItemName(menuSelection, sim_id);
if(helpRequired) {
resMsg.setResultCode(ResultCode.HELP_INFO_REQUIRED);
}else {
resMsg.setResultCode(ResultCode.OK);
}
resMsg.setMenuSelection(menuSelection);
break;
}
break;
……
}
……
//调用Telephone层提供的接口,将响应命令交到Telephony层进行处理。
mStkService[sim_id].onCmdResponse(resMsg);
}
4. Telephony层处理STK应用发来的请求
文件路径:frameworks/opt/telephony/src/java/com/android/internal/telephony/cat/CatService.java
CatService继承了Handler,实现了AppInterface接口,用来提供上层应用调用的接口方法
public synchronized voidonCmdResponse(CatResponseMessage resMsg) {
……
Messagemsg = this.obtainMessage(MSG_ID_RESPONSE, resMsg);
msg.sendToTarget();
}
发送给自身消息处理中心
public void handleMessage(Message msg) {
……
switch (msg.what) {
……
case MSG_ID_RESPONSE:
handleCmdResponse((CatResponseMessage)msg.obj);
break;
……
}
}
处理响应命令
private voidhandleCmdResponse(CatResponseMessage resMsg) {
……
switch (resMsg.resCode) {
……
case OK:
case PRFRMD_WITH_PARTIAL_COMPREHENSION:
case PRFRMD_WITH_MISSING_INFO:
casePRFRMD_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:
switch(AppInterface.CommandType.fromInt(cmdDet.typeOfCommand)) {
case SET_UP_MENU:
CatLog.d("CatService","SET_UP_MENU");
helpRequired =resMsg.resCode == ResultCode.HELP_INFO_REQUIRED;
sendMenuSelection(resMsg.usersMenuSelection, helpRequired);
return;
…..
}
}
//根据协议的规定,组装成报文,然后交给RIL.java去发送
private voidsendMenuSelection(int menuId, boolean helpRequired) {
……
//组装成报文
……
//调用RIL的接口进行发送
mCmdIf.sendEnvelope(hexString, null);
}
文件路径:frameworks/opt/telephony/src/java/com/android/internal/telephony/RIL.java
public void sendEnvelope(String contents, Message response) {
RILRequest rr = RILRequest.obtain(
RILConstants.RIL_REQUEST_STK_SEND_ENVELOPE_COMMAND, response);
if (RILJ_LOGD)riljLog(rr.serialString() + "> " + requestToString(rr.mRequest));
rr.mp.writeString(contents);
//通过socket发给RILD守护进程
send(rr);
}
到这里为止,一条菜单请求命令,就已经通过Telephony层发送到RIL层了,RIL层根据命令标示号,组装成响应AT命令,然后下发给基带,
基带从运营商那里得到子菜单内容,上报给Telephony解析,然后传给STK应用进行显示。
总结:第1点整体上了解STK工作原理,第2点侧重了解StkAppService(应用层)和CatService(Telephony层)的交互流程,
第3,4点从代码上了解STK应用的启动,以及菜单的加载,还有单击一项加载子菜单,从应用层到Telephony层,Telephony层到RIL层的流程。