Android系统分析之运营商显示流程分析之运营商信息的读取流程二

时间:2022-03-26 17:30:53

运营商显示流程分析之运营商信息的读取流程

一. SIM卡运营商信息的读取

从前面的 运营商信息的获取和赋值 可以知道SIM卡运营商的赋值最终是在 SIMRecords 中完成的, 而SIM卡信息的相关读取是在Uicc模块中

1.1. SIMRecords的创建流程

SIMRecords 的创建是在 UiccCardApplication 中完成的:

private IccRecords createIccRecords(AppType type, Context c, CommandsInterface ci) {
if (type == AppType.APPTYPE_USIM || type == AppType.APPTYPE_SIM) {
// 创建SIMRecords
return new SIMRecords(this, c, ci);
} else if (type == AppType.APPTYPE_RUIM || type == AppType.APPTYPE_CSIM){
return new RuimRecords(this, c, ci);
} else if (type == AppType.APPTYPE_ISIM) {
return new IsimUiccRecords(this, c, ci);
} else {
// Unknown app type (maybe detection is still in progress)
return null;
}
}

UiccCardApplicationcreateIccRecords() 方法被调用是在 UiccCardApplication构造方法update() 方法中

UiccCardApplication(UiccCard uiccCard,
IccCardApplicationStatus as,
Context c,
CommandsInterface ci) {
...
mIccFh = createIccFileHandler(as.app_type);
// 创建SIMRecords
mIccRecords = createIccRecords(as.app_type, mContext, mCi);
...
} void update (IccCardApplicationStatus as, Context c, CommandsInterface ci) {
synchronized (mLock) {
...
if (mAppType != oldAppType) {
if (mIccFh != null) { mIccFh.dispose();}
if (mIccRecords != null) { mIccRecords.dispose();}
mIccFh = createIccFileHandler(as.app_type);
// 创建SIMRecords
mIccRecords = createIccRecords(as.app_type, c, ci);
}
...
}
}

从上面可以看出 SIMRecordsUiccCardApplication 初始化的时候就已经被创建, 所以下面我们需要查看 UiccCardApplication 是在何处被创建的

1.2 UiccCardApplication的创建流程

通过搜索可以发现 UiccCardApplication构造方法update() 的调用都是在 UiccCardupdate() 方法中

public void update(Context c, CommandsInterface ci, IccCardStatus ics) {
synchronized (mLock) {
...
CardState oldState = mCardState;
mCardState = ics.mCardState;
mUniversalPinState = ics.mUniversalPinState;
mGsmUmtsSubscriptionAppIndex = ics.mGsmUmtsSubscriptionAppIndex;
mCdmaSubscriptionAppIndex = ics.mCdmaSubscriptionAppIndex;
mImsSubscriptionAppIndex = ics.mImsSubscriptionAppIndex;
mContext = c;
mCi = ci;
//update applications
if (DBG) log(ics.mApplications.length + " applications");
for ( int i = 0; i < mUiccApplications.length; i++) {
if (mUiccApplications[i] == null) {
//Create newly added Applications
if (i < ics.mApplications.length) {
// UiccCardApplication的创建
mUiccApplications[i] = new UiccCardApplication(this,
ics.mApplications[i], mContext, mCi);
}
} else if (i >= ics.mApplications.length) {
//Delete removed applications
mUiccApplications[i].dispose();
mUiccApplications[i] = null;
} else {
//Update the rest
// UiccCardApplication的更新
mUiccApplications[i].update(ics.mApplications[i], mContext, mCi);
}
}
...
}
}

再看 UiccCardupdate() 方法的调用是在 UiccCard构造方法UiccControlleronGetIccCardStatusDone() 方法中, 而 UiccCard 的构造方法最终也在 UiccControlleronGetIccCardStatusDone() 中被调用, 所以接下来直接查看 UiccController

1.3. UiccController的创建流程

先查看 UiccControlleronGetIccCardStatusDone() 方法:

private synchronized void onGetIccCardStatusDone(AsyncResult ar) {
...
IccCardStatus status = (IccCardStatus)ar.result;
// 创建和更新UiccCard
if (mUiccCard == null) {
//Create new card
mUiccCard = new UiccCard(mContext, mCi, status);
} else {
//Update already existing card
mUiccCard.update(mContext, mCi , status);
} if (DBG) log("Notifying IccChangedRegistrants");
mIccChangedRegistrants.notifyRegistrants();
}

onGetIccCardStatusDone() 的调用是在 handleMessage() 方法中

