Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d2085a9b authored by Ravneet Dhanjal's avatar Ravneet Dhanjal Committed by Android (Google) Code Review
Browse files

Merge "Add CameraService Watchdog" into tm-dev

parents a2469775 98ffa75d
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -42,6 +42,7 @@ cc_library_shared {

    srcs: [
        "CameraService.cpp",
        "CameraServiceWatchdog.cpp",
        "CameraFlashlight.cpp",
        "common/Camera2ClientBase.cpp",
        "common/CameraDeviceBase.cpp",
+90 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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 "CameraServiceWatchdog"

#include "CameraServiceWatchdog.h"

namespace android {

bool CameraServiceWatchdog::threadLoop()
{
    {
        AutoMutex _l(mWatchdogLock);

        while (mPause) {
            mWatchdogCondition.wait(mWatchdogLock);
        }
    }

    std::this_thread::sleep_for(std::chrono::milliseconds(mCycleLengthMs));

    {
        AutoMutex _l(mWatchdogLock);

        for (auto it = tidToCycleCounterMap.begin(); it != tidToCycleCounterMap.end(); it++) {
            uint32_t currentThreadId = it->first;

            tidToCycleCounterMap[currentThreadId]++;

            if (tidToCycleCounterMap[currentThreadId] >= mMaxCycles) {
                ALOGW("CameraServiceWatchdog triggering kill for pid: %d", getpid());
                kill(getpid(), SIGKILL);
            }
        }
    }

    return true;
}

void CameraServiceWatchdog::requestExit()
{
    Thread::requestExit();

    AutoMutex _l(mWatchdogLock);

    tidToCycleCounterMap.clear();

    if (mPause) {
        mPause = false;
        mWatchdogCondition.signal();
    }
}

void CameraServiceWatchdog::stop(uint32_t tid)
{
    AutoMutex _l(mWatchdogLock);

    tidToCycleCounterMap.erase(tid);

    if (tidToCycleCounterMap.empty()) {
        mPause = true;
    }
}

void CameraServiceWatchdog::start(uint32_t tid)
{
    AutoMutex _l(mWatchdogLock);

    tidToCycleCounterMap[tid] = 0;

    if (mPause) {
        mPause = false;
        mWatchdogCondition.signal();
    }
}

}   // namespace android
+121 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.
 */

/**
 * The CameraService watchdog is used to help detect bad states in the
 * Camera HAL. The threadloop uses cycle counters, assigned to each calling
 * thread, to monitor the elapsing time and kills the process when the
 * expected duration has exceeded.
 * Notes on multi-threaded behaviors:
 *    - The threadloop is blocked/paused when there are no calls being
 *   monitored.
 *   - The start and stop functions handle simultaneous call monitoring
 *   and single call monitoring differently. See function documentation for
 *   more details.
 */

#include <chrono>
#include <thread>
#include <time.h>
#include <utils/Thread.h>
#include <utils/Log.h>
#include <unordered_map>

// Used to wrap the call of interest in start and stop calls
#define WATCH(toMonitor) watchThread([&]() { return toMonitor;}, gettid())
#define WATCH_CUSTOM_TIMER(toMonitor, cycles, cycleLength) \
        watchThread([&]() { return toMonitor;}, gettid(), cycles, cycleLength);

// Default cycles and cycle length values used to calculate permitted elapsed time
const static size_t   kMaxCycles     = 100;
const static uint32_t kCycleLengthMs = 100;

namespace android {

class CameraServiceWatchdog : public Thread {

public:
    explicit CameraServiceWatchdog() : mPause(true), mMaxCycles(kMaxCycles),
            mCycleLengthMs(kCycleLengthMs) {};

    explicit CameraServiceWatchdog (size_t maxCycles, uint32_t cycleLengthMs) :
            mPause(true), mMaxCycles(maxCycles), mCycleLengthMs(cycleLengthMs) {};

    virtual ~CameraServiceWatchdog() {};

    virtual void requestExit();

    /** Used to wrap monitored calls in start and stop functions using custom timer values */
    template<typename T>
    auto watchThread(T func, uint32_t tid, uint32_t cycles, uint32_t cycleLength) {
        auto res = NULL;

        if (cycles != mMaxCycles || cycleLength != mCycleLengthMs) {
            // Create another instance of the watchdog to prevent disruption
            // of timer for current monitored calls
            sp<CameraServiceWatchdog> tempWatchdog =
                    new CameraServiceWatchdog(cycles, cycleLength);
            tempWatchdog->run("CameraServiceWatchdog");
            res = tempWatchdog->watchThread(func, tid);
            tempWatchdog->requestExit();
            tempWatchdog.clear();
        } else {
            // If custom timer values are equivalent to set class timer values, use
            // current thread
            res = watchThread(func, tid);
        }

        return res;
    }

    /** Used to wrap monitored calls in start and stop functions using class timer values */
    template<typename T>
    auto watchThread(T func, uint32_t tid) {
        auto res = NULL;

        start(tid);
        res = func();
        stop(tid);

        return res;
    }

private:

    /**
     * Start adds a cycle counter for the calling thread. When threadloop is blocked/paused,
     * start() unblocks and starts the watchdog
     */
    void start(uint32_t tid);

    /**
     * If there are no calls left to be monitored, stop blocks/pauses threadloop
     * otherwise stop() erases the cycle counter to end watchdog for the calling thread
     */
    void stop(uint32_t tid);

    virtual bool    threadLoop();

    Mutex           mWatchdogLock;        // Lock for condition variable
    Condition       mWatchdogCondition;   // Condition variable for stop/start
    bool            mPause;               // True if thread is currently paused
    uint32_t        mMaxCycles;           // Max cycles
    uint32_t        mCycleLengthMs;       // Length of time elapsed per cycle

    std::unordered_map<uint32_t, uint32_t> tidToCycleCounterMap; // Thread Id to cycle counter map
};

}   // namespace android
+17 −2
Original line number Diff line number Diff line
@@ -115,6 +115,10 @@ const String8& Camera3Device::getId() const {

status_t Camera3Device::initializeCommonLocked() {

    /** Start watchdog thread */
    mCameraServiceWatchdog = new CameraServiceWatchdog();
    mCameraServiceWatchdog->run("CameraServiceWatchdog");

    /** Start up status tracker thread */
    mStatusTracker = new StatusTracker(this);
    status_t res = mStatusTracker->run(String8::format("C3Dev-%s-Status", mId.string()).string());
@@ -316,7 +320,7 @@ status_t Camera3Device::disconnectImpl() {

        // Call close without internal mutex held, as the HAL close may need to
        // wait on assorted callbacks,etc, to complete before it can return.
        interface->close();
        mCameraServiceWatchdog->WATCH(interface->close());

        flushInflightRequests();

@@ -339,6 +343,12 @@ status_t Camera3Device::disconnectImpl() {
        }
    }
    ALOGI("%s: X", __FUNCTION__);

    if (mCameraServiceWatchdog != NULL) {
        mCameraServiceWatchdog->requestExit();
        mCameraServiceWatchdog.clear();
    }

    return res;
}

@@ -1719,7 +1729,12 @@ status_t Camera3Device::flush(int64_t *frameNumber) {
        mSessionStatsBuilder.stopCounter();
    }

    return mRequestThread->flush();
    // Calculate expected duration for flush with additional buffer time in ms for watchdog
    uint64_t maxExpectedDuration = (getExpectedInFlightDuration() + kBaseGetBufferWait) / 1e6;
    status_t res = mCameraServiceWatchdog->WATCH_CUSTOM_TIMER(mRequestThread->flush(),
            maxExpectedDuration / kCycleLengthMs, kCycleLengthMs);

    return res;
}

status_t Camera3Device::prepare(int streamId) {
+4 −0
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@
#include <camera/CaptureResult.h>

#include "android/hardware/camera/metadata/3.8/types.h"
#include "CameraServiceWatchdog.h"
#include "common/CameraDeviceBase.h"
#include "device3/BufferUtils.h"
#include "device3/StatusTracker.h"
@@ -98,6 +99,9 @@ class Camera3Device :

    metadata_vendor_id_t getVendorTagId() const override { return mVendorTagId; }

    // Watchdog thread
    sp<CameraServiceWatchdog> mCameraServiceWatchdog;

    // Transitions to idle state on success.
    virtual status_t initialize(sp<CameraProviderManager> /*manager*/,
            const String8& /*monitorTags*/) = 0;