• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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