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 android.content.ComponentName;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.ServiceConnection;
23 import android.os.Handler;
24 import android.os.HandlerThread;
25 import android.os.IBinder;
26 import android.os.RemoteException;
27 import android.util.Log;
28 import android.util.Size;
29 import android.view.SurfaceControlViewHost;
30 import android.view.SurfaceHolder;
31 import android.view.SurfaceView;
32 import android.view.ViewGroup;
33 import android.view.ViewTreeObserver;
34 import android.view.cts.util.aidl.IAttachEmbeddedWindow;
35 
36 import androidx.annotation.NonNull;
37 
38 import java.util.concurrent.CountDownLatch;
39 
40 public class SurfaceControlViewHostHelper {
41     private final String mTag;
42 
43     private boolean mIsAttached;
44 
45     private IAttachEmbeddedWindow mIAttachEmbeddedWindow;
46 
47     private SurfaceView mSurfaceView;
48 
49     private final int mDisplayId;
50 
51     private final long mDelayMs;
52     private SurfaceControlViewHost.SurfacePackage mSurfacePackage;
53 
54     private final Size mInitialSize;
55 
56     private boolean mSurfaceCreated;
57 
58     private boolean mSurfaceViewAttached;
59 
60     private final CountDownLatch mReadyLatch;
61 
62     private final Context mContext;
63 
64     HandlerThread mHandlerThread;
65 
SurfaceControlViewHostHelper(String tag, CountDownLatch countDownLatch, Context context, long delayMs, Size initialSize)66     public SurfaceControlViewHostHelper(String tag, CountDownLatch countDownLatch, Context context,
67             long delayMs, Size initialSize) {
68         mTag = tag;
69         mContext = context;
70         mDisplayId = context.getDisplayId();
71         mDelayMs = delayMs;
72         mInitialSize = initialSize;
73         mReadyLatch = countDownLatch;
74         mHandlerThread = new HandlerThread("SurfaceControlViewHostHelper");
75         mHandlerThread.start();
76     }
77 
attachSurfaceView(ViewGroup parent, ViewGroup.LayoutParams layoutParams)78     public SurfaceView attachSurfaceView(ViewGroup parent, ViewGroup.LayoutParams layoutParams) {
79         mSurfaceView = new SurfaceView(mContext);
80         mSurfaceView.getHolder().addCallback(mCallback);
81         mSurfaceView.getViewTreeObserver().addOnWindowAttachListener(
82                 new ViewTreeObserver.OnWindowAttachListener() {
83                     @Override
84                     public void onWindowAttached() {
85                         mSurfaceViewAttached = true;
86                         attachEmbedded();
87                     }
88 
89                     @Override
90                     public void onWindowDetached() {
91                         mSurfaceViewAttached = false;
92                     }
93                 });
94         parent.addView(mSurfaceView, layoutParams);
95         return mSurfaceView;
96     }
97 
98     private final ServiceConnection mConnection = new ServiceConnection() {
99         // Called when the connection with the service is established
100         public void onServiceConnected(ComponentName className, IBinder service) {
101             Log.d(mTag, "Service Connected");
102             mIAttachEmbeddedWindow = IAttachEmbeddedWindow.Stub.asInterface(service);
103             attachEmbedded();
104         }
105 
106         public void onServiceDisconnected(ComponentName className) {
107             Log.d(mTag, "Service Disconnected");
108             mIAttachEmbeddedWindow = null;
109             mIsAttached = false;
110         }
111     };
112 
113     final SurfaceHolder.Callback mCallback = new SurfaceHolder.Callback() {
114         @Override
115         public void surfaceCreated(@NonNull SurfaceHolder holder) {
116             Log.d(mTag, "surface created");
117             mSurfaceCreated = true;
118             attachEmbedded();
119         }
120 
121         @Override
122         public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width,
123                 int height) {
124         }
125 
126         @Override
127         public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
128         }
129     };
130 
isReadyToAttach()131     private boolean isReadyToAttach() {
132         if (!mSurfaceCreated) {
133             Log.d(mTag, "surface is not created");
134         }
135         if (!mSurfaceViewAttached) {
136             Log.d(mTag, "SurfaceView is not attached to window");
137         }
138         if (mIAttachEmbeddedWindow == null) {
139             Log.d(mTag, "Service is not attached");
140         }
141         if (mIsAttached) {
142             Log.d(mTag, "Already attached");
143         }
144 
145         return mSurfaceCreated && mIAttachEmbeddedWindow != null && !mIsAttached;
146     }
147 
attachEmbedded()148     private void attachEmbedded() {
149         if (!isReadyToAttach()) {
150             return;
151         }
152         mIsAttached = true;
153 
154         Handler handler = new Handler(mHandlerThread.getLooper());
155         handler.post(() -> {
156             try {
157                 mSurfacePackage = mIAttachEmbeddedWindow.attachEmbedded(mSurfaceView.getHostToken(),
158                         mInitialSize.getWidth(), mInitialSize.getHeight(), mDisplayId, mDelayMs);
159                 mSurfaceView.setChildSurfacePackage(mSurfacePackage);
160             } catch (RemoteException e) {
161                 Log.e(mTag, "Failed to attach embedded window");
162             }
163 
164             mReadyLatch.countDown();
165         });
166     }
167 
getSurfacePackage()168     public SurfaceControlViewHost.SurfacePackage getSurfacePackage() {
169         return mSurfacePackage;
170     }
171 
getAttachedEmbeddedWindow()172     public IAttachEmbeddedWindow getAttachedEmbeddedWindow() {
173         return mIAttachEmbeddedWindow;
174     }
175 
bindEmbeddedService(boolean inProcess)176     public void bindEmbeddedService(boolean inProcess) {
177         Class<? extends EmbeddedSCVHService> classToBind;
178         if (inProcess) {
179             classToBind = InProcessEmbeddedSCVHService.class;
180         } else {
181             classToBind = EmbeddedSCVHService.class;
182         }
183         Intent intent = new Intent(mContext, classToBind);
184         intent.setAction(classToBind.getName());
185         mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
186     }
187 
188     // Use this one for in process requests.
189     public static class InProcessEmbeddedSCVHService extends EmbeddedSCVHService {
190     }
191 }
192