一.简介
STK 或者 UTK 就是 Sim Tool Kit (sim卡工具包),定制了一系列与运营商相关的应用(查询天气,话费,彩铃等),可以理解为安装在SIM卡上的应用。运营商将相关应用信息保存在SIM卡中,STK应用需要从SIM卡中读取相关应用信息,SIM卡也会向STK应用主动上报应用信息。
我们知道SIM卡是插在Modem中的,要读取SIM卡的内容,就必须要经过Modem层,而与Modem层进行交互离不开AT指令。在framework中,发送AT指令主要是在RIL中完成的。所以读取SIM卡的内容的流程可以归结为:STK应用---->RILJ---->RILC---->Modem---->运营商的基站。而这里只需要跟踪一下STK应用到RIL的RILJ层就可以了,以后有空可以研究一下RILC层的代码。既然最后到达的是RIL层,那么我们从RIL层往STK应用层反推它的流程。看看从SIM卡中读取的信息是怎样到达STK应用的。在我们插卡开机的时候,Modem检测到有卡插入,这时候它会读取SIM中的相关信息,并把消息上报给RIL层。Modem上报消息的过程这里先不管。我们只研究RIL,要研究RIL层,我们需要先了解一下RIL层的消息分类。
二.RIL中的消息类型
无论是哪一种消息,从Modem处上传上来的信息,它的处理过程大体一致。我们只需要沿着具体一个消息的进行追踪,就可以弄清楚它的流程走向。这里我们研究一下UnSolicited的这个消息RIL_UNSOL_STK_PROACTIVE_COMMAND。
三.流程分析
在插卡开机的时候,Modem读取此SIM卡中STK显示的菜单,并向RIL层发送RIL_UNSOL_STK_PROACTIVE_COMMAND消息。这个消息被RILReceiver捕获并处理,从这里开始。流程分三个部分,分别是:1.RILJ部分。2.Framework部分。3.STK部分(应用层)
1.RILJ部分
RILReceiver是RIL的内部类,负责监听Socket消息,从Socket中接收并处理RILC上报的消息。(RILJ与RILC的通信主要通过Socket)其逻辑是:
一. 维护Socket的连接。
二. 把从Socket中读取到的消息交给RIL中processResponse()方法处理。
这个方法主要是根据消息的两种类型,做分别的处理。Parcel存储着从Socket中读取的信息。
Parcel:Parcel就是一个存放读取数据的容器, Android系统中的binder进程间通信(IPC)就使用了Parcel类来进行客户端与服务端数据的交互,而且AIDL的数据也是通过Parcel来交互的。在Java空间和C++都实现了Parcel,由于它在C/C++中,直接使用了内存来读取数据,因此,它更有效率。
private void processResponse (Parcel p) {
int type;
type = p.readInt();
if (type == RESPONSE_UNSOLICITED) {
processUnsolicited (p);
} else if (type == RESPONSE_SOLICITED) {
RILRequest rr = processSolicited (p);
if (rr != null) {
rr.release();
decrementWakeLock();
}
}
}
protected void processUnsolicited (Parcel p) {
int response;
Object ret;
response = p.readInt(); //获取UnSolicited Response消息类型
try {switch(response) { //根据UnSolicited Response消息类型获取不同的ret对象
....
case RIL_UNSOL_STK_SESSION_END: ret = responseVoid(p); break;
case RIL_UNSOL_STK_PROACTIVE_COMMAND: ret = responseString(p); break;
case RIL_UNSOL_STK_EVENT_NOTIFY: ret = responseString(p); break;
case RIL_UNSOL_STK_CALL_SETUP: ret = responseInts(p); break;
...
}
switch(response) { //根据UnSolicited Response消息类型发出不同的消息通知
...
case RIL_UNSOL_STK_PROACTIVE_COMMAND:
if (RILJ_LOGD) unsljLogRet(response, ret);
if (mCatProCmdRegistrant != null) {
mCatProCmdRegistrant.notifyRegistrant(
new AsyncResult (null, ret, null));
}
break;
...
}
}
2.Framework部分
这个notifyRegistrant()方法是调用的哪个类里边的呢。我们来看看mCatProCmdRegistrant这个变量:protected Registrant mCatProCmdRegistrant;
Registrant.java
public void notifyRegistrant(AsyncResult ar) {
internalNotifyRegistrant (ar.result, ar.exception);
}
void internalNotifyRegistrant (Object result, Throwable exception) {
Handler h = getHandler();
if (h == null) {
clear();
} else {
Message msg = Message.obtain();
msg.what = what;
msg.obj = new AsyncResult(userObj, result, exception);
h.sendMessage(msg);
}
}
BaseCommands.java
protected Registrant mCatProCmdRegistrant;
@Override
public void setOnCatProactiveCmd(Handler h, int what, Object obj) {
mCatProCmdRegistrant = new Registrant (h, what, obj);
}
CatService.java
构造方法:
mCmdIf.setOnCatSessionEnd(this, MSG_ID_SESSION_END, null);
mCmdIf.setOnCatProactiveCmd(this, MSG_ID_PROACTIVE_COMMAND, null);//Handler的what标示是MSG_ID_PROACTIVE_COMMAND
mCmdIf.setOnCatEvent(this, MSG_ID_EVENT_NOTIFY, null);
mCmdIf.setOnCatCallSetUp(this, MSG_ID_CALL_SETUP, null);
好了,到这里我们先整理一下:
插卡开机,Medem读取SIM卡信息,上报RIL_UNSOL_STK_PROACTIVE_COMMAND消息。RIlJ中的RILReceiver类进行接收并交给processUnsolicited()方法处理。processUnsolicited()方法根据消息类型通知对应的Handler处理()。而这些Handler的处理在CatService.java的handleMessage()方法中,如下:
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_ID_PROACTIVE_COMMAND:
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;
}
}
}
if (lastCmd != null && data != null && lastCmd.equals(data)) {
CatLog.d(this, "<" + mPhoneId + ">" + "duplicate command, ignored !!");
break;
}
if (data != null) {
lastCmd = new String(data);
} else {
lastCmd = null;
}
if(MSG_ID_PROACTIVE_COMMAND == msg.what && null != data) {
if((mSetupMenuFlag&(1<<mPhoneId))==0 && isSetupMenuCMD(data)) {
saveSetupMenuData(data);
}
}
mMsgDecoder.sendStartDecodingMessageParams(new RilMessage(msg.what, data));
break;
case MSG_ID_RIL_MSG_DECODED:
handleRilMsg((RilMessage) msg.obj);
break;
}
RilMessageDecoder.java
public void sendStartDecodingMessageParams(RilMessage rilMsg) {
Message msg = obtainMessage(CMD_START);
msg.obj = rilMsg;
sendMessage(msg);
}
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;
}
}
private void handleRilMsg(RilMessage rilMsg) {
case MSG_ID_PROACTIVE_COMMAND:
try {
cmdParams = (CommandParams) rilMsg.mData;
} catch (ClassCastException e) {
// for error handling : cast exception
CatLog.d(this, "Fail to parse proactive command");
sendTerminalResponse(mCurrntCmd.mCmdDet, ResultCode.CMD_DATA_NOT_UNDERSTOOD,
false, 0x00, null);
break;
}
if (cmdParams != null) {
if (rilMsg.mResCode == ResultCode.OK ||
// ignore icon problem
rilMsg.mResCode == ResultCode.PRFRMD_ICON_NOT_DISPLAYED) {
handleProactiveCommand(cmdParams);
} else {
// for proactive commands that couldn't be decoded
// successfully respond with the code generated by the
// message decoder.
sendTerminalResponse(cmdParams.mCmdDet, rilMsg.mResCode,
false, 0, null);
}
}
break;
}
private void handleProactiveCommand(CommandParams cmdParams) {
Intent intent = new Intent(AppInterface.CAT_CMD_ACTION);
intent.putExtra("STK CMD", cmdMsg);
intent.putExtra("phone_id", mPhoneId);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
mContext.sendBroadcast(intent);
}
3.STK部分(应用层)
AndroidManifest.xml:
<receiver android:name="com.android.stk.StkCmdReceiver" >
<intent-filter>
<action android:name="android.intent.action.stk.command" />
<action android:name="android.intent.action.stk.session_end" />
<action android:name="android.intent.action.stk.event" />
<action android:name="android.intent.action.LOCALE_CHANGED" />
</intent-filter>
</receiver>
public class StkCmdReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
int phoneId = intent.getIntExtra(StkAppService.PHONE_ID, 0);
if(action.equals(AppInterface.CAT_CMD_EVENT) || Intent.ACTION_LOCALE_CHANGED.equals(action)) {
handleEventDownload(context, intent);
return;
}
if (phoneId == StkAppService.PHONE_ID_NUM) {
if (action.equals(AppInterface.CAT_CMD_ACTION)) {
handleCommandMessage(context, intent);
} else if (action.equals(AppInterface.CAT_SESSION_END_ACTION)) {
handleSessionEnd(context, intent);
}
}
}
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));
}
}
StkAppService.java
public void onStart(Intent intent, int startId) {
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;
}
...
mServiceHandler.sendMessage(msg);
}
private final class ServiceHandler extends Handler {
@Override
public void handleMessage(Message msg) {
int opcode = msg.arg1;
switch (opcode) {
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;
}
}
}
private void handleCmd(CatCmdMessage cmdMsg) {
if (cmdMsg == null) {
return;
}
// save local reference for state tracking.
mCurrentCmd = cmdMsg;
CommandDetails cmddet = cmdMsg.getCmdDet();
boolean waitForUsersResponse = true;
CatLog.d(this, cmdMsg.getCmdType().name());
switch (cmdMsg.getCmdType()) {
case DISPLAY_TEXT:
….
case SELECT_ITEM:
mCurrentMenu = cmdMsg.getMenu();
isSendSS = false;
//launchMenuActivity(cmdMsg.getMenu());
if (phoneIsIdle()) {
launchMenuActivity(cmdMsg.getMenu());
CatLog.d(this,"SELECT_ITEM start StkMenuActivity");
} else {
CatLog.d(this, "SELECT_ITEM is on call, mMenuIsVisibile = " + mMenuIsVisibile);
if (mMenuIsVisibile) {
launchMenuActivity(cmdMsg.getMenu());
CatLog.d(this,"mMenuIsVisibile start StkMenuActivity");
}
return;
}
break;
}
}
private void launchMenuActivity(Menu menu) {
Intent newIntent = new Intent(Intent.ACTION_VIEW);
newIntent.setClassName(PACKAGE_NAME, MENU_ACTIVITY_NAME);
int intentFlags = Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TOP;
if (menu == null) {
// We assume this was initiated by the user pressing the tool kit icon
intentFlags |= getFlagActivityNoUserAction(InitiatedByUserAction.yes);
newIntent.putExtra("STATE", StkMenuActivity.STATE_MAIN);
} else {
// We don't know and we'll let getFlagActivityNoUserAction decide.
intentFlags |= getFlagActivityNoUserAction(InitiatedByUserAction.unknown);
newIntent.putExtra("STATE", StkMenuActivity.STATE_SECONDARY);
}
newIntent.setFlags(intentFlags);
CatLog.d(this, "launchMenuActivity: intent=" + newIntent + ", mCurrentMenu="+ mCurrentMenu);
mContext.startActivity(newIntent);
mStkMenuRef = true;
}
1、2、02_01_getpid :#include <iostream>using namespace std;int main(){ pid_t id = getpid(); cout << "Hello World! pid : " << id << endl; re...
文章目录FreeSWITCH使用MySQL数据库填坑 就是自动生成的表结构不对,要自己删除表,运行一下sqlFreeSWITCH使用MySQL数据库https://www.cnblogs.com/cash/p/13331299.html填坑 就是自动生成的表结构不对,要自己删除表,运行一下sqlSET NAMES utf8mb4;SET FOREIGN_KEY_CHECKS = 0;-- ------------------------------ Table structure fo
原文地址:https://blog.csdn.net/j1231230/article/details/79880748数据库:mysqlIDE:Spring Tool Suitebuild工具:Maven项目版本:springBoot1.5.10 + JDK1.8 + Maven3.0.5 (之前用的是2.0.0版本的springBoot,集成activiti6后启动一直报错,...
1.java实现截图并保存到本地提供给大家三个方法,随意整理的2.方法一:package com.credream.ocr;import java.awt.Dimension;import java.awt.Rectangle;import java.awt.Robot;import java.awt.Toolkit;import java.awt.image.Buff
近日,前瞻产业研究院发布了《2019年物联网行业市场研究报告》,对物联网行业发展环境、现状、产业链、前景及趋势进行深度分析,整理出了物联网产业链全景图谱、2018年世界物...
GIF动图是现在常用的图片格式,比起普通的静态图片更加的生动丰富有趣。自然制作GIF动图的方法也有很多,比如最常见的图片合成GIF动图。那么,我们自己应该怎样将多张图片合成GIF动图呢?这时候,大家就可以使用**动态图片在线制作**工具,打开网站一键就可以在线生成GIF动图,具体的操作步骤如下: 点击“GIF合成”按钮,从电脑中选择想要合成的图片,如果想要从多张图片中选择几张图片合成的话,可以按照键盘“ctrl”键+鼠标左键来进行点选。 图片上传之后,设置生成gif的参数,比如宽高尺寸,延迟(图片
PCF8575 IO扩展器PCF8575 IO扩展器用两线双向总线(I2C)的16位I / O扩展器设计用于2.5V至5.5V VCC操作。PCF8575器件通过I2C接口[串行时钟(SCL),串行数据(SDA)]为大多数微控制器系列提供通用的远程I / O扩展。该器件具有一个16位准双向输入/输出(I / O)端口(P07-P00,P17-P10),包括具有大电流驱动能力的锁存输出,可直接驱动LED。 每个准双向I / O都可以用作输入或输出,而无需使用数据方向控制信号。 上电时,I / O为高。
附转载链接:https://www.hrwhisper.me/introduction-to-simplex-algorithm/ 本文将详细的介绍单纯形算法,包括但不限于LP问题 单纯形算法原理 无界、无解、循环等情况 python代码实现 线性规划问题首先引入如下的问题:假设食物的各种营养成分、价格如下表:Food Energy(能量) Prot...
版权声明:本文为博主原创文章,未经博主允许不得转载。目录(?)[+]涉及的内容一句话理解四种启动模式网上太多了onNewIntent是什么四种启动模式什么时候用呢把网上找的基本都测了一遍自己的感悟吧一些常用的flag组合没写的是我很少用没有发言权呀安卓的启动模式 估计都学过 但好长时间内不知道怎么用,感觉都忘了启动模式的存在了,一般都
MybatisPlus学习总结(上)一、介绍二、集成MP2.1 Mybatis + MP2.3 Spring + Mybatis + MP2.2 SpringBoot + Mybatis + MP三、CRUD基本用法3.1 插入操作3.1.1 代码示例3.1.2 @TableId3.1.3 @TableField3.2 更新操作3.2.1 根据id更新3.2.2 根据条件更新3.3 删除操作3.3.1 deleteById3.3.2 deleteByMap3.3.3 delete3.3.4 deleteBa
最近想要有一个Linux环境来学习,又不想装双系统,想起来有个朋友给我推荐过docker,于是开始入坑docker,主要参考的是docker菜鸟教程。win10家庭版安装docker我的电脑系统是win10家庭版,不能直接用docker for windows安装,按照教程里介绍的利用docker toolbox安装,我下载的是DockerToolbox-18.03.0-ce.exe,然后打开任务管理器查看一下CPU是否开启了虚拟化,需要开启,安装toolbox一路点确定就行,没啥操作,安装完成后发现桌
1. 简介1.1 Web前端前端位于整个项目首要要开发的内容,直接和用户打交道,实现页面的展现和交互功能,所以在分类上俗称为前端。随着软件技术的日趋成熟,前端内容也呈现爆炸式的增长,和革命性的变革,前端借鉴后端的发展经验,也开始应用分层体系,也开始术有专攻,解决一个专项的问题,也开始变得庞大和臃肿。从简单单一功能向框架过渡。1.2 软件架构图1.3 技术栈HTML ———— 页面展示CSS ———— 页面美化BootStrap ———— 前端UI(User Interface)Jav