Merge "NsdService: bootstrap unit tests"
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 5edc7c6..1da0d28 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -20,6 +20,7 @@
 import android.os.Parcelable;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.BitUtils;
 
 import java.util.Objects;
 
@@ -292,7 +293,7 @@
      * @hide
      */
     public int[] getCapabilities() {
-        return enumerateBits(mNetworkCapabilities);
+        return BitUtils.unpackBits(mNetworkCapabilities);
     }
 
     /**
@@ -308,19 +309,6 @@
         return ((mNetworkCapabilities & (1 << capability)) != 0);
     }
 
-    private int[] enumerateBits(long val) {
-        int size = Long.bitCount(val);
-        int[] result = new int[size];
-        int index = 0;
-        int resource = 0;
-        while (val > 0) {
-            if ((val & 1) == 1) result[index++] = resource;
-            val = val >> 1;
-            resource++;
-        }
-        return result;
-    }
-
     private void combineNetCapabilities(NetworkCapabilities nc) {
         this.mNetworkCapabilities |= nc.mNetworkCapabilities;
     }
@@ -433,6 +421,15 @@
     private static final int MIN_TRANSPORT = TRANSPORT_CELLULAR;
     private static final int MAX_TRANSPORT = TRANSPORT_WIFI_AWARE;
 
+    private static final String[] TRANSPORT_NAMES = {
+        "CELLULAR",
+        "WIFI",
+        "BLUETOOTH",
+        "ETHERNET",
+        "VPN",
+        "WIFI_AWARE"
+    };
+
     /**
      * Adds the given transport type to this {@code NetworkCapability} instance.
      * Multiple transports may be applied sequentially.  Note that when searching
@@ -479,7 +476,7 @@
      * @hide
      */
     public int[] getTransportTypes() {
-        return enumerateBits(mTransportTypes);
+        return BitUtils.unpackBits(mTransportTypes);
     }
 
     /**
@@ -886,18 +883,23 @@
      * @hide
      */
     public static String transportNamesOf(int[] types) {
-        String transports = "";
-        for (int i = 0; i < types.length;) {
-            switch (types[i]) {
-                case TRANSPORT_CELLULAR:    transports += "CELLULAR"; break;
-                case TRANSPORT_WIFI:        transports += "WIFI"; break;
-                case TRANSPORT_BLUETOOTH:   transports += "BLUETOOTH"; break;
-                case TRANSPORT_ETHERNET:    transports += "ETHERNET"; break;
-                case TRANSPORT_VPN:         transports += "VPN"; break;
-                case TRANSPORT_WIFI_AWARE:  transports += "WIFI_AWARE"; break;
-            }
-            if (++i < types.length) transports += "|";
+        if (types == null || types.length == 0) {
+            return "";
         }
-        return transports;
+        StringBuilder transports = new StringBuilder();
+        for (int t : types) {
+            transports.append("|").append(transportNameOf(t));
+        }
+        return transports.substring(1);
+    }
+
+    /**
+     * @hide
+     */
+    public static String transportNameOf(int transport) {
+        if (transport < 0 || TRANSPORT_NAMES.length <= transport) {
+            return "UNKNOWN";
+        }
+        return TRANSPORT_NAMES[transport];
     }
 }
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 616a8c0..88bc54d 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -49,7 +49,6 @@
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
-import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.net.ConnectivityManager;
 import android.net.ConnectivityManager.PacketKeepalive;
@@ -70,6 +69,7 @@
 import android.net.NetworkMisc;
 import android.net.NetworkQuotaInfo;
 import android.net.NetworkRequest;
+import android.net.NetworkSpecifier;
 import android.net.NetworkState;
 import android.net.NetworkUtils;
 import android.net.Proxy;
@@ -110,7 +110,6 @@
 import android.util.LocalLog;
 import android.util.LocalLog.ReadOnlyLocalLog;
 import android.util.Log;
-import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
@@ -4131,6 +4130,18 @@
                 0, 0, thresholds);
     }
 
