Have MobileDataStateTracker & DataConnectionTracker communicate directly.

Added CMD_SET_DATA_ENABLE which is sent when data is enabled/disabled
via the ConnectivityService. It is anticipated that the communication
channel will be used for additional commands and to receive unsoliciated
commands from DataConnectionTracker back to MobileDataStateTracker.

Change-Id: I3863e7385155d503f069b1dcb7e4f766ec78b5f8
diff --git a/core/java/android/net/DummyDataStateTracker.java b/core/java/android/net/DummyDataStateTracker.java
index a759865..daa1c09 100644
--- a/core/java/android/net/DummyDataStateTracker.java
+++ b/core/java/android/net/DummyDataStateTracker.java
@@ -189,6 +189,9 @@
         return -1;
     }
 
+    public void setDataEnable(boolean enabled) {
+    }
+
     @Override
     public String toString() {
         StringBuffer sb = new StringBuffer("Dummy data state: none, dummy!");
diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java
index 4df8db5..b3a354f 100644
--- a/core/java/android/net/MobileDataStateTracker.java
+++ b/core/java/android/net/MobileDataStateTracker.java
@@ -20,14 +20,21 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.Messenger;
 import android.os.RemoteException;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
 import android.os.ServiceManager;
+
+import com.android.internal.telephony.DataConnectionTracker;
 import com.android.internal.telephony.ITelephony;
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.util.AsyncChannel;
+
 import android.net.NetworkInfo.DetailedState;
 import android.net.NetworkInfo;
 import android.net.LinkProperties;
@@ -67,6 +74,10 @@
     // the other is also disconnected before we reset sockets
     private boolean mIsDefaultOrHipri = false;
 
+    private Handler mHandler;
+    private AsyncChannel mDataConnectionTrackerAc;
+    private Messenger mMessenger;
+
     /**
      * Create a new MobileDataStateTracker
      * @param netType the ConnectivityManager network type
@@ -107,14 +118,54 @@
         mTarget = target;
         mContext = context;
 
+        HandlerThread handlerThread = new HandlerThread("ConnectivityServiceThread");
+        handlerThread.start();
+        mHandler = new MdstHandler(handlerThread.getLooper(), this);
+
         IntentFilter filter = new IntentFilter();
         filter.addAction(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
         filter.addAction(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED);
+        filter.addAction(DataConnectionTracker.ACTION_DATA_CONNECTION_TRACKER_MESSENGER);
 
         mContext.registerReceiver(new MobileDataStateReceiver(), filter);
         mMobileDataState = Phone.DataState.DISCONNECTED;
     }
 
+    static class MdstHandler extends Handler {
+        private MobileDataStateTracker mMdst;
+
+        MdstHandler(Looper looper, MobileDataStateTracker mdst) {
+            super(looper);
+            mMdst = mdst;
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
+                    if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
+                        if (DBG) {
+                            mMdst.log("MdstHandler connected");
+                        }
+                        mMdst.mDataConnectionTrackerAc = (AsyncChannel) msg.obj;
+                    } else {
+                        if (DBG) {
+                            mMdst.log("MdstHandler %s NOT connected error=" + msg.arg1);
+                        }
+                    }
+                    break;
+                case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
+                    mMdst.log("Disconnected from DataStateTracker");
+                    mMdst.mDataConnectionTrackerAc = null;
+                    break;
+                default: {
+                    mMdst.log("Ignorning unknown message=" + msg);
+                    break;
+                }
+            }
+        }
+    }
+
     /**
      * Return the IP addresses of the DNS servers available for the mobile data
      * network interface.
@@ -179,7 +230,7 @@
                         false));
 
                 if (DBG) {
-                    log(mApnType + " Received state=" + state + ", old=" + mMobileDataState +
+                    log("Received state=" + state + ", old=" + mMobileDataState +
                         ", reason=" + (reason == null ? "(unspecified)" : reason));
                 }
                 if (mMobileDataState != state) {
@@ -265,10 +316,16 @@
                 String reason = intent.getStringExtra(Phone.FAILURE_REASON_KEY);
                 String apnName = intent.getStringExtra(Phone.DATA_APN_KEY);
                 if (DBG) {
-                    log(mApnType + "Received " + intent.getAction() +
+                    log("Received " + intent.getAction() +
                                 " broadcast" + reason == null ? "" : "(" + reason + ")");
                 }
                 setDetailedState(DetailedState.FAILED, reason, apnName);
+            } else if (intent.getAction().
+                    equals(DataConnectionTracker.ACTION_DATA_CONNECTION_TRACKER_MESSENGER)) {
+                if (DBG) log(mApnType + " got ACTION_DATA_CONNECTION_TRACKER_MESSENGER");
+                mMessenger = intent.getParcelableExtra(DataConnectionTracker.EXTRA_MESSENGER);
+                AsyncChannel ac = new AsyncChannel();
+                ac.connect(mContext, MobileDataStateTracker.this.mHandler, mMessenger);
             } else {
                 if (DBG) log("Broadcast received: ignore " + intent.getAction());
             }
@@ -488,6 +545,20 @@
         return -1;
     }
 
+    /**
+     * @param enabled
+     */
+    public void setDataEnable(boolean enabled) {
+        try {
+            log("setDataEnable: E enabled=" + enabled);
+            mDataConnectionTrackerAc.sendMessage(DataConnectionTracker.CMD_SET_DATA_ENABLE,
+                    enabled ? DataConnectionTracker.ENABLED : DataConnectionTracker.DISABLED);
+            log("setDataEnable: X enabled=" + enabled);
+        } catch (Exception e) {
+            log("setDataEnable: X mAc was null" + e);
+        }
+    }
+
     @Override
     public String toString() {
         StringBuffer sb = new StringBuffer("Mobile data state: ");
@@ -543,7 +614,7 @@
             case ConnectivityManager.TYPE_MOBILE_HIPRI:
                 return Phone.APN_TYPE_HIPRI;
             default:
-                loge("Error mapping networkType " + netType + " to apnType.");
+                sloge("Error mapping networkType " + netType + " to apnType.");
                 return null;
         }
     }
