CameraService and Stagefright: Support AppOps

Camera:
- Signal to AppOpsService when camera usage starts and stops
- Listen to permissions revocations and act on them
- Currently just kill camera connection when permissions lost

Stagefright:
- Pass on client name, UID to camera as needed

Bug: 8181262
Change-Id: I9e33c9d05e9daa77dbb2d795045d08eb887ec8f0
diff --git a/services/camera/libcameraservice/Camera2Client.cpp b/services/camera/libcameraservice/Camera2Client.cpp
index e8b3b7f..38d6949 100644
--- a/services/camera/libcameraservice/Camera2Client.cpp
+++ b/services/camera/libcameraservice/Camera2Client.cpp
@@ -40,12 +40,14 @@
 
 Camera2Client::Camera2Client(const sp<CameraService>& cameraService,
         const sp<ICameraClient>& cameraClient,
+        const String16& clientPackageName,
         int cameraId,
         int cameraFacing,
         int clientPid,
+        uid_t clientUid,
         int servicePid):
-        Client(cameraService, cameraClient,
-                cameraId, cameraFacing, clientPid, servicePid),
+        Client(cameraService, cameraClient, clientPackageName,
+                cameraId, cameraFacing, clientPid, clientUid, servicePid),
         mSharedCameraClient(cameraClient),
         mParameters(cameraId, cameraFacing)
 {
@@ -73,6 +75,12 @@
     ALOGV("%s: Initializing client for camera %d", __FUNCTION__, mCameraId);
     status_t res;
 
+    // Verify ops permissions
+    res = startCameraOps();
+    if (res != OK) {
+        return res;
+    }
+
     res = mDevice->initialize(module);
     if (res != OK) {
         ALOGE("%s: Camera %d: unable to initialize device: %s (%d)",
@@ -741,8 +749,7 @@
 
     switch (state) {
         case Parameters::DISCONNECTED:
-            ALOGE("%s: Camera %d: Call before initialized",
-                    __FUNCTION__, mCameraId);
+            // Nothing to do.
             break;
         case Parameters::STOPPED:
         case Parameters::VIDEO_SNAPSHOT:
diff --git a/services/camera/libcameraservice/Camera2Client.h b/services/camera/libcameraservice/Camera2Client.h
index a4d4478..173b65e 100644
--- a/services/camera/libcameraservice/Camera2Client.h
+++ b/services/camera/libcameraservice/Camera2Client.h
@@ -72,9 +72,11 @@
 
     Camera2Client(const sp<CameraService>& cameraService,
             const sp<ICameraClient>& cameraClient,
+            const String16& clientPackageName,
             int cameraId,
             int cameraFacing,
             int clientPid,
+            uid_t clientUid,
             int servicePid);
     virtual ~Camera2Client();
 
diff --git a/services/camera/libcameraservice/CameraClient.cpp b/services/camera/libcameraservice/CameraClient.cpp
index f67c9f4..90f8f40 100644
--- a/services/camera/libcameraservice/CameraClient.cpp
+++ b/services/camera/libcameraservice/CameraClient.cpp
@@ -35,9 +35,12 @@
 
 CameraClient::CameraClient(const sp<CameraService>& cameraService,
         const sp<ICameraClient>& cameraClient,
-        int cameraId, int cameraFacing, int clientPid, int servicePid):
-        Client(cameraService, cameraClient,
-                cameraId, cameraFacing, clientPid, servicePid)
+        const String16& clientPackageName,
+        int cameraId, int cameraFacing,
+        int clientPid, int clientUid,
+        int servicePid):
+        Client(cameraService, cameraClient, clientPackageName,
+                cameraId, cameraFacing, clientPid, clientUid, servicePid)
 {
     int callingPid = getCallingPid();
     LOG1("CameraClient::CameraClient E (pid %d, id %d)", callingPid, cameraId);
@@ -57,10 +60,17 @@
 
 status_t CameraClient::initialize(camera_module_t *module) {
     int callingPid = getCallingPid();
+    status_t res;
+
     LOG1("CameraClient::initialize E (pid %d, id %d)", callingPid, mCameraId);
 
+    // Verify ops permissions
+    res = startCameraOps();
+    if (res != OK) {
+        return res;
+    }
+
     char camera_device_name[10];
-    status_t res;
     snprintf(camera_device_name, sizeof(camera_device_name), "%d", mCameraId);
 
     mHardware = new CameraHardwareInterface(camera_device_name);
diff --git a/services/camera/libcameraservice/CameraClient.h b/services/camera/libcameraservice/CameraClient.h
index 74829ce..00dc90c 100644
--- a/services/camera/libcameraservice/CameraClient.h
+++ b/services/camera/libcameraservice/CameraClient.h
@@ -53,9 +53,11 @@
     // Interface used by CameraService
     CameraClient(const sp<CameraService>& cameraService,
             const sp<ICameraClient>& cameraClient,
+            const String16& clientPackageName,
             int cameraId,
             int cameraFacing,
             int clientPid,
+            int clientUid,
             int servicePid);
     ~CameraClient();
 
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index eb8bc05..ec1c3f0 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -22,6 +22,7 @@
 #include <sys/types.h>
 #include <pthread.h>
 
+#include <binder/AppOpsManager.h>
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 #include <binder/MemoryBase.h>
@@ -72,7 +73,7 @@
 static CameraService *gCameraService;
 
 CameraService::CameraService()
-:mSoundRef(0), mModule(0)
+    :mSoundRef(0), mModule(0)
 {
     ALOGI("CameraService started (pid=%d)", getpid());
     gCameraService = this;
@@ -155,10 +156,27 @@
 }
 
 sp<ICamera> CameraService::connect(
-        const sp<ICameraClient>& cameraClient, int cameraId) {
+        const sp<ICameraClient>& cameraClient,
+        int cameraId,
+        const String16& clientPackageName,
+        int clientUid) {
+
+    String8 clientName8(clientPackageName);
     int callingPid = getCallingPid();
 
-    LOG1("CameraService::connect E (pid %d, id %d)", callingPid, cameraId);
+    LOG1("CameraService::connect E (pid %d \"%s\", id %d)", callingPid,
+            clientName8.string(), cameraId);
+
+    if (clientUid == USE_CALLING_UID) {
+        clientUid = getCallingUid();
+    } else {
+        // We only trust our own process to forward client UIDs
+        if (callingPid != getpid()) {
+            ALOGE("CameraService::connect X (pid %d) rejected (don't trust clientUid)",
+                    callingPid);
+            return NULL;
+        }
+    }
 
     if (!mModule) {
         ALOGE("Camera HAL module not loaded");
@@ -208,8 +226,10 @@
     would be fine
     */
     if (mBusy[cameraId]) {
-        ALOGW("CameraService::connect X (pid %d) rejected"
-                " (camera %d is still busy).", callingPid, cameraId);
+
+        ALOGW("CameraService::connect X (pid %d, \"%s\") rejected"
+                " (camera %d is still busy).", callingPid,
+                clientName8.string(), cameraId);
         return NULL;
     }
 
@@ -218,13 +238,15 @@
 
     switch(deviceVersion) {
       case CAMERA_DEVICE_API_VERSION_1_0:
-        client = new CameraClient(this, cameraClient, cameraId,
-                facing, callingPid, getpid());
+        client = new CameraClient(this, cameraClient,
+                clientPackageName, cameraId,
+                facing, callingPid, clientUid, getpid());
         break;
       case CAMERA_DEVICE_API_VERSION_2_0:
       case CAMERA_DEVICE_API_VERSION_2_1:
-        client = new Camera2Client(this, cameraClient, cameraId,
-                facing, callingPid, getpid());
+        client = new Camera2Client(this, cameraClient,
+                clientPackageName, cameraId,
+                facing, callingPid, clientUid, getpid());
         break;
       case -1:
         ALOGE("Invalid camera id %d", cameraId);
@@ -283,8 +305,8 @@
         break;
       case CAMERA_DEVICE_API_VERSION_2_0:
       case CAMERA_DEVICE_API_VERSION_2_1:
-        client = new ProCamera2Client(this, cameraCb, cameraId,
-                facing, callingPid, getpid());
+        client = new ProCamera2Client(this, cameraCb, String16(),
+                cameraId, facing, callingPid, USE_CALLING_UID, getpid());
         break;
       case -1:
         ALOGE("Invalid camera id %d", cameraId);
@@ -302,7 +324,8 @@
 
     cameraCb->asBinder()->linkToDeath(this);
 
-    LOG1("CameraService::connect X (id %d, this pid is %d)", cameraId, getpid());
+    LOG1("CameraService::connectPro X (id %d, this pid is %d)", cameraId,
+            getpid());
     return client;
 
 
@@ -522,10 +545,15 @@
 
 CameraService::Client::Client(const sp<CameraService>& cameraService,
         const sp<ICameraClient>& cameraClient,
-        int cameraId, int cameraFacing, int clientPid, int servicePid) :
+        const String16& clientPackageName,
+        int cameraId, int cameraFacing,
+        int clientPid, uid_t clientUid,
+        int servicePid) :
         CameraService::BasicClient(cameraService, cameraClient->asBinder(),
-                                   cameraId, cameraFacing,
-                                   clientPid, servicePid)
+                clientPackageName,
+                cameraId, cameraFacing,
+                clientPid, clientUid,
+                servicePid)
 {
     int callingPid = getCallingPid();
     LOG1("Client::Client E (pid %d, id %d)", callingPid, cameraId);
@@ -534,6 +562,7 @@
 
     cameraService->setCameraBusy(cameraId);
     cameraService->loadSound();
+
     LOG1("Client::Client X (pid %d, id %d)", callingPid, cameraId);
 }
 
@@ -542,23 +571,27 @@
     mDestructionStarted = true;
 
     mCameraService->releaseSound();
-
+    finishCameraOps();
     // unconditionally disconnect. function is idempotent
     Client::disconnect();
 }
 
 CameraService::BasicClient::BasicClient(const sp<CameraService>& cameraService,
-                                   const sp<IBinder>& remoteCallback,
-                                   int cameraId, int cameraFacing,
-                                   int clientPid, int servicePid)
+        const sp<IBinder>& remoteCallback,
+        const String16& clientPackageName,
+        int cameraId, int cameraFacing,
+        int clientPid, uid_t clientUid,
+        int servicePid):
+        mClientPackageName(clientPackageName)
 {
     mCameraService = cameraService;
     mRemoteCallback = remoteCallback;
     mCameraId = cameraId;
     mCameraFacing = cameraFacing;
     mClientPid = clientPid;
+    mClientUid = clientUid;
     mServicePid = servicePid;
-
+    mOpsActive = false;
     mDestructionStarted = false;
 }
 
@@ -570,6 +603,66 @@
     mCameraService->removeClientByRemote(mRemoteCallback);
 }
 
+status_t CameraService::BasicClient::startCameraOps() {
+    int32_t res;
+
+    mOpsCallback = new OpsCallback(this);
+
+    mAppOpsManager.startWatchingMode(AppOpsManager::OP_CAMERA,
+            mClientPackageName, mOpsCallback);
+    res = mAppOpsManager.startOp(AppOpsManager::OP_CAMERA,
+            mClientUid, mClientPackageName);
+
+    if (res != AppOpsManager::MODE_ALLOWED) {
+        ALOGI("Camera %d: Access for \"%s\" has been revoked",
+                mCameraId, String8(mClientPackageName).string());
+        return PERMISSION_DENIED;
+    }
+    mOpsActive = true;
+    return OK;
+}
+
+status_t CameraService::BasicClient::finishCameraOps() {
+    if (mOpsActive) {
+        mAppOpsManager.finishOp(AppOpsManager::OP_CAMERA, mClientUid,
+                mClientPackageName);
+        mOpsActive = false;
+    }
+    mAppOpsManager.stopWatchingMode(mOpsCallback);
+    mOpsCallback.clear();
+
+    return OK;
+}
+
+void CameraService::BasicClient::opChanged(int32_t op, const String16& packageName) {
+    String8 name(packageName);
+    String8 myName(mClientPackageName);
+
+    if (op != AppOpsManager::OP_CAMERA) {
+        ALOGW("Unexpected app ops notification received: %d", op);
+        return;
+    }
+
+    int32_t res;
+    res = mAppOpsManager.checkOp(AppOpsManager::OP_CAMERA,
+            mClientUid, mClientPackageName);
+    ALOGV("checkOp returns: %d, %s ", res,
+            res == AppOpsManager::MODE_ALLOWED ? "ALLOWED" :
+            res == AppOpsManager::MODE_IGNORED ? "IGNORED" :
+            res == AppOpsManager::MODE_ERRORED ? "ERRORED" :
+            "UNKNOWN");
+
+    if (res != AppOpsManager::MODE_ALLOWED) {
+        ALOGI("Camera %d: Access for \"%s\" revoked", mCameraId,
+                myName.string());
+        // Reset the client PID to allow server-initiated disconnect,
+        // and to prevent further calls by client.
+        mClientPid = getCallingPid();
+        notifyError();
+        disconnect();
+    }
+}
+
 // ----------------------------------------------------------------------------
 
 Mutex* CameraService::Client::getClientLockFromCookie(void* user) {
@@ -592,25 +685,43 @@
     return client;
 }
 
+void CameraService::Client::notifyError() {
+    mCameraClient->notifyCallback(CAMERA_MSG_ERROR, CAMERA_ERROR_RELEASED, 0);
+}
+
 // NOTE: function is idempotent
 void CameraService::Client::disconnect() {
     BasicClient::disconnect();
     mCameraService->setCameraFree(mCameraId);
 }
 
+CameraService::Client::OpsCallback::OpsCallback(wp<BasicClient> client):
+        mClient(client) {
+}
+
+void CameraService::Client::OpsCallback::opChanged(int32_t op,
+        const String16& packageName) {
+    sp<BasicClient> client = mClient.promote();
+    if (client != NULL) {
+        client->opChanged(op, packageName);
+    }
+}
+
 // ----------------------------------------------------------------------------
 //                  IProCamera
 // ----------------------------------------------------------------------------
 
 CameraService::ProClient::ProClient(const sp<CameraService>& cameraService,
-                const sp<IProCameraCallbacks>& remoteCallback,
-                int cameraId,
-                int cameraFacing,
-                int clientPid,
-                int servicePid)
- :       CameraService::BasicClient(cameraService, remoteCallback->asBinder(),
-                                   cameraId, cameraFacing,
-                                   clientPid, servicePid)
+        const sp<IProCameraCallbacks>& remoteCallback,
+        const String16& clientPackageName,
+        int cameraId,
+        int cameraFacing,
+        int clientPid,
+        uid_t clientUid,
+        int servicePid)
+        : CameraService::BasicClient(cameraService, remoteCallback->asBinder(),
+                clientPackageName, cameraId, cameraFacing,
+                clientPid,  clientUid, servicePid)
 {
     mRemoteCallback = remoteCallback;
 }
@@ -683,6 +794,10 @@
     return INVALID_OPERATION;
 }
 
+void CameraService::ProClient::notifyError() {
+    ALOGE("%s: not implemented yet", __FUNCTION__);
+}
+
 // ----------------------------------------------------------------------------
 
 static const int kDumpLockRetries = 50;
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 9e0f62a..b017505 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -19,7 +19,9 @@
 #define ANDROID_SERVERS_CAMERA_CAMERASERVICE_H
 
 #include <utils/Vector.h>
+#include <binder/AppOpsManager.h>
 #include <binder/BinderService.h>
+#include <binder/IAppOpsCallback.h>
 #include <camera/ICameraService.h>
 #include <hardware/camera.h>
 
@@ -54,9 +56,11 @@
     virtual int32_t     getNumberOfCameras();
     virtual status_t    getCameraInfo(int cameraId,
                                       struct CameraInfo* cameraInfo);
-    virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient, int cameraId);
-    virtual sp<IProCameraUser>
-                        connect(const sp<IProCameraCallbacks>& cameraCb, int cameraId);
+
+    virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient, int cameraId,
+            const String16& clientPackageName, int clientUid);
+    virtual sp<IProCameraUser> connect(const sp<IProCameraCallbacks>& cameraCb,
+            int cameraId);
 
     // Extra permissions checks
     virtual status_t    onTransact(uint32_t code, const Parcel& data,
@@ -100,9 +104,11 @@
     protected:
         BasicClient(const sp<CameraService>& cameraService,
                 const sp<IBinder>& remoteCallback,
+                const String16& clientPackageName,
                 int cameraId,
                 int cameraFacing,
                 int clientPid,
+                uid_t clientUid,
                 int servicePid);
 
         virtual ~BasicClient();
@@ -117,12 +123,41 @@
         sp<CameraService>               mCameraService;  // immutable after constructor
         int                             mCameraId;       // immutable after constructor
         int                             mCameraFacing;   // immutable after constructor
+        const String16                  mClientPackageName;
         pid_t                           mClientPid;
+        uid_t                           mClientUid;      // immutable after constructor
         pid_t                           mServicePid;     // immutable after constructor
 
         // - The app-side Binder interface to receive callbacks from us
         wp<IBinder>                     mRemoteCallback; // immutable after constructor
-    };
+
+        // permissions management
+        status_t                        startCameraOps();
+        status_t                        finishCameraOps();
+
+        // Notify client about a fatal error
+        virtual void                    notifyError() = 0;
+    private:
+        AppOpsManager                   mAppOpsManager;
+
+        class OpsCallback : public BnAppOpsCallback {
+        public:
+            OpsCallback(wp<BasicClient> client);
+            virtual void opChanged(int32_t op, const String16& packageName);
+
+        private:
+            wp<BasicClient> mClient;
+
+        }; // class OpsCallback
+
+        sp<OpsCallback> mOpsCallback;
+        // Track whether startCameraOps was called successfully, to avoid
+        // finishing what we didn't start.
+        bool            mOpsActive;
+
+        // IAppOpsCallback interface, indirected through opListener
+        virtual void opChanged(int32_t op, const String16& packageName);
+    }; // class BasicClient
 
     class Client : public BnCamera, public BasicClient
     {
@@ -153,9 +188,11 @@
         // Interface used by CameraService
         Client(const sp<CameraService>& cameraService,
                 const sp<ICameraClient>& cameraClient,
+                const String16& clientPackageName,
                 int cameraId,
                 int cameraFacing,
                 int clientPid,
+                uid_t clientUid,
                 int servicePid);
         ~Client();
 
@@ -169,19 +206,24 @@
         // convert client from cookie. Client lock should be acquired before getting Client.
         static Client*       getClientFromCookie(void* user);
 
+        virtual void         notifyError();
+
         // Initialized in constructor
 
         // - The app-side Binder interface to receive callbacks from us
         sp<ICameraClient>               mCameraClient;
-    };
+
+    }; // class Client
 
     class ProClient : public BnProCameraUser, public BasicClient {
     public:
         ProClient(const sp<CameraService>& cameraService,
                 const sp<IProCameraCallbacks>& remoteCallback,
+                const String16& clientPackageName,
                 int cameraId,
                 int cameraFacing,
                 int clientPid,
+                uid_t clientUid,
                 int servicePid);
 
         virtual ~ProClient();
@@ -217,9 +259,10 @@
         virtual status_t      cancelStream(int streamId);
 
     protected:
-        sp<IProCameraCallbacks> mRemoteCallback;
+        virtual void          notifyError();
 
-    };
+        sp<IProCameraCallbacks> mRemoteCallback;
+    }; // class ProClient
 
 private:
 
