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

Commit 1e5c72c7 authored by Kazuhiro Ondo's avatar Kazuhiro Ondo Committed by Wink Saville
Browse files

Data connection retry pattern could be broken by PDP sharing

If a DataConnection is pending re-connect alarm, the new request
from another ApnContext sharing the same DC could disrupt
the re-connection pattern currently engaged.

This patch is to ensure the new request for PDP sharing
scenario will not trigger another SETUP_DATA request if
reconnection alarm is set in AlarmManager.

Bug: 4901019
Change-Id: I98b0d9af8b58fb880efdbc0246009de5cb116a54
parent 0711c86c
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;
    }