@Override
public void handleMessage (Message msg) {
synchronized (mLock) {
switch (msg.what) {
case EVENT_ICC_STATUS_CHANGED:
...
// 首先获取到IccCard状态改变的消息, 然后主动查询, 然后在下面的 EVENT_GET_ICC_STATUS_DONE 逻辑中获取处理IccCard的状态信息
mCi.getIccCardStatus(obtainMessage(EVENT_GET_ICC_STATUS_DONE));
break;
case EVENT_GET_ICC_STATUS_DONE:
...
// 接收处理IccCard的窗台信息
AsyncResult ar = (AsyncResult)msg.obj;
onGetIccCardStatusDone(ar);
break;
...
default:
Rlog.e(LOG_TAG, " Unknown Event " + msg.what);
}
}
}

至此, 我们需要知道 EVENT_ICC_STATUS_CHANGED 消息是谁发送的? 在Android的源码中, 很多的使用观察者模式, 通过将 MessageHandler 进行注册其他的类中, 当前其他类的状态发生改变, 通过注册的 HandlerMessage 回调到进行注册的类, 此处我们需要查看该消息是在何处被注册的, 在 UiccController的构造方法中

public UiccController(Context c, CommandsInterface ci) {
mContext = c;
mCi = ci; // ci 即 RIL
// 注册EVENT_ICC_STATUS_CHANGED消息
mCi.registerForIccStatusChanged(this, EVENT_ICC_STATUS_CHANGED, null);
mCi.registerForOn(this, EVENT_ICC_STATUS_CHANGED, null);
mCi.registerForNotAvailable(this, EVENT_RADIO_UNAVAILABLE, null);
}

1.4. UiccController的创建流程

UiccController 的创建是在 PhoneFactory

/**
* FIXME replace this with some other way of making these
* instances
*/
public static void makeDefaultPhone(Context context) {
synchronized(Phone.class) {
if (!sMadeDefaults) {
...
//reads the system properties and makes commandsinterface
sCommandsInterface = new RIL(context, networkMode, cdmaSubscription); // Instantiate UiccController so that all other classes can just call getInstance()
// 创建UiccController
UiccController.make(context, sCommandsInterface); int phoneType = TelephonyManager.getPhoneType(networkMode);
if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
Rlog.i(LOG_TAG, "Creating GSMPhone");
// 创建GSMPhone
sProxyPhone = new PhoneProxy(new GSMPhone(context,
sCommandsInterface, sPhoneNotifier));
}
...
}
}
}

UiccControllermakeDefaultPhone() 的调用是在 PhoneGlobalsonCreate() 方法中

1.5 PhoneGlobals的onCreate()方法

public void onCreate() {
...
if (phone == null) {
// Initialize the telephony framework
PhoneFactory.makeDefaultPhones(this); // Get the default phone
phone = PhoneFactory.getDefaultPhone(); // Start TelephonyDebugService After the default phone is created.
Intent intent = new Intent(this, TelephonyDebugService.class);
startService(intent); mCM = CallManager.getInstance();
mCM.registerPhone(phone);
...
}
}

PhoneGlobalsonCreate() 被调用是在 PhoneApponCreate() 方法中

1.6. PhoneApponCreate() 方法

public class PhoneApp extends Application {
public void onCreate() {
if (UserHandle.myUserId() == 0) {
mPhoneGlobals = new PhoneGlobals(this);
mPhoneGlobals.onCreate();
}
}
}

至此, SIMRecords 的创建流程已经完成, PhoneApp 的加载是系统 com.android.phone 进程时加载

因为SIM卡的运营商信息是通过 SIMRecords 获取的, 因此我们需要了解 SIMRecords 的创建流程, 接下看SIM卡信息的读取流程

二. SIM卡运营商信息的读取流程

接着上面 SIMRecords 的创建, 我们首先看 SIMRecords 的构造方法:

public SIMRecords(UiccCardApplication app, Context c, CommandsInterface ci) {
super(app, c, ci);
...
// Start off by setting empty state
resetRecords();// 重置SIM卡的相关状态信息
// 注册EVENT_APP_READY的Message消息, 该消息被注册到UiccCardApplication中,
// 当UiccApplication的update()方法被调用时, 执行回调发送该消息
// 而UiccApplication的update()方法的调用已经在上面说明
mParentApp.registerForReady(this, EVENT_APP_READY, null);
if (DBG) log("SIMRecords X ctor this=" + this);
}

