Commit 1e0fbe99 authored by Finnur Thorarinsson's avatar Finnur Thorarinsson Committed by Commit Bot

[Android] Photo Picker: Enforce UI thread for DecoderServiceHost.

Chromium uses the DecoderServiceHost on the UI thread, but the test
was using it from the test thread, which causes flakiness due to
multi-threading issues (decoder replies are bounced to the UI thread).
Flakiness was discovered locally while testing another thing (no
special bug linked).

Also marked a couple of functions private (that don't need to be
public).

Bug: 656015
Change-Id: Iaae56f6ce10b9747c3deba6f2318ecc421910050
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2074758Reviewed-by: default avatarBoris Sazonov <bsazonov@chromium.org>
Commit-Queue: Finnur Thorarinsson <finnur@chromium.org>
Cr-Commit-Position: refs/heads/master@{#744704}
parent 2bd2b61a
...@@ -24,6 +24,7 @@ import androidx.annotation.Nullable; ...@@ -24,6 +24,7 @@ import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import org.chromium.base.Log; import org.chromium.base.Log;
import org.chromium.base.ThreadUtils;
import org.chromium.base.metrics.RecordHistogram; import org.chromium.base.metrics.RecordHistogram;
import org.chromium.base.task.AsyncTask; import org.chromium.base.task.AsyncTask;
import org.chromium.base.task.PostTask; import org.chromium.base.task.PostTask;
...@@ -248,6 +249,8 @@ public class DecoderServiceHost ...@@ -248,6 +249,8 @@ public class DecoderServiceHost
*/ */
public void decodeImage(Uri uri, @PickerBitmap.TileTypes int fileType, int width, public void decodeImage(Uri uri, @PickerBitmap.TileTypes int fileType, int width,
boolean fullWidth, ImagesDecodedCallback callback) { boolean fullWidth, ImagesDecodedCallback callback) {
ThreadUtils.assertOnUiThread();
DecoderServiceParams params = new DecoderServiceParams( DecoderServiceParams params = new DecoderServiceParams(
uri, width, fullWidth, fileType, /*firstFrame=*/true, callback); uri, width, fullWidth, fileType, /*firstFrame=*/true, callback);
mPendingRequests.add(params); mPendingRequests.add(params);
...@@ -409,7 +412,7 @@ public class DecoderServiceHost ...@@ -409,7 +412,7 @@ public class DecoderServiceHost
}); });
} }
public void closeRequestWithError(String filePath) { private void closeRequestWithError(String filePath) {
closeRequest(filePath, false, false, null, null, -1, 1.0f); closeRequest(filePath, false, false, null, null, -1, 1.0f);
} }
...@@ -424,7 +427,7 @@ public class DecoderServiceHost ...@@ -424,7 +427,7 @@ public class DecoderServiceHost
* @param decodeTime The length of time it took to decode the bitmaps. * @param decodeTime The length of time it took to decode the bitmaps.
* @param ratio The ratio of the images (>1.0=portrait, <1.0=landscape). * @param ratio The ratio of the images (>1.0=portrait, <1.0=landscape).
*/ */
public void closeRequest(String filePath, boolean isVideo, boolean fullWidth, private void closeRequest(String filePath, boolean isVideo, boolean fullWidth,
@Nullable List<Bitmap> bitmaps, String videoDuration, long decodeTime, float ratio) { @Nullable List<Bitmap> bitmaps, String videoDuration, long decodeTime, float ratio) {
// If this assert triggers, it means that simultaneous requests have been sent for // If this assert triggers, it means that simultaneous requests have been sent for
// decoding, which should not happen. // decoding, which should not happen.
...@@ -541,6 +544,8 @@ public class DecoderServiceHost ...@@ -541,6 +544,8 @@ public class DecoderServiceHost
* @param filePath The path to the image to cancel decoding. * @param filePath The path to the image to cancel decoding.
*/ */
public void cancelDecodeImage(String filePath) { public void cancelDecodeImage(String filePath) {
ThreadUtils.assertOnUiThread();
// It is important not to null out only pending requests and not mProcessingRequest, because // It is important not to null out only pending requests and not mProcessingRequest, because
// it is used as a signal to see if the decoder is busy. // it is used as a signal to see if the decoder is busy.
Iterator it = mPendingRequests.iterator(); Iterator it = mPendingRequests.iterator();
......
...@@ -25,6 +25,7 @@ import org.chromium.chrome.browser.ChromeActivity; ...@@ -25,6 +25,7 @@ import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.ChromeSwitches; import org.chromium.chrome.browser.ChromeSwitches;
import org.chromium.chrome.test.ChromeActivityTestRule; import org.chromium.chrome.test.ChromeActivityTestRule;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner; import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.content_public.browser.test.util.TestThreadUtils;
import java.io.File; import java.io.File;
import java.util.List; import java.util.List;
...@@ -101,6 +102,16 @@ public class DecoderServiceHostTest implements DecoderServiceHost.ServiceReadyCa ...@@ -101,6 +102,16 @@ public class DecoderServiceHostTest implements DecoderServiceHost.ServiceReadyCa
onDecodedCallback.waitForCallback(callCount, 1, WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS); onDecodedCallback.waitForCallback(callCount, 1, WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS);
} }
private void decodeImage(DecoderServiceHost host, Uri uri, @PickerBitmap.TileTypes int fileType,
int width, boolean fullWidth, DecoderServiceHost.ImagesDecodedCallback callback) {
TestThreadUtils.runOnUiThreadBlocking(
() -> host.decodeImage(uri, fileType, width, fullWidth, callback));
}
private void cancelDecodeImage(DecoderServiceHost host, String filePath) {
TestThreadUtils.runOnUiThreadBlocking(() -> host.cancelDecodeImage(filePath));
}
@Test @Test
@LargeTest @LargeTest
public void testDecodingOrder() throws Throwable { public void testDecodingOrder() throws Throwable {
...@@ -116,12 +127,12 @@ public class DecoderServiceHostTest implements DecoderServiceHost.ServiceReadyCa ...@@ -116,12 +127,12 @@ public class DecoderServiceHostTest implements DecoderServiceHost.ServiceReadyCa
File file2 = new File(UrlUtils.getIsolatedTestFilePath(filePath + video2)); File file2 = new File(UrlUtils.getIsolatedTestFilePath(filePath + video2));
File file3 = new File(UrlUtils.getIsolatedTestFilePath(filePath + jpg1)); File file3 = new File(UrlUtils.getIsolatedTestFilePath(filePath + jpg1));
host.decodeImage( decodeImage(host, Uri.fromFile(file1), PickerBitmap.TileTypes.VIDEO, 10,
Uri.fromFile(file1), PickerBitmap.TileTypes.VIDEO, 10, /*fullWidth=*/false, this); /*fullWidth=*/false, this);
host.decodeImage( decodeImage(host, Uri.fromFile(file2), PickerBitmap.TileTypes.VIDEO, 10,
Uri.fromFile(file2), PickerBitmap.TileTypes.VIDEO, 10, /*fullWidth=*/false, this); /*fullWidth=*/false, this);
host.decodeImage( decodeImage(host, Uri.fromFile(file3), PickerBitmap.TileTypes.PICTURE, 10,
Uri.fromFile(file3), PickerBitmap.TileTypes.PICTURE, 10, /*fullWidth=*/false, this); /*fullWidth=*/false, this);
// First decoding result should be first frame of video 1. Even though still images take // First decoding result should be first frame of video 1. Even though still images take
// priority over video decoding, video 1 will be the only item in the queue when the first // priority over video decoding, video 1 will be the only item in the queue when the first
...@@ -180,8 +191,8 @@ public class DecoderServiceHostTest implements DecoderServiceHost.ServiceReadyCa ...@@ -180,8 +191,8 @@ public class DecoderServiceHostTest implements DecoderServiceHost.ServiceReadyCa
File file2 = new File(UrlUtils.getIsolatedTestFilePath(filePath + jpg1)); File file2 = new File(UrlUtils.getIsolatedTestFilePath(filePath + jpg1));
// Thumbnail photo. 100 x 100 -> 10 x 10. // Thumbnail photo. 100 x 100 -> 10 x 10.
host.decodeImage( decodeImage(host, Uri.fromFile(file2), PickerBitmap.TileTypes.PICTURE, 10,
Uri.fromFile(file2), PickerBitmap.TileTypes.PICTURE, 10, /*fullWidth=*/false, this); /*fullWidth=*/false, this);
waitForThumbnailDecode(); waitForThumbnailDecode();
Assert.assertTrue(mLastDecodedPath.contains(jpg1)); Assert.assertTrue(mLastDecodedPath.contains(jpg1));
Assert.assertEquals(false, mLastIsVideo); Assert.assertEquals(false, mLastIsVideo);
...@@ -192,8 +203,8 @@ public class DecoderServiceHostTest implements DecoderServiceHost.ServiceReadyCa ...@@ -192,8 +203,8 @@ public class DecoderServiceHostTest implements DecoderServiceHost.ServiceReadyCa
Assert.assertEquals(10, mLastInitialFrame.getHeight()); Assert.assertEquals(10, mLastInitialFrame.getHeight());
// Full-width photo. 100 x 100 -> 200 x 200. // Full-width photo. 100 x 100 -> 200 x 200.
host.decodeImage( decodeImage(host, Uri.fromFile(file2), PickerBitmap.TileTypes.PICTURE, 200,
Uri.fromFile(file2), PickerBitmap.TileTypes.PICTURE, 200, /*fullWidth=*/true, this); /*fullWidth=*/true, this);
waitForThumbnailDecode(); waitForThumbnailDecode();
Assert.assertTrue(mLastDecodedPath.contains(jpg1)); Assert.assertTrue(mLastDecodedPath.contains(jpg1));
Assert.assertEquals(false, mLastIsVideo); Assert.assertEquals(false, mLastIsVideo);
...@@ -204,8 +215,8 @@ public class DecoderServiceHostTest implements DecoderServiceHost.ServiceReadyCa ...@@ -204,8 +215,8 @@ public class DecoderServiceHostTest implements DecoderServiceHost.ServiceReadyCa
Assert.assertEquals(200, mLastInitialFrame.getHeight()); Assert.assertEquals(200, mLastInitialFrame.getHeight());
// Thumbnail video. 1920 x 1080 -> 10 x 10. // Thumbnail video. 1920 x 1080 -> 10 x 10.
host.decodeImage( decodeImage(host, Uri.fromFile(file1), PickerBitmap.TileTypes.VIDEO, 10,
Uri.fromFile(file1), PickerBitmap.TileTypes.VIDEO, 10, /*fullWidth=*/false, this); /*fullWidth=*/false, this);
waitForThumbnailDecode(); // Initial frame. waitForThumbnailDecode(); // Initial frame.
Assert.assertTrue(mLastDecodedPath.contains(video1)); Assert.assertTrue(mLastDecodedPath.contains(video1));
Assert.assertEquals(true, mLastIsVideo); Assert.assertEquals(true, mLastIsVideo);
...@@ -224,8 +235,8 @@ public class DecoderServiceHostTest implements DecoderServiceHost.ServiceReadyCa ...@@ -224,8 +235,8 @@ public class DecoderServiceHostTest implements DecoderServiceHost.ServiceReadyCa
Assert.assertEquals(10, mLastInitialFrame.getHeight()); Assert.assertEquals(10, mLastInitialFrame.getHeight());
// Full-width video. 1920 x 1080 -> 2000 x 1125. // Full-width video. 1920 x 1080 -> 2000 x 1125.
host.decodeImage( decodeImage(host, Uri.fromFile(file1), PickerBitmap.TileTypes.VIDEO, 2000,
Uri.fromFile(file1), PickerBitmap.TileTypes.VIDEO, 2000, /*fullWidth=*/true, this); /*fullWidth=*/true, this);
waitForThumbnailDecode(); // Initial frame. waitForThumbnailDecode(); // Initial frame.
Assert.assertTrue(mLastDecodedPath.contains(video1)); Assert.assertTrue(mLastDecodedPath.contains(video1));
Assert.assertEquals(true, mLastIsVideo); Assert.assertEquals(true, mLastIsVideo);
...@@ -262,15 +273,15 @@ public class DecoderServiceHostTest implements DecoderServiceHost.ServiceReadyCa ...@@ -262,15 +273,15 @@ public class DecoderServiceHostTest implements DecoderServiceHost.ServiceReadyCa
String yellowPath = UrlUtils.getIsolatedTestFilePath(filePath + yellow); String yellowPath = UrlUtils.getIsolatedTestFilePath(filePath + yellow);
String redPath = UrlUtils.getIsolatedTestFilePath(filePath + red); String redPath = UrlUtils.getIsolatedTestFilePath(filePath + red);
host.decodeImage(Uri.fromFile(new File(greenPath)), PickerBitmap.TileTypes.PICTURE, 10, decodeImage(host, Uri.fromFile(new File(greenPath)), PickerBitmap.TileTypes.PICTURE, 10,
/*fullWidth=*/false, this); /*fullWidth=*/false, this);
host.decodeImage(Uri.fromFile(new File(yellowPath)), PickerBitmap.TileTypes.PICTURE, 10, decodeImage(host, Uri.fromFile(new File(yellowPath)), PickerBitmap.TileTypes.PICTURE, 10,
/*fullWidth=*/false, this); /*fullWidth=*/false, this);
// Now add and subsequently remove the request. // Now add and subsequently remove the request.
host.decodeImage(Uri.fromFile(new File(redPath)), PickerBitmap.TileTypes.PICTURE, 10, decodeImage(host, Uri.fromFile(new File(redPath)), PickerBitmap.TileTypes.PICTURE, 10,
/*fullWidth=*/false, this); /*fullWidth=*/false, this);
host.cancelDecodeImage(redPath); cancelDecodeImage(host, redPath);
// First decoding result should be the green image. // First decoding result should be the green image.
waitForThumbnailDecode(); waitForThumbnailDecode();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment