diff options
12 files changed, 972 insertions, 522 deletions
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index 1429bc1..a127df0 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -18,6 +18,7 @@ package android.net; import android.annotation.SdkConstant; import android.annotation.SdkConstant.SdkConstantType; +import android.os.Binder; import android.os.RemoteException; /** @@ -114,15 +115,64 @@ public class ConnectivityManager public static final String ACTION_BACKGROUND_DATA_SETTING_CHANGED = "android.net.conn.BACKGROUND_DATA_SETTING_CHANGED"; - public static final int TYPE_MOBILE = 0; - public static final int TYPE_WIFI = 1; + /** + * The Default Mobile data connection. When active, all data traffic + * will use this connection by default. Should not coexist with other + * default connections. + */ + public static final int TYPE_MOBILE = 0; + /** + * The Default WIFI data connection. When active, all data traffic + * will use this connection by default. Should not coexist with other + * default connections. + */ + public static final int TYPE_WIFI = 1; + /** + * An MMS-specific Mobile data connection. This connection may be the + * same as {@link #TYPEMOBILE} but it may be different. This is used + * by applications needing to talk to the carrier's Multimedia Messaging + * Service servers. It may coexist with default data connections. + * {@hide} + */ + public static final int TYPE_MOBILE_MMS = 2; + /** + * A SUPL-specific Mobile data connection. This connection may be the + * same as {@link #TYPEMOBILE} but it may be different. This is used + * by applications needing to talk to the carrier's Secure User Plane + * Location servers for help locating the device. It may coexist with + * default data connections. + * {@hide} + */ + public static final int TYPE_MOBILE_SUPL = 3; + /** + * A DUN-specific Mobile data connection. This connection may be the + * same as {@link #TYPEMOBILE} but it may be different. This is used + * by applicaitons performing a Dial Up Networking bridge so that + * the carrier is aware of DUN traffic. It may coexist with default data + * connections. + * {@hide} + */ + public static final int TYPE_MOBILE_DUN = 4; + /** + * A High Priority Mobile data connection. This connection is typically + * the same as {@link #TYPEMOBILE} but the routing setup is different. + * Only requesting processes will have access to the Mobile DNS servers + * and only IP's explicitly requested via {@link #requestRouteToHost} + * will route over this interface. + *{@hide} + */ + public static final int TYPE_MOBILE_HIPRI = 5; + /** {@hide} */ + public static final int MAX_RADIO_TYPE = TYPE_WIFI; + /** {@hide} */ + public static final int MAX_NETWORK_TYPE = TYPE_MOBILE_HIPRI; public static final int DEFAULT_NETWORK_PREFERENCE = TYPE_WIFI; private IConnectivityManager mService; static public boolean isNetworkTypeValid(int networkType) { - return networkType == TYPE_WIFI || networkType == TYPE_MOBILE; + return networkType >= 0 && networkType <= MAX_NETWORK_TYPE; } public void setNetworkPreference(int preference) { @@ -195,7 +245,8 @@ public class ConnectivityManager */ public int startUsingNetworkFeature(int networkType, String feature) { try { - return mService.startUsingNetworkFeature(networkType, feature); + return mService.startUsingNetworkFeature(networkType, feature, + new Binder()); } catch (RemoteException e) { return -1; } diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl index de68598..9f59cce 100644 --- a/core/java/android/net/IConnectivityManager.aidl +++ b/core/java/android/net/IConnectivityManager.aidl @@ -17,6 +17,7 @@ package android.net; import android.net.NetworkInfo; +import android.os.IBinder; /** * Interface that answers queries about, and allows changing, the @@ -39,7 +40,8 @@ interface IConnectivityManager boolean setRadio(int networkType, boolean turnOn); - int startUsingNetworkFeature(int networkType, in String feature); + int startUsingNetworkFeature(int networkType, in String feature, + in IBinder binder); int stopUsingNetworkFeature(int networkType, in String feature); diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java index 1064fb6..6b00900 100644 --- a/core/java/android/net/MobileDataStateTracker.java +++ b/core/java/android/net/MobileDataStateTracker.java @@ -32,9 +32,6 @@ import android.telephony.TelephonyManager; import android.util.Log; import android.text.TextUtils; -import java.util.List; -import java.util.ArrayList; - /** * Track the state of mobile data connectivity. This is done by * receiving broadcast intents from the Phone process whenever @@ -45,36 +42,47 @@ import java.util.ArrayList; public class MobileDataStateTracker extends NetworkStateTracker { private static final String TAG = "MobileDataStateTracker"; - private static final boolean DBG = false; + private static final boolean DBG = true; private Phone.DataState mMobileDataState; private ITelephony mPhoneService; - private static final String[] sDnsPropNames = { - "net.rmnet0.dns1", - "net.rmnet0.dns2", - "net.eth0.dns1", - "net.eth0.dns2", - "net.eth0.dns3", - "net.eth0.dns4", - "net.gprs.dns1", - "net.gprs.dns2" - }; - private List<String> mDnsServers; - private String mInterfaceName; - private int mDefaultGatewayAddr; - private int mLastCallingPid = -1; + + private String mApnType; + private boolean mEnabled; + private boolean mTeardownRequested; /** * Create a new MobileDataStateTracker * @param context the application context of the caller * @param target a message handler for getting callbacks about state changes + * @param netType the ConnectivityManager network type + * @param apnType the Phone apnType + * @param tag the name of this network */ - public MobileDataStateTracker(Context context, Handler target) { - super(context, target, ConnectivityManager.TYPE_MOBILE, - TelephonyManager.getDefault().getNetworkType(), "MOBILE", - TelephonyManager.getDefault().getNetworkTypeName()); + public MobileDataStateTracker(Context context, Handler target, + int netType, String apnType, String tag) { + super(context, target, netType, + TelephonyManager.getDefault().getNetworkType(), tag, + TelephonyManager.getDefault().getNetworkTypeName()); + mApnType = apnType; mPhoneService = null; - mDnsServers = new ArrayList<String>(); + mTeardownRequested = false; + if(netType == ConnectivityManager.TYPE_MOBILE) { + mEnabled = true; + } else { + mEnabled = false; + } + + mDnsPropNames = new String[] { + "net.rmnet0.dns1", + "net.rmnet0.dns2", + "net.eth0.dns1", + "net.eth0.dns2", + "net.eth0.dns3", + "net.eth0.dns4", + "net.gprs.dns1", + "net.gprs.dns2"}; + } /** @@ -93,31 +101,70 @@ public class MobileDataStateTracker extends NetworkStateTracker { mMobileDataState = Phone.DataState.DISCONNECTED; } - private static Phone.DataState getMobileDataState(Intent intent) { + private Phone.DataState getMobileDataState(Intent intent) { String str = intent.getStringExtra(Phone.STATE_KEY); - if (str != null) - return Enum.valueOf(Phone.DataState.class, str); - else - return Phone.DataState.DISCONNECTED; + if (str != null) { + String apnTypeList = + intent.getStringExtra(Phone.DATA_APN_TYPES_KEY); + if (isApnTypeIncluded(apnTypeList)) { + return Enum.valueOf(Phone.DataState.class, str); + } + } + return Phone.DataState.DISCONNECTED; + } + + private boolean isApnTypeIncluded(String typeList) { + /* comma seperated list - split and check */ + if (typeList == null) + return false; + + String[] list = typeList.split(","); + for(int i=0; i< list.length; i++) { + if (TextUtils.equals(list[i], mApnType) || + TextUtils.equals(list[i], Phone.APN_TYPE_ALL)) { + return true; + } + } + return false; } private class MobileDataStateReceiver extends BroadcastReceiver { public void onReceive(Context context, Intent intent) { - if (intent.getAction().equals(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED)) { + if (intent.getAction().equals(TelephonyIntents. + ACTION_ANY_DATA_CONNECTION_STATE_CHANGED)) { Phone.DataState state = getMobileDataState(intent); - String reason = intent.getStringExtra(Phone.STATE_CHANGE_REASON_KEY); + String reason = + intent.getStringExtra(Phone.STATE_CHANGE_REASON_KEY); String apnName = intent.getStringExtra(Phone.DATA_APN_KEY); - boolean unavailable = intent.getBooleanExtra(Phone.NETWORK_UNAVAILABLE_KEY, false); - if (DBG) Log.d(TAG, "Received " + intent.getAction() + - " broadcast - state = " + state - + ", unavailable = " + unavailable - + ", reason = " + (reason == null ? "(unspecified)" : reason)); + + String apnTypeList = + intent.getStringExtra(Phone.DATA_APN_TYPES_KEY); + + boolean unavailable = intent.getBooleanExtra( + Phone.NETWORK_UNAVAILABLE_KEY, false); + if (DBG) Log.d(TAG, mApnType + " Received " + + intent.getAction() + " broadcast - state = " + + state + ", unavailable = " + unavailable + + ", reason = " + + (reason == null ? "(unspecified)" : reason)); + + if ((!isApnTypeIncluded(apnTypeList)) || mEnabled == false) { + if (DBG) Log.e(TAG, " dropped - mEnabled = "+mEnabled); + return; + } + + mNetworkInfo.setIsAvailable(!unavailable); if (mMobileDataState != state) { mMobileDataState = state; switch (state) { case DISCONNECTED: + if(mTeardownRequested) { + mEnabled = false; + mTeardownRequested = false; + } + setDetailedState(DetailedState.DISCONNECTED, reason, apnName); if (mInterfaceName != null) { NetworkUtils.resetConnections(mInterfaceName); @@ -136,12 +183,12 @@ public class MobileDataStateTracker extends NetworkStateTracker { if (mInterfaceName == null) { Log.d(TAG, "CONNECTED event did not supply interface name."); } - setupDnsProperties(); setDetailedState(DetailedState.CONNECTED, reason, apnName); break; } } } else if (intent.getAction().equals(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED)) { + mEnabled = false; String reason = intent.getStringExtra(Phone.FAILURE_REASON_KEY); String apnName = intent.getStringExtra(Phone.DATA_APN_KEY); if (DBG) Log.d(TAG, "Received " + intent.getAction() + " broadcast" + @@ -154,40 +201,6 @@ public class MobileDataStateTracker extends NetworkStateTracker { } } - /** - * Make sure that route(s) exist to the carrier DNS server(s). - */ - public void addPrivateRoutes() { - if (mInterfaceName != null) { - for (String addrString : mDnsServers) { - int addr = NetworkUtils.lookupHost(addrString); - if (addr != -1) { - NetworkUtils.addHostRoute(mInterfaceName, addr); - } - } - } - } - - public void removePrivateRoutes() { - if(mInterfaceName != null) { - NetworkUtils.removeHostRoutes(mInterfaceName); - } - } - - public void removeDefaultRoute() { - if(mInterfaceName != null) { - mDefaultGatewayAddr = NetworkUtils.getDefaultRoute(mInterfaceName); - NetworkUtils.removeDefaultRoute(mInterfaceName); - } - } - - public void restoreDefaultRoute() { - // 0 is not a valid address for a gateway - if (mInterfaceName != null && mDefaultGatewayAddr != 0) { - NetworkUtils.setDefaultRoute(mInterfaceName, mDefaultGatewayAddr); - } - } - private void getPhoneService(boolean forceRefresh) { if ((mPhoneService == null) || forceRefresh) { mPhoneService = ITelephony.Stub.asInterface(ServiceManager.getService("phone")); @@ -219,15 +232,6 @@ public class MobileDataStateTracker extends NetworkStateTracker { } /** - * Return the IP addresses of the DNS servers available for the mobile data - * network interface. - * @return a list of DNS addresses, with no holes. - */ - public String[] getNameServers() { - return getNameServerList(sDnsPropNames); - } - - /** * {@inheritDoc} * The mobile data network subtype indicates what generation network technology is in effect, * e.g., GPRS, EDGE, UMTS, etc. @@ -273,54 +277,19 @@ public class MobileDataStateTracker extends NetworkStateTracker { */ @Override public boolean teardown() { - getPhoneService(false); - /* - * If the phone process has crashed in the past, we'll get a - * RemoteException and need to re-reference the service. - */ - for (int retry = 0; retry < 2; retry++) { - if (mPhoneService == null) { - Log.w(TAG, - "Ignoring mobile data teardown request because could not acquire PhoneService"); - break; - } - - try { - return mPhoneService.disableDataConnectivity(); - } catch (RemoteException e) { - if (retry == 0) getPhoneService(true); - } - } - - Log.w(TAG, "Failed to tear down mobile data connectivity"); - return false; + mTeardownRequested = true; + return (setEnableApn(mApnType, false) != Phone.APN_REQUEST_FAILED); } /** * Re-enable mobile data connectivity after a {@link #teardown()}. */ public boolean reconnect() { - getPhoneService(false); - /* - * If the phone process has crashed in the past, we'll get a - * RemoteException and need to re-reference the service. - */ - for (int retry = 0; retry < 2; retry++) { - if (mPhoneService == null) { - Log.w(TAG, - "Ignoring mobile data connect request because could not acquire PhoneService"); - break; - } - - try { - return mPhoneService.enableDataConnectivity(); - } catch (RemoteException e) { - if (retry == 0) getPhoneService(true); - } - } - - Log.w(TAG, "Failed to set up mobile data connectivity"); - return false; + mEnabled = true; + mTeardownRequested = false; + mEnabled = (setEnableApn(mApnType, true) != + Phone.APN_REQUEST_FAILED); + return mEnabled; } /** @@ -374,14 +343,7 @@ public class MobileDataStateTracker extends NetworkStateTracker { * </ul> */ public int startUsingNetworkFeature(String feature, int callingPid, int callingUid) { - if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_MMS)) { - mLastCallingPid = callingPid; - return setEnableApn(Phone.APN_TYPE_MMS, true); - } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_SUPL)) { - return setEnableApn(Phone.APN_TYPE_SUPL, true); - } else { - return -1; - } + return -1; } /** @@ -397,13 +359,7 @@ public class MobileDataStateTracker extends NetworkStateTracker { * the value {@code -1} always indicates failure. */ public int stopUsingNetworkFeature(String feature, int callingPid, int callingUid) { - if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_MMS)) { - return setEnableApn(Phone.APN_TYPE_MMS, false); - } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_SUPL)) { - return setEnableApn(Phone.APN_TYPE_SUPL, false); - } else { - return -1; - } + return -1; } /** @@ -433,43 +389,6 @@ public class MobileDataStateTracker extends NetworkStateTracker { return sb.toString(); } - private void setupDnsProperties() { - mDnsServers.clear(); - // Set up per-process DNS server list on behalf of the MMS process - int i = 1; - if (mInterfaceName != null) { - for (String propName : sDnsPropNames) { - if (propName.indexOf(mInterfaceName) != -1) { - String propVal = SystemProperties.get(propName); - if (propVal != null && propVal.length() != 0 && !propVal.equals("0.0.0.0")) { - mDnsServers.add(propVal); - if (mLastCallingPid != -1) { - SystemProperties.set("net.dns" + i + "." + mLastCallingPid, propVal); - } - ++i; - } - } - } - } - if (i == 1) { - Log.d(TAG, "DNS server addresses are not known."); - } else if (mLastCallingPid != -1) { - /* - * Bump the property that tells the name resolver library - * to reread the DNS server list from the properties. - */ - String propVal = SystemProperties.get("net.dnschange"); - if (propVal.length() != 0) { - try { - int n = Integer.parseInt(propVal); - SystemProperties.set("net.dnschange", "" + (n+1)); - } catch (NumberFormatException e) { - } - } - } - mLastCallingPid = -1; - } - /** * Internal method supporting the ENABLE_MMS feature. * @param apnType the type of APN to be enabled or disabled (e.g., mms) diff --git a/core/java/android/net/NetworkStateTracker.java b/core/java/android/net/NetworkStateTracker.java index 37087ac..418f511 100644 --- a/core/java/android/net/NetworkStateTracker.java +++ b/core/java/android/net/NetworkStateTracker.java @@ -27,6 +27,7 @@ import android.text.TextUtils; import android.util.Config; import android.util.Log; + /** * Each subclass of this class keeps track of the state of connectivity * of a network interface. All state information for a network should @@ -40,11 +41,16 @@ public abstract class NetworkStateTracker extends Handler { protected NetworkInfo mNetworkInfo; protected Context mContext; protected Handler mTarget; + protected String mInterfaceName; + protected String[] mDnsPropNames; + private boolean mPrivateDnsRouteSet; + protected int mDefaultGatewayAddr; + private boolean mDefaultRouteSet; private boolean mTeardownRequested; - private static boolean DBG = Config.LOGV; + private static boolean DBG = Config.LOGV; private static final String TAG = "NetworkStateTracker"; - + public static final int EVENT_STATE_CHANGED = 1; public static final int EVENT_SCAN_RESULTS_AVAILABLE = 2; /** @@ -56,6 +62,7 @@ public abstract class NetworkStateTracker extends Handler { public static final int EVENT_CONFIGURATION_CHANGED = 4; public static final int EVENT_ROAMING_CHANGED = 5; public static final int EVENT_NETWORK_SUBTYPE_CHANGED = 6; + public static final int EVENT_RESTORE_DEFAULT_NETWORK = 7; public NetworkStateTracker(Context context, Handler target, @@ -67,6 +74,7 @@ public abstract class NetworkStateTracker extends Handler { mContext = context; mTarget = target; mTeardownRequested = false; + this.mNetworkInfo = new NetworkInfo(networkType, subType, typeName, subtypeName); } @@ -75,19 +83,21 @@ public abstract class NetworkStateTracker extends Handler { } /** - * Return the list of DNS servers associated with this network. - * @return a list of the IP addresses of the DNS servers available - * for the network. - */ - public abstract String[] getNameServers(); - - /** * Return the system properties name associated with the tcp buffer sizes * for this network. */ public abstract String getTcpBufferSizesPropName(); /** + * Return the IP addresses of the DNS servers available for the mobile data + * network interface. + * @return a list of DNS addresses, with no holes. + */ + public String[] getNameServers() { + return getNameServerList(mDnsPropNames); + } + + /** * Return the IP addresses of the DNS servers available for this * network interface. * @param propertyNames the names of the system properties whose values @@ -112,6 +122,50 @@ public abstract class NetworkStateTracker extends Handler { return dnsAddresses; } + public void addPrivateDnsRoutes() { + if (DBG) Log.d(TAG, "addPrivateDnsRoutes for " + this + + "(" + mInterfaceName + ")"); + if (mInterfaceName != null && !mPrivateDnsRouteSet) { + for (String addrString : getNameServers()) { + int addr = NetworkUtils.lookupHost(addrString); + if (addr != -1) { + NetworkUtils.addHostRoute(mInterfaceName, addr); + } + } + mPrivateDnsRouteSet = true; + } + } + + public void removePrivateDnsRoutes() { + if (DBG) Log.d(TAG, "removePrivateDnsRoutes for " + this + + "(" + mInterfaceName + ")"); + // TODO - we should do this explicitly but the NetUtils api doesnt + // support this yet - must remove all. No worse than before + if (mInterfaceName != null && mPrivateDnsRouteSet) { + NetworkUtils.removeHostRoutes(mInterfaceName); + mPrivateDnsRouteSet = false; + } + } + + public void addDefaultRoute() { + if (DBG) Log.d(TAG, "addDefaultRoute for " + this + "(" + + mInterfaceName + "), GatewayAddr=" + mDefaultGatewayAddr); + if ((mInterfaceName != null) && (mDefaultGatewayAddr != 0) && + mDefaultRouteSet == false) { + NetworkUtils.setDefaultRoute(mInterfaceName, mDefaultGatewayAddr); + mDefaultRouteSet = true; + } + } + + public void removeDefaultRoute() { + if (DBG) Log.d(TAG, "removeDefaultRoute for " + this + "(" + + mInterfaceName + ")"); + if (mInterfaceName != null && mDefaultRouteSet == true) { + NetworkUtils.removeDefaultRoute(mInterfaceName); + mDefaultRouteSet = false; + } + } + /** * Reads the network specific TCP buffer sizes from SystemProperties * net.tcp.buffersize.[default|wifi|umts|edge|gprs] and set them for system @@ -209,6 +263,7 @@ public abstract class NetworkStateTracker extends Handler { * @param extraInfo optional {@code String} providing extra information about the state change */ public void setDetailedState(NetworkInfo.DetailedState state, String reason, String extraInfo) { + if (DBG) Log.d(TAG, "setDetailed state, old ="+mNetworkInfo.getDetailedState()+" and new state="+state); if (state != mNetworkInfo.getDetailedState()) { boolean wasConnecting = (mNetworkInfo.getState() == NetworkInfo.State.CONNECTING); String lastReason = mNetworkInfo.getReason(); diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index f655b27..92c776c 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -38,4 +38,23 @@ <!-- Flag indicating whether Last Name comes before First Name. This becomes true in Japan, for example.--> <bool name="config_lastname_comes_before_firstname">false</bool> + + <!-- 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 hardward --> + <!-- An Array of "[type-name],[associated radio-name],[priority] --> + <string-array translatable="false" name="networkAttributes"> + <item>"default,wifi,0"</item> + <item>"default,mobile,0"</item> + <item>"mms,mobile,1"</item> + <item>"supl,mobile,1"</item> + <item>"dun,mobile,1"</item> + <item>"hipri,mobile,2"</item> + </string-array> + + <!-- This string array should be overridden by the device to present a list of radio attributes. This is used by the connectivity manager to decide which networks can coexist based on the hardware --> + <!-- An Array of "[radio-name],[priority] --> + <!-- [# simultaneous connection types]" --> + <string-array translatable="false" name="radioAttributes"> + <item>"wifi,1,1"</item> + <item>"mobile,0,1"</item> + </string-array> </resources> diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index 62b839d..1c60058 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -30,48 +30,130 @@ import android.net.NetworkStateTracker; import android.net.wifi.WifiStateTracker; import android.os.Binder; import android.os.Handler; +import android.os.IBinder; import android.os.Looper; import android.os.Message; +import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemProperties; import android.provider.Settings; +import android.text.TextUtils; import android.util.EventLog; import android.util.Log; +import com.android.internal.telephony.Phone; + import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; /** * @hide */ public class ConnectivityService extends IConnectivityManager.Stub { - private static final boolean DBG = false; + private static final boolean DBG = true; private static final String TAG = "ConnectivityService"; // Event log tags (must be in sync with event-log-tags) private static final int EVENTLOG_CONNECTIVITY_STATE_CHANGED = 50020; + // how long to wait before switching back to a radio's default network + private static final int RESTORE_DEFAULT_NETWORK_DELAY = 1 * 60 * 1000; + // system property that can override the above value + private static final String NETWORK_RESTORE_DELAY_PROP_NAME = + "android.telephony.apn-restore"; + /** * Sometimes we want to refer to the individual network state * trackers separately, and sometimes we just want to treat them * abstractly. */ private NetworkStateTracker mNetTrackers[]; - private WifiStateTracker mWifiStateTracker; - private MobileDataStateTracker mMobileDataStateTracker; + + /** + * A per Net list of the PID's that requested access to the net + * used both as a refcount and for per-PID DNS selection + */ + private List mNetRequestersPids[]; + private WifiWatchdogService mWifiWatchdogService; + // priority order of the nettrackers + // (excluding dynamically set mNetworkPreference) + // TODO - move mNetworkTypePreference into this + private int[] mPriorityList; + private Context mContext; private int mNetworkPreference; - private NetworkStateTracker mActiveNetwork; + private int mActiveDefaultNetwork = -1; private int mNumDnsEntries; - private static int sDnsChangeCounter; private boolean mTestMode; private static ConnectivityService sServiceInstance; + private Handler mHandler; + + // list of DeathRecipients used to make sure features are turned off when + // a process dies + private List mFeatureUsers; + + private class NetworkAttributes { + /** + * Class for holding settings read from resources. + */ + public String mName; + public int mType; + public int mRadio; + public int mPriority; + public NetworkAttributes(String init) { + String fragments[] = init.split(","); + mName = fragments[0].toLowerCase(); + if (fragments[1].toLowerCase().equals("wifi")) { + mRadio = ConnectivityManager.TYPE_WIFI; + } else { + mRadio = ConnectivityManager.TYPE_MOBILE; + } + if (mName.equals("default")) { + mType = mRadio; + } else if (mName.equals("mms")) { + mType = ConnectivityManager.TYPE_MOBILE_MMS; + } else if (mName.equals("supl")) { + mType = ConnectivityManager.TYPE_MOBILE_SUPL; + } else if (mName.equals("dun")) { + mType = ConnectivityManager.TYPE_MOBILE_DUN; + } else if (mName.equals("hipri")) { + mType = ConnectivityManager.TYPE_MOBILE_HIPRI; + } + mPriority = Integer.parseInt(fragments[2]); + } + public boolean isDefault() { + return (mType == mRadio); + } + } + NetworkAttributes[] mNetAttributes; + + private class RadioAttributes { + public String mName; + public int mPriority; + public int mSimultaneity; + public int mType; + public RadioAttributes(String init) { + String fragments[] = init.split(","); + mName = fragments[0].toLowerCase(); + mPriority = Integer.parseInt(fragments[1]); + mSimultaneity = Integer.parseInt(fragments[2]); + if (mName.equals("wifi")) { + mType = ConnectivityManager.TYPE_WIFI; + } else { + mType = ConnectivityManager.TYPE_MOBILE; + } + } + } + RadioAttributes[] mRadioAttributes; + private static class ConnectivityThread extends Thread { private Context mContext; @@ -118,11 +200,54 @@ public class ConnectivityService extends IConnectivityManager.Stub { private ConnectivityService(Context context) { if (DBG) Log.v(TAG, "ConnectivityService starting up"); mContext = context; - mNetTrackers = new NetworkStateTracker[2]; - Handler handler = new MyHandler(); + mNetTrackers = new NetworkStateTracker[ + ConnectivityManager.MAX_NETWORK_TYPE+1]; + mHandler = new MyHandler(); mNetworkPreference = getPersistedNetworkPreference(); + // Load device network attributes from resources + mNetAttributes = new NetworkAttributes[ + ConnectivityManager.MAX_NETWORK_TYPE+1]; + mRadioAttributes = new RadioAttributes[ + ConnectivityManager.MAX_RADIO_TYPE+1]; + String[] naStrings = context.getResources().getStringArray( + com.android.internal.R.array.networkAttributes); + // TODO - what if the setting has gaps/unknown types? + for (String a : naStrings) { + NetworkAttributes n = new NetworkAttributes(a); + mNetAttributes[n.mType] = n; + } + String[] raStrings = context.getResources().getStringArray( + com.android.internal.R.array.radioAttributes); + for (String a : raStrings) { + RadioAttributes r = new RadioAttributes(a); + mRadioAttributes[r.mType] = r; + } + + // high priority first + mPriorityList = new int[naStrings.length]; + { + int priority = 0; //lowest + int nextPos = naStrings.length-1; + while (nextPos>-1) { + for (int i = 0; i < mNetAttributes.length; i++) { + if(mNetAttributes[i].mPriority == priority) { + mPriorityList[nextPos--] = i; + } + } + priority++; + } + } + + mNetRequestersPids = + new ArrayList[ConnectivityManager.MAX_NETWORK_TYPE+1]; + for (int i=0; i<=ConnectivityManager.MAX_NETWORK_TYPE; i++) { + mNetRequestersPids[i] = new ArrayList(); + } + + mFeatureUsers = new ArrayList(); + /* * Create the network state trackers for Wi-Fi and mobile * data. Maybe this could be done with a factory class, @@ -131,15 +256,36 @@ public class ConnectivityService extends IConnectivityManager.Stub { * to change very often. */ if (DBG) Log.v(TAG, "Starting Wifi Service."); - mWifiStateTracker = new WifiStateTracker(context, handler); - WifiService wifiService = new WifiService(context, mWifiStateTracker); + WifiStateTracker wst = new WifiStateTracker(context, mHandler); + WifiService wifiService = new WifiService(context, wst); ServiceManager.addService(Context.WIFI_SERVICE, wifiService); - mNetTrackers[ConnectivityManager.TYPE_WIFI] = mWifiStateTracker; + mNetTrackers[ConnectivityManager.TYPE_WIFI] = wst; + + mNetTrackers[ConnectivityManager.TYPE_MOBILE] = + new MobileDataStateTracker(context, mHandler, + ConnectivityManager.TYPE_MOBILE, Phone.APN_TYPE_DEFAULT, + "MOBILE"); + + mNetTrackers[ConnectivityManager.TYPE_MOBILE_MMS] = + new MobileDataStateTracker(context, mHandler, + ConnectivityManager.TYPE_MOBILE_MMS, Phone.APN_TYPE_MMS, + "MOBILE_MMS"); - mMobileDataStateTracker = new MobileDataStateTracker(context, handler); - mNetTrackers[ConnectivityManager.TYPE_MOBILE] = mMobileDataStateTracker; + mNetTrackers[ConnectivityManager.TYPE_MOBILE_SUPL] = + new MobileDataStateTracker(context, mHandler, + ConnectivityManager.TYPE_MOBILE_SUPL, Phone.APN_TYPE_SUPL, + "MOBILE_SUPL"); + + mNetTrackers[ConnectivityManager.TYPE_MOBILE_DUN] = + new MobileDataStateTracker(context, mHandler, + ConnectivityManager.TYPE_MOBILE_DUN, Phone.APN_TYPE_DUN, + "MOBILE_DUN"); + + mNetTrackers[ConnectivityManager.TYPE_MOBILE_HIPRI] = + new MobileDataStateTracker(context, mHandler, + ConnectivityManager.TYPE_MOBILE_HIPRI, Phone.APN_TYPE_HIPRI, + "MOBILE_HIPRI"); - mActiveNetwork = null; mNumDnsEntries = 0; mTestMode = SystemProperties.get("cm.test.mode").equals("true") @@ -149,8 +295,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { t.startMonitoring(); // Constructing this starts it too - mWifiWatchdogService = new WifiWatchdogService(context, - mWifiStateTracker); + mWifiWatchdogService = new WifiWatchdogService(context, wst); } /** @@ -159,7 +304,8 @@ public class ConnectivityService extends IConnectivityManager.Stub { */ public synchronized void setNetworkPreference(int preference) { enforceChangePermission(); - if (ConnectivityManager.isNetworkTypeValid(preference)) { + if (ConnectivityManager.isNetworkTypeValid(preference) && + mNetAttributes[preference].isDefault()) { if (mNetworkPreference != preference) { persistNetworkPreference(preference); mNetworkPreference = preference; @@ -199,23 +345,16 @@ public class ConnectivityService extends IConnectivityManager.Stub { * (see {@link #handleDisconnect(NetworkInfo)}). */ private void enforcePreference() { - if (mActiveNetwork == null) + if (mNetTrackers[mNetworkPreference].getNetworkInfo().isConnected()) return; - for (NetworkStateTracker t : mNetTrackers) { - if (t == mActiveNetwork) { - int netType = t.getNetworkInfo().getType(); - int otherNetType = ((netType == ConnectivityManager.TYPE_WIFI) ? - ConnectivityManager.TYPE_MOBILE : - ConnectivityManager.TYPE_WIFI); - - if (t.getNetworkInfo().getType() != mNetworkPreference) { - NetworkStateTracker otherTracker = - mNetTrackers[otherNetType]; - if (otherTracker.isAvailable()) { - teardown(t); - } - } + if (!mNetTrackers[mNetworkPreference].isAvailable()) + return; + + for (int t=0; t <= ConnectivityManager.MAX_RADIO_TYPE; t++) { + if (t != mNetworkPreference && + mNetTrackers[t].getNetworkInfo().isConnected()) { + teardown(mNetTrackers[t]); } } } @@ -238,9 +377,16 @@ public class ConnectivityService extends IConnectivityManager.Stub { */ public NetworkInfo getActiveNetworkInfo() { enforceAccessPermission(); - for (NetworkStateTracker t : mNetTrackers) { + for (int type=0; type <= ConnectivityManager.MAX_NETWORK_TYPE; type++) { + if (!mNetAttributes[type].isDefault()) { + continue; + } + NetworkStateTracker t = mNetTrackers[type]; NetworkInfo info = t.getNetworkInfo(); if (info.isConnected()) { + if (DBG && type != mActiveDefaultNetwork) Log.e(TAG, + "connected default network is not " + + "mActiveDefaultNetwork!"); return info; } } @@ -285,30 +431,189 @@ public class ConnectivityService extends IConnectivityManager.Stub { return tracker != null && tracker.setRadio(turnOn); } - public int startUsingNetworkFeature(int networkType, String feature) { + private class FeatureUser implements IBinder.DeathRecipient { + int mNetworkType; + String mFeature; + IBinder mBinder; + int mPid; + int mUid; + + FeatureUser(int type, String feature, IBinder binder) { + super(); + mNetworkType = type; + mFeature = feature; + mBinder = binder; + mPid = getCallingPid(); + mUid = getCallingUid(); + try { + mBinder.linkToDeath(this, 0); + } catch (RemoteException e) { + binderDied(); + } + } + + void unlinkDeathRecipient() { + mBinder.unlinkToDeath(this, 0); + } + + public void binderDied() { + Log.d(TAG, "ConnectivityService FeatureUser binderDied(" + + mNetworkType + ", " + mFeature + ", " + mBinder); + stopUsingNetworkFeature(mNetworkType, mFeature, mPid, mUid); + } + + } + + public int startUsingNetworkFeature(int networkType, String feature, + IBinder binder) { + if (DBG) { + Log.d(TAG, "startUsingNetworkFeature for net " + networkType + + ": " + feature); + } enforceChangePermission(); if (!ConnectivityManager.isNetworkTypeValid(networkType)) { - return -1; + return Phone.APN_REQUEST_FAILED; } - NetworkStateTracker tracker = mNetTrackers[networkType]; - if (tracker != null) { - return tracker.startUsingNetworkFeature(feature, getCallingPid(), - getCallingUid()); + + synchronized (mFeatureUsers) { + mFeatureUsers.add(new FeatureUser(networkType, feature, binder)); + } + + // TODO - move this into the MobileDataStateTracker + int usedNetworkType = networkType; + if(networkType == ConnectivityManager.TYPE_MOBILE) { + if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_MMS)) { + usedNetworkType = ConnectivityManager.TYPE_MOBILE_MMS; + } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_SUPL)) { + usedNetworkType = ConnectivityManager.TYPE_MOBILE_SUPL; + } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_DUN)) { + usedNetworkType = ConnectivityManager.TYPE_MOBILE_DUN; + } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_HIPRI)) { + usedNetworkType = ConnectivityManager.TYPE_MOBILE_HIPRI; + } + } + NetworkStateTracker network = mNetTrackers[usedNetworkType]; + if (network != null) { + if (usedNetworkType != networkType) { + Integer currentPid = new Integer(getCallingPid()); + + NetworkStateTracker radio = mNetTrackers[networkType]; + NetworkInfo ni = network.getNetworkInfo(); + + if (ni.isAvailable() == false) { + if (DBG) Log.d(TAG, "special network not available"); + return Phone.APN_TYPE_NOT_AVAILABLE; + } + + if (!mNetRequestersPids[usedNetworkType].contains(currentPid)) { + // this gets used for per-pid dns when connected + mNetRequestersPids[usedNetworkType].add(currentPid); + } + + if (ni.isConnectedOrConnecting() == true) { + if (ni.isConnected() == true) { + // add the pid-specific dns + handleDnsConfigurationChange(); + if (DBG) Log.d(TAG, "special network already active"); + return Phone.APN_ALREADY_ACTIVE; + } + if (DBG) Log.d(TAG, "special network already connecting"); + return Phone.APN_REQUEST_STARTED; + } + + // check if the radio in play can make another contact + // assume if cannot for now + + // since we have to drop the default on this radio, setup + // an automatic event to switch back + if(mHandler.hasMessages(NetworkStateTracker. + EVENT_RESTORE_DEFAULT_NETWORK, radio) || + radio.getNetworkInfo().isConnectedOrConnecting()) { + mHandler.removeMessages(NetworkStateTracker. + EVENT_RESTORE_DEFAULT_NETWORK, + radio); + mHandler.sendMessageDelayed(mHandler.obtainMessage( + NetworkStateTracker.EVENT_RESTORE_DEFAULT_NETWORK, + radio), getRestoreDefaultNetworkDelay()); + } + if (DBG) Log.d(TAG, "reconnecting to special network"); + network.reconnect(); + return Phone.APN_REQUEST_STARTED; + } else { + return network.startUsingNetworkFeature(feature, + getCallingPid(), getCallingUid()); + } } - return -1; + return Phone.APN_TYPE_NOT_AVAILABLE; } public int stopUsingNetworkFeature(int networkType, String feature) { + return stopUsingNetworkFeature(networkType, feature, getCallingPid(), + getCallingUid()); + } + + private int stopUsingNetworkFeature(int networkType, String feature, + int pid, int uid) { + if (DBG) { + Log.d(TAG, "stopUsingNetworkFeature for net " + networkType + + ": " + feature); + } enforceChangePermission(); if (!ConnectivityManager.isNetworkTypeValid(networkType)) { return -1; } - NetworkStateTracker tracker = mNetTrackers[networkType]; - if (tracker != null) { - return tracker.stopUsingNetworkFeature(feature, getCallingPid(), - getCallingUid()); + + synchronized (mFeatureUsers) { + for (int i=0; i < mFeatureUsers.size(); i++) { + FeatureUser u = (FeatureUser)mFeatureUsers.get(i); + if (uid == u.mUid && pid == u.mPid && + networkType == u.mNetworkType && + TextUtils.equals(feature, u.mFeature)) { + u.unlinkDeathRecipient(); + mFeatureUsers.remove(i); + break; + } + } + } + + // TODO - move to MobileDataStateTracker + int usedNetworkType = networkType; + if (networkType == ConnectivityManager.TYPE_MOBILE) { + if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_MMS)) { + usedNetworkType = ConnectivityManager.TYPE_MOBILE_MMS; + } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_SUPL)) { + usedNetworkType = ConnectivityManager.TYPE_MOBILE_SUPL; + } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_DUN)) { + usedNetworkType = ConnectivityManager.TYPE_MOBILE_DUN; + } else if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_HIPRI)) { + usedNetworkType = ConnectivityManager.TYPE_MOBILE_HIPRI; + } + } + NetworkStateTracker tracker = mNetTrackers[usedNetworkType]; + if(usedNetworkType != networkType) { + Integer currentPid = new Integer(pid); + if (mNetRequestersPids[usedNetworkType].remove(currentPid)) { + reassessPidDns(pid, true); + } + if (mNetRequestersPids[usedNetworkType].size() != 0) { + if (DBG) Log.d(TAG, "not tearing down special network - " + + "others still using it"); + return 1; + } + + tracker.teardown(); + NetworkStateTracker radio = mNetTrackers[networkType]; + // Check if we want to revert to the default + if (mHandler.hasMessages(NetworkStateTracker. + EVENT_RESTORE_DEFAULT_NETWORK, radio)) { + mHandler.removeMessages(NetworkStateTracker. + EVENT_RESTORE_DEFAULT_NETWORK, radio); + radio.reconnect(); + } + return 1; + } else { + return tracker.stopUsingNetworkFeature(feature, pid, uid); } - return -1; } /** @@ -337,7 +642,8 @@ public class ConnectivityService extends IConnectivityManager.Stub { if (getNumConnectedNetworks() > 1) { return tracker.requestRouteToHost(hostAddress); } else { - return tracker.getNetworkInfo().getType() == networkType; + return (mNetAttributes[networkType].isDefault() && + tracker.getNetworkInfo().isConnected()); } } @@ -402,45 +708,26 @@ public class ConnectivityService extends IConnectivityManager.Stub { private void handleDisconnect(NetworkInfo info) { if (DBG) Log.v(TAG, "Handle DISCONNECT for " + info.getTypeName()); + int prevNetType = info.getType(); - mNetTrackers[info.getType()].setTeardownRequested(false); + mNetTrackers[prevNetType].setTeardownRequested(false); /* * If the disconnected network is not the active one, then don't report * this as a loss of connectivity. What probably happened is that we're * getting the disconnect for a network that we explicitly disabled * in accordance with network preference policies. */ - if (mActiveNetwork == null || - info.getType() != mActiveNetwork.getNetworkInfo().getType()) - return; - - NetworkStateTracker newNet; - if (info.getType() == ConnectivityManager.TYPE_MOBILE) { - newNet = mWifiStateTracker; - } else /* info().getType() == TYPE_WIFI */ { - newNet = mMobileDataStateTracker; - } - - /** - * See if the other network is available to fail over to. - * If is not available, we enable it anyway, so that it - * will be able to connect when it does become available, - * but we report a total loss of connectivity rather than - * report that we are attempting to fail over. - */ - NetworkInfo switchTo = null; - if (newNet.isAvailable()) { - mActiveNetwork = newNet; - switchTo = newNet.getNetworkInfo(); - switchTo.setFailover(true); - if (!switchTo.isConnectedOrConnecting()) { - newNet.reconnect(); + if (!mNetAttributes[prevNetType].isDefault()) { + List pids = mNetRequestersPids[prevNetType]; + for (int i = 0; i<pids.size(); i++) { + Integer pid = (Integer)pids.get(i); + // will remove them because the net's no longer connected + // need to do this now as only now do we know the pids and + // can properly null things that are no longer referenced. + reassessPidDns(pid.intValue(), false); } - } else { - newNet.reconnect(); } - boolean otherNetworkConnected = false; Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION); intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info); if (info.isFailover()) { @@ -454,33 +741,92 @@ public class ConnectivityService extends IConnectivityManager.Stub { intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, info.getExtraInfo()); } - if (switchTo != null) { - otherNetworkConnected = switchTo.isConnected(); - if (DBG) { - if (otherNetworkConnected) { - Log.v(TAG, "Switching to already connected " + - switchTo.getTypeName()); + + /* + * If this is a default network, check if other defaults are available + * or active + */ + NetworkStateTracker newNet = null; + if (mNetAttributes[prevNetType].isDefault()) { + if (DBG) Log.d(TAG, "disconnecting a default network"); + if (mActiveDefaultNetwork == prevNetType) { + mActiveDefaultNetwork = -1; + } + + int newType = -1; + int newPriority = -1; + for (int checkType=0; checkType <= + ConnectivityManager.MAX_NETWORK_TYPE; checkType++) { + if (checkType == prevNetType) { + 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 + */ + if (checkType == mNetworkPreference) { + newType = checkType; + break; + } + if (mRadioAttributes[mNetAttributes[checkType].mRadio]. + mPriority > newPriority) { + newType = checkType; + newPriority = mRadioAttributes[mNetAttributes[newType]. + mRadio].mPriority; + } + } + } + + if (newType != -1) { + newNet = mNetTrackers[newType]; + /** + * See if the other network is available to fail over to. + * If is not available, we enable it anyway, so that it + * will be able to connect when it does become available, + * but we report a total loss of connectivity rather than + * report that we are attempting to fail over. + */ + if (newNet.isAvailable()) { + NetworkInfo switchTo = newNet.getNetworkInfo(); + switchTo.setFailover(true); + if (!switchTo.isConnectedOrConnecting()) { + newNet.reconnect(); + } + if (DBG) { + if (switchTo.isConnected()) { + Log.v(TAG, "Switching to already connected " + + switchTo.getTypeName()); + } else { + Log.v(TAG, "Attempting to switch to " + + switchTo.getTypeName()); + } + } + intent.putExtra(ConnectivityManager. + EXTRA_OTHER_NETWORK_INFO, switchTo); } else { - Log.v(TAG, "Attempting to switch to " + - switchTo.getTypeName()); + newNet.reconnect(); } + } else { + intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, + true); } - intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO, - switchTo); - } else { - intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true); } + + // do this before we broadcast the change + handleConnectivityChange(); + if (DBG) Log.v(TAG, "Sending DISCONNECT bcast for " + info.getTypeName() + - (switchTo == null ? "" : " other=" + switchTo.getTypeName())); + (newNet == null || !newNet.isAvailable() ? "" : " other=" + + newNet.getNetworkInfo().getTypeName())); mContext.sendStickyBroadcast(intent); /* * If the failover network is already connected, then immediately send * out a followup broadcast indicating successful failover */ - if (switchTo != null && otherNetworkConnected) - sendConnectedBroadcast(switchTo); + if (newNet != null && newNet.getNetworkInfo().isConnected()) + sendConnectedBroadcast(newNet.getNetworkInfo()); } private void sendConnectedBroadcast(NetworkInfo info) { @@ -506,93 +852,85 @@ public class ConnectivityService extends IConnectivityManager.Stub { */ private void handleConnectionFailure(NetworkInfo info) { mNetTrackers[info.getType()].setTeardownRequested(false); - if (getActiveNetworkInfo() == null) { - String reason = info.getReason(); - String extraInfo = info.getExtraInfo(); - if (DBG) { - String reasonText; - if (reason == null) { - reasonText = "."; - } else { - reasonText = " (" + reason + ")."; - } - Log.v(TAG, "Attempt to connect to " + info.getTypeName() + - " failed" + reasonText); + String reason = info.getReason(); + String extraInfo = info.getExtraInfo(); + + if (DBG) { + String reasonText; + if (reason == null) { + reasonText = "."; + } else { + reasonText = " (" + reason + ")."; } + Log.v(TAG, "Attempt to connect to " + info.getTypeName() + + " failed" + reasonText); + } - Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION); - intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info); + Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION); + intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info); + if (getActiveNetworkInfo() == null) { intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true); - if (reason != null) { - intent.putExtra(ConnectivityManager.EXTRA_REASON, reason); - } - if (extraInfo != null) { - intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, - extraInfo); - } - if (info.isFailover()) { - intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true); - info.setFailover(false); - } - mContext.sendStickyBroadcast(intent); } + if (reason != null) { + intent.putExtra(ConnectivityManager.EXTRA_REASON, reason); + } + if (extraInfo != null) { + intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, extraInfo); + } + if (info.isFailover()) { + intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true); + info.setFailover(false); + } + mContext.sendStickyBroadcast(intent); } private void handleConnect(NetworkInfo info) { - if (DBG) Log.v(TAG, "Handle CONNECT for " + info.getTypeName()); + if (DBG) Log.d(TAG, "Handle CONNECT for " + info.getTypeName()); + + int type = info.getType(); // snapshot isFailover, because sendConnectedBroadcast() resets it boolean isFailover = info.isFailover(); - NetworkStateTracker thisNet = mNetTrackers[info.getType()]; - NetworkStateTracker deadnet = null; - NetworkStateTracker otherNet; - if (info.getType() == ConnectivityManager.TYPE_MOBILE) { - otherNet = mWifiStateTracker; - } else /* info().getType() == TYPE_WIFI */ { - otherNet = mMobileDataStateTracker; - } - /* - * Check policy to see whether we are connected to a non-preferred - * network that now needs to be torn down. - */ - NetworkInfo wifiInfo = mWifiStateTracker.getNetworkInfo(); - NetworkInfo mobileInfo = mMobileDataStateTracker.getNetworkInfo(); - if (wifiInfo.isConnected() && mobileInfo.isConnected()) { - if (mNetworkPreference == ConnectivityManager.TYPE_WIFI) - deadnet = mMobileDataStateTracker; - else - deadnet = mWifiStateTracker; - } - - boolean toredown = false; - thisNet.setTeardownRequested(false); - if (!mTestMode && deadnet != null) { - if (DBG) Log.v(TAG, "Policy requires " + - deadnet.getNetworkInfo().getTypeName() + " teardown"); - toredown = teardown(deadnet); - if (DBG && !toredown) { - Log.d(TAG, "Network declined teardown request"); - } - } - - /* - * Note that if toredown is true, deadnet cannot be null, so there is - * no danger of a null pointer exception here.. - */ - if (!toredown || deadnet.getNetworkInfo().getType() != info.getType()) { - mActiveNetwork = thisNet; - if (DBG) Log.v(TAG, "Sending CONNECT bcast for " + - info.getTypeName()); - thisNet.updateNetworkSettings(); - sendConnectedBroadcast(info); - if (isFailover) { - otherNet.releaseWakeLock(); + NetworkStateTracker thisNet = mNetTrackers[type]; + + // if this is a default net and other default is running + // kill the one not preferred + if (mNetAttributes[type].isDefault()) { + if (DBG) Log.d(TAG, "connecting to a default network"); + if (mActiveDefaultNetwork != -1 && mActiveDefaultNetwork != type) { + if ((type != mNetworkPreference && + mNetAttributes[mActiveDefaultNetwork].mPriority > + mNetAttributes[type].mPriority) || + mNetworkPreference == mActiveDefaultNetwork) { + // don't accept this one + if (DBG) Log.v(TAG, "Not broadcasting CONNECT_ACTION " + + "to torn down network " + info.getTypeName()); + teardown(thisNet); + return; + } else { + // tear down the other + NetworkStateTracker otherNet = + mNetTrackers[mActiveDefaultNetwork]; + if (DBG) Log.v(TAG, "Policy requires " + + otherNet.getNetworkInfo().getTypeName() + + " teardown"); + if (!teardown(otherNet)) { + Log.e(TAG, "Network declined teardown request"); + return; + } + if (isFailover) { + otherNet.releaseWakeLock(); + } + } } - } else { - if (DBG) Log.v(TAG, "Not broadcasting CONNECT_ACTION to torn " + - "down network " + info.getTypeName()); + mActiveDefaultNetwork = type; } + thisNet.setTeardownRequested(false); + if (DBG) Log.d(TAG, "Sending CONNECT bcast for " + info.getTypeName()); + thisNet.updateNetworkSettings(); + handleConnectivityChange(); + sendConnectedBroadcast(info); } private void handleScanResultsAvailable(NetworkInfo info) { @@ -626,60 +964,148 @@ public class ConnectivityService extends IConnectivityManager.Stub { * table entries exist. */ private void handleConnectivityChange() { + if (DBG) Log.d(TAG, "handleConnectivityChange"); /* + * If a non-default network is enabled, add the host routes that + * will allow it's DNS servers to be accessed. Only * If both mobile and wifi are enabled, add the host routes that * will allow MMS traffic to pass on the mobile network. But * remove the default route for the mobile network, so that there * will be only one default route, to ensure that all traffic * except MMS will travel via Wi-Fi. */ - int numConnectedNets = handleConfigurationChange(); - if (numConnectedNets > 1) { - mMobileDataStateTracker.addPrivateRoutes(); - mMobileDataStateTracker.removeDefaultRoute(); - } else if (mMobileDataStateTracker.getNetworkInfo().isConnected()) { - mMobileDataStateTracker.removePrivateRoutes(); - mMobileDataStateTracker.restoreDefaultRoute(); + handleDnsConfigurationChange(); + + for (int netType : mPriorityList) { + if (mNetTrackers[netType].getNetworkInfo().isConnected()) { + if (mNetAttributes[netType].isDefault()) { + mNetTrackers[netType].addDefaultRoute(); + } else { + mNetTrackers[netType].addPrivateDnsRoutes(); + } + } else { + if (mNetAttributes[netType].isDefault()) { + mNetTrackers[netType].removeDefaultRoute(); + } else { + mNetTrackers[netType].removePrivateDnsRoutes(); + } + } } } - private int handleConfigurationChange() { + /** + * Adjust the per-process dns entries (net.dns<x>.<pid>) based + * on the highest priority active net which this process requested. + * If there aren't any, clear it out + */ + private void reassessPidDns(int myPid, boolean doBump) + { + if (DBG) Log.d(TAG, "reassessPidDns for pid " + myPid); + for(int i : mPriorityList) { + if (mNetAttributes[i].isDefault()) { + continue; + } + NetworkStateTracker nt = mNetTrackers[i]; + if (nt.getNetworkInfo().isConnected() && + !nt.isTeardownRequested()) { + List pids = mNetRequestersPids[i]; + for (int j=0; j<pids.size(); j++) { + Integer pid = (Integer)pids.get(j); + if (pid.intValue() == myPid) { + String[] dnsList = nt.getNameServers(); + writePidDns(dnsList, myPid); + if (doBump) { + bumpDns(); + } + return; + } + } + } + } + // nothing found - delete + for (int i = 1; ; i++) { + String prop = "net.dns" + i + "." + myPid; + if (SystemProperties.get(prop).length() == 0) { + if (doBump) { + bumpDns(); + } + return; + } + SystemProperties.set(prop, ""); + } + } + + private void writePidDns(String[] dnsList, int pid) { + int j = 1; + for (String dns : dnsList) { + if (dns != null && !TextUtils.equals(dns, "0.0.0.0")) { + SystemProperties.set("net.dns" + j++ + "." + pid, dns); + } + } + } + + private void bumpDns() { /* - * Set DNS properties. Always put Wi-Fi entries at the front of - * the list if it is active. + * Bump the property that tells the name resolver library to reread + * the DNS server list from the properties. */ - int index = 1; - String lastDns = ""; - int numConnectedNets = 0; - int incrValue = ConnectivityManager.TYPE_MOBILE - - ConnectivityManager.TYPE_WIFI; - int stopValue = ConnectivityManager.TYPE_MOBILE + incrValue; + String propVal = SystemProperties.get("net.dnschange"); + int n = 0; + if (propVal.length() != 0) { + try { + n = Integer.parseInt(propVal); + } catch (NumberFormatException e) {} + } + SystemProperties.set("net.dnschange", "" + (n+1)); + } - for (int netType = ConnectivityManager.TYPE_WIFI; netType != stopValue; - netType += incrValue) { + private void handleDnsConfigurationChange() { + if (DBG) Log.d(TAG, "handleDnsConfig Change"); + // add default net's dns entries + for (int x = mPriorityList.length-1; x>= 0; x--) { + int netType = mPriorityList[x]; NetworkStateTracker nt = mNetTrackers[netType]; - if (nt.getNetworkInfo().isConnected() && + if (DBG) Log.d(TAG, " checking " + nt); + if (nt != null && nt.getNetworkInfo().isConnected() && !nt.isTeardownRequested()) { - ++numConnectedNets; + if (DBG) Log.d(TAG, " connected"); String[] dnsList = nt.getNameServers(); - for (int i = 0; i < dnsList.length && dnsList[i] != null; i++) { - // skip duplicate entries - if (!dnsList[i].equals(lastDns)) { - SystemProperties.set("net.dns" + index++, dnsList[i]); - lastDns = dnsList[i]; + if (mNetAttributes[netType].isDefault()) { + int j = 1; + for (String dns : dnsList) { + if (dns != null && !TextUtils.equals(dns, "0.0.0.0")) { + SystemProperties.set("net.dns" + j++, dns); + } + } + for (int k=j ; k<mNumDnsEntries; k++) { + SystemProperties.set("net.dns" + j, ""); + } + mNumDnsEntries = j; + } else { + // set per-pid dns for attached secondary nets + List pids = mNetRequestersPids[netType]; + for (int y=0; y< pids.size(); y++) { + Integer pid = (Integer)pids.get(y); + writePidDns(dnsList, pid.intValue()); } } } } - // Null out any DNS properties that are no longer used - for (int i = index; i <= mNumDnsEntries; i++) { - SystemProperties.set("net.dns" + i, ""); + + bumpDns(); + } + + private int getRestoreDefaultNetworkDelay() { + String restoreDefaultNetworkDelayStr = SystemProperties.get( + NETWORK_RESTORE_DELAY_PROP_NAME); + if(restoreDefaultNetworkDelayStr != null && + restoreDefaultNetworkDelayStr.length() != 0) { + try { + return Integer.valueOf(restoreDefaultNetworkDelayStr); + } catch (NumberFormatException e) { + } } - mNumDnsEntries = index - 1; - // Notify the name resolver library of the change - SystemProperties.set("net.dnschange", - String.valueOf(sDnsChangeCounter++)); - return numConnectedNets; + return RESTORE_DEFAULT_NETWORK_DELAY; } @Override @@ -692,20 +1118,19 @@ public class ConnectivityService extends IConnectivityManager.Stub { Binder.getCallingUid()); return; } - if (mActiveNetwork == null) { - pw.println("No active network"); - } else { - pw.println("Active network: " + - mActiveNetwork.getNetworkInfo().getTypeName()); - } pw.println(); for (NetworkStateTracker nst : mNetTrackers) { + if (nst.getNetworkInfo().isConnected()) { + pw.println("Active network: " + nst.getNetworkInfo(). + getTypeName()); + } pw.println(nst.getNetworkInfo()); pw.println(nst); pw.println(); } } + // must be stateless - things change under us. private class MyHandler extends Handler { @Override public void handleMessage(Message msg) { @@ -713,7 +1138,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { switch (msg.what) { case NetworkStateTracker.EVENT_STATE_CHANGED: info = (NetworkInfo) msg.obj; - if (DBG) Log.v(TAG, "ConnectivityChange for " + + if (DBG) Log.d(TAG, "ConnectivityChange for " + info.getTypeName() + ": " + info.getState() + "/" + info.getDetailedState()); @@ -748,7 +1173,6 @@ public class ConnectivityService extends IConnectivityManager.Stub { } else if (info.getState() == NetworkInfo.State.CONNECTED) { handleConnect(info); } - handleConnectivityChange(); break; case NetworkStateTracker.EVENT_SCAN_RESULTS_AVAILABLE: @@ -761,7 +1185,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { (Notification) msg.obj); case NetworkStateTracker.EVENT_CONFIGURATION_CHANGED: - handleConfigurationChange(); + handleDnsConfigurationChange(); break; case NetworkStateTracker.EVENT_ROAMING_CHANGED: @@ -771,6 +1195,15 @@ public class ConnectivityService extends IConnectivityManager.Stub { case NetworkStateTracker.EVENT_NETWORK_SUBTYPE_CHANGED: // fill me in break; + case NetworkStateTracker.EVENT_RESTORE_DEFAULT_NETWORK: + for (NetworkStateTracker net : mNetTrackers) { + NetworkInfo i = net.getNetworkInfo(); + if (i.isConnected() && + !mNetAttributes[i.getType()].isDefault()) { + teardown(net); + } + } + break; } } } diff --git a/services/java/com/android/server/TelephonyRegistry.java b/services/java/com/android/server/TelephonyRegistry.java index 9f2856c..7379b5d 100644 --- a/services/java/com/android/server/TelephonyRegistry.java +++ b/services/java/com/android/server/TelephonyRegistry.java @@ -88,6 +88,8 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { private String mDataConnectionApn = ""; + private String[] mDataConnectionApnTypes = null; + private String mDataConnectionInterfaceName = ""; private Bundle mCellLocation = new Bundle(); @@ -337,7 +339,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } public void notifyDataConnection(int state, boolean isDataConnectivityPossible, - String reason, String apn, String interfaceName) { + String reason, String apn, String[] apnTypes, String interfaceName) { if (!checkNotifyPermission("notifyDataConnection()" )) { return; } @@ -346,6 +348,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { mDataConnectionPossible = isDataConnectivityPossible; mDataConnectionReason = reason; mDataConnectionApn = apn; + mDataConnectionApnTypes = apnTypes; mDataConnectionInterfaceName = interfaceName; for (int i = mRecords.size() - 1; i >= 0; i--) { Record r = mRecords.get(i); @@ -359,7 +362,7 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } } broadcastDataConnectionStateChanged(state, isDataConnectivityPossible, reason, apn, - interfaceName); + apnTypes, interfaceName); } public void notifyDataConnectionFailed(String reason) { @@ -517,8 +520,9 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { mContext.sendBroadcast(intent, android.Manifest.permission.READ_PHONE_STATE); } - private void broadcastDataConnectionStateChanged(int state, boolean isDataConnectivityPossible, - String reason, String apn, String interfaceName) { + private void broadcastDataConnectionStateChanged(int state, + boolean isDataConnectivityPossible, + String reason, String apn, String[] apnTypes, String interfaceName) { // Note: not reporting to the battery stats service here, because the // status bar takes care of that after taking into account all of the // required info. @@ -531,6 +535,11 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { intent.putExtra(Phone.STATE_CHANGE_REASON_KEY, reason); } intent.putExtra(Phone.DATA_APN_KEY, apn); + String types = apnTypes[0]; + for (int i = 1; i < apnTypes.length; i++) { + types = types+","+apnTypes[i]; + } + intent.putExtra(Phone.DATA_APN_TYPES_KEY, types); intent.putExtra(Phone.DATA_IFACE_NAME_KEY, interfaceName); mContext.sendStickyBroadcast(intent); } diff --git a/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java b/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java index d6151c6..00d7c72 100644 --- a/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java +++ b/telephony/java/com/android/internal/telephony/DefaultPhoneNotifier.java @@ -94,8 +94,11 @@ public class DefaultPhoneNotifier implements PhoneNotifier { public void notifyDataConnection(Phone sender, String reason) { try { - mRegistry.notifyDataConnection(convertDataState(sender.getDataConnectionState()), - sender.isDataConnectivityPossible(), reason, sender.getActiveApn(), + mRegistry.notifyDataConnection( + convertDataState(sender.getDataConnectionState()), + sender.isDataConnectivityPossible(), reason, + sender.getActiveApn(), + sender.getActiveApnTypes(), sender.getInterfaceName(null)); } catch (RemoteException ex) { // system process is dead diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl index 865c6ca..6b42e6b 100644 --- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl @@ -32,7 +32,7 @@ interface ITelephonyRegistry { void notifyCallForwardingChanged(boolean cfi); void notifyDataActivity(int state); void notifyDataConnection(int state, boolean isDataConnectivityPossible, - String reason, String apn, String interfaceName); + String reason, String apn, in String[] apnTypes, String interfaceName); void notifyDataConnectionFailed(String reason); void notifyCellLocation(in Bundle cellLocation); } diff --git a/telephony/java/com/android/internal/telephony/Phone.java b/telephony/java/com/android/internal/telephony/Phone.java index 86ea12b..622b47d 100644 --- a/telephony/java/com/android/internal/telephony/Phone.java +++ b/telephony/java/com/android/internal/telephony/Phone.java @@ -99,8 +99,9 @@ public interface Phone { static final String PHONE_NAME_KEY = "phoneName"; static final String FAILURE_REASON_KEY = "reason"; static final String STATE_CHANGE_REASON_KEY = "reason"; - static final String DATA_APN_TYPE_KEY = "apnType"; + static final String DATA_APN_TYPES_KEY = "apnType"; static final String DATA_APN_KEY = "apn"; + static final String DATA_IFACE_NAME_KEY = "iface"; static final String NETWORK_UNAVAILABLE_KEY = "networkUnvailable"; static final String PHONE_IN_ECM_STATE = "phoneinECMState"; @@ -120,10 +121,16 @@ public interface Phone { static final String APN_TYPE_MMS = "mms"; /** APN type for SUPL assisted GPS */ static final String APN_TYPE_SUPL = "supl"; + /** APN type for DUN traffic */ + static final String APN_TYPE_DUN = "dun"; + /** APN type for HiPri traffic */ + static final String APN_TYPE_HIPRI = "hipri"; // "Features" accessible through the connectivity manager static final String FEATURE_ENABLE_MMS = "enableMMS"; static final String FEATURE_ENABLE_SUPL = "enableSUPL"; + static final String FEATURE_ENABLE_DUN = "enableDUN"; + static final String FEATURE_ENABLE_HIPRI = "enableHIPRI"; /** * Return codes for <code>enableApnType()</code> diff --git a/telephony/java/com/android/internal/telephony/gsm/ApnSetting.java b/telephony/java/com/android/internal/telephony/gsm/ApnSetting.java index 3ca39de..dc6f92d 100644 --- a/telephony/java/com/android/internal/telephony/gsm/ApnSetting.java +++ b/telephony/java/com/android/internal/telephony/gsm/ApnSetting.java @@ -72,7 +72,10 @@ public class ApnSetting { boolean canHandleType(String type) { for (String t : types) { - if (t.equals(type) || t.equals(Phone.APN_TYPE_ALL)) { + // DEFAULT handles all, and HIPRI is handled by DEFAULT + if (t.equals(type) || t.equals(Phone.APN_TYPE_ALL) || + (t.equals(Phone.APN_TYPE_DEFAULT) && + type.equals(Phone.APN_TYPE_HIPRI))) { return true; } } diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java index 8b3529f..bf60bfe 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java @@ -135,12 +135,16 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { /** Currently active PdpConnection */ private PdpConnection mActivePdp; + private static int APN_INVALID_ID = -1; private static int APN_DEFAULT_ID = 0; private static int APN_MMS_ID = 1; private static int APN_SUPL_ID = 2; - private static int APN_NUM_TYPES = 3; + private static int APN_DUN_ID = 3; + private static int APN_HIPRI_ID = 4; + private static int APN_NUM_TYPES = 5; private boolean[] dataEnabled = new boolean[APN_NUM_TYPES]; + private int enabledCount = 0; /** Is packet service restricted by network */ private boolean mIsPsRestricted = false; @@ -213,7 +217,6 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { GsmDataConnectionTracker(GSMPhone p) { super(p); mGsmPhone = p; - p.mCM.registerForAvailable (this, EVENT_RADIO_AVAILABLE, null); p.mCM.registerForOffOrNotAvailable(this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null); p.mSIMRecords.registerForRecordsLoaded(this, EVENT_RECORDS_LOADED, null); @@ -252,6 +255,9 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { // and 2) whether the RIL will setup the baseband to auto-PS attach. SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(phone.getContext()); dataEnabled[APN_DEFAULT_ID] = !sp.getBoolean(GSMPhone.DATA_DISABLED_ON_BOOT_KEY, false); + if (dataEnabled[APN_DEFAULT_ID]) { + enabledCount++; + } noAutoAttach = !dataEnabled[APN_DEFAULT_ID]; if (!mRetryMgr.configure(SystemProperties.get("ro.gsm.data_retry_config"))) { @@ -332,6 +338,22 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { return result; } + protected int apnTypeToId(String type) { + if (TextUtils.equals(type, Phone.APN_TYPE_DEFAULT)) { + return APN_DEFAULT_ID; + } else if (TextUtils.equals(type, Phone.APN_TYPE_MMS)) { + return APN_MMS_ID; + } else if (TextUtils.equals(type, Phone.APN_TYPE_SUPL)) { + return APN_SUPL_ID; + } else if (TextUtils.equals(type, Phone.APN_TYPE_DUN)) { + return APN_DUN_ID; + } else if (TextUtils.equals(type, Phone.APN_TYPE_HIPRI)) { + return APN_HIPRI_ID; + } else { + return APN_INVALID_ID; + } + } + /** * Ensure that we are connected to an APN of the specified type. * @param type the APN type (currently the only valid values @@ -343,25 +365,15 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { * the APN has been established. */ protected int enableApnType(String type) { - if (!TextUtils.equals(type, Phone.APN_TYPE_MMS) && - !TextUtils.equals(type, Phone.APN_TYPE_SUPL)) { + int id = apnTypeToId(type); + if (id == APN_INVALID_ID) { return Phone.APN_REQUEST_FAILED; } // If already active, return - Log.d(LOG_TAG, "enableApnType("+type+")"); + if(DBG) Log.d(LOG_TAG, "enableApnType("+type+"), isApnTypeActive = " + + isApnTypeActive(type) + " and state = " + state); if (isApnTypeActive(type)) { - setEnabled(type, true); - removeMessages(EVENT_RESTORE_DEFAULT_APN); - /** - * We're being asked to enable a non-default APN that's already in use. - * This means we should restart the timer that will automatically - * switch back to the default APN and disable the non-default APN - * when it expires. - */ - sendMessageDelayed( - obtainMessage(EVENT_RESTORE_DEFAULT_APN), - getRestoreDefaultApnDelay()); if (state == State.INITING) return Phone.APN_REQUEST_STARTED; else if (state == State.CONNECTED) return Phone.APN_ALREADY_ACTIVE; } @@ -370,7 +382,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { return Phone.APN_TYPE_NOT_AVAILABLE; } - setEnabled(type, true); + setEnabled(id, true); mRequestedApnType = type; sendMessage(obtainMessage(EVENT_ENABLE_NEW_APN)); return Phone.APN_REQUEST_STARTED; @@ -385,31 +397,21 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { * @return */ protected int disableApnType(String type) { - Log.d(LOG_TAG, "disableApnType("+type+")"); - if ((TextUtils.equals(type, Phone.APN_TYPE_MMS) || - TextUtils.equals(type, Phone.APN_TYPE_SUPL)) - && isEnabled(type)) { - removeMessages(EVENT_RESTORE_DEFAULT_APN); - setEnabled(type, false); + if (DBG) Log.d(LOG_TAG, "disableApnType("+type+")"); + int id = apnTypeToId(type); + if (id == APN_INVALID_ID) { + return Phone.APN_REQUEST_FAILED; + } + if (isEnabled(id)) { + setEnabled(id, false); if (isApnTypeActive(Phone.APN_TYPE_DEFAULT)) { mRequestedApnType = Phone.APN_TYPE_DEFAULT; if (dataEnabled[APN_DEFAULT_ID]) { return Phone.APN_ALREADY_ACTIVE; } else { - Message msg = obtainMessage(EVENT_CLEAN_UP_CONNECTION); - msg.arg1 = 1; // tearDown is true; - msg.obj = Phone.REASON_DATA_DISABLED; - sendMessage(msg); return Phone.APN_REQUEST_STARTED; } } else { - /* - * Note that if default data is disabled, the following - * has the effect of disabling the MMS APN, and then - * ignoring the request to enable the default APN. - * The net result is that data is completely disabled. - */ - sendMessage(obtainMessage(EVENT_RESTORE_DEFAULT_APN)); return Phone.APN_REQUEST_STARTED; } } else { @@ -455,30 +457,30 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { return false; } - private boolean isEnabled(String apnType) { - if (TextUtils.equals(apnType, Phone.APN_TYPE_DEFAULT)) { - return dataEnabled[APN_DEFAULT_ID]; - } else if (TextUtils.equals(apnType, Phone.APN_TYPE_MMS)) { - return dataEnabled[APN_MMS_ID]; - } else if (TextUtils.equals(apnType, Phone.APN_TYPE_SUPL)) { - return dataEnabled[APN_SUPL_ID]; - } else { - return false; + private boolean isEnabled(int id) { + if (id != APN_INVALID_ID) { + return dataEnabled[id]; } + return false; } - private void setEnabled(String apnType, boolean enable) { - Log.d(LOG_TAG, "setEnabled(" + apnType + ", " + enable + ')'); - if (TextUtils.equals(apnType, Phone.APN_TYPE_DEFAULT)) { - dataEnabled[APN_DEFAULT_ID] = enable; - } else if (TextUtils.equals(apnType, Phone.APN_TYPE_MMS)) { - dataEnabled[APN_MMS_ID] = enable; - } else if (TextUtils.equals(apnType, Phone.APN_TYPE_SUPL)) { - dataEnabled[APN_SUPL_ID] = enable; + private void setEnabled(int id, boolean enable) { + if (DBG) Log.d(LOG_TAG, "setEnabled(" + id + ", " + enable + ')'); + if (dataEnabled[id] != enable) { + dataEnabled[id] = enable; + + if (enable) { + enabledCount++; + } else { + enabledCount--; + } + + if (enabledCount == 0) { + setPrivateDataEnabled(false); + } else if (enabledCount == 1) { + setPrivateDataEnabled(true); + } } - Log.d(LOG_TAG, "dataEnabled[DEFAULT_APN]=" + dataEnabled[APN_DEFAULT_ID] + - " dataEnabled[MMS_APN]=" + dataEnabled[APN_MMS_ID] + - " dataEnabled[SUPL_APN]=" + dataEnabled[APN_SUPL_ID]); } /** @@ -493,30 +495,20 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { * @return {@code true} if the operation succeeded */ public boolean setDataEnabled(boolean enable) { - boolean isEnabled = isEnabled(Phone.APN_TYPE_DEFAULT); - Log.d(LOG_TAG, "setDataEnabled("+enable+") isEnabled=" + isEnabled); - if (!isEnabled && enable) { - setEnabled(Phone.APN_TYPE_DEFAULT, true); - // trySetupData() will be a no-op if we are currently - // connected to the MMS APN + if (DBG) Log.d(LOG_TAG, "setDataEnabled("+enable+")"); + setEnabled(APN_DEFAULT_ID, enable); + return true; + } + + private void setPrivateDataEnabled(boolean enable) { + if (DBG) Log.d(LOG_TAG, "setPrivateDataEnabled("+enable+")"); + if (enable) { sendMessage(obtainMessage(EVENT_TRY_SETUP_DATA)); - return true; - } else if (!enable) { - setEnabled(Phone.APN_TYPE_DEFAULT, false); - // Don't tear down if there is an active APN and it handles MMS or SUPL. - // TODO: This isn't very general. - if ((isApnTypeActive(Phone.APN_TYPE_MMS) && isEnabled(Phone.APN_TYPE_MMS)) || - (isApnTypeActive(Phone.APN_TYPE_SUPL) && isEnabled(Phone.APN_TYPE_SUPL))) { - return false; - } + } else { Message msg = obtainMessage(EVENT_CLEAN_UP_CONNECTION); msg.arg1 = 1; // tearDown is true msg.obj = Phone.REASON_DATA_DISABLED; sendMessage(msg); - return true; - } else { - // isEnabled && enable - return true; } } @@ -536,7 +528,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { * {@code true} otherwise. */ public boolean getAnyDataEnabled() { - return dataEnabled[APN_DEFAULT_ID] || dataEnabled[APN_MMS_ID] || dataEnabled[APN_SUPL_ID]; + return (enabledCount != 0); } /** @@ -618,7 +610,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { } if (DBG) { - log ("Setup watingApns : " + apnListToString(waitingApns)); + log ("Setup waitngApns : " + apnListToString(waitingApns)); } return setupData(reason); } else { @@ -629,6 +621,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { " sim=" + mGsmPhone.mSIMRecords.getRecordsLoaded() + " UMTS=" + mGsmPhone.mSST.isConcurrentVoiceAndData() + " phoneState=" + phone.getState() + + " isDataAllowed=" + isDataAllowed() + " dataEnabled=" + getAnyDataEnabled() + " roaming=" + roaming + " dataOnRoamingEnable=" + getDataOnRoamingEnabled() + @@ -1251,15 +1244,6 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { trySetupData(reason); } - protected void onRestoreDefaultApn() { - if (DBG) Log.d(LOG_TAG, "Restore default APN"); - setEnabled(Phone.APN_TYPE_MMS, false); - mRequestedApnType = Phone.APN_TYPE_DEFAULT; - if (!isApnTypeActive(Phone.APN_TYPE_DEFAULT)) { - cleanUpConnection(true, Phone.REASON_RESTORE_DEFAULT_APN); - } - } - protected void onRoamingOff() { trySetupData(Phone.REASON_ROAMING_OFF); } @@ -1313,22 +1297,6 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { if (ar.exception == null) { // everything is setup - - /* - * We may have switched away from the default PDP context - * in order to enable a "special" APN (e.g., for MMS - * traffic). Set a timer to switch back and/or disable the - * special APN, so that a negligient application doesn't - * permanently prevent data connectivity. What we are - * protecting against here is not malicious apps, but - * rather an app that inadvertantly fails to reset to the - * default APN, or that dies before doing so. - */ - if (dataEnabled[APN_MMS_ID] || dataEnabled[APN_SUPL_ID]) { - removeMessages(EVENT_RESTORE_DEFAULT_APN); - sendMessageDelayed(obtainMessage(EVENT_RESTORE_DEFAULT_APN), - getRestoreDefaultApnDelay()); - } if (isApnTypeActive(Phone.APN_TYPE_DEFAULT)) { SystemProperties.set("gsm.defaultpdpcontext.active", "true"); if (canSetPreferApn && preferredApn == null) { @@ -1432,18 +1400,6 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { cleanUpConnection(tearDown, reason); } - private int getRestoreDefaultApnDelay() { - String restoreApnDelayStr = SystemProperties.get(APN_RESTORE_DELAY_PROP_NAME); - - if (restoreApnDelayStr != null && restoreApnDelayStr.length() != 0) { - try { - return Integer.valueOf(restoreApnDelayStr); - } catch (NumberFormatException e) { - } - } - return RESTORE_DEFAULT_APN_DELAY; - } - /** * Based on the sim operator numeric, create a list for all possible pdps * with all apns associated with that pdp @@ -1568,10 +1524,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { private void startDelayedRetry(PdpConnection.FailCause cause, String reason) { notifyNoData(cause); - if (mRequestedApnType != Phone.APN_TYPE_DEFAULT) { - sendMessage(obtainMessage(EVENT_RESTORE_DEFAULT_APN)); - } - else { + if (mRequestedApnType == Phone.APN_TYPE_DEFAULT) { reconnectAfterFail(cause, reason); } } @@ -1626,7 +1579,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { } public void handleMessage (Message msg) { - + if (DBG) Log.d(LOG_TAG,"GSMDataConnTrack handleMessage "+msg); switch (msg.what) { case EVENT_RECORDS_LOADED: onRecordsLoaded(); @@ -1636,10 +1589,6 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { onEnableNewApn(); break; - case EVENT_RESTORE_DEFAULT_APN: - onRestoreDefaultApn(); - break; - case EVENT_GPRS_DETACHED: onGprsDetached(); break; |