1 /* 2 * Copyright (C) 2024 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 package com.android.virtualization.terminal 17 18 import android.crosvm.ICrosvmAndroidDisplayService 19 import android.graphics.PixelFormat 20 import android.os.ParcelFileDescriptor 21 import android.os.RemoteException 22 import android.os.ServiceManager 23 import android.system.virtualizationservice_internal.IVirtualizationServiceInternal 24 import android.util.Log 25 import android.view.SurfaceControl 26 import android.view.SurfaceHolder 27 import android.view.SurfaceView 28 import com.android.virtualization.terminal.DisplayProvider.CursorHandler 29 import com.android.virtualization.terminal.MainActivity.Companion.TAG 30 import java.io.IOException 31 import java.lang.Exception 32 import java.lang.RuntimeException 33 import java.nio.ByteBuffer 34 import java.nio.ByteOrder 35 import libcore.io.IoBridge 36 37 /** Provides Android-side surface from given SurfaceView to a VM instance as a display for that */ 38 internal class DisplayProvider( 39 private val mainView: SurfaceView, 40 private val cursorView: SurfaceView, 41 ) { <lambda>null42 private val virtService: IVirtualizationServiceInternal by lazy { 43 val b = ServiceManager.waitForService("android.system.virtualizationservice") 44 IVirtualizationServiceInternal.Stub.asInterface(b) 45 } 46 private var cursorHandler: CursorHandler? = null 47 48 init { 49 mainView.setSurfaceLifecycle(SurfaceView.SURFACE_LIFECYCLE_FOLLOWS_ATTACHMENT) 50 mainView.holder.addCallback(Callback(SurfaceKind.MAIN)) 51 cursorView.setSurfaceLifecycle(SurfaceView.SURFACE_LIFECYCLE_FOLLOWS_ATTACHMENT) 52 cursorView.holder.addCallback(Callback(SurfaceKind.CURSOR)) 53 cursorView.holder.setFormat(PixelFormat.RGBA_8888) 54 // TODO: do we need this z-order? 55 cursorView.setZOrderMediaOverlay(true) 56 } 57 notifyDisplayIsGoingToInvisiblenull58 fun notifyDisplayIsGoingToInvisible() { 59 // When the display is going to be invisible (by putting in the background), save the frame 60 // of the main surface so that we can re-draw it next time the display becomes visible. This 61 // is to save the duration of time where nothing is drawn by VM. 62 try { 63 getDisplayService().saveFrameForSurface(false /* forCursor */) 64 } catch (e: RemoteException) { 65 throw RuntimeException("Failed to save frame for the main surface", e) 66 } 67 } 68 69 @Synchronized getDisplayServicenull70 private fun getDisplayService(): ICrosvmAndroidDisplayService { 71 try { 72 val b = virtService.waitDisplayService() 73 return ICrosvmAndroidDisplayService.Stub.asInterface(b) 74 } catch (e: Exception) { 75 throw RuntimeException("Error while getting display service", e) 76 } 77 } 78 79 enum class SurfaceKind { 80 MAIN, 81 CURSOR, 82 } 83 84 inner class Callback(private val surfaceKind: SurfaceKind) : SurfaceHolder.Callback { isForCursornull85 fun isForCursor(): Boolean { 86 return surfaceKind == SurfaceKind.CURSOR 87 } 88 surfaceCreatednull89 override fun surfaceCreated(holder: SurfaceHolder) { 90 try { 91 getDisplayService().setSurface(holder.getSurface(), isForCursor()) 92 } catch (e: Exception) { 93 // TODO: don't consume this exception silently. For some unknown reason, setSurface 94 // call above throws IllegalArgumentException and that fails the surface 95 // configuration. 96 Log.e(TAG, "Failed to present surface $surfaceKind to VM", e) 97 } 98 try { 99 when (surfaceKind) { 100 SurfaceKind.MAIN -> getDisplayService().drawSavedFrameForSurface(isForCursor()) 101 SurfaceKind.CURSOR -> { 102 val stream = createNewCursorStream() 103 getDisplayService().setCursorStream(stream) 104 } 105 } 106 } catch (e: Exception) { 107 // TODO: don't consume exceptions here too 108 Log.e(TAG, "Failed to configure surface $surfaceKind", e) 109 } 110 } 111 surfaceChangednull112 override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) { 113 // TODO: support resizeable display. We could actually change the display size that the 114 // VM sees, or keep the size and render it by fitting it in the new surface. 115 } 116 surfaceDestroyednull117 override fun surfaceDestroyed(holder: SurfaceHolder) { 118 try { 119 getDisplayService().removeSurface(isForCursor()) 120 } catch (e: RemoteException) { 121 throw RuntimeException("Error while destroying surface for $surfaceKind", e) 122 } 123 } 124 } 125 createNewCursorStreamnull126 private fun createNewCursorStream(): ParcelFileDescriptor? { 127 cursorHandler?.interrupt() 128 var pfds: Array<ParcelFileDescriptor> = 129 try { 130 ParcelFileDescriptor.createSocketPair() 131 } catch (e: IOException) { 132 throw RuntimeException("Failed to create socketpair for cursor stream", e) 133 } 134 cursorHandler = CursorHandler(pfds[0]).also { it.start() } 135 return pfds[1] 136 } 137 138 /** 139 * Thread reading cursor coordinate from a stream, and updating the position of the cursor 140 * surface accordingly. 141 */ 142 private inner class CursorHandler(private val stream: ParcelFileDescriptor) : Thread() { 143 private val cursor: SurfaceControl = this@DisplayProvider.cursorView.surfaceControl 144 private val transaction: SurfaceControl.Transaction = SurfaceControl.Transaction() 145 146 init { 147 val main = this@DisplayProvider.mainView.surfaceControl 148 transaction.reparent(cursor, main).apply() 149 } 150 runnull151 override fun run() { 152 try { 153 val byteBuffer = ByteBuffer.allocate(8 /* (x: u32, y: u32) */) 154 byteBuffer.order(ByteOrder.LITTLE_ENDIAN) 155 while (true) { 156 if (interrupted()) { 157 Log.d(TAG, "CursorHandler thread interrupted!") 158 return 159 } 160 byteBuffer.clear() 161 val bytes = 162 IoBridge.read( 163 stream.fileDescriptor, 164 byteBuffer.array(), 165 0, 166 byteBuffer.array().size, 167 ) 168 if (bytes == -1) { 169 Log.e(TAG, "cannot read from cursor stream, stop the handler") 170 return 171 } 172 val x = (byteBuffer.getInt() and -0x1).toFloat() 173 val y = (byteBuffer.getInt() and -0x1).toFloat() 174 transaction.setPosition(cursor, x, y).apply() 175 } 176 } catch (e: IOException) { 177 Log.e(TAG, "failed to run CursorHandler", e) 178 } 179 } 180 } 181 } 182