AI 147450: CTS: add test cases for database.AbstractCursorTest, AbstractWindowedCursor, CursorJoiner, CursorWindow, DatabaseUtils, MatrixCursor and MergeCursor

Automated import of CL 147450
diff --git a/tests/tests/database/src/android/database/cts/AbstractCursorTest.java b/tests/tests/database/src/android/database/cts/AbstractCursorTest.java
new file mode 100644
index 0000000..8e0c2b9
--- /dev/null
+++ b/tests/tests/database/src/android/database/cts/AbstractCursorTest.java
@@ -0,0 +1,855 @@
+/*
+ * Copyright (C) 2008 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.database.cts;
+
+import dalvik.annotation.TestLevel;
+import dalvik.annotation.TestTargetClass;
+import dalvik.annotation.TestTargetNew;
+import dalvik.annotation.TestTargets;
+import dalvik.annotation.ToBeFixed;
+
+import android.database.AbstractCursor;
+import android.database.CharArrayBuffer;
+import android.database.ContentObserver;
+import android.database.CursorIndexOutOfBoundsException;
+import android.database.CursorWindow;
+import android.database.DataSetObserver;
+import android.database.sqlite.SQLiteDatabase;
+import android.net.Uri;
+import android.os.Bundle;
+import android.test.InstrumentationTestCase;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Random;
+
+/**
+ * Test {@link AbstractCursor}.
+ */
+@TestTargetClass(AbstractCursor.class)
+public class AbstractCursorTest extends InstrumentationTestCase {
+    private static final int POSITION0 = 0;
+    private static final int POSITION1 = 1;
+    private  static final int ROW_MAX = 10;
+    private static final int DATA_COUNT = 10;
+    private static final String[] COLUMN_NAMES1 = new String[] {
+        "_id",             // 0
+        "number"           // 1
+    };
+    private static final String[] COLUMN_NAMES = new String[] { "name", "number", "profit" };
+    private TestAbstractCursor mTestAbstractCursor;
+    private Object mLockObj = new Object();
+
+    private SQLiteDatabase mDatabase;
+    private File mDatabaseFile;
+    private AbstractCursor mDatabaseCursor;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        setupDatabase();
+        ArrayList<ArrayList> list = createTestList(ROW_MAX, COLUMN_NAMES.length);
+        mTestAbstractCursor = new TestAbstractCursor(COLUMN_NAMES, list);
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "AbstractCursor",
+        args = {}
+    )
+    public void testConstructor() {
+        TestAbstractCursor abstractCursor = new TestAbstractCursor();
+        assertEquals(-1, abstractCursor.getPosition());
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "getBlob",
+        args = {int.class}
+    )
+    public void testGetBlob() {
+        try {
+            mTestAbstractCursor.getBlob(0);
+            fail("getBlob should throws a UnsupportedOperationException here");
+        } catch (UnsupportedOperationException e) {
+            // expected
+        }
+    }
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "registerDataSetObserver",
+            args = {android.database.DataSetObserver.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "unregisterDataSetObserver",
+            args = {android.database.DataSetObserver.class}
+        )
+    })
+    public void testRegisterDataSetObserver() {
+        MockDataSetObserver datasetObserver = new MockDataSetObserver();
+
+        try {
+            mDatabaseCursor.unregisterDataSetObserver(datasetObserver);
+            fail("Can't unregister DataSetObserver before it is registered.");
+        } catch (IllegalStateException e) {
+            // expected
+        }
+
+        mDatabaseCursor.registerDataSetObserver(datasetObserver);
+
+        try {
+            mDatabaseCursor.registerDataSetObserver(datasetObserver);
+            fail("Can't register DataSetObserver twice before unregister it.");
+        } catch (IllegalStateException e) {
+            // expected
+        }
+
+        mDatabaseCursor.unregisterDataSetObserver(datasetObserver);
+        mDatabaseCursor.registerDataSetObserver(datasetObserver);
+    }
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "registerContentObserver",
+            args = {android.database.ContentObserver.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "unregisterContentObserver",
+            args = {android.database.ContentObserver.class}
+        )
+    })
+    public void testRegisterContentObserver() {
+        MockContentObserver contentObserver = new MockContentObserver();
+
+        try {
+            mDatabaseCursor.unregisterContentObserver(contentObserver);
+            fail("Can't unregister ContentObserver before it is registered.");
+        } catch (IllegalStateException e) {
+            // expected
+        }
+
+        mDatabaseCursor.registerContentObserver(contentObserver);
+
+        try {
+            mDatabaseCursor.registerContentObserver(contentObserver);
+            fail("Can't register DataSetObserver twice before unregister it.");
+        } catch (IllegalStateException e) {
+            // expected
+        }
+
+        mDatabaseCursor.unregisterContentObserver(contentObserver);
+        mDatabaseCursor.registerContentObserver(contentObserver);
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "setNotificationUri",
+        args = {android.content.ContentResolver.class, android.net.Uri.class}
+    )
+    public void testSetNotificationUri() {
+        String MOCK_URI = "content://abstractrcursortest/testtable";
+        mDatabaseCursor.setNotificationUri(getInstrumentation().getContext().getContentResolver(),
+                Uri.parse(MOCK_URI));
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "respond",
+        args = {android.os.Bundle.class}
+    )
+    public void testRespond() {
+        Bundle b = new Bundle();
+        Bundle bundle = mDatabaseCursor.respond(b);
+        assertSame(Bundle.EMPTY, bundle);
+
+        bundle = mDatabaseCursor.respond(null);
+        assertSame(Bundle.EMPTY, bundle);
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "requery",
+        args = {}
+    )
+    public void testRequery() {
+        MockDataSetObserver mock = new MockDataSetObserver();
+        mDatabaseCursor.registerDataSetObserver(mock);
+        assertFalse(mock.hadCalledOnChanged());
+        mDatabaseCursor.requery();
+        assertTrue(mock.hadCalledOnChanged());
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        notes = "Test onChange().Subclasses must call this method when they finish committing" +
+                " updates to notify all observers.",
+        method = "onChange",
+        args = {boolean.class}
+    )
+    public void testOnChange() throws InterruptedException {
+        MockContentObserver mock = new MockContentObserver();
+        mTestAbstractCursor.registerContentObserver(mock);
+        assertFalse(mock.hadCalledOnChange());
+        mTestAbstractCursor.onChange(true);
+        synchronized(mLockObj) {
+            if ( !mock.hadCalledOnChange() ) {
+                mLockObj.wait(5000);
+            }
+        }
+        assertTrue(mock.hadCalledOnChange());
+    }
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "onMove",
+            args = {int.class, int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "moveToFirst",
+            args = {}
+        )
+    })
+    public void testOnMove() {
+        mTestAbstractCursor.resetOnMoveRet();
+        assertFalse(mTestAbstractCursor.getOnMoveRet());
+        mTestAbstractCursor.moveToFirst();
+        mTestAbstractCursor.moveToPosition(5);
+        assertTrue(mTestAbstractCursor.getOnMoveRet());
+        assertEquals(0, mTestAbstractCursor.getOldPos());
+        assertEquals(5, mTestAbstractCursor.getNewPos());
+    }
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "moveToPrevious",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "moveToPosition",
+            args = {int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getPosition",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "moveToNext",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "isFirst",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "moveToLast",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "isLast",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "isBeforeFirst",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "isAfterLast",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "moveToFirst",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "move",
+            args = {int.class}
+        )
+    })
+    public void testMoveToPrevious() {
+        // Test moveToFirst, isFirst, moveToNext, getPosition
+        assertTrue(mDatabaseCursor.moveToFirst());
+        assertTrue(mDatabaseCursor.isFirst());
+        assertEquals(0, mDatabaseCursor.getPosition());
+        assertTrue(mDatabaseCursor.moveToNext());
+        assertEquals(1, mDatabaseCursor.getPosition());
+        assertFalse(mDatabaseCursor.isFirst());
+        assertTrue(mDatabaseCursor.moveToNext());
+        assertEquals(2, mDatabaseCursor.getPosition());
+
+        // invoke moveToPosition with a number larger than row count.
+        assertFalse(mDatabaseCursor.moveToPosition(30000));
+        assertEquals(mDatabaseCursor.getCount(), mDatabaseCursor.getPosition());
+
+        assertFalse(mDatabaseCursor.moveToPosition(-1));
+        assertEquals(-1, mDatabaseCursor.getPosition());
+        assertTrue(mDatabaseCursor.isBeforeFirst());
+
+        mDatabaseCursor.moveToPosition(5);
+        assertEquals(5, mDatabaseCursor.getPosition());
+
+        // Test moveToPrevious
+        assertTrue(mDatabaseCursor.moveToPrevious());
+        assertEquals(4, mDatabaseCursor.getPosition());
+        assertTrue(mDatabaseCursor.moveToPrevious());
+        assertEquals(3, mDatabaseCursor.getPosition());
+        assertTrue(mDatabaseCursor.moveToPrevious());
+        assertEquals(2, mDatabaseCursor.getPosition());
+
+        // Test moveToLast, isLast, moveToPrevius, isAfterLast.
+        assertFalse(mDatabaseCursor.isLast());
+        assertTrue(mDatabaseCursor.moveToLast());
+        assertTrue(mDatabaseCursor.isLast());
+        assertFalse(mDatabaseCursor.isAfterLast());
+
+        assertFalse(mDatabaseCursor.moveToNext());
+        assertTrue(mDatabaseCursor.isAfterLast());
+        assertFalse(mDatabaseCursor.moveToNext());
+        assertTrue(mDatabaseCursor.isAfterLast());
+        assertFalse(mDatabaseCursor.isLast());
+        assertTrue(mDatabaseCursor.moveToPrevious());
+        assertTrue(mDatabaseCursor.isLast());
+        assertTrue(mDatabaseCursor.moveToPrevious());
+        assertFalse(mDatabaseCursor.isLast());
+
+        // Test move(int).
+        mDatabaseCursor.moveToFirst();
+        assertEquals(0, mDatabaseCursor.getPosition());
+        assertFalse(mDatabaseCursor.move(-1));
+        assertEquals(-1, mDatabaseCursor.getPosition());
+        assertTrue(mDatabaseCursor.move(1));
+        assertEquals(0, mDatabaseCursor.getPosition());
+
+        assertTrue(mDatabaseCursor.move(5));
+        assertEquals(5, mDatabaseCursor.getPosition());
+        assertTrue(mDatabaseCursor.move(-1));
+        assertEquals(4, mDatabaseCursor.getPosition());
+
+        mDatabaseCursor.moveToLast();
+        assertTrue(mDatabaseCursor.isLast());
+        assertFalse(mDatabaseCursor.isAfterLast());
+        assertFalse(mDatabaseCursor.move(1));
+        assertFalse(mDatabaseCursor.isLast());
+        assertTrue(mDatabaseCursor.isAfterLast());
+        assertTrue(mDatabaseCursor.move(-1));
+        assertTrue(mDatabaseCursor.isLast());
+        assertFalse(mDatabaseCursor.isAfterLast());
+    }
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "isClosed",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "close",
+            args = {}
+        )
+    })
+    public void testIsClosed() {
+        assertFalse(mDatabaseCursor.isClosed());
+        mDatabaseCursor.close();
+        assertTrue(mDatabaseCursor.isClosed());
+    }
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getWindow",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "fillWindow",
+            args = {int.class, android.database.CursorWindow.class}
+        )
+    })
+    public void testGetWindow() {
+        assertNull(mDatabaseCursor.getWindow());
+        CursorWindow window = new CursorWindow(false);
+        assertEquals(0, window.getNumRows());
+        // fill window from position 0
+        mDatabaseCursor.fillWindow(0, window);
+
+        assertNotNull(mDatabaseCursor.getWindow());
+        assertEquals(mDatabaseCursor.getCount(), window.getNumRows());
+
+        while (mDatabaseCursor.moveToNext()) {
+            assertEquals(mDatabaseCursor.getInt(POSITION1),
+                    window.getInt(mDatabaseCursor.getPosition(), POSITION1));
+        }
+        window.clear();
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        notes = "Test getWantsAllOnMoveCalls(), it always returns false.",
+        method = "getWantsAllOnMoveCalls",
+        args = {}
+    )
+    public void testGetWantsAllOnMoveCalls() {
+        assertFalse(mDatabaseCursor.getWantsAllOnMoveCalls());
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "isFieldUpdated",
+        args = {int.class}
+    )
+    @ToBeFixed(bug = "1569265", explanation = "All other updating-related methods are 'hide' and "
+        + "'deprecated.")
+    public void testIsFieldUpdated() {
+        mTestAbstractCursor.moveToFirst();
+        assertFalse(mTestAbstractCursor.isFieldUpdated(0));
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "getUpdatedField",
+        args = {int.class}
+    )
+    @ToBeFixed(bug = "1569265", explanation = "All other updating-related methods are 'hide' and "
+        + "'deprecated.")
+    public void testGetUpdatedField() {
+        mTestAbstractCursor.moveToFirst();
+        try {
+            assertEquals("hello", mTestAbstractCursor.getUpdatedField(0));
+            fail("getUpdatedField should throws a NullPointerException here.");
+        } catch (NullPointerException e) {
+            // expected
+        }
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "getExtras",
+        args = {}
+    )
+    public void testGetExtras() {
+        assertSame(Bundle.EMPTY, mDatabaseCursor.getExtras());
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "getCount",
+        args = {}
+    )
+    public void testGetCount() {
+        assertEquals(DATA_COUNT, mDatabaseCursor.getCount());
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "getColumnNames",
+        args = {}
+    )
+    public void testGetColumnNames() {
+        String[] names = mDatabaseCursor.getColumnNames();
+        assertEquals(COLUMN_NAMES1.length, names.length);
+
+        for (int i = 0; i < COLUMN_NAMES1.length; i++) {
+            assertEquals(COLUMN_NAMES1[i], names[i]);
+        }
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "getColumnName",
+        args = {int.class}
+    )
+    public void testGetColumnName() {
+        assertEquals(COLUMN_NAMES1[0], mDatabaseCursor.getColumnName(0));
+        assertEquals(COLUMN_NAMES1[1], mDatabaseCursor.getColumnName(1));
+    }
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getColumnIndexOrThrow",
+            args = {java.lang.String.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getColumnIndex",
+            args = {java.lang.String.class}
+        )
+    })
+    public void testGetColumnIndexOrThrow() {
+        final String COLUMN_FAKE = "fake_name";
+        assertEquals(POSITION0, mDatabaseCursor.getColumnIndex(COLUMN_NAMES1[POSITION0]));
+        assertEquals(POSITION1, mDatabaseCursor.getColumnIndex(COLUMN_NAMES1[POSITION1]));
+        assertEquals(POSITION0, mDatabaseCursor.getColumnIndexOrThrow(COLUMN_NAMES1[POSITION0]));
+        assertEquals(POSITION1, mDatabaseCursor.getColumnIndexOrThrow(COLUMN_NAMES1[POSITION1]));
+
+        assertEquals(0, mDatabaseCursor.getColumnIndexOrThrow(COLUMN_FAKE));
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "getColumnIndex.",
+        args = {java.lang.String.class}
+    )
+    public void testGetColumnIndex() {
+        assertEquals(POSITION0, mDatabaseCursor.getColumnIndex(COLUMN_NAMES1[POSITION0]));
+        assertEquals(POSITION1, mDatabaseCursor.getColumnIndex(COLUMN_NAMES1[POSITION1]));
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "getColumnCount",
+        args = {}
+    )
+    public void testGetColumnCount() {
+        assertEquals(COLUMN_NAMES1.length, mDatabaseCursor.getColumnCount());
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "deactivateInternal",
+        args = {}
+    )
+    public void testDeactivateInternal() {
+        MockDataSetObserver mock = new MockDataSetObserver();
+        mDatabaseCursor.registerDataSetObserver(mock);
+        assertFalse(mock.hadCalledOnInvalid());
+        mDatabaseCursor.deactivateInternal();
+        assertTrue(mock.hadCalledOnInvalid());
+    }
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "deactivate",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "registerDataSetObserver",
+            args = {android.database.DataSetObserver.class}
+        )
+    })
+    public void testDeactivate() {
+        MockDataSetObserver mock = new MockDataSetObserver();
+        mDatabaseCursor.registerDataSetObserver(mock);
+        assertFalse(mock.hadCalledOnInvalid());
+        mDatabaseCursor.deactivate();
+        assertTrue(mock.hadCalledOnInvalid());
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "copyStringToBuffer",
+        args = {int.class, android.database.CharArrayBuffer.class}
+    )
+    public void testCopyStringToBuffer() {
+        CharArrayBuffer ca = new CharArrayBuffer(1000);
+        mTestAbstractCursor.moveToFirst();
+        mTestAbstractCursor.copyStringToBuffer(0, ca);
+        CursorWindow window = new CursorWindow(false);
+        mTestAbstractCursor.fillWindow(0, window);
+
+        StringBuffer sb = new StringBuffer();
+        sb.append(window.getString(0, 0));
+        String str = ((TestAbstractCursor)mTestAbstractCursor).getString(0);
+        assertEquals(str.length(), ca.sizeCopied);
+        assertEquals(sb.toString(), new String(ca.data, 0, ca.sizeCopied));
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "checkPosition",
+        args = {}
+    )
+    public void testCheckPosition() {
+        // Test with position = -1.
+        try {
+            mTestAbstractCursor.checkPosition();
+            fail("copyStringToBuffer() should throws CursorIndexOutOfBoundsException here.");
+        } catch (CursorIndexOutOfBoundsException e) {
+            // expected
+        }
+
+        // Test with position = count.
+        assertTrue(mTestAbstractCursor.moveToPosition(mTestAbstractCursor.getCount() - 1));
+        mTestAbstractCursor.checkPosition();
+
+        try {
+            assertFalse(mTestAbstractCursor.moveToPosition(mTestAbstractCursor.getCount()));
+            assertEquals(mTestAbstractCursor.getCount(), mTestAbstractCursor.getPosition());
+            mTestAbstractCursor.checkPosition();
+            fail("copyStringToBuffer() should throws CursorIndexOutOfBoundsException here.");
+        } catch (CursorIndexOutOfBoundsException e) {
+            // expected
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private static ArrayList<ArrayList> createTestList(int rows, int cols) {
+        ArrayList<ArrayList> list = new ArrayList<ArrayList>();
+        Random ran = new Random();
+
+        for (int i = 0; i < rows; i++) {
+            ArrayList<Integer> col = new ArrayList<Integer>();
+            list.add(col);
+
+            for (int j = 0; j < cols; j++) {
+                // generate random number
+                Integer r = ran.nextInt();
+                col.add(r);
+            }
+        }
+
+        return list;
+    }
+
+    private void setupDatabase() {
+        mDatabaseFile = new File("/sqlite_stmt_journals", "database_test.db");
+        if (mDatabaseFile.exists()) {
+            mDatabaseFile.delete();
+        }
+        mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null);
+        assertNotNull(mDatabaseFile);
+        mDatabase.execSQL("CREATE TABLE test1 (_id INTEGER PRIMARY KEY, number TEXT);");
+        generateData();
+        mDatabaseCursor = (AbstractCursor) mDatabase.query("test1", null, null, null, null, null,
+                null);
+    }
+
+    private void generateData() {
+        for ( int i = 0; i < DATA_COUNT; i++) {
+            mDatabase.execSQL("INSERT INTO test1 (number) VALUES ('" + i + "');");
+        }
+    }
+
+    private class TestAbstractCursor extends AbstractCursor {
+        private boolean mOnMoveReturnValue;
+        private int mOldPosition;
+        private int mNewPosition;
+        private String[] mColumnNames;
+        private ArrayList<Object>[] mRows;
+        private boolean mHadCalledOnChange = false;
+
+        public TestAbstractCursor() {
+            super();
+        }
+        @SuppressWarnings("unchecked")
+        public TestAbstractCursor(String[] columnNames, ArrayList<ArrayList> rows) {
+            int colCount = columnNames.length;
+            boolean foundID = false;
+            mRowIdColumnIndex = 0;
+
+            // Add an _id column if not in columnNames
+            for (int i = 0; i < colCount; ++i) {
+                if (columnNames[i].compareToIgnoreCase("_id") == 0) {
+                    mColumnNames = columnNames;
+                    foundID = true;
+                    break;
+                }
+            }
+
+            if (!foundID) {
+                mColumnNames = new String[colCount + 1];
+                System.arraycopy(columnNames, 0, mColumnNames, 0, columnNames.length);
+                mColumnNames[colCount] = "_id";
+            }
+
+            int rowCount = rows.size();
+            mRows = new ArrayList[rowCount];
+
+            for (int i = 0; i < rowCount; ++i) {
+                mRows[i] = rows.get(i);
+
+                if (!foundID) {
+                    mRows[i].add(Long.valueOf(i));
+                }
+            }
+        }
+
+        public boolean getOnMoveRet() {
+            return mOnMoveReturnValue;
+        }
+
+        public void resetOnMoveRet() {
+            mOnMoveReturnValue = false;
+        }
+
+        public int getOldPos() {
+            return mOldPosition;
+        }
+
+        public int getNewPos() {
+            return mNewPosition;
+        }
+
+        @Override
+        public boolean onMove(int oldPosition, int newPosition) {
+            mOnMoveReturnValue = super.onMove(oldPosition, newPosition);
+            mOldPosition = oldPosition;
+            mNewPosition = newPosition;
+            return mOnMoveReturnValue;
+        }
+
+        @Override
+        public int getCount() {
+            return mRows.length;
+        }
+
+        @Override
+        public boolean deleteRow() {
+            return false;
+        }
+
+        @Override
+        public String[] getColumnNames() {
+            return mColumnNames;
+        }
+
+        @Override
+        public String getString(int columnIndex) {
+            Object cell = mRows[mPos].get(columnIndex);
+            return (cell == null) ? null : cell.toString();
+        }
+
+        @Override
+        public short getShort(int columnIndex) {
+            Number num = (Number) mRows[mPos].get(columnIndex);
+            return num.shortValue();
+        }
+
+        @Override
+        public int getInt(int columnIndex) {
+            Number num = (Number) mRows[mPos].get(columnIndex);
+            return num.intValue();
+        }
+
+        @Override
+        public long getLong(int columnIndex) {
+            Number num = (Number) mRows[mPos].get(columnIndex);
+            return num.longValue();
+        }
+
+        @Override
+        public float getFloat(int columnIndex) {
+            Number num = (Number) mRows[mPos].get(columnIndex);
+            return num.floatValue();
+        }
+
+        @Override
+        public double getDouble(int columnIndex) {
+            Number num = (Number) mRows[mPos].get(columnIndex);
+            return num.doubleValue();
+        }
+
+        @Override
+        public boolean isNull(int column) {
+            return false;
+        }
+
+        public boolean hadCalledOnChange() {
+            return mHadCalledOnChange;
+        }
+
+        // the following are protected methods
+        protected void checkPosition() {
+            super.checkPosition();
+        }
+
+        protected Object getUpdatedField(int columnIndex) {
+            return super.getUpdatedField(columnIndex);
+        }
+
+        protected boolean isFieldUpdated(int columnIndex) {
+            return super.isFieldUpdated(columnIndex);
+        }
+
+        protected void onChange(boolean selfChange) {
+            super.onChange(selfChange);
+            mHadCalledOnChange = true;
+        }
+    }
+
+    private class MockContentObserver extends ContentObserver {
+        public boolean mHadCalledOnChange;
+
+        public MockContentObserver() {
+            super(null);
+        }
+
+        @Override
+        public void onChange(boolean selfChange) {
+            super.onChange(selfChange);
+            mHadCalledOnChange = true;
+            synchronized(mLockObj) {
+                mLockObj.notify();
+            }
+        }
+
+        @Override
+        public boolean deliverSelfNotifications() {
+            return true;
+        }
+
+        public boolean hadCalledOnChange() {
+            return mHadCalledOnChange;
+        }
+    }
+
+    private class MockDataSetObserver extends DataSetObserver {
+        private boolean mHadCalledOnChanged;
+        private boolean mHadCalledOnInvalid;
+
+        @Override
+        public void onChanged() {
+            super.onChanged();
+            mHadCalledOnChanged = true;
+        }
+
+        @Override
+        public void onInvalidated() {
+            super.onInvalidated();
+            mHadCalledOnInvalid = true;
+        }
+
+        public boolean hadCalledOnChanged() {
+            return mHadCalledOnChanged;
+        }
+
+        public boolean hadCalledOnInvalid() {
+            return mHadCalledOnInvalid;
+        }
+    }
+}
diff --git a/tests/tests/database/src/android/database/cts/AbstractWindowedCursorTest.java b/tests/tests/database/src/android/database/cts/AbstractWindowedCursorTest.java
new file mode 100644
index 0000000..bcce519
--- /dev/null
+++ b/tests/tests/database/src/android/database/cts/AbstractWindowedCursorTest.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2008 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.database.cts;
+
+import java.util.Arrays;
+
+import android.database.AbstractWindowedCursor;
+import android.database.CharArrayBuffer;
+import android.database.CursorIndexOutOfBoundsException;
+import android.database.CursorWindow;
+import android.database.StaleDataException;
+import android.test.InstrumentationTestCase;
+import dalvik.annotation.TestTargets;
+import dalvik.annotation.TestTargetNew;
+import dalvik.annotation.TestLevel;
+import dalvik.annotation.TestTargetClass;
+
+@TestTargetClass(AbstractWindowedCursor.class)
+public class AbstractWindowedCursorTest extends InstrumentationTestCase {
+    private static final String TEST_STRING = "TESTSTRING";
+    private static final int COLUMN_INDEX0 = 0;
+    private static final int COLUMN_INDEX1 = 1;
+    private static final int ROW_INDEX0 = 0;
+    private static final int TEST_COLUMN_COUNT = 7;
+    private MockAbstractWindowedCursor mCursor;
+    private CursorWindow mWindow;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        mCursor = new MockAbstractWindowedCursor();
+        mWindow = new CursorWindow(false);
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "isNull",
+        args = {int.class}
+    )
+    public void testIsNull() {
+        mCursor.setWindow(mWindow);
+
+        assertTrue(mWindow.setNumColumns(TEST_COLUMN_COUNT));
+        mCursor.moveToFirst();
+        assertTrue(mCursor.isNull(COLUMN_INDEX0));
+        assertTrue(mWindow.allocRow());
+
+        String str = "abcdefg";
+        assertTrue(mWindow.putString(str, ROW_INDEX0, COLUMN_INDEX0));
+        assertFalse(mCursor.isNull(COLUMN_INDEX0));
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "isBlob",
+        args = {int.class}
+    )
+    public void testIsBlob() {
+        mCursor.setWindow(mWindow);
+        assertTrue(mWindow.setNumColumns(TEST_COLUMN_COUNT));
+        assertTrue(mWindow.allocRow());
+
+        mCursor.moveToFirst();
+        assertFalse(mCursor.isBlob(COLUMN_INDEX0));
+
+        String str = "abcdefg";
+        assertTrue(mWindow.putString(str, ROW_INDEX0, COLUMN_INDEX0));
+        assertTrue(mWindow.putBlob(new byte[10], ROW_INDEX0, COLUMN_INDEX1));
+        assertTrue(mCursor.isBlob(COLUMN_INDEX1));
+    }
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "hasWindow",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setWindow",
+            args = {android.database.CursorWindow.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getWindow",
+            args = {}
+        )
+    })
+    public void testHasWindow() {
+        assertFalse(mCursor.hasWindow());
+        assertNull(mCursor.getWindow());
+
+        mCursor.setWindow(mWindow);
+        assertTrue(mCursor.hasWindow());
+        assertSame(mWindow, mCursor.getWindow());
+
+        mCursor.setWindow(null);
+        assertFalse(mCursor.hasWindow());
+        assertNull(mCursor.getWindow());
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "getString",
+        args = {int.class}
+    )
+    public void testGetString() {
+        mCursor.setWindow(mWindow);
+        assertTrue(mWindow.setNumColumns(TEST_COLUMN_COUNT));
+        assertTrue(mWindow.allocRow());
+
+        mCursor.moveToFirst();
+        String str = "abcdefg";
+        assertTrue(mWindow.putString(str, ROW_INDEX0, COLUMN_INDEX0));
+        assertEquals(str, mCursor.getString(COLUMN_INDEX0));
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "getShort",
+        args = {int.class}
+    )
+    public void testGetShort() {
+        mCursor.setWindow(mWindow);
+        assertTrue(mWindow.setNumColumns(TEST_COLUMN_COUNT));
+        assertTrue(mWindow.allocRow());
+
+        mCursor.moveToFirst();
+        short shortNumber = 10;
+        assertTrue(mWindow.putLong((long) shortNumber, ROW_INDEX0, COLUMN_INDEX0));
+        assertEquals(shortNumber, mCursor.getShort(COLUMN_INDEX0));
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "getLong",
+        args = {int.class}
+    )
+    public void testGetLong() {
+        mCursor.setWindow(mWindow);
+        assertTrue(mWindow.setNumColumns(TEST_COLUMN_COUNT));
+        assertTrue(mWindow.allocRow());
+
+        mCursor.moveToFirst();
+        long longNumber = 10;
+        assertTrue(mWindow.putLong(longNumber, ROW_INDEX0, COLUMN_INDEX0));
+        assertEquals(longNumber, mCursor.getLong(COLUMN_INDEX0));
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "getInt",
+        args = {int.class}
+    )
+    public void testGetInt() {
+        mCursor.setWindow(mWindow);
+        assertTrue(mWindow.setNumColumns(TEST_COLUMN_COUNT));
+        assertTrue(mWindow.allocRow());
+
+        mCursor.moveToFirst();
+        int intNumber = 10;
+        assertTrue(mWindow.putLong((long) intNumber, ROW_INDEX0, COLUMN_INDEX0));
+        assertEquals(intNumber, mCursor.getInt(COLUMN_INDEX0));
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "getFloat",
+        args = {int.class}
+    )
+    public void testGetFloat() {
+        mCursor.setWindow(mWindow);
+        assertTrue(mWindow.setNumColumns(TEST_COLUMN_COUNT));
+        assertTrue(mWindow.allocRow());
+
+        mCursor.moveToFirst();
+        float f1oatNumber = 1.26f;
+        assertTrue(mWindow.putDouble((double) f1oatNumber, ROW_INDEX0, COLUMN_INDEX0));
+        assertEquals(f1oatNumber, mCursor.getFloat(COLUMN_INDEX0));
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "getDouble",
+        args = {int.class}
+    )
+    public void testGetDouble() {
+        mCursor.setWindow(mWindow);
+        assertTrue(mWindow.setNumColumns(TEST_COLUMN_COUNT));
+        assertTrue(mWindow.allocRow());
+
+        double db1 = 1.26;
+        assertTrue(mWindow.putDouble(db1, ROW_INDEX0, COLUMN_INDEX0));
+
+        double db2 = mWindow.getDouble(ROW_INDEX0, COLUMN_INDEX0);
+        assertEquals(db1, db2);
+
+        mCursor.moveToFirst();
+        double cd = mCursor.getDouble(COLUMN_INDEX0);
+        assertEquals(db1, cd);
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "getBlob",
+        args = {int.class}
+    )
+    public void testGetBlob() {
+        byte TEST_VALUE = 3;
+        byte BLOB_SIZE = 100;
+        assertTrue(mWindow.setNumColumns(TEST_COLUMN_COUNT));
+        assertTrue(mWindow.allocRow());
+        assertTrue(mWindow.putString("", ROW_INDEX0, COLUMN_INDEX0));
+
+        byte[] blob = new byte[BLOB_SIZE];
+        Arrays.fill(blob, TEST_VALUE);
+        assertTrue(mWindow.putBlob(blob, ROW_INDEX0, COLUMN_INDEX1));
+
+        mCursor.setWindow(mWindow);
+        mCursor.moveToFirst();
+
+        byte[] targetBuffer = mCursor.getBlob(COLUMN_INDEX1);
+        assertEquals(BLOB_SIZE, targetBuffer.length);
+        assertTrue(Arrays.equals(blob, targetBuffer));
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "copyStringToBuffer",
+        args = {int.class, android.database.CharArrayBuffer.class}
+    )
+    public void testCopyStringToBuffer() {
+        assertTrue(mWindow.setNumColumns(TEST_COLUMN_COUNT));
+        assertTrue(mWindow.allocRow());
+        assertTrue(mWindow.putString(TEST_STRING, ROW_INDEX0, COLUMN_INDEX0));
+        assertTrue(mWindow.putString("", ROW_INDEX0, COLUMN_INDEX1));
+
+        mCursor.setWindow(mWindow);
+        mCursor.moveToFirst();
+
+        CharArrayBuffer charArrayBuffer = new CharArrayBuffer(TEST_STRING.length());
+
+        mCursor.copyStringToBuffer(COLUMN_INDEX0, charArrayBuffer);
+        assertEquals(TEST_STRING.length(), charArrayBuffer.sizeCopied);
+        assertTrue(Arrays.equals(TEST_STRING.toCharArray(), charArrayBuffer.data));
+
+        Arrays.fill(charArrayBuffer.data, '\0');
+        mCursor.copyStringToBuffer(COLUMN_INDEX1, charArrayBuffer);
+        assertEquals(0, charArrayBuffer.sizeCopied);
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "checkPosition",
+        args = {}
+    )
+    public void testCheckPosition() {
+        try {
+            mCursor.checkPosition();
+            fail("testCheckPosition failed");
+        } catch (CursorIndexOutOfBoundsException e) {
+            // expected
+        }
+
+        try {
+            assertTrue(mCursor.moveToPosition(mCursor.getCount() - 1));
+            mCursor.checkPosition();
+            fail("testCheckPosition failed");
+        } catch (StaleDataException e) {
+            // expected
+        }
+
+        assertTrue(mCursor.moveToPosition(mCursor.getCount() - 1));
+        mCursor.setWindow(mWindow);
+        mCursor.checkPosition();
+    }
+
+    private class MockAbstractWindowedCursor extends AbstractWindowedCursor {
+
+        public MockAbstractWindowedCursor() {
+        }
+
+        @Override
+        public String[] getColumnNames() {
+            return new String[] {
+                    "col1", "col2", "col3"
+            };
+        }
+
+        @Override
+        public int getCount() {
+            return 1;
+        }
+
+        @Override
+        protected void checkPosition() {
+            super.checkPosition();
+        }
+    }
+}
diff --git a/tests/tests/database/src/android/database/cts/CursorJoinerTest.java b/tests/tests/database/src/android/database/cts/CursorJoinerTest.java
new file mode 100644
index 0000000..b76c23b
--- /dev/null
+++ b/tests/tests/database/src/android/database/cts/CursorJoinerTest.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2008 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.database.cts;
+
+import dalvik.annotation.TestLevel;
+import dalvik.annotation.TestTargetClass;
+import dalvik.annotation.TestTargetNew;
+import dalvik.annotation.TestTargets;
+
+import android.database.Cursor;
+import android.database.CursorJoiner;
+import android.database.CursorJoiner.Result;
+import android.database.sqlite.SQLiteDatabase;
+import android.test.AndroidTestCase;
+
+import java.io.File;
+
+@TestTargetClass(android.database.CursorJoiner.class)
+public class CursorJoinerTest extends AndroidTestCase {
+
+    private static final int TEST_ITEM_COUNT = 10;
+    private static final int DEFAULT_TABLE1_VALUE_BEGINS = 1;
+    private static final int DEFAULT_TABLE2_VALUE_BEGINS = 11;
+    private static final int EQUAL_START = 18;
+    // Every table has 7 unique numbers, and 3 other numbers they all have.
+    private static final int UNIQUE_COUNT = 7;
+    private static final int MAX_VALUE = 20;
+    private static final int EQUAL_VALUE_COUNT = MAX_VALUE - EQUAL_START + 1;
+    private static final String TABLE_NAME_1 = "test1";
+    private static final String TABLE_NAME_2 = "test2";
+    private static final String TABLE1_COLUMNS = " number TEXT";
+    private static final String TABLE2_COLUMNS = " number TEXT, int_number INTEGER";
+
+    private SQLiteDatabase mDatabase;
+    private File mDatabaseFile;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        setupDatabase();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        mDatabase.close();
+        mDatabaseFile.delete();
+        super.tearDown();
+    }
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "CursorJoiner",
+            args = {android.database.Cursor.class, java.lang.String[].class,
+                    android.database.Cursor.class, java.lang.String[].class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "iterator",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            notes = "it always throws UnsupportedOperationException.",
+            method = "remove",
+            args = {}
+        )
+    })
+    public void testCursorJoinerAndIterator() {
+        Cursor cursor1 = getCursor(TABLE_NAME_1, null, null);
+        Cursor cursor2 = getCursor(TABLE_NAME_2, null, null);
+        // Test with different length ColumenNAmes
+        try {
+            new CursorJoiner(cursor1, cursor1.getColumnNames(), cursor2, cursor2.getColumnNames());
+            fail("CursorJoiner's constructor should throws  IllegalArgumentException here.");
+        } catch (IllegalArgumentException e) {
+            //expected
+        }
+        closeCursor(cursor1);
+        closeCursor(cursor2);
+
+        String[] columnNames = new String[] { "number" };
+        cursor1 = getCursor(TABLE_NAME_1, null, columnNames);
+        cursor2 = getCursor(TABLE_NAME_2, null, columnNames);
+
+        CursorJoiner cursorJoiner = new CursorJoiner(cursor1, cursor1.getColumnNames(), cursor2,
+                cursor2.getColumnNames());
+
+        // Test remove()
+        try {
+            cursorJoiner.remove();
+            fail("remove() should throws UnsupportedOperationException here");
+        } catch (UnsupportedOperationException e) {
+            // expected
+        }
+
+        assertEquals(TEST_ITEM_COUNT, cursor1.getCount());
+        assertEquals(TEST_ITEM_COUNT, cursor2.getCount());
+
+        // Test iterator
+        for (CursorJoiner.Result joinResult : cursorJoiner) {
+            switch (joinResult) {
+            case LEFT:
+                // Add the values into table test1 which table test1 possess and table test2 don't.
+                assertTrue(cursor1.getString(0).compareTo(cursor2.getString(0)) < 0);
+                addValueIntoTable(TABLE_NAME_2, cursor1.getString(0));
+                break;
+            case RIGHT:
+                // Add the values into table test2 which table test2 possess and table test1 don't.
+                assertTrue(cursor1.getString(0).compareTo(cursor2.getString(0)) > 0);
+                addValueIntoTable(TABLE_NAME_1, cursor2.getString(0));
+                break;
+            case BOTH:
+                // Delete the values table test1 and test2 both possess.
+                assertEquals(cursor1.getString(0), cursor2.getString(0));
+                deleteValueFromTable(TABLE_NAME_1, cursor1.getString(0));
+                deleteValueFromTable(TABLE_NAME_2, cursor2.getString(0));
+                break;
+            }
+        }
+        cursor1.requery();
+        cursor2.requery();
+
+        // Finally, two tables's number columns have the same contents
+        assertEquals(UNIQUE_COUNT * 2, cursor1.getCount());
+        assertEquals(UNIQUE_COUNT * 2, cursor2.getCount());
+
+        // For every  table, merged with the other one's unique numbers, and deleted the originally
+        // mutual same numbers(EQUAL_START~MAX_VALUE);
+        cursor1.moveToFirst();
+        cursor2.moveToFirst();
+        for (int i = 0; i < UNIQUE_COUNT; i++) {
+            assertEquals(getOrderNumberString(DEFAULT_TABLE1_VALUE_BEGINS + i, MAX_VALUE),
+                    cursor1.getString(0));
+            assertEquals(cursor1.getString(0), cursor2.getString(0));
+            cursor1.moveToNext();
+            cursor2.moveToNext();
+        }
+        closeCursor(cursor2);
+        closeCursor(cursor1);
+    }
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            notes = "Fisrt UNIQUE_COUNT 'next' operations, the joiner controls cursor1 to move;" +
+                    " and the second UNIQUE_COUNT 'next' ops, the joiner controlscursor2" +
+                    " to move, the last EQUAL_VALUE_COUNT 'next' ops, the joiner control" +
+                    " bothcursor1 and cursor2 to move.",
+            method = "next",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "hasNext",
+            args = {}
+        )
+    })
+    public void testNext() {
+        String[] columnNames = new String[] { "number" };
+        Cursor cursor1 = getCursor(TABLE_NAME_1, null, columnNames);
+        Cursor cursor2 = getCursor(TABLE_NAME_2, null, columnNames);
+
+        // For cursor1 , values are '01'~'07' and 'EQUAL_START'~'MAX_VALUE'
+        assertEquals(TEST_ITEM_COUNT, cursor1.getCount());
+        // For cursor2 , values are '11'~'17' and 'EQUAL_START'~'MAX_VALUE'
+        assertEquals(TEST_ITEM_COUNT, cursor2.getCount());
+        CursorJoiner cursorJoiner = new CursorJoiner(cursor1, cursor1.getColumnNames(), cursor2,
+                cursor2.getColumnNames());
+        for (int i = 0; i < UNIQUE_COUNT; i++) {
+            // For cursor1, value 1~7 result value as LEFT to cursor2 value '11'
+            assertTrue(cursorJoiner.hasNext());
+            assertEquals(Result.LEFT, cursorJoiner.next());
+            assertEquals(getOrderNumberString(DEFAULT_TABLE1_VALUE_BEGINS + i, MAX_VALUE), cursor1
+                    .getString(0));
+            assertEquals(getOrderNumberString(DEFAULT_TABLE2_VALUE_BEGINS, MAX_VALUE), cursor2
+                  .getString(0));
+        }
+        for (int i = 0; i < UNIQUE_COUNT; i++) {
+            // For cursor2, value 11~17 result a value as LEFT to cursor1 value '18'
+            assertTrue(cursorJoiner.hasNext());
+            assertEquals(Result.RIGHT, cursorJoiner.next());
+            assertEquals(getOrderNumberString(EQUAL_START, MAX_VALUE), cursor1.getString(0));
+            assertEquals(getOrderNumberString(DEFAULT_TABLE2_VALUE_BEGINS + i, MAX_VALUE), cursor2
+                    .getString(0));
+        }
+        for (int i = 0; i < EQUAL_VALUE_COUNT; i++) {
+            // For cursor1 and cursor2, value 18~20 result a value as BOTH
+            assertTrue(cursorJoiner.hasNext());
+            assertEquals(Result.BOTH, cursorJoiner.next());
+            assertEquals(getOrderNumberString(EQUAL_START + i, MAX_VALUE), cursor1.getString(0));
+            assertEquals(getOrderNumberString(EQUAL_START + i, MAX_VALUE), cursor2.getString(0));
+        }
+        closeCursor(cursor1);
+        closeCursor(cursor2);
+    }
+
+    /**
+     * This function accepts integer maxValue to determine max length of number.
+     * Return a converted decimal number string of input integer parameter 'value',
+     *  according to  the max length, '0' will be placeholder(s).
+     * For example: if max length is 2, 1 -> '01', 10 -> '10'.
+     * @param value
+     * @param maxValue
+     * @return
+     */
+    private String getOrderNumberString(int value, int maxValue) {
+        // Convert decimal number as string, '0' as placeholder
+        int maxLength = Integer.toString(maxValue).length();
+        int basicLength = Integer.toString(value).length();
+        String placeHolders = "";
+        for (int i = 0; i < (maxLength - basicLength); i++) {
+            placeHolders += "0";
+        }
+        return placeHolders + Integer.toString(value);
+    }
+
+    private void initializeTables() {
+        // Add 1 to 7 into Table1
+        addValuesIntoTable(TABLE_NAME_1, DEFAULT_TABLE1_VALUE_BEGINS,
+                DEFAULT_TABLE1_VALUE_BEGINS + UNIQUE_COUNT - 1);
+        // Add 18 to 20 into Table1
+        addValuesIntoTable(TABLE_NAME_1, DEFAULT_TABLE2_VALUE_BEGINS + UNIQUE_COUNT, MAX_VALUE);
+        // Add 11 to 17 into Table2
+        addValuesIntoTable(TABLE_NAME_2, DEFAULT_TABLE2_VALUE_BEGINS,
+                DEFAULT_TABLE2_VALUE_BEGINS + UNIQUE_COUNT - 1);
+        // Add 18 to 20 into Table2
+        addValuesIntoTable(TABLE_NAME_2, DEFAULT_TABLE2_VALUE_BEGINS + UNIQUE_COUNT, MAX_VALUE);
+    }
+
+    private void setupDatabase() {
+        mDatabaseFile = new File("/sqlite_stmt_journals", "database_test.db");
+        if (mDatabaseFile.exists()) {
+            mDatabaseFile.delete();
+        }
+        mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null);
+        assertNotNull(mDatabaseFile);
+        createTable(TABLE_NAME_1, TABLE1_COLUMNS);
+        createTable(TABLE_NAME_2, TABLE2_COLUMNS);
+        initializeTables();
+    }
+
+    private void closeCursor(Cursor cursor) {
+        if (null != cursor) {
+            cursor.close();
+            cursor = null;
+        }
+    }
+
+    private void createTable(String tableName, String columnNames) {
+        String sql = "Create TABLE " + tableName + " (_id INTEGER PRIMARY KEY, " + columnNames
+                + " );";
+        mDatabase.execSQL(sql);
+    }
+
+    private void addValuesIntoTable(String tableName, int start, int end) {
+        for (int i = start; i <= end; i++) {
+            mDatabase.execSQL("INSERT INTO " + tableName + "(number) VALUES ('"
+                    + getOrderNumberString(i, MAX_VALUE) + "');");
+        }
+    }
+
+    private void addValueIntoTable(String tableName, String value) {
+        mDatabase.execSQL("INSERT INTO " + tableName + "(number) VALUES ('" + value + "');");
+    }
+
+    private void deleteValueFromTable(String tableName, String value) {
+        mDatabase.execSQL("DELETE FROM " + tableName + " WHERE number = '" + value + "';");
+    }
+
+    private Cursor getCursor(String tableName, String selection, String[] columnNames) {
+        return mDatabase.query(tableName, columnNames, selection, null, null, null, "number");
+    }
+}
diff --git a/tests/tests/database/src/android/database/cts/CursorWindowTest.java b/tests/tests/database/src/android/database/cts/CursorWindowTest.java
new file mode 100644
index 0000000..eff2db8
--- /dev/null
+++ b/tests/tests/database/src/android/database/cts/CursorWindowTest.java
@@ -0,0 +1,696 @@
+/*
+ * Copyright (C) 2008 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.database.cts;
+
+import com.android.internal.database.ArrayListCursor;
+import com.google.android.collect.Lists;
+
+import dalvik.annotation.TestLevel;
+import dalvik.annotation.TestTargetClass;
+import dalvik.annotation.TestTargetNew;
+import dalvik.annotation.TestTargets;
+import dalvik.annotation.ToBeFixed;
+
+import android.database.AbstractCursor;
+import android.database.CharArrayBuffer;
+import android.database.CursorWindow;
+import android.database.sqlite.SQLiteException;
+import android.os.Parcel;
+import android.test.AndroidTestCase;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Random;
+
+@TestTargetClass(android.database.CursorWindow.class)
+public class CursorWindowTest extends AndroidTestCase {
+
+    private static final String TEST_STRING = "Test String";
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            notes = "This is a test from unittest of CursorWindowTest.",
+            method = "clear",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            notes = "This is a test from unittest of CursorWindowTest.",
+            method = "setStartPosition",
+            args = {int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            notes = "This is a test from unittest of CursorWindowTest.",
+            method = "putString",
+            args = {java.lang.String.class, int.class, int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            notes = "This is a test from unittest of CursorWindowTest.",
+            method = "putNull",
+            args = {int.class, int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            notes = "This is a test from unittest of CursorWindowTest.",
+            method = "setNumColumns",
+            args = {int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            notes = "This is a test from unittest of CursorWindowTest.",
+            method = "freeLastRow",
+            args = {}
+        )
+    })
+    public void testWriteCursorToWindow() throws Exception {
+        // create cursor
+        String[] colNames = new String[]{"name", "number", "profit"};
+        int colsize = colNames.length;
+        ArrayList<ArrayList> list = createTestList(10, colsize);
+        AbstractCursor cursor = new ArrayListCursor(colNames, list);
+
+        // fill window
+        CursorWindow window = new CursorWindow(false);
+        cursor.fillWindow(0, window);
+
+        // read from cursor window
+        for (int i = 0; i < list.size(); i++) {
+            ArrayList col = list.get(i);
+            for (int j = 0; j < colsize; j++) {
+                String s = window.getString(i, j);
+                int r2 = (Integer) col.get(j);
+                int r1 = Integer.parseInt(s);
+                assertEquals(r2, r1);
+            }
+        }
+
+        // test cursor window handle startpos != 0
+        window.clear();
+        cursor.fillWindow(1, window);
+        // read from cursor from window
+        for (int i = 1; i < list.size(); i++) {
+            ArrayList col = list.get(i);
+            for (int j = 0; j < colsize; j++) {
+                String s = window.getString(i, j);
+                int r2 = (Integer) col.get(j);
+                int r1 = Integer.parseInt(s);
+                assertEquals(r2, r1);
+            }
+        }
+
+        // Clear the window and make sure it's empty
+        window.clear();
+        assertEquals(0, window.getNumRows());
+    }
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            notes = "This is a test from unittest of CursorWindowTest.",
+            method = "putNull",
+            args = {int.class, int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            notes = "This is a test from unittest of CursorWindowTest.",
+            method = "getString",
+            args = {int.class, int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            notes = "This is a test from unittest of CursorWindowTest.",
+            method = "getBlob",
+            args = {int.class, int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            notes = "This is a test from unittest of CursorWindowTest.",
+            method = "getDouble",
+            args = {int.class, int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            notes = "This is a test from unittest of CursorWindowTest.",
+            method = "getLong",
+            args = {int.class, int.class}
+        )
+    })
+    public void testNull() {
+        CursorWindow window = getOneByOneWindow();
+
+        // Put in a null value and read it back as various types
+        assertTrue(window.putNull(0, 0));
+        assertNull(window.getString(0, 0));
+        assertEquals(0, window.getLong(0, 0));
+        assertEquals(0.0, window.getDouble(0, 0));
+        assertNull(window.getBlob(0, 0));
+    }
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            notes = "This is a test from unittest of CursorWindowTest.",
+            method = "putString",
+            args = {java.lang.String.class, int.class, int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            notes = "This is a test from unittest of CursorWindowTest.",
+            method = "getString",
+            args = {int.class, int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            notes = "This is a test from unittest of CursorWindowTest.",
+            method = "getLong",
+            args = {int.class, int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            notes = "This is a test from unittest of CursorWindowTest.",
+            method = "getDouble",
+            args = {int.class, int.class}
+        )
+    })
+    public void testEmptyString() {
+        CursorWindow window = getOneByOneWindow();
+
+        // put size 0 string and read it back as various types
+        assertTrue(window.putString("", 0, 0));
+        assertEquals("", window.getString(0, 0));
+        assertEquals(0, window.getLong(0, 0));
+        assertEquals(0.0, window.getDouble(0, 0));
+    }
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "CursorWindow",
+            args = {boolean.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getStartPosition",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "newFromParcel",
+            args = {android.os.Parcel.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "writeToParcel",
+            args = {android.os.Parcel.class, int.class}
+        )
+    })
+    @ToBeFixed(bug = " ", explanation = "Can't create a remote binder for newFromParcel here.")
+    public void testConstructors() {
+        int TEST_NUMBER = 5;
+        CursorWindow cursorWindow;
+
+        // Test constructor with 'true' input, and getStartPosition should return 0
+        cursorWindow = new CursorWindow(true);
+        assertEquals(0, cursorWindow.getStartPosition());
+
+        // Test constructor with 'false' input
+        cursorWindow = new CursorWindow(false);
+        assertEquals(0, cursorWindow.getStartPosition());
+
+        // Test newFromParcel
+        Parcel parcel = Parcel.obtain();
+        try {
+            cursorWindow = CursorWindow.newFromParcel(parcel);
+            fail("Can't accept a local binder.");
+        } catch (IllegalStateException e) {
+            // expected
+        }
+
+        cursorWindow = new CursorWindow(true);
+        cursorWindow.setStartPosition(TEST_NUMBER);
+        cursorWindow.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        assertNotNull(parcel.readStrongBinder());
+        assertEquals(TEST_NUMBER, parcel.readInt());
+    }
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setNumColumns",
+            args = {int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getNumRows",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "allocRow",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "freeLastRow",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "close",
+            args = {}
+        )
+    })
+    public void testDataStructureOperations() {
+        CursorWindow cursorWindow = new CursorWindow(true);
+
+        // Test with normal values
+        assertTrue(cursorWindow.setNumColumns(0));
+        // If the column has been set to zero, can't put String.
+        assertFalse(cursorWindow.putString(TEST_STRING, 0, 0));
+
+        // Test allocRow().
+        assertTrue(cursorWindow.allocRow());
+        assertEquals(1, cursorWindow.getNumRows());
+        assertTrue(cursorWindow.allocRow());
+        assertEquals(2, cursorWindow.getNumRows());
+        // Though allocate a row, but the column number is still 0, so can't putString.
+        assertFalse(cursorWindow.putString(TEST_STRING, 0, 0));
+
+        // Test freeLstRow
+        cursorWindow.freeLastRow();
+        assertEquals(1, cursorWindow.getNumRows());
+        cursorWindow.freeLastRow();
+        assertEquals(0, cursorWindow.getNumRows());
+
+        cursorWindow = new CursorWindow(true);
+        assertTrue(cursorWindow.setNumColumns(6));
+        assertTrue(cursorWindow.allocRow());
+        // Column number set to negative number, so now can put values.
+        assertTrue(cursorWindow.putString(TEST_STRING, 0, 0));
+        assertEquals(TEST_STRING, cursorWindow.getString(0, 0));
+
+        // Test with negative value
+        assertFalse(cursorWindow.setNumColumns(-1));
+
+        // Test with reference limitation
+        cursorWindow.releaseReference();
+        try {
+            cursorWindow.setNumColumns(5);
+            fail("setNumColumns() should throws IllegalStateException here.");
+        } catch (IllegalStateException e) {
+            // expected
+        }
+
+        // Test close(), close will also minus references, that will lead acquireReference()
+        // related operation failed.
+        cursorWindow.close();
+        try {
+            cursorWindow.acquireReference();
+            fail("setNumColumns() should throws IllegalStateException here.");
+        } catch (IllegalStateException e) {
+            // expected
+        }
+    }
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "putString",
+            args = {java.lang.String.class, int.class, int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "putNull",
+            args = {int.class, int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "putLong",
+            args = {long.class, int.class, int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "putDouble",
+            args = {double.class, int.class, int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "putBlob",
+            args = {byte[].class, int.class, int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getString",
+            args = {int.class, int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getShort",
+            args = {int.class, int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getLong",
+            args = {int.class, int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getInt",
+            args = {int.class, int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getFloat",
+            args = {int.class, int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getDouble",
+            args = {int.class, int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getBlob",
+            args = {int.class, int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "isNull",
+            args = {int.class, int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "isBlob",
+            args = {int.class, int.class}
+        )
+    })
+    public void testAccessDataValues() {
+        final long NUMBER_LONG_INTEGER = (long) 0xaabbccddffL;
+        final long NUMBER_INTEGER = (int) NUMBER_LONG_INTEGER;
+        final long NUMBER_SHORT = (short) NUMBER_INTEGER;
+        final float NUMBER_FLOAT_SCIENCE = 7.332952E11f;
+        final double NUMBER_DOUBLE_SCIENCE = 7.33295205887E11;
+        final String NUMBER_FLOAT_SCIENCE_STRING = "7.332952E11";
+        final String NUMBER_DOUBLE_SCIENCE_STRING = "7.33295205887E11";
+        final String NUMBER_FLOAT_SCIENCE_STRING2 = "7.33295e+11";
+
+        byte[] originalBlob = new byte[Byte.MAX_VALUE];
+        for (int i = 0; i < Byte.MAX_VALUE; i++) {
+            originalBlob[i] = (byte) i;
+        }
+
+        CursorWindow cursorWindow = new CursorWindow(true);
+        cursorWindow.setNumColumns(5);
+        cursorWindow.allocRow();
+
+        // Test putString, getString, getLong, getInt, isBlob
+        assertTrue(cursorWindow.putString(Long.toString(NUMBER_LONG_INTEGER), 0, 0));
+        assertEquals(Long.toString(NUMBER_LONG_INTEGER), cursorWindow.getString(0, 0));
+        assertEquals(NUMBER_LONG_INTEGER, cursorWindow.getLong(0, 0));
+        assertEquals(NUMBER_INTEGER, cursorWindow.getInt(0, 0));
+        assertEquals(NUMBER_SHORT, cursorWindow.getShort(0, 0));
+        // Converting of Float, there would be some little precision differences. So just compare
+        // first 6 digits.
+        assertEquals(NUMBER_FLOAT_SCIENCE_STRING.substring(0, 6), Float.toString(
+                cursorWindow.getFloat(0, 0)).substring(0, 6));
+        assertEquals(NUMBER_DOUBLE_SCIENCE_STRING, Double.toString(cursorWindow.getDouble(0, 0)));
+        assertFalse(cursorWindow.isNull(0, 0));
+        assertFalse(cursorWindow.isBlob(0, 0));
+
+        // Test null String
+        assertTrue(cursorWindow.putString("", 0, 0));
+        assertEquals("", cursorWindow.getString(0, 0));
+        assertEquals(0, cursorWindow.getLong(0, 0));
+        assertEquals(0, cursorWindow.getInt(0, 0));
+        assertEquals(0, cursorWindow.getShort(0, 0));
+        assertEquals(0.0, cursorWindow.getDouble(0, 0));
+        assertEquals(0.0f, cursorWindow.getFloat(0, 0), 0.00000001f);
+        assertFalse(cursorWindow.isNull(0, 0));
+        assertFalse(cursorWindow.isBlob(0, 0));
+
+        // Test putNull, getString, getLong, getDouble, getBlob, getInd, getShort, getFloat,
+        // isBlob.
+        assertTrue(cursorWindow.putNull(0, 1));
+        assertNull(cursorWindow.getString(0, 1));
+        assertEquals(0, cursorWindow.getLong(0, 1));
+        assertEquals(0, cursorWindow.getInt(0, 1));
+        assertEquals(0, cursorWindow.getShort(0, 1));
+        assertEquals(0.0, cursorWindow.getDouble(0, 1));
+        assertEquals(0.0f, cursorWindow.getFloat(0, 1), 0.00000001f);
+        assertNull(cursorWindow.getBlob(0, 1));
+        assertTrue(cursorWindow.isNull(0, 1));
+        // If the field is null, isBlob will return true.
+        assertTrue(cursorWindow.isBlob(0, 1));
+
+        // Test putLong, getLong, getInt, getString , getShort, getFloat, getDouble, isBlob.
+        assertTrue(cursorWindow.putLong(NUMBER_LONG_INTEGER, 0, 2));
+        assertEquals(NUMBER_LONG_INTEGER, cursorWindow.getLong(0, 2));
+        assertEquals(NUMBER_INTEGER, cursorWindow.getInt(0, 2));
+        assertEquals(Long.toString(NUMBER_LONG_INTEGER), cursorWindow.getString(0, 2));
+        assertEquals(NUMBER_SHORT, cursorWindow.getShort(0, 2));
+        assertEquals(NUMBER_FLOAT_SCIENCE, cursorWindow.getFloat(0, 2), 0.00000001f);
+        assertEquals(NUMBER_DOUBLE_SCIENCE, cursorWindow.getDouble(0, 2), 0.00000001);
+        try {
+            cursorWindow.getBlob(0, 2);
+            fail("Can't get Blob from a Integer value.");
+        } catch (SQLiteException e) {
+            // expected
+        }
+        assertFalse(cursorWindow.isNull(0, 2));
+        assertFalse(cursorWindow.isBlob(0, 2));
+
+        // Test putDouble
+        assertTrue(cursorWindow.putDouble(NUMBER_DOUBLE_SCIENCE, 0, 3));
+        assertEquals(NUMBER_LONG_INTEGER, cursorWindow.getLong(0, 3));
+        assertEquals(NUMBER_INTEGER, cursorWindow.getInt(0, 3));
+        // Converting from Double to String, there would be some little precision differences. So
+        // Just compare first 6 digits.
+        assertEquals(NUMBER_FLOAT_SCIENCE_STRING2.substring(0, 6), cursorWindow.getString(0, 3)
+                .substring(0, 6));
+        assertEquals(NUMBER_SHORT, cursorWindow.getShort(0, 3));
+        assertEquals(NUMBER_FLOAT_SCIENCE, cursorWindow.getFloat(0, 3), 0.00000001f);
+        assertEquals(NUMBER_DOUBLE_SCIENCE, cursorWindow.getDouble(0, 3), 0.00000001);
+        try {
+            cursorWindow.getBlob(0, 3);
+            fail("Can't get Blob from a Double value.");
+        } catch (SQLiteException e) {
+            // expected
+        }
+        assertFalse(cursorWindow.isNull(0, 3));
+        assertFalse(cursorWindow.isBlob(0, 3));
+
+        // Test putBlob
+        assertTrue(cursorWindow.putBlob(originalBlob, 0, 4));
+        byte[] targetBlob = cursorWindow.getBlob(0, 4);
+        assertTrue(Arrays.equals(originalBlob, targetBlob));
+        assertFalse(cursorWindow.isNull(0, 4));
+        // Test isBlob
+        assertTrue(cursorWindow.isBlob(0, 4));
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "copyStringToBuffer",
+        args = {int.class, int.class, android.database.CharArrayBuffer.class}
+    )
+    public void testCopyStringToBuffer() {
+        int DEFAULT_ARRAY_LENGTH = 64;
+        String baseString = "0123456789";
+        String expectedString = "";
+        // Create a 60 characters string.
+        for (int i = 0; i < 6; i++) {
+            expectedString += baseString;
+        }
+        CharArrayBuffer charArrayBuffer = new CharArrayBuffer(null);
+        CursorWindow cursorWindow = new CursorWindow(true);
+        cursorWindow.setNumColumns(2);
+        cursorWindow.allocRow();
+
+        assertEquals(null, charArrayBuffer.data);
+        cursorWindow.putString(expectedString, 0, 0);
+        cursorWindow.copyStringToBuffer(0, 0, charArrayBuffer);
+        assertNotNull(charArrayBuffer.data);
+        // By default, if the field's string is shorter than 64, array will be allocated as 64.
+        assertEquals(DEFAULT_ARRAY_LENGTH, charArrayBuffer.data.length);
+        assertEquals(expectedString,
+                new String(charArrayBuffer.data, 0, charArrayBuffer.sizeCopied));
+
+        // Test in case of string is longer than 64,
+        expectedString += baseString;
+        charArrayBuffer = new CharArrayBuffer(null);
+        cursorWindow.putString(expectedString, 0, 1);
+        cursorWindow.copyStringToBuffer(0, 1, charArrayBuffer);
+        assertNotNull(charArrayBuffer.data);
+        // If the string is longer than 64, array will be allocated as needed(longer than 64).
+        assertEquals(expectedString,
+                new String(charArrayBuffer.data, 0, charArrayBuffer.sizeCopied));
+        assertEquals(70, expectedString.length());
+        assertEquals(expectedString.length(), charArrayBuffer.data.length);
+    }
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "setStartPosition",
+            args = {int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getStartPosition",
+            args = {}
+        )
+    })
+    public void testAccessStartPosition() {
+        final int TEST_POSITION_1 = 0;
+        final int TEST_POSITION_2 = 3;
+
+        CursorWindow cursorWindow = new CursorWindow(true);
+        fillCursorTestContents(cursorWindow, 5);
+
+        // Test setStartPosition
+        assertEquals(TEST_POSITION_1, cursorWindow.getStartPosition());
+        assertEquals(3, cursorWindow.getInt(3, 0));
+        assertEquals(TEST_STRING + "3", cursorWindow.getString(3, 1));
+        assertEquals(4, cursorWindow.getInt(4, 0));
+        assertEquals(TEST_STRING + "4", cursorWindow.getString(4, 1));
+        cursorWindow.setStartPosition(TEST_POSITION_2);
+
+        assertEquals(TEST_POSITION_2, cursorWindow.getStartPosition());
+
+        assertEquals(0, cursorWindow.getInt(3, 0));
+        assertEquals(TEST_STRING + "0", cursorWindow.getString(3, 1));
+        assertEquals(1, cursorWindow.getInt(4, 0));
+        assertEquals(TEST_STRING + "1", cursorWindow.getString(4, 1));
+        try {
+            cursorWindow.getBlob(0, 0);
+            fail("Row number is smaller than startPosition, will cause a IllegalStateException.");
+        } catch (IllegalStateException e) {
+            // expected
+        }
+    }
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "clear",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "onAllReferencesReleased",
+            args = {}
+        )
+    })
+    public void testClearAndOnAllReferencesReleased() {
+        MockCursorWindow cursorWindow = new MockCursorWindow(true);
+
+        assertEquals(0, cursorWindow.getNumRows());
+        fillCursorTestContents(cursorWindow, 10);
+        assertEquals(10, cursorWindow.getNumRows());
+        assertEquals(0, cursorWindow.getStartPosition());
+        cursorWindow.setStartPosition(5);
+        assertEquals(5, cursorWindow.getStartPosition());
+
+        // Test clear(). a complete calling process of cursorWindow has a perfect acquiring and
+        // releasing pair, so the references number will be equal at the begin and the end.
+        assertFalse(cursorWindow.hasReleasedAllReferences());
+        cursorWindow.clear();
+        assertEquals(0, cursorWindow.getNumRows());
+        assertEquals(0, cursorWindow.getStartPosition());
+        assertFalse(cursorWindow.hasReleasedAllReferences());
+
+        // Test onAllReferencesReleased.
+        // By default, cursorWindow's reference is 1, when it reachs 0, onAllReferencesReleased
+        // be invoked.
+        cursorWindow = new MockCursorWindow(true);
+        cursorWindow.releaseReference();
+        assertTrue(cursorWindow.hasReleasedAllReferences());
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "describeContents",
+        args = {}
+    )
+    public void testDescribeContents() {
+        CursorWindow cursorWindow = new CursorWindow(true);
+        assertEquals(0, cursorWindow.describeContents());
+    }
+
+    private class MockCursorWindow extends CursorWindow {
+        private boolean mHasReleasedAllReferences = false;
+
+        public MockCursorWindow(boolean localWindow) {
+            super(localWindow);
+        }
+
+        @Override
+        protected void onAllReferencesReleased() {
+            super.onAllReferencesReleased();
+            mHasReleasedAllReferences = true;
+        }
+
+        public boolean hasReleasedAllReferences() {
+            return mHasReleasedAllReferences;
+        }
+
+        public void resetStatus() {
+            mHasReleasedAllReferences = false;
+        }
+    }
+
+    private void fillCursorTestContents(CursorWindow cursorWindow, int length) {
+        cursorWindow.clear();
+        cursorWindow.setStartPosition(0);
+        cursorWindow.setNumColumns(2);
+        for (int i = 0; i < length; i++) {
+            cursorWindow.allocRow();
+            cursorWindow.putLong(i, i, 0);
+            cursorWindow.putString(TEST_STRING + i, i, 1);
+        }
+    }
+
+    private static ArrayList<ArrayList> createTestList(int rows, int cols) {
+        ArrayList<ArrayList> list = Lists.newArrayList();
+        Random generator = new Random();
+
+        for (int i = 0; i < rows; i++) {
+            ArrayList<Integer> col = Lists.newArrayList();
+            list.add(col);
+            for (int j = 0; j < cols; j++) {
+                // generate random number
+                Integer r = generator.nextInt();
+                col.add(r);
+            }
+        }
+        return list;
+    }
+
+    /**
+     * The method comes from unit_test CursorWindowTest.
+     */
+    private CursorWindow getOneByOneWindow() {
+        CursorWindow window = new CursorWindow(false);
+        assertTrue(window.setNumColumns(1));
+        assertTrue(window.allocRow());
+        return window;
+    }
+}
diff --git a/tests/tests/database/src/android/database/cts/DatabaseUtilsTest.java b/tests/tests/database/src/android/database/cts/DatabaseUtilsTest.java
new file mode 100644
index 0000000..ad89d5f
--- /dev/null
+++ b/tests/tests/database/src/android/database/cts/DatabaseUtilsTest.java
@@ -0,0 +1,727 @@
+/*
+ * Copyright (C) 2008 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.database.cts;
+
+import dalvik.annotation.TestLevel;
+import dalvik.annotation.TestTargetClass;
+import dalvik.annotation.TestTargetNew;
+import dalvik.annotation.TestTargets;
+import dalvik.annotation.ToBeFixed;
+
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.DatabaseUtils;
+import android.database.DatabaseUtils.InsertHelper;
+import android.database.sqlite.SQLiteAbortException;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteDoneException;
+import android.database.sqlite.SQLiteException;
+import android.database.sqlite.SQLiteStatement;
+import android.os.Parcel;
+import android.test.AndroidTestCase;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.PrintStream;
+
+@TestTargetClass(android.database.DatabaseUtils.class)
+public class DatabaseUtilsTest extends AndroidTestCase {
+    private SQLiteDatabase mDatabase;
+    private File mDatabaseFile;
+    private static final String[] TEST_PROJECTION = new String[] {
+        "_id",             // 0
+        "name",            // 1
+        "age",             // 2
+        "address"          // 3
+    };
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mDatabaseFile = new File("/sqlite_stmt_journals", "database_test.db");
+        if (mDatabaseFile.exists()) {
+            mDatabaseFile.delete();
+        }
+        mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null);
+        assertNotNull(mDatabase);
+        mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, " +
+                "name TEXT, age INTEGER, address TEXT);");
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        mDatabase.close();
+        mDatabaseFile.delete();
+        super.tearDown();
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "appendEscapedSQLString",
+        args = {java.lang.StringBuilder.class, java.lang.String.class}
+    )
+    public void testAppendEscapedSQLString() {
+        String expected = "name='Mike'";
+        StringBuilder sb = new StringBuilder("name=");
+        DatabaseUtils.appendEscapedSQLString(sb, "Mike");
+        assertEquals(expected, sb.toString());
+
+        expected = "'name=''Mike'''";
+        sb = new StringBuilder();
+        DatabaseUtils.appendEscapedSQLString(sb, "name='Mike'");
+        assertEquals(expected, sb.toString());
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "sqlEscapeString",
+        args = {java.lang.String.class}
+    )
+    public void testSqlEscapeString() {
+        String expected = "'Jack'";
+        assertEquals(expected, DatabaseUtils.sqlEscapeString("Jack"));
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "appendValueToSql",
+        args = {java.lang.StringBuilder.class, java.lang.Object.class}
+    )
+    public void testAppendValueToSql() {
+        String expected = "address='LA'";
+        StringBuilder sb = new StringBuilder("address=");
+        DatabaseUtils.appendValueToSql(sb, "LA");
+        assertEquals(expected, sb.toString());
+
+        expected = "address=NULL";
+        sb = new StringBuilder("address=");
+        DatabaseUtils.appendValueToSql(sb, null);
+        assertEquals(expected, sb.toString());
+
+        expected = "flag=1";
+        sb = new StringBuilder("flag=");
+        DatabaseUtils.appendValueToSql(sb, true);
+        assertEquals(expected, sb.toString());
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "bindObjectToProgram",
+        args = {android.database.sqlite.SQLiteProgram.class, int.class, java.lang.Object.class}
+    )
+    public void testBindObjectToProgram() {
+        String name = "Mike";
+        int age = 21;
+        String address = "LA";
+
+        // at the beginning, there are no records in the database.
+        Cursor cursor = mDatabase.query("test", TEST_PROJECTION, null, null, null, null, null);
+        assertNotNull(cursor);
+        assertEquals(0, cursor.getCount());
+
+        String sql = "INSERT INTO test (name, age, address) VALUES (?, ?, ?);";
+        SQLiteStatement statement = mDatabase.compileStatement(sql);
+        DatabaseUtils.bindObjectToProgram(statement, 1, name);
+        DatabaseUtils.bindObjectToProgram(statement, 2, age);
+        DatabaseUtils.bindObjectToProgram(statement, 3, address);
+        statement.execute();
+        statement.close();
+
+        cursor = mDatabase.query("test", TEST_PROJECTION, null, null, null, null, null);
+        assertNotNull(cursor);
+        assertEquals(1, cursor.getCount());
+        cursor.moveToFirst();
+        assertEquals(name, cursor.getString(1));
+        assertEquals(age, cursor.getInt(2));
+        assertEquals(address, cursor.getString(3));
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "createDbFromSqlStatements",
+        args = {android.content.Context.class, java.lang.String.class, int.class,
+                java.lang.String.class}
+    )
+    public void testCreateDbFromSqlStatements() {
+        String dbName = "ExampleName";
+        String sqls = "CREATE TABLE test (_id INTEGER PRIMARY KEY, name TEXT);\n"
+                + "INSERT INTO test (name) VALUES ('Mike');\n";
+        DatabaseUtils.createDbFromSqlStatements(getContext(), dbName, 1, sqls);
+
+        SQLiteDatabase db = getContext().openOrCreateDatabase(dbName, 0, null);
+        final String[] PROJECTION = new String[] {
+            "_id",             // 0
+            "name"             // 1
+        };
+        Cursor cursor = db.query("test", PROJECTION, null, null, null, null, null);
+        assertNotNull(cursor);
+        assertEquals(1, cursor.getCount());
+        cursor.moveToFirst();
+        assertEquals("Mike", cursor.getString(1));
+        getContext().deleteDatabase(dbName);
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "cursorDoubleToContentValues",
+        args = {android.database.Cursor.class, java.lang.String.class,
+                android.content.ContentValues.class, java.lang.String.class}
+    )
+    public void testCursorDoubleToContentValues() {
+        mDatabase.execSQL("INSERT INTO test (name, age, address) VALUES ('Mike', '20', 'LA');");
+        Cursor cursor = mDatabase.query("test", TEST_PROJECTION, null, null, null, null, null);
+        assertNotNull(cursor);
+
+        ContentValues contentValues = new ContentValues();
+        String key = "key";
+        cursor.moveToFirst();
+        DatabaseUtils.cursorDoubleToContentValues(cursor, "age", contentValues, key);
+        assertEquals(20.0, contentValues.getAsDouble(key));
+
+        DatabaseUtils.cursorDoubleToContentValues(cursor, "Error Field Name", contentValues, key);
+        assertNull(contentValues.getAsDouble(key));
+
+        DatabaseUtils.cursorDoubleToContentValues(cursor, "name", contentValues, key);
+        assertEquals(0.0, contentValues.getAsDouble(key));
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "cursorDoubleToCursorValues",
+        args = {android.database.Cursor.class, java.lang.String.class,
+                android.content.ContentValues.class}
+    )
+    @ToBeFixed(bug = "1586458", explanation = "It's probably a typo.")
+    public void testCursorDoubleToCursorValues() {
+        mDatabase.execSQL("INSERT INTO test (name, age, address) VALUES ('Mike', '20', 'LA');");
+        Cursor cursor = mDatabase.query("test", TEST_PROJECTION, null, null, null, null, null);
+        assertNotNull(cursor);
+
+        ContentValues contentValues = new ContentValues();
+        cursor.moveToFirst();
+        DatabaseUtils.cursorDoubleToCursorValues(cursor, "age", contentValues);
+        assertEquals(20.0, contentValues.getAsDouble("age"));
+
+        DatabaseUtils.cursorDoubleToCursorValues(cursor, "Error Field Name", contentValues);
+        assertNull(contentValues.getAsDouble("Error Field Name"));
+
+        DatabaseUtils.cursorDoubleToCursorValues(cursor, "name", contentValues);
+        assertEquals(0.0, contentValues.getAsDouble("name"));
+    }
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "cursorIntToContentValues",
+            args = {android.database.Cursor.class, java.lang.String.class,
+                    android.content.ContentValues.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "cursorIntToContentValues",
+            args = {android.database.Cursor.class, java.lang.String.class,
+                    android.content.ContentValues.class, java.lang.String.class}
+        )
+    })
+    public void testCursorIntToContentValues() {
+        mDatabase.execSQL("INSERT INTO test (name, age, address) VALUES ('Mike', '20', 'LA');");
+        Cursor cursor = mDatabase.query("test", TEST_PROJECTION, null, null, null, null, null);
+        assertNotNull(cursor);
+
+        ContentValues contentValues = new ContentValues();
+        String key = "key";
+        cursor.moveToFirst();
+        DatabaseUtils.cursorIntToContentValues(cursor, "age", contentValues, key);
+        assertEquals(Integer.valueOf(20), contentValues.getAsInteger(key));
+
+        DatabaseUtils.cursorIntToContentValues(cursor, "Error Field Name", contentValues, key);
+        assertNull(contentValues.getAsInteger(key));
+
+        DatabaseUtils.cursorIntToContentValues(cursor, "name", contentValues, key);
+        assertEquals(Integer.valueOf(0), contentValues.getAsInteger(key));
+
+        contentValues = new ContentValues();
+        DatabaseUtils.cursorIntToContentValues(cursor, "age", contentValues);
+        assertEquals(Integer.valueOf(20), contentValues.getAsInteger("age"));
+
+        DatabaseUtils.cursorIntToContentValues(cursor, "Error Field Name", contentValues);
+        assertNull(contentValues.getAsInteger("Error Field Name"));
+
+        DatabaseUtils.cursorIntToContentValues(cursor, "name", contentValues);
+        assertEquals(Integer.valueOf(0), contentValues.getAsInteger("name"));
+    }
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "cursorLongToContentValues",
+            args = {android.database.Cursor.class, java.lang.String.class,
+                    android.content.ContentValues.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "cursorLongToContentValues",
+            args = {android.database.Cursor.class, java.lang.String.class,
+                    android.content.ContentValues.class, java.lang.String.class}
+        )
+    })
+    public void testcursorLongToContentValues() {
+        mDatabase.execSQL("INSERT INTO test (name, age, address) VALUES ('Mike', '20', 'LA');");
+        Cursor cursor = mDatabase.query("test", TEST_PROJECTION, null, null, null, null, null);
+        assertNotNull(cursor);
+
+        ContentValues contentValues = new ContentValues();
+        String key = "key";
+        cursor.moveToNext();
+        DatabaseUtils.cursorLongToContentValues(cursor, "age", contentValues, key);
+        assertEquals(Long.valueOf(20), contentValues.getAsLong(key));
+
+        DatabaseUtils.cursorLongToContentValues(cursor, "Error Field Name", contentValues, key);
+        assertNull(contentValues.getAsLong(key));
+
+        DatabaseUtils.cursorLongToContentValues(cursor, "name", contentValues, key);
+        assertEquals(Long.valueOf(0), contentValues.getAsLong(key));
+
+        contentValues = new ContentValues();
+        DatabaseUtils.cursorLongToContentValues(cursor, "age", contentValues);
+        assertEquals(Long.valueOf(20), contentValues.getAsLong("age"));
+
+        DatabaseUtils.cursorLongToContentValues(cursor, "Error Field Name", contentValues);
+        assertNull(contentValues.getAsLong("Error Field Name"));
+
+        DatabaseUtils.cursorLongToContentValues(cursor, "name", contentValues);
+        assertEquals(Long.valueOf(0), contentValues.getAsLong("name"));
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "cursorRowToContentValues",
+        args = {android.database.Cursor.class, android.content.ContentValues.class}
+    )
+    public void testCursorRowToContentValues() {
+        mDatabase.execSQL("INSERT INTO test (name, age, address) VALUES ('Mike', '20', 'LA');");
+        Cursor cursor = mDatabase.query("test", TEST_PROJECTION, null, null, null, null, null);
+        assertNotNull(cursor);
+
+        ContentValues contentValues = new ContentValues();
+        cursor.moveToNext();
+        DatabaseUtils.cursorRowToContentValues(cursor, contentValues);
+        assertEquals("Mike", (String) contentValues.get("name"));
+        assertEquals("20", (String) contentValues.get("age"));
+        assertEquals("LA", (String) contentValues.get("address"));
+    }
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "cursorStringToContentValues",
+            args = {android.database.Cursor.class, java.lang.String.class,
+                    android.content.ContentValues.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "cursorStringToContentValues",
+            args = {android.database.Cursor.class, java.lang.String.class,
+                    android.content.ContentValues.class, java.lang.String.class}
+        )
+    })
+    public void testCursorStringToContentValues() {
+        mDatabase.execSQL("INSERT INTO test (name, age, address) VALUES ('Mike', '20', 'LA');");
+        Cursor cursor = mDatabase.query("test", TEST_PROJECTION, null, null, null, null, null);
+        assertNotNull(cursor);
+
+        ContentValues contentValues = new ContentValues();
+        String key = "key";
+        cursor.moveToNext();
+        DatabaseUtils.cursorStringToContentValues(cursor, "age", contentValues, key);
+        assertEquals("20", (String) contentValues.get(key));
+
+        try {
+            DatabaseUtils.cursorStringToContentValues(cursor, "Error Field Name",
+                    contentValues, key);
+            fail("should throw IllegalArgumentException.");
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+
+        DatabaseUtils.cursorStringToContentValues(cursor, "name", contentValues, key);
+        assertEquals("Mike", contentValues.get(key));
+
+        contentValues = new ContentValues();
+        DatabaseUtils.cursorStringToContentValues(cursor, "age", contentValues);
+        assertEquals("20", contentValues.get("age"));
+
+        try {
+            DatabaseUtils.cursorStringToContentValues(cursor, "Error Field Name", contentValues);
+            fail("should throw IllegalArgumentException.");
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+
+        DatabaseUtils.cursorStringToContentValues(cursor, "name", contentValues);
+        assertEquals("Mike", contentValues.get("name"));
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "cursorStringToInsertHelper",
+        args = {android.database.Cursor.class, java.lang.String.class,
+                android.database.DatabaseUtils.InsertHelper.class, int.class}
+    )
+    public void testCursorStringToInsertHelper() {
+        // create a new table.
+        mDatabase.execSQL("CREATE TABLE test_copy (_id INTEGER PRIMARY KEY, " +
+                "name TEXT, age INTEGER, address TEXT);");
+
+        mDatabase.execSQL("INSERT INTO test (name, age, address) VALUES ('Mike', '20', 'LA');");
+        Cursor cursor = mDatabase.query("test_copy", TEST_PROJECTION, null, null, null, null, null);
+        assertEquals(0, cursor.getCount());
+
+        InsertHelper insertHelper = new InsertHelper(mDatabase, "test_copy");
+        int indexName = insertHelper.getColumnIndex("name");
+        int indexAge = insertHelper.getColumnIndex("age");
+        int indexAddress = insertHelper.getColumnIndex("address");
+
+        cursor = mDatabase.query("test", TEST_PROJECTION, null, null, null, null, null);
+        cursor.moveToNext();
+        insertHelper.prepareForInsert();
+        DatabaseUtils.cursorStringToInsertHelper(cursor, "name", insertHelper, indexName);
+        DatabaseUtils.cursorStringToInsertHelper(cursor, "age", insertHelper, indexAge);
+        DatabaseUtils.cursorStringToInsertHelper(cursor, "address", insertHelper, indexAddress);
+        insertHelper.execute();
+
+        cursor = mDatabase.query("test_copy", TEST_PROJECTION, null, null, null, null, null);
+        assertEquals(1, cursor.getCount());
+        cursor.moveToNext();
+        assertEquals("Mike", cursor.getString(1));
+        assertEquals(20, cursor.getInt(2));
+        assertEquals("LA", cursor.getString(3));
+    }
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "dumpCurrentRow",
+            args = {android.database.Cursor.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "dumpCurrentRow",
+            args = {android.database.Cursor.class, java.io.PrintStream.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "dumpCurrentRow",
+            args = {android.database.Cursor.class, java.lang.StringBuilder.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "dumpCurrentRowToString",
+            args = {android.database.Cursor.class}
+        )
+    })
+    public void testDumpCurrentRow() {
+        mDatabase.execSQL("INSERT INTO test (name, age, address) VALUES ('Mike', '20', 'LA');");
+        Cursor cursor = mDatabase.query("test", TEST_PROJECTION, null, null, null, null, null);
+        assertNotNull(cursor);
+        cursor.moveToNext();
+        String expected = "0 {\n   _id=1\n   name=Mike\n   age=20\n   address=LA\n}\n";
+
+        DatabaseUtils.dumpCurrentRow(cursor);
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        PrintStream os = new PrintStream(bos);
+        DatabaseUtils.dumpCurrentRow(cursor, os);
+        os.flush();
+        os.close();
+        assertEquals(expected, bos.toString());
+
+        StringBuilder sb = new StringBuilder();
+        DatabaseUtils.dumpCurrentRow(cursor, sb);
+        assertEquals(expected, sb.toString());
+
+        assertEquals(expected, DatabaseUtils.dumpCurrentRowToString(cursor));
+    }
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "dumpCursor",
+            args = {android.database.Cursor.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "dumpCursor",
+            args = {android.database.Cursor.class, java.io.PrintStream.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "dumpCursor",
+            args = {android.database.Cursor.class, java.lang.StringBuilder.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "dumpCursorToString",
+            args = {android.database.Cursor.class}
+        )
+    })
+    public void testDumpCursor() {
+        mDatabase.execSQL("INSERT INTO test (name, age, address) VALUES ('Mike', '20', 'LA');");
+        mDatabase.execSQL("INSERT INTO test (name, age, address) VALUES ('Jack', '30', 'London');");
+        Cursor cursor = mDatabase.query("test", TEST_PROJECTION, null, null, null, null, null);
+        assertNotNull(cursor);
+        int pos = cursor.getPosition();
+        String expected = ">>>>> Dumping cursor " + cursor + "\n" +
+                "0 {\n" +
+                "   _id=1\n" +
+                "   name=Mike\n" +
+                "   age=20\n" +
+                "   address=LA\n" +
+                "}\n" +
+                "1 {\n" +
+                "   _id=2\n" +
+                "   name=Jack\n" +
+                "   age=30\n" +
+                "   address=London\n" +
+                "}\n" +
+                "<<<<<\n";
+
+        DatabaseUtils.dumpCursor(cursor);
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        PrintStream os = new PrintStream(bos);
+        DatabaseUtils.dumpCursor(cursor, os);
+        os.flush();
+        os.close();
+        assertEquals(pos, cursor.getPosition()); // dumpCursor should not change status of cursor
+        assertEquals(expected, bos.toString());
+
+        StringBuilder sb = new StringBuilder();
+        DatabaseUtils.dumpCursor(cursor, sb);
+        assertEquals(pos, cursor.getPosition()); // dumpCursor should not change status of cursor
+        assertEquals(expected, sb.toString());
+
+        assertEquals(expected, DatabaseUtils.dumpCursorToString(cursor));
+        assertEquals(pos, cursor.getPosition()); // dumpCursor should not change status of cursor
+    }
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getCollationKey",
+            args = {java.lang.String.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getHexCollationKey",
+            args = {java.lang.String.class}
+        )
+    })
+    public void testCollationKey() {
+        String key1 = DatabaseUtils.getCollationKey("abc");
+        String key2 = DatabaseUtils.getCollationKey("ABC");
+        String key3 = DatabaseUtils.getCollationKey("bcd");
+
+        assertTrue(key1.equals(key2));
+        assertFalse(key1.equals(key3));
+
+        key1 = DatabaseUtils.getHexCollationKey("abc");
+        key2 = DatabaseUtils.getHexCollationKey("ABC");
+        key3 = DatabaseUtils.getHexCollationKey("bcd");
+
+        assertTrue(key1.equals(key2));
+        assertFalse(key1.equals(key3));
+    }
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "longForQuery",
+            args = {android.database.sqlite.SQLiteDatabase.class, java.lang.String.class,
+                    java.lang.String[].class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "longForQuery",
+            args = {android.database.sqlite.SQLiteStatement.class, java.lang.String[].class}
+        )
+    })
+    public void testLongForQuery() {
+        mDatabase.execSQL("INSERT INTO test (name, age, address) VALUES ('Mike', '20', 'LA');");
+
+        String query = "SELECT age FROM test";
+        assertEquals(20, DatabaseUtils.longForQuery(mDatabase, query, null));
+
+        mDatabase.execSQL("INSERT INTO test (name, age, address) VALUES ('Jack', '35', 'London');");
+        query = "SELECT age FROM test WHERE name = ?";
+        String[] args = new String[] { "Jack" };
+        assertEquals(35, DatabaseUtils.longForQuery(mDatabase, query, args));
+        args = new String[] { "No such name" };
+        try {
+            DatabaseUtils.longForQuery(mDatabase, query, args);
+            fail("should throw SQLiteDoneException");
+        } catch (SQLiteDoneException e) {
+            // expected
+        }
+
+        query = "SELECT count(*) FROM test;";
+        SQLiteStatement statement = mDatabase.compileStatement(query);
+        assertEquals(2, DatabaseUtils.longForQuery(statement, null));
+
+        query = "SELECT age FROM test WHERE address = ?;";
+        statement = mDatabase.compileStatement(query);
+        args = new String[] { "London" };
+        assertEquals(35, DatabaseUtils.longForQuery(statement, args));
+
+        args = new String[] { "No such address" };
+        try {
+            DatabaseUtils.longForQuery(statement, args);
+            fail("should throw SQLiteDoneException");
+        } catch (SQLiteDoneException e) {
+            // expected
+        }
+        statement.close();
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "queryNumEntries",
+        args = {android.database.sqlite.SQLiteDatabase.class, java.lang.String.class}
+    )
+    public void testQueryNumEntries() {
+        assertEquals(0, DatabaseUtils.queryNumEntries(mDatabase, "test"));
+
+        mDatabase.execSQL("INSERT INTO test (name, age, address) VALUES ('Mike', '20', 'LA');");
+        assertEquals(1, DatabaseUtils.queryNumEntries(mDatabase, "test"));
+
+        mDatabase.execSQL("INSERT INTO test (name, age, address) VALUES ('Mike', '20', 'LA');");
+        assertEquals(2, DatabaseUtils.queryNumEntries(mDatabase, "test"));
+
+        try {
+            DatabaseUtils.queryNumEntries(mDatabase, "NoSuchTable");
+            fail("should throw SQLiteException.");
+        } catch (SQLiteException e) {
+            // expected
+        }
+    }
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "readExceptionFromParcel",
+            args = {android.os.Parcel.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "writeExceptionToParcel",
+            args = {android.os.Parcel.class, java.lang.Exception.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "readExceptionWithFileNotFoundExceptionFromParcel",
+            args = {android.os.Parcel.class}
+        )
+    })
+    public void testExceptionFromParcel() {
+        Parcel parcel = Parcel.obtain();
+        DatabaseUtils.writeExceptionToParcel(parcel, new IllegalArgumentException());
+        parcel.setDataPosition(0);
+        try {
+            DatabaseUtils.readExceptionFromParcel(parcel);
+            fail("should throw IllegalArgumentException.");
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+
+        parcel = Parcel.obtain();
+        DatabaseUtils.writeExceptionToParcel(parcel, new SQLiteAbortException());
+        parcel.setDataPosition(0);
+        try {
+            DatabaseUtils.readExceptionFromParcel(parcel);
+            fail("should throw SQLiteAbortException.");
+        } catch (SQLiteAbortException e) {
+            // expected
+        }
+
+        parcel = Parcel.obtain();
+        DatabaseUtils.writeExceptionToParcel(parcel, new FileNotFoundException());
+        parcel.setDataPosition(0);
+        try {
+            DatabaseUtils.readExceptionFromParcel(parcel);
+            fail("should throw RuntimeException.");
+        } catch (RuntimeException e) {
+            // expected
+        }
+
+        parcel = Parcel.obtain();
+        DatabaseUtils.writeExceptionToParcel(parcel, new FileNotFoundException());
+        parcel.setDataPosition(0);
+        try {
+            DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(parcel);
+            fail("should throw FileNotFoundException.");
+        } catch (FileNotFoundException e) {
+            // expected
+        }
+    }
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "stringForQuery",
+            args = {android.database.sqlite.SQLiteDatabase.class, java.lang.String.class,
+                    java.lang.String[].class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "stringForQuery",
+            args = {android.database.sqlite.SQLiteStatement.class, java.lang.String[].class}
+        )
+    })
+    public void testStringForQuery() {
+        mDatabase.execSQL("INSERT INTO test (name, age, address) VALUES ('Mike', '20', 'LA');");
+
+        String query = "SELECT name FROM test";
+        assertEquals("Mike", DatabaseUtils.stringForQuery(mDatabase, query, null));
+
+        mDatabase.execSQL("INSERT INTO test (name, age, address) VALUES ('Jack', '35', 'London');");
+        query = "SELECT name FROM test WHERE address = ?";
+        String[] args = new String[] { "London" };
+        assertEquals("Jack", DatabaseUtils.stringForQuery(mDatabase, query, args));
+        args = new String[] { "No such address" };
+        try {
+            DatabaseUtils.stringForQuery(mDatabase, query, args);
+            fail("should throw SQLiteDoneException");
+        } catch (SQLiteDoneException e) {
+            // expected
+        }
+
+        query = "SELECT name FROM test WHERE age = ?;";
+        SQLiteStatement statement = mDatabase.compileStatement(query);
+        args = new String[] { "20" };
+        assertEquals("Mike", DatabaseUtils.stringForQuery(statement, args));
+
+        args = new String[] { "1000" }; // NO people can be older than this.
+        try {
+            DatabaseUtils.stringForQuery(statement, args);
+            fail("should throw SQLiteDoneException");
+        } catch (SQLiteDoneException e) {
+            // expected
+        }
+        statement.close();
+    }
+}
diff --git a/tests/tests/database/src/android/database/cts/MatrixCursorTest.java b/tests/tests/database/src/android/database/cts/MatrixCursorTest.java
new file mode 100644
index 0000000..ce5861e
--- /dev/null
+++ b/tests/tests/database/src/android/database/cts/MatrixCursorTest.java
@@ -0,0 +1,372 @@
+/*
+ * Copyright (C) 2008 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.database.cts;
+
+import dalvik.annotation.TestLevel;
+import dalvik.annotation.TestTargetClass;
+import dalvik.annotation.TestTargetNew;
+import dalvik.annotation.TestTargets;
+
+import android.database.CursorIndexOutOfBoundsException;
+import android.database.MatrixCursor;
+import android.database.MatrixCursor.RowBuilder;
+
+import java.util.ArrayList;
+
+import junit.framework.TestCase;
+
+@TestTargetClass(MatrixCursor.class)
+public class MatrixCursorTest extends TestCase {
+    private static final String COLUMN0_NAME = "column0";
+
+    private static final String COLUMN1_NAME = "column1";
+
+    private static final String COLUMN2_NAME = "column2";
+
+    private static final int COLUMN0_INDEX = 0;
+
+    private static final int COLUMN1_INDEX = 1;
+
+    private static final int COLUMN2_INDEX = 2;
+
+    private MatrixCursor mMatrixCursor;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mMatrixCursor = new MatrixCursor(new String[] { COLUMN0_NAME, COLUMN1_NAME, COLUMN2_NAME });
+    }
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "MatrixCursor",
+            args = {java.lang.String[].class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "MatrixCursor",
+            args = {java.lang.String[].class, int.class}
+        )
+    })
+    public void testMatrixCursor() {
+        new MatrixCursor(new String[0]);
+
+        new MatrixCursor(new String[] { COLUMN0_NAME }, 10);
+    }
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "newRow",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "isNull",
+            args = {int.class}
+        )
+    })
+    public void testNewRow() {
+        assertEquals(0, mMatrixCursor.getCount());
+        RowBuilder builder = mMatrixCursor.newRow();
+        assertNotNull(builder);
+        assertEquals(1, mMatrixCursor.getCount());
+        mMatrixCursor.moveToFirst();
+        assertTrue(mMatrixCursor.isNull(COLUMN0_INDEX));
+        assertTrue(mMatrixCursor.isNull(COLUMN1_INDEX));
+        assertTrue(mMatrixCursor.isNull(COLUMN2_INDEX));
+
+        builder.add(Integer.MIN_VALUE);
+        assertFalse(mMatrixCursor.isNull(COLUMN0_INDEX));
+        assertEquals(Integer.MIN_VALUE, mMatrixCursor.getInt(COLUMN0_INDEX));
+        assertTrue(mMatrixCursor.isNull(COLUMN1_INDEX));
+        assertTrue(mMatrixCursor.isNull(COLUMN2_INDEX));
+
+        builder.add(0);
+        assertFalse(mMatrixCursor.isNull(COLUMN0_INDEX));
+        assertEquals(Integer.MIN_VALUE, mMatrixCursor.getInt(COLUMN0_INDEX));
+        assertFalse(mMatrixCursor.isNull(COLUMN1_INDEX));
+        assertEquals(0, mMatrixCursor.getInt(COLUMN1_INDEX));
+        assertTrue(mMatrixCursor.isNull(COLUMN2_INDEX));
+
+        builder.add(Integer.MAX_VALUE);
+        assertFalse(mMatrixCursor.isNull(COLUMN0_INDEX));
+        assertEquals(Integer.MIN_VALUE, mMatrixCursor.getInt(COLUMN0_INDEX));
+        assertFalse(mMatrixCursor.isNull(COLUMN1_INDEX));
+        assertEquals(0, mMatrixCursor.getInt(COLUMN1_INDEX));
+        assertFalse(mMatrixCursor.isNull(COLUMN2_INDEX));
+        assertEquals(Integer.MAX_VALUE, mMatrixCursor.getInt(COLUMN2_INDEX));
+
+        try {
+            builder.add(1);
+            fail("Should throw CursorIndexOutOfBoundsException when adding too many values");
+        } catch (CursorIndexOutOfBoundsException e) {
+            // expected
+        }
+
+        assertNotNull(mMatrixCursor.newRow());
+        assertEquals(2, mMatrixCursor.getCount());
+    }
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "addRow",
+            args = {java.lang.Object[].class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "addRow",
+            args = {java.lang.Iterable.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getCount",
+            args = {}
+        )
+    })
+    public void testAddRow() {
+        assertEquals(0, mMatrixCursor.getCount());
+
+        mMatrixCursor.addRow(new Long[] { Long.MIN_VALUE, 0l, Long.MAX_VALUE });
+        assertEquals(1, mMatrixCursor.getCount());
+        mMatrixCursor.moveToLast();
+        assertFalse(mMatrixCursor.isNull(COLUMN0_INDEX));
+        assertEquals(Long.MIN_VALUE, mMatrixCursor.getLong(COLUMN0_INDEX));
+        assertFalse(mMatrixCursor.isNull(COLUMN1_INDEX));
+        assertEquals(0, mMatrixCursor.getLong(COLUMN1_INDEX));
+        assertFalse(mMatrixCursor.isNull(COLUMN2_INDEX));
+        assertEquals(Long.MAX_VALUE, mMatrixCursor.getLong(COLUMN2_INDEX));
+
+        try {
+            mMatrixCursor.addRow(new Long[2]);
+            fail("Should throw IllegalArgumentException if values does not have the same length "
+                    + "as the columns");
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+
+        try {
+            mMatrixCursor.addRow(new Long[4]);
+            fail("Should throw IllegalArgumentException if values does not have the same length "
+                    + "as the columns");
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+
+        ArrayList<String> values = new ArrayList<String>();
+        values.add("value0");
+        values.add("value1");
+        try {
+            mMatrixCursor.addRow(values);
+            fail("Should throw IllegalArgumentException if values does not have the same length "
+                    + "as the columns");
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+
+        values.add("value2");
+        mMatrixCursor.addRow(values);
+        assertEquals(2, mMatrixCursor.getCount());
+        mMatrixCursor.moveToLast();
+        assertFalse(mMatrixCursor.isNull(COLUMN0_INDEX));
+        assertEquals("value0", mMatrixCursor.getString(COLUMN0_INDEX));
+        assertFalse(mMatrixCursor.isNull(COLUMN1_INDEX));
+        assertEquals("value1", mMatrixCursor.getString(COLUMN1_INDEX));
+        assertFalse(mMatrixCursor.isNull(COLUMN2_INDEX));
+        assertEquals("value2", mMatrixCursor.getString(COLUMN2_INDEX));
+
+        values.add("value3");
+        try {
+            mMatrixCursor.addRow(values);
+            fail("Should throw IllegalArgumentException if values does not have the same length "
+                    + "as the columns");
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "getColumnNames",
+        args = {}
+    )
+    public void testGetColumnNames() {
+        String[] names = mMatrixCursor.getColumnNames();
+        assertEquals(3, names.length);
+        assertEquals(COLUMN0_NAME, names[0]);
+        assertEquals(COLUMN1_NAME, names[1]);
+        assertEquals(COLUMN2_NAME, names[2]);
+
+        // the following methods from super class will call {@link MatrixCursor#getColumnNames()}
+        assertEquals(COLUMN0_INDEX, mMatrixCursor.getColumnIndex(COLUMN0_NAME));
+        assertEquals(COLUMN1_INDEX, mMatrixCursor.getColumnIndex(COLUMN1_NAME));
+        assertEquals(COLUMN2_INDEX, mMatrixCursor.getColumnIndex(COLUMN2_NAME));
+    }
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getString",
+            args = {int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getShort",
+            args = {int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getInt",
+            args = {int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getLong",
+            args = {int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getFloat",
+            args = {int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getDouble",
+            args = {int.class}
+        )
+    })
+    public void testGetters() {
+        mMatrixCursor.addRow(new Short[] { Short.MIN_VALUE, 0, Short.MAX_VALUE });
+        mMatrixCursor.moveToLast();
+        assertFalse(mMatrixCursor.isNull(COLUMN0_INDEX));
+        assertFalse(mMatrixCursor.isNull(COLUMN1_INDEX));
+        assertFalse(mMatrixCursor.isNull(COLUMN2_INDEX));
+
+        // numbers can be converted to each other and strings
+        assertEquals(Short.MIN_VALUE, mMatrixCursor.getShort(COLUMN0_INDEX));
+        assertEquals(0, mMatrixCursor.getShort(COLUMN1_INDEX));
+        assertEquals(Short.MAX_VALUE, mMatrixCursor.getShort(COLUMN2_INDEX));
+
+        assertEquals(Short.MIN_VALUE, mMatrixCursor.getInt(COLUMN0_INDEX));
+        assertEquals(0, mMatrixCursor.getInt(COLUMN1_INDEX));
+        assertEquals(Short.MAX_VALUE, mMatrixCursor.getInt(COLUMN2_INDEX));
+
+        assertEquals(Short.MIN_VALUE, mMatrixCursor.getLong(COLUMN0_INDEX));
+        assertEquals(0, mMatrixCursor.getLong(COLUMN1_INDEX));
+        assertEquals(Short.MAX_VALUE, mMatrixCursor.getLong(COLUMN2_INDEX));
+
+        assertEquals(-32768f, mMatrixCursor.getFloat(COLUMN0_INDEX), 0.0f);
+        assertEquals(0f, mMatrixCursor.getFloat(COLUMN1_INDEX), 0.0f);
+        assertEquals(32767f, mMatrixCursor.getFloat(COLUMN2_INDEX), 0.0f);
+
+        assertEquals(-32768d, mMatrixCursor.getDouble(COLUMN0_INDEX), 0.0d);
+        assertEquals(0d, mMatrixCursor.getDouble(COLUMN1_INDEX), 0.0d);
+        assertEquals(32767d, mMatrixCursor.getDouble(COLUMN2_INDEX), 0.0d);
+
+        assertEquals("-32768", mMatrixCursor.getString(COLUMN0_INDEX));
+        assertEquals("0", mMatrixCursor.getString(COLUMN1_INDEX));
+        assertEquals("32767", mMatrixCursor.getString(COLUMN2_INDEX));
+
+        mMatrixCursor.addRow(new Double[] { Double.MIN_VALUE, 0d, Double.MAX_VALUE });
+        mMatrixCursor.moveToLast();
+        assertFalse(mMatrixCursor.isNull(COLUMN0_INDEX));
+        assertFalse(mMatrixCursor.isNull(COLUMN1_INDEX));
+        assertFalse(mMatrixCursor.isNull(COLUMN2_INDEX));
+
+        // numbers can be converted to each other but loose accuracy
+        assertEquals(0, mMatrixCursor.getShort(COLUMN0_INDEX));
+        assertEquals(0, mMatrixCursor.getShort(COLUMN1_INDEX));
+        assertEquals(-1, mMatrixCursor.getShort(COLUMN2_INDEX));
+
+        assertEquals(0, mMatrixCursor.getInt(COLUMN0_INDEX));
+        assertEquals(0, mMatrixCursor.getInt(COLUMN1_INDEX));
+        assertEquals(Integer.MAX_VALUE, mMatrixCursor.getInt(COLUMN2_INDEX));
+
+        assertEquals(0, mMatrixCursor.getLong(COLUMN0_INDEX));
+        assertEquals(0, mMatrixCursor.getLong(COLUMN1_INDEX));
+        assertEquals(Long.MAX_VALUE, mMatrixCursor.getLong(COLUMN2_INDEX));
+
+        assertEquals(0.0f, mMatrixCursor.getFloat(COLUMN0_INDEX), 0.0f);
+        assertEquals(0f, mMatrixCursor.getFloat(COLUMN1_INDEX), 0.0f);
+        assertEquals(Float.POSITIVE_INFINITY, mMatrixCursor.getFloat(COLUMN2_INDEX), 0.0f);
+
+        assertEquals(Double.MIN_VALUE, mMatrixCursor.getDouble(COLUMN0_INDEX), 0.0d);
+        assertEquals(0d, mMatrixCursor.getDouble(COLUMN1_INDEX), 0.0d);
+        assertEquals(Double.MAX_VALUE, mMatrixCursor.getDouble(COLUMN2_INDEX), 0.0d);
+
+        assertEquals(Double.toString(Double.MIN_VALUE), mMatrixCursor.getString(COLUMN0_INDEX));
+        assertEquals(Double.toString(0d), mMatrixCursor.getString(COLUMN1_INDEX));
+        assertEquals(Double.toString(Double.MAX_VALUE), mMatrixCursor.getString(COLUMN2_INDEX));
+
+        mMatrixCursor.addRow(new String[] { "-1", "0", "1" });
+        mMatrixCursor.moveToLast();
+        assertFalse(mMatrixCursor.isNull(COLUMN0_INDEX));
+        assertFalse(mMatrixCursor.isNull(COLUMN1_INDEX));
+        assertFalse(mMatrixCursor.isNull(COLUMN2_INDEX));
+        // strings can be converted to numbers
+        assertEquals(-1, mMatrixCursor.getShort(COLUMN0_INDEX));
+        assertEquals(0, mMatrixCursor.getShort(COLUMN1_INDEX));
+        assertEquals(1, mMatrixCursor.getShort(COLUMN2_INDEX));
+
+        assertEquals(-1, mMatrixCursor.getInt(COLUMN0_INDEX));
+        assertEquals(0, mMatrixCursor.getInt(COLUMN1_INDEX));
+        assertEquals(1, mMatrixCursor.getInt(COLUMN2_INDEX));
+
+        assertEquals(-1, mMatrixCursor.getLong(COLUMN0_INDEX));
+        assertEquals(0, mMatrixCursor.getLong(COLUMN1_INDEX));
+        assertEquals(1, mMatrixCursor.getLong(COLUMN2_INDEX));
+
+        assertEquals(-1f, mMatrixCursor.getFloat(COLUMN0_INDEX), 0.0f);
+        assertEquals(0f, mMatrixCursor.getFloat(COLUMN1_INDEX), 0.0f);
+        assertEquals(1f, mMatrixCursor.getFloat(COLUMN2_INDEX), 0.0f);
+
+        assertEquals(-1d, mMatrixCursor.getDouble(COLUMN0_INDEX), 0.0d);
+        assertEquals(0f, mMatrixCursor.getDouble(COLUMN1_INDEX), 0.0d);
+        assertEquals(1d, mMatrixCursor.getDouble(COLUMN2_INDEX), 0.0d);
+
+        assertEquals("-1", mMatrixCursor.getString(COLUMN0_INDEX));
+        assertEquals("0", mMatrixCursor.getString(COLUMN1_INDEX));
+        assertEquals("1", mMatrixCursor.getString(COLUMN2_INDEX));
+
+        mMatrixCursor.addRow(new String[] { "1.0E2", "0xff", "not number" });
+        mMatrixCursor.moveToLast();
+        assertFalse(mMatrixCursor.isNull(COLUMN0_INDEX));
+        assertFalse(mMatrixCursor.isNull(COLUMN1_INDEX));
+        assertFalse(mMatrixCursor.isNull(COLUMN2_INDEX));
+        assertEquals("1.0E2", mMatrixCursor.getString(COLUMN0_INDEX));
+        assertEquals("0xff", mMatrixCursor.getString(COLUMN1_INDEX));
+        assertEquals("not number", mMatrixCursor.getString(COLUMN2_INDEX));
+
+        assertEquals(100f, mMatrixCursor.getFloat(COLUMN0_INDEX), 0.0f);
+        // strings can not be converted to numbers
+        try {
+            mMatrixCursor.getShort(COLUMN1_INDEX);
+            fail("Should throw NumberFormatException");
+        } catch (NumberFormatException e) {
+            // expected
+        }
+
+        try {
+            mMatrixCursor.getShort(COLUMN2_INDEX);
+            fail("Should throw NumberFormatException");
+        } catch (NumberFormatException e) {
+            // expected
+        }
+    }
+}
diff --git a/tests/tests/database/src/android/database/cts/MatrixCursor_RowBuilderTest.java b/tests/tests/database/src/android/database/cts/MatrixCursor_RowBuilderTest.java
new file mode 100644
index 0000000..98ed135
--- /dev/null
+++ b/tests/tests/database/src/android/database/cts/MatrixCursor_RowBuilderTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2008 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.database.cts;
+
+import dalvik.annotation.TestLevel;
+import dalvik.annotation.TestTargetClass;
+import dalvik.annotation.TestTargetNew;
+
+import android.database.CursorIndexOutOfBoundsException;
+import android.database.MatrixCursor;
+import android.database.MatrixCursor.RowBuilder;
+
+import junit.framework.TestCase;
+
+@TestTargetClass(RowBuilder.class)
+public class MatrixCursor_RowBuilderTest extends TestCase {
+    private static final int COLUMN0_INDEX = 0;
+
+    private static final int COLUMN1_INDEX = 1;
+
+    private static final int COLUMN2_INDEX = 2;
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "add",
+        args = {java.lang.Object.class}
+    )
+    public void testAdd() {
+        MatrixCursor cursor = new MatrixCursor(new String[] { "column0", "column1", "column2" });
+        assertEquals(0, cursor.getCount());
+        RowBuilder builder = cursor.newRow();
+        assertNotNull(builder);
+        assertEquals(1, cursor.getCount());
+        cursor.moveToFirst();
+        assertTrue(cursor.isNull(COLUMN0_INDEX));
+        assertTrue(cursor.isNull(COLUMN1_INDEX));
+        assertTrue(cursor.isNull(COLUMN2_INDEX));
+
+        builder.add(Integer.MIN_VALUE);
+        assertFalse(cursor.isNull(COLUMN0_INDEX));
+        assertEquals(Integer.MIN_VALUE, cursor.getInt(COLUMN0_INDEX));
+        assertTrue(cursor.isNull(COLUMN1_INDEX));
+        assertTrue(cursor.isNull(COLUMN2_INDEX));
+
+        builder.add(0);
+        assertFalse(cursor.isNull(COLUMN0_INDEX));
+        assertEquals(Integer.MIN_VALUE, cursor.getInt(COLUMN0_INDEX));
+        assertFalse(cursor.isNull(COLUMN1_INDEX));
+        assertEquals(0, cursor.getInt(COLUMN1_INDEX));
+        assertTrue(cursor.isNull(COLUMN2_INDEX));
+
+        builder.add(Integer.MAX_VALUE);
+        assertFalse(cursor.isNull(COLUMN0_INDEX));
+        assertEquals(Integer.MIN_VALUE, cursor.getInt(COLUMN0_INDEX));
+        assertFalse(cursor.isNull(COLUMN1_INDEX));
+        assertEquals(0, cursor.getInt(COLUMN1_INDEX));
+        assertFalse(cursor.isNull(COLUMN2_INDEX));
+        assertEquals(Integer.MAX_VALUE, cursor.getInt(COLUMN2_INDEX));
+
+        try {
+            builder.add(1);
+            fail("Should throw CursorIndexOutOfBoundsException when adding too many values");
+        } catch (CursorIndexOutOfBoundsException e) {
+            // expected
+        }
+
+        assertNotNull(cursor.newRow());
+        assertEquals(2, cursor.getCount());
+    }
+}
diff --git a/tests/tests/database/src/android/database/cts/MergeCursorTest.java b/tests/tests/database/src/android/database/cts/MergeCursorTest.java
new file mode 100644
index 0000000..25e6aec
--- /dev/null
+++ b/tests/tests/database/src/android/database/cts/MergeCursorTest.java
@@ -0,0 +1,516 @@
+/*
+ * Copyright (C) 2008 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.database.cts;
+
+import dalvik.annotation.TestLevel;
+import dalvik.annotation.TestTargetClass;
+import dalvik.annotation.TestTargetNew;
+import dalvik.annotation.TestTargets;
+
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.database.DataSetObserver;
+import android.database.MergeCursor;
+import android.database.StaleDataException;
+import android.database.sqlite.SQLiteDatabase;
+import android.test.AndroidTestCase;
+
+import java.io.File;
+import java.util.Arrays;
+
+@TestTargetClass(android.database.MergeCursor.class)
+public class MergeCursorTest extends AndroidTestCase {
+    private final int NUMBER_1_COLUMN_INDEX = 1;
+    private static final String TABLE1_NAME = "test1";
+    private static final String TABLE2_NAME = "test2";
+    private static final String TABLE3_NAME = "test3";
+    private static final String TABLE4_NAME = "test4";
+    private static final String TABLE5_NAME = "test5";
+    private static final String COLUMN_FOR_NULL_TEST = "Null Field";
+
+    private SQLiteDatabase mDatabase;
+    private File mDatabaseFile;
+
+    Cursor[] mCursors = null;
+    private static final String TABLE1_COLUMNS = " number_1 INTEGER";
+    private static final String TABLE2_COLUMNS = " number_1 INTEGER, number_2 INTEGER";
+    private static final String TABLE3_COLUMNS = " text_1 TEXT, number_3 INTEGER, number_4 REAL";
+    private static final String TABLE2_COLUMN_NAMES = "_id,number_1,number_2";
+    private static final String TABLE3_COLUMN_NAMES = "_id,text_1,number_3,number_4";
+    private static final String TEXT_COLUMN_NAME = "text_1";
+    private static final int TABLE2_COLUMN_COUNT = 3;
+    private static final int TABLE3_COLUMN_COUNT = 4;
+    private static final int DEFAULT_TABLE_VALUE_BEGINS = 1;
+    private static final int MAX_VALUE = 10;
+    private static final int HALF_VALUE = MAX_VALUE / 2;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        setupDatabase();
+        mCursors = new Cursor[2];
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        for (int i = 0; i < mCursors.length; i++) {
+            if (null != mCursors[i]) {
+                mCursors[i].close();
+            }
+        }
+        mDatabase.close();
+        mDatabaseFile.delete();
+        super.tearDown();
+    }
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "MergeCursor",
+            args = {android.database.Cursor[].class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getCount",
+            args = {}
+        )
+    })
+    public void testConstructor() {
+        // If each item of mCursors are null, count will be zero.
+        MergeCursor mergeCursor = new MergeCursor(mCursors);
+        assertEquals(0, mergeCursor.getCount());
+
+        createCursors();
+
+        // if the items are not null, getCount() will return the sum of all cursors' count.
+        mergeCursor = new MergeCursor(mCursors);
+        assertEquals(mCursors[0].getCount() + mCursors[1].getCount(), mergeCursor.getCount());
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        method = "onMove",
+        args = {int.class, int.class}
+    )
+    public void testOnMove() {
+        createCursors();
+        MergeCursor mergeCursor = new MergeCursor(mCursors);
+        for (int i = 0; i < MAX_VALUE; i++) {
+            mergeCursor.moveToNext();
+            //From 1~5, mCursor should be in mCursors[0], larger than 5, it should be in
+            //mCursors[1].
+            assertEquals(i + 1, mergeCursor.getInt(NUMBER_1_COLUMN_INDEX));
+        }
+    }
+
+    @TestTargetNew(
+        level = TestLevel.COMPLETE,
+        notes = "Test method related to column infos during MergeCursor is switching" +
+                " between different single cursors",
+        method = "getColumnNames",
+        args = {}
+    )
+    public void testCursorSwiching() {
+        mDatabase.execSQL("CREATE TABLE " + TABLE5_NAME + " (_id INTEGER PRIMARY KEY,"
+                + TABLE3_COLUMNS + ");");
+        String sql = "INSERT INTO " + TABLE5_NAME + " (" + TEXT_COLUMN_NAME + ") VALUES ('TEXT')";
+        mDatabase.execSQL(sql);
+
+        Cursor[] cursors = new Cursor[2];
+        cursors[0] = mDatabase.query(TABLE5_NAME, null, null, null, null, null, null);
+        assertEquals(1, cursors[0].getCount());
+        createCursors();
+        cursors[1] = mCursors[1];
+        assertTrue(cursors[1].getCount() > 0);
+        MergeCursor mergeCursor = new MergeCursor(cursors);
+        // MergeCursor should points to cursors[0] after moveToFirst.
+        mergeCursor.moveToFirst();
+
+        String[] tableColumns = TABLE3_COLUMN_NAMES.split("[,]");
+        assertEquals(TABLE3_COLUMN_COUNT, mergeCursor.getColumnCount());
+        assertTrue(Arrays.equals(tableColumns, mergeCursor.getColumnNames()));
+
+        // MergeCursor should points to cursors[1] moveToNext.
+        mergeCursor.moveToNext();
+        tableColumns = TABLE2_COLUMN_NAMES.split("[,]");
+        assertEquals(TABLE2_COLUMN_COUNT, mergeCursor.getColumnCount());
+        assertTrue(Arrays.equals(tableColumns, mergeCursor.getColumnNames()));
+    }
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getBlob",
+            args = {int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getDouble",
+            args = {int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getFloat",
+            args = {int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getInt",
+            args = {int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getLong",
+            args = {int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getShort",
+            args = {int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getString",
+            args = {int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "isNull",
+            args = {int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "onMove",
+            args = {int.class, int.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getColumnNames",
+            args = {}
+        )
+    })
+    public void testGetValues() {
+        byte NUMBER_BLOB_UNIT = 99;
+        String[] TEST_STRING = new String[] {"Test String1", "Test String2"};
+        String[] tableNames = new String[] {TABLE3_NAME, TABLE4_NAME};
+
+        final double NUMBER_DOUBLE = Double.MAX_VALUE;
+        final double NUMBER_FLOAT = (float) NUMBER_DOUBLE;
+        final long NUMBER_LONG_INTEGER = (long) 0xaabbccddffL;
+        final long NUMBER_INTEGER = (int) NUMBER_LONG_INTEGER;
+        final long NUMBER_SHORT = (short) NUMBER_INTEGER;
+
+        // create tables
+        byte[][] originalBlobs = new byte[2][];
+        for (int i = 0; i < 2; i++) {
+            // insert blob and other values
+            originalBlobs[i] = new byte[1000];
+            Arrays.fill(originalBlobs[i], (byte) (NUMBER_BLOB_UNIT - i));
+            buildDatabaseWithTestValues(TEST_STRING[i], NUMBER_DOUBLE - i, NUMBER_LONG_INTEGER - i,
+                    originalBlobs[i], tableNames[i]);
+            // Get cursors.
+            mCursors[i] = mDatabase.query(tableNames[i], null, null, null, null, null, null);
+        }
+
+        MergeCursor mergeCursor = new MergeCursor(mCursors);
+        assertEquals(4, mergeCursor.getCount());
+        String[] testColumns = new String[] {"_id", "string_text", "double_number", "int_number",
+                    "blob_data"};
+        // Test getColumnNames().
+        assertTrue(Arrays.equals(testColumns, mergeCursor.getColumnNames()));
+
+        int columnBlob = mCursors[0].getColumnIndexOrThrow("blob_data");
+        int columnString = mCursors[0].getColumnIndexOrThrow("string_text");
+        int columnDouble = mCursors[0].getColumnIndexOrThrow("double_number");
+        int columnInteger = mCursors[0].getColumnIndexOrThrow("int_number");
+
+        // Test values.
+        for (int i = 0; i < 2; i++) {
+            mergeCursor.moveToNext();
+            assertEquals(5, mergeCursor.getColumnCount());
+
+            // Test getting value methods.
+            byte[] targetBlob = mergeCursor.getBlob(columnBlob);
+            assertTrue(Arrays.equals(originalBlobs[i], targetBlob));
+
+            assertEquals(TEST_STRING[i], mergeCursor.getString(columnString));
+            assertEquals(NUMBER_DOUBLE - i, mergeCursor.getDouble(columnDouble), 0.000000000001);
+            assertEquals(NUMBER_FLOAT - i, mergeCursor.getFloat(columnDouble), 0.000000000001f);
+            assertEquals(NUMBER_LONG_INTEGER - i, mergeCursor.getLong(columnInteger));
+            assertEquals(NUMBER_INTEGER - i, mergeCursor.getInt(columnInteger));
+            assertEquals(NUMBER_SHORT - i, mergeCursor.getShort(columnInteger));
+
+            // Test isNull(int).
+            assertFalse(mergeCursor.isNull(columnBlob));
+            mergeCursor.moveToNext();
+            assertEquals(COLUMN_FOR_NULL_TEST, mergeCursor.getString(columnString));
+            assertTrue(mergeCursor.isNull(columnBlob));
+        }
+    }
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "registerContentObserver",
+            args = {android.database.ContentObserver.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "unregisterContentObserver",
+            args = {android.database.ContentObserver.class}
+        )
+    })
+    public void testContentObsererOperations() throws IllegalStateException {
+        createCursors();
+        MergeCursor mergeCursor = new MergeCursor(mCursors);
+        ContentObserver observer = new ContentObserver(null) {};
+
+        // Can't unregister a Observer before it has been registered.
+        try {
+            mergeCursor.unregisterContentObserver(observer);
+            fail("testUnregisterContentObserver failed");
+        } catch (IllegalStateException e) {
+            // expected
+        }
+
+        mergeCursor.registerContentObserver(observer);
+
+        // Can't register a same observer twice before unregister it.
+        try {
+            mergeCursor.registerContentObserver(observer);
+            fail("testRegisterContentObserver failed");
+        } catch (IllegalStateException e) {
+            // expected
+        }
+
+        mergeCursor.unregisterContentObserver(observer);
+        // one Observer can be registered again after it has been unregistered.
+        mergeCursor.registerContentObserver(observer);
+
+        mergeCursor.unregisterContentObserver(observer);
+
+        try {
+            mergeCursor.unregisterContentObserver(observer);
+            fail("testUnregisterContentObserver failed");
+        } catch (IllegalStateException e) {
+            // expected
+        }
+    }
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "deactivate",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "registerDataSetObserver",
+            args = {android.database.DataSetObserver.class}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "unregisterDataSetObserver",
+            args = {android.database.DataSetObserver.class}
+        )
+    })
+    public void testDeactivate() throws IllegalStateException {
+        createCursors();
+        MergeCursor mergeCursor = new MergeCursor(mCursors);
+        MockObserver observer = new MockObserver();
+
+        // one DataSetObserver can't unregistered before it had been registered.
+        try {
+            mergeCursor.unregisterDataSetObserver(observer);
+            fail("testUnregisterDataSetObserver failed");
+        } catch (IllegalStateException e) {
+            // expected
+        }
+
+        // Before registering, observer can't be notified.
+        assertFalse(observer.hasInvalidated());
+        mergeCursor.moveToLast();
+        mergeCursor.deactivate();
+        assertFalse(observer.hasInvalidated());
+
+        // Test with registering DataSetObserver
+        assertTrue(mergeCursor.requery());
+        mergeCursor.registerDataSetObserver(observer);
+        assertFalse(observer.hasInvalidated());
+        mergeCursor.moveToLast();
+        assertEquals(MAX_VALUE, mergeCursor.getInt(NUMBER_1_COLUMN_INDEX));
+        mergeCursor.deactivate();
+        // deactivate method can invoke invalidate() method, can be observed by DataSetObserver.
+        assertTrue(observer.hasInvalidated());
+        // After deactivating, the cursor can not provide values from database record.
+        try {
+            mergeCursor.getInt(NUMBER_1_COLUMN_INDEX);
+            fail("After deactivating, cursor cannot execute getting value operations.");
+        } catch (StaleDataException e) {
+            // expected
+        }
+
+        // Can't register a same observer twice before unregister it.
+        try {
+            mergeCursor.registerDataSetObserver(observer);
+            fail("testRegisterDataSetObserver failed");
+        } catch (IllegalStateException e) {
+            // expected
+        }
+
+        // After runegistering, observer can't be notified.
+        mergeCursor.unregisterDataSetObserver(observer);
+        observer.resetStatus();
+        assertFalse(observer.hasInvalidated());
+        mergeCursor.moveToLast();
+        mergeCursor.deactivate();
+        assertFalse(observer.hasInvalidated());
+
+        // one DataSetObserver can't be unregistered twice continuously.
+        try {
+            mergeCursor.unregisterDataSetObserver(observer);
+            fail("testUnregisterDataSetObserver failed");
+        } catch (IllegalStateException e) {
+            // expected
+        }
+    }
+
+    @TestTargets({
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "requery",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "getCount",
+            args = {}
+        ),
+        @TestTargetNew(
+            level = TestLevel.COMPLETE,
+            method = "close",
+            args = {}
+        )
+    })
+    public void testRequery() {
+        final String TEST_VALUE1 = Integer.toString(MAX_VALUE + 1);
+        final String TEST_VALUE2 = Integer.toString(MAX_VALUE + 2);
+        createCursors();
+        MergeCursor mergeCursor = new MergeCursor(mCursors);
+        int cursor1Count = mCursors[0].getCount();
+        int cursor2Count = mCursors[0].getCount();
+
+        mDatabase.execSQL("INSERT INTO " + TABLE1_NAME + " (number_1) VALUES ('" + TEST_VALUE1
+                + "');");
+        assertEquals(cursor1Count + cursor2Count, mergeCursor.getCount());
+        assertTrue(mergeCursor.requery());
+        cursor1Count += 1;
+        assertEquals(cursor1Count + cursor2Count, mergeCursor.getCount());
+        mDatabase.execSQL("INSERT INTO " + TABLE2_NAME + " (number_1) VALUES ('" + TEST_VALUE2
+                + "');");
+        cursor2Count += 1;
+        assertTrue(mergeCursor.requery());
+        assertEquals(cursor1Count + cursor2Count, mergeCursor.getCount());
+
+        mergeCursor.close();
+        assertFalse(mergeCursor.requery());
+    }
+
+    private void buildDatabaseWithTestValues(String text, double doubleNumber, long intNumber,
+            byte[] blob, String tablename) {
+        Object[] args = new Object[4];
+        args[0] = text;
+        args[1] = doubleNumber;
+        args[2] = intNumber;
+        args[3] = blob;
+        mDatabase.execSQL("CREATE TABLE " + tablename + " (_id INTEGER PRIMARY KEY,"
+                + "string_text TEXT, double_number REAL, int_number INTEGER, blob_data BLOB);");
+
+        // Insert record in giving table.
+        String sql = "INSERT INTO " + tablename + " (string_text, double_number, int_number,"
+                + " blob_data) VALUES (?,?,?,?)";
+        mDatabase.execSQL(sql, args);
+        // insert null blob.
+        sql = "INSERT INTO " + tablename + " (string_text) VALUES ('" + COLUMN_FOR_NULL_TEST + "')";
+        mDatabase.execSQL(sql);
+    }
+
+    private void setupDatabase() {
+        mDatabaseFile = new File("/sqlite_stmt_journals", "database_test.db");
+        if (mDatabaseFile.exists()) {
+            mDatabaseFile.delete();
+        }
+        mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null);
+        assertNotNull(mDatabaseFile);
+        createTable(TABLE1_NAME, TABLE1_COLUMNS);
+        createTable(TABLE2_NAME, TABLE2_COLUMNS);
+        addValuesIntoTable(TABLE1_NAME, DEFAULT_TABLE_VALUE_BEGINS, HALF_VALUE);
+        addValuesIntoTable(TABLE2_NAME, HALF_VALUE + 1, MAX_VALUE);
+    }
+
+    private void createTable(String tableName, String columnNames) {
+        String sql = "Create TABLE " + tableName + " (_id INTEGER PRIMARY KEY, " + columnNames
+                + " );";
+        mDatabase.execSQL(sql);
+    }
+
+    private void addValuesIntoTable(String tableName, int start, int end) {
+        for (int i = start; i <= end; i++) {
+            mDatabase.execSQL("INSERT INTO " + tableName + "(number_1) VALUES ('"
+                    + i + "');");
+        }
+    }
+
+    private Cursor getCursor(String tableName, String selection, String[] columnNames) {
+        return mDatabase.query(tableName, columnNames, selection, null, null, null, "number_1");
+    }
+
+    private void createCursors() {
+        mCursors[0] = getCursor(TABLE1_NAME, null, null);
+        mCursors[1] = getCursor(TABLE2_NAME, null, null);
+    }
+
+    private class MockObserver extends DataSetObserver {
+        private boolean mHasChanged = false;
+        private boolean mHasInvalidated = false;
+
+        @Override
+        public void onChanged() {
+            super.onChanged();
+            mHasChanged = true;
+        }
+
+        @Override
+        public void onInvalidated() {
+            super.onInvalidated();
+            mHasInvalidated = true;
+        }
+
+        public void resetStatus() {
+            mHasChanged = false;
+            mHasInvalidated = false;
+        }
+
+        public boolean hasChanged() {
+            return mHasChanged;
+        }
+
+        public boolean hasInvalidated () {
+            return mHasInvalidated;
+        }
+    }
+}