Test binder callbacks

Make sure that the VM can asynchronously call back to the app, and
vice versa. This is mostly to ensure that all the necessary thread
pools exist.

While I'm here, suppress one AIDL warning, and promote any new ones to
errors.

Bug: 268335700
Test: atest MicrodroidTests
Change-Id: I6b6d957f6e5d645e76eeee0454843debe6af7730
This commit is contained in:
Alan Stokes 2023-02-22 14:56:57 +00:00
parent 91b5a5976e
commit 63fa37b581
7 changed files with 161 additions and 2 deletions

View File

@ -6,6 +6,10 @@ aidl_interface {
name: "com.android.microdroid.testservice",
srcs: ["com/android/microdroid/testservice/**/*.aidl"],
unstable: true,
flags: [
"-Werror",
"-Wno-mixed-oneway",
],
backend: {
java: {
gen_rpc: true,

View File

@ -0,0 +1,31 @@
/*
* Copyright 2023 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.testservice;
import com.android.microdroid.testservice.IVmCallback;
/**
* An interface exposed by the app for callbacks from the VM.
*
* {@hide}
*/
interface IAppCallback {
/** Invites the app to call vmCallback#echoMessage() */
void setVmCallback(IVmCallback vmCallback);
/** Asynchronusly called by the VM in response to a call to echoMessage(). */
void onEchoRequestReceived(String message);
}

View File

@ -15,7 +15,12 @@
*/
package com.android.microdroid.testservice;
/** {@hide} */
import com.android.microdroid.testservice.IAppCallback;
/**
* This is the service exposed by the test payload, called by the test app.
* {@hide}
*/
interface ITestService {
const long SERVICE_PORT = 5678;
@ -62,6 +67,9 @@ interface ITestService {
/** Returns flags for the given mountPoint. */
int getMountFlags(String mountPoint);
/** Requests the VM to asynchronously call appCallback.setVmCallback() */
void requestCallback(IAppCallback appCallback);
/**
* Request the service to exit, triggering the termination of the VM. This may cause any
* requests in flight to fail.

View File

@ -0,0 +1,26 @@
/*
* Copyright 2023 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.testservice;
/**
* An interface exposed by the VM for callbacks from the app.
*
* {@hide}
*/
interface IVmCallback {
/** Requests the VM to asynchronously call the app's onEchoRequestReceived() callback. */
void echoMessage(String message);
}

View File

@ -58,7 +58,9 @@ import android.util.Log;
import com.android.compatibility.common.util.CddTest;
import com.android.microdroid.test.device.MicrodroidDeviceTestBase;
import com.android.microdroid.test.vmshare.IVmShareTestService;
import com.android.microdroid.testservice.IAppCallback;
import com.android.microdroid.testservice.ITestService;
import com.android.microdroid.testservice.IVmCallback;
import org.junit.After;
import org.junit.Before;
@ -399,6 +401,59 @@ public class MicrodroidTests extends MicrodroidDeviceTestBase {
assertThat(response.get()).isEqualTo(new StringBuilder(request).reverse().toString());
}
@Test
@CddTest(requirements = {"9.17/C-1-1"})
public void binderCallbacksWork() throws Exception {
assumeSupportedKernel();
VirtualMachineConfig config =
newVmConfigBuilder()
.setPayloadBinaryName("MicrodroidTestNativeLib.so")
.setMemoryBytes(minMemoryRequired())
.setDebugLevel(DEBUG_LEVEL_FULL)
.build();
VirtualMachine vm = forceCreateNewVirtualMachine("test_vm", config);
String request = "Hello";
CompletableFuture<String> response = new CompletableFuture<>();
IAppCallback appCallback =
new IAppCallback.Stub() {
@Override
public void setVmCallback(IVmCallback vmCallback) {
// Do this on a separate thread to simulate an asynchronous trigger,
// and to make sure it doesn't happen in the context of an inbound binder
// call.
new Thread() {
@Override
public void run() {
try {
vmCallback.echoMessage(request);
} catch (Exception e) {
response.completeExceptionally(e);
}
}
}.start();
}
@Override
public void onEchoRequestReceived(String message) {
response.complete(message);
}
};
TestResults testResults =
runVmTestService(
TAG,
vm,
(service, results) -> {
service.requestCallback(appCallback);
response.get(10, TimeUnit.SECONDS);
});
testResults.assertNoException();
assertThat(response.getNow("no response")).isEqualTo("Received: " + request);
}
@Test
@CddTest(requirements = {"9.17/C-1-1"})
public void vmConfigGetAndSetTests() {

View File

@ -15,6 +15,8 @@
*/
#include <aidl/com/android/microdroid/testservice/BnTestService.h>
#include <aidl/com/android/microdroid/testservice/BnVmCallback.h>
#include <aidl/com/android/microdroid/testservice/IAppCallback.h>
#include <android-base/file.h>
#include <android-base/properties.h>
#include <android-base/result.h>
@ -47,6 +49,8 @@ using android::fs_mgr::GetEntryForMountPoint;
using android::fs_mgr::ReadFstabFromFile;
using aidl::com::android::microdroid::testservice::BnTestService;
using aidl::com::android::microdroid::testservice::BnVmCallback;
using aidl::com::android::microdroid::testservice::IAppCallback;
using ndk::ScopedAStatus;
extern void testlib_sub();
@ -144,7 +148,25 @@ Result<void> start_echo_reverse_server() {
}
Result<void> start_test_service() {
class VmCallbackImpl : public BnVmCallback {
private:
std::shared_ptr<IAppCallback> mAppCallback;
public:
explicit VmCallbackImpl(const std::shared_ptr<IAppCallback>& appCallback)
: mAppCallback(appCallback) {}
ScopedAStatus echoMessage(const std::string& message) override {
std::thread callback_thread{[=, appCallback = mAppCallback] {
appCallback->onEchoRequestReceived("Received: " + message);
}};
callback_thread.detach();
return ScopedAStatus::ok();
}
};
class TestService : public BnTestService {
public:
ScopedAStatus addInteger(int32_t a, int32_t b, int32_t* out) override {
*out = a + b;
return ScopedAStatus::ok();
@ -226,7 +248,7 @@ Result<void> start_test_service() {
return ScopedAStatus::ok();
}
virtual ::ScopedAStatus runEchoReverseServer() override {
ScopedAStatus runEchoReverseServer() override {
auto result = start_echo_reverse_server();
if (result.ok()) {
return ScopedAStatus::ok();
@ -284,6 +306,13 @@ Result<void> start_test_service() {
return ScopedAStatus::ok();
}
ScopedAStatus requestCallback(const std::shared_ptr<IAppCallback>& appCallback) {
auto vmCallback = ndk::SharedRefBase::make<VmCallbackImpl>(appCallback);
std::thread callback_thread{[=] { appCallback->setVmCallback(vmCallback); }};
callback_thread.detach();
return ScopedAStatus::ok();
}
ScopedAStatus quit() override { exit(0); }
};
auto testService = ndk::SharedRefBase::make<TestService>();

View File

@ -29,6 +29,7 @@ import android.util.Log;
import com.android.microdroid.test.vmshare.IVmShareTestService;
import com.android.microdroid.testservice.ITestService;
import com.android.microdroid.testservice.IAppCallback;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
@ -239,6 +240,11 @@ public class VmShareServiceImpl extends Service {
throw new UnsupportedOperationException("Not supported");
}
@Override
public void requestCallback(IAppCallback appCallback) {
throw new UnsupportedOperationException("Not supported");
}
@Override
public void quit() throws RemoteException {
throw new UnsupportedOperationException("Not supported");