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