Merge "pvmfw: bcc: Move wrong method documentation" into main
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/InstalledImage.kt b/android/TerminalApp/java/com/android/virtualization/terminal/InstalledImage.kt
index 086ff3d..59be4ae 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/InstalledImage.kt
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/InstalledImage.kt
@@ -83,7 +83,7 @@
}
@Throws(IOException::class)
- fun getSize(): Long {
+ fun getApparentSize(): Long {
return Files.size(rootPartition)
}
@@ -118,7 +118,7 @@
@Throws(IOException::class)
fun resize(desiredSize: Long): Long {
val roundedUpDesiredSize = roundUp(desiredSize)
- val curSize = getSize()
+ val curSize = getApparentSize()
runE2fsck(rootPartition)
@@ -130,7 +130,21 @@
allocateSpace(rootPartition, roundedUpDesiredSize)
}
resizeFilesystem(rootPartition, roundedUpDesiredSize)
- return getSize()
+ return getApparentSize()
+ }
+
+ @Throws(IOException::class)
+ fun shrinkToMinimumSize(): Long {
+ // Fix filesystem before resizing.
+ runE2fsck(rootPartition)
+
+ val p: String = rootPartition.toAbsolutePath().toString()
+ runCommand("/system/bin/resize2fs", "-M", p)
+ Log.d(TAG, "resize2fs -M completed: $rootPartition")
+
+ // resize2fs may result in an inconsistent filesystem state. Fix with e2fsck.
+ runE2fsck(rootPartition)
+ return getApparentSize()
}
@Throws(IOException::class)
@@ -213,7 +227,7 @@
}
}
- private fun roundUp(bytes: Long): Long {
+ internal fun roundUp(bytes: Long): Long {
// Round up every diskSizeStep MB
return ceil((bytes.toDouble()) / RESIZE_STEP_BYTES).toLong() * RESIZE_STEP_BYTES
}
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.kt b/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.kt
index d85242b..52afef4 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.kt
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.kt
@@ -50,6 +50,7 @@
import com.android.internal.annotations.VisibleForTesting
import com.android.microdroid.test.common.DeviceProperties
import com.android.system.virtualmachine.flags.Flags
+import com.android.virtualization.terminal.ErrorActivity.Companion.start
import com.android.virtualization.terminal.VmLauncherService.VmLauncherServiceCallback
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayoutMediator
@@ -368,7 +369,7 @@
)
.build()
- val diskSize = intent.getLongExtra(EXTRA_DISK_SIZE, image.getSize())
+ val diskSize = intent.getLongExtra(EXTRA_DISK_SIZE, image.getApparentSize())
val intent =
VmLauncherService.getIntentForStart(
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/SettingsActivity.kt b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsActivity.kt
index a4a0a84..1183b46 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/SettingsActivity.kt
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsActivity.kt
@@ -19,6 +19,7 @@
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
+import com.android.system.virtualmachine.flags.Flags
import com.google.android.material.appbar.MaterialToolbar
class SettingsActivity : AppCompatActivity() {
@@ -29,27 +30,34 @@
val toolbar: MaterialToolbar = findViewById(R.id.settings_toolbar)
setSupportActionBar(toolbar)
- val settingsItems =
- arrayOf(
+ var settingsItems = mutableListOf<SettingsItem>()
+ if (!Flags.terminalStorageBalloon()) {
+ settingsItems.add(
SettingsItem(
resources.getString(R.string.settings_disk_resize_title),
resources.getString(R.string.settings_disk_resize_sub_title),
R.drawable.baseline_storage_24,
SettingsItemEnum.DiskResize,
- ),
- SettingsItem(
- resources.getString(R.string.settings_port_forwarding_title),
- resources.getString(R.string.settings_port_forwarding_sub_title),
- R.drawable.baseline_call_missed_outgoing_24,
- SettingsItemEnum.PortForwarding,
- ),
- SettingsItem(
- resources.getString(R.string.settings_recovery_title),
- resources.getString(R.string.settings_recovery_sub_title),
- R.drawable.baseline_settings_backup_restore_24,
- SettingsItemEnum.Recovery,
- ),
+ )
)
+ }
+ settingsItems.add(
+ SettingsItem(
+ resources.getString(R.string.settings_port_forwarding_title),
+ resources.getString(R.string.settings_port_forwarding_sub_title),
+ R.drawable.baseline_call_missed_outgoing_24,
+ SettingsItemEnum.PortForwarding,
+ )
+ )
+ settingsItems.add(
+ SettingsItem(
+ resources.getString(R.string.settings_recovery_title),
+ resources.getString(R.string.settings_recovery_sub_title),
+ R.drawable.baseline_settings_backup_restore_24,
+ SettingsItemEnum.Recovery,
+ )
+ )
+
val settingsListItemAdapter = SettingsItemAdapter(settingsItems)
val recyclerView: RecyclerView = findViewById(R.id.settings_list_recycler_view)
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/SettingsDiskResizeActivity.kt b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsDiskResizeActivity.kt
index af1ae95..144db40 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/SettingsDiskResizeActivity.kt
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsDiskResizeActivity.kt
@@ -71,7 +71,7 @@
diskSizeStepMb = 1L shl resources.getInteger(R.integer.disk_size_round_up_step_size_in_mb)
val image = InstalledImage.getDefault(this)
- diskSizeMb = bytesToMb(image.getSize())
+ diskSizeMb = bytesToMb(image.getApparentSize())
val minDiskSizeMb = bytesToMb(image.getSmallestSizePossible()).coerceAtMost(diskSizeMb)
val usableSpaceMb =
bytesToMb(Environment.getDataDirectory().getUsableSpace()) and
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/SettingsItemAdapter.kt b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsItemAdapter.kt
index 132d749..aa0c3f5 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/SettingsItemAdapter.kt
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/SettingsItemAdapter.kt
@@ -24,7 +24,7 @@
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.card.MaterialCardView
-class SettingsItemAdapter(private val dataSet: Array<SettingsItem>) :
+class SettingsItemAdapter(private val dataSet: List<SettingsItem>) :
RecyclerView.Adapter<SettingsItemAdapter.ViewHolder>() {
class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/SplitInitializer.kt b/android/TerminalApp/java/com/android/virtualization/terminal/SplitInitializer.kt
index 7562779..2653ba9 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/SplitInitializer.kt
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/SplitInitializer.kt
@@ -15,16 +15,96 @@
*/
package com.android.virtualization.terminal
+import android.content.ComponentName
import android.content.Context
+import android.content.Intent
import androidx.startup.Initializer
+import androidx.window.embedding.ActivityFilter
+import androidx.window.embedding.EmbeddingAspectRatio
import androidx.window.embedding.RuleController
+import androidx.window.embedding.SplitAttributes
+import androidx.window.embedding.SplitPairFilter
+import androidx.window.embedding.SplitPairRule
+import androidx.window.embedding.SplitPlaceholderRule
+import androidx.window.embedding.SplitRule
+import com.android.system.virtualmachine.flags.Flags
class SplitInitializer : Initializer<RuleController> {
override fun create(context: Context): RuleController {
- return RuleController.getInstance(context).apply {
- setRules(RuleController.parseRules(context, R.xml.main_split_config))
+ val filters =
+ mutableSetOf(
+ SplitPairFilter(
+ ComponentName(context, SettingsActivity::class.java),
+ ComponentName(context, SettingsPortForwardingActivity::class.java),
+ null,
+ )
+ )
+
+ if (Flags.terminalStorageBalloon()) {
+ filters.add(
+ SplitPairFilter(
+ ComponentName(context, SettingsActivity::class.java),
+ ComponentName(context, SettingsDiskResizeActivity::class.java),
+ null,
+ )
+ )
}
+
+ filters.add(
+ SplitPairFilter(
+ ComponentName(context, SettingsActivity::class.java),
+ ComponentName(context, SettingsRecoveryActivity::class.java),
+ null,
+ )
+ )
+ val splitPairRules =
+ SplitPairRule.Builder(filters)
+ .setClearTop(true)
+ .setFinishPrimaryWithSecondary(SplitRule.FinishBehavior.ADJACENT)
+ .setFinishSecondaryWithPrimary(SplitRule.FinishBehavior.ALWAYS)
+ .setDefaultSplitAttributes(
+ SplitAttributes.Builder()
+ .setLayoutDirection(SplitAttributes.LayoutDirection.LOCALE)
+ .setSplitType(
+ SplitAttributes.SplitType.ratio(
+ context.resources.getFloat(R.dimen.activity_split_ratio)
+ )
+ )
+ .build()
+ )
+ .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ALWAYS_ALLOW)
+ .setMinWidthDp(context.resources.getInteger(R.integer.split_min_width))
+ .build()
+
+ val placeholderRule =
+ SplitPlaceholderRule.Builder(
+ setOf(
+ ActivityFilter(ComponentName(context, SettingsActivity::class.java), null)
+ ),
+ Intent(context, SettingsDiskResizeActivity::class.java),
+ )
+ .setFinishPrimaryWithPlaceholder(SplitRule.FinishBehavior.ADJACENT)
+ .setDefaultSplitAttributes(
+ SplitAttributes.Builder()
+ .setLayoutDirection(SplitAttributes.LayoutDirection.LOCALE)
+ .setSplitType(
+ SplitAttributes.SplitType.ratio(
+ context.resources.getFloat(R.dimen.activity_split_ratio)
+ )
+ )
+ .build()
+ )
+ .setMaxAspectRatioInPortrait(EmbeddingAspectRatio.ALWAYS_ALLOW)
+ .setMinWidthDp(context.resources.getInteger(R.integer.split_min_width))
+ .setSticky(false)
+ .build()
+
+ val ruleController = RuleController.getInstance(context)
+ ruleController.addRule(splitPairRules)
+ ruleController.addRule(placeholderRule)
+
+ return ruleController
}
override fun dependencies(): List<Class<out Initializer<*>>> {
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/VmLauncherService.kt b/android/TerminalApp/java/com/android/virtualization/terminal/VmLauncherService.kt
index f426ce6..d43a8d1 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/VmLauncherService.kt
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/VmLauncherService.kt
@@ -41,6 +41,7 @@
import android.widget.Toast
import androidx.annotation.WorkerThread
import com.android.system.virtualmachine.flags.Flags
+import com.android.virtualization.terminal.InstalledImage.Companion.roundUp
import com.android.virtualization.terminal.MainActivity.Companion.PREFIX
import com.android.virtualization.terminal.MainActivity.Companion.TAG
import io.grpc.Grpc
@@ -120,7 +121,7 @@
// Note: this doesn't always do the resizing. If the current image size is the same
// as the requested size which is rounded up to the page alignment, resizing is not
// done.
- val diskSize = intent.getLongExtra(EXTRA_DISK_SIZE, image.getSize())
+ val diskSize = intent.getLongExtra(EXTRA_DISK_SIZE, image.getApparentSize())
mainWorkerThread.submit({
doStart(notification, displayInfo, diskSize, resultReceiver)
@@ -140,16 +141,18 @@
return START_NOT_STICKY
}
- private fun truncateDiskIfNecessary(image: InstalledImage) {
- val curSize = image.getSize()
- val physicalSize = image.getPhysicalSize()
-
- // Change the rootfs disk's apparent size to GUEST_SPARSE_DISK_SIZE_PERCENTAGE of the total
- // disk size.
- // Note that the physical size is not changed.
+ private fun calculateSparseDiskSize(): Long {
+ // With storage ballooning enabled, we create a sparse file with 95% of the total size.
val statFs = StatFs(filesDir.absolutePath)
val hostSize = statFs.totalBytes
- val expectedSize = hostSize * GUEST_SPARSE_DISK_SIZE_PERCENTAGE / 100
+ return roundUp(hostSize * GUEST_SPARSE_DISK_SIZE_PERCENTAGE / 100)
+ }
+
+ private fun truncateDiskIfNecessary(image: InstalledImage) {
+ val curSize = image.getApparentSize()
+ val physicalSize = image.getPhysicalSize()
+
+ val expectedSize = calculateSparseDiskSize()
Log.d(
TAG,
"rootfs apparent size=$curSize, physical size=$physicalSize, expectedSize=$expectedSize",
@@ -164,6 +167,38 @@
}
}
+ // Convert the rootfs disk to a non-sparse file.
+ private fun convertToNonSparseDiskIfNecessary(image: InstalledImage) {
+ try {
+ val curApparentSize = image.getApparentSize()
+ val curPhysicalSize = image.getPhysicalSize()
+ Log.d(TAG, "Current disk size: apparent=$curApparentSize, physical=$curPhysicalSize")
+
+ // If storage ballooning was enabled via Flags.terminalStorageBalloon() before but it's
+ // now disabled, the disk is still a sparse file whose apparent size is too large.
+ // We need to shrink it to the minimum size.
+ //
+ // The disk file is considered sparse if its apparent disk size matches the expected
+ // sparse disk size.
+ // In addition, we consider it sparse if the physical size is clearly smaller than its
+ // apparent size. This additional condition is a fallback for cases
+ // where the logic of calculating the expected sparse disk size since the disk is
+ // created.
+ if (
+ curApparentSize == calculateSparseDiskSize() ||
+ curPhysicalSize <
+ curApparentSize * EXPECTED_PHYSICAL_SIZE_PERCENTAGE_FOR_NON_SPARSE / 100
+ ) {
+ Log.d(TAG, "A sparse disk is detected. Shrink it to the minimum size.")
+ val newSize = image.shrinkToMinimumSize()
+ Log.d(TAG, "Shrink the disk image: $curApparentSize -> $newSize")
+ }
+ } catch (e: IOException) {
+ throw RuntimeException("Failed to shrink rootfs disk", e)
+ return
+ }
+ }
+
@WorkerThread
private fun doStart(
notification: Notification,
@@ -180,6 +215,10 @@
// When storage ballooning flag is enabled, convert rootfs disk into a sparse file.
truncateDiskIfNecessary(image)
} else {
+ // Convert rootfs disk into a sparse file if storage ballooning flag had been enabled
+ // and then disabled.
+ convertToNonSparseDiskIfNecessary(image)
+
// Note: this doesn't always do the resizing. If the current image size is the same as
// the requested size which is rounded up to the page alignment, resizing is not done.
image.resize(diskSize)
@@ -479,6 +518,7 @@
private const val KEY_TERMINAL_PORT = "port"
private const val GUEST_SPARSE_DISK_SIZE_PERCENTAGE = 95
+ private const val EXPECTED_PHYSICAL_SIZE_PERCENTAGE_FOR_NON_SPARSE = 90
private val VM_BOOT_TIMEOUT_SECONDS: Int =
{
diff --git a/android/TerminalApp/res/values/config.xml b/android/TerminalApp/res/values/config.xml
index 7f0b5e6..a2c9183 100644
--- a/android/TerminalApp/res/values/config.xml
+++ b/android/TerminalApp/res/values/config.xml
@@ -16,4 +16,5 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<bool name="terminal_portrait_only">true</bool>
+ <item name="activity_split_ratio" format="float" type="dimen">0.3</item>
</resources>
diff --git a/android/TerminalApp/res/values/dimens.xml b/android/TerminalApp/res/values/dimens.xml
deleted file mode 100644
index e00ef7c..0000000
--- a/android/TerminalApp/res/values/dimens.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-
-<resources>
- <dimen name="activity_split_ratio">0.3</dimen>
-</resources>
\ No newline at end of file
diff --git a/android/TerminalApp/res/xml/main_split_config.xml b/android/TerminalApp/res/xml/main_split_config.xml
deleted file mode 100644
index bd0271b..0000000
--- a/android/TerminalApp/res/xml/main_split_config.xml
+++ /dev/null
@@ -1,50 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright 2024 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-
-<resources xmlns:window="http://schemas.android.com/apk/res-auto">
-
- <!-- Define a split for the named activities. -->
- <SplitPairRule
- window:clearTop="true"
- window:finishPrimaryWithSecondary="adjacent"
- window:finishSecondaryWithPrimary="always"
- window:splitLayoutDirection="locale"
- window:splitMaxAspectRatioInPortrait="alwaysAllow"
- window:splitMinWidthDp="@integer/split_min_width"
- window:splitRatio="@dimen/activity_split_ratio">
- <SplitPairFilter
- window:primaryActivityName="com.android.virtualization.terminal.SettingsActivity"
- window:secondaryActivityName="com.android.virtualization.terminal.SettingsDiskResizeActivity" />
- <SplitPairFilter
- window:primaryActivityName="com.android.virtualization.terminal.SettingsActivity"
- window:secondaryActivityName="com.android.virtualization.terminal.SettingsPortForwardingActivity" />
- <SplitPairFilter
- window:primaryActivityName="com.android.virtualization.terminal.SettingsActivity"
- window:secondaryActivityName="com.android.virtualization.terminal.SettingsRecoveryActivity" />
- </SplitPairRule>
-
- <SplitPlaceholderRule
- window:placeholderActivityName="com.android.virtualization.terminal.SettingsDiskResizeActivity"
- window:finishPrimaryWithPlaceholder="adjacent"
- window:splitLayoutDirection="locale"
- window:splitMaxAspectRatioInPortrait="alwaysAllow"
- window:splitMinWidthDp="@integer/split_min_width"
- window:splitRatio="@dimen/activity_split_ratio"
- window:stickyPlaceholder="false">
- <ActivityFilter
- window:activityName="com.android.virtualization.terminal.SettingsActivity"/>
- </SplitPlaceholderRule>
-</resources>
\ No newline at end of file
diff --git a/android/virtmgr/src/aidl.rs b/android/virtmgr/src/aidl.rs
index d7f68b8..190acc7 100644
--- a/android/virtmgr/src/aidl.rs
+++ b/android/virtmgr/src/aidl.rs
@@ -641,6 +641,24 @@
let calling_partition = find_partition(CALLING_EXE_PATH.as_deref())?;
+ let instance_id = extract_instance_id(config);
+ // Require vendor instance IDs to start with a specific prefix so that they don't conflict
+ // with system instance IDs.
+ //
+ // We should also make sure that non-vendor VMs do not use the vendor prefix, but there are
+ // already system VMs in the wild that may have randomly generated IDs with the prefix, so,
+ // for now, we only check in one direction.
+ const INSTANCE_ID_VENDOR_PREFIX: &[u8] = &[0xFF, 0xFF, 0xFF, 0xFF];
+ if matches!(calling_partition, CallingPartition::Vendor | CallingPartition::Odm)
+ && !instance_id.starts_with(INSTANCE_ID_VENDOR_PREFIX)
+ {
+ return Err(anyhow!(
+ "vendor initiated VMs must have instance IDs starting with 0xFFFFFFFF, got {}",
+ hex::encode(instance_id)
+ ))
+ .or_service_specific_exception(-1);
+ }
+
check_config_features(config)?;
if cfg!(early) {
@@ -668,7 +686,6 @@
check_gdb_allowed(config)?;
}
- let instance_id = extract_instance_id(config);
let mut device_tree_overlays = vec![];
if let Some(dt_overlay) =
maybe_create_reference_dt_overlay(config, &instance_id, &temporary_directory)?
diff --git a/android/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl b/android/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl
index 5193e21..7db5135 100644
--- a/android/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl
+++ b/android/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineAppConfig.aidl
@@ -23,7 +23,11 @@
/** Name of VM */
String name;
- /** Id of the VM instance */
+ /**
+ * Id of the VM instance
+ *
+ * See AVirtualMachineRawConfig_setInstanceId for details.
+ */
byte[64] instanceId;
/** Main APK */
diff --git a/android/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl b/android/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl
index c5fe982..1e4fe03 100644
--- a/android/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl
+++ b/android/virtualizationservice/aidl/android/system/virtualizationservice/VirtualMachineRawConfig.aidl
@@ -31,7 +31,11 @@
/** Name of VM */
String name;
- /** Id of the VM instance */
+ /**
+ * Id of the VM instance
+ *
+ * See AVirtualMachineRawConfig_setInstanceId for details.
+ */
byte[64] instanceId;
/** The kernel image, if any. */
diff --git a/android/virtualizationservice/src/aidl.rs b/android/virtualizationservice/src/aidl.rs
index 1646117..e26cd4f 100644
--- a/android/virtualizationservice/src/aidl.rs
+++ b/android/virtualizationservice/src/aidl.rs
@@ -489,6 +489,9 @@
id.try_fill(&mut rand::thread_rng())
.context("Failed to allocate instance_id")
.or_service_specific_exception(-1)?;
+ // Randomly allocated IDs always start with all 7s to avoid colliding with statically
+ // assigned IDs.
+ id[..4].fill(0x77);
let uid = get_calling_uid();
info!("Allocated a VM's instance_id: {:?}..., for uid: {:?}", &hex::encode(id)[..8], uid);
self.try_updating_sk_state(&id);
diff --git a/libs/libavf/include/android/virtualization.h b/libs/libavf/include/android/virtualization.h
index 4bfe47a..e907ac4 100644
--- a/libs/libavf/include/android/virtualization.h
+++ b/libs/libavf/include/android/virtualization.h
@@ -78,6 +78,9 @@
* The `instanceId` is expected to be re-used for the VM instance with an associated state (secret,
* encrypted storage) - i.e., rebooting the VM must not change the instanceId.
*
+ * `instanceId` MUST start with 0xFFFFFFFF if and only if this library is being
+ * called from code in a vendor or odm partition,
+ *
* \param config a virtual machine config object.
* \param instanceId a pointer to a 64-byte buffer for the instance ID.
* \param instanceIdSize the number of bytes in `instanceId`.