Home | History | Annotate | Download | only in app
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 #include "EvsStateControl.h"
     17 #include "RenderDirectView.h"
     18 #include "RenderTopView.h"
     19 
     20 #include <stdio.h>
     21 #include <string.h>
     22 
     23 #include <log/log.h>
     24 
     25 
     26 // TODO:  Seems like it'd be nice if the Vehicle HAL provided such helpers (but how & where?)
     27 inline constexpr VehiclePropertyType getPropType(VehicleProperty prop) {
     28     return static_cast<VehiclePropertyType>(
     29             static_cast<int32_t>(prop)
     30             & static_cast<int32_t>(VehiclePropertyType::MASK));
     31 }
     32 
     33 
     34 EvsStateControl::EvsStateControl(android::sp <IVehicle>       pVnet,
     35                                  android::sp <IEvsEnumerator> pEvs,
     36                                  android::sp <IEvsDisplay>    pDisplay,
     37                                  const ConfigManager&         config) :
     38     mVehicle(pVnet),
     39     mEvs(pEvs),
     40     mDisplay(pDisplay),
     41     mConfig(config),
     42     mCurrentState(OFF) {
     43 
     44     // Initialize the property value containers we'll be updating (they'll be zeroed by default)
     45     static_assert(getPropType(VehicleProperty::GEAR_SELECTION) == VehiclePropertyType::INT32,
     46                   "Unexpected type for GEAR_SELECTION property");
     47     static_assert(getPropType(VehicleProperty::TURN_SIGNAL_STATE) == VehiclePropertyType::INT32,
     48                   "Unexpected type for TURN_SIGNAL_STATE property");
     49 
     50     mGearValue.prop       = static_cast<int32_t>(VehicleProperty::GEAR_SELECTION);
     51     mTurnSignalValue.prop = static_cast<int32_t>(VehicleProperty::TURN_SIGNAL_STATE);
     52 
     53 #if 0 // This way we only ever deal with cameras which exist in the system
     54     // Build our set of cameras for the states we support
     55     ALOGD("Requesting camera list");
     56     mEvs->getCameraList([this, &config](hidl_vec<CameraDesc> cameraList) {
     57                             ALOGI("Camera list callback received %zu cameras",
     58                                   cameraList.size());
     59                             for (auto&& cam: cameraList) {
     60                                 ALOGD("Found camera %s", cam.cameraId.c_str());
     61                                 bool cameraConfigFound = false;
     62 
     63                                 // Check our configuration for information about this camera
     64                                 // Note that a camera can have a compound function string
     65                                 // such that a camera can be "right/reverse" and be used for both.
     66                                 // If more than one camera is listed for a given function, we'll
     67                                 // list all of them and let the UX/rendering logic use one, some
     68                                 // or all of them as appropriate.
     69                                 for (auto&& info: config.getCameras()) {
     70                                     if (cam.cameraId == info.cameraId) {
     71                                         // We found a match!
     72                                         if (info.function.find("reverse") != std::string::npos) {
     73                                             mCameraList[State::REVERSE].push_back(info);
     74                                         }
     75                                         if (info.function.find("right") != std::string::npos) {
     76                                             mCameraList[State::RIGHT].push_back(info);
     77                                         }
     78                                         if (info.function.find("left") != std::string::npos) {
     79                                             mCameraList[State::LEFT].push_back(info);
     80                                         }
     81                                         if (info.function.find("park") != std::string::npos) {
     82                                             mCameraList[State::PARKING].push_back(info);
     83                                         }
     84                                         cameraConfigFound = true;
     85                                         break;
     86                                     }
     87                                 }
     88                                 if (!cameraConfigFound) {
     89                                     ALOGW("No config information for hardware camera %s",
     90                                           cam.cameraId.c_str());
     91                                 }
     92                             }
     93                         }
     94     );
     95 #else // This way we use placeholders for cameras in the configuration but not reported by EVS
     96     // Build our set of cameras for the states we support
     97     ALOGD("Requesting camera list");
     98     for (auto&& info: config.getCameras()) {
     99         if (info.function.find("reverse") != std::string::npos) {
    100             mCameraList[State::REVERSE].push_back(info);
    101         }
    102         if (info.function.find("right") != std::string::npos) {
    103             mCameraList[State::RIGHT].push_back(info);
    104         }
    105         if (info.function.find("left") != std::string::npos) {
    106             mCameraList[State::LEFT].push_back(info);
    107         }
    108         if (info.function.find("park") != std::string::npos) {
    109             mCameraList[State::PARKING].push_back(info);
    110         }
    111     }
    112 #endif
    113 
    114     ALOGD("State controller ready");
    115 }
    116 
    117 
    118 bool EvsStateControl::startUpdateLoop() {
    119     // Create the thread and report success if it gets started
    120     mRenderThread = std::thread([this](){ updateLoop(); });
    121     return mRenderThread.joinable();
    122 }
    123 
    124 
    125 void EvsStateControl::postCommand(const Command& cmd) {
    126     // Push the command onto the queue watched by updateLoop
    127     mLock.lock();
    128     mCommandQueue.push(cmd);
    129     mLock.unlock();
    130 
    131     // Send a signal to wake updateLoop in case it is asleep
    132     mWakeSignal.notify_all();
    133 }
    134 
    135 
    136 void EvsStateControl::updateLoop() {
    137     ALOGD("Starting EvsStateControl update loop");
    138 
    139     bool run = true;
    140     while (run) {
    141         // Process incoming commands
    142         {
    143             std::lock_guard <std::mutex> lock(mLock);
    144             while (!mCommandQueue.empty()) {
    145                 const Command& cmd = mCommandQueue.front();
    146                 switch (cmd.operation) {
    147                 case Op::EXIT:
    148                     run = false;
    149                     break;
    150                 case Op::CHECK_VEHICLE_STATE:
    151                     // Just running selectStateForCurrentConditions below will take care of this
    152                     break;
    153                 case Op::TOUCH_EVENT:
    154                     // TODO:  Implement this given the x/y location of the touch event
    155                     // Ignore for now
    156                     break;
    157                 }
    158                 mCommandQueue.pop();
    159             }
    160         }
    161 
    162         // Review vehicle state and choose an appropriate renderer
    163         if (!selectStateForCurrentConditions()) {
    164             ALOGE("selectStateForCurrentConditions failed so we're going to die");
    165             break;
    166         }
    167 
    168         // If we have an active renderer, give it a chance to draw
    169         if (mCurrentRenderer) {
    170             // Get the output buffer we'll use to display the imagery
    171             BufferDesc tgtBuffer = {};
    172             mDisplay->getTargetBuffer([&tgtBuffer](const BufferDesc& buff) {
    173                                           tgtBuffer = buff;
    174                                       }
    175             );
    176 
    177             if (tgtBuffer.memHandle == nullptr) {
    178                 ALOGE("Didn't get requested output buffer -- skipping this frame.");
    179             } else {
    180                 // Generate our output image
    181                 if (!mCurrentRenderer->drawFrame(tgtBuffer)) {
    182                     // If drawing failed, we want to exit quickly so an app restart can happen
    183                     run = false;
    184                 }
    185 
    186                 // Send the finished image back for display
    187                 mDisplay->returnTargetBufferForDisplay(tgtBuffer);
    188             }
    189         } else {
    190             // No active renderer, so sleep until somebody wakes us with another command
    191             std::unique_lock<std::mutex> lock(mLock);
    192             mWakeSignal.wait(lock);
    193         }
    194     }
    195 
    196     ALOGW("EvsStateControl update loop ending");
    197 
    198     // TODO:  Fix it so we can exit cleanly from the main thread instead
    199     printf("Shutting down app due to state control loop ending\n");
    200     ALOGE("KILLING THE APP FROM THE EvsStateControl LOOP ON DRAW FAILURE!!!");
    201     exit(1);
    202 }
    203 
    204 
    205 bool EvsStateControl::selectStateForCurrentConditions() {
    206     static int32_t sDummyGear   = int32_t(VehicleGear::GEAR_REVERSE);
    207     static int32_t sDummySignal = int32_t(VehicleTurnSignal::NONE);
    208 
    209     if (mVehicle != nullptr) {
    210         // Query the car state
    211         if (invokeGet(&mGearValue) != StatusCode::OK) {
    212             ALOGE("GEAR_SELECTION not available from vehicle.  Exiting.");
    213             return false;
    214         }
    215         if ((mTurnSignalValue.prop == 0) || (invokeGet(&mTurnSignalValue) != StatusCode::OK)) {
    216             // Silently treat missing turn signal state as no turn signal active
    217             mTurnSignalValue.value.int32Values.setToExternal(&sDummySignal, 1);
    218             mTurnSignalValue.prop = 0;
    219         }
    220     } else {
    221         // While testing without a vehicle, behave as if we're in reverse for the first 20 seconds
    222         static const int kShowTime = 20;    // seconds
    223 
    224         // See if it's time to turn off the default reverse camera
    225         static std::chrono::steady_clock::time_point start = std::chrono::steady_clock::now();
    226         std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now();
    227         if (std::chrono::duration_cast<std::chrono::seconds>(now - start).count() > kShowTime) {
    228             // Switch to drive (which should turn off the reverse camera)
    229             sDummyGear = int32_t(VehicleGear::GEAR_DRIVE);
    230         }
    231 
    232         // Build the dummy vehicle state values (treating single values as 1 element vectors)
    233         mGearValue.value.int32Values.setToExternal(&sDummyGear, 1);
    234         mTurnSignalValue.value.int32Values.setToExternal(&sDummySignal, 1);
    235     }
    236 
    237     // Choose our desired EVS state based on the current car state
    238     // TODO:  Update this logic, and consider user input when choosing if a view should be presented
    239     State desiredState = OFF;
    240     if (mGearValue.value.int32Values[0] == int32_t(VehicleGear::GEAR_REVERSE)) {
    241         desiredState = REVERSE;
    242     } else if (mTurnSignalValue.value.int32Values[0] == int32_t(VehicleTurnSignal::RIGHT)) {
    243         desiredState = RIGHT;
    244     } else if (mTurnSignalValue.value.int32Values[0] == int32_t(VehicleTurnSignal::LEFT)) {
    245         desiredState = LEFT;
    246     } else if (mGearValue.value.int32Values[0] == int32_t(VehicleGear::GEAR_PARK)) {
    247         desiredState = PARKING;
    248     }
    249 
    250     // Apply the desire state
    251     return configureEvsPipeline(desiredState);
    252 }
    253 
    254 
    255 StatusCode EvsStateControl::invokeGet(VehiclePropValue *pRequestedPropValue) {
    256     StatusCode status = StatusCode::TRY_AGAIN;
    257 
    258     // Call the Vehicle HAL, which will block until the callback is complete
    259     mVehicle->get(*pRequestedPropValue,
    260                   [pRequestedPropValue, &status]
    261                   (StatusCode s, const VehiclePropValue& v) {
    262                        status = s;
    263                        if (s == StatusCode::OK) {
    264                            *pRequestedPropValue = v;
    265                        }
    266                   }
    267     );
    268 
    269     return status;
    270 }
    271 
    272 
    273 bool EvsStateControl::configureEvsPipeline(State desiredState) {
    274     if (mCurrentState == desiredState) {
    275         // Nothing to do here...
    276         return true;
    277     }
    278 
    279     ALOGD("Switching to state %d.", desiredState);
    280     ALOGD("  Current state %d has %zu cameras", mCurrentState,
    281           mCameraList[mCurrentState].size());
    282     ALOGD("  Desired state %d has %zu cameras", desiredState,
    283           mCameraList[desiredState].size());
    284 
    285     // Since we're changing states, shut down the current renderer
    286     if (mCurrentRenderer != nullptr) {
    287         mCurrentRenderer->deactivate();
    288         mCurrentRenderer = nullptr; // It's a smart pointer, so destructs on assignment to null
    289     }
    290 
    291     // Do we need a new direct view renderer?
    292     if (mCameraList[desiredState].size() > 1 || desiredState == PARKING) {
    293         // TODO:  DO we want other kinds of compound view or else sequentially selected views?
    294         mCurrentRenderer = std::make_unique<RenderTopView>(mEvs,
    295                                                            mCameraList[desiredState],
    296                                                            mConfig);
    297         if (!mCurrentRenderer) {
    298             ALOGE("Failed to construct top view renderer.  Skipping state change.");
    299             return false;
    300         }
    301     } else if (mCameraList[desiredState].size() == 1) {
    302         // We have a camera assigned to this state for direct view
    303         mCurrentRenderer = std::make_unique<RenderDirectView>(mEvs,
    304                                                               mCameraList[desiredState][0]);
    305         if (!mCurrentRenderer) {
    306             ALOGE("Failed to construct direct renderer.  Skipping state change.");
    307             return false;
    308         }
    309     }
    310 
    311     // Now set the display state based on whether we have a video feed to show
    312     if (mCurrentRenderer == nullptr) {
    313         ALOGD("Turning off the display");
    314         mDisplay->setDisplayState(DisplayState::NOT_VISIBLE);
    315     } else {
    316         // Start the camera stream
    317         ALOGD("Starting camera stream");
    318         if (!mCurrentRenderer->activate()) {
    319             ALOGE("New renderer failed to activate");
    320             return false;
    321         }
    322 
    323         // Activate the display
    324         ALOGD("Arming the display");
    325         Return<EvsResult> result = mDisplay->setDisplayState(DisplayState::VISIBLE_ON_NEXT_FRAME);
    326         if (result != EvsResult::OK) {
    327             ALOGE("setDisplayState returned an error (%d)", (EvsResult)result);
    328             return false;
    329         }
    330     }
    331 
    332     // Record our current state
    333     ALOGI("Activated state %d.", desiredState);
    334     mCurrentState = desiredState;
    335 
    336     return true;
    337 }
    338