eclair snapshot
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 @@
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.os.Binder;
import android.os.RemoteException;
/**
@@ -114,15 +115,64 @@
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 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 @@
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/NetworkInfo.java b/core/java/android/net/NetworkInfo.java
index 9f53937..649cb8c 100644
--- a/core/java/android/net/NetworkInfo.java
+++ b/core/java/android/net/NetworkInfo.java
@@ -131,7 +131,7 @@
mSubtypeName = subtypeName;
setDetailedState(DetailedState.IDLE, null, null);
mState = State.UNKNOWN;
- mIsAvailable = true;
+ mIsAvailable = false; // until we're told otherwise, assume unavailable
mIsRoaming = false;
}
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index 1153648..a3ae01b 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -25,6 +25,9 @@
* {@hide}
*/
public class NetworkUtils {
+ /** Bring the named network interface up. */
+ public native static int enableInterface(String interfaceName);
+
/** Bring the named network interface down. */
public native static int disableInterface(String interfaceName);
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 8383deb..feb0dad 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -23,6 +23,7 @@
#include <arpa/inet.h>
extern "C" {
+int ifc_enable(const char *ifname);
int ifc_disable(const char *ifname);
int ifc_add_host_route(const char *ifname, uint32_t addr);
int ifc_remove_host_routes(const char *ifname);
@@ -66,6 +67,16 @@
jfieldID leaseDuration;
} dhcpInfoFieldIds;
+static jint android_net_utils_enableInterface(JNIEnv* env, jobject clazz, jstring ifname)
+{
+ int result;
+
+ const char *nameStr = env->GetStringUTFChars(ifname, NULL);
+ result = ::ifc_enable(nameStr);
+ env->ReleaseStringUTFChars(ifname, nameStr);
+ return (jint)result;
+}
+
static jint android_net_utils_disableInterface(JNIEnv* env, jobject clazz, jstring ifname)
{
int result;
@@ -209,6 +220,7 @@
static JNINativeMethod gNetworkUtilMethods[] = {
/* name, signature, funcPtr */
+ { "enableInterface", "(Ljava/lang/String;)I", (void *)android_net_utils_enableInterface },
{ "disableInterface", "(Ljava/lang/String;)I", (void *)android_net_utils_disableInterface },
{ "addHostRoute", "(Ljava/lang/String;I)I", (void *)android_net_utils_addHostRoute },
{ "removeHostRoutes", "(Ljava/lang/String;)I", (void *)android_net_utils_removeHostRoutes },
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 493bd09..78215b0 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -30,51 +30,136 @@
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 boolean mSystemReady;
+ private ArrayList<Intent> mDeferredBroadcasts;
+
+ 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;
-
+
private ConnectivityThread(Context context) {
super("ConnectivityThread");
mContext = context;
@@ -89,11 +174,11 @@
}
Looper.loop();
}
-
+
public static ConnectivityService getServiceInstance(Context context) {
ConnectivityThread thread = new ConnectivityThread(context);
thread.start();
-
+
synchronized (thread) {
while (sServiceInstance == null) {
try {
@@ -101,27 +186,71 @@
thread.wait();
} catch (InterruptedException ignore) {
Log.e(TAG,
- "Unexpected InterruptedException while waiting for ConnectivityService thread");
+ "Unexpected InterruptedException while waiting"+
+ " for ConnectivityService thread");
}
}
}
-
+
return sServiceInstance;
}
}
-
+
public static ConnectivityService getInstance(Context context) {
return ConnectivityThread.getServiceInstance(context);
}
-
+
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,
@@ -130,15 +259,36 @@
* 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;
- mMobileDataStateTracker = new MobileDataStateTracker(context, handler);
- mNetTrackers[ConnectivityManager.TYPE_MOBILE] = mMobileDataStateTracker;
-
- mActiveNetwork = null;
+ 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");
+
+ 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");
+
mNumDnsEntries = 0;
mTestMode = SystemProperties.get("cm.test.mode").equals("true")
@@ -148,16 +298,17 @@
t.startMonitoring();
// Constructing this starts it too
- mWifiWatchdogService = new WifiWatchdogService(context, mWifiStateTracker);
+ mWifiWatchdogService = new WifiWatchdogService(context, wst);
}
/**
- * Sets the preferred network.
+ * Sets the preferred network.
* @param preference the new preference
*/
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;
@@ -173,9 +324,10 @@
private void persistNetworkPreference(int networkPreference) {
final ContentResolver cr = mContext.getContentResolver();
- Settings.Secure.putInt(cr, Settings.Secure.NETWORK_PREFERENCE, networkPreference);
+ Settings.Secure.putInt(cr, Settings.Secure.NETWORK_PREFERENCE,
+ networkPreference);
}
-
+
private int getPersistedNetworkPreference() {
final ContentResolver cr = mContext.getContentResolver();
@@ -187,31 +339,30 @@
return ConnectivityManager.DEFAULT_NETWORK_PREFERENCE;
}
-
+
/**
- * Make the state of network connectivity conform to the preference settings.
+ * Make the state of network connectivity conform to the preference settings
* In this method, we only tear down a non-preferred network. Establishing
* a connection to the preferred network is taken care of when we handle
* the disconnect event from the non-preferred network
* (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 (!mNetTrackers[mNetworkPreference].isAvailable())
+ return;
- if (t.getNetworkInfo().getType() != mNetworkPreference) {
- NetworkStateTracker otherTracker = mNetTrackers[otherNetType];
- if (otherTracker.isAvailable()) {
- teardown(t);
- }
+ for (int t=0; t <= ConnectivityManager.MAX_RADIO_TYPE; t++) {
+ if (t != mNetworkPreference &&
+ mNetTrackers[t].getNetworkInfo().isConnected()) {
+ if (DBG) {
+ Log.d(TAG, "tearing down " +
+ mNetTrackers[t].getNetworkInfo() +
+ " in enforcePreference");
}
+ teardown(mNetTrackers[t]);
}
}
}
@@ -229,13 +380,21 @@
* Return NetworkInfo for the active (i.e., connected) network interface.
* It is assumed that at most one network is active at a time. If more
* than one is active, it is indeterminate which will be returned.
- * @return the info for the active network, or {@code null} if none is active
+ * @return the info for the active network, or {@code null} if none is
+ * active
*/
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;
}
}
@@ -280,36 +439,253 @@
return tracker != null && tracker.setRadio(turnOn);
}
- public int startUsingNetworkFeature(int networkType, String feature) {
- enforceChangePermission();
- if (!ConnectivityManager.isNetworkTypeValid(networkType)) {
- return -1;
+ /**
+ * Used to notice when the calling process dies so we can self-expire
+ *
+ * Also used to know if the process has cleaned up after itself when
+ * our auto-expire timer goes off. The timer has a link to an object.
+ *
+ */
+ 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();
+ }
}
- NetworkStateTracker tracker = mNetTrackers[networkType];
- if (tracker != null) {
- return tracker.startUsingNetworkFeature(feature, getCallingPid(), getCallingUid());
+
+ void unlinkDeathRecipient() {
+ mBinder.unlinkToDeath(this, 0);
}
- return -1;
+
+ public void binderDied() {
+ Log.d(TAG, "ConnectivityService FeatureUser binderDied(" +
+ mNetworkType + ", " + mFeature + ", " + mBinder);
+ stopUsingNetworkFeature(this, false);
+ }
+
+ public void expire() {
+ Log.d(TAG, "ConnectivityService FeatureUser expire(" +
+ mNetworkType + ", " + mFeature + ", " + mBinder);
+ stopUsingNetworkFeature(this, false);
+ }
}
+ // javadoc from interface
+ 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 Phone.APN_REQUEST_FAILED;
+ }
+
+ FeatureUser f = 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;
+ }
+
+ synchronized(this) {
+ mFeatureUsers.add(f);
+ if (!mNetRequestersPids[usedNetworkType].contains(currentPid)) {
+ // this gets used for per-pid dns when connected
+ mNetRequestersPids[usedNetworkType].add(currentPid);
+ }
+ }
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(
+ NetworkStateTracker.EVENT_RESTORE_DEFAULT_NETWORK,
+ f), getRestoreDefaultNetworkDelay());
+
+
+ if ((ni.isConnectedOrConnecting() == true) &&
+ !network.isTeardownRequested()) {
+ 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
+
+ if (DBG) Log.d(TAG, "reconnecting to special network");
+ network.reconnect();
+ return Phone.APN_REQUEST_STARTED;
+ } else {
+ synchronized(this) {
+ mFeatureUsers.add(f);
+ }
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(
+ NetworkStateTracker.EVENT_RESTORE_DEFAULT_NETWORK,
+ f), getRestoreDefaultNetworkDelay());
+
+ return network.startUsingNetworkFeature(feature,
+ getCallingPid(), getCallingUid());
+ }
+ }
+ return Phone.APN_TYPE_NOT_AVAILABLE;
+ }
+
+ // javadoc from interface
public int stopUsingNetworkFeature(int networkType, String feature) {
+ int pid = getCallingPid();
+ int uid = getCallingUid();
+
+ FeatureUser u = null;
+ boolean found = false;
+
+ synchronized(this) {
+ for (int i = 0; i < mFeatureUsers.size() ; i++) {
+ u = (FeatureUser)mFeatureUsers.get(i);
+ if (uid == u.mUid && pid == u.mPid &&
+ networkType == u.mNetworkType &&
+ TextUtils.equals(feature, u.mFeature)) {
+ found = true;
+ break;
+ }
+ }
+ }
+ if (found && u != null) {
+ // stop regardless of how many other time this proc had called start
+ return stopUsingNetworkFeature(u, true);
+ } else {
+ // none found!
+ return 1;
+ }
+ }
+
+ private int stopUsingNetworkFeature(FeatureUser u, boolean ignoreDups) {
+ int networkType = u.mNetworkType;
+ String feature = u.mFeature;
+ int pid = u.mPid;
+ int uid = u.mUid;
+
+ NetworkStateTracker tracker = null;
+ boolean callTeardown = false; // used to carry our decision outside of sync block
+
+ 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());
+
+ // need to link the mFeatureUsers list with the mNetRequestersPids state in this
+ // sync block
+ synchronized(this) {
+ // check if this process still has an outstanding start request
+ if (!mFeatureUsers.contains(u)) {
+ return 1;
+ }
+ u.unlinkDeathRecipient();
+ mFeatureUsers.remove(mFeatureUsers.indexOf(u));
+ // If we care about duplicate requests, check for that here.
+ //
+ // This is done to support the extension of a request - the app
+ // can request we start the network feature again and renew the
+ // auto-shutoff delay. Normal "stop" calls from the app though
+ // do not pay attention to duplicate requests - in effect the
+ // API does not refcount and a single stop will counter multiple starts.
+ if (ignoreDups == false) {
+ for (int i = 0; i < mFeatureUsers.size() ; i++) {
+ FeatureUser x = (FeatureUser)mFeatureUsers.get(i);
+ if (x.mUid == u.mUid && x.mPid == u.mPid &&
+ x.mNetworkType == u.mNetworkType &&
+ TextUtils.equals(x.mFeature, u.mFeature)) {
+ return 1;
+ }
+ }
+ }
+
+ // 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;
+ }
+ }
+ tracker = mNetTrackers[usedNetworkType];
+ if(usedNetworkType != networkType) {
+ Integer currentPid = new Integer(pid);
+ reassessPidDns(pid, true);
+ mNetRequestersPids[usedNetworkType].remove(currentPid);
+ if (mNetRequestersPids[usedNetworkType].size() != 0) {
+ if (DBG) Log.d(TAG, "not tearing down special network - " +
+ "others still using it");
+ return 1;
+ }
+ callTeardown = true;
+ }
}
- return -1;
+
+ if (callTeardown) {
+ tracker.teardown();
+ return 1;
+ } else {
+ // do it the old fashioned way
+ return tracker.stopUsingNetworkFeature(feature, pid, uid);
+ }
}
/**
* Ensure that a network route exists to deliver traffic to the specified
* host via the specified network interface.
- * @param networkType the type of the network over which traffic to the specified
- * host is to be routed
- * @param hostAddress the IP address of the host to which the route is desired
+ * @param networkType the type of the network over which traffic to the
+ * specified host is to be routed
+ * @param hostAddress the IP address of the host to which the route is
+ * desired
* @return {@code true} on success, {@code false} on failure
*/
public boolean requestRouteToHost(int networkType, int hostAddress) {
@@ -318,19 +694,14 @@
return false;
}
NetworkStateTracker tracker = mNetTrackers[networkType];
- /*
- * If there's only one connected network, and it's the one requested,
- * then we don't have to do anything - the requested route already
- * exists. If it's not the requested network, then it's not possible
- * to establish the requested route. Finally, if there is more than
- * one connected network, then we must insert an entry in the routing
- * table.
- */
- if (getNumConnectedNetworks() > 1) {
- return tracker.requestRouteToHost(hostAddress);
- } else {
- return tracker.getNetworkInfo().getType() == networkType;
+
+ if (!tracker.getNetworkInfo().isConnected() || tracker.isTeardownRequested()) {
+ if (DBG) {
+ Log.d(TAG, "requestRouteToHost on down network (" + networkType + " - dropped");
+ }
+ return false;
}
+ return tracker.requestRouteToHost(hostAddress);
}
/**
@@ -340,7 +711,7 @@
return Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.BACKGROUND_DATA, 1) == 1;
}
-
+
/**
* @see ConnectivityManager#setBackgroundDataSetting(boolean)
*/
@@ -348,22 +719,24 @@
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_BACKGROUND_DATA_SETTING,
"ConnectivityService");
-
+
if (getBackgroundDataSetting() == allowBackgroundDataUsage) return;
Settings.Secure.putInt(mContext.getContentResolver(),
- Settings.Secure.BACKGROUND_DATA, allowBackgroundDataUsage ? 1 : 0);
-
+ Settings.Secure.BACKGROUND_DATA,
+ allowBackgroundDataUsage ? 1 : 0);
+
Intent broadcast = new Intent(
ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED);
mContext.sendBroadcast(broadcast);
- }
-
+ }
+
private int getNumConnectedNetworks() {
int numConnectedNets = 0;
for (NetworkStateTracker nt : mNetTrackers) {
- if (nt.getNetworkInfo().isConnected() && !nt.isTeardownRequested()) {
+ if (nt.getNetworkInfo().isConnected() &&
+ !nt.isTeardownRequested()) {
++numConnectedNets;
}
}
@@ -371,64 +744,46 @@
}
private void enforceAccessPermission() {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_NETWORK_STATE,
- "ConnectivityService");
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.ACCESS_NETWORK_STATE,
+ "ConnectivityService");
}
private void enforceChangePermission() {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_NETWORK_STATE,
- "ConnectivityService");
-
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.CHANGE_NETWORK_STATE,
+ "ConnectivityService");
}
/**
- * Handle a {@code DISCONNECTED} event. If this pertains to the non-active network,
- * we ignore it. If it is for the active network, we send out a broadcast.
- * But first, we check whether it might be possible to connect to a different
- * network.
+ * Handle a {@code DISCONNECTED} event. If this pertains to the non-active
+ * network, we ignore it. If it is for the active network, we send out a
+ * broadcast. But first, we check whether it might be possible to connect
+ * to a different network.
* @param info the {@code NetworkInfo} for the network
*/
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()) {
@@ -439,31 +794,92 @@
intent.putExtra(ConnectivityManager.EXTRA_REASON, info.getReason());
}
if (info.getExtraInfo() != null) {
- intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, info.getExtraInfo());
+ 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());
- } else {
- Log.v(TAG, "Attempting to switch to " + switchTo.getTypeName());
+
+ /*
+ * If this is a default network, check if other defaults are available
+ * or active
+ */
+ NetworkStateTracker newNet = null;
+ if (mNetAttributes[prevNetType].isDefault()) {
+ 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;
+ }
}
}
- intent.putExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO, switchTo);
- } else {
- intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
- }
- if (DBG) Log.v(TAG, "Sending DISCONNECT bcast for " + info.getTypeName() +
- (switchTo == null ? "" : " other=" + switchTo.getTypeName()));
- mContext.sendStickyBroadcast(intent);
+ 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.isTeardownRequested()) {
+ 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 {
+ intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY,
+ true);
+ newNet.reconnect();
+ }
+ } else {
+ intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY,
+ true);
+ }
+ }
+
+ // do this before we broadcast the change
+ handleConnectivityChange();
+
+ sendStickyBroadcast(intent);
/*
- * If the failover network is already connected, then immediately send out
- * a followup broadcast indicating successful failover
+ * 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) {
@@ -477,9 +893,10 @@
intent.putExtra(ConnectivityManager.EXTRA_REASON, info.getReason());
}
if (info.getExtraInfo() != null) {
- intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, info.getExtraInfo());
+ intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO,
+ info.getExtraInfo());
}
- mContext.sendStickyBroadcast(intent);
+ sendStickyBroadcast(intent);
}
/**
@@ -488,106 +905,124 @@
*/
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 + ").";
}
-
- Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
- intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
+ 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);
+ if (getActiveNetworkInfo() == null) {
intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
- if (reason != null) {
- intent.putExtra(ConnectivityManager.EXTRA_REASON, reason);
+ }
+ 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);
+ }
+ sendStickyBroadcast(intent);
+ }
+
+ private void sendStickyBroadcast(Intent intent) {
+ synchronized(this) {
+ if (mSystemReady) {
+ mContext.sendStickyBroadcast(intent);
+ } else {
+ if (mDeferredBroadcasts == null) {
+ mDeferredBroadcasts = new ArrayList<Intent>();
+ }
+ mDeferredBroadcasts.add(intent);
}
- if (extraInfo != null) {
- intent.putExtra(ConnectivityManager.EXTRA_EXTRA_INFO, extraInfo);
+ }
+ }
+
+ void systemReady() {
+ synchronized(this) {
+ mSystemReady = true;
+ if (mDeferredBroadcasts != null) {
+ int count = mDeferredBroadcasts.size();
+ for (int i = 0; i < count; i++) {
+ mContext.sendStickyBroadcast(mDeferredBroadcasts.get(i));
+ }
+ mDeferredBroadcasts = null;
}
- 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());
+ 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;
- }
+ NetworkStateTracker thisNet = mNetTrackers[type];
- boolean toredown = false;
+ // if this is a default net and other default is running
+ // kill the one not preferred
+ if (mNetAttributes[type].isDefault()) {
+ 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();
+ }
+ }
+ }
+ mActiveDefaultNetwork = type;
+ }
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();
- }
- } else {
- if (DBG) Log.v(TAG, "Not broadcasting CONNECT_ACTION to torn down network " +
- info.getTypeName());
- }
+ thisNet.updateNetworkSettings();
+ handleConnectivityChange();
+ sendConnectedBroadcast(info);
}
private void handleScanResultsAvailable(NetworkInfo info) {
int networkType = info.getType();
if (networkType != ConnectivityManager.TYPE_WIFI) {
- if (DBG) Log.v(TAG, "Got ScanResultsAvailable for " + info.getTypeName() + " network."
- + " Don't know how to handle.");
+ if (DBG) Log.v(TAG, "Got ScanResultsAvailable for " +
+ info.getTypeName() + " network. Don't know how to handle.");
}
-
+
mNetTrackers[networkType].interpretScanResultsAvailable();
}
- private void handleNotificationChange(boolean visible, int id, Notification notification) {
+ private void handleNotificationChange(boolean visible, int id,
+ Notification notification) {
NotificationManager notificationManager = (NotificationManager) mContext
.getSystemService(Context.NOTIFICATION_SERVICE);
-
+
if (visible) {
notificationManager.notify(id, notification);
} else {
@@ -605,79 +1040,173 @@
*/
private void 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() {
- /*
- * Set DNS properties. Always put Wi-Fi entries at the front of
- * the list if it is active.
- */
- int index = 1;
- String lastDns = "";
- int numConnectedNets = 0;
- int incrValue = ConnectivityManager.TYPE_MOBILE - ConnectivityManager.TYPE_WIFI;
- int stopValue = ConnectivityManager.TYPE_MOBILE + incrValue;
+ /**
+ * 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, "");
+ }
+ }
- for (int netType = ConnectivityManager.TYPE_WIFI; netType != stopValue; netType += incrValue) {
+ 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() {
+ /*
+ * Bump the property that tells the name resolver library to reread
+ * the DNS server list from the properties.
+ */
+ 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));
+ }
+
+ private void handleDnsConfigurationChange() {
+ // 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() && !nt.isTeardownRequested()) {
- ++numConnectedNets;
+ if (nt != null && nt.getNetworkInfo().isConnected() &&
+ !nt.isTeardownRequested()) {
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")) {
+ if (DBG) {
+ Log.d(TAG, "adding dns " + dns + " for " +
+ nt.getNetworkInfo().getTypeName());
+ }
+ SystemProperties.set("net.dns" + j++, dns);
+ }
+ }
+ for (int k=j ; k<mNumDnsEntries; k++) {
+ if (DBG) Log.d(TAG, "erasing net.dns" + k);
+ SystemProperties.set("net.dns" + k, "");
+ }
+ 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
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+ if (mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
- pw.println("Permission Denial: can't dump ConnectivityService from from pid="
- + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid());
+ pw.println("Permission Denial: can't dump ConnectivityService " +
+ "from from pid=" + Binder.getCallingPid() + ", uid=" +
+ 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) {
@@ -685,46 +1214,54 @@
switch (msg.what) {
case NetworkStateTracker.EVENT_STATE_CHANGED:
info = (NetworkInfo) msg.obj;
- if (DBG) Log.v(TAG, "ConnectivityChange for " + info.getTypeName() + ": " +
+ if (DBG) Log.d(TAG, "ConnectivityChange for " +
+ info.getTypeName() + ": " +
info.getState() + "/" + info.getDetailedState());
// Connectivity state changed:
// [31-13] Reserved for future use
- // [12-9] Network subtype (for mobile network, as defined by TelephonyManager)
- // [8-3] Detailed state ordinal (as defined by NetworkInfo.DetailedState)
+ // [12-9] Network subtype (for mobile network, as defined
+ // by TelephonyManager)
+ // [8-3] Detailed state ordinal (as defined by
+ // NetworkInfo.DetailedState)
// [2-0] Network type (as defined by ConnectivityManager)
int eventLogParam = (info.getType() & 0x7) |
((info.getDetailedState().ordinal() & 0x3f) << 3) |
(info.getSubtype() << 9);
- EventLog.writeEvent(EVENTLOG_CONNECTIVITY_STATE_CHANGED, eventLogParam);
-
- if (info.getDetailedState() == NetworkInfo.DetailedState.FAILED) {
+ EventLog.writeEvent(EVENTLOG_CONNECTIVITY_STATE_CHANGED,
+ eventLogParam);
+
+ if (info.getDetailedState() ==
+ NetworkInfo.DetailedState.FAILED) {
handleConnectionFailure(info);
- } else if (info.getState() == NetworkInfo.State.DISCONNECTED) {
+ } else if (info.getState() ==
+ NetworkInfo.State.DISCONNECTED) {
handleDisconnect(info);
} else if (info.getState() == NetworkInfo.State.SUSPENDED) {
// TODO: need to think this over.
- // the logic here is, handle SUSPENDED the same as DISCONNECTED. The
- // only difference being we are broadcasting an intent with NetworkInfo
- // that's suspended. This allows the applications an opportunity to
- // handle DISCONNECTED and SUSPENDED differently, or not.
+ // the logic here is, handle SUSPENDED the same as
+ // DISCONNECTED. The only difference being we are
+ // broadcasting an intent with NetworkInfo that's
+ // suspended. This allows the applications an
+ // opportunity to handle DISCONNECTED and SUSPENDED
+ // differently, or not.
handleDisconnect(info);
} else if (info.getState() == NetworkInfo.State.CONNECTED) {
handleConnect(info);
}
- handleConnectivityChange();
break;
case NetworkStateTracker.EVENT_SCAN_RESULTS_AVAILABLE:
info = (NetworkInfo) msg.obj;
handleScanResultsAvailable(info);
break;
-
+
case NetworkStateTracker.EVENT_NOTIFICATION_CHANGED:
- handleNotificationChange(msg.arg1 == 1, msg.arg2, (Notification) msg.obj);
+ handleNotificationChange(msg.arg1 == 1, msg.arg2,
+ (Notification) msg.obj);
case NetworkStateTracker.EVENT_CONFIGURATION_CHANGED:
- handleConfigurationChange();
+ handleDnsConfigurationChange();
break;
case NetworkStateTracker.EVENT_ROAMING_CHANGED:
@@ -734,6 +1271,10 @@
case NetworkStateTracker.EVENT_NETWORK_SUBTYPE_CHANGED:
// fill me in
break;
+ case NetworkStateTracker.EVENT_RESTORE_DEFAULT_NETWORK:
+ FeatureUser u = (FeatureUser)msg.obj;
+ u.expire();
+ break;
}
}
}