diff options
Diffstat (limited to 'services/java/com/android/server/ConnectivityService.java')
| -rw-r--r-- | services/java/com/android/server/ConnectivityService.java | 317 |
1 files changed, 280 insertions, 37 deletions
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index 81b8d40..9c504fe 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -26,13 +26,16 @@ import android.net.ConnectivityManager; import android.net.IConnectivityManager; import android.net.MobileDataStateTracker; import android.net.NetworkInfo; +import android.net.NetworkProperties; import android.net.NetworkStateTracker; import android.net.wifi.WifiStateTracker; +import android.net.NetworkUtils; import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; +import android.os.PowerManager; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemProperties; @@ -46,8 +49,13 @@ import com.android.internal.telephony.Phone; import com.android.server.connectivity.Tethering; import java.io.FileDescriptor; +import java.io.FileWriter; +import java.io.IOException; import java.io.PrintWriter; +import java.net.InetAddress; +import java.net.UnknownHostException; import java.util.ArrayList; +import java.util.Collection; import java.util.List; /** @@ -64,7 +72,6 @@ public class ConnectivityService extends IConnectivityManager.Stub { private static final String NETWORK_RESTORE_DELAY_PROP_NAME = "android.telephony.apn-restore"; - private Tethering mTethering; private boolean mTetheringConfigValid = false; @@ -104,6 +111,11 @@ public class ConnectivityService extends IConnectivityManager.Stub { private boolean mSystemReady; private Intent mInitialBroadcast; + private PowerManager.WakeLock mNetTransitionWakeLock; + private String mNetTransitionWakeLockCausedBy = ""; + private int mNetTransitionWakeLockSerialNumber; + private int mNetTransitionWakeLockTimeout; + private static class NetworkAttributes { /** * Class for holding settings read from resources. @@ -194,6 +206,12 @@ public class ConnectivityService extends IConnectivityManager.Stub { } mContext = context; + + PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); + mNetTransitionWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); + mNetTransitionWakeLockTimeout = mContext.getResources().getInteger( + com.android.internal.R.integer.config_networkTransitionTimeout); + mNetTrackers = new NetworkStateTracker[ ConnectivityManager.MAX_NETWORK_TYPE+1]; mHandler = new MyHandler(); @@ -597,15 +615,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { 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 -1; } } return Phone.APN_TYPE_NOT_AVAILABLE; @@ -724,8 +734,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { tracker.teardown(); return 1; } else { - // do it the old fashioned way - return tracker.stopUsingNetworkFeature(feature, pid, uid); + return -1; } } @@ -736,6 +745,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { * specified host is to be routed * @param hostAddress the IP address of the host to which the route is * desired + * todo - deprecate (only v4!) * @return {@code true} on success, {@code false} on failure */ public boolean requestRouteToHost(int networkType, int hostAddress) { @@ -752,7 +762,39 @@ public class ConnectivityService extends IConnectivityManager.Stub { } return false; } - return tracker.requestRouteToHost(hostAddress); + try { + InetAddress addr = InetAddress.getByAddress(NetworkUtils.v4IntToArray(hostAddress)); + return addHostRoute(tracker, addr); + } catch (UnknownHostException e) {} + return false; + } + + /** + * Ensure that a network route exists to deliver traffic to the specified + * host via the mobile data network. + * @param hostAddress the IP address of the host to which the route is desired, + * in network byte order. + * TODO - deprecate + * @return {@code true} on success, {@code false} on failure + */ + private boolean addHostRoute(NetworkStateTracker nt, InetAddress hostAddress) { + if (nt.getNetworkInfo().getType() == ConnectivityManager.TYPE_WIFI) { + return false; + } + + NetworkProperties p = nt.getNetworkProperties(); + if (p == null) return false; + String interfaceName = p.getInterfaceName(); + + if (DBG) { + Slog.d(TAG, "Requested host route to " + hostAddress + "(" + interfaceName + ")"); + } + if (interfaceName != null) { + return NetworkUtils.addHostRoute(interfaceName, hostAddress) == 0; + } else { + if (DBG) Slog.e(TAG, "addHostRoute failed due to null interface name"); + return false; + } } /** @@ -859,6 +901,12 @@ public class ConnectivityService extends IConnectivityManager.Stub { "ConnectivityService"); } + private void enforceConnectivityInternalPermission() { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CONNECTIVITY_INTERNAL, + "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 @@ -1134,15 +1182,23 @@ public class ConnectivityService extends IConnectivityManager.Stub { Slog.e(TAG, "Network declined teardown request"); return; } - if (isFailover) { - otherNet.releaseWakeLock(); - } + } + } + synchronized (ConnectivityService.this) { + // have a new default network, release the transition wakelock in a second + // if it's held. The second pause is to allow apps to reconnect over the + // new network + if (mNetTransitionWakeLock.isHeld()) { + mHandler.sendMessageDelayed(mHandler.obtainMessage( + NetworkStateTracker.EVENT_CLEAR_NET_TRANSITION_WAKELOCK, + mNetTransitionWakeLockSerialNumber, 0), + 1000); } } mActiveDefaultNetwork = type; } thisNet.setTeardownRequested(false); - thisNet.updateNetworkSettings(); + updateNetworkSettings(thisNet); handleConnectivityChange(); sendConnectedBroadcast(info); } @@ -1192,20 +1248,167 @@ public class ConnectivityService extends IConnectivityManager.Stub { for (int netType : mPriorityList) { if (mNetTrackers[netType].getNetworkInfo().isConnected()) { if (mNetAttributes[netType].isDefault()) { - mNetTrackers[netType].addDefaultRoute(); + addDefaultRoute(mNetTrackers[netType]); } else { - mNetTrackers[netType].addPrivateDnsRoutes(); + addPrivateDnsRoutes(mNetTrackers[netType]); } } else { if (mNetAttributes[netType].isDefault()) { - mNetTrackers[netType].removeDefaultRoute(); + removeDefaultRoute(mNetTrackers[netType]); } else { - mNetTrackers[netType].removePrivateDnsRoutes(); + removePrivateDnsRoutes(mNetTrackers[netType]); } } } } + private void addPrivateDnsRoutes(NetworkStateTracker nt) { + boolean privateDnsRouteSet = nt.isPrivateDnsRouteSet(); + NetworkProperties p = nt.getNetworkProperties(); + if (p == null) return; + String interfaceName = p.getInterfaceName(); + + if (DBG) { + Slog.d(TAG, "addPrivateDnsRoutes for " + nt + + "(" + interfaceName + ") - mPrivateDnsRouteSet = " + privateDnsRouteSet); + } + if (interfaceName != null && !privateDnsRouteSet) { + Collection<InetAddress> dnsList = p.getDnses(); + for (InetAddress dns : dnsList) { + if (DBG) Slog.d(TAG, " adding " + dns); + NetworkUtils.addHostRoute(interfaceName, dns); + } + nt.privateDnsRouteSet(true); + } + } + + private void removePrivateDnsRoutes(NetworkStateTracker nt) { + // TODO - we should do this explicitly but the NetUtils api doesnt + // support this yet - must remove all. No worse than before + NetworkProperties p = nt.getNetworkProperties(); + if (p == null) return; + String interfaceName = p.getInterfaceName(); + boolean privateDnsRouteSet = nt.isPrivateDnsRouteSet(); + if (interfaceName != null && privateDnsRouteSet) { + if (DBG) { + Slog.d(TAG, "removePrivateDnsRoutes for " + nt.getNetworkInfo().getTypeName() + + " (" + interfaceName + ")"); + } + NetworkUtils.removeHostRoutes(interfaceName); + nt.privateDnsRouteSet(false); + } + } + + + private void addDefaultRoute(NetworkStateTracker nt) { + NetworkProperties p = nt.getNetworkProperties(); + if (p == null) return; + String interfaceName = p.getInterfaceName(); + InetAddress defaultGatewayAddr = p.getGateway(); + boolean defaultRouteSet = nt.isDefaultRouteSet(); + + if ((interfaceName != null) && (defaultGatewayAddr != null ) && + (defaultRouteSet == false)) { + boolean error = (NetworkUtils.setDefaultRoute(interfaceName, defaultGatewayAddr) < 0); + + if (DBG && !error) { + NetworkInfo networkInfo = nt.getNetworkInfo(); + Slog.d(TAG, "addDefaultRoute for " + networkInfo.getTypeName() + + " (" + interfaceName + "), GatewayAddr=" + defaultGatewayAddr); + } + nt.defaultRouteSet(!error); + } + } + + + public void removeDefaultRoute(NetworkStateTracker nt) { + NetworkProperties p = nt.getNetworkProperties(); + if (p == null) return; + String interfaceName = p.getInterfaceName(); + boolean defaultRouteSet = nt.isDefaultRouteSet(); + + if (interfaceName != null && defaultRouteSet == true) { + boolean error = (NetworkUtils.removeDefaultRoute(interfaceName) < 0); + if (DBG && !error) { + NetworkInfo networkInfo = nt.getNetworkInfo(); + Slog.d(TAG, "removeDefaultRoute for " + networkInfo.getTypeName() + " (" + + interfaceName + ")"); + } + nt.defaultRouteSet(error); + } + } + + /** + * Reads the network specific TCP buffer sizes from SystemProperties + * net.tcp.buffersize.[default|wifi|umts|edge|gprs] and set them for system + * wide use + */ + public void updateNetworkSettings(NetworkStateTracker nt) { + String key = nt.getTcpBufferSizesPropName(); + String bufferSizes = SystemProperties.get(key); + + if (bufferSizes.length() == 0) { + Slog.e(TAG, key + " not found in system properties. Using defaults"); + + // Setting to default values so we won't be stuck to previous values + key = "net.tcp.buffersize.default"; + bufferSizes = SystemProperties.get(key); + } + + // Set values in kernel + if (bufferSizes.length() != 0) { + if (DBG) { + Slog.v(TAG, "Setting TCP values: [" + bufferSizes + + "] which comes from [" + key + "]"); + } + setBufferSize(bufferSizes); + } + } + + /** + * Writes TCP buffer sizes to /sys/kernel/ipv4/tcp_[r/w]mem_[min/def/max] + * which maps to /proc/sys/net/ipv4/tcp_rmem and tcpwmem + * + * @param bufferSizes in the format of "readMin, readInitial, readMax, + * writeMin, writeInitial, writeMax" + */ + private void setBufferSize(String bufferSizes) { + try { + String[] values = bufferSizes.split(","); + + if (values.length == 6) { + final String prefix = "/sys/kernel/ipv4/tcp_"; + stringToFile(prefix + "rmem_min", values[0]); + stringToFile(prefix + "rmem_def", values[1]); + stringToFile(prefix + "rmem_max", values[2]); + stringToFile(prefix + "wmem_min", values[3]); + stringToFile(prefix + "wmem_def", values[4]); + stringToFile(prefix + "wmem_max", values[5]); + } else { + Slog.e(TAG, "Invalid buffersize string: " + bufferSizes); + } + } catch (IOException e) { + Slog.e(TAG, "Can't set tcp buffer sizes:" + e); + } + } + + /** + * Writes string to file. Basically same as "echo -n $string > $filename" + * + * @param filename + * @param string + * @throws IOException + */ + private void stringToFile(String filename, String string) throws IOException { + FileWriter out = new FileWriter(filename); + try { + out.write(string); + } finally { + out.close(); + } + } + + /** * Adjust the per-process dns entries (net.dns<x>.<pid>) based * on the highest priority active net which this process requested. @@ -1221,12 +1424,14 @@ public class ConnectivityService extends IConnectivityManager.Stub { NetworkStateTracker nt = mNetTrackers[i]; if (nt.getNetworkInfo().isConnected() && !nt.isTeardownRequested()) { + NetworkProperties p = nt.getNetworkProperties(); + if (p == null) continue; 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); + Collection<InetAddress> dnses = p.getDnses(); + writePidDns(dnses, myPid); if (doBump) { bumpDns(); } @@ -1248,12 +1453,10 @@ public class ConnectivityService extends IConnectivityManager.Stub { } } - private void writePidDns(String[] dnsList, int pid) { + private void writePidDns(Collection <InetAddress> dnses, 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); - } + for (InetAddress dns : dnses) { + SystemProperties.set("net.dns" + j++ + "." + pid, dns.getHostAddress()); } } @@ -1279,17 +1482,17 @@ public class ConnectivityService extends IConnectivityManager.Stub { NetworkStateTracker nt = mNetTrackers[netType]; if (nt != null && nt.getNetworkInfo().isConnected() && !nt.isTeardownRequested()) { - String[] dnsList = nt.getNameServers(); + NetworkProperties p = nt.getNetworkProperties(); + if (p == null) continue; + Collection<InetAddress> dnses = p.getDnses(); if (mNetAttributes[netType].isDefault()) { int j = 1; - for (String dns : dnsList) { - if (dns != null && !TextUtils.equals(dns, "0.0.0.0")) { - if (DBG) { - Slog.d(TAG, "adding dns " + dns + " for " + - nt.getNetworkInfo().getTypeName()); - } - SystemProperties.set("net.dns" + j++, dns); + for (InetAddress dns : dnses) { + if (DBG) { + Slog.d(TAG, "adding dns " + dns + " for " + + nt.getNetworkInfo().getTypeName()); } + SystemProperties.set("net.dns" + j++, dns.getHostAddress()); } for (int k=j ; k<mNumDnsEntries; k++) { if (DBG) Slog.d(TAG, "erasing net.dns" + k); @@ -1301,7 +1504,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { List pids = mNetRequestersPids[netType]; for (int y=0; y< pids.size(); y++) { Integer pid = (Integer)pids.get(y); - writePidDns(dnsList, pid.intValue()); + writePidDns(dnses, pid.intValue()); } } } @@ -1362,6 +1565,13 @@ public class ConnectivityService extends IConnectivityManager.Stub { } pw.println(); + synchronized (this) { + pw.println("NetworkTranstionWakeLock is currently " + + (mNetTransitionWakeLock.isHeld() ? "" : "not ") + "held."); + pw.println("It was last requested for "+mNetTransitionWakeLockCausedBy); + } + pw.println(); + mTethering.dump(fd, pw, args); } @@ -1453,6 +1663,20 @@ public class ConnectivityService extends IConnectivityManager.Stub { FeatureUser u = (FeatureUser)msg.obj; u.expire(); break; + case NetworkStateTracker.EVENT_CLEAR_NET_TRANSITION_WAKELOCK: + String causedBy = null; + synchronized (ConnectivityService.this) { + if (msg.arg1 == mNetTransitionWakeLockSerialNumber && + mNetTransitionWakeLock.isHeld()) { + mNetTransitionWakeLock.release(); + causedBy = mNetTransitionWakeLockCausedBy; + } + } + if (causedBy != null) { + Slog.d(TAG, "NetTransition Wakelock for " + + causedBy + " released by timeout"); + } + break; } } } @@ -1536,4 +1760,23 @@ public class ConnectivityService extends IConnectivityManager.Stub { Settings.Secure.TETHER_SUPPORTED, defaultVal) != 0); return tetherEnabledInSettings && mTetheringConfigValid; } + + // An API NetworkStateTrackers can call when they lose their network. + // This will automatically be cleared after X seconds or a network becomes CONNECTED, + // whichever happens first. The timer is started by the first caller and not + // restarted by subsequent callers. + public void requestNetworkTransitionWakelock(String forWhom) { + enforceConnectivityInternalPermission(); + synchronized (this) { + if (mNetTransitionWakeLock.isHeld()) return; + mNetTransitionWakeLockSerialNumber++; + mNetTransitionWakeLock.acquire(); + mNetTransitionWakeLockCausedBy = forWhom; + } + mHandler.sendMessageDelayed(mHandler.obtainMessage( + NetworkStateTracker.EVENT_CLEAR_NET_TRANSITION_WAKELOCK, + mNetTransitionWakeLockSerialNumber, 0), + mNetTransitionWakeLockTimeout); + return; + } } |