+    private void ensureValidNetworkSpecifier(NetworkCapabilities nc) {
+        if (nc == null) {
+            return;
+        }
+        NetworkSpecifier ns = nc.getNetworkSpecifier();
+        if (ns == null) {
+            return;
+        }
+        MatchAllNetworkSpecifier.checkNotMatchAllNetworkSpecifier(ns);
+        ns.assertValidFromUid(Binder.getCallingUid());
+    }
+
     @Override
     public NetworkRequest requestNetwork(NetworkCapabilities networkCapabilities,
             Messenger messenger, int timeoutMs, IBinder binder, int legacyType) {
@@ -4156,9 +4167,7 @@
         if (timeoutMs < 0) {
             throw new IllegalArgumentException("Bad timeout specified");
         }
-
-        MatchAllNetworkSpecifier.checkNotMatchAllNetworkSpecifier(
-                networkCapabilities.getNetworkSpecifier());
+        ensureValidNetworkSpecifier(networkCapabilities);
 
         NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, legacyType,
                 nextNetworkRequestId(), type);
@@ -4230,9 +4239,7 @@
         enforceNetworkRequestPermissions(networkCapabilities);
         enforceMeteredApnPolicy(networkCapabilities);
         ensureRequestableCapabilities(networkCapabilities);
-
-        MatchAllNetworkSpecifier.checkNotMatchAllNetworkSpecifier(
-                networkCapabilities.getNetworkSpecifier());
+        ensureValidNetworkSpecifier(networkCapabilities);
 
         NetworkRequest networkRequest = new NetworkRequest(networkCapabilities, TYPE_NONE,
                 nextNetworkRequestId(), NetworkRequest.Type.REQUEST);
@@ -4294,9 +4301,7 @@
             // can't request networks.
             nc.addCapability(NET_CAPABILITY_FOREGROUND);
         }
-
-        MatchAllNetworkSpecifier.checkNotMatchAllNetworkSpecifier(
-                networkCapabilities.getNetworkSpecifier());
+        ensureValidNetworkSpecifier(networkCapabilities);
 
         NetworkRequest networkRequest = new NetworkRequest(nc, TYPE_NONE, nextNetworkRequestId(),
                 NetworkRequest.Type.LISTEN);
@@ -4314,9 +4319,7 @@
         if (!hasWifiNetworkListenPermission(networkCapabilities)) {
             enforceAccessPermission();
         }
-
-        MatchAllNetworkSpecifier.checkNotMatchAllNetworkSpecifier(
-                networkCapabilities.getNetworkSpecifier());
+        ensureValidNetworkSpecifier(networkCapabilities);
 
         NetworkRequest networkRequest = new NetworkRequest(
                 new NetworkCapabilities(networkCapabilities), TYPE_NONE, nextNetworkRequestId(),
diff --git a/services/net/java/android/net/apf/ApfCapabilities.java b/services/net/java/android/net/apf/ApfCapabilities.java
index b0e0230..703b415 100644
--- a/services/net/java/android/net/apf/ApfCapabilities.java
+++ b/services/net/java/android/net/apf/ApfCapabilities.java
@@ -46,7 +46,7 @@
     }
 
     public String toString() {
-        return String.format("%s{version: %d, maxSize: %d format: %d}", getClass().getSimpleName(),
+        return String.format("%s{version: %d, maxSize: %d, format: %d}", getClass().getSimpleName(),
                 apfVersionSupported, maximumApfProgramSize, apfPacketFormat);
     }
 }
