Merge "Allow wifi and ethenet connected at same time."
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index f2e9078..8333b81 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -26,7 +26,6 @@
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
@@ -3801,8 +3800,9 @@
private void unsupportedStartingFrom(int version) {
if (Process.myUid() == Process.SYSTEM_UID) {
- // The getApplicationInfo() call we make below is not supported in system context, and
- // we want to allow the system to use these APIs anyway.
+ // The getApplicationInfo() call we make below is not supported in system context. Let
+ // the call through here, and rely on the fact that ConnectivityService will refuse to
+ // allow the system to use these APIs anyway.
return;
}
@@ -3819,11 +3819,6 @@
// functions by accessing ConnectivityService directly. However, it should be clear that doing
// so is unsupported and may break in the future. http://b/22728205
private void checkLegacyRoutingApiAccess() {
- if (mContext.checkCallingOrSelfPermission("com.android.permission.INJECT_OMADM_SETTINGS")
- == PackageManager.PERMISSION_GRANTED) {
- return;
- }
-
unsupportedStartingFrom(VERSION_CODES.M);
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 2c5327b..953c99f 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -57,6 +57,7 @@
import android.net.ConnectivityManager.PacketKeepalive;
import android.net.IConnectivityManager;
import android.net.IIpConnectivityMetrics;
+import android.net.INetd;
import android.net.INetdEventCallback;
import android.net.INetworkManagementEventObserver;
import android.net.INetworkPolicyListener;
@@ -88,6 +89,7 @@
import android.net.metrics.NetworkEvent;
import android.net.netlink.InetDiagMessage;
import android.net.util.MultinetworkPolicyTracker;
+import android.net.util.NetdService;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
@@ -259,7 +261,8 @@
// 0 is full bad, 100 is full good
private int mDefaultInetConditionPublished = 0;
- private INetworkManagementService mNetd;
+ private INetworkManagementService mNMS;
+ private INetd mNetd;
private INetworkStatsService mStatsService;
private INetworkPolicyManager mPolicyManager;
private NetworkPolicyManagerInternal mPolicyManagerInternal;
@@ -765,7 +768,7 @@
mLingerDelayMs = mSystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS);
mContext = checkNotNull(context, "missing Context");
- mNetd = checkNotNull(netManager, "missing INetworkManagementService");
+ mNMS = checkNotNull(netManager, "missing INetworkManagementService");
mStatsService = checkNotNull(statsService, "missing INetworkStatsService");
mPolicyManager = checkNotNull(policyManager, "missing INetworkPolicyManager");
mPolicyManagerInternal = checkNotNull(
@@ -773,6 +776,7 @@
"missing NetworkPolicyManagerInternal");
mProxyTracker = new ProxyTracker(context, mHandler, EVENT_PROXY_HAS_CHANGED);
+ mNetd = NetdService.getInstance();
mKeyStore = KeyStore.getInstance();
mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
@@ -855,7 +859,7 @@
mTethering = makeTethering();
- mPermissionMonitor = new PermissionMonitor(mContext, mNetd);
+ mPermissionMonitor = new PermissionMonitor(mContext, mNMS);
//set up the listener for user state for creating user VPNs
IntentFilter intentFilter = new IntentFilter();
@@ -870,8 +874,8 @@
new IntentFilter(Intent.ACTION_USER_PRESENT), null, null);
try {
- mNetd.registerObserver(mTethering);
- mNetd.registerObserver(mDataActivityObserver);
+ mNMS.registerObserver(mTethering);
+ mNMS.registerObserver(mDataActivityObserver);
} catch (RemoteException e) {
loge("Error registering observer :" + e);
}
@@ -902,7 +906,7 @@
mMultipathPolicyTracker = new MultipathPolicyTracker(mContext, mHandler);
- mDnsManager = new DnsManager(mContext, mNetd, mSystemProperties);
+ mDnsManager = new DnsManager(mContext, mNMS, mSystemProperties);
registerPrivateDnsSettingsCallbacks();
}
@@ -918,7 +922,7 @@
return mDefaultRequest;
}
};
- return new Tethering(mContext, mNetd, mStatsService, mPolicyManager,
+ return new Tethering(mContext, mNMS, mStatsService, mPolicyManager,
IoThread.get().getLooper(), new MockableSystemProperties(),
deps);
}
@@ -1495,6 +1499,20 @@
};
/**
+ * Ensures that the system cannot call a particular method.
+ */
+ private boolean disallowedBecauseSystemCaller() {
+ // TODO: start throwing a SecurityException when GnssLocationProvider stops calling
+ // requestRouteToHost.
+ if (isSystem(Binder.getCallingUid())) {
+ log("This method exists only for app backwards compatibility"
+ + " and must not be called by system services.");
+ return true;
+ }
+ return false;
+ }
+
+ /**
* 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
@@ -1505,6 +1523,9 @@
*/
@Override
public boolean requestRouteToHostAddress(int networkType, byte[] hostAddress) {
+ if (disallowedBecauseSystemCaller()) {
+ return false;
+ }
enforceChangePermission();
if (mProtectedNetworks.contains(networkType)) {
enforceConnectivityInternalPermission();
@@ -1582,7 +1603,7 @@
if (DBG) log("Adding legacy route " + bestRoute +
" for UID/PID " + uid + "/" + Binder.getCallingPid());
try {
- mNetd.addLegacyRouteForNetId(netId, bestRoute, uid);
+ mNMS.addLegacyRouteForNetId(netId, bestRoute, uid);
} catch (Exception e) {
// never crash - catch them all
if (DBG) loge("Exception trying to add a route: " + e);
@@ -1872,7 +1893,7 @@
if (timeout > 0 && iface != null && type != ConnectivityManager.TYPE_NONE) {
try {
- mNetd.addIdleTimer(iface, timeout, type);
+ mNMS.addIdleTimer(iface, timeout, type);
} catch (Exception e) {
// You shall not crash!
loge("Exception in setupDataActivityTracking " + e);
@@ -1891,7 +1912,7 @@
caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))) {
try {
// the call fails silently if no idle timer setup for this interface
- mNetd.removeIdleTimer(iface);
+ mNMS.removeIdleTimer(iface);
} catch (Exception e) {
loge("Exception in removeDataActivityTracking " + e);
}
@@ -1899,6 +1920,18 @@
}
/**
+ * Update data activity tracking when network state is updated.
+ */
+ private void updateDataActivityTracking(NetworkAgentInfo newNetwork,
+ NetworkAgentInfo oldNetwork) {
+ if (newNetwork != null) {
+ setupDataActivityTracking(newNetwork);
+ }
+ if (oldNetwork != null) {
+ removeDataActivityTracking(oldNetwork);
+ }
+ }
+ /**
* Reads the network specific MTU size from resources.
* and set it on it's iface.
*/
@@ -1926,7 +1959,7 @@
try {
if (VDBG) log("Setting MTU size: " + iface + ", " + mtu);
- mNetd.setMtu(iface, mtu);
+ mNMS.setMtu(iface, mtu);
} catch (Exception e) {
Slog.e(TAG, "exception in setMtu()" + e);
}
@@ -2580,7 +2613,7 @@
}
nai.clearLingerState();
if (nai.isSatisfyingRequest(mDefaultRequest.requestId)) {
- removeDataActivityTracking(nai);
+ updateDataActivityTracking(null /* newNetwork */, nai);
notifyLockdownVpn(nai);
ensureNetworkTransitionWakelock(nai.name());
}
@@ -2600,7 +2633,7 @@
// NetworkFactories, so network traffic isn't interrupted for an unnecessarily
// long time.
try {
- mNetd.removeNetwork(nai.network.netId);
+ mNMS.removeNetwork(nai.network.netId);
} catch (Exception e) {
loge("Exception removing network: " + e);
}
@@ -2798,20 +2831,6 @@
}
}
- // TODO: remove this code once we know that the Slog.wtf is never hit.
- //
- // Find all networks that are satisfying this request and remove the request
- // from their request lists.
- // TODO - it's my understanding that for a request there is only a single
- // network satisfying it, so this loop is wasteful
- for (NetworkAgentInfo otherNai : mNetworkAgentInfos.values()) {
- if (otherNai.isSatisfyingRequest(nri.request.requestId) && otherNai != nai) {
- Slog.wtf(TAG, "Request " + nri.request + " satisfied by " +
- otherNai.name() + ", but mNetworkAgentInfos says " +
- (nai != null ? nai.name() : "null"));
- }
- }
-
// Maintain the illusion. When this request arrived, we might have pretended
// that a network connected to serve it, even though the network was already
// connected. Now that this request has gone away, we might have to pretend
@@ -3779,7 +3798,7 @@
Slog.w(TAG, "VPN for user " + user + " not ready yet. Skipping lockdown");
return false;
}
- setLockdownTracker(new LockdownVpnTracker(mContext, mNetd, this, vpn, profile));
+ setLockdownTracker(new LockdownVpnTracker(mContext, mNMS, this, vpn, profile));
} else {
setLockdownTracker(null);
}
@@ -4034,7 +4053,7 @@
loge("Starting user already has a VPN");
return;
}
- userVpn = new Vpn(mHandler.getLooper(), mContext, mNetd, userId);
+ userVpn = new Vpn(mHandler.getLooper(), mContext, mNMS, userId);
mVpns.put(userId, userVpn);
if (mUserManager.getUserInfo(userId).isPrimary() && LockdownVpnTracker.isEnabled()) {
updateLockdownVpn();
@@ -4655,7 +4674,7 @@
mDnsManager.updatePrivateDnsStatus(netId, newLp);
// Start or stop clat accordingly to network state.
- networkAgent.updateClat(mNetd);
+ networkAgent.updateClat(mNMS);
if (isDefaultNetwork(networkAgent)) {
handleApplyDefaultProxy(newLp.getHttpProxy());
} else {
@@ -4694,9 +4713,9 @@
final String prefix = "iface:" + iface;
try {
if (add) {
- mNetd.getNetdService().wakeupAddInterface(iface, prefix, mark, mask);
+ mNetd.wakeupAddInterface(iface, prefix, mark, mask);
} else {
- mNetd.getNetdService().wakeupDelInterface(iface, prefix, mark, mask);
+ mNetd.wakeupDelInterface(iface, prefix, mark, mask);
}
} catch (Exception e) {
loge("Exception modifying wakeup packet monitoring: " + e);
@@ -4712,7 +4731,7 @@
for (String iface : interfaceDiff.added) {
try {
if (DBG) log("Adding iface " + iface + " to network " + netId);
- mNetd.addInterfaceToNetwork(iface, netId);
+ mNMS.addInterfaceToNetwork(iface, netId);
wakeupModifyInterface(iface, caps, true);
} catch (Exception e) {
loge("Exception adding interface: " + e);
@@ -4722,7 +4741,7 @@
try {
if (DBG) log("Removing iface " + iface + " from network " + netId);
wakeupModifyInterface(iface, caps, false);
- mNetd.removeInterfaceFromNetwork(iface, netId);
+ mNMS.removeInterfaceFromNetwork(iface, netId);
} catch (Exception e) {
loge("Exception removing interface: " + e);
}
@@ -4746,7 +4765,7 @@
if (route.hasGateway()) continue;
if (VDBG) log("Adding Route [" + route + "] to network " + netId);
try {
- mNetd.addRoute(netId, route);
+ mNMS.addRoute(netId, route);
} catch (Exception e) {
if ((route.getDestination().getAddress() instanceof Inet4Address) || VDBG) {
loge("Exception in addRoute for non-gateway: " + e);
@@ -4757,7 +4776,7 @@
if (route.hasGateway() == false) continue;
if (VDBG) log("Adding Route [" + route + "] to network " + netId);
try {
- mNetd.addRoute(netId, route);
+ mNMS.addRoute(netId, route);
} catch (Exception e) {
if ((route.getGateway() instanceof Inet4Address) || VDBG) {
loge("Exception in addRoute for gateway: " + e);
@@ -4768,7 +4787,7 @@
for (RouteInfo route : routeDiff.removed) {
if (VDBG) log("Removing Route [" + route + "] from network " + netId);
try {
- mNetd.removeRoute(netId, route);
+ mNMS.removeRoute(netId, route);
} catch (Exception e) {
loge("Exception in removeRoute: " + e);
}
@@ -4880,7 +4899,7 @@
final String newPermission = getNetworkPermission(newNc);
if (!Objects.equals(oldPermission, newPermission) && nai.created && !nai.isVPN()) {
try {
- mNetd.setNetworkPermission(nai.network.netId, newPermission);
+ mNMS.setNetworkPermission(nai.network.netId, newPermission);
} catch (RemoteException e) {
loge("Exception in setNetworkPermission: " + e);
}
@@ -4940,12 +4959,12 @@
if (!newRanges.isEmpty()) {
final UidRange[] addedRangesArray = new UidRange[newRanges.size()];
newRanges.toArray(addedRangesArray);
- mNetd.addVpnUidRanges(nai.network.netId, addedRangesArray);
+ mNMS.addVpnUidRanges(nai.network.netId, addedRangesArray);
}
if (!prevRanges.isEmpty()) {
final UidRange[] removedRangesArray = new UidRange[prevRanges.size()];
prevRanges.toArray(removedRangesArray);
- mNetd.removeVpnUidRanges(nai.network.netId, removedRangesArray);
+ mNMS.removeVpnUidRanges(nai.network.netId, removedRangesArray);
}
} catch (Exception e) {
// Never crash!
@@ -5114,9 +5133,9 @@
private void makeDefault(NetworkAgentInfo newNetwork) {
if (DBG) log("Switching to new default network: " + newNetwork);
- setupDataActivityTracking(newNetwork);
+
try {
- mNetd.setDefaultNetId(newNetwork.network.netId);
+ mNMS.setDefaultNetId(newNetwork.network.netId);
} catch (Exception e) {
loge("Exception setting default network :" + e);
}
@@ -5289,6 +5308,7 @@
}
}
if (isNewDefault) {
+ updateDataActivityTracking(newNetwork, oldDefaultNetwork);
// Notify system services that this network is up.
makeDefault(newNetwork);
// Log 0 -> X and Y -> X default network transitions, where X is the new default.
@@ -5511,12 +5531,12 @@
try {
// This should never fail. Specifying an already in use NetID will cause failure.
if (networkAgent.isVPN()) {
- mNetd.createVirtualNetwork(networkAgent.network.netId,
+ mNMS.createVirtualNetwork(networkAgent.network.netId,
!networkAgent.linkProperties.getDnsServers().isEmpty(),
(networkAgent.networkMisc == null ||
!networkAgent.networkMisc.allowBypass));
} else {
- mNetd.createPhysicalNetwork(networkAgent.network.netId,
+ mNMS.createPhysicalNetwork(networkAgent.network.netId,
getNetworkPermission(networkAgent.networkCapabilities));
}
} catch (Exception e) {
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 7437805..1c77fcc 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -4536,4 +4536,78 @@
mCellNetworkAgent.disconnect();
mCm.unregisterNetworkCallback(networkCallback);
}
+
+ @Test
+ public void testDataActivityTracking() throws RemoteException {
+ final TestNetworkCallback networkCallback = new TestNetworkCallback();
+ final NetworkRequest networkRequest = new NetworkRequest.Builder()
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .build();
+ mCm.registerNetworkCallback(networkRequest, networkCallback);
+
+ mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ final LinkProperties cellLp = new LinkProperties();
+ cellLp.setInterfaceName(MOBILE_IFNAME);
+ mCellNetworkAgent.sendLinkProperties(cellLp);
+ reset(mNetworkManagementService);
+ mCellNetworkAgent.connect(true);
+ networkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ verify(mNetworkManagementService, times(1)).addIdleTimer(eq(MOBILE_IFNAME), anyInt(),
+ eq(ConnectivityManager.TYPE_MOBILE));
+
+ mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ final LinkProperties wifiLp = new LinkProperties();
+ wifiLp.setInterfaceName(WIFI_IFNAME);
+ mWiFiNetworkAgent.sendLinkProperties(wifiLp);
+
+ // Network switch
+ reset(mNetworkManagementService);
+ mWiFiNetworkAgent.connect(true);
+ networkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ networkCallback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+ networkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
+ verify(mNetworkManagementService, times(1)).addIdleTimer(eq(WIFI_IFNAME), anyInt(),
+ eq(ConnectivityManager.TYPE_WIFI));
+ verify(mNetworkManagementService, times(1)).removeIdleTimer(eq(MOBILE_IFNAME));
+
+ // Disconnect wifi and switch back to cell
+ reset(mNetworkManagementService);
+ mWiFiNetworkAgent.disconnect();
+ networkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
+ assertNoCallbacks(networkCallback);
+ verify(mNetworkManagementService, times(1)).removeIdleTimer(eq(WIFI_IFNAME));
+ verify(mNetworkManagementService, times(1)).addIdleTimer(eq(MOBILE_IFNAME), anyInt(),
+ eq(ConnectivityManager.TYPE_MOBILE));
+
+ // reconnect wifi
+ mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ wifiLp.setInterfaceName(WIFI_IFNAME);
+ mWiFiNetworkAgent.sendLinkProperties(wifiLp);
+ mWiFiNetworkAgent.connect(true);
+ networkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+ networkCallback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+ networkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
+
+ // Disconnect cell
+ reset(mNetworkManagementService);
+ mCellNetworkAgent.disconnect();
+ networkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
+ // LOST callback is triggered earlier than removing idle timer. Broadcast should also be
+ // sent as network being switched. Ensure rule removal for cell will not be triggered
+ // unexpectedly before network being removed.
+ waitForIdle();
+ verify(mNetworkManagementService, times(0)).removeIdleTimer(eq(MOBILE_IFNAME));
+ verify(mNetworkManagementService, times(1)).removeNetwork(
+ eq(mCellNetworkAgent.getNetwork().netId));
+
+ // Disconnect wifi
+ ConditionVariable cv = waitForConnectivityBroadcasts(1);
+ reset(mNetworkManagementService);
+ mWiFiNetworkAgent.disconnect();
+ waitFor(cv);
+ verify(mNetworkManagementService, times(1)).removeIdleTimer(eq(WIFI_IFNAME));
+
+ // Clean up
+ mCm.unregisterNetworkCallback(networkCallback);
+ }
}