Make the DUN apn data secure.

Rather than come out of the user-modifiable APN DB, the DUN APN data will
come first from a built-in resource and then potentially overriden by a secure
setting (which is gservices upgradable).

Also made the "require-dun" setting secure-setting overridable.
bug:2736390

Change-Id: I1e4644c3839f06c977b83797641f3948785146a2
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 9f19f11..e12dfb0 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2368,6 +2368,24 @@
         public static final String TETHER_SUPPORTED = "tether_supported";
 
         /**
+         * Used to require DUN APN on the device or not - defaults to a build config value
+         * which defaults to false
+         * @hide
+         */
+        public static final String TETHER_DUN_REQUIRED = "tether_dun_required";
+
+        /**
+         * Used to hold a gservices-provisioned apn value for DUN.  If set, or the
+         * corresponding build config values are set it will override the APN DB
+         * values.
+         * Consists of a comma seperated list of strings:
+         * "name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type"
+         * note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN"
+         * @hide
+         */
+        public static final String TETHER_DUN_APN = "tether_dun_apn";
+
+        /**
          * No longer supported.
          */
         public static final String PARENTAL_CONTROL_ENABLED = "parental_control_enabled";
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 64f05fe..088ab44 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -91,8 +91,18 @@
     <string-array translatable="false" name="config_tether_upstream_regexs">
     </string-array>
 
-    <!-- Boolean indicating if we require the use of DUN on mobile for tethering -->
-    <bool translatable="false" name="config_tether_dun_required">true</bool>
+    <!-- Boolean indicating if we require the use of DUN on mobile for tethering.
+         Note that this defaults to false so that if you move to a carrier that
+         hasn't configured anything tethering will still work.  If you'd rather
+         make the device untetherable on unconfigured devices, set to true -->
+    <bool translatable="false" name="config_tether_dun_required">false</bool>
+
+    <!-- String containing the apn value for tethering.  May be overriden by secure settings
+         TETHER_DUN_APN.  Value is a comma separated series of strings:
+         "name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type"
+         note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" -->
+    <string translatable="false" name="config_tether_apndata"></string>
+
 
     <!-- Flag indicating whether the keyguard should be bypassed when
          the slider is open.  This can be set or unset depending how easily
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index b43b86c..b29f875 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -98,6 +98,7 @@
     private static final String DNS_DEFAULT_SERVER1 = "8.8.8.8";
     private static final String DNS_DEFAULT_SERVER2 = "4.2.2.2";
 
+    // resampled each time we turn on tethering - used as cache for settings/config-val
     private boolean mDunRequired;  // configuration info - must use DUN apn on 3g
 
     private HierarchicalStateMachine mTetherMasterSM;
@@ -157,8 +158,7 @@
             mDhcpRange[2] = DHCP_DEFAULT_RANGE2_START;
             mDhcpRange[3] = DHCP_DEFAULT_RANGE2_STOP;
         }
-        mDunRequired = context.getResources().getBoolean(
-                com.android.internal.R.bool.config_tether_dun_required);
+        mDunRequired = false; // resample when we turn on
 
         mTetherableUsbRegexs = context.getResources().getStringArray(
                 com.android.internal.R.array.config_tether_usb_regexs);
@@ -555,7 +555,11 @@
     }
 
     public boolean isDunRequired() {
-        return mDunRequired;
+        boolean defaultVal = mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_tether_dun_required);
+        boolean result = (Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Secure.TETHER_DUN_REQUIRED, (defaultVal ? 1 : 0)) == 1);
+        return result;
     }
 
     public String[] getTetheredIfaces() {
@@ -1263,6 +1267,7 @@
                 boolean retValue = true;
                 switch (message.what) {
                     case CMD_TETHER_MODE_REQUESTED:
+                        mDunRequired = isDunRequired();
                         TetherInterfaceSM who = (TetherInterfaceSM)message.obj;
                         Log.d(TAG, "Tether Mode requested by " + who.toString());
                         mNotifyList.add(who);
diff --git a/telephony/java/com/android/internal/telephony/gsm/ApnSetting.java b/telephony/java/com/android/internal/telephony/gsm/ApnSetting.java
index 4cbfc87..05527af 100644
--- a/telephony/java/com/android/internal/telephony/gsm/ApnSetting.java
+++ b/telephony/java/com/android/internal/telephony/gsm/ApnSetting.java
@@ -55,6 +55,35 @@
         this.types = types;
     }
 
+    // data[0] = name
+    // data[1] = apn
+    // data[2] = proxy
+    // data[3] = port
+    // data[4] = username
+    // data[5] = password
+    // data[6] = server
+    // data[7] = mmsc
+    // data[8] = mmsproxy
+    // data[9] = mmsport
+    // data[10] = mcc
+    // data[11] = mnc
+    // data[12] = auth
+    // data[13] = first type...
+    public static ApnSetting fromString(String data) {
+        if (data == null) return null;
+        String[] a = data.split("\\s*,\\s*");
+        if (a.length < 14) return null;
+        int authType = 0;
+        try {
+            authType = Integer.parseInt(a[12]);
+        } catch (Exception e) {
+        }
+        String[] typeArray = new String[a.length - 13];
+        System.arraycopy(a, 13, typeArray, 0, a.length - 13);
+        return new ApnSetting(-1,a[10]+a[11],a[0],a[1],a[2],a[3],a[7],a[8],
+                a[9],a[4],a[5],authType,typeArray);
+    }
+
     public String toString() {
         StringBuilder sb = new StringBuilder();
         sb.append(carrier)
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index cbfb550..627d94d 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -49,6 +49,7 @@
 import android.util.EventLog;
 import android.util.Log;
 
+import com.android.internal.R;
 import com.android.internal.telephony.DataCallState;
 import com.android.internal.telephony.DataConnection;
 import com.android.internal.telephony.DataConnectionTracker;
@@ -366,6 +367,10 @@
 
     @Override
     protected boolean isApnTypeAvailable(String type) {
+        if (type.equals(Phone.APN_TYPE_DUN)) {
+            return (fetchDunApn() != null);
+        }
+
         if (allApns != null) {
             for (ApnSetting apn : allApns) {
                 if (apn.canHandleType(type)) {
@@ -1303,6 +1308,17 @@
         }
     }
 
+    private ApnSetting fetchDunApn() {
+        Context c = phone.getContext();
+        String apnData = Settings.Secure.getString(c.getContentResolver(),
+                                    Settings.Secure.TETHER_DUN_APN);
+        ApnSetting dunSetting = ApnSetting.fromString(apnData);
+        if (dunSetting != null) return dunSetting;
+
+        apnData = c.getResources().getString(R.string.config_tether_apndata);
+        return ApnSetting.fromString(apnData);
+    }
+
     /**
      *
      * @return waitingApns list to be used to create PDP
@@ -1310,6 +1326,13 @@
      */
     private ArrayList<ApnSetting> buildWaitingApns() {
         ArrayList<ApnSetting> apnList = new ArrayList<ApnSetting>();
+
+        if (mRequestedApnType.equals(Phone.APN_TYPE_DUN)) {
+            ApnSetting dun = fetchDunApn();
+            if (dun != null) apnList.add(dun);
+            return apnList;
+        }
+
         String operator = mGsmPhone.mSIMRecords.getSIMOperatorNumeric();
 
         if (mRequestedApnType.equals(Phone.APN_TYPE_DEFAULT)) {