mobile data setup retry(移动网络--数据业务重试)

not all data call setup will be successful 100%, and if failure encountered, retry is just the normal way DCTs will go.

Dct uses PendingIntents to trigger retry and there are two kinds for the Intents:

INTENT_RECONNECT_ALARM and INTENT_RESTART_TRYSETUP_ALARM

DcTrackerBase.java

    protected static final String INTENT_RECONNECT_ALARM =
            "com.android.internal.telephony.data-reconnect";
    protected static final String INTENT_RECONNECT_ALARM_EXTRA_TYPE = "reconnect_alarm_extra_type";
    protected static final String INTENT_RECONNECT_ALARM_EXTRA_REASON =
            "reconnect_alarm_extra_reason";

    protected static final String INTENT_RESTART_TRYSETUP_ALARM =
            "com.android.internal.telephony.data-restart-trysetup";
    protected static final String INTENT_RESTART_TRYSETUP_ALARM_EXTRA_TYPE =
            "restart_trysetup_alarm_extra_type";
when Dcts were initialized, each APN type instance will register Receiver for the two

DcTracker.java

    public DcTracker(PhoneBase p) {
...
        for (ApnContext apnContext : mApnContexts.values()) {
            // Register the reconnect and restart actions.
            IntentFilter filter = new IntentFilter();
            filter.addAction(INTENT_RECONNECT_ALARM + '.' + apnContext.getApnType());
            filter.addAction(INTENT_RESTART_TRYSETUP_ALARM + '.' + apnContext.getApnType());
            mPhone.getContext().registerReceiver(mIntentReceiver, filter, null, mPhone);
        }
and the code that handle the case were:

DcTrackerBase.java

    protected BroadcastReceiver mIntentReceiver = new BroadcastReceiver ()
    {
        @Override
        public void onReceive(Context context, Intent intent)
        {
...
            } else if (action.startsWith(INTENT_RECONNECT_ALARM)) {
                if (DBG) log("Reconnect alarm. Previous state was " + mState);
                onActionIntentReconnectAlarm(intent);
            } else if (action.startsWith(INTENT_RESTART_TRYSETUP_ALARM)) {
                if (DBG) log("Restart trySetup alarm");
                onActionIntentRestartTrySetupAlarm(intent);
look, the details of the handler were warpped into two functions.

both the functions will send msg to DcT to tell her to setup data call:

    protected void onActionIntentReconnectAlarm(Intent intent) {
        String reason = intent.getStringExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON);
        String apnType = intent.getStringExtra(INTENT_RECONNECT_ALARM_EXTRA_TYPE);

        ApnContext apnContext = mApnContexts.get(apnType);
...
        if ((apnContext != null) && (apnContext.isEnabled())) {
...
           sendMessage(obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, apnContext));

            apnContext.setReconnectIntent(null);
        }
    }

    protected void onActionIntentRestartTrySetupAlarm(Intent intent) {
        String apnType = intent.getStringExtra(INTENT_RESTART_TRYSETUP_ALARM_EXTRA_TYPE);
        ApnContext apnContext = mApnContexts.get(apnType);
...
        sendMessage(obtainMessage(DctConstants.EVENT_TRY_SETUP_DATA, apnContext));
    }
you will find that the latter is more directly, so the scenarios to trigger this kind should be more restricted.

OK, let's back and find where the two types was really triggered.

one is

DcTracker.java

    private void startAlarmForReconnect(int delay, ApnContext apnContext) {
        String apnType = apnContext.getApnType();

        Intent intent = new Intent(INTENT_RECONNECT_ALARM + "." + apnType);
        intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON, apnContext.getReason());
        intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_TYPE, apnType);
...
        PendingIntent alarmIntent = PendingIntent.getBroadcast (mPhone.getContext(), 0,
                                        intent, PendingIntent.FLAG_UPDATE_CURRENT);
        apnContext.setReconnectIntent(alarmIntent);
        mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                SystemClock.elapsedRealtime() + delay, alarmIntent);
    }
