diff options
author | Android (Google) Code Review <android-gerrit@google.com> | 2009-08-13 14:02:08 -0700 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2009-08-13 14:02:08 -0700 |
commit | a9085a59ecdd3bf05fe997c790a5a5146f3a97f6 (patch) | |
tree | 336e3b1527650d10c16e18b39f798133c5f80070 /services | |
parent | bcda5e039ff44d3510d18857ba4538e38c619d21 (diff) | |
parent | 42acef37339afe6ac608c842f1637870ee9c4f6c (diff) | |
download | frameworks_base-a9085a59ecdd3bf05fe997c790a5a5146f3a97f6.zip frameworks_base-a9085a59ecdd3bf05fe997c790a5a5146f3a97f6.tar.gz frameworks_base-a9085a59ecdd3bf05fe997c790a5a5146f3a97f6.tar.bz2 |
Merge change 20853
* changes:
Add net type to mobile for mobile-required traffic
Diffstat (limited to 'services')
-rw-r--r-- | services/java/com/android/server/ConnectivityService.java | 843 | ||||
-rw-r--r-- | services/java/com/android/server/TelephonyRegistry.java | 17 |
2 files changed, 651 insertions, 209 deletions
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); } |