Merge "Camera2: Add reprocess timestamps test" into mnc-dev
diff --git a/tests/tests/content/src/android/content/cts/ContentResolverTest.java b/tests/tests/content/src/android/content/cts/ContentResolverTest.java
index 4176da3..628f956 100644
--- a/tests/tests/content/src/android/content/cts/ContentResolverTest.java
+++ b/tests/tests/content/src/android/content/cts/ContentResolverTest.java
@@ -58,6 +58,11 @@
     private static final Uri SELF_URI = Uri.parse("content://" + AUTHORITY + "/self/");
     private static final Uri CRASH_URI = Uri.parse("content://" + AUTHORITY + "/crash/");
 
+    private static final Uri LEVEL1_URI = Uri.parse("content://" + AUTHORITY + "/level/");
+    private static final Uri LEVEL2_URI = Uri.parse("content://" + AUTHORITY + "/level/child");
+    private static final Uri LEVEL3_URI = Uri.parse("content://" + AUTHORITY
+            + "/level/child/grandchild/");
+
     private static final String REMOTE_AUTHORITY = "remotectstest";
     private static final Uri REMOTE_TABLE1_URI = Uri.parse("content://"
                 + REMOTE_AUTHORITY + "/testtable1/");
@@ -88,7 +93,7 @@
         mContext = getContext();
         mContentResolver = mContext.getContentResolver();
 
-        android.provider.Settings.System.putInt(mContentResolver, "__cts_crash_on_launch", 0);
+        MockContentProvider.setCrashOnLaunch(mContext, false);
 
         // add three rows to database when every test case start.
         ContentValues values = new ContentValues();
@@ -151,13 +156,12 @@
             fail("Content provider process is not gone!");
         }
         try {
-            android.provider.Settings.System.putInt(mContentResolver, "__cts_crash_on_launch", 1);
+            MockContentProvider.setCrashOnLaunch(mContext, true);
             String type1 = mContentResolver.getType(REMOTE_TABLE1_URI);
-            assertEquals(android.provider.Settings.System.getInt(mContentResolver,
-                "__cts_crash_on_launch", 0), 0);
+            assertFalse(MockContentProvider.getCrashOnLaunch(mContext));
             assertTrue(type1.startsWith(ContentResolver.CURSOR_DIR_BASE_TYPE, 0));
         } finally {
-            android.provider.Settings.System.putInt(mContentResolver, "__cts_crash_on_launch", 0);
+            MockContentProvider.setCrashOnLaunch(mContext, false);
         }
     }
 
@@ -352,12 +356,11 @@
 
     public void testCrashingQuery() {
         try {
-            android.provider.Settings.System.putInt(mContentResolver, "__cts_crash_on_launch", 1);
+            MockContentProvider.setCrashOnLaunch(mContext, true);
             mCursor = mContentResolver.query(REMOTE_CRASH_URI, null, null, null, null);
-            assertEquals(android.provider.Settings.System.getInt(mContentResolver,
-                "__cts_crash_on_launch", 0), 0);
+            assertFalse(MockContentProvider.getCrashOnLaunch(mContext));
         } finally {
-            android.provider.Settings.System.putInt(mContentResolver, "__cts_crash_on_launch", 0);
+            MockContentProvider.setCrashOnLaunch(mContext, false);
         }
 
         assertNotNull(mCursor);
@@ -570,16 +573,15 @@
     public void testCrashingOpenAssetFileDescriptor() throws IOException {
         AssetFileDescriptor afd = null;
         try {
-            android.provider.Settings.System.putInt(mContentResolver, "__cts_crash_on_launch", 1);
+            MockContentProvider.setCrashOnLaunch(mContext, true);
             afd = mContentResolver.openAssetFileDescriptor(REMOTE_CRASH_URI, "rw");
-            assertEquals(android.provider.Settings.System.getInt(mContentResolver,
-                    "__cts_crash_on_launch", 0), 0);
+            assertFalse(MockContentProvider.getCrashOnLaunch(mContext));
             assertNotNull(afd);
             String str = consumeAssetFileDescriptor(afd);
             afd = null;
             assertEquals(str, "This is the openAssetFile test data!");
         } finally {
-            android.provider.Settings.System.putInt(mContentResolver, "__cts_crash_on_launch", 0);
+            MockContentProvider.setCrashOnLaunch(mContext, false);
             if (afd != null) {
                 afd.close();
             }
@@ -602,17 +604,16 @@
     public void testCrashingOpenTypedAssetFileDescriptor() throws IOException {
         AssetFileDescriptor afd = null;
         try {
-            android.provider.Settings.System.putInt(mContentResolver, "__cts_crash_on_launch", 1);
+            MockContentProvider.setCrashOnLaunch(mContext, true);
             afd = mContentResolver.openTypedAssetFileDescriptor(
                     REMOTE_CRASH_URI, "text/plain", null);
-            assertEquals(android.provider.Settings.System.getInt(mContentResolver,
-                    "__cts_crash_on_launch", 0), 0);
+            assertFalse(MockContentProvider.getCrashOnLaunch(mContext));
             assertNotNull(afd);
             String str = consumeAssetFileDescriptor(afd);
             afd = null;
             assertEquals(str, "This is the openTypedAssetFile test data!");
         } finally {
-            android.provider.Settings.System.putInt(mContentResolver, "__cts_crash_on_launch", 0);
+            MockContentProvider.setCrashOnLaunch(mContext, false);
             if (afd != null) {
                 afd.close();
             }
@@ -957,6 +958,53 @@
         }
     }
 
+    public void testRegisterContentObserverDescendantBehavior() throws Exception {
+        final MockContentObserver mco1 = new MockContentObserver();
+        final MockContentObserver mco2 = new MockContentObserver();
+
+        // Register one content observer with notifyDescendants set to false, and
+        // another with true.
+        mContentResolver.registerContentObserver(LEVEL2_URI, false, mco1);
+        mContentResolver.registerContentObserver(LEVEL2_URI, true, mco2);
+
+        // Initially nothing has happened.
+        assertFalse(mco1.hadOnChanged());
+        assertFalse(mco2.hadOnChanged());
+
+        // Fire a change with the exact URI.
+        // Should signal both observers due to exact match, notifyDescendants doesn't matter.
+        mContentResolver.notifyChange(LEVEL2_URI, null);
+        Thread.sleep(200);
+        assertTrue(mco1.hadOnChanged());
+        assertTrue(mco2.hadOnChanged());
+        mco1.reset();
+        mco2.reset();
+
+        // Fire a change with a descendant URI.
+        // Should only signal observer with notifyDescendants set to true.
+        mContentResolver.notifyChange(LEVEL3_URI, null);
+        Thread.sleep(200);
+        assertFalse(mco1.hadOnChanged());
+        assertTrue(mco2.hadOnChanged());
+        mco2.reset();
+
+        // Fire a change with an ancestor URI.
+        // Should signal both observers due to ancestry, notifyDescendants doesn't matter.
+        mContentResolver.notifyChange(LEVEL1_URI, null);
+        Thread.sleep(200);
+        assertTrue(mco1.hadOnChanged());
+        assertTrue(mco2.hadOnChanged());
+        mco1.reset();
+        mco2.reset();
+
+        // Fire a change with an unrelated URI.
+        // Should signal neither observer.
+        mContentResolver.notifyChange(TABLE1_URI, null);
+        Thread.sleep(200);
+        assertFalse(mco1.hadOnChanged());
+        assertFalse(mco2.hadOnChanged());
+    }
+
     public void testNotifyChange1() {
         final MockContentObserver mco = new MockContentObserver();
 
diff --git a/tests/tests/content/src/android/content/cts/MockContentProvider.java b/tests/tests/content/src/android/content/cts/MockContentProvider.java
index de82c0d..bddc82d 100644
--- a/tests/tests/content/src/android/content/cts/MockContentProvider.java
+++ b/tests/tests/content/src/android/content/cts/MockContentProvider.java
@@ -16,6 +16,7 @@
 
 package android.content.cts;
 
+import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
@@ -28,6 +29,7 @@
 import android.content.ContentUris;
 import android.content.ContentValues;
 import android.content.Context;
+import android.content.SharedPreferences;
 import android.content.UriMatcher;
 import android.content.ContentProvider.PipeDataWriter;
 import android.content.res.AssetFileDescriptor;
@@ -122,15 +124,7 @@
     @Override
     public boolean onCreate() {
         mOpenHelper = new DatabaseHelper(getContext(), mDbName);
-        if (android.provider.Settings.System.getInt(getContext().getContentResolver(),
-                "__cts_crash_on_launch", 0) != 0) {
-            // The test case wants us to crash our process on first launch.
-            // Well, okay then!
-            Log.i("MockContentProvider", "TEST IS CRASHING SELF, CROSS FINGERS!");
-            android.provider.Settings.System.putInt(getContext().getContentResolver(),
-                    "__cts_crash_on_launch", 0);
-            android.os.Process.killProcess(android.os.Process.myPid());
-        }
+        crashOnLaunchIfNeeded();
         return true;
     }
 
@@ -277,15 +271,7 @@
             break;
 
         case CRASH_ID:
-            if (android.provider.Settings.System.getInt(getContext().getContentResolver(),
-                    "__cts_crash_on_launch", 0) != 0) {
-                // The test case wants us to crash while querying.
-                // Well, okay then!
-                Log.i("MockContentProvider", "TEST IS CRASHING SELF, CROSS FINGERS!");
-                android.provider.Settings.System.putInt(getContext().getContentResolver(),
-                        "__cts_crash_on_launch", 0);
-                android.os.Process.killProcess(android.os.Process.myPid());
-            }
+            crashOnLaunchIfNeeded();
             qb.setTables("TestTable1");
             qb.setProjectionMap(CTSDBTABLE1_LIST_PROJECTION_MAP);
             break;
@@ -351,15 +337,7 @@
     public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
         switch (URL_MATCHER.match(uri)) {
             case CRASH_ID:
-                if (android.provider.Settings.System.getInt(getContext().getContentResolver(),
-                        "__cts_crash_on_launch", 0) != 0) {
-                    // The test case wants us to crash while querying.
-                    // Well, okay then!
-                    Log.i("MockContentProvider", "TEST IS CRASHING SELF, CROSS FINGERS!");
-                    android.provider.Settings.System.putInt(getContext().getContentResolver(),
-                            "__cts_crash_on_launch", 0);
-                    android.os.Process.killProcess(android.os.Process.myPid());
-                }
+                crashOnLaunchIfNeeded();
                 return new AssetFileDescriptor(
                         openPipeHelper(uri, null, null,
                                 "This is the openAssetFile test data!", this), 0,
@@ -375,15 +353,7 @@
             throws FileNotFoundException {
         switch (URL_MATCHER.match(uri)) {
             case CRASH_ID:
-                if (android.provider.Settings.System.getInt(getContext().getContentResolver(),
-                        "__cts_crash_on_launch", 0) != 0) {
-                    // The test case wants us to crash while querying.
-                    // Well, okay then!
-                    Log.i("MockContentProvider", "TEST IS CRASHING SELF, CROSS FINGERS!");
-                    android.provider.Settings.System.putInt(getContext().getContentResolver(),
-                            "__cts_crash_on_launch", 0);
-                    android.os.Process.killProcess(android.os.Process.myPid());
-                }
+                crashOnLaunchIfNeeded();
                 return new AssetFileDescriptor(
                         openPipeHelper(uri, null, null,
                                 "This is the openTypedAssetFile test data!", this), 0,
@@ -414,4 +384,36 @@
             }
         }
     }
+
+    private void crashOnLaunchIfNeeded() {
+        if (getCrashOnLaunch(getContext())) {
+            // The test case wants us to crash our process on first launch.
+            // Well, okay then!
+            Log.i("MockContentProvider", "TEST IS CRASHING SELF, CROSS FINGERS!");
+            setCrashOnLaunch(getContext(), false);
+            android.os.Process.killProcess(android.os.Process.myPid());
+        }
+    }
+
+    public static boolean getCrashOnLaunch(Context context) {
+        File file = getCrashOnLaunchFile(context);
+        return file.exists();
+    }
+
+    public static void setCrashOnLaunch(Context context, boolean value) {
+        File file = getCrashOnLaunchFile(context);
+        if (value) {
+            try {
+                file.createNewFile();
+            } catch (IOException ex) {
+                throw new RuntimeException("Could not create crash on launch file.", ex);
+            }
+        } else {
+            file.delete();
+        }
+    }
+
+    private static File getCrashOnLaunchFile(Context context) {
+        return context.getFileStreamPath("MockContentProvider.crashonlaunch");
+    }
 }
diff --git a/tests/tests/media/src/android/media/cts/EnumDevicesTest.java b/tests/tests/media/src/android/media/cts/EnumDevicesTest.java
index bf0d6cf..53dd5e6 100644
--- a/tests/tests/media/src/android/media/cts/EnumDevicesTest.java
+++ b/tests/tests/media/src/android/media/cts/EnumDevicesTest.java
@@ -153,15 +153,33 @@
     }
 
     public void test_deviceCallback() {
+        // null callback?
         mAudioManager.registerAudioDeviceCallback(null,null);
-        assertTrue(true);
 
         AudioDeviceCallback callback =  new EmptyDeviceCallback();
+        AudioDeviceCallback someOtherCallback =  new EmptyDeviceCallback();
+        // null Handler
         mAudioManager.registerAudioDeviceCallback(callback, null);
-        assertTrue(true);
 
+        // unregister null callback
+        mAudioManager.unregisterAudioDeviceCallback(null);
+        // unregister callback not registered
+        mAudioManager.unregisterAudioDeviceCallback(someOtherCallback);
+        // nominal case
+        mAudioManager.unregisterAudioDeviceCallback(callback);
+        // remove twice
+        mAudioManager.unregisterAudioDeviceCallback(callback);
+
+        // non-null Handler
         mAudioManager.registerAudioDeviceCallback(callback, new Handler());
-        assertTrue(true);
+        // unregister null callback
+        mAudioManager.unregisterAudioDeviceCallback(null);
+        // unregister callback not registered
+        mAudioManager.unregisterAudioDeviceCallback(someOtherCallback);
+        // nominal case
+        mAudioManager.unregisterAudioDeviceCallback(callback);
+        // remove twice
+        mAudioManager.unregisterAudioDeviceCallback(callback);
     }
 
     //TODO - Need tests for device connect/disconnect callbacks
diff --git a/tests/tests/os/src/android/os/cts/MessageQueueTest.java b/tests/tests/os/src/android/os/cts/MessageQueueTest.java
index 8906c42..0c051bf 100644
--- a/tests/tests/os/src/android/os/cts/MessageQueueTest.java
+++ b/tests/tests/os/src/android/os/cts/MessageQueueTest.java
@@ -25,6 +25,8 @@
 import android.os.ParcelFileDescriptor;
 import android.os.ParcelFileDescriptor.AutoCloseInputStream;
 import android.os.ParcelFileDescriptor.AutoCloseOutputStream;
+import android.system.ErrnoException;
+import android.system.Os;
 import android.os.SystemClock;
 import android.os.MessageQueue.IdleHandler;
 import android.test.AndroidTestCase;
@@ -66,7 +68,7 @@
         }
     }
 
-    private enum Test {ADD_IDLE_HANDLER, REMOVE_IDLE_HANDLER};
+    private enum Test {ADD_IDLE_HANDLER, REMOVE_IDLE_HANDLER}
 
     /**
      * {@link HandlerThread} that adds or removes an idle handler depending on the {@link Test}
@@ -91,6 +93,7 @@
             super.onLooperPrepared();
 
             IdleHandler idleHandler = new IdleHandler() {
+                @Override
                 public boolean queueIdle() {
                     mIdleLatch.countDown();
                     return false;
@@ -168,6 +171,7 @@
     public void testMessageOrder() throws Exception {
 
         OrderTestHelper tester = new OrderTestHelper() {
+            @Override
             public void init() {
                 super.init();
                 long now = SystemClock.uptimeMillis() + 200;
@@ -191,6 +195,7 @@
 
         OrderTestHelper tester = new OrderTestHelper() {
 
+            @Override
             public void init() {
                 super.init();
                 long now = SystemClock.uptimeMillis() + 200;
@@ -200,6 +205,7 @@
                 mHandler.sendMessageAtFrontOfQueue(mHandler.obtainMessage(0));
             }
 
+            @Override
             public void handleMessage(Message msg) {
                 super.handleMessage(msg);
                 if (msg.what == 0) {
@@ -386,9 +392,9 @@
      * edge cases around closing file descriptors within the callback and adding
      * new ones with the same number.
      *
-     * Register a file descriptor, close it from within the callback before
-     * returning, return.  Then create a new file descriptor (with the same number),
-     * register it.  Ensure that we start getting events for the new file descriptor.
+     * Register a file descriptor, close it from within the callback, then return.
+     * Later, create a new file descriptor register it.  Ensure that we start getting
+     * events for the new file descriptor.
      *
      * This test exercises special logic in Looper.cpp for EPOLL_CTL_DEL handling EBADF.
      */
@@ -401,23 +407,19 @@
             final Handler handler = new Handler(thread.getLooper());
 
             final ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
-            final int oldReaderFd = pipe[0].getFd();
             try (final FileInputStream reader = new AutoCloseInputStream(pipe[0]);
                     final FileOutputStream writer = new AutoCloseOutputStream(pipe[1])) {
                 // Register the callback.
-                final boolean[] awoke = new boolean[1];
+                final CountDownLatch awoke = new CountDownLatch(1);
                 queue.addOnFileDescriptorEventListener(reader.getFD(),
-                        OnFileDescriptorEventListener.EVENT_ERROR, new OnFileDescriptorEventListener() {
+                        OnFileDescriptorEventListener.EVENT_ERROR,
+                        new OnFileDescriptorEventListener() {
                     @Override
                     public int onFileDescriptorEvents(FileDescriptor fd, int events) {
-                        awoke[0] = true;
+                        awoke.countDown();
 
-                        // Close the file descriptor before we return.
-                        try {
-                            reader.close();
-                        } catch (IOException ex) {
-                            throw new RuntimeException(ex);
-                        }
+                        // Close the reader before we return.
+                        closeQuietly(reader);
 
                         // Return 0 to unregister the callback.
                         return 0;
@@ -428,26 +430,23 @@
                 writer.close();
 
                 // Wait for the looper to catch up and run the callback.
+                assertTrue("awoke", awoke.await(TIMEOUT, TimeUnit.MILLISECONDS));
                 syncWait(handler);
-                assertTrue(awoke[0]);
             }
 
             // At this point, the reader and writer are both closed.
-            // If we're lucky, we can create a new pipe with the same file
-            // descriptor numbers as before.
+            // Make a new pipe and ensure that things still work as expected.
             final ParcelFileDescriptor[] pipe2 = ParcelFileDescriptor.createPipe();
-            assertEquals("Expected new pipe to be created with same fd number as "
-                    + "previous pipe we just closed for the purpose of this test.",
-                    oldReaderFd, pipe2[0].getFd());
             try (final FileInputStream reader2 = new AutoCloseInputStream(pipe2[0]);
                     final FileOutputStream writer2 = new AutoCloseOutputStream(pipe2[1])) {
                 // Register the callback.
-                final boolean[] awoke = new boolean[1];
+                final CountDownLatch awoke = new CountDownLatch(1);
                 queue.addOnFileDescriptorEventListener(reader2.getFD(),
-                        OnFileDescriptorEventListener.EVENT_INPUT, new OnFileDescriptorEventListener() {
+                        OnFileDescriptorEventListener.EVENT_INPUT,
+                        new OnFileDescriptorEventListener() {
                     @Override
                     public int onFileDescriptorEvents(FileDescriptor fd, int events) {
-                        awoke[0] = true;
+                        awoke.countDown();
 
                         // Return 0 to unregister the callback.
                         return 0;
@@ -458,8 +457,8 @@
                 writer2.close();
 
                 // Wait for the looper to catch up and run the callback.
+                assertTrue("awoke", awoke.await(TIMEOUT, TimeUnit.MILLISECONDS));
                 syncWait(handler);
-                assertTrue(awoke[0]);
             }
         } finally {
             thread.quitAndRethrow();
@@ -471,10 +470,10 @@
      * edge cases around closing file descriptors within the callback and adding
      * new ones with the same number.
      *
-     * Register a file descriptor, close it from within the callback before
-     * returning, create a new file descriptor (with the same number) and return.
-     * Then register the same file descriptor.  Ensure that we start getting events for
-     * the new file descriptor.
+     * Register a file descriptor, close it from within the callback, reassign its
+     * number to a different pipe, then return.  Later, register the same file descriptor
+     * again (now referring to a new pipe).  Ensure that we start getting
+     * events for the new file descriptor.
      *
      * This test exercises special logic in Looper.cpp for EPOLL_CTL_DEL handling ENOENT.
      */
@@ -487,69 +486,67 @@
             final Handler handler = new Handler(thread.getLooper());
 
             final ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
-            final int oldReaderFd = pipe[0].getFd();
-            try (final FileInputStream reader = new AutoCloseInputStream(pipe[0]);
-                    final FileOutputStream writer = new AutoCloseOutputStream(pipe[1])) {
-                // Register the callback.
-                final boolean[] awoke = new boolean[1];
-                queue.addOnFileDescriptorEventListener(reader.getFD(),
-                        OnFileDescriptorEventListener.EVENT_ERROR, new OnFileDescriptorEventListener() {
-                    @Override
-                    public int onFileDescriptorEvents(FileDescriptor fd, int events) {
-                        awoke[0] = true;
+            final ParcelFileDescriptor[] pipe2 = ParcelFileDescriptor.createPipe();
+            try {
+                final int oldReaderFd = pipe[0].getFd();
+                try (final FileInputStream reader = new AutoCloseInputStream(pipe[0]);
+                        final FileOutputStream writer = new AutoCloseOutputStream(pipe[1])) {
+                    // Register the callback.
+                    final CountDownLatch awoke = new CountDownLatch(1);
+                    queue.addOnFileDescriptorEventListener(reader.getFD(),
+                            OnFileDescriptorEventListener.EVENT_ERROR,
+                            new OnFileDescriptorEventListener() {
+                        @Override
+                        public int onFileDescriptorEvents(FileDescriptor fd, int events) {
+                            awoke.countDown();
 
-                        try {
-                            // Close the file descriptor before we return.
-                            reader.close();
+                            // Close the reader before we return and hijack its fd.
+                            hijackFd(pipe2, pipe);
 
-                            // At this point, the reader and writer are both closed.
-                            // Assuming no one else has created a file descriptor in the meantime,
-                            // when we recreate the pipe we will get the same number as before.
-                            final ParcelFileDescriptor[] pipe2 = ParcelFileDescriptor.createPipe();
-                            assertEquals("Expected new pipe to be created with same fd number as "
-                                    + "previous pipe we just closed for the purpose of this test.",
-                                    oldReaderFd, pipe2[0].getFd());
-                            pipe[0] = pipe2[0];
-                            pipe[1] = pipe2[1];
-                        } catch (IOException ex) {
-                            throw new RuntimeException(ex);
+                            // Return 0 to unregister the callback.
+                            return 0;
                         }
+                    });
 
-                        // Return 0 to unregister the callback.
-                        return 0;
-                    }
-                });
+                    // Close the writer to wake up the callback (due to hangup).
+                    writer.close();
 
-                // Close the writer to wake up the callback (due to hangup).
-                writer.close();
+                    // Wait for the looper to catch up and run the callback.
+                    assertTrue("awoke", awoke.await(TIMEOUT, TimeUnit.MILLISECONDS));
+                    syncWait(handler);
+                }
 
-                // Wait for the looper to catch up and run the callback.
-                syncWait(handler);
-                assertTrue(awoke[0]);
-            }
+                // Now we have a new pipe with the same file descriptor, make sure we can
+                // register it successfully.
+                assertEquals(oldReaderFd, pipe2[0].getFd());
+                try (final FileInputStream reader2 = new AutoCloseInputStream(pipe2[0]);
+                        final FileOutputStream writer2 = new AutoCloseOutputStream(pipe2[1])) {
+                    // Register the callback.
+                    final CountDownLatch awoke = new CountDownLatch(1);
+                    queue.addOnFileDescriptorEventListener(reader2.getFD(),
+                            OnFileDescriptorEventListener.EVENT_INPUT,
+                            new OnFileDescriptorEventListener() {
+                        @Override
+                        public int onFileDescriptorEvents(FileDescriptor fd, int events) {
+                            awoke.countDown();
 
-            // Now we have a new pipe, make sure we can register it successfully.
-            try (final FileInputStream reader2 = new AutoCloseInputStream(pipe[0]);
-                    final FileOutputStream writer2 = new AutoCloseOutputStream(pipe[1])) {
-                // Register the callback.
-                final boolean[] awoke = new boolean[1];
-                queue.addOnFileDescriptorEventListener(reader2.getFD(),
-                        OnFileDescriptorEventListener.EVENT_INPUT, new OnFileDescriptorEventListener() {
-                    @Override
-                    public int onFileDescriptorEvents(FileDescriptor fd, int events) {
-                        awoke[0] = true;
+                            // Return 0 to unregister the callback.
+                            return 0;
+                        }
+                    });
 
-                        // Return 0 to unregister the callback.
-                        return 0;
-                    }
-                });
+                    // Close the writer to wake up the callback (due to hangup).
+                    writer2.close();
 
-                // Close the writer to wake up the callback (due to hangup).
-                writer2.close();
-
-                // Wait for the looper to catch up and run the callback.
-                syncWait(handler);
-                assertTrue(awoke[0]);
+                    // Wait for the looper to catch up and run the callback.
+                    assertTrue("awoke", awoke.await(TIMEOUT, TimeUnit.MILLISECONDS));
+                    syncWait(handler);
+                }
+            } finally {
+                closeQuietly(pipe[0]);
+                closeQuietly(pipe[1]);
+                closeQuietly(pipe2[0]);
+                closeQuietly(pipe2[1]);
             }
         } finally {
             thread.quitAndRethrow();
@@ -561,10 +558,9 @@
      * edge cases around closing file descriptors within the callback and adding
      * new ones with the same number.
      *
-     * Register a file descriptor, close it from within the callback before
-     * returning, create a new file descriptor (with the same number),
-     * register it, and return.  Ensure that we start getting events for the
-     * new file descriptor.
+     * Register a file descriptor, close it from within the callback, reassign its
+     * number to a different pipe, register it, then return.
+     * Ensure that we start getting events for the new file descriptor.
      *
      * This test exercises special logic in Looper.cpp for EPOLL_CTL_MOD handling
      * ENOENT and fallback to EPOLL_CTL_ADD as well as sequence number checks when removing
@@ -579,71 +575,66 @@
             final Handler handler = new Handler(thread.getLooper());
 
             final ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
-            final boolean[] awoke2 = new boolean[1];
-            final int oldReaderFd = pipe[0].getFd();
-            try (final FileInputStream reader = new AutoCloseInputStream(pipe[0]);
-                    final FileOutputStream writer = new AutoCloseOutputStream(pipe[1])) {
-                // Register the callback.
-                final boolean[] awoke = new boolean[1];
-                queue.addOnFileDescriptorEventListener(reader.getFD(),
-                        OnFileDescriptorEventListener.EVENT_ERROR, new OnFileDescriptorEventListener() {
-                    @Override
-                    public int onFileDescriptorEvents(FileDescriptor fd, int events) {
-                        awoke[0] = true;
+            final ParcelFileDescriptor[] pipe2 = ParcelFileDescriptor.createPipe();
+            try {
+                final CountDownLatch awoke2 = new CountDownLatch(1);
+                final int oldReaderFd = pipe[0].getFd();
+                try (final FileInputStream reader = new AutoCloseInputStream(pipe[0]);
+                        final FileOutputStream writer = new AutoCloseOutputStream(pipe[1])) {
+                    // Register the callback.
+                    final CountDownLatch awoke = new CountDownLatch(1);
+                    queue.addOnFileDescriptorEventListener(reader.getFD(),
+                            OnFileDescriptorEventListener.EVENT_ERROR,
+                            new OnFileDescriptorEventListener() {
+                        @Override
+                        public int onFileDescriptorEvents(FileDescriptor fd, int events) {
+                            awoke.countDown();
 
-                        final ParcelFileDescriptor[] pipe2;
-                        try {
-                            // Close the file descriptor before we return.
-                            reader.close();
+                            // Close the reader before we return and hijack its fd.
+                            hijackFd(pipe2, pipe);
 
-                            // At this point, the reader and writer are both closed.
-                            // Assuming no one else has created a file descriptor in the meantime,
-                            // when we recreate the pipe we will get the same number as before.
-                            pipe2 = ParcelFileDescriptor.createPipe();
-                            assertEquals("Expected new pipe to be created with same fd number as "
-                                    + "previous pipe we just closed for the purpose of this test.",
-                                    oldReaderFd, pipe2[0].getFd());
-                            pipe[0] = pipe2[0];
-                            pipe[1] = pipe2[1];
-                        } catch (IOException ex) {
-                            throw new RuntimeException(ex);
+                            // Now we have a new pipe, make sure we can register it successfully.
+                            queue.addOnFileDescriptorEventListener(pipe2[0].getFileDescriptor(),
+                                    OnFileDescriptorEventListener.EVENT_INPUT,
+                                    new OnFileDescriptorEventListener() {
+                                @Override
+                                public int onFileDescriptorEvents(FileDescriptor fd, int events) {
+                                    awoke2.countDown();
+
+                                    // Return 0 to unregister the callback.
+                                    return 0;
+                                }
+                            });
+
+                            // Return 0 to unregister the callback.
+                            return 0;
                         }
+                    });
 
-                        // Now we have a new pipe, make sure we can register it successfully.
-                        queue.addOnFileDescriptorEventListener(pipe[0].getFileDescriptor(),
-                                OnFileDescriptorEventListener.EVENT_INPUT,
-                                new OnFileDescriptorEventListener() {
-                            @Override
-                            public int onFileDescriptorEvents(FileDescriptor fd, int events) {
-                                awoke2[0] = true;
+                    // Close the writer to wake up the callback (due to hangup).
+                    writer.close();
 
-                                // Return 0 to unregister the callback.
-                                return 0;
-                            }
-                        });
+                    // Wait for the looper to catch up and run the callback.
+                    assertTrue("awoke", awoke.await(TIMEOUT, TimeUnit.MILLISECONDS));
+                    syncWait(handler);
+                }
 
-                        // Return 0 to unregister the callback.
-                        return 0;
-                    }
-                });
-
-                // Close the writer to wake up the callback (due to hangup).
-                writer.close();
+                // Close the second writer to wake up the second callback (due to hangup).
+                pipe2[1].close();
 
                 // Wait for the looper to catch up and run the callback.
+                assertTrue("awoke2", awoke2.await(TIMEOUT, TimeUnit.MILLISECONDS));
                 syncWait(handler);
-                assertTrue(awoke[0]);
+
+                // Close the second reader now that we're done with the test.
+                assertEquals(oldReaderFd, pipe2[0].getFd());
+                pipe2[0].close();
+            } finally {
+                closeQuietly(pipe[0]);
+                closeQuietly(pipe[1]);
+                closeQuietly(pipe2[0]);
+                closeQuietly(pipe2[1]);
             }
-
-            // Close the second writer to wake up the second callback (due to hangup).
-            pipe[1].close();
-
-            // Wait for the looper to catch up and run the callback.
-            syncWait(handler);
-            assertTrue(awoke2[0]);
-
-            // Close the second reader now that we're done with the test.
-            pipe[0].close();
         } finally {
             thread.quitAndRethrow();
         }
@@ -655,7 +646,7 @@
      * new ones with the same number.
      *
      * Register a file descriptor, make a duplicate of it, close it from within the
-     * callback before returning, return.  Look for signs that the Looper is spinning
+     * callback, then return.  Look for signs that the Looper is spinning
      * and never getting a chance to block.
      *
      * This test exercises special logic in Looper.cpp for rebuilding the epoll set
@@ -676,12 +667,12 @@
                 try (final FileInputStream reader = new AutoCloseInputStream(pipe[0]);
                         final FileOutputStream writer = new AutoCloseOutputStream(pipe[1])) {
                     // Register the callback.
-                    final boolean[] awoke = new boolean[1];
+                    final CountDownLatch awoke = new CountDownLatch(1);
                     queue.addOnFileDescriptorEventListener(reader.getFD(),
                             OnFileDescriptorEventListener.EVENT_ERROR, new OnFileDescriptorEventListener() {
                         @Override
                         public int onFileDescriptorEvents(FileDescriptor fd, int events) {
-                            awoke[0] = true;
+                            awoke.countDown();
 
                             // Close the file descriptor before we return.
                             try {
@@ -699,8 +690,8 @@
                     writer.close();
 
                     // Wait for the looper to catch up and run the callback.
+                    assertTrue("awoke", awoke.await(TIMEOUT, TimeUnit.MILLISECONDS));
                     syncWait(handler);
-                    assertTrue(awoke[0]);
                 }
 
                 // Wait a little bit before we stop the thread.
@@ -729,6 +720,7 @@
             private int mBarrierToken1;
             private int mBarrierToken2;
 
+            @Override
             public void init() {
                 super.init();
                 mLastMessage = 10;
@@ -741,6 +733,7 @@
                 mHandler.sendEmptyMessage(6);
             }
 
+            @Override
             public void handleMessage(Message msg) {
                 super.handleMessage(msg);
                 if (msg.what == 3) {
@@ -797,7 +790,35 @@
                 latch.countDown();
             }
         });
-        latch.await(TIMEOUT, TimeUnit.MILLISECONDS);
+        assertTrue("Handler got stuck.", latch.await(TIMEOUT, TimeUnit.MILLISECONDS));
+    }
+
+    private static void closeQuietly(AutoCloseable c) {
+        if (c != null) {
+            try {
+                c.close();
+            } catch (RuntimeException rethrown) {
+                throw rethrown;
+            } catch (Exception ex) {
+            }
+        }
+    }
+
+    private static void hijackFd(ParcelFileDescriptor[] newPipe, ParcelFileDescriptor[] oldPipe) {
+        // Detach the old pipe's first fd and get its number.
+        int fd = oldPipe[0].detachFd();
+
+        // Assign the new pipe's first fd to the same number as the old pipe's first fd.
+        // This causes the old pipe's first fd to be closed and reassigned.
+        try {
+            Os.dup2(newPipe[0].getFileDescriptor(), fd);
+        } catch (ErrnoException ex) {
+            throw new RuntimeException(ex);
+        }
+
+        // Fix up the new pipe's first fd object.
+        closeQuietly(newPipe[0]);
+        newPipe[0] = ParcelFileDescriptor.adoptFd(fd);
     }
 
     /**
@@ -814,6 +835,7 @@
 
         public void init() {
             mHandler = new Handler() {
+                @Override
                 public void handleMessage(Message msg) {
                     OrderTestHelper.this.handleMessage(msg);
                 }
@@ -863,6 +885,7 @@
                 super("MessengerLooperThread");
             }
 
+            @Override
             public void onLooperPrepared() {
                 init();
                 mLooper = getLooper();
@@ -908,7 +931,7 @@
      * A HandlerThread that propagates exceptions out of the event loop
      * instead of crashing the process.
      */
-    private class AssertableHandlerThread extends HandlerThread {
+    private static class AssertableHandlerThread extends HandlerThread {
         private Throwable mThrowable;
         private long mRuntime;
 
diff --git a/tests/tests/provider/src/android/provider/cts/ContactsContract_ProviderStatus.java b/tests/tests/provider/src/android/provider/cts/ContactsContract_ProviderStatus.java
new file mode 100644
index 0000000..9c38ec0
--- /dev/null
+++ b/tests/tests/provider/src/android/provider/cts/ContactsContract_ProviderStatus.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.provider.cts;
+
+import android.content.ContentProviderClient;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.provider.ContactsContract.ProviderStatus;
+import android.provider.ContactsContract.RawContacts;
+import android.provider.cts.ContactsContract_TestDataBuilder.TestRawContact;
+import android.provider.cts.contacts.DatabaseAsserts;
+import android.test.AndroidTestCase;
+
+/**
+ * CTS tests for {@link android.provider.ContactsContract.ProviderStatus}.
+ *
+ * Unfortunately, we can't check that the value of ProviderStatus equals
+ * {@link ProviderStatus#STATUS_EMPTY} initially. Some carriers pre-install
+ * accounts. As a result, the value STATUS_EMPTY will never be achieved.
+ */
+public class ContactsContract_ProviderStatus extends AndroidTestCase {
+    private ContentResolver mResolver;
+    private ContactsContract_TestDataBuilder mBuilder;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mResolver = getContext().getContentResolver();
+        ContentProviderClient provider =
+                mResolver.acquireContentProviderClient(ContactsContract.AUTHORITY);
+        mBuilder = new ContactsContract_TestDataBuilder(provider);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        mBuilder.cleanup();
+    }
+
+    public void testProviderStatus_addedContacts() throws Exception {
+        // Setup: add a contact to CP2.
+        TestRawContact rawContact1 = mBuilder.newRawContact()
+                .with(RawContacts.ACCOUNT_TYPE, "test_account")
+                .with(RawContacts.ACCOUNT_NAME, "test_name")
+                .insert();
+        rawContact1.newDataRow(StructuredName.CONTENT_ITEM_TYPE)
+                .with(StructuredName.GIVEN_NAME, "first1")
+                .with(StructuredName.FAMILY_NAME, "last1")
+                .insert();
+
+        // Execute: fetch CP2 status
+        Cursor cursor = mResolver.query(ProviderStatus.CONTENT_URI, null, null, null, null);
+
+        // Verify: CP2 status is normal instead of STATUS_EMPTY.
+        try {
+            assertEquals(1, cursor.getCount());
+            cursor.moveToFirst();
+            ContentValues values = new ContentValues();
+            values.put(ProviderStatus.STATUS, ProviderStatus.STATUS_NORMAL);
+            DatabaseAsserts.assertCursorValuesMatchExactly(cursor, values);
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+    }
+}