@@ -562,11 +633,15 @@
         return new LinkCapabilities(mLinkCapabilities);
     }
 
-    static private void log(String s) {
-        Slog.d(TAG, s);
+    private void log(String s) {
+        Slog.d(TAG, mApnType + ": " + s);
     }
 
-    static private void loge(String s) {
+    private void loge(String s) {
+        Slog.e(TAG, mApnType + ": " + s);
+    }
+
+    static private void sloge(String s) {
         Slog.e(TAG, s);
     }
 }
diff --git a/core/java/android/net/NetworkStateTracker.java b/core/java/android/net/NetworkStateTracker.java
index 8afdcee..e378506 100644
--- a/core/java/android/net/NetworkStateTracker.java
+++ b/core/java/android/net/NetworkStateTracker.java
@@ -169,6 +169,11 @@
     public int stopUsingNetworkFeature(String feature, int callingPid, int callingUid);
 
     /**
+     * @param enabled
+     */
+    public void setDataEnable(boolean enabled);
+
+    /**
      * -------------------------------------------------------------
      * Storage API used by ConnectivityService for saving
      * Network specific information.
@@ -204,5 +209,4 @@
      * Indicate tear down requested from connectivity
      */
     public void setTeardownRequested(boolean isRequested);
-
 }
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 758f9f3..9d11d87 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -387,7 +387,6 @@
          * the number of different network types is not going
          * to change very often.
          */
