OSDN Git Service

Fix tethering upstream determination.
authorRobert Greenwalt <rgreenwalt@google.com>
Fri, 3 Jun 2011 00:30:47 +0000 (17:30 -0700)
committerRobert Greenwalt <rgreenwalt@google.com>
Mon, 20 Jun 2011 18:09:23 +0000 (11:09 -0700)
Used to use iface names, but on mobile ifaces are multipurpose.
Refined to use ConnectivityService types.
bug:4530116

Change-Id: Iaa632d5f540d27ffff683bd7d94f8004bf422e71

core/java/android/net/ConnectivityManager.java
core/res/res/values/config.xml
services/java/com/android/server/ConnectivityService.java
services/java/com/android/server/connectivity/Tethering.java

index eaf9191..5b8076e 100644 (file)
@@ -164,6 +164,12 @@ public class ConnectivityManager
     public static final String EXTRA_ERRORED_TETHER = "erroredArray";
 
     /**
+     * The absence of APN..
+     * @hide
+     */
+    public static final int TYPE_NONE        = -1;
+
+    /**
      * The Default Mobile data connection.  When active, all data traffic
      * will use this connection by default.
      */
index 60f11ce..843259d 100755 (executable)
@@ -98,7 +98,7 @@
     <!-- This string array should be overridden by the device to present a list of network
          attributes.  This is used by the connectivity manager to decide which networks can coexist
          based on the hardware -->
-    <!-- An Array of "[Connection name],[ConnectivityManager connection type],
+    <!-- An Array of "[Connection name],[ConnectivityManager.TYPE_xxxx],
          [associated radio-type],[priority],[restoral-timer(ms)],[dependencyMet]  -->
     <!-- the 5th element "resore-time" indicates the number of milliseconds to delay
          before automatically restore the default connection.  Set -1 if the connection
     <string-array translatable="false" name="config_tether_dhcp_range">
     </string-array>
 
-    <!-- Regex array of allowable upstream ifaces for tethering - for example if you want
-         tethering on a new interface called "foo2" add <item>"foo\\d"</item> to the array -->
-    <string-array translatable="false" name="config_tether_upstream_regexs">
-    </string-array>
-
-    <!-- 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>
+    <!-- Array of ConnectivityManager.TYPE_xxxx values allowable for tethering -->
+    <!-- Common options are [1, 4] for TYPE_WIFI and TYPE_MOBILE_DUN or
+    <!== [0,1,5,7] for TYPE_MOBILE, TYPE_WIFI, TYPE_MOBILE_HIPRI and TYPE_BLUETOOTH -->
+    <integer-array translatable="false" name="config_tether_upstream_types">
+        <item>1</item>
+        <item>4</item>
+    </integer-array>
 
     <!-- String containing the apn value for tethering.  May be overriden by secure settings
          TETHER_DUN_APN.  Value is a comma separated series of strings:
index cc65b56..a8ddc15 100644 (file)
@@ -431,12 +431,10 @@ public class ConnectivityService extends IConnectivityManager.Stub {
         }
 
         mTethering = new Tethering(mContext, mHandler.getLooper());
