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