Merge "InvalidKeyException for "opaque" keys null getEncoded()"
diff --git a/dalvik/src/main/java/org/apache/harmony/dalvik/NativeTestTarget.java b/dalvik/src/main/java/org/apache/harmony/dalvik/NativeTestTarget.java
index 7b46d2e..5daf6a0 100644
--- a/dalvik/src/main/java/org/apache/harmony/dalvik/NativeTestTarget.java
+++ b/dalvik/src/main/java/org/apache/harmony/dalvik/NativeTestTarget.java
@@ -22,25 +22,20 @@
  * and performance of calling native methods.
  */
 public final class NativeTestTarget {
-    /**
-     * This class is uninstantiable.
-     */
-    private NativeTestTarget() {
-        // This space intentionally left blank.
+    public NativeTestTarget() {
     }
 
-    /**
-     * This is an empty native static method with no args, hooked up using
-     * JNI.
-     */
+    public static native synchronized void emptyJniStaticSynchronizedMethod0();
+
+    public native synchronized void emptyJniSynchronizedMethod0();
+
     public static native void emptyJniStaticMethod0();
 
-    /**
-     * This is an empty native static method with six args, hooked up using
-     * JNI.
-     */
-    public static native void emptyJniStaticMethod6(int a, int b, int c,
-        int d, int e, int f);
+    public native void emptyJniMethod0();
+
+    public static native void emptyJniStaticMethod6(int a, int b, int c, int d, int e, int f);
+
+    public native void emptyJniMethod6(int a, int b, int c, int d, int e, int f);
 
     /**
      * This is an empty native static method with six args, hooked up
@@ -51,18 +46,17 @@
     public static native void emptyJniStaticMethod6L(String a, String[] b,
         int[][] c, Object d, Object[] e, Object[][][][] f);
 
+    public native void emptyJniMethod6L(String a, String[] b,
+        int[][] c, Object d, Object[] e, Object[][][][] f);
+
     /**
-     * This method is intended to be "inlined" by the virtual machine
-     * (e.g., given special treatment as an intrinsic).
+     * This is used to benchmark dalvik's inline natives.
      */
     public static void emptyInlineMethod() {
-        // This space intentionally left blank.
     }
 
     /**
-     * This method is intended to be defined in native code and hooked
-     * up using the virtual machine's special fast-path native linkage
-     * (as opposed to being hooked up using JNI).
+     * This is used to benchmark dalvik's inline natives.
      */
     public static native void emptyInternalStaticMethod();
 }
diff --git a/dalvik/src/main/native/org_apache_harmony_dalvik_NativeTestTarget.cpp b/dalvik/src/main/native/org_apache_harmony_dalvik_NativeTestTarget.cpp
index 50e2fc2..11735eb 100644
--- a/dalvik/src/main/native/org_apache_harmony_dalvik_NativeTestTarget.cpp
+++ b/dalvik/src/main/native/org_apache_harmony_dalvik_NativeTestTarget.cpp
@@ -14,55 +14,30 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "NativeTestTarget"
+
 #include "JNIHelp.h"
+#include "../../../../luni/src/main/native/JniConstants.h"
 
-/*
- * public static void emptyJniStaticMethod0()
- *
- * For benchmarks, a do-nothing JNI method with no arguments.
- */
-static void emptyJniStaticMethod0(JNIEnv*, jclass)
-{
-    // This space intentionally left blank.
-}
-
-/*
- * public static void emptyJniStaticMethod6(int a, int b, int c,
- *   int d, int e, int f)
- *
- * For benchmarks, a do-nothing JNI method with six arguments.
- */
-static void emptyJniStaticMethod6(JNIEnv*, jclass,
-    int, int, int, int, int, int)
-{
-    // This space intentionally left blank.
-}
-
-/*
- * public static void emptyJniStaticMethod6L(String a, String[] b,
- *   int[][] c, Object d, Object[] e, Object[][][][] f)
- *
- * For benchmarks, a do-nothing JNI method with six arguments.
- */
-static void emptyJniStaticMethod6L(JNIEnv*, jclass,
-    jobject, jarray, jarray, jobject, jarray, jarray)
-{
-    // This space intentionally left blank.
-}
+static void NativeTestTarget_emptyJniMethod0(JNIEnv*, jobject) { }
+static void NativeTestTarget_emptyJniMethod6(JNIEnv*, jclass, int, int, int, int, int, int) { }
+static void NativeTestTarget_emptyJniMethod6L(JNIEnv*, jclass, jobject, jarray, jarray, jobject, jarray, jarray) { }
+static void NativeTestTarget_emptyJniStaticMethod0(JNIEnv*, jclass) { }
+static void NativeTestTarget_emptyJniStaticMethod6(JNIEnv*, jclass, int, int, int, int, int, int) { }
+static void NativeTestTarget_emptyJniStaticMethod6L(JNIEnv*, jclass, jobject, jarray, jarray, jobject, jarray, jarray) { }
+static void NativeTestTarget_emptyJniStaticSynchronizedMethod0(JNIEnv*, jclass) { }
+static void NativeTestTarget_emptyJniSynchronizedMethod0(JNIEnv*, jclass) { }
 
 static JNINativeMethod gMethods[] = {
-  { "emptyJniStaticMethod0",  "()V",       (void*)emptyJniStaticMethod0 },
-  { "emptyJniStaticMethod6",  "(IIIIII)V", (void*)emptyJniStaticMethod6 },
-  { "emptyJniStaticMethod6L", "(Ljava/lang/String;[Ljava/lang/String;[[ILjava/lang/Object;[Ljava/lang/Object;[[[[Ljava/lang/Object;)V", (void*)emptyJniStaticMethod6L },
+    NATIVE_METHOD(NativeTestTarget, emptyJniMethod0, "()V"),
+    NATIVE_METHOD(NativeTestTarget, emptyJniMethod6, "(IIIIII)V"),
+    NATIVE_METHOD(NativeTestTarget, emptyJniMethod6L, "(Ljava/lang/String;[Ljava/lang/String;[[ILjava/lang/Object;[Ljava/lang/Object;[[[[Ljava/lang/Object;)V"),
+    NATIVE_METHOD(NativeTestTarget, emptyJniStaticMethod0, "()V"),
+    NATIVE_METHOD(NativeTestTarget, emptyJniStaticMethod6, "(IIIIII)V"),
+    NATIVE_METHOD(NativeTestTarget, emptyJniStaticMethod6L, "(Ljava/lang/String;[Ljava/lang/String;[[ILjava/lang/Object;[Ljava/lang/Object;[[[[Ljava/lang/Object;)V"),
+    NATIVE_METHOD(NativeTestTarget, emptyJniStaticSynchronizedMethod0, "()V"),
+    NATIVE_METHOD(NativeTestTarget, emptyJniSynchronizedMethod0, "()V"),
 };
 int register_org_apache_harmony_dalvik_NativeTestTarget(JNIEnv* env) {
-    int result = jniRegisterNativeMethods(env,
-            "org/apache/harmony/dalvik/NativeTestTarget",
-            gMethods, NELEM(gMethods));
-    if (result != 0) {
-        /* print warning, but allow to continue */
-        ALOGW("WARNING: NativeTestTarget not registered\n");
-        env->ExceptionClear();
-    }
-    return 0;
+    return jniRegisterNativeMethods(env, "org/apache/harmony/dalvik/NativeTestTarget", gMethods, NELEM(gMethods));
 }
diff --git a/luni/src/main/java/java/nio/BaseByteBuffer.java b/luni/src/main/java/java/nio/BaseByteBuffer.java
deleted file mode 100644
index d6cfb93..0000000
--- a/luni/src/main/java/java/nio/BaseByteBuffer.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 java.nio;
-
-/**
- * Serves as the root of other byte buffer impl classes, implements common
- * methods that can be shared by child classes.
- */
-abstract class BaseByteBuffer extends ByteBuffer {
-
-    protected BaseByteBuffer(int capacity, MemoryBlock block) {
-        super(capacity, block);
-    }
-
-    @Override
-    public final CharBuffer asCharBuffer() {
-        return CharToByteBufferAdapter.asCharBuffer(this);
-    }
-
-    @Override
-    public final DoubleBuffer asDoubleBuffer() {
-        return DoubleToByteBufferAdapter.asDoubleBuffer(this);
-    }
-
-    @Override
-    public final FloatBuffer asFloatBuffer() {
-        return FloatToByteBufferAdapter.asFloatBuffer(this);
-    }
-
-    @Override
-    public final IntBuffer asIntBuffer() {
-        return IntToByteBufferAdapter.asIntBuffer(this);
-    }
-
-    @Override
-    public final LongBuffer asLongBuffer() {
-        return LongToByteBufferAdapter.asLongBuffer(this);
-    }
-
-    @Override
-    public final ShortBuffer asShortBuffer() {
-        return ShortToByteBufferAdapter.asShortBuffer(this);
-    }
-
-    @Override
-    public char getChar() {
-        return (char) getShort();
-    }
-
-    @Override
-    public char getChar(int index) {
-        return (char) getShort(index);
-    }
-
-    @Override
-    public ByteBuffer putChar(char value) {
-        return putShort((short) value);
-    }
-
-    @Override
-    public ByteBuffer putChar(int index, char value) {
-        return putShort(index, (short) value);
-    }
-}
diff --git a/luni/src/main/java/java/nio/Buffer.java b/luni/src/main/java/java/nio/Buffer.java
index b90744b..3ba2503 100644
--- a/luni/src/main/java/java/nio/Buffer.java
+++ b/luni/src/main/java/java/nio/Buffer.java
@@ -300,14 +300,6 @@
      *                if <code>newLimit</code> is invalid.
      */
     public final Buffer limit(int newLimit) {
-        limitImpl(newLimit);
-        return this;
-    }
-
-    /**
-     * Subverts the fact that limit(int) is final, for the benefit of MappedByteBufferAdapter.
-     */
-    void limitImpl(int newLimit) {
         if (newLimit < 0 || newLimit > capacity) {
             throw new IllegalArgumentException("Bad limit (capacity " + capacity + "): " + newLimit);
         }
@@ -319,6 +311,7 @@
         if ((mark != UNSET_MARK) && (mark > newLimit)) {
             mark = UNSET_MARK;
         }
+        return this;
     }
 
     /**
@@ -409,15 +402,11 @@
         return this;
     }
 
+    /**
+     * Returns a string describing this buffer.
+     */
     @Override public String toString() {
-        StringBuilder buf = new StringBuilder();
-        buf.append(getClass().getName());
-        buf.append(", status: capacity=");
-        buf.append(capacity);
-        buf.append(" position=");
-        buf.append(position);
-        buf.append(" limit=");
-        buf.append(limit);
-        return buf.toString();
+        return getClass().getName() +
+            "[position=" + position + ",limit=" + limit + ",capacity=" + capacity + "]";
     }
 }
