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 static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; 20 21 import android.app.Service; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.graphics.Color; 25 import android.graphics.PixelFormat; 26 import android.hardware.display.DisplayManager; 27 import android.os.Handler; 28 import android.os.IBinder; 29 import android.os.Looper; 30 import android.util.Log; 31 import android.view.Display; 32 import android.view.SurfaceControl.Transaction; 33 import android.view.SurfaceControlViewHost; 34 import android.view.View; 35 import android.view.WindowManager; 36 import android.widget.FrameLayout; 37 import android.widget.TextView; 38 39 import androidx.annotation.NonNull; 40 import androidx.annotation.Nullable; 41 42 import java.util.concurrent.CountDownLatch; 43 import java.util.concurrent.TimeUnit; 44 45 public class EmbeddedSCVHService extends Service { 46 private static final String TAG = "SCVHEmbeddedService"; 47 private SurfaceControlViewHost mVr; 48 49 private Handler mHandler; 50 51 private SlowView mSlowView; 52 53 @Override onCreate()54 public void onCreate() { 55 super.onCreate(); 56 mHandler = new Handler(Looper.getMainLooper()); 57 } 58 59 @Nullable 60 @Override onBind(Intent intent)61 public IBinder onBind(Intent intent) { 62 // Return the interface 63 return new AttachEmbeddedWindow(); 64 } 65 66 public static class SlowView extends TextView { 67 private long mDelayMs; 68 SlowView(Context context)69 public SlowView(Context context) { 70 super(context); 71 } 72 73 @Override onMeasure(int widthMeasureSpec, int heightMeasureSpec)74 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 75 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 76 try { 77 Thread.sleep(mDelayMs); 78 } catch (InterruptedException e) { 79 } 80 } 81 setDelay(long delayMs)82 public void setDelay(long delayMs) { 83 mDelayMs = delayMs; 84 } 85 } 86 87 private class AttachEmbeddedWindow extends IAttachEmbeddedWindow.Stub { 88 @Override attachEmbedded(IBinder hostToken, int width, int height, int displayId, long delayMs)89 public SurfaceControlViewHost.SurfacePackage attachEmbedded(IBinder hostToken, int width, 90 int height, int displayId, long delayMs) { 91 CountDownLatch countDownLatch = new CountDownLatch(1); 92 mHandler.post(() -> { 93 Context context = EmbeddedSCVHService.this; 94 Display display = getApplicationContext().getSystemService( 95 DisplayManager.class).getDisplay(displayId); 96 mVr = new SurfaceControlViewHost(context, display, hostToken); 97 FrameLayout content = new FrameLayout(context); 98 99 mSlowView = new SlowView(context); 100 mSlowView.setDelay(delayMs); 101 mSlowView.setBackgroundColor(Color.BLUE); 102 mSlowView.setTextColor(Color.WHITE); 103 content.addView(mSlowView); 104 WindowManager.LayoutParams lp = new WindowManager.LayoutParams(width, height, 105 TYPE_APPLICATION, 0, PixelFormat.OPAQUE); 106 lp.setTitle("EmbeddedWindow"); 107 mVr.setView(content, lp); 108 109 content.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { 110 @Override 111 public void onViewAttachedToWindow(@NonNull View v) { 112 // First frame isn't included in the sync so don't notify the host about the 113 // surface package until the first draw has completed. 114 Transaction transaction = new Transaction().addTransactionCommittedListener( 115 getMainExecutor(), countDownLatch::countDown); 116 v.getRootSurfaceControl().applyTransactionOnDraw(transaction); 117 } 118 119 @Override 120 public void onViewDetachedFromWindow(@NonNull View v) { 121 } 122 }); 123 }); 124 try { 125 countDownLatch.await(5, TimeUnit.SECONDS); 126 } catch (InterruptedException e) { 127 Log.e(TAG, "Failed to wait for timeout"); 128 } 129 return mVr.getSurfacePackage(); 130 } 131 132 @Override relayout(WindowManager.LayoutParams lp)133 public void relayout(WindowManager.LayoutParams lp) { 134 Runnable runnable = () -> { 135 mSlowView.setText(lp.width + "x" + lp.height); 136 mVr.relayout(lp); 137 }; 138 139 if (Thread.currentThread() == mHandler.getLooper().getThread()) { 140 runnable.run(); 141 } else { 142 mHandler.post(runnable); 143 } 144 145 } 146 147 @Override sendCrash()148 public void sendCrash() { 149 mVr.getView().getViewTreeObserver().addOnPreDrawListener(() -> { 150 throw new RuntimeException(); 151 }); 152 } 153 } 154 } 155