Merge "Remove odsign_e2e_tests_full from avf-postsubmit group" into main
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 3a6a3e3..8fe6948 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -12,6 +12,9 @@
"name": "MicrodroidTestApp"
},
{
+ "name": "MicrodroidTestAppNoPerm"
+ },
+ {
"name": "VmAttestationTestApp"
},
{
diff --git a/java/service/Android.bp b/java/service/Android.bp
index fdfb203..8bac7be 100644
--- a/java/service/Android.bp
+++ b/java/service/Android.bp
@@ -29,6 +29,9 @@
"framework",
"services.core",
],
+ static_libs: [
+ "android.system.virtualizationmaintenance-java",
+ ],
sdk_version: "core_platform",
apex_available: ["com.android.virt"],
installable: true,
diff --git a/java/service/src/com/android/system/virtualmachine/VirtualizationSystemService.java b/java/service/src/com/android/system/virtualmachine/VirtualizationSystemService.java
index 2905acd..3f973b4 100644
--- a/java/service/src/com/android/system/virtualmachine/VirtualizationSystemService.java
+++ b/java/service/src/com/android/system/virtualmachine/VirtualizationSystemService.java
@@ -16,16 +16,121 @@
package com.android.system.virtualmachine;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.system.virtualizationmaintenance.IVirtualizationMaintenance;
+import android.util.Log;
+
+import com.android.internal.os.BackgroundThread;
import com.android.server.SystemService;
-/** TODO */
+/**
+ * This class exists to notify virtualization service of relevant things happening in the Android
+ * framework.
+ *
+ * <p>It currently is responsible for Secretkeeper-related maintenance - ensuring that we are not
+ * storing secrets for apps or users that no longer exist.
+ */
public class VirtualizationSystemService extends SystemService {
+ private static final String TAG = VirtualizationSystemService.class.getName();
+ private static final String SERVICE_NAME = "android.system.virtualizationmaintenance";
+ private Handler mHandler;
public VirtualizationSystemService(Context context) {
super(context);
}
@Override
- public void onStart() {}
+ public void onStart() {
+ // Nothing needed here - we don't expose any binder service. The binder service we use is
+ // exposed as a lazy service by the virtualizationservice native binary.
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase != PHASE_BOOT_COMPLETED) return;
+
+ mHandler = BackgroundThread.getHandler();
+ new Receiver().registerForBroadcasts();
+ }
+
+ private void notifyAppRemoved(int uid) {
+ try {
+ IVirtualizationMaintenance maintenance = connectToMaintenanceService();
+ maintenance.appRemoved(UserHandle.getUserId(uid), UserHandle.getAppId(uid));
+ } catch (Exception e) {
+ Log.e(TAG, "notifyAppRemoved failed", e);
+ }
+ }
+
+ private void notifyUserRemoved(int userId) {
+ try {
+ IVirtualizationMaintenance maintenance = connectToMaintenanceService();
+ maintenance.userRemoved(userId);
+ } catch (Exception e) {
+ Log.e(TAG, "notifyUserRemoved failed", e);
+ }
+ }
+
+ private static IVirtualizationMaintenance connectToMaintenanceService() {
+ IBinder binder = ServiceManager.waitForService(SERVICE_NAME);
+ IVirtualizationMaintenance maintenance =
+ IVirtualizationMaintenance.Stub.asInterface(binder);
+ if (maintenance == null) {
+ throw new IllegalStateException("Failed to connect to " + SERVICE_NAME);
+ }
+ return maintenance;
+ }
+
+ private class Receiver extends BroadcastReceiver {
+ public void registerForBroadcasts() {
+ Context allUsers = getContext().createContextAsUser(UserHandle.ALL, 0 /* flags */);
+
+ allUsers.registerReceiver(this, new IntentFilter(Intent.ACTION_USER_REMOVED));
+
+ IntentFilter packageFilter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED);
+ packageFilter.addDataScheme("package");
+ allUsers.registerReceiver(this, packageFilter);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ switch (intent.getAction()) {
+ case Intent.ACTION_USER_REMOVED:
+ onUserRemoved(intent);
+ break;
+ case Intent.ACTION_PACKAGE_REMOVED:
+ onPackageRemoved(intent);
+ break;
+ default:
+ Log.e(TAG, "received unexpected intent: " + intent.getAction());
+ break;
+ }
+ }
+
+ private void onUserRemoved(Intent intent) {
+ int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+ if (userId != UserHandle.USER_NULL) {
+ mHandler.post(() -> notifyUserRemoved(userId));
+ }
+ }
+
+ private void onPackageRemoved(Intent intent) {
+ if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)
+ || !intent.getBooleanExtra(Intent.EXTRA_DATA_REMOVED, false)) {
+ // Package is being updated rather than uninstalled.
+ return;
+ }
+ int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
+ if (uid != -1) {
+ mHandler.post(() -> notifyAppRemoved(uid));
+ }
+ }
+ }
}
diff --git a/tests/testapk_no_perm/Android.bp b/tests/testapk_no_perm/Android.bp
new file mode 100644
index 0000000..22616de
--- /dev/null
+++ b/tests/testapk_no_perm/Android.bp
@@ -0,0 +1,26 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test {
+ name: "MicrodroidTestAppNoPerm",
+ static_libs: [
+ "MicrodroidDeviceTestHelper",
+ "MicrodroidTestHelper",
+ "androidx.test.runner",
+ "androidx.test.ext.junit",
+ "com.android.microdroid.testservice-java",
+ "truth",
+ "compatibility-common-util-devicesidelib",
+ ],
+ jni_libs: [
+ "MicrodroidTestNativeLib",
+ ],
+ test_suites: [
+ "general-tests",
+ "cts",
+ ],
+ srcs: ["src/java/**/*.java"],
+ defaults: ["MicrodroidTestAppsDefaults"],
+ min_sdk_version: "33",
+}
diff --git a/tests/testapk_no_perm/AndroidManifest.xml b/tests/testapk_no_perm/AndroidManifest.xml
new file mode 100644
index 0000000..44aa92a
--- /dev/null
+++ b/tests/testapk_no_perm/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.microdroid.test_no_perm">
+ <uses-sdk android:minSdkVersion="33" android:targetSdkVersion="33" />
+ <uses-feature android:name="android.software.virtualization_framework" android:required="false" />
+ <application />
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.microdroid.test_no_perm"
+ android:label="No Permission Microdroid Test" />
+</manifest>
diff --git a/tests/testapk_no_perm/AndroidTest.xml b/tests/testapk_no_perm/AndroidTest.xml
new file mode 100644
index 0000000..d4a818f
--- /dev/null
+++ b/tests/testapk_no_perm/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Runs Microdroid Tests with no permission">
+ <option name="test-suite-tag" value="cts" />
+ <option name="config-descriptor:metadata" key="component" value="security" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="test-file-name" value="MicrodroidTestAppNoPerm.apk" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.microdroid.test_no_perm" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="shell-timeout" value="300000" />
+ <option name="test-timeout" value="300000" />
+ </test>
+</configuration>
diff --git a/tests/testapk_no_perm/src/java/com/android/microdroid/test/MicrodroidTestAppNoPerm.java b/tests/testapk_no_perm/src/java/com/android/microdroid/test/MicrodroidTestAppNoPerm.java
new file mode 100644
index 0000000..3c20a0f
--- /dev/null
+++ b/tests/testapk_no_perm/src/java/com/android/microdroid/test/MicrodroidTestAppNoPerm.java
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+package com.android.microdroid.test;
+
+import android.system.virtualmachine.VirtualMachineConfig;
+
+import com.android.compatibility.common.util.CddTest;
+import com.android.microdroid.test.device.MicrodroidDeviceTestBase;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Test that the android.permission.MANAGE_VIRTUAL_MACHINE is enforced and that an app cannot launch
+ * a VM without said permission.
+ */
+@RunWith(JUnit4.class)
+public class MicrodroidTestAppNoPerm extends MicrodroidDeviceTestBase {
+ @Test
+ @CddTest(
+ requirements = {
+ "9.17/C-1-1",
+ "9.17/C-1-2",
+ "9.17/C-1-4",
+ })
+ public void createVmRequiresPermission() {
+ assumeSupportedDevice();
+
+ VirtualMachineConfig config =
+ newVmConfigBuilderWithPayloadBinary("MicrodroidTestNativeLib.so").build();
+
+ SecurityException e =
+ assertThrows(
+ SecurityException.class,
+ () -> forceCreateNewVirtualMachine("test_vm_requires_permission", config));
+ assertThat(e)
+ .hasMessageThat()
+ .contains("android.permission.MANAGE_VIRTUAL_MACHINE permission");
+ }
+}
diff --git a/virtualizationservice/aidl/Android.bp b/virtualizationservice/aidl/Android.bp
index 66de092..112e1cc 100644
--- a/virtualizationservice/aidl/Android.bp
+++ b/virtualizationservice/aidl/Android.bp
@@ -61,6 +61,9 @@
backend: {
java: {
sdk_version: "module_current",
+ apex_available: [
+ "com.android.virt",
+ ],
},
rust: {
enabled: true,