Android 6.0 IMS流程(1)——IMS开机初始化
最近在学习Wifi Calling,Wifi Calling走的是IMS的流程,本文会讲解在开机时IMS初始化时相关的内容。
一、IMS开机初始化
(如果图片看不清的话,可以右键选择在查看图片,或者把图片另存到自己电脑再查看。)
1.1 监控IMS Service
PhoneApp进程是在系统开机时启动的,Phone进程初始化的时候(步骤1~6),在创建GSMPhone或者CDMAPhone之后,会执行监控IMS Service的流程,也就是流程图上的步骤7,在PhoneFactory.java的makeDefaultPhone()方法中:
/**
* FIXME replace this with some other way of making these
* instances
*/
public static void makeDefaultPhone(Context context) {
......
//先根据phoneType创建GSMPhone或者CDMAPhone
for (int i = 0; i < numPhones; i++) {
PhoneBase phone = null;
int phoneType = TelephonyManager.getPhoneType(networkModes[i]);
if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
phone = TelephonyPluginDelegate.getInstance().makeGSMPhone(context,
sCommandsInterfaces[i], sPhoneNotifier, i);
} else if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
phone = TelephonyPluginDelegate.getInstance().makeCDMALTEPhone(context,
sCommandsInterfaces[i], sPhoneNotifier, i);
}
Rlog.i(LOG_TAG, "Creating Phone with type = " + phoneType + " sub = " + i);
sProxyPhones[i] = TelephonyPluginDelegate.getInstance().makePhoneProxy(phone);
}
......
//在makeDefaultPhone()方法的最后,等所有要初始化的操作
//(如创建RIL,Default Phone等)都执行完之后。
for (int i = 0; i < numPhones; i++) {
//开始执行监控IMS Service的流程
sProxyPhones[i].startMonitoringImsService();
}
}
接着来到PhoneProxy.java的startMonitoringImsService()方法中,可以看到在监控IMS service之前必须先创建Default Phone。
public void startMonitoringImsService() {
//必须先创建Default Phone
if (mActivePhone != null) {
mActivePhone.startMonitoringImsService();
}
}
接着来到PhoneBase.java的startMonitoringImsService()方法中,来到这里,主要是为了注册上监听IMS SERVICE UP或者DOWN的广播。等到ImsService onCreate()的时候,会发出ACTION_IMS_SERVICE_UP的广播,然后PhoneBase接收到广播之后就可以执行创建IMSPhone对象(1.3小节会详细讲)和通知modem turn on IMS的流程(1.4小节会详细讲)。
@Override
public void startMonitoringImsService() {
if (getPhoneType() == PhoneConstants.PHONE_TYPE_SIP) {
return;
}
synchronized(PhoneProxy.lockForRadioTechnologyChange) {
IntentFilter filter = new IntentFilter();
filter.addAction(ImsManager.ACTION_IMS_SERVICE_UP);
filter.addAction(ImsManager.ACTION_IMS_SERVICE_DOWN);
//注册监听IMS SERVICE UP或者DOWN的广播
mContext.registerReceiver(mImsIntentReceiver, filter);
mImsIntentReceiverRegistered = true;
// Monitor IMS service - but first poll to see if already up (could miss
// intent)
//初始化ImsManager对象
ImsManager imsManager = ImsManager.getInstance(mContext, getPhoneId());
//IMS service目前还不可用
if (imsManager != null && imsManager.isServiceAvailable()) {
mImsServiceReady = true;
updateImsPhone();
ImsManager.updateImsServiceConfig(mContext, mPhoneId, false);
}
}
}
1.2 IMS Service的启动
步骤11 :ImsServiceAutoboot.java监听了ACTION_BOOT_COMPLETED和ACTION_SIM_STATE_CHANGED广播
//ClassName是ImsService,所以下面启动的Service就是Ims Service
private final static String mClassName = ImsService.class.getName();
public void onReceive(Context context, Intent intent) {
String intentAction = intent.getAction();
//在开机的时候,会收到ACTION_SIM_STATE_CHANGED广播
if ((Intent.ACTION_BOOT_COMPLETED.equals(intentAction)) ||
(ACTION_SIM_STATE_CHANGED.equals(intentAction))) {
//如果这时候Service还没有启动
if (!isServiceRunning(context)) {
Log.d(TAG, "Starting " + mClassName + " : " + intentAction + " received. ");
//启动Service
startService(context);
} else {
Log.d(TAG, mClassName + " is already running. " +
intentAction + " ignored. ");
}
} else {
Log.e(TAG, "Received Intent: " + intent.toString());
}
}
private void startService(Context context) {
ComponentName comp = new ComponentName(context.getPackageName(), mClassName);
//启动Ims Service
ComponentName service = context.startService(new Intent().setComponent(comp));
if (service == null) {
Log.e(TAG, "Could Not Start Service " + comp.toString());
} else {
Log.e(TAG, mClassName + " service Started Successfully");
}
}
在ImsService.java的onCreate()方法中
public void onCreate() {
super.onCreate();
Log.d (LOG_TAG, "ImsService created!");
mServiceSub = new ImsServiceSub[getNumSubscriptions()];
for (int i = 0; i < getNumSubscriptions(); i++) {
//在这里初始化ImsServiceSub对象,在ImsServiceSub的
//构造方法中进而会初始化ImsSenderRxr、ImsServiceSubHandler
//等对象并且监听IMS、Call等的状态变化;
//(ImsSenderRxr跟RILJ作用类似,可以跟Qcril交互,
// IMS的流程会走ImsSenderRxr而不会走RIL.java)
mServiceSub[i] = new ImsServiceSub(i + 1, this);
}
ServiceManager.addService("ims", mBinder);
Intent intent = new Intent(ImsManager.ACTION_IMS_SERVICE_UP);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
//先判断是否是双卡
if (TelephonyManager.getDefault().isMultiSimEnabled()) {
//如果是双卡,还需把ImsPhoneId传递过去
intent.putExtra(ImsManager.EXTRA_PHONE_ID, mImsPhoneId);
//发送广播,通知PhoneBase
this.sendStickyBroadcast(intent);
} else {
this.sendStickyBroadcast(intent);
}
final int defaultSub = 1;
ImsVideoGlobals.init(mServiceSub[defaultSub-1], this);
/* Check if any change in socket communication is required */
initSubscriptionStatus();
/* Initialize Call Deflect support to not supported */
initCallDeflectStatus();
}
在 IMS Service的启动之后,回到PhoneBase.java的mImsIntentReceiver广播接收器中
private BroadcastReceiver mImsIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Rlog.d(LOG_TAG, "mImsIntentReceiver: action " + intent.getAction());
//先判断intent中是否包含Phone Id
if (intent.hasExtra(ImsManager.EXTRA_PHONE_ID)) {
int extraPhoneId = intent.getIntExtra(ImsManager.EXTRA_PHONE_ID,
SubscriptionManager.INVALID_PHONE_INDEX);
Rlog.d(LOG_TAG, "mImsIntentReceiver: extraPhoneId = " + extraPhoneId);
//如果获取到的Phone Id等于-1或者Phone Id跟当前get到的
//phone Id不一样,说明有误,所以直接return
if (extraPhoneId == SubscriptionManager.INVALID_PHONE_INDEX ||
extraPhoneId != getPhoneId()) {
return;
}
}
synchronized (PhoneProxy.lockForRadioTechnologyChange) {
//如果ACTION是IMS_SERVICE_UP,说明IMS Service已启动
if (intent.getAction().equals(ImsManager.ACTION_IMS_SERVICE_UP)) {
//将标志设置成true,在更新ImsPhone时会用到
mImsServiceReady = true;
//创建或者更新ImsPhone(1.3小节会详细讲)
updateImsPhone();
//更新配置信息,通知Modem turn On/Off IMS(1.4小节会详细讲)
ImsManager.updateImsServiceConfig(mContext, mPhoneId, false);
} else if (intent.getAction().equals(ImsManager.ACTION_IMS_SERVICE_DOWN)){
mImsServiceReady = false;
updateImsPhone();
}
}
}
};
1.3 创建ImsPhone
protected void updateImsPhone() {
Rlog.d(LOG_TAG, "updateImsPhone"
+ " mImsServiceReady=" + mImsServiceReady);
//如果ImsService已启动并且ImsPhone还没有被创建
if (mImsServiceReady && (mImsPhone == null)) {
//通过PhoneFactory创建ImsPhone,这里的this就是default Phone
//ImsPhone
mImsPhone = PhoneFactory.makeImsPhone(mNotifier, this);
//registerPhone的作用是把新创建的ImsPhone对象添加到CallManager
//的Phone list中,并且为ImsPhone注册监听各种Phone States
CallManager.getInstance().registerPhone(mImsPhone);
mImsPhone.registerForSilentRedial(
this, EVENT_INITIATE_SILENT_REDIAL, null);
} else if (!mImsServiceReady && (mImsPhone != null)) {
CallManager.getInstance().unregisterPhone(mImsPhone);
mImsPhone.unregisterForSilentRedial(this);
mImsPhone.dispose();
// Potential GC issue if someone keeps a reference to ImsPhone.
// However: this change will make sure that such a reference does
// not access functions through NULL pointer.
//mImsPhone.removeReferences();
mImsPhone = null;
}
}
接着PhoneFactory还是调了ImsPhoneFactory的makePhone()方法
/**
* Makes a {@link ImsPhone} object.
* @return the {@code ImsPhone} object or null if the exception occured
*/
public static ImsPhone makeImsPhone(PhoneNotifier phoneNotifier, Phone defaultPhone) {
return ImsPhoneFactory.makePhone(sContext, phoneNotifier, defaultPhone);
}
而ImsPhoneFactory中只有一个方法,也就是
public static ImsPhone makePhone(Context context,
PhoneNotifier phoneNotifier, Phone defaultPhone) {
try {
//直接new ImsPhone,在ImsPhone的构造方法中又
//会new ImsPhoneCallTracker,在ImsPhoneCallTracker的
//构造方法中,会执行获取IMS注册状态的流程(第二节会详细讲)
return new ImsPhone(context, phoneNotifier, defaultPhone);
} catch (Exception e) {
Rlog.e("VoltePhoneFactory", "makePhone", e);
return null;
}
}
到这里,ImsPhone就被创建啦。
1.4 通知Modem turn On IMS
在PhoneBase.java的mImsIntentReceiver广播接收器中,除了创建ImsPhone对象,还调了ImsManager.java的updateImsServiceConfig()方法更新配置信息。
public static void updateImsServiceConfig(Context context, int phoneId, boolean force) {
final ImsManager imsManager = ImsManager.getInstance(context, phoneId);
//imsManager.mConfigUpdated的值还是false的话,说明还没有更新过
if (imsManager != null && (!imsManager.mConfigUpdated || force)) {
try {
//如果Volte、VideoCall、WfC(Wifi Calling)任意一项可用且enable的话,那么就要turn On IMS
boolean turnOn = imsManager.updateVolteFeatureValue();
turnOn |= imsManager.updateVideoCallFeatureValue();
turnOn |= imsManager.updateWfcFeatureAndProvisionedValues();
if (turnOn) {
//通知modem turn On IMS,不同平台有不同的实现
imsManager.turnOnIms();
} else if (getBooleanCarrierConfig(context,
CarrierConfigManager.KEY_CARRIER_ALLOW_TURNOFF_IMS_BOOL)) {
imsManager.turnOffIms();
}
//把标志位设成true,除非强制更新,否则不会再进来这里
imsManager.mConfigUpdated = true;
} catch (ImsException e) {
loge("updateImsServiceConfig: " + e);
imsManager.mConfigUpdated = false;
}
}
}
二、获取IMS注册状态
(如果图片看不清的话,可以右键选择在查看图片,或者把图片另存到自己电脑再查看。)
获取IMS注册状态本来也属于开机是执行的流程,但是本段流程有点长,所以把它独立起来讲。紧跟着1.3节创建ImsPhone,在ImsPhone的构造方法中:
ImsPhone(Context context, PhoneNotifier notifier, Phone defaultPhone) {
super("ImsPhone", context, notifier);
mDefaultPhone = (PhoneBase) defaultPhone;
//初始化ImsPhoneCallTracker对象
mCT = new ImsPhoneCallTracker(this);
......
}
而ImsPhoneCallTracker的构造方法中
ImsPhoneCallTracker(ImsPhone phone) {
this.mPhone = phone;
IntentFilter intentfilter = new IntentFilter();
intentfilter.addAction(ImsManager.ACTION_IMS_INCOMING_CALL);
//注册监听来电的广播
mPhone.getContext().registerReceiver(mReceiver, intentfilter);
//启动新线程
Thread t = new Thread() {
public void run() {
//执行Opens IMS service的流程
getImsService();
}
};
t.start();
}
private void getImsService() {
if (DBG) log("getImsService");
//先得到ImsManager对象
mImsManager = ImsManager.getInstance(mPhone.getContext(), mPhone.getPhoneId());
try {
//然后调用它的open()方法获取到Service Id
//这里传递进去的serviceClass是ImsServiceClass.MMTEL(它的值是1)
mServiceId = mImsManager.open(ImsServiceClass.MMTEL,
createIncomingCallPendingIntent(),
mImsConnectionStateListener);
// Get the ECBM interface and set IMSPhone's listener object for notifications
getEcbmInterface().setEcbmStateListener(mPhone.mImsEcbmStateListener);
if (mPhone.isInEcm()) {
// Call exit ECBM which will invoke onECBMExited
mPhone.exitEmergencyCallbackMode();
}
int mPreferredTtyMode = Settings.Secure.getInt(
mPhone.getContext().getContentResolver(),
Settings.Secure.PREFERRED_TTY_MODE,
Phone.TTY_MODE_OFF);
mImsManager.setUiTTYMode(mPhone.getContext(), mServiceId, mPreferredTtyMode, null);
} catch (ImsException e) {
loge("getImsService: " + e);
//Leave mImsManager as null, then CallStateException will be thrown when dialing
mImsManager = null;
}
}
步骤3~5,主要对获取到的Service Id进行判断和存储
步骤6: ImsServiceSub.java的getServiceId()方法中,主要就是获取Service Id和向modem获取IMS的注册状态。
/**
* Creates/updates the tracker object for the service class
* @param serviceClass a service class specified in {@link ImsServiceClass}
* For VoLTE service, it MUST be a {@link ImsServiceClass#MMTEL}.
* @param incomingCallPendingIntent When an incoming call is received,
* the IMS service will call {@link PendingIntent#send(Context, int, Intent)} to
* send back the intent to the caller with {@link #INCOMING_CALL_RESULT_CODE}
* as the result code and the intent to fill in the call ID; It cannot be null
* @param listener To listen to IMS registration events; It cannot be null
* @return Unique identifier
*/
public int getServiceId(int serviceClass, PendingIntent intent,
IImsRegistrationListener listener) {
int serviceId = 0; // O is not used - boundary value between error and correct value
//如果serviceClass不是1
if(serviceClass != ImsServiceClass.MMTEL) {
//则返回-2,service Id为负数说明出错了。
serviceId = CODE_SERVICE_CLASS_NOT_SUPPORTED;
} else {
//先尝试从Map集合中获取ImsServiceClassTracker 对象
ImsServiceClassTracker tracker = mTrackerTable.get(new Integer(serviceClass));
//获取不到
if (tracker == null) {
//创建ImsServiceClassTracker对象
tracker = new ImsServiceClassTracker(serviceClass, intent, listener, mCi,
mContext, this);
tracker.updateFeatureCapabilities(isVideoSupported(), isVoiceSupported());
//将ImsServiceClassTracker对象放入Map集合中
mTrackerTable.put(new Integer(serviceClass), tracker);
mServiceIdTable.put(new Integer(tracker.getServiceId()), tracker);
} else {
//更新ImsServiceClassTracker对象的内容
tracker.mIncomingCallIntent = intent;
tracker.mRegListener = listener;
}
createFeatureCapabilityCallBackThread(listener);
//获取到service Id
serviceId = tracker.getServiceId();
}
Log.d(LOG_TAG, "getServiceId returns " + serviceId);
//向modem获取IMS的注册状态
mCi.getImsRegistrationState(mHandler.obtainMessage(EVENT_IMS_STATE_DONE));
return serviceId;
}
modem返回IMS的注册状态信息
@Override
public void handleMessage(Message msg) {
.....
case EVENT_IMS_STATE_DONE:
ar = (AsyncResult) msg.obj;
//对状态的处理
handleImsStateChanged(ar);
break;
.....
}
private void handleImsStateChanged(AsyncResult ar) {
log("handleImsStateChanged");
//注册失败的错误码
int errorCode = ImsReasonInfo.CODE_UNSPECIFIED;
//注册失败的消息,如REG99-unable to connect
String errorMessage = null;
//注册状态有三种,分别是REGISTERED、NOT_REGISTERED、REGISTERING。
int regState = ImsQmiIF.Registration.NOT_REGISTERED;
//LTE、WIFI或者IWLAN,默认是UNKNOWN
int imsRat = ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN;
if (ar != null && ar.exception == null && ar.result instanceof ImsQmiIF.Registration) {
ImsQmiIF.Registration registration = (ImsQmiIF.Registration) ar.result;
errorCode = registration.hasErrorCode() ? registration.getErrorCode()
: ImsReasonInfo.CODE_UNSPECIFIED;
errorMessage = registration.hasErrorMessage() ? registration
.getErrorMessage() : null;
regState = registration.hasState() ? registration.getState()
: ImsQmiIF.Registration.NOT_REGISTERED;
imsRat = getRilRadioTech(registration);
if (regState != ImsQmiIF.Registration.NOT_REGISTERED) {
mCi.queryServiceStatus(mHandler.obtainMessage(EVENT_GET_SRV_STATUS));
}
} else {
loge("handleImsStateChanged error");
}
//创建ImsReasonInfo对象,传递到UI的话可以通过状态栏的形式提醒用户
ImsReasonInfo imsReasonInfo = new ImsReasonInfo(
ImsReasonInfo.CODE_REGISTRATION_ERROR,
errorCode, errorMessage);
for (Map.Entry<Integer, ImsServiceClassTracker> e : mTrackerTable.entrySet()) {
//创建新线程,把状态信息传递给ImsPhoneCallTracker
createRegCallBackThread(e.getValue().mRegListener, regState, imsReasonInfo, imsRat);
}
}
最终IMS的注册信息会传递到ImsPhoneCallTracker.java中
/**
* Listen to the IMS service state change
*
*/
private ImsConnectionStateListener mImsConnectionStateListener =
new ImsConnectionStateListener() {
@Override
public void onImsConnected() {
if (DBG) log("onImsConnected");
//更新Service的状态
mPhone.setServiceState(ServiceState.STATE_IN_SERVICE);
//把IMSPhone的mImsRegistered值设置成true
mPhone.setImsRegistered(true);
}
......
}
谷歌原生的流程到这里就结束了,ImsPhoneCallTracker.java还处于Framework层,如果想把IMS的注册状态上报给APP层,还需自己添加代码。比如添加并实现mPhone.notifyIMSRegisterStateChanged();
假如IMS已注册上,并且WiFi Calling又是Enable的话,那么插入某些运营商(如T-mobile)的SIM卡就可以拨打WiFi Call。由于这篇文章目前已经写得较长,所以这里暂时就不分析IMS Call的内容了,更多内容将会写在下一篇《Android 6.0 IMS流程(二)——IMS Call》
- 1楼zzx4ever昨天 17:31
- 写得真好,非常详细,受益匪浅!n非常期待后续大作nAndroid 6.0 IMS流程(二)——IMS Cal