查看 EVENT_APP_READY 消息的处理:

public void handleMessage(Message msg) {
...
try { switch (msg.what) {
case EVENT_APP_READY:
onReady();//
break;
...
}
} protected void fetchSimRecords() {
mRecordsRequested = true;
...
// 我们只关心SPN的获取, 所以看此处
getSpnFsm(true, null);
...
}

获取 SPN 的流程:

/**
* Finite State Machine to load Service Provider Name , which can be stored
* in either EF_SPN (3GPP), EF_SPN_CPHS, or EF_SPN_SHORT_CPHS (CPHS4.2)
*
* After starting, FSM will search SPN EFs in order and stop after finding
* the first valid SPN
*
* If the FSM gets restart while waiting for one of
* SPN EFs results (i.e. a SIM refresh occurs after issuing
* read EF_CPHS_SPN), it will re-initialize only after
* receiving and discarding the unfinished SPN EF result.
*
* @param start set true only for initialize loading
* @param ar the AsyncResult from loadEFTransparent
* ar.exception holds exception in error
* ar.result is byte[] for data in success
*/
private void getSpnFsm(boolean start, AsyncResult ar) {
byte[] data;
// start: 第一次执行为 true, 后续执行为false
if (start) {
// 初始化获取SPN的状态
// Check previous state to see if there is outstanding
// SPN read
if(mSpnState == GetSpnFsmState.READ_SPN_3GPP ||
mSpnState == GetSpnFsmState.READ_SPN_CPHS ||
mSpnState == GetSpnFsmState.READ_SPN_SHORT_CPHS ||
mSpnState == GetSpnFsmState.INIT) {
// Set INIT then return so the INIT code
// will run when the outstanding read done.
mSpnState = GetSpnFsmState.INIT;
return;
} else {
// 开始为INIT状态
mSpnState = GetSpnFsmState.INIT;
}
}
// 执行顺序INIT --> READ_SPN_3GPP --> READ_SPN_CPHS --> READ_SPN_SHORT_CPHS, 执行顺序由3GPP协议确定
switch(mSpnState){
case INIT:
mSpn = null;
// 加载EF_SPN, 回调到 EVENT_GET_SPN_DONE 消息处理
mFh.loadEFTransparent(EF_SPN,
obtainMessage(EVENT_GET_SPN_DONE));
mRecordsToLoad++;
// 修改状态
mSpnState = GetSpnFsmState.READ_SPN_3GPP;
break;
case READ_SPN_3GPP: if (ar != null && ar.exception == null) {
// EF_SPN 加载成功
data = (byte[]) ar.result;
mSpnDisplayCondition = 0xff & data[0];
mSpn = IccUtils.adnStringFieldToString(data, 1, data.length - 1); if (DBG) log("Load EF_SPN: " + mSpn
+ " spnDisplayCondition: " + mSpnDisplayCondition);
// 获取到的运营商名称, 即通过TelephonyManager.getSimOperatorName()方法获取
SystemProperties.set(PROPERTY_ICC_OPERATOR_ALPHA, mSpn); mSpnState = GetSpnFsmState.IDLE;
} else {
// EF_SPN 加载失败, 加载 EF_SPN_CPHS, 回调到 EVENT_GET_SPN_DONE 消息处理
mFh.loadEFTransparent( EF_SPN_CPHS,
obtainMessage(EVENT_GET_SPN_DONE));
mRecordsToLoad++; mSpnState = GetSpnFsmState.READ_SPN_CPHS; // See TS 51.011 10.3.11. Basically, default to
// show PLMN always, and SPN also if roaming.
mSpnDisplayCondition = -1;
}
break;
case READ_SPN_CPHS:
if (ar != null && ar.exception == null) {
// 加载 EF_SPN_CPHS 成功
data = (byte[]) ar.result;
mSpn = IccUtils.adnStringFieldToString(data, 0, data.length); if (DBG) log("Load EF_SPN_CPHS: " + mSpn);
// 获取到的运营商名称, 即通过TelephonyManager.getSimOperatorName()方法获取
SystemProperties.set(PROPERTY_ICC_OPERATOR_ALPHA, mSpn); mSpnState = GetSpnFsmState.IDLE;
} else {
// 加载 EF_SPN_CPHS 失败, 加载 EF_SPN_SHORT_CPHS, 回调到 EVENT_GET_SPN_DONE 消息处理
mFh.loadEFTransparent(
EF_SPN_SHORT_CPHS, obtainMessage(EVENT_GET_SPN_DONE));
mRecordsToLoad++; mSpnState = GetSpnFsmState.READ_SPN_SHORT_CPHS;
}
break;
case READ_SPN_SHORT_CPHS:
if (ar != null && ar.exception == null) {
// 加载 EF_SPN_SHORT_CPHS 成功
data = (byte[]) ar.result;
mSpn = IccUtils.adnStringFieldToString(data, 0, data.length); if (DBG) log("Load EF_SPN_SHORT_CPHS: " + mSpn);
// 获取到的运营商名称, 即通过TelephonyManager.getSimOperatorName()方法获取
SystemProperties.set(PROPERTY_ICC_OPERATOR_ALPHA, mSpn);
}else {
if (DBG) log("No SPN loaded in either CHPS or 3GPP");
} mSpnState = GetSpnFsmState.IDLE;
break;
default:
mSpnState = GetSpnFsmState.IDLE;
}
}

接着查看 EVENT_GET_SPN_DONE 消息的处理:

@Override
public void handleMessage(Message msg) {
try { switch (msg.what) {
case EVENT_GET_SPN_DONE:
isRecordLoadResponse = true;
ar = (AsyncResult) msg.obj;
// 注意: 第一参数为 false
getSpnFsm(false, ar);// 继续执行getSpnFsm()方法
break;
} finally {
// Count up record load responses even if they are fails
if (isRecordLoadResponse) {
// 当获取到SPN后执行该方法
onRecordLoaded();
}
}
} protected void onRecordLoaded() {
// One record loaded successfully or failed, In either case
// we need to update the recordsToLoad count
mRecordsToLoad -= 1;
if (DBG) log("onRecordLoaded " + mRecordsToLoad + " requested: " + mRecordsRequested); if (mRecordsToLoad == 0 && mRecordsRequested == true) {
// 所有信息都已加载完成
onAllRecordsLoaded();
} else if (mRecordsToLoad < 0) {
loge("recordsToLoad <0, programmer error suspected");
mRecordsToLoad = 0;
}
}

查看对已经获取到的 SIM卡信息 的处理:

@Override
protected void onAllRecordsLoaded() {
if (DBG) log("record load complete"); // Some fields require more than one SIM record to set
// 获取到运营商代码
String operator = getOperatorNumeric();
if (!TextUtils.isEmpty(operator)) {
log("onAllRecordsLoaded set 'gsm.sim.operator.numeric' to operator='" +
operator + "'");
// 保存运营商代码, 即通过TelephonyManager.getSimOperator()方法获取
SystemProperties.set(PROPERTY_ICC_OPERATOR_NUMERIC, operator);
} else {
log("onAllRecordsLoaded empty 'gsm.sim.operator.numeric' skipping");
} if (!TextUtils.isEmpty(mImsi)) {
log("onAllRecordsLoaded set mcc imsi=" + mImsi);
SystemProperties.set(PROPERTY_ICC_OPERATOR_ISO_COUNTRY,
MccTable.countryCodeForMcc(Integer.parseInt(mImsi.substring(0,3))));
} else {
log("onAllRecordsLoaded empty imsi skipping setting mcc");
} setVoiceMailByCountry(operator);
setSpnFromConfig(operator); mRecordsLoadedRegistrants.notifyRegistrants(
new AsyncResult(null, null, null));
} private void setSpnFromConfig(String carrier) {
// 如果本地有配置运营商名称, 则覆盖从SIM卡中获取到的SIM卡运营商名称
// 本地配置的文件是: spn-conf.xml文件
if (mSpnOverride.containsCarrier(carrier)) {
mSpn = mSpnOverride.getSpn(carrier);
}
}

Android系统分析之运营商显示流程分析之运营商信息的读取流程二的更多相关文章

  1. MyCat源码分析系列之——配置信息和启动流程

    更多MyCat源码分析,请戳MyCat源码分析系列 MyCat配置信息 除了一些默认的配置参数,大多数的MyCat配置信息是通过读取若干.xml/.properties文件获取的,主要包括: 1)se ...

  2. Android Launcher分析和修改9——Launcher启动APP流程

    本来想分析AppsCustomizePagedView类,不过今天突然接到一个临时任务.客户反馈说机器界面的图标很难点击启动程序,经常点击了没有反应,Boss说要优先解决这问题.没办法,只能看看是怎么 ...

  3. Android 7&period;1 WindowManagerService 屏幕旋转流程分析 &lpar;二&rpar;

    一.概述 从上篇[Android 7.1 屏幕旋转流程分析]知道实际的旋转由WindowManagerService来完成,这里接着上面具体详细展开. 调了三个函数完成了三件事,即首先调用update ...

  4. 源码分析篇 - Android绘制流程(一)窗口启动流程分析

    Activity.View.Window之间的关系可以用以下的简要UML关系图表示,在这里贴出来,比较能够帮组后面流程分析部分的阅读. 一.Activity的启动流程 在startActivity() ...

  5. 【转】android SystemUI 流程分析

    android4 SystemUI 流程分析 什么是SystemUI? 对于Phone来说SystemUI指的是:StatusBar(状态栏).NavigationBar(导航栏).而对于Tablet ...

  6. MSM8909中LK阶段LCM屏适配与显示流程分析&lpar;二&rpar;

    1.前言 在前面的文章MSM8909中LK阶段LCM屏适配与显示流程分析(一),链接如下: https://www.cnblogs.com/Cqlismy/p/12019317.html 介绍了如何使 ...

  7. Android系统加载Apk文件的时机和流程分析(1)--Android 4&period;4&period;4 r1的源码

    本文博客地址:https://blog.csdn.net/QQ1084283172/article/details/80982869 Android系统在启动时安装应用程序的过程,这些应用程序安装好之 ...

  8. Android 9&period;0 默认输入法的设置流程分析

    Android 输入法设置文章 Android 9.0 默认输入法的设置流程分析 Android 9.0 添加预置第三方输入法/设置默认输入法(软键盘) 前言 在上一篇文章  Android 9.0 ...

  9. Android 4&period;4KitKat AudioRecord 流程分析

    Android是架构分为三层: 底层      Linux Kernel 中间层  主要由C++实现 (Android 60%源码都是C++实现) 应用层  主要由JAVA开发的应用程序 应用程序执行 ...

随机推荐

  1. HDU 1403-Longest Common Substring (后缀数组)

    Description Given two strings, you have to tell the length of the Longest Common Substring of them. ...

  2. PHP实现CSV大文件数据导入到MYSQL数据库

    <?php $db_host="192.168.1.10"; $db_user="root"; $db_psw="11111"; $d ...

  3. Oracle动态执行语句

      一.为什么要使用动态执行语句? 由于在PL/SQL 块或者存储过程中只支持DML语句及控制流语句,并不支持DDL语句,所以Oracle动态执行语句便应允而生了.关于DDL与DML的区别,请参见:D ...

  4. WebService 实例

    使用Java WebService API实现 1.服务端接口: package com.h3c.itac.webservice; import javax.jws.WebService; @WebS ...

  5. &lbrack;转&rsqb;Dropdownlist doesn&&num;39&semi;t postback after Page&lowbar;ClientValidate&lpar;&rpar;

    本文转自:http://*.com/questions/2083929/dropdownlist-doesnt-postback-after-page-clientvalida ...

  6. 新建txt文件新增内容并打印出

    #!/usr/bin/python import os file1=open("C:\Python34\ceshi.txt","a+");  #a+开一个文件用 ...

  7. 最简单的视音频播放示例3:Direct3D播放YUV,RGB(通过Surface)

    上一篇文章记录了GDI播放视频的技术.打算接下来写两篇文章记录Direct3D(简称D3D)播放视频的技术.Direct3D应该Windows下最常用的播放视频的技术.实际上视频播放只是Direct3 ...

  8. 两年的坚持,最后还是决定将ISoft开源

    还记得2011年9月份,我在上大四,本来想着考研能上个好点的学校,可我怎么就不愿去自习室上自习.每天晚上睡觉前都告诉自己明天早晨一定早起去上自习,但又每次醒来都不想起床啊,懒,没办法.睡到不想再睡了才 ...

  9. 开发设计模式(七)工厂模式(Factory Method Pattern)

    工厂模式是我们最常用的模式了,著名的Jive论坛 ,就大量使用了工厂模式,工厂模式在Java程序系统可以说是随处可见. 为什么工厂模式是如此常用?因为工厂模式就相当于创建实例对象的new,我们经常要根 ...

  10. 【译】基于主机的卡仿真&lpar;Host-based Card Emulation&rpar;

    基于主机的卡仿真(Host-based Card Emulation) 能提供NFC功能很多Android手机已经支持NFC卡模拟.在大多数情况下,该卡是由设备中的单独的芯片仿真,所谓的安全元件.由无 ...