Merge "Map available JCA algorithms and availability in javadoc"
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ObservableTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ObservableTest.java
index b13a876..fcd9e2f 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ObservableTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ObservableTest.java
@@ -20,6 +20,7 @@
 import java.util.Observable;
 import java.util.Observer;
 import java.util.Vector;
+import java.util.concurrent.atomic.AtomicReference;
 
 public class ObservableTest extends junit.framework.TestCase {
 
@@ -229,6 +230,24 @@
                 .elementAt(0).equals(obj));
     }
 
+    static final class AlwaysChangedObservable extends Observable {
+        @Override
+        public boolean hasChanged() {
+            return true;
+        }
+    }
+
+    // http://b/28797950
+    public void test_observableWithOverridenHasChanged() throws Exception {
+        final AtomicReference<Observable> updated = new AtomicReference<>();
+        final Observer observer = (observable1, data) -> updated.set(observable1);
+
+        Observable alwaysChanging = new AlwaysChangedObservable();
+        alwaysChanging.addObserver(observer);
+        alwaysChanging.notifyObservers(null);
+        assertSame(alwaysChanging, updated.get());
+    }
+
     /**
      * Sets up the fixture, for example, open a network connection. This method
      * is called before a test is executed.
diff --git a/luni/src/main/java/libcore/io/ForwardingOs.java b/luni/src/main/java/libcore/io/ForwardingOs.java
index fbf8939..854e34e 100644
--- a/luni/src/main/java/libcore/io/ForwardingOs.java
+++ b/luni/src/main/java/libcore/io/ForwardingOs.java
@@ -99,8 +99,10 @@
     public int getxattr(String path, String name, byte[] outValue) throws ErrnoException { return os.getxattr(path, name, outValue); }
     public String if_indextoname(int index) { return os.if_indextoname(index); }
     public InetAddress inet_pton(int family, String address) { return os.inet_pton(family, address); }
+    public int ioctlFlags(FileDescriptor fd, String interfaceName) throws ErrnoException { return os.ioctlFlags(fd, interfaceName); };
     public InetAddress ioctlInetAddress(FileDescriptor fd, int cmd, String interfaceName) throws ErrnoException { return os.ioctlInetAddress(fd, cmd, interfaceName); }
     public int ioctlInt(FileDescriptor fd, int cmd, MutableInt arg) throws ErrnoException { return os.ioctlInt(fd, cmd, arg); }
+    public int ioctlMTU(FileDescriptor fd, String interfaceName) throws ErrnoException { return os.ioctlMTU(fd, interfaceName); };
     public boolean isatty(FileDescriptor fd) { return os.isatty(fd); }
     public void kill(int pid, int signal) throws ErrnoException { os.kill(pid, signal); }
     public void lchown(String path, int uid, int gid) throws ErrnoException { os.lchown(path, uid, gid); }
diff --git a/luni/src/main/java/libcore/io/Os.java b/luni/src/main/java/libcore/io/Os.java
index 006a29e..3f6d2b1 100644
--- a/luni/src/main/java/libcore/io/Os.java
+++ b/luni/src/main/java/libcore/io/Os.java
@@ -91,8 +91,10 @@
     public int getxattr(String path, String name, byte[] outValue) throws ErrnoException;
     public String if_indextoname(int index);
     public InetAddress inet_pton(int family, String address);
+    public int ioctlFlags(FileDescriptor fd, String interfaceName) throws ErrnoException;
     public InetAddress ioctlInetAddress(FileDescriptor fd, int cmd, String interfaceName) throws ErrnoException;
     public int ioctlInt(FileDescriptor fd, int cmd, MutableInt arg) throws ErrnoException;
+    public int ioctlMTU(FileDescriptor fd, String interfaceName) throws ErrnoException;
     public boolean isatty(FileDescriptor fd);
     public void kill(int pid, int signal) throws ErrnoException;
     public void lchown(String path, int uid, int gid) throws ErrnoException;
diff --git a/luni/src/main/java/libcore/io/Posix.java b/luni/src/main/java/libcore/io/Posix.java
index a341641..46f33e5 100644
--- a/luni/src/main/java/libcore/io/Posix.java
+++ b/luni/src/main/java/libcore/io/Posix.java
@@ -93,8 +93,10 @@
     public native int getxattr(String path, String name, byte[] outValue) throws ErrnoException;
     public native String if_indextoname(int index);
     public native InetAddress inet_pton(int family, String address);
+    public native int ioctlFlags(FileDescriptor fd, String interfaceName) throws ErrnoException;
     public native InetAddress ioctlInetAddress(FileDescriptor fd, int cmd, String interfaceName) throws ErrnoException;
     public native int ioctlInt(FileDescriptor fd, int cmd, MutableInt arg) throws ErrnoException;
+    public native int ioctlMTU(FileDescriptor fd, String interfaceName) throws ErrnoException;
     public native boolean isatty(FileDescriptor fd);
     public native void kill(int pid, int signal) throws ErrnoException;
     public native void lchown(String path, int uid, int gid) throws ErrnoException;
diff --git a/luni/src/main/native/libcore_io_Posix.cpp b/luni/src/main/native/libcore_io_Posix.cpp
index 5d7e42d..3c8d7ce 100644
--- a/luni/src/main/native/libcore_io_Posix.cpp
+++ b/luni/src/main/native/libcore_io_Posix.cpp
@@ -1210,6 +1210,16 @@
     return sockaddrToInetAddress(env, ss, NULL);
 }
 
+static jint Posix_ioctlFlags(JNIEnv* env, jobject, jobject javaFd, jstring javaInterfaceName) {
+     struct ifreq req;
+     if (!fillIfreq(env, javaInterfaceName, req)) {
+        return 0;
+     }
+     int fd = jniGetFDFromFileDescriptor(env, javaFd);
+     throwIfMinusOne(env, "ioctl", TEMP_FAILURE_RETRY(ioctl(fd, SIOCGIFFLAGS, &req)));
+     return req.ifr_flags;
+}
+
 static jobject Posix_ioctlInetAddress(JNIEnv* env, jobject, jobject javaFd, jint cmd, jstring javaInterfaceName) {
     struct ifreq req;
     if (!fillIfreq(env, javaInterfaceName, req)) {
@@ -1236,6 +1246,16 @@
     return rc;
 }
 
+static jint Posix_ioctlMTU(JNIEnv* env, jobject, jobject javaFd, jstring javaInterfaceName) {
+     struct ifreq req;
+     if (!fillIfreq(env, javaInterfaceName, req)) {
+        return 0;
+     }
+     int fd = jniGetFDFromFileDescriptor(env, javaFd);
+     throwIfMinusOne(env, "ioctl", TEMP_FAILURE_RETRY(ioctl(fd, SIOCGIFMTU, &req)));
+     return req.ifr_mtu;
+}
+
 static jboolean Posix_isatty(JNIEnv* env, jobject, jobject javaFd) {
     int fd = jniGetFDFromFileDescriptor(env, javaFd);
     return TEMP_FAILURE_RETRY(isatty(fd)) == 1;
@@ -2010,8 +2030,10 @@
     NATIVE_METHOD(Posix, getxattr, "(Ljava/lang/String;Ljava/lang/String;[B)I"),
     NATIVE_METHOD(Posix, if_indextoname, "(I)Ljava/lang/String;"),
     NATIVE_METHOD(Posix, inet_pton, "(ILjava/lang/String;)Ljava/net/InetAddress;"),
+    NATIVE_METHOD(Posix, ioctlFlags, "(Ljava/io/FileDescriptor;Ljava/lang/String;)I"),
     NATIVE_METHOD(Posix, ioctlInetAddress, "(Ljava/io/FileDescriptor;ILjava/lang/String;)Ljava/net/InetAddress;"),
     NATIVE_METHOD(Posix, ioctlInt, "(Ljava/io/FileDescriptor;ILandroid/util/MutableInt;)I"),
+    NATIVE_METHOD(Posix, ioctlMTU, "(Ljava/io/FileDescriptor;Ljava/lang/String;)I"),
     NATIVE_METHOD(Posix, isatty, "(Ljava/io/FileDescriptor;)Z"),
     NATIVE_METHOD(Posix, kill, "(II)V"),
     NATIVE_METHOD(Posix, lchown, "(Ljava/lang/String;II)V"),
diff --git a/luni/src/test/java/libcore/java/lang/reflect/MethodTest.java b/luni/src/test/java/libcore/java/lang/reflect/MethodTest.java
index a3f9065..49b3ad1 100644
--- a/luni/src/test/java/libcore/java/lang/reflect/MethodTest.java
+++ b/luni/src/test/java/libcore/java/lang/reflect/MethodTest.java
@@ -16,7 +16,10 @@
 
 package libcore.java.lang.reflect;
 
+import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Proxy;
 
 import junit.framework.TestCase;
 
@@ -226,14 +229,320 @@
         private void a() {}
         public static void b() {}
     }
-    public static interface InterfaceA {
+    public interface InterfaceA {
         void a();
     }
     public static abstract class Sub extends Super implements InterfaceA {
     }
 
-    public static interface InterfaceB extends InterfaceA {}
-    public static interface InterfaceC extends InterfaceB {}
+    public interface InterfaceB extends InterfaceA {}
+    public interface InterfaceC extends InterfaceB {}
     public static abstract class ImplementsC implements InterfaceC {}
     public static abstract class ExtendsImplementsC extends ImplementsC {}
+
+    // Static interface method reflection.
+
+    public interface InterfaceWithStatic {
+        static String staticMethod() {
+            return identifyCaller();
+        }
+    }
+
+    public void testStaticInterfaceMethod_getMethod() throws Exception {
+        Method method = InterfaceWithStatic.class.getMethod("staticMethod");
+        assertFalse(method.isDefault());
+        assertEquals(Modifier.PUBLIC | Modifier.STATIC, method.getModifiers());
+        assertEquals(InterfaceWithStatic.class, method.getDeclaringClass());
+    }
+
+    public void testStaticInterfaceMethod_getDeclaredMethod() throws Exception {
+        Method declaredMethod = InterfaceWithStatic.class.getDeclaredMethod("staticMethod");
+        assertFalse(declaredMethod.isDefault());
+        assertEquals(Modifier.PUBLIC | Modifier.STATIC, declaredMethod.getModifiers());
+        assertEquals(InterfaceWithStatic.class, declaredMethod.getDeclaringClass());
+    }
+
+    public void testStaticInterfaceMethod_invoke() throws Exception {
+        String interfaceWithStaticClassName = InterfaceWithStatic.class.getName();
+        assertEquals(interfaceWithStaticClassName, InterfaceWithStatic.staticMethod());
+
+        Method method = InterfaceWithStatic.class.getMethod("staticMethod");
+        assertEquals(interfaceWithStaticClassName, method.invoke(null));
+        assertEquals(interfaceWithStaticClassName, method.invoke(new InterfaceWithStatic() {}));
+    }
+
+    public void testStaticInterfaceMethod_setAccessible() throws Exception {
+        String interfaceWithStaticClassName = InterfaceWithStatic.class.getName();
+        Method method = InterfaceWithStatic.class.getMethod("staticMethod");
+        method.setAccessible(false);
+        // No effect expected.
+        assertEquals(interfaceWithStaticClassName, method.invoke(null));
+    }
+
+    // Default method reflection.
+
+    public interface InterfaceWithDefault {
+        default String defaultMethod() {
+            return identifyCaller();
+        }
+    }
+
+    public static class ImplementationWithDefault implements InterfaceWithDefault {
+    }
+
+    public void testDefaultMethod_getDeclaredMethod_interface() throws Exception {
+        Class<InterfaceWithDefault> interfaceWithDefaultClass = InterfaceWithDefault.class;
+        Method defaultMethod = interfaceWithDefaultClass.getDeclaredMethod("defaultMethod");
+        assertEquals(InterfaceWithDefault.class, defaultMethod.getDeclaringClass());
+        assertTrue(defaultMethod.isDefault());
+    }
+
+    public void testDefaultMethod_inheritance() throws Exception {
+        Class<InterfaceWithDefault> interfaceWithDefaultClass = InterfaceWithDefault.class;
+        String interfaceWithDefaultClassName = interfaceWithDefaultClass.getName();
+        Method defaultMethod = interfaceWithDefaultClass.getDeclaredMethod("defaultMethod");
+
+        InterfaceWithDefault anon = new InterfaceWithDefault() {};
+        Class<?> anonClass = anon.getClass();
+        Method inheritedDefaultMethod = anonClass.getMethod("defaultMethod");
+        assertEquals(inheritedDefaultMethod, defaultMethod);
+
+        // Check invocation behavior.
+        assertEquals(interfaceWithDefaultClassName, defaultMethod.invoke(anon));
+        assertEquals(interfaceWithDefaultClassName, inheritedDefaultMethod.invoke(anon));
+        assertEquals(interfaceWithDefaultClassName, anon.defaultMethod());
+
+        // Check other method properties.
+        assertEquals(InterfaceWithDefault.class, inheritedDefaultMethod.getDeclaringClass());
+        assertTrue(inheritedDefaultMethod.isDefault());
+
+        // Confirm the method is not considered declared on the anonymous class.
+        assertNull(getDeclaredMethodOrNull(anonClass, "defaultMethod"));
+    }
+
+    public void testDefaultMethod_override() throws Exception {
+        Class<InterfaceWithDefault> interfaceWithDefaultClass = InterfaceWithDefault.class;
+        Method defaultMethod = interfaceWithDefaultClass.getDeclaredMethod("defaultMethod");
+
+        InterfaceWithDefault anon = new InterfaceWithDefault() {
+            @Override public String defaultMethod() {
+                return identifyCaller();
+            }
+        };
+
+        Class<? extends InterfaceWithDefault> anonClass = anon.getClass();
+        String anonymousClassName = anonClass.getName();
+
+        Method overriddenDefaultMethod = getDeclaredMethodOrNull(anonClass, "defaultMethod");
+        assertNotNull(overriddenDefaultMethod);
+        assertFalse(overriddenDefaultMethod.equals(defaultMethod));
+
+        // Check invocation behavior.
+        assertEquals(anonymousClassName, defaultMethod.invoke(anon));
+        assertEquals(anonymousClassName, overriddenDefaultMethod.invoke(anon));
+        assertEquals(anonymousClassName, anon.defaultMethod());
+
+        // Check other method properties.
+        assertEquals(anonClass, overriddenDefaultMethod.getDeclaringClass());
+        assertFalse(overriddenDefaultMethod.isDefault());
+    }
+
+    public void testDefaultMethod_setAccessible() throws Exception {
+        InterfaceWithDefault anon = new InterfaceWithDefault() {};
+
+        Method defaultMethod = anon.getClass().getMethod("defaultMethod");
+        defaultMethod.setAccessible(false);
+        // setAccessible(false) should have no effect.
+        assertEquals(InterfaceWithDefault.class.getName(), defaultMethod.invoke(anon));
+
+        InterfaceWithDefault anon2 = new InterfaceWithDefault() {
+            @Override public String defaultMethod() {
+                return identifyCaller();
+            }
+        };
+
+        Class<? extends InterfaceWithDefault> anon2Class = anon2.getClass();
+        Method overriddenDefaultMethod = anon2Class.getDeclaredMethod("defaultMethod");
+        overriddenDefaultMethod.setAccessible(false);
+        // setAccessible(false) should have no effect.
+        assertEquals(anon2Class.getName(), overriddenDefaultMethod.invoke(anon2));
+    }
+
+    interface InterfaceWithReAbstractedMethod extends InterfaceWithDefault {
+        // Re-abstract a default method.
+        @Override String defaultMethod();
+    }
+
+    public void testDefaultMethod_reabstracted() throws Exception {
+        Class<InterfaceWithReAbstractedMethod> subclass = InterfaceWithReAbstractedMethod.class;
+
+        Method reabstractedDefaultMethod = subclass.getMethod("defaultMethod");
+        assertFalse(reabstractedDefaultMethod.isDefault());
+        assertEquals(reabstractedDefaultMethod, subclass.getDeclaredMethod("defaultMethod"));
+        assertEquals(subclass, reabstractedDefaultMethod.getDeclaringClass());
+    }
+
+    public void testDefaultMethod_reimplementedInClass() throws Exception {
+        InterfaceWithDefault impl = new InterfaceWithReAbstractedMethod() {
+            // Implement a reabstracted default method.
+            @Override public String defaultMethod() {
+                return identifyCaller();
+            }
+        };
+        Class<?> implClass = impl.getClass();
+        String implClassName = implClass.getName();
+
+        Method implClassDefaultMethod = getDeclaredMethodOrNull(implClass, "defaultMethod");
+        assertEquals(implClassDefaultMethod, implClass.getMethod("defaultMethod"));
+
+        // Check invocation behavior.
+        assertEquals(implClassName, impl.defaultMethod());
+        assertEquals(implClassName, implClassDefaultMethod.invoke(impl));
+
+        // Check other method properties.
+        assertEquals(implClass, implClassDefaultMethod.getDeclaringClass());
+        assertFalse(implClassDefaultMethod.isDefault());
+    }
+
+    interface InterfaceWithRedefinedMethods extends InterfaceWithReAbstractedMethod {
+        // Reimplement an abstracted default method.
+        @Override default String defaultMethod() {
+            return identifyCaller();
+        }
+    }
+
+    public void testDefaultMethod_reimplementInInterface() throws Exception {
+        Class<?> interfaceClass = InterfaceWithRedefinedMethods.class;
+        String interfaceClassName = interfaceClass.getName();
+
+        // NOTE: The line below defines an anonymous class that implements
+        // InterfaceWithReDefinedMethods (and does not need to provide any declarations).
+        // See the {}.
+        InterfaceWithDefault impl = new InterfaceWithRedefinedMethods() {};
+        Class<?> implClass = impl.getClass();
+
+        Method implClassDefaultMethod = implClass.getMethod("defaultMethod");
+        assertNull(getDeclaredMethodOrNull(implClass, "defaultMethod"));
+
+        // Check invocation behavior.
+        assertEquals(interfaceClassName, impl.defaultMethod());
+        assertEquals(interfaceClassName, implClassDefaultMethod.invoke(impl));
+
+        // Check other method properties.
+        assertEquals(interfaceClass, implClassDefaultMethod.getDeclaringClass());
+        assertTrue(implClassDefaultMethod.isDefault());
+    }
+
+    public void testDefaultMethod_invoke() throws Exception {
+        InterfaceWithDefault impl1 = new InterfaceWithRedefinedMethods() {};
+        InterfaceWithDefault impl2 = new InterfaceWithReAbstractedMethod() {
+            @Override public String defaultMethod() {
+                return identifyCaller();
+            }
+        };
+        InterfaceWithDefault impl3 = new InterfaceWithDefault() {};
+
+        Class[] classes = {
+            InterfaceWithRedefinedMethods.class,
+            impl1.getClass(),
+            InterfaceWithReAbstractedMethod.class,
+            impl2.getClass(),
+            InterfaceWithDefault.class,
+            impl3.getClass(),
+        };
+        Object[] instances = { impl1, impl2, impl3 };
+
+        // Attempt to invoke all declarations of defaultMethod() on a selection of instances.
+        for (Class<?> clazz : classes) {
+            Method method = clazz.getMethod("defaultMethod");
+            for (Object instance : instances) {
+                if (method.getDeclaringClass().isAssignableFrom(instance.getClass())) {
+                    Method trueMethod = instance.getClass().getMethod("defaultMethod");
+                    // All implementations of defaultMethod return the class where the method is
+                    // declared, enabling us to tell if the correct implementation has been called.
+                    Class<?> declaringClass = trueMethod.getDeclaringClass();
+                    assertEquals(declaringClass.getName(), method.invoke(instance));
+                } else {
+                    try {
+                        method.invoke(instance);
+                        fail();
+                    } catch (IllegalArgumentException expected) {
+                    }
+                }
+            }
+        }
+    }
+
+    interface OtherInterfaceWithDefault {
+        default String defaultMethod() {
+            return identifyCaller();
+        }
+    }
+
+    public void testDefaultMethod_superSyntax() throws Exception {
+        class ImplementationSuperUser implements InterfaceWithDefault, OtherInterfaceWithDefault {
+            @Override public String defaultMethod() {
+                return identifyCaller() + ":" +
+                        InterfaceWithDefault.super.defaultMethod() + ":" +
+                        OtherInterfaceWithDefault.super.defaultMethod();
+            }
+        }
+
+        String implementationSuperUserClassName = ImplementationSuperUser.class.getName();
+        String interfaceWithDefaultClassName = InterfaceWithDefault.class.getName();
+        String otherInterfaceWithDefaultClassName = OtherInterfaceWithDefault.class.getName();
+        String expectedReturnValue = implementationSuperUserClassName + ":" +
+            interfaceWithDefaultClassName + ":" + otherInterfaceWithDefaultClassName;
+        ImplementationSuperUser obj = new ImplementationSuperUser();
+        assertEquals(expectedReturnValue, obj.defaultMethod());
+
+        Method defaultMethod = ImplementationSuperUser.class.getMethod("defaultMethod");
+        assertEquals(expectedReturnValue, defaultMethod.invoke(obj));
+    }
+
+    public void testProxyWithDefaultMethods() throws Exception {
+        InvocationHandler invocationHandler = new InvocationHandler() {
+            @Override public Object invoke(Object proxy, Method method, Object[] args)
+                    throws Throwable {
+                assertSame(InterfaceWithDefault.class, method.getDeclaringClass());
+                return identifyCaller();
+            }
+        };
+
+        InterfaceWithDefault proxyWithDefaultMethod = (InterfaceWithDefault) Proxy.newProxyInstance(
+                Thread.currentThread().getContextClassLoader(),
+                new Class[] { InterfaceWithDefault.class },
+                invocationHandler);
+        String invocationHandlerClassName = invocationHandler.getClass().getName();
+
+        // Check the proxy implements the default method.
+        Class<? extends InterfaceWithDefault> proxyClass = proxyWithDefaultMethod.getClass();
+        Method defaultMethod = proxyClass.getMethod("defaultMethod");
+        assertEquals(proxyClass, defaultMethod.getDeclaringClass());
+        assertFalse(defaultMethod.isDefault());
+
+        // The default method is intercepted like anything else.
+        assertEquals(invocationHandlerClassName, proxyWithDefaultMethod.defaultMethod());
+    }
+
+    private static Method getDeclaredMethodOrNull(Class<?> clazz, String methodName) {
+        try {
+            Method m = clazz.getDeclaredMethod(methodName);
+            assertNotNull(m);
+            return m;
+        } catch (NoSuchMethodException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Keep this package-protected or public to avoid the introduction of synthetic methods that
+     * throw off the offset.
+     */
+    static String identifyCaller() {
+        StackTraceElement[] stack = Thread.currentThread().getStackTrace();
+        int i = 0;
+        while (!stack[i++].getMethodName().equals("identifyCaller")) {}
+        return stack[i].getClassName();
+    }
 }
