Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit cf2980ff authored by Wink Saville's avatar Wink Saville Committed by Android Git Automerger
Browse files

am 29d1dde9: Merge "Data connection retry pattern could be broken by PDP...

am 29d1dde9: Merge "Data connection retry pattern could be broken by PDP sharing" into honeycomb-LTE

* commit '29d1dde9':
  Data connection retry pattern could be broken by PDP sharing
parents 229ff64b 29d1dde9
Loading
Loading
Loading
Loading
+2 −16
Original line number Diff line number Diff line
@@ -16,8 +16,6 @@

package com.android.internal.telephony;

import android.app.PendingIntent;

import android.util.Log;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -49,8 +47,6 @@ public class ApnContext {

    String mReason;

    PendingIntent mReconnectIntent;

    /**
     * user/app requested connection on this APN
     */
@@ -90,9 +86,9 @@ public class ApnContext {

    public synchronized void setDataConnectionAc(DataConnectionAc dcac) {
        if (dcac != null) {
            dcac.addApnContext(this);
            dcac.addApnContextSync(this);
        } else {
            if (mDataConnectionAc != null) mDataConnectionAc.removeApnContext(this);
            if (mDataConnectionAc != null) mDataConnectionAc.removeApnContextSync(this);
        }
        mDataConnectionAc = dcac;
    }
@@ -169,16 +165,6 @@ public class ApnContext {
        return mReason;
    }

    public synchronized void setReconnectIntent(PendingIntent intent) {
        if (DBG)
            log("set ReconnectIntent for type " + mApnType);
        mReconnectIntent = intent;
    }

    public synchronized PendingIntent getReconnectIntent() {
        return mReconnectIntent;
    }

    public boolean isReady() {
        return mDataEnabled.get() && mDependencyMet.get();
    }
+42 −1
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import com.android.internal.util.Protocol;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;

import android.app.PendingIntent;
import android.net.LinkAddress;
import android.net.LinkCapabilities;
import android.net.LinkProperties;
@@ -35,8 +36,10 @@ import android.os.Parcelable;
import android.os.SystemProperties;
import android.text.TextUtils;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

/**
@@ -68,6 +71,8 @@ public abstract class DataConnection extends StateMachine {
    protected static int mCount;
    protected AsyncChannel mAc;

    private List<ApnContext> mApnList = null;
    PendingIntent mReconnectIntent = null;

    /**
     * Used internally for saving connecting parameters.
@@ -250,6 +255,8 @@ public abstract class DataConnection extends StateMachine {
            addState(mDisconnectingState, mDefaultState);
            addState(mDisconnectingErrorCreatingConnection, mDefaultState);
        setInitialState(mInactiveState);

        mApnList = new ArrayList<ApnContext>();
        if (DBG) log("DataConnection constructor X");
    }

@@ -662,7 +669,41 @@ public abstract class DataConnection extends StateMachine {
                    mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_REFCOUNT, mRefCount);
                    break;
                }

                case DataConnectionAc.REQ_ADD_APNCONTEXT: {
                    ApnContext apnContext = (ApnContext) msg.obj;
                    if (VDBG) log("REQ_ADD_APNCONTEXT apn=" + apnContext.getApnType());
                    if (!mApnList.contains(apnContext)) {
                        mApnList.add(apnContext);
                    }
                    mAc.replyToMessage(msg, DataConnectionAc.RSP_ADD_APNCONTEXT);
                    break;
                }
                case DataConnectionAc.REQ_REMOVE_APNCONTEXT: {
                    ApnContext apnContext = (ApnContext) msg.obj;
                    if (VDBG) log("REQ_REMOVE_APNCONTEXT apn=" + apnContext.getApnType());
                    mApnList.remove(apnContext);
                    mAc.replyToMessage(msg, DataConnectionAc.RSP_REMOVE_APNCONTEXT);
                    break;
                }
                case DataConnectionAc.REQ_GET_APNCONTEXT_LIST: {
                    if (VDBG) log("REQ_GET_APNCONTEXT_LIST num in list=" + mApnList.size());
                    mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_APNCONTEXT_LIST,
                                       new ArrayList(mApnList));
                    break;
                }
                case DataConnectionAc.REQ_SET_RECONNECT_INTENT: {
                    PendingIntent intent = (PendingIntent) msg.obj;
                    if (VDBG) log("REQ_SET_RECONNECT_INTENT");
                    mReconnectIntent = intent;
                    mAc.replyToMessage(msg, DataConnectionAc.RSP_SET_RECONNECT_INTENT);
                    break;
                }
                case DataConnectionAc.REQ_GET_RECONNECT_INTENT: {
                    if (VDBG) log("REQ_GET_RECONNECT_INTENT");
                    mAc.replyToMessage(msg, DataConnectionAc.RSP_GET_RECONNECT_INTENT,
                                       mReconnectIntent);
                    break;
                }
                case EVENT_CONNECT:
                    if (DBG) log("DcDefaultState: msg.what=EVENT_CONNECT, fail not expected");
                    ConnectionParams cp = (ConnectionParams) msg.obj;
+140 −13
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.internal.telephony;
import com.android.internal.util.AsyncChannel;
import com.android.internal.util.Protocol;

import android.app.PendingIntent;
import android.net.LinkCapabilities;
import android.net.LinkProperties;
import android.net.ProxyProperties;
@@ -26,8 +27,6 @@ import android.os.Message;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

/**
 * AsyncChannel to a DataConnection
@@ -35,7 +34,6 @@ import java.util.List;
public class DataConnectionAc extends AsyncChannel {
    private static final boolean DBG = false;
    private String mLogTag;
    private List<ApnContext> mApnList = null;

    public DataConnection dataConnection;

@@ -68,6 +66,21 @@ public class DataConnectionAc extends AsyncChannel {
    public static final int REQ_GET_REFCOUNT = BASE + 16;
    public static final int RSP_GET_REFCOUNT = BASE + 17;

    public static final int REQ_ADD_APNCONTEXT = BASE + 18;
    public static final int RSP_ADD_APNCONTEXT = BASE + 19;

    public static final int REQ_REMOVE_APNCONTEXT = BASE + 20;
    public static final int RSP_REMOVE_APNCONTEXT = BASE + 21;

    public static final int REQ_GET_APNCONTEXT_LIST = BASE + 22;
    public static final int RSP_GET_APNCONTEXT_LIST = BASE + 23;

    public static final int REQ_SET_RECONNECT_INTENT = BASE + 24;
    public static final int RSP_SET_RECONNECT_INTENT = BASE + 25;

    public static final int REQ_GET_RECONNECT_INTENT = BASE + 26;
    public static final int RSP_GET_RECONNECT_INTENT = BASE + 27;

    /**
     * enum used to notify action taken or necessary to be
     * taken after the link property is changed.
@@ -91,7 +104,6 @@ public class DataConnectionAc extends AsyncChannel {
    public DataConnectionAc(DataConnection dc, String logTag) {
        dataConnection = dc;
        mLogTag = logTag;
        mApnList = Collections.synchronizedList(new ArrayList<ApnContext>());
    }

    /**
@@ -379,32 +391,147 @@ public class DataConnectionAc extends AsyncChannel {
    }

    /**
     * Add ApnContext association.
     * Request to add ApnContext association.
     * Response RSP_ADD_APNCONTEXT when complete.
     */
    public void reqAddApnContext(ApnContext apnContext) {
        Message response = sendMessageSynchronously(REQ_ADD_APNCONTEXT, apnContext);
        if (DBG) log("reqAddApnContext");
    }

    /**
     * Add ApnContext association synchronoulsy.
     *
     * @param ApnContext to associate
     */
    public void addApnContext(ApnContext apnContext) {
        if (!mApnList.contains(apnContext)) {
            mApnList.add(apnContext);
    public void addApnContextSync(ApnContext apnContext) {
        Message response = sendMessageSynchronously(REQ_ADD_APNCONTEXT, apnContext);
        if ((response != null) && (response.what == RSP_ADD_APNCONTEXT)) {
            if (DBG) log("addApnContext ok");
        } else {
            log("addApnContext error response=" + response);
        }
    }

    /**
     * Request to remove ApnContext association.
     * Response RSP_REMOVE_APNCONTEXT when complete.
     */
    public void reqRemomveApnContext(ApnContext apnContext) {
        Message response = sendMessageSynchronously(REQ_REMOVE_APNCONTEXT, apnContext);
        if (DBG) log("reqRemomveApnContext");
    }

    /**
     * Remove ApnContext associateion.
     *
     * @param ApnContext to dissociate
     */
    public void removeApnContext(ApnContext apnContext) {
        mApnList.remove(apnContext);
    public void removeApnContextSync(ApnContext apnContext) {
        Message response = sendMessageSynchronously(REQ_REMOVE_APNCONTEXT, apnContext);
        if ((response != null) && (response.what == RSP_REMOVE_APNCONTEXT)) {
            if (DBG) log("removeApnContext ok");
        } else {
            log("removeApnContext error response=" + response);
        }
    }

    /**
     * Request to retrive ApnContext List associated with DC.
     * Response RSP_GET_APNCONTEXT_LIST when complete.
     */
    public void reqGetApnList(ApnContext apnContext) {
        Message response = sendMessageSynchronously(REQ_GET_APNCONTEXT_LIST);
        if (DBG) log("reqGetApnList");
    }

    /**
     * Retrieve Collection of ApnContext from the response message.
     *
     * @param Message sent from DC in response to REQ_GET_APNCONTEXT_LIST.
     * @return Collection of ApnContext
     */
    public Collection<ApnContext> rspApnList(Message response) {
        Collection<ApnContext> retVal = (Collection<ApnContext>)response.obj;
        if (retVal == null) retVal = new ArrayList<ApnContext>();
        return retVal;
    }

    /**
     * Retrieve collection of ApnContext currently associated with the DataConnectionAc.
     * Retrieve collection of ApnContext currently associated with
     * the DataConnectionA synchronously.
     *
     * @return Collection of ApnContext
     */
    public Collection<ApnContext> getApnList() {
        return mApnList;
    public Collection<ApnContext> getApnListSync() {
        Message response = sendMessageSynchronously(REQ_GET_APNCONTEXT_LIST);
        if ((response != null) && (response.what == RSP_GET_APNCONTEXT_LIST)) {
            if (DBG) log("getApnList ok");
            return rspApnList(response);
        } else {
            log("getApnList error response=" + response);
            // return dummy list with no entry
            return new ArrayList<ApnContext>();
        }
    }

    /**
     * Request to set Pending ReconnectIntent to DC.
     * Response RSP_SET_RECONNECT_INTENT when complete.
     */
    public void reqSetReconnectIntent(PendingIntent intent) {
        Message response = sendMessageSynchronously(REQ_SET_RECONNECT_INTENT, intent);
        if (DBG) log("reqSetReconnectIntent");
    }

    /**
     * Set pending reconnect intent to DC synchronously.
     *
     * @param PendingIntent to set.
     */
    public void setReconnectIntentSync(PendingIntent intent) {
        Message response = sendMessageSynchronously(REQ_SET_RECONNECT_INTENT, intent);
        if ((response != null) && (response.what == RSP_SET_RECONNECT_INTENT)) {
            if (DBG) log("setReconnectIntent ok");
        } else {
            log("setReconnectIntent error response=" + response);
        }
    }

    /**
     * Request to get Pending ReconnectIntent to DC.
     * Response RSP_GET_RECONNECT_INTENT when complete.
     */
    public void reqGetReconnectIntent() {
        Message response = sendMessageSynchronously(REQ_GET_RECONNECT_INTENT);
        if (DBG) log("reqGetReconnectIntent");
    }

    /**
     * Retrieve reconnect intent from response message from DC.
     *
     * @param Message which contains the reconnect intent.
     * @return PendingIntent from the response.
     */
    public PendingIntent rspReconnectIntent(Message response) {
        PendingIntent retVal = (PendingIntent) response.obj;
        return retVal;
    }

    /**
     * Retrieve reconnect intent currently set in DC synchronously.
     *
     * @return PendingIntent reconnect intent current ly set in DC
     */
    public PendingIntent getReconnectIntentSync() {
        Message response = sendMessageSynchronously(REQ_GET_RECONNECT_INTENT);
        if ((response != null) && (response.what == RSP_GET_RECONNECT_INTENT)) {
            if (DBG) log("getReconnectIntent ok");
            return rspReconnectIntent(response);
        } else {
            log("getReconnectIntent error response=" + response);
            return null;
        }
    }

    private void log(String s) {
+121 −66
Original line number Diff line number Diff line
@@ -125,15 +125,21 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
        if (DBG) log("GPRS reconnect alarm. Previous state was " + mState);

        String reason = intent.getStringExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON);
        String type = intent.getStringExtra(INTENT_RECONNECT_ALARM_EXTRA_TYPE);
        ApnContext apnContext = mApnContexts.get(type);
        if (apnContext != null) {
        int connectionId = intent.getIntExtra(INTENT_RECONNECT_ALARM_EXTRA_TYPE, -1);

        DataConnectionAc dcac= mDataConnectionAsyncChannels.get(connectionId);

        if (dcac != null) {
            for (ApnContext apnContext : dcac.getApnListSync()) {
                apnContext.setReason(reason);
                if (apnContext.getState() == State.FAILED) {
                    apnContext.setState(State.IDLE);
                }
                sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA, apnContext));
            }
            // Alram had expired. Clear pending intent recorded on the DataConnection.
            dcac.setReconnectIntentSync(null);
        }
    }

    /** Watches for changes to the APN db. */
@@ -591,17 +597,25 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
    }

    private void setupDataOnReadyApns(String reason) {
        // Stop reconnect alarms on all data connections pending
        // retry. Reset ApnContext state to IDLE.
        for (DataConnectionAc dcac : mDataConnectionAsyncChannels.values()) {
            if (dcac.getReconnectIntentSync() != null) {
                cancelReconnectAlarm(dcac);
                if (dcac.dataConnection != null) {
                    dcac.dataConnection.resetRetryCount();
                }

                Collection<ApnContext> apnList = dcac.getApnListSync();
                for (ApnContext apnContext : apnList) {
                    apnContext.setState(State.IDLE);
                }
            }
        }

        // Only check for default APN state
        for (ApnContext apnContext : mApnContexts.values()) {
            if (apnContext.isReady()) {
                if (apnContext.getState() == State.FAILED) {
                    cleanApnContextBeforeRestart(apnContext);
                    if (apnContext.getDataConnection() != null) {
                        apnContext.getDataConnection().resetRetryCount();
                    }
                }
                // Do not start ApnContext in SCANNING state
                // FAILED state must be reset to IDLE by now
                if (apnContext.getState() == State.IDLE) {
                    apnContext.setReason(reason);
                    trySetupData(apnContext);
@@ -751,53 +765,70 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
        if (DBG) {
            log("cleanUpConnection: tearDown=" + tearDown + " reason=" + apnContext.getReason());
        }
        if (tearDown && cleanApnContextBeforeRestart(apnContext)) {
            // if the request is tearDown and ApnContext does not hold an active connection,
            // we're ok to return here.
            return;
        }

        DataConnectionAc dcac = apnContext.getDataConnectionAc();
        if (tearDown && (dcac != null)) {
        if (tearDown) {
            boolean isConnected = (apnContext.getState() != State.IDLE
                                   && apnContext.getState() != State.FAILED);
            if (!isConnected) {
                // The request is tearDown and but ApnContext is not connected.
                // If apnContext is not enabled anymore, break the linkage to the DCAC/DC.
                apnContext.setState(State.IDLE);
                if (!apnContext.isReady()) {
                    apnContext.setDataConnection(null);
                    apnContext.setDataConnectionAc(null);
                }
            } else {
                // Connection is still there. Try to clean up.
                if (dcac != null) {
                    if (apnContext.getState() != State.DISCONNECTING) {
                        if (DBG) log("cleanUpConnection: tearing down");
                        Message msg = obtainMessage(EVENT_DISCONNECT_DONE, apnContext);
                        apnContext.getDataConnection().tearDown(apnContext.getReason(), msg);
                        apnContext.setState(State.DISCONNECTING);
                    } else {
                        // apn is connected but no reference to dcac.
                        // Should not be happen, but reset the state in case.
                        apnContext.setState(State.IDLE);
                        mPhone.notifyDataConnection(apnContext.getReason(),
                                                    apnContext.getApnType());
                    }
                }
            }
        } else {
            // force clean up the data connection.
            if (dcac != null) dcac.resetSync();
            apnContext.setState(State.IDLE);
            mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
            apnContext.setDataConnection(null);
            apnContext.setDataConnectionAc(null);
        }

        // make sure reconnection alarm is cleaned up if there is no ApnContext
        // associated to the connection.
        if (dcac != null) {
            Collection<ApnContext> apnList = dcac.getApnListSync();
            if (apnList.isEmpty()) {
                cancelReconnectAlarm(dcac);
            }
        }
    }

    /**
     * @param APNContext to clean
     * @return true if ApnContext is not connected anymore.
     *         false if ApnContext still holds a connection.
     * Cancels the alarm associated with DCAC.
     *
     * @param DataConnectionAc on which the alarm should be stopped.
     */
    private boolean cleanApnContextBeforeRestart(ApnContext apnContext) {
        if (apnContext == null) return true;
    private void cancelReconnectAlarm(DataConnectionAc dcac) {
        if (dcac == null) return;

        // Clear the reconnect alarm, if set.
        if (apnContext.getReconnectIntent() != null) {
        PendingIntent intent = dcac.getReconnectIntentSync();

        if (intent != null) {
                AlarmManager am =
                    (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE);
            am.cancel(apnContext.getReconnectIntent());
            apnContext.setReconnectIntent(null);
        }

        if (apnContext.getState() == State.IDLE || apnContext.getState() == State.DISCONNECTING) {
            if (DBG) log("cleanUpConnection: state= " + apnContext.getState());
            return true;
                am.cancel(intent);
                dcac.setReconnectIntentSync(null);
        }

        if (apnContext.getState() == State.FAILED) {
            apnContext.setState(State.IDLE);
            return true;
        }
        return false;
    }

    /**
@@ -936,17 +967,26 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
                configureRetry(dc, apnContext.getApnType());
            }
            apnContext.setDataConnectionAc(dcac);
            apnContext.setApnSetting(apn);
            apnContext.setDataConnection(dc);
        }

        apnContext.setApnSetting(apn);
        apnContext.setState(State.INITING);
        mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
        // If reconnect alarm is active on this DataConnection, wait for the alarm being
        // fired so that we don't disruppt data retry pattern engaged.
        if (apnContext.getDataConnectionAc().getReconnectIntentSync() != null) {
            if (DBG) log("setupData: data reconnection pending");
            apnContext.setState(State.FAILED);
            mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
            return true;
        }

        Message msg = obtainMessage();
        msg.what = EVENT_DATA_SETUP_COMPLETE;
        msg.obj = apnContext;
        dc.bringUp(msg, apn);

        apnContext.setState(State.INITING);
        mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());
        if (DBG) log("setupData: initing!");
        return true;
    }
@@ -1063,13 +1103,12 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
            // no associated DataConnection found. Ignore.
            if (dcac == null) continue;

            Collection<ApnContext> apns = dcac.getApnList();
            Collection<ApnContext> apns = dcac.getApnListSync();

            // filter out ApnContext with "Connected" state.
            ArrayList<ApnContext> connectedApns = new ArrayList<ApnContext>();
            for (ApnContext apnContext : apns) {
                if ((apnContext != null) &&
                    (apnContext.getState() == State.CONNECTED)) {
                if (apnContext.getState() == State.CONNECTED) {
                    connectedApns.add(apnContext);
                }
            }
@@ -1449,21 +1488,28 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
                + (delay / 1000) + "s");
        }

        DataConnectionAc dcac = apnContext.getDataConnectionAc();

        if ((dcac == null) || (dcac.dataConnection == null)) {
            // should not happen, but just in case.
            loge("null dcac or dc.");
            return;
        }

        AlarmManager am =
            (AlarmManager) mPhone.getContext().getSystemService(Context.ALARM_SERVICE);

        // TODO : Register the receiver only once maybe in baseclass.
        IntentFilter filter = new IntentFilter();
        filter.addAction(INTENT_RECONNECT_ALARM + '.'+apnContext.getApnType());
        mPhone.getContext().registerReceiver(mIntentReceiver, filter, null, mPhone);

        Intent intent = new Intent(INTENT_RECONNECT_ALARM + '.' + apnContext.getApnType());
        Intent intent = new Intent(INTENT_RECONNECT_ALARM + '.' +
                                   dcac.dataConnection.getDataConnectionId());
        intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_REASON, apnContext.getReason());
        intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_TYPE, apnContext.getApnType());
        apnContext.setReconnectIntent(PendingIntent.getBroadcast (
                mPhone.getContext(), 0, intent, 0));
        intent.putExtra(INTENT_RECONNECT_ALARM_EXTRA_TYPE,
                        dcac.dataConnection.getDataConnectionId());

        PendingIntent alarmIntent = PendingIntent.getBroadcast (mPhone.getContext(), 0,
                                                                intent, 0);
        dcac.setReconnectIntentSync(alarmIntent);
        am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                SystemClock.elapsedRealtime() + delay, apnContext.getReconnectIntent());
                SystemClock.elapsedRealtime() + delay, alarmIntent);

    }

@@ -1768,9 +1814,6 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
        }

        apnContext.setState(State.IDLE);
        apnContext.setApnSetting(null);
        apnContext.setDataConnection(null);
        apnContext.setDataConnectionAc(null);

        mPhone.notifyDataConnection(apnContext.getReason(), apnContext.getApnType());

@@ -1779,6 +1822,9 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
        if (!isConnected()) {
            if (mPhone.getServiceStateTracker().processPendingRadioPowerOffAfterDataOff()) {
                // Radio will be turned off. No need to retry data setup
                apnContext.setApnSetting(null);
                apnContext.setDataConnection(null);
                apnContext.setDataConnectionAc(null);
                return;
            }
        }
@@ -1790,6 +1836,10 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
            // we're not tying up the RIL command channel.
            // This also helps in any external dependency to turn off the context.
            startAlarmForReconnect(APN_DELAY_MILLIS, apnContext);
        } else {
            apnContext.setApnSetting(null);
            apnContext.setDataConnection(null);
            apnContext.setDataConnectionAc(null);
        }
    }

@@ -1915,6 +1965,11 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker {
                    " status=" + status);
        }

        // install reconnect intent filter for this data connection.
        IntentFilter filter = new IntentFilter();
        filter.addAction(INTENT_RECONNECT_ALARM + '.' + id);
        mPhone.getContext().registerReceiver(mIntentReceiver, filter, null, mPhone);

        if (DBG) log("createDataConnection() X id=" + id);
        return conn;
    }