and the other

    private void startAlarmForRestartTrySetup(int delay, ApnContext apnContext) {
        String apnType = apnContext.getApnType();
        Intent intent = new Intent(INTENT_RESTART_TRYSETUP_ALARM + "." + apnType);
        intent.putExtra(INTENT_RESTART_TRYSETUP_ALARM_EXTRA_TYPE, apnType);
...
        PendingIntent alarmIntent = PendingIntent.getBroadcast (mPhone.getContext(), 0,
                                        intent, PendingIntent.FLAG_UPDATE_CURRENT);
        apnContext.setReconnectIntent(alarmIntent);
        mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                SystemClock.elapsedRealtime() + delay, alarmIntent);
    }
then comes the question that where the two is called?

let's firstly focus on startAlarmForReconnect()

still DcTracker.java

    protected void onDataSetupCompleteError(AsyncResult ar) {
...
        ApnContext apnContext = null;
...
        if (apnContext.getWaitingApns().isEmpty()) {
...
            if (apnContext.getWaitingApnsPermFailCount() == 0) {
...
            } else {
                startAlarmForRestartTrySetup(APN_DELAY_MILLIS, apnContext);
            }
        } else {
            startAlarmForReconnect(APN_DELAY_MILLIS, apnContext);
        }
    }
look, the first trigger point is the time that setup data call completed while with error encountered.

and you can find out that this function will be called only if there is waiting APNs that try to setup datacall.

you can easily find our startAlarmForRestartTrySetup() here too! and it is the only place where it is called!