diff --git a/luni/src/test/java/libcore/java/nio/channels/MembershipKeyTest.java b/luni/src/test/java/libcore/java/nio/channels/MembershipKeyTest.java
new file mode 100644
index 0000000..18dc615
--- /dev/null
+++ b/luni/src/test/java/libcore/java/nio/channels/MembershipKeyTest.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2016 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 libcore.java.nio.channels;
+
+import junit.framework.TestCase;
+
+import java.io.IOException;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.net.StandardProtocolFamily;
+import java.net.StandardSocketOptions;
+import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+import java.nio.channels.DatagramChannel;
+import java.nio.channels.MembershipKey;
+import java.util.Enumeration;
+
+public class MembershipKeyTest extends TestCase {
+
+    private MembershipKey key;
+    private MembershipKey keyWithSource;
+    private final int PORT = 5000;
+    private final String TEST_MESSAGE = "hello";
+    private DatagramChannel client;
+    private InetAddress sourceAddress = Inet4Address.LOOPBACK;
+    private final static InetAddress MULTICAST_ADDRESS = getMulticastAddress();
+    private final static NetworkInterface NETWORK_INTERFACE = getNetworkInterface();
+
+    private void setup(boolean withSource) throws Exception {
+        client = DatagramChannel.open(StandardProtocolFamily.INET)
+                .bind(new InetSocketAddress(Inet4Address.ANY, PORT));
+        client.configureBlocking(false);
+
+        if (withSource) {
+            keyWithSource = client.join(MULTICAST_ADDRESS, NETWORK_INTERFACE, sourceAddress);
+        } else {
+            key = client.join(MULTICAST_ADDRESS, NETWORK_INTERFACE);
+        }
+    }
+
+    @Override
+    public void tearDown() throws IOException {
+        client.close();
+        key = null;
+    }
+
+    public void test_isValid_OnChannelCloseWithJoinWithoutSource() throws Exception {
+        setup(false);
+        test_isValid();
+    }
+
+    public void test_isValid_OnChannelCloseWithJoinWithSource() throws Exception {
+        setup(true);
+        test_isValid();
+    }
+
+    private void test_isValid() throws IOException {
+        assertTrue(key.isValid());
+        client.close();
+        assertFalse(key.isValid());
+    }
+
+    public void test_isValid_OnDropJoinWithoutSource() throws Exception {
+        setup(false);
+        test_isValid_OnDrop();
+    }
+
+    public void test_isValid_OnDropJoinWithSource() throws Exception {
+        setup(true);
+        test_isValid_OnDrop();
+    }
+
+    private void test_isValid_OnDrop() {
+        assertTrue(key.isValid());
+        key.drop();
+        assertFalse(key.isValid());
+    }
+
+    public void test_dropWithJoinWithoutSource() throws Exception {
+        setup(false);
+        test_drop();
+    }
+
+    public void test_dropWithJoinWithSource() throws Exception {
+        setup(true);
+        test_drop();
+    }
+
+    private void test_drop() throws IOException {
+        key.drop();
+        try(DatagramChannel dc = DatagramChannel.open(StandardProtocolFamily.INET)) {
+            assertEquals(TEST_MESSAGE.length(), dc
+                    .bind(new InetSocketAddress(Inet4Address.LOOPBACK, 0))
+                    .send(ByteBuffer.wrap(TEST_MESSAGE.getBytes()),
+                            new InetSocketAddress(MULTICAST_ADDRESS, PORT)));
+        }
+
+        ByteBuffer buffer = ByteBuffer.allocate(1048);
+        client.receive(buffer);
+        buffer.flip();
+        assertEquals(0, buffer.limit());
+    }
+
+    public void test_networkInterface() throws Exception {
+        setup(false);
+        assertEquals(NETWORK_INTERFACE, key.networkInterface());
+        client.close();
+        assertEquals(NETWORK_INTERFACE, key.networkInterface());
+    }
+
+    public void test_sourceAddressWithJoinWithSource() throws Exception {
+        setup(true);
+        assertEquals(sourceAddress, key.sourceAddress());
+    }
+
+    public void test_sourceAddressWithJoinWithoutSource() throws Exception {
+        setup(false);
+        assertNull(key.sourceAddress());
+    }
+
+    public void test_groupWithJoinWithSource() throws Exception {
+        setup(true);
+        assertEquals(MULTICAST_ADDRESS, key.group());
+    }
+
+    public void test_groupWithoutJoinWIthSource() throws Exception {
+        setup(false);
+        assertEquals(MULTICAST_ADDRESS, key.group());
+    }
+
+    public void test_channelWithJoinWithSource() throws Exception {
+        setup(true);
+        assertEquals(client, key.channel());
+        key.drop();
+        assertEquals(client, key.channel());
+    }
+
+    public void test_channelWithJoinWithoutSource() throws Exception {
+        setup(false);
+        assertEquals(client, key.channel());
+        key.drop();
+        assertEquals(client, key.channel());
+    }
+
+    public void test_blockWithJoinWithSource() throws Exception {
+        setup(true);
+        try {
+            key.block(sourceAddress);
+            fail();
+        } catch (IllegalStateException expected) {}
+    }
+
+    public void test_blockWithJoinWithoutSource() throws Exception {
+        setup(false);
+        key.block(sourceAddress);
+
+        try (DatagramChannel dc = DatagramChannel.open(StandardProtocolFamily.INET)) {
+            assertEquals(TEST_MESSAGE.length(), dc
+                    .bind(new InetSocketAddress(Inet4Address.LOOPBACK, 0))
+                    .send(ByteBuffer.wrap(TEST_MESSAGE.getBytes()),
+                            new InetSocketAddress(MULTICAST_ADDRESS, PORT)));
+        }
+
+        ByteBuffer buffer = ByteBuffer.allocate(1048);
+        client.receive(buffer);
+        buffer.flip();
+        assertEquals(0, buffer.limit());
+    }
+
+    public void test_block_Exception () throws Exception {
+        setup(false);
+
+        // Blocking a multicast channel
+        try {
+            key.block(Inet4Address.getByName("224.0.0.10"));
+        } catch (IllegalArgumentException expected) {}
+
+        // Different address type than the group
+        try {
+            key.block(Inet6Address.LOOPBACK);
+        } catch (IllegalArgumentException expected) {}
+
+        key.drop();
+        try {
+            key.block(sourceAddress);
+        } catch (IllegalStateException expected) {}
+    }
+
+    public void test_unblockWithJoinWithSource() throws Exception {
+        setup(true);
+        try {
+            key.unblock(Inet4Address.getByName("127.0.0.2"));
+        } catch (IllegalStateException expected) {}
+    }
+
+    public void test_unblockWithJoinWithoutSource() throws Exception {
+        setup(false);
+
+        key.block(sourceAddress);
+        key.unblock(sourceAddress);
+
+        try (DatagramChannel dc = DatagramChannel.open(StandardProtocolFamily.INET)) {
+            assertEquals(TEST_MESSAGE.length(), dc
+                    .bind(new InetSocketAddress(Inet4Address.LOOPBACK, 0))
+                    .setOption(StandardSocketOptions.IP_MULTICAST_LOOP, true /* enable loop */)
+                    .send(ByteBuffer.wrap(TEST_MESSAGE.getBytes()),
+                            new InetSocketAddress(MULTICAST_ADDRESS, PORT)));
+        }
+
+        ByteBuffer buffer = ByteBuffer.allocate(1048);
+        client.receive(buffer);
+        buffer.flip();
+        int limits = buffer.limit();
+        byte bytes[] = new byte[limits];
+        buffer.get(bytes, 0, limits);
+        String receivedMessage = new String(bytes);
+        assertEquals(TEST_MESSAGE, receivedMessage);
+    }
+
+    public void test_unblock_Exception() throws Exception {
+        setup(false);
+        try {
+            key.unblock(sourceAddress);
+        } catch (IllegalStateException expected) {}
+
+        key.drop();
+
+        try {
+            key.unblock(sourceAddress);
+        } catch (IllegalStateException expected) {}
+    }
+
+    private static InetAddress getMulticastAddress() {
+        try {
+            return InetAddress.getByName("239.255.0.1");
+        } catch (UnknownHostException exception) {
+            throw new RuntimeException(exception);
+        }
+    }
+
+    private static NetworkInterface getNetworkInterface() {
+        try {
+            return NetworkInterface.getByName("lo");
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/ojluni/src/main/java/java/net/NetworkInterface.java b/ojluni/src/main/java/java/net/NetworkInterface.java
index edae4b3..3ea67be 100755
--- a/ojluni/src/main/java/java/net/NetworkInterface.java
+++ b/ojluni/src/main/java/java/net/NetworkInterface.java
@@ -25,11 +25,19 @@
 
 package java.net;
 
+import android.system.ErrnoException;
+
+import java.io.FileDescriptor;
 import java.util.Enumeration;
 import java.util.NoSuchElementException;
+
+import libcore.io.IoUtils;
+import libcore.io.Libcore;
 import sun.security.action.*;
 import java.security.AccessController;
 
+import static android.system.OsConstants.*;
+
 /**
  * This class represents a Network Interface made up of a name,
  * and a list of IP addresses assigned to this interface.
@@ -374,7 +382,7 @@
      */
 
     public boolean isUp() throws SocketException {
-        return isUp0(name, index);
+        return (getFlags() & IFF_UP) != 0;
     }
 
     /**
@@ -386,7 +394,7 @@
      */
 
     public boolean isLoopback() throws SocketException {
-        return isLoopback0(name, index);
+        return (getFlags() & IFF_LOOPBACK) != 0;
     }
 
     /**
@@ -401,7 +409,7 @@
      */
 
     public boolean isPointToPoint() throws SocketException {
-        return isP2P0(name, index);
+        return (getFlags() & IFF_POINTOPOINT) != 0;
     }
 
     /**
@@ -413,7 +421,7 @@
      */
 
     public boolean supportsMulticast() throws SocketException {
-        return supportsMulticast0(name, index);
+        return (getFlags() & IFF_MULTICAST) != 0;
     }
 
     /**
@@ -442,7 +450,17 @@
      * @since 1.6
      */
     public int getMTU() throws SocketException {
-        return getMTU0(name, index);
+        FileDescriptor fd = null;
+        try {
+            fd = Libcore.os.socket(AF_INET, SOCK_DGRAM, 0);
+            return Libcore.os.ioctlMTU(fd, name);
+        } catch (ErrnoException e) {
+            throw e.rethrowAsSocketException();
+        } catch (Exception ex) {
+            throw new SocketException(ex);
+        } finally {
+            IoUtils.closeQuietly(fd);
+        }
     }
 
     /**
@@ -462,11 +480,19 @@
         return virtual;
     }
 
-    private native static boolean isUp0(String name, int ind) throws SocketException;
-    private native static boolean isLoopback0(String name, int ind) throws SocketException;
-    private native static boolean supportsMulticast0(String name, int ind) throws SocketException;
-    private native static boolean isP2P0(String name, int ind) throws SocketException;
-    private native static int getMTU0(String name, int ind) throws SocketException;
+    private int getFlags() throws SocketException {
+        FileDescriptor fd = null;
+        try {
+            fd = Libcore.os.socket(AF_INET, SOCK_DGRAM, 0);
+            return Libcore.os.ioctlFlags(fd, name);
+        } catch (ErrnoException e) {
+            throw e.rethrowAsSocketException();
+        } catch (Exception ex) {
+            throw new SocketException(ex);
+        } finally {
+            IoUtils.closeQuietly(fd);
+        }
+    }
 
     /**
      * Compares this object against the specified object.
diff --git a/ojluni/src/main/java/java/util/Observable.java b/ojluni/src/main/java/java/util/Observable.java
index 48c6c13..1d8be3b 100755
--- a/ojluni/src/main/java/java/util/Observable.java
+++ b/ojluni/src/main/java/java/util/Observable.java
@@ -138,18 +138,19 @@
 
         synchronized (this) {
             /* We don't want the Observer doing callbacks into
-             * arbitrary code while holding its own Monitor.
+             * arbitrary Observables while holding its own Monitor.
              * The code where we extract each Observable from
-             * the Vector and store the state of the Observer
+             * the ArrayList and store the state of the Observer
              * needs synchronization, but notifying observers
              * does not (should not).  The worst result of any
              * potential race-condition here is that:
+             *
              * 1) a newly-added Observer will miss a
              *   notification in progress
              * 2) a recently unregistered Observer will be
              *   wrongly notified when it doesn't care
              */
-            if (!changed)
+            if (!hasChanged())
                 return;
 
             arrLocal = observers.toArray(new Observer[observers.size()]);
diff --git a/ojluni/src/main/native/NetworkInterface.c b/ojluni/src/main/native/NetworkInterface.c
index 48450e1..ad89692 100755
--- a/ojluni/src/main/native/NetworkInterface.c
+++ b/ojluni/src/main/native/NetworkInterface.c
@@ -108,7 +108,6 @@
 
 /** Private methods declarations **/
 static jobject createNetworkInterface(JNIEnv *env, netif *ifs);
-static int     getFlags0(JNIEnv *env, jstring  ifname);
 
 static netif  *enumInterfaces(JNIEnv *env);
 
@@ -116,12 +115,10 @@
 static void    freeif(netif *ifs);
 
 static int     openSocket(JNIEnv *env, int proto);
-static int     openSocketWithFallback(JNIEnv *env, const char *ifname);
 
 static int     getIndex(int sock, const char *ifname);
 
 static int     getFlags(int sock, const char *ifname, int *flags);
-static int     getMTU(JNIEnv *env, int sock, const char *ifname);
 
 /******************* Java entry points *****************************/
 
@@ -382,109 +379,8 @@
     return netIFArr;
 }
 
-
-/*
- * Class:     java_net_NetworkInterface
- * Method:    isUp0
- * Signature: (Ljava/lang/String;I)Z
- */
-JNIEXPORT jboolean JNICALL NetworkInterface_isUp0(JNIEnv *env, jclass cls, jstring name, jint index) {
-    int ret = getFlags0(env, name);
-    return ((ret & IFF_UP) && (ret & IFF_RUNNING)) ? JNI_TRUE :  JNI_FALSE;
-}
-
-/*
- * Class:     java_net_NetworkInterface
- * Method:    isP2P0
- * Signature: (Ljava/lang/String;I)Z
- */
-JNIEXPORT jboolean JNICALL NetworkInterface_isP2P0(JNIEnv *env, jclass cls, jstring name, jint index) {
-    int ret = getFlags0(env, name);
-    return (ret & IFF_POINTOPOINT) ? JNI_TRUE :  JNI_FALSE;
-}
-
-/*
- * Class:     java_net_NetworkInterface
- * Method:    isLoopback0
- * Signature: (Ljava/lang/String;I)Z
- */
-JNIEXPORT jboolean JNICALL NetworkInterface_isLoopback0(JNIEnv *env, jclass cls, jstring name, jint index) {
-  int ret = getFlags0(env, name);
-  return (ret & IFF_LOOPBACK) ? JNI_TRUE :  JNI_FALSE;
-}
-
-/*
- * Class:     java_net_NetworkInterface
- * Method:    supportsMulticast0
- * Signature: (Ljava/lang/String;I)Z
- */
-JNIEXPORT jboolean JNICALL NetworkInterface_supportsMulticast0(JNIEnv *env, jclass cls, jstring name, jint index) {
-  int ret = getFlags0(env, name);
-  return (ret & IFF_MULTICAST) ? JNI_TRUE :  JNI_FALSE;
-}
-
-/*
- * Class:       java_net_NetworkInterface
- * Method:      getMTU0
- * Signature:   ([bLjava/lang/String;I)I
- */
-
-JNIEXPORT jint JNICALL NetworkInterface_getMTU0(JNIEnv *env, jclass class, jstring name, jint index) {
-  jboolean isCopy;
-  int ret = -1;
-  int sock;
-  const char* name_utf;
-
-  name_utf = (*env)->GetStringUTFChars(env, name, &isCopy);
-
-  if ((sock =openSocketWithFallback(env, name_utf)) < 0) {
-    (*env)->ReleaseStringUTFChars(env, name, name_utf);
-    return JNI_FALSE;
-  }
-
-  ret = getMTU(env, sock, name_utf);
-
-  (*env)->ReleaseStringUTFChars(env, name, name_utf);
-
-  untagSocket(env, sock);
-  close(sock);
-  return ret;
-}
-
 /*** Private methods definitions ****/
 
-static int getFlags0(JNIEnv *env, jstring name) {
-  jboolean isCopy;
-  int ret, sock;
-  const char* name_utf;
-  int flags = 0;
-
-  name_utf = (*env)->GetStringUTFChars(env, name, &isCopy);
-
-  if ((sock = openSocketWithFallback(env, name_utf)) < 0) {
-    (*env)->ReleaseStringUTFChars(env, name, name_utf);
-    return -1;
-  }
-
-  name_utf = (*env)->GetStringUTFChars(env, name, &isCopy);
-
-  ret = getFlags(sock, name_utf, &flags);
-
-  untagSocket(env, sock);
-  close(sock);
-  (*env)->ReleaseStringUTFChars(env, name, name_utf);
-
-  if (ret < 0) {
-    NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "IOCTL  SIOCGLIFFLAGS failed");
-    return -1;
-  }
-
-  return flags;
-}
-
-
-
-
 /*
  * Create a NetworkInterface object, populate the name and index, and
  * populate the InetAddress array based on the IP addresses for this
@@ -989,34 +885,6 @@
 
 /** Linux **/
 
-/* Open socket for further ioct calls, try v4 socket first and
- * if it falls return v6 socket
- */
-
-static int openSocketWithFallback(JNIEnv *env, const char *ifname){
-  int sock;
-  struct ifreq if2;
-
-  if ((sock = JVM_Socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
-    if (errno == EPROTONOSUPPORT){
-      if ( (sock = JVM_Socket(AF_INET6, SOCK_DGRAM, 0)) < 0 ){
-        NET_ThrowByNameWithLastError(env , JNU_JAVANETPKG "SocketException", "IPV6 Socket creation failed");
-        return -1;
-      }
-    }
-    else{ // errno is not NOSUPPORT
-      NET_ThrowByNameWithLastError(env , JNU_JAVANETPKG "SocketException", "IPV4 Socket creation failed");
-      return -1;
-    }
-  }
-
-  /* Linux starting from 2.6.? kernel allows ioctl call with either IPv4 or IPv6 socket regardless of type
-     of address of an interface */
-
-  tagSocket(env, sock);
-  return sock;
-}
-
 static int getIndex(int sock, const char *name){
   /*
    * Try to get the interface index
@@ -1032,19 +900,6 @@
   return if2.ifr_ifindex;
 }
 
-static int getMTU(JNIEnv *env, int sock,  const char *ifname) {
-  struct ifreq if2;
-
-  memset((char *) &if2, 0, sizeof(if2));
-  strcpy(if2.ifr_name, ifname);
-
-  if (ioctl(sock, SIOCGIFMTU, (char *)&if2) < 0) {
-    NET_ThrowByNameWithLastError(env, JNU_JAVANETPKG "SocketException", "IOCTL SIOCGIFMTU failed");
-    return -1;
-  }
-
-  return  if2.ifr_mtu;
-}
 
 static int getFlags(int sock, const char *ifname, int *flags) {
   struct ifreq if2;
@@ -1065,11 +920,6 @@
 }
 
 static JNINativeMethod gMethods[] = {
-  NATIVE_METHOD(NetworkInterface, getMTU0, "(Ljava/lang/String;I)I"),
-  NATIVE_METHOD(NetworkInterface, supportsMulticast0, "(Ljava/lang/String;I)Z"),
-  NATIVE_METHOD(NetworkInterface, isLoopback0, "(Ljava/lang/String;I)Z"),
-  NATIVE_METHOD(NetworkInterface, isP2P0, "(Ljava/lang/String;I)Z"),
-  NATIVE_METHOD(NetworkInterface, isUp0, "(Ljava/lang/String;I)Z"),
   NATIVE_METHOD(NetworkInterface, getAll, "()[Ljava/net/NetworkInterface;"),
   NATIVE_METHOD(NetworkInterface, getByInetAddress0, "(Ljava/net/InetAddress;)Ljava/net/NetworkInterface;"),
   NATIVE_METHOD(NetworkInterface, getByIndex0, "(I)Ljava/net/NetworkInterface;"),
diff --git a/ojluni/src/main/native/java_net_NetworkInterface.h b/ojluni/src/main/native/java_net_NetworkInterface.h
index 05472e8..53391d3 100644
--- a/ojluni/src/main/native/java_net_NetworkInterface.h
+++ b/ojluni/src/main/native/java_net_NetworkInterface.h
@@ -68,38 +68,6 @@
 
 /*
  * Class:     java_net_NetworkInterface
- * Method:    isUp0
- * Signature: (Ljava/lang/String;I)Z
- */
-JNIEXPORT jboolean JNICALL NetworkInterface_isUp0
-  (JNIEnv *, jclass, jstring, jint);
-
-/*
- * Class:     java_net_NetworkInterface
- * Method:    isLoopback0
- * Signature: (Ljava/lang/String;I)Z
- */
-JNIEXPORT jboolean JNICALL NetworkInterface_isLoopback0
-  (JNIEnv *, jclass, jstring, jint);
-
-/*
- * Class:     java_net_NetworkInterface
- * Method:    supportsMulticast0
- * Signature: (Ljava/lang/String;I)Z
- */
-JNIEXPORT jboolean JNICALL NetworkInterface_supportsMulticast0
-  (JNIEnv *, jclass, jstring, jint);
-
-/*
- * Class:     java_net_NetworkInterface
- * Method:    isP2P0
- * Signature: (Ljava/lang/String;I)Z
- */
-JNIEXPORT jboolean JNICALL NetworkInterface_isP2P0
-  (JNIEnv *, jclass, jstring, jint);
-
-/*
- * Class:     java_net_NetworkInterface
  * Method:    getMacAddr0
  * Signature: ([BLjava/lang/String;I)[B
  */
@@ -108,14 +76,6 @@
 
 /*
  * Class:     java_net_NetworkInterface
- * Method:    getMTU0
- * Signature: (Ljava/lang/String;I)I
- */
-JNIEXPORT jint JNICALL NetworkInterface_getMTU0
-  (JNIEnv *, jclass, jstring, jint);
-
-/*
- * Class:     java_net_NetworkInterface
  * Method:    init
  * Signature: ()V
  */