-        boolean noMobileData = !getMobileDataEnabled();
         for (int netType : mPriorityList) {
             switch (mNetAttributes[netType].mRadio) {
             case ConnectivityManager.TYPE_WIFI:
@@ -407,10 +406,6 @@
                 mNetTrackers[netType] = new MobileDataStateTracker(netType,
                         mNetAttributes[netType].mName);
                 mNetTrackers[netType].startMonitoring(context, mHandler);
-                if (noMobileData) {
-                    if (DBG) log("tearing down Mobile networks due to setting");
-                    mNetTrackers[netType].teardown();
-                }
                 break;
             case ConnectivityManager.TYPE_DUMMY:
                 mNetTrackers[netType] = new DummyDataStateTracker(netType,
@@ -691,10 +686,6 @@
         // TODO - move this into the MobileDataStateTracker
         int usedNetworkType = networkType;
         if(networkType == ConnectivityManager.TYPE_MOBILE) {
-            if (!getMobileDataEnabled()) {
-                if (DBG) log("requested special network with data disabled - rejected");
-                return Phone.APN_TYPE_NOT_AVAILABLE;
-            }
             if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_MMS)) {
                 usedNetworkType = ConnectivityManager.TYPE_MOBILE_MMS;
             } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_SUPL)) {
@@ -985,6 +976,9 @@
      * @see ConnectivityManager#getMobileDataEnabled()
      */
     public boolean getMobileDataEnabled() {
+        // TODO: This detail should probably be in DataConnectionTracker's
+        //       which is where we store the value and maybe make this
+        //       asynchronous.
         enforceAccessPermission();
         boolean retVal = Settings.Secure.getInt(mContext.getContentResolver(),
                 Settings.Secure.MOBILE_DATA, 1) == 1;
@@ -1004,42 +998,14 @@
     }
 
     private void handleSetMobileData(boolean enabled) {
-        if (getMobileDataEnabled() == enabled) return;
-
-        Settings.Secure.putInt(mContext.getContentResolver(),
-                Settings.Secure.MOBILE_DATA, enabled ? 1 : 0);
-
-        if (enabled) {
-            if (mNetTrackers[ConnectivityManager.TYPE_MOBILE] != null) {
-                if (DBG) {
-                    log("starting up " + mNetTrackers[ConnectivityManager.TYPE_MOBILE]);
-                }
-                mNetTrackers[ConnectivityManager.TYPE_MOBILE].reconnect();
+        if (mNetTrackers[ConnectivityManager.TYPE_MOBILE] != null) {
+            if (DBG) {
+                Slog.d(TAG, mNetTrackers[ConnectivityManager.TYPE_MOBILE].toString() + enabled);
             }
-        } else {
-            for (NetworkStateTracker nt : mNetTrackers) {
-                if (nt == null) continue;
-                int netType = nt.getNetworkInfo().getType();
-                if (mNetAttributes[netType].mRadio == ConnectivityManager.TYPE_MOBILE) {
-                    if (DBG) log("tearing down " + nt);
-                    nt.teardown();
-                }
-            }
+            mNetTrackers[ConnectivityManager.TYPE_MOBILE].setDataEnable(enabled);
         }
     }
 
-    private int getNumConnectedNetworks() {
-        int numConnectedNets = 0;
-
-        for (NetworkStateTracker nt : mNetTrackers) {
-            if (nt != null && nt.getNetworkInfo().isConnected() &&
-                    !nt.isTeardownRequested()) {
-                ++numConnectedNets;
-            }
-        }
-        return numConnectedNets;
-    }
-
     private void enforceAccessPermission() {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.ACCESS_NETWORK_STATE,
@@ -1159,16 +1125,9 @@
 
             int newType = -1;
             int newPriority = -1;
-            boolean noMobileData = !getMobileDataEnabled();
             for (int checkType=0; checkType <= ConnectivityManager.MAX_NETWORK_TYPE; checkType++) {
                 if (checkType == prevNetType) continue;
                 if (mNetAttributes[checkType] == null) continue;
-                if (mNetAttributes[checkType].mRadio == ConnectivityManager.TYPE_MOBILE &&
-                        noMobileData) {
-                    loge("not failing over to mobile type " + checkType +
-                            " because Mobile Data Disabled");
-                    continue;
-                }
                 if (mNetAttributes[checkType].isDefault()) {
                     /* TODO - if we have multiple nets we could use
                      * we may want to put more thought into which we choose
diff --git a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
index 069e1b8..737342f 100644
--- a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java
@@ -30,6 +30,7 @@
 import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.Message;
+import android.os.Messenger;
 import android.os.ServiceManager;
 import android.preference.PreferenceManager;
 import android.provider.Settings;
@@ -81,6 +82,10 @@
         DORMANT
     }
 
+    public static String ACTION_DATA_CONNECTION_TRACKER_MESSENGER =
+        "com.android.internal.telephony";
+    public static String EXTRA_MESSENGER = "EXTRA_MESSENGER";
+
     /***** Event Codes *****/
     protected static final int EVENT_DATA_SETUP_COMPLETE = 1;
     protected static final int EVENT_RADIO_AVAILABLE = 3;
@@ -113,6 +118,8 @@
     protected static final int EVENT_SET_INTERNAL_DATA_ENABLE = 37;
     protected static final int EVENT_RESET_DONE = 38;
 
+    public static final int CMD_SET_DATA_ENABLE = 39;
+
     /***** Constants *****/
 
     protected static final int APN_INVALID_ID = -1;
@@ -123,13 +130,18 @@
     protected static final int APN_HIPRI_ID = 4;
     protected static final int APN_NUM_TYPES = 5;
 
-    protected static final int DISABLED = 0;
-    protected static final int ENABLED = 1;
+    public static final int DISABLED = 0;
+    public static final int ENABLED = 1;
 
     // responds to the setInternalDataEnabled call - used internally to turn off data
     // for example during emergency calls
     protected boolean mInternalDataEnabled = true;
 
+    // responds to public (user) API to enable/disable data use
+    // independent of mInternalDataEnabled and requests for APN access
+    // persisted
+    protected boolean mDataEnabled = true;
+
     protected boolean[] dataEnabled = new boolean[APN_NUM_TYPES];
 
     protected int enabledCount = 0;
@@ -289,6 +301,9 @@
         filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
         filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
 
+        mDataEnabled = Settings.Secure.getInt(mPhone.getContext().getContentResolver(),
+                Settings.Secure.MOBILE_DATA, 1) == 1;
+
         // TODO: Why is this registering the phone as the receiver of the intent
         //       and not its own handler?
         mPhone.getContext().registerReceiver(mIntentReceiver, filter, null, mPhone);
@@ -296,16 +311,8 @@
         // This preference tells us 1) initial condition for "dataEnabled",
         // and 2) whether the RIL will setup the baseband to auto-PS attach.
         SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mPhone.getContext());
-        boolean dataEnabledSetting = true;
-        try {
-            dataEnabledSetting = IConnectivityManager.Stub.asInterface(ServiceManager.
-                    getService(Context.CONNECTIVITY_SERVICE)).getMobileDataEnabled();
-        } catch (Exception e) {
-            // nothing to do - use the old behavior and leave data on
-        }
         dataEnabled[APN_DEFAULT_ID] =
-                !sp.getBoolean(PhoneBase.DATA_DISABLED_ON_BOOT_KEY, false) &&
-                dataEnabledSetting;
+                !sp.getBoolean(PhoneBase.DATA_DISABLED_ON_BOOT_KEY, false);
         if (dataEnabled[APN_DEFAULT_ID]) {
             enabledCount++;
         }
@@ -316,6 +323,12 @@
         mPhone.getContext().unregisterReceiver(this.mIntentReceiver);
     }
 
+    protected void broadcastMessenger() {
+        Intent intent = new Intent(ACTION_DATA_CONNECTION_TRACKER_MESSENGER);
+        intent.putExtra(EXTRA_MESSENGER, new Messenger(this));
+        mPhone.getContext().sendBroadcast(intent);
+    }
+
     public Activity getActivity() {
         return mActivity;
     }
@@ -490,14 +503,20 @@
                 onCleanUpConnection(tearDown, (String) msg.obj);
                 break;
 
-            case EVENT_SET_INTERNAL_DATA_ENABLE:
+            case EVENT_SET_INTERNAL_DATA_ENABLE: {
                 boolean enabled = (msg.arg1 == ENABLED) ? true : false;
                 onSetInternalDataEnabled(enabled);
                 break;
-
+            }
             case EVENT_RESET_DONE:
                 onResetDone((AsyncResult) msg.obj);
                 break;
+            case CMD_SET_DATA_ENABLE: {
+                log("CMD_SET_DATA_ENABLE msg=" + msg);
+                boolean enabled = (msg.arg1 == ENABLED) ? true : false;
+                onSetDataEnabled(enabled);
+                break;
+            }
 
             default:
                 Log.e("DATA", "Unidentified event = " + msg.what);
@@ -512,7 +531,7 @@
      *         {@code true} otherwise.
      */
     public synchronized boolean getAnyDataEnabled() {
-        return (mInternalDataEnabled && (enabledCount != 0));
+        return (mInternalDataEnabled && mDataEnabled && (enabledCount != 0));
     }
 
     protected abstract void startNetStatPoll();
@@ -828,11 +847,6 @@
      * Prevent mobile data connections from being established, or once again
      * allow mobile data connections. If the state toggles, then either tear
      * down or set up data, as appropriate to match the new state.
-     * <p>
-     * This operation only affects the default APN, and if the same APN is
-     * currently being used for MMS traffic, the teardown will not happen even
-     * when {@code enable} is {@code false}.
-     * </p>
      *
      * @param enable indicates whether to enable ({@code true}) or disable (
      *            {@code false}) data
@@ -849,15 +863,41 @@
     }
 
     protected void onSetInternalDataEnabled(boolean enable) {
+        boolean prevEnabled = getAnyDataEnabled();
         if (mInternalDataEnabled != enable) {
             synchronized (this) {
                 mInternalDataEnabled = enable;
             }
-            if (enable) {
-                mRetryMgr.resetRetryCount();
-                onTrySetupData(Phone.REASON_DATA_ENABLED);
-            } else {
-                onCleanUpConnection(true, Phone.REASON_DATA_DISABLED);
+            if (prevEnabled != getAnyDataEnabled()) {
+                if (!prevEnabled) {
+                    mRetryMgr.resetRetryCount();
+                    onTrySetupData(Phone.REASON_DATA_ENABLED);
+                } else {
+                    onCleanUpConnection(true, Phone.REASON_DATA_DISABLED);
+                }
+            }
+        }
+    }
+
+    public synchronized boolean getDataEnabled() {
+        return mDataEnabled;
+    }
+
+    protected void onSetDataEnabled(boolean enable) {
+        boolean prevEnabled = getAnyDataEnabled();
+        if (mDataEnabled != enable) {
+            synchronized (this) {
+                mDataEnabled = enable;
+            }
+            Settings.Secure.putInt(mPhone.getContext().getContentResolver(),
+                    Settings.Secure.MOBILE_DATA, enable ? 1 : 0);
+            if (prevEnabled != getAnyDataEnabled()) {
+                if (!prevEnabled) {
+                    mRetryMgr.resetRetryCount();
+                    onTrySetupData(Phone.REASON_DATA_ENABLED);
+                } else {
+                    onCleanUpConnection(true, Phone.REASON_DATA_DISABLED);
+                }
             }
         }
     }
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
index b005cd3..60df7df 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaDataConnectionTracker.java
@@ -113,6 +113,7 @@
         mDataConnectionTracker = this;
 
         createAllDataConnectionList();
+        broadcastMessenger();
     }
 
     @Override
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index 4713c24..cd0d9e3 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -160,6 +160,7 @@
 
         /** Create the default connection */
         createDataConnection(Phone.APN_TYPE_DEFAULT);
+        broadcastMessenger();
     }
 
     @Override
diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java
index d0231be..2aff7ec 100644
--- a/wifi/java/android/net/wifi/WifiStateTracker.java
+++ b/wifi/java/android/net/wifi/WifiStateTracker.java
@@ -170,6 +170,13 @@
     }
 
     /**
+     * @param enabled
+     */
+    public void setDataEnable(boolean enabled) {
+        android.util.Log.d(TAG, "setDataEnabled: IGNORING enabled=" + enabled);
+    }
+
+    /**
      * Check if private DNS route is set for the network
      */
     public boolean isPrivateDnsRouteSet() {