so you should know that this trigger point is just to make requests in order and fair: if current request failed while there is any other request still waiting, then let it go and delay our retry. or it will call the startAlarmForRestartTrySetup() only when getWaitingApnsPermFailCount is not 0(in fact there is refCounter in ApnContext for all waitingApns, and the initialization is by calling ApnContext.setWaitingApns(), which is called in trySetupData() from class DcTracker. once DcTracker received DataSetupComplete event and the permanent error detected, the refCounter decreased and related ApnSettings be removed from ApnContext's waitingApns list).

here the obj in ar.userObj should always be an instance of ApnContext or exceptions occurred.

ApnContext.java

    public synchronized void setWaitingApns(ArrayList<ApnSetting> waitingApns) {
        mWaitingApns = waitingApns;
        mWaitingApnsPermanentFailureCountDown.set(mWaitingApns.size());
    }
    public int getWaitingApnsPermFailCount() {
        return mWaitingApnsPermanentFailureCountDown.get();
    }
    public void decWaitingApnsPermFailCount() {
        mWaitingApnsPermanentFailureCountDown.decrementAndGet();
    }
...
    public synchronized void removeWaitingApn(ApnSetting apn) {
        if (mWaitingApns != null) {
            mWaitingApns.remove(apn);
        }
    }
DcTracker.java
    private boolean trySetupData(ApnContext apnContext) {
...
            if (apnContext.getState() == DctConstants.State.IDLE) {
                ArrayList<ApnSetting> waitingApns = buildWaitingApns(apnContext.getApnType());
                if (waitingApns.isEmpty()) {
...
                } else {
                    apnContext.setWaitingApns(waitingApns);
...

    @Override
    protected void onDataSetupComplete(AsyncResult ar) {
...
        if (ar.exception == null) {
...
        } else {
            cause = (DcFailCause) (ar.result);
...
            if (cause.isPermanentFail()) apnContext.decWaitingApnsPermFailCount();
            apnContext.removeWaitingApn(apnContext.getApnSetting());

let's back and the other place is

    protected void onDisconnectDone(int connId, AsyncResult ar) {
        ApnContext apnContext = null;
...
        if (apnContext.isReady() && retryAfterDisconnected(apnContext.getReason())) {
            startAlarmForReconnect(APN_DELAY_MILLIS, apnContext);
        } else {

the two conditions needs are both from ApnContext

ApnContext.java

    public boolean isReady() {
        return mDataEnabled.get() && mDependencyMet.get();
    }
and the latter function of retryAfterDisconnected() will be false if only the reson in ApnContext is Phone . REASON_RADIO_TURNED_OFF.

so unless radio off, mostly retry will be depended on the ready status of the ApnContext.
also as above, here the obj in ar.userObj should always be an instance of ApnContext or exceptions occurred.

seems all covered for retrying now, is it right?

DataConnection.java

    private boolean initConnection(ConnectionParams cp) {
...
        configureRetry(mApnSetting.canHandleType(PhoneConstants.APN_TYPE_DEFAULT));
        mRetryManager.setRetryCount(0);
        mRetryManager.setCurMaxRetryCount(mConnectionParams.mInitialMaxRetry);
        mRetryManager.setRetryForever(false);
do you notice that in our setup data call flow?

and here is define too

    static final int BASE = Protocol.BASE_DATA_CONNECTION;
...
    static final int EVENT_RETRY_CONNECTION = BASE + 10;

and the places trigger it is:

1.just setup data call completed and back to DataConnection before notice DcTracker.

    private class DcActivatingState extends State {
        @Override
        public boolean processMessage(Message msg) {
...
                case EVENT_SETUP_DATA_CONNECTION_DONE:
                    ar = (AsyncResult) msg.obj;
                    cp = (ConnectionParams) ar.userObj;

                    DataCallResponse.SetupResult result = onSetupConnectionCompleted(ar);
...
                    switch (result) {
...
                        case ERR_RilError:
                            int delay = mDcRetryAlarmController.getSuggestedRetryTime(
                                                                    DataConnection.this, ar);
...
                            if (delay >= 0) {
                                mDcRetryAlarmController.startRetryAlarm(EVENT_RETRY_CONNECTION,
                                                            mTag, delay);
                                transitionTo(mRetryingState);
                            } else {
...
                case EVENT_GET_LAST_FAIL_DONE:
                    ar = (AsyncResult) msg.obj;
                    cp = (ConnectionParams) ar.userObj;
                    if (cp.mTag == mTag) {
...
                        int retryDelay = mRetryManager.getRetryTimer();
...
                        if (mRetryManager.isRetryNeeded()) {
                            mDcRetryAlarmController.startRetryAlarm(EVENT_RETRY_CONNECTION, mTag,
                                                            retryDelay);
                            transitionTo(mRetryingState);
                        } else {
...
2.dataconnection was connected while now data connection lost detected from lower layer

    private class DcActiveState extends State {
..
        public boolean processMessage(Message msg) {
...
                case EVENT_LOST_CONNECTION: {
...
                    if (mRetryManager.isRetryNeeded()) {
                        // We're going to retry
                        int delayMillis = mRetryManager.getRetryTimer();
                        mDcRetryAlarmController.startRetryAlarm(EVENT_RETRY_CONNECTION, mTag,
                                delayMillis);
                        transitionTo(mRetryingState);
                    } else {
...

we all know that the retry will be delayed but how do we know the delay should be?

in DcTracker the value is configured as constant while in DataConnection you see that all are coming from RetryManger.

let's put it a little later.
//+++++++++++

//not done

//-------------------

from both the trigger points we start delayed retry by DcRetryAlarmController and switch to RetryingState waiting for the retry time coming.

in class DcRetryAlarmController, it will use PendingIntent to implement the delay and after timer come up, it send retry msg(EVENT_RETRY_CONNECTION) back to DataConection.(code not list here now)

the switching handlers is simple as exit() do nothing in DcActiveState(just wondering if it will be better that some variables cleanup be placed in its exit() handler):

    private class DcRetryingState extends State {
...
        @Override
        public void enter() {
            notifyAllOfDisconnectDcRetrying(Phone.REASON_LOST_DATA_CONNECTION);

            // Remove ourselves from cid mapping
            mDcController.removeActiveDcByCid(DataConnection.this);
            mCid = -1;
        }

and in the state, it handes as:

            switch (msg.what) {
                case EVENT_RETRY_CONNECTION: {
                    if (msg.arg1 == mTag) {
                        mRetryManager.increaseRetryCount();
...
                        onConnect(mConnectionParams);
                        transitionTo(mActivatingState);
just try connect and back to ActivatingState to wait for retry result.

now the question is, why the mechanism will be seperated into two place(or file or layer)?

and there both in DcTracker and DataConnection will retry, will it be redundant and will there be any conflict and timing problem caused?

waiting for your answers.

//+++++++++++

//not done

//-------------------

notice: here i just find another article also talk about the same topic, if you are interested, you can visit the site by

http://m.blog.csdn.net/blog/eqiang8271/7973334

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值