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

Commit 02648a4b authored by Robert Greenwalt's avatar Robert Greenwalt
Browse files

Clean up APN notifications.

Add APNType info to notifications so you can tell what's happening.  Now, even if a new APN
shares a connection with an already-connected-to- apn type, the new type will get all
the connecting and connected messages on connect and disconnecting/disconnected on disconnect
even though the shared connection remains connected.

Cleaning out the hacks MobileDataStateTracker needed to deal with the old situation.
bug:2226092

Change-Id: Iddd7421d6b91cda7c8405f9c3d5404ac04ef8e42
parent 1d46191d
Loading
Loading
Loading
Loading
+22 −157
Original line number Diff line number Diff line
@@ -23,7 +23,6 @@ import android.content.IntentFilter;
import android.os.RemoteException;
import android.os.Handler;
import android.os.ServiceManager;
import android.os.SystemProperties;
import com.android.internal.telephony.ITelephony;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.TelephonyIntents;
@@ -48,9 +47,6 @@ public class MobileDataStateTracker extends NetworkStateTracker {
    private ITelephony mPhoneService;

    private String mApnType;
    private String mApnTypeToWatchFor;
    private String mApnName;
    private boolean mEnabled;
    private BroadcastReceiver mStateReceiver;

    /**
@@ -66,18 +62,8 @@ public class MobileDataStateTracker extends NetworkStateTracker {
                TelephonyManager.getDefault().getNetworkType(), tag,
                TelephonyManager.getDefault().getNetworkTypeName());
        mApnType = networkTypeToApnType(netType);
        if (TextUtils.equals(mApnType, Phone.APN_TYPE_HIPRI)) {
            mApnTypeToWatchFor = Phone.APN_TYPE_DEFAULT;
        } else {
            mApnTypeToWatchFor = mApnType;
        }

        mPhoneService = null;
        if(netType == ConnectivityManager.TYPE_MOBILE) {
            mEnabled = true;
        } else {
            mEnabled = false;
        }

        mDnsPropNames = new String[] {
                "net.rmnet0.dns1",
@@ -103,89 +89,38 @@ public class MobileDataStateTracker extends NetworkStateTracker {
        filter.addAction(TelephonyIntents.ACTION_SERVICE_STATE_CHANGED);

        mStateReceiver = new MobileDataStateReceiver();
        Intent intent = mContext.registerReceiver(mStateReceiver, filter);
        if (intent != null)
            mMobileDataState = getMobileDataState(intent);
        else
        mContext.registerReceiver(mStateReceiver, filter);
        mMobileDataState = Phone.DataState.DISCONNECTED;
    }

    private Phone.DataState getMobileDataState(Intent intent) {
        String str = intent.getStringExtra(Phone.STATE_KEY);
        if (str != null) {
            String apnTypeList =
                    intent.getStringExtra(Phone.DATA_APN_TYPES_KEY);
            if (isApnTypeIncluded(apnTypeList)) {
                return Enum.valueOf(Phone.DataState.class, str);
            }
        }
        return Phone.DataState.DISCONNECTED;
    }

    private boolean isApnTypeIncluded(String typeList) {
        /* comma seperated list - split and check */
        if (typeList == null)
            return false;

        String[] list = typeList.split(",");
        for(int i=0; i< list.length; i++) {
            if (TextUtils.equals(list[i], mApnTypeToWatchFor) ||
                TextUtils.equals(list[i], Phone.APN_TYPE_ALL)) {
                return true;
            }
        }
        return false;
    }

    private class MobileDataStateReceiver extends BroadcastReceiver {
        public void onReceive(Context context, Intent intent) {
            synchronized(this) {
                if (intent.getAction().equals(TelephonyIntents.
                        ACTION_ANY_DATA_CONNECTION_STATE_CHANGED)) {
                    Phone.DataState state = getMobileDataState(intent);
                    String apnType = intent.getStringExtra(Phone.DATA_APN_TYPE_KEY);

                    if (!TextUtils.equals(apnType, mApnType)) {
                        return;
                    }
                    Phone.DataState state = Enum.valueOf(Phone.DataState.class,
                            intent.getStringExtra(Phone.STATE_KEY));
                    String reason = intent.getStringExtra(Phone.STATE_CHANGE_REASON_KEY);
                    String apnName = intent.getStringExtra(Phone.DATA_APN_KEY);
                    String apnTypeList = intent.getStringExtra(Phone.DATA_APN_TYPES_KEY);
                    mApnName = apnName;

                    boolean unavailable = intent.getBooleanExtra(Phone.NETWORK_UNAVAILABLE_KEY,
                            false);

                    // set this regardless of the apnTypeList.  It's all the same radio/network
                    // underneath
                    mNetworkInfo.setIsAvailable(!unavailable);

                    if (isApnTypeIncluded(apnTypeList)) {
                        if (mEnabled == false) {
                            // if we're not enabled but the APN Type is supported by this connection
                            // we should record the interface name if one's provided.  If the user
                            // turns on this network we will need the interfacename but won't get
                            // a fresh connected message - TODO fix this when we get per-APN
                            // notifications
                            if (state == Phone.DataState.CONNECTED) {
                                if (DBG) Log.d(TAG, "replacing old mInterfaceName (" +
                                        mInterfaceName + ") with " +
                                        intent.getStringExtra(Phone.DATA_IFACE_NAME_KEY) +
                                        " for " + mApnType);
                                mInterfaceName = intent.getStringExtra(Phone.DATA_IFACE_NAME_KEY);
                            }
                            return;
                        }
                    } else {
                        return;
                    }

                    if (DBG) Log.d(TAG, mApnType + " Received state= " + state + ", old= " +
                            mMobileDataState + ", reason= " +
                            (reason == null ? "(unspecified)" : reason) +
                            ", apnTypeList= " + apnTypeList);
                            (reason == null ? "(unspecified)" : reason));

                    if (mMobileDataState != state) {
                        mMobileDataState = state;
                        switch (state) {
                            case DISCONNECTED:
                                if(isTeardownRequested()) {
                                    mEnabled = false;
                                    setTeardownRequested(false);
                                }

@@ -218,11 +153,14 @@ public class MobileDataStateTracker extends NetworkStateTracker {
                    }
                } else if (intent.getAction().
                        equals(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED)) {
                    mEnabled = false;
                    String apnType = intent.getStringExtra(Phone.DATA_APN_TYPE_KEY);
                    if (!TextUtils.equals(apnType, mApnType)) {
                        return;
                    }
                    String reason = intent.getStringExtra(Phone.FAILURE_REASON_KEY);
                    String apnName = intent.getStringExtra(Phone.DATA_APN_KEY);
                    if (DBG) Log.d(TAG, "Received " + intent.getAction() + " broadcast" +
                            reason == null ? "" : "(" + reason + ")");
                    if (DBG) Log.d(TAG, mApnType + "Received " + intent.getAction() +
                            " broadcast" + reason == null ? "" : "(" + reason + ")");
                    setDetailedState(DetailedState.FAILED, reason, apnName);
                }
                TelephonyManager tm = TelephonyManager.getDefault();
@@ -320,70 +258,38 @@ public class MobileDataStateTracker extends NetworkStateTracker {
    /**
     * Tear down mobile data connectivity, i.e., disable the ability to create
     * mobile data connections.
     * TODO - make async and return nothing?
     */
    @Override
    public boolean teardown() {
        // since we won't get a notification currently (TODO - per APN notifications)
        // we won't get a disconnect message until all APN's on the current connection's
        // APN list are disabled.  That means privateRoutes for DNS and such will remain on -
        // not a problem since that's all shared with whatever other APN is still on, but
        // ugly.
        setTeardownRequested(true);
        return (setEnableApn(mApnType, false) != Phone.APN_REQUEST_FAILED);
    }

    /**
     * Re-enable mobile data connectivity after a {@link #teardown()}.
     * TODO - make async and always get a notification?
     */
    public boolean reconnect() {
        boolean retValue = false; //connected or expect to be?
        setTeardownRequested(false);
        switch (setEnableApn(mApnType, true)) {
            case Phone.APN_ALREADY_ACTIVE:
                // TODO - remove this when we get per-apn notifications
                mEnabled = true;
                // need to set self to CONNECTING so the below message is handled.
                mMobileDataState = Phone.DataState.CONNECTING;
                setDetailedState(DetailedState.CONNECTING, Phone.REASON_APN_CHANGED, null);
                //send out a connected message
                Intent intent = new Intent(TelephonyIntents.
                        ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
                intent.putExtra(Phone.STATE_KEY, Phone.DataState.CONNECTED.toString());
                intent.putExtra(Phone.STATE_CHANGE_REASON_KEY, Phone.REASON_APN_CHANGED);
                intent.putExtra(Phone.DATA_APN_TYPES_KEY, mApnTypeToWatchFor);
                intent.putExtra(Phone.DATA_APN_KEY, mApnName);
                intent.putExtra(Phone.DATA_IFACE_NAME_KEY, mInterfaceName);
                intent.putExtra(Phone.NETWORK_UNAVAILABLE_KEY, false);
                if (mStateReceiver != null) mStateReceiver.onReceive(mContext, intent);
                retValue = true;
                break;
            case Phone.APN_REQUEST_STARTED:
                mEnabled = true;
                // no need to do anything - we're already due some status update intents
                retValue = true;
                break;
            case Phone.APN_REQUEST_FAILED:
                if (mPhoneService == null && mApnType == Phone.APN_TYPE_DEFAULT) {
                    // on startup we may try to talk to the phone before it's ready
                    // since the phone will come up enabled, go with that.
                    // TODO - this also comes up on telephony crash: if we think mobile data is
                    // off and the telephony stuff crashes and has to restart it will come up
                    // enabled (making a data connection).  We will then be out of sync.
                    // A possible solution is a broadcast when telephony restarts.
                    mEnabled = true;
                    return false;
                }
                // else fall through
            case Phone.APN_TYPE_NOT_AVAILABLE:
                // Default is always available, but may be off due to
                // AirplaneMode or E-Call or whatever..
                if (mApnType != Phone.APN_TYPE_DEFAULT) {
                    mEnabled = false;
                }
                break;
            default:
                Log.e(TAG, "Error in reconnect - unexpected response.");
                mEnabled = false;
                break;
        }
        return mEnabled;
        return retValue;
    }

    /**
@@ -415,47 +321,6 @@ public class MobileDataStateTracker extends NetworkStateTracker {
        return false;
    }

    /**
     * Tells the phone sub-system that the caller wants to
     * begin using the named feature. The only supported features at
     * this time are {@code Phone.FEATURE_ENABLE_MMS}, which allows an application
     * to specify that it wants to send and/or receive MMS data, and
     * {@code Phone.FEATURE_ENABLE_SUPL}, which is used for Assisted GPS.
     * @param feature the name of the feature to be used
     * @param callingPid the process ID of the process that is issuing this request
     * @param callingUid the user ID of the process that is issuing this request
     * @return an integer value representing the outcome of the request.
     * The interpretation of this value is feature-specific.
     * specific, except that the value {@code -1}
     * always indicates failure. For {@code Phone.FEATURE_ENABLE_MMS},
     * the other possible return values are
     * <ul>
     * <li>{@code Phone.APN_ALREADY_ACTIVE}</li>
     * <li>{@code Phone.APN_REQUEST_STARTED}</li>
     * <li>{@code Phone.APN_TYPE_NOT_AVAILABLE}</li>
     * <li>{@code Phone.APN_REQUEST_FAILED}</li>
     * </ul>
     */
    public int startUsingNetworkFeature(String feature, int callingPid, int callingUid) {
        return -1;
    }

    /**
     * Tells the phone sub-system that the caller is finished
     * using the named feature. The only supported feature at
     * this time is {@code Phone.FEATURE_ENABLE_MMS}, which allows an application
     * to specify that it wants to send and/or receive MMS data.
     * @param feature the name of the feature that is no longer needed
     * @param callingPid the process ID of the process that is issuing this request
     * @param callingUid the user ID of the process that is issuing this request
     * @return an integer value representing the outcome of the request.
     * The interpretation of this value is feature-specific, except that
     * the value {@code -1} always indicates failure.
     */
    public int stopUsingNetworkFeature(String feature, int callingPid, int callingUid) {
        return -1;
    }

    /**
     * Ensure that a network route exists to deliver traffic to the specified
     * host via the mobile data network.
+1 −28
Original line number Diff line number Diff line
@@ -348,6 +348,7 @@ public abstract class NetworkStateTracker extends Handler {

    /**
     * Reenable connectivity to a network after a {@link #teardown()}.
     * @return {@code true} if we're connected or expect to be connected
     */
    public abstract boolean reconnect();

@@ -364,34 +365,6 @@ public abstract class NetworkStateTracker extends Handler {
     */
    public abstract boolean isAvailable();

    /**
     * Tells the underlying networking system that the caller wants to
     * begin using the named feature. The interpretation of {@code feature}
     * is completely up to each networking implementation.
     * @param feature the name of the feature to be used
     * @param callingPid the process ID of the process that is issuing this request
     * @param callingUid the user ID of the process that is issuing this request
     * @return an integer value representing the outcome of the request.
     * The interpretation of this value is specific to each networking
     * implementation+feature combination, except that the value {@code -1}
     * always indicates failure.
     */
    public abstract int startUsingNetworkFeature(String feature, int callingPid, int callingUid);

    /**
     * Tells the underlying networking system that the caller is finished
     * using the named feature. The interpretation of {@code feature}
     * is completely up to each networking implementation.
     * @param feature the name of the feature that is no longer needed.
     * @param callingPid the process ID of the process that is issuing this request
     * @param callingUid the user ID of the process that is issuing this request
     * @return an integer value representing the outcome of the request.
     * The interpretation of this value is specific to each networking
     * implementation+feature combination, except that the value {@code -1}
     * always indicates failure.
     */
    public abstract int stopUsingNetworkFeature(String feature, int callingPid, int callingUid);

    /**
     * Ensure that a network route exists to deliver traffic to the specified
     * host via this network interface.
+2 −11
Original line number Diff line number Diff line
@@ -597,15 +597,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
                network.reconnect();
                return Phone.APN_REQUEST_STARTED;
            } else {
                synchronized(this) {
                    mFeatureUsers.add(f);
                }
                mHandler.sendMessageDelayed(mHandler.obtainMessage(
                        NetworkStateTracker.EVENT_RESTORE_DEFAULT_NETWORK,
                        f), getRestoreDefaultNetworkDelay());

                return network.startUsingNetworkFeature(feature,
                        getCallingPid(), getCallingUid());
                return -1;
            }
        }
        return Phone.APN_TYPE_NOT_AVAILABLE;
@@ -724,8 +716,7 @@ public class ConnectivityService extends IConnectivityManager.Stub {
            tracker.teardown();
            return 1;
        } else {
            // do it the old fashioned way
            return tracker.stopUsingNetworkFeature(feature, pid, uid);
            return -1;
        }
    }

+51 −43
Original line number Diff line number Diff line
@@ -88,7 +88,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {

    private String mDataConnectionApn = "";

    private String[] mDataConnectionApnTypes = null;
    private ArrayList<String> mConnectedApns;

    private String mDataConnectionInterfaceName = "";

@@ -120,6 +120,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
        }
        mContext = context;
        mBatteryStats = BatteryStatsService.getService();
        mConnectedApns = new ArrayList<String>();
    }

    public void listen(String pkgForDebug, IPhoneStateListener callback, int events,
@@ -235,8 +236,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
        synchronized (mRecords) {
            mCallState = state;
            mCallIncomingNumber = incomingNumber;
            for (int i = mRecords.size() - 1; i >= 0; i--) {
                Record r = mRecords.get(i);
            for (Record r : mRecords) {
                if ((r.events & PhoneStateListener.LISTEN_CALL_STATE) != 0) {
                    try {
                        r.callback.onCallStateChanged(state, incomingNumber);
@@ -255,8 +255,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
        }
        synchronized (mRecords) {
            mServiceState = state;
            for (int i = mRecords.size() - 1; i >= 0; i--) {
                Record r = mRecords.get(i);
            for (Record r : mRecords) {
                if ((r.events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) {
                    sendServiceState(r, state);
                }
@@ -271,8 +270,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
        }
        synchronized (mRecords) {
            mSignalStrength = signalStrength;
            for (int i = mRecords.size() - 1; i >= 0; i--) {
                Record r = mRecords.get(i);
            for (Record r : mRecords) {
                if ((r.events & PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) != 0) {
                    sendSignalStrength(r, signalStrength);
                }
@@ -296,8 +294,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
        }
        synchronized (mRecords) {
            mMessageWaiting = mwi;
            for (int i = mRecords.size() - 1; i >= 0; i--) {
                Record r = mRecords.get(i);
            for (Record r : mRecords) {
                if ((r.events & PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR) != 0) {
                    try {
                        r.callback.onMessageWaitingIndicatorChanged(mwi);
@@ -315,8 +312,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
        }
        synchronized (mRecords) {
            mCallForwarding = cfi;
            for (int i = mRecords.size() - 1; i >= 0; i--) {
                Record r = mRecords.get(i);
            for (Record r : mRecords) {
                if ((r.events & PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR) != 0) {
                    try {
                        r.callback.onCallForwardingIndicatorChanged(cfi);
@@ -334,8 +330,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
        }
        synchronized (mRecords) {
            mDataActivity = state;
            for (int i = mRecords.size() - 1; i >= 0; i--) {
                Record r = mRecords.get(i);
            for (Record r : mRecords) {
                if ((r.events & PhoneStateListener.LISTEN_DATA_ACTIVITY) != 0) {
                    try {
                        r.callback.onDataActivity(state);
@@ -348,20 +343,40 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
    }

    public void notifyDataConnection(int state, boolean isDataConnectivityPossible,
            String reason, String apn, String[] apnTypes, String interfaceName, int networkType) {
            String reason, String apn, String apnType, String interfaceName, int networkType) {
        if (!checkNotifyPermission("notifyDataConnection()" )) {
            return;
        }
        synchronized (mRecords) {
            boolean modified = false;
            if (state == TelephonyManager.DATA_CONNECTED) {
                if (!mConnectedApns.contains(apnType)) {
                    mConnectedApns.add(apnType);
                    if (mDataConnectionState != state) {
                        mDataConnectionState = state;
                        modified = true;
                    }
                }
            } else {
                mConnectedApns.remove(apnType);
                if (mConnectedApns.isEmpty()) {
                    mDataConnectionState = state;
                    modified = true;
                } else {
                    // we're still connected, so send that out if we send anything.
                    state = mDataConnectionState;
                }
            }
            mDataConnectionPossible = isDataConnectivityPossible;
            mDataConnectionReason = reason;
            mDataConnectionApn = apn;
            mDataConnectionApnTypes = apnTypes;
            mDataConnectionInterfaceName = interfaceName;
            if (mDataConnectionNetworkType != networkType) {
                mDataConnectionNetworkType = networkType;
            for (int i = mRecords.size() - 1; i >= 0; i--) {
                Record r = mRecords.get(i);
                modified = true;
            }
            if (modified) {
                for (Record r : mRecords) {
                    if ((r.events & PhoneStateListener.LISTEN_DATA_CONNECTION_STATE) != 0) {
                        try {
                            r.callback.onDataConnectionStateChanged(state, networkType);
@@ -371,17 +386,18 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
                    }
                }
            }
        }
        broadcastDataConnectionStateChanged(state, isDataConnectivityPossible, reason, apn,
                apnTypes, interfaceName);
                apnType, interfaceName);
    }

    public void notifyDataConnectionFailed(String reason) {
    public void notifyDataConnectionFailed(String reason, String apnType) {
        if (!checkNotifyPermission("notifyDataConnectionFailed()")) {
            return;
        }
        /*
         * This is commented out because there is on onDataConnectionFailed callback
         * on PhoneStateListener. There should be
         * This is commented out because there is no onDataConnectionFailed callback
         * in PhoneStateListener. There should be.
        synchronized (mRecords) {
            mDataConnectionFailedReason = reason;
            final int N = mRecords.size();
@@ -393,7 +409,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
            }
        }
        */
        broadcastDataConnectionFailed(reason);
        broadcastDataConnectionFailed(reason, apnType);
    }

    public void notifyCellLocation(Bundle cellLocation) {
@@ -402,8 +418,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
        }
        synchronized (mRecords) {
            mCellLocation = cellLocation;
            for (int i = mRecords.size() - 1; i >= 0; i--) {
                Record r = mRecords.get(i);
            for (Record r : mRecords) {
                if ((r.events & PhoneStateListener.LISTEN_CELL_LOCATION) != 0) {
                    sendCellLocation(r, cellLocation);
                }
@@ -463,8 +478,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
            pw.println("  mDataConnectionInterfaceName=" + mDataConnectionInterfaceName);
            pw.println("  mCellLocation=" + mCellLocation);
            pw.println("registrations: count=" + recordCount);
            for (int i = 0; i < recordCount; i++) {
                Record r = mRecords.get(i);
            for (Record r : mRecords) {
                pw.println("  " + r.pkgForDebug + " 0x" + Integer.toHexString(r.events));
            }
        }
@@ -535,7 +549,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {

    private void broadcastDataConnectionStateChanged(int state,
            boolean isDataConnectivityPossible,
            String reason, String apn, String[] apnTypes, String interfaceName) {
            String reason, String apn, String apnType, String interfaceName) {
        // Note: not reporting to the battery stats service here, because the
        // status bar takes care of that after taking into account all of the
        // required info.
@@ -549,22 +563,16 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub {
            intent.putExtra(Phone.STATE_CHANGE_REASON_KEY, reason);
        }
        intent.putExtra(Phone.DATA_APN_KEY, apn);
        String types = new String("");
        if (apnTypes.length > 0) {
            types = apnTypes[0];
            for (int i = 1; i < apnTypes.length; i++) {
                types = types+","+apnTypes[i];
            }
        }
        intent.putExtra(Phone.DATA_APN_TYPES_KEY, types);
        intent.putExtra(Phone.DATA_APN_TYPE_KEY, apnType);
        intent.putExtra(Phone.DATA_IFACE_NAME_KEY, interfaceName);
        mContext.sendStickyBroadcast(intent);
    }

    private void broadcastDataConnectionFailed(String reason) {
    private void broadcastDataConnectionFailed(String reason, String apnType) {
        Intent intent = new Intent(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED);
        intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
        intent.putExtra(Phone.FAILURE_REASON_KEY, reason);
        intent.putExtra(Phone.DATA_APN_TYPE_KEY, apnType);
        mContext.sendStickyBroadcast(intent);
    }

+2 −1
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import android.telephony.CellLocation;
import android.util.Log;

import com.android.internal.telephony.IPhoneStateListener;
import com.android.internal.telephony.Phone;

/**
 * A listener class for monitoring changes in specific telephony states
@@ -284,7 +285,7 @@ public class PhoneStateListener {
        }

        public void onDataConnectionStateChanged(int state, int networkType) {
            Message.obtain(mHandler, LISTEN_DATA_CONNECTION_STATE, state, networkType, null).
            Message.obtain(mHandler, LISTEN_DATA_CONNECTION_STATE, state, networkType).
                    sendToTarget();
        }

Loading