Merge changes from topic "libfdt_baremetal" into main
* changes:
liblibfdt: Add no_std variant for libfdt_baremetal
liblibfdt_bindgen: Only use libfdt as header_libs
diff --git a/OWNERS b/OWNERS
index 717a4db..afd2555 100644
--- a/OWNERS
+++ b/OWNERS
@@ -35,3 +35,4 @@
per-file android/TerminalApp/**=jiyong@google.com,jeongik@google.com
per-file android/VmLauncherApp/**=jiyong@google.com,jeongik@google.com
per-file libs/vm_launcher_lib/**=jiyong@google.com,jeongik@google.com
+per-file build/debian/**=jiyong@google.com,jeongik@google.com
diff --git a/android/FerrochromeApp/Android.bp b/android/FerrochromeApp/Android.bp
deleted file mode 100644
index 3e4ad14..0000000
--- a/android/FerrochromeApp/Android.bp
+++ /dev/null
@@ -1,42 +0,0 @@
-package {
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-java_defaults {
- name: "VmPayloadInstaller",
- init_rc: [":custom_vm_setup.rc"],
- required: ["custom_vm_setup"],
- // TODO(b/348113995): move this app to product partition
- system_ext_specific: true,
- platform_apis: true,
- privileged: true,
-}
-
-android_app {
- name: "FerrochromeApp",
- srcs: ["java/**/*.java"],
- resource_dirs: ["res"],
- defaults: ["VmPayloadInstaller"],
- required: [
- "privapp-permissions-ferrochrome.xml",
- ],
-}
-
-prebuilt_etc {
- name: "privapp-permissions-ferrochrome.xml",
- src: "privapp-permissions-ferrochrome.xml",
- sub_dir: "permissions",
- system_ext_specific: true,
-}
-
-filegroup {
- name: "custom_vm_setup.rc",
- srcs: ["custom_vm_setup.rc"],
-}
-
-sh_binary {
- name: "custom_vm_setup",
- src: "custom_vm_setup.sh",
- system_ext_specific: true,
- host_supported: false,
-}
diff --git a/android/FerrochromeApp/AndroidManifest.xml b/android/FerrochromeApp/AndroidManifest.xml
deleted file mode 100644
index f6d3f6a..0000000
--- a/android/FerrochromeApp/AndroidManifest.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.virtualization.ferrochrome" >
-
- <uses-permission android:name="android.permission.INTERNET" />
- <uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES" />
- <uses-permission android:name="android.permission.KILL_ALL_BACKGROUND_PROCESSES" />
- <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
- <uses-permission android:name="com.android.virtualization.vmlauncher.permission.USE_VM_LAUNCHER" />
-
- <queries>
- <intent>
- <action android:name="android.virtualization.VM_LAUNCHER" />
- </intent>
- <intent>
- <action android:name="android.virtualization.FERROCHROME_DOWNLOADER" />
- </intent>
- </queries>
- <application
- android:label="Ferrochrome">
- <activity android:name=".FerrochromeActivity"
- android:configChanges="orientation|screenSize|keyboard|keyboardHidden|navigation|uiMode"
- android:screenOrientation="landscape"
- android:resizeableActivity="false"
- android:exported="true">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- <activity android:name=".OpenUrlActivity"
- android:theme="@android:style/Theme.NoDisplay"
- android:launchMode="singleTask"
- android:exported="true">
- <intent-filter>
- <action android:name="android.intent.action.SEND" />
- <category android:name="android.intent.category.DEFAULT" />
- <data android:mimeType="text/*" />
- </intent-filter>
- </activity>
- </application>
-
-</manifest>
diff --git a/android/FerrochromeApp/java/com/android/virtualization/ferrochrome/FerrochromeActivity.java b/android/FerrochromeApp/java/com/android/virtualization/ferrochrome/FerrochromeActivity.java
deleted file mode 100644
index dba0078..0000000
--- a/android/FerrochromeApp/java/com/android/virtualization/ferrochrome/FerrochromeActivity.java
+++ /dev/null
@@ -1,307 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-package com.android.virtualization.ferrochrome;
-
-import android.annotation.WorkerThread;
-import android.app.Activity;
-import android.app.ActivityManager;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.os.Bundle;
-import android.os.Environment;
-import android.os.SystemProperties;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.WindowManager;
-import android.widget.TextView;
-
-import java.io.BufferedReader;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.StandardCopyOption;
-import java.util.List;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
-public class FerrochromeActivity extends Activity {
- private static final String TAG = FerrochromeActivity.class.getName();
- private static final String ACTION_VM_LAUNCHER = "android.virtualization.VM_LAUNCHER";
- private static final String ACTION_FERROCHROME_DOWNLOAD =
- "android.virtualization.FERROCHROME_DOWNLOADER";
- private static final String EXTRA_FERROCHROME_DEST_DIR = "dest_dir";
- private static final String EXTRA_FERROCHROME_UPDATE_NEEDED = "update_needed";
-
- private static final Path DEST_DIR =
- Path.of(Environment.getExternalStorageDirectory().getPath(), "ferrochrome");
- private static final String ASSET_DIR = "ferrochrome";
- private static final Path VERSION_FILE = Path.of(DEST_DIR.toString(), "version");
-
- private static final int REQUEST_CODE_VMLAUNCHER = 1;
- private static final int REQUEST_CODE_FERROCHROME_DOWNLOADER = 2;
-
- private ResolvedActivity mVmLauncher;
-
- ExecutorService executorService = Executors.newSingleThreadExecutor();
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- if (!isTaskRoot()) {
- // In case we launched this activity multiple times, only start one instance of this
- // activity by only starting this as the root activity in task.
- finish();
- Log.w(TAG, "Not starting because not task root");
- return;
- }
- setContentView(R.layout.activity_ferrochrome);
- getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
-
- // Find VM Launcher
- mVmLauncher = ResolvedActivity.resolve(getPackageManager(), ACTION_VM_LAUNCHER);
- if (mVmLauncher == null) {
- updateStatus("Failed to resolve VM Launcher");
- return;
- }
-
- // Clean up the existing vm launcher process if there is
- ActivityManager am = getSystemService(ActivityManager.class);
- am.killBackgroundProcesses(mVmLauncher.activityInfo.packageName);
-
- executorService.execute(
- () -> {
- if (hasLocalAssets()) {
- if (updateImageIfNeeded()) {
- updateStatus("Starting Ferrochrome...");
- runOnUiThread(
- () ->
- startActivityForResult(
- mVmLauncher.intent, REQUEST_CODE_VMLAUNCHER));
- }
- } else {
- tryLaunchDownloader();
- }
- });
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- if (requestCode == REQUEST_CODE_VMLAUNCHER) {
- finishAndRemoveTask();
- } else if (requestCode == REQUEST_CODE_FERROCHROME_DOWNLOADER) {
- String destDir = data.getStringExtra(EXTRA_FERROCHROME_DEST_DIR);
- boolean updateNeeded =
- data.getBooleanExtra(EXTRA_FERROCHROME_UPDATE_NEEDED, /* default= */ true);
-
- if (resultCode != RESULT_OK || TextUtils.isEmpty(destDir)) {
- Log.w(
- TAG,
- "Ferrochrome downloader returned error, code="
- + resultCode
- + ", dest="
- + destDir);
- updateStatus("User didn't accepted ferrochrome download..");
- return;
- }
-
- Log.w(TAG, "Ferrochrome downloader returned OK");
-
- if (!updateNeeded) {
- updateStatus("Starting Ferrochrome...");
- startActivityForResult(mVmLauncher.intent, REQUEST_CODE_VMLAUNCHER);
- }
-
- executorService.execute(
- () -> {
- if (!extractImages(destDir)) {
- updateStatus("Images from downloader looks bad..");
- return;
- }
- updateStatus("Starting Ferrochrome...");
- runOnUiThread(
- () ->
- startActivityForResult(
- mVmLauncher.intent, REQUEST_CODE_VMLAUNCHER));
- });
- }
- }
-
- @WorkerThread
- private boolean hasLocalAssets() {
- try {
- String[] files = getAssets().list(ASSET_DIR);
- return files != null && files.length > 0;
- } catch (IOException e) {
- return false;
- }
- }
-
- @WorkerThread
- private boolean updateImageIfNeeded() {
- if (!isUpdateNeeded()) {
- Log.d(TAG, "No update needed.");
- return true;
- }
-
- try {
- if (Files.notExists(DEST_DIR)) {
- Files.createDirectory(DEST_DIR);
- }
-
- updateStatus("Copying images...");
- String[] files = getAssets().list("ferrochrome");
- for (String file : files) {
- updateStatus(file);
- Path dst = Path.of(DEST_DIR.toString(), file);
- updateFile(getAssets().open("ferrochrome/" + file), dst);
- }
- } catch (IOException e) {
- Log.e(TAG, "Error while updating image: " + e);
- updateStatus("Failed.");
- return false;
- }
- updateStatus("Done.");
-
- return extractImages(DEST_DIR.toAbsolutePath().toString());
- }
-
- @WorkerThread
- private void tryLaunchDownloader() {
- // TODO(jaewan): Add safeguard to check whether ferrochrome downloader is valid.
- Log.w(TAG, "No built-in assets found. Try again with ferrochrome downloader");
-
- ResolvedActivity downloader =
- ResolvedActivity.resolve(getPackageManager(), ACTION_FERROCHROME_DOWNLOAD);
- if (downloader == null) {
- Log.d(TAG, "Ferrochrome downloader doesn't exist");
- updateStatus("ChromeOS image not found. Please go/try-ferrochrome");
- return;
- }
- String pkgName = downloader.activityInfo.packageName;
- Log.d(TAG, "Resolved Ferrochrome Downloader, pkgName=" + pkgName);
- updateStatus("Launching Ferrochrome downloader for update");
-
- // onActivityResult() will handle downloader result.
- startActivityForResult(downloader.intent, REQUEST_CODE_FERROCHROME_DOWNLOADER);
- }
-
- @WorkerThread
- private boolean extractImages(String destDir) {
- updateStatus("Extracting images...");
-
- if (TextUtils.isEmpty(destDir)) {
- throw new RuntimeException("Internal error: destDir shouldn't be null");
- }
-
- SystemProperties.set("debug.custom_vm_setup.path", destDir);
- SystemProperties.set("debug.custom_vm_setup.done", "false");
- SystemProperties.set("debug.custom_vm_setup.start", "true");
- while (!SystemProperties.getBoolean("debug.custom_vm_setup.done", false)) {
- try {
- Thread.sleep(1000);
- } catch (Exception e) {
- Log.e(TAG, "Error while extracting image: " + e);
- updateStatus("Failed.");
- return false;
- }
- }
-
- updateStatus("Done.");
- return true;
- }
-
- @WorkerThread
- private boolean isUpdateNeeded() {
- Path[] pathsToCheck = {DEST_DIR, VERSION_FILE};
- for (Path p : pathsToCheck) {
- if (Files.notExists(p)) {
- Log.d(TAG, p.toString() + " does not exist.");
- return true;
- }
- }
-
- try {
- String installedVer = readLine(new FileInputStream(VERSION_FILE.toFile()));
- String updatedVer = readLine(getAssets().open("ferrochrome/version"));
- if (installedVer.equals(updatedVer)) {
- return false;
- }
- Log.d(TAG, "Version mismatch. Installed: " + installedVer + " Updated: " + updatedVer);
- } catch (IOException e) {
- Log.e(TAG, "Error while checking version: " + e);
- }
- return true;
- }
-
- private static String readLine(InputStream input) throws IOException {
- try (BufferedReader reader = new BufferedReader(new InputStreamReader(input))) {
- return reader.readLine();
- } catch (IOException e) {
- throw e;
- }
- }
-
- private static void updateFile(InputStream input, Path path) throws IOException {
- try {
- Files.copy(input, path, StandardCopyOption.REPLACE_EXISTING);
- } finally {
- input.close();
- }
- }
-
- private void updateStatus(String line) {
- runOnUiThread(
- () -> {
- TextView statusView = findViewById(R.id.status_txt_view);
- statusView.append(line + "\n");
- });
- }
-
- private static final class ResolvedActivity {
- public final ActivityInfo activityInfo;
- public final Intent intent;
-
- private ResolvedActivity(ActivityInfo activityInfo, Intent intent) {
- this.activityInfo = activityInfo;
- this.intent = intent;
- }
-
- /* synthetic access */
- static ResolvedActivity resolve(PackageManager pm, String action) {
- Intent intent = new Intent(action).setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
- List<ResolveInfo> resolveInfos =
- pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
- if (resolveInfos == null || resolveInfos.size() != 1) {
- Log.w(
- TAG,
- "Failed to resolve activity, action="
- + action
- + ", resolved="
- + resolveInfos);
- return null;
- }
- ActivityInfo activityInfo = resolveInfos.get(0).activityInfo;
- intent.setClassName(activityInfo.packageName, activityInfo.name);
- return new ResolvedActivity(activityInfo, intent);
- }
- }
-}
diff --git a/android/FerrochromeApp/java/com/android/virtualization/ferrochrome/OpenUrlActivity.java b/android/FerrochromeApp/java/com/android/virtualization/ferrochrome/OpenUrlActivity.java
deleted file mode 100644
index 433e89c..0000000
--- a/android/FerrochromeApp/java/com/android/virtualization/ferrochrome/OpenUrlActivity.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-package com.android.virtualization.ferrochrome;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.util.Log;
-
-public class OpenUrlActivity extends Activity {
- private static final String TAG = OpenUrlActivity.class.getSimpleName();
-
- private static final String ACTION_VM_OPEN_URL = "android.virtualization.VM_OPEN_URL";
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- finish();
-
- if (!Intent.ACTION_SEND.equals(getIntent().getAction())) {
- return;
- }
- String text = getIntent().getStringExtra(Intent.EXTRA_TEXT);
- if (text == null) {
- return;
- }
- Uri uri = Uri.parse(text);
- if (uri == null) {
- return;
- }
- String scheme = uri.getScheme();
- if (!("http".equals(scheme) || "https".equals(scheme) || "mailto".equals(scheme))) {
- Log.e(TAG, "Unsupported URL scheme: " + scheme);
- return;
- }
- Log.i(TAG, "Sending " + scheme + " URL to VM");
- startActivity(
- new Intent(ACTION_VM_OPEN_URL)
- .setFlags(
- Intent.FLAG_ACTIVITY_SINGLE_TOP
- | Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP)
- .putExtra(Intent.EXTRA_TEXT, text));
- }
-}
diff --git a/android/FerrochromeApp/privapp-permissions-ferrochrome.xml b/android/FerrochromeApp/privapp-permissions-ferrochrome.xml
deleted file mode 100644
index 987db9c..0000000
--- a/android/FerrochromeApp/privapp-permissions-ferrochrome.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<permissions>
- <privapp-permissions package="com.android.virtualization.ferrochrome">
- <permission name="android.permission.KILL_ALL_BACKGROUND_PROCESSES"/>
- </privapp-permissions>
-</permissions>
\ No newline at end of file
diff --git a/android/FerrochromeApp/repack.sh b/android/FerrochromeApp/repack.sh
deleted file mode 100755
index b2a96dd..0000000
--- a/android/FerrochromeApp/repack.sh
+++ /dev/null
@@ -1,53 +0,0 @@
-#!/bin/bash
-# Repacks chromiumos_*.bin into the assets of FerrochromeApp
-
-usage() {
- echo "Usage: $0 CHROME_OS_DISK_IMAGE"
- exit 1
-}
-
-if [ "$#" -ne 1 ]; then
- usage
-fi
-
-disk=$1
-
-loop=$(sudo losetup --show -f -P ${disk})
-kern=$(sudo fdisk -x ${loop} | grep KERN-A | awk "{print\$1}")
-root=$(sudo fdisk -x ${loop} | grep ROOT-A | awk "{print\$1}")
-efi=$(sudo fdisk -x ${loop} | grep EFI-SYSTEM | awk "{print\$1}")
-state=$(sudo fdisk -x ${loop} | grep STATE | awk "{print\$1}")
-root_guid=$(sudo fdisk -x ${loop} | grep ROOT-A | awk "{print\$6}")
-
-tempdir=$(mktemp -d)
-pushd ${tempdir} > /dev/null
-echo Extracting partition images...
-sudo cp --sparse=always ${kern} kernel.img
-sudo cp --sparse=always ${root} root.img
-sudo cp --sparse=always ${efi} efi.img
-sudo cp --sparse=always ${state} state.img
-sudo chmod 777 *.img
-
-echo Archiving. This can take long...
-tar czvS -f images.tar.gz *.img
-
-echo Splitting...
-split -b 100M -d images.tar.gz images.tar.gz.part
-
-popd > /dev/null
-asset_dir=$(dirname $0)/assets/ferrochrome
-echo Updating ${asset_dir}...
-vm_config_template=$(dirname $0)/vm_config.json.template
-mkdir -p ${asset_dir}
-rm ${asset_dir}/images.tar.gz.part*
-mv ${tempdir}/images.tar.gz.part* ${asset_dir}
-sed -E s/GUID/${root_guid}/ ${vm_config_template} > ${asset_dir}/vm_config.json
-
-echo Calculating hash...
-hash=$(cat ${tempdir}/images.tar.gz ${asset_dir}/vm_config.json | sha1sum | cut -d' ' -f 1)
-echo ${hash} > ${asset_dir}/version
-
-echo Cleanup...
-sudo losetup -d ${loop}
-rm -rf ${tempdir}
-echo Done.
diff --git a/android/FerrochromeApp/res/layout/activity_ferrochrome.xml b/android/FerrochromeApp/res/layout/activity_ferrochrome.xml
deleted file mode 100644
index 3967167..0000000
--- a/android/FerrochromeApp/res/layout/activity_ferrochrome.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<RelativeLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:fitsSystemWindows="true"
- android:paddingLeft="16dp"
- android:paddingRight="16dp">
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textSize="28sp"
- android:id="@+id/status_txt_view"/>
-
-</RelativeLayout>
diff --git a/android/FerrochromeApp/vm_config.json.template b/android/FerrochromeApp/vm_config.json.template
deleted file mode 100644
index 380f016..0000000
--- a/android/FerrochromeApp/vm_config.json.template
+++ /dev/null
@@ -1,56 +0,0 @@
-{
- "name": "cros",
- "disks": [
- {
- "writable": true,
- "partitions": [
- {
- "label": "STATE",
- "path": "/data/local/tmp/state.img",
- "writable": true
- },
- {
- "label": "KERN-A",
- "path": "/data/local/tmp/kernel.img"
- },
- {
- "label": "ROOT-A",
- "path": "/data/local/tmp/root.img",
- "guid": "GUID"
- },
- {
- "label": "EFI-SYSTEM",
- "path": "/data/local/tmp/efi.img"
- }
- ]
- }
- ],
- "protected": false,
- "cpu_topology": "match_host",
- "platform_version": "~1.0",
- "memory_mib": 8096,
- "debuggable": true,
- "console_out": true,
- "connect_console": true,
- "console_input_device": "hvc0",
- "network": true,
- "input": {
- "touchscreen": true,
- "keyboard": true,
- "mouse": true,
- "trackpad": true,
- "switches": true
- },
- "audio": {
- "speaker": true,
- "microphone": true
- },
- "gpu": {
- "backend": "virglrenderer",
- "context_types": ["virgl2"]
- },
- "display": {
- "scale": "0.77",
- "refresh_rate": "30"
- }
-}
diff --git a/android/LinuxInstaller/Android.bp b/android/LinuxInstaller/Android.bp
index f70452d..f7994ef 100644
--- a/android/LinuxInstaller/Android.bp
+++ b/android/LinuxInstaller/Android.bp
@@ -2,13 +2,22 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
+java_defaults {
+ name: "LinuxVmPayloadInstaller",
+ init_rc: [":linux_vm_setup.rc"],
+ required: ["linux_vm_setup"],
+ system_ext_specific: true,
+ platform_apis: true,
+ privileged: true,
+}
+
android_app {
name: "LinuxInstallerApp",
srcs: ["java/**/*.java"],
resource_dirs: ["res"],
asset_dirs: ["assets"],
manifest: "AndroidManifest.xml",
- defaults: ["VmPayloadInstaller"],
+ defaults: ["LinuxVmPayloadInstaller"],
overrides: ["LinuxInstallerAppStub"],
required: [
"privapp-permissions-linuxinstaller.xml",
@@ -21,7 +30,7 @@
srcs: ["java/**/*.java"],
resource_dirs: ["res"],
manifest: "AndroidManifest_stub.xml",
- defaults: ["VmPayloadInstaller"],
+ defaults: ["LinuxVmPayloadInstaller"],
required: [
"privapp-permissions-linuxinstaller.xml",
],
@@ -39,3 +48,15 @@
name: "com.android.virtualization.linuxinstaller_certificate",
certificate: "com_android_virtualization_linuxinstaller",
}
+
+filegroup {
+ name: "linux_vm_setup.rc",
+ srcs: ["linux_vm_setup.rc"],
+}
+
+sh_binary {
+ name: "linux_vm_setup",
+ src: "linux_vm_setup.sh",
+ system_ext_specific: true,
+ host_supported: false,
+}
diff --git a/android/LinuxInstaller/java/com/android/virtualization/linuxinstaller/MainActivity.java b/android/LinuxInstaller/java/com/android/virtualization/linuxinstaller/MainActivity.java
index 1d875cb..0351f97 100644
--- a/android/LinuxInstaller/java/com/android/virtualization/linuxinstaller/MainActivity.java
+++ b/android/LinuxInstaller/java/com/android/virtualization/linuxinstaller/MainActivity.java
@@ -136,10 +136,10 @@
throw new RuntimeException("Internal error: destDir shouldn't be null");
}
- SystemProperties.set("debug.custom_vm_setup.path", destDir);
- SystemProperties.set("debug.custom_vm_setup.done", "false");
- SystemProperties.set("debug.custom_vm_setup.start", "true");
- while (!SystemProperties.getBoolean("debug.custom_vm_setup.done", false)) {
+ SystemProperties.set("debug.linux_vm_setup.path", destDir);
+ SystemProperties.set("debug.linux_vm_setup.done", "false");
+ SystemProperties.set("debug.linux_vm_setup.start", "true");
+ while (!SystemProperties.getBoolean("debug.linux_vm_setup.done", false)) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
diff --git a/android/FerrochromeApp/custom_vm_setup.rc b/android/LinuxInstaller/linux_vm_setup.rc
similarity index 77%
rename from android/FerrochromeApp/custom_vm_setup.rc
rename to android/LinuxInstaller/linux_vm_setup.rc
index 68f370e..9264d96 100644
--- a/android/FerrochromeApp/custom_vm_setup.rc
+++ b/android/LinuxInstaller/linux_vm_setup.rc
@@ -1,4 +1,4 @@
-# Copyright (C) 2024 The Android Open Source Project
+# 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.
@@ -12,12 +12,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-service custom_vm_setup /system_ext/bin/custom_vm_setup
+service linux_vm_setup /system_ext/bin/linux_vm_setup
user shell
group shell media_rw
disabled
oneshot
seclabel u:r:shell:s0
-on property:debug.custom_vm_setup.start=true
- start custom_vm_setup
+on property:debug.linux_vm_setup.start=true
+ start linux_vm_setup
diff --git a/android/FerrochromeApp/custom_vm_setup.sh b/android/LinuxInstaller/linux_vm_setup.sh
similarity index 82%
rename from android/FerrochromeApp/custom_vm_setup.sh
rename to android/LinuxInstaller/linux_vm_setup.sh
index df1a3a6..6a93f6f 100644
--- a/android/FerrochromeApp/custom_vm_setup.sh
+++ b/android/LinuxInstaller/linux_vm_setup.sh
@@ -7,7 +7,7 @@
}
function install() {
- src_dir=$(getprop debug.custom_vm_setup.path)
+ src_dir=$(getprop debug.linux_vm_setup.path)
src_dir=${src_dir/#\/storage\/emulated\//\/data\/media\/}
dst_dir=/data/local/tmp/
@@ -26,7 +26,7 @@
rm ${src_dir}/vm_config.json
}
-setprop debug.custom_vm_setup.done false
+setprop debug.linux_vm_setup.done false
install
-setprop debug.custom_vm_setup.start false
-setprop debug.custom_vm_setup.done true
+setprop debug.linux_vm_setup.start false
+setprop debug.linux_vm_setup.done true
diff --git a/build/apex/product_packages.mk b/build/apex/product_packages.mk
index b2a4ca2..c678693 100644
--- a/build/apex/product_packages.mk
+++ b/build/apex/product_packages.mk
@@ -26,11 +26,13 @@
com.android.compos \
features_com.android.virt.xml
-# TODO(b/207336449): Figure out how to get these off /system
+ifneq (true, $(RELEASE_INSTALL_APEX_SYSTEMSERVER_DEXPREOPT_SAME_PARTITION))
PRODUCT_ARTIFACT_PATH_REQUIREMENT_ALLOWED_LIST := \
system/framework/oat/%@service-compos.jar@classes.odex \
system/framework/oat/%@service-compos.jar@classes.vdex \
+endif
+
PRODUCT_APEX_SYSTEM_SERVER_JARS := com.android.compos:service-compos
PRODUCT_SYSTEM_EXT_PROPERTIES := ro.config.isolated_compilation_enabled=true
diff --git a/build/debian/build.sh b/build/debian/build.sh
index 2177b17..c50e5e5 100755
--- a/build/debian/build.sh
+++ b/build/debian/build.sh
@@ -38,12 +38,16 @@
DEBIAN_FRONTEND=noninteractive \
apt install --no-install-recommends --assume-yes \
binfmt-support \
+ build-essential \
ca-certificates \
+ curl \
debsums \
dosfstools \
fai-server \
fai-setup-storage \
fdisk \
+ gcc-aarch64-linux-gnu \
+ libc6-dev-arm64-cross \
make \
python3 \
python3-libcloud \
@@ -53,9 +57,17 @@
qemu-system-arm \
qemu-user-static \
qemu-utils \
+ sudo \
udev \
+ if [ ! -f $HOME/.cargo/bin/cargo ]; then
+ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
+ fi
+
+ source $HOME/.cargo/env
+ rustup target add aarch64-unknown-linux-gnu
+
sed -i s/losetup\ -f/losetup\ -P\ -f/g /usr/sbin/fai-diskimage
sed -i 's/wget \$/wget -t 0 \$/g' /usr/share/debootstrap/functions
@@ -88,6 +100,15 @@
mkdir -p ${dst}/files/usr/local/bin/ttyd
wget ${url} -O ${dst}/files/usr/local/bin/ttyd/AVF
chmod 777 ${dst}/files/usr/local/bin/ttyd/AVF
+
+ pushd $(dirname $0)/forwarder_guest > /dev/null
+ RUSTFLAGS="-C linker=aarch64-linux-gnu-gcc" cargo build \
+ --target aarch64-unknown-linux-gnu \
+ --target-dir ${workdir}/forwarder_guest
+ mkdir -p ${dst}/files/usr/local/bin/forwarder_guest
+ cp ${workdir}/forwarder_guest/aarch64-unknown-linux-gnu/debug/forwarder_guest ${dst}/files/usr/local/bin/forwarder_guest/AVF
+ chmod 777 ${dst}/files/usr/local/bin/forwarder_guest/AVF
+ popd > /dev/null
}
run_fai() {
diff --git a/build/debian/build_in_container.sh b/build/debian/build_in_container.sh
new file mode 100755
index 0000000..6bc366b
--- /dev/null
+++ b/build/debian/build_in_container.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+if [ -z $ANDROID_BUILD_TOP ]; then echo "forgot to source build/envsetup.sh?" && exit 1; fi
+
+docker run --privileged -it -v $ANDROID_BUILD_TOP/packages/modules/Virtualization:/root/Virtualization -v /dev:/dev ubuntu:22.04 /root/Virtualization/build/debian/build.sh
diff --git a/build/debian/fai_config/scripts/AVF/10-systemd b/build/debian/fai_config/scripts/AVF/10-systemd
index e04a562..d33b92a 100755
--- a/build/debian/fai_config/scripts/AVF/10-systemd
+++ b/build/debian/fai_config/scripts/AVF/10-systemd
@@ -1,5 +1,6 @@
#!/bin/bash
+chmod +x $target/usr/local/bin/forwarder_guest
chmod +x $target/usr/local/bin/ttyd
chmod +x $target/usr/local/bin/vsock.py
ln -s /etc/systemd/system/ttyd.service $target/etc/systemd/system/multi-user.target.wants/ttyd.service
diff --git a/build/debian/kokoro/gcp_ubuntu_docker/build.sh b/build/debian/kokoro/gcp_ubuntu_docker/build.sh
index 4598d1c..4cc4769 100644
--- a/build/debian/kokoro/gcp_ubuntu_docker/build.sh
+++ b/build/debian/kokoro/gcp_ubuntu_docker/build.sh
@@ -6,4 +6,4 @@
sudo losetup -D
grep vmx /proc/cpuinfo || true
sudo ./build.sh
-cp image.raw ${KOKORO_ARTIFACTS_DIR}
+tar czvS -f ${KOKORO_ARTIFACTS_DIR}/image.tar.gz image.raw
diff --git a/build/debian/kokoro/gcp_ubuntu_docker/continuous.cfg b/build/debian/kokoro/gcp_ubuntu_docker/continuous.cfg
index 111096d..97ebd5d 100644
--- a/build/debian/kokoro/gcp_ubuntu_docker/continuous.cfg
+++ b/build/debian/kokoro/gcp_ubuntu_docker/continuous.cfg
@@ -8,6 +8,6 @@
action {
define_artifacts {
- regex: "image.raw"
+ regex: "image.tar.gz"
}
}
diff --git a/build/debian/kokoro/gcp_ubuntu_docker/hourly.cfg b/build/debian/kokoro/gcp_ubuntu_docker/hourly.cfg
index 111096d..97ebd5d 100644
--- a/build/debian/kokoro/gcp_ubuntu_docker/hourly.cfg
+++ b/build/debian/kokoro/gcp_ubuntu_docker/hourly.cfg
@@ -8,6 +8,6 @@
action {
define_artifacts {
- regex: "image.raw"
+ regex: "image.tar.gz"
}
}
diff --git a/build/debian/port_listener/build.sh b/build/debian/port_listener/build.sh
new file mode 100755
index 0000000..a1d0205
--- /dev/null
+++ b/build/debian/port_listener/build.sh
@@ -0,0 +1,54 @@
+#!/bin/bash
+
+set -e
+
+check_sudo() {
+ if [ "$EUID" -ne 0 ]; then
+ echo "Please run as root."
+ exit
+ fi
+}
+
+install_prerequisites() {
+ apt update
+ apt install --no-install-recommends --assume-yes \
+ bpftool \
+ clang \
+ libbpf-dev \
+ libgoogle-glog-dev \
+ libstdc++-14-dev
+}
+
+build_port_listener() {
+ cp $(dirname $0)/src/* ${workdir}
+ out_dir=${PWD}
+ pushd ${workdir}
+ bpftool btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h
+ clang \
+ -O2 \
+ -Wall \
+ -target bpf \
+ -g \
+ -c listen_tracker.ebpf.c \
+ -o listen_tracker.ebpf.o
+ bpftool gen skeleton listen_tracker.ebpf.o > listen_tracker.skel.h
+ clang++ \
+ -O2 \
+ -Wall \
+ -lbpf \
+ -lglog \
+ -o port_listener \
+ main.cc
+ cp port_listener ${out_dir}
+ popd
+}
+
+clean_up() {
+ rm -rf ${workdir}
+}
+trap clean_up EXIT
+workdir=$(mktemp -d)
+
+check_sudo
+install_prerequisites
+build_port_listener
diff --git a/build/debian/port_listener/src/common.h b/build/debian/port_listener/src/common.h
new file mode 100644
index 0000000..d6e507c
--- /dev/null
+++ b/build/debian/port_listener/src/common.h
@@ -0,0 +1,31 @@
+// 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.
+
+// Copied from ChromiumOS with relicensing:
+// src/platform2/vm_tools/port_listener/common.h
+
+#ifndef VM_TOOLS_PORT_LISTENER_COMMON_H_
+#define VM_TOOLS_PORT_LISTENER_COMMON_H_
+
+enum State {
+ kPortListenerUp,
+ kPortListenerDown,
+};
+
+struct event {
+ enum State state;
+ uint16_t port;
+};
+
+#endif // VM_TOOLS_PORT_LISTENER_COMMON_H_
diff --git a/build/debian/port_listener/src/listen_tracker.ebpf.c b/build/debian/port_listener/src/listen_tracker.ebpf.c
new file mode 100644
index 0000000..030ded0
--- /dev/null
+++ b/build/debian/port_listener/src/listen_tracker.ebpf.c
@@ -0,0 +1,81 @@
+// 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.
+
+// Copied from ChromiumOS with relicensing:
+// src/platform2/vm_tools/port_listener/listen_tracker.ebpf.c
+
+// bpf_helpers.h uses types defined here
+#include "vmlinux.h"
+
+#include <bpf/bpf_helpers.h>
+
+#include "common.h"
+
+// For some reason 6.1 doesn't include these symbols in the debug build
+// so they don't get included in vmlinux.h. These features have existed since
+// well before 6.1.
+#define BPF_F_NO_PREALLOC (1U << 0)
+#define BPF_ANY 0
+
+struct {
+ __uint(type, BPF_MAP_TYPE_RINGBUF);
+ __uint(max_entries, 1 << 24);
+} events SEC(".maps");
+
+struct {
+ __uint(type, BPF_MAP_TYPE_HASH);
+ __type(key, struct sock*);
+ __type(value, __u8);
+ __uint(max_entries, 65535);
+ __uint(map_flags, BPF_F_NO_PREALLOC);
+} sockmap SEC(".maps");
+
+const __u8 set_value = 0;
+
+SEC("tp/sock/inet_sock_set_state")
+int tracepoint_inet_sock_set_state(struct trace_event_raw_inet_sock_set_state* ctx) {
+ // We don't support anything other than TCP.
+ if (ctx->protocol != IPPROTO_TCP) {
+ return 0;
+ }
+ struct sock* sk = (struct sock*)ctx->skaddr;
+ // If we're transitioning away from LISTEN but we don't know about this
+ // socket yet then don't report anything.
+ if (ctx->oldstate == BPF_TCP_LISTEN && bpf_map_lookup_elem(&sockmap, &sk) == NULL) {
+ return 0;
+ }
+ // If we aren't transitioning to or from TCP_LISTEN then we don't care.
+ if (ctx->newstate != BPF_TCP_LISTEN && ctx->oldstate != BPF_TCP_LISTEN) {
+ return 0;
+ }
+
+ struct event* ev;
+ ev = bpf_ringbuf_reserve(&events, sizeof(*ev), 0);
+ if (!ev) {
+ return 0;
+ }
+ ev->port = ctx->sport;
+
+ if (ctx->newstate == BPF_TCP_LISTEN) {
+ bpf_map_update_elem(&sockmap, &sk, &set_value, BPF_ANY);
+ ev->state = kPortListenerUp;
+ }
+ if (ctx->oldstate == BPF_TCP_LISTEN) {
+ bpf_map_delete_elem(&sockmap, &sk);
+ ev->state = kPortListenerDown;
+ }
+ bpf_ringbuf_submit(ev, 0);
+
+ return 0;
+}
diff --git a/build/debian/port_listener/src/main.cc b/build/debian/port_listener/src/main.cc
new file mode 100644
index 0000000..b0b0979
--- /dev/null
+++ b/build/debian/port_listener/src/main.cc
@@ -0,0 +1,168 @@
+// 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.
+
+// Copied from ChromiumOS with relicensing:
+// src/platform2/vm_tools/port_listener/main.cc
+
+#include <bpf/libbpf.h>
+#include <bpf/libbpf_legacy.h>
+#include <glog/logging.h>
+#include <sys/socket.h>
+
+#include <linux/vm_sockets.h> // Needs to come after sys/socket.h
+
+#include <memory>
+#include <unordered_map>
+
+#include "common.h"
+#include "listen_tracker.skel.h"
+
+typedef std::unordered_map<int, int> port_usage_map;
+
+namespace port_listener {
+namespace {
+
+int HandleEvent(void* ctx, void* const data, size_t size) {
+ port_usage_map* map = reinterpret_cast<port_usage_map*>(ctx);
+ const struct event* ev = (struct event*)data;
+
+ switch (ev->state) {
+ case kPortListenerUp:
+ (*map)[ev->port]++;
+ break;
+
+ case kPortListenerDown:
+ if ((*map)[ev->port] > 0) {
+ (*map)[ev->port]--;
+ } else {
+ LOG(INFO) << "Received down event while port count was 0; ignoring";
+ }
+
+ break;
+
+ default:
+ LOG(ERROR) << "Unknown event state " << ev->state;
+ }
+
+ LOG(INFO) << "Listen event: port=" << ev->port << " state=" << ev->state;
+
+ return 0;
+}
+
+typedef std::unique_ptr<struct ring_buffer, decltype(&ring_buffer__free)> ring_buffer_ptr;
+typedef std::unique_ptr<listen_tracker_ebpf, decltype(&listen_tracker_ebpf__destroy)>
+ listen_tracker_ptr;
+
+// BPFProgram tracks the state and resources of the listen_tracker BPF program.
+class BPFProgram {
+public:
+ // Default movable but not copyable.
+ BPFProgram(BPFProgram&& other) = default;
+ BPFProgram(const BPFProgram& other) = delete;
+ BPFProgram& operator=(BPFProgram&& other) = default;
+ BPFProgram& operator=(const BPFProgram& other) = delete;
+
+ // Load loads the listen_tracker BPF program and prepares it for polling. On
+ // error nullptr is returned.
+ static std::unique_ptr<BPFProgram> Load() {
+ auto* skel = listen_tracker_ebpf__open();
+ if (!skel) {
+ PLOG(ERROR) << "Failed to open listen_tracker BPF skeleton";
+ return nullptr;
+ }
+ listen_tracker_ptr skeleton(skel, listen_tracker_ebpf__destroy);
+
+ int err = listen_tracker_ebpf__load(skeleton.get());
+ if (err) {
+ PLOG(ERROR) << "Failed to load listen_tracker BPF program";
+ return nullptr;
+ }
+
+ auto map = std::make_unique<port_usage_map>();
+ auto* rb = ring_buffer__new(bpf_map__fd(skel->maps.events), HandleEvent, map.get(), NULL);
+ if (!rb) {
+ PLOG(ERROR) << "Failed to open ring buffer for listen_tracker";
+ return nullptr;
+ }
+ ring_buffer_ptr ringbuf(rb, ring_buffer__free);
+
+ err = listen_tracker_ebpf__attach(skeleton.get());
+ if (err) {
+ PLOG(ERROR) << "Failed to attach listen_tracker";
+ return nullptr;
+ }
+
+ return std::unique_ptr<BPFProgram>(
+ new BPFProgram(std::move(skeleton), std::move(ringbuf), std::move(map)));
+ }
+
+ // Poll waits for the listen_tracker BPF program to post a new event to the
+ // ring buffer. BPFProgram handles integrating this new event into the
+ // port_usage map and callers should consult port_usage() after Poll returns
+ // for the latest data.
+ const bool Poll() {
+ int err = ring_buffer__poll(rb_.get(), -1);
+ if (err < 0) {
+ LOG(ERROR) << "Error polling ring buffer ret=" << err;
+ return false;
+ }
+
+ return true;
+ }
+
+ const port_usage_map& port_usage() { return *port_usage_; }
+
+private:
+ BPFProgram(listen_tracker_ptr&& skeleton, ring_buffer_ptr&& rb,
+ std::unique_ptr<port_usage_map>&& port_usage)
+ : skeleton_(std::move(skeleton)),
+ rb_(std::move(rb)),
+ port_usage_(std::move(port_usage)) {}
+
+ listen_tracker_ptr skeleton_;
+ ring_buffer_ptr rb_;
+ std::unique_ptr<port_usage_map> port_usage_;
+};
+
+} // namespace
+} // namespace port_listener
+
+int main(int argc, char** argv) {
+ google::InitGoogleLogging(argv[0]);
+ libbpf_set_strict_mode(LIBBPF_STRICT_ALL);
+
+ // Load our BPF program.
+ auto program = port_listener::BPFProgram::Load();
+ if (program == nullptr) {
+ LOG(ERROR) << "Failed to load BPF program";
+ return EXIT_FAILURE;
+ }
+
+ // main loop: poll for listen updates
+ for (;;) {
+ if (!program->Poll()) {
+ LOG(ERROR) << "Failure while polling BPF program";
+ return EXIT_FAILURE;
+ }
+ // port_usage will be updated with the latest usage data
+
+ for (auto it : program->port_usage()) {
+ if (it.second <= 0) {
+ continue;
+ }
+ // TODO(b/340126051): Add listening TCP4 ports.
+ }
+ // TODO(b/340126051): Notify port information to the guest agent.
+ }
+}
diff --git a/guest/pvmfw/README.md b/guest/pvmfw/README.md
index 58ba10c..3ffa3f0 100644
--- a/guest/pvmfw/README.md
+++ b/guest/pvmfw/README.md
@@ -233,10 +233,10 @@
[header]: src/config.rs
[DTBO]: https://android.googlesource.com/platform/external/dtc/+/refs/heads/main/Documentation/dt-object-internal.txt
-[debug_policy]: ../docs/debug/README.md#debug-policy
-[device_assignment]: ../docs/device_assignment.md
+[debug_policy]: ../../docs/debug/README.md#debug-policy
+[device_assignment]: ../../docs/device_assignment.md
[secretkeeper_key]: https://android.googlesource.com/platform/system/secretkeeper/+/refs/heads/main/README.md#secretkeeper-public-key
-[vendor_hashtree_digest]: ../microdroid/README.md#verification-of-vendor-image
+[vendor_hashtree_digest]: ../../build/microdroid/README.md#verification-of-vendor-image
#### Virtual Platform DICE Chain Handover
@@ -402,7 +402,7 @@
};
```
-[dt.md]: ../docs/device_trees.md#avf_specific-properties-and-nodes
+[dt.md]: ../../docs/device_trees.md#avf_specific-properties-and-nodes
### Guest Image Signing
diff --git a/libs/framework-virtualization/README.md b/libs/framework-virtualization/README.md
index 0dd7e64..a742ccc 100644
--- a/libs/framework-virtualization/README.md
+++ b/libs/framework-virtualization/README.md
@@ -1,12 +1,12 @@
# Android Virtualization Framework API
These Java APIs allow an app to configure and run a Virtual Machine running
-[Microdroid](../build/microdroid/README.md) and execute native code from the app (the
+[Microdroid](../../build/microdroid/README.md) and execute native code from the app (the
payload) within it.
-There is more information on AVF [here](../README.md). To see how to package the
+There is more information on AVF [here](../../README.md). To see how to package the
payload code that is to run inside a VM, and the native API available to it, see
-the [VM Payload API](../libs/libvm_payload/README.md)
+the [VM Payload API](../libvm_payload/README.md)
The API classes are all in the
[`android.system.virtualmachine`](src/android/system/virtualmachine) package.
diff --git a/libs/libvm_payload/README.md b/libs/libvm_payload/README.md
index 8ef1bac..bbcfc61 100644
--- a/libs/libvm_payload/README.md
+++ b/libs/libvm_payload/README.md
@@ -9,7 +9,7 @@
available in the VM, and only 64 bit code is supported.
To create a VM and run the payload from Android see the [AVF Java
-APIs](../libs/framework-virtualization/README.md).
+APIs](../framework-virtualization/README.md).
## Entry point
diff --git a/tests/hostside/AndroidTest.xml b/tests/hostside/AndroidTest.xml
index 18728ad..f77def3 100644
--- a/tests/hostside/AndroidTest.xml
+++ b/tests/hostside/AndroidTest.xml
@@ -28,4 +28,8 @@
<test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
<option name="jar" value="MicrodroidHostTestCases.jar" />
</test>
+
+ <!-- Controller that will skip the module if a native bridge situation is detected -->
+ <!-- For example: module wants to run arm and device is x86 -->
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.NativeBridgeModuleController" />
</configuration>
diff --git a/tests/testapk/AndroidTest.xml b/tests/testapk/AndroidTest.xml
index 22cd0dc..e490da4 100644
--- a/tests/testapk/AndroidTest.xml
+++ b/tests/testapk/AndroidTest.xml
@@ -39,4 +39,8 @@
<option name="shell-timeout" value="300000" />
<option name="test-timeout" value="300000" />
</test>
+
+ <!-- Controller that will skip the module if a native bridge situation is detected -->
+ <!-- For example: module wants to run arm and device is x86 -->
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.NativeBridgeModuleController" />
</configuration>