am 89ab6757: (-s ours) am 6626398a: Merge "Fix for updating TCP buffer size when switching network type"
* commit '89ab67578e4e06c41f0e96d840de952d10895233':
Fix for updating TCP buffer size when switching network type
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 5f8793c..d30ef04 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -89,11 +89,21 @@
* should always obtain network information through
* {@link #getActiveNetworkInfo()} or
* {@link #getAllNetworkInfo()}.
+ * @see #EXTRA_NETWORK_TYPE
*/
@Deprecated
public static final String EXTRA_NETWORK_INFO = "networkInfo";
/**
+ * Network type which triggered a {@link #CONNECTIVITY_ACTION} broadcast.
+ * Can be used with {@link #getNetworkInfo(int)} to get {@link NetworkInfo}
+ * state based on the calling application.
+ *
+ * @see android.content.Intent#getIntExtra(String, int)
+ */
+ public static final String EXTRA_NETWORK_TYPE = "networkType";
+
+ /**
* The lookup key for a boolean that indicates whether a connect event
* is for a network to which the connectivity manager was failing over
* following a disconnect on another network.
@@ -137,6 +147,28 @@
public static final String EXTRA_INET_CONDITION = "inetCondition";
/**
+ * Broadcast action to indicate the change of data activity status
+ * (idle or active) on a network in a recent period.
+ * The network becomes active when data transimission is started, or
+ * idle if there is no data transimition for a period of time.
+ * {@hide}
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_DATA_ACTIVITY_CHANGE = "android.net.conn.DATA_ACTIVITY_CHANGE";
+ /**
+ * The lookup key for an enum that indicates the network device type on which this data activity
+ * change happens.
+ * {@hide}
+ */
+ public static final String EXTRA_DEVICE_TYPE = "deviceType";
+ /**
+ * The lookup key for a boolean that indicates the device is active or not. {@code true} means
+ * it is actively sending or receiving data and {@code false} means it is idle.
+ * {@hide}
+ */
+ public static final String EXTRA_IS_ACTIVE = "isActive";
+
+ /**
* Broadcast Action: The setting for background data usage has changed
* values. Use {@link #getBackgroundDataSetting()} to get the current value.
* <p>
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 86ada40..375ba68 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -17,8 +17,16 @@
package com.android.server;
import static android.Manifest.permission.MANAGE_NETWORK_POLICY;
+import static android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE;
+import static android.net.ConnectivityManager.TYPE_BLUETOOTH;
+import static android.net.ConnectivityManager.TYPE_DUMMY;
+import static android.net.ConnectivityManager.TYPE_ETHERNET;
+import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.ConnectivityManager.TYPE_WIFI;
+import static android.net.ConnectivityManager.TYPE_WIMAX;
+import static android.net.ConnectivityManager.getNetworkTypeName;
import static android.net.ConnectivityManager.isNetworkTypeValid;
import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
@@ -35,6 +43,7 @@
import android.net.DummyDataStateTracker;
import android.net.EthernetDataTracker;
import android.net.IConnectivityManager;
+import android.net.INetworkManagementEventObserver;
import android.net.INetworkPolicyListener;
import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
@@ -81,16 +90,16 @@
import com.android.server.am.BatteryStatsService;
import com.android.server.connectivity.Tethering;
import com.android.server.connectivity.Vpn;
+import com.android.server.net.BaseNetworkObserver;
import com.google.android.collect.Lists;
import com.google.android.collect.Sets;
+
import dalvik.system.DexClassLoader;
+
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Constructor;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.lang.reflect.InvocationTargetException;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
@@ -186,95 +195,81 @@
private static final boolean TO_DEFAULT_TABLE = true;
private static final boolean TO_SECONDARY_TABLE = false;
- // Share the event space with NetworkStateTracker (which can't see this
- // internal class but sends us events). If you change these, change
- // NetworkStateTracker.java too.
- private static final int MIN_NETWORK_STATE_TRACKER_EVENT = 1;
- private static final int MAX_NETWORK_STATE_TRACKER_EVENT = 100;
-
/**
* used internally as a delayed event to make us switch back to the
* default network
*/
- private static final int EVENT_RESTORE_DEFAULT_NETWORK =
- MAX_NETWORK_STATE_TRACKER_EVENT + 1;
+ private static final int EVENT_RESTORE_DEFAULT_NETWORK = 1;
/**
* used internally to change our mobile data enabled flag
*/
- private static final int EVENT_CHANGE_MOBILE_DATA_ENABLED =
- MAX_NETWORK_STATE_TRACKER_EVENT + 2;
+ private static final int EVENT_CHANGE_MOBILE_DATA_ENABLED = 2;
/**
* used internally to change our network preference setting
* arg1 = networkType to prefer
*/
- private static final int EVENT_SET_NETWORK_PREFERENCE =
- MAX_NETWORK_STATE_TRACKER_EVENT + 3;
+ private static final int EVENT_SET_NETWORK_PREFERENCE = 3;
/**
* used internally to synchronize inet condition reports
* arg1 = networkType
* arg2 = condition (0 bad, 100 good)
*/
- private static final int EVENT_INET_CONDITION_CHANGE =
- MAX_NETWORK_STATE_TRACKER_EVENT + 4;
+ private static final int EVENT_INET_CONDITION_CHANGE = 4;
/**
* used internally to mark the end of inet condition hold periods
* arg1 = networkType
*/
- private static final int EVENT_INET_CONDITION_HOLD_END =
- MAX_NETWORK_STATE_TRACKER_EVENT + 5;
+ private static final int EVENT_INET_CONDITION_HOLD_END = 5;
/**
* used internally to set enable/disable cellular data
* arg1 = ENBALED or DISABLED
*/
- private static final int EVENT_SET_MOBILE_DATA =
- MAX_NETWORK_STATE_TRACKER_EVENT + 7;
+ private static final int EVENT_SET_MOBILE_DATA = 7;
/**
* used internally to clear a wakelock when transitioning
* from one net to another
*/
- private static final int EVENT_CLEAR_NET_TRANSITION_WAKELOCK =
- MAX_NETWORK_STATE_TRACKER_EVENT + 8;
+ private static final int EVENT_CLEAR_NET_TRANSITION_WAKELOCK = 8;
/**
* used internally to reload global proxy settings
*/
- private static final int EVENT_APPLY_GLOBAL_HTTP_PROXY =
- MAX_NETWORK_STATE_TRACKER_EVENT + 9;
+ private static final int EVENT_APPLY_GLOBAL_HTTP_PROXY = 9;
/**
* used internally to set external dependency met/unmet
* arg1 = ENABLED (met) or DISABLED (unmet)
* arg2 = NetworkType
*/
- private static final int EVENT_SET_DEPENDENCY_MET =
- MAX_NETWORK_STATE_TRACKER_EVENT + 10;
+ private static final int EVENT_SET_DEPENDENCY_MET = 10;
/**
* used internally to restore DNS properties back to the
* default network
*/
- private static final int EVENT_RESTORE_DNS =
- MAX_NETWORK_STATE_TRACKER_EVENT + 11;
+ private static final int EVENT_RESTORE_DNS = 11;
/**
* used internally to send a sticky broadcast delayed.
*/
- private static final int EVENT_SEND_STICKY_BROADCAST_INTENT =
- MAX_NETWORK_STATE_TRACKER_EVENT + 12;
+ private static final int EVENT_SEND_STICKY_BROADCAST_INTENT = 12;
/**
* Used internally to
* {@link NetworkStateTracker#setPolicyDataEnable(boolean)}.
*/
- private static final int EVENT_SET_POLICY_DATA_ENABLE = MAX_NETWORK_STATE_TRACKER_EVENT + 13;
+ private static final int EVENT_SET_POLICY_DATA_ENABLE = 13;
- private Handler mHandler;
+ /** Handler used for internal events. */
+ private InternalHandler mHandler;
+ /** Handler used for incoming {@link NetworkStateTracker} events. */
+ private NetworkStateTrackerHandler mTrackerHandler;
// list of DeathRecipients used to make sure features are turned off when
// a process dies
@@ -328,11 +323,24 @@
public ConnectivityService(Context context, INetworkManagementService netd,
INetworkStatsService statsService, INetworkPolicyManager policyManager) {
+ // Currently, omitting a NetworkFactory will create one internally
+ // TODO: create here when we have cleaner WiMAX support
+ this(context, netd, statsService, policyManager, null);
+ }
+
+ public ConnectivityService(Context context, INetworkManagementService netd,
+ INetworkStatsService statsService, INetworkPolicyManager policyManager,
+ NetworkFactory netFactory) {
if (DBG) log("ConnectivityService starting up");
HandlerThread handlerThread = new HandlerThread("ConnectivityServiceThread");
handlerThread.start();
- mHandler = new MyHandler(handlerThread.getLooper());
+ mHandler = new InternalHandler(handlerThread.getLooper());
+ mTrackerHandler = new NetworkStateTrackerHandler(handlerThread.getLooper());
+
+ if (netFactory == null) {
+ netFactory = new DefaultNetworkFactory(context, mTrackerHandler);
+ }
// setup our unique device name
if (TextUtils.isEmpty(SystemProperties.get("net.hostname"))) {
@@ -472,59 +480,27 @@
mTestMode = SystemProperties.get("cm.test.mode").equals("true")
&& SystemProperties.get("ro.build.type").equals("eng");
- /*
- * Create the network state trackers for Wi-Fi and mobile
- * data. Maybe this could be done with a factory class,
- * but it's not clear that it's worth it, given that
- * the number of different network types is not going
- * to change very often.
- */
- for (int netType : mPriorityList) {
- switch (mNetConfigs[netType].radio) {
- case ConnectivityManager.TYPE_WIFI:
- mNetTrackers[netType] = new WifiStateTracker(netType,
- mNetConfigs[netType].name);
- mNetTrackers[netType].startMonitoring(context, mHandler);
- break;
- case ConnectivityManager.TYPE_MOBILE:
- mNetTrackers[netType] = new MobileDataStateTracker(netType,
- mNetConfigs[netType].name);
- mNetTrackers[netType].startMonitoring(context, mHandler);
- break;
- case ConnectivityManager.TYPE_DUMMY:
- mNetTrackers[netType] = new DummyDataStateTracker(netType,
- mNetConfigs[netType].name);
- mNetTrackers[netType].startMonitoring(context, mHandler);
- break;
- case ConnectivityManager.TYPE_BLUETOOTH:
- mNetTrackers[netType] = BluetoothTetheringDataTracker.getInstance();
- mNetTrackers[netType].startMonitoring(context, mHandler);
- break;
- case ConnectivityManager.TYPE_WIMAX:
- mNetTrackers[netType] = makeWimaxStateTracker();
- if (mNetTrackers[netType]!= null) {
- mNetTrackers[netType].startMonitoring(context, mHandler);
- }
- break;
- case ConnectivityManager.TYPE_ETHERNET:
- mNetTrackers[netType] = EthernetDataTracker.getInstance();
- mNetTrackers[netType].startMonitoring(context, mHandler);
- break;
- default:
- loge("Trying to create a DataStateTracker for an unknown radio type " +
- mNetConfigs[netType].radio);
+
+ // Create and start trackers for hard-coded networks
+ for (int targetNetworkType : mPriorityList) {
+ final NetworkConfig config = mNetConfigs[targetNetworkType];
+ final NetworkStateTracker tracker;
+ try {
+ tracker = netFactory.createTracker(targetNetworkType, config);
+ mNetTrackers[targetNetworkType] = tracker;
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Problem creating " + getNetworkTypeName(targetNetworkType)
+ + " tracker: " + e);
continue;
}
- mCurrentLinkProperties[netType] = null;
- if (mNetTrackers[netType] != null && mNetConfigs[netType].isDefault()) {
- mNetTrackers[netType].reconnect();
+
+ tracker.startMonitoring(context, mTrackerHandler);
+ if (config.isDefault()) {
+ tracker.reconnect();
}
}
- IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
- INetworkManagementService nmService = INetworkManagementService.Stub.asInterface(b);
-
- mTethering = new Tethering(mContext, nmService, statsService, this, mHandler.getLooper());
+ mTethering = new Tethering(mContext, mNetd, statsService, this, mHandler.getLooper());
mTetheringConfigValid = ((mTethering.getTetherableUsbRegexs().length != 0 ||
mTethering.getTetherableWifiRegexs().length != 0 ||
mTethering.getTetherableBluetoothRegexs().length != 0) &&
@@ -533,8 +509,9 @@
mVpn = new Vpn(mContext, new VpnCallback());
try {
- nmService.registerObserver(mTethering);
- nmService.registerObserver(mVpn);
+ mNetd.registerObserver(mTethering);
+ mNetd.registerObserver(mVpn);
+ mNetd.registerObserver(mDataActivityObserver);
} catch (RemoteException e) {
loge("Error registering observer :" + e);
}
@@ -548,8 +525,55 @@
loadGlobalProxy();
}
-private NetworkStateTracker makeWimaxStateTracker() {
- //Initialize Wimax
+
+ /**
+ * Factory that creates {@link NetworkStateTracker} instances using given
+ * {@link NetworkConfig}.
+ */
+ public interface NetworkFactory {
+ public NetworkStateTracker createTracker(int targetNetworkType, NetworkConfig config);
+ }
+
+ private static class DefaultNetworkFactory implements NetworkFactory {
+ private final Context mContext;
+ private final Handler mTrackerHandler;
+
+ public DefaultNetworkFactory(Context context, Handler trackerHandler) {
+ mContext = context;
+ mTrackerHandler = trackerHandler;
+ }
+
+ @Override
+ public NetworkStateTracker createTracker(int targetNetworkType, NetworkConfig config) {
+ switch (config.radio) {
+ case TYPE_WIFI:
+ return new WifiStateTracker(targetNetworkType, config.name);
+ case TYPE_MOBILE:
+ return new MobileDataStateTracker(targetNetworkType, config.name);
+ case TYPE_DUMMY:
+ return new DummyDataStateTracker(targetNetworkType, config.name);
+ case TYPE_BLUETOOTH:
+ return BluetoothTetheringDataTracker.getInstance();
+ case TYPE_WIMAX:
+ return makeWimaxStateTracker(mContext, mTrackerHandler);
+ case TYPE_ETHERNET:
+ return EthernetDataTracker.getInstance();
+ default:
+ throw new IllegalArgumentException(
+ "Trying to create a NetworkStateTracker for an unknown radio type: "
+ + config.radio);
+ }
+ }
+ }
+
+ /**
+ * Loads external WiMAX library and registers as system service, returning a
+ * {@link NetworkStateTracker} for WiMAX. Caller is still responsible for
+ * invoking {@link NetworkStateTracker#startMonitoring(Context, Handler)}.
+ */
+ private static NetworkStateTracker makeWimaxStateTracker(
+ Context context, Handler trackerHandler) {
+ // Initialize Wimax
DexClassLoader wimaxClassLoader;
Class wimaxStateTrackerClass = null;
Class wimaxServiceClass = null;
@@ -562,25 +586,25 @@
NetworkStateTracker wimaxStateTracker = null;
- boolean isWimaxEnabled = mContext.getResources().getBoolean(
+ boolean isWimaxEnabled = context.getResources().getBoolean(
com.android.internal.R.bool.config_wimaxEnabled);
if (isWimaxEnabled) {
try {
- wimaxJarLocation = mContext.getResources().getString(
+ wimaxJarLocation = context.getResources().getString(
com.android.internal.R.string.config_wimaxServiceJarLocation);
- wimaxLibLocation = mContext.getResources().getString(
+ wimaxLibLocation = context.getResources().getString(
com.android.internal.R.string.config_wimaxNativeLibLocation);
- wimaxManagerClassName = mContext.getResources().getString(
+ wimaxManagerClassName = context.getResources().getString(
com.android.internal.R.string.config_wimaxManagerClassname);
- wimaxServiceClassName = mContext.getResources().getString(
+ wimaxServiceClassName = context.getResources().getString(
com.android.internal.R.string.config_wimaxServiceClassname);
- wimaxStateTrackerClassName = mContext.getResources().getString(
+ wimaxStateTrackerClassName = context.getResources().getString(
com.android.internal.R.string.config_wimaxStateTrackerClassname);
log("wimaxJarLocation: " + wimaxJarLocation);
wimaxClassLoader = new DexClassLoader(wimaxJarLocation,
- new ContextWrapper(mContext).getCacheDir().getAbsolutePath(),
+ new ContextWrapper(context).getCacheDir().getAbsolutePath(),
wimaxLibLocation, ClassLoader.getSystemClassLoader());
try {
@@ -601,13 +625,13 @@
Constructor wmxStTrkrConst = wimaxStateTrackerClass.getConstructor
(new Class[] {Context.class, Handler.class});
- wimaxStateTracker = (NetworkStateTracker)wmxStTrkrConst.newInstance(mContext,
- mHandler);
+ wimaxStateTracker = (NetworkStateTracker) wmxStTrkrConst.newInstance(
+ context, trackerHandler);
Constructor wmxSrvConst = wimaxServiceClass.getDeclaredConstructor
(new Class[] {Context.class, wimaxStateTrackerClass});
wmxSrvConst.setAccessible(true);
- IBinder svcInvoker = (IBinder)wmxSrvConst.newInstance(mContext, wimaxStateTracker);
+ IBinder svcInvoker = (IBinder)wmxSrvConst.newInstance(context, wimaxStateTracker);
wmxSrvConst.setAccessible(false);
ServiceManager.addService(WimaxManagerConstants.WIMAX_SERVICE, svcInvoker);
@@ -623,6 +647,7 @@
return wimaxStateTracker;
}
+
/**
* Sets the preferred network.
* @param preference the new preference
@@ -630,7 +655,8 @@
public void setNetworkPreference(int preference) {
enforceChangePermission();
- mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_NETWORK_PREFERENCE, preference, 0));
+ mHandler.sendMessage(
+ mHandler.obtainMessage(EVENT_SET_NETWORK_PREFERENCE, preference, 0));
}
public int getNetworkPreference() {
@@ -923,6 +949,14 @@
return tracker != null && tracker.setRadio(turnOn);
}
+ private INetworkManagementEventObserver mDataActivityObserver = new BaseNetworkObserver() {
+ @Override
+ public void interfaceClassDataActivityChanged(String label, boolean active) {
+ int deviceType = Integer.parseInt(label);
+ sendDataActivityBroadcast(deviceType, active);
+ }
+ };
+
/**
* Used to notice when the calling process dies so we can self-expire
*
@@ -1591,6 +1625,10 @@
int prevNetType = info.getType();
mNetTrackers[prevNetType].setTeardownRequested(false);
+
+ // Remove idletimer previously setup in {@code handleConnect}
+ removeDataActivityTracking(prevNetType);
+
/*
* 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
@@ -1610,6 +1648,7 @@
Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
+ intent.putExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, info.getType());
if (info.isFailover()) {
intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
info.setFailover(false);
@@ -1736,6 +1775,7 @@
private Intent makeGeneralIntent(NetworkInfo info, String bcastType) {
Intent intent = new Intent(bcastType);
intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
+ intent.putExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, info.getType());
if (info.isFailover()) {
intent.putExtra(ConnectivityManager.EXTRA_IS_FAILOVER, true);
info.setFailover(false);
@@ -1759,6 +1799,13 @@
sendStickyBroadcastDelayed(makeGeneralIntent(info, bcastType), delayMs);
}
+ private void sendDataActivityBroadcast(int deviceType, boolean active) {
+ Intent intent = new Intent(ConnectivityManager.ACTION_DATA_ACTIVITY_CHANGE);
+ intent.putExtra(ConnectivityManager.EXTRA_DEVICE_TYPE, deviceType);
+ intent.putExtra(ConnectivityManager.EXTRA_IS_ACTIVE, active);
+ mContext.sendOrderedBroadcast(intent, RECEIVE_DATA_ACTIVITY_CHANGE);
+ }
+
/**
* Called when an attempt to fail over to another network has failed.
* @param info the {@link NetworkInfo} for the failed network
@@ -1779,6 +1826,7 @@
Intent intent = new Intent(ConnectivityManager.CONNECTIVITY_ACTION);
intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
+ intent.putExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, info.getType());
if (getActiveNetworkInfo() == null) {
intent.putExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, true);
}
@@ -1861,9 +1909,12 @@
private void handleConnect(NetworkInfo info) {
final int type = info.getType();
+ setupDataActivityTracking(type);
+
// snapshot isFailover, because sendConnectedBroadcast() resets it
boolean isFailover = info.isFailover();
final NetworkStateTracker thisNet = mNetTrackers[type];
+ final String thisIface = thisNet.getLinkProperties().getInterfaceName();
// if this is a default net and other default is running
// kill the one not preferred
@@ -1922,10 +1973,9 @@
sendConnectedBroadcastDelayed(info, getConnectivityChangeDelay());
// notify battery stats service about this network
- final String iface = thisNet.getLinkProperties().getInterfaceName();
- if (iface != null) {
+ if (thisIface != null) {
try {
- BatteryStatsService.getService().noteNetworkInterfaceType(iface, type);
+ BatteryStatsService.getService().noteNetworkInterfaceType(thisIface, type);
} catch (RemoteException e) {
// ignored; service lives in system_server
}
@@ -1933,6 +1983,58 @@
}
/**
+ * Setup data activity tracking for the given network interface.
+ *
+ * Every {@code setupDataActivityTracking} should be paired with a
+ * {@link removeDataActivityTracking} for cleanup.
+ */
+ private void setupDataActivityTracking(int type) {
+ final NetworkStateTracker thisNet = mNetTrackers[type];
+ final String iface = thisNet.getLinkProperties().getInterfaceName();
+
+ final int timeout;
+
+ if (ConnectivityManager.isNetworkTypeMobile(type)) {
+ timeout = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.DATA_ACTIVITY_TIMEOUT_MOBILE,
+ 0);
+ // Canonicalize mobile network type
+ type = ConnectivityManager.TYPE_MOBILE;
+ } else if (ConnectivityManager.TYPE_WIFI == type) {
+ timeout = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.DATA_ACTIVITY_TIMEOUT_WIFI,
+ 0);
+ } else {
+ // do not track any other networks
+ timeout = 0;
+ }
+
+ if (timeout > 0 && iface != null) {
+ try {
+ mNetd.addIdleTimer(iface, timeout, Integer.toString(type));
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ /**
+ * Remove data activity tracking when network disconnects.
+ */
+ private void removeDataActivityTracking(int type) {
+ final NetworkStateTracker net = mNetTrackers[type];
+ final String iface = net.getLinkProperties().getInterfaceName();
+
+ if (iface != null && (ConnectivityManager.isNetworkTypeMobile(type) ||
+ ConnectivityManager.TYPE_WIFI == type)) {
+ try {
+ // the call fails silently if no idletimer setup for this interface
+ mNetd.removeIdleTimer(iface);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ /**
* After a change in the connectivity state of a network. We're mainly
* concerned with making sure that the list of DNS servers is set up
* according to which networks are connected, and ensuring that the
@@ -2431,8 +2533,8 @@
}
// must be stateless - things change under us.
- private class MyHandler extends Handler {
- public MyHandler(Looper looper) {
+ private class NetworkStateTrackerHandler extends Handler {
+ public NetworkStateTrackerHandler(Looper looper) {
super(looper);
}
@@ -2490,11 +2592,19 @@
// @see bug/4455071
handleConnectivityChange(info.getType(), false);
break;
- case NetworkStateTracker.EVENT_NETWORK_SUBTYPE_CHANGED:
- info = (NetworkInfo) msg.obj;
- type = info.getType();
- updateNetworkSettings(mNetTrackers[type]);
- break;
+ }
+ }
+ }
+
+ private class InternalHandler extends Handler {
+ public InternalHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ NetworkInfo info;
+ switch (msg.what) {
case EVENT_CLEAR_NET_TRANSITION_WAKELOCK:
String causedBy = null;
synchronized (ConnectivityService.this) {
@@ -2907,11 +3017,11 @@
}
}
- private void log(String s) {
+ private static void log(String s) {
Slog.d(TAG, s);
}
- private void loge(String s) {
+ private static void loge(String s) {
Slog.e(TAG, s);
}
diff --git a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
new file mode 100644
index 0000000..93ea6a2
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE;
+import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.ConnectivityManager.TYPE_WIFI;
+import static android.net.ConnectivityManager.getNetworkTypeName;
+import static android.net.NetworkStateTracker.EVENT_STATE_CHANGED;
+import static com.google.testing.littlemock.LittleMock.anyInt;
+import static com.google.testing.littlemock.LittleMock.createCaptor;
+import static com.google.testing.littlemock.LittleMock.doNothing;
+import static com.google.testing.littlemock.LittleMock.doReturn;
+import static com.google.testing.littlemock.LittleMock.doThrow;
+import static com.google.testing.littlemock.LittleMock.eq;
+import static com.google.testing.littlemock.LittleMock.isA;
+import static com.google.testing.littlemock.LittleMock.mock;
+import static com.google.testing.littlemock.LittleMock.reset;
+import static com.google.testing.littlemock.LittleMock.verify;
+
+import android.content.Context;
+import android.net.INetworkPolicyManager;
+import android.net.INetworkStatsService;
+import android.net.LinkProperties;
+import android.net.NetworkConfig;
+import android.net.NetworkInfo;
+import android.net.NetworkInfo.DetailedState;
+import android.net.NetworkStateTracker;
+import android.net.RouteInfo;
+import android.os.Handler;
+import android.os.INetworkManagementService;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.util.Log;
+import android.util.LogPrinter;
+
+import com.google.testing.littlemock.ArgumentCaptor;
+
+import java.net.InetAddress;
+import java.util.concurrent.Future;
+
+/**
+ * Tests for {@link ConnectivityService}.
+ */
+@LargeTest
+public class ConnectivityServiceTest extends AndroidTestCase {
+ private static final String TAG = "ConnectivityServiceTest";
+
+ private static final String MOBILE_IFACE = "rmnet3";
+ private static final String WIFI_IFACE = "wlan6";
+
+ private static final RouteInfo MOBILE_ROUTE_V4 = RouteInfo.makeHostRoute(parse("10.0.0.33"));
+ private static final RouteInfo MOBILE_ROUTE_V6 = RouteInfo.makeHostRoute(parse("fd00::33"));
+
+ private static final RouteInfo WIFI_ROUTE_V4 = RouteInfo.makeHostRoute(
+ parse("192.168.0.66"), parse("192.168.0.1"));
+ private static final RouteInfo WIFI_ROUTE_V6 = RouteInfo.makeHostRoute(
+ parse("fd00::66"), parse("fd00::"));
+
+ private INetworkManagementService mNetManager;
+ private INetworkStatsService mStatsService;
+ private INetworkPolicyManager mPolicyService;
+ private ConnectivityService.NetworkFactory mNetFactory;
+
+ private BroadcastInterceptingContext mServiceContext;
+ private ConnectivityService mService;
+
+ private MockNetwork mMobile;
+ private MockNetwork mWifi;
+
+ private Handler mTrackerHandler;
+
+ private static class MockNetwork {
+ public NetworkStateTracker tracker;
+ public NetworkInfo info;
+ public LinkProperties link;
+
+ public MockNetwork(int type) {
+ tracker = mock(NetworkStateTracker.class);
+ info = new NetworkInfo(type, -1, getNetworkTypeName(type), null);
+ link = new LinkProperties();
+ }
+
+ public void doReturnDefaults() {
+ // TODO: eventually CS should make defensive copies
+ doReturn(new NetworkInfo(info)).when(tracker).getNetworkInfo();
+ doReturn(new LinkProperties(link)).when(tracker).getLinkProperties();
+
+ // fallback to default TCP buffers
+ doReturn("").when(tracker).getTcpBufferSizesPropName();
+ }
+ }
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mServiceContext = new BroadcastInterceptingContext(getContext());
+
+ mNetManager = mock(INetworkManagementService.class);
+ mStatsService = mock(INetworkStatsService.class);
+ mPolicyService = mock(INetworkPolicyManager.class);
+ mNetFactory = mock(ConnectivityService.NetworkFactory.class);
+
+ mMobile = new MockNetwork(TYPE_MOBILE);
+ mWifi = new MockNetwork(TYPE_WIFI);
+
+ // omit most network trackers
+ doThrow(new IllegalArgumentException("Not supported in test environment"))
+ .when(mNetFactory).createTracker(anyInt(), isA(NetworkConfig.class));
+
+ doReturn(mMobile.tracker)
+ .when(mNetFactory).createTracker(eq(TYPE_MOBILE), isA(NetworkConfig.class));
+ doReturn(mWifi.tracker)
+ .when(mNetFactory).createTracker(eq(TYPE_WIFI), isA(NetworkConfig.class));
+
+ final ArgumentCaptor<Handler> trackerHandler = createCaptor();
+ doNothing().when(mMobile.tracker)
+ .startMonitoring(isA(Context.class), trackerHandler.capture());
+
+ mService = new ConnectivityService(
+ mServiceContext, mNetManager, mStatsService, mPolicyService, mNetFactory);
+ mService.systemReady();
+
+ mTrackerHandler = trackerHandler.getValue();
+ mTrackerHandler.getLooper().setMessageLogging(new LogPrinter(Log.INFO, TAG));
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ public void testMobileConnectedAddedRoutes() throws Exception {
+ Future<?> nextConnBroadcast;
+
+ // bring up mobile network
+ mMobile.info.setDetailedState(DetailedState.CONNECTED, null, null);
+ mMobile.link.setInterfaceName(MOBILE_IFACE);
+ mMobile.link.addRoute(MOBILE_ROUTE_V4);
+ mMobile.link.addRoute(MOBILE_ROUTE_V6);
+ mMobile.doReturnDefaults();
+
+ nextConnBroadcast = mServiceContext.nextBroadcastIntent(CONNECTIVITY_ACTION_IMMEDIATE);
+ mTrackerHandler.obtainMessage(EVENT_STATE_CHANGED, mMobile.info).sendToTarget();
+ nextConnBroadcast.get();
+
+ // verify that both routes were added and DNS was flushed
+ verify(mNetManager).addRoute(eq(MOBILE_IFACE), eq(MOBILE_ROUTE_V4));
+ verify(mNetManager).addRoute(eq(MOBILE_IFACE), eq(MOBILE_ROUTE_V6));
+ verify(mNetManager).flushInterfaceDnsCache(MOBILE_IFACE);
+
+ }
+
+ public void testMobileWifiHandoff() throws Exception {
+ Future<?> nextConnBroadcast;
+
+ // bring up mobile network
+ mMobile.info.setDetailedState(DetailedState.CONNECTED, null, null);
+ mMobile.link.setInterfaceName(MOBILE_IFACE);
+ mMobile.link.addRoute(MOBILE_ROUTE_V4);
+ mMobile.link.addRoute(MOBILE_ROUTE_V6);
+ mMobile.doReturnDefaults();
+
+ nextConnBroadcast = mServiceContext.nextBroadcastIntent(CONNECTIVITY_ACTION_IMMEDIATE);
+ mTrackerHandler.obtainMessage(EVENT_STATE_CHANGED, mMobile.info).sendToTarget();
+ nextConnBroadcast.get();
+
+ reset(mNetManager);
+
+ // now bring up wifi network
+ mWifi.info.setDetailedState(DetailedState.CONNECTED, null, null);
+ mWifi.link.setInterfaceName(WIFI_IFACE);
+ mWifi.link.addRoute(WIFI_ROUTE_V4);
+ mWifi.link.addRoute(WIFI_ROUTE_V6);
+ mWifi.doReturnDefaults();
+
+ // expect that mobile will be torn down
+ doReturn(true).when(mMobile.tracker).teardown();
+
+ nextConnBroadcast = mServiceContext.nextBroadcastIntent(CONNECTIVITY_ACTION_IMMEDIATE);
+ mTrackerHandler.obtainMessage(EVENT_STATE_CHANGED, mWifi.info).sendToTarget();
+ nextConnBroadcast.get();
+
+ // verify that wifi routes added, and teardown requested
+ verify(mNetManager).addRoute(eq(WIFI_IFACE), eq(WIFI_ROUTE_V4));
+ verify(mNetManager).addRoute(eq(WIFI_IFACE), eq(WIFI_ROUTE_V6));
+ verify(mNetManager).flushInterfaceDnsCache(WIFI_IFACE);
+ verify(mMobile.tracker).teardown();
+
+ reset(mNetManager, mMobile.tracker);
+
+ // tear down mobile network, as requested
+ mMobile.info.setDetailedState(DetailedState.DISCONNECTED, null, null);
+ mMobile.link.clear();
+ mMobile.doReturnDefaults();
+
+ nextConnBroadcast = mServiceContext.nextBroadcastIntent(CONNECTIVITY_ACTION_IMMEDIATE);
+ mTrackerHandler.obtainMessage(EVENT_STATE_CHANGED, mMobile.info).sendToTarget();
+ nextConnBroadcast.get();
+
+ verify(mNetManager).removeRoute(eq(MOBILE_IFACE), eq(MOBILE_ROUTE_V4));
+ verify(mNetManager).removeRoute(eq(MOBILE_IFACE), eq(MOBILE_ROUTE_V6));
+
+ }
+
+ private static InetAddress parse(String addr) {
+ return InetAddress.parseNumericAddress(addr);
+ }
+}