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 com.android.microdroid.test.sharevm; 18 19 import android.app.Service; 20 import android.content.Intent; 21 import android.os.IBinder; 22 import android.os.RemoteException; 23 import android.system.virtualmachine.VirtualMachine; 24 import android.system.virtualmachine.VirtualMachineCallback; 25 import android.system.virtualmachine.VirtualMachineDescriptor; 26 import android.system.virtualmachine.VirtualMachineException; 27 import android.system.virtualmachine.VirtualMachineManager; 28 import android.util.Log; 29 30 import com.android.microdroid.test.vmshare.IVmShareTestService; 31 import com.android.microdroid.testservice.ITestService; 32 import com.android.microdroid.testservice.IAppCallback; 33 34 import java.util.UUID; 35 import java.util.concurrent.CountDownLatch; 36 import java.util.concurrent.TimeUnit; 37 import java.util.concurrent.TimeoutException; 38 39 /** 40 * A {@link Service} that is used in end-to-end tests of the {@link VirtualMachine} sharing 41 * functionality. 42 * 43 * <p>During the test {@link com.android.microdroid.test.MicrodroidTests} will bind to this service, 44 * and call {@link #startVm(VirtualMachineDescriptor)} to share the VM. This service then will 45 * create a {@link VirtualMachine} from that descriptor, {@link VirtualMachine#run() run} it, and 46 * send back {@link RemoteTestServiceDelegate}. The {@code MicrodroidTests} can use that {@link 47 * RemoteTestServiceDelegate} to assert conditions on the VM running in the {@link 48 * VmShareServiceImpl}. 49 * 50 * <p>The {@link VirtualMachine} running in this service will be stopped on {@link 51 * #onUnbind(Intent)}. 52 * 53 * @see com.android.microdroid.test.MicrodroidTests#testShareVmWithAnotherApp 54 */ 55 public class VmShareServiceImpl extends Service { 56 57 private static final String TAG = "VmShareApp"; 58 59 private IVmShareTestService.Stub mBinder; 60 61 private VirtualMachine mVirtualMachine; 62 63 @Override onCreate()64 public void onCreate() { 65 mBinder = new ServiceImpl(); 66 } 67 68 @Override onBind(Intent intent)69 public IBinder onBind(Intent intent) { 70 Log.i(TAG, "onBind " + intent + " binder = " + mBinder); 71 return mBinder; 72 } 73 74 @Override onUnbind(Intent intent)75 public boolean onUnbind(Intent intent) { 76 deleteVm(); 77 // Tell framework that it shouldn't call onRebind. 78 return false; 79 } 80 deleteVm()81 private void deleteVm() { 82 if (mVirtualMachine == null) { 83 return; 84 } 85 try { 86 mVirtualMachine.stop(); 87 String name = mVirtualMachine.getName(); 88 VirtualMachineManager vmm = getSystemService(VirtualMachineManager.class); 89 vmm.delete(name); 90 mVirtualMachine = null; 91 } catch (VirtualMachineException e) { 92 Log.e(TAG, "Failed to stop " + mVirtualMachine, e); 93 } 94 } 95 importVm(VirtualMachineDescriptor vmDesc)96 public void importVm(VirtualMachineDescriptor vmDesc) throws Exception { 97 // Cleanup VM left from the previous test. 98 deleteVm(); 99 100 // Add random uuid to make sure that different tests that bind to this service don't trip 101 // over each other. 102 String vmName = "imported_vm" + UUID.randomUUID(); 103 104 VirtualMachineManager vmm = getSystemService(VirtualMachineManager.class); 105 mVirtualMachine = vmm.importFromDescriptor(vmName, vmDesc); 106 } 107 startVm()108 public ITestService startVm() throws Exception { 109 final CountDownLatch latch = new CountDownLatch(1); 110 VirtualMachineCallback callback = 111 new VirtualMachineCallback() { 112 113 @Override 114 public void onPayloadStarted(VirtualMachine vm) { 115 // Ignored 116 } 117 118 @Override 119 public void onPayloadReady(VirtualMachine vm) { 120 latch.countDown(); 121 } 122 123 @Override 124 public void onPayloadFinished(VirtualMachine vm, int exitCode) { 125 // Ignored 126 } 127 128 @Override 129 public void onError(VirtualMachine vm, int errorCode, String message) { 130 throw new RuntimeException( 131 "VM failed with error " + errorCode + " : " + message); 132 } 133 134 @Override 135 public void onStopped(VirtualMachine vm, int reason) { 136 // Ignored 137 } 138 }; 139 140 mVirtualMachine.setCallback(getMainExecutor(), callback); 141 142 Log.i(TAG, "Starting VM " + mVirtualMachine.getName()); 143 mVirtualMachine.run(); 144 if (!latch.await(1, TimeUnit.MINUTES)) { 145 throw new TimeoutException("Timed out starting VM"); 146 } 147 148 Log.i( 149 TAG, 150 "Payload is ready, connecting to the vsock service at port " + ITestService.PORT); 151 ITestService testService = 152 ITestService.Stub.asInterface( 153 mVirtualMachine.connectToVsockServer(ITestService.PORT)); 154 return new RemoteTestServiceDelegate(testService); 155 } 156 157 final class ServiceImpl extends IVmShareTestService.Stub { 158 159 @Override importVm(VirtualMachineDescriptor vmDesc)160 public void importVm(VirtualMachineDescriptor vmDesc) { 161 Log.i(TAG, "importVm binder call received"); 162 try { 163 VmShareServiceImpl.this.importVm(vmDesc); 164 } catch (Exception e) { 165 Log.e(TAG, "Failed to importVm", e); 166 throw new IllegalStateException("Failed to importVm", e); 167 } 168 } 169 170 @Override startVm()171 public ITestService startVm() { 172 Log.i(TAG, "startVm binder call received"); 173 try { 174 return VmShareServiceImpl.this.startVm(); 175 } catch (Exception e) { 176 Log.e(TAG, "Failed to startVm", e); 177 throw new IllegalStateException("Failed to startVm", e); 178 } 179 } 180 } 181 182 private static class RemoteTestServiceDelegate extends ITestService.Stub { 183 184 private final ITestService mServiceInVm; 185 RemoteTestServiceDelegate(ITestService serviceInVm)186 private RemoteTestServiceDelegate(ITestService serviceInVm) { 187 mServiceInVm = serviceInVm; 188 } 189 190 @Override addInteger(int a, int b)191 public int addInteger(int a, int b) throws RemoteException { 192 return mServiceInVm.addInteger(a, b); 193 } 194 195 @Override readProperty(String prop)196 public String readProperty(String prop) throws RemoteException { 197 throw new UnsupportedOperationException("Not supported"); 198 } 199 200 @Override insecurelyExposeVmInstanceSecret()201 public byte[] insecurelyExposeVmInstanceSecret() throws RemoteException { 202 throw new UnsupportedOperationException("Not supported"); 203 } 204 205 @Override insecurelyExposeAttestationCdi()206 public byte[] insecurelyExposeAttestationCdi() throws RemoteException { 207 throw new UnsupportedOperationException("Not supported"); 208 } 209 210 @Override getBcc()211 public byte[] getBcc() throws RemoteException { 212 throw new UnsupportedOperationException("Not supported"); 213 } 214 215 @Override getApkContentsPath()216 public String getApkContentsPath() throws RemoteException { 217 throw new UnsupportedOperationException("Not supported"); 218 } 219 220 @Override getEncryptedStoragePath()221 public String getEncryptedStoragePath() throws RemoteException { 222 throw new UnsupportedOperationException("Not supported"); 223 } 224 225 @Override runEchoReverseServer()226 public void runEchoReverseServer() throws RemoteException { 227 throw new UnsupportedOperationException("Not supported"); 228 } 229 230 @Override getEffectiveCapabilities()231 public String[] getEffectiveCapabilities() throws RemoteException { 232 throw new UnsupportedOperationException("Not supported"); 233 } 234 235 @Override getUid()236 public int getUid() throws RemoteException { 237 throw new UnsupportedOperationException("Not supported"); 238 } 239 240 @Override writeToFile(String content, String path)241 public void writeToFile(String content, String path) throws RemoteException { 242 throw new UnsupportedOperationException("Not supported"); 243 } 244 245 @Override readFromFile(String path)246 public String readFromFile(String path) throws RemoteException { 247 return mServiceInVm.readFromFile(path); 248 } 249 250 @Override getFilePermissions(String path)251 public int getFilePermissions(String path) throws RemoteException { 252 throw new UnsupportedOperationException("Not supported"); 253 } 254 255 @Override getMountFlags(String path)256 public int getMountFlags(String path) throws RemoteException { 257 throw new UnsupportedOperationException("Not supported"); 258 } 259 260 @Override requestCallback(IAppCallback appCallback)261 public void requestCallback(IAppCallback appCallback) { 262 throw new UnsupportedOperationException("Not supported"); 263 } 264 265 @Override readLineFromConsole()266 public String readLineFromConsole() { 267 throw new UnsupportedOperationException("Not supported"); 268 } 269 270 @Override quit()271 public void quit() throws RemoteException { 272 throw new UnsupportedOperationException("Not supported"); 273 } 274 } 275 } 276