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