-        mTetheringConfigValid = (((mNetTrackers[ConnectivityManager.TYPE_MOBILE_DUN] != null) ||
-                                  !mTethering.isDunRequired()) &&
-                                 (mTethering.getTetherableUsbRegexs().length != 0 ||
+        mTetheringConfigValid = ((mTethering.getTetherableUsbRegexs().length != 0 ||
                                   mTethering.getTetherableWifiRegexs().length != 0 ||
                                   mTethering.getTetherableBluetoothRegexs().length != 0) &&
-                                 mTethering.getUpstreamIfaceRegexs().length != 0);
+                                 mTethering.getUpstreamIfaceTypes().length != 0);
 
         if (DBG) {
             mInetLog = new ArrayList();
@@ -1400,12 +1398,6 @@ public class ConnectivityService extends IConnectivityManager.Stub {
             } else {
                 addPrivateDnsRoutes(mNetTrackers[netType]);
             }
-
-            /** Notify TetheringService if interface name has been changed. */
-            if (TextUtils.equals(mNetTrackers[netType].getNetworkInfo().getReason(),
-                                 Phone.REASON_LINK_PROPERTIES_CHANGED)) {
-                handleTetherIfaceChange(netType);
-            }
         } else {
             if (mNetConfigs[netType].isDefault()) {
                 removeDefaultRoute(mNetTrackers[netType]);
@@ -2253,14 +2245,6 @@ public class ConnectivityService extends IConnectivityManager.Stub {
         }
     }
 
-    private void handleTetherIfaceChange(int type) {
-        String iface = mNetTrackers[type].getLinkProperties().getInterfaceName();
-
-        if (isTetheringSupported()) {
-            mTethering.handleTetherIfaceChange(iface);
-        }
-    }
-
     private void log(String s) {
         Slog.d(TAG, s);
     }
index ffadc65..099c950 100644 (file)
@@ -57,7 +57,9 @@ import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.net.InetAddress;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.Set;
 /**
@@ -82,7 +84,15 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
     private String[] mTetherableUsbRegexs;
     private String[] mTetherableWifiRegexs;
     private String[] mTetherableBluetoothRegexs;
-    private String[] mUpstreamIfaceRegexs;
+    private Collection<Integer> mUpstreamIfaceTypes;
+
+    private static final Integer MOBILE_TYPE = new Integer(ConnectivityManager.TYPE_MOBILE);
+    private static final Integer HIPRI_TYPE = new Integer(ConnectivityManager.TYPE_MOBILE_HIPRI);
+    private static final Integer DUN_TYPE = new Integer(ConnectivityManager.TYPE_MOBILE_DUN);
+
+    // if we have to connect to mobile, what APN type should we use?  Calculated by examining the
+    // upstream type list and the DUN_REQUIRED secure-setting
+    private int mPreferredUpstreamMobileApn = ConnectivityManager.TYPE_NONE;
 
     private Looper mLooper;
     private HandlerThread mThread;
@@ -120,9 +130,6 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
     private static final String DNS_DEFAULT_SERVER1 = "8.8.8.8";
     private static final String DNS_DEFAULT_SERVER2 = "8.8.4.4";
 
-    // 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 StateMachine mTetherMasterSM;
 
     private Notification mTetheredNotification;
@@ -189,7 +196,6 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
             mDhcpRange[12] = DHCP_DEFAULT_RANGE7_START;
             mDhcpRange[13] = DHCP_DEFAULT_RANGE7_STOP;
         }
-        mDunRequired = false; // resample when we turn on
 
         mTetherableUsbRegexs = context.getResources().getStringArray(
                 com.android.internal.R.array.config_tether_usb_regexs);
@@ -197,8 +203,15 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
                 com.android.internal.R.array.config_tether_wifi_regexs);
         mTetherableBluetoothRegexs = context.getResources().getStringArray(
                 com.android.internal.R.array.config_tether_bluetooth_regexs);
-        mUpstreamIfaceRegexs = context.getResources().getStringArray(
-                com.android.internal.R.array.config_tether_upstream_regexs);
+        int ifaceTypes[] = context.getResources().getIntArray(
+                com.android.internal.R.array.config_tether_upstream_types);
+        mUpstreamIfaceTypes = new ArrayList();
+        for (int i : ifaceTypes) {
+            mUpstreamIfaceTypes.add(new Integer(i));
+        }
+
+        // check if the upstream type list needs to be modified due to secure-settings
+        checkDunRequired();
 
         // TODO - remove and rely on real notifications of the current iface
         mDnsServers = new String[2];
@@ -602,16 +615,44 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
         return mTetherableBluetoothRegexs;
     }
 
-    public String[] getUpstreamIfaceRegexs() {
-        return mUpstreamIfaceRegexs;
+    public int[] getUpstreamIfaceTypes() {
+        int values[] = new int[mUpstreamIfaceTypes.size()];
+        Iterator<Integer> iterator = mUpstreamIfaceTypes.iterator();
+        for (int i=0; i < mUpstreamIfaceTypes.size(); i++) {
+            values[i] = iterator.next();
+        }
+        return values;
     }
 
-    public boolean isDunRequired() {
-        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 void checkDunRequired() {
+        int requiredApn = ((Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Secure.TETHER_DUN_REQUIRED, 0) == 1) ?
+                ConnectivityManager.TYPE_MOBILE_DUN :
+                ConnectivityManager.TYPE_MOBILE_HIPRI);
+        if (mPreferredUpstreamMobileApn != requiredApn) {
+            if (requiredApn == ConnectivityManager.TYPE_MOBILE_DUN) {
+                while (mUpstreamIfaceTypes.contains(MOBILE_TYPE)) {
+                    mUpstreamIfaceTypes.remove(MOBILE_TYPE);
+                }
+                while (mUpstreamIfaceTypes.contains(HIPRI_TYPE)) {
+                    mUpstreamIfaceTypes.remove(HIPRI_TYPE);
+                }
+                if (mUpstreamIfaceTypes.contains(DUN_TYPE) == false) {
+                    mUpstreamIfaceTypes.add(DUN_TYPE);
+                }
+            } else {
+                while (mUpstreamIfaceTypes.contains(DUN_TYPE)) {
+                    mUpstreamIfaceTypes.remove(DUN_TYPE);
+                }
+                if (mUpstreamIfaceTypes.contains(MOBILE_TYPE) == false) {
+                    mUpstreamIfaceTypes.add(MOBILE_TYPE);
+                }
+                if (mUpstreamIfaceTypes.contains(HIPRI_TYPE) == false) {
+                    mUpstreamIfaceTypes.add(HIPRI_TYPE);
+                }
+            }
+            mPreferredUpstreamMobileApn = requiredApn;
+        }
     }
 
     public String[] getTetheredIfaces() {
@@ -668,17 +709,6 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
         return retVal;
     }
 
-    public void handleTetherIfaceChange(String iface) {
-        // check if iface is white listed
-        for (String regex : mUpstreamIfaceRegexs) {
-            if (iface.matches(regex)) {
-                if (DEBUG) Log.d(TAG, "Tethering got Interface Change");
-                mTetherMasterSM.sendMessage(TetherMasterSM.CMD_IFACE_CHANGED, iface);
-                break;
-            }
-        }
-    }
-
     class TetherInterfaceSM extends StateMachine {
         // notification from the master SM that it's not in tether mode
         static final int CMD_TETHER_MODE_DEAD            =  1;
@@ -1086,8 +1116,6 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
         static final int CMD_CELL_CONNECTION_RENEW   = 4;
         // we don't have a valid upstream conn, check again after a delay
         static final int CMD_RETRY_UPSTREAM          = 5;
-        // received an indication that upstream interface has changed
-        static final int CMD_IFACE_CHANGED           = 6;
 
         // This indicates what a timeout event relates to.  A state that
         // sends itself a delayed timeout event and handles incoming timeout events
@@ -1107,7 +1135,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
         private ArrayList mNotifyList;
 
         private int mCurrentConnectionSequence;
-        private boolean mMobileReserved = false;
+        private int mMobileApnReserved = ConnectivityManager.TYPE_NONE;
 
         private String mUpstreamIfaceName = null;
 
@@ -1146,22 +1174,34 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
             public boolean processMessage(Message m) {
                 return false;
             }
-            protected boolean turnOnMobileConnection() {
+            protected String enableString(int apnType) {
+                switch (apnType) {
+                case ConnectivityManager.TYPE_MOBILE_DUN:
+                    return Phone.FEATURE_ENABLE_DUN_ALWAYS;
+                case ConnectivityManager.TYPE_MOBILE:
+                case ConnectivityManager.TYPE_MOBILE_HIPRI:
+                    return Phone.FEATURE_ENABLE_HIPRI;
+                }
+                return null;
+            }
+            protected boolean turnOnUpstreamMobileConnection(int apnType) {
                 boolean retValue = true;
-                if (mMobileReserved) return retValue;
+                if (apnType == ConnectivityManager.TYPE_NONE) return false;
+                if (apnType != mMobileApnReserved) turnOffUpstreamMobileConnection();
                 IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
                 IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
                 int result = Phone.APN_REQUEST_FAILED;
+                String enableString = enableString(apnType);
+                if (enableString == null) return false;
                 try {
                     result = service.startUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
-                            (mDunRequired ? Phone.FEATURE_ENABLE_DUN_ALWAYS :
-                            Phone.FEATURE_ENABLE_HIPRI), new Binder());
+                            enableString, new Binder());
                 } catch (Exception e) {
                 }
                 switch (result) {
                 case Phone.APN_ALREADY_ACTIVE:
                 case Phone.APN_REQUEST_STARTED:
-                    mMobileReserved = true;
+                    mMobileApnReserved = apnType;
                     Message m = obtainMessage(CMD_CELL_CONNECTION_RENEW);
                     m.arg1 = ++mCurrentConnectionSequence;
                     sendMessageDelayed(m, CELL_CONNECTION_RENEW_MS);
@@ -1174,19 +1214,18 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
 
                 return retValue;
             }
-            protected boolean turnOffMobileConnection() {
-                if (mMobileReserved) {
+            protected boolean turnOffUpstreamMobileConnection() {
+                if (mMobileApnReserved != ConnectivityManager.TYPE_NONE) {
                     IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
                     IConnectivityManager service =
                             IConnectivityManager.Stub.asInterface(b);
                     try {
                         service.stopUsingNetworkFeature(ConnectivityManager.TYPE_MOBILE,
-                                (mDunRequired? Phone.FEATURE_ENABLE_DUN_ALWAYS :
-                                             Phone.FEATURE_ENABLE_HIPRI));
+                                enableString(mMobileApnReserved));
                     } catch (Exception e) {
                         return false;
                     }
-                    mMobileReserved = false;
+                    mMobileApnReserved = ConnectivityManager.TYPE_NONE;
                 }
                 return true;
             }
@@ -1238,111 +1277,55 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
                 transitionTo(mInitialState);
                 return true;
             }
-            protected String findActiveUpstreamIface() {
-                // check for what iface we can use - if none found switch to error.
+
+            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;
 
-                try {
-                    LinkProperties defaultProp = cm.getActiveLinkProperties();
-                    if (defaultProp != null) {
-                        String iface = defaultProp.getInterfaceName();
-                        for(String regex : mUpstreamIfaceRegexs) {
-                            if (iface.matches(regex)) return iface;
-                        }
-                    }
-                } catch (RemoteException e) { }
-
-                b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
-                INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
-
-                String[] ifaces = new String[0];
-                try {
-                    ifaces = service.listInterfaces();
-                } catch (Exception e) {
-                    Log.e(TAG, "Error listing Interfaces :" + e);
-                    return null;
-                }
-
-                for (String iface : ifaces) {
-                    for (String regex : mUpstreamIfaceRegexs) {
-                        if (iface.matches(regex)) {
-                            // verify it is active
-                            InterfaceConfiguration ifcg = null;
-                            try {
-                                ifcg = service.getInterfaceConfig(iface);
-                                if (ifcg.isActive()) {
-                                    return iface;
-                                }
-                            } catch (Exception e) {
-                                Log.e(TAG, "Error getting iface config :" + e);
-                                // ignore - try next
-                                continue;
-                            }
-                        }
+                for (Integer netType : mUpstreamIfaceTypes) {
+                    NetworkInfo info = null;
+                    try {
+                        info = cm.getNetworkInfo(netType.intValue());
+                    } catch (RemoteException e) { }
+                    if ((info != null) && info.isConnected()) {
+                        upType = netType.intValue();
+                        break;
                     }
                 }
-                return null;
-            }
 
-            protected void chooseUpstreamType(boolean tryCell) {
-                // decide if the current upstream is good or not and if not
-                // do something about it (start up DUN if required or HiPri if not)
-                String iface = findActiveUpstreamIface();
-                IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
-                IConnectivityManager cm = IConnectivityManager.Stub.asInterface(b);
-                mMobileReserved = false;
                 if (DEBUG) {
-                    Log.d(TAG, "chooseUpstreamType(" + tryCell + "),  dunRequired ="
-                            + mDunRequired + ", iface=" + iface);
+                    Log.d(TAG, "chooseUpstreamType(" + tryCell + "), preferredApn ="
+                            + mPreferredUpstreamMobileApn + ", got type=" + upType);
                 }
-                if (iface != null) {
-                    try {
-                        if (mDunRequired) {
-                            // check if Dun is on - we can use that
-                            NetworkInfo info = cm.getNetworkInfo(
-                                    ConnectivityManager.TYPE_MOBILE_DUN);
-                            if (info.isConnected()) {
-                                if (DEBUG) Log.d(TAG, "setting dun ifacename =" + iface);
-                                // even if we're already connected - it may be somebody else's
-                                // refcount, so add our own
-                                turnOnMobileConnection();
-                            } else {
-                                // verify the iface is not the default mobile - can't use that!
-                                info = cm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
-                                if (info.isConnected()) {
-                                    iface = null; // can't accept this one
-                                }
-                            }
-                        } else {
-                            if (DEBUG) Log.d(TAG, "checking if hipri brought us this connection");
-                            NetworkInfo info = cm.getNetworkInfo(
-                                    ConnectivityManager.TYPE_MOBILE_HIPRI);
-                            if (info.isConnected()) {
-                                if (DEBUG) Log.d(TAG, "yes - hipri in use");
-                                // even if we're already connected - it may be sombody else's
-                                // refcount, so add our own
-                                turnOnMobileConnection();
-                            }
-                        }
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "RemoteException calling ConnectivityManager " + e);
-                        iface = null;
-                    }
+
+                // if we're on DUN, put our own grab on it
+                if (upType == ConnectivityManager.TYPE_MOBILE_DUN ||
+                        upType == ConnectivityManager.TYPE_MOBILE_HIPRI) {
+                    turnOnUpstreamMobileConnection(upType);
                 }
-                // may have been set to null in the if above
-                if (iface == null ) {
-                    boolean success = false;
-                    if (tryCell == TRY_TO_SETUP_MOBILE_CONNECTION) {
-                        success = turnOnMobileConnection();
+
+                if (upType == ConnectivityManager.TYPE_NONE) {
+                    boolean tryAgainLater = true;
+                    if ((tryCell == TRY_TO_SETUP_MOBILE_CONNECTION) &&
+                            (turnOnUpstreamMobileConnection(mPreferredUpstreamMobileApn) == true)) {
+                        // we think mobile should be coming up - don't set a retry
+                        tryAgainLater = false;
                     }
-                    if (!success) {
-                        // wait for things to settle and retry
+                    if (tryAgainLater) {
                         sendMessageDelayed(CMD_RETRY_UPSTREAM, UPSTREAM_SETTLE_TIME_MS);
                     }
+                } else {
+                    LinkProperties linkProperties = null;
+                    try {
+                        linkProperties = cm.getLinkProperties(upType);
+                    } catch (RemoteException e) { }
+                    if (linkProperties != null) iface = linkProperties.getInterfaceName();
                 }
                 notifyTetheredOfNewUpstreamIface(iface);
             }
+
             protected void notifyTetheredOfNewUpstreamIface(String ifaceName) {
                 if (DEBUG) Log.d(TAG, "notifying tethered with iface =" + ifaceName);
                 mUpstreamIfaceName = ifaceName;
@@ -1357,7 +1340,6 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
         class InitialState extends TetherMasterUtilState {
             @Override
             public void enter() {
-                mMobileReserved = false;
             }
             @Override
             public boolean processMessage(Message message) {
@@ -1365,7 +1347,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
                 boolean retValue = true;
                 switch (message.what) {
                     case CMD_TETHER_MODE_REQUESTED:
-                        mDunRequired = isDunRequired();
+                        checkDunRequired();
                         TetherInterfaceSM who = (TetherInterfaceSM)message.obj;
                         if (DEBUG) Log.d(TAG, "Tether Mode requested by " + who.toString());
                         mNotifyList.add(who);
@@ -1399,7 +1381,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
             }
             @Override
             public void exit() {
-                turnOffMobileConnection();
+                turnOffUpstreamMobileConnection();
                 notifyTetheredOfNewUpstreamIface(null);
             }
             @Override
@@ -1437,19 +1419,13 @@ public class Tethering extends INetworkManagementEventObserver.Stub {
                                 Log.d(TAG, "renewing mobile connection - requeuing for another " +
                                         CELL_CONNECTION_RENEW_MS + "ms");
                             }
-                            mMobileReserved = false; // need to renew it
-                            turnOnMobileConnection();
+                            turnOnUpstreamMobileConnection(mMobileApnReserved);
                         }
                         break;
                     case CMD_RETRY_UPSTREAM:
                         chooseUpstreamType(mTryCell);
                         mTryCell = !mTryCell;
                         break;
-                    case CMD_IFACE_CHANGED:
-                        String iface = (String)message.obj;
-                        if (DEBUG) Log.d(TAG, "Activie upstream interface changed: " + iface);
-                        notifyTetheredOfNewUpstreamIface(iface);
-                        break;
                     default:
                         retValue = false;
                         break;