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.content.Context 19 import android.util.Log 20 import androidx.annotation.Keep 21 import com.android.internal.annotations.GuardedBy 22 import com.android.system.virtualmachine.flags.Flags 23 import com.android.virtualization.terminal.MainActivity.Companion.TAG 24 import com.android.virtualization.terminal.proto.DebianServiceGrpc.DebianServiceImplBase 25 import com.android.virtualization.terminal.proto.ForwardingRequestItem 26 import com.android.virtualization.terminal.proto.QueueOpeningRequest 27 import com.android.virtualization.terminal.proto.ReportVmActivePortsRequest 28 import com.android.virtualization.terminal.proto.ReportVmActivePortsResponse 29 import com.android.virtualization.terminal.proto.ShutdownQueueOpeningRequest 30 import com.android.virtualization.terminal.proto.ShutdownRequestItem 31 import com.android.virtualization.terminal.proto.StorageBalloonQueueOpeningRequest 32 import com.android.virtualization.terminal.proto.StorageBalloonRequestItem 33 import io.grpc.stub.ServerCallStreamObserver 34 import io.grpc.stub.StreamObserver 35 36 internal class DebianServiceImpl(context: Context) : DebianServiceImplBase() { 37 private val portsStateManager = PortsStateManager.getInstance(context) 38 private var portsStateListener: PortsStateManager.Listener? = null 39 private var shutdownRunnable: Runnable? = null 40 private val mLock = Object() 41 @GuardedBy("mLock") private var storageBalloonCallback: StorageBalloonCallback? = null 42 reportVmActivePortsnull43 override fun reportVmActivePorts( 44 request: ReportVmActivePortsRequest, 45 responseObserver: StreamObserver<ReportVmActivePortsResponse?>, 46 ) { 47 portsStateManager.updateActivePorts(request.portsList) 48 Log.d(TAG, "reportVmActivePorts: " + portsStateManager.getActivePorts()) 49 val reply = ReportVmActivePortsResponse.newBuilder().setSuccess(true).build() 50 responseObserver.onNext(reply) 51 responseObserver.onCompleted() 52 } 53 openForwardingRequestQueuenull54 override fun openForwardingRequestQueue( 55 request: QueueOpeningRequest, 56 responseObserver: StreamObserver<ForwardingRequestItem?>, 57 ) { 58 Log.d(TAG, "OpenForwardingRequestQueue") 59 portsStateListener = 60 object : PortsStateManager.Listener { 61 override fun onPortsStateUpdated( 62 oldActivePorts: Set<Int>, 63 newActivePorts: Set<Int>, 64 ) { 65 updateListeningPorts() 66 } 67 } 68 portsStateManager.registerListener(portsStateListener!!) 69 updateListeningPorts() 70 runForwarderHost(request.cid, ForwarderHostCallback(responseObserver)) 71 responseObserver.onCompleted() 72 } 73 shutdownDebiannull74 fun shutdownDebian(): Boolean { 75 if (shutdownRunnable == null) { 76 Log.d(TAG, "mShutdownRunnable is not ready.") 77 return false 78 } 79 shutdownRunnable!!.run() 80 return true 81 } 82 openShutdownRequestQueuenull83 override fun openShutdownRequestQueue( 84 request: ShutdownQueueOpeningRequest?, 85 responseObserver: StreamObserver<ShutdownRequestItem?>, 86 ) { 87 val serverCallStreamObserver = 88 responseObserver as ServerCallStreamObserver<ShutdownRequestItem?> 89 serverCallStreamObserver.setOnCancelHandler { shutdownRunnable = null } 90 Log.d(TAG, "openShutdownRequestQueue") 91 shutdownRunnable = Runnable { 92 if (serverCallStreamObserver.isCancelled()) { 93 return@Runnable 94 } 95 responseObserver.onNext(ShutdownRequestItem.newBuilder().build()) 96 responseObserver.onCompleted() 97 shutdownRunnable = null 98 } 99 } 100 101 private class StorageBalloonCallback( 102 private val responseObserver: StreamObserver<StorageBalloonRequestItem?> 103 ) { setAvailableStorageBytesnull104 fun setAvailableStorageBytes(availableBytes: Long) { 105 Log.d(TAG, "send setStorageBalloon: $availableBytes") 106 val item = 107 StorageBalloonRequestItem.newBuilder().setAvailableBytes(availableBytes).build() 108 responseObserver.onNext(item) 109 } 110 closeConnectionnull111 fun closeConnection() { 112 Log.d(TAG, "close StorageBalloonQueue") 113 responseObserver.onCompleted() 114 } 115 } 116 setAvailableStorageBytesnull117 fun setAvailableStorageBytes(availableBytes: Long): Boolean { 118 synchronized(mLock) { 119 if (storageBalloonCallback == null) { 120 Log.d(TAG, "storageBalloonCallback is not ready.") 121 return false 122 } 123 storageBalloonCallback!!.setAvailableStorageBytes(availableBytes) 124 } 125 return true 126 } 127 openStorageBalloonRequestQueuenull128 override fun openStorageBalloonRequestQueue( 129 request: StorageBalloonQueueOpeningRequest?, 130 responseObserver: StreamObserver<StorageBalloonRequestItem?>, 131 ) { 132 if (!Flags.terminalStorageBalloon()) { 133 return 134 } 135 Log.d(TAG, "openStorageRequestQueue") 136 synchronized(mLock) { 137 if (storageBalloonCallback != null) { 138 Log.d(TAG, "RequestQueue already exists. Closing connection.") 139 storageBalloonCallback!!.closeConnection() 140 } 141 storageBalloonCallback = StorageBalloonCallback(responseObserver) 142 } 143 } 144 closeStorageBalloonRequestQueuenull145 fun closeStorageBalloonRequestQueue() { 146 Log.d(TAG, "Stopping storage balloon queue") 147 synchronized(mLock) { 148 if (storageBalloonCallback != null) { 149 storageBalloonCallback!!.closeConnection() 150 storageBalloonCallback = null 151 } 152 } 153 } 154 155 @Keep 156 private class ForwarderHostCallback( 157 private val responseObserver: StreamObserver<ForwardingRequestItem?> 158 ) { 159 onForwardingRequestReceivednull160 fun onForwardingRequestReceived(guestTcpPort: Int, vsockPort: Int) { 161 val item = 162 ForwardingRequestItem.newBuilder() 163 .setGuestTcpPort(guestTcpPort) 164 .setVsockPort(vsockPort) 165 .build() 166 responseObserver.onNext(item) 167 } 168 } 169 killForwarderHostnull170 fun killForwarderHost() { 171 Log.d(TAG, "Stopping port forwarding") 172 if (portsStateListener != null) { 173 portsStateManager.unregisterListener(portsStateListener!!) 174 portsStateListener = null 175 } 176 terminateForwarderHost() 177 } 178 updateListeningPortsnull179 private fun updateListeningPorts() { 180 val activePorts: Set<Int> = portsStateManager.getActivePorts() 181 val enabledPorts: Set<Int> = portsStateManager.getEnabledPorts() 182 updateListeningPorts(activePorts.filter { enabledPorts.contains(it) }.toIntArray()) 183 } 184 185 companion object { 186 init { 187 System.loadLibrary("forwarder_host_jni") 188 } 189 runForwarderHostnull190 @JvmStatic private external fun runForwarderHost(cid: Int, callback: ForwarderHostCallback?) 191 192 @JvmStatic private external fun terminateForwarderHost() 193 194 @JvmStatic private external fun updateListeningPorts(ports: IntArray?) 195 } 196 } 197