Merge "Add CarFragmentActivity"
diff --git a/car-libs/car-core/src/android/support/car/annotation/VersionDef.java b/car-libs/car-core/src/android/support/car/annotation/VersionDef.java
new file mode 100644
index 0000000..21ad653
--- /dev/null
+++ b/car-libs/car-core/src/android/support/car/annotation/VersionDef.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.car.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation for specifying supported version of member for a Parcelable.
+ * Version defaults to 1 if only @VersionDef is used without specifying version.
+ */
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.SOURCE)
+public @interface VersionDef {
+    int version() default 1;
+}
diff --git a/car-libs/car-core/src/android/support/car/app/CarProxyActivity.java b/car-libs/car-core/src/android/support/car/app/CarProxyActivity.java
index 3084366..de9be23 100644
--- a/car-libs/car-core/src/android/support/car/app/CarProxyActivity.java
+++ b/car-libs/car-core/src/android/support/car/app/CarProxyActivity.java
@@ -308,6 +308,11 @@
         }
     }
 
+    // TODO: eventually we should get CarActivity to directly host fragment, see b/25935686.
+    public CarActivity getCarActivity() {
+        return mCarActivity;
+    }
+
     private static Integer[] convertArray(int[] array) {
         Integer[] grantResults = new Integer[array.length];
         for (int i = 0; i < array.length; i++) {
diff --git a/car-libs/car-core/src/android/support/car/input/CarRestrictedEditText.java b/car-libs/car-core/src/android/support/car/input/CarRestrictedEditText.java
index 2d4d900..37533bb 100644
--- a/car-libs/car-core/src/android/support/car/input/CarRestrictedEditText.java
+++ b/car-libs/car-core/src/android/support/car/input/CarRestrictedEditText.java
@@ -46,8 +46,8 @@
     private KeyListener mListener;
 
     public interface KeyListener {
-        void onKeyDown(char key);
-        void onKeyUp(char key);
+        void onKeyDown(int keyCode);
+        void onKeyUp(int keyCode);
         void onCommitText(String input);
         void onCloseKeyboard();
         void onDelete();
@@ -112,9 +112,17 @@
             public boolean sendKeyEvent(android.view.KeyEvent event) {
                 if (mListener != null) {
                     if (event.getAction() == KeyEvent.ACTION_DOWN) {
-                        mListener.onKeyDown((char) event.getKeyCode());
+                        mListener.onKeyDown(event.getKeyCode());
                     } else if (event.getAction() == KeyEvent.ACTION_UP) {
-                        mListener.onKeyUp((char) event.getKeyCode());
+                        mListener.onKeyUp(event.getKeyCode());
+
+                        // InputMethodService#sendKeyChar doesn't call
+                        // InputConnection#commitText for digit chars.
+                        // TODO: fix projected IME to be in coherence with system IME.
+                        char unicodeChar = (char) event.getUnicodeChar();
+                        if (Character.isDigit(unicodeChar)) {
+                            commitText(String.valueOf(unicodeChar), 1);
+                        }
                     }
                     return true;
                 } else {
diff --git a/car-libs/car-core/src/android/support/car/os/ExtendableParcelable.java b/car-libs/car-core/src/android/support/car/os/ExtendableParcelable.java
new file mode 100644
index 0000000..e107102
--- /dev/null
+++ b/car-libs/car-core/src/android/support/car/os/ExtendableParcelable.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support.car.os;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.support.car.annotation.VersionDef;
+
+/**
+ * ExtendableParcelable helps extending Parcelable in future in safe way.
+ * Basic idea is to write version code and payload length when writing to Parcel.
+ * Reader side reads version and do following actions:
+ *   - if writer version <= reader version: Reader should know up to which element is supported by
+ *     writer and read only up to that portion.
+ *   - if writer version > reader version: Reader reads all members it knows and throw away remaing
+ *     data so that additional Parcel reading can be done safely.
+ *
+ * For reader side:
+ *  - In constructor with Parcel, call super(Parcel) first so that version number and payload length
+ *    is read properly.
+ *  - Call {@link #readHeader(Parcel)}.
+ *  - After finishing all recognized or available members, call
+ *    {@link #completeReading(Parcel, int)} with second argument set to return value from
+ *    {@link #readHeader(Parcel)}. This will throw away any unread data.
+ *
+ * For writer side, when implementing writeToParcel:
+ *   - call {@link #writeHeader(Parcel)} before writing anything else.
+ *   - call {@link #completeWriting(Parcel, int)} with second argument set to return value from
+ *     {@link #writeHeader(Parcel)}.
+ */
+public abstract class ExtendableParcelable implements Parcelable {
+    /**
+     * Version of this Parcelable. Reader side needs to read only up to the version written.
+     * This does not represent class version but contents version. For example, original
+     * Parcelable (=V1) passed to V2 supporting Parcelable will have version 1 even if
+     * V2 supporting Parcelable may have additional member variables added in V2.
+     */
+    @VersionDef(version = 1)
+    public final int version;
+
+    /**
+     * Constructor for reading parcel. Always call this first before reading anything else.
+     * @param in
+     * @param maxVersion
+     */
+    protected ExtendableParcelable(Parcel in, int version) {
+        int writerVersion = in.readInt();
+        if (version < writerVersion) { // version limited by reader
+            this.version = version;
+        } else { // version limited by writer
+            this.version = writerVersion;
+        }
+    }
+
+    /**
+     * Constructor for writer side. Version should be always set.
+     * @param version
+     */
+    protected ExtendableParcelable(int version) {
+        this.version = version;
+    }
+
+    /**
+     * Read header of Parcelable from Pacel. This should be done after super(Parcel, int) and
+     * before reading any Parcel. After all reading is done, {@link #completeReading(Parcel, int)}
+     * should be called.
+     *
+     * @param in
+     * @return last position. This should be passed to {@link #completeReading(Parcel, int)}.
+     */
+    protected int readHeader(Parcel in) {
+        int payloadLength = in.readInt();
+        int startingPosition = in.dataPosition();
+        return startingPosition + payloadLength;
+    }
+
+    /**
+     * Complete reading and safely throw away any unread data.
+     * @param in
+     * @param lastPosition Last position of of this Parcelable in the passed Parcel.
+     *                     The value is passed from {@link #readHeader(Parcel)}.
+     */
+    protected void completeReading(Parcel in, int lastPosition) {
+        in.setDataPosition(lastPosition);
+    }
+
+    /**
+     * Write header for writing to Parcel. This should be done before writing anything else.
+     * Code to use ths can look like:
+     *   int pos = writeHeader(dest);
+     *   dest.writeInt(0); // whatever relevant data
+     *   ...
+     *   completeWrite(dest, pos);
+     *
+     * @param dest
+     * @return startingPosition which should be passed when calling completeWrite.
+     */
+    protected int writeHeader(Parcel dest) {
+        dest.writeInt(version);
+        // temporary value for playload length. will be replaced in completeWrite
+        dest.writeInt(0);
+        // previous int is 4 bytes before this.
+        return dest.dataPosition();
+    }
+
+    /**
+     * Complete writing the current Parcelable. No more write to Parcel should be done after
+     * this call.
+     * @param dest
+     * @param startingPosition startingPosition returned from writeHeader
+     */
+    protected void completeWriting(Parcel dest, int startingPosition) {
+        int currentPosition = dest.dataPosition();
+        dest.setDataPosition(startingPosition - 4);
+        int payloadLength = currentPosition - startingPosition;
+        dest.writeInt(payloadLength);
+        dest.setDataPosition(currentPosition);
+    }
+}
diff --git a/tests/api_test/src/com/android/support/car/apitest/ExtendableParcelableTest.java b/tests/api_test/src/com/android/support/car/apitest/ExtendableParcelableTest.java
new file mode 100644
index 0000000..d6a3264
--- /dev/null
+++ b/tests/api_test/src/com/android/support/car/apitest/ExtendableParcelableTest.java
@@ -0,0 +1,482 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.support.car.apitest;
+
+import android.os.Parcel;
+import android.support.car.annotation.VersionDef;
+import android.support.car.os.ExtendableParcelable;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import java.util.Arrays;
+
+public class ExtendableParcelableTest extends AndroidTestCase {
+
+    private static final String TAG = ExtendableParcelableTest.class.getSimpleName();
+
+    public static class V1Parcelable extends ExtendableParcelable {
+        @VersionDef(version = 1)
+        public final byte[] byteData0;
+        @VersionDef(version = 1)
+        public final int intData0;
+        @VersionDef(version = 1)
+        public final String stringData0;
+        @VersionDef(version = 1)
+        public final int intData1;
+
+        private static final int VERSION = 1;
+
+        public V1Parcelable(byte[] byteData0, int intData0, String stringData0, int intData1) {
+            super(VERSION);
+            this.byteData0 = byteData0;
+            this.intData0 = intData0;
+            this.stringData0 = stringData0;
+            this.intData1 = intData1;
+        }
+
+        public V1Parcelable(Parcel in) {
+            super(in, VERSION);
+            int lastPosition = readHeader(in);
+            byteData0 = in.createByteArray();
+            intData0 = in.readInt();
+            stringData0 = in.readString();
+            intData1 = in.readInt();
+            completeReading(in, lastPosition);
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            int pos = writeHeader(dest);
+            dest.writeByteArray(byteData0);
+            dest.writeInt(intData0);
+            dest.writeString(stringData0);
+            dest.writeInt(intData1);
+            completeWriting(dest, pos);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (o instanceof V1Parcelable) {
+                V1Parcelable other = (V1Parcelable) o;
+                return Arrays.equals(byteData0, other.byteData0) && intData0 == other.intData0 &&
+                        (stringData0 == null ? other.stringData0 == null :
+                            stringData0.equals(other.stringData0)) &&
+                            intData1 == other.intData1;
+            }
+            return false;
+        }
+
+        @Override
+        public String toString() {
+            return "V1Parcelable byteData0:" + Arrays.toString(byteData0) + ",intData0:" + intData0
+                    + ",stringData0:" + stringData0 + ",intData1:" + intData1;
+        }
+    }
+
+    public static class V2Parcelable extends ExtendableParcelable {
+        @VersionDef(version = 1)
+        public final byte[] byteData0;
+        @VersionDef(version = 1)
+        public final int intData0;
+        @VersionDef(version = 1)
+        public final String stringData0;
+        @VersionDef(version = 1)
+        public final int intData1;
+        @VersionDef(version = 2)
+        public final byte[] byteData1;
+        @VersionDef(version = 2)
+        public final int intData2;
+
+        private static final int VERSION = 2;
+
+        public V2Parcelable(byte[] byteData0, int intData0, String stringData0, int intData1,
+                byte[] byteData1, int intData2) {
+            super(VERSION);
+            this.byteData0 = byteData0;
+            this.intData0 = intData0;
+            this.stringData0 = stringData0;
+            this.intData1 = intData1;
+            this.byteData1 = byteData1;
+            this.intData2 = intData2;
+        }
+
+        public V2Parcelable(Parcel in) {
+            super(in, VERSION);
+            int lastPosition = readHeader(in);
+            byteData0 = in.createByteArray();
+            intData0 = in.readInt();
+            stringData0 = in.readString();
+            intData1 = in.readInt();
+            if (version >= 2) { // do not use VERSION here as VERSION will become 3 in next revision
+                byteData1 = in.createByteArray();
+                intData2 = in.readInt();
+            } else {
+                byteData1 = null;
+                intData2 = 0;
+            }
+            completeReading(in, lastPosition);
+        }
+
+        /** provide has method if null check is not possible. */
+        public boolean hasIntData1() {
+            return version >= 2;
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            int pos = writeHeader(dest);
+            dest.writeByteArray(byteData0);
+            dest.writeInt(intData0);
+            dest.writeString(stringData0);
+            dest.writeInt(intData1);
+            dest.writeByteArray(byteData1);
+            dest.writeInt(intData2);
+            completeWriting(dest, pos);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (o instanceof V2Parcelable) {
+                V2Parcelable other = (V2Parcelable) o;
+                return Arrays.equals(byteData0, other.byteData0) && intData0 == other.intData0 &&
+                        (stringData0 == null ? other.stringData0 == null :
+                            stringData0.equals(other.stringData0)) &&
+                            intData1 == other.intData1 && Arrays.equals(byteData1, other.byteData1)
+                            && intData2 == other.intData2;
+            }
+            return false;
+        }
+
+        @Override
+        public String toString() {
+            return "V2Parcelable byteData0:" + Arrays.toString(byteData0) + ",intData0:" + intData0
+                    + ",stringData0:" + stringData0 + ",intData1:" + intData1 +
+                    ",byteData1:" + Arrays.toString(byteData1) + ",intData2:" + intData2;
+        }
+    }
+
+    public static class V3Parcelable extends ExtendableParcelable {
+        @VersionDef(version = 1)
+        public final byte[] byteData0;
+        @VersionDef(version = 1)
+        public final int intData0;
+        @VersionDef(version = 1)
+        public final String stringData0;
+        @VersionDef(version = 1)
+        public final int intData1;
+        @VersionDef(version = 2)
+        public final byte[] byteData1;
+        @VersionDef(version = 2)
+        public final int intData2;
+        @VersionDef(version = 3)
+        public final String stringData1;
+        @VersionDef(version = 3)
+        public final int intData3;
+
+        private static final int VERSION = 3;
+
+        public V3Parcelable(byte[] byteData0, int intData0, String stringData0, int intData1,
+                byte[] byteData1, int intData2, String stringData1, int intData3) {
+            super(VERSION);
+            this.byteData0 = byteData0;
+            this.intData0 = intData0;
+            this.stringData0 = stringData0;
+            this.intData1 = intData1;
+            this.byteData1 = byteData1;
+            this.intData2 = intData2;
+            this.stringData1 = stringData1;
+            this.intData3 = intData3;
+        }
+
+        public V3Parcelable(Parcel in) {
+            super(in, VERSION);
+            int lastPosition = readHeader(in);
+            byteData0 = in.createByteArray();
+            intData0 = in.readInt();
+            stringData0 = in.readString();
+            intData1 = in.readInt();
+            if (version >= 2) {
+                byteData1 = in.createByteArray();
+                intData2 = in.readInt();
+            } else {
+                byteData1 = null;
+                intData2 = 0;
+            }
+            if (version >= 3) {
+                stringData1 = in.readString();
+                intData3 = in.readInt();
+            } else {
+                stringData1 = null;
+                intData3 = 0;
+            }
+            completeReading(in, lastPosition);
+        }
+
+        /** provide has method if null check is not possible. */
+        public boolean hasIntData1() {
+            return version >= 2;
+        }
+
+        /** provide has method if null check is not possible. */
+        public boolean hasIntData2() {
+            return version >= 3;
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            int pos = writeHeader(dest);
+            dest.writeByteArray(byteData0);
+            dest.writeInt(intData0);
+            dest.writeString(stringData0);
+            dest.writeInt(intData1);
+            dest.writeByteArray(byteData1);
+            dest.writeInt(intData2);
+            dest.writeString(stringData1);
+            dest.writeInt(intData3);
+            completeWriting(dest, pos);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (o instanceof V3Parcelable) {
+                V3Parcelable other = (V3Parcelable) o;
+                return Arrays.equals(byteData0, other.byteData0) && intData0 == other.intData0 &&
+                        (stringData0 == null ? other.stringData0 == null :
+                            stringData0.equals(other.stringData0)) &&
+                            intData1 == other.intData1 && Arrays.equals(byteData1, other.byteData1)
+                            && intData2 == other.intData2 &&
+                            (stringData1 == null ? other.stringData1 == null :
+                                stringData1.equals(other.stringData1)) &&
+                            intData3 == other.intData3;
+            }
+            return false;
+        }
+
+        @Override
+        public String toString() {
+            return "V3Parcelable byteData0:" + Arrays.toString(byteData0) + ",intData0:" + intData0
+                    + ",stringData0:" + stringData0 + ",intData1:" + intData1 +
+                    ",byteData1:" + Arrays.toString(byteData1) + ",intData2:" + intData2 +
+                    ",stringData1:" + stringData1 + ",intData3:" + intData3;
+        }
+    }
+
+    public void testV1ToV3() throws Exception {
+        Parcel p = Parcel.obtain();
+        int startPos = p.dataPosition();
+        byte[] byteData0 = new byte[] { 0x0, 0x1, 0x2 };
+        int intData0 = 1234;
+        String stringData0 = null;
+        int intData1 = 5678;
+        V1Parcelable v1 = new V1Parcelable(byteData0, intData0, stringData0, intData1);
+        // expected after reading parcel
+        V3Parcelable v3Expected = new V3Parcelable(byteData0, intData0, stringData0, intData1,
+                null, 0, null, 0);
+        v1.writeToParcel(p, 0);
+        final int additionalData = 0x8fffffff;
+        p.writeInt(additionalData);
+
+        p.setDataPosition(startPos);
+        V3Parcelable v3 = new V3Parcelable(p);
+        Log.i(TAG, "v1:" + v1);
+        Log.i(TAG, "v3 expected:" + v3Expected);
+        Log.i(TAG, "v3 read:" + v3);
+        assertTrue(v3Expected.equals(v3));
+        assertEquals(1, v3.version);
+        assertFalse(v3.hasIntData1());
+        assertFalse(v3.hasIntData2());
+        assertEquals(additionalData, p.readInt());
+    }
+
+    public void testV2ToV3() throws Exception {
+        Parcel p = Parcel.obtain();
+        int startPos = p.dataPosition();
+        byte[] byteData0 = new byte[] { 0x0, 0x1, 0x2 };
+        int intData0 = 1234;
+        String stringData0 = null;
+        int intData1 = 5678;
+        byte[] byteData1 = new byte[] { 0x3, 0x4, 0x5 };
+        int intData2 = 9012;
+        V2Parcelable v2 = new V2Parcelable(byteData0, intData0, stringData0, intData1, byteData1,
+                intData2);
+        // expected after reading parcel
+        V3Parcelable v3Expected = new V3Parcelable(byteData0, intData0, stringData0, intData1,
+                byteData1, intData2, null, 0);
+        v2.writeToParcel(p, 0);
+        final int additionalData = 0x8fffffff;
+        p.writeInt(additionalData);
+
+        p.setDataPosition(startPos);
+        V3Parcelable v3 = new V3Parcelable(p);
+        Log.i(TAG, "v2:" + v2);
+        Log.i(TAG, "v3 expected:" + v3Expected);
+        Log.i(TAG, "v3 read:" + v3);
+        assertTrue(v3Expected.equals(v3));
+        assertEquals(2, v3.version);
+        assertTrue(v3.hasIntData1());
+        assertFalse(v3.hasIntData2());
+        assertEquals(additionalData, p.readInt());
+    }
+
+    public void testV3ToV1() throws Exception {
+        Parcel p = Parcel.obtain();
+        int startPos = p.dataPosition();
+        byte[] byteData0 = new byte[] { 0x0, 0x1, 0x2 };
+        int intData0 = 1234;
+        String stringData0 = null;
+        int intData1 = 5678;
+        byte[] byteData1 = new byte[] { 0x3, 0x4, 0x5 };
+        int intData2 = 9012;
+        // v3
+        String stringData1 = "Hello";
+        int intData3 = -1;
+        V3Parcelable v3 = new V3Parcelable(byteData0, intData0, stringData0, intData1, byteData1,
+                intData2, stringData1, intData3);
+        // expected after reading parcel
+        V1Parcelable v1Expected = new V1Parcelable(byteData0, intData0, stringData0, intData1);
+        v3.writeToParcel(p, 0);
+        final int additionalData = 0x8fffffff;
+        p.writeInt(additionalData);
+
+        p.setDataPosition(startPos);
+        V1Parcelable v1 = new V1Parcelable(p);
+        Log.i(TAG, "v3:" + v3);
+        Log.i(TAG, "v1 expected:" + v1Expected);
+        Log.i(TAG, "v1 read:" + v1);
+        assertTrue(v1Expected.equals(v1));
+        assertEquals(1, v1.version);
+        assertEquals(additionalData, p.readInt());
+    }
+
+    public void testV2ToV2() throws Exception {
+        Parcel p = Parcel.obtain();
+        int startPos = p.dataPosition();
+        byte[] byteData0 = new byte[] { 0x0, 0x1, 0x2 };
+        int intData0 = 1234;
+        String stringData0 = null;
+        int intData1 = 5678;
+        byte[] byteData1 = new byte[] { 0x3, 0x4, 0x5 };
+        int intData2 = 9012;
+        V2Parcelable v2 = new V2Parcelable(byteData0, intData0, stringData0, intData1, byteData1,
+                intData2);
+        v2.writeToParcel(p, 0);
+        final int additionalData = 0x8fffffff;
+        p.writeInt(additionalData);
+
+        p.setDataPosition(startPos);
+        V2Parcelable v2Read = new V2Parcelable(p);
+        Log.i(TAG, "v2:" + v2);
+        Log.i(TAG, "v2 read:" + v2Read);
+        assertTrue(v2.equals(v2Read));
+        assertEquals(2, v2.version);
+        assertTrue(v2.hasIntData1());
+        assertEquals(additionalData, p.readInt());
+    }
+
+    public void testV3ToV1Array() throws Exception {
+        Parcel p = Parcel.obtain();
+        int startPos = p.dataPosition();
+        byte[] byteData0 = new byte[] { 0x0, 0x1, 0x2 };
+        int intData0 = 1234;
+        String stringData0 = null;
+        int intData1 = 5678;
+        byte[] byteData1 = new byte[] { 0x3, 0x4, 0x5 };
+        int intData2 = 9012;
+        // v3
+        String stringData1 = "Hello";
+        int intData3 = -1;
+        V3Parcelable v3_0 = new V3Parcelable(byteData0, intData0, stringData0, intData1, byteData1,
+                intData2, stringData1, intData3);
+        V1Parcelable v1Expected0 = new V1Parcelable(byteData0, intData0, stringData0, intData1);
+        byteData0 = null;
+        intData0 = 1;
+        stringData0 = "world";
+        V3Parcelable v3_1 = new V3Parcelable(byteData0, intData0, stringData0, intData1, byteData1,
+                intData2, stringData1, intData3);
+        V1Parcelable v1Expected1 = new V1Parcelable(byteData0, intData0, stringData0, intData1);
+        // test write of arrays without initial length portion
+        v3_0.writeToParcel(p, 0);
+        v3_1.writeToParcel(p, 0);
+        final int additionalData = 0x8fffffff;
+        p.writeInt(additionalData);
+
+        p.setDataPosition(startPos);
+        V1Parcelable v1_0 = new V1Parcelable(p);
+        V1Parcelable v1_1 = new V1Parcelable(p);
+        Log.i(TAG, "v3_1:" + v3_1);
+        Log.i(TAG, "v1 expected 1:" + v1Expected1);
+        Log.i(TAG, "v1_1 read:" + v1_1);
+        assertTrue(v1Expected0.equals(v1_0));
+        assertTrue(v1Expected1.equals(v1_1));
+        assertEquals(1, v1_0.version);
+        assertEquals(1, v1_1.version);
+        assertEquals(additionalData, p.readInt());
+    }
+
+    public void testV1ToV3Array() throws Exception {
+        Parcel p = Parcel.obtain();
+        int startPos = p.dataPosition();
+        byte[] byteData0 = new byte[] { 0x0, 0x1, 0x2 };
+        int intData0 = 1234;
+        String stringData0 = null;
+        int intData1 = 5678;
+        V1Parcelable v1_0 = new V1Parcelable(byteData0, intData0, stringData0, intData1);
+        // expected after reading parcel
+        V3Parcelable v3Expected0 = new V3Parcelable(byteData0, intData0, stringData0, intData1,
+                null, 0, null, 0);
+        byteData0 = null;
+        intData0 = 4567;
+        stringData0 = "Hi";
+        V1Parcelable v1_1 = new V1Parcelable(byteData0, intData0, stringData0, intData1);
+        // expected after reading parcel
+        V3Parcelable v3Expected1 = new V3Parcelable(byteData0, intData0, stringData0, intData1,
+                null, 0, null, 0);
+        v1_0.writeToParcel(p, 0);
+        v1_1.writeToParcel(p, 0);
+        final int additionalData = 0x8fffffff;
+        p.writeInt(additionalData);
+
+        p.setDataPosition(startPos);
+        V3Parcelable v3_0 = new V3Parcelable(p);
+        V3Parcelable v3_1 = new V3Parcelable(p);
+        Log.i(TAG, "v1:" + v1_1);
+        Log.i(TAG, "v3 expected:" + v3Expected1);
+        Log.i(TAG, "v3 read:" + v3_1);
+        assertTrue(v3Expected0.equals(v3_0));
+        assertFalse(v3_0.hasIntData1());
+        assertFalse(v3_0.hasIntData2());
+        assertFalse(v3_1.hasIntData1());
+        assertFalse(v3_1.hasIntData2());
+        assertEquals(1, v3_0.version);
+        assertEquals(1, v3_1.version);
+        assertEquals(additionalData, p.readInt());
+    }
+}