blob: ec29e39059b1be56e9a1ab765d34d9bb1a380e2a [file] [log] [blame]
/*
* Copyright (C) 2016 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.
*/
#define LOG_TAG "EVSAPP"
#include "EvsStateControl.h"
#include <stdio.h>
#include <string.h>
#include <log/log.h>
// TODO: Seems like it'd be nice if the Vehicle HAL provided such helpers (but how & where?)
inline constexpr VehiclePropertyType getPropType(VehicleProperty prop) {
return static_cast<VehiclePropertyType>(
static_cast<int32_t>(prop)
& static_cast<int32_t>(VehiclePropertyType::MASK));
}
EvsStateControl::EvsStateControl(android::sp <IVehicle> pVnet,
android::sp <IEvsEnumerator> pEvs,
android::sp <IEvsDisplay> pDisplay,
const ConfigManager& config) :
mVehicle(pVnet),
mEvs(pEvs),
mDisplay(pDisplay),
mCurrentState(OFF) {
// Initialize the property value containers we'll be updating (they'll be zeroed by default)
static_assert(getPropType(VehicleProperty::GEAR_SELECTION) == VehiclePropertyType::INT32,
"Unexpected type for GEAR_SELECTION property");
static_assert(getPropType(VehicleProperty::TURN_SIGNAL_STATE) == VehiclePropertyType::INT32,
"Unexpected type for TURN_SIGNAL_STATE property");
mGearValue.prop = static_cast<int32_t>(VehicleProperty::GEAR_SELECTION);
mTurnSignalValue.prop = static_cast<int32_t>(VehicleProperty::TURN_SIGNAL_STATE);
// Build our set of cameras for the states we support
ALOGD("Requesting camera list");
mEvs->getCameraList([this, &config]
(hidl_vec<CameraDesc> cameraList) {
ALOGI("Camera list callback received %zu cameras",
cameraList.size());
for (auto&& cam: cameraList) {
ALOGD("Found camera %s", cam.cameraId.c_str());
bool cameraConfigFound = false;
// Check our configuration for information about this camera
// Note that a camera can have a compound function string
// such that a camera can be "right/reverse" and be used for both.
for (auto&& info: config.getCameras()) {
if (cam.cameraId == info.cameraId) {
// We found a match!
if (info.function.find("reverse") != std::string::npos) {
mCameraInfo[State::REVERSE] = info;
}
if (info.function.find("right") != std::string::npos) {
mCameraInfo[State::RIGHT] = info;
}
if (info.function.find("left") != std::string::npos) {
mCameraInfo[State::LEFT] = info;
}
cameraConfigFound = true;
break;
}
}
if (!cameraConfigFound) {
ALOGW("No config information for hardware camera %s",
cam.cameraId.c_str());
}
}
}
);
ALOGD("State controller ready");
}
bool EvsStateControl::configureForVehicleState() {
ALOGD("configureForVehicleState");
static int32_t sDummyGear = int32_t(VehicleGear::GEAR_REVERSE);
static int32_t sDummySignal = int32_t(VehicleTurnSignal::NONE);
if (mVehicle != nullptr) {
// Query the car state
if (invokeGet(&mGearValue) != StatusCode::OK) {
ALOGE("GEAR_SELECTION not available from vehicle. Exiting.");
return false;
}
if (invokeGet(&mTurnSignalValue) != StatusCode::OK) {
// Silently treat missing turn signal state as no turn signal active
mTurnSignalValue.value.int32Values.setToExternal(&sDummySignal, 1);
}
} else {
// While testing without a vehicle, behave as if we're in reverse for the first 20 seconds
static const int kShowTime = 20; // seconds
// See if it's time to turn off the default reverse camera
static std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
if (std::chrono::duration_cast<std::chrono::seconds>(now - start).count() > kShowTime) {
// Switch to drive (which should turn off the reverse camera)
sDummyGear = int32_t(VehicleGear::GEAR_DRIVE);
}
// Build the dummy vehicle state values (treating single values as 1 element vectors)
mGearValue.value.int32Values.setToExternal(&sDummyGear, 1);
mTurnSignalValue.value.int32Values.setToExternal(&sDummySignal, 1);
}
// Choose our desired EVS state based on the current car state
State desiredState = OFF;
if (mGearValue.value.int32Values[0] == int32_t(VehicleGear::GEAR_REVERSE)) {
desiredState = REVERSE;
} else if (mTurnSignalValue.value.int32Values[0] == int32_t(VehicleTurnSignal::RIGHT)) {
desiredState = RIGHT;
} else if (mTurnSignalValue.value.int32Values[0] == int32_t(VehicleTurnSignal::LEFT)) {
desiredState = LEFT;
}
// Apply the desire state
ALOGV("Selected state %d.", desiredState);
configureEvsPipeline(desiredState);
// Operation was successful
return true;
}
StatusCode EvsStateControl::invokeGet(VehiclePropValue *pRequestedPropValue) {
ALOGD("invokeGet");
StatusCode status = StatusCode::TRY_AGAIN;
bool called = false;
// Call the Vehicle HAL, which will block until the callback is complete
mVehicle->get(*pRequestedPropValue,
[pRequestedPropValue, &status, &called]
(StatusCode s, const VehiclePropValue& v) {
status = s;
*pRequestedPropValue = v;
called = true;
}
);
// This should be true as long as the get call is block as it should
// TODO: Once we've got some milage on this code and the underlying HIDL services,
// we should remove this belt-and-suspenders check for correct operation as unnecessary.
if (!called) {
ALOGE("VehicleNetwork query did not run as expected.");
}
return status;
}
bool EvsStateControl::configureEvsPipeline(State desiredState) {
ALOGD("configureEvsPipeline");
if (mCurrentState == desiredState) {
// Nothing to do here...
return true;
}
// See if we actually have to change cameras
if (mCameraInfo[mCurrentState].cameraId != mCameraInfo[desiredState].cameraId) {
ALOGI("Camera change required");
ALOGD(" Current cameraId (%d) = %s", mCurrentState,
mCameraInfo[mCurrentState].cameraId.c_str());
ALOGD(" Desired cameraId (%d) = %s", desiredState,
mCameraInfo[desiredState].cameraId.c_str());
// Yup, we need to change cameras, so close the previous one, if necessary.
if (mCurrentCamera != nullptr) {
mCurrentStreamHandler->blockingStopStream();
mCurrentStreamHandler = nullptr;
mCurrentCamera = nullptr;
}
// Now do we need a new camera?
if (!mCameraInfo[desiredState].cameraId.empty()) {
// Need a new camera, so open it
ALOGD("Open camera %s", mCameraInfo[desiredState].cameraId.c_str());
mCurrentCamera = mEvs->openCamera(mCameraInfo[desiredState].cameraId);
// If we didn't get the camera we asked for, we need to bail out and try again later
if (mCurrentCamera == nullptr) {
ALOGE("Failed to open EVS camera. Skipping state change.");
return false;
}
}
// Now set the display state based on whether we have a camera feed to show
if (mCurrentCamera == nullptr) {
ALOGD("Turning off the display");
mDisplay->setDisplayState(DisplayState::NOT_VISIBLE);
} else {
// Create the stream handler object to receive and forward the video frames
mCurrentStreamHandler = new StreamHandler(mCurrentCamera, mDisplay);
// Start the camera stream
ALOGD("Starting camera stream");
mCurrentStreamHandler->startStream();
// Activate the display
ALOGD("Arming the display");
mDisplay->setDisplayState(DisplayState::VISIBLE_ON_NEXT_FRAME);
}
}
// Record our current state
ALOGI("Activated state %d.", desiredState);
mCurrentState = desiredState;
return true;
}