C++程序  |  415行  |  13.66 KB

/*
 * Copyright (C) 2016-2019 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 "EvsEnumerator.h"
#include "EvsV4lCamera.h"
#include "EvsGlDisplay.h"

#include <dirent.h>
#include <hardware_legacy/uevent.h>
#include <hwbinder/IPCThreadState.h>
#include <cutils/android_filesystem_config.h>


using namespace std::chrono_literals;

namespace android {
namespace hardware {
namespace automotive {
namespace evs {
namespace V1_0 {
namespace implementation {


// 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::unordered_map<std::string, EvsEnumerator::CameraRecord> EvsEnumerator::sCameraList;
wp<EvsGlDisplay>                                             EvsEnumerator::sActiveDisplay;
std::mutex                                                   EvsEnumerator::sLock;
std::condition_variable                                      EvsEnumerator::sCameraSignal;

// Constants
const auto kEnumerationTimeout = 10s;


bool EvsEnumerator::checkPermission() {
    hardware::IPCThreadState *ipc = hardware::IPCThreadState::self();
    if (AID_AUTOMOTIVE_EVS != ipc->getCallingUid()) {
        ALOGE("EVS access denied: pid = %d, uid = %d", ipc->getCallingPid(), ipc->getCallingUid());
        return false;
    }

    return true;
}

void EvsEnumerator::EvsUeventThread(std::atomic<bool>& running) {
    int status = uevent_init();
    if (!status) {
        ALOGE("Failed to initialize uevent handler.");
        return;
    }

    char uevent_data[PAGE_SIZE - 2] = {};
    while (running) {
        int length = uevent_next_event(uevent_data, static_cast<int32_t>(sizeof(uevent_data)));

        // Ensure double-null termination.
        uevent_data[length] = uevent_data[length + 1] = '\0';

        const char *action = nullptr;
        const char *devname = nullptr;
        const char *subsys = nullptr;
        char *cp = uevent_data;
        while (*cp) {
            // EVS is interested only in ACTION, SUBSYSTEM, and DEVNAME.
            if (!std::strncmp(cp, "ACTION=", 7)) {
                action = cp + 7;
            } else if (!std::strncmp(cp, "SUBSYSTEM=", 10)) {
                subsys = cp + 10;
            } else if (!std::strncmp(cp, "DEVNAME=", 8)) {
                devname = cp + 8;
            }

            // Advance to after next \0
            while (*cp++);
        }

        if (!devname || !subsys || std::strcmp(subsys, "video4linux")) {
            // EVS expects that the subsystem of enabled video devices is
            // video4linux.
            continue;
        }

        // Update shared list.
        bool cmd_addition = !std::strcmp(action, "add");
        bool cmd_removal  = !std::strcmp(action, "remove");
        {
            std::string devpath = "/dev/";
            devpath += devname;

            std::lock_guard<std::mutex> lock(sLock);
            if (cmd_removal) {
                sCameraList.erase(devpath);
                ALOGI("%s is removed", devpath.c_str());
            } else if (cmd_addition) {
                // NOTE: we are here adding new device without a validation
                // because it always fails to open, b/132164956.
                sCameraList.emplace(devpath, devpath.c_str());
                ALOGI("%s is added", devpath.c_str());
            } else {
                // Ignore all other actions including "change".
            }

            // Notify the change.
            sCameraSignal.notify_all();
        }
    }

    return;
}

EvsEnumerator::EvsEnumerator() {
    ALOGD("EvsEnumerator created");

    enumerateDevices();
}

void EvsEnumerator::enumerateDevices() {
    // For every video* entry in the dev folder, see if it reports suitable capabilities
    // WARNING:  Depending on the driver implementations this could be slow, especially if
    //           there are timeouts or round trips to hardware required to collect the needed
    //           information.  Platform implementers should consider hard coding this list of
    //           known good devices to speed up the startup time of their EVS implementation.
    //           For example, this code might be replaced with nothing more than:
    //                   sCameraList.emplace("/dev/video0");
    //                   sCameraList.emplace("/dev/video1");
    ALOGI("%s: Starting dev/video* enumeration", __FUNCTION__);
    unsigned videoCount   = 0;
    unsigned captureCount = 0;
    DIR* dir = opendir("/dev");
    if (!dir) {
        LOG_FATAL("Failed to open /dev folder\n");
    }
    struct dirent* entry;
    {
        std::lock_guard<std::mutex> lock(sLock);

        while ((entry = readdir(dir)) != nullptr) {
            // We're only looking for entries starting with 'video'
            if (strncmp(entry->d_name, "video", 5) == 0) {
                std::string deviceName("/dev/");
                deviceName += entry->d_name;
                videoCount++;
                if (sCameraList.find(deviceName) != sCameraList.end()) {
                    ALOGI("%s has been added already.", deviceName.c_str());
                    captureCount++;
                } else if(qualifyCaptureDevice(deviceName.c_str())) {
                    sCameraList.emplace(deviceName, deviceName.c_str());
                    captureCount++;
                }
            }
        }
    }

    ALOGI("Found %d qualified video capture devices of %d checked\n", captureCount, videoCount);
}

// Methods from ::android::hardware::automotive::evs::V1_0::IEvsEnumerator follow.
Return<void> EvsEnumerator::getCameraList(getCameraList_cb _hidl_cb)  {
    ALOGD("getCameraList");
    if (!checkPermission()) {
        return Void();
    }

    {
        std::unique_lock<std::mutex> lock(sLock);
        if (sCameraList.size() < 1) {
            // No qualified device has been found.  Wait until new device is ready,
            // for 10 seconds.
            if (!sCameraSignal.wait_for(lock,
                                        kEnumerationTimeout,
                                        []{ return sCameraList.size() > 0; })) {
                ALOGD("Timer expired.  No new device has been added.");
            }
        }
    }

    const unsigned numCameras = sCameraList.size();

    // Build up a packed array of CameraDesc for return
    hidl_vec<CameraDesc> hidlCameras;
    hidlCameras.resize(numCameras);
    unsigned i = 0;
    for (const auto& [key, cam] : sCameraList) {
        hidlCameras[i++] = cam.desc;
    }

    // Send back the results
    ALOGD("reporting %zu cameras available", hidlCameras.size());
    _hidl_cb(hidlCameras);

    // HIDL convention says we return Void if we sent our result back via callback
    return Void();
}


Return<sp<IEvsCamera>> EvsEnumerator::openCamera(const hidl_string& cameraId) {
    ALOGD("openCamera");
    if (!checkPermission()) {
        return nullptr;
    }

    // Is this a recognized camera id?
    CameraRecord *pRecord = findCameraById(cameraId);

    // Has this camera already been instantiated by another caller?
    sp<EvsV4lCamera> 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 EvsV4lCamera(cameraId.c_str());
    pRecord->activeInstance = pActiveCamera;
    if (pActiveCamera == nullptr) {
        ALOGE("Failed to allocate new EvsV4lCamera object for %s\n", cameraId.c_str());
    }

    return pActiveCamera;
}


Return<void> EvsEnumerator::closeCamera(const ::android::sp<IEvsCamera>& pCamera) {
    ALOGD("closeCamera");

    if (pCamera == nullptr) {
        ALOGE("Ignoring call to closeCamera with null camera ptr");
        return Void();
    }

    // Get the camera id so we can find it in our list
    std::string cameraId;
    pCamera->getCameraInfo([&cameraId](CameraDesc desc) {
                               cameraId = desc.cameraId;
                           }
    );

    // Find the named camera
    CameraRecord *pRecord = findCameraById(cameraId);

    // Is the display being destroyed actually the one we think is active?
    if (!pRecord) {
        ALOGE("Asked to close a camera whose name isn't recognized");
    } else {
        sp<EvsV4lCamera> 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->shutdown();
            pRecord->activeInstance = nullptr;
        }
    }

    return Void();
}


Return<sp<IEvsDisplay>> EvsEnumerator::openDisplay() {
    ALOGD("openDisplay");
    if (!checkPermission()) {
        return nullptr;
    }

    // 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<EvsGlDisplay> pActiveDisplay = sActiveDisplay.promote();
    if (pActiveDisplay != nullptr) {
        ALOGW("Killing previous display because of new caller");
        closeDisplay(pActiveDisplay);
    }

    // Create a new display interface and return it
    pActiveDisplay = new EvsGlDisplay();
    sActiveDisplay = pActiveDisplay;

    ALOGD("Returning new EvsGlDisplay object %p", pActiveDisplay.get());
    return pActiveDisplay;
}


Return<void> EvsEnumerator::closeDisplay(const ::android::sp<IEvsDisplay>& pDisplay) {
    ALOGD("closeDisplay");

    // Do we still have a display object we think should be active?
    sp<EvsGlDisplay> pActiveDisplay = sActiveDisplay.promote();
    if (pActiveDisplay == nullptr) {
        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
        pActiveDisplay->forceShutdown();
        sActiveDisplay = nullptr;
    }

    return Void();
}


Return<DisplayState> EvsEnumerator::getDisplayState()  {
    ALOGD("getDisplayState");
    if (!checkPermission()) {
        return DisplayState::DEAD;
    }

    // Do we still have a display object we think should be active?
    sp<IEvsDisplay> pActiveDisplay = sActiveDisplay.promote();
    if (pActiveDisplay != nullptr) {
        return pActiveDisplay->getDisplayState();
    } else {
        return DisplayState::NOT_OPEN;
    }
}


bool EvsEnumerator::qualifyCaptureDevice(const char* deviceName) {
    class FileHandleWrapper {
    public:
        FileHandleWrapper(int fd)   { mFd = fd; }
        ~FileHandleWrapper()        { if (mFd > 0) close(mFd); }
        operator int() const        { return mFd; }
    private:
        int mFd = -1;
    };


    FileHandleWrapper fd = open(deviceName, O_RDWR, 0);
    if (fd < 0) {
        return false;
    }

    v4l2_capability caps;
    int result = ioctl(fd, VIDIOC_QUERYCAP, &caps);
    if (result  < 0) {
        return false;
    }
    if (((caps.capabilities & V4L2_CAP_VIDEO_CAPTURE) == 0) ||
        ((caps.capabilities & V4L2_CAP_STREAMING)     == 0)) {
        return false;
    }

    // Enumerate the available capture formats (if any)
    v4l2_fmtdesc formatDescription;
    formatDescription.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    bool found = false;
    for (int i=0; !found; i++) {
        formatDescription.index = i;
        if (ioctl(fd, VIDIOC_ENUM_FMT, &formatDescription) == 0) {
            ALOGI("FORMAT 0x%X, type 0x%X, desc %s, flags 0x%X",
                  formatDescription.pixelformat, formatDescription.type,
                  formatDescription.description, formatDescription.flags);
            switch (formatDescription.pixelformat)
            {
                case V4L2_PIX_FMT_YUYV:     found = true; break;
                case V4L2_PIX_FMT_NV21:     found = true; break;
                case V4L2_PIX_FMT_NV16:     found = true; break;
                case V4L2_PIX_FMT_YVU420:   found = true; break;
                case V4L2_PIX_FMT_RGB32:    found = true; break;
#ifdef V4L2_PIX_FMT_ARGB32  // introduced with kernel v3.17
                case V4L2_PIX_FMT_ARGB32:   found = true; break;
                case V4L2_PIX_FMT_XRGB32:   found = true; break;
#endif // V4L2_PIX_FMT_ARGB32
                default:
                    ALOGW("Unsupported, 0x%X", formatDescription.pixelformat);
                    break;
            }
        } else {
            // No more formats available.
            break;
        }
    }

    return found;
}


EvsEnumerator::CameraRecord* EvsEnumerator::findCameraById(const std::string& cameraId) {
    // Find the named camera
    auto found = sCameraList.find(cameraId);
    if (sCameraList.end() != found) {
        // Found a match!
        return &found->second;
    }

    // We didn't find a match
    return nullptr;
}


} // namespace implementation
} // namespace V1_0
} // namespace evs
} // namespace automotive
} // namespace hardware
} // namespace android