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.system.virtualmachine.VirtualMachine 20 import android.system.virtualmachine.VirtualMachineCallback 21 import android.system.virtualmachine.VirtualMachineConfig 22 import android.system.virtualmachine.VirtualMachineException 23 import android.system.virtualmachine.VirtualMachineManager 24 import android.util.Log 25 import com.android.virtualization.terminal.MainActivity.Companion.TAG 26 import java.util.concurrent.CompletableFuture 27 import java.util.concurrent.ForkJoinPool 28 29 /** Utility class for creating a VM and waiting for it to finish. */ 30 internal class Runner private constructor(val vm: VirtualMachine, callback: Callback) { 31 /** Get future about VM's exit status. */ 32 val exitStatus = callback.finishedSuccessfully 33 34 private class Callback : VirtualMachineCallback { 35 val finishedSuccessfully: CompletableFuture<Boolean> = CompletableFuture<Boolean>() 36 onPayloadStartednull37 override fun onPayloadStarted(vm: VirtualMachine) { 38 // This event is only from Microdroid-based VM. Custom VM shouldn't emit this. 39 } 40 onPayloadReadynull41 override fun onPayloadReady(vm: VirtualMachine) { 42 // This event is only from Microdroid-based VM. Custom VM shouldn't emit this. 43 } 44 onPayloadFinishednull45 override fun onPayloadFinished(vm: VirtualMachine, exitCode: Int) { 46 // This event is only from Microdroid-based VM. Custom VM shouldn't emit this. 47 } 48 onErrornull49 override fun onError(vm: VirtualMachine, errorCode: Int, message: String) { 50 Log.e(TAG, "Error from VM. code: $errorCode ($message)") 51 finishedSuccessfully.complete(false) 52 } 53 onStoppednull54 override fun onStopped(vm: VirtualMachine, reason: Int) { 55 Log.d(TAG, "VM stopped. Reason: $reason") 56 finishedSuccessfully.complete(true) 57 } 58 } 59 60 companion object { 61 /** Create a virtual machine of the given config, under the given context. */ 62 @Throws(VirtualMachineException::class) createnull63 fun create(context: Context, config: VirtualMachineConfig): Runner { 64 // context may already be the app context, but calling this again is not harmful. 65 // See b/359439878 on why vmm should be obtained from the app context. 66 val appContext = context.getApplicationContext() 67 val vmm = 68 appContext.getSystemService<VirtualMachineManager>( 69 VirtualMachineManager::class.java 70 ) 71 val customConfig = config.customImageConfig 72 requireNotNull(customConfig) { "CustomImageConfig is missing" } 73 74 val name = customConfig.name 75 require(!name.isNullOrEmpty()) { "Virtual machine's name is missing in the config" } 76 77 var vm = vmm.getOrCreate(name, config) 78 try { 79 vm.config = config 80 } catch (e: VirtualMachineException) { 81 vmm.delete(name) 82 vm = vmm.create(name, config) 83 Log.w(TAG, "Re-creating virtual machine ($name)", e) 84 } 85 86 val cb = Callback() 87 vm.setCallback(ForkJoinPool.commonPool(), cb) 88 vm.run() 89 return Runner(vm, cb) 90 } 91 } 92 } 93