blob: 945d4e9873e7b02992b0cebe5589d0cf32536ffc [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 "host-common/AndroidPipe.h"
#include "aemu/base/synchronization/Lock.h"
#include "host-common/opengles.h"
#include <assert.h>
#include <atomic>
#include <memory>
#include <unordered_set>
#include <string.h>
using android::base::AutoLock;
using android::base::Lock;
namespace android {
namespace opengl {
struct ProcessPipeIdRegistry {
Lock lock;
std::unordered_set<uint64_t> ids;
};
static ProcessPipeIdRegistry sRegistry;
static bool sIdExistsInRegistry(uint64_t id) {
AutoLock lock(sRegistry.lock);
auto it = sRegistry.ids.find(id);
return it != sRegistry.ids.end();
}
namespace {
// GLProcessPipe is a pipe service that is used for releasing graphics resources
// per guest process. At the time being, guest processes can acquire host color
// buffer handles / EGLImage handles and they need to be properly released when
// guest process exits unexpectedly. This class is used to detect if guest
// process exits, so that a proper cleanup function can be called.
// It is done by setting up a pipe per guest process before acquiring color
// buffer handles. When guest process exits, the pipe will be closed, and
// onGuestClose() will trigger the cleanup path.
class GLProcessPipe : public AndroidPipe {
public:
//////////////////////////////////////////////////////////////////////////
// The pipe service class for this implementation.
class Service : public AndroidPipe::Service {
public:
Service() : AndroidPipe::Service("GLProcessPipe") {}
bool canLoad() const override { return true; }
AndroidPipe* create(void* hwPipe, const char* args, enum AndroidPipeFlags flags) override {
return new GLProcessPipe(hwPipe, this, flags);
}
AndroidPipe* load(void* hwPipe, const char* args,
base::Stream* stream) override {
return new GLProcessPipe(hwPipe, this, (AndroidPipeFlags)0, stream);
}
void preLoad(base::Stream* stream) override {
GLProcessPipe::s_headId.store(stream->getBe64());
}
void preSave(base::Stream* stream) override {
stream->putBe64(GLProcessPipe::s_headId.load());
}
};
GLProcessPipe(void* hwPipe, Service* service, enum AndroidPipeFlags flags,
base::Stream* loadStream = nullptr)
: AndroidPipe(hwPipe, service) {
if (loadStream) {
m_uniqueId = loadStream->getBe64();
m_hasData = (loadStream->getByte() != 0);
} else {
if (flags & ANDROID_PIPE_VIRTIO_GPU_BIT) {
m_uniqueId = (uint64_t)(uintptr_t)hwPipe;
s_headId = m_uniqueId;
} else {
m_uniqueId = ++s_headId;
}
}
AutoLock lock(sRegistry.lock);
sRegistry.ids.insert(m_uniqueId);
android_onGuestGraphicsProcessCreate(m_uniqueId);
}
~GLProcessPipe() {
AutoLock lock(sRegistry.lock);
sRegistry.ids.erase(m_uniqueId);
}
void onSave(base::Stream* stream) override {
stream->putBe64(m_uniqueId);
stream->putByte(m_hasData ? 1 : 0);
}
void onGuestClose(PipeCloseReason reason) override {
if (sIdExistsInRegistry(m_uniqueId)) {
android_cleanupProcGLObjects(m_uniqueId);
}
delete this;
}
unsigned onGuestPoll() const override {
return PIPE_POLL_IN | PIPE_POLL_OUT;
}
int onGuestRecv(AndroidPipeBuffer* buffers, int numBuffers) override {
assert(buffers[0].size >= 8);
if (m_hasData) {
m_hasData = false;
memcpy(buffers[0].data, (const char*)&m_uniqueId, sizeof(m_uniqueId));
return sizeof(m_uniqueId);
} else {
return 0;
}
}
int onGuestSend(const AndroidPipeBuffer* buffers,
int numBuffers,
void** newPipePtr) override {
// The guest is supposed to send us a confirm code first. The code is
// 100 (4 byte integer).
assert(buffers[0].size >= 4);
int32_t confirmInt = *((int32_t*)buffers[0].data);
assert(confirmInt == 100);
(void)confirmInt;
m_hasData = true;
return buffers[0].size;
}
void onGuestWantWakeOn(int flags) override {}
private:
// An identifier for the guest process corresponding to this pipe.
// With very high probability, all currently-active processes have unique
// identifiers, since the IDs are assigned sequentially from a 64-bit ID
// space.
// Please change it if you ever have a use case that exhausts them
uint64_t m_uniqueId;
bool m_hasData = false;
static std::atomic<uint64_t> s_headId;
};
std::atomic<uint64_t> GLProcessPipe::s_headId {0};
}
void registerGLProcessPipeService() {
AndroidPipe::Service::add(std::make_unique<GLProcessPipe::Service>());
}
void forEachProcessPipeId(std::function<void(uint64_t)> f) {
AutoLock lock(sRegistry.lock);
for (auto id: sRegistry.ids) {
f(id);
}
}
void forEachProcessPipeIdRunAndErase(std::function<void(uint64_t)> f) {
AutoLock lock(sRegistry.lock);
auto it = sRegistry.ids.begin();
while (it != sRegistry.ids.end()) {
f(*it);
it = sRegistry.ids.erase(it);
}
}
}
}