Tethering: IPV6 tethering support

- Added two APIs addUpstreamV6Interface and removeUpstreamV6Interface,
  required to pass the interfaces to use to the Router Advertisement
  Proxy.
- Added a dun entry to the networkAttributes value in
  res/values/config.xml. This is to allow tethering to work with the
  dunRequired property.
- Added dun to allowable tether upstream type.
- Enabled debug flags for debug logging inside framework components.
- Regardless of MOBILE_TYPE_DUN APN being available, if dun_required
  flag is not set, fall back to HIPRI as a preferred APN for the
  embedded data call.
- Debug logging was enabled by default which was causing security
  vulnerabilities. This patch disables debug logging.
- Added a NULL check for Network info in CMD_UPSTREAM_CHANGED
- Changed the exception type thrown from IllegalStateException to
  RemoteException whenever the IPv6 tethering rules fail to apply. The
  IllegalStateException is not caught by the caller causing the JVM
  to crash. By throwing RemoteException, the caller will catch and
  print and error message instead of crashing.

Conflicts:
        services/core/java/com/android/server/NetworkManagementService.java

CRs-fixed: 665103
Change-Id: Idb0971449473554bcd749d478563db7ba351d55d
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index 16250c7..ee8484f 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -185,6 +185,16 @@
     void disableNat(String internalInterface, String externalInterface);
 
     /**
+     * Add an upstream IPv6 interface
+     */
+    void addUpstreamV6Interface(String iface);
+
+    /**
+     * Remove an upstream IPv6 interface
+     */
+    void removeUpstreamV6Interface(String iface);
+
+    /**
      ** PPPD
      **/
 
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 25d47b0..e7fbc19 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -191,6 +191,7 @@
         <item>"mobile,0,0,0,-1,true"</item>
         <item>"mobile_mms,2,0,2,60000,true"</item>
         <item>"mobile_supl,3,0,2,60000,true"</item>
+        <item>"mobile_dun,4,0,2,60000,true"</item>
         <item>"mobile_hipri,5,0,3,60000,true"</item>
         <item>"mobile_fota,10,0,2,60000,true"</item>
         <item>"mobile_ims,11,0,2,60000,true"</item>
@@ -308,6 +309,7 @@
     <integer-array translatable="false" name="config_tether_upstream_types">
         <item>0</item>
         <item>1</item>
+        <item>4</item>
         <item>5</item>
         <item>7</item>
         <item>9</item>
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 020c951..f545d84 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -16,6 +16,7 @@
 
 package com.android.server;
 
+import static android.Manifest.permission.ACCESS_NETWORK_STATE;
 import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
 import static android.Manifest.permission.DUMP;
 import static android.Manifest.permission.SHUTDOWN;
@@ -63,6 +64,7 @@
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.SystemProperties;
+import android.provider.Settings;
 import android.telephony.DataConnectionRealTimeInfo;
 import android.telephony.PhoneStateListener;
 import android.telephony.SubscriptionManager;
@@ -70,6 +72,7 @@
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseBooleanArray;
+import java.util.List;
 
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.net.NetworkStatsFactory;
@@ -138,6 +141,7 @@
         public static final int TetheringStatsResult      = 221;
         public static final int DnsProxyQueryResult       = 222;
         public static final int ClatdStatusResult         = 223;
+        public static final int V6RtrAdvResult            = 227;
 
         public static final int InterfaceChange           = 600;
         public static final int BandwidthControl          = 601;
@@ -815,6 +819,36 @@
     }
 
     @Override
+    public void addUpstreamV6Interface(String iface) throws RemoteException {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
+
+        Slog.d(TAG, "addUpstreamInterface("+ iface + ")");
+        try {
+            final Command cmd = new Command("tether", "interface", "add_upstream");
+            cmd.appendArg(iface);
+            mConnector.execute(cmd);
+        } catch (NativeDaemonConnectorException e) {
+            throw new RemoteException("Cannot add upstream interface");
+        }
+    }
+
+    @Override
+    public void removeUpstreamV6Interface(String iface) throws RemoteException {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
+
+        Slog.d(TAG, "removeUpstreamInterface(" + iface + ")");
+        try {
+            final Command cmd = new Command("tether", "interface", "remove_upstream");
+            cmd.appendArg(iface);
+            mConnector.execute(cmd);
+        } catch (NativeDaemonConnectorException e) {
+            throw new RemoteException("Cannot remove upstream interface");
+        }
+    }
+
+    @Override
     public InterfaceConfiguration getInterfaceConfig(String iface) {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
 
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 6be64cc..eaf6edd 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -26,6 +26,7 @@
 import android.content.res.Resources;
 import android.hardware.usb.UsbManager;
 import android.net.ConnectivityManager;
+import android.net.IConnectivityManager;
 import android.net.INetworkStatsService;
 import android.net.InterfaceConfiguration;
 import android.net.LinkAddress;
@@ -35,10 +36,12 @@
 import android.net.NetworkUtils;
 import android.net.RouteInfo;
 import android.os.Binder;
+import android.os.IBinder;
 import android.os.INetworkManagementService;
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.util.Log;
@@ -553,7 +556,7 @@
                 if (networkInfo != null &&
                         networkInfo.getDetailedState() != NetworkInfo.DetailedState.FAILED) {
                     if (VDBG) Log.d(TAG, "Tethering got CONNECTIVITY_ACTION");
-                    mTetherMasterSM.sendMessage(TetherMasterSM.CMD_UPSTREAM_CHANGED);
+                    mTetherMasterSM.sendMessage(TetherMasterSM.CMD_UPSTREAM_CHANGED, networkInfo);
                 }
             } else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
                 updateConfiguration();