diff --git a/luni/src/main/java/java/nio/ByteArrayBuffer.java b/luni/src/main/java/java/nio/ByteArrayBuffer.java
new file mode 100644
index 0000000..e8d7ecc
--- /dev/null
+++ b/luni/src/main/java/java/nio/ByteArrayBuffer.java
@@ -0,0 +1,435 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You 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 java.nio;
+
+import libcore.io.SizeOf;
+import libcore.io.Memory;
+
+/**
+ * ByteArrayBuffer implements byte[]-backed ByteBuffers.
+ */
+final class ByteArrayBuffer extends ByteBuffer {
+
+  /**
+   * These fields are non-private for NioUtils.unsafeArray.
+   */
+  final byte[] backingArray;
+  final int arrayOffset;
+
+  private final boolean isReadOnly;
+
+  ByteArrayBuffer(byte[] backingArray) {
+    this(backingArray.length, backingArray, 0, false);
+  }
+
+  private ByteArrayBuffer(int capacity, byte[] backingArray, int arrayOffset, boolean isReadOnly) {
+    super(capacity, null);
+    this.backingArray = backingArray;
+    this.arrayOffset = arrayOffset;
+    this.isReadOnly = isReadOnly;
+    if (arrayOffset + capacity > backingArray.length) {
+      throw new IndexOutOfBoundsException("backingArray.length=" + backingArray.length +
+                                              ", capacity=" + capacity + ", arrayOffset=" + arrayOffset);
+    }
+  }
+
+  private static ByteArrayBuffer copy(ByteArrayBuffer other, int markOfOther, boolean isReadOnly) {
+    ByteArrayBuffer buf = new ByteArrayBuffer(other.capacity(), other.backingArray, other.arrayOffset, isReadOnly);
+    buf.limit = other.limit;
+    buf.position = other.position();
+    buf.mark = markOfOther;
+    return buf;
+  }
+
+  @Override public ByteBuffer asReadOnlyBuffer() {
+    return copy(this, mark, true);
+  }
+
+  @Override public ByteBuffer compact() {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
+    }
+    System.arraycopy(backingArray, position + arrayOffset, backingArray, arrayOffset, remaining());
+    position = limit - position;
+    limit = capacity;
+    mark = UNSET_MARK;
+    return this;
+  }
+
+  @Override public ByteBuffer duplicate() {
+    return copy(this, mark, isReadOnly);
+  }
+
+  @Override public ByteBuffer slice() {
+    return new ByteArrayBuffer(remaining(), backingArray, arrayOffset + position, isReadOnly);
+  }
+
+  @Override public boolean isReadOnly() {
+    return isReadOnly;
+  }
+
+  @Override byte[] protectedArray() {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
+    }
+    return backingArray;
+  }
+
+  @Override int protectedArrayOffset() {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
+    }
+    return arrayOffset;
+  }
+
+  @Override boolean protectedHasArray() {
+    if (isReadOnly) {
+      return false;
+    }
+    return true;
+  }
+
+  @Override public final ByteBuffer get(byte[] dst, int dstOffset, int byteCount) {
+    checkGetBounds(1, dst.length, dstOffset, byteCount);
+    System.arraycopy(backingArray, arrayOffset + position, dst, dstOffset, byteCount);
+    position += byteCount;
+    return this;
+  }
+
+  final void get(char[] dst, int dstOffset, int charCount) {
+    int byteCount = checkGetBounds(SizeOf.CHAR, dst.length, dstOffset, charCount);
+    Memory.unsafeBulkGet(dst, dstOffset, byteCount, backingArray, arrayOffset + position, SizeOf.CHAR, order.needsSwap);
+    position += byteCount;
+  }
+
+  final void get(double[] dst, int dstOffset, int doubleCount) {
+    int byteCount = checkGetBounds(SizeOf.DOUBLE, dst.length, dstOffset, doubleCount);
+    Memory.unsafeBulkGet(dst, dstOffset, byteCount, backingArray, arrayOffset + position, SizeOf.DOUBLE, order.needsSwap);
+    position += byteCount;
+  }
+
+  final void get(float[] dst, int dstOffset, int floatCount) {
+    int byteCount = checkGetBounds(SizeOf.FLOAT, dst.length, dstOffset, floatCount);
+    Memory.unsafeBulkGet(dst, dstOffset, byteCount, backingArray, arrayOffset + position, SizeOf.FLOAT, order.needsSwap);
+    position += byteCount;
+  }
+
+  final void get(int[] dst, int dstOffset, int intCount) {
+    int byteCount = checkGetBounds(SizeOf.INT, dst.length, dstOffset, intCount);
+    Memory.unsafeBulkGet(dst, dstOffset, byteCount, backingArray, arrayOffset + position, SizeOf.INT, order.needsSwap);
+    position += byteCount;
+  }
+
+  final void get(long[] dst, int dstOffset, int longCount) {
+    int byteCount = checkGetBounds(SizeOf.LONG, dst.length, dstOffset, longCount);
+    Memory.unsafeBulkGet(dst, dstOffset, byteCount, backingArray, arrayOffset + position, SizeOf.LONG, order.needsSwap);
+    position += byteCount;
+  }
+
+  final void get(short[] dst, int dstOffset, int shortCount) {
+    int byteCount = checkGetBounds(SizeOf.SHORT, dst.length, dstOffset, shortCount);
+    Memory.unsafeBulkGet(dst, dstOffset, byteCount, backingArray, arrayOffset + position, SizeOf.SHORT, order.needsSwap);
+    position += byteCount;
+  }
+
+  @Override public final byte get() {
+    if (position == limit) {
+      throw new BufferUnderflowException();
+    }
+    return backingArray[arrayOffset + position++];
+  }
+
+  @Override public final byte get(int index) {
+    checkIndex(index);
+    return backingArray[arrayOffset + index];
+  }
+
+  @Override public final char getChar() {
+    int newPosition = position + SizeOf.CHAR;
+    if (newPosition > limit) {
+      throw new BufferUnderflowException();
+    }
+    char result = (char) Memory.peekShort(backingArray, arrayOffset + position, order);
+    position = newPosition;
+    return result;
+  }
+
+  @Override public final char getChar(int index) {
+    checkIndex(index, SizeOf.CHAR);
+    return (char) Memory.peekShort(backingArray, arrayOffset + index, order);
+  }
+
+  @Override public final double getDouble() {
+    return Double.longBitsToDouble(getLong());
+  }
+
+  @Override public final double getDouble(int index) {
+    return Double.longBitsToDouble(getLong(index));
+  }
+
+  @Override public final float getFloat() {
+    return Float.intBitsToFloat(getInt());
+  }
+
+  @Override public final float getFloat(int index) {
+    return Float.intBitsToFloat(getInt(index));
+  }
+
+  @Override public final int getInt() {
+    int newPosition = position + SizeOf.INT;
+    if (newPosition > limit) {
+      throw new BufferUnderflowException();
+    }
+    int result = Memory.peekInt(backingArray, arrayOffset + position, order);
+    position = newPosition;
+    return result;
+  }
+
+  @Override public final int getInt(int index) {
+    checkIndex(index, SizeOf.INT);
+    return Memory.peekInt(backingArray, arrayOffset + index, order);
+  }
+
+  @Override public final long getLong() {
+    int newPosition = position + SizeOf.LONG;
+    if (newPosition > limit) {
+      throw new BufferUnderflowException();
+    }
+    long result = Memory.peekLong(backingArray, arrayOffset + position, order);
+    position = newPosition;
+    return result;
+  }
+
+  @Override public final long getLong(int index) {
+    checkIndex(index, SizeOf.LONG);
+    return Memory.peekLong(backingArray, arrayOffset + index, order);
+  }
+
+  @Override public final short getShort() {
+    int newPosition = position + SizeOf.SHORT;
+    if (newPosition > limit) {
+      throw new BufferUnderflowException();
+    }
+    short result = Memory.peekShort(backingArray, arrayOffset + position, order);
+    position = newPosition;
+    return result;
+  }
+
+  @Override public final short getShort(int index) {
+    checkIndex(index, SizeOf.SHORT);
+    return Memory.peekShort(backingArray, arrayOffset + index, order);
+  }
+
+  @Override public final boolean isDirect() {
+    return false;
+  }
+
+  @Override public ByteBuffer put(byte b) {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
+    }
+    if (position == limit) {
+      throw new BufferOverflowException();
+    }
+    backingArray[arrayOffset + position++] = b;
+    return this;
+  }
+
+  @Override public ByteBuffer put(int index, byte b) {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
+    }
+    checkIndex(index);
+    backingArray[arrayOffset + index] = b;
+    return this;
+  }
+
+  @Override public ByteBuffer put(byte[] src, int srcOffset, int byteCount) {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
+    }
+    checkPutBounds(1, src.length, srcOffset, byteCount);
+    System.arraycopy(src, srcOffset, backingArray, arrayOffset + position, byteCount);
+    position += byteCount;
+    return this;
+  }
+
+  final void put(char[] src, int srcOffset, int charCount) {
+    int byteCount = checkPutBounds(SizeOf.CHAR, src.length, srcOffset, charCount);
+    Memory.unsafeBulkPut(backingArray, arrayOffset + position, byteCount, src, srcOffset, SizeOf.CHAR, order.needsSwap);
+    position += byteCount;
+  }
+
+  final void put(double[] src, int srcOffset, int doubleCount) {
+    int byteCount = checkPutBounds(SizeOf.DOUBLE, src.length, srcOffset, doubleCount);
+    Memory.unsafeBulkPut(backingArray, arrayOffset + position, byteCount, src, srcOffset, SizeOf.DOUBLE, order.needsSwap);
+    position += byteCount;
+  }
+
+  final void put(float[] src, int srcOffset, int floatCount) {
+    int byteCount = checkPutBounds(SizeOf.FLOAT, src.length, srcOffset, floatCount);
+    Memory.unsafeBulkPut(backingArray, arrayOffset + position, byteCount, src, srcOffset, SizeOf.FLOAT, order.needsSwap);
+    position += byteCount;
+  }
+
+  final void put(int[] src, int srcOffset, int intCount) {
+    int byteCount = checkPutBounds(SizeOf.INT, src.length, srcOffset, intCount);
+    Memory.unsafeBulkPut(backingArray, arrayOffset + position, byteCount, src, srcOffset, SizeOf.INT, order.needsSwap);
+    position += byteCount;
+  }
+
+  final void put(long[] src, int srcOffset, int longCount) {
+    int byteCount = checkPutBounds(SizeOf.LONG, src.length, srcOffset, longCount);
+    Memory.unsafeBulkPut(backingArray, arrayOffset + position, byteCount, src, srcOffset, SizeOf.LONG, order.needsSwap);
+    position += byteCount;
+  }
+
+  final void put(short[] src, int srcOffset, int shortCount) {
+    int byteCount = checkPutBounds(SizeOf.SHORT, src.length, srcOffset, shortCount);
+    Memory.unsafeBulkPut(backingArray, arrayOffset + position, byteCount, src, srcOffset, SizeOf.SHORT, order.needsSwap);
+    position += byteCount;
+  }
+
+  @Override public ByteBuffer putChar(int index, char value) {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
+    }
+    checkIndex(index, SizeOf.CHAR);
+    Memory.pokeShort(backingArray, arrayOffset + index, (short) value, order);
+    return this;
+  }
+
+  @Override public ByteBuffer putChar(char value) {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
+    }
+    int newPosition = position + SizeOf.CHAR;
+    if (newPosition > limit) {
+      throw new BufferOverflowException();
+    }
+    Memory.pokeShort(backingArray, arrayOffset + position, (short) value, order);
+    position = newPosition;
+    return this;
+  }
+
+  @Override public ByteBuffer putDouble(double value) {
+    return putLong(Double.doubleToRawLongBits(value));
+  }
+
+  @Override public ByteBuffer putDouble(int index, double value) {
+    return putLong(index, Double.doubleToRawLongBits(value));
+  }
+
+  @Override public ByteBuffer putFloat(float value) {
+    return putInt(Float.floatToRawIntBits(value));
+  }
+
+  @Override public ByteBuffer putFloat(int index, float value) {
+    return putInt(index, Float.floatToRawIntBits(value));
+  }
+
+  @Override public ByteBuffer putInt(int value) {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
+    }
+    int newPosition = position + SizeOf.INT;
+    if (newPosition > limit) {
+      throw new BufferOverflowException();
+    }
+    Memory.pokeInt(backingArray, arrayOffset + position, value, order);
+    position = newPosition;
+    return this;
+  }
+
+  @Override public ByteBuffer putInt(int index, int value) {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
+    }
+    checkIndex(index, SizeOf.INT);
+    Memory.pokeInt(backingArray, arrayOffset + index, value, order);
+    return this;
+  }
+
+  @Override public ByteBuffer putLong(int index, long value) {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
+    }
+    checkIndex(index, SizeOf.LONG);
+    Memory.pokeLong(backingArray, arrayOffset + index, value, order);
+    return this;
+  }
+
+  @Override public ByteBuffer putLong(long value) {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
+    }
+    int newPosition = position + SizeOf.LONG;
+    if (newPosition > limit) {
+      throw new BufferOverflowException();
+    }
+    Memory.pokeLong(backingArray, arrayOffset + position, value, order);
+    position = newPosition;
+    return this;
+  }
+
+  @Override public ByteBuffer putShort(int index, short value) {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
+    }
+    checkIndex(index, SizeOf.SHORT);
+    Memory.pokeShort(backingArray, arrayOffset + index, value, order);
+    return this;
+  }
+
+  @Override public ByteBuffer putShort(short value) {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
+    }
+    int newPosition = position + SizeOf.SHORT;
+    if (newPosition > limit) {
+      throw new BufferOverflowException();
+    }
+    Memory.pokeShort(backingArray, arrayOffset + position, value, order);
+    position = newPosition;
+    return this;
+  }
+
+  @Override public final CharBuffer asCharBuffer() {
+    return ByteBufferAsCharBuffer.asCharBuffer(this);
+  }
+
+  @Override public final DoubleBuffer asDoubleBuffer() {
+    return ByteBufferAsDoubleBuffer.asDoubleBuffer(this);
+  }
+
+  @Override public final FloatBuffer asFloatBuffer() {
+    return ByteBufferAsFloatBuffer.asFloatBuffer(this);
+  }
+
+  @Override public final IntBuffer asIntBuffer() {
+    return ByteBufferAsIntBuffer.asIntBuffer(this);
+  }
+
+  @Override public final LongBuffer asLongBuffer() {
+    return ByteBufferAsLongBuffer.asLongBuffer(this);
+  }
+
+  @Override public final ShortBuffer asShortBuffer() {
+    return ByteBufferAsShortBuffer.asShortBuffer(this);
+  }
+}
diff --git a/luni/src/main/java/java/nio/ByteBuffer.java b/luni/src/main/java/java/nio/ByteBuffer.java
index 6a3624a..cd39a53 100644
--- a/luni/src/main/java/java/nio/ByteBuffer.java
+++ b/luni/src/main/java/java/nio/ByteBuffer.java
@@ -35,6 +35,10 @@
  *
  */
 public abstract class ByteBuffer extends Buffer implements Comparable<ByteBuffer> {
+    /**
+     * The byte order of this buffer, default is {@code BIG_ENDIAN}.
+     */
+    ByteOrder order = ByteOrder.BIG_ENDIAN;
 
     /**
      * Creates a byte buffer based on a newly allocated byte array.
@@ -49,7 +53,7 @@
         if (capacity < 0) {
             throw new IllegalArgumentException("capacity < 0: " + capacity);
         }
-        return new ReadWriteHeapByteBuffer(capacity);
+        return new ByteArrayBuffer(new byte[capacity]);
     }
 
     /**
@@ -65,7 +69,7 @@
         if (capacity < 0) {
             throw new IllegalArgumentException("capacity < 0: " + capacity);
         }
-        return new ReadWriteDirectByteBuffer(capacity);
+        return new DirectByteBuffer(MemoryBlock.allocate(capacity), capacity, 0, false, null);
     }
 
     /**
@@ -79,7 +83,7 @@
      * @return the created byte buffer.
      */
     public static ByteBuffer wrap(byte[] array) {
-        return new ReadWriteHeapByteBuffer(array);
+        return new ByteArrayBuffer(array);
     }
 
     /**
@@ -102,17 +106,12 @@
      */
     public static ByteBuffer wrap(byte[] array, int start, int byteCount) {
         Arrays.checkOffsetAndCount(array.length, start, byteCount);
-        ByteBuffer buf = new ReadWriteHeapByteBuffer(array);
+        ByteBuffer buf = new ByteArrayBuffer(array);
         buf.position = start;
         buf.limit = start + byteCount;
         return buf;
     }
 
-    /**
-     * The byte order of this buffer, default is {@code BIG_ENDIAN}.
-     */
-    ByteOrder order = ByteOrder.BIG_ENDIAN;
-
     ByteBuffer(int capacity, MemoryBlock block) {
         super(0, capacity, block);
     }
@@ -644,18 +643,11 @@
      * @see ByteOrder
      */
     public final ByteBuffer order(ByteOrder byteOrder) {
-        orderImpl(byteOrder);
-        return this;
-    }
-
-    /**
-     * Subverts the fact that order(ByteOrder) is final, for the benefit of MappedByteBufferAdapter.
-     */
-    void orderImpl(ByteOrder byteOrder) {
         if (byteOrder == null) {
             byteOrder = ByteOrder.LITTLE_ENDIAN;
         }
         order = byteOrder;
+        return this;
     }
 
     /**
@@ -761,6 +753,9 @@
      *                if no changes may be made to the contents of this buffer.
      */
     public ByteBuffer put(ByteBuffer src) {
+        if (isReadOnly()) {
+            throw new ReadOnlyBufferException();
+        }
         if (src == this) {
             throw new IllegalArgumentException("src == this");
         }
diff --git a/luni/src/main/java/java/nio/CharToByteBufferAdapter.java b/luni/src/main/java/java/nio/ByteBufferAsCharBuffer.java
similarity index 87%
rename from luni/src/main/java/java/nio/CharToByteBufferAdapter.java
rename to luni/src/main/java/java/nio/ByteBufferAsCharBuffer.java
index b9100a2..ef5c52d 100644
--- a/luni/src/main/java/java/nio/CharToByteBufferAdapter.java
+++ b/luni/src/main/java/java/nio/ByteBufferAsCharBuffer.java
@@ -31,17 +31,17 @@
  * </p>
  *
  */
-final class CharToByteBufferAdapter extends CharBuffer {
+final class ByteBufferAsCharBuffer extends CharBuffer {
 
     private final ByteBuffer byteBuffer;
 
     static CharBuffer asCharBuffer(ByteBuffer byteBuffer) {
         ByteBuffer slice = byteBuffer.slice();
         slice.order(byteBuffer.order());
-        return new CharToByteBufferAdapter(slice);
+        return new ByteBufferAsCharBuffer(slice);
     }
 
-    private CharToByteBufferAdapter(ByteBuffer byteBuffer) {
+    private ByteBufferAsCharBuffer(ByteBuffer byteBuffer) {
         super(byteBuffer.capacity() / SizeOf.CHAR);
         this.byteBuffer = byteBuffer;
         this.byteBuffer.clear();
@@ -50,7 +50,7 @@
 
     @Override
     public CharBuffer asReadOnlyBuffer() {
-        CharToByteBufferAdapter buf = new CharToByteBufferAdapter(byteBuffer.asReadOnlyBuffer());
+        ByteBufferAsCharBuffer buf = new ByteBufferAsCharBuffer(byteBuffer.asReadOnlyBuffer());
         buf.limit = limit;
         buf.position = position;
         buf.mark = mark;
@@ -76,7 +76,7 @@
     @Override
     public CharBuffer duplicate() {
         ByteBuffer bb = byteBuffer.duplicate().order(byteBuffer.order());
-        CharToByteBufferAdapter buf = new CharToByteBufferAdapter(bb);
+        ByteBufferAsCharBuffer buf = new ByteBufferAsCharBuffer(bb);
         buf.limit = limit;
         buf.position = position;
         buf.mark = mark;
@@ -104,7 +104,7 @@
         if (byteBuffer instanceof DirectByteBuffer) {
             ((DirectByteBuffer) byteBuffer).get(dst, dstOffset, charCount);
         } else {
-            ((HeapByteBuffer) byteBuffer).get(dst, dstOffset, charCount);
+            ((ByteArrayBuffer) byteBuffer).get(dst, dstOffset, charCount);
         }
         this.position += charCount;
         return this;
@@ -157,10 +157,10 @@
     public CharBuffer put(char[] src, int srcOffset, int charCount) {
         byteBuffer.limit(limit * SizeOf.CHAR);
         byteBuffer.position(position * SizeOf.CHAR);
-        if (byteBuffer instanceof ReadWriteDirectByteBuffer) {
-            ((ReadWriteDirectByteBuffer) byteBuffer).put(src, srcOffset, charCount);
+        if (byteBuffer instanceof DirectByteBuffer) {
+            ((DirectByteBuffer) byteBuffer).put(src, srcOffset, charCount);
         } else {
-            ((ReadWriteHeapByteBuffer) byteBuffer).put(src, srcOffset, charCount);
+            ((ByteArrayBuffer) byteBuffer).put(src, srcOffset, charCount);
         }
         this.position += charCount;
         return this;
@@ -171,7 +171,7 @@
         byteBuffer.limit(limit * SizeOf.CHAR);
         byteBuffer.position(position * SizeOf.CHAR);
         ByteBuffer bb = byteBuffer.slice().order(byteBuffer.order());
-        CharBuffer result = new CharToByteBufferAdapter(bb);
+        CharBuffer result = new ByteBufferAsCharBuffer(bb);
         byteBuffer.clear();
         return result;
     }
diff --git a/luni/src/main/java/java/nio/DoubleToByteBufferAdapter.java b/luni/src/main/java/java/nio/ByteBufferAsDoubleBuffer.java
similarity index 86%
rename from luni/src/main/java/java/nio/DoubleToByteBufferAdapter.java
rename to luni/src/main/java/java/nio/ByteBufferAsDoubleBuffer.java
index 8b1e084..044bf59 100644
--- a/luni/src/main/java/java/nio/DoubleToByteBufferAdapter.java
+++ b/luni/src/main/java/java/nio/ByteBufferAsDoubleBuffer.java
@@ -31,17 +31,17 @@
  * </p>
  *
  */
-final class DoubleToByteBufferAdapter extends DoubleBuffer {
+final class ByteBufferAsDoubleBuffer extends DoubleBuffer {
 
     private final ByteBuffer byteBuffer;
 
     static DoubleBuffer asDoubleBuffer(ByteBuffer byteBuffer) {
         ByteBuffer slice = byteBuffer.slice();
         slice.order(byteBuffer.order());
-        return new DoubleToByteBufferAdapter(slice);
+        return new ByteBufferAsDoubleBuffer(slice);
     }
 
-    private DoubleToByteBufferAdapter(ByteBuffer byteBuffer) {
+    private ByteBufferAsDoubleBuffer(ByteBuffer byteBuffer) {
         super(byteBuffer.capacity() / SizeOf.DOUBLE);
         this.byteBuffer = byteBuffer;
         this.byteBuffer.clear();
@@ -50,7 +50,7 @@
 
     @Override
     public DoubleBuffer asReadOnlyBuffer() {
-        DoubleToByteBufferAdapter buf = new DoubleToByteBufferAdapter(byteBuffer.asReadOnlyBuffer());
+        ByteBufferAsDoubleBuffer buf = new ByteBufferAsDoubleBuffer(byteBuffer.asReadOnlyBuffer());
         buf.limit = limit;
         buf.position = position;
         buf.mark = mark;
@@ -76,7 +76,7 @@
     @Override
     public DoubleBuffer duplicate() {
         ByteBuffer bb = byteBuffer.duplicate().order(byteBuffer.order());
-        DoubleToByteBufferAdapter buf = new DoubleToByteBufferAdapter(bb);
+        ByteBufferAsDoubleBuffer buf = new ByteBufferAsDoubleBuffer(bb);
         buf.limit = limit;
         buf.position = position;
         buf.mark = mark;
@@ -104,7 +104,7 @@
         if (byteBuffer instanceof DirectByteBuffer) {
             ((DirectByteBuffer) byteBuffer).get(dst, dstOffset, doubleCount);
         } else {
-            ((HeapByteBuffer) byteBuffer).get(dst, dstOffset, doubleCount);
+            ((ByteArrayBuffer) byteBuffer).get(dst, dstOffset, doubleCount);
         }
         this.position += doubleCount;
         return this;
@@ -157,10 +157,10 @@
     public DoubleBuffer put(double[] src, int srcOffset, int doubleCount) {
         byteBuffer.limit(limit * SizeOf.DOUBLE);
         byteBuffer.position(position * SizeOf.DOUBLE);
-        if (byteBuffer instanceof ReadWriteDirectByteBuffer) {
-            ((ReadWriteDirectByteBuffer) byteBuffer).put(src, srcOffset, doubleCount);
+        if (byteBuffer instanceof DirectByteBuffer) {
+            ((DirectByteBuffer) byteBuffer).put(src, srcOffset, doubleCount);
         } else {
-            ((ReadWriteHeapByteBuffer) byteBuffer).put(src, srcOffset, doubleCount);
+            ((ByteArrayBuffer) byteBuffer).put(src, srcOffset, doubleCount);
         }
         this.position += doubleCount;
         return this;
@@ -171,7 +171,7 @@
         byteBuffer.limit(limit * SizeOf.DOUBLE);
         byteBuffer.position(position * SizeOf.DOUBLE);
         ByteBuffer bb = byteBuffer.slice().order(byteBuffer.order());
-        DoubleBuffer result = new DoubleToByteBufferAdapter(bb);
+        DoubleBuffer result = new ByteBufferAsDoubleBuffer(bb);
         byteBuffer.clear();
         return result;
     }
diff --git a/luni/src/main/java/java/nio/FloatToByteBufferAdapter.java b/luni/src/main/java/java/nio/ByteBufferAsFloatBuffer.java
similarity index 86%
rename from luni/src/main/java/java/nio/FloatToByteBufferAdapter.java
rename to luni/src/main/java/java/nio/ByteBufferAsFloatBuffer.java
index 0ed944b..a67affe 100644
--- a/luni/src/main/java/java/nio/FloatToByteBufferAdapter.java
+++ b/luni/src/main/java/java/nio/ByteBufferAsFloatBuffer.java
@@ -30,17 +30,17 @@
  * </ul>
  * </p>
  */
-final class FloatToByteBufferAdapter extends FloatBuffer {
+final class ByteBufferAsFloatBuffer extends FloatBuffer {
 
     private final ByteBuffer byteBuffer;
 
     static FloatBuffer asFloatBuffer(ByteBuffer byteBuffer) {
         ByteBuffer slice = byteBuffer.slice();
         slice.order(byteBuffer.order());
-        return new FloatToByteBufferAdapter(slice);
+        return new ByteBufferAsFloatBuffer(slice);
     }
 
-    FloatToByteBufferAdapter(ByteBuffer byteBuffer) {
+    ByteBufferAsFloatBuffer(ByteBuffer byteBuffer) {
         super(byteBuffer.capacity() / SizeOf.FLOAT);
         this.byteBuffer = byteBuffer;
         this.byteBuffer.clear();
@@ -49,7 +49,7 @@
 
     @Override
     public FloatBuffer asReadOnlyBuffer() {
-        FloatToByteBufferAdapter buf = new FloatToByteBufferAdapter(byteBuffer.asReadOnlyBuffer());
+        ByteBufferAsFloatBuffer buf = new ByteBufferAsFloatBuffer(byteBuffer.asReadOnlyBuffer());
         buf.limit = limit;
         buf.position = position;
         buf.mark = mark;
@@ -75,7 +75,7 @@
     @Override
     public FloatBuffer duplicate() {
         ByteBuffer bb = byteBuffer.duplicate().order(byteBuffer.order());
-        FloatToByteBufferAdapter buf = new FloatToByteBufferAdapter(bb);
+        ByteBufferAsFloatBuffer buf = new ByteBufferAsFloatBuffer(bb);
         buf.limit = limit;
         buf.position = position;
         buf.mark = mark;
@@ -103,7 +103,7 @@
         if (byteBuffer instanceof DirectByteBuffer) {
             ((DirectByteBuffer) byteBuffer).get(dst, dstOffset, floatCount);
         } else {
-            ((HeapByteBuffer) byteBuffer).get(dst, dstOffset, floatCount);
+            ((ByteArrayBuffer) byteBuffer).get(dst, dstOffset, floatCount);
         }
         this.position += floatCount;
         return this;
@@ -156,10 +156,10 @@
     public FloatBuffer put(float[] src, int srcOffset, int floatCount) {
         byteBuffer.limit(limit * SizeOf.FLOAT);
         byteBuffer.position(position * SizeOf.FLOAT);
-        if (byteBuffer instanceof ReadWriteDirectByteBuffer) {
-            ((ReadWriteDirectByteBuffer) byteBuffer).put(src, srcOffset, floatCount);
+        if (byteBuffer instanceof DirectByteBuffer) {
+            ((DirectByteBuffer) byteBuffer).put(src, srcOffset, floatCount);
         } else {
-            ((ReadWriteHeapByteBuffer) byteBuffer).put(src, srcOffset, floatCount);
+            ((ByteArrayBuffer) byteBuffer).put(src, srcOffset, floatCount);
         }
         this.position += floatCount;
         return this;
@@ -170,7 +170,7 @@
         byteBuffer.limit(limit * SizeOf.FLOAT);
         byteBuffer.position(position * SizeOf.FLOAT);
         ByteBuffer bb = byteBuffer.slice().order(byteBuffer.order());
-        FloatBuffer result = new FloatToByteBufferAdapter(bb);
+        FloatBuffer result = new ByteBufferAsFloatBuffer(bb);
         byteBuffer.clear();
         return result;
     }
diff --git a/luni/src/main/java/java/nio/IntToByteBufferAdapter.java b/luni/src/main/java/java/nio/ByteBufferAsIntBuffer.java
similarity index 87%
rename from luni/src/main/java/java/nio/IntToByteBufferAdapter.java
rename to luni/src/main/java/java/nio/ByteBufferAsIntBuffer.java
index 1af5f86..a3211b8 100644
--- a/luni/src/main/java/java/nio/IntToByteBufferAdapter.java
+++ b/luni/src/main/java/java/nio/ByteBufferAsIntBuffer.java
@@ -31,17 +31,17 @@
  * </p>
  *
  */
-final class IntToByteBufferAdapter extends IntBuffer {
+final class ByteBufferAsIntBuffer extends IntBuffer {
 
     private final ByteBuffer byteBuffer;
 
     static IntBuffer asIntBuffer(ByteBuffer byteBuffer) {
         ByteBuffer slice = byteBuffer.slice();
         slice.order(byteBuffer.order());
-        return new IntToByteBufferAdapter(slice);
+        return new ByteBufferAsIntBuffer(slice);
     }
 
-    private IntToByteBufferAdapter(ByteBuffer byteBuffer) {
+    private ByteBufferAsIntBuffer(ByteBuffer byteBuffer) {
         super(byteBuffer.capacity() / SizeOf.INT);
         this.byteBuffer = byteBuffer;
         this.byteBuffer.clear();
@@ -50,7 +50,7 @@
 
     @Override
     public IntBuffer asReadOnlyBuffer() {
-        IntToByteBufferAdapter buf = new IntToByteBufferAdapter(byteBuffer.asReadOnlyBuffer());
+        ByteBufferAsIntBuffer buf = new ByteBufferAsIntBuffer(byteBuffer.asReadOnlyBuffer());
         buf.limit = limit;
         buf.position = position;
         buf.mark = mark;
@@ -76,7 +76,7 @@
     @Override
     public IntBuffer duplicate() {
         ByteBuffer bb = byteBuffer.duplicate().order(byteBuffer.order());
-        IntToByteBufferAdapter buf = new IntToByteBufferAdapter(bb);
+        ByteBufferAsIntBuffer buf = new ByteBufferAsIntBuffer(bb);
         buf.limit = limit;
         buf.position = position;
         buf.mark = mark;
@@ -104,7 +104,7 @@
         if (byteBuffer instanceof DirectByteBuffer) {
             ((DirectByteBuffer) byteBuffer).get(dst, dstOffset, intCount);
         } else {
-            ((HeapByteBuffer) byteBuffer).get(dst, dstOffset, intCount);
+            ((ByteArrayBuffer) byteBuffer).get(dst, dstOffset, intCount);
         }
         this.position += intCount;
         return this;
@@ -157,10 +157,10 @@
     public IntBuffer put(int[] src, int srcOffset, int intCount) {
         byteBuffer.limit(limit * SizeOf.INT);
         byteBuffer.position(position * SizeOf.INT);
-        if (byteBuffer instanceof ReadWriteDirectByteBuffer) {
-            ((ReadWriteDirectByteBuffer) byteBuffer).put(src, srcOffset, intCount);
+        if (byteBuffer instanceof DirectByteBuffer) {
+            ((DirectByteBuffer) byteBuffer).put(src, srcOffset, intCount);
         } else {
-            ((ReadWriteHeapByteBuffer) byteBuffer).put(src, srcOffset, intCount);
+            ((ByteArrayBuffer) byteBuffer).put(src, srcOffset, intCount);
         }
         this.position += intCount;
         return this;
@@ -171,7 +171,7 @@
         byteBuffer.limit(limit * SizeOf.INT);
         byteBuffer.position(position * SizeOf.INT);
         ByteBuffer bb = byteBuffer.slice().order(byteBuffer.order());
-        IntBuffer result = new IntToByteBufferAdapter(bb);
+        IntBuffer result = new ByteBufferAsIntBuffer(bb);
         byteBuffer.clear();
         return result;
     }
diff --git a/luni/src/main/java/java/nio/LongToByteBufferAdapter.java b/luni/src/main/java/java/nio/ByteBufferAsLongBuffer.java
similarity index 86%
rename from luni/src/main/java/java/nio/LongToByteBufferAdapter.java
rename to luni/src/main/java/java/nio/ByteBufferAsLongBuffer.java
index e8bf8df..550c675 100644
--- a/luni/src/main/java/java/nio/LongToByteBufferAdapter.java
+++ b/luni/src/main/java/java/nio/ByteBufferAsLongBuffer.java
@@ -31,17 +31,17 @@
  * </p>
  *
  */
-final class LongToByteBufferAdapter extends LongBuffer {
+final class ByteBufferAsLongBuffer extends LongBuffer {
 
     private final ByteBuffer byteBuffer;
 
     static LongBuffer asLongBuffer(ByteBuffer byteBuffer) {
         ByteBuffer slice = byteBuffer.slice();
         slice.order(byteBuffer.order());
-        return new LongToByteBufferAdapter(slice);
+        return new ByteBufferAsLongBuffer(slice);
     }
 
-    private LongToByteBufferAdapter(ByteBuffer byteBuffer) {
+    private ByteBufferAsLongBuffer(ByteBuffer byteBuffer) {
         super(byteBuffer.capacity() / SizeOf.LONG);
         this.byteBuffer = byteBuffer;
         this.byteBuffer.clear();
@@ -50,7 +50,7 @@
 
     @Override
     public LongBuffer asReadOnlyBuffer() {
-        LongToByteBufferAdapter buf = new LongToByteBufferAdapter(byteBuffer.asReadOnlyBuffer());
+        ByteBufferAsLongBuffer buf = new ByteBufferAsLongBuffer(byteBuffer.asReadOnlyBuffer());
         buf.limit = limit;
         buf.position = position;
         buf.mark = mark;
@@ -76,7 +76,7 @@
     @Override
     public LongBuffer duplicate() {
         ByteBuffer bb = byteBuffer.duplicate().order(byteBuffer.order());
-        LongToByteBufferAdapter buf = new LongToByteBufferAdapter(bb);
+        ByteBufferAsLongBuffer buf = new ByteBufferAsLongBuffer(bb);
         buf.limit = limit;
         buf.position = position;
         buf.mark = mark;
@@ -104,7 +104,7 @@
         if (byteBuffer instanceof DirectByteBuffer) {
             ((DirectByteBuffer) byteBuffer).get(dst, dstOffset, longCount);
         } else {
-            ((HeapByteBuffer) byteBuffer).get(dst, dstOffset, longCount);
+            ((ByteArrayBuffer) byteBuffer).get(dst, dstOffset, longCount);
         }
         this.position += longCount;
         return this;
@@ -157,10 +157,10 @@
     public LongBuffer put(long[] src, int srcOffset, int longCount) {
         byteBuffer.limit(limit * SizeOf.LONG);
         byteBuffer.position(position * SizeOf.LONG);
-        if (byteBuffer instanceof ReadWriteDirectByteBuffer) {
-            ((ReadWriteDirectByteBuffer) byteBuffer).put(src, srcOffset, longCount);
+        if (byteBuffer instanceof DirectByteBuffer) {
+            ((DirectByteBuffer) byteBuffer).put(src, srcOffset, longCount);
         } else {
-            ((ReadWriteHeapByteBuffer) byteBuffer).put(src, srcOffset, longCount);
+            ((ByteArrayBuffer) byteBuffer).put(src, srcOffset, longCount);
         }
         this.position += longCount;
         return this;
@@ -171,7 +171,7 @@
         byteBuffer.limit(limit * SizeOf.LONG);
         byteBuffer.position(position * SizeOf.LONG);
         ByteBuffer bb = byteBuffer.slice().order(byteBuffer.order());
-        LongBuffer result = new LongToByteBufferAdapter(bb);
+        LongBuffer result = new ByteBufferAsLongBuffer(bb);
         byteBuffer.clear();
         return result;
     }
diff --git a/luni/src/main/java/java/nio/ShortToByteBufferAdapter.java b/luni/src/main/java/java/nio/ByteBufferAsShortBuffer.java
similarity index 86%
rename from luni/src/main/java/java/nio/ShortToByteBufferAdapter.java
rename to luni/src/main/java/java/nio/ByteBufferAsShortBuffer.java
index ee36709..ff81409 100644
--- a/luni/src/main/java/java/nio/ShortToByteBufferAdapter.java
+++ b/luni/src/main/java/java/nio/ByteBufferAsShortBuffer.java
@@ -30,17 +30,17 @@
  * </ul>
  * </p>
  */
-final class ShortToByteBufferAdapter extends ShortBuffer {
+final class ByteBufferAsShortBuffer extends ShortBuffer {
 
     private final ByteBuffer byteBuffer;
 
     static ShortBuffer asShortBuffer(ByteBuffer byteBuffer) {
         ByteBuffer slice = byteBuffer.slice();
         slice.order(byteBuffer.order());
-        return new ShortToByteBufferAdapter(slice);
+        return new ByteBufferAsShortBuffer(slice);
     }
 
-    private ShortToByteBufferAdapter(ByteBuffer byteBuffer) {
+    private ByteBufferAsShortBuffer(ByteBuffer byteBuffer) {
         super(byteBuffer.capacity() / SizeOf.SHORT);
         this.byteBuffer = byteBuffer;
         this.byteBuffer.clear();
@@ -49,7 +49,7 @@
 
     @Override
     public ShortBuffer asReadOnlyBuffer() {
-        ShortToByteBufferAdapter buf = new ShortToByteBufferAdapter(byteBuffer.asReadOnlyBuffer());
+        ByteBufferAsShortBuffer buf = new ByteBufferAsShortBuffer(byteBuffer.asReadOnlyBuffer());
         buf.limit = limit;
         buf.position = position;
         buf.mark = mark;
@@ -75,7 +75,7 @@
     @Override
     public ShortBuffer duplicate() {
         ByteBuffer bb = byteBuffer.duplicate().order(byteBuffer.order());
-        ShortToByteBufferAdapter buf = new ShortToByteBufferAdapter(bb);
+        ByteBufferAsShortBuffer buf = new ByteBufferAsShortBuffer(bb);
         buf.limit = limit;
         buf.position = position;
         buf.mark = mark;
@@ -103,7 +103,7 @@
         if (byteBuffer instanceof DirectByteBuffer) {
             ((DirectByteBuffer) byteBuffer).get(dst, dstOffset, shortCount);
         } else {
-            ((HeapByteBuffer) byteBuffer).get(dst, dstOffset, shortCount);
+            ((ByteArrayBuffer) byteBuffer).get(dst, dstOffset, shortCount);
         }
         this.position += shortCount;
         return this;
@@ -156,10 +156,10 @@
     public ShortBuffer put(short[] src, int srcOffset, int shortCount) {
         byteBuffer.limit(limit * SizeOf.SHORT);
         byteBuffer.position(position * SizeOf.SHORT);
-        if (byteBuffer instanceof ReadWriteDirectByteBuffer) {
-            ((ReadWriteDirectByteBuffer) byteBuffer).put(src, srcOffset, shortCount);
+        if (byteBuffer instanceof DirectByteBuffer) {
+            ((DirectByteBuffer) byteBuffer).put(src, srcOffset, shortCount);
         } else {
-            ((ReadWriteHeapByteBuffer) byteBuffer).put(src, srcOffset, shortCount);
+            ((ByteArrayBuffer) byteBuffer).put(src, srcOffset, shortCount);
         }
         this.position += shortCount;
         return this;
@@ -170,7 +170,7 @@
         byteBuffer.limit(limit * SizeOf.SHORT);
         byteBuffer.position(position * SizeOf.SHORT);
         ByteBuffer bb = byteBuffer.slice().order(byteBuffer.order());
-        ShortBuffer result = new ShortToByteBufferAdapter(bb);
+        ShortBuffer result = new ByteBufferAsShortBuffer(bb);
         byteBuffer.clear();
         return result;
     }
diff --git a/luni/src/main/java/java/nio/CharArrayBuffer.java b/luni/src/main/java/java/nio/CharArrayBuffer.java
index 53d7fb6..43a75a6 100644
--- a/luni/src/main/java/java/nio/CharArrayBuffer.java
+++ b/luni/src/main/java/java/nio/CharArrayBuffer.java
@@ -18,82 +18,153 @@
 package java.nio;
 
 /**
- * CharArrayBuffer, ReadWriteCharArrayBuffer and ReadOnlyCharArrayBuffer compose
- * the implementation of array based char buffers.
- * <p>
- * CharArrayBuffer implements all the shared readonly methods and is extended by
- * the other two classes.
- * </p>
- * <p>
- * All methods are marked final for runtime performance.
- * </p>
- *
+ * CharArrayBuffer implements char[]-based CharBuffers.
  */
-abstract class CharArrayBuffer extends CharBuffer {
+final class CharArrayBuffer extends CharBuffer {
 
-    protected final char[] backingArray;
+  private final char[] backingArray;
 
-    protected final int offset;
+  private final int arrayOffset;
 
-    CharArrayBuffer(char[] array) {
-        this(array.length, array, 0);
+  private final boolean isReadOnly;
+
+  CharArrayBuffer(char[] array) {
+    this(array.length, array, 0, false);
+  }
+
+  private CharArrayBuffer(int capacity, char[] backingArray, int arrayOffset, boolean isReadOnly) {
+    super(capacity);
+    this.backingArray = backingArray;
+    this.arrayOffset = arrayOffset;
+    this.isReadOnly = isReadOnly;
+  }
+
+  private static CharArrayBuffer copy(CharArrayBuffer other, int markOfOther, boolean isReadOnly) {
+    CharArrayBuffer buf = new CharArrayBuffer(other.capacity(), other.backingArray, other.arrayOffset, isReadOnly);
+    buf.limit = other.limit;
+    buf.position = other.position();
+    buf.mark = markOfOther;
+    return buf;
+  }
+
+  @Override public CharBuffer asReadOnlyBuffer() {
+    return copy(this, mark, true);
+  }
+
+  @Override public CharBuffer compact() {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
     }
+    System.arraycopy(backingArray, position + arrayOffset, backingArray, arrayOffset, remaining());
+    position = limit - position;
+    limit = capacity;
+    mark = UNSET_MARK;
+    return this;
+  }
 
-    CharArrayBuffer(int capacity) {
-        this(capacity, new char[capacity], 0);
-    }
+  @Override public CharBuffer duplicate() {
+    return copy(this, mark, isReadOnly);
+  }
 
-    CharArrayBuffer(int capacity, char[] backingArray, int offset) {
-        super(capacity);
-        this.backingArray = backingArray;
-        this.offset = offset;
-    }
+  @Override public CharBuffer slice() {
+    return new CharArrayBuffer(remaining(), backingArray, arrayOffset + position, isReadOnly);
+  }
 
-    @Override
-    public final char get() {
-        if (position == limit) {
-            throw new BufferUnderflowException();
-        }
-        return backingArray[offset + position++];
-    }
+  @Override public boolean isReadOnly() {
+    return isReadOnly;
+  }
 
-    @Override
-    public final char get(int index) {
-        checkIndex(index);
-        return backingArray[offset + index];
+  @Override char[] protectedArray() {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
     }
+    return backingArray;
+  }
 
-    @Override
-    public final CharBuffer get(char[] dst, int srcOffset, int charCount) {
-        if (charCount > remaining()) {
-            throw new BufferUnderflowException();
-        }
-        System.arraycopy(backingArray, offset + position, dst, srcOffset, charCount);
-        position += charCount;
-        return this;
+  @Override int protectedArrayOffset() {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
     }
+    return arrayOffset;
+  }
 
-    @Override
-    public final boolean isDirect() {
-        return false;
+  @Override boolean protectedHasArray() {
+    if (isReadOnly) {
+      return false;
     }
+    return true;
+  }
 
-    @Override
-    public final ByteOrder order() {
-        return ByteOrder.nativeOrder();
+  @Override public final char get() {
+    if (position == limit) {
+      throw new BufferUnderflowException();
     }
+    return backingArray[arrayOffset + position++];
+  }
 
-    @Override
-    public final CharSequence subSequence(int start, int end) {
-        checkStartEndRemaining(start, end);
-        CharBuffer result = duplicate();
-        result.limit(position + end);
-        result.position(position + start);
-        return result;
-    }
+  @Override public final char get(int index) {
+    checkIndex(index);
+    return backingArray[arrayOffset + index];
+  }
 
-    @Override
-    public final String toString() {
-        return String.copyValueOf(backingArray, offset + position, remaining());
+  @Override public final CharBuffer get(char[] dst, int srcOffset, int charCount) {
+    if (charCount > remaining()) {
+      throw new BufferUnderflowException();
     }
+    System.arraycopy(backingArray, arrayOffset + position, dst, srcOffset, charCount);
+    position += charCount;
+    return this;
+  }
+
+  @Override public final boolean isDirect() {
+    return false;
+  }
+
+  @Override public final ByteOrder order() {
+    return ByteOrder.nativeOrder();
+  }
+
+  @Override public final CharSequence subSequence(int start, int end) {
+    checkStartEndRemaining(start, end);
+    CharBuffer result = duplicate();
+    result.limit(position + end);
+    result.position(position + start);
+    return result;
+  }
+
+  @Override public CharBuffer put(char c) {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
+    }
+    if (position == limit) {
+      throw new BufferOverflowException();
+    }
+    backingArray[arrayOffset + position++] = c;
+    return this;
+  }
+
+  @Override public CharBuffer put(int index, char c) {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
+    }
+    checkIndex(index);
+    backingArray[arrayOffset + index] = c;
+    return this;
+  }
+
+  @Override public CharBuffer put(char[] src, int srcOffset, int charCount) {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
+    }
+    if (charCount > remaining()) {
+      throw new BufferOverflowException();
+    }
+    System.arraycopy(src, srcOffset, backingArray, arrayOffset + position, charCount);
+    position += charCount;
+    return this;
+  }
+
+  @Override public final String toString() {
+    return String.copyValueOf(backingArray, arrayOffset + position, remaining());
+  }
 }
diff --git a/luni/src/main/java/java/nio/CharBuffer.java b/luni/src/main/java/java/nio/CharBuffer.java
index 6429ae2..5c6f0fc 100644
--- a/luni/src/main/java/java/nio/CharBuffer.java
+++ b/luni/src/main/java/java/nio/CharBuffer.java
@@ -51,7 +51,7 @@
         if (capacity < 0) {
             throw new IllegalArgumentException("capacity < 0: " + capacity);
         }
-        return new ReadWriteCharArrayBuffer(capacity);
+        return new CharArrayBuffer(new char[capacity]);
     }
 
     /**
@@ -88,7 +88,7 @@
      */
     public static CharBuffer wrap(char[] array, int start, int charCount) {
         Arrays.checkOffsetAndCount(array.length, start, charCount);
-        CharBuffer buf = new ReadWriteCharArrayBuffer(array);
+        CharBuffer buf = new CharArrayBuffer(array);
         buf.position = start;
         buf.limit = start + charCount;
         return buf;
@@ -499,6 +499,9 @@
      *                if no changes may be made to the contents of this buffer.
      */
     public CharBuffer put(CharBuffer src) {
+        if (isReadOnly()) {
+            throw new ReadOnlyBufferException();
+        }
         if (src == this) {
             throw new IllegalArgumentException("src == this");
         }
@@ -568,6 +571,9 @@
      *                if no changes may be made to the contents of this buffer.
      */
     public CharBuffer put(String str, int start, int end) {
+        if (isReadOnly()) {
+            throw new ReadOnlyBufferException();
+        }
         if (start < 0 || end < start || end > str.length()) {
             throw new IndexOutOfBoundsException("str.length()=" + str.length() +
                     ", start=" + start + ", end=" + end);
diff --git a/luni/src/main/java/java/nio/DirectByteBuffer.java b/luni/src/main/java/java/nio/DirectByteBuffer.java
index 0ddef14..b624a41 100644
--- a/luni/src/main/java/java/nio/DirectByteBuffer.java
+++ b/luni/src/main/java/java/nio/DirectByteBuffer.java
@@ -17,208 +17,464 @@
 
 package java.nio;
 
+import java.nio.channels.FileChannel.MapMode;
+
+import libcore.io.Memory;
 import libcore.io.SizeOf;
 
-abstract class DirectByteBuffer extends BaseByteBuffer {
-    // This is the offset into {@code Buffer.block} at which this buffer logically starts.
-    // TODO: rewrite this so we set 'block' to an OffsetMemoryBlock?
-    protected final int offset;
+class DirectByteBuffer extends MappedByteBuffer {
+  // This is the offset into {@code Buffer.block} at which this buffer logically starts.
+  // TODO: rewrite this so we set 'block' to an OffsetMemoryBlock?
+  protected final int offset;
 
-    protected DirectByteBuffer(MemoryBlock block, int capacity, int offset) {
-        super(capacity, block);
+  private final boolean isReadOnly;
 
-        long baseSize = block.getSize();
-        if (baseSize >= 0 && (capacity + offset) > baseSize) {
-            throw new IllegalArgumentException("capacity + offset > baseSize");
-        }
+  protected DirectByteBuffer(MemoryBlock block, int capacity, int offset, boolean isReadOnly, MapMode mapMode) {
+    super(block, capacity, mapMode);
 
-        this.offset = offset;
-        this.effectiveDirectAddress = block.toInt() + offset;
+    long baseSize = block.getSize();
+    if (baseSize >= 0 && (capacity + offset) > baseSize) {
+      throw new IllegalArgumentException("capacity + offset > baseSize");
     }
 
-    @Override
-    public final ByteBuffer get(byte[] dst, int dstOffset, int byteCount) {
-        checkGetBounds(1, dst.length, dstOffset, byteCount);
-        this.block.peekByteArray(offset + position, dst, dstOffset, byteCount);
-        position += byteCount;
-        return this;
-    }
+    this.effectiveDirectAddress = block.toInt() + offset;
 
-    final void get(char[] dst, int dstOffset, int charCount) {
-        int byteCount = checkGetBounds(SizeOf.CHAR, dst.length, dstOffset, charCount);
-        this.block.peekCharArray(offset + position, dst, dstOffset, charCount, order.needsSwap);
-        position += byteCount;
-    }
+    this.offset = offset;
+    this.isReadOnly = isReadOnly;
+  }
 
-    final void get(double[] dst, int dstOffset, int doubleCount) {
-        int byteCount = checkGetBounds(SizeOf.DOUBLE, dst.length, dstOffset, doubleCount);
-        this.block.peekDoubleArray(offset + position, dst, dstOffset, doubleCount, order.needsSwap);
-        position += byteCount;
-    }
+  // Used by the JNI NewDirectByteBuffer function.
+  DirectByteBuffer(int address, int capacity) {
+    this(MemoryBlock.wrapFromJni(address, capacity), capacity, 0, false, null);
+  }
 
-    final void get(float[] dst, int dstOffset, int floatCount) {
-        int byteCount = checkGetBounds(SizeOf.FLOAT, dst.length, dstOffset, floatCount);
-        this.block.peekFloatArray(offset + position, dst, dstOffset, floatCount, order.needsSwap);
-        position += byteCount;
-    }
+  private static DirectByteBuffer copy(DirectByteBuffer other, int markOfOther, boolean isReadOnly) {
+    DirectByteBuffer buf = new DirectByteBuffer(other.block, other.capacity(), other.offset, isReadOnly, other.mapMode);
+    buf.limit = other.limit;
+    buf.position = other.position();
+    buf.mark = markOfOther;
+    return buf;
+  }
 
-    final void get(int[] dst, int dstOffset, int intCount) {
-        int byteCount = checkGetBounds(SizeOf.INT, dst.length, dstOffset, intCount);
-        this.block.peekIntArray(offset + position, dst, dstOffset, intCount, order.needsSwap);
-        position += byteCount;
-    }
+  @Override public ByteBuffer asReadOnlyBuffer() {
+    return copy(this, mark, true);
+  }
 
-    final void get(long[] dst, int dstOffset, int longCount) {
-        int byteCount = checkGetBounds(SizeOf.LONG, dst.length, dstOffset, longCount);
-        this.block.peekLongArray(offset + position, dst, dstOffset, longCount, order.needsSwap);
-        position += byteCount;
+  @Override public ByteBuffer compact() {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
     }
+    Memory.memmove(this, 0, this, position, remaining());
+    position = limit - position;
+    limit = capacity;
+    mark = UNSET_MARK;
+    return this;
+  }
 
-    final void get(short[] dst, int dstOffset, int shortCount) {
-        int byteCount = checkGetBounds(SizeOf.SHORT, dst.length, dstOffset, shortCount);
-        this.block.peekShortArray(offset + position, dst, dstOffset, shortCount, order.needsSwap);
-        position += byteCount;
-    }
+  @Override public ByteBuffer duplicate() {
+    return copy(this, mark, isReadOnly);
+  }
 
-    @Override
-    public final byte get() {
-        if (position == limit) {
-            throw new BufferUnderflowException();
-        }
-        return this.block.peekByte(offset + position++);
-    }
+  @Override public ByteBuffer slice() {
+    return new DirectByteBuffer(block, remaining(), offset + position, isReadOnly, mapMode);
+  }
 
-    @Override
-    public final byte get(int index) {
-        checkIndex(index);
-        return this.block.peekByte(offset + index);
-    }
+  @Override public boolean isReadOnly() {
+    return isReadOnly;
+  }
 
-    @Override
-    public final char getChar() {
-        int newPosition = position + SizeOf.CHAR;
-        if (newPosition > limit) {
-            throw new BufferUnderflowException();
-        }
-        char result = (char) this.block.peekShort(offset + position, order);
-        position = newPosition;
-        return result;
+  @Override byte[] protectedArray() {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
     }
+    byte[] array = this.block.array();
+    if (array == null) {
+      throw new UnsupportedOperationException();
+    }
+    return array;
+  }
 
-    @Override
-    public final char getChar(int index) {
-        checkIndex(index, SizeOf.CHAR);
-        return (char) this.block.peekShort(offset + index, order);
-    }
+  @Override int protectedArrayOffset() {
+    protectedArray(); // Throw if we don't have an array or are read-only.
+    return offset;
+  }
 
-    @Override
-    public final double getDouble() {
-        int newPosition = position + SizeOf.DOUBLE;
-        if (newPosition > limit) {
-            throw new BufferUnderflowException();
-        }
-        double result = Double.longBitsToDouble(this.block.peekLong(offset + position, order));
-        position = newPosition;
-        return result;
-    }
+  @Override boolean protectedHasArray() {
+    return !isReadOnly && (block.array() != null);
+  }
 
-    @Override
-    public final double getDouble(int index) {
-        checkIndex(index, SizeOf.DOUBLE);
-        return Double.longBitsToDouble(this.block.peekLong(offset + index, order));
-    }
+  @Override public final ByteBuffer get(byte[] dst, int dstOffset, int byteCount) {
+    checkGetBounds(1, dst.length, dstOffset, byteCount);
+    this.block.peekByteArray(offset + position, dst, dstOffset, byteCount);
+    position += byteCount;
+    return this;
+  }
 
-    @Override
-    public final float getFloat() {
-        int newPosition = position + SizeOf.FLOAT;
-        if (newPosition > limit) {
-            throw new BufferUnderflowException();
-        }
-        float result = Float.intBitsToFloat(this.block.peekInt(offset + position, order));
-        position = newPosition;
-        return result;
-    }
+  final void get(char[] dst, int dstOffset, int charCount) {
+    int byteCount = checkGetBounds(SizeOf.CHAR, dst.length, dstOffset, charCount);
+    this.block.peekCharArray(offset + position, dst, dstOffset, charCount, order.needsSwap);
+    position += byteCount;
+  }
 
-    @Override
-    public final float getFloat(int index) {
-        checkIndex(index, SizeOf.FLOAT);
-        return Float.intBitsToFloat(this.block.peekInt(offset + index, order));
-    }
+  final void get(double[] dst, int dstOffset, int doubleCount) {
+    int byteCount = checkGetBounds(SizeOf.DOUBLE, dst.length, dstOffset, doubleCount);
+    this.block.peekDoubleArray(offset + position, dst, dstOffset, doubleCount, order.needsSwap);
+    position += byteCount;
+  }
 
-    @Override
-    public final int getInt() {
-        int newPosition = position + SizeOf.INT;
-        if (newPosition > limit) {
-            throw new BufferUnderflowException();
-        }
-        int result = this.block.peekInt(offset + position, order);
-        position = newPosition;
-        return result;
-    }
+  final void get(float[] dst, int dstOffset, int floatCount) {
+    int byteCount = checkGetBounds(SizeOf.FLOAT, dst.length, dstOffset, floatCount);
+    this.block.peekFloatArray(offset + position, dst, dstOffset, floatCount, order.needsSwap);
+    position += byteCount;
+  }
 
-    @Override
-    public final int getInt(int index) {
-        checkIndex(index, SizeOf.INT);
-        return this.block.peekInt(offset + index, order);
-    }
+  final void get(int[] dst, int dstOffset, int intCount) {
+    int byteCount = checkGetBounds(SizeOf.INT, dst.length, dstOffset, intCount);
+    this.block.peekIntArray(offset + position, dst, dstOffset, intCount, order.needsSwap);
+    position += byteCount;
+  }
 
-    @Override
-    public final long getLong() {
-        int newPosition = position + SizeOf.LONG;
-        if (newPosition > limit) {
-            throw new BufferUnderflowException();
-        }
-        long result = this.block.peekLong(offset + position, order);
-        position = newPosition;
-        return result;
-    }
+  final void get(long[] dst, int dstOffset, int longCount) {
+    int byteCount = checkGetBounds(SizeOf.LONG, dst.length, dstOffset, longCount);
+    this.block.peekLongArray(offset + position, dst, dstOffset, longCount, order.needsSwap);
+    position += byteCount;
+  }
 
-    @Override
-    public final long getLong(int index) {
-        checkIndex(index, SizeOf.LONG);
-        return this.block.peekLong(offset + index, order);
-    }
+  final void get(short[] dst, int dstOffset, int shortCount) {
+    int byteCount = checkGetBounds(SizeOf.SHORT, dst.length, dstOffset, shortCount);
+    this.block.peekShortArray(offset + position, dst, dstOffset, shortCount, order.needsSwap);
+    position += byteCount;
+  }
 
-    @Override
-    public final short getShort() {
-        int newPosition = position + SizeOf.SHORT;
-        if (newPosition > limit) {
-            throw new BufferUnderflowException();
-        }
-        short result = this.block.peekShort(offset + position, order);
-        position = newPosition;
-        return result;
+  @Override public final byte get() {
+    if (position == limit) {
+      throw new BufferUnderflowException();
     }
+    return this.block.peekByte(offset + position++);
+  }
 
-    @Override
-    public final short getShort(int index) {
-        checkIndex(index, SizeOf.SHORT);
-        return this.block.peekShort(offset + index, order);
-    }
+  @Override public final byte get(int index) {
+    checkIndex(index);
+    return this.block.peekByte(offset + index);
+  }
 
-    @Override
-    public final boolean isDirect() {
-        return true;
+  @Override public final char getChar() {
+    int newPosition = position + SizeOf.CHAR;
+    if (newPosition > limit) {
+      throw new BufferUnderflowException();
     }
+    char result = (char) this.block.peekShort(offset + position, order);
+    position = newPosition;
+    return result;
+  }
 
-    public final void free() {
-        block.free();
-    }
+  @Override public final char getChar(int index) {
+    checkIndex(index, SizeOf.CHAR);
+    return (char) this.block.peekShort(offset + index, order);
+  }
 
-    @Override byte[] protectedArray() {
-        byte[] array = this.block.array();
-        if (array == null) {
-            throw new UnsupportedOperationException();
-        }
-        return array;
+  @Override public final double getDouble() {
+    int newPosition = position + SizeOf.DOUBLE;
+    if (newPosition > limit) {
+      throw new BufferUnderflowException();
     }
+    double result = Double.longBitsToDouble(this.block.peekLong(offset + position, order));
+    position = newPosition;
+    return result;
+  }
 
-    @Override int protectedArrayOffset() {
-        protectedArray(); // Throw if we don't have an array.
-        return offset;
-    }
+  @Override public final double getDouble(int index) {
+    checkIndex(index, SizeOf.DOUBLE);
+    return Double.longBitsToDouble(this.block.peekLong(offset + index, order));
+  }
 
-    @Override boolean protectedHasArray() {
-        return this.block.array() != null;
+  @Override public final float getFloat() {
+    int newPosition = position + SizeOf.FLOAT;
+    if (newPosition > limit) {
+      throw new BufferUnderflowException();
     }
+    float result = Float.intBitsToFloat(this.block.peekInt(offset + position, order));
+    position = newPosition;
+    return result;
+  }
+
+  @Override public final float getFloat(int index) {
+    checkIndex(index, SizeOf.FLOAT);
+    return Float.intBitsToFloat(this.block.peekInt(offset + index, order));
+  }
+
+  @Override public final int getInt() {
+    int newPosition = position + SizeOf.INT;
+    if (newPosition > limit) {
+      throw new BufferUnderflowException();
+    }
+    int result = this.block.peekInt(offset + position, order);
+    position = newPosition;
+    return result;
+  }
+
+  @Override public final int getInt(int index) {
+    checkIndex(index, SizeOf.INT);
+    return this.block.peekInt(offset + index, order);
+  }
+
+  @Override public final long getLong() {
+    int newPosition = position + SizeOf.LONG;
+    if (newPosition > limit) {
+      throw new BufferUnderflowException();
+    }
+    long result = this.block.peekLong(offset + position, order);
+    position = newPosition;
+    return result;
+  }
+
+  @Override public final long getLong(int index) {
+    checkIndex(index, SizeOf.LONG);
+    return this.block.peekLong(offset + index, order);
+  }
+
+  @Override public final short getShort() {
+    int newPosition = position + SizeOf.SHORT;
+    if (newPosition > limit) {
+      throw new BufferUnderflowException();
+    }
+    short result = this.block.peekShort(offset + position, order);
+    position = newPosition;
+    return result;
+  }
+
+  @Override public final short getShort(int index) {
+    checkIndex(index, SizeOf.SHORT);
+    return this.block.peekShort(offset + index, order);
+  }
+
+  @Override public final boolean isDirect() {
+    return true;
+  }
+
+  public final void free() {
+    block.free();
+  }
+
+  @Override public final CharBuffer asCharBuffer() {
+    return ByteBufferAsCharBuffer.asCharBuffer(this);
+  }
+
+  @Override public final DoubleBuffer asDoubleBuffer() {
+    return ByteBufferAsDoubleBuffer.asDoubleBuffer(this);
+  }
+
+  @Override public final FloatBuffer asFloatBuffer() {
+    return ByteBufferAsFloatBuffer.asFloatBuffer(this);
+  }
+
+  @Override public final IntBuffer asIntBuffer() {
+    return ByteBufferAsIntBuffer.asIntBuffer(this);
+  }
+
+  @Override public final LongBuffer asLongBuffer() {
+    return ByteBufferAsLongBuffer.asLongBuffer(this);
+  }
+
+  @Override public final ShortBuffer asShortBuffer() {
+    return ByteBufferAsShortBuffer.asShortBuffer(this);
+  }
+
+  @Override public ByteBuffer put(byte value) {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
+    }
+    if (position == limit) {
+      throw new BufferOverflowException();
+    }
+    this.block.pokeByte(offset + position++, value);
+    return this;
+  }
+
+  @Override public ByteBuffer put(int index, byte value) {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
+    }
+    checkIndex(index);
+    this.block.pokeByte(offset + index, value);
+    return this;
+  }
+
+  @Override public ByteBuffer put(byte[] src, int srcOffset, int byteCount) {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
+    }
+    checkPutBounds(1, src.length, srcOffset, byteCount);
+    this.block.pokeByteArray(offset + position, src, srcOffset, byteCount);
+    position += byteCount;
+    return this;
+  }
+
+  final void put(char[] src, int srcOffset, int charCount) {
+    int byteCount = checkPutBounds(SizeOf.CHAR, src.length, srcOffset, charCount);
+    this.block.pokeCharArray(offset + position, src, srcOffset, charCount, order.needsSwap);
+    position += byteCount;
+  }
+
+  final void put(double[] src, int srcOffset, int doubleCount) {
+    int byteCount = checkPutBounds(SizeOf.DOUBLE, src.length, srcOffset, doubleCount);
+    this.block.pokeDoubleArray(offset + position, src, srcOffset, doubleCount, order.needsSwap);
+    position += byteCount;
+  }
+
+  final void put(float[] src, int srcOffset, int floatCount) {
+    int byteCount = checkPutBounds(SizeOf.FLOAT, src.length, srcOffset, floatCount);
+    this.block.pokeFloatArray(offset + position, src, srcOffset, floatCount, order.needsSwap);
+    position += byteCount;
+  }
+
+  final void put(int[] src, int srcOffset, int intCount) {
+    int byteCount = checkPutBounds(SizeOf.INT, src.length, srcOffset, intCount);
+    this.block.pokeIntArray(offset + position, src, srcOffset, intCount, order.needsSwap);
+    position += byteCount;
+  }
+
+  final void put(long[] src, int srcOffset, int longCount) {
+    int byteCount = checkPutBounds(SizeOf.LONG, src.length, srcOffset, longCount);
+    this.block.pokeLongArray(offset + position, src, srcOffset, longCount, order.needsSwap);
+    position += byteCount;
+  }
+
+  final void put(short[] src, int srcOffset, int shortCount) {
+    int byteCount = checkPutBounds(SizeOf.SHORT, src.length, srcOffset, shortCount);
+    this.block.pokeShortArray(offset + position, src, srcOffset, shortCount, order.needsSwap);
+    position += byteCount;
+  }
+
+  @Override public ByteBuffer putChar(char value) {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
+    }
+    int newPosition = position + SizeOf.CHAR;
+    if (newPosition > limit) {
+      throw new BufferOverflowException();
+    }
+    this.block.pokeShort(offset + position, (short) value, order);
+    position = newPosition;
+    return this;
+  }
+
+  @Override public ByteBuffer putChar(int index, char value) {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
+    }
+    checkIndex(index, SizeOf.CHAR);
+    this.block.pokeShort(offset + index, (short) value, order);
+    return this;
+  }
+
+  @Override public ByteBuffer putDouble(double value) {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
+    }
+    int newPosition = position + SizeOf.DOUBLE;
+    if (newPosition > limit) {
+      throw new BufferOverflowException();
+    }
+    this.block.pokeLong(offset + position, Double.doubleToRawLongBits(value), order);
+    position = newPosition;
+    return this;
+  }
+
+  @Override public ByteBuffer putDouble(int index, double value) {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
+    }
+    checkIndex(index, SizeOf.DOUBLE);
+    this.block.pokeLong(offset + index, Double.doubleToRawLongBits(value), order);
+    return this;
+  }
+
+  @Override public ByteBuffer putFloat(float value) {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
+    }
+    int newPosition = position + SizeOf.FLOAT;
+    if (newPosition > limit) {
+      throw new BufferOverflowException();
+    }
+    this.block.pokeInt(offset + position, Float.floatToRawIntBits(value), order);
+    position = newPosition;
+    return this;
+  }
+
+  @Override public ByteBuffer putFloat(int index, float value) {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
+    }
+    checkIndex(index, SizeOf.FLOAT);
+    this.block.pokeInt(offset + index, Float.floatToRawIntBits(value), order);
+    return this;
+  }
+
+  @Override public ByteBuffer putInt(int value) {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
+    }
+    int newPosition = position + SizeOf.INT;
+    if (newPosition > limit) {
+      throw new BufferOverflowException();
+    }
+    this.block.pokeInt(offset + position, value, order);
+    position = newPosition;
+    return this;
+  }
+
+  @Override public ByteBuffer putInt(int index, int value) {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
+    }
+    checkIndex(index, SizeOf.INT);
+    this.block.pokeInt(offset + index, value, order);
+    return this;
+  }
+
+  @Override public ByteBuffer putLong(long value) {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
+    }
+    int newPosition = position + SizeOf.LONG;
+    if (newPosition > limit) {
+      throw new BufferOverflowException();
+    }
+    this.block.pokeLong(offset + position, value, order);
+    position = newPosition;
+    return this;
+  }
+
+  @Override public ByteBuffer putLong(int index, long value) {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
+    }
+    checkIndex(index, SizeOf.LONG);
+    this.block.pokeLong(offset + index, value, order);
+    return this;
+  }
+
+  @Override public ByteBuffer putShort(short value) {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
+    }
+    int newPosition = position + SizeOf.SHORT;
+    if (newPosition > limit) {
+      throw new BufferOverflowException();
+    }
+    this.block.pokeShort(offset + position, value, order);
+    position = newPosition;
+    return this;
+  }
+
+  @Override public ByteBuffer putShort(int index, short value) {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
+    }
+    checkIndex(index, SizeOf.SHORT);
+    this.block.pokeShort(offset + index, value, order);
+    return this;
+  }
 }
diff --git a/luni/src/main/java/java/nio/DoubleArrayBuffer.java b/luni/src/main/java/java/nio/DoubleArrayBuffer.java
index ec32a80..f8e4e59 100644
--- a/luni/src/main/java/java/nio/DoubleArrayBuffer.java
+++ b/luni/src/main/java/java/nio/DoubleArrayBuffer.java
@@ -18,69 +18,141 @@
 package java.nio;
 
 /**
- * DoubleArrayBuffer, ReadWriteDoubleArrayBuffer and ReadOnlyDoubleArrayBuffer
- * compose the implementation of array based double buffers.
- * <p>
- * DoubleArrayBuffer implements all the shared readonly methods and is extended
- * by the other two classes.
- * </p>
- * <p>
- * All methods are marked final for runtime performance.
- * </p>
- *
+ * DoubleArrayBuffer implements double[]-based DoubleBuffers.
  */
-abstract class DoubleArrayBuffer extends DoubleBuffer {
+final class DoubleArrayBuffer extends DoubleBuffer {
 
-    protected final double[] backingArray;
+  private final double[] backingArray;
 
-    protected final int offset;
+  private final int arrayOffset;
 
-    DoubleArrayBuffer(double[] array) {
-        this(array.length, array, 0);
+  private final boolean isReadOnly;
+
+  DoubleArrayBuffer(double[] array) {
+    this(array.length, array, 0, false);
+  }
+
+  private DoubleArrayBuffer(int capacity, double[] backingArray, int arrayOffset, boolean isReadOnly) {
+    super(capacity);
+    this.backingArray = backingArray;
+    this.arrayOffset = arrayOffset;
+    this.isReadOnly = isReadOnly;
+  }
+
+  private static DoubleArrayBuffer copy(DoubleArrayBuffer other, int markOfOther, boolean isReadOnly) {
+    DoubleArrayBuffer buf = new DoubleArrayBuffer(other.capacity(), other.backingArray, other.arrayOffset, isReadOnly);
+    buf.limit = other.limit;
+    buf.position = other.position();
+    buf.mark = markOfOther;
+    return buf;
+  }
+
+  @Override public DoubleBuffer asReadOnlyBuffer() {
+    return copy(this, mark, true);
+  }
+
+  @Override public DoubleBuffer compact() {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
     }
+    System.arraycopy(backingArray, position + arrayOffset, backingArray, arrayOffset, remaining());
+    position = limit - position;
+    limit = capacity;
+    mark = UNSET_MARK;
+    return this;
+  }
 
-    DoubleArrayBuffer(int capacity) {
-        this(capacity, new double[capacity], 0);
+  @Override public DoubleBuffer duplicate() {
+    return copy(this, mark, isReadOnly);
+  }
+
+  @Override public DoubleBuffer slice() {
+    return new DoubleArrayBuffer(remaining(), backingArray, arrayOffset + position, isReadOnly);
+  }
+
+  @Override public boolean isReadOnly() {
+    return isReadOnly;
+  }
+
+  @Override double[] protectedArray() {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
     }
+    return backingArray;
+  }
 
-    DoubleArrayBuffer(int capacity, double[] backingArray, int offset) {
-        super(capacity);
-        this.backingArray = backingArray;
-        this.offset = offset;
+  @Override int protectedArrayOffset() {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
     }
+    return arrayOffset;
+  }
 
-    @Override
-    public final double get() {
-        if (position == limit) {
-            throw new BufferUnderflowException();
-        }
-        return backingArray[offset + position++];
+  @Override boolean protectedHasArray() {
+    if (isReadOnly) {
+      return false;
     }
+    return true;
+  }
 
-    @Override
-    public final double get(int index) {
-        checkIndex(index);
-        return backingArray[offset + index];
+  @Override public final double get() {
+    if (position == limit) {
+      throw new BufferUnderflowException();
     }
+    return backingArray[arrayOffset + position++];
+  }
 
-    @Override
-    public final DoubleBuffer get(double[] dst, int dstOffset, int doubleCount) {
-        if (doubleCount > remaining()) {
-            throw new BufferUnderflowException();
-        }
-        System.arraycopy(backingArray, offset + position, dst, dstOffset, doubleCount);
-        position += doubleCount;
-        return this;
+  @Override public final double get(int index) {
+    checkIndex(index);
+    return backingArray[arrayOffset + index];
+  }
+
+  @Override public final DoubleBuffer get(double[] dst, int dstOffset, int doubleCount) {
+    if (doubleCount > remaining()) {
+      throw new BufferUnderflowException();
     }
+    System.arraycopy(backingArray, arrayOffset + position, dst, dstOffset, doubleCount);
+    position += doubleCount;
+    return this;
+  }
 
-    @Override
-    public final boolean isDirect() {
-        return false;
+  @Override public final boolean isDirect() {
+    return false;
+  }
+
+  @Override public final ByteOrder order() {
+    return ByteOrder.nativeOrder();
+  }
+
+  @Override public DoubleBuffer put(double c) {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
     }
-
-    @Override
-    public final ByteOrder order() {
-        return ByteOrder.nativeOrder();
+    if (position == limit) {
+      throw new BufferOverflowException();
     }
+    backingArray[arrayOffset + position++] = c;
+    return this;
+  }
 
+  @Override public DoubleBuffer put(int index, double c) {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
+    }
+    checkIndex(index);
+    backingArray[arrayOffset + index] = c;
+    return this;
+  }
+
+  @Override public DoubleBuffer put(double[] src, int srcOffset, int doubleCount) {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
+    }
+    if (doubleCount > remaining()) {
+      throw new BufferOverflowException();
+    }
+    System.arraycopy(src, srcOffset, backingArray, arrayOffset + position, doubleCount);
+    position += doubleCount;
+    return this;
+  }
 }
diff --git a/luni/src/main/java/java/nio/DoubleBuffer.java b/luni/src/main/java/java/nio/DoubleBuffer.java
index 8d90f89..401536d 100644
--- a/luni/src/main/java/java/nio/DoubleBuffer.java
+++ b/luni/src/main/java/java/nio/DoubleBuffer.java
@@ -49,7 +49,7 @@
         if (capacity < 0) {
             throw new IllegalArgumentException("capacity < 0: " + capacity);
         }
-        return new ReadWriteDoubleArrayBuffer(capacity);
+        return new DoubleArrayBuffer(new double[capacity]);
     }
 
     /**
@@ -86,7 +86,7 @@
      */
     public static DoubleBuffer wrap(double[] array, int start, int doubleCount) {
         Arrays.checkOffsetAndCount(array.length, start, doubleCount);
-        DoubleBuffer buf = new ReadWriteDoubleArrayBuffer(array);
+        DoubleBuffer buf = new DoubleArrayBuffer(array);
         buf.position = start;
         buf.limit = start + doubleCount;
         return buf;
@@ -437,6 +437,9 @@
      *                if no changes may be made to the contents of this buffer.
      */
     public DoubleBuffer put(DoubleBuffer src) {
+        if (isReadOnly()) {
+            throw new ReadOnlyBufferException();
+        }
         if (src == this) {
             throw new IllegalArgumentException("src == this");
         }
diff --git a/luni/src/main/java/java/nio/FileChannelImpl.java b/luni/src/main/java/java/nio/FileChannelImpl.java
index 98be748..14e06b7 100644
--- a/luni/src/main/java/java/nio/FileChannelImpl.java
+++ b/luni/src/main/java/java/nio/FileChannelImpl.java
@@ -247,7 +247,7 @@
         long alignment = position - position % Libcore.os.sysconf(_SC_PAGE_SIZE);
         int offset = (int) (position - alignment);
         MemoryBlock block = MemoryBlock.mmap(fd, alignment, size + offset, mapMode);
-        return new MappedByteBufferAdapter(block, (int) size, offset, mapMode);
+        return new DirectByteBuffer(block, (int) size, offset, (mapMode == MapMode.READ_ONLY), mapMode);
     }
 
     public long position() throws IOException {
diff --git a/luni/src/main/java/java/nio/FloatArrayBuffer.java b/luni/src/main/java/java/nio/FloatArrayBuffer.java
index c47ee21..698174c 100644
--- a/luni/src/main/java/java/nio/FloatArrayBuffer.java
+++ b/luni/src/main/java/java/nio/FloatArrayBuffer.java
@@ -18,69 +18,142 @@
 package java.nio;
 
 /**
- * FloatArrayBuffer, ReadWriteFloatArrayBuffer and ReadOnlyFloatArrayBuffer
- * compose the implementation of array based float buffers.
- * <p>
- * FloatArrayBuffer implements all the shared readonly methods and is extended
- * by the other two classes.
- * </p>
- * <p>
- * All methods are marked final for runtime performance.
- * </p>
- *
+ * FloatArrayBuffer implements float[]-based FloatBuffers.
  */
-abstract class FloatArrayBuffer extends FloatBuffer {
+final class FloatArrayBuffer extends FloatBuffer {
 
-    protected final float[] backingArray;
+  private final float[] backingArray;
 
-    protected final int offset;
+  private final int arrayOffset;
 
-    FloatArrayBuffer(float[] array) {
-        this(array.length, array, 0);
+  private final boolean isReadOnly;
+
+  FloatArrayBuffer(float[] array) {
+    this(array.length, array, 0, false);
+  }
+
+  private FloatArrayBuffer(int capacity, float[] backingArray, int arrayOffset, boolean isReadOnly) {
+    super(capacity);
+    this.backingArray = backingArray;
+    this.arrayOffset = arrayOffset;
+    this.isReadOnly = isReadOnly;
+  }
+
+  private static FloatArrayBuffer copy(FloatArrayBuffer other, int markOfOther, boolean isReadOnly) {
+    FloatArrayBuffer buf = new FloatArrayBuffer(other.capacity(), other.backingArray, other.arrayOffset, isReadOnly);
+    buf.limit = other.limit;
+    buf.position = other.position();
+    buf.mark = markOfOther;
+    return buf;
+  }
+
+  @Override public FloatBuffer asReadOnlyBuffer() {
+    return copy(this, mark, true);
+  }
+
+
+  @Override public FloatBuffer compact() {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
     }
+    System.arraycopy(backingArray, position + arrayOffset, backingArray, arrayOffset, remaining());
+    position = limit - position;
+    limit = capacity;
+    mark = UNSET_MARK;
+    return this;
+  }
 
-    FloatArrayBuffer(int capacity) {
-        this(capacity, new float[capacity], 0);
+  @Override public FloatBuffer duplicate() {
+    return copy(this, mark, isReadOnly);
+  }
+
+  @Override public FloatBuffer slice() {
+    return new FloatArrayBuffer(remaining(), backingArray, arrayOffset + position, isReadOnly);
+  }
+
+  @Override public boolean isReadOnly() {
+    return isReadOnly;
+  }
+
+  @Override float[] protectedArray() {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
     }
+    return backingArray;
+  }
 
-    FloatArrayBuffer(int capacity, float[] backingArray, int offset) {
-        super(capacity);
-        this.backingArray = backingArray;
-        this.offset = offset;
+  @Override int protectedArrayOffset() {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
     }
+    return arrayOffset;
+  }
 
-    @Override
-    public final float get() {
-        if (position == limit) {
-            throw new BufferUnderflowException();
-        }
-        return backingArray[offset + position++];
+  @Override boolean protectedHasArray() {
+    if (isReadOnly) {
+      return false;
     }
+    return true;
+  }
 
-    @Override
-    public final float get(int index) {
-        checkIndex(index);
-        return backingArray[offset + index];
+  @Override public final float get() {
+    if (position == limit) {
+      throw new BufferUnderflowException();
     }
+    return backingArray[arrayOffset + position++];
+  }
 
-    @Override
-    public final FloatBuffer get(float[] dst, int dstOffset, int floatCount) {
-        if (floatCount > remaining()) {
-            throw new BufferUnderflowException();
-        }
-        System.arraycopy(backingArray, offset + position, dst, dstOffset, floatCount);
-        position += floatCount;
-        return this;
+  @Override public final float get(int index) {
+    checkIndex(index);
+    return backingArray[arrayOffset + index];
+  }
+
+  @Override public final FloatBuffer get(float[] dst, int dstOffset, int floatCount) {
+    if (floatCount > remaining()) {
+      throw new BufferUnderflowException();
     }
+    System.arraycopy(backingArray, arrayOffset + position, dst, dstOffset, floatCount);
+    position += floatCount;
+    return this;
+  }
 
-    @Override
-    public final boolean isDirect() {
-        return false;
+  @Override public final boolean isDirect() {
+    return false;
+  }
+
+  @Override public final ByteOrder order() {
+    return ByteOrder.nativeOrder();
+  }
+
+  @Override public FloatBuffer put(float c) {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
     }
-
-    @Override
-    public final ByteOrder order() {
-        return ByteOrder.nativeOrder();
+    if (position == limit) {
+      throw new BufferOverflowException();
     }
+    backingArray[arrayOffset + position++] = c;
+    return this;
+  }
 
+  @Override public FloatBuffer put(int index, float c) {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
+    }
+    checkIndex(index);
+    backingArray[arrayOffset + index] = c;
+    return this;
+  }
+
+  @Override public FloatBuffer put(float[] src, int srcOffset, int floatCount) {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
+    }
+    if (floatCount > remaining()) {
+      throw new BufferOverflowException();
+    }
+    System.arraycopy(src, srcOffset, backingArray, arrayOffset + position, floatCount);
+    position += floatCount;
+    return this;
+  }
 }
diff --git a/luni/src/main/java/java/nio/FloatBuffer.java b/luni/src/main/java/java/nio/FloatBuffer.java
index 814eb53..ac5e572 100644
--- a/luni/src/main/java/java/nio/FloatBuffer.java
+++ b/luni/src/main/java/java/nio/FloatBuffer.java
@@ -48,7 +48,7 @@
         if (capacity < 0) {
             throw new IllegalArgumentException("capacity < 0: " + capacity);
         }
-        return new ReadWriteFloatArrayBuffer(capacity);
+        return new FloatArrayBuffer(new float[capacity]);
     }
 
     /**
@@ -87,7 +87,7 @@
      */
     public static FloatBuffer wrap(float[] array, int start, int floatCount) {
         Arrays.checkOffsetAndCount(array.length, start, floatCount);
-        FloatBuffer buf = new ReadWriteFloatArrayBuffer(array);
+        FloatBuffer buf = new FloatArrayBuffer(array);
         buf.position = start;
         buf.limit = start + floatCount;
         return buf;
@@ -436,6 +436,9 @@
      *                if no changes may be made to the contents of this buffer.
      */
     public FloatBuffer put(FloatBuffer src) {
+        if (isReadOnly()) {
+            throw new ReadOnlyBufferException();
+        }
         if (src == this) {
             throw new IllegalArgumentException("src == this");
         }
diff --git a/luni/src/main/java/java/nio/HeapByteBuffer.java b/luni/src/main/java/java/nio/HeapByteBuffer.java
deleted file mode 100644
index 6b65ec1..0000000
--- a/luni/src/main/java/java/nio/HeapByteBuffer.java
+++ /dev/null
@@ -1,211 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one or more
- *  contributor license agreements.  See the NOTICE file distributed with
- *  this work for additional information regarding copyright ownership.
- *  The ASF licenses this file to You 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 java.nio;
-
-import libcore.io.SizeOf;
-import libcore.io.Memory;
-
-/**
- * HeapByteBuffer, ReadWriteHeapByteBuffer and ReadOnlyHeapByteBuffer compose
- * the implementation of array based byte buffers.
- * <p>
- * HeapByteBuffer implements all the shared readonly methods and is extended by
- * the other two classes.
- * </p>
- * <p>
- * All methods are marked final for runtime performance.
- * </p>
- *
- */
-abstract class HeapByteBuffer extends BaseByteBuffer {
-
-    /**
-     * These fields are non-private for NioUtils.unsafeArray.
-     */
-    final byte[] backingArray;
-    final int offset;
-
-    HeapByteBuffer(byte[] backingArray) {
-        this(backingArray, backingArray.length, 0);
-    }
-
-    HeapByteBuffer(int capacity) {
-        this(new byte[capacity], capacity, 0);
-    }
-
-    HeapByteBuffer(byte[] backingArray, int capacity, int offset) {
-        super(capacity, null);
-        this.backingArray = backingArray;
-        this.offset = offset;
-        if (offset + capacity > backingArray.length) {
-            throw new IndexOutOfBoundsException("backingArray.length=" + backingArray.length +
-                    ", capacity=" + capacity + ", offset=" + offset);
-        }
-    }
-
-    @Override
-    public final ByteBuffer get(byte[] dst, int dstOffset, int byteCount) {
-        checkGetBounds(1, dst.length, dstOffset, byteCount);
-        System.arraycopy(backingArray, offset + position, dst, dstOffset, byteCount);
-        position += byteCount;
-        return this;
-    }
-
-    final void get(char[] dst, int dstOffset, int charCount) {
-        int byteCount = checkGetBounds(SizeOf.CHAR, dst.length, dstOffset, charCount);
-        Memory.unsafeBulkGet(dst, dstOffset, byteCount, backingArray, offset + position, SizeOf.CHAR, order.needsSwap);
-        position += byteCount;
-    }
-
-    final void get(double[] dst, int dstOffset, int doubleCount) {
-        int byteCount = checkGetBounds(SizeOf.DOUBLE, dst.length, dstOffset, doubleCount);
-        Memory.unsafeBulkGet(dst, dstOffset, byteCount, backingArray, offset + position, SizeOf.DOUBLE, order.needsSwap);
-        position += byteCount;
-    }
-
-    final void get(float[] dst, int dstOffset, int floatCount) {
-        int byteCount = checkGetBounds(SizeOf.FLOAT, dst.length, dstOffset, floatCount);
-        Memory.unsafeBulkGet(dst, dstOffset, byteCount, backingArray, offset + position, SizeOf.FLOAT, order.needsSwap);
-        position += byteCount;
-    }
-
-    final void get(int[] dst, int dstOffset, int intCount) {
-        int byteCount = checkGetBounds(SizeOf.INT, dst.length, dstOffset, intCount);
-        Memory.unsafeBulkGet(dst, dstOffset, byteCount, backingArray, offset + position, SizeOf.INT, order.needsSwap);
-        position += byteCount;
-    }
-
-    final void get(long[] dst, int dstOffset, int longCount) {
-        int byteCount = checkGetBounds(SizeOf.LONG, dst.length, dstOffset, longCount);
-        Memory.unsafeBulkGet(dst, dstOffset, byteCount, backingArray, offset + position, SizeOf.LONG, order.needsSwap);
-        position += byteCount;
-    }
-
-    final void get(short[] dst, int dstOffset, int shortCount) {
-        int byteCount = checkGetBounds(SizeOf.SHORT, dst.length, dstOffset, shortCount);
-        Memory.unsafeBulkGet(dst, dstOffset, byteCount, backingArray, offset + position, SizeOf.SHORT, order.needsSwap);
-        position += byteCount;
-    }
-
-    @Override
-    public final byte get() {
-        if (position == limit) {
-            throw new BufferUnderflowException();
-        }
-        return backingArray[offset + position++];
-    }
-
-    @Override
-    public final byte get(int index) {
-        checkIndex(index);
-        return backingArray[offset + index];
-    }
-
-    @Override
-    public final char getChar() {
-        int newPosition = position + SizeOf.CHAR;
-        if (newPosition > limit) {
-            throw new BufferUnderflowException();
-        }
-        char result = (char) Memory.peekShort(backingArray, offset + position, order);
-        position = newPosition;
-        return result;
-    }
-
-    @Override
-    public final char getChar(int index) {
-        checkIndex(index, SizeOf.CHAR);
-        return (char) Memory.peekShort(backingArray, offset + index, order);
-    }
-
-    @Override
-    public final double getDouble() {
-        return Double.longBitsToDouble(getLong());
-    }
-
-    @Override
-    public final double getDouble(int index) {
-        return Double.longBitsToDouble(getLong(index));
-    }
-
-    @Override
-    public final float getFloat() {
-        return Float.intBitsToFloat(getInt());
-    }
-
-    @Override
-    public final float getFloat(int index) {
-        return Float.intBitsToFloat(getInt(index));
-    }
-
-    @Override
-    public final int getInt() {
-        int newPosition = position + SizeOf.INT;
-        if (newPosition > limit) {
-            throw new BufferUnderflowException();
-        }
-        int result = Memory.peekInt(backingArray, offset + position, order);
-        position = newPosition;
-        return result;
-    }
-
-    @Override
-    public final int getInt(int index) {
-        checkIndex(index, SizeOf.INT);
-        return Memory.peekInt(backingArray, offset + index, order);
-    }
-
-    @Override
-    public final long getLong() {
-        int newPosition = position + SizeOf.LONG;
-        if (newPosition > limit) {
-            throw new BufferUnderflowException();
-        }
-        long result = Memory.peekLong(backingArray, offset + position, order);
-        position = newPosition;
-        return result;
-    }
-
-    @Override
-    public final long getLong(int index) {
-        checkIndex(index, SizeOf.LONG);
-        return Memory.peekLong(backingArray, offset + index, order);
-    }
-
-    @Override
-    public final short getShort() {
-        int newPosition = position + SizeOf.SHORT;
-        if (newPosition > limit) {
-            throw new BufferUnderflowException();
-        }
-        short result = Memory.peekShort(backingArray, offset + position, order);
-        position = newPosition;
-        return result;
-    }
-
-    @Override
-    public final short getShort(int index) {
-        checkIndex(index, SizeOf.SHORT);
-        return Memory.peekShort(backingArray, offset + index, order);
-    }
-
-    @Override
-    public final boolean isDirect() {
-        return false;
-    }
-}
diff --git a/luni/src/main/java/java/nio/IntArrayBuffer.java b/luni/src/main/java/java/nio/IntArrayBuffer.java
index d031415..a5f9f39 100644
--- a/luni/src/main/java/java/nio/IntArrayBuffer.java
+++ b/luni/src/main/java/java/nio/IntArrayBuffer.java
@@ -18,69 +18,141 @@
 package java.nio;
 
 /**
- * IntArrayBuffer, ReadWriteIntArrayBuffer and ReadOnlyIntArrayBuffer compose
- * the implementation of array based int buffers.
- * <p>
- * IntArrayBuffer implements all the shared readonly methods and is extended by
- * the other two classes.
- * </p>
- * <p>
- * All methods are marked final for runtime performance.
- * </p>
- *
+ * IntArrayBuffer implements int[]-based IntBuffers.
  */
-abstract class IntArrayBuffer extends IntBuffer {
+final class IntArrayBuffer extends IntBuffer {
 
-    protected final int[] backingArray;
+  private final int[] backingArray;
 
-    protected final int offset;
+  private final int arrayOffset;
 
-    IntArrayBuffer(int[] array) {
-        this(array.length, array, 0);
+  private final boolean isReadOnly;
+
+  IntArrayBuffer(int[] array) {
+    this(array.length, array, 0, false);
+  }
+
+  private IntArrayBuffer(int capacity, int[] backingArray, int arrayOffset, boolean isReadOnly) {
+    super(capacity);
+    this.backingArray = backingArray;
+    this.arrayOffset = arrayOffset;
+    this.isReadOnly = isReadOnly;
+  }
+
+  private static IntArrayBuffer copy(IntArrayBuffer other, int markOfOther, boolean isReadOnly) {
+    IntArrayBuffer buf = new IntArrayBuffer(other.capacity(), other.backingArray, other.arrayOffset, isReadOnly);
+    buf.limit = other.limit;
+    buf.position = other.position();
+    buf.mark = markOfOther;
+    return buf;
+  }
+
+  @Override public IntBuffer asReadOnlyBuffer() {
+    return copy(this, mark, true);
+  }
+
+  @Override public IntBuffer compact() {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
     }
+    System.arraycopy(backingArray, position + arrayOffset, backingArray, arrayOffset, remaining());
+    position = limit - position;
+    limit = capacity;
+    mark = UNSET_MARK;
+    return this;
+  }
 
-    IntArrayBuffer(int capacity) {
-        this(capacity, new int[capacity], 0);
+  @Override public IntBuffer duplicate() {
+    return copy(this, mark, isReadOnly);
+  }
+
+  @Override public IntBuffer slice() {
+    return new IntArrayBuffer(remaining(), backingArray, arrayOffset + position, isReadOnly);
+  }
+
+  @Override public boolean isReadOnly() {
+    return isReadOnly;
+  }
+
+  @Override int[] protectedArray() {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
     }
+    return backingArray;
+  }
 
-    IntArrayBuffer(int capacity, int[] backingArray, int offset) {
-        super(capacity);
-        this.backingArray = backingArray;
-        this.offset = offset;
+  @Override int protectedArrayOffset() {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
     }
+    return arrayOffset;
+  }
 
-    @Override
-    public final int get() {
-        if (position == limit) {
-            throw new BufferUnderflowException();
-        }
-        return backingArray[offset + position++];
+  @Override boolean protectedHasArray() {
+    if (isReadOnly) {
+      return false;
     }
+    return true;
+  }
 
-    @Override
-    public final int get(int index) {
-        checkIndex(index);
-        return backingArray[offset + index];
+  @Override public final int get() {
+    if (position == limit) {
+      throw new BufferUnderflowException();
     }
+    return backingArray[arrayOffset + position++];
+  }
 
-    @Override
-    public final IntBuffer get(int[] dst, int dstOffset, int intCount) {
-        if (intCount > remaining()) {
-            throw new BufferUnderflowException();
-        }
-        System.arraycopy(backingArray, offset + position, dst, dstOffset, intCount);
-        position += intCount;
-        return this;
+  @Override public final int get(int index) {
+    checkIndex(index);
+    return backingArray[arrayOffset + index];
+  }
+
+  @Override public final IntBuffer get(int[] dst, int dstOffset, int intCount) {
+    if (intCount > remaining()) {
+      throw new BufferUnderflowException();
     }
+    System.arraycopy(backingArray, arrayOffset + position, dst, dstOffset, intCount);
+    position += intCount;
+    return this;
+  }
 
-    @Override
-    public final boolean isDirect() {
-        return false;
+  @Override public final boolean isDirect() {
+    return false;
+  }
+
+  @Override public final ByteOrder order() {
+    return ByteOrder.nativeOrder();
+  }
+
+  @Override public IntBuffer put(int c) {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
     }
-
-    @Override
-    public final ByteOrder order() {
-        return ByteOrder.nativeOrder();
+    if (position == limit) {
+      throw new BufferOverflowException();
     }
+    backingArray[arrayOffset + position++] = c;
+    return this;
+  }
 
+  @Override public IntBuffer put(int index, int c) {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
+    }
+    checkIndex(index);
+    backingArray[arrayOffset + index] = c;
+    return this;
+  }
+
+  @Override public IntBuffer put(int[] src, int srcOffset, int intCount) {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
+    }
+    if (intCount > remaining()) {
+      throw new BufferOverflowException();
+    }
+    System.arraycopy(src, srcOffset, backingArray, arrayOffset + position, intCount);
+    position += intCount;
+    return this;
+  }
 }
diff --git a/luni/src/main/java/java/nio/IntBuffer.java b/luni/src/main/java/java/nio/IntBuffer.java
index 0ff758a..bbcc2e3 100644
--- a/luni/src/main/java/java/nio/IntBuffer.java
+++ b/luni/src/main/java/java/nio/IntBuffer.java
@@ -46,7 +46,7 @@
         if (capacity < 0) {
             throw new IllegalArgumentException("capacity < 0: " + capacity);
         }
-        return new ReadWriteIntArrayBuffer(capacity);
+        return new IntArrayBuffer(new int[capacity]);
     }
 
     /**
@@ -83,7 +83,7 @@
      */
     public static IntBuffer wrap(int[] array, int start, int intCount) {
         Arrays.checkOffsetAndCount(array.length, start, intCount);
-        IntBuffer buf = new ReadWriteIntArrayBuffer(array);
+        IntBuffer buf = new IntArrayBuffer(array);
         buf.position = start;
         buf.limit = start + intCount;
         return buf;
@@ -395,6 +395,9 @@
      *                if no changes may be made to the contents of this buffer.
      */
     public IntBuffer put(int[] src, int srcOffset, int intCount) {
+        if (isReadOnly()) {
+            throw new ReadOnlyBufferException();
+        }
         Arrays.checkOffsetAndCount(src.length, srcOffset, intCount);
         if (intCount > remaining()) {
             throw new BufferOverflowException();
@@ -422,6 +425,9 @@
      *                if no changes may be made to the contents of this buffer.
      */
     public IntBuffer put(IntBuffer src) {
+        if (isReadOnly()) {
+            throw new ReadOnlyBufferException();
+        }
         if (src == this) {
             throw new IllegalArgumentException("src == this");
         }
diff --git a/luni/src/main/java/java/nio/LongArrayBuffer.java b/luni/src/main/java/java/nio/LongArrayBuffer.java
index 8dd0cfb..6419d73 100644
--- a/luni/src/main/java/java/nio/LongArrayBuffer.java
+++ b/luni/src/main/java/java/nio/LongArrayBuffer.java
@@ -18,69 +18,142 @@
 package java.nio;
 
 /**
- * LongArrayBuffer, ReadWriteLongArrayBuffer and ReadOnlyLongArrayBuffer compose
- * the implementation of array based long buffers.
- * <p>
- * LongArrayBuffer implements all the shared readonly methods and is extended by
- * the other two classes.
- * </p>
- * <p>
- * All methods are marked final for runtime performance.
- * </p>
- *
+ * LongArrayBuffer implements long[]-based LongBuffers.
  */
-abstract class LongArrayBuffer extends LongBuffer {
+final class LongArrayBuffer extends LongBuffer {
 
-    protected final long[] backingArray;
+  private final long[] backingArray;
 
-    protected final int offset;
+  private final int arrayOffset;
 
-    LongArrayBuffer(long[] array) {
-        this(array.length, array, 0);
+  private final boolean isReadOnly;
+
+  LongArrayBuffer(long[] array) {
+    this(array.length, array, 0, false);
+  }
+
+  private LongArrayBuffer(int capacity, long[] backingArray, int arrayOffset, boolean isReadOnly) {
+    super(capacity);
+    this.backingArray = backingArray;
+    this.arrayOffset = arrayOffset;
+    this.isReadOnly = isReadOnly;
+  }
+
+  private static LongArrayBuffer copy(LongArrayBuffer other, int markOfOther, boolean isReadOnly) {
+    LongArrayBuffer buf = new LongArrayBuffer(other.capacity(), other.backingArray, other.arrayOffset, isReadOnly);
+    buf.limit = other.limit;
+    buf.position = other.position();
+    buf.mark = markOfOther;
+    return buf;
+  }
+
+  @Override public LongBuffer asReadOnlyBuffer() {
+    return copy(this, mark, true);
+  }
+
+  @Override public LongBuffer compact() {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
     }
+    System.arraycopy(backingArray, position + arrayOffset, backingArray, arrayOffset, remaining());
+    position = limit - position;
+    limit = capacity;
+    mark = UNSET_MARK;
+    return this;
+  }
 
-    LongArrayBuffer(int capacity) {
-        this(capacity, new long[capacity], 0);
-    }
+  @Override public LongBuffer duplicate() {
+    return copy(this, mark, isReadOnly);
+  }
 
-    LongArrayBuffer(int capacity, long[] backingArray, int offset) {
-        super(capacity);
-        this.backingArray = backingArray;
-        this.offset = offset;
-    }
+  @Override public LongBuffer slice() {
+    return new LongArrayBuffer(remaining(), backingArray, arrayOffset + position, isReadOnly);
+  }
 
-    @Override
-    public final long get() {
-        if (position == limit) {
-            throw new BufferUnderflowException();
-        }
-        return backingArray[offset + position++];
-    }
+  @Override public boolean isReadOnly() {
+    return isReadOnly;
+  }
 
-    @Override
-    public final long get(int index) {
-        checkIndex(index);
-        return backingArray[offset + index];
+  @Override long[] protectedArray() {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
     }
+    return backingArray;
+  }
 
-    @Override
-    public final LongBuffer get(long[] dst, int dstOffset, int longCount) {
-        if (longCount > remaining()) {
-            throw new BufferUnderflowException();
-        }
-        System.arraycopy(backingArray, offset + position, dst, dstOffset, longCount);
-        position += longCount;
-        return this;
+  @Override int protectedArrayOffset() {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
     }
+    return arrayOffset;
+  }
 
-    @Override
-    public final boolean isDirect() {
-        return false;
+  @Override boolean protectedHasArray() {
+    if (isReadOnly) {
+      return false;
     }
+    return true;
+  }
 
-    @Override
-    public final ByteOrder order() {
-        return ByteOrder.nativeOrder();
+  @Override public final long get() {
+    if (position == limit) {
+      throw new BufferUnderflowException();
     }
+    return backingArray[arrayOffset + position++];
+  }
+
+  @Override public final long get(int index) {
+    checkIndex(index);
+    return backingArray[arrayOffset + index];
+  }
+
+  @Override public final LongBuffer get(long[] dst, int dstOffset, int longCount) {
+    if (longCount > remaining()) {
+      throw new BufferUnderflowException();
+    }
+    System.arraycopy(backingArray, arrayOffset + position, dst, dstOffset, longCount);
+    position += longCount;
+    return this;
+  }
+
+  @Override public final boolean isDirect() {
+    return false;
+  }
+
+  @Override public final ByteOrder order() {
+    return ByteOrder.nativeOrder();
+  }
+
+  @Override public LongBuffer put(long c) {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
+    }
+    if (position == limit) {
+      throw new BufferOverflowException();
+    }
+    backingArray[arrayOffset + position++] = c;
+    return this;
+  }
+
+  @Override public LongBuffer put(int index, long c) {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
+    }
+    checkIndex(index);
+    backingArray[arrayOffset + index] = c;
+    return this;
+  }
+
+  @Override public LongBuffer put(long[] src, int srcOffset, int longCount) {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
+    }
+    if (longCount > remaining()) {
+      throw new BufferOverflowException();
+    }
+    System.arraycopy(src, srcOffset, backingArray, arrayOffset + position, longCount);
+    position += longCount;
+    return this;
+  }
 
 }
diff --git a/luni/src/main/java/java/nio/LongBuffer.java b/luni/src/main/java/java/nio/LongBuffer.java
index 1254ddb..58d7518 100644
--- a/luni/src/main/java/java/nio/LongBuffer.java
+++ b/luni/src/main/java/java/nio/LongBuffer.java
@@ -48,7 +48,7 @@
         if (capacity < 0) {
             throw new IllegalArgumentException("capacity < 0: " + capacity);
         }
-        return new ReadWriteLongArrayBuffer(capacity);
+        return new LongArrayBuffer(new long[capacity]);
     }
 
     /**
@@ -85,7 +85,7 @@
      */
     public static LongBuffer wrap(long[] array, int start, int longCount) {
         Arrays.checkOffsetAndCount(array.length, start, longCount);
-        LongBuffer buf = new ReadWriteLongArrayBuffer(array);
+        LongBuffer buf = new LongArrayBuffer(array);
         buf.position = start;
         buf.limit = start + longCount;
         return buf;
@@ -426,6 +426,9 @@
      *                if no changes may be made to the contents of this buffer.
      */
     public LongBuffer put(LongBuffer src) {
+        if (isReadOnly()) {
+            throw new ReadOnlyBufferException();
+        }
         if (src == this) {
             throw new IllegalArgumentException("src == this");
         }
diff --git a/luni/src/main/java/java/nio/MappedByteBuffer.java b/luni/src/main/java/java/nio/MappedByteBuffer.java
index 0e8bf09..2ba9258 100644
--- a/luni/src/main/java/java/nio/MappedByteBuffer.java
+++ b/luni/src/main/java/java/nio/MappedByteBuffer.java
@@ -37,92 +37,89 @@
  * {@code MappedByteBuffer} is undefined.
  */
 public abstract class MappedByteBuffer extends ByteBuffer {
+  final MapMode mapMode;
 
-    final DirectByteBuffer wrapped;
+  MappedByteBuffer(MemoryBlock block, int capacity, MapMode mapMode) {
+    super(capacity, block);
+    this.mapMode = mapMode;
+  }
 
-    private final MapMode mapMode;
+  /**
+   * Returns true if there is a high probability that every page of this buffer is currently
+   * loaded in RAM, meaning that accesses will not cause a page fault. It is impossible to give
+   * a strong guarantee since this is only a snapshot of a dynamic situation.
+   */
+  public final boolean isLoaded() {
+    checkIsMapped();
 
-    MappedByteBuffer(ByteBuffer directBuffer) {
-        super(directBuffer.capacity, directBuffer.block);
-        if (!directBuffer.isDirect()) {
-            throw new IllegalArgumentException("directBuffer is not a direct buffer: " + directBuffer);
-        }
-        this.wrapped = (DirectByteBuffer) directBuffer;
-        this.mapMode = null;
+    long address = block.toInt();
+    long size = block.getSize();
+    if (size == 0) {
+      return true;
     }
 
-    MappedByteBuffer(MemoryBlock block, int capacity, int offset, MapMode mapMode) {
-        super(capacity, block);
-        this.mapMode = mapMode;
-        if (mapMode == MapMode.READ_ONLY) {
-            wrapped = new ReadOnlyDirectByteBuffer(block, capacity, offset);
-        } else {
-            wrapped = new ReadWriteDirectByteBuffer(block, capacity, offset);
+    try {
+      int pageSize = (int) Libcore.os.sysconf(_SC_PAGE_SIZE);
+      int pageOffset = (int) (address % pageSize);
+      address -= pageOffset;
+      size += pageOffset;
+      int pageCount = (int) ((size + pageSize - 1) / pageSize);
+      byte[] vector = new byte[pageCount];
+      Libcore.os.mincore(address, size, vector);
+      for (int i = 0; i < vector.length; ++i) {
+        if ((vector[i] & 1) != 1) {
+          return false;
         }
+      }
+      return true;
+    } catch (ErrnoException errnoException) {
+      return false;
     }
+  }
 
-    /**
-     * Returns true if there is a high probability that every page of this buffer is currently
-     * loaded in RAM, meaning that accesses will not cause a page fault. It is impossible to give
-     * a strong guarantee since this is only a snapshot of a dynamic situation.
-     */
-    public final boolean isLoaded() {
-        long address = block.toInt();
-        long size = block.getSize();
-        if (size == 0) {
-            return true;
-        }
+  /**
+   * Attempts to load every page of this buffer into RAM. See {@link #isLoaded}.
+   * @return this buffer.
+   */
+  public final MappedByteBuffer load() {
+    checkIsMapped();
 
-        try {
-            int pageSize = (int) Libcore.os.sysconf(_SC_PAGE_SIZE);
-            int pageOffset = (int) (address % pageSize);
-            address -= pageOffset;
-            size += pageOffset;
-            int pageCount = (int) ((size + pageSize - 1) / pageSize);
-            byte[] vector = new byte[pageCount];
-            Libcore.os.mincore(address, size, vector);
-            for (int i = 0; i < vector.length; ++i) {
-                if ((vector[i] & 1) != 1) {
-                    return false;
-                }
-            }
-            return true;
-        } catch (ErrnoException errnoException) {
-            return false;
-        }
+    try {
+      Libcore.os.mlock(block.toInt(), block.getSize());
+      Libcore.os.munlock(block.toInt(), block.getSize());
+    } catch (ErrnoException ignored) {
     }
+    return this;
+  }
 
-    /**
-     * Attempts to load every page of this buffer into RAM. See {@link #isLoaded}.
-     * @return this buffer.
-     */
-    public final MappedByteBuffer load() {
-        try {
-            Libcore.os.mlock(block.toInt(), block.getSize());
-            Libcore.os.munlock(block.toInt(), block.getSize());
-        } catch (ErrnoException ignored) {
-        }
-        return this;
-    }
+  /**
+   * Writes all changes of the buffer to the mapped file. If the mapped file
+   * is stored on a local device, it is guaranteed that the changes are
+   * written to the file. No such guarantee is given if the file is located on
+   * a remote device.
+   *
+   * @return this buffer.
+   */
+  public final MappedByteBuffer force() {
+    checkIsMapped();
 
-    /**
-     * Writes all changes of the buffer to the mapped file. If the mapped file
-     * is stored on a local device, it is guaranteed that the changes are
-     * written to the file. No such guarantee is given if the file is located on
-     * a remote device.
-     *
-     * @return this buffer.
-     */
-    public final MappedByteBuffer force() {
-        if (mapMode == MapMode.READ_WRITE) {
-            try {
-                Libcore.os.msync(block.toInt(), block.getSize(), MS_SYNC);
-            } catch (ErrnoException errnoException) {
-                // The RI doesn't throw, presumably on the assumption that you can't get into
-                // a state where msync(2) could return an error.
-                throw new AssertionError(errnoException);
-            }
-        }
-        return this;
+    if (mapMode == MapMode.READ_WRITE) {
+      try {
+        Libcore.os.msync(block.toInt(), block.getSize(), MS_SYNC);
+      } catch (ErrnoException errnoException) {
+        // The RI doesn't throw, presumably on the assumption that you can't get into
+        // a state where msync(2) could return an error.
+        throw new AssertionError(errnoException);
+      }
     }
+    return this;
+  }
+
+  // DirectByteBuffer is a subclass of MappedByteBuffer, but not all DirectByteBuffers
+  // actually correspond to an mmap(2)ed region.
+  private void checkIsMapped() {
+    if (mapMode == null) {
+      throw new UnsupportedOperationException();
+    }
+  }
 }
diff --git a/luni/src/main/java/java/nio/MappedByteBufferAdapter.java b/luni/src/main/java/java/nio/MappedByteBufferAdapter.java
deleted file mode 100644
index 59cfe8e..0000000
--- a/luni/src/main/java/java/nio/MappedByteBufferAdapter.java
+++ /dev/null
@@ -1,378 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one or more
- *  contributor license agreements.  See the NOTICE file distributed with
- *  this work for additional information regarding copyright ownership.
- *  The ASF licenses this file to You 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 java.nio;
-
-import java.nio.channels.FileChannel.MapMode;
-import libcore.io.SizeOf;
-
-/**
- * Rather than duplicate all the code from ReadOnlyDirectByteBuffer and
- * ReadWriteDirectByteBuffer (and their superclasses), we delegate to one or the other.
- * The tricky part is that we need to keep our fields in sync with our delegate's fields.
- * There are lots of methods that access the fields directly.
- *
- * The main consequences of this implementation are:
- *
- * 1. we need to explicitly call wrapped.position(int) before any operation on our delegate
- * that makes use of the implicit position.
- *
- * 2. we need to explicitly update position after any operation on our delegate that makes
- * use of the implicit position.
- *
- * This means that, even more than usual, the implicit iteration
- * operations are more expensive than the indexed operations.
- *
- * But we save a ton of code, for classes that no-one really uses because the API's broken
- * by design (disallowing munmap(2) calls). Internally, we can use libcore.io.MemoryMappedFile
- * as a high-performance and more usable replacement for MappedByteBuffer.
- *
- * FIXME: harmony changed their implementation after we diverged, switching to a scheme
- * where DirectByteBuffer extends MappedByteBuffer and this class doesn't exist. That's
- * much better than their original implementation, fossilized here.
- */
-final class MappedByteBufferAdapter extends MappedByteBuffer {
-    private MappedByteBufferAdapter(ByteBuffer buffer) {
-        super(buffer);
-        effectiveDirectAddress = wrapped.effectiveDirectAddress;
-    }
-
-    public MappedByteBufferAdapter(MemoryBlock block, int capacity, int offset, MapMode mode) {
-        super(block, capacity, offset, mode);
-        effectiveDirectAddress = wrapped.effectiveDirectAddress;
-    }
-
-    @Override void limitImpl(int newLimit) {
-        super.limitImpl(newLimit);
-        wrapped.limit(newLimit);
-    }
-
-    @Override void positionImpl(int newPosition) {
-        super.positionImpl(newPosition);
-        wrapped.position(newPosition);
-    }
-
-    @Override
-    public CharBuffer asCharBuffer() {
-        return wrapped.asCharBuffer();
-    }
-
-    @Override
-    public DoubleBuffer asDoubleBuffer() {
-        return wrapped.asDoubleBuffer();
-    }
-
-    @Override
-    public FloatBuffer asFloatBuffer() {
-        return wrapped.asFloatBuffer();
-    }
-
-    @Override
-    public IntBuffer asIntBuffer() {
-        return wrapped.asIntBuffer();
-    }
-
-    @Override
-    public LongBuffer asLongBuffer() {
-        return wrapped.asLongBuffer();
-    }
-
-    @Override
-    public ByteBuffer asReadOnlyBuffer() {
-        MappedByteBufferAdapter result = new MappedByteBufferAdapter(wrapped.asReadOnlyBuffer());
-        result.limit(limit);
-        result.position(position);
-        result.mark = mark;
-        return result;
-    }
-
-    @Override
-    public ShortBuffer asShortBuffer() {
-        return wrapped.asShortBuffer();
-    }
-
-    @Override
-    public ByteBuffer compact() {
-        if (wrapped.isReadOnly()) {
-            throw new ReadOnlyBufferException();
-        }
-        wrapped.compact();
-        limit(capacity);
-        position(wrapped.position());
-        this.mark = UNSET_MARK;
-        return this;
-    }
-
-    @Override
-    public ByteBuffer duplicate() {
-        MappedByteBufferAdapter result = new MappedByteBufferAdapter(wrapped.duplicate());
-        result.limit(limit);
-        result.position(position);
-        result.mark = mark;
-        return result;
-    }
-
-    @Override
-    public byte get() {
-        wrapped.position(position);
-        byte result = wrapped.get();
-        ++position;
-        return result;
-    }
-
-    @Override
-    public byte get(int index) {
-        return wrapped.get(index);
-    }
-
-    @Override
-    public ByteBuffer get(byte[] dst, int dstOffset, int byteCount) {
-        ByteBuffer result = wrapped.get(dst, dstOffset, byteCount);
-        position += byteCount;
-        return result;
-    }
-
-    @Override
-    public char getChar() {
-        wrapped.position(position);
-        char result = wrapped.getChar();
-        position += SizeOf.CHAR;
-        return result;
-    }
-
-    @Override
-    public char getChar(int index) {
-        return wrapped.getChar(index);
-    }
-
-    @Override
-    public double getDouble() {
-        wrapped.position(position);
-        double result = wrapped.getDouble();
-        position += SizeOf.DOUBLE;
-        return result;
-    }
-
-    @Override
-    public double getDouble(int index) {
-        return wrapped.getDouble(index);
-    }
-
-    @Override
-    public float getFloat() {
-        wrapped.position(position);
-        float result = wrapped.getFloat();
-        position += SizeOf.FLOAT;
-        return result;
-    }
-
-    @Override
-    public float getFloat(int index) {
-        return wrapped.getFloat(index);
-    }
-
-    @Override
-    public int getInt() {
-        wrapped.position(position);
-        int result = wrapped.getInt();
-        position += SizeOf.INT;
-        return result;
-    }
-
-    @Override
-    public int getInt(int index) {
-        return wrapped.getInt(index);
-    }
-
-    @Override
-    public long getLong() {
-        wrapped.position(position);
-        long result = wrapped.getLong();
-        position += SizeOf.LONG;
-        return result;
-    }
-
-    @Override
-    public long getLong(int index) {
-        return wrapped.getLong(index);
-    }
-
-    @Override
-    public short getShort() {
-        wrapped.position(position);
-        short result = wrapped.getShort();
-        position += SizeOf.SHORT;
-        return result;
-    }
-
-    @Override
-    public short getShort(int index) {
-        return wrapped.getShort(index);
-    }
-
-    @Override
-    public boolean isDirect() {
-        return true;
-    }
-
-    @Override
-    public boolean isReadOnly() {
-        return wrapped.isReadOnly();
-    }
-
-    @Override void orderImpl(ByteOrder byteOrder) {
-        super.orderImpl(byteOrder);
-        wrapped.order(byteOrder);
-    }
-
-    @Override
-    public ByteBuffer put(byte b) {
-        wrapped.position(this.position);
-        wrapped.put(b);
-        this.position++;
-        return this;
-    }
-
-    @Override
-    public ByteBuffer put(byte[] src, int srcOffset, int byteCount) {
-        wrapped.position(this.position);
-        wrapped.put(src, srcOffset, byteCount);
-        this.position += byteCount;
-        return this;
-    }
-
-    @Override
-    public ByteBuffer put(int index, byte b) {
-        wrapped.position(this.position);
-        wrapped.put(index, b);
-        return this;
-    }
-
-    @Override
-    public ByteBuffer putChar(char value) {
-        wrapped.position(this.position);
-        wrapped.putChar(value);
-        this.position += SizeOf.CHAR;
-        return this;
-    }
-
-    @Override
-    public ByteBuffer putChar(int index, char value) {
-        wrapped.position(this.position);
-        wrapped.putChar(index, value);
-        return this;
-    }
-
-    @Override
-    public ByteBuffer putDouble(double value) {
-        wrapped.position(this.position);
-        wrapped.putDouble(value);
-        this.position += SizeOf.DOUBLE;
-        return this;
-    }
-
-    @Override
-    public ByteBuffer putDouble(int index, double value) {
-        wrapped.position(this.position);
-        wrapped.putDouble(index, value);
-        return this;
-    }
-
-    @Override
-    public ByteBuffer putFloat(float value) {
-        wrapped.position(this.position);
-        wrapped.putFloat(value);
-        this.position += SizeOf.FLOAT;
-        return this;
-    }
-
-    @Override
-    public ByteBuffer putFloat(int index, float value) {
-        wrapped.position(this.position);
-        wrapped.putFloat(index, value);
-        return this;
-    }
-
-    @Override
-    public ByteBuffer putInt(int index, int value) {
-        wrapped.position(this.position);
-        wrapped.putInt(index, value);
-        return this;
-    }
-
-    @Override
-    public ByteBuffer putInt(int value) {
-        wrapped.position(this.position);
-        wrapped.putInt(value);
-        this.position += SizeOf.INT;
-        return this;
-    }
-
-    @Override
-    public ByteBuffer putLong(int index, long value) {
-        wrapped.position(this.position);
-        wrapped.putLong(index, value);
-        return this;
-    }
-
-    @Override
-    public ByteBuffer putLong(long value) {
-        wrapped.position(this.position);
-        wrapped.putLong(value);
-        this.position += SizeOf.LONG;
-        return this;
-    }
-
-    @Override
-    public ByteBuffer putShort(int index, short value) {
-        wrapped.position(this.position);
-        wrapped.putShort(index, value);
-        return this;
-    }
-
-    @Override
-    public ByteBuffer putShort(short value) {
-        wrapped.position(this.position);
-        wrapped.putShort(value);
-        this.position += SizeOf.SHORT;
-        return this;
-    }
-
-    @Override
-    public ByteBuffer slice() {
-        wrapped.position(this.position);
-        MappedByteBufferAdapter result = new MappedByteBufferAdapter(wrapped.slice());
-        wrapped.clear();
-        return result;
-    }
-
-    @Override byte[] protectedArray() {
-        return wrapped.protectedArray();
-    }
-
-    @Override int protectedArrayOffset() {
-        return wrapped.protectedArrayOffset();
-    }
-
-    @Override boolean protectedHasArray() {
-        return wrapped.protectedHasArray();
-    }
-
-    public final void free() {
-        wrapped.free();
-    }
-}
diff --git a/luni/src/main/java/java/nio/NioUtils.java b/luni/src/main/java/java/nio/NioUtils.java
index 56b8b8b..7c51da1 100644
--- a/luni/src/main/java/java/nio/NioUtils.java
+++ b/luni/src/main/java/java/nio/NioUtils.java
@@ -49,13 +49,7 @@
         if (buffer == null) {
             return;
         }
-        if (buffer instanceof DirectByteBuffer) {
-            ((DirectByteBuffer) buffer).free();
-        } else if (buffer instanceof MappedByteBuffer) {
-            ((MappedByteBufferAdapter) buffer).free();
-        } else {
-            throw new AssertionError();
-        }
+        ((DirectByteBuffer) buffer).free();
     }
 
     /**
@@ -77,7 +71,7 @@
      * Normally, attempting to access the array backing a read-only buffer throws.
      */
     public static byte[] unsafeArray(ByteBuffer b) {
-        return ((HeapByteBuffer) b).backingArray;
+        return ((ByteArrayBuffer) b).backingArray;
     }
 
     /**
@@ -85,6 +79,6 @@
      * even if the ByteBuffer is read-only.
      */
     public static int unsafeArrayOffset(ByteBuffer b) {
-        return ((HeapByteBuffer) b).offset;
+        return ((ByteArrayBuffer) b).arrayOffset;
     }
 }
diff --git a/luni/src/main/java/java/nio/ReadOnlyCharArrayBuffer.java b/luni/src/main/java/java/nio/ReadOnlyCharArrayBuffer.java
deleted file mode 100644
index 75e7b9b..0000000
--- a/luni/src/main/java/java/nio/ReadOnlyCharArrayBuffer.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one or more
- *  contributor license agreements.  See the NOTICE file distributed with
- *  this work for additional information regarding copyright ownership.
- *  The ASF licenses this file to You 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 java.nio;
-
-/**
- * CharArrayBuffer, ReadWriteCharArrayBuffer and ReadOnlyCharArrayBuffer compose
- * the implementation of array based char buffers.
- * <p>
- * ReadOnlyCharArrayBuffer extends CharArrayBuffer with all the write methods
- * throwing read only exception.
- * </p>
- * <p>
- * This class is marked final for runtime performance.
- * </p>
- *
- */
-final class ReadOnlyCharArrayBuffer extends CharArrayBuffer {
-
-    static ReadOnlyCharArrayBuffer copy(CharArrayBuffer other, int markOfOther) {
-        ReadOnlyCharArrayBuffer buf =
-                new ReadOnlyCharArrayBuffer(other.capacity(), other.backingArray, other.offset);
-        buf.limit = other.limit;
-        buf.position = other.position();
-        buf.mark = markOfOther;
-        return buf;
-    }
-
-    ReadOnlyCharArrayBuffer(int capacity, char[] backingArray, int arrayOffset) {
-        super(capacity, backingArray, arrayOffset);
-    }
-
-    @Override
-    public CharBuffer asReadOnlyBuffer() {
-        return duplicate();
-    }
-
-    @Override
-    public CharBuffer compact() {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public CharBuffer duplicate() {
-        return copy(this, mark);
-    }
-
-    @Override
-    public boolean isReadOnly() {
-        return true;
-    }
-
-    @Override char[] protectedArray() {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override int protectedArrayOffset() {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override boolean protectedHasArray() {
-        return false;
-    }
-
-    @Override
-    public CharBuffer put(char c) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public CharBuffer put(int index, char c) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public final CharBuffer put(char[] src, int srcOffset, int charCount) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public final CharBuffer put(CharBuffer src) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public CharBuffer put(String src, int start, int end) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public CharBuffer slice() {
-        return new ReadOnlyCharArrayBuffer(remaining(), backingArray, offset + position);
-    }
-}
diff --git a/luni/src/main/java/java/nio/ReadOnlyDirectByteBuffer.java b/luni/src/main/java/java/nio/ReadOnlyDirectByteBuffer.java
deleted file mode 100644
index 0d61797..0000000
--- a/luni/src/main/java/java/nio/ReadOnlyDirectByteBuffer.java
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one or more
- *  contributor license agreements.  See the NOTICE file distributed with
- *  this work for additional information regarding copyright ownership.
- *  The ASF licenses this file to You 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 java.nio;
-
-/**
- * DirectByteBuffer, ReadWriteDirectByteBuffer and ReadOnlyDirectByteBuffer
- * compose the implementation of platform memory based byte buffers.
- * <p>
- * ReadOnlyDirectByteBuffer extends DirectByteBuffer with all the write methods
- * throwing read only exception.
- * </p>
- * <p>
- * This class is marked final for runtime performance.
- * </p>
- */
-final class ReadOnlyDirectByteBuffer extends DirectByteBuffer {
-    static ReadOnlyDirectByteBuffer copy(DirectByteBuffer other, int markOfOther) {
-        ReadOnlyDirectByteBuffer buf = new ReadOnlyDirectByteBuffer(other.block, other.capacity(), other.offset);
-        buf.limit = other.limit;
-        buf.position = other.position();
-        buf.mark = markOfOther;
-        return buf;
-    }
-
-    protected ReadOnlyDirectByteBuffer(MemoryBlock block, int capacity, int offset) {
-        super(block, capacity, offset);
-    }
-
-    @Override
-    public ByteBuffer asReadOnlyBuffer() {
-        return copy(this, mark);
-    }
-
-    @Override
-    public ByteBuffer compact() {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public ByteBuffer duplicate() {
-        return copy(this, mark);
-    }
-
-    @Override
-    public boolean isReadOnly() {
-        return true;
-    }
-
-    @Override
-    public ByteBuffer put(byte value) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public ByteBuffer put(int index, byte value) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public ByteBuffer put(byte[] src, int srcOffset, int byteCount) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public ByteBuffer putDouble(double value) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public ByteBuffer putDouble(int index, double value) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public ByteBuffer putFloat(float value) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public ByteBuffer putFloat(int index, float value) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public ByteBuffer putInt(int value) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public ByteBuffer putInt(int index, int value) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public ByteBuffer putLong(int index, long value) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public ByteBuffer putLong(long value) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public ByteBuffer putShort(int index, short value) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public ByteBuffer putShort(short value) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public ByteBuffer put(ByteBuffer buf) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public ByteBuffer slice() {
-        return new ReadOnlyDirectByteBuffer(block, remaining(), offset + position);
-    }
-
-    @Override protected byte[] protectedArray() {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override protected int protectedArrayOffset() {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override protected boolean protectedHasArray() {
-        return false;
-    }
-}
diff --git a/luni/src/main/java/java/nio/ReadOnlyDoubleArrayBuffer.java b/luni/src/main/java/java/nio/ReadOnlyDoubleArrayBuffer.java
deleted file mode 100644
index 9812789..0000000
--- a/luni/src/main/java/java/nio/ReadOnlyDoubleArrayBuffer.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one or more
- *  contributor license agreements.  See the NOTICE file distributed with
- *  this work for additional information regarding copyright ownership.
- *  The ASF licenses this file to You 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 java.nio;
-
-/**
- * DoubleArrayBuffer, ReadWriteDoubleArrayBuffer and ReadOnlyDoubleArrayBuffer
- * compose the implementation of array based double buffers.
- * <p>
- * ReadOnlyDoubleArrayBuffer extends DoubleArrayBuffer with all the write
- * methods throwing read only exception.
- * </p>
- * <p>
- * This class is marked final for runtime performance.
- * </p>
- *
- */
-final class ReadOnlyDoubleArrayBuffer extends DoubleArrayBuffer {
-
-    static ReadOnlyDoubleArrayBuffer copy(DoubleArrayBuffer other, int markOfOther) {
-        ReadOnlyDoubleArrayBuffer buf =
-                new ReadOnlyDoubleArrayBuffer(other.capacity(), other.backingArray, other.offset);
-        buf.limit = other.limit;
-        buf.position = other.position();
-        buf.mark = markOfOther;
-        return buf;
-    }
-
-    ReadOnlyDoubleArrayBuffer(int capacity, double[] backingArray, int arrayOffset) {
-        super(capacity, backingArray, arrayOffset);
-    }
-
-    @Override
-    public DoubleBuffer asReadOnlyBuffer() {
-        return duplicate();
-    }
-
-    @Override
-    public DoubleBuffer compact() {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public DoubleBuffer duplicate() {
-        return copy(this, mark);
-    }
-
-    @Override
-    public boolean isReadOnly() {
-        return true;
-    }
-
-    @Override double[] protectedArray() {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override int protectedArrayOffset() {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override boolean protectedHasArray() {
-        return false;
-    }
-
-    @Override
-    public DoubleBuffer put(double c) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public DoubleBuffer put(int index, double c) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public final DoubleBuffer put(double[] src, int srcOffset, int byteCount) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public final DoubleBuffer put(DoubleBuffer buf) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public DoubleBuffer slice() {
-        return new ReadOnlyDoubleArrayBuffer(remaining(), backingArray, offset + position);
-    }
-
-}
diff --git a/luni/src/main/java/java/nio/ReadOnlyFloatArrayBuffer.java b/luni/src/main/java/java/nio/ReadOnlyFloatArrayBuffer.java
deleted file mode 100644
index 3c74966..0000000
--- a/luni/src/main/java/java/nio/ReadOnlyFloatArrayBuffer.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one or more
- *  contributor license agreements.  See the NOTICE file distributed with
- *  this work for additional information regarding copyright ownership.
- *  The ASF licenses this file to You 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 java.nio;
-
-/**
- * FloatArrayBuffer, ReadWriteFloatArrayBuffer and ReadOnlyFloatArrayBuffer
- * compose the implementation of array based float buffers.
- * <p>
- * ReadOnlyFloatArrayBuffer extends FloatArrayBuffer with all the write methods
- * throwing read only exception.
- * </p>
- * <p>
- * This class is marked final for runtime performance.
- * </p>
- *
- */
-final class ReadOnlyFloatArrayBuffer extends FloatArrayBuffer {
-
-    static ReadOnlyFloatArrayBuffer copy(FloatArrayBuffer other, int markOfOther) {
-        ReadOnlyFloatArrayBuffer buf =
-                new ReadOnlyFloatArrayBuffer(other.capacity(), other.backingArray, other.offset);
-        buf.limit = other.limit;
-        buf.position = other.position();
-        buf.mark = markOfOther;
-        return buf;
-    }
-
-    ReadOnlyFloatArrayBuffer(int capacity, float[] backingArray, int arrayOffset) {
-        super(capacity, backingArray, arrayOffset);
-    }
-
-    @Override
-    public FloatBuffer asReadOnlyBuffer() {
-        return duplicate();
-    }
-
-    @Override
-    public FloatBuffer compact() {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public FloatBuffer duplicate() {
-        return copy(this, mark);
-    }
-
-    @Override
-    public boolean isReadOnly() {
-        return true;
-    }
-
-    @Override float[] protectedArray() {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override int protectedArrayOffset() {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override boolean protectedHasArray() {
-        return false;
-    }
-
-    @Override
-    public FloatBuffer put(float c) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public FloatBuffer put(int index, float c) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public FloatBuffer put(FloatBuffer buf) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public final FloatBuffer put(float[] src, int srcOffset, int byteCount) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public FloatBuffer slice() {
-        return new ReadOnlyFloatArrayBuffer(remaining(), backingArray, offset + position);
-    }
-
-}
diff --git a/luni/src/main/java/java/nio/ReadOnlyHeapByteBuffer.java b/luni/src/main/java/java/nio/ReadOnlyHeapByteBuffer.java
deleted file mode 100644
index c5073b0..0000000
--- a/luni/src/main/java/java/nio/ReadOnlyHeapByteBuffer.java
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one or more
- *  contributor license agreements.  See the NOTICE file distributed with
- *  this work for additional information regarding copyright ownership.
- *  The ASF licenses this file to You 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 java.nio;
-
-/**
- * HeapByteBuffer, ReadWriteHeapByteBuffer and ReadOnlyHeapByteBuffer compose
- * the implementation of array based byte buffers.
- * <p>
- * ReadOnlyHeapByteBuffer extends HeapByteBuffer with all the write methods
- * throwing read only exception.
- * </p>
- * <p>
- * This class is marked final for runtime performance.
- * </p>
- *
- */
-final class ReadOnlyHeapByteBuffer extends HeapByteBuffer {
-
-    static ReadOnlyHeapByteBuffer copy(HeapByteBuffer other, int markOfOther) {
-        ReadOnlyHeapByteBuffer buf =
-                new ReadOnlyHeapByteBuffer(other.backingArray, other.capacity(), other.offset);
-        buf.limit = other.limit;
-        buf.position = other.position();
-        buf.mark = markOfOther;
-        return buf;
-    }
-
-    ReadOnlyHeapByteBuffer(byte[] backingArray, int capacity, int arrayOffset) {
-        super(backingArray, capacity, arrayOffset);
-    }
-
-    @Override
-    public ByteBuffer asReadOnlyBuffer() {
-        return ReadOnlyHeapByteBuffer.copy(this, mark);
-    }
-
-    @Override
-    public ByteBuffer compact() {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public ByteBuffer duplicate() {
-        return copy(this, mark);
-    }
-
-    @Override
-    public boolean isReadOnly() {
-        return true;
-    }
-
-    @Override byte[] protectedArray() {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override int protectedArrayOffset() {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override boolean protectedHasArray() {
-        return false;
-    }
-
-    @Override
-    public ByteBuffer put(byte b) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public ByteBuffer put(int index, byte b) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public ByteBuffer put(byte[] src, int srcOffset, int byteCount) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public ByteBuffer putDouble(double value) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public ByteBuffer putDouble(int index, double value) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public ByteBuffer putFloat(float value) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public ByteBuffer putFloat(int index, float value) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public ByteBuffer putInt(int value) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public ByteBuffer putInt(int index, int value) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public ByteBuffer putLong(int index, long value) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public ByteBuffer putLong(long value) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public ByteBuffer putShort(int index, short value) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public ByteBuffer putShort(short value) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public ByteBuffer put(ByteBuffer buf) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public ByteBuffer slice() {
-        return new ReadOnlyHeapByteBuffer(backingArray, remaining(), offset + position);
-    }
-}
diff --git a/luni/src/main/java/java/nio/ReadOnlyIntArrayBuffer.java b/luni/src/main/java/java/nio/ReadOnlyIntArrayBuffer.java
deleted file mode 100644
index ef73251..0000000
--- a/luni/src/main/java/java/nio/ReadOnlyIntArrayBuffer.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one or more
- *  contributor license agreements.  See the NOTICE file distributed with
- *  this work for additional information regarding copyright ownership.
- *  The ASF licenses this file to You 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 java.nio;
-
-/**
- * IntArrayBuffer, ReadWriteIntArrayBuffer and ReadOnlyIntArrayBuffer compose
- * the implementation of array based int buffers.
- * <p>
- * ReadOnlyIntArrayBuffer extends IntArrayBuffer with all the write methods
- * throwing read only exception.
- * </p>
- * <p>
- * This class is marked final for runtime performance.
- * </p>
- *
- */
-final class ReadOnlyIntArrayBuffer extends IntArrayBuffer {
-
-    static ReadOnlyIntArrayBuffer copy(IntArrayBuffer other, int markOfOther) {
-        ReadOnlyIntArrayBuffer buf =
-                new ReadOnlyIntArrayBuffer(other.capacity(), other.backingArray, other.offset);
-        buf.limit = other.limit;
-        buf.position = other.position();
-        buf.mark = markOfOther;
-        return buf;
-    }
-
-    ReadOnlyIntArrayBuffer(int capacity, int[] backingArray, int arrayOffset) {
-        super(capacity, backingArray, arrayOffset);
-    }
-
-    @Override
-    public IntBuffer asReadOnlyBuffer() {
-        return duplicate();
-    }
-
-    @Override
-    public IntBuffer compact() {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public IntBuffer duplicate() {
-        return copy(this, mark);
-    }
-
-    @Override
-    public boolean isReadOnly() {
-        return true;
-    }
-
-    @Override int[] protectedArray() {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override int protectedArrayOffset() {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override boolean protectedHasArray() {
-        return false;
-    }
-
-    @Override
-    public IntBuffer put(int c) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public IntBuffer put(int index, int c) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public IntBuffer put(IntBuffer buf) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public final IntBuffer put(int[] src, int srcOffset, int intCount) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public IntBuffer slice() {
-        return new ReadOnlyIntArrayBuffer(remaining(), backingArray, offset + position);
-    }
-
-}
diff --git a/luni/src/main/java/java/nio/ReadOnlyLongArrayBuffer.java b/luni/src/main/java/java/nio/ReadOnlyLongArrayBuffer.java
deleted file mode 100644
index a28815d..0000000
--- a/luni/src/main/java/java/nio/ReadOnlyLongArrayBuffer.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one or more
- *  contributor license agreements.  See the NOTICE file distributed with
- *  this work for additional information regarding copyright ownership.
- *  The ASF licenses this file to You 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 java.nio;
-
-/**
- * LongArrayBuffer, ReadWriteLongArrayBuffer and ReadOnlyLongArrayBuffer compose
- * the implementation of array based long buffers.
- * <p>
- * ReadOnlyLongArrayBuffer extends LongArrayBuffer with all the write methods
- * throwing read only exception.
- * </p>
- * <p>
- * This class is marked final for runtime performance.
- * </p>
- *
- */
-final class ReadOnlyLongArrayBuffer extends LongArrayBuffer {
-
-    static ReadOnlyLongArrayBuffer copy(LongArrayBuffer other, int markOfOther) {
-        ReadOnlyLongArrayBuffer buf =
-                new ReadOnlyLongArrayBuffer(other.capacity(), other.backingArray, other.offset);
-        buf.limit = other.limit;
-        buf.position = other.position();
-        buf.mark = markOfOther;
-        return buf;
-    }
-
-    ReadOnlyLongArrayBuffer(int capacity, long[] backingArray, int arrayOffset) {
-        super(capacity, backingArray, arrayOffset);
-    }
-
-    @Override
-    public LongBuffer asReadOnlyBuffer() {
-        return duplicate();
-    }
-
-    @Override
-    public LongBuffer compact() {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public LongBuffer duplicate() {
-        return copy(this, mark);
-    }
-
-    @Override
-    public boolean isReadOnly() {
-        return true;
-    }
-
-    @Override long[] protectedArray() {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override int protectedArrayOffset() {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override boolean protectedHasArray() {
-        return false;
-    }
-
-    @Override
-    public LongBuffer put(long c) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public LongBuffer put(int index, long c) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public LongBuffer put(LongBuffer buf) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public final LongBuffer put(long[] src, int srcOffset, int longCount) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public LongBuffer slice() {
-        return new ReadOnlyLongArrayBuffer(remaining(), backingArray, offset + position);
-    }
-
-}
diff --git a/luni/src/main/java/java/nio/ReadOnlyShortArrayBuffer.java b/luni/src/main/java/java/nio/ReadOnlyShortArrayBuffer.java
deleted file mode 100644
index 075ff38..0000000
--- a/luni/src/main/java/java/nio/ReadOnlyShortArrayBuffer.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one or more
- *  contributor license agreements.  See the NOTICE file distributed with
- *  this work for additional information regarding copyright ownership.
- *  The ASF licenses this file to You 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 java.nio;
-
-/**
- * ShortArrayBuffer, ReadWriteShortArrayBuffer and ReadOnlyShortArrayBuffer
- * compose the implementation of array based short buffers.
- * <p>
- * ReadOnlyShortArrayBuffer extends ShortArrayBuffer with all the write methods
- * throwing read only exception.
- * </p>
- * <p>
- * This class is marked final for runtime performance.
- * </p>
- *
- */
-final class ReadOnlyShortArrayBuffer extends ShortArrayBuffer {
-
-    static ReadOnlyShortArrayBuffer copy(ShortArrayBuffer other, int markOfOther) {
-        ReadOnlyShortArrayBuffer buf =
-                new ReadOnlyShortArrayBuffer(other.capacity(), other.backingArray, other.offset);
-        buf.limit = other.limit;
-        buf.position = other.position();
-        buf.mark = markOfOther;
-        return buf;
-    }
-
-    ReadOnlyShortArrayBuffer(int capacity, short[] backingArray, int arrayOffset) {
-        super(capacity, backingArray, arrayOffset);
-    }
-
-    @Override
-    public ShortBuffer asReadOnlyBuffer() {
-        return duplicate();
-    }
-
-    @Override
-    public ShortBuffer compact() {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public ShortBuffer duplicate() {
-        return copy(this, mark);
-    }
-
-    @Override
-    public boolean isReadOnly() {
-        return true;
-    }
-
-    @Override short[] protectedArray() {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override int protectedArrayOffset() {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override boolean protectedHasArray() {
-        return false;
-    }
-
-    @Override
-    public ShortBuffer put(ShortBuffer buf) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public ShortBuffer put(short c) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public ShortBuffer put(int index, short c) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public final ShortBuffer put(short[] src, int srcOffset, int shortCount) {
-        throw new ReadOnlyBufferException();
-    }
-
-    @Override
-    public ShortBuffer slice() {
-        return new ReadOnlyShortArrayBuffer(remaining(), backingArray, offset + position);
-    }
-
-}
diff --git a/luni/src/main/java/java/nio/ReadWriteCharArrayBuffer.java b/luni/src/main/java/java/nio/ReadWriteCharArrayBuffer.java
deleted file mode 100644
index 58fc5ae..0000000
--- a/luni/src/main/java/java/nio/ReadWriteCharArrayBuffer.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one or more
- *  contributor license agreements.  See the NOTICE file distributed with
- *  this work for additional information regarding copyright ownership.
- *  The ASF licenses this file to You 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 java.nio;
-
-/**
- * CharArrayBuffer, ReadWriteCharArrayBuffer and ReadOnlyCharArrayBuffer compose
- * the implementation of array based char buffers.
- * <p>
- * ReadWriteCharArrayBuffer extends CharArrayBuffer with all the write methods.
- * </p>
- * <p>
- * This class is marked final for runtime performance.
- * </p>
- *
- */
-final class ReadWriteCharArrayBuffer extends CharArrayBuffer {
-
-    static ReadWriteCharArrayBuffer copy(CharArrayBuffer other, int markOfOther) {
-        ReadWriteCharArrayBuffer buf =
-                new ReadWriteCharArrayBuffer(other.capacity(), other.backingArray, other.offset);
-        buf.limit = other.limit;
-        buf.position = other.position();
-        buf.mark = markOfOther;
-        return buf;
-    }
-
-    ReadWriteCharArrayBuffer(char[] array) {
-        super(array);
-    }
-
-    ReadWriteCharArrayBuffer(int capacity) {
-        super(capacity);
-    }
-
-    ReadWriteCharArrayBuffer(int capacity, char[] backingArray, int arrayOffset) {
-        super(capacity, backingArray, arrayOffset);
-    }
-
-    @Override
-    public CharBuffer asReadOnlyBuffer() {
-        return ReadOnlyCharArrayBuffer.copy(this, mark);
-    }
-
-    @Override
-    public CharBuffer compact() {
-        System.arraycopy(backingArray, position + offset, backingArray, offset, remaining());
-        position = limit - position;
-        limit = capacity;
-        mark = UNSET_MARK;
-        return this;
-    }
-
-    @Override
-    public CharBuffer duplicate() {
-        return copy(this, mark);
-    }
-
-    @Override
-    public boolean isReadOnly() {
-        return false;
-    }
-
-    @Override char[] protectedArray() {
-        return backingArray;
-    }
-
-    @Override int protectedArrayOffset() {
-        return offset;
-    }
-
-    @Override boolean protectedHasArray() {
-        return true;
-    }
-
-    @Override
-    public CharBuffer put(char c) {
-        if (position == limit) {
-            throw new BufferOverflowException();
-        }
-        backingArray[offset + position++] = c;
-        return this;
-    }
-
-    @Override
-    public CharBuffer put(int index, char c) {
-        checkIndex(index);
-        backingArray[offset + index] = c;
-        return this;
-    }
-
-    @Override
-    public CharBuffer put(char[] src, int srcOffset, int charCount) {
-        if (charCount > remaining()) {
-            throw new BufferOverflowException();
-        }
-        System.arraycopy(src, srcOffset, backingArray, offset + position, charCount);
-        position += charCount;
-        return this;
-    }
-
-    @Override
-    public CharBuffer slice() {
-        return new ReadWriteCharArrayBuffer(remaining(), backingArray, offset + position);
-    }
-
-}
diff --git a/luni/src/main/java/java/nio/ReadWriteDirectByteBuffer.java b/luni/src/main/java/java/nio/ReadWriteDirectByteBuffer.java
deleted file mode 100644
index 5edce30..0000000
--- a/luni/src/main/java/java/nio/ReadWriteDirectByteBuffer.java
+++ /dev/null
@@ -1,255 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one or more
- *  contributor license agreements.  See the NOTICE file distributed with
- *  this work for additional information regarding copyright ownership.
- *  The ASF licenses this file to You 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 java.nio;
-
-import libcore.io.SizeOf;
-import libcore.io.Memory;
-
-/**
- * DirectByteBuffer, ReadWriteDirectByteBuffer and ReadOnlyDirectByteBuffer
- * compose the implementation of platform memory based byte buffers.
- * <p>
- * ReadWriteDirectByteBuffer extends DirectByteBuffer with all the write
- * methods.
- * </p>
- * <p>
- * This class is marked final for runtime performance.
- * </p>
- */
-final class ReadWriteDirectByteBuffer extends DirectByteBuffer {
-    static ReadWriteDirectByteBuffer copy(DirectByteBuffer other, int markOfOther) {
-        ReadWriteDirectByteBuffer buf =
-                new ReadWriteDirectByteBuffer(other.block, other.capacity(), other.offset);
-        buf.limit = other.limit;
-        buf.position = other.position();
-        buf.mark = markOfOther;
-        return buf;
-    }
-
-    // Used by ByteBuffer.allocateDirect.
-    ReadWriteDirectByteBuffer(int capacity) {
-        super(MemoryBlock.allocate(capacity), capacity, 0);
-    }
-
-    // Used by the JNI NewDirectByteBuffer function.
-    ReadWriteDirectByteBuffer(int address, int capacity) {
-        super(MemoryBlock.wrapFromJni(address, capacity), capacity, 0);
-    }
-
-    ReadWriteDirectByteBuffer(MemoryBlock block, int capacity, int offset) {
-        super(block, capacity, offset);
-    }
-
-    @Override
-    public ByteBuffer asReadOnlyBuffer() {
-        return ReadOnlyDirectByteBuffer.copy(this, mark);
-    }
-
-    @Override
-    public ByteBuffer compact() {
-        Memory.memmove(this, 0, this, position, remaining());
-        position = limit - position;
-        limit = capacity;
-        mark = UNSET_MARK;
-        return this;
-    }
-
-    @Override
-    public ByteBuffer duplicate() {
-        return copy(this, mark);
-    }
-
-    @Override
-    public boolean isReadOnly() {
-        return false;
-    }
-
-    @Override
-    public ByteBuffer put(byte value) {
-        if (position == limit) {
-            throw new BufferOverflowException();
-        }
-        this.block.pokeByte(offset + position++, value);
-        return this;
-    }
-
-    @Override
-    public ByteBuffer put(int index, byte value) {
-        checkIndex(index);
-        this.block.pokeByte(offset + index, value);
-        return this;
-    }
-
-    @Override
-    public ByteBuffer put(byte[] src, int srcOffset, int byteCount) {
-        checkPutBounds(1, src.length, srcOffset, byteCount);
-        this.block.pokeByteArray(offset + position, src, srcOffset, byteCount);
-        position += byteCount;
-        return this;
-    }
-
-    final void put(char[] src, int srcOffset, int charCount) {
-        int byteCount = checkPutBounds(SizeOf.CHAR, src.length, srcOffset, charCount);
-        this.block.pokeCharArray(offset + position, src, srcOffset, charCount, order.needsSwap);
-        position += byteCount;
-    }
-
-    final void put(double[] src, int srcOffset, int doubleCount) {
-        int byteCount = checkPutBounds(SizeOf.DOUBLE, src.length, srcOffset, doubleCount);
-        this.block.pokeDoubleArray(offset + position, src, srcOffset, doubleCount, order.needsSwap);
-        position += byteCount;
-    }
-
-    final void put(float[] src, int srcOffset, int floatCount) {
-        int byteCount = checkPutBounds(SizeOf.FLOAT, src.length, srcOffset, floatCount);
-        this.block.pokeFloatArray(offset + position, src, srcOffset, floatCount, order.needsSwap);
-        position += byteCount;
-    }
-
-    final void put(int[] src, int srcOffset, int intCount) {
-        int byteCount = checkPutBounds(SizeOf.INT, src.length, srcOffset, intCount);
-        this.block.pokeIntArray(offset + position, src, srcOffset, intCount, order.needsSwap);
-        position += byteCount;
-    }
-
-    final void put(long[] src, int srcOffset, int longCount) {
-        int byteCount = checkPutBounds(SizeOf.LONG, src.length, srcOffset, longCount);
-        this.block.pokeLongArray(offset + position, src, srcOffset, longCount, order.needsSwap);
-        position += byteCount;
-    }
-
-    final void put(short[] src, int srcOffset, int shortCount) {
-        int byteCount = checkPutBounds(SizeOf.SHORT, src.length, srcOffset, shortCount);
-        this.block.pokeShortArray(offset + position, src, srcOffset, shortCount, order.needsSwap);
-        position += byteCount;
-    }
-
-    @Override
-    public ByteBuffer putChar(char value) {
-        int newPosition = position + SizeOf.CHAR;
-        if (newPosition > limit) {
-            throw new BufferOverflowException();
-        }
-        this.block.pokeShort(offset + position, (short) value, order);
-        position = newPosition;
-        return this;
-    }
-
-    @Override
-    public ByteBuffer putChar(int index, char value) {
-        checkIndex(index, SizeOf.CHAR);
-        this.block.pokeShort(offset + index, (short) value, order);
-        return this;
-    }
-
-    @Override
-    public ByteBuffer putDouble(double value) {
-        int newPosition = position + SizeOf.DOUBLE;
-        if (newPosition > limit) {
-            throw new BufferOverflowException();
-        }
-        this.block.pokeLong(offset + position, Double.doubleToRawLongBits(value), order);
-        position = newPosition;
-        return this;
-    }
-
-    @Override
-    public ByteBuffer putDouble(int index, double value) {
-        checkIndex(index, SizeOf.DOUBLE);
-        this.block.pokeLong(offset + index, Double.doubleToRawLongBits(value), order);
-        return this;
-    }
-
-    @Override
-    public ByteBuffer putFloat(float value) {
-        int newPosition = position + SizeOf.FLOAT;
-        if (newPosition > limit) {
-            throw new BufferOverflowException();
-        }
-        this.block.pokeInt(offset + position, Float.floatToRawIntBits(value), order);
-        position = newPosition;
-        return this;
-    }
-
-    @Override
-    public ByteBuffer putFloat(int index, float value) {
-        checkIndex(index, SizeOf.FLOAT);
-        this.block.pokeInt(offset + index, Float.floatToRawIntBits(value), order);
-        return this;
-    }
-
-    @Override
-    public ByteBuffer putInt(int value) {
-        int newPosition = position + SizeOf.INT;
-        if (newPosition > limit) {
-            throw new BufferOverflowException();
-        }
-        this.block.pokeInt(offset + position, value, order);
-        position = newPosition;
-        return this;
-    }
-
-    @Override
-    public ByteBuffer putInt(int index, int value) {
-        checkIndex(index, SizeOf.INT);
-        this.block.pokeInt(offset + index, value, order);
-        return this;
-    }
-
-    @Override
-    public ByteBuffer putLong(long value) {
-        int newPosition = position + SizeOf.LONG;
-        if (newPosition > limit) {
-            throw new BufferOverflowException();
-        }
-        this.block.pokeLong(offset + position, value, order);
-        position = newPosition;
-        return this;
-    }
-
-    @Override
-    public ByteBuffer putLong(int index, long value) {
-        checkIndex(index, SizeOf.LONG);
-        this.block.pokeLong(offset + index, value, order);
-        return this;
-    }
-
-    @Override
-    public ByteBuffer putShort(short value) {
-        int newPosition = position + SizeOf.SHORT;
-        if (newPosition > limit) {
-            throw new BufferOverflowException();
-        }
-        this.block.pokeShort(offset + position, value, order);
-        position = newPosition;
-        return this;
-    }
-
-    @Override
-    public ByteBuffer putShort(int index, short value) {
-        checkIndex(index, SizeOf.SHORT);
-        this.block.pokeShort(offset + index, value, order);
-        return this;
-    }
-
-    @Override
-    public ByteBuffer slice() {
-        return new ReadWriteDirectByteBuffer(block, remaining(), offset + position);
-    }
-
-}
diff --git a/luni/src/main/java/java/nio/ReadWriteDoubleArrayBuffer.java b/luni/src/main/java/java/nio/ReadWriteDoubleArrayBuffer.java
deleted file mode 100644
index f9328d6..0000000
--- a/luni/src/main/java/java/nio/ReadWriteDoubleArrayBuffer.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one or more
- *  contributor license agreements.  See the NOTICE file distributed with
- *  this work for additional information regarding copyright ownership.
- *  The ASF licenses this file to You 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 java.nio;
-
-/**
- * DoubleArrayBuffer, ReadWriteDoubleArrayBuffer and ReadOnlyDoubleArrayBuffer
- * compose the implementation of array based double buffers.
- * <p>
- * ReadWriteDoubleArrayBuffer extends DoubleArrayBuffer with all the write
- * methods.
- * </p>
- * <p>
- * This class is marked final for runtime performance.
- * </p>
- *
- */
-final class ReadWriteDoubleArrayBuffer extends DoubleArrayBuffer {
-
-    static ReadWriteDoubleArrayBuffer copy(DoubleArrayBuffer other, int markOfOther) {
-        ReadWriteDoubleArrayBuffer buf =
-                new ReadWriteDoubleArrayBuffer(other.capacity(), other.backingArray, other.offset);
-        buf.limit = other.limit;
-        buf.position = other.position();
-        buf.mark = markOfOther;
-        return buf;
-    }
-
-    ReadWriteDoubleArrayBuffer(double[] array) {
-        super(array);
-    }
-
-    ReadWriteDoubleArrayBuffer(int capacity) {
-        super(capacity);
-    }
-
-    ReadWriteDoubleArrayBuffer(int capacity, double[] backingArray, int arrayOffset) {
-        super(capacity, backingArray, arrayOffset);
-    }
-
-    @Override
-    public DoubleBuffer asReadOnlyBuffer() {
-        return ReadOnlyDoubleArrayBuffer.copy(this, mark);
-    }
-
-    @Override
-    public DoubleBuffer compact() {
-        System.arraycopy(backingArray, position + offset, backingArray, offset, remaining());
-        position = limit - position;
-        limit = capacity;
-        mark = UNSET_MARK;
-        return this;
-    }
-
-    @Override
-    public DoubleBuffer duplicate() {
-        return copy(this, mark);
-    }
-
-    @Override
-    public boolean isReadOnly() {
-        return false;
-    }
-
-    @Override double[] protectedArray() {
-        return backingArray;
-    }
-
-    @Override int protectedArrayOffset() {
-        return offset;
-    }
-
-    @Override boolean protectedHasArray() {
-        return true;
-    }
-
-    @Override
-    public DoubleBuffer put(double c) {
-        if (position == limit) {
-            throw new BufferOverflowException();
-        }
-        backingArray[offset + position++] = c;
-        return this;
-    }
-
-    @Override
-    public DoubleBuffer put(int index, double c) {
-        checkIndex(index);
-        backingArray[offset + index] = c;
-        return this;
-    }
-
-    @Override
-    public DoubleBuffer put(double[] src, int srcOffset, int doubleCount) {
-        if (doubleCount > remaining()) {
-            throw new BufferOverflowException();
-        }
-        System.arraycopy(src, srcOffset, backingArray, offset + position, doubleCount);
-        position += doubleCount;
-        return this;
-    }
-
-    @Override
-    public DoubleBuffer slice() {
-        return new ReadWriteDoubleArrayBuffer(remaining(), backingArray, offset + position);
-    }
-
-}
diff --git a/luni/src/main/java/java/nio/ReadWriteFloatArrayBuffer.java b/luni/src/main/java/java/nio/ReadWriteFloatArrayBuffer.java
deleted file mode 100644
index eee3aa7..0000000
--- a/luni/src/main/java/java/nio/ReadWriteFloatArrayBuffer.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one or more
- *  contributor license agreements.  See the NOTICE file distributed with
- *  this work for additional information regarding copyright ownership.
- *  The ASF licenses this file to You 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 java.nio;
-
-/**
- * FloatArrayBuffer, ReadWriteFloatArrayBuffer and ReadOnlyFloatArrayBuffer
- * compose the implementation of array based float buffers.
- * <p>
- * ReadWriteFloatArrayBuffer extends FloatArrayBuffer with all the write
- * methods.
- * </p>
- * <p>
- * This class is marked final for runtime performance.
- * </p>
- *
- */
-final class ReadWriteFloatArrayBuffer extends FloatArrayBuffer {
-
-    static ReadWriteFloatArrayBuffer copy(FloatArrayBuffer other, int markOfOther) {
-        ReadWriteFloatArrayBuffer buf =
-                new ReadWriteFloatArrayBuffer(other.capacity(), other.backingArray, other.offset);
-        buf.limit = other.limit;
-        buf.position = other.position();
-        buf.mark = markOfOther;
-        return buf;
-    }
-
-    ReadWriteFloatArrayBuffer(float[] array) {
-        super(array);
-    }
-
-    ReadWriteFloatArrayBuffer(int capacity) {
-        super(capacity);
-    }
-
-    ReadWriteFloatArrayBuffer(int capacity, float[] backingArray, int arrayOffset) {
-        super(capacity, backingArray, arrayOffset);
-    }
-
-    @Override
-    public FloatBuffer asReadOnlyBuffer() {
-        return ReadOnlyFloatArrayBuffer.copy(this, mark);
-    }
-
-    @Override
-    public FloatBuffer compact() {
-        System.arraycopy(backingArray, position + offset, backingArray, offset, remaining());
-        position = limit - position;
-        limit = capacity;
-        mark = UNSET_MARK;
-        return this;
-    }
-
-    @Override
-    public FloatBuffer duplicate() {
-        return copy(this, mark);
-    }
-
-    @Override
-    public boolean isReadOnly() {
-        return false;
-    }
-
-    @Override float[] protectedArray() {
-        return backingArray;
-    }
-
-    @Override int protectedArrayOffset() {
-        return offset;
-    }
-
-    @Override boolean protectedHasArray() {
-        return true;
-    }
-
-    @Override
-    public FloatBuffer put(float c) {
-        if (position == limit) {
-            throw new BufferOverflowException();
-        }
-        backingArray[offset + position++] = c;
-        return this;
-    }
-
-    @Override
-    public FloatBuffer put(int index, float c) {
-        checkIndex(index);
-        backingArray[offset + index] = c;
-        return this;
-    }
-
-    @Override
-    public FloatBuffer put(float[] src, int srcOffset, int floatCount) {
-        if (floatCount > remaining()) {
-            throw new BufferOverflowException();
-        }
-        System.arraycopy(src, srcOffset, backingArray, offset + position, floatCount);
-        position += floatCount;
-        return this;
-    }
-
-    @Override
-    public FloatBuffer slice() {
-        return new ReadWriteFloatArrayBuffer(remaining(), backingArray, offset + position);
-    }
-
-}
diff --git a/luni/src/main/java/java/nio/ReadWriteHeapByteBuffer.java b/luni/src/main/java/java/nio/ReadWriteHeapByteBuffer.java
deleted file mode 100644
index 02296c6..0000000
--- a/luni/src/main/java/java/nio/ReadWriteHeapByteBuffer.java
+++ /dev/null
@@ -1,248 +0,0 @@
-/* Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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 java.nio;
-
-import libcore.io.Memory;
-import libcore.io.SizeOf;
-
-/**
- * HeapByteBuffer, ReadWriteHeapByteBuffer and ReadOnlyHeapByteBuffer compose
- * the implementation of array based byte buffers.
- * <p>
- * ReadWriteHeapByteBuffer extends HeapByteBuffer with all the write methods.
- * </p>
- * <p>
- * This class is marked final for runtime performance.
- * </p>
- *
- */
-final class ReadWriteHeapByteBuffer extends HeapByteBuffer {
-
-    static ReadWriteHeapByteBuffer copy(HeapByteBuffer other, int markOfOther) {
-        ReadWriteHeapByteBuffer buf =
-                new ReadWriteHeapByteBuffer(other.backingArray, other.capacity(), other.offset);
-        buf.limit = other.limit;
-        buf.position = other.position();
-        buf.mark = markOfOther;
-        return buf;
-    }
-
-    ReadWriteHeapByteBuffer(byte[] backingArray) {
-        super(backingArray);
-    }
-
-    ReadWriteHeapByteBuffer(int capacity) {
-        super(capacity);
-    }
-
-    ReadWriteHeapByteBuffer(byte[] backingArray, int capacity, int arrayOffset) {
-        super(backingArray, capacity, arrayOffset);
-    }
-
-    @Override
-    public ByteBuffer asReadOnlyBuffer() {
-        return ReadOnlyHeapByteBuffer.copy(this, mark);
-    }
-
-    @Override
-    public ByteBuffer compact() {
-        System.arraycopy(backingArray, position + offset, backingArray, offset, remaining());
-        position = limit - position;
-        limit = capacity;
-        mark = UNSET_MARK;
-        return this;
-    }
-
-    @Override
-    public ByteBuffer duplicate() {
-        return copy(this, mark);
-    }
-
-    @Override
-    public boolean isReadOnly() {
-        return false;
-    }
-
-    @Override byte[] protectedArray() {
-        return backingArray;
-    }
-
-    @Override int protectedArrayOffset() {
-        return offset;
-    }
-
-    @Override boolean protectedHasArray() {
-        return true;
-    }
-
-    @Override
-    public ByteBuffer put(byte b) {
-        if (position == limit) {
-            throw new BufferOverflowException();
-        }
-        backingArray[offset + position++] = b;
-        return this;
-    }
-
-    @Override
-    public ByteBuffer put(int index, byte b) {
-        checkIndex(index);
-        backingArray[offset + index] = b;
-        return this;
-    }
-
-    @Override
-    public ByteBuffer put(byte[] src, int srcOffset, int byteCount) {
-        checkPutBounds(1, src.length, srcOffset, byteCount);
-        System.arraycopy(src, srcOffset, backingArray, offset + position, byteCount);
-        position += byteCount;
-        return this;
-    }
-
-    final void put(char[] src, int srcOffset, int charCount) {
-        int byteCount = checkPutBounds(SizeOf.CHAR, src.length, srcOffset, charCount);
-        Memory.unsafeBulkPut(backingArray, offset + position, byteCount, src, srcOffset, SizeOf.CHAR, order.needsSwap);
-        position += byteCount;
-    }
-
-    final void put(double[] src, int srcOffset, int doubleCount) {
-        int byteCount = checkPutBounds(SizeOf.DOUBLE, src.length, srcOffset, doubleCount);
-        Memory.unsafeBulkPut(backingArray, offset + position, byteCount, src, srcOffset, SizeOf.DOUBLE, order.needsSwap);
-        position += byteCount;
-    }
-
-    final void put(float[] src, int srcOffset, int floatCount) {
-        int byteCount = checkPutBounds(SizeOf.FLOAT, src.length, srcOffset, floatCount);
-        Memory.unsafeBulkPut(backingArray, offset + position, byteCount, src, srcOffset, SizeOf.FLOAT, order.needsSwap);
-        position += byteCount;
-    }
-
-    final void put(int[] src, int srcOffset, int intCount) {
-        int byteCount = checkPutBounds(SizeOf.INT, src.length, srcOffset, intCount);
-        Memory.unsafeBulkPut(backingArray, offset + position, byteCount, src, srcOffset, SizeOf.INT, order.needsSwap);
-        position += byteCount;
-    }
-
-    final void put(long[] src, int srcOffset, int longCount) {
-        int byteCount = checkPutBounds(SizeOf.LONG, src.length, srcOffset, longCount);
-        Memory.unsafeBulkPut(backingArray, offset + position, byteCount, src, srcOffset, SizeOf.LONG, order.needsSwap);
-        position += byteCount;
-    }
-
-    final void put(short[] src, int srcOffset, int shortCount) {
-        int byteCount = checkPutBounds(SizeOf.SHORT, src.length, srcOffset, shortCount);
-        Memory.unsafeBulkPut(backingArray, offset + position, byteCount, src, srcOffset, SizeOf.SHORT, order.needsSwap);
-        position += byteCount;
-    }
-
-    @Override
-    public ByteBuffer putChar(int index, char value) {
-        checkIndex(index, SizeOf.CHAR);
-        Memory.pokeShort(backingArray, offset + index, (short) value, order);
-        return this;
-    }
-
-    @Override
-    public ByteBuffer putChar(char value) {
-        int newPosition = position + SizeOf.CHAR;
-        if (newPosition > limit) {
-            throw new BufferOverflowException();
-        }
-        Memory.pokeShort(backingArray, offset + position, (short) value, order);
-        position = newPosition;
-        return this;
-    }
-
-    @Override
-    public ByteBuffer putDouble(double value) {
-        return putLong(Double.doubleToRawLongBits(value));
-    }
-
-    @Override
-    public ByteBuffer putDouble(int index, double value) {
-        return putLong(index, Double.doubleToRawLongBits(value));
-    }
-
-    @Override
-    public ByteBuffer putFloat(float value) {
-        return putInt(Float.floatToRawIntBits(value));
-    }
-
-    @Override
-    public ByteBuffer putFloat(int index, float value) {
-        return putInt(index, Float.floatToRawIntBits(value));
-    }
-
-    @Override
-    public ByteBuffer putInt(int value) {
-        int newPosition = position + SizeOf.INT;
-        if (newPosition > limit) {
-            throw new BufferOverflowException();
-        }
-        Memory.pokeInt(backingArray, offset + position, value, order);
-        position = newPosition;
-        return this;
-    }
-
-    @Override
-    public ByteBuffer putInt(int index, int value) {
-        checkIndex(index, SizeOf.INT);
-        Memory.pokeInt(backingArray, offset + index, value, order);
-        return this;
-    }
-
-    @Override
-    public ByteBuffer putLong(int index, long value) {
-        checkIndex(index, SizeOf.LONG);
-        Memory.pokeLong(backingArray, offset + index, value, order);
-        return this;
-    }
-
-    @Override
-    public ByteBuffer putLong(long value) {
-        int newPosition = position + SizeOf.LONG;
-        if (newPosition > limit) {
-            throw new BufferOverflowException();
-        }
-        Memory.pokeLong(backingArray, offset + position, value, order);
-        position = newPosition;
-        return this;
-    }
-
-    @Override
-    public ByteBuffer putShort(int index, short value) {
-        checkIndex(index, SizeOf.SHORT);
-        Memory.pokeShort(backingArray, offset + index, value, order);
-        return this;
-    }
-
-    @Override
-    public ByteBuffer putShort(short value) {
-        int newPosition = position + SizeOf.SHORT;
-        if (newPosition > limit) {
-            throw new BufferOverflowException();
-        }
-        Memory.pokeShort(backingArray, offset + position, value, order);
-        position = newPosition;
-        return this;
-    }
-
-    @Override
-    public ByteBuffer slice() {
-        return new ReadWriteHeapByteBuffer(backingArray, remaining(), offset + position);
-    }
-}
diff --git a/luni/src/main/java/java/nio/ReadWriteIntArrayBuffer.java b/luni/src/main/java/java/nio/ReadWriteIntArrayBuffer.java
deleted file mode 100644
index e8e67c2..0000000
--- a/luni/src/main/java/java/nio/ReadWriteIntArrayBuffer.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one or more
- *  contributor license agreements.  See the NOTICE file distributed with
- *  this work for additional information regarding copyright ownership.
- *  The ASF licenses this file to You 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 java.nio;
-
-/**
- * IntArrayBuffer, ReadWriteIntArrayBuffer and ReadOnlyIntArrayBuffer compose
- * the implementation of array based int buffers.
- * <p>
- * ReadWriteIntArrayBuffer extends IntArrayBuffer with all the write methods.
- * </p>
- * <p>
- * This class is marked final for runtime performance.
- * </p>
- *
- */
-final class ReadWriteIntArrayBuffer extends IntArrayBuffer {
-
-    static ReadWriteIntArrayBuffer copy(IntArrayBuffer other, int markOfOther) {
-        ReadWriteIntArrayBuffer buf =
-                new ReadWriteIntArrayBuffer(other.capacity(), other.backingArray, other.offset);
-        buf.limit = other.limit;
-        buf.position = other.position();
-        buf.mark = markOfOther;
-        return buf;
-    }
-
-    ReadWriteIntArrayBuffer(int[] array) {
-        super(array);
-    }
-
-    ReadWriteIntArrayBuffer(int capacity) {
-        super(capacity);
-    }
-
-    ReadWriteIntArrayBuffer(int capacity, int[] backingArray, int arrayOffset) {
-        super(capacity, backingArray, arrayOffset);
-    }
-
-    @Override
-    public IntBuffer asReadOnlyBuffer() {
-        return ReadOnlyIntArrayBuffer.copy(this, mark);
-    }
-
-    @Override
-    public IntBuffer compact() {
-        System.arraycopy(backingArray, position + offset, backingArray, offset, remaining());
-        position = limit - position;
-        limit = capacity;
-        mark = UNSET_MARK;
-        return this;
-    }
-
-    @Override
-    public IntBuffer duplicate() {
-        return copy(this, mark);
-    }
-
-    @Override
-    public boolean isReadOnly() {
-        return false;
-    }
-
-    @Override int[] protectedArray() {
-        return backingArray;
-    }
-
-    @Override int protectedArrayOffset() {
-        return offset;
-    }
-
-    @Override boolean protectedHasArray() {
-        return true;
-    }
-
-    @Override
-    public IntBuffer put(int c) {
-        if (position == limit) {
-            throw new BufferOverflowException();
-        }
-        backingArray[offset + position++] = c;
-        return this;
-    }
-
-    @Override
-    public IntBuffer put(int index, int c) {
-        checkIndex(index);
-        backingArray[offset + index] = c;
-        return this;
-    }
-
-    @Override
-    public IntBuffer put(int[] src, int srcOffset, int intCount) {
-        if (intCount > remaining()) {
-            throw new BufferOverflowException();
-        }
-        System.arraycopy(src, srcOffset, backingArray, offset + position, intCount);
-        position += intCount;
-        return this;
-    }
-
-    @Override
-    public IntBuffer slice() {
-        return new ReadWriteIntArrayBuffer(remaining(), backingArray, offset + position);
-    }
-
-}
diff --git a/luni/src/main/java/java/nio/ReadWriteLongArrayBuffer.java b/luni/src/main/java/java/nio/ReadWriteLongArrayBuffer.java
deleted file mode 100644
index df5f09f..0000000
--- a/luni/src/main/java/java/nio/ReadWriteLongArrayBuffer.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one or more
- *  contributor license agreements.  See the NOTICE file distributed with
- *  this work for additional information regarding copyright ownership.
- *  The ASF licenses this file to You 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 java.nio;
-
-/**
- * LongArrayBuffer, ReadWriteLongArrayBuffer and ReadOnlyLongArrayBuffer compose
- * the implementation of array based long buffers.
- * <p>
- * ReadWriteLongArrayBuffer extends LongArrayBuffer with all the write methods.
- * </p>
- * <p>
- * This class is marked final for runtime performance.
- * </p>
- *
- */
-final class ReadWriteLongArrayBuffer extends LongArrayBuffer {
-
-    static ReadWriteLongArrayBuffer copy(LongArrayBuffer other, int markOfOther) {
-        ReadWriteLongArrayBuffer buf =
-                new ReadWriteLongArrayBuffer(other.capacity(), other.backingArray, other.offset);
-        buf.limit = other.limit;
-        buf.position = other.position();
-        buf.mark = markOfOther;
-        return buf;
-    }
-
-    ReadWriteLongArrayBuffer(long[] array) {
-        super(array);
-    }
-
-    ReadWriteLongArrayBuffer(int capacity) {
-        super(capacity);
-    }
-
-    ReadWriteLongArrayBuffer(int capacity, long[] backingArray, int arrayOffset) {
-        super(capacity, backingArray, arrayOffset);
-    }
-
-    @Override
-    public LongBuffer asReadOnlyBuffer() {
-        return ReadOnlyLongArrayBuffer.copy(this, mark);
-    }
-
-    @Override
-    public LongBuffer compact() {
-        System.arraycopy(backingArray, position + offset, backingArray, offset, remaining());
-        position = limit - position;
-        limit = capacity;
-        mark = UNSET_MARK;
-        return this;
-    }
-
-    @Override
-    public LongBuffer duplicate() {
-        return copy(this, mark);
-    }
-
-    @Override
-    public boolean isReadOnly() {
-        return false;
-    }
-
-    @Override long[] protectedArray() {
-        return backingArray;
-    }
-
-    @Override int protectedArrayOffset() {
-        return offset;
-    }
-
-    @Override boolean protectedHasArray() {
-        return true;
-    }
-
-    @Override
-    public LongBuffer put(long c) {
-        if (position == limit) {
-            throw new BufferOverflowException();
-        }
-        backingArray[offset + position++] = c;
-        return this;
-    }
-
-    @Override
-    public LongBuffer put(int index, long c) {
-        checkIndex(index);
-        backingArray[offset + index] = c;
-        return this;
-    }
-
-    @Override
-    public LongBuffer put(long[] src, int srcOffset, int longCount) {
-        if (longCount > remaining()) {
-            throw new BufferOverflowException();
-        }
-        System.arraycopy(src, srcOffset, backingArray, offset + position, longCount);
-        position += longCount;
-        return this;
-    }
-
-    @Override
-    public LongBuffer slice() {
-        return new ReadWriteLongArrayBuffer(remaining(), backingArray, offset + position);
-    }
-
-}
diff --git a/luni/src/main/java/java/nio/ReadWriteShortArrayBuffer.java b/luni/src/main/java/java/nio/ReadWriteShortArrayBuffer.java
deleted file mode 100644
index 2a43e91..0000000
--- a/luni/src/main/java/java/nio/ReadWriteShortArrayBuffer.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- *  Licensed to the Apache Software Foundation (ASF) under one or more
- *  contributor license agreements.  See the NOTICE file distributed with
- *  this work for additional information regarding copyright ownership.
- *  The ASF licenses this file to You 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 java.nio;
-
-/**
- * ShortArrayBuffer, ReadWriteShortArrayBuffer and ReadOnlyShortArrayBuffer
- * compose the implementation of array based short buffers.
- * <p>
- * ReadWriteShortArrayBuffer extends ShortArrayBuffer with all the write
- * methods.
- * </p>
- * <p>
- * This class is marked final for runtime performance.
- * </p>
- *
- */
-final class ReadWriteShortArrayBuffer extends ShortArrayBuffer {
-
-    static ReadWriteShortArrayBuffer copy(ShortArrayBuffer other, int markOfOther) {
-        ReadWriteShortArrayBuffer buf =
-                new ReadWriteShortArrayBuffer(other.capacity(), other.backingArray, other.offset);
-        buf.limit = other.limit;
-        buf.position = other.position();
-        buf.mark = markOfOther;
-        return buf;
-    }
-
-    ReadWriteShortArrayBuffer(short[] array) {
-        super(array);
-    }
-
-    ReadWriteShortArrayBuffer(int capacity) {
-        super(capacity);
-    }
-
-    ReadWriteShortArrayBuffer(int capacity, short[] backingArray,
-            int arrayOffset) {
-        super(capacity, backingArray, arrayOffset);
-    }
-
-    @Override
-    public ShortBuffer asReadOnlyBuffer() {
-        return ReadOnlyShortArrayBuffer.copy(this, mark);
-    }
-
-    @Override
-    public ShortBuffer compact() {
-        System.arraycopy(backingArray, position + offset, backingArray, offset, remaining());
-        position = limit - position;
-        limit = capacity;
-        mark = UNSET_MARK;
-        return this;
-    }
-
-    @Override
-    public ShortBuffer duplicate() {
-        return copy(this, mark);
-    }
-
-    @Override
-    public boolean isReadOnly() {
-        return false;
-    }
-
-    @Override short[] protectedArray() {
-        return backingArray;
-    }
-
-    @Override int protectedArrayOffset() {
-        return offset;
-    }
-
-    @Override boolean protectedHasArray() {
-        return true;
-    }
-
-    @Override
-    public ShortBuffer put(short c) {
-        if (position == limit) {
-            throw new BufferOverflowException();
-        }
-        backingArray[offset + position++] = c;
-        return this;
-    }
-
-    @Override
-    public ShortBuffer put(int index, short c) {
-        checkIndex(index);
-        backingArray[offset + index] = c;
-        return this;
-    }
-
-    @Override
-    public ShortBuffer put(short[] src, int srcOffset, int shortCount) {
-        if (shortCount > remaining()) {
-            throw new BufferOverflowException();
-        }
-        System.arraycopy(src, srcOffset, backingArray, offset + position, shortCount);
-        position += shortCount;
-        return this;
-    }
-
-    @Override
-    public ShortBuffer slice() {
-        return new ReadWriteShortArrayBuffer(remaining(), backingArray, offset + position);
-    }
-
-}
diff --git a/luni/src/main/java/java/nio/ShortArrayBuffer.java b/luni/src/main/java/java/nio/ShortArrayBuffer.java
index bf6f752..a092cb0 100644
--- a/luni/src/main/java/java/nio/ShortArrayBuffer.java
+++ b/luni/src/main/java/java/nio/ShortArrayBuffer.java
@@ -18,69 +18,141 @@
 package java.nio;
 
 /**
- * ShortArrayBuffer, ReadWriteShortArrayBuffer and ReadOnlyShortArrayBuffer
- * compose the implementation of array based short buffers.
- * <p>
- * ShortArrayBuffer implements all the shared readonly methods and is extended
- * by the other two classes.
- * </p>
- * <p>
- * All methods are marked final for runtime performance.
- * </p>
- *
+ * ShortArrayBuffer implements short[]-based ShortBuffers.
  */
-abstract class ShortArrayBuffer extends ShortBuffer {
+final class ShortArrayBuffer extends ShortBuffer {
 
-    protected final short[] backingArray;
+  private final short[] backingArray;
 
-    protected final int offset;
+  private final int arrayOffset;
 
-    ShortArrayBuffer(short[] array) {
-        this(array.length, array, 0);
+  private final boolean isReadOnly;
+
+  ShortArrayBuffer(short[] array) {
+    this(array.length, array, 0, false);
+  }
+
+  private ShortArrayBuffer(int capacity, short[] backingArray, int arrayOffset, boolean isReadOnly) {
+    super(capacity);
+    this.backingArray = backingArray;
+    this.arrayOffset = arrayOffset;
+    this.isReadOnly = isReadOnly;
+  }
+
+  private static ShortArrayBuffer copy(ShortArrayBuffer other, int markOfOther, boolean isReadOnly) {
+    ShortArrayBuffer buf = new ShortArrayBuffer(other.capacity(), other.backingArray, other.arrayOffset, isReadOnly);
+    buf.limit = other.limit;
+    buf.position = other.position();
+    buf.mark = markOfOther;
+    return buf;
+  }
+
+  @Override public ShortBuffer asReadOnlyBuffer() {
+    return copy(this, mark, true);
+  }
+
+  @Override public ShortBuffer compact() {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
     }
+    System.arraycopy(backingArray, position + arrayOffset, backingArray, arrayOffset, remaining());
+    position = limit - position;
+    limit = capacity;
+    mark = UNSET_MARK;
+    return this;
+  }
 
-    ShortArrayBuffer(int capacity) {
-        this(capacity, new short[capacity], 0);
+  @Override public ShortBuffer duplicate() {
+    return copy(this, mark, isReadOnly);
+  }
+
+  @Override public ShortBuffer slice() {
+    return new ShortArrayBuffer(remaining(), backingArray, arrayOffset + position, isReadOnly);
+  }
+
+  @Override public boolean isReadOnly() {
+    return isReadOnly;
+  }
+
+  @Override short[] protectedArray() {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
     }
+    return backingArray;
+  }
 
-    ShortArrayBuffer(int capacity, short[] backingArray, int offset) {
-        super(capacity);
-        this.backingArray = backingArray;
-        this.offset = offset;
+  @Override int protectedArrayOffset() {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
     }
+    return arrayOffset;
+  }
 
-    @Override
-    public final short get() {
-        if (position == limit) {
-            throw new BufferUnderflowException();
-        }
-        return backingArray[offset + position++];
+  @Override boolean protectedHasArray() {
+    if (isReadOnly) {
+      return false;
     }
+    return true;
+  }
 
-    @Override
-    public final short get(int index) {
-        checkIndex(index);
-        return backingArray[offset + index];
+  @Override public final short get() {
+    if (position == limit) {
+      throw new BufferUnderflowException();
     }
+    return backingArray[arrayOffset + position++];
+  }
 
-    @Override
-    public final ShortBuffer get(short[] dst, int dstOffset, int shortCount) {
-        if (shortCount > remaining()) {
-            throw new BufferUnderflowException();
-        }
-        System.arraycopy(backingArray, offset + position, dst, dstOffset, shortCount);
-        position += shortCount;
-        return this;
+  @Override public final short get(int index) {
+    checkIndex(index);
+    return backingArray[arrayOffset + index];
+  }
+
+  @Override public final ShortBuffer get(short[] dst, int dstOffset, int shortCount) {
+    if (shortCount > remaining()) {
+      throw new BufferUnderflowException();
     }
+    System.arraycopy(backingArray, arrayOffset + position, dst, dstOffset, shortCount);
+    position += shortCount;
+    return this;
+  }
 
-    @Override
-    public final boolean isDirect() {
-        return false;
+  @Override public final boolean isDirect() {
+    return false;
+  }
+
+  @Override public final ByteOrder order() {
+    return ByteOrder.nativeOrder();
+  }
+
+  @Override public ShortBuffer put(short c) {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
     }
-
-    @Override
-    public final ByteOrder order() {
-        return ByteOrder.nativeOrder();
+    if (position == limit) {
+      throw new BufferOverflowException();
     }
+    backingArray[arrayOffset + position++] = c;
+    return this;
+  }
 
+  @Override public ShortBuffer put(int index, short c) {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
+    }
+    checkIndex(index);
+    backingArray[arrayOffset + index] = c;
+    return this;
+  }
+
+  @Override public ShortBuffer put(short[] src, int srcOffset, int shortCount) {
+    if (isReadOnly) {
+      throw new ReadOnlyBufferException();
+    }
+    if (shortCount > remaining()) {
+      throw new BufferOverflowException();
+    }
+    System.arraycopy(src, srcOffset, backingArray, arrayOffset + position, shortCount);
+    position += shortCount;
+    return this;
+  }
 }
diff --git a/luni/src/main/java/java/nio/ShortBuffer.java b/luni/src/main/java/java/nio/ShortBuffer.java
index d12a49e..ea20bed 100644
--- a/luni/src/main/java/java/nio/ShortBuffer.java
+++ b/luni/src/main/java/java/nio/ShortBuffer.java
@@ -48,7 +48,7 @@
         if (capacity < 0) {
             throw new IllegalArgumentException("capacity < 0: " + capacity);
         }
-        return new ReadWriteShortArrayBuffer(capacity);
+        return new ShortArrayBuffer(new short[capacity]);
     }
 
     /**
@@ -85,7 +85,7 @@
      */
     public static ShortBuffer wrap(short[] array, int start, int shortCount) {
         Arrays.checkOffsetAndCount(array.length, start, shortCount);
-        ShortBuffer buf = new ReadWriteShortArrayBuffer(array);
+        ShortBuffer buf = new ShortArrayBuffer(array);
         buf.position = start;
         buf.limit = start + shortCount;
         return buf;
@@ -425,6 +425,9 @@
      *                if no changes may be made to the contents of this buffer.
      */
     public ShortBuffer put(ShortBuffer src) {
+        if (isReadOnly()) {
+            throw new ReadOnlyBufferException();
+        }
         if (src == this) {
             throw new IllegalArgumentException("src == this");
         }
diff --git a/luni/src/test/java/libcore/java/nio/BufferTest.java b/luni/src/test/java/libcore/java/nio/BufferTest.java
index 4c500a5..a4e5eaf 100644
--- a/luni/src/test/java/libcore/java/nio/BufferTest.java
+++ b/luni/src/test/java/libcore/java/nio/BufferTest.java
@@ -714,7 +714,7 @@
 
     public void testHasArrayOnJniDirectByteBuffer() throws Exception {
         // Simulate a call to JNI's NewDirectByteBuffer.
-        Class<?> c = Class.forName("java.nio.ReadWriteDirectByteBuffer");
+        Class<?> c = Class.forName("java.nio.DirectByteBuffer");
         Constructor<?> ctor = c.getDeclaredConstructor(int.class, int.class);
         ctor.setAccessible(true);
         ByteBuffer bb = (ByteBuffer) ctor.newInstance(0, 0);
@@ -807,4 +807,52 @@
             assertTrue(expected.getMessage().contains("limit=0"));
         }
     }
+
+    public void testUsingDirectBufferAsMappedBuffer() throws Exception {
+        MappedByteBuffer notMapped = (MappedByteBuffer) ByteBuffer.allocateDirect(1);
+        try {
+            notMapped.force();
+            fail();
+        } catch (UnsupportedOperationException expected) {
+        }
+        try {
+            notMapped.isLoaded();
+            fail();
+        } catch (UnsupportedOperationException expected) {
+        }
+        try {
+            notMapped.load();
+            fail();
+        } catch (UnsupportedOperationException expected) {
+        }
+
+        MappedByteBuffer mapped = (MappedByteBuffer) allocateMapped(1);
+        mapped.force();
+        mapped.isLoaded();
+        mapped.load();
+    }
+
+    // https://code.google.com/p/android/issues/detail?id=53637
+    public void testBug53637() throws Exception {
+        MappedByteBuffer mapped = (MappedByteBuffer) allocateMapped(1);
+        mapped.get();
+        mapped.rewind();
+        mapped.get();
+
+        mapped.rewind();
+        mapped.mark();
+        mapped.get();
+        mapped.reset();
+        mapped.get();
+
+        mapped.rewind();
+        mapped.get();
+        mapped.clear();
+        mapped.get();
+
+        mapped.rewind();
+        mapped.get();
+        mapped.flip();
+        mapped.get();
+    }
 }