/* * 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 (when the TID cycle to counter map is empty). * - The start and stop functions handle simultaneous call monitoring * and single call monitoring differently. See function documentation for * more details. * To disable/enable: * - adb shell cmd media.camera set-cameraservice-watchdog [0/1] */ #pragma once #include #include #include #include #include #include #include #include "utils/CameraServiceProxyWrapper.h" // Used to wrap the call of interest in start and stop calls #define WATCH(toMonitor) watchThread([&]() { return toMonitor;}, gettid(), __FUNCTION__) #define WATCH_CUSTOM_TIMER(toMonitor, cycles, cycleLength) \ watchThread([&]() { return toMonitor;}, gettid(), __FUNCTION__, 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 { struct MonitoredFunction { uint32_t cycles; std::string functionName; }; public: explicit CameraServiceWatchdog(const String8 &cameraId, std::shared_ptr cameraServiceProxyWrapper) : mCameraId(cameraId), mPause(true), mMaxCycles(kMaxCycles), mCycleLengthMs(kCycleLengthMs), mEnabled(true), mCameraServiceProxyWrapper(cameraServiceProxyWrapper) {}; explicit CameraServiceWatchdog (const String8 &cameraId, size_t maxCycles, uint32_t cycleLengthMs, bool enabled, std::shared_ptr cameraServiceProxyWrapper) : mCameraId(cameraId), mPause(true), mMaxCycles(maxCycles), mCycleLengthMs(cycleLengthMs), mEnabled(enabled), mCameraServiceProxyWrapper(cameraServiceProxyWrapper) {}; virtual ~CameraServiceWatchdog() {}; virtual void requestExit(); /** Enables/disables the watchdog */ void setEnabled(bool enable); /** Used to wrap monitored calls in start and stop functions using custom timer values */ template auto watchThread(T func, uint32_t tid, const char* functionName, uint32_t cycles, uint32_t cycleLength) { decltype(func()) res; if (cycles != mMaxCycles || cycleLength != mCycleLengthMs) { // Create another instance of the watchdog to prevent disruption // of timer for current monitored calls // Lock for mEnabled mEnabledLock.lock(); sp tempWatchdog = new CameraServiceWatchdog( mCameraId, cycles, cycleLength, mEnabled, mCameraServiceProxyWrapper); mEnabledLock.unlock(); status_t status = tempWatchdog->run("CameraServiceWatchdog"); if (status != OK) { ALOGE("Unable to watch thread: %s (%d)", strerror(-status), status); res = watchThread(func, tid, functionName); return res; } res = tempWatchdog->watchThread(func, tid, functionName); tempWatchdog->requestExit(); tempWatchdog.clear(); } else { // If custom timer values are equivalent to set class timer values, use // current thread res = watchThread(func, tid, functionName); } return res; } /** Used to wrap monitored calls in start and stop functions using class timer values */ template auto watchThread(T func, uint32_t tid, const char* functionName) { decltype(func()) res; AutoMutex _l(mEnabledLock); if (mEnabled) { start(tid, functionName); res = func(); stop(tid); } else { res = func(); } 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, const char* functionName); /** * 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); std::string getAbortMessage(const std::string& functionName); virtual bool threadLoop(); Mutex mWatchdogLock; // Lock for condition variable Mutex mEnabledLock; // Lock for enabled status Condition mWatchdogCondition; // Condition variable for stop/start String8 mCameraId; // Camera Id the watchdog belongs to bool mPause; // True if tid map is empty uint32_t mMaxCycles; // Max cycles uint32_t mCycleLengthMs; // Length of time elapsed per cycle bool mEnabled; // True if watchdog is enabled std::shared_ptr mCameraServiceProxyWrapper; std::unordered_map mTidMap; // Thread Id to MonitoredFunction type // which retrieves the num of cycles // and name of the function }; } // namespace android