diff --git a/engine/src/flutter/testing/scenario_app/android/app/src/androidTest/java/dev/flutter/scenariosui/ScreenshotUtil.java b/engine/src/flutter/testing/scenario_app/android/app/src/androidTest/java/dev/flutter/scenariosui/ScreenshotUtil.java index dd3319df598..4ee963ef0a5 100644 --- a/engine/src/flutter/testing/scenario_app/android/app/src/androidTest/java/dev/flutter/scenariosui/ScreenshotUtil.java +++ b/engine/src/flutter/testing/scenario_app/android/app/src/androidTest/java/dev/flutter/scenariosui/ScreenshotUtil.java @@ -4,15 +4,14 @@ package dev.flutter.scenariosui; -import android.graphics.Bitmap; import androidx.annotation.NonNull; -import androidx.test.InstrumentationRegistry; import dev.flutter.scenarios.TestableFlutterActivity; -import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.nio.ByteBuffer; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.Executors; @@ -30,24 +29,28 @@ public class ScreenshotUtil { private static class Connection { final Socket clientSocket; final OutputStream out; + final InputStream in; Connection(Socket socket) throws IOException { clientSocket = socket; out = socket.getOutputStream(); + in = socket.getInputStream(); } - synchronized void writeFile(String name, byte[] fileContent, int pixelCount) - throws IOException { - final ByteBuffer buffer = ByteBuffer.allocate(name.length() + fileContent.length + 12); + synchronized void writeFile(String name) throws IOException { + final ByteBuffer buffer = ByteBuffer.allocate(name.length() + 12); // See ScreenshotBlobTransformer#bind in screenshot_transformer.dart for consumer side. buffer.putInt(name.length()); - buffer.putInt(fileContent.length); - buffer.putInt(pixelCount); + buffer.putInt(0); + buffer.putInt(0); buffer.put(name.getBytes()); - buffer.put(fileContent); final byte[] bytes = buffer.array(); out.write(bytes, 0, bytes.length); out.flush(); + + // Wait on run_android_tests.dart to write a single byte into the socket + // as a signal that adb screencapture has completed. + in.read(); } synchronized void close() throws IOException { @@ -95,14 +98,16 @@ public class ScreenshotUtil { * @param fileContent The file content. */ public static synchronized void writeFile( - @NonNull String filename, @NonNull byte[] fileContent, int pixelCount) { + @NonNull String filename, @NonNull CountDownLatch latch) { if (executor != null && conn != null) { executor.execute( () -> { try { - conn.writeFile(filename, fileContent, pixelCount); + conn.writeFile(filename); } catch (IOException e) { throw new RuntimeException(e); + } finally { + latch.countDown(); } }); } @@ -119,16 +124,9 @@ public class ScreenshotUtil { */ public static void capture(@NonNull TestableFlutterActivity activity, @NonNull String captureName) throws Exception { + CountDownLatch latch = new CountDownLatch(1); activity.waitUntilFlutterRendered(); - - final Bitmap bitmap = - InstrumentationRegistry.getInstrumentation().getUiAutomation().takeScreenshot(); - if (bitmap == null) { - throw new RuntimeException("failed to capture screenshot"); - } - int pixelCount = bitmap.getWidth() * bitmap.getHeight(); - final ByteArrayOutputStream out = new ByteArrayOutputStream(); - bitmap.compress(Bitmap.CompressFormat.PNG, 100, out); - ScreenshotUtil.writeFile(captureName, out.toByteArray(), pixelCount); + ScreenshotUtil.writeFile(captureName, latch); + latch.await(); } } diff --git a/engine/src/flutter/testing/scenario_app/bin/run_android_tests.dart b/engine/src/flutter/testing/scenario_app/bin/run_android_tests.dart index a4782f31cf9..51635d9fbe6 100644 --- a/engine/src/flutter/testing/scenario_app/bin/run_android_tests.dart +++ b/engine/src/flutter/testing/scenario_app/bin/run_android_tests.dart @@ -5,7 +5,6 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; -import 'dart:typed_data'; import 'package:dir_contents_diff/dir_contents_diff.dart' show dirContentsDiff; import 'package:engine_repo_tools/engine_repo_tools.dart'; @@ -191,19 +190,32 @@ Future _run({ stdout.writeln('client connected ${client.remoteAddress.address}:${client.remotePort}'); } pendingConnections.add(client); - client.transform(const ScreenshotBlobTransformer()).listen((Screenshot screenshot) { + client.transform(const ScreenshotBlobTransformer()).listen((Screenshot screenshot) async { final String fileName = screenshot.filename; - final Uint8List fileContent = screenshot.fileContent; - if (verbose) { - log('host received ${fileContent.lengthInBytes} bytes for screenshot `$fileName`'); + final String filePath = join(screenshotPath, fileName); + { + const String remotePath = '/data/local/tmp/flutter_screenshot.png'; + ProcessResult result = await pm.run(['adb', 'shell', 'screencap', '-p', remotePath]); + if (result.exitCode != 0) { + panic(['Failed to capture screenshot']); + } + result = await pm.run( + ['adb', 'pull', remotePath, filePath], + ); + if (result.exitCode != 0) { + panic(['Failed to pull screenshot']); + } + result = await pm.run(['adb', 'shell', 'rm', remotePath]); + if (result.exitCode != 0) { + stderr.writeln('Warning: failed to delete old screenshot on device.'); + } } + // Write a single byte into the socket as a signal to ScreenshotUtil.java + // that the screenshot was taken. + client.write(0x8); + assert(skiaGoldClient != null, 'expected Skia Gold client'); - late File goldenFile; - try { - goldenFile = File(join(screenshotPath, fileName))..writeAsBytesSync(fileContent, flush: true); - } on FileSystemException catch (err) { - panic(['failed to create screenshot $fileName: $err']); - } + final File goldenFile = File(filePath); if (verbose) { log('wrote ${goldenFile.absolute.path}'); }