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();
+ }
+ }
+ }
+}