From 53722fa75c1b74b7accdf2293fda9948beaeb461 Mon Sep 17 00:00:00 2001 From: Emilian Peev Date: Fri, 22 Feb 2019 17:47:20 -0800 Subject: [PATCH] Camera: Update listeners about permission changes Camera service listeners must be able to receive information about camera access permission changes. Bug: 121379978 Test: Camera CTS Change-Id: I2e13fdd35a267901a3caa0e0ce78ab1cea83e7ab --- .../aidl/android/hardware/ICameraService.aidl | 2 +- .../hardware/ICameraServiceListener.aidl | 7 ++ camera/ndk/impl/ACameraManager.h | 5 ++ camera/tests/CameraBinderTests.cpp | 5 ++ .../camera/libcameraservice/CameraService.cpp | 88 +++++++++++++++++-- .../camera/libcameraservice/CameraService.h | 37 +++++++- .../hidl/AidlCameraServiceListener.h | 4 + 7 files changed, 138 insertions(+), 10 deletions(-) diff --git a/camera/aidl/android/hardware/ICameraService.aidl b/camera/aidl/android/hardware/ICameraService.aidl index 0e969c7662..3e8992aaaf 100644 --- a/camera/aidl/android/hardware/ICameraService.aidl +++ b/camera/aidl/android/hardware/ICameraService.aidl @@ -108,7 +108,7 @@ interface ICameraService * * Also returns the set of currently-known camera IDs and state of each device. * Adding a listener will trigger the torch status listener to fire for all - * devices that have a flash unit + * devices that have a flash unit. */ CameraStatus[] addListener(ICameraServiceListener listener); diff --git a/camera/aidl/android/hardware/ICameraServiceListener.aidl b/camera/aidl/android/hardware/ICameraServiceListener.aidl index f871ce41c9..e9dcbdb6f6 100644 --- a/camera/aidl/android/hardware/ICameraServiceListener.aidl +++ b/camera/aidl/android/hardware/ICameraServiceListener.aidl @@ -76,4 +76,11 @@ interface ICameraServiceListener const int TORCH_STATUS_UNKNOWN = -1; oneway void onTorchStatusChanged(int status, String cameraId); + + /** + * Notify registered clients about camera access priority changes. + * Clients which were previously unable to open a certain camera device + * can retry after receiving this callback. + */ + oneway void onCameraAccessPrioritiesChanged(); } diff --git a/camera/ndk/impl/ACameraManager.h b/camera/ndk/impl/ACameraManager.h index 55bfa7ea54..c3407f0395 100644 --- a/camera/ndk/impl/ACameraManager.h +++ b/camera/ndk/impl/ACameraManager.h @@ -86,6 +86,11 @@ class CameraManagerGlobal final : public RefBase { return binder::Status::ok(); } + // Access priority API not implemented yet + virtual binder::Status onCameraAccessPrioritiesChanged() { + return binder::Status::ok(); + } + private: const wp mCameraManager; }; diff --git a/camera/tests/CameraBinderTests.cpp b/camera/tests/CameraBinderTests.cpp index 8534b289ea..8fe029aea7 100644 --- a/camera/tests/CameraBinderTests.cpp +++ b/camera/tests/CameraBinderTests.cpp @@ -90,6 +90,11 @@ public: return binder::Status::ok(); }; + virtual binder::Status onCameraAccessPrioritiesChanged() { + // No op + return binder::Status::ok(); + } + bool waitForNumCameras(size_t num) const { Mutex::Autolock l(mLock); diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp index e06897f492..62ec955732 100644 --- a/services/camera/libcameraservice/CameraService.cpp +++ b/services/camera/libcameraservice/CameraService.cpp @@ -227,7 +227,7 @@ void CameraService::broadcastTorchModeStatus(const String8& cameraId, TorchModeS Mutex::Autolock lock(mStatusListenerLock); for (auto& i : mListenerList) { - i.second->onTorchStatusChanged(mapToInterface(status), String16{cameraId}); + i.second->getListener()->onTorchStatusChanged(mapToInterface(status), String16{cameraId}); } } @@ -1654,6 +1654,18 @@ Status CameraService::notifySystemEvent(int32_t eventId, return Status::ok(); } +void CameraService::notifyMonitoredUids() { + Mutex::Autolock lock(mStatusListenerLock); + + for (const auto& it : mListenerList) { + auto ret = it.second->getListener()->onCameraAccessPrioritiesChanged(); + if (!ret.isOk()) { + ALOGE("%s: Failed to trigger permission callback: %d", __FUNCTION__, + ret.exceptionCode()); + } + } +} + Status CameraService::notifyDeviceStateChange(int64_t newState) { const int pid = CameraThreadState::getCallingPid(); const int selfPid = getpid(); @@ -1721,15 +1733,25 @@ Status CameraService::addListenerHelper(const sp& listen { Mutex::Autolock lock(mStatusListenerLock); - for (auto& it : mListenerList) { - if (IInterface::asBinder(it.second) == IInterface::asBinder(listener)) { + for (const auto &it : mListenerList) { + if (IInterface::asBinder(it.second->getListener()) == IInterface::asBinder(listener)) { ALOGW("%s: Tried to add listener %p which was already subscribed", __FUNCTION__, listener.get()); return STATUS_ERROR(ERROR_ALREADY_EXISTS, "Listener already registered"); } } - mListenerList.emplace_back(isVendorListener, listener); + auto clientUid = CameraThreadState::getCallingUid(); + sp serviceListener = new ServiceListener(this, listener, clientUid); + auto ret = serviceListener->initialize(); + if (ret != NO_ERROR) { + String8 msg = String8::format("Failed to initialize service listener: %s (%d)", + strerror(-ret), ret); + ALOGE("%s: %s", __FUNCTION__, msg.string()); + return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT, msg.string()); + } + mListenerList.emplace_back(isVendorListener, serviceListener); + mUidPolicy->registerMonitorUid(clientUid); } /* Collect current devices and status */ @@ -1776,7 +1798,9 @@ Status CameraService::removeListener(const sp& listener) { Mutex::Autolock lock(mStatusListenerLock); for (auto it = mListenerList.begin(); it != mListenerList.end(); it++) { - if (IInterface::asBinder(it->second) == IInterface::asBinder(listener)) { + if (IInterface::asBinder(it->second->getListener()) == IInterface::asBinder(listener)) { + mUidPolicy->unregisterMonitorUid(it->second->getListenerUid()); + IInterface::asBinder(listener)->unlinkToDeath(it->second); mListenerList.erase(it); return Status::ok(); } @@ -2396,6 +2420,8 @@ status_t CameraService::BasicClient::startCameraOps() { sCameraService->updateProxyDeviceState(ICameraServiceProxy::CAMERA_STATE_OPEN, mCameraIdStr, mCameraFacing, mClientPackageName, apiLevel); + sCameraService->mUidPolicy->registerMonitorUid(mClientUid); + return OK; } @@ -2433,6 +2459,8 @@ status_t CameraService::BasicClient::finishCameraOps() { } mOpsCallback.clear(); + sCameraService->mUidPolicy->unregisterMonitorUid(mClientUid); + return OK; } @@ -2523,7 +2551,7 @@ void CameraService::UidPolicy::registerSelf() { if (mRegistered) return; am.registerUidObserver(this, ActivityManager::UID_OBSERVER_GONE | ActivityManager::UID_OBSERVER_IDLE - | ActivityManager::UID_OBSERVER_ACTIVE, + | ActivityManager::UID_OBSERVER_ACTIVE | ActivityManager::UID_OBSERVER_PROCSTATE, ActivityManager::PROCESS_STATE_UNKNOWN, String16("cameraserver")); status_t res = am.linkToDeath(this); @@ -2569,6 +2597,51 @@ void CameraService::UidPolicy::onUidIdle(uid_t uid, bool /* disabled */) { } } +void CameraService::UidPolicy::onUidStateChanged(uid_t uid, int32_t procState, + int64_t /*procStateSeq*/) { + bool procStateChange = false; + { + Mutex::Autolock _l(mUidLock); + if ((mMonitoredUids.find(uid) != mMonitoredUids.end()) && + (mMonitoredUids[uid].first != procState)) { + mMonitoredUids[uid].first = procState; + procStateChange = true; + } + } + + if (procStateChange) { + sp service = mService.promote(); + if (service != nullptr) { + service->notifyMonitoredUids(); + } + } +} + +void CameraService::UidPolicy::registerMonitorUid(uid_t uid) { + Mutex::Autolock _l(mUidLock); + auto it = mMonitoredUids.find(uid); + if (it != mMonitoredUids.end()) { + it->second.second++; + } else { + mMonitoredUids.emplace( + std::pair> (uid, + std::pair (ActivityManager::PROCESS_STATE_NONEXISTENT, 1))); + } +} + +void CameraService::UidPolicy::unregisterMonitorUid(uid_t uid) { + Mutex::Autolock _l(mUidLock); + auto it = mMonitoredUids.find(uid); + if (it != mMonitoredUids.end()) { + it->second.second--; + if (it->second.second == 0) { + mMonitoredUids.erase(it); + } + } else { + ALOGE("%s: Trying to unregister uid: %d which is not monitored!", __FUNCTION__, uid); + } +} + bool CameraService::UidPolicy::isUidActive(uid_t uid, String16 callingPackage) { Mutex::Autolock _l(mUidLock); return isUidActiveLocked(uid, callingPackage); @@ -3118,7 +3191,8 @@ void CameraService::updateStatus(StatusInternal status, const String8& cameraId, cameraId.c_str()); continue; } - listener.second->onStatusChanged(mapToInterface(status), String16(cameraId)); + listener.second->getListener()->onStatusChanged(mapToInterface(status), + String16(cameraId)); } }); } diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h index cf0cef84ec..65727ecec5 100644 --- a/services/camera/libcameraservice/CameraService.h +++ b/services/camera/libcameraservice/CameraService.h @@ -179,6 +179,9 @@ public: /*out*/ std::vector* cameraStatuses, bool isVendor = false); + // Monitored UIDs availability notification + void notifyMonitoredUids(); + ///////////////////////////////////////////////////////////////////// // Client functionality @@ -543,11 +546,14 @@ private: void onUidGone(uid_t uid, bool disabled); void onUidActive(uid_t uid); void onUidIdle(uid_t uid, bool disabled); - void onUidStateChanged(uid_t uid __unused, int32_t procState __unused, int64_t procStateSeq __unused) {} + void onUidStateChanged(uid_t uid, int32_t procState, int64_t procStateSeq); void addOverrideUid(uid_t uid, String16 callingPackage, bool active); void removeOverrideUid(uid_t uid, String16 callingPackage); + void registerMonitorUid(uid_t uid); + void unregisterMonitorUid(uid_t uid); + // IBinder::DeathRecipient implementation virtual void binderDied(const wp &who); private: @@ -558,6 +564,8 @@ private: bool mRegistered; wp mService; std::unordered_set mActiveUids; + // Monitored uid map to cached procState and refCount pair + std::unordered_map> mMonitoredUids; std::unordered_map mOverrideUids; }; // class UidPolicy @@ -790,8 +798,33 @@ private: sp mCameraProviderManager; + class ServiceListener : public virtual IBinder::DeathRecipient { + public: + ServiceListener(sp parent, sp listener, + int uid) : mParent(parent), mListener(listener), mListenerUid(uid) {} + + status_t initialize() { + return IInterface::asBinder(mListener)->linkToDeath(this); + } + + virtual void binderDied(const wp &/*who*/) { + auto parent = mParent.promote(); + if (parent.get() != nullptr) { + parent->removeListener(mListener); + } + } + + int getListenerUid() { return mListenerUid; } + sp getListener() { return mListener; } + + private: + wp mParent; + sp mListener; + int mListenerUid; + }; + // Guarded by mStatusListenerMutex - std::vector>> mListenerList; + std::vector>> mListenerList; Mutex mStatusListenerLock; diff --git a/services/camera/libcameraservice/hidl/AidlCameraServiceListener.h b/services/camera/libcameraservice/hidl/AidlCameraServiceListener.h index ca9143d12c..0f6be79be0 100644 --- a/services/camera/libcameraservice/hidl/AidlCameraServiceListener.h +++ b/services/camera/libcameraservice/hidl/AidlCameraServiceListener.h @@ -50,6 +50,10 @@ struct H2BCameraServiceListener : virtual ::android::binder::Status onTorchStatusChanged( int32_t status, const ::android::String16& cameraId) override; + virtual binder::Status onCameraAccessPrioritiesChanged() { + // TODO: no implementation yet. + return binder::Status::ok(); + } }; } // implementation