@@ -699,10 +702,14 @@
                         mUpstreamIfaceTypes.add(HIPRI_TYPE);
                     }
                 }
-            }
-            if (mUpstreamIfaceTypes.contains(DUN_TYPE)) {
-                mPreferredUpstreamMobileApn = ConnectivityManager.TYPE_MOBILE_DUN;
+                /* if DUN is still available, make that a priority */
+                if (mUpstreamIfaceTypes.contains(DUN_TYPE)) {
+                    mPreferredUpstreamMobileApn = ConnectivityManager.TYPE_MOBILE_DUN;
+                } else {
+                    mPreferredUpstreamMobileApn = ConnectivityManager.TYPE_MOBILE_HIPRI;
+                }
             } else {
+                /* dun_required is not set, fall back to HIPRI in that case */
                 mPreferredUpstreamMobileApn = ConnectivityManager.TYPE_MOBILE_HIPRI;
             }
         }
@@ -1312,7 +1319,54 @@
                 return true;
             }
 
+            protected void addUpstreamV6Interface(String iface) {
+                IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+                INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
+
+                Log.d(TAG, "adding v6 interface " + iface);
+                try {
+                    service.addUpstreamV6Interface(iface);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Unable to append v6 upstream interface");
+                }
+            }
+
+            protected void removeUpstreamV6Interface(String iface) {
+                IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+                INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
+
+                Log.d(TAG, "removing v6 interface " + iface);
+                try {
+                    service.removeUpstreamV6Interface(iface);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Unable to remove v6 upstream interface");
+                }
+            }
+
+            boolean isIpv6Connected(IConnectivityManager cm, LinkProperties linkProps) {
+                boolean ret = false;
+                Collection <InetAddress> addresses = null;
+
+                if (cm == null || linkProps == null) {
+                    return false;
+                }
+                addresses = linkProps.getAddresses();
+                for (InetAddress addr: addresses) {
+                    if (addr instanceof java.net.Inet6Address) {
+                        java.net.Inet6Address i6addr = (java.net.Inet6Address) addr;
+                        if (!i6addr.isAnyLocalAddress() && !i6addr.isLinkLocalAddress() &&
+                                !i6addr.isLoopbackAddress() && !i6addr.isMulticastAddress()) {
+                            ret = true;
+                            break;
+                        }
+                    }
+                }
+                return ret;
+            }
+
             protected void chooseUpstreamType(boolean tryCell) {
+                IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
+                IConnectivityManager cm = IConnectivityManager.Stub.asInterface(b);
                 int upType = ConnectivityManager.TYPE_NONE;
                 String iface = null;
 
@@ -1327,10 +1381,21 @@
                     }
 
                     for (Integer netType : mUpstreamIfaceTypes) {
-                        NetworkInfo info =
-                                getConnectivityManager().getNetworkInfo(netType.intValue());
+                        NetworkInfo info = null;
+                        LinkProperties props = null;
+                        boolean isV6Connected = false;
+                        try {
+                            info = cm.getNetworkInfo(netType.intValue());
+                            if (info != null) {
+                                props = cm.getLinkPropertiesForType(info.getType());
+                                isV6Connected = isIpv6Connected(cm, props);
+                            }
+                        } catch (RemoteException e) { }
                         if ((info != null) && info.isConnected()) {
                             upType = netType.intValue();
+                            if (isV6Connected) {
+                                addUpstreamV6Interface(props.getInterfaceName());
+                            }
                             break;
                         }
                     }
@@ -1510,8 +1575,19 @@
                     case CMD_UPSTREAM_CHANGED:
                         if(VDBG) Log.d(TAG, "CMD_UPSTREAM_CHANGED event received");
                         // need to try DUN immediately if Wifi goes down
+                        NetworkInfo info = (NetworkInfo) message.obj;
                         mTryCell = !WAIT_FOR_NETWORK_TO_SETTLE;
                         chooseUpstreamType(mTryCell);
+                        if ((info != null) && (!info.isConnected())) {
+                            IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
+                            IConnectivityManager cm = IConnectivityManager.Stub.asInterface(b);
+                            try {
+                                LinkProperties props = cm.getLinkPropertiesForType(info.getType());
+                                removeUpstreamV6Interface(props.getInterfaceName());
+                            } catch(RemoteException e) {
+                                Log.e(TAG, "Exception querying ConnectivityManager", e);
+                            }
+                        }
                         mTryCell = !mTryCell;
                         break;
                     case CMD_CELL_CONNECTION_RENEW: