diff --git a/core/java/android/view/ScrollCaptureConnection.java b/core/java/android/view/ScrollCaptureConnection.java index 99141c386c0db..278b2fcc36781 100644 --- a/core/java/android/view/ScrollCaptureConnection.java +++ b/core/java/android/view/ScrollCaptureConnection.java @@ -24,9 +24,9 @@ import android.annotation.UiThread; import android.graphics.Point; import android.graphics.Rect; import android.os.CancellationSignal; +import android.os.IBinder; import android.os.ICancellationSignal; import android.os.RemoteException; -import android.os.Trace; import android.util.CloseGuard; import android.util.Log; @@ -44,7 +44,8 @@ import java.util.function.Consumer; * * @hide */ -public class ScrollCaptureConnection extends IScrollCaptureConnection.Stub { +public class ScrollCaptureConnection extends IScrollCaptureConnection.Stub implements + IBinder.DeathRecipient { private static final String TAG = "ScrollCaptureConnection"; @@ -54,15 +55,13 @@ public class ScrollCaptureConnection extends IScrollCaptureConnection.Stub { private final Executor mUiThread; private final CloseGuard mCloseGuard = new CloseGuard(); - private ScrollCaptureCallback mLocal; private IScrollCaptureCallbacks mRemote; - private ScrollCaptureSession mSession; - private CancellationSignal mCancellation; private volatile boolean mActive; + private volatile boolean mConnected; /** * Constructs a ScrollCaptureConnection. @@ -87,13 +86,14 @@ public class ScrollCaptureConnection extends IScrollCaptureConnection.Stub { @Override public ICancellationSignal startCapture(@NonNull Surface surface, @NonNull IScrollCaptureCallbacks remote) throws RemoteException { - mCloseGuard.open("close"); if (!surface.isValid()) { throw new RemoteException(new IllegalArgumentException("surface must be valid")); } mRemote = requireNonNull(remote, " must non-null"); + mRemote.asBinder().linkToDeath(this, 0); + mConnected = true; ICancellationSignal cancellation = CancellationSignal.createTransport(); mCancellation = CancellationSignal.fromTransport(cancellation); @@ -115,14 +115,14 @@ public class ScrollCaptureConnection extends IScrollCaptureConnection.Stub { Log.w(TAG, "Shutting down due to error: ", e); close(); } + mCancellation = null; } @BinderThread @Override public ICancellationSignal requestImage(Rect requestRect) throws RemoteException { - Trace.beginSection("requestImage"); checkActive(); - + cancelPendingAction(); ICancellationSignal cancellation = CancellationSignal.createTransport(); mCancellation = CancellationSignal.fromTransport(cancellation); @@ -131,7 +131,6 @@ public class ScrollCaptureConnection extends IScrollCaptureConnection.Stub { // -> UiThread mUiThread.execute(() -> mLocal.onScrollCaptureImageRequest( mSession, mCancellation, new Rect(requestRect), listener)); - Trace.endSection(); return cancellation; } @@ -142,6 +141,8 @@ public class ScrollCaptureConnection extends IScrollCaptureConnection.Stub { } catch (RemoteException e) { Log.w(TAG, "Shutting down due to error: ", e); close(); + } finally { + mCancellation = null; } } @@ -149,7 +150,7 @@ public class ScrollCaptureConnection extends IScrollCaptureConnection.Stub { @Override public ICancellationSignal endCapture() throws RemoteException { checkActive(); - + cancelPendingAction(); ICancellationSignal cancellation = CancellationSignal.createTransport(); mCancellation = CancellationSignal.fromTransport(cancellation); @@ -170,26 +171,32 @@ public class ScrollCaptureConnection extends IScrollCaptureConnection.Stub { } catch (RemoteException e) { Log.w(TAG, "Caught exception confirming capture end!", e); } finally { + mCancellation = null; close(); } } + @Override + public void binderDied() { + Log.e(TAG, "Controlling process just died."); + close(); + } + @BinderThread @Override public void close() { if (mActive) { - if (mCancellation != null) { - Log.w(TAG, "close(): cancelling pending operation."); - mCancellation.cancel(); - mCancellation = null; - } Log.w(TAG, "close(): capture session still active! Ending now."); - // -> UiThread + cancelPendingAction(); final ScrollCaptureCallback callback = mLocal; mUiThread.execute(() -> callback.onScrollCaptureEnd(() -> { /* ignore */ })); mActive = false; } + if (mRemote != null) { + mRemote.asBinder().unlinkToDeath(this, 0); + } mActive = false; + mConnected = false; mSession = null; mRemote = null; mLocal = null; @@ -197,6 +204,19 @@ public class ScrollCaptureConnection extends IScrollCaptureConnection.Stub { Reference.reachabilityFence(this); } + private void cancelPendingAction() { + if (mCancellation != null) { + Log.w(TAG, "cancelling pending operation."); + mCancellation.cancel(); + mCancellation = null; + } + } + + @VisibleForTesting + public boolean isConnected() { + return mConnected; + } + @VisibleForTesting public boolean isActive() { return mActive; @@ -236,7 +256,7 @@ public class ScrollCaptureConnection extends IScrollCaptureConnection.Stub { protected SafeCallback(CancellationSignal signal, Executor executor, T value) { mSignal = signal; - mValue = new AtomicReference(value); + mValue = new AtomicReference<>(value); mExecutor = executor; } @@ -257,7 +277,7 @@ public class ScrollCaptureConnection extends IScrollCaptureConnection.Stub { static Consumer create(CancellationSignal signal, Executor executor, Consumer target) { - return new ConsumerCallback(signal, executor, target); + return new ConsumerCallback<>(signal, executor, target); } } diff --git a/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java b/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java index e7b88c8df3936..469be4c2b008e 100644 --- a/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java +++ b/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java @@ -21,16 +21,19 @@ import static androidx.test.InstrumentationRegistry.getTargetContext; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import android.graphics.Point; import android.graphics.Rect; +import android.os.Binder; import android.os.Handler; +import android.os.IBinder; import android.os.ICancellationSignal; import android.os.RemoteException; import android.platform.test.annotations.Presubmit; @@ -60,6 +63,7 @@ public class ScrollCaptureConnectionTest { private ScrollCaptureTarget mTarget; private ScrollCaptureConnection mConnection; + private IBinder mConnectionBinder = new Binder("ScrollCaptureConnection Test"); private Handler mHandler; @@ -76,6 +80,7 @@ public class ScrollCaptureConnectionTest { mHandler = new Handler(getTargetContext().getMainLooper()); when(mSurface.isValid()).thenReturn(true); when(mView.getScrollCaptureHint()).thenReturn(View.SCROLL_CAPTURE_HINT_INCLUDE); + when(mRemote.asBinder()).thenReturn(mConnectionBinder); mTarget = new ScrollCaptureTarget(mView, mLocalVisibleRect, mPositionInWindow, mCallback); mTarget.setScrollBounds(mScrollBounds); @@ -107,12 +112,13 @@ public class ScrollCaptureConnectionTest { @Test public void testStartCapture() throws Exception { mConnection.startCapture(mSurface, mRemote); + assertTrue(mConnection.isConnected()); + assertFalse(mConnection.isActive()); mCallback.completeStartRequest(); assertTrue(mConnection.isActive()); verify(mRemote, times(1)).onCaptureStarted(); - verifyNoMoreInteractions(mRemote); } @Test @@ -123,7 +129,7 @@ public class ScrollCaptureConnectionTest { mCallback.completeStartRequest(); assertFalse(mConnection.isActive()); - verifyNoMoreInteractions(mRemote); + verify(mRemote, never()).onCaptureStarted(); } /** @see ScrollCaptureConnection#requestImage(Rect) */ @@ -131,42 +137,63 @@ public class ScrollCaptureConnectionTest { public void testRequestImage() throws Exception { mConnection.startCapture(mSurface, mRemote); mCallback.completeStartRequest(); - reset(mRemote); mConnection.requestImage(new Rect(1, 2, 3, 4)); mCallback.completeImageRequest(new Rect(1, 2, 3, 4)); verify(mRemote, times(1)) .onImageRequestCompleted(eq(0), eq(new Rect(1, 2, 3, 4))); - verifyNoMoreInteractions(mRemote); } @Test public void testRequestImage_cancellation() throws Exception { mConnection.startCapture(mSurface, mRemote); mCallback.completeStartRequest(); - reset(mRemote); ICancellationSignal signal = mConnection.requestImage(new Rect(1, 2, 3, 4)); signal.cancel(); mCallback.completeImageRequest(new Rect(1, 2, 3, 4)); - verifyNoMoreInteractions(mRemote); + verify(mRemote, never()).onImageRequestCompleted(anyInt(), any(Rect.class)); } /** @see ScrollCaptureConnection#endCapture() */ @Test public void testEndCapture() throws Exception { mConnection.startCapture(mSurface, mRemote); + assertTrue(mConnection.isConnected()); + mCallback.completeStartRequest(); - reset(mRemote); + assertTrue(mConnection.isActive()); mConnection.endCapture(); mCallback.completeEndRequest(); + assertFalse(mConnection.isActive()); // And the reply is sent verify(mRemote, times(1)).onCaptureEnded(); - verifyNoMoreInteractions(mRemote); + + assertFalse(mConnection.isConnected()); + } + + /** @see ScrollCaptureConnection#endCapture() */ + @Test + public void testClose_withPendingOperation() throws Exception { + mConnection.startCapture(mSurface, mRemote); + mCallback.completeStartRequest(); + + mConnection.requestImage(new Rect(1, 2, 3, 4)); + assertFalse(mCallback.getLastCancellationSignal().isCanceled()); + + mConnection.close(); + + // And the reply is sent + assertTrue(mCallback.onScrollCaptureEndCalled()); + assertTrue(mCallback.getLastCancellationSignal().isCanceled()); + assertFalse(mConnection.isActive()); + assertFalse(mConnection.isConnected()); + verify(mRemote, never()).onCaptureEnded(); + verify(mRemote, never()).onImageRequestCompleted(anyInt(), any(Rect.class)); } /** @see ScrollCaptureConnection#endCapture() */ @@ -174,20 +201,19 @@ public class ScrollCaptureConnectionTest { public void testEndCapture_cancellation() throws Exception { mConnection.startCapture(mSurface, mRemote); mCallback.completeStartRequest(); - reset(mRemote); ICancellationSignal signal = mConnection.endCapture(); signal.cancel(); mCallback.completeEndRequest(); - verifyNoMoreInteractions(mRemote); + verify(mRemote, never()).onCaptureEnded(); } @Test public void testClose() { mConnection.close(); assertFalse(mConnection.isActive()); - verifyNoMoreInteractions(mRemote); + assertFalse(mConnection.isConnected()); } @Test @@ -196,9 +222,11 @@ public class ScrollCaptureConnectionTest { mCallback.completeStartRequest(); assertTrue(mConnection.isActive()); + assertTrue(mConnection.isConnected()); mConnection.close(); mCallback.completeEndRequest(); assertFalse(mConnection.isActive()); + assertFalse(mConnection.isConnected()); } } diff --git a/core/tests/coretests/src/android/view/TestScrollCaptureCallback.java b/core/tests/coretests/src/android/view/TestScrollCaptureCallback.java index 1520c6e34a954..02921e794b495 100644 --- a/core/tests/coretests/src/android/view/TestScrollCaptureCallback.java +++ b/core/tests/coretests/src/android/view/TestScrollCaptureCallback.java @@ -31,10 +31,13 @@ class TestScrollCaptureCallback implements ScrollCaptureCallback { private Consumer mImageOnComplete; private Runnable mOnEndReady; private volatile int mModCount; + private boolean mOnScrollCaptureEndCalled; + private CancellationSignal mLastCancellationSignal; @Override public void onScrollCaptureSearch(@NonNull CancellationSignal signal, @NonNull Consumer onReady) { + mLastCancellationSignal = signal; mSearchConsumer = onReady; mModCount++; } @@ -42,6 +45,7 @@ class TestScrollCaptureCallback implements ScrollCaptureCallback { @Override public void onScrollCaptureStart(@NonNull ScrollCaptureSession session, @NonNull CancellationSignal signal, @NonNull Runnable onReady) { + mLastCancellationSignal = signal; mStartOnReady = onReady; mModCount++; } @@ -50,6 +54,7 @@ class TestScrollCaptureCallback implements ScrollCaptureCallback { public void onScrollCaptureImageRequest(@NonNull ScrollCaptureSession session, @NonNull CancellationSignal signal, @NonNull Rect captureArea, @NonNull Consumer onComplete) { + mLastCancellationSignal = signal; mImageOnComplete = onComplete; mModCount++; } @@ -57,8 +62,12 @@ class TestScrollCaptureCallback implements ScrollCaptureCallback { @Override public void onScrollCaptureEnd(@NonNull Runnable onReady) { mOnEndReady = onReady; + mOnScrollCaptureEndCalled = true; } + public boolean onScrollCaptureEndCalled() { + return mOnScrollCaptureEndCalled; + } void completeSearchRequest(Rect scrollBounds) { assertNotNull("Did not receive search request", mSearchConsumer); mSearchConsumer.accept(scrollBounds); @@ -71,16 +80,26 @@ class TestScrollCaptureCallback implements ScrollCaptureCallback { void completeStartRequest() { assertNotNull("Did not receive start request", mStartOnReady); - mStartOnReady.run(); + if (mLastCancellationSignal != null && !mLastCancellationSignal.isCanceled()) { + mStartOnReady.run(); + } } void completeImageRequest(Rect captured) { assertNotNull("Did not receive image request", mImageOnComplete); - mImageOnComplete.accept(captured); + if (mLastCancellationSignal != null && !mLastCancellationSignal.isCanceled()) { + mImageOnComplete.accept(captured); + } } void completeEndRequest() { assertNotNull("Did not receive end request", mOnEndReady); - mOnEndReady.run(); + if (mLastCancellationSignal != null && !mLastCancellationSignal.isCanceled()) { + mOnEndReady.run(); + } + } + + public CancellationSignal getLastCancellationSignal() { + return mLastCancellationSignal; } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java index 7d26857bc73e0..a0118ab8a312b 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java @@ -422,11 +422,6 @@ public class ScreenshotController { } else { mScreenshotView.animateDismissal(); } - - if (mLastScrollCaptureResponse != null) { - mLastScrollCaptureResponse.close(); - mLastScrollCaptureResponse = null; - } } boolean isPendingSharedTransition() { @@ -456,6 +451,9 @@ public class ScreenshotController { mScreenshotView.init(mUiEventLogger, new ScreenshotView.ScreenshotViewCallback() { @Override public void onUserInteraction() { + if (DEBUG_INPUT) { + Log.d(TAG, "onUserInteraction"); + } resetTimeout(); } @@ -670,52 +668,7 @@ public class ScreenshotController { mScreenshotView.prepareScrollingTransition(response, mScreenBitmap, newScreenshot, mScreenshotTakenInPortrait); // delay starting scroll capture to make sure the scrim is up before the app moves - mScreenshotView.post(() -> { - // Clear the reference to prevent close() in dismissScreenshot - mLastScrollCaptureResponse = null; - final ListenableFuture future = - mScrollCaptureController.run(response); - future.addListener(() -> { - ScrollCaptureController.LongScreenshot longScreenshot; - - try { - longScreenshot = future.get(); - } catch (CancellationException - | InterruptedException - | ExecutionException e) { - Log.e(TAG, "Exception", e); - mScreenshotView.restoreNonScrollingUi(); - return; - } - - if (longScreenshot.getHeight() == 0) { - mScreenshotView.restoreNonScrollingUi(); - return; - } - - mLongScreenshotHolder.setLongScreenshot(longScreenshot); - mLongScreenshotHolder.setTransitionDestinationCallback( - (transitionDestination, onTransitionEnd) -> - mScreenshotView.startLongScreenshotTransition( - transitionDestination, onTransitionEnd, - longScreenshot)); - - final Intent intent = new Intent(mContext, LongScreenshotActivity.class); - intent.setFlags( - Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); - - mContext.startActivity(intent, - ActivityOptions.makeCustomAnimation(mContext, 0, 0).toBundle()); - RemoteAnimationAdapter runner = new RemoteAnimationAdapter( - SCREENSHOT_REMOTE_RUNNER, 0, 0); - try { - WindowManagerGlobal.getWindowManagerService() - .overridePendingAppTransitionRemote(runner, DEFAULT_DISPLAY); - } catch (Exception e) { - Log.e(TAG, "Error overriding screenshot app transition", e); - } - }, mMainExecutor); - }); + mScreenshotView.post(() -> runBatchScrollCapture(response)); }); } catch (CancellationException e) { // Ignore @@ -723,6 +676,57 @@ public class ScreenshotController { Log.e(TAG, "requestScrollCapture failed", e); } } + ListenableFuture mLongScreenshotFuture; + + private void runBatchScrollCapture(ScrollCaptureResponse response) { + // Clear the reference to prevent close() in dismissScreenshot + mLastScrollCaptureResponse = null; + + if (mLongScreenshotFuture != null) { + mLongScreenshotFuture.cancel(true); + } + mLongScreenshotFuture = mScrollCaptureController.run(response); + mLongScreenshotFuture.addListener(() -> { + ScrollCaptureController.LongScreenshot longScreenshot; + try { + longScreenshot = mLongScreenshotFuture.get(); + } catch (CancellationException e) { + Log.e(TAG, "Long screenshot cancelled"); + return; + } catch (InterruptedException | ExecutionException e) { + Log.e(TAG, "Exception", e); + mScreenshotView.restoreNonScrollingUi(); + return; + } + + if (longScreenshot.getHeight() == 0) { + mScreenshotView.restoreNonScrollingUi(); + return; + } + + mLongScreenshotHolder.setLongScreenshot(longScreenshot); + mLongScreenshotHolder.setTransitionDestinationCallback( + (transitionDestination, onTransitionEnd) -> + mScreenshotView.startLongScreenshotTransition( + transitionDestination, onTransitionEnd, + longScreenshot)); + + final Intent intent = new Intent(mContext, LongScreenshotActivity.class); + intent.setFlags( + Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); + + mContext.startActivity(intent, + ActivityOptions.makeCustomAnimation(mContext, 0, 0).toBundle()); + RemoteAnimationAdapter runner = new RemoteAnimationAdapter( + SCREENSHOT_REMOTE_RUNNER, 0, 0); + try { + WindowManagerGlobal.getWindowManagerService() + .overridePendingAppTransitionRemote(runner, DEFAULT_DISPLAY); + } catch (Exception e) { + Log.e(TAG, "Error overriding screenshot app transition", e); + } + }, mMainExecutor); + } private void withWindowAttached(Runnable action) { View decorView = mWindow.getDecorView(); @@ -824,16 +828,27 @@ public class ScreenshotController { /** Reset screenshot view and then call onCompleteRunnable */ private void finishDismiss() { - if (DEBUG_UI) { + if (DEBUG_DISMISS) { Log.d(TAG, "finishDismiss"); } - cancelTimeout(); - removeWindow(); - mScreenshotView.reset(); + if (mLastScrollCaptureRequest != null) { + mLastScrollCaptureRequest.cancel(true); + mLastScrollCaptureRequest = null; + } + if (mLastScrollCaptureResponse != null) { + mLastScrollCaptureResponse.close(); + mLastScrollCaptureResponse = null; + } + if (mLongScreenshotFuture != null) { + mLongScreenshotFuture.cancel(true); + } if (mCurrentRequestCallback != null) { mCurrentRequestCallback.onFinish(); mCurrentRequestCallback = null; } + mScreenshotView.reset(); + removeWindow(); + cancelTimeout(); } /** @@ -861,6 +876,9 @@ public class ScreenshotController { } private void cancelTimeout() { + if (DEBUG_DISMISS) { + Log.d(TAG, "cancel timeout"); + } mScreenshotHandler.removeMessages(MESSAGE_CORNER_TIMEOUT); } @@ -876,7 +894,7 @@ public class ScreenshotController { mScreenshotHandler.sendMessageDelayed( mScreenshotHandler.obtainMessage(MESSAGE_CORNER_TIMEOUT), timeoutMs); - if (DEBUG_UI) { + if (DEBUG_DISMISS) { Log.d(TAG, "dismiss timeout: " + timeoutMs + " ms"); } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java index e8123975108f7..3314c75b5a867 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java @@ -1117,9 +1117,11 @@ public class ScreenshotView extends FrameLayout implements mPreviousX = mStartX; return true; } else if (event.getActionMasked() == MotionEvent.ACTION_UP) { - if (isPastDismissThreshold() - && (mDismissAnimation == null || !mDismissAnimation.isRunning())) { - if (DEBUG_INPUT) { + if (mDismissAnimation != null && mDismissAnimation.isRunning()) { + return true; + } + if (isPastDismissThreshold()) { + if (DEBUG_DISMISS) { Log.d(TAG, "dismiss triggered via swipe gesture"); } mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_SWIPE_DISMISSED); @@ -1129,9 +1131,7 @@ public class ScreenshotView extends FrameLayout implements if (DEBUG_DISMISS) { Log.d(TAG, "swipe gesture abandoned"); } - if ((mDismissAnimation == null || !mDismissAnimation.isRunning())) { - createSwipeReturnAnimation().start(); - } + createSwipeReturnAnimation().start(); } return true; } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java index ef7355a09fbf3..83b60fb23b90b 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java @@ -35,6 +35,7 @@ import com.android.systemui.screenshot.ScrollCaptureClient.Session; import com.google.common.util.concurrent.ListenableFuture; +import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; @@ -72,6 +73,7 @@ public class ScrollCaptureController { private ListenableFuture mTileFuture; private ListenableFuture mEndFuture; private String mWindowOwner; + private volatile boolean mCancelled; static class LongScreenshot { private final ImageTileSet mImageTileSet; @@ -159,9 +161,11 @@ public class ScrollCaptureController { * @return a future ImageTile set containing the result */ ListenableFuture run(ScrollCaptureResponse response) { + mCancelled = false; return CallbackToFutureAdapter.getFuture(completer -> { mCaptureCompleter = completer; mWindowOwner = response.getPackageName(); + mCaptureCompleter.addCancellationListener(this::onCancelled, mBgExecutor); mBgExecutor.execute(() -> { float maxPages = Settings.Secure.getFloat(mContext.getContentResolver(), SETTING_KEY_MAX_PAGES, MAX_PAGES_DEFAULT); @@ -172,6 +176,24 @@ public class ScrollCaptureController { }); } + /** + * The ListenableFuture for the long screenshot was cancelled. Be sure to cancel all downstream + * futures that might be pending. + */ + private void onCancelled() { + mCancelled = true; + if (mSessionFuture != null) { + mSessionFuture.cancel(true); + } + if (mTileFuture != null) { + mTileFuture.cancel(true); + } + if (mSession != null) { + mSession.end(); + } + mEventLogger.log(ScreenshotEvent.SCREENSHOT_LONG_SCREENSHOT_FAILURE, 0, mWindowOwner); + } + private void onStartComplete() { try { mSession = mSessionFuture.get(); @@ -192,18 +214,21 @@ public class ScrollCaptureController { if (LogConfig.DEBUG_SCROLL) { Log.d(TAG, "requestNextTile: " + topPx); } + if (mCancelled) { + Log.d(TAG, "requestNextTile: CANCELLED"); + return; + } mTileFuture = mSession.requestTile(topPx); mTileFuture.addListener(() -> { try { - if (LogConfig.DEBUG_SCROLL) { - Log.d(TAG, "onCaptureResult"); - } onCaptureResult(mTileFuture.get()); + } catch (CancellationException e) { + Log.e(TAG, "requestTile cancelled"); } catch (InterruptedException | ExecutionException e) { Log.e(TAG, "requestTile failed!", e); mCaptureCompleter.setException(e); } - }, mContext.getMainExecutor()); + }, mBgExecutor); } private void onCaptureResult(CaptureResult result) {