Merge "Check result of registerAsService in dumpstate." into oc-dev
diff --git a/audio/2.0/default/Device.cpp b/audio/2.0/default/Device.cpp
index 8a51cd7..5ced0bc 100644
--- a/audio/2.0/default/Device.cpp
+++ b/audio/2.0/default/Device.cpp
@@ -59,6 +59,14 @@
}
}
+void Device::closeInputStream(audio_stream_in_t* stream) {
+ mDevice->close_input_stream(mDevice, stream);
+}
+
+void Device::closeOutputStream(audio_stream_out_t* stream) {
+ mDevice->close_output_stream(mDevice, stream);
+}
+
char* Device::halGetParameters(const char* keys) {
return mDevice->get_parameters(mDevice, keys);
}
@@ -160,7 +168,7 @@
ALOGV("open_output_stream status %d stream %p", status, halStream);
sp<IStreamOut> streamOut;
if (status == OK) {
- streamOut = new StreamOut(mDevice, halStream);
+ streamOut = new StreamOut(this, halStream);
}
AudioConfig suggestedConfig;
HidlUtils::audioConfigFromHal(halConfig, &suggestedConfig);
@@ -196,7 +204,7 @@
ALOGV("open_input_stream status %d stream %p", status, halStream);
sp<IStreamIn> streamIn;
if (status == OK) {
- streamIn = new StreamIn(mDevice, halStream);
+ streamIn = new StreamIn(this, halStream);
}
AudioConfig suggestedConfig;
HidlUtils::audioConfigFromHal(halConfig, &suggestedConfig);
diff --git a/audio/2.0/default/Device.h b/audio/2.0/default/Device.h
index 46177fc..7738361 100644
--- a/audio/2.0/default/Device.h
+++ b/audio/2.0/default/Device.h
@@ -98,6 +98,8 @@
// Utility methods for extending interfaces.
Result analyzeStatus(const char* funcName, int status);
+ void closeInputStream(audio_stream_in_t* stream);
+ void closeOutputStream(audio_stream_out_t* stream);
audio_hw_device_t* device() const { return mDevice; }
private:
diff --git a/audio/2.0/default/StreamIn.cpp b/audio/2.0/default/StreamIn.cpp
index b641e82..2745607 100644
--- a/audio/2.0/default/StreamIn.cpp
+++ b/audio/2.0/default/StreamIn.cpp
@@ -135,7 +135,7 @@
} // namespace
-StreamIn::StreamIn(audio_hw_device_t* device, audio_stream_in_t* stream)
+StreamIn::StreamIn(const sp<Device>& device, audio_stream_in_t* stream)
: mIsClosed(false), mDevice(device), mStream(stream),
mStreamCommon(new Stream(&stream->common)),
mStreamMmap(new StreamMmap<audio_stream_in_t>(stream)),
@@ -154,9 +154,8 @@
status_t status = EventFlag::deleteEventFlag(&mEfGroup);
ALOGE_IF(status, "read MQ event flag deletion error: %s", strerror(-status));
}
- mDevice->close_input_stream(mDevice, mStream);
+ mDevice->closeInputStream(mStream);
mStream = nullptr;
- mDevice = nullptr;
}
// Methods from ::android::hardware::audio::V2_0::IStream follow.
diff --git a/audio/2.0/default/StreamIn.h b/audio/2.0/default/StreamIn.h
index b867387..950d68f 100644
--- a/audio/2.0/default/StreamIn.h
+++ b/audio/2.0/default/StreamIn.h
@@ -27,6 +27,7 @@
#include <hidl/Status.h>
#include <utils/Thread.h>
+#include "Device.h"
#include "Stream.h"
namespace android {
@@ -55,7 +56,7 @@
typedef MessageQueue<uint8_t, kSynchronizedReadWrite> DataMQ;
typedef MessageQueue<ReadStatus, kSynchronizedReadWrite> StatusMQ;
- StreamIn(audio_hw_device_t* device, audio_stream_in_t* stream);
+ StreamIn(const sp<Device>& device, audio_stream_in_t* stream);
// Methods from ::android::hardware::audio::V2_0::IStream follow.
Return<uint64_t> getFrameSize() override;
@@ -101,10 +102,10 @@
private:
bool mIsClosed;
- audio_hw_device_t *mDevice;
+ const sp<Device> mDevice;
audio_stream_in_t *mStream;
- sp<Stream> mStreamCommon;
- sp<StreamMmap<audio_stream_in_t>> mStreamMmap;
+ const sp<Stream> mStreamCommon;
+ const sp<StreamMmap<audio_stream_in_t>> mStreamMmap;
std::unique_ptr<CommandMQ> mCommandMQ;
std::unique_ptr<DataMQ> mDataMQ;
std::unique_ptr<StatusMQ> mStatusMQ;
diff --git a/audio/2.0/default/StreamOut.cpp b/audio/2.0/default/StreamOut.cpp
index d820f3c..88045a0 100644
--- a/audio/2.0/default/StreamOut.cpp
+++ b/audio/2.0/default/StreamOut.cpp
@@ -135,7 +135,7 @@
} // namespace
-StreamOut::StreamOut(audio_hw_device_t* device, audio_stream_out_t* stream)
+StreamOut::StreamOut(const sp<Device>& device, audio_stream_out_t* stream)
: mIsClosed(false), mDevice(device), mStream(stream),
mStreamCommon(new Stream(&stream->common)),
mStreamMmap(new StreamMmap<audio_stream_out_t>(stream)),
@@ -155,9 +155,8 @@
ALOGE_IF(status, "write MQ event flag deletion error: %s", strerror(-status));
}
mCallback.clear();
- mDevice->close_output_stream(mDevice, mStream);
+ mDevice->closeOutputStream(mStream);
mStream = nullptr;
- mDevice = nullptr;
}
// Methods from ::android::hardware::audio::V2_0::IStream follow.
diff --git a/audio/2.0/default/StreamOut.h b/audio/2.0/default/StreamOut.h
index bbe64a1..99352bc 100644
--- a/audio/2.0/default/StreamOut.h
+++ b/audio/2.0/default/StreamOut.h
@@ -27,6 +27,7 @@
#include <fmq/MessageQueue.h>
#include <utils/Thread.h>
+#include "Device.h"
#include "Stream.h"
namespace android {
@@ -57,7 +58,7 @@
typedef MessageQueue<uint8_t, kSynchronizedReadWrite> DataMQ;
typedef MessageQueue<WriteStatus, kSynchronizedReadWrite> StatusMQ;
- StreamOut(audio_hw_device_t* device, audio_stream_out_t* stream);
+ StreamOut(const sp<Device>& device, audio_stream_out_t* stream);
// Methods from ::android::hardware::audio::V2_0::IStream follow.
Return<uint64_t> getFrameSize() override;
@@ -112,10 +113,10 @@
private:
bool mIsClosed;
- audio_hw_device_t *mDevice;
+ const sp<Device> mDevice;
audio_stream_out_t *mStream;
- sp<Stream> mStreamCommon;
- sp<StreamMmap<audio_stream_out_t>> mStreamMmap;
+ const sp<Stream> mStreamCommon;
+ const sp<StreamMmap<audio_stream_out_t>> mStreamMmap;
sp<IStreamOutCallback> mCallback;
std::unique_ptr<CommandMQ> mCommandMQ;
std::unique_ptr<DataMQ> mDataMQ;
diff --git a/automotive/evs/1.0/IEvsCamera.hal b/automotive/evs/1.0/IEvsCamera.hal
index 1b55d1f..dbcaf92 100644
--- a/automotive/evs/1.0/IEvsCamera.hal
+++ b/automotive/evs/1.0/IEvsCamera.hal
@@ -28,10 +28,10 @@
/**
* 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.
diff --git a/automotive/evs/1.0/IEvsDisplay.hal b/automotive/evs/1.0/IEvsDisplay.hal
index bbad428..12541f3 100644
--- a/automotive/evs/1.0/IEvsDisplay.hal
+++ b/automotive/evs/1.0/IEvsDisplay.hal
@@ -27,7 +27,7 @@
/**
* 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);
diff --git a/automotive/evs/1.0/IEvsEnumerator.hal b/automotive/evs/1.0/IEvsEnumerator.hal
index 334430b..98d117a 100644
--- a/automotive/evs/1.0/IEvsEnumerator.hal
+++ b/automotive/evs/1.0/IEvsEnumerator.hal
@@ -31,14 +31,14 @@
*/
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 @@
* 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 @@
* 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);
diff --git a/automotive/evs/1.0/default/Android.bp b/automotive/evs/1.0/default/Android.bp
index 8b214e3..2574e86 100644
--- a/automotive/evs/1.0/default/Android.bp
+++ b/automotive/evs/1.0/default/Android.bp
@@ -23,4 +23,9 @@
"liblog",
"libutils",
],
+
+ cflags: [
+ "-O0",
+ "-g",
+ ],
}
diff --git a/automotive/evs/1.0/default/EvsCamera.cpp b/automotive/evs/1.0/default/EvsCamera.cpp
index c4436ee..148b796 100644
--- a/automotive/evs/1.0/default/EvsCamera.cpp
+++ b/automotive/evs/1.0/default/EvsCamera.cpp
@@ -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 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 @@
// 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() {
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");
+Return<void> EvsCamera::getCameraInfo(getCameraInfo_cb _hidl_cb) {
+ ALOGD("getCameraInfo");
- id_cb(mDescription.cameraId);
-
+ // Send back our self description
+ _hidl_cb(mDescription);
return Void();
}
@@ -111,6 +118,12 @@
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 @@
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 @@
lock.lock();
mStreamState = STOPPED;
+ mStream = nullptr;
ALOGD("Stream marked STOPPED.");
}
@@ -232,6 +251,12 @@
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 @@
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 @@
}
}
- // 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
diff --git a/automotive/evs/1.0/default/EvsCamera.h b/automotive/evs/1.0/default/EvsCamera.h
index ee91ca4..ff6eb39 100644
--- a/automotive/evs/1.0/default/EvsCamera.h
+++ b/automotive/evs/1.0/default/EvsCamera.h
@@ -32,54 +32,50 @@
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 @@
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;
};
diff --git a/automotive/evs/1.0/default/EvsDisplay.cpp b/automotive/evs/1.0/default/EvsDisplay.cpp
index a1a76d0..9ad332a 100644
--- a/automotive/evs/1.0/default/EvsDisplay.cpp
+++ b/automotive/evs/1.0/default/EvsDisplay.cpp
@@ -30,12 +30,6 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
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 @@
// 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
diff --git a/automotive/evs/1.0/default/EvsDisplay.h b/automotive/evs/1.0/default/EvsDisplay.h
index fcf4a06..ebd6446 100644
--- a/automotive/evs/1.0/default/EvsDisplay.h
+++ b/automotive/evs/1.0/default/EvsDisplay.h
@@ -27,6 +27,7 @@
namespace V1_0 {
namespace implementation {
+
class EvsDisplay : public IEvsDisplay {
public:
// Methods from ::android::hardware::automotive::evs::V1_0::IEvsDisplay follow.
@@ -40,6 +41,8 @@
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
diff --git a/automotive/evs/1.0/default/EvsEnumerator.cpp b/automotive/evs/1.0/default/EvsEnumerator.cpp
index e54f699..731e21b 100644
--- a/automotive/evs/1.0/default/EvsEnumerator.cpp
+++ b/automotive/evs/1.0/default/EvsEnumerator.cpp
@@ -28,33 +28,36 @@
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();
}
+
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 {
diff --git a/automotive/evs/1.0/default/EvsEnumerator.h b/automotive/evs/1.0/default/EvsEnumerator.h
index 3d6e264..6b70f9b 100644
--- a/automotive/evs/1.0/default/EvsEnumerator.h
+++ b/automotive/evs/1.0/default/EvsEnumerator.h
@@ -22,7 +22,6 @@
#include <list>
-#include "EvsCamera.h"
namespace android {
namespace hardware {
@@ -31,6 +30,11 @@
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 @@
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
diff --git a/automotive/evs/1.0/types.hal b/automotive/evs/1.0/types.hal
index 0ce39d1..7cebf6d 100644
--- a/automotive/evs/1.0/types.hal
+++ b/automotive/evs/1.0/types.hal
@@ -18,40 +18,14 @@
/**
- * 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 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 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 @@
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 @@
INVALID_ARG,
STREAM_ALREADY_RUNNING,
BUFFER_NOT_AVAILABLE,
+ OWNERSHIP_LOST,
UNDERLYING_SERVICE_ERROR,
};
diff --git a/drm/1.0/default/DrmPlugin.cpp b/drm/1.0/default/DrmPlugin.cpp
index 6f51e0e..c7428a5 100644
--- a/drm/1.0/default/DrmPlugin.cpp
+++ b/drm/1.0/default/DrmPlugin.cpp
@@ -77,7 +77,7 @@
android::DrmPlugin::KeyRequestType legacyRequestType =
android::DrmPlugin::kKeyRequestType_Unknown;
- status_t status = mLegacyPlugin->getKeyRequest(toVector(scope),
+ status = mLegacyPlugin->getKeyRequest(toVector(scope),
toVector(initData), String8(mimeType), legacyKeyType,
legacyOptionalParameters, legacyRequest, defaultUrl,
&legacyRequestType);
@@ -93,7 +93,7 @@
requestType = KeyRequestType::RELEASE;
break;
case android::DrmPlugin::kKeyRequestType_Unknown:
- status = android::BAD_VALUE;
+ requestType = KeyRequestType::UNKNOWN;
break;
}
}
diff --git a/radio/1.0/vts/functional/Android.bp b/radio/1.0/vts/functional/Android.bp
index 24e3926..7808de1 100644
--- a/radio/1.0/vts/functional/Android.bp
+++ b/radio/1.0/vts/functional/Android.bp
@@ -44,3 +44,27 @@
"-g",
],
}
+
+cc_test {
+ name: "VtsHalSapV1_0TargetTest",
+ defaults: ["hidl_defaults"],
+ srcs: ["sap_callback.cpp",
+ "sap_hidl_hal_api.cpp",
+ "sap_hidl_hal_test.cpp",
+ "VtsHalSapV1_0TargetTest.cpp"],
+ shared_libs: [
+ "libbase",
+ "liblog",
+ "libcutils",
+ "libhidlbase",
+ "libhidltransport",
+ "libnativehelper",
+ "libutils",
+ "android.hardware.radio@1.0",
+ ],
+ static_libs: ["VtsHalHidlTargetTestBase"],
+ cflags: [
+ "-O0",
+ "-g",
+ ],
+}
diff --git a/radio/1.0/vts/functional/VtsHalSapV1_0TargetTest.cpp b/radio/1.0/vts/functional/VtsHalSapV1_0TargetTest.cpp
new file mode 100644
index 0000000..f902588
--- /dev/null
+++ b/radio/1.0/vts/functional/VtsHalSapV1_0TargetTest.cpp
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2017 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<sap_hidl_hal_utils.h>
+
+int main(int argc, char** argv) {
+ // Add Sim-access Profile Hidl Environment
+ ::testing::AddGlobalTestEnvironment(new SapHidlEnvironment);
+ ::testing::InitGoogleTest(&argc, argv);
+
+ int status = RUN_ALL_TESTS();
+ LOG(INFO) << "Test result = " << status;
+
+ return status;
+}
diff --git a/radio/1.0/vts/functional/sap_callback.cpp b/radio/1.0/vts/functional/sap_callback.cpp
new file mode 100644
index 0000000..563d066
--- /dev/null
+++ b/radio/1.0/vts/functional/sap_callback.cpp
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2017 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<sap_hidl_hal_utils.h>
+
+SapCallback::SapCallback(SapHidlTest& parent) : parent(parent) {
+}
+
+Return<void> SapCallback::connectResponse(int32_t token, SapConnectRsp /*sapConnectRsp*/,
+ int32_t /*maxMsgSize*/) {
+ sapResponseToken = token;
+ parent.notify();
+ return Void();
+}
+
+Return<void> SapCallback::disconnectResponse(int32_t token) {
+ sapResponseToken = token;
+ parent.notify();
+ return Void();
+}
+
+Return<void> SapCallback::disconnectIndication(int32_t /*token*/,
+ SapDisconnectType /*disconnectType*/) {
+ return Void();
+}
+
+Return<void> SapCallback::apduResponse(int32_t token, SapResultCode resultCode,
+ const ::android::hardware::hidl_vec<uint8_t>& /*apduRsp*/) {
+ sapResponseToken = token;
+ sapResultCode = resultCode;
+ parent.notify();
+ return Void();
+}
+
+Return<void> SapCallback::transferAtrResponse(int32_t token, SapResultCode resultCode,
+ const ::android::hardware::hidl_vec<uint8_t>& /*atr*/) {
+ sapResponseToken = token;
+ sapResultCode = resultCode;
+ parent.notify();
+ return Void();
+}
+
+Return<void> SapCallback::powerResponse(int32_t token, SapResultCode resultCode) {
+ sapResponseToken = token;
+ sapResultCode = resultCode;
+ parent.notify();
+ return Void();
+}
+
+Return<void> SapCallback::resetSimResponse(int32_t token, SapResultCode resultCode) {
+ sapResponseToken = token;
+ sapResultCode = resultCode;
+ parent.notify();
+ return Void();
+}
+
+Return<void> SapCallback::statusIndication(int32_t /*token*/, SapStatus /*status*/) {
+ return Void();
+}
+
+Return<void> SapCallback::transferCardReaderStatusResponse(int32_t token,
+ SapResultCode resultCode, int32_t /*cardReaderStatus*/) {
+ sapResponseToken = token;
+ sapResultCode = resultCode;
+ parent.notify();
+ return Void();
+}
+
+Return<void> SapCallback::errorResponse(int32_t /*token*/) {
+ return Void();
+}
+
+Return<void> SapCallback::transferProtocolResponse(int32_t token,
+ SapResultCode resultCode) {
+ sapResponseToken = token;
+ sapResultCode = resultCode;
+ parent.notify();
+ return Void();
+}
diff --git a/radio/1.0/vts/functional/sap_hidl_hal_api.cpp b/radio/1.0/vts/functional/sap_hidl_hal_api.cpp
new file mode 100644
index 0000000..e806bd7
--- /dev/null
+++ b/radio/1.0/vts/functional/sap_hidl_hal_api.cpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2017 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<sap_hidl_hal_utils.h>
+
+/*
+ * Test ISap.connectReq() for the response returned.
+ */
+TEST_F(SapHidlTest, connectReq) {
+ int32_t token = 0;
+ int32_t maxMsgSize = 100;
+
+ sap->connectReq(++token, maxMsgSize);
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(sapCb->sapResponseToken, token);
+}
+
+/*
+ * Test IRadio.disconnectReq() for the response returned
+ */
+TEST_F(SapHidlTest, disconnectReq) {
+ int32_t token = 0;
+
+ sap->disconnectReq(++token);
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(sapCb->sapResponseToken, token);
+}
+
+/*
+ * Test IRadio.apduReq() for the response returned.
+ */
+TEST_F(SapHidlTest, apduReq) {
+ int32_t token = 0;
+ SapApduType sapApduType = SapApduType::APDU;
+ android::hardware::hidl_vec<uint8_t> command = {};
+
+ sap->apduReq(++token, sapApduType, command);
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(sapCb->sapResponseToken, token);
+
+ ASSERT_TRUE(SapResultCode::CARD_NOT_ACCESSSIBLE == sapCb->sapResultCode ||
+ SapResultCode::CARD_ALREADY_POWERED_OFF == sapCb->sapResultCode ||
+ SapResultCode::CARD_REMOVED == sapCb->sapResultCode);
+}
+
+/*
+ * Test IRadio.transferAtrReq() for the response returned.
+ */
+TEST_F(SapHidlTest, transferAtrReq) {
+ int32_t token = 0;
+
+ sap->transferAtrReq(++token);
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(sapCb->sapResponseToken, token);
+
+ ASSERT_TRUE(SapResultCode::DATA_NOT_AVAILABLE == sapCb->sapResultCode ||
+ SapResultCode::CARD_ALREADY_POWERED_OFF == sapCb->sapResultCode ||
+ SapResultCode::CARD_REMOVED == sapCb->sapResultCode);
+}
+
+/*
+ * Test IRadio.powerReq() for the response returned.
+ */
+TEST_F(SapHidlTest, powerReq) {
+ int32_t token = 0;
+ bool state = true;
+
+ sap->powerReq(++token, state);
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(sapCb->sapResponseToken, token);
+
+ ASSERT_TRUE(SapResultCode::CARD_NOT_ACCESSSIBLE == sapCb->sapResultCode ||
+ SapResultCode::CARD_ALREADY_POWERED_OFF == sapCb->sapResultCode ||
+ SapResultCode::CARD_REMOVED == sapCb->sapResultCode ||
+ SapResultCode::CARD_ALREADY_POWERED_ON == sapCb->sapResultCode);
+}
+
+/*
+ * Test IRadio.resetSimReq() for the response returned.
+ */
+TEST_F(SapHidlTest, resetSimReq) {
+ int32_t token = 0;
+
+ sap->resetSimReq(++token);
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(sapCb->sapResponseToken, token);
+
+ ASSERT_TRUE(SapResultCode::CARD_NOT_ACCESSSIBLE == sapCb->sapResultCode ||
+ SapResultCode::CARD_ALREADY_POWERED_OFF == sapCb->sapResultCode ||
+ SapResultCode::CARD_REMOVED == sapCb->sapResultCode);
+}
+
+/*
+ * Test IRadio.transferCardReaderStatusReq() for the response returned.
+ */
+TEST_F(SapHidlTest, transferCardReaderStatusReq) {
+ int32_t token = 0;
+
+ sap->transferCardReaderStatusReq(++token);
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(sapCb->sapResponseToken, token);
+
+ EXPECT_EQ(SapResultCode::DATA_NOT_AVAILABLE, sapCb->sapResultCode);
+}
+
+/*
+ * Test IRadio.setTransferProtocolReq() for the response returned.
+ */
+TEST_F(SapHidlTest, setTransferProtocolReq) {
+ int32_t token = 0;
+ SapTransferProtocol sapTransferProtocol = SapTransferProtocol::T0;
+
+ sap->setTransferProtocolReq(++token, sapTransferProtocol);
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(sapCb->sapResponseToken, token);
+
+ EXPECT_EQ(SapResultCode::NOT_SUPPORTED, sapCb->sapResultCode);
+}
diff --git a/radio/1.0/vts/functional/sap_hidl_hal_test.cpp b/radio/1.0/vts/functional/sap_hidl_hal_test.cpp
new file mode 100644
index 0000000..a67c5b6
--- /dev/null
+++ b/radio/1.0/vts/functional/sap_hidl_hal_test.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 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<sap_hidl_hal_utils.h>
+
+void SapHidlTest::SetUp() {
+ sap = ::testing::VtsHalHidlTargetTestBase::getService<ISap>(hidl_string("sap_uim_socket1"));
+ ASSERT_NE(sap, nullptr);
+
+ sapCb = new SapCallback(*this);
+ ASSERT_NE(sapCb, nullptr);
+
+ count = 0;
+
+ sap->setCallback(sapCb);
+}
+
+void SapHidlTest::TearDown() {
+}
+
+void SapHidlTest::notify() {
+ std::unique_lock<std::mutex> lock(mtx);
+ count++;
+ cv.notify_one();
+}
+
+std::cv_status SapHidlTest::wait() {
+ std::unique_lock<std::mutex> lock(mtx);
+
+ std::cv_status status = std::cv_status::no_timeout;
+ auto now = std::chrono::system_clock::now();
+ while (count == 0) {
+ status = cv.wait_until(lock, now + std::chrono::seconds(TIMEOUT_PERIOD));
+ if (status == std::cv_status::timeout) {
+ return status;
+ }
+ }
+ count--;
+ return status;
+}
+
diff --git a/radio/1.0/vts/functional/sap_hidl_hal_utils.h b/radio/1.0/vts/functional/sap_hidl_hal_utils.h
new file mode 100644
index 0000000..e3efa50
--- /dev/null
+++ b/radio/1.0/vts/functional/sap_hidl_hal_utils.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2017 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 <android-base/logging.h>
+
+#include <VtsHalHidlTargetTestBase.h>
+#include <chrono>
+#include <condition_variable>
+#include <mutex>
+
+#include <android/hardware/radio/1.0/ISap.h>
+#include <android/hardware/radio/1.0/ISapCallback.h>
+#include <android/hardware/radio/1.0/types.h>
+
+using namespace ::android::hardware::radio::V1_0;
+
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+
+#define TIMEOUT_PERIOD 40
+
+class SapHidlTest;
+
+/* Callback class for sap response */
+class SapCallback : public ISapCallback {
+private:
+ SapHidlTest& parent;
+
+public:
+ SapResultCode sapResultCode;
+ int32_t sapResponseToken;
+
+ SapCallback(SapHidlTest& parent);
+
+ virtual ~SapCallback() = default;
+
+ Return<void> connectResponse(int32_t token, SapConnectRsp sapConnectRsp, int32_t maxMsgSize);
+
+ Return<void> disconnectResponse(int32_t token);
+
+ Return<void> disconnectIndication(int32_t token, SapDisconnectType disconnectType);
+
+ Return<void> apduResponse(int32_t token, SapResultCode resultCode,
+ const ::android::hardware::hidl_vec<uint8_t>& apduRsp);
+
+ Return<void> transferAtrResponse(int32_t token, SapResultCode resultCode,
+ const ::android::hardware::hidl_vec<uint8_t>& atr);
+
+ Return<void> powerResponse(int32_t token, SapResultCode resultCode);
+
+ Return<void> resetSimResponse(int32_t token, SapResultCode resultCode);
+
+ Return<void> statusIndication(int32_t token, SapStatus status);
+
+ Return<void> transferCardReaderStatusResponse(int32_t token, SapResultCode resultCode,
+ int32_t cardReaderStatus);
+
+ Return<void> errorResponse(int32_t token);
+
+ Return<void> transferProtocolResponse(int32_t token, SapResultCode resultCode);
+};
+
+// The main test class for Sap HIDL.
+class SapHidlTest : public ::testing::VtsHalHidlTargetTestBase {
+private:
+ std::mutex mtx;
+ std::condition_variable cv;
+ int count;
+
+public:
+ virtual void SetUp() override;
+
+ virtual void TearDown() override;
+
+ /* Used as a mechanism to inform the test about data/event callback */
+ void notify();
+
+ /* Test code calls this function to wait for response */
+ std::cv_status wait();
+
+ /* Sap service */
+ sp<ISap> sap;
+
+ /* Sap Callback object */
+ sp<SapCallback> sapCb;
+};
+
+// A class for test environment setup
+class SapHidlEnvironment : public ::testing::Environment {
+public:
+ virtual void SetUp() {}
+ virtual void TearDown() {}
+};