diff --git a/services/camera/libcameraservice/ProCamera2Client.cpp b/services/camera/libcameraservice/ProCamera2Client.cpp
index c264e2a..eda3012 100644
--- a/services/camera/libcameraservice/ProCamera2Client.cpp
+++ b/services/camera/libcameraservice/ProCamera2Client.cpp
@@ -43,12 +43,14 @@
 
 ProCamera2Client::ProCamera2Client(const sp<CameraService>& cameraService,
         const sp<IProCameraCallbacks>& remoteCallback,
+        const String16& clientPackageName,
         int cameraId,
         int cameraFacing,
         int clientPid,
+        uid_t clientUid,
         int servicePid):
-        ProClient(cameraService, remoteCallback,
-                cameraId, cameraFacing, clientPid, servicePid),
+        ProClient(cameraService, remoteCallback, clientPackageName,
+                cameraId, cameraFacing, clientPid, clientUid, servicePid),
         mSharedCameraCallbacks(remoteCallback)
 {
     ATRACE_CALL();
diff --git a/services/camera/libcameraservice/ProCamera2Client.h b/services/camera/libcameraservice/ProCamera2Client.h
index cd0a2ae..9f514f4 100644
--- a/services/camera/libcameraservice/ProCamera2Client.h
+++ b/services/camera/libcameraservice/ProCamera2Client.h
@@ -77,9 +77,11 @@
 
     ProCamera2Client(const sp<CameraService>& cameraService,
             const sp<IProCameraCallbacks>& remoteCallback,
+            const String16& clientPackageName,
             int cameraId,
             int cameraFacing,
             int clientPid,
+            uid_t clientUid,
             int servicePid);
     virtual ~ProCamera2Client();