1 /* 2 * Copyright (C) 2023 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 android.view.cts.util; 18 19 import static android.server.wm.BuildUtils.HW_TIMEOUT_MULTIPLIER; 20 import static android.view.cts.util.ASurfaceControlInputReceiverTestUtils.nCreateInputReceiver; 21 import static android.view.cts.util.ASurfaceControlInputReceiverTestUtils.nDeleteInputReceiver; 22 import static android.view.cts.util.ASurfaceControlInputReceiverTestUtils.nGetInputTransferToken; 23 import static android.view.cts.util.ASurfaceControlTestUtils.nSurfaceControl_create; 24 import static android.view.cts.util.ASurfaceControlTestUtils.nSurfaceControl_fromJava; 25 import static android.view.cts.util.ASurfaceControlTestUtils.nSurfaceControl_release; 26 import static android.view.cts.util.ASurfaceControlTestUtils.nSurfaceTransaction_apply; 27 import static android.view.cts.util.ASurfaceControlTestUtils.nSurfaceTransaction_create; 28 import static android.view.cts.util.ASurfaceControlTestUtils.nSurfaceTransaction_releaseBuffer; 29 import static android.view.cts.util.ASurfaceControlTestUtils.nSurfaceTransaction_reparent; 30 import static android.view.cts.util.ASurfaceControlTestUtils.nSurfaceTransaction_setOnCommitCallback; 31 import static android.view.cts.util.ASurfaceControlTestUtils.nSurfaceTransaction_setSolidBuffer; 32 import static android.view.cts.util.ASurfaceControlTestUtils.nSurfaceTransaction_setVisibility; 33 34 import android.app.Service; 35 import android.content.Context; 36 import android.content.Intent; 37 import android.graphics.Canvas; 38 import android.graphics.Color; 39 import android.graphics.Rect; 40 import android.hardware.display.DisplayManager; 41 import android.os.Handler; 42 import android.os.IBinder; 43 import android.os.Looper; 44 import android.os.RemoteException; 45 import android.util.Log; 46 import android.view.Choreographer; 47 import android.view.Display; 48 import android.view.KeyEvent; 49 import android.view.MotionEvent; 50 import android.view.Surface; 51 import android.view.SurfaceControl; 52 import android.view.SurfaceControl.Transaction; 53 import android.view.SurfaceControlViewHost; 54 import android.view.View; 55 import android.view.WindowManager; 56 import android.view.cts.util.ASurfaceControlInputReceiverTestUtils.InputReceiver; 57 import android.view.cts.util.aidl.IAttachEmbeddedWindow; 58 import android.view.cts.util.aidl.IMotionEventReceiver; 59 import android.widget.FrameLayout; 60 import android.widget.TextView; 61 import android.window.InputTransferToken; 62 63 import androidx.annotation.NonNull; 64 import androidx.annotation.Nullable; 65 66 import java.util.concurrent.CountDownLatch; 67 import java.util.concurrent.TimeUnit; 68 69 public class EmbeddedSCVHService extends Service { 70 private static final long WAIT_TIME_S = 5L * HW_TIMEOUT_MULTIPLIER; 71 72 private static final String TAG = "SCVHEmbeddedService"; 73 private SurfaceControlViewHost mVr; 74 75 private Handler mHandler; 76 77 private SlowView mSlowView; 78 79 private SurfaceControl mSurfaceControl; 80 private long mNativeSurfaceControl; 81 private long mBuffer; 82 private long mNativeBatchedInputReceiver; 83 84 private WindowManager mWm; 85 86 private InputTransferToken mEmbeddedInputTransferToken; 87 88 @Override onCreate()89 public void onCreate() { 90 super.onCreate(); 91 mHandler = new Handler(Looper.getMainLooper()); 92 mWm = getSystemService(WindowManager.class); 93 } 94 95 @Nullable 96 @Override onBind(Intent intent)97 public IBinder onBind(Intent intent) { 98 // Return the interface 99 return new AttachEmbeddedWindow(); 100 } 101 102 public static class SlowView extends TextView { 103 private long mDelayMs; 104 SlowView(Context context)105 public SlowView(Context context) { 106 super(context); 107 } 108 109 @Override onMeasure(int widthMeasureSpec, int heightMeasureSpec)110 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 111 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 112 try { 113 Thread.sleep(mDelayMs); 114 } catch (InterruptedException e) { 115 } 116 } 117 setDelay(long delayMs)118 public void setDelay(long delayMs) { 119 mDelayMs = delayMs; 120 } 121 } 122 123 private class AttachEmbeddedWindow extends IAttachEmbeddedWindow.Stub { 124 @Override attachEmbedded(IBinder hostToken, int width, int height, int displayId, long delayMs)125 public SurfaceControlViewHost.SurfacePackage attachEmbedded(IBinder hostToken, int width, 126 int height, int displayId, long delayMs) { 127 CountDownLatch countDownLatch = new CountDownLatch(1); 128 mHandler.post(() -> { 129 Context context = EmbeddedSCVHService.this; 130 Display display = getApplicationContext().getSystemService( 131 DisplayManager.class).getDisplay(displayId); 132 mVr = new SurfaceControlViewHost(context, display, hostToken); 133 FrameLayout content = new FrameLayout(context); 134 135 mSlowView = new SlowView(context); 136 mSlowView.setDelay(delayMs); 137 mSlowView.setBackgroundColor(Color.BLUE); 138 mSlowView.setTextColor(Color.WHITE); 139 content.addView(mSlowView); 140 mVr.setView(content, width, height); 141 142 content.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { 143 @Override 144 public void onViewAttachedToWindow(@NonNull View v) { 145 // First frame isn't included in the sync so don't notify the host about the 146 // surface package until the first draw has completed. 147 Transaction transaction = new Transaction().addTransactionCommittedListener( 148 getMainExecutor(), countDownLatch::countDown); 149 v.getRootSurfaceControl().applyTransactionOnDraw(transaction); 150 } 151 152 @Override 153 public void onViewDetachedFromWindow(@NonNull View v) { 154 } 155 }); 156 }); 157 try { 158 countDownLatch.await(5, TimeUnit.SECONDS); 159 } catch (InterruptedException e) { 160 Log.e(TAG, "Failed to wait for timeout"); 161 } 162 return mVr.getSurfacePackage(); 163 } 164 165 @Override relayout(WindowManager.LayoutParams lp)166 public void relayout(WindowManager.LayoutParams lp) { 167 Runnable runnable = () -> { 168 mSlowView.setText(lp.width + "x" + lp.height); 169 mVr.relayout(lp.width, lp.height); 170 }; 171 172 if (Thread.currentThread() == mHandler.getLooper().getThread()) { 173 runnable.run(); 174 } else { 175 mHandler.post(runnable); 176 } 177 178 } 179 180 @Override sendCrash()181 public void sendCrash() { 182 mVr.getView().getViewTreeObserver().addOnPreDrawListener(() -> { 183 throw new RuntimeException(); 184 }); 185 } 186 187 @Override attachEmbeddedSurfaceControl(SurfaceControl parentSc, InputTransferToken hostToken, int width, int height, boolean transferTouchToHost, @Nullable IMotionEventReceiver receiver)188 public String attachEmbeddedSurfaceControl(SurfaceControl parentSc, 189 InputTransferToken hostToken, int width, int height, boolean transferTouchToHost, 190 @Nullable IMotionEventReceiver receiver) { 191 CountDownLatch registeredLatch = new CountDownLatch(1); 192 String name = "Child SurfaceControl"; 193 mHandler.post(() -> { 194 mSurfaceControl = new SurfaceControl.Builder().setName(name) 195 .setParent(parentSc).setBufferSize(width, height).build(); 196 new SurfaceControl.Transaction().setVisibility(mSurfaceControl, true).setCrop( 197 mSurfaceControl, new Rect(0, 0, width, height)).apply(); 198 199 Surface surface = new Surface(mSurfaceControl); 200 Canvas c = surface.lockCanvas(null); 201 c.drawColor(Color.BLUE); 202 surface.unlockCanvasAndPost(c); 203 204 mEmbeddedInputTransferToken = mWm.registerBatchedSurfaceControlInputReceiver( 205 hostToken, mSurfaceControl, Choreographer.getInstance(), 206 event -> { 207 if (event instanceof MotionEvent) { 208 if (transferTouchToHost) { 209 mWm.transferTouchGesture(mEmbeddedInputTransferToken, 210 hostToken); 211 } 212 213 try { 214 receiver.onMotionEventReceived( 215 MotionEvent.obtain((MotionEvent) event)); 216 } catch (RemoteException e) { 217 Log.e(TAG, "Failed to send motion event to host", e); 218 } 219 } 220 return false; 221 }); 222 registeredLatch.countDown(); 223 }); 224 225 try { 226 if (!registeredLatch.await(WAIT_TIME_S, TimeUnit.SECONDS)) { 227 Log.e(TAG, "Failed to wait for input to be registered"); 228 return null; 229 } 230 } catch (InterruptedException e) { 231 return null; 232 } 233 // Use name instead of token because retrieving the token is a through a TestApi that 234 // this process is unable to call 235 return name; 236 } 237 238 @Override getEmbeddedInputTransferToken()239 public InputTransferToken getEmbeddedInputTransferToken() { 240 return mEmbeddedInputTransferToken; 241 } 242 243 @Override tearDownEmbeddedSurfaceControl()244 public void tearDownEmbeddedSurfaceControl() { 245 mHandler.post(() -> { 246 if (mSurfaceControl != null) { 247 mWm.unregisterSurfaceControlInputReceiver(mSurfaceControl); 248 new Transaction().reparent(mSurfaceControl, null); 249 mSurfaceControl.release(); 250 } 251 }); 252 } 253 254 @Override attachEmbeddedASurfaceControl(SurfaceControl parentSc, InputTransferToken hostToken, int width, int height, boolean transferTouchToHost, @Nullable IMotionEventReceiver receiver)255 public boolean attachEmbeddedASurfaceControl(SurfaceControl parentSc, 256 InputTransferToken hostToken, int width, int height, boolean transferTouchToHost, 257 @Nullable IMotionEventReceiver receiver) { 258 CountDownLatch registeredLatch = new CountDownLatch(2); 259 mHandler.post(() -> { 260 mNativeSurfaceControl = nSurfaceControl_create( 261 nSurfaceControl_fromJava(parentSc)); 262 long surfaceTransaction = nSurfaceTransaction_create(); 263 nSurfaceTransaction_setVisibility(mNativeSurfaceControl, surfaceTransaction, true); 264 mBuffer = nSurfaceTransaction_setSolidBuffer(mNativeSurfaceControl, 265 surfaceTransaction, 266 width, height, Color.RED); 267 nSurfaceTransaction_setOnCommitCallback(surfaceTransaction, 268 (latchTime, presentTime) -> registeredLatch.countDown()); 269 nSurfaceTransaction_apply(surfaceTransaction); 270 271 mNativeBatchedInputReceiver = nCreateInputReceiver(true /* batched */, 272 hostToken, mNativeSurfaceControl, new InputReceiver() { 273 @Override 274 public boolean onMotionEvent(MotionEvent motionEvent) { 275 if (transferTouchToHost && mEmbeddedInputTransferToken != null 276 && motionEvent.getAction() == MotionEvent.ACTION_DOWN) { 277 mWm.transferTouchGesture(mEmbeddedInputTransferToken, 278 hostToken); 279 } 280 281 try { 282 receiver.onMotionEventReceived(MotionEvent.obtain(motionEvent)); 283 } catch (RemoteException e) { 284 Log.e(TAG, "Failed to send motion event to host", e); 285 } 286 return false; 287 } 288 289 @Override 290 public boolean onKeyEvent(KeyEvent keyEvent) { 291 return false; 292 } 293 }); 294 mEmbeddedInputTransferToken = nGetInputTransferToken(mNativeBatchedInputReceiver); 295 registeredLatch.countDown(); 296 }); 297 298 try { 299 if (!registeredLatch.await(WAIT_TIME_S, TimeUnit.SECONDS)) { 300 Log.e(TAG, "Failed to wait for input to be registered"); 301 return false; 302 } 303 } catch (InterruptedException e) { 304 return false; 305 } 306 return true; 307 } 308 309 @Override tearDownEmbeddedASurfaceControl()310 public void tearDownEmbeddedASurfaceControl() { 311 mHandler.post(() -> { 312 if (mNativeSurfaceControl != 0) { 313 long surfaceTransaction = nSurfaceTransaction_create(); 314 nSurfaceTransaction_reparent(mNativeSurfaceControl, 0, surfaceTransaction); 315 nSurfaceTransaction_apply(surfaceTransaction); 316 nSurfaceControl_release(mNativeSurfaceControl); 317 318 nSurfaceTransaction_releaseBuffer(mBuffer); 319 nDeleteInputReceiver(mNativeBatchedInputReceiver); 320 } 321 }); 322 } 323 324 } 325 } 326