Annotate blocking methods
For all of our API methods which may block (due to lock contention,
I/O, binder calls or, well, starting a VM) add the @WorkerThread
annotation and a note in the javadoc.
This includes some apparently trivial methods - but since in some
cases we do significant work while holding mLock, anything that
acquires it may also block. OTOH there's really no reason to call any
of these on a UI thread anyway.
There is one substantive change here - I moved deleting the VM
directory outside of mLock, now that we have mWasDeleted. (And
sCreateLock means the delete can't race with a create.)
Bug: 261037705
Test: atest MicrodroidTests
Change-Id: I9029c90c5cbd52dd0947d7f68fb9214b63174474
diff --git a/javalib/api/system-current.txt b/javalib/api/system-current.txt
index 592a751..30e437b 100644
--- a/javalib/api/system-current.txt
+++ b/javalib/api/system-current.txt
@@ -3,19 +3,19 @@
public class VirtualMachine implements java.lang.AutoCloseable {
method public void clearCallback();
- method public void close();
- method @NonNull public android.os.IBinder connectToVsockServer(@IntRange(from=android.system.virtualmachine.VirtualMachine.MIN_VSOCK_PORT, to=android.system.virtualmachine.VirtualMachine.MAX_VSOCK_PORT) long) throws android.system.virtualmachine.VirtualMachineException;
- method @NonNull public android.os.ParcelFileDescriptor connectVsock(@IntRange(from=android.system.virtualmachine.VirtualMachine.MIN_VSOCK_PORT, to=android.system.virtualmachine.VirtualMachine.MAX_VSOCK_PORT) long) throws android.system.virtualmachine.VirtualMachineException;
- method @NonNull public android.system.virtualmachine.VirtualMachineConfig getConfig();
- method @NonNull public java.io.InputStream getConsoleOutput() throws android.system.virtualmachine.VirtualMachineException;
- method @NonNull public java.io.InputStream getLogOutput() throws android.system.virtualmachine.VirtualMachineException;
+ method @WorkerThread public void close();
+ method @NonNull @WorkerThread public android.os.IBinder connectToVsockServer(@IntRange(from=android.system.virtualmachine.VirtualMachine.MIN_VSOCK_PORT, to=android.system.virtualmachine.VirtualMachine.MAX_VSOCK_PORT) long) throws android.system.virtualmachine.VirtualMachineException;
+ method @NonNull @WorkerThread public android.os.ParcelFileDescriptor connectVsock(@IntRange(from=android.system.virtualmachine.VirtualMachine.MIN_VSOCK_PORT, to=android.system.virtualmachine.VirtualMachine.MAX_VSOCK_PORT) long) throws android.system.virtualmachine.VirtualMachineException;
+ method @NonNull @WorkerThread public android.system.virtualmachine.VirtualMachineConfig getConfig();
+ method @NonNull @WorkerThread public java.io.InputStream getConsoleOutput() throws android.system.virtualmachine.VirtualMachineException;
+ method @NonNull @WorkerThread public java.io.InputStream getLogOutput() throws android.system.virtualmachine.VirtualMachineException;
method @NonNull public String getName();
- method public int getStatus();
- method @RequiresPermission(android.system.virtualmachine.VirtualMachine.MANAGE_VIRTUAL_MACHINE_PERMISSION) public void run() throws android.system.virtualmachine.VirtualMachineException;
+ method @WorkerThread public int getStatus();
+ method @RequiresPermission(android.system.virtualmachine.VirtualMachine.MANAGE_VIRTUAL_MACHINE_PERMISSION) @WorkerThread public void run() throws android.system.virtualmachine.VirtualMachineException;
method public void setCallback(@NonNull java.util.concurrent.Executor, @NonNull android.system.virtualmachine.VirtualMachineCallback);
- method @NonNull public android.system.virtualmachine.VirtualMachineConfig setConfig(@NonNull android.system.virtualmachine.VirtualMachineConfig) throws android.system.virtualmachine.VirtualMachineException;
- method public void stop() throws android.system.virtualmachine.VirtualMachineException;
- method @NonNull public android.system.virtualmachine.VirtualMachineDescriptor toDescriptor() throws android.system.virtualmachine.VirtualMachineException;
+ method @NonNull @WorkerThread public android.system.virtualmachine.VirtualMachineConfig setConfig(@NonNull android.system.virtualmachine.VirtualMachineConfig) throws android.system.virtualmachine.VirtualMachineException;
+ method @WorkerThread public void stop() throws android.system.virtualmachine.VirtualMachineException;
+ method @NonNull @WorkerThread public android.system.virtualmachine.VirtualMachineDescriptor toDescriptor() throws android.system.virtualmachine.VirtualMachineException;
field public static final String MANAGE_VIRTUAL_MACHINE_PERMISSION = "android.permission.MANAGE_VIRTUAL_MACHINE";
field public static final long MAX_VSOCK_PORT = 4294967295L; // 0xffffffffL
field public static final long MIN_VSOCK_PORT = 1024L; // 0x400L
@@ -91,12 +91,12 @@
}
public class VirtualMachineManager {
- method @NonNull @RequiresPermission(android.system.virtualmachine.VirtualMachine.MANAGE_VIRTUAL_MACHINE_PERMISSION) public android.system.virtualmachine.VirtualMachine create(@NonNull String, @NonNull android.system.virtualmachine.VirtualMachineConfig) throws android.system.virtualmachine.VirtualMachineException;
- method public void delete(@NonNull String) throws android.system.virtualmachine.VirtualMachineException;
- method @Nullable public android.system.virtualmachine.VirtualMachine get(@NonNull String) throws android.system.virtualmachine.VirtualMachineException;
+ method @NonNull @RequiresPermission(android.system.virtualmachine.VirtualMachine.MANAGE_VIRTUAL_MACHINE_PERMISSION) @WorkerThread public android.system.virtualmachine.VirtualMachine create(@NonNull String, @NonNull android.system.virtualmachine.VirtualMachineConfig) throws android.system.virtualmachine.VirtualMachineException;
+ method @WorkerThread public void delete(@NonNull String) throws android.system.virtualmachine.VirtualMachineException;
+ method @Nullable @WorkerThread public android.system.virtualmachine.VirtualMachine get(@NonNull String) throws android.system.virtualmachine.VirtualMachineException;
method public int getCapabilities();
- method @NonNull public android.system.virtualmachine.VirtualMachine getOrCreate(@NonNull String, @NonNull android.system.virtualmachine.VirtualMachineConfig) throws android.system.virtualmachine.VirtualMachineException;
- method @NonNull public android.system.virtualmachine.VirtualMachine importFromDescriptor(@NonNull String, @NonNull android.system.virtualmachine.VirtualMachineDescriptor) throws android.system.virtualmachine.VirtualMachineException;
+ method @NonNull @WorkerThread public android.system.virtualmachine.VirtualMachine getOrCreate(@NonNull String, @NonNull android.system.virtualmachine.VirtualMachineConfig) throws android.system.virtualmachine.VirtualMachineException;
+ method @NonNull @WorkerThread public android.system.virtualmachine.VirtualMachine importFromDescriptor(@NonNull String, @NonNull android.system.virtualmachine.VirtualMachineDescriptor) throws android.system.virtualmachine.VirtualMachineException;
field public static final int CAPABILITY_NON_PROTECTED_VM = 2; // 0x2
field public static final int CAPABILITY_PROTECTED_VM = 1; // 0x1
}
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachine.java b/javalib/src/android/system/virtualmachine/VirtualMachine.java
index d2bc993..b38e0e4 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachine.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachine.java
@@ -52,6 +52,7 @@
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
+import android.annotation.WorkerThread;
import android.content.ComponentCallbacks2;
import android.content.Context;
import android.content.res.Configuration;
@@ -521,8 +522,8 @@
// Once we explicitly delete a VM it must remain permanently in the deleted state;
// if a new VM is created with the same name (and files) that's unrelated.
mWasDeleted = true;
- deleteVmDirectory(context, name);
}
+ deleteVmDirectory(context, name);
}
static void deleteVmDirectory(Context context, String name) throws VirtualMachineException {
@@ -582,9 +583,12 @@
* machine can change its config, which can be done by calling {@link
* #setConfig(VirtualMachineConfig)}.
*
+ * <p>NOTE: This method may block and should not be called on the main thread.
+ *
* @hide
*/
@SystemApi
+ @WorkerThread
@NonNull
public VirtualMachineConfig getConfig() {
synchronized (mLock) {
@@ -595,9 +599,12 @@
/**
* Returns the current status of this virtual machine.
*
+ * <p>NOTE: This method may block and should not be called on the main thread.
+ *
* @hide
*/
@SystemApi
+ @WorkerThread
@Status
public int getStatus() {
IVirtualMachine virtualMachine;
@@ -736,11 +743,14 @@
* registering a callback using {@link #setCallback(Executor, VirtualMachineCallback)} before
* calling {@code run()}.
*
+ * <p>NOTE: This method may block and should not be called on the main thread.
+ *
* @throws VirtualMachineException if the virtual machine is not stopped or could not be
* started.
* @hide
*/
@SystemApi
+ @WorkerThread
@RequiresPermission(MANAGE_VIRTUAL_MACHINE_PERMISSION)
public void run() throws VirtualMachineException {
synchronized (mLock) {
@@ -881,10 +891,13 @@
/**
* Returns the stream object representing the console output from the virtual machine.
*
+ * <p>NOTE: This method may block and should not be called on the main thread.
+ *
* @throws VirtualMachineException if the stream could not be created.
* @hide
*/
@SystemApi
+ @WorkerThread
@NonNull
public InputStream getConsoleOutput() throws VirtualMachineException {
synchronized (mLock) {
@@ -896,10 +909,13 @@
/**
* Returns the stream object representing the log output from the virtual machine.
*
+ * <p>NOTE: This method may block and should not be called on the main thread.
+ *
* @throws VirtualMachineException if the stream could not be created.
* @hide
*/
@SystemApi
+ @WorkerThread
@NonNull
public InputStream getLogOutput() throws VirtualMachineException {
synchronized (mLock) {
@@ -913,11 +929,14 @@
* computer; the machine halts immediately. Software running on the virtual machine is not
* notified of the event. A stopped virtual machine can be re-started by calling {@link #run()}.
*
+ * <p>NOTE: This method may block and should not be called on the main thread.
+ *
* @throws VirtualMachineException if the virtual machine is not running or could not be
* stopped.
* @hide
*/
@SystemApi
+ @WorkerThread
public void stop() throws VirtualMachineException {
synchronized (mLock) {
if (mVirtualMachine == null) {
@@ -937,10 +956,13 @@
/**
* Stops this virtual machine, if it is running.
*
+ * <p>NOTE: This method may block and should not be called on the main thread.
+ *
* @see #stop()
* @hide
*/
@SystemApi
+ @WorkerThread
@Override
public void close() {
synchronized (mLock) {
@@ -991,12 +1013,15 @@
* <p>The new config must be {@link VirtualMachineConfig#isCompatibleWith compatible with} the
* existing config.
*
+ * <p>NOTE: This method may block and should not be called on the main thread.
+ *
* @return the old config
* @throws VirtualMachineException if the virtual machine is not stopped, or the new config is
* incompatible.
* @hide
*/
@SystemApi
+ @WorkerThread
@NonNull
public VirtualMachineConfig setConfig(@NonNull VirtualMachineConfig newConfig)
throws VirtualMachineException {
@@ -1025,11 +1050,14 @@
* VirtualMachineCallback#onPayloadReady(VirtualMachine)}, it can use this method to establish a
* connection to the guest VM.
*
+ * <p>NOTE: This method may block and should not be called on the main thread.
+ *
* @throws VirtualMachineException if the virtual machine is not running or the connection
* failed.
* @hide
*/
@SystemApi
+ @WorkerThread
@NonNull
public IBinder connectToVsockServer(
@IntRange(from = MIN_VSOCK_PORT, to = MAX_VSOCK_PORT) long port)
@@ -1048,10 +1076,13 @@
/**
* Opens a vsock connection to the VM on the given port.
*
+ * <p>NOTE: This method may block and should not be called on the main thread.
+ *
* @throws VirtualMachineException if connecting fails.
* @hide
*/
@SystemApi
+ @WorkerThread
@NonNull
public ParcelFileDescriptor connectVsock(
@IntRange(from = MIN_VSOCK_PORT, to = MAX_VSOCK_PORT) long port)
@@ -1097,12 +1128,15 @@
* VirtualMachineManager#importFromDescriptor} is called. It is recommended that the VM not be
* started until that operation is complete.
*
+ * <p>NOTE: This method may block and should not be called on the main thread.
+ *
* @return a {@link VirtualMachineDescriptor} instance that represents the VM's state.
* @throws VirtualMachineException if the virtual machine is not stopped, or the state could not
* be captured.
* @hide
*/
@SystemApi
+ @WorkerThread
@NonNull
public VirtualMachineDescriptor toDescriptor() throws VirtualMachineException {
synchronized (mLock) {
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachineManager.java b/javalib/src/android/system/virtualmachine/VirtualMachineManager.java
index 5b30617..6aa8133 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachineManager.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachineManager.java
@@ -24,6 +24,7 @@
import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
+import android.annotation.WorkerThread;
import android.content.Context;
import android.content.pm.PackageManager;
import android.sysprop.HypervisorProperties;
@@ -123,12 +124,15 @@
* the name and the config are the same as a deleted one. The new virtual machine will initially
* be stopped.
*
+ * <p>NOTE: This method may block and should not be called on the main thread.
+ *
* @throws VirtualMachineException if the VM cannot be created, or there is an existing VM with
* the given name.
* @hide
*/
@SystemApi
@NonNull
+ @WorkerThread
@RequiresPermission(VirtualMachine.MANAGE_VIRTUAL_MACHINE_PERMISSION)
public VirtualMachine create(@NonNull String name, @NonNull VirtualMachineConfig config)
throws VirtualMachineException {
@@ -154,12 +158,15 @@
* machine instance. Multiple calls to get() passing the same name will get the same object
* returned, until the virtual machine is deleted (via {@link #delete}) and then recreated.
*
+ * <p>NOTE: This method may block and should not be called on the main thread.
+ *
* @see #getOrCreate
* @throws VirtualMachineException if the virtual machine exists but could not be successfully
* retrieved.
* @hide
*/
@SystemApi
+ @WorkerThread
@Nullable
public VirtualMachine get(@NonNull String name) throws VirtualMachineException {
synchronized (sCreateLock) {
@@ -186,11 +193,14 @@
*
* <p>The new virtual machine will be in the same state as the descriptor indicates.
*
+ * <p>NOTE: This method may block and should not be called on the main thread.
+ *
* @throws VirtualMachineException if the VM cannot be imported.
* @hide
*/
@NonNull
@SystemApi
+ @WorkerThread
public VirtualMachine importFromDescriptor(
@NonNull String name, @NonNull VirtualMachineDescriptor vmDescriptor)
throws VirtualMachineException {
@@ -205,10 +215,13 @@
* Returns an existing {@link VirtualMachine} if it exists, or create a new one. The config
* parameter is used only when a new virtual machine is created.
*
+ * <p>NOTE: This method may block and should not be called on the main thread.
+ *
* @throws VirtualMachineException if the virtual machine could not be created or retrieved.
* @hide
*/
@SystemApi
+ @WorkerThread
@NonNull
public VirtualMachine getOrCreate(@NonNull String name, @NonNull VirtualMachineConfig config)
throws VirtualMachineException {
@@ -229,11 +242,14 @@
* with the same name is different from an already deleted virtual machine even if it has the
* same config.
*
+ * <p>NOTE: This method may block and should not be called on the main thread.
+ *
* @throws VirtualMachineException if the virtual machine does not exist, is not stopped, or
* cannot be deleted.
* @hide
*/
@SystemApi
+ @WorkerThread
public void delete(@NonNull String name) throws VirtualMachineException {
synchronized (sCreateLock) {
VirtualMachine vm = getVmByName(name);