diff --git a/tests/net/java/com/android/internal/util/BitUtilsTest.java b/tests/net/java/com/android/internal/util/BitUtilsTest.java
new file mode 100644
index 0000000..0ad8a21
--- /dev/null
+++ b/tests/net/java/com/android/internal/util/BitUtilsTest.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2017 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.internal.util;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import java.nio.ByteBuffer;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.assertEquals;
+import static com.android.internal.util.BitUtils.*;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BitUtilsTest {
+
+    @Test
+    public void testUnsignedByteWideningConversions() {
+        byte b0 = 0;
+        byte b1 = 1;
+        byte bm1 = -1;
+        assertEquals(0, uint8(b0));
+        assertEquals(1, uint8(b1));
+        assertEquals(127, uint8(Byte.MAX_VALUE));
+        assertEquals(128, uint8(Byte.MIN_VALUE));
+        assertEquals(255, uint8(bm1));
+        assertEquals(255, uint8((byte)255));
+    }
+
+    @Test
+    public void testUnsignedShortWideningConversions() {
+        short s0 = 0;
+        short s1 = 1;
+        short sm1 = -1;
+        assertEquals(0, uint16(s0));
+        assertEquals(1, uint16(s1));
+        assertEquals(32767, uint16(Short.MAX_VALUE));
+        assertEquals(32768, uint16(Short.MIN_VALUE));
+        assertEquals(65535, uint16(sm1));
+        assertEquals(65535, uint16((short)65535));
+    }
+
+    @Test
+    public void testUnsignedIntWideningConversions() {
+        assertEquals(0, uint32(0));
+        assertEquals(1, uint32(1));
+        assertEquals(2147483647L, uint32(Integer.MAX_VALUE));
+        assertEquals(2147483648L, uint32(Integer.MIN_VALUE));
+        assertEquals(4294967295L, uint32(-1));
+        assertEquals(4294967295L, uint32((int)4294967295L));
+    }
+
+    @Test
+    public void testBytesToInt() {
+        assertEquals(0x00000000, bytesToBEInt(bytes(0, 0, 0, 0)));
+        assertEquals(0xffffffff, bytesToBEInt(bytes(255, 255, 255, 255)));
+        assertEquals(0x0a000001, bytesToBEInt(bytes(10, 0, 0, 1)));
+        assertEquals(0x0a000002, bytesToBEInt(bytes(10, 0, 0, 2)));
+        assertEquals(0x0a001fff, bytesToBEInt(bytes(10, 0, 31, 255)));
+        assertEquals(0xe0000001, bytesToBEInt(bytes(224, 0, 0, 1)));
+
+        assertEquals(0x00000000, bytesToLEInt(bytes(0, 0, 0, 0)));
+        assertEquals(0x01020304, bytesToLEInt(bytes(4, 3, 2, 1)));
+        assertEquals(0xffff0000, bytesToLEInt(bytes(0, 0, 255, 255)));
+    }
+
+    @Test
+    public void testUnsignedGetters() {
+      ByteBuffer b = ByteBuffer.allocate(4);
+      b.putInt(0xffff);
+
+      assertEquals(0x0, getUint8(b, 0));
+      assertEquals(0x0, getUint8(b, 1));
+      assertEquals(0xff, getUint8(b, 2));
+      assertEquals(0xff, getUint8(b, 3));
+
+      assertEquals(0x0, getUint16(b, 0));
+      assertEquals(0xffff, getUint16(b, 2));
+
+      b.rewind();
+      b.putInt(0xffffffff);
+      assertEquals(0xffffffffL, getUint32(b, 0));
+    }
+
+    static byte[] bytes(int b1, int b2, int b3, int b4) {
+        return new byte[] {b(b1), b(b2), b(b3), b(b4)};
+    }
+
+    static byte b(int i) {
+        return (byte) i;
+    }
+}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 1be8d5e..5173278 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -1993,6 +1993,40 @@
     }
 
     @SmallTest
+    public void testNetworkSpecifierUidSpoofSecurityException() {
+        class UidAwareNetworkSpecifier extends NetworkSpecifier implements Parcelable {
+            @Override
+            public boolean satisfiedBy(NetworkSpecifier other) {
+                return true;
+            }
+
+            @Override
+            public void assertValidFromUid(int requestorUid) {
+                throw new SecurityException("failure");
+            }
+
+            @Override
+            public int describeContents() { return 0; }
+            @Override
+            public void writeToParcel(Parcel dest, int flags) {}
+        }
+
+        mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+        mWiFiNetworkAgent.connect(false);
+
+        UidAwareNetworkSpecifier networkSpecifier = new UidAwareNetworkSpecifier();
+        NetworkRequest networkRequest = newWifiRequestBuilder().setNetworkSpecifier(
+                networkSpecifier).build();
+        TestNetworkCallback networkCallback = new TestNetworkCallback();
+        try {
+            mCm.requestNetwork(networkRequest, networkCallback);
+            fail("Network request with spoofed UID did not throw a SecurityException");
+        } catch (SecurityException e) {
+            // expected
+        }
+    }
+
+    @SmallTest
     public void testRegisterDefaultNetworkCallback() throws Exception {
         final TestNetworkCallback defaultNetworkCallback = new TestNetworkCallback();
         mCm.registerDefaultNetworkCallback(defaultNetworkCallback);