Android 6.0 IMS流程(1)——IMS开机初始化

Android 6.0 IMS流程(一)——IMS开机初始化

最近在学习Wifi Calling,Wifi Calling走的是IMS的流程,本文会讲解在开机时IMS初始化时相关的内容。

一、IMS开机初始化

Android 6.0 IMS流程(1)——IMS开机初始化
(如果图片看不清的话,可以右键选择在查看图片,或者把图片另存到自己电脑再查看。)


http://blog.****.net/linyongan


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的启动

Android 6.0 IMS流程(1)——IMS开机初始化
步骤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

Android 6.0 IMS流程(1)——IMS开机初始化

    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

Android 6.0 IMS流程(1)——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注册状态

Android 6.0 IMS流程(1)——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