diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index fbe2b9db4a..0626415af6 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -2333,6 +2333,18 @@ public class ConnectivityService extends IConnectivityManager.Stub if (VDBG || (DBG && nri.isRequest())) log("releasing NetworkRequest " + request); nri.unlinkDeathRecipient(); mNetworkRequests.remove(request); + synchronized (mUidToNetworkRequestCount) { + int requests = mUidToNetworkRequestCount.get(nri.mUid, 0); + if (requests < 1) { + Slog.wtf(TAG, "BUG: too small request count " + requests + " for UID " + + nri.mUid); + } else if (requests == 1) { + mUidToNetworkRequestCount.removeAt( + mUidToNetworkRequestCount.indexOfKey(nri.mUid)); + } else { + mUidToNetworkRequestCount.put(nri.mUid, requests - 1); + } + } mNetworkRequestInfoLogs.log("RELEASE " + nri); if (nri.isRequest()) { // Find all networks that are satisfying this request and remove the request @@ -3680,6 +3692,11 @@ public class ConnectivityService extends IConnectivityManager.Stub private final HashMap mNetworkRequests = new HashMap(); + private static final int MAX_NETWORK_REQUESTS_PER_UID = 100; + // Map from UID to number of NetworkRequests that UID has filed. + @GuardedBy("mUidToNetworkRequestCount") + private final SparseIntArray mUidToNetworkRequestCount = new SparseIntArray(); + private static class NetworkFactoryInfo { public final String name; public final Messenger messenger; @@ -3740,6 +3757,7 @@ public class ConnectivityService extends IConnectivityManager.Stub mPid = getCallingPid(); mUid = getCallingUid(); mType = type; + enforceRequestCountLimit(); } NetworkRequestInfo(Messenger m, NetworkRequest r, IBinder binder, NetworkRequestType type) { @@ -3751,6 +3769,7 @@ public class ConnectivityService extends IConnectivityManager.Stub mUid = getCallingUid(); mType = type; mPendingIntent = null; + enforceRequestCountLimit(); try { mBinder.linkToDeath(this, 0); @@ -3759,6 +3778,16 @@ public class ConnectivityService extends IConnectivityManager.Stub } } + private void enforceRequestCountLimit() { + synchronized (mUidToNetworkRequestCount) { + int networkRequests = mUidToNetworkRequestCount.get(mUid, 0) + 1; + if (networkRequests >= MAX_NETWORK_REQUESTS_PER_UID) { + throw new IllegalArgumentException("Too many NetworkRequests filed"); + } + mUidToNetworkRequestCount.put(mUid, networkRequests); + } + } + private String typeString() { switch (mType) { case LISTEN: return "Listen"; diff --git a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java index 26eed24c4f..4fae4a732a 100644 --- a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java @@ -74,6 +74,7 @@ import com.android.server.connectivity.NetworkMonitor.CaptivePortalProbeResult; import com.android.server.net.NetworkPinner; import java.net.InetAddress; +import java.util.ArrayList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; @@ -1788,4 +1789,96 @@ public class ConnectivityServiceTest extends AndroidTestCase { waitFor(cv); assertPinnedToWifiWithCellDefault(); } + + @SmallTest + public void testNetworkRequestMaximum() { + final int MAX_REQUESTS = 100; + // Test that the limit is enforced when MAX_REQUESTS simultaneous requests are added. + NetworkRequest networkRequest = new NetworkRequest.Builder().build(); + ArrayList networkCallbacks = new ArrayList(); + try { + for (int i = 0; i < MAX_REQUESTS; i++) { + NetworkCallback networkCallback = new NetworkCallback(); + mCm.requestNetwork(networkRequest, networkCallback); + networkCallbacks.add(networkCallback); + } + fail("Registering " + MAX_REQUESTS + " NetworkRequests did not throw exception"); + } catch (IllegalArgumentException expected) {} + for (NetworkCallback networkCallback : networkCallbacks) { + mCm.unregisterNetworkCallback(networkCallback); + } + networkCallbacks.clear(); + + try { + for (int i = 0; i < MAX_REQUESTS; i++) { + NetworkCallback networkCallback = new NetworkCallback(); + mCm.registerNetworkCallback(networkRequest, networkCallback); + networkCallbacks.add(networkCallback); + } + fail("Registering " + MAX_REQUESTS + " NetworkCallbacks did not throw exception"); + } catch (IllegalArgumentException expected) {} + for (NetworkCallback networkCallback : networkCallbacks) { + mCm.unregisterNetworkCallback(networkCallback); + } + networkCallbacks.clear(); + + ArrayList pendingIntents = new ArrayList(); + try { + for (int i = 0; i < MAX_REQUESTS + 1; i++) { + PendingIntent pendingIntent = + PendingIntent.getBroadcast(mContext, 0, new Intent("a" + i), 0); + mCm.requestNetwork(networkRequest, pendingIntent); + pendingIntents.add(pendingIntent); + } + fail("Registering " + MAX_REQUESTS + + " PendingIntent NetworkRequests did not throw exception"); + } catch (IllegalArgumentException expected) {} + for (PendingIntent pendingIntent : pendingIntents) { + mCm.unregisterNetworkCallback(pendingIntent); + } + pendingIntents.clear(); + + try { + for (int i = 0; i < MAX_REQUESTS + 1; i++) { + PendingIntent pendingIntent = + PendingIntent.getBroadcast(mContext, 0, new Intent("a" + i), 0); + mCm.registerNetworkCallback(networkRequest, pendingIntent); + pendingIntents.add(pendingIntent); + } + fail("Registering " + MAX_REQUESTS + + " PendingIntent NetworkCallbacks did not throw exception"); + } catch (IllegalArgumentException expected) {} + for (PendingIntent pendingIntent : pendingIntents) { + mCm.unregisterNetworkCallback(pendingIntent); + } + pendingIntents.clear(); + mService.waitForIdle(5000); + + // Test that the limit is not hit when MAX_REQUESTS requests are added and removed. + for (int i = 0; i < MAX_REQUESTS; i++) { + NetworkCallback networkCallback = new NetworkCallback(); + mCm.requestNetwork(networkRequest, networkCallback); + mCm.unregisterNetworkCallback(networkCallback); + } + mService.waitForIdle(); + for (int i = 0; i < MAX_REQUESTS; i++) { + NetworkCallback networkCallback = new NetworkCallback(); + mCm.registerNetworkCallback(networkRequest, networkCallback); + mCm.unregisterNetworkCallback(networkCallback); + } + mService.waitForIdle(); + for (int i = 0; i < MAX_REQUESTS; i++) { + PendingIntent pendingIntent = + PendingIntent.getBroadcast(mContext, 0, new Intent("b" + i), 0); + mCm.requestNetwork(networkRequest, pendingIntent); + mCm.unregisterNetworkCallback(pendingIntent); + } + mService.waitForIdle(); + for (int i = 0; i < MAX_REQUESTS; i++) { + PendingIntent pendingIntent = + PendingIntent.getBroadcast(mContext, 0, new Intent("c" + i), 0); + mCm.registerNetworkCallback(networkRequest, pendingIntent); + mCm.unregisterNetworkCallback(pendingIntent); + } + } }