From 7e540682b362a3e8679e274084aeaefdf01314da Mon Sep 17 00:00:00 2001 From: Shuzhen Wang Date: Fri, 10 Apr 2020 13:30:25 -0700 Subject: [PATCH] Camera: Fix race between callback and unregisteration of callbacks Because callbacks are called from a looper thread, if the subscriber unsubscribes and frees the callback, invalid memory access may occur from the looper thread. Address this by draining all pending callbacks at the time of listener unregistration. NOTE: There is rare chance that the wait on the condition times out, still resulting in the original NPE issue. We intensionally allow that to avoid deadlock, in case the application uses the same mutex lock for callbacks and unregister functions. Test: for i in {1..10}; do atest -it NativeImageReaderTest; done Test: ACameraNdkVendorTest Bug: 148976953 Change-Id: I92f5a02a4e3e63f2e72e8043ff4f4ac16c1eec5d --- camera/ndk/impl/ACameraManager.cpp | 50 ++++++++++++++++++- camera/ndk/impl/ACameraManager.h | 14 +++++- camera/ndk/include/camera/NdkCameraManager.h | 6 +++ camera/ndk/ndk_vendor/impl/ACameraManager.cpp | 44 +++++++++++++++- camera/ndk/ndk_vendor/impl/ACameraManager.h | 13 ++++- 5 files changed, 123 insertions(+), 4 deletions(-) diff --git a/camera/ndk/impl/ACameraManager.cpp b/camera/ndk/impl/ACameraManager.cpp index f408b6a854..b58ebe21e9 100644 --- a/camera/ndk/impl/ACameraManager.cpp +++ b/camera/ndk/impl/ACameraManager.cpp @@ -35,6 +35,7 @@ const char* CameraManagerGlobal::kCameraIdKey = "CameraId"; const char* CameraManagerGlobal::kPhysicalCameraIdKey = "PhysicalCameraId"; const char* CameraManagerGlobal::kCallbackFpKey = "CallbackFp"; const char* CameraManagerGlobal::kContextKey = "CallbackContext"; +const nsecs_t CameraManagerGlobal::kCallbackDrainTimeout = 5000000; // 5 ms Mutex CameraManagerGlobal::sLock; CameraManagerGlobal* CameraManagerGlobal::sInstance = nullptr; @@ -117,7 +118,7 @@ sp CameraManagerGlobal::getCameraServiceLocked() { return nullptr; } if (mHandler == nullptr) { - mHandler = new CallbackHandler(); + mHandler = new CallbackHandler(this); } mCbLooper->registerHandler(mHandler); } @@ -211,6 +212,9 @@ void CameraManagerGlobal::registerExtendedAvailabilityCallback( void CameraManagerGlobal::unregisterExtendedAvailabilityCallback( const ACameraManager_ExtendedAvailabilityCallbacks *callback) { Mutex::Autolock _l(mLock); + + drainPendingCallbacksLocked(); + Callback cb(callback); mCallbacks.erase(cb); } @@ -223,10 +227,32 @@ void CameraManagerGlobal::registerAvailabilityCallback( void CameraManagerGlobal::unregisterAvailabilityCallback( const ACameraManager_AvailabilityCallbacks *callback) { Mutex::Autolock _l(mLock); + + drainPendingCallbacksLocked(); + Callback cb(callback); mCallbacks.erase(cb); } +void CameraManagerGlobal::onCallbackCalled() { + Mutex::Autolock _l(mLock); + if (mPendingCallbackCnt > 0) { + mPendingCallbackCnt--; + } + mCallbacksCond.signal(); +} + +void CameraManagerGlobal::drainPendingCallbacksLocked() { + while (mPendingCallbackCnt > 0) { + auto res = mCallbacksCond.waitRelative(mLock, kCallbackDrainTimeout); + if (res != NO_ERROR) { + ALOGE("%s: Error waiting to drain callbacks: %s(%d)", + __FUNCTION__, strerror(-res), res); + break; + } + } +} + template void CameraManagerGlobal::registerAvailCallback(const T *callback) { Mutex::Autolock _l(mLock); @@ -250,6 +276,7 @@ void CameraManagerGlobal::registerAvailCallback(const T *callback) { msg->setPointer(kCallbackFpKey, (void *) cbFunc); msg->setPointer(kContextKey, cb.mContext); msg->setString(kCameraIdKey, AString(cameraId)); + mPendingCallbackCnt++; msg->post(); // Physical camera unavailable callback @@ -263,6 +290,7 @@ void CameraManagerGlobal::registerAvailCallback(const T *callback) { msg->setPointer(kContextKey, cb.mContext); msg->setString(kCameraIdKey, AString(cameraId)); msg->setString(kPhysicalCameraIdKey, AString(physicalCameraId)); + mPendingCallbackCnt++; msg->post(); } } @@ -323,6 +351,16 @@ bool CameraManagerGlobal::isStatusAvailable(int32_t status) { } void CameraManagerGlobal::CallbackHandler::onMessageReceived( + const sp &msg) { + onMessageReceivedInternal(msg); + if (msg->what() == kWhatSendSingleCallback || + msg->what() == kWhatSendSingleAccessCallback || + msg->what() == kWhatSendSinglePhysicalCameraCallback) { + notifyParent(); + } +} + +void CameraManagerGlobal::CallbackHandler::onMessageReceivedInternal( const sp &msg) { switch (msg->what()) { case kWhatSendSingleCallback: @@ -405,6 +443,13 @@ void CameraManagerGlobal::CallbackHandler::onMessageReceived( } } +void CameraManagerGlobal::CallbackHandler::notifyParent() { + sp parent = mParent.promote(); + if (parent != nullptr) { + parent->onCallbackCalled(); + } +} + binder::Status CameraManagerGlobal::CameraServiceListener::onCameraAccessPrioritiesChanged() { sp cm = mCameraManager.promote(); if (cm != nullptr) { @@ -445,6 +490,7 @@ void CameraManagerGlobal::onCameraAccessPrioritiesChanged() { if (cbFp != nullptr) { msg->setPointer(kCallbackFpKey, (void *) cbFp); msg->setPointer(kContextKey, cb.mContext); + mPendingCallbackCnt++; msg->post(); } } @@ -491,6 +537,7 @@ void CameraManagerGlobal::onStatusChangedLocked( msg->setPointer(kCallbackFpKey, (void *) cbFp); msg->setPointer(kContextKey, cb.mContext); msg->setString(kCameraIdKey, AString(cameraId)); + mPendingCallbackCnt++; msg->post(); } } @@ -545,6 +592,7 @@ void CameraManagerGlobal::onStatusChangedLocked( msg->setPointer(kContextKey, cb.mContext); msg->setString(kCameraIdKey, AString(cameraId)); msg->setString(kPhysicalCameraIdKey, AString(physicalCameraId)); + mPendingCallbackCnt++; msg->post(); } } diff --git a/camera/ndk/impl/ACameraManager.h b/camera/ndk/impl/ACameraManager.h index 7fba188bd7..ccbfaa986b 100644 --- a/camera/ndk/impl/ACameraManager.h +++ b/camera/ndk/impl/ACameraManager.h @@ -163,6 +163,12 @@ class CameraManagerGlobal final : public RefBase { ACameraManager_PhysicalCameraAvailabilityCallback mPhysicalCamUnavailable; void* mContext; }; + + android::Condition mCallbacksCond; + size_t mPendingCallbackCnt = 0; + void onCallbackCalled(); + void drainPendingCallbacksLocked(); + std::set mCallbacks; // definition of handler and message @@ -175,10 +181,16 @@ class CameraManagerGlobal final : public RefBase { static const char* kPhysicalCameraIdKey; static const char* kCallbackFpKey; static const char* kContextKey; + static const nsecs_t kCallbackDrainTimeout; class CallbackHandler : public AHandler { public: - CallbackHandler() {} + CallbackHandler(wp parent) : mParent(parent) {} void onMessageReceived(const sp &msg) override; + + private: + wp mParent; + void notifyParent(); + void onMessageReceivedInternal(const sp &msg); }; sp mHandler; sp mCbLooper; // Looper thread where callbacks actually happen on diff --git a/camera/ndk/include/camera/NdkCameraManager.h b/camera/ndk/include/camera/NdkCameraManager.h index e2b71bf017..0a2ee57510 100644 --- a/camera/ndk/include/camera/NdkCameraManager.h +++ b/camera/ndk/include/camera/NdkCameraManager.h @@ -191,6 +191,9 @@ camera_status_t ACameraManager_registerAvailabilityCallback( * *

Removing a callback that isn't registered has no effect.

* + *

This function must not be called with a mutex lock also held by + * the availability callbacks.

+ * * @param manager the {@link ACameraManager} of interest. * @param callback the {@link ACameraManager_AvailabilityCallbacks} to be unregistered. * @@ -382,6 +385,9 @@ camera_status_t ACameraManager_registerExtendedAvailabilityCallback( * *

Removing a callback that isn't registered has no effect.

* + *

This function must not be called with a mutex lock also held by + * the extended availability callbacks.

+ * * @param manager the {@link ACameraManager} of interest. * @param callback the {@link ACameraManager_ExtendedAvailabilityCallbacks} to be unregistered. * diff --git a/camera/ndk/ndk_vendor/impl/ACameraManager.cpp b/camera/ndk/ndk_vendor/impl/ACameraManager.cpp index e2097b5639..5aa9c467fb 100644 --- a/camera/ndk/ndk_vendor/impl/ACameraManager.cpp +++ b/camera/ndk/ndk_vendor/impl/ACameraManager.cpp @@ -45,6 +45,7 @@ const char* CameraManagerGlobal::kCameraIdKey = "CameraId"; const char* CameraManagerGlobal::kPhysicalCameraIdKey = "PhysicalCameraId"; const char* CameraManagerGlobal::kCallbackFpKey = "CallbackFp"; const char* CameraManagerGlobal::kContextKey = "CallbackContext"; +const nsecs_t CameraManagerGlobal::kCallbackDrainTimeout = 5000000; // 5 ms Mutex CameraManagerGlobal::sLock; CameraManagerGlobal* CameraManagerGlobal::sInstance = nullptr; @@ -249,7 +250,7 @@ sp CameraManagerGlobal::getCameraService() { return nullptr; } if (mHandler == nullptr) { - mHandler = new CallbackHandler(); + mHandler = new CallbackHandler(this); } mCbLooper->registerHandler(mHandler); } @@ -317,6 +318,7 @@ void CameraManagerGlobal::registerAvailabilityCallback( void CameraManagerGlobal::unregisterAvailabilityCallback( const ACameraManager_AvailabilityCallbacks *callback) { Mutex::Autolock _l(mLock); + drainPendingCallbacksLocked(); Callback cb(callback); mCallbacks.erase(cb); } @@ -329,10 +331,30 @@ void CameraManagerGlobal::registerExtendedAvailabilityCallback( void CameraManagerGlobal::unregisterExtendedAvailabilityCallback( const ACameraManager_ExtendedAvailabilityCallbacks *callback) { Mutex::Autolock _l(mLock); + drainPendingCallbacksLocked(); Callback cb(callback); mCallbacks.erase(cb); } +void CameraManagerGlobal::onCallbackCalled() { + Mutex::Autolock _l(mLock); + if (mPendingCallbackCnt > 0) { + mPendingCallbackCnt--; + } + mCallbacksCond.signal(); +} + +void CameraManagerGlobal::drainPendingCallbacksLocked() { + while (mPendingCallbackCnt > 0) { + auto res = mCallbacksCond.waitRelative(mLock, kCallbackDrainTimeout); + if (res != NO_ERROR) { + ALOGE("%s: Error waiting to drain callbacks: %s(%d)", + __FUNCTION__, strerror(-res), res); + break; + } + } +} + template void CameraManagerGlobal::registerAvailCallback(const T *callback) { Mutex::Autolock _l(mLock); @@ -351,6 +373,7 @@ void CameraManagerGlobal::registerAvailCallback(const T *callback) { msg->setPointer(kCallbackFpKey, (void *) cbFunc); msg->setPointer(kContextKey, cb.mContext); msg->setString(kCameraIdKey, AString(cameraId.c_str())); + mPendingCallbackCnt++; msg->post(); // Physical camera unavailable callback @@ -363,6 +386,7 @@ void CameraManagerGlobal::registerAvailCallback(const T *callback) { msg->setPointer(kContextKey, cb.mContext); msg->setString(kCameraIdKey, AString(cameraId.c_str())); msg->setString(kPhysicalCameraIdKey, AString(physicalCameraId.c_str())); + mPendingCallbackCnt++; msg->post(); } } @@ -406,6 +430,15 @@ bool CameraManagerGlobal::isStatusAvailable(CameraDeviceStatus status) { } void CameraManagerGlobal::CallbackHandler::onMessageReceived( + const sp &msg) { + onMessageReceivedInternal(msg); + if (msg->what() == kWhatSendSingleCallback || + msg->what() == kWhatSendSinglePhysicalCameraCallback) { + notifyParent(); + } +} + +void CameraManagerGlobal::CallbackHandler::onMessageReceivedInternal( const sp &msg) { switch (msg->what()) { case kWhatSendSingleCallback: @@ -466,6 +499,13 @@ void CameraManagerGlobal::CallbackHandler::onMessageReceived( } } +void CameraManagerGlobal::CallbackHandler::notifyParent() { + sp parent = mParent.promote(); + if (parent != nullptr) { + parent->onCallbackCalled(); + } +} + hardware::Return CameraManagerGlobal::CameraServiceListener::onStatusChanged( const CameraStatusAndId &statusAndId) { sp cm = mCameraManager.promote(); @@ -512,6 +552,7 @@ void CameraManagerGlobal::onStatusChangedLocked( msg->setPointer(kCallbackFpKey, (void *) cbFp); msg->setPointer(kContextKey, cb.mContext); msg->setString(kCameraIdKey, AString(cameraId.c_str())); + mPendingCallbackCnt++; msg->post(); } if (status == CameraDeviceStatus::STATUS_NOT_PRESENT) { @@ -577,6 +618,7 @@ void CameraManagerGlobal::onStatusChangedLocked( msg->setPointer(kContextKey, cb.mContext); msg->setString(kCameraIdKey, AString(cameraId.c_str())); msg->setString(kPhysicalCameraIdKey, AString(physicalCameraId.c_str())); + mPendingCallbackCnt++; msg->post(); } } diff --git a/camera/ndk/ndk_vendor/impl/ACameraManager.h b/camera/ndk/ndk_vendor/impl/ACameraManager.h index 36c8e2bd8f..85da3e9554 100644 --- a/camera/ndk/ndk_vendor/impl/ACameraManager.h +++ b/camera/ndk/ndk_vendor/impl/ACameraManager.h @@ -156,6 +156,12 @@ class CameraManagerGlobal final : public RefBase { ACameraManager_PhysicalCameraAvailabilityCallback mPhysicalCamUnavailable; void* mContext; }; + + android::Condition mCallbacksCond; + size_t mPendingCallbackCnt = 0; + void onCallbackCalled(); + void drainPendingCallbacksLocked(); + std::set mCallbacks; // definition of handler and message @@ -167,10 +173,15 @@ class CameraManagerGlobal final : public RefBase { static const char* kPhysicalCameraIdKey; static const char* kCallbackFpKey; static const char* kContextKey; + static const nsecs_t kCallbackDrainTimeout; class CallbackHandler : public AHandler { public: - CallbackHandler() {} + CallbackHandler(wp parent) : mParent(parent) {} void onMessageReceived(const sp &msg) override; + private: + wp mParent; + void notifyParent(); + void onMessageReceivedInternal(const sp &msg); }; sp mHandler; sp mCbLooper; // Looper thread where callbacks actually happen on