Merge "camera2 CTS: Allow AE convergence in DngCreatorTest." into mnc-dev
diff --git a/libs/deviceutillegacy/src/android/webkit/cts/WebViewOnUiThread.java b/libs/deviceutillegacy/src/android/webkit/cts/WebViewOnUiThread.java
index 5cd6f30..6f310dd 100644
--- a/libs/deviceutillegacy/src/android/webkit/cts/WebViewOnUiThread.java
+++ b/libs/deviceutillegacy/src/android/webkit/cts/WebViewOnUiThread.java
@@ -21,6 +21,7 @@
import android.graphics.Bitmap;
import android.graphics.Picture;
import android.graphics.Rect;
+import android.net.Uri;
import android.os.Bundle;
import android.os.Looper;
import android.os.Message;
@@ -36,6 +37,8 @@
import android.webkit.ValueCallback;
import android.webkit.WebBackForwardList;
import android.webkit.WebChromeClient;
+import android.webkit.WebMessage;
+import android.webkit.WebMessagePort;
import android.webkit.WebSettings;
import android.webkit.WebView.HitTestResult;
import android.webkit.WebView.PictureListener;
@@ -307,6 +310,24 @@
});
}
+ public WebMessagePort[] createWebMessageChannel() {
+ return getValue(new ValueGetter<WebMessagePort[]>() {
+ @Override
+ public WebMessagePort[] capture() {
+ return mWebView.createWebMessageChannel();
+ }
+ });
+ }
+
+ public void postMessageToMainFrame(final WebMessage message, final Uri targetOrigin) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mWebView.postMessageToMainFrame(message, targetOrigin);
+ }
+ });
+ }
+
public void addJavascriptInterface(final Object object, final String name) {
runOnUiThread(new Runnable() {
@Override
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
index ea333eb..08f628f 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
@@ -61,10 +61,12 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
+import java.util.HashMap;
import java.util.List;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
/**
* A package private utility class for wrapping up the camera2 cts test common utility functions
@@ -284,6 +286,35 @@
}
}
+ public static class SimpleImageWriterListener implements ImageWriter.ImageListener {
+ private final Semaphore mImageReleasedSema = new Semaphore(0);
+ private final ImageWriter mWriter;
+ @Override
+ public void onInputImageReleased(ImageWriter writer) {
+ if (writer != mWriter) {
+ return;
+ }
+
+ if (VERBOSE) {
+ Log.v(TAG, "Input image is released");
+ }
+ mImageReleasedSema.release();
+ }
+
+ public SimpleImageWriterListener(ImageWriter writer) {
+ if (writer == null) {
+ throw new IllegalArgumentException("writer cannot be null");
+ }
+ mWriter = writer;
+ }
+
+ public void waitForImageReleased(long timeoutMs) throws InterruptedException {
+ if (!mImageReleasedSema.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
+ fail("wait for image available timed out after " + timeoutMs + "ms");
+ }
+ }
+ }
+
public static class SimpleCaptureCallback extends CameraCaptureSession.CaptureCallback {
private final LinkedBlockingQueue<TotalCaptureResult> mQueue =
new LinkedBlockingQueue<TotalCaptureResult>();
@@ -391,16 +422,63 @@
*/
public TotalCaptureResult getTotalCaptureResultForRequest(CaptureRequest myRequest,
int numResultsWait) {
+ ArrayList<CaptureRequest> captureRequests = new ArrayList<>(1);
+ captureRequests.add(myRequest);
+ return getTotalCaptureResultsForRequests(captureRequests, numResultsWait)[0];
+ }
+
+ /**
+ * Get an array of {@link #TotalCaptureResult total capture results} for a given list of
+ * {@link #CaptureRequest capture requests}. This can be used when the order of results
+ * may not the same as the order of requests.
+ *
+ * @param captureRequests The list of {@link #CaptureRequest capture requests} whose
+ * corresponding {@link #TotalCaptureResult capture results} are
+ * being waited for.
+ * @param numResultsWait Number of frames to wait for the capture results
+ * before timeout.
+ * @throws TimeoutRuntimeException If more than numResultsWait results are
+ * seen before all the results matching captureRequests arrives.
+ */
+ public TotalCaptureResult[] getTotalCaptureResultsForRequests(
+ List<CaptureRequest> captureRequests, int numResultsWait) {
if (numResultsWait < 0) {
throw new IllegalArgumentException("numResultsWait must be no less than 0");
}
+ if (captureRequests == null || captureRequests.size() == 0) {
+ throw new IllegalArgumentException("captureRequests must have at least 1 request.");
+ }
- TotalCaptureResult result;
+ // Create a request -> a list of result indices map that it will wait for.
+ HashMap<CaptureRequest, ArrayList<Integer>> remainingResultIndicesMap = new HashMap<>();
+ for (int i = 0; i < captureRequests.size(); i++) {
+ CaptureRequest request = captureRequests.get(i);
+ ArrayList<Integer> indices = remainingResultIndicesMap.get(request);
+ if (indices == null) {
+ indices = new ArrayList<>();
+ remainingResultIndicesMap.put(request, indices);
+ }
+ indices.add(i);
+ }
+
+ TotalCaptureResult[] results = new TotalCaptureResult[captureRequests.size()];
int i = 0;
do {
- result = getTotalCaptureResult(CAPTURE_RESULT_TIMEOUT_MS);
- if (result.getRequest().equals(myRequest)) {
- return result;
+ TotalCaptureResult result = getTotalCaptureResult(CAPTURE_RESULT_TIMEOUT_MS);
+ CaptureRequest request = result.getRequest();
+ ArrayList<Integer> indices = remainingResultIndicesMap.get(request);
+ if (indices != null) {
+ results[indices.get(0)] = result;
+ indices.remove(0);
+
+ // Remove the entry if all results for this request has been fulfilled.
+ if (indices.isEmpty()) {
+ remainingResultIndicesMap.remove(request);
+ }
+ }
+
+ if (remainingResultIndicesMap.isEmpty()) {
+ return results;
}
} while (i++ < numResultsWait);
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/ImageWriterTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/ImageWriterTest.java
index 1ce32a4..d9e5cdf 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/ImageWriterTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/ImageWriterTest.java
@@ -132,27 +132,6 @@
}
}
- private final class SimpleImageWriterListener implements ImageWriter.ImageListener {
- private final ConditionVariable imageReleased = new ConditionVariable();
- @Override
- public void onInputImageReleased(ImageWriter writer) {
- if (writer != mWriter) {
- return;
- }
-
- if (VERBOSE) Log.v(TAG, "Input image is released");
- imageReleased.open();
- }
-
- public void waitForImageReleassed(long timeoutMs) {
- if (imageReleased.block(timeoutMs)) {
- imageReleased.close();
- } else {
- fail("wait for image available timed out after " + timeoutMs + "ms");
- }
- }
- }
-
private void readerWriterFormatTestByCamera(int format) throws Exception {
List<Size> sizes = getSortedSizesForFormat(mCamera.getId(), mCameraManager, format, null);
Size maxSize = sizes.get(0);
@@ -178,7 +157,7 @@
Surface surface = mReaderForWriter.getSurface();
assertNotNull("Surface from ImageReader shouldn't be null", surface);
mWriter = ImageWriter.newInstance(surface, MAX_NUM_IMAGES);
- SimpleImageWriterListener writerImageListener = new SimpleImageWriterListener();
+ SimpleImageWriterListener writerImageListener = new SimpleImageWriterListener(mWriter);
mWriter.setImageListener(writerImageListener, mHandler);
// Start capture: capture 2 images.
@@ -257,7 +236,7 @@
outputImage.close();
// Make sure ImageWriter listener callback is fired.
- writerImageListener.waitForImageReleassed(CAPTURE_IMAGE_TIMEOUT_MS);
+ writerImageListener.waitForImageReleased(CAPTURE_IMAGE_TIMEOUT_MS);
// Test case 2: Directly inject the image into ImageWriter: works for all formats.
@@ -291,7 +270,7 @@
outputImage.close();
// Make sure ImageWriter listener callback is fired.
- writerImageListener.waitForImageReleassed(CAPTURE_IMAGE_TIMEOUT_MS);
+ writerImageListener.waitForImageReleased(CAPTURE_IMAGE_TIMEOUT_MS);
}
stopCapture(/*fast*/false);
@@ -321,24 +300,16 @@
validateOpaqueImage(outputImage, "First Opaque image output by ImageWriter: ",
maxSize, result);
outputImage.close();
- writerListener.waitForImageReleassed(CAPTURE_IMAGE_TIMEOUT_MS);
+ writerListener.waitForImageReleased(CAPTURE_IMAGE_TIMEOUT_MS);
}
}
private void validateOpaqueImage(Image image, String msg, Size imageSize,
CaptureResult result) {
assertNotNull("Opaque image Capture result should not be null", result != null);
- mCollector.expectTrue(msg + "Opaque image format should be: " + CAMERA_OPAQUE_FORMAT,
- image.getFormat() == CAMERA_OPAQUE_FORMAT);
- mCollector.expectTrue(msg + "Opaque image format should be: " + CAMERA_OPAQUE_FORMAT,
- image.getFormat() == CAMERA_OPAQUE_FORMAT);
+ mCollector.expectImageProperties(msg + "Opaque ", image, CAMERA_OPAQUE_FORMAT,
+ imageSize, result.get(CaptureResult.SENSOR_TIMESTAMP));
mCollector.expectTrue(msg + "Opaque image number planes should be zero",
image.getPlanes().length == 0);
- mCollector.expectTrue(msg + "Opaque image size should be " + imageSize,
- image.getWidth() == imageSize.getWidth() &&
- image.getHeight() == imageSize.getHeight());
- long timestampNs = result.get(CaptureResult.SENSOR_TIMESTAMP);
- mCollector.expectTrue(msg + "Opaque image timestamp should be " + timestampNs,
- image.getTimestamp() == timestampNs);
}
}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/ReprocessCaptureTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/ReprocessCaptureTest.java
index dfd1aa8..7d6ab55 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/ReprocessCaptureTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/ReprocessCaptureTest.java
@@ -25,6 +25,7 @@
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.cts.helpers.StaticMetadata;
import android.hardware.camera2.cts.helpers.StaticMetadata.CheckLevel;
@@ -48,8 +49,6 @@
private static final String TAG = "ReprocessCaptureTest";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private static final int MAX_NUM_IMAGE_READER_IMAGES = 3;
- private static final int MAX_NUM_IMAGE_WRITER_IMAGES = 3;
private static final int CAPTURE_TIMEOUT_FRAMES = 100;
private static final int CAPTURE_TIMEOUT_MS = 3000;
private static final int WAIT_FOR_SURFACE_CHANGE_TIMEOUT_MS = 1000;
@@ -57,16 +56,27 @@
private static final int PREVIEW_TEMPLATE = CameraDevice.TEMPLATE_PREVIEW;
private static final int NUM_REPROCESS_TEST_LOOP = 3;
private static final int NUM_REPROCESS_CAPTURES = 3;
+ private static final int NUM_REPROCESS_BURST = 3;
private int mDumpFrameCount = 0;
// The image reader for the first regular capture
private ImageReader mFirstImageReader;
// The image reader for the reprocess capture
private ImageReader mSecondImageReader;
+ // A flag indicating whether the regular capture and the reprocess capture share the same image
+ // reader. If it's true, mFirstImageReader should be used for regular and reprocess outputs.
+ private boolean mShareOneImageReader;
private SimpleImageReaderListener mFirstImageReaderListener;
private SimpleImageReaderListener mSecondImageReaderListener;
private Surface mInputSurface;
private ImageWriter mImageWriter;
+ private SimpleImageWriterListener mImageWriterListener;
+
+ private enum CaptureTestCase {
+ SINGLE_SHOT,
+ BURST,
+ MIXED_BURST
+ }
/**
* Test YUV_420_888 -> YUV_420_888 with maximal supported sizes
@@ -137,7 +147,8 @@
// open Camera device
openDevice(id);
// no preview
- testReprocessingAllCombinations(id, null);
+ testReprocessingAllCombinations(id, /*previewSize*/null,
+ CaptureTestCase.SINGLE_SHOT);
} finally {
closeDevice();
}
@@ -156,7 +167,8 @@
try {
// open Camera device
openDevice(id);
- testReprocessingAllCombinations(id, mOrderedPreviewSizes.get(0));
+ testReprocessingAllCombinations(id, mOrderedPreviewSizes.get(0),
+ CaptureTestCase.SINGLE_SHOT);
} finally {
closeDevice();
}
@@ -234,8 +246,8 @@
}
setupImageReaders(inputSize, inputFormat, reprocessOutputSize,
- reprocessOutputFormat);
- setupReprocessibleSession(/*previewSurface*/null);
+ reprocessOutputFormat, /*maxImages*/1);
+ setupReprocessibleSession(/*previewSurface*/null, /*numImageWriterImages*/1);
TotalCaptureResult result = submitCaptureRequest(mFirstImageReader.getSurface(),
/*inputResult*/null);
@@ -246,11 +258,17 @@
// recreate the session
closeReprossibleSession();
- setupReprocessibleSession(/*previewSurface*/null);
+ setupReprocessibleSession(/*previewSurface*/null, /*numImageWriterImages*/1);
try {
+ TotalCaptureResult reprocessResult;
// issue and wait on reprocess capture request
- TotalCaptureResult reprocessResult =
- submitCaptureRequest(mSecondImageReader.getSurface(), result);
+ if (mShareOneImageReader) {
+ reprocessResult =
+ submitCaptureRequest(mFirstImageReader.getSurface(), result);
+ } else {
+ reprocessResult =
+ submitCaptureRequest(mSecondImageReader.getSurface(), result);
+ }
fail("Camera " + id + ": should get IllegalArgumentException for cross " +
"session reprocess captrue.");
} catch (IllegalArgumentException e) {
@@ -268,8 +286,52 @@
}
}
- // todo: test aborting reprocessing captures.
- // todo: test burst reprocessing captures.
+ /**
+ * Test burst reprocessing captures with and without preview.
+ */
+ public void testBurstReprocessing() throws Exception {
+ for (String id : mCameraIds) {
+ if (!isYuvReprocessSupported(id) && !isOpaqueReprocessSupported(id)) {
+ continue;
+ }
+
+ try {
+ // open Camera device
+ openDevice(id);
+ // no preview
+ testReprocessingAllCombinations(id, /*previewSize*/null, CaptureTestCase.BURST);
+ // with preview
+ testReprocessingAllCombinations(id, mOrderedPreviewSizes.get(0),
+ CaptureTestCase.BURST);
+ } finally {
+ closeDevice();
+ }
+ }
+ }
+
+ /**
+ * Test burst captures mixed with regular and reprocess captures with and without preview.
+ */
+ public void testMixedBurstReprocessing() throws Exception {
+ for (String id : mCameraIds) {
+ if (!isYuvReprocessSupported(id) && !isOpaqueReprocessSupported(id)) {
+ continue;
+ }
+
+ try {
+ // open Camera device
+ openDevice(id);
+ // no preview
+ testReprocessingAllCombinations(id, /*previewSize*/null,
+ CaptureTestCase.MIXED_BURST);
+ // with preview
+ testReprocessingAllCombinations(id, mOrderedPreviewSizes.get(0),
+ CaptureTestCase.MIXED_BURST);
+ } finally {
+ closeDevice();
+ }
+ }
+ }
/**
* Test the input format and output format with the largest input and output sizes.
@@ -294,8 +356,8 @@
/**
* Test all input format, input size, output format, and output size combinations.
*/
- private void testReprocessingAllCombinations(String cameraId,
- Size previewSize) throws Exception {
+ private void testReprocessingAllCombinations(String cameraId, Size previewSize,
+ CaptureTestCase captureTestCase) throws Exception {
int[] supportedInputFormats =
mStaticInfo.getAvailableFormats(StaticMetadata.StreamDirection.Input);
@@ -314,15 +376,165 @@
StaticMetadata.StreamDirection.Output);
for (Size reprocessOutputSize : supportedReprocessOutputSizes) {
- testReprocess(cameraId, inputSize, inputFormat,
- reprocessOutputSize, reprocessOutputFormat, previewSize,
- NUM_REPROCESS_CAPTURES);
+ switch (captureTestCase) {
+ case SINGLE_SHOT:
+ testReprocess(cameraId, inputSize, inputFormat,
+ reprocessOutputSize, reprocessOutputFormat, previewSize,
+ NUM_REPROCESS_CAPTURES);
+ break;
+ case BURST:
+ testReprocessBurst(cameraId, inputSize, inputFormat,
+ reprocessOutputSize, reprocessOutputFormat, previewSize,
+ NUM_REPROCESS_BURST);
+ break;
+ case MIXED_BURST:
+ testReprocessMixedBurst(cameraId, inputSize, inputFormat,
+ reprocessOutputSize, reprocessOutputFormat, previewSize,
+ NUM_REPROCESS_BURST);
+ break;
+ default:
+ throw new IllegalArgumentException("Invalid capture type");
+ }
}
}
}
}
}
+ /**
+ * Test burst that is mixed with regular and reprocess capture requests.
+ */
+ private void testReprocessMixedBurst(String cameraId, Size inputSize, int inputFormat,
+ Size reprocessOutputSize, int reprocessOutputFormat, Size previewSize,
+ int numBurst) throws Exception {
+ if (VERBOSE) {
+ Log.v(TAG, "testReprocessMixedBurst: cameraId: " + cameraId + " inputSize: " +
+ inputSize + " inputFormat: " + inputFormat + " reprocessOutputSize: " +
+ reprocessOutputSize + " reprocessOutputFormat: " + reprocessOutputFormat +
+ " previewSize: " + previewSize + " numBurst: " + numBurst);
+ }
+
+ boolean enablePreview = (previewSize != null);
+ ImageResultHolder[] imageResultHolders = new ImageResultHolder[0];
+
+ try {
+ // totalNumBurst = number of regular burst + number of reprocess burst.
+ int totalNumBurst = numBurst * 2;
+
+ if (enablePreview) {
+ updatePreviewSurface(previewSize);
+ } else {
+ mPreviewSurface = null;
+ }
+
+ setupImageReaders(inputSize, inputFormat, reprocessOutputSize, reprocessOutputFormat,
+ totalNumBurst);
+ setupReprocessibleSession(mPreviewSurface, /*numImageWriterImages*/numBurst);
+
+ if (enablePreview) {
+ startPreview(mPreviewSurface);
+ }
+
+ // Prepare an array of booleans indicating each capture's type (regular or reprocess)
+ boolean[] isReprocessCaptures = new boolean[totalNumBurst];
+ for (int i = 0; i < totalNumBurst; i++) {
+ if ((i & 1) == 0) {
+ isReprocessCaptures[i] = true;
+ } else {
+ isReprocessCaptures[i] = false;
+ }
+ }
+
+ imageResultHolders = doMixedReprocessBurstCapture(isReprocessCaptures);
+ for (ImageResultHolder holder : imageResultHolders) {
+ Image reprocessedImage = holder.getImage();
+ TotalCaptureResult result = holder.getTotalCaptureResult();
+
+ mCollector.expectImageProperties("testReprocessMixedBurst", reprocessedImage,
+ reprocessOutputFormat, reprocessOutputSize,
+ result.get(CaptureResult.SENSOR_TIMESTAMP));
+
+ if (DEBUG) {
+ Log.d(TAG, String.format("camera %s in %dx%d %d out %dx%d %d",
+ cameraId, inputSize.getWidth(), inputSize.getHeight(), inputFormat,
+ reprocessOutputSize.getWidth(), reprocessOutputSize.getHeight(),
+ reprocessOutputFormat));
+ dumpImage(reprocessedImage,
+ "/testReprocessMixedBurst_camera" + cameraId + "_" + mDumpFrameCount);
+ mDumpFrameCount++;
+ }
+ }
+ } finally {
+ for (ImageResultHolder holder : imageResultHolders) {
+ holder.getImage().close();
+ }
+ closeReprossibleSession();
+ closeImageReaders();
+ }
+ }
+
+ /**
+ * Test burst of reprocess capture requests.
+ */
+ private void testReprocessBurst(String cameraId, Size inputSize, int inputFormat,
+ Size reprocessOutputSize, int reprocessOutputFormat, Size previewSize,
+ int numBurst) throws Exception {
+ if (VERBOSE) {
+ Log.v(TAG, "testReprocessBurst: cameraId: " + cameraId + " inputSize: " +
+ inputSize + " inputFormat: " + inputFormat + " reprocessOutputSize: " +
+ reprocessOutputSize + " reprocessOutputFormat: " + reprocessOutputFormat +
+ " previewSize: " + previewSize + " numBurst: " + numBurst);
+ }
+
+ boolean enablePreview = (previewSize != null);
+ ImageResultHolder[] imageResultHolders = new ImageResultHolder[0];
+
+ try {
+ if (enablePreview) {
+ updatePreviewSurface(previewSize);
+ } else {
+ mPreviewSurface = null;
+ }
+
+ setupImageReaders(inputSize, inputFormat, reprocessOutputSize, reprocessOutputFormat,
+ numBurst);
+ setupReprocessibleSession(mPreviewSurface, numBurst);
+
+ if (enablePreview) {
+ startPreview(mPreviewSurface);
+ }
+
+ imageResultHolders = doReprocessBurstCapture(numBurst);
+ for (ImageResultHolder holder : imageResultHolders) {
+ Image reprocessedImage = holder.getImage();
+ TotalCaptureResult result = holder.getTotalCaptureResult();
+
+ mCollector.expectImageProperties("testReprocessBurst", reprocessedImage,
+ reprocessOutputFormat, reprocessOutputSize,
+ result.get(CaptureResult.SENSOR_TIMESTAMP));
+
+ if (DEBUG) {
+ Log.d(TAG, String.format("camera %s in %dx%d %d out %dx%d %d",
+ cameraId, inputSize.getWidth(), inputSize.getHeight(), inputFormat,
+ reprocessOutputSize.getWidth(), reprocessOutputSize.getHeight(),
+ reprocessOutputFormat));
+ dumpImage(reprocessedImage,
+ "/testReprocessBurst_camera" + cameraId + "_" + mDumpFrameCount);
+ mDumpFrameCount++;
+ }
+ }
+ } finally {
+ for (ImageResultHolder holder : imageResultHolders) {
+ holder.getImage().close();
+ }
+ closeReprossibleSession();
+ closeImageReaders();
+ }
+ }
+
+ /**
+ * Test a sequences of reprocess capture requests.
+ */
private void testReprocess(String cameraId, Size inputSize, int inputFormat,
Size reprocessOutputSize, int reprocessOutputFormat, Size previewSize,
int numReprocessCaptures) throws Exception {
@@ -342,57 +554,39 @@
mPreviewSurface = null;
}
- setupImageReaders(inputSize, inputFormat, reprocessOutputSize, reprocessOutputFormat);
- setupReprocessibleSession(mPreviewSurface);
+ setupImageReaders(inputSize, inputFormat, reprocessOutputSize, reprocessOutputFormat,
+ /*maxImages*/1);
+ setupReprocessibleSession(mPreviewSurface, /*numImageWriterImages*/1);
if (enablePreview) {
startPreview(mPreviewSurface);
}
for (int i = 0; i < numReprocessCaptures; i++) {
- Image reprocessedImage = null;
+ ImageResultHolder imageResultHolder = null;
try {
- reprocessedImage = doReprocessCapture();
+ imageResultHolder = doReprocessCapture();
+ Image reprocessedImage = imageResultHolder.getImage();
+ TotalCaptureResult result = imageResultHolder.getTotalCaptureResult();
- assertTrue(String.format("Reprocess output size is %dx%d. Expecting %dx%d.",
- reprocessedImage.getWidth(), reprocessedImage.getHeight(),
- reprocessOutputSize.getWidth(), reprocessOutputSize.getHeight()),
- reprocessedImage.getWidth() == reprocessOutputSize.getWidth() &&
- reprocessedImage.getHeight() == reprocessOutputSize.getHeight());
- assertTrue(String.format("Reprocess output format is %d. Expecting %d.",
- reprocessedImage.getFormat(), reprocessOutputFormat),
- reprocessedImage.getFormat() == reprocessOutputFormat);
+ mCollector.expectImageProperties("testReprocess", reprocessedImage,
+ reprocessOutputFormat, reprocessOutputSize,
+ result.get(CaptureResult.SENSOR_TIMESTAMP));
if (DEBUG) {
- String filename = DEBUG_FILE_NAME_BASE + "/reprocessed_camera" + cameraId +
- "_" + mDumpFrameCount;
- mDumpFrameCount++;
-
- switch(reprocessedImage.getFormat()) {
- case ImageFormat.JPEG:
- filename += ".jpg";
- break;
- case ImageFormat.NV16:
- case ImageFormat.NV21:
- case ImageFormat.YUV_420_888:
- filename += ".yuv";
- break;
- default:
- filename += "." + reprocessedImage.getFormat();
- break;
- }
-
- Log.d(TAG, "dumping an image to " + filename);
Log.d(TAG, String.format("camera %s in %dx%d %d out %dx%d %d",
cameraId, inputSize.getWidth(), inputSize.getHeight(), inputFormat,
reprocessOutputSize.getWidth(), reprocessOutputSize.getHeight(),
reprocessOutputFormat));
- dumpFile(filename , getDataFromImage(reprocessedImage));
+
+ dumpImage(reprocessedImage,
+ "/testReprocess_camera" + cameraId + "_" + mDumpFrameCount);
+ mDumpFrameCount++;
}
} finally {
- if (reprocessedImage != null) {
- reprocessedImage.close();
+ if (imageResultHolder != null) {
+ imageResultHolder.getImage().close();
}
}
}
@@ -402,20 +596,37 @@
}
}
+ /**
+ * Set up two image readers: one for regular capture (used for reprocess input) and one for
+ * reprocess capture.
+ */
private void setupImageReaders(Size inputSize, int inputFormat, Size reprocessOutputSize,
- int reprocessOutputFormat) {
+ int reprocessOutputFormat, int maxImages) {
+ mShareOneImageReader = false;
+ // If the regular output and reprocess output have the same size and format,
+ // they can share one image reader.
+ if (inputFormat == reprocessOutputFormat &&
+ inputSize.equals(reprocessOutputSize)) {
+ maxImages *= 2;
+ mShareOneImageReader = true;
+ }
// create an ImageReader for the regular capture
mFirstImageReaderListener = new SimpleImageReaderListener();
- mFirstImageReader = makeImageReader(inputSize, inputFormat,
- MAX_NUM_IMAGE_READER_IMAGES, mFirstImageReaderListener, mHandler);
+ mFirstImageReader = makeImageReader(inputSize, inputFormat, maxImages,
+ mFirstImageReaderListener, mHandler);
- // create an ImageReader for the reprocess capture
- mSecondImageReaderListener = new SimpleImageReaderListener();
- mSecondImageReader = makeImageReader(reprocessOutputSize, reprocessOutputFormat,
- MAX_NUM_IMAGE_READER_IMAGES, mSecondImageReaderListener, mHandler);
+ if (!mShareOneImageReader) {
+ // create an ImageReader for the reprocess capture
+ mSecondImageReaderListener = new SimpleImageReaderListener();
+ mSecondImageReader = makeImageReader(reprocessOutputSize, reprocessOutputFormat,
+ maxImages, mSecondImageReaderListener, mHandler);
+ }
}
+ /**
+ * Close two image readers.
+ */
private void closeImageReaders() {
CameraTestUtils.closeImageReader(mFirstImageReader);
mFirstImageReader = null;
@@ -423,17 +634,31 @@
mSecondImageReader = null;
}
- private void setupReprocessibleSession(Surface previewSurface) throws Exception {
+ /**
+ * Set up a reprocessible session and create an ImageWriter with the sessoin's input surface.
+ */
+ private void setupReprocessibleSession(Surface previewSurface, int numImageWriterImages)
+ throws Exception {
// create a reprocessible capture session
List<Surface> outSurfaces = new ArrayList<Surface>();
outSurfaces.add(mFirstImageReader.getSurface());
- outSurfaces.add(mSecondImageReader.getSurface());
+ if (!mShareOneImageReader) {
+ outSurfaces.add(mSecondImageReader.getSurface());
+ }
if (previewSurface != null) {
outSurfaces.add(previewSurface);
}
InputConfiguration inputConfig = new InputConfiguration(mFirstImageReader.getWidth(),
mFirstImageReader.getHeight(), mFirstImageReader.getImageFormat());
+ assertTrue(String.format("inputConfig is wrong: %dx%d format %d. Expect %dx%d format %d",
+ inputConfig.getWidth(), inputConfig.getHeight(), inputConfig.getFormat(),
+ mFirstImageReader.getWidth(), mFirstImageReader.getHeight(),
+ mFirstImageReader.getImageFormat()),
+ inputConfig.getWidth() == mFirstImageReader.getWidth() &&
+ inputConfig.getHeight() == mFirstImageReader.getHeight() &&
+ inputConfig.getFormat() == mFirstImageReader.getImageFormat());
+
mSessionListener = new BlockingSessionCallback();
mSession = configureReprocessibleCameraSession(mCamera, inputConfig, outSurfaces,
mSessionListener, mHandler);
@@ -441,9 +666,15 @@
// create an ImageWriter
mInputSurface = mSession.getInputSurface();
mImageWriter = ImageWriter.newInstance(mInputSurface,
- MAX_NUM_IMAGE_WRITER_IMAGES);
+ numImageWriterImages);
+
+ mImageWriterListener = new SimpleImageWriterListener(mImageWriter);
+ mImageWriter.setImageListener(mImageWriterListener, mHandler);
}
+ /**
+ * Close the reprocessible session and ImageWriter.
+ */
private void closeReprossibleSession() {
mInputSurface = null;
@@ -458,20 +689,74 @@
}
}
- private Image doReprocessCapture() throws Exception {
- // issue and wait on regular capture request
- TotalCaptureResult result = submitCaptureRequest(mFirstImageReader.getSurface(),
- /*inputResult*/null);
- Image image = mFirstImageReaderListener.getImage(CAPTURE_TIMEOUT_MS);
+ /**
+ * Do one reprocess capture.
+ */
+ private ImageResultHolder doReprocessCapture() throws Exception {
+ return doReprocessBurstCapture(/*numBurst*/1)[0];
+ }
- // queue the image to image writer
- mImageWriter.queueInputImage(image);
+ /**
+ * Do a burst of reprocess captures.
+ */
+ private ImageResultHolder[] doReprocessBurstCapture(int numBurst) throws Exception {
+ boolean[] isReprocessCaptures = new boolean[numBurst];
+ for (int i = 0; i < numBurst; i++) {
+ isReprocessCaptures[i] = true;
+ }
- // issue and wait on reprocess capture request
- TotalCaptureResult reprocessResult =
- submitCaptureRequest(mSecondImageReader.getSurface(), result);
+ return doMixedReprocessBurstCapture(isReprocessCaptures);
+ }
- return mSecondImageReaderListener.getImage(CAPTURE_TIMEOUT_MS);
+ /**
+ * Do a burst of captures that are mixed with regular and reprocess captures.
+ *
+ * @param isReprocessCaptures An array whose elements indicate whether it's a reprocess capture
+ * request. If the element is true, it represents a reprocess capture
+ * request. If the element is false, it represents a regular capture
+ * request. The size of the array is the number of capture requests
+ * in the burst.
+ */
+ private ImageResultHolder[] doMixedReprocessBurstCapture(boolean[] isReprocessCaptures)
+ throws Exception {
+ if (isReprocessCaptures == null || isReprocessCaptures.length <= 0) {
+ throw new IllegalArgumentException("isReprocessCaptures must have at least 1 capture.");
+ }
+
+ TotalCaptureResult[] results = new TotalCaptureResult[isReprocessCaptures.length];
+ for (int i = 0; i < isReprocessCaptures.length; i++) {
+ // submit a capture and get the result if this entry is a reprocess capture.
+ if (isReprocessCaptures[i]) {
+ results[i] = submitCaptureRequest(mFirstImageReader.getSurface(),
+ /*inputResult*/null);
+ mImageWriter.queueInputImage(
+ mFirstImageReaderListener.getImage(CAPTURE_TIMEOUT_MS));
+ }
+ }
+
+ Surface[] outputSurfaces = new Surface[isReprocessCaptures.length];
+ for (int i = 0; i < isReprocessCaptures.length; i++) {
+ if (mShareOneImageReader) {
+ outputSurfaces[i] = mFirstImageReader.getSurface();
+ } else {
+ outputSurfaces[i] = mSecondImageReader.getSurface();
+ }
+ }
+
+ TotalCaptureResult[] finalResults = submitMixedCaptureBurstRequest(outputSurfaces, results);
+
+ ImageResultHolder[] holders = new ImageResultHolder[isReprocessCaptures.length];
+ for (int i = 0; i < isReprocessCaptures.length; i++) {
+ Image image;
+ if (mShareOneImageReader) {
+ image = mFirstImageReaderListener.getImage(CAPTURE_TIMEOUT_MS);
+ } else {
+ image = mSecondImageReaderListener.getImage(CAPTURE_TIMEOUT_MS);
+ }
+ holders[i] = new ImageResultHolder(image, finalResults[i]);
+ }
+
+ return holders;
}
/**
@@ -487,25 +772,85 @@
* Issue a capture request and return the result. If inputResult is null, it's a regular
* request. Otherwise, it's a reprocess request.
*/
- private TotalCaptureResult submitCaptureRequest(Surface output, TotalCaptureResult inputResult)
- throws Exception {
- SimpleCaptureCallback captureCallback = new SimpleCaptureCallback();
- CaptureRequest.Builder builder;
- boolean isReprocess = inputResult != null;
- if (isReprocess) {
- builder = mCamera.createReprocessCaptureRequest(inputResult);
- } else {
- builder = mCamera.createCaptureRequest(CAPTURE_TEMPLATE);
+ private TotalCaptureResult submitCaptureRequest(Surface output,
+ TotalCaptureResult inputResult) throws Exception {
+ Surface[] outputs = new Surface[1];
+ outputs[0] = output;
+ TotalCaptureResult[] inputResults = new TotalCaptureResult[1];
+ inputResults[0] = inputResult;
+
+ return submitMixedCaptureBurstRequest(outputs, inputResults)[0];
+ }
+
+ /**
+ * Submit a burst request mixed with regular and reprocess requests.
+ *
+ * @param outputs An array of output surfaces. One output surface will be used in one request
+ * so the length of the array is the number of requests in a burst request.
+ * @param inputResults An array of input results. If it's null, all requests are regular
+ * requests. If an element is null, that element represents a regular
+ * request. If an element if not null, that element represents a reprocess
+ * request.
+ *
+ */
+ private TotalCaptureResult[] submitMixedCaptureBurstRequest(Surface[] outputs,
+ TotalCaptureResult[] inputResults) throws Exception {
+ if (outputs == null || outputs.length <= 0) {
+ throw new IllegalArgumentException("outputs must have at least 1 surface");
+ } else if (inputResults != null && inputResults.length != outputs.length) {
+ throw new IllegalArgumentException("The lengths of outputs and inputResults " +
+ "don't match");
}
- builder.addTarget(output);
- CaptureRequest request = builder.build();
- assertTrue("Capture request reprocess type " + request.isReprocess() + " is wrong.",
- request.isReprocess() == isReprocess);
- mSession.capture(request, captureCallback, mHandler);
+ int numReprocessCaptures = 0;
+ SimpleCaptureCallback captureCallback = new SimpleCaptureCallback();
+ ArrayList<CaptureRequest> captureRequests = new ArrayList<>(outputs.length);
- // wait for regular capture result
- return captureCallback.getTotalCaptureResultForRequest(request, CAPTURE_TIMEOUT_FRAMES);
+ // Prepare a list of capture requests. Whether it's a regular or reprocess capture request
+ // is based on inputResults array.
+ for (int i = 0; i < outputs.length; i++) {
+ CaptureRequest.Builder builder;
+ boolean isReprocess = (inputResults != null && inputResults[i] != null);
+ if (isReprocess) {
+ builder = mCamera.createReprocessCaptureRequest(inputResults[i]);
+ numReprocessCaptures++;
+ } else {
+ builder = mCamera.createCaptureRequest(CAPTURE_TEMPLATE);
+ }
+ builder.addTarget(outputs[i]);
+ CaptureRequest request = builder.build();
+ assertTrue("Capture request reprocess type " + request.isReprocess() + " is wrong.",
+ request.isReprocess() == isReprocess);
+
+ captureRequests.add(request);
+ }
+
+ if (captureRequests.size() == 1) {
+ mSession.capture(captureRequests.get(0), captureCallback, mHandler);
+ } else {
+ mSession.captureBurst(captureRequests, captureCallback, mHandler);
+ }
+
+ TotalCaptureResult[] results;
+ if (numReprocessCaptures == 0 || numReprocessCaptures == outputs.length) {
+ results = new TotalCaptureResult[outputs.length];
+ // If the requests are not mixed, they should come in order.
+ for (int i = 0; i < results.length; i++){
+ results[i] = captureCallback.getTotalCaptureResultForRequest(
+ captureRequests.get(i), CAPTURE_TIMEOUT_FRAMES);
+ }
+ } else {
+ // If the requests are mixed, they may not come in order.
+ results = captureCallback.getTotalCaptureResultsForRequests(
+ captureRequests, CAPTURE_TIMEOUT_FRAMES * captureRequests.size());
+ }
+
+ // make sure all input surfaces are released.
+ for (int i = 0; i < numReprocessCaptures; i++) {
+ mImageWriterListener.waitForImageReleased(CAPTURE_TIMEOUT_MS);
+ }
+
+ return results;
}
private Size getMaxSize(int format, StaticMetadata.StreamDirection direction) {
@@ -520,4 +865,45 @@
private boolean isOpaqueReprocessSupported(String cameraId) throws Exception {
return isReprocessSupported(cameraId, ImageFormat.PRIVATE);
}
-}
\ No newline at end of file
+
+ private void dumpImage(Image image, String name) {
+ String filename = DEBUG_FILE_NAME_BASE + name;
+ switch(image.getFormat()) {
+ case ImageFormat.JPEG:
+ filename += ".jpg";
+ break;
+ case ImageFormat.NV16:
+ case ImageFormat.NV21:
+ case ImageFormat.YUV_420_888:
+ filename += ".yuv";
+ break;
+ default:
+ filename += "." + image.getFormat();
+ break;
+ }
+
+ Log.d(TAG, "dumping an image to " + filename);
+ dumpFile(filename , getDataFromImage(image));
+ }
+
+ /**
+ * A class that holds an Image and a TotalCaptureResult.
+ */
+ private static class ImageResultHolder {
+ private final Image mImage;
+ private final TotalCaptureResult mResult;
+
+ public ImageResultHolder(Image image, TotalCaptureResult result) {
+ mImage = image;
+ mResult = result;
+ }
+
+ public Image getImage() {
+ return mImage;
+ }
+
+ public TotalCaptureResult getTotalCaptureResult() {
+ return mResult;
+ }
+ }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/CameraErrorCollector.java b/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/CameraErrorCollector.java
index 0ee5ffc..9f0c012 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/CameraErrorCollector.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/CameraErrorCollector.java
@@ -22,6 +22,7 @@
import android.hardware.camera2.CaptureRequest.Builder;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.params.MeteringRectangle;
+import android.media.Image;
import android.util.Log;
import android.util.Size;
@@ -1049,4 +1050,13 @@
Set<T> sizeSet = new HashSet<T>(list);
expectTrue(msg + " each element must be distinct", sizeSet.size() == list.size());
}
+
+ public void expectImageProperties(String msg, Image image, int format, Size size,
+ long timestampNs) {
+ expectEquals(msg + "Image format is wrong.", image.getFormat(), format);
+ expectEquals(msg + "Image width is wrong.", image.getWidth(), size.getWidth());
+ expectEquals(msg + "Image height is wrong.", image.getHeight(), size.getHeight());
+ expectEquals(msg + "Image timestamp is wrong.", image.getTimestamp(), timestampNs);
+ }
+
}
diff --git a/tests/tests/media/assets/fileSequence0.ts b/tests/tests/media/assets/fileSequence0.ts
new file mode 100644
index 0000000..48f2bcd
--- /dev/null
+++ b/tests/tests/media/assets/fileSequence0.ts
Binary files differ
diff --git a/tests/tests/media/assets/fileSequence1.ts b/tests/tests/media/assets/fileSequence1.ts
new file mode 100644
index 0000000..737fbd0
--- /dev/null
+++ b/tests/tests/media/assets/fileSequence1.ts
Binary files differ
diff --git a/tests/tests/media/assets/prog_index.m3u8 b/tests/tests/media/assets/prog_index.m3u8
new file mode 100644
index 0000000..88f99d3
--- /dev/null
+++ b/tests/tests/media/assets/prog_index.m3u8
@@ -0,0 +1,10 @@
+#EXTM3U
+#EXT-X-TARGETDURATION:10
+#EXT-X-VERSION:3
+#EXT-X-MEDIA-SEQUENCE:0
+#EXT-X-PLAYLIST-TYPE:VOD
+#EXTINF:9.90000,
+fileSequence0.ts
+#EXTINF:10.00000,
+fileSequence1.ts
+#EXT-X-ENDLIST
diff --git a/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java b/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java
index dd7c1f6..e10d71c 100644
--- a/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java
+++ b/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java
@@ -18,12 +18,16 @@
import android.cts.util.MediaUtils;
import android.media.MediaFormat;
import android.media.MediaPlayer;
+import android.media.MediaPlayer.TrackInfo;
+import android.media.TimedMetaData;
import android.os.Looper;
+import android.os.PowerManager;
import android.os.SystemClock;
import android.util.Log;
import android.webkit.cts.CtsTestServer;
import java.io.IOException;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* Tests of MediaPlayer streaming capabilities.
@@ -307,6 +311,94 @@
localHlsTest("hls.m3u8", false, true);
}
+ public void testPlayHlsStreamWithTimedId3() throws Throwable {
+ mServer = new CtsTestServer(mContext);
+ try {
+ // counter must be final if we want to access it inside onTimedMetaData;
+ // use AtomicInteger so we can have a final counter object with mutable integer value.
+ final AtomicInteger counter = new AtomicInteger();
+ String stream_url = mServer.getAssetUrl("prog_index.m3u8");
+ mMediaPlayer.setDataSource(stream_url);
+ mMediaPlayer.setDisplay(getActivity().getSurfaceHolder());
+ mMediaPlayer.setScreenOnWhilePlaying(true);
+ mMediaPlayer.setWakeMode(mContext, PowerManager.PARTIAL_WAKE_LOCK);
+ mMediaPlayer.setOnTimedMetaDataListener(new MediaPlayer.OnTimedMetaDataListener() {
+ @Override
+ public void onTimedMetaData(MediaPlayer mp, TimedMetaData md) {
+ counter.incrementAndGet();
+ int pos = mp.getCurrentPosition();
+ long timeUs = md.getTimeUs();
+ byte[] rawData = md.getRawData();
+ // Raw data contains an id3 tag holding the decimal string representation of
+ // the associated time stamp rounded to the closest half second.
+
+ int offset = 0;
+ offset += 3; // "ID3"
+ offset += 2; // version
+ offset += 1; // flags
+ offset += 4; // size
+ offset += 4; // "TXXX"
+ offset += 4; // frame size
+ offset += 2; // frame flags
+ offset += 1; // "\x03" : UTF-8 encoded Unicode
+ offset += 1; // "\x00" : null-terminated empty description
+
+ int length = rawData.length;
+ length -= offset;
+ length -= 1; // "\x00" : terminating null
+
+ String data = new String(rawData, offset, length);
+ int dataTimeUs = Integer.parseInt(data);
+ assertTrue("Timed ID3 timestamp does not match content",
+ Math.abs(dataTimeUs - timeUs) < 500000);
+ assertTrue("Timed ID3 arrives after timestamp", pos * 1000 < timeUs);
+ }
+ });
+
+ final Object completion = new Object();
+ mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
+ int run;
+ @Override
+ public void onCompletion(MediaPlayer mp) {
+ if (run++ == 0) {
+ mMediaPlayer.seekTo(0);
+ mMediaPlayer.start();
+ } else {
+ mMediaPlayer.stop();
+ synchronized (completion) {
+ completion.notify();
+ }
+ }
+ }
+ });
+
+ mMediaPlayer.prepare();
+ mMediaPlayer.start();
+ assertTrue("MediaPlayer not playing", mMediaPlayer.isPlaying());
+
+ int i = -1;
+ TrackInfo[] trackInfos = mMediaPlayer.getTrackInfo();
+ for (i = 0; i < trackInfos.length; i++) {
+ TrackInfo trackInfo = trackInfos[i];
+ if (trackInfo.getTrackType() == TrackInfo.MEDIA_TRACK_TYPE_METADATA) {
+ break;
+ }
+ }
+ assertTrue("Stream has no timed ID3 track", i >= 0);
+ mMediaPlayer.selectTrack(i);
+
+ synchronized (completion) {
+ completion.wait();
+ }
+
+ // There are a total of 19 metadata access units in the test stream; every one of them
+ // should be received twice: once before the seek and once after.
+ assertTrue("Incorrect number of timed ID3s recieved", counter.get() == 38);
+ } finally {
+ mServer.shutdown();
+ }
+ }
+
private static class WorkerWithPlayer implements Runnable {
private final Object mLock = new Object();
private Looper mLooper;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicBLAS.java b/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicBLAS.java
index ff5bf84..f6b3176 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicBLAS.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicBLAS.java
@@ -1999,14 +1999,8 @@
if (cM != cN) {
return false;
}
- if (TransA != ScriptIntrinsicBLAS.NO_TRANSPOSE) {
- if (aN != cM) {
- return false;
- }
- } else {
- if (aM != cM) {
- return false;
- }
+ if (aM != cM) {
+ return false;
}
} else if (A != null && B != null) {
// A and B only
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/rsAllocationCopyTest.java b/tests/tests/renderscript/src/android/renderscript/cts/rsAllocationCopyTest.java
new file mode 100644
index 0000000..f74fa38
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/rsAllocationCopyTest.java
@@ -0,0 +1,539 @@
+/*
+ * 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.renderscript.cts;
+
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.Type;
+import java.util.Random;
+import android.util.Log;
+
+public class rsAllocationCopyTest extends RSBaseCompute {
+
+ public void test_rsAllocationCopy1D_Byte() {
+ Random random = new Random(0x172d8ab9);
+ int width = random.nextInt(512);
+ int arr_len = width;
+ int offset = random.nextInt(arr_len);
+ int count = random.nextInt(arr_len - offset);
+
+ byte[] inArray = new byte[arr_len];
+ byte[] outArray = new byte[arr_len];
+ random.nextBytes(inArray);
+
+ Type.Builder typeBuilder = new Type.Builder(mRS, Element.I8(mRS));
+ typeBuilder.setX(width);
+ Allocation aIn = Allocation.createTyped(mRS, typeBuilder.create());
+ Allocation aOut = Allocation.createTyped(mRS, typeBuilder.create());
+ aIn.copyFrom(inArray);
+ aOut.copyFrom(outArray);
+
+ ScriptC_rsallocationcopy s = new ScriptC_rsallocationcopy(mRS);
+ s.set_aIn1D(aIn);
+ s.set_aOut1D(aOut);
+ s.set_xOff(offset);
+ s.set_xCount(count);
+ s.invoke_test1D();
+ mRS.finish();
+ aOut.copyTo(outArray);
+
+ boolean result = true;
+ for (int i = 0; i < arr_len; i++) {
+ if (offset <= i && i < offset + count) {
+ if (inArray[i] != outArray[i]) {
+ result = false;
+ break;
+ }
+ } else {
+ if (outArray[i] != 0) {
+ result = false;
+ break;
+ }
+ }
+ }
+ assertTrue("test_rsAllocationCopy1D_Byte failed, output array does not match input",
+ result);
+ }
+
+ public void test_rsAllocationCopy1D_Short() {
+ Random random = new Random(0x172d8ab9);
+ int width = random.nextInt(512);
+ int arr_len = width;
+ int offset = random.nextInt(arr_len);
+ int count = random.nextInt(arr_len - offset);
+
+ short[] inArray = new short[arr_len];
+ short[] outArray = new short[arr_len];
+ for (int i = 0; i < arr_len; i++) {
+ inArray[i] = (short)random.nextInt();
+ }
+
+ Type.Builder typeBuilder = new Type.Builder(mRS, Element.I16(mRS));
+ typeBuilder.setX(width);
+ Allocation aIn = Allocation.createTyped(mRS, typeBuilder.create());
+ Allocation aOut = Allocation.createTyped(mRS, typeBuilder.create());
+ aIn.copyFrom(inArray);
+ aOut.copyFrom(outArray);
+
+ ScriptC_rsallocationcopy s = new ScriptC_rsallocationcopy(mRS);
+ s.set_aIn1D(aIn);
+ s.set_aOut1D(aOut);
+ s.set_xOff(offset);
+ s.set_xCount(count);
+ s.invoke_test1D();
+ mRS.finish();
+ aOut.copyTo(outArray);
+
+ boolean result = true;
+ for (int i = 0; i < arr_len; i++) {
+ if (offset <= i && i < offset + count) {
+ if (inArray[i] != outArray[i]) {
+ result = false;
+ break;
+ }
+ } else {
+ if (outArray[i] != 0) {
+ result = false;
+ break;
+ }
+ }
+ }
+ assertTrue("test_rsAllocationCopy1D_Short failed, output array does not match input",
+ result);
+ }
+
+ public void test_rsAllocationCopy1D_Int() {
+ Random random = new Random(0x172d8ab9);
+ int width = random.nextInt(512);
+ int arr_len = width;
+ int offset = random.nextInt(arr_len);
+ int count = random.nextInt(arr_len - offset);
+
+ int[] inArray = new int[arr_len];
+ int[] outArray = new int[arr_len];
+ for (int i = 0; i < arr_len; i++) {
+ inArray[i] = random.nextInt();
+ }
+
+ Type.Builder typeBuilder = new Type.Builder(mRS, Element.I32(mRS));
+ typeBuilder.setX(width);
+ Allocation aIn = Allocation.createTyped(mRS, typeBuilder.create());
+ Allocation aOut = Allocation.createTyped(mRS, typeBuilder.create());
+ aIn.copyFrom(inArray);
+ aOut.copyFrom(outArray);
+
+ ScriptC_rsallocationcopy s = new ScriptC_rsallocationcopy(mRS);
+ s.set_aIn1D(aIn);
+ s.set_aOut1D(aOut);
+ s.set_xOff(offset);
+ s.set_xCount(count);
+ s.invoke_test1D();
+ mRS.finish();
+ aOut.copyTo(outArray);
+
+ boolean result = true;
+ for (int i = 0; i < arr_len; i++) {
+ if (offset <= i && i < offset + count) {
+ if (inArray[i] != outArray[i]) {
+ result = false;
+ break;
+ }
+ } else {
+ if (outArray[i] != 0) {
+ result = false;
+ break;
+ }
+ }
+ }
+ assertTrue("test_rsAllocationCopy1D_Int failed, output array does not match input",
+ result);
+ }
+
+ public void test_rsAllocationCopy1D_Float() {
+ Random random = new Random(0x172d8ab9);
+ int width = random.nextInt(512);
+ int arr_len = width;
+ int offset = random.nextInt(arr_len);
+ int count = random.nextInt(arr_len - offset);
+
+ float[] inArray = new float[arr_len];
+ float[] outArray = new float[arr_len];
+ for (int i = 0; i < arr_len; i++) {
+ inArray[i] = random.nextFloat();
+ }
+
+ Type.Builder typeBuilder = new Type.Builder(mRS, Element.F32(mRS));
+ typeBuilder.setX(width);
+ Allocation aIn = Allocation.createTyped(mRS, typeBuilder.create());
+ Allocation aOut = Allocation.createTyped(mRS, typeBuilder.create());
+ aIn.copyFrom(inArray);
+ aOut.copyFrom(outArray);
+
+ ScriptC_rsallocationcopy s = new ScriptC_rsallocationcopy(mRS);
+ s.set_aIn1D(aIn);
+ s.set_aOut1D(aOut);
+ s.set_xOff(offset);
+ s.set_xCount(count);
+ s.invoke_test1D();
+ mRS.finish();
+ aOut.copyTo(outArray);
+
+
+ boolean result = true;
+ for (int i = 0; i < arr_len; i++) {
+ if (offset <= i && i < offset + count) {
+ if (inArray[i] != outArray[i]) {
+ result = false;
+ break;
+ }
+ } else {
+ if (outArray[i] != 0) {
+ result = false;
+ break;
+ }
+ }
+ }
+ assertTrue("test_rsAllocationCopy1D_Float failed, output array does not match input",
+ result);
+ }
+
+ public void test_rsAllocationCopy1D_Long() {
+ Random random = new Random(0x172d8ab9);
+ int width = random.nextInt(512);
+ int arr_len = width;
+ int offset = random.nextInt(arr_len);
+ int count = random.nextInt(arr_len - offset);
+
+ long[] inArray = new long[arr_len];
+ long[] outArray = new long[arr_len];
+ for (int i = 0; i < arr_len; i++) {
+ inArray[i] = random.nextLong();
+ }
+
+ Type.Builder typeBuilder = new Type.Builder(mRS, Element.I64(mRS));
+ typeBuilder.setX(width);
+ Allocation aIn = Allocation.createTyped(mRS, typeBuilder.create());
+ Allocation aOut = Allocation.createTyped(mRS, typeBuilder.create());
+ aIn.copyFrom(inArray);
+ aOut.copyFrom(outArray);
+
+ ScriptC_rsallocationcopy s = new ScriptC_rsallocationcopy(mRS);
+ s.set_aIn1D(aIn);
+ s.set_aOut1D(aOut);
+ s.set_xOff(offset);
+ s.set_xCount(count);
+ s.invoke_test1D();
+ mRS.finish();
+ aOut.copyTo(outArray);
+
+ boolean result = true;
+ for (int i = 0; i < arr_len; i++) {
+ if (offset <= i && i < offset + count) {
+ if (inArray[i] != outArray[i]) {
+ result = false;
+ break;
+ }
+ } else {
+ if (outArray[i] != 0) {
+ result = false;
+ break;
+ }
+ }
+ }
+ assertTrue("test_rsAllocationCopy1D_Long failed, output array does not match input",
+ result);
+ }
+
+
+ public void test_rsAllocationCopy2D_Byte() {
+ Random random = new Random(0x172d8ab9);
+ int width = random.nextInt(128);
+ int height = random.nextInt(128);
+ int xOff = random.nextInt(width);
+ int yOff = random.nextInt(height);
+ int xCount = random.nextInt(width - xOff);
+ int yCount = random.nextInt(height - yOff);
+ int arr_len = width * height;
+
+ byte[] inArray = new byte[arr_len];
+ byte[] outArray = new byte[arr_len];
+ random.nextBytes(inArray);
+
+ Type.Builder typeBuilder = new Type.Builder(mRS, Element.I8(mRS));
+ typeBuilder.setX(width).setY(height);
+ Allocation aIn = Allocation.createTyped(mRS, typeBuilder.create());
+ Allocation aOut = Allocation.createTyped(mRS, typeBuilder.create());
+ aIn.copyFrom(inArray);
+ aOut.copyFrom(outArray);
+
+ ScriptC_rsallocationcopy s = new ScriptC_rsallocationcopy(mRS);
+ s.set_aIn2D(aIn);
+ s.set_aOut2D(aOut);
+ s.set_xOff(xOff);
+ s.set_yOff(yOff);
+ s.set_xCount(xCount);
+ s.set_yCount(yCount);
+ s.invoke_test2D();
+ mRS.finish();
+ aOut.copyTo(outArray);
+
+ boolean result = true;
+ for (int i = 0; i < height; i++) {
+ for (int j = 0; j < width; j++) {
+ int pos = i * width + j;
+ if (yOff <= i && i < yOff + yCount &&
+ xOff <= j && j < xOff + xCount) {
+ if (inArray[pos] != outArray[pos]) {
+ result = false;
+ break;
+ }
+ } else {
+ if (outArray[pos] != 0) {
+ result = false;
+ break;
+ }
+ }
+ }
+ }
+ assertTrue("test_rsAllocationCopy2D_Byte failed, output array does not match input",
+ result);
+ }
+
+ public void test_rsAllocationCopy2D_Short() {
+ Random random = new Random(0x172d8ab9);
+ int width = random.nextInt(128);
+ int height = random.nextInt(128);
+ int xOff = random.nextInt(width);
+ int yOff = random.nextInt(height);
+ int xCount = random.nextInt(width - xOff);
+ int yCount = random.nextInt(height - yOff);
+ int arr_len = width * height;
+
+ short[] inArray = new short[arr_len];
+ short[] outArray = new short[arr_len];
+ for (int i = 0; i < arr_len; i++) {
+ inArray[i] = (short)random.nextInt();
+ }
+
+ Type.Builder typeBuilder = new Type.Builder(mRS, Element.I16(mRS));
+ typeBuilder.setX(width).setY(height);
+ Allocation aIn = Allocation.createTyped(mRS, typeBuilder.create());
+ Allocation aOut = Allocation.createTyped(mRS, typeBuilder.create());
+ aIn.copyFrom(inArray);
+ aOut.copyFrom(outArray);
+
+ ScriptC_rsallocationcopy s = new ScriptC_rsallocationcopy(mRS);
+ s.set_aIn2D(aIn);
+ s.set_aOut2D(aOut);
+ s.set_xOff(xOff);
+ s.set_yOff(yOff);
+ s.set_xCount(xCount);
+ s.set_yCount(yCount);
+ s.invoke_test2D();
+ mRS.finish();
+ aOut.copyTo(outArray);
+
+ boolean result = true;
+ for (int i = 0; i < height; i++) {
+ for (int j = 0; j < width; j++) {
+ int pos = i * width + j;
+ if (yOff <= i && i < yOff + yCount &&
+ xOff <= j && j < xOff + xCount) {
+ if (inArray[pos] != outArray[pos]) {
+ result = false;
+ break;
+ }
+ } else {
+ if (outArray[pos] != 0) {
+ result = false;
+ break;
+ }
+ }
+ }
+ }
+ assertTrue("test_rsAllocationCopy2D_Short failed, output array does not match input",
+ result);
+ }
+
+ public void test_rsAllocationCopy2D_Int() {
+ Random random = new Random(0x172d8ab9);
+ int width = random.nextInt(128);
+ int height = random.nextInt(128);
+ int xOff = random.nextInt(width);
+ int yOff = random.nextInt(height);
+ int xCount = random.nextInt(width - xOff);
+ int yCount = random.nextInt(height - yOff);
+ int arr_len = width * height;
+
+ int[] inArray = new int[arr_len];
+ int[] outArray = new int[arr_len];
+ for (int i = 0; i < arr_len; i++) {
+ inArray[i] = random.nextInt();
+ }
+
+ Type.Builder typeBuilder = new Type.Builder(mRS, Element.I32(mRS));
+ typeBuilder.setX(width).setY(height);
+ Allocation aIn = Allocation.createTyped(mRS, typeBuilder.create());
+ Allocation aOut = Allocation.createTyped(mRS, typeBuilder.create());
+ aIn.copyFrom(inArray);
+ aOut.copyFrom(outArray);
+
+ ScriptC_rsallocationcopy s = new ScriptC_rsallocationcopy(mRS);
+ s.set_aIn2D(aIn);
+ s.set_aOut2D(aOut);
+ s.set_xOff(xOff);
+ s.set_yOff(yOff);
+ s.set_xCount(xCount);
+ s.set_yCount(yCount);
+ s.invoke_test2D();
+ mRS.finish();
+ aOut.copyTo(outArray);
+
+ boolean result = true;
+ for (int i = 0; i < height; i++) {
+ for (int j = 0; j < width; j++) {
+ int pos = i * width + j;
+ if (yOff <= i && i < yOff + yCount &&
+ xOff <= j && j < xOff + xCount) {
+ if (inArray[pos] != outArray[pos]) {
+ result = false;
+ break;
+ }
+ } else {
+ if (outArray[pos] != 0) {
+ result = false;
+ break;
+ }
+ }
+ }
+ }
+ assertTrue("test_rsAllocationCopy2D_Int failed, output array does not match input",
+ result);
+ }
+
+ public void test_rsAllocationCopy2D_Float() {
+ Random random = new Random(0x172d8ab9);
+ int width = random.nextInt(128);
+ int height = random.nextInt(128);
+ int xOff = random.nextInt(width);
+ int yOff = random.nextInt(height);
+ int xCount = random.nextInt(width - xOff);
+ int yCount = random.nextInt(height - yOff);
+ int arr_len = width * height;
+
+ float[] inArray = new float[arr_len];
+ float[] outArray = new float[arr_len];
+ for (int i = 0; i < arr_len; i++) {
+ inArray[i] = random.nextFloat();
+ }
+
+ Type.Builder typeBuilder = new Type.Builder(mRS, Element.F32(mRS));
+ typeBuilder.setX(width).setY(height);
+ Allocation aIn = Allocation.createTyped(mRS, typeBuilder.create());
+ Allocation aOut = Allocation.createTyped(mRS, typeBuilder.create());
+ aIn.copyFrom(inArray);
+ aOut.copyFrom(outArray);
+
+ ScriptC_rsallocationcopy s = new ScriptC_rsallocationcopy(mRS);
+ s.set_aIn2D(aIn);
+ s.set_aOut2D(aOut);
+ s.set_xOff(xOff);
+ s.set_yOff(yOff);
+ s.set_xCount(xCount);
+ s.set_yCount(yCount);
+ s.invoke_test2D();
+ mRS.finish();
+ aOut.copyTo(outArray);
+
+ boolean result = true;
+ for (int i = 0; i < height; i++) {
+ for (int j = 0; j < width; j++) {
+ int pos = i * width + j;
+ if (yOff <= i && i < yOff + yCount &&
+ xOff <= j && j < xOff + xCount) {
+ if (inArray[pos] != outArray[pos]) {
+ result = false;
+ break;
+ }
+ } else {
+ if (outArray[pos] != 0) {
+ result = false;
+ break;
+ }
+ }
+ }
+ }
+ assertTrue("test_rsAllocationCopy2D_Float failed, output array does not match input",
+ result);
+ }
+
+ public void test_rsAllocationCopy2D_Long() {
+ Random random = new Random(0x172d8ab9);
+ int width = random.nextInt(128);
+ int height = random.nextInt(128);
+ int xOff = random.nextInt(width);
+ int yOff = random.nextInt(height);
+ int xCount = random.nextInt(width - xOff);
+ int yCount = random.nextInt(height - yOff);
+ int arr_len = width * height;
+
+ long[] inArray = new long[arr_len];
+ long[] outArray = new long[arr_len];
+ for (int i = 0; i < arr_len; i++) {
+ inArray[i] = random.nextLong();
+ }
+
+ Type.Builder typeBuilder = new Type.Builder(mRS, Element.I64(mRS));
+ typeBuilder.setX(width).setY(height);
+ Allocation aIn = Allocation.createTyped(mRS, typeBuilder.create());
+ Allocation aOut = Allocation.createTyped(mRS, typeBuilder.create());
+ aIn.copyFrom(inArray);
+ aOut.copyFrom(outArray);
+
+ ScriptC_rsallocationcopy s = new ScriptC_rsallocationcopy(mRS);
+ s.set_aIn2D(aIn);
+ s.set_aOut2D(aOut);
+ s.set_xOff(xOff);
+ s.set_yOff(yOff);
+ s.set_xCount(xCount);
+ s.set_yCount(yCount);
+ s.invoke_test2D();
+ mRS.finish();
+ aOut.copyTo(outArray);
+
+ boolean result = true;
+ for (int i = 0; i < height; i++) {
+ for (int j = 0; j < width; j++) {
+ int pos = i * width + j;
+ if (yOff <= i && i < yOff + yCount &&
+ xOff <= j && j < xOff + xCount) {
+ if (inArray[pos] != outArray[pos]) {
+ result = false;
+ break;
+ }
+ } else {
+ if (outArray[pos] != 0) {
+ result = false;
+ break;
+ }
+ }
+ }
+ }
+ assertTrue("test_rsAllocationCopy2D_Long failed, output array does not match input",
+ result);
+ }
+}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/rsallocationcopy.rs b/tests/tests/renderscript/src/android/renderscript/cts/rsallocationcopy.rs
new file mode 100644
index 0000000..4d76493
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/rsallocationcopy.rs
@@ -0,0 +1,19 @@
+#include "shared.rsh"
+
+rs_allocation aIn1D;
+rs_allocation aOut1D;
+rs_allocation aIn2D;
+rs_allocation aOut2D;
+
+int xOff = 0;
+int yOff = 0;
+int xCount = 0;
+int yCount = 0;
+
+void test1D() {
+ rsAllocationCopy1DRange(aOut1D, xOff, 0, xCount, aIn1D, xOff, 0);
+}
+
+void test2D() {
+ rsAllocationCopy2DRange(aOut2D, xOff, yOff, 0, 0, xCount, yCount, aIn2D, xOff, yOff, 0, 0);
+}
diff --git a/tests/tests/webkit/src/android/webkit/cts/PostMessageTest.java b/tests/tests/webkit/src/android/webkit/cts/PostMessageTest.java
new file mode 100644
index 0000000..e393bb6
--- /dev/null
+++ b/tests/tests/webkit/src/android/webkit/cts/PostMessageTest.java
@@ -0,0 +1,222 @@
+/*
+ * 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.webkit.cts;
+
+import android.cts.util.NullWebViewUtils;
+import android.cts.util.PollingCheck;
+import android.net.Uri;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.UiThreadTest;
+import android.webkit.WebMessage;
+import android.webkit.WebMessagePort;
+import android.webkit.WebView;
+
+import java.util.concurrent.CountDownLatch;
+import junit.framework.Assert;
+
+public class PostMessageTest extends ActivityInstrumentationTestCase2<WebViewCtsActivity> {
+ public static final long TIMEOUT = 20000L;
+
+ private WebView mWebView;
+ private WebViewOnUiThread mOnUiThread;
+
+ private static final String WEBVIEW_MESSAGE = "from_webview";
+ private static final String BASE_URI = "http://www.example.com";
+
+ public PostMessageTest() {
+ super("com.android.cts.webkit", WebViewCtsActivity.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ final WebViewCtsActivity activity = getActivity();
+ mWebView = activity.getWebView();
+ if (mWebView != null) {
+ mOnUiThread = new WebViewOnUiThread(this, mWebView);
+ mOnUiThread.getSettings().setJavaScriptEnabled(true);
+ }
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ if (mOnUiThread != null) {
+ mOnUiThread.cleanUp();
+ }
+ super.tearDown();
+ }
+
+ private static final String TITLE_FROM_POST_MESSAGE =
+ "<!DOCTYPE html><html><body>"
+ + " <script>"
+ + " var received = '';"
+ + " onmessage = function (e) {"
+ + " received += e.data;"
+ + " document.title = received; };"
+ + " </script>"
+ + "</body></html>";
+
+ // Acks each received message from the message channel with a seq number.
+ private static final String CHANNEL_MESSAGE =
+ "<!DOCTYPE html><html><body>"
+ + " <script>"
+ + " var counter = 0;"
+ + " onmessage = function (e) {"
+ + " var myPort = e.ports[0];"
+ + " myPort.onmessage = function (f) {"
+ + " myPort.postMessage(f.data + counter++);"
+ + " }"
+ + " }"
+ + " </script>"
+ + "</body></html>";
+
+ private void loadPage(String data) {
+ mOnUiThread.loadDataWithBaseURLAndWaitForCompletion(BASE_URI, data,
+ "text/html", "UTF-8", null);
+ }
+
+ private void waitForTitle(final String title) {
+ new PollingCheck(TIMEOUT) {
+ @Override
+ protected boolean check() {
+ return mOnUiThread.getTitle().equals(title);
+ }
+ }.run();
+ }
+
+ // Post a string message to main frame and make sure it is received.
+ public void testSimpleMessageToMainFrame() throws Throwable {
+ if (!NullWebViewUtils.isWebViewAvailable()) {
+ return;
+ }
+ loadPage(TITLE_FROM_POST_MESSAGE);
+ WebMessage message = new WebMessage(WEBVIEW_MESSAGE);
+ mOnUiThread.postMessageToMainFrame(message, Uri.parse(BASE_URI));
+ waitForTitle(WEBVIEW_MESSAGE);
+ }
+
+ // Post multiple messages to main frame and make sure they are received in
+ // correct order.
+ public void testMultipleMessagesToMainFrame() throws Throwable {
+ if (!NullWebViewUtils.isWebViewAvailable()) {
+ return;
+ }
+ loadPage(TITLE_FROM_POST_MESSAGE);
+ for (int i = 0; i < 10; i++) {
+ mOnUiThread.postMessageToMainFrame(new WebMessage(Integer.toString(i)),
+ Uri.parse(BASE_URI));
+ }
+ waitForTitle("0123456789");
+ }
+
+ // Create a message channel and make sure it can be used for data transfer to/from js.
+ public void testMessageChannel() throws Throwable {
+ if (!NullWebViewUtils.isWebViewAvailable()) {
+ return;
+ }
+ loadPage(CHANNEL_MESSAGE);
+ final WebMessagePort[] channel = mOnUiThread.createWebMessageChannel();
+ WebMessage message = new WebMessage(WEBVIEW_MESSAGE, new WebMessagePort[]{channel[1]});
+ mOnUiThread.postMessageToMainFrame(message, Uri.parse(BASE_URI));
+ final int messageCount = 3;
+ final CountDownLatch latch = new CountDownLatch(messageCount);
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ for (int i = 0; i < messageCount; i++) {
+ channel[0].postMessage(new WebMessage(WEBVIEW_MESSAGE + i));
+ }
+ channel[0].setWebMessageCallback(new WebMessagePort.WebMessageCallback() {
+ @Override
+ public void onMessage(WebMessagePort port, WebMessage message) {
+ int i = messageCount - (int)latch.getCount();
+ assertEquals(WEBVIEW_MESSAGE + i + i, message.getData());
+ latch.countDown();
+ }
+ });
+ }
+ });
+ // Wait for all the responses to arrive.
+ boolean ignore = latch.await(TIMEOUT, java.util.concurrent.TimeUnit.MILLISECONDS);
+ }
+
+ // Test that a message port that is closed cannot used to send a message
+ public void testClose() throws Throwable {
+ if (!NullWebViewUtils.isWebViewAvailable()) {
+ return;
+ }
+ loadPage(CHANNEL_MESSAGE);
+ final WebMessagePort[] channel = mOnUiThread.createWebMessageChannel();
+ WebMessage message = new WebMessage(WEBVIEW_MESSAGE, new WebMessagePort[]{channel[1]});
+ mOnUiThread.postMessageToMainFrame(message, Uri.parse(BASE_URI));
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ channel[0].close();
+ channel[0].postMessage(new WebMessage(WEBVIEW_MESSAGE));
+ } catch (IllegalStateException ex) {
+ // expect to receive an exception
+ return;
+ }
+ Assert.fail("A closed port cannot be used to transfer messages");
+ }
+ });
+ }
+
+ // Sends a new message channel from JS to Java.
+ private static final String CHANNEL_FROM_JS =
+ "<!DOCTYPE html><html><body>"
+ + " <script>"
+ + " var counter = 0;"
+ + " var mc = new MessageChannel();"
+ + " var received = '';"
+ + " mc.port1.onmessage = function (e) {"
+ + " received = e.data;"
+ + " document.title = e.data;"
+ + " };"
+ + " onmessage = function (e) {"
+ + " var myPort = e.ports[0];"
+ + " myPort.postMessage('', [mc.port2]);"
+ + " };"
+ + " </script>"
+ + "</body></html>";
+
+ // Test a message port created in JS can be received and used for message transfer.
+ public void testReceiveMessagePort() throws Throwable {
+ final String hello = "HELLO";
+ if (!NullWebViewUtils.isWebViewAvailable()) {
+ return;
+ }
+ loadPage(CHANNEL_FROM_JS);
+ final WebMessagePort[] channel = mOnUiThread.createWebMessageChannel();
+ WebMessage message = new WebMessage(WEBVIEW_MESSAGE, new WebMessagePort[]{channel[1]});
+ mOnUiThread.postMessageToMainFrame(message, Uri.parse(BASE_URI));
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ channel[0].setWebMessageCallback(new WebMessagePort.WebMessageCallback() {
+ @Override
+ public void onMessage(WebMessagePort port, WebMessage message) {
+ message.getPorts()[0].postMessage(new WebMessage(hello));
+ }
+ });
+ }
+ });
+ waitForTitle(hello);
+ }
+}