1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.screenshot; 18 19 import android.content.pm.ActivityInfo; 20 import android.graphics.Canvas; 21 import android.graphics.Color; 22 import android.graphics.HardwareRenderer; 23 import android.graphics.Paint; 24 import android.graphics.RecordingCanvas; 25 import android.graphics.Rect; 26 import android.graphics.RenderNode; 27 import android.os.CancellationSignal; 28 import android.os.ICancellationSignal; 29 import android.os.RemoteException; 30 import android.view.IScrollCaptureCallbacks; 31 import android.view.IScrollCaptureConnection; 32 import android.view.Surface; 33 34 /** 35 * An IScrollCaptureConnection which returns a sequence of solid filled rectangles in the 36 * locations requested, in alternating colors. 37 */ 38 class FakeScrollCaptureConnection extends IScrollCaptureConnection.Stub { 39 private final int[] mColors = {Color.RED, Color.GREEN, Color.BLUE}; 40 private IScrollCaptureCallbacks mCallbacks; 41 private Paint mPaint; 42 private int mNextColor; 43 private HwuiContext mHwuiContext; 44 private CancellationSignal mCancellationSignal; 45 46 @Override startCapture(Surface surface, IScrollCaptureCallbacks callbacks)47 public ICancellationSignal startCapture(Surface surface, IScrollCaptureCallbacks callbacks) { 48 mCallbacks = callbacks; 49 mHwuiContext = new HwuiContext(surface); 50 mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 51 mPaint.setStyle(Paint.Style.FILL); 52 try { 53 mCallbacks.onCaptureStarted(); 54 } catch (RemoteException e) { 55 e.rethrowAsRuntimeException(); 56 } 57 ICancellationSignal signal = CancellationSignal.createTransport(); 58 mCancellationSignal = CancellationSignal.fromTransport(signal); 59 return signal; 60 } 61 62 @Override requestImage(Rect rect)63 public ICancellationSignal requestImage(Rect rect) { 64 Canvas canvas = mHwuiContext.lockCanvas(rect.width(), rect.height()); 65 mPaint.setColor(mColors[mNextColor]); 66 canvas.drawRect(rect, mPaint); 67 mNextColor = (mNextColor++) % mColors.length; 68 mHwuiContext.unlockAndPost(canvas); 69 try { 70 mCallbacks.onImageRequestCompleted(0, rect); 71 } catch (RemoteException e) { 72 e.rethrowAsRuntimeException(); 73 } 74 ICancellationSignal signal = CancellationSignal.createTransport(); 75 mCancellationSignal = CancellationSignal.fromTransport(signal); 76 return signal; 77 } 78 79 @Override endCapture()80 public ICancellationSignal endCapture() { 81 try { 82 mCallbacks.onCaptureEnded(); 83 } catch (RemoteException e) { 84 e.rethrowAsRuntimeException(); 85 } finally { 86 mHwuiContext.destroy(); 87 mCallbacks = null; 88 } 89 ICancellationSignal signal = CancellationSignal.createTransport(); 90 mCancellationSignal = CancellationSignal.fromTransport(signal); 91 return signal; 92 } 93 94 @Override close()95 public void close() throws RemoteException { 96 } 97 98 // From android.view.Surface, but issues render requests synchronously with waitForPresent(true) 99 private static final class HwuiContext { 100 private final RenderNode mRenderNode; 101 private final HardwareRenderer mHardwareRenderer; 102 private RecordingCanvas mCanvas; 103 HwuiContext(Surface surface)104 HwuiContext(Surface surface) { 105 mRenderNode = RenderNode.create("HwuiCanvas", null); 106 mRenderNode.setClipToBounds(false); 107 mRenderNode.setForceDarkAllowed(false); 108 109 mHardwareRenderer = new HardwareRenderer(); 110 mHardwareRenderer.setContentRoot(mRenderNode); 111 mHardwareRenderer.setSurface(surface, true); 112 mHardwareRenderer.setColorMode(ActivityInfo.COLOR_MODE_DEFAULT); 113 mHardwareRenderer.setLightSourceAlpha(0.0f, 0.0f); 114 mHardwareRenderer.setLightSourceGeometry(0.0f, 0.0f, 0.0f, 0.0f); 115 } 116 lockCanvas(int width, int height)117 Canvas lockCanvas(int width, int height) { 118 if (mCanvas != null) { 119 throw new IllegalStateException("Surface was already locked!"); 120 } 121 mCanvas = mRenderNode.beginRecording(width, height); 122 return mCanvas; 123 } 124 unlockAndPost(Canvas canvas)125 void unlockAndPost(Canvas canvas) { 126 if (canvas != mCanvas) { 127 throw new IllegalArgumentException("canvas object must be the same instance that " 128 + "was previously returned by lockCanvas"); 129 } 130 mRenderNode.endRecording(); 131 mCanvas = null; 132 mHardwareRenderer.createRenderRequest() 133 .setVsyncTime(System.nanoTime()) 134 .setWaitForPresent(true) // sync! 135 .syncAndDraw(); 136 } 137 destroy()138 void destroy() { 139 mHardwareRenderer.destroy(); 140 } 141 } 142 } 143