blob: 8136992a3e218fb40db339a6ac1f81f30065ecee [file] [log] [blame]
// Copyright 2020 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.
#include "AndroidPipe.h"
#include "android_pipe_base.h"
#include "base/Optional.h"
#include "base/StringFormat.h"
#include "base/MemStream.h"
#include "base/Lock.h"
#include "android_pipe_device.h"
#include "android_pipe_host.h"
#include "host-common/GfxstreamFatalError.h"
#include "DeviceContextRunner.h"
#include "VmLock.h"
#include <algorithm>
#include <memory>
#include <string>
#include <vector>
#include <unordered_set>
#include <assert.h>
#include <string.h>
#define DEBUG 0
#if DEBUG >= 1
#include <stdio.h>
#define D(...) fprintf(stderr, __VA_ARGS__), fprintf(stderr, "\n")
#else
#define D(...) (void)0
#endif
#if DEBUG >= 2
#define DD(...) fprintf(stderr, __VA_ARGS__), fprintf(stderr, "\n")
#else
#define DD(...) (void)0
#endif
#define E(...) fprintf(stderr, "ERROR:" __VA_ARGS__), fprintf(stderr, "\n")
static const AndroidPipeHwFuncs* getPipeHwFuncs(const void* hwPipe) {
return *static_cast<const AndroidPipeHwFuncs* const *>(hwPipe);
}
using namespace android::base;
using AndroidPipe = android::AndroidPipe;
using BaseStream = android::base::Stream;
using CStream = ::Stream;
using OptionalString = android::base::Optional<std::string>;
using Service = android::AndroidPipe::Service;
using ServiceList = std::vector<std::unique_ptr<Service>>;
using VmLock = android::VmLock;
using android::base::MemStream;
using android::base::StringFormat;
using emugl::ABORT_REASON_OTHER;
using emugl::FatalError;
static BaseStream* asBaseStream(CStream* stream) {
return reinterpret_cast<BaseStream*>(stream);
}
#define CHECK_VM_STATE_LOCK() (void)0
namespace android {
namespace {
static bool isPipeOptional(const std::string& name) {
return name == "OffworldPipe";
}
// Write an optional string |str| to |stream|. |str| can be null. Use
// readOptionalString() to read it back later.
static void writeOptionalString(BaseStream* stream, const char* str) {
if (str) {
stream->putByte(1);
stream->putString(str);
} else {
stream->putByte(0);
}
}
// Read an optional string from |stream|. If the result is not constructed
// (i.e. equals to false), this means the original |str| parameter was null.
static OptionalString readOptionalString(BaseStream* stream) {
if (stream->getByte()) {
return OptionalString(stream->getString());
}
return OptionalString();
}
// forward
Service* findServiceByName(const char* name);
// Implementation of a special AndroidPipe class used to model the state
// of a pipe connection before the service name has been written to the
// file descriptor by the guest. The most important method is onGuestSend()
// which will detect when this happens.
class ConnectorPipe : public AndroidPipe {
public:
ConnectorPipe(void* hwPipe, Service* service)
: AndroidPipe(hwPipe, service) {
DD("%s: Creating new ConnectorPipe hwpipe=%p", __FUNCTION__, mHwPipe);
}
virtual void onGuestClose(PipeCloseReason reason) override {
// nothing to do here
DD("%s: closing ConnectorPipe hwpipe=%p prematurily (reason=%d)!",
__func__, mHwPipe, (int)reason);
}
virtual unsigned onGuestPoll() const override {
// A connector always want to receive data.
DD("%s: polling hwpipe=%p", __FUNCTION__, mHwPipe);
return PIPE_POLL_OUT;
}
virtual int onGuestRecv(AndroidPipeBuffer* buffers,
int numBuffers) override {
// This pipe never wants to write to the guest, so getting there
// is an error since PIPE_WAKE_IN is never signaled.
DD("%s: trying to receive data from hwpipe=%p", __FUNCTION__, mHwPipe);
return PIPE_ERROR_IO;
}
// TECHNICAL NOTE: This function reads data from the guest until it
// finds a zero-terminated C-string. After that it parses it to find
// a registered service corresponding to one of the allowed formats
// (see below). In case of success, this creates a new AndroidPipe
// instance and calls AndroidPipeHwFuncs::resetPipe() to associate it with
// the current hardware-side |mHwPipe|, then *deletes* the current
// instance! In case of error (e.g. invalid service name, or error during
// initialization), PIPE_ERROR_INVAL will be returned, otherwise, the
// number of bytes accepted from the guest is returned.
virtual int onGuestSend(const AndroidPipeBuffer* buffers,
int numBuffers,
void** newPipePtr) override {
int result = 0;
size_t avail = kBufferSize - mPos;
bool foundZero = false;
for (; !foundZero && avail > 0 && numBuffers > 0;
buffers++, numBuffers--) {
const uint8_t* data = buffers[0].data;
size_t count = std::min(avail, buffers[0].size);
// Read up to |count| bytes, stopping after the first zero.
size_t n = 0;
while (n < count) {
uint8_t byte = data[n++];
mBuffer[mPos++] = (char) byte;
if (!byte) {
foundZero = true;
break;
}
}
result += static_cast<int>(n);
avail -= n;
}
DD("%s: receiving %d connection bytes from hwpipe=%p", __FUNCTION__,
result, mHwPipe);
if (!foundZero) {
if (avail == 0) {
DD("%s: service name buffer full, force-closing connection",
__FUNCTION__);
return PIPE_ERROR_IO;
}
// Still waiting for terminating zero.
DD("%s: still waiting for terminating zero!", __FUNCTION__);
return result;
}
// Acceptable formats for the connection string are:
//
// pipe:<name>
// pipe:<name>:<arguments>
//
char* pipeName;
char* pipeArgs;
D("%s: connector: '%s'", __FUNCTION__, mBuffer);
if (memcmp(mBuffer, "pipe:", 5) != 0) {
// Nope, we don't handle these for now.
D("%s: Unknown pipe connection: '%s'", __FUNCTION__, mBuffer);
return PIPE_ERROR_INVAL;
}
pipeName = mBuffer + 5;
pipeArgs = strchr(pipeName, ':');
Service* svc = nullptr;
// As a special case, if the service name is as:
// qemud:<name>
// qemud:<name>:args
//
// First look for a registered pipe service named "qemud:<name>"
// and if not found, fallback to "qemud" only.
//
// This is useful to support qemud services that are now served
// by a dedicated (and faster) pipe service, e.g. 'qemud:adb'
// as currently implemented by QEMU2 (and soon by QEMU1).
static const char kQemudPrefix[] = "qemud:";
const size_t kQemudPrefixSize = sizeof(kQemudPrefix) - 1U;
if (!::strncmp(pipeName, kQemudPrefix, kQemudPrefixSize)) {
assert(pipeArgs == pipeName + kQemudPrefixSize - 1);
char* pipeArgs2 = strchr(pipeArgs + 1, ':');
if (pipeArgs2) {
*pipeArgs2 = '\0';
}
svc = findServiceByName(pipeName);
if (svc) {
pipeArgs = pipeArgs2;
} else if (pipeArgs2) {
// Restore colon.
*pipeArgs2 = ':';
}
}
if (pipeArgs) {
*pipeArgs++ = '\0';
if (!pipeArgs) {
pipeArgs = NULL;
}
}
if (!svc) {
svc = findServiceByName(pipeName);
}
if (!svc) {
D("%s: Unknown server with name %s!", __FUNCTION__, pipeName);
return PIPE_ERROR_INVAL;
}
AndroidPipe* newPipe = svc->create(mHwPipe, pipeArgs, mFlags);
if (!newPipe) {
D("%s: Initialization failed for %s pipe!", __FUNCTION__, pipeName);
return PIPE_ERROR_INVAL;
}
// Swap your host-side pipe instance with this one weird trick!
D("%s: starting new pipe %p (swapping %p) for service %s",
__FUNCTION__,
newPipe,
mHwPipe,
pipeName);
newPipe->setFlags(mFlags);
*newPipePtr = newPipe;
delete this;
return result;
}
virtual void onGuestWantWakeOn(int wakeFlags) override {
// nothing to do here
DD("%s: signaling wakeFlags=%d for hwpipe=%p", __FUNCTION__, wakeFlags,
mHwPipe);
}
virtual void onSave(BaseStream* stream) override {
DD("%s: saving connector state for hwpipe=%p", __FUNCTION__, mHwPipe);
stream->putBe32(mPos);
stream->write(mBuffer, mPos);
}
bool onLoad(BaseStream* stream) {
DD("%s: loading connector state for hwpipe=%p", __FUNCTION__, mHwPipe);
int32_t len = stream->getBe32();
if (len < 0 || len > kBufferSize) {
D("%s: invalid length %d (expected 0 <= len <= %d)", __FUNCTION__,
static_cast<int>(len), kBufferSize);
return false;
}
mPos = (int)len;
int ret = (int)stream->read(mBuffer, mPos);
DD("%s: read %d bytes (%d expected)", __FUNCTION__, ret, mPos);
return (ret == mPos);
}
private:
static constexpr int kBufferSize = 128;
char mBuffer[kBufferSize];
int mPos = 0;
};
// Associated AndroidPipe::Service class for ConnectorPipe instances.
class ConnectorService : public Service {
public:
ConnectorService() : Service("<connector>") {}
virtual AndroidPipe* create(void* hwPipe, const char* args,
enum AndroidPipeFlags flags) override {
return new ConnectorPipe(hwPipe, this);
}
virtual bool canLoad() const override { return true; }
virtual AndroidPipe* load(void* hwPipe,
const char* args,
BaseStream* stream) override {
ConnectorPipe* pipe = new ConnectorPipe(hwPipe, this);
if (!pipe->onLoad(stream)) {
delete pipe;
return nullptr;
}
return pipe;
}
};
// A helper class used to send signalWake() and closeFromHost() commands to
// the device thread, depending on the threading mode setup by the emulation
// engine.
struct PipeWakeCommand {
void* hwPipe;
int wakeFlags;
};
class PipeWaker final : public DeviceContextRunner<PipeWakeCommand> {
public:
void signalWake(void* hwPipe, int wakeFlags) {
queueDeviceOperation({ hwPipe, wakeFlags });
}
void closeFromHost(void* hwPipe) {
signalWake(hwPipe, PIPE_WAKE_CLOSED);
}
void abortPending(void* hwPipe) {
removeAllPendingOperations([hwPipe](const PipeWakeCommand& cmd) {
return cmd.hwPipe == hwPipe;
});
}
void abortAllPending() {
removeAllPendingOperations([](const PipeWakeCommand& cmd) {
return true;
});
}
int getPendingFlags(void* hwPipe) const {
int flags = 0;
forEachPendingOperation([hwPipe, &flags](const PipeWakeCommand& cmd) {
if (cmd.hwPipe == hwPipe) {
flags |= cmd.wakeFlags;
}
});
return flags;
}
private:
virtual void performDeviceOperation(const PipeWakeCommand& wake_cmd) {
void* hwPipe = wake_cmd.hwPipe;
int flags = wake_cmd.wakeFlags;
// Not used when in virtio mode.
if (flags & PIPE_WAKE_CLOSED) {
getPipeHwFuncs(hwPipe)->closeFromHost(hwPipe);
} else {
getPipeHwFuncs(hwPipe)->signalWake(hwPipe, flags);
}
}
};
struct Globals {
ServiceList services;
ConnectorService connectorService;
PipeWaker pipeWaker;
// Searches for a service position in the |services| list and returns the
// index. |startPosHint| is a _hint_ and suggests where to start from.
// Returns the index of the service or -1 if there's no |name| service.
int findServicePositionByName(const char* name,
const int startPosHint = 0) const {
const auto searchByNameFunc =
[name](const std::unique_ptr<Service>& service) {
return service->name() == name;
};
// First, try to search starting from the hint position.
auto end = services.end();
auto it = std::find_if(services.begin() + startPosHint, end,
searchByNameFunc);
// If there was a hint that didn't help, continue searching from the
// beginning of the contatiner to check the rest of it.
if (it == end && startPosHint > 0) {
end = services.begin() + startPosHint;
it = std::find_if(services.begin(), end, searchByNameFunc);
}
return it == end ? -1 : it - services.begin();
}
Service* loadServiceByName(BaseStream* stream) {
OptionalString serviceName = readOptionalString(stream);
if (!serviceName) {
DD("%s: no name (assuming connector state)", __FUNCTION__);
return &connectorService;
}
DD("%s: found [%s]", __FUNCTION__, serviceName->c_str());
return findServiceByName(serviceName->c_str());
}
};
static Globals* sGlobals() { static Globals* g = new Globals; return g; }
Service* findServiceByName(const char* name) {
const int pos = sGlobals()->findServicePositionByName(name);
return pos < 0 ? nullptr : sGlobals()->services[pos].get();
}
AndroidPipe* loadPipeFromStreamCommon(BaseStream* stream,
void* hwPipe,
Service* service,
char* pForceClose) {
*pForceClose = 0;
OptionalString args = readOptionalString(stream);
AndroidPipe* pipe = nullptr;
if (service->canLoad()) {
DD("%s: loading state for [%s] hwpipe=%p", __FUNCTION__,
service->name().c_str(), hwPipe);
pipe = service->load(hwPipe, args ? args->c_str() : nullptr, stream);
if (!pipe) {
*pForceClose = 1;
}
} else {
DD("%s: force-closing hwpipe=%p", __FUNCTION__, hwPipe);
*pForceClose = 1;
}
const int pendingFlags = stream->getBe32();
if (pendingFlags && pipe && !*pForceClose) {
if (!hwPipe) {
GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
<< "fatal: AndroidPipe [" << pipe->name() << "] hwPipe is NULL(flags = 0x"
<< std::hex << unsigned(pendingFlags) << " )";
}
sGlobals()->pipeWaker.signalWake(hwPipe, pendingFlags);
DD("%s: singalled wake flags %d for pipe hwpipe=%p", __func__,
pendingFlags, hwPipe);
}
return pipe;
}
} // namespace
// static
void AndroidPipe::initThreading(VmLock* vmLock) {
// TODO: Make this work in qemu with the actual goldfish pipe device.
// In virtio land, this won't be needed, so include trivial timer interface.
sGlobals()->pipeWaker.init(vmLock, {
// installFunc
[](DeviceContextRunner<PipeWakeCommand>* dcr, std::function<void()> installedFunc) {
(void)dcr;
(void)installedFunc;
},
// uninstallFunc
[](DeviceContextRunner<PipeWakeCommand>* dcr) {
(void)dcr;
},
// startWithTimeoutFunc
[](DeviceContextRunner<PipeWakeCommand>* dcr, uint64_t timeout) {
(void)dcr;
(void)timeout;
}
});
}
AndroidPipe::~AndroidPipe() {
DD("%s: for hwpipe=%p (host %p '%s')", __FUNCTION__, mHwPipe, this,
mService->name().c_str());
}
// static
void AndroidPipe::Service::add(std::unique_ptr<Service> service) {
DD("Adding new pipe service '%s' this=%p", service->name().c_str(),
service.get());
sGlobals()->services.push_back(std::move(service));
}
// static
void AndroidPipe::Service::resetAll() {
DD("Resetting all pipe services");
sGlobals()->services.clear();
}
void AndroidPipe::signalWake(int wakeFlags) {
// i.e., pipe not using normal pipe device
if (mFlags) return;
if (!mHwPipe) {
GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
<< "AndroidPipe [" << name() << "]: hwPipe is NULL (flags = 0x" << std::hex
<< unsigned(wakeFlags) << ")";
}
sGlobals()->pipeWaker.signalWake(mHwPipe, wakeFlags);
}
void AndroidPipe::closeFromHost() {
// i.e., pipe not using normal pipe device
if (mFlags) return;
if (!mHwPipe) {
GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
<< "AndroidPipe [" << name() << "]: hwPipe is NULL";
}
sGlobals()->pipeWaker.closeFromHost(mHwPipe);
}
void AndroidPipe::abortPendingOperation() {
// i.e., pipe not using normal pipe device
if (mFlags) return;
if (!mHwPipe) {
GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
<< "AndroidPipe [" << name() << "]: hwPipe is NULL";
}
sGlobals()->pipeWaker.abortPending(mHwPipe);
}
void AndroidPipe::saveToStream(BaseStream* stream) {
// First, write service name.
if (mService == &sGlobals()->connectorService) {
// A connector pipe
stream->putByte(0);
} else {
// A regular service pipe.
stream->putByte(1);
stream->putString(mService->name());
}
MemStream pipeStream;
writeOptionalString(&pipeStream, mArgs.c_str());
// Save pipe-specific state now.
if (mService->canLoad()) {
mService->savePipe(this, &pipeStream);
}
// Save the pending wake or close operations as well.
const int pendingFlags = sGlobals()->pipeWaker.getPendingFlags(mHwPipe);
pipeStream.putBe32(pendingFlags);
pipeStream.save(stream);
}
// static
AndroidPipe* AndroidPipe::loadFromStream(BaseStream* stream,
void* hwPipe,
char* pForceClose) {
Service* service = sGlobals()->loadServiceByName(stream);
// Always load the pipeStream, it allows us to safely skip loading streams.
MemStream pipeStream;
pipeStream.load(stream);
if (!service) {
return nullptr;
}
return loadPipeFromStreamCommon(&pipeStream, hwPipe, service, pForceClose);
}
// static
AndroidPipe* AndroidPipe::loadFromStreamLegacy(BaseStream* stream,
void* hwPipe,
uint64_t* pChannel,
unsigned char* pWakes,
unsigned char* pClosed,
char* pForceClose) {
Service* service = sGlobals()->loadServiceByName(stream);
// Always load the pipeStream, it allows us to safely skip loading streams.
MemStream pipeStream;
pipeStream.load(stream);
if (!service) {
return nullptr;
}
*pChannel = pipeStream.getBe64();
*pWakes = pipeStream.getByte();
*pClosed = pipeStream.getByte();
return loadPipeFromStreamCommon(&pipeStream, hwPipe, service, pForceClose);
}
} // namespace android
// API for the virtual device.
void android_pipe_reset_services() {
AndroidPipe::Service::resetAll();
}
void* android_pipe_guest_open(void* hwpipe) {
CHECK_VM_STATE_LOCK();
DD("%s: Creating new connector pipe for hwpipe=%p", __FUNCTION__, hwpipe);
return android::sGlobals()->connectorService.create(hwpipe, nullptr, (AndroidPipeFlags)0);
}
void* android_pipe_guest_open_with_flags(void* hwpipe, uint32_t flags) {
CHECK_VM_STATE_LOCK();
DD("%s: Creating new connector pipe for hwpipe=%p", __FUNCTION__, hwpipe);
auto pipe =
android::sGlobals()->connectorService.create(hwpipe, nullptr, (AndroidPipeFlags)flags);
pipe->setFlags((AndroidPipeFlags)flags);
return pipe;
}
void android_pipe_guest_close(void* internalPipe, PipeCloseReason reason) {
CHECK_VM_STATE_LOCK();
auto pipe = static_cast<android::AndroidPipe*>(internalPipe);
if (pipe) {
D("%s: host=%p [%s] reason=%d", __FUNCTION__, pipe, pipe->name(),
(int)reason);
pipe->abortPendingOperation();
pipe->onGuestClose(reason);
}
}
template <class Func>
static void forEachServiceToStream(CStream* stream, Func&& func) {
BaseStream* const bs = asBaseStream(stream);
bs->putBe16(android::sGlobals()->services.size());
for (const auto& service : android::sGlobals()->services) {
bs->putString(service->name());
// Write to the pipeStream first so that we know the length and can
// enable skipping loading specific pipes on load, see isPipeOptional.
MemStream pipeStream;
func(service.get(), &pipeStream);
pipeStream.save(bs);
}
}
template <class Func>
static void forEachServiceFromStream(CStream* stream, Func&& func) {
const auto& services = android::sGlobals()->services;
BaseStream* const bs = asBaseStream(stream);
const int count = bs->getBe16();
int servicePos = -1;
std::unordered_set<Service*> missingServices;
for (const auto& service : services) {
missingServices.insert(service.get());
}
for (int i = 0; i < count; ++i) {
const auto name = bs->getString();
servicePos = android::sGlobals()->findServicePositionByName(
name.c_str(), servicePos + 1);
// Always load the pipeStream, so that if the pipe is missing it does
// not corrupt the next pipe.
MemStream pipeStream;
pipeStream.load(bs);
if (servicePos >= 0) {
const auto& service = services[servicePos];
func(service.get(), &pipeStream);
missingServices.erase(service.get());
} else if (android::isPipeOptional(name)) {
D("%s: Skipping optional pipe %s\n", __FUNCTION__, name.c_str());
} else {
assert(false && "Service for snapshot pipe does not exist");
E("%s: Could not load pipe %s, service does not exist\n",
__FUNCTION__, name.c_str());
}
}
// Now call the same function for all services that weren't in the snapshot.
// Pass |nullptr| instead of the stream pointer to make sure they know
// that while we're loading from a snapshot these services aren't part
// of it.
for (const auto service : missingServices) {
func(service, nullptr);
}
}
void android_pipe_guest_pre_load(CStream* stream) {
CHECK_VM_STATE_LOCK();
// We may not call qemu_set_irq() until the snapshot is loaded.
android::sGlobals()->pipeWaker.abortAllPending();
android::sGlobals()->pipeWaker.setContextRunMode(
android::ContextRunMode::DeferAlways);
forEachServiceFromStream(stream, [](Service* service, BaseStream* bs) {
if (service->canLoad()) {
service->preLoad(bs);
}
});
}
void android_pipe_guest_post_load(CStream* stream) {
CHECK_VM_STATE_LOCK();
forEachServiceFromStream(stream, [](Service* service, BaseStream* bs) {
if (service->canLoad()) {
service->postLoad(bs);
}
});
// Restore the regular handling of pipe interrupt requests.
android::sGlobals()->pipeWaker.setContextRunMode(
android::ContextRunMode::DeferIfNotLocked);
}
void android_pipe_guest_pre_save(CStream* stream) {
CHECK_VM_STATE_LOCK();
forEachServiceToStream(stream, [](Service* service, BaseStream* bs) {
if (service->canLoad()) {
service->preSave(bs);
}
});
}
void android_pipe_guest_post_save(CStream* stream) {
CHECK_VM_STATE_LOCK();
forEachServiceToStream(stream, [](Service* service, BaseStream* bs) {
if (service->canLoad()) {
service->postSave(bs);
}
});
}
void android_pipe_guest_save(void* internalPipe, CStream* stream) {
CHECK_VM_STATE_LOCK();
auto pipe = static_cast<android::AndroidPipe*>(internalPipe);
DD("%s: host=%p [%s]", __FUNCTION__, pipe, pipe->name());
pipe->saveToStream(asBaseStream(stream));
}
void* android_pipe_guest_load(CStream* stream,
void* hwPipe,
char* pForceClose) {
CHECK_VM_STATE_LOCK();
DD("%s: hwpipe=%p", __FUNCTION__, hwPipe);
return AndroidPipe::loadFromStream(asBaseStream(stream), hwPipe,
pForceClose);
}
void* android_pipe_guest_load_legacy(CStream* stream,
void* hwPipe,
uint64_t* pChannel,
unsigned char* pWakes,
unsigned char* pClosed,
char* pForceClose) {
CHECK_VM_STATE_LOCK();
DD("%s: hwpipe=%p", __FUNCTION__, hwPipe);
return android::AndroidPipe::loadFromStreamLegacy(asBaseStream(stream),
hwPipe, pChannel, pWakes,
pClosed, pForceClose);
}
unsigned android_pipe_guest_poll(void* internalPipe) {
CHECK_VM_STATE_LOCK();
auto pipe = static_cast<AndroidPipe*>(internalPipe);
DD("%s: host=%p [%s]", __FUNCTION__, pipe, pipe->name());
return pipe->onGuestPoll();
}
int android_pipe_guest_recv(void* internalPipe,
AndroidPipeBuffer* buffers,
int numBuffers) {
CHECK_VM_STATE_LOCK();
auto pipe = static_cast<AndroidPipe*>(internalPipe);
// Note that pipe may be deleted during this call, so it's not safe to
// access pipe after this point.
return pipe->onGuestRecv(buffers, numBuffers);
}
int android_pipe_guest_send(void** internalPipe,
const AndroidPipeBuffer* buffers,
int numBuffers) {
CHECK_VM_STATE_LOCK();
auto pipe = static_cast<AndroidPipe*>(*internalPipe);
// Note that pipe may be deleted during this call, so it's not safe to
// access pipe after this point.
return pipe->onGuestSend(buffers, numBuffers, internalPipe);
}
void android_pipe_guest_wake_on(void* internalPipe, unsigned wakes) {
CHECK_VM_STATE_LOCK();
auto pipe = static_cast<AndroidPipe*>(internalPipe);
pipe->onGuestWantWakeOn(wakes);
}
// API implemented by the virtual device.
void android_pipe_host_close(void* hwpipe) {
auto pipe = static_cast<android::AndroidPipe*>(hwpipe);
D("%s: host=%p [%s]", __FUNCTION__, pipe, pipe->name());
android::sGlobals()->pipeWaker.closeFromHost(pipe);
}
void android_pipe_host_signal_wake(void* hwpipe, unsigned flags) {
android::sGlobals()->pipeWaker.signalWake(hwpipe, flags);
}
// Not used when in virtio mode.
int android_pipe_get_id(void* hwpipe) {
return getPipeHwFuncs(hwpipe)->getPipeId(hwpipe);
}
static std::vector<std::pair<void*(*)(int), const char*>> lookup_by_id_callbacks;
void android_pipe_append_lookup_by_id_callback(void*(*cb)(int), const char* tag) {
lookup_by_id_callbacks.push_back({cb, tag});
}
void* android_pipe_lookup_by_id(const int id) {
void* hwPipeFound = nullptr;
const char* tagFound = "(null)";
for (const auto &cb : lookup_by_id_callbacks) {
void* hwPipe = (*cb.first)(id);
if (hwPipe) {
if (hwPipeFound) {
GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
<< "Pipe id (" << id << ") is not unique, at least two pipes are found: `"
<< tagFound << "` and `" << cb.second << "`";
} else {
hwPipeFound = hwPipe;
tagFound = cb.second;
}
}
}
return hwPipeFound;
}