Loading core/java/android/net/ConnectivityManager.java +6 −0 Original line number Diff line number Diff line Loading @@ -163,6 +163,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. Loading core/res/res/values/config.xml +8 −12 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -154,20 +154,16 @@ <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 --> <!-- Interfaces will be prioritized according to the order listed --> <string-array translatable="false" name="config_tether_upstream_regexs"> </string-array> <!-- Regex of wired ethernet ifaces --> <string translatable="false" name="config_ethernet_iface_regex">eth\\d</string> <!-- 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: Loading services/java/com/android/server/ConnectivityService.java +2 −18 Original line number Diff line number Diff line Loading @@ -466,12 +466,10 @@ public class ConnectivityService extends IConnectivityManager.Stub { INetworkManagementService nmService = INetworkManagementService.Stub.asInterface(b); mTethering = new Tethering(mContext, nmService, 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); mVpn = new Vpn(mContext, new VpnCallback()); Loading Loading @@ -1576,12 +1574,6 @@ public class ConnectivityService extends IConnectivityManager.Stub { } 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]); Loading Loading @@ -2412,14 +2404,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); } Loading services/java/com/android/server/connectivity/Tethering.java +115 −136 Original line number Diff line number Diff line Loading @@ -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; /** Loading @@ -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 INetworkManagementService mNMService; private Looper mLooper; Loading Loading @@ -112,9 +122,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; Loading Loading @@ -159,7 +166,6 @@ public class Tethering extends INetworkManagementEventObserver.Stub { if ((mDhcpRange.length == 0) || (mDhcpRange.length % 2 ==1)) { mDhcpRange = DHCP_DEFAULT_RANGE; } mDunRequired = false; // resample when we turn on mTetherableUsbRegexs = context.getResources().getStringArray( com.android.internal.R.array.config_tether_usb_regexs); Loading @@ -167,8 +173,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]; Loading Loading @@ -582,16 +595,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() { Loading Loading @@ -648,17 +689,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; Loading Loading @@ -1051,8 +1081,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 Loading @@ -1072,7 +1100,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; Loading Loading @@ -1111,22 +1139,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 cm = IConnectivityManager.Stub.asInterface(b); int result = Phone.APN_REQUEST_FAILED; String enableString = enableString(apnType); if (enableString == null) return false; try { result = cm.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); Loading @@ -1139,18 +1179,17 @@ 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 cm = IConnectivityManager.Stub.asInterface(b); try { cm.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; } Loading Loading @@ -1196,108 +1235,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; for (Integer netType : mUpstreamIfaceTypes) { NetworkInfo info = null; try { LinkProperties defaultProp = cm.getActiveLinkProperties(); if (defaultProp != null) { String iface = defaultProp.getInterfaceName(); for(String regex : mUpstreamIfaceRegexs) { if (iface.matches(regex)) return iface; } } info = cm.getNetworkInfo(netType.intValue()); } catch (RemoteException e) { } String[] ifaces = new String[0]; try { ifaces = mNMService.listInterfaces(); } catch (Exception e) { Log.e(TAG, "Error listing Interfaces", e); return null; } for (String regex : mUpstreamIfaceRegexs) { for (String iface : ifaces) { if (iface.matches(regex)) { // verify it is active InterfaceConfiguration ifcg = null; try { ifcg = mNMService.getInterfaceConfig(iface); if (ifcg.isActive()) { return iface; } } catch (Exception e) { Log.e(TAG, "Error getting iface config", e); // ignore - try next continue; } } } 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); } 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; Log.d(TAG, "chooseUpstreamType(" + tryCell + "), preferredApn =" + mPreferredUpstreamMobileApn + ", got type=" + upType); } // 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; Loading @@ -1312,7 +1298,6 @@ public class Tethering extends INetworkManagementEventObserver.Stub { class InitialState extends TetherMasterUtilState { @Override public void enter() { mMobileReserved = false; } @Override public boolean processMessage(Message message) { Loading @@ -1320,7 +1305,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); Loading Loading @@ -1354,7 +1339,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub { } @Override public void exit() { turnOffMobileConnection(); turnOffUpstreamMobileConnection(); notifyTetheredOfNewUpstreamIface(null); } @Override Loading Loading @@ -1392,19 +1377,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; Loading Loading
core/java/android/net/ConnectivityManager.java +6 −0 Original line number Diff line number Diff line Loading @@ -163,6 +163,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. Loading
core/res/res/values/config.xml +8 −12 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -154,20 +154,16 @@ <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 --> <!-- Interfaces will be prioritized according to the order listed --> <string-array translatable="false" name="config_tether_upstream_regexs"> </string-array> <!-- Regex of wired ethernet ifaces --> <string translatable="false" name="config_ethernet_iface_regex">eth\\d</string> <!-- 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: Loading
services/java/com/android/server/ConnectivityService.java +2 −18 Original line number Diff line number Diff line Loading @@ -466,12 +466,10 @@ public class ConnectivityService extends IConnectivityManager.Stub { INetworkManagementService nmService = INetworkManagementService.Stub.asInterface(b); mTethering = new Tethering(mContext, nmService, 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); mVpn = new Vpn(mContext, new VpnCallback()); Loading Loading @@ -1576,12 +1574,6 @@ public class ConnectivityService extends IConnectivityManager.Stub { } 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]); Loading Loading @@ -2412,14 +2404,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); } Loading
services/java/com/android/server/connectivity/Tethering.java +115 −136 Original line number Diff line number Diff line Loading @@ -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; /** Loading @@ -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 INetworkManagementService mNMService; private Looper mLooper; Loading Loading @@ -112,9 +122,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; Loading Loading @@ -159,7 +166,6 @@ public class Tethering extends INetworkManagementEventObserver.Stub { if ((mDhcpRange.length == 0) || (mDhcpRange.length % 2 ==1)) { mDhcpRange = DHCP_DEFAULT_RANGE; } mDunRequired = false; // resample when we turn on mTetherableUsbRegexs = context.getResources().getStringArray( com.android.internal.R.array.config_tether_usb_regexs); Loading @@ -167,8 +173,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]; Loading Loading @@ -582,16 +595,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() { Loading Loading @@ -648,17 +689,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; Loading Loading @@ -1051,8 +1081,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 Loading @@ -1072,7 +1100,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; Loading Loading @@ -1111,22 +1139,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 cm = IConnectivityManager.Stub.asInterface(b); int result = Phone.APN_REQUEST_FAILED; String enableString = enableString(apnType); if (enableString == null) return false; try { result = cm.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); Loading @@ -1139,18 +1179,17 @@ 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 cm = IConnectivityManager.Stub.asInterface(b); try { cm.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; } Loading Loading @@ -1196,108 +1235,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; for (Integer netType : mUpstreamIfaceTypes) { NetworkInfo info = null; try { LinkProperties defaultProp = cm.getActiveLinkProperties(); if (defaultProp != null) { String iface = defaultProp.getInterfaceName(); for(String regex : mUpstreamIfaceRegexs) { if (iface.matches(regex)) return iface; } } info = cm.getNetworkInfo(netType.intValue()); } catch (RemoteException e) { } String[] ifaces = new String[0]; try { ifaces = mNMService.listInterfaces(); } catch (Exception e) { Log.e(TAG, "Error listing Interfaces", e); return null; } for (String regex : mUpstreamIfaceRegexs) { for (String iface : ifaces) { if (iface.matches(regex)) { // verify it is active InterfaceConfiguration ifcg = null; try { ifcg = mNMService.getInterfaceConfig(iface); if (ifcg.isActive()) { return iface; } } catch (Exception e) { Log.e(TAG, "Error getting iface config", e); // ignore - try next continue; } } } 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); } 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; Log.d(TAG, "chooseUpstreamType(" + tryCell + "), preferredApn =" + mPreferredUpstreamMobileApn + ", got type=" + upType); } // 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; Loading @@ -1312,7 +1298,6 @@ public class Tethering extends INetworkManagementEventObserver.Stub { class InitialState extends TetherMasterUtilState { @Override public void enter() { mMobileReserved = false; } @Override public boolean processMessage(Message message) { Loading @@ -1320,7 +1305,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); Loading Loading @@ -1354,7 +1339,7 @@ public class Tethering extends INetworkManagementEventObserver.Stub { } @Override public void exit() { turnOffMobileConnection(); turnOffUpstreamMobileConnection(); notifyTetheredOfNewUpstreamIface(null); } @Override Loading Loading @@ -1392,19 +1377,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; Loading