Simplify EVS HAL and move to "agressive opens"

This adapts the API implementation to allow a duplicate "open" operation
to automatically close any previous connections to the device.  This
works around a binder level issue that can cause destructors triggered
by remote clients to be delivered out of order to the server.

This was originally change ag/1969959 on master, but has been
recreated on oc-dev (cherry-picking was broken at the time).
The original master change will be abandoned in favor of this getting
merged down from oc-dev.

Test:  Run Vts test (added in following change)
Change-Id: I7b417998e59a4d592fbb91811c4101f39097c5dd
This commit is contained in:
Scott Randolph 2017-03-30 14:04:12 -07:00
parent 63e15f0790
commit de9880eece
11 changed files with 275 additions and 184 deletions

View file

@ -28,10 +28,10 @@ interface IEvsCamera {
/**
* Returns the ID of this camera.
*
* Returns the string id of this camera. This must be the same value as reported in
* the camera_id field of the CameraDesc structure by EvsEnumerator::getCamerList().
* Returns the description of this camera. This must be the same value as reported
* by EvsEnumerator::getCamerList().
*/
getId() generates (string cameraId);
getCameraInfo() generates (CameraDesc info);
/**
* Specifies the depth of the buffer chain the camera is asked to support.

View file

@ -27,7 +27,7 @@ interface IEvsDisplay {
/**
* Returns basic information about the EVS display provided by the system.
*
* See the description of the DisplayDesc structure below for details.
* See the description of the DisplayDesc structure for details.
*/
getDisplayInfo() generates (DisplayDesc info);

View file

@ -31,14 +31,14 @@ interface IEvsEnumerator {
*/
getCameraList() generates (vec<CameraDesc> cameras);
/**
* Get the IEvsCamera associated with a cameraId from a CameraDesc
*
* Given a camera's unique cameraId from ca CameraDesc, returns
* the ICamera interface assocaited with the specified camera.
* When done using the camera, it must be returned by calling
* closeCamera on the ICamera interface.
* the ICamera interface associated with the specified camera.
* When done using the camera, the caller may release it by calling closeCamera().
* TODO(b/36122635) Reliance on the sp<> going out of scope is not recommended because the
* resources may not be released right away due to asynchronos behavior in the hardware binder.
*/
openCamera(string cameraId) generates (IEvsCamera carCamera);
@ -57,6 +57,9 @@ interface IEvsEnumerator {
* There can be at most one EVS display object for the system and this function
* requests access to it. If the EVS display is not available or is already in use,
* a null pointer is returned.
* When done using the display, the caller may release it by calling closeDisplay().
* TODO(b/36122635) Reliance on the sp<> going out of scope is not recommended because the
* resources may not be released right away due to asynchronos behavior in the hardware binder.
*/
openDisplay() generates (IEvsDisplay display);
@ -64,7 +67,7 @@ interface IEvsEnumerator {
* Return the specified IEvsDisplay interface as no longer in use
*
* When the IEvsDisplay object is no longer required, it must be released.
* NOTE: All buffer must have been returned to the display before making this call.
* NOTE: All buffers must have been returned to the display before making this call.
*/
closeDisplay(IEvsDisplay display);

View file

@ -23,4 +23,9 @@ cc_binary {
"liblog",
"libutils",
],
cflags: [
"-O0",
"-g",
],
}

View file

@ -17,6 +17,7 @@
#define LOG_TAG "android.hardware.automotive.evs@1.0-service"
#include "EvsCamera.h"
#include "EvsEnumerator.h"
#include <ui/GraphicBufferAllocator.h>
#include <ui/GraphicBufferMapper.h>
@ -30,18 +31,15 @@ namespace V1_0 {
namespace implementation {
// These are the special camera names for which we'll initialize custom test data
// Special camera names for which we'll initialize alternate test data
const char EvsCamera::kCameraName_Backup[] = "backup";
const char EvsCamera::kCameraName_RightTurn[] = "Right Turn";
// Arbitrary limit on number of graphics buffers allowed to be allocated
// Safeguards against unreasonable resource consumption and provides a testable limit
const unsigned MAX_BUFFERS_IN_FLIGHT = 100;
// TODO(b/31632518): Need to get notification when our client dies so we can close the camera.
// As it stands, if the client dies suddenly, the buffer may be stranded.
EvsCamera::EvsCamera(const char *id) :
mFramesAllowed(0),
mFramesInUse(0),
@ -53,22 +51,14 @@ EvsCamera::EvsCamera(const char *id) :
// Set up dummy data for testing
if (mDescription.cameraId == kCameraName_Backup) {
mDescription.hints = static_cast<uint32_t>(UsageHint::USAGE_HINT_REVERSE);
mDescription.vendorFlags = 0xFFFFFFFF; // Arbitrary value
mDescription.defaultHorResolution = 320; // 1/2 NTSC/VGA
mDescription.defaultVerResolution = 240; // 1/2 NTSC/VGA
} else if (mDescription.cameraId == kCameraName_RightTurn) {
// Nothing but the name and the usage hint
mDescription.hints = static_cast<uint32_t>(UsageHint::USAGE_HINT_RIGHT_TURN);
mWidth = 640; // full NTSC/VGA
mHeight = 480; // full NTSC/VGA
mDescription.vendorFlags = 0xFFFFFFFF; // Arbitrary value
} else {
// Leave empty for a minimalist camera description without even a hint
mWidth = 320; // 1/2 NTSC/VGA
mHeight = 240; // 1/2 NTSC/VGA
}
// Set our buffer properties
mWidth = (mDescription.defaultHorResolution) ? mDescription.defaultHorResolution : 640;
mHeight = (mDescription.defaultVerResolution) ? mDescription.defaultVerResolution : 480;
mFormat = HAL_PIXEL_FORMAT_RGBA_8888;
mUsage = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_CAMERA_WRITE |
GRALLOC_USAGE_SW_READ_RARELY | GRALLOC_USAGE_SW_WRITE_RARELY;
@ -77,32 +67,49 @@ EvsCamera::EvsCamera(const char *id) :
EvsCamera::~EvsCamera() {
ALOGD("EvsCamera being destroyed");
std::lock_guard<std::mutex> lock(mAccessLock);
forceShutdown();
}
//
// This gets called if another caller "steals" ownership of the camera
//
void EvsCamera::forceShutdown()
{
ALOGD("EvsCamera forceShutdown");
// Make sure our output stream is cleaned up
// (It really should be already)
stopVideoStream();
// Claim the lock while we work on internal state
std::lock_guard <std::mutex> lock(mAccessLock);
// Drop all the graphics buffers we've been using
GraphicBufferAllocator& alloc(GraphicBufferAllocator::get());
for (auto&& rec : mBuffers) {
if (rec.inUse) {
ALOGE("Error - releasing buffer despite remote ownership");
if (mBuffers.size() > 0) {
GraphicBufferAllocator& alloc(GraphicBufferAllocator::get());
for (auto&& rec : mBuffers) {
if (rec.inUse) {
ALOGE("Error - releasing buffer despite remote ownership");
}
alloc.free(rec.handle);
rec.handle = nullptr;
}
alloc.free(rec.handle);
rec.handle = nullptr;
mBuffers.clear();
}
ALOGD("EvsCamera destroyed");
// Put this object into an unrecoverable error state since somebody else
// is going to own the underlying camera now
mStreamState = DEAD;
}
// Methods from ::android::hardware::automotive::evs::V1_0::IEvsCamera follow.
Return<void> EvsCamera::getId(getId_cb id_cb) {
ALOGD("getId");
id_cb(mDescription.cameraId);
Return<void> EvsCamera::getCameraInfo(getCameraInfo_cb _hidl_cb) {
ALOGD("getCameraInfo");
// Send back our self description
_hidl_cb(mDescription);
return Void();
}
@ -111,6 +118,12 @@ Return<EvsResult> EvsCamera::setMaxFramesInFlight(uint32_t bufferCount) {
ALOGD("setMaxFramesInFlight");
std::lock_guard<std::mutex> lock(mAccessLock);
// If we've been displaced by another owner of the camera, then we can't do anything else
if (mStreamState == DEAD) {
ALOGE("ignoring setMaxFramesInFlight call when camera has been lost.");
return EvsResult::OWNERSHIP_LOST;
}
// We cannot function without at least one video buffer to send data
if (bufferCount < 1) {
ALOGE("Ignoring setMaxFramesInFlight with less than one buffer requested");
@ -130,6 +143,11 @@ Return<EvsResult> EvsCamera::startVideoStream(const ::android::sp<IEvsCameraStre
ALOGD("startVideoStream");
std::lock_guard<std::mutex> lock(mAccessLock);
// If we've been displaced by another owner of the camera, then we can't do anything else
if (mStreamState == DEAD) {
ALOGE("ignoring startVideoStream call when camera has been lost.");
return EvsResult::OWNERSHIP_LOST;
}
if (mStreamState != STOPPED) {
ALOGE("ignoring startVideoStream call when a stream is already running.");
return EvsResult::STREAM_ALREADY_RUNNING;
@ -207,6 +225,7 @@ Return<void> EvsCamera::stopVideoStream() {
lock.lock();
mStreamState = STOPPED;
mStream = nullptr;
ALOGD("Stream marked STOPPED.");
}
@ -232,6 +251,12 @@ Return<EvsResult> EvsCamera::setExtendedInfo(uint32_t /*opaqueIdentifier*/, int3
ALOGD("setExtendedInfo");
std::lock_guard<std::mutex> lock(mAccessLock);
// If we've been displaced by another owner of the camera, then we can't do anything else
if (mStreamState == DEAD) {
ALOGE("ignoring setExtendedInfo call when camera has been lost.");
return EvsResult::OWNERSHIP_LOST;
}
// We don't store any device specific information in this implementation
return EvsResult::INVALID_ARG;
}
@ -358,7 +383,9 @@ void EvsCamera::generateFrames() {
while (true) {
bool timeForFrame = false;
// Lock scope
nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
// Lock scope for updating shared state
{
std::lock_guard<std::mutex> lock(mAccessLock);
@ -427,8 +454,15 @@ void EvsCamera::generateFrames() {
}
}
// We arbitrarily choose to generate frames at 10 fps (1/10 * uSecPerSec)
usleep(100000);
// We arbitrarily choose to generate frames at 12 fps to ensure we pass the 10fps test requirement
static const int kTargetFrameRate = 12;
static const nsecs_t kTargetFrameTimeUs = 1000*1000 / kTargetFrameRate;
const nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
const nsecs_t workTimeUs = (now - startTime) / 1000;
const nsecs_t sleepDurationUs = kTargetFrameTimeUs - workTimeUs;
if (sleepDurationUs > 0) {
usleep(sleepDurationUs);
}
}
// If we've been asked to stop, send one last NULL frame to signal the actual end of stream

View file

@ -32,54 +32,50 @@ namespace V1_0 {
namespace implementation {
// From EvsEnumerator.h
class EvsEnumerator;
class EvsCamera : public IEvsCamera {
public:
// Methods from ::android::hardware::automotive::evs::V1_0::IEvsCamera follow.
Return<void> getId(getId_cb id_cb) override;
Return<void> getCameraInfo(getCameraInfo_cb _hidl_cb) override;
Return <EvsResult> setMaxFramesInFlight(uint32_t bufferCount) override;
Return <EvsResult> startVideoStream(const ::android::sp<IEvsCameraStream>& stream) override;
Return<void> doneWithFrame(const BufferDesc& buffer) override;
Return<void> stopVideoStream() override;
Return <int32_t> getExtendedInfo(uint32_t opaqueIdentifier) override;
Return <EvsResult> setExtendedInfo(uint32_t opaqueIdentifier, int32_t opaqueValue) override;
// Implementation details
EvsCamera(const char* id);
EvsCamera(const char *id);
virtual ~EvsCamera() override;
void forceShutdown(); // This gets called if another caller "steals" ownership of the camera
const CameraDesc& getDesc() { return mDescription; };
static const char kCameraName_Backup[];
static const char kCameraName_RightTurn[];
private:
// These three functions are expected to be called while mAccessLock is held
bool setAvailableFrames_Locked(unsigned bufferCount);
unsigned increaseAvailableFrames_Locked(unsigned numToAdd);
unsigned decreaseAvailableFrames_Locked(unsigned numToRemove);
void generateFrames();
void fillTestFrame(const BufferDesc& buff);
CameraDesc mDescription = {}; // The properties of this camera
sp<EvsEnumerator> mEnumerator; // The enumerator object that created this camera
CameraDesc mDescription = {}; // The properties of this camera
std::thread mCaptureThread; // The thread we'll use to synthesize frames
uint32_t mWidth = 0; // Horizontal pixel count in the buffers
uint32_t mHeight = 0; // Vertical pixel count in the buffers
uint32_t mFormat = 0; // Values from android_pixel_format_t [TODO: YUV? Leave opaque?]
uint32_t mUsage = 0; // Values from from Gralloc.h
uint32_t mStride = 0; // Bytes per line in the buffers
uint32_t mWidth = 0; // Horizontal pixel count in the buffers
uint32_t mHeight = 0; // Vertical pixel count in the buffers
uint32_t mFormat = 0; // Values from android_pixel_format_t [TODO: YUV? Leave opaque?]
uint32_t mUsage = 0; // Values from from Gralloc.h
uint32_t mStride = 0; // Bytes per line in the buffers
sp <IEvsCameraStream> mStream = nullptr; // The callback used to deliver each frame
@ -98,10 +94,11 @@ private:
STOPPED,
RUNNING,
STOPPING,
DEAD,
};
StreamStateValues mStreamState;
// Syncrhonization necessary to deconflict mCaptureThread from the main service thread
// Synchronization necessary to deconflict mCaptureThread from the main service thread
std::mutex mAccessLock;
};

View file

@ -30,12 +30,6 @@ namespace V1_0 {
namespace implementation {
// TODO(b/31632518): Need to get notification when our client dies so we can close the camera.
// As it stands, if the client dies suddently, the buffer may be stranded.
// As possible work around would be to give the client a HIDL object to exclusively hold
// and use it's destructor to perform some work in the server side.
EvsDisplay::EvsDisplay() {
ALOGD("EvsDisplay instantiated");
@ -43,34 +37,55 @@ EvsDisplay::EvsDisplay() {
// NOTE: These are arbitrary values chosen for testing
mInfo.displayId = "Mock Display";
mInfo.vendorFlags = 3870;
mInfo.defaultHorResolution = 320;
mInfo.defaultVerResolution = 240;
// Assemble the buffer description we'll use for our render target
mBuffer.width = 320;
mBuffer.height = 240;
mBuffer.format = HAL_PIXEL_FORMAT_RGBA_8888;
mBuffer.usage = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER;
mBuffer.bufferId = 0x3870; // Arbitrary magic number for self recognition
mBuffer.pixelSize = 4;
}
EvsDisplay::~EvsDisplay() {
ALOGD("EvsDisplay being destroyed");
forceShutdown();
}
/**
* This gets called if another caller "steals" ownership of the display
*/
void EvsDisplay::forceShutdown()
{
ALOGD("EvsDisplay forceShutdown");
std::lock_guard<std::mutex> lock(mAccessLock);
// Report if we're going away while a buffer is outstanding
if (mFrameBusy) {
ALOGE("EvsDisplay going down while client is holding a buffer");
}
// Make sure we release our frame buffer
// If the buffer isn't being held by a remote client, release it now as an
// optimization to release the resources more quickly than the destructor might
// get called.
if (mBuffer.memHandle) {
// Report if we're going away while a buffer is outstanding
if (mFrameBusy) {
ALOGE("EvsDisplay going down while client is holding a buffer");
}
// Drop the graphics buffer we've been using
GraphicBufferAllocator& alloc(GraphicBufferAllocator::get());
alloc.free(mBuffer.memHandle);
mBuffer.memHandle = nullptr;
}
ALOGD("EvsDisplay destroyed");
// Put this object into an unrecoverable error state since somebody else
// is going to own the display now.
mRequestedState = DisplayState::DEAD;
}
/**
* Returns basic information about the EVS display provided by the system.
* See the description of the DisplayDesc structure below for details.
* See the description of the DisplayDesc structure for details.
*/
Return<void> EvsDisplay::getDisplayInfo(getDisplayInfo_cb _hidl_cb) {
ALOGD("getDisplayInfo");
@ -94,6 +109,11 @@ Return<EvsResult> EvsDisplay::setDisplayState(DisplayState state) {
ALOGD("setDisplayState");
std::lock_guard<std::mutex> lock(mAccessLock);
if (mRequestedState == DisplayState::DEAD) {
// This object no longer owns the display -- it's been superceeded!
return EvsResult::OWNERSHIP_LOST;
}
// Ensure we recognize the requested state so we don't go off the rails
if (state < DisplayState::NUM_STATES) {
// Record the requested state
@ -119,10 +139,7 @@ Return<DisplayState> EvsDisplay::getDisplayState() {
ALOGD("getDisplayState");
std::lock_guard<std::mutex> lock(mAccessLock);
// At the moment, we treat the requested state as immediately active
DisplayState currentState = mRequestedState;
return currentState;
return mRequestedState;
}
@ -137,15 +154,16 @@ Return<void> EvsDisplay::getTargetBuffer(getTargetBuffer_cb _hidl_cb) {
ALOGD("getTargetBuffer");
std::lock_guard<std::mutex> lock(mAccessLock);
if (mRequestedState == DisplayState::DEAD) {
ALOGE("Rejecting buffer request from object that lost ownership of the display.");
BufferDesc nullBuff = {};
_hidl_cb(nullBuff);
return Void();
}
// If we don't already have a buffer, allocate one now
if (!mBuffer.memHandle) {
// Assemble the buffer description we'll use for our render target
mBuffer.width = mInfo.defaultHorResolution;
mBuffer.height = mInfo.defaultVerResolution;
mBuffer.format = HAL_PIXEL_FORMAT_RGBA_8888;
mBuffer.usage = GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER;
mBuffer.bufferId = 0x3870; // Arbitrary magic number for self recognition
// Allocate the buffer that will hold our displayable image
buffer_handle_t handle = nullptr;
GraphicBufferAllocator& alloc(GraphicBufferAllocator::get());
status_t result = alloc.allocate(mBuffer.width, mBuffer.height,
@ -220,6 +238,11 @@ Return<EvsResult> EvsDisplay::returnTargetBufferForDisplay(const BufferDesc& buf
mFrameBusy = false;
// If we've been displaced by another owner of the display, then we can't do anything else
if (mRequestedState == DisplayState::DEAD) {
return EvsResult::OWNERSHIP_LOST;
}
// If we were waiting for a new frame, this is it!
if (mRequestedState == DisplayState::VISIBLE_ON_NEXT_FRAME) {
mRequestedState = DisplayState::VISIBLE;
@ -248,8 +271,8 @@ Return<EvsResult> EvsDisplay::returnTargetBufferForDisplay(const BufferDesc& buf
// Check the test pixels
bool frameLooksGood = true;
for (unsigned row = 0; row < mInfo.defaultVerResolution; row++) {
for (unsigned col = 0; col < mInfo.defaultHorResolution; col++) {
for (unsigned row = 0; row < mBuffer.height; row++) {
for (unsigned col = 0; col < mBuffer.width; col++) {
// Index into the row to check the pixel at this column.
// We expect 0xFF in the LSB channel, a vertical gradient in the
// second channel, a horitzontal gradient in the third channel, and

View file

@ -27,6 +27,7 @@ namespace evs {
namespace V1_0 {
namespace implementation {
class EvsDisplay : public IEvsDisplay {
public:
// Methods from ::android::hardware::automotive::evs::V1_0::IEvsDisplay follow.
@ -40,6 +41,8 @@ public:
EvsDisplay();
virtual ~EvsDisplay() override;
void forceShutdown(); // This gets called if another caller "steals" ownership of the display
private:
DisplayDesc mInfo = {};
BufferDesc mBuffer = {}; // A graphics buffer into which we'll store images

View file

@ -28,33 +28,36 @@ namespace V1_0 {
namespace implementation {
// TODO(b/31632518): Need to get notification when our client dies so we can close the camera.
// As it stands, if the client dies suddenly, the camera will be stuck "open".
// NOTE: Display should already be safe by virtue of holding only a weak pointer.
// NOTE: All members values are static so that all clients operate on the same state
// That is to say, this is effectively a singleton despite the fact that HIDL
// constructs a new instance for each client.
std::list<EvsEnumerator::CameraRecord> EvsEnumerator::sCameraList;
wp<EvsDisplay> EvsEnumerator::sActiveDisplay;
EvsEnumerator::EvsEnumerator() {
ALOGD("EvsEnumerator created");
// Add sample camera data to our list of cameras
// NOTE: The id strings trigger special initialization inside the EvsCamera constructor
mCameraList.emplace_back( new EvsCamera(EvsCamera::kCameraName_Backup), false );
mCameraList.emplace_back( new EvsCamera("LaneView"), false );
mCameraList.emplace_back( new EvsCamera(EvsCamera::kCameraName_RightTurn), false );
// In a real driver, this would be expected to can the available hardware
sCameraList.emplace_back(EvsCamera::kCameraName_Backup);
sCameraList.emplace_back("LaneView");
sCameraList.emplace_back("right turn");
}
// Methods from ::android::hardware::automotive::evs::V1_0::IEvsEnumerator follow.
Return<void> EvsEnumerator::getCameraList(getCameraList_cb _hidl_cb) {
ALOGD("getCameraList");
const unsigned numCameras = mCameraList.size();
const unsigned numCameras = sCameraList.size();
// Build up a packed array of CameraDesc for return
// NOTE: Only has to live until the callback returns
std::vector<CameraDesc> descriptions;
descriptions.reserve(numCameras);
for (const auto& cam : mCameraList) {
descriptions.push_back( cam.pCamera->getDesc() );
for (const auto& cam : sCameraList) {
descriptions.push_back( cam.desc );
}
// Encapsulate our camera descriptions in the HIDL vec type
@ -68,97 +71,137 @@ Return<void> EvsEnumerator::getCameraList(getCameraList_cb _hidl_cb) {
return Void();
}
Return<sp<IEvsCamera>> EvsEnumerator::openCamera(const hidl_string& cameraId) {
ALOGD("openCamera");
// Find the named camera
CameraRecord *pRecord = nullptr;
for (auto &&cam : mCameraList) {
if (cam.pCamera->getDesc().cameraId == cameraId) {
for (auto &&cam : sCameraList) {
if (cam.desc.cameraId == cameraId) {
// Found a match!
pRecord = &cam;
break;
}
}
// Is this a recognized camera id?
if (!pRecord) {
ALOGE("Requested camera %s not found", cameraId.c_str());
return nullptr;
} else if (pRecord->inUse) {
ALOGE("Cannot open camera %s which is already in use", cameraId.c_str());
return nullptr;
} else {
pRecord->inUse = true;
return(pRecord->pCamera);
}
// Has this camera already been instantiated by another caller?
sp<EvsCamera> pActiveCamera = pRecord->activeInstance.promote();
if (pActiveCamera != nullptr) {
ALOGW("Killing previous camera because of new caller");
closeCamera(pActiveCamera);
}
// Construct a camera instance for the caller
pActiveCamera = new EvsCamera(cameraId);
pRecord->activeInstance = pActiveCamera;
if (pActiveCamera == nullptr) {
ALOGE("Failed to allocate new EvsCamera object for %s\n", cameraId.c_str());
}
return pActiveCamera;
}
Return<void> EvsEnumerator::closeCamera(const ::android::sp<IEvsCamera>& camera) {
Return<void> EvsEnumerator::closeCamera(const ::android::sp<IEvsCamera>& pCamera) {
ALOGD("closeCamera");
if (camera == nullptr) {
ALOGE("Ignoring call to closeCamera with null camera pointer");
} else {
// Find this camera in our list
auto it = std::find_if(mCameraList.begin(),
mCameraList.end(),
[camera](const CameraRecord& rec) {
return (rec.pCamera == camera);
});
if (it == mCameraList.end()) {
ALOGE("Ignoring close on unrecognized camera");
} else {
// Make sure the camera has stopped streaming
camera->stopVideoStream();
if (pCamera == nullptr) {
ALOGE("Ignoring call to closeCamera with null camera ptr");
return Void();
}
it->inUse = false;
// Get the camera id so we can find it in our list
std::string cameraId;
pCamera->getCameraInfo([&cameraId](CameraDesc desc) {
// TODO(b/36532780) Should we able to just use a simple assignment?
// cameraId = desc.cameraId;
cameraId.assign(desc.cameraId.c_str());
}
);
// Find the named camera
CameraRecord *pRecord = nullptr;
for (auto &&cam : sCameraList) {
if (cam.desc.cameraId == cameraId) {
// Found a match!
pRecord = &cam;
break;
}
}
// Is the display being destroyed actually the one we think is active?
if (!pRecord) {
ALOGE("Asked to close a camera who's name isn't recognized");
} else {
sp<EvsCamera> pActiveCamera = pRecord->activeInstance.promote();
if (pActiveCamera == nullptr) {
ALOGE("Somehow a camera is being destroyed when the enumerator didn't know one existed");
} else if (pActiveCamera != pCamera) {
// This can happen if the camera was aggressively reopened, orphaning this previous instance
ALOGW("Ignoring close of previously orphaned camera - why did a client steal?");
} else {
// Drop the active camera
pActiveCamera->forceShutdown();
pRecord->activeInstance = nullptr;
}
}
return Void();
}
Return<sp<IEvsDisplay>> EvsEnumerator::openDisplay() {
ALOGD("openDisplay");
// If we already have a display active, then this request must be denied
sp<IEvsDisplay> pActiveDisplay = mActiveDisplay.promote();
// If we already have a display active, then we need to shut it down so we can
// give exclusive access to the new caller.
sp<EvsDisplay> pActiveDisplay = sActiveDisplay.promote();
if (pActiveDisplay != nullptr) {
ALOGW("Rejecting openDisplay request the display is already in use.");
return nullptr;
} else {
// Create a new display interface and return it
pActiveDisplay = new EvsDisplay();
mActiveDisplay = pActiveDisplay;
ALOGD("Returning new EvsDisplay object %p", pActiveDisplay.get());
return pActiveDisplay;
ALOGW("Killing previous display because of new caller");
closeDisplay(pActiveDisplay);
}
// Create a new display interface and return it
pActiveDisplay = new EvsDisplay();
sActiveDisplay = pActiveDisplay;
ALOGD("Returning new EvsDisplay object %p", pActiveDisplay.get());
return pActiveDisplay;
}
Return<void> EvsEnumerator::closeDisplay(const ::android::sp<IEvsDisplay>& display) {
Return<void> EvsEnumerator::closeDisplay(const ::android::sp<IEvsDisplay>& pDisplay) {
ALOGD("closeDisplay");
// Do we still have a display object we think should be active?
sp<IEvsDisplay> pActiveDisplay = mActiveDisplay.promote();
sp<EvsDisplay> pActiveDisplay = sActiveDisplay.promote();
if (pActiveDisplay == nullptr) {
ALOGE("Ignoring closeDisplay when there is no active display.");
} else if (display != pActiveDisplay) {
ALOGE("Ignoring closeDisplay on a display we didn't issue");
ALOGI("Got %p while active display is %p.", display.get(), pActiveDisplay.get());
ALOGE("Somehow a display is being destroyed when the enumerator didn't know one existed");
} else if (sActiveDisplay != pDisplay) {
ALOGW("Ignoring close of previously orphaned display - why did a client steal?");
} else {
// Drop the active display
mActiveDisplay = nullptr;
pActiveDisplay->forceShutdown();
sActiveDisplay = nullptr;
}
return Void();
}
Return<DisplayState> EvsEnumerator::getDisplayState() {
ALOGD("getDisplayState");
// Do we still have a display object we think should be active?
sp<IEvsDisplay> pActiveDisplay = mActiveDisplay.promote();
sp<IEvsDisplay> pActiveDisplay = sActiveDisplay.promote();
if (pActiveDisplay != nullptr) {
return pActiveDisplay->getDisplayState();
} else {

View file

@ -22,7 +22,6 @@
#include <list>
#include "EvsCamera.h"
namespace android {
namespace hardware {
@ -31,6 +30,11 @@ namespace evs {
namespace V1_0 {
namespace implementation {
class EvsCamera; // from EvsCamera.h
class EvsDisplay; // from EvsDisplay.h
class EvsEnumerator : public IEvsEnumerator {
public:
// Methods from ::android::hardware::automotive::evs::V1_0::IEvsEnumerator follow.
@ -45,14 +49,18 @@ public:
EvsEnumerator();
private:
// NOTE: All members values are static so that all clients operate on the same state
// That is to say, this is effectively a singleton despite the fact that HIDL
// constructs a new instance for each client.
struct CameraRecord {
sp<EvsCamera> pCamera;
bool inUse;
CameraRecord(EvsCamera* p, bool b) : pCamera(p), inUse(b) {}
};
std::list<CameraRecord> mCameraList;
CameraDesc desc;
wp<EvsCamera> activeInstance;
wp<IEvsDisplay> mActiveDisplay; // Weak pointer -> object destructs if client dies
CameraRecord(const char *cameraId) : desc() { desc.cameraId = cameraId; }
};
static std::list<CameraRecord> sCameraList;
static wp<EvsDisplay> sActiveDisplay; // Weak pointer. Object destructs if client dies.
};
} // namespace implementation

View file

@ -17,41 +17,15 @@
package android.hardware.automotive.evs@1.0;
/**
* Bit flags indicating suggested uses for a given EVS camera
*
* The values in the UsageHint bit field provide a generic expression of how a
* given camera is intended to be used. The values for these flags support
* existing use cases, and are used by the default EVS application to select
* appropriate cameras for display based on observed vehicle state (such as
* turn signal activation or selection of reverse gear). When implementing
* their own specialized EVS Applications, OEMs are free to use these flags
* and/or the opaque vendor_flags to drive their own vehicle specific logic.
*/
enum UsageHint : uint32_t {
USAGE_HINT_REVERSE = 0x00000001,
USAGE_HINT_LEFT_TURN = 0x00000002,
USAGE_HINT_RIGHT_TURN = 0x00000004,
};
/**
* Structure describing the basic properties of an EVS camera
*
* The HAL is responsible for filling out this structure for each
* EVS camera in the system. Attention should be given to the field
* of view, direction of view, and location parameters as these may
* be used to (if available) to project overlay graphics into the
* scene by an EVS application.
* Any of these values for which the HAL does not have reasonable values
* should be set to ZERO.
* EVS camera in the system.
*/
struct CameraDesc {
string cameraId;
bitfield<UsageHint> hints; // Mask of usage hints
uint32_t vendorFlags; // Opaque value from driver
uint32_t defaultHorResolution; // Units of pixels
uint32_t defaultVerResolution; // Units of pixels
string cameraId;
uint32_t vendorFlags; // Opaque value from driver
};
@ -65,9 +39,7 @@ struct CameraDesc {
*/
struct DisplayDesc {
string displayId;
uint32_t vendorFlags; // Opaque value from driver
uint32_t defaultHorResolution; // Units of pixels
uint32_t defaultVerResolution; // Units of pixels
uint32_t vendorFlags; // Opaque value from driver
};
@ -86,7 +58,8 @@ struct DisplayDesc {
struct BufferDesc {
uint32_t width; // Units of pixels
uint32_t height; // Units of pixels
uint32_t stride; // Units of bytes
uint32_t stride; // Units of pixels to match gralloc
uint32_t pixelSize; // Units of bytes
uint32_t format; // May contain values from android_pixel_format_t
uint32_t usage; // May contain values from from Gralloc.h
uint32_t bufferId; // Opaque value from driver
@ -108,6 +81,7 @@ enum DisplayState : uint32_t {
NOT_VISIBLE, // Display is inhibited
VISIBLE_ON_NEXT_FRAME, // Will become visible with next frame
VISIBLE, // Display is currently active
DEAD, // Driver is in an undefined state. Interface should be closed.
NUM_STATES // Must be last
};
@ -118,5 +92,6 @@ enum EvsResult : uint32_t {
INVALID_ARG,
STREAM_ALREADY_RUNNING,
BUFFER_NOT_AVAILABLE,
OWNERSHIP_LOST,
UNDERLYING_SERVICE_ERROR,
};