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
diff --git a/camera/aidl/android/hardware/ICameraService.aidl b/camera/aidl/android/hardware/ICameraService.aidl
index 0e969c7..3e8992a 100644
--- a/camera/aidl/android/hardware/ICameraService.aidl
+++ b/camera/aidl/android/hardware/ICameraService.aidl
@@ -108,7 +108,7 @@
      *
      * 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 f871ce4..e9dcbdb 100644
--- a/camera/aidl/android/hardware/ICameraServiceListener.aidl
+++ b/camera/aidl/android/hardware/ICameraServiceListener.aidl
@@ -76,4 +76,11 @@
     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 55bfa7e..c3407f0 100644
--- a/camera/ndk/impl/ACameraManager.h
+++ b/camera/ndk/impl/ACameraManager.h
@@ -86,6 +86,11 @@
             return binder::Status::ok();
         }
 
+        // Access priority API not implemented yet
+        virtual binder::Status onCameraAccessPrioritiesChanged() {
+            return binder::Status::ok();
+        }
+
       private:
         const wp<CameraManagerGlobal> mCameraManager;
     };
diff --git a/camera/tests/CameraBinderTests.cpp b/camera/tests/CameraBinderTests.cpp
index 8534b28..8fe029a 100644
--- a/camera/tests/CameraBinderTests.cpp
+++ b/camera/tests/CameraBinderTests.cpp
@@ -90,6 +90,11 @@
         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 e06897f..62ec955 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -227,7 +227,7 @@
     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 @@
     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 @@
 
     {
         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> 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 @@
     {
         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 @@
     sCameraService->updateProxyDeviceState(ICameraServiceProxy::CAMERA_STATE_OPEN,
             mCameraIdStr, mCameraFacing, mClientPackageName, apiLevel);
 
+    sCameraService->mUidPolicy->registerMonitorUid(mClientUid);
+
     return OK;
 }
 
@@ -2433,6 +2459,8 @@
     }
     mOpsCallback.clear();
 
+    sCameraService->mUidPolicy->unregisterMonitorUid(mClientUid);
+
     return OK;
 }
 
@@ -2523,7 +2551,7 @@
     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::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<CameraService> 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_t, std::pair<int32_t, size_t>> (uid,
+                    std::pair<int32_t, size_t> (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 @@
                           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 cf0cef8..65727ec 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -179,6 +179,9 @@
             /*out*/
             std::vector<hardware::CameraStatus>* cameraStatuses, bool isVendor = false);
 
+    // Monitored UIDs availability notification
+    void                notifyMonitoredUids();
+
     /////////////////////////////////////////////////////////////////////
     // Client functionality
 
@@ -543,11 +546,14 @@
         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<IBinder> &who);
     private:
@@ -558,6 +564,8 @@
         bool mRegistered;
         wp<CameraService> mService;
         std::unordered_set<uid_t> mActiveUids;
+        // Monitored uid map to cached procState and refCount pair
+        std::unordered_map<uid_t, std::pair<int32_t, size_t>> mMonitoredUids;
         std::unordered_map<uid_t, bool> mOverrideUids;
     }; // class UidPolicy
 
@@ -790,8 +798,33 @@
 
     sp<CameraProviderManager> mCameraProviderManager;
 
+    class ServiceListener : public virtual IBinder::DeathRecipient {
+        public:
+            ServiceListener(sp<CameraService> parent, sp<hardware::ICameraServiceListener> listener,
+                    int uid) : mParent(parent), mListener(listener), mListenerUid(uid) {}
+
+            status_t initialize() {
+                return IInterface::asBinder(mListener)->linkToDeath(this);
+            }
+
+            virtual void binderDied(const wp<IBinder> &/*who*/) {
+                auto parent = mParent.promote();
+                if (parent.get() != nullptr) {
+                    parent->removeListener(mListener);
+                }
+            }
+
+            int getListenerUid() { return mListenerUid; }
+            sp<hardware::ICameraServiceListener> getListener() { return mListener; }
+
+        private:
+            wp<CameraService> mParent;
+            sp<hardware::ICameraServiceListener> mListener;
+            int mListenerUid;
+    };
+
     // Guarded by mStatusListenerMutex
-    std::vector<std::pair<bool, sp<hardware::ICameraServiceListener>>> mListenerList;
+    std::vector<std::pair<bool, sp<ServiceListener>>> mListenerList;
 
     Mutex       mStatusListenerLock;
 
diff --git a/services/camera/libcameraservice/hidl/AidlCameraServiceListener.h b/services/camera/libcameraservice/hidl/AidlCameraServiceListener.h
index ca9143d..0f6be79 100644
--- a/services/camera/libcameraservice/hidl/AidlCameraServiceListener.h
+++ b/services/camera/libcameraservice/hidl/AidlCameraServiceListener.h
@@ -50,6 +50,10 @@
 
     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