From 634a51509ee50475f3e9f8ccf897e90fc72ded31 Mon Sep 17 00:00:00 2001 From: Igor Murashkin Date: Wed, 20 Feb 2013 17:15:11 -0800 Subject: Camera: Add ProCamera private binder interface for an API2-light functionality Change-Id: I2af7a807c99df75ea659e6e6acc9c4fca6a56274 --- services/camera/libcameraservice/CameraService.cpp | 296 +++++++++++++++++++-- services/camera/libcameraservice/CameraService.h | 147 ++++++++-- 2 files changed, 390 insertions(+), 53 deletions(-) (limited to 'services/camera') diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp index b1c594a..4941965 100644 --- a/services/camera/libcameraservice/CameraService.cpp +++ b/services/camera/libcameraservice/CameraService.cpp @@ -79,6 +79,8 @@ CameraService::CameraService() void CameraService::onFirstRef() { + LOG1("CameraService::onFirstRef"); + BnCameraService::onFirstRef(); if (hw_get_module(CAMERA_HARDWARE_MODULE_ID, @@ -131,6 +133,26 @@ status_t CameraService::getCameraInfo(int cameraId, return rc; } +int CameraService::getDeviceVersion(int cameraId, int* facing) { + struct camera_info info; + if (mModule->get_camera_info(cameraId, &info) != OK) { + return -1; + } + + int deviceVersion; + if (mModule->common.module_api_version >= CAMERA_MODULE_API_VERSION_2_0) { + deviceVersion = info.device_version; + } else { + deviceVersion = CAMERA_DEVICE_API_VERSION_1_0; + } + + if (facing) { + *facing = info.facing; + } + + return deviceVersion; +} + sp CameraService::connect( const sp& cameraClient, int cameraId) { int callingPid = getCallingPid(); @@ -175,34 +197,96 @@ sp CameraService::connect( mClient[cameraId].clear(); } + /* + mBusy is set to false as the last step of the Client destructor, + after which it is guaranteed that the Client destructor has finished ( + including any inherited destructors) + + We only need this for a Client subclasses since we don't allow + multiple Clents to be opened concurrently, but multiple BasicClient + would be fine + */ if (mBusy[cameraId]) { ALOGW("CameraService::connect X (pid %d) rejected" " (camera %d is still busy).", callingPid, cameraId); return NULL; } - struct camera_info info; - if (mModule->get_camera_info(cameraId, &info) != OK) { + int facing = -1; + int deviceVersion = getDeviceVersion(cameraId, &facing); + + switch(deviceVersion) { + case CAMERA_DEVICE_API_VERSION_1_0: + client = new CameraClient(this, cameraClient, cameraId, + facing, callingPid, 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()); + break; + case -1: ALOGE("Invalid camera id %d", cameraId); return NULL; + default: + ALOGE("Unknown camera device HAL version: %d", deviceVersion); + return NULL; } - int deviceVersion; - if (mModule->common.module_api_version == CAMERA_MODULE_API_VERSION_2_0) { - deviceVersion = info.device_version; - } else { - deviceVersion = CAMERA_DEVICE_API_VERSION_1_0; + if (client->initialize(mModule) != OK) { + return NULL; + } + + cameraClient->asBinder()->linkToDeath(this); + + mClient[cameraId] = client; + LOG1("CameraService::connect X (id %d, this pid is %d)", cameraId, getpid()); + return client; +} + +sp CameraService::connect( + const sp& cameraCb, + int cameraId) +{ + int callingPid = getCallingPid(); + + LOG1("CameraService::connectPro E (pid %d, id %d)", callingPid, cameraId); + + if (!mModule) { + ALOGE("Camera HAL module not loaded"); + return NULL; + } + + sp client; + if (cameraId < 0 || cameraId >= mNumberOfCameras) { + ALOGE("CameraService::connectPro X (pid %d) rejected (invalid cameraId %d).", + callingPid, cameraId); + return NULL; } + char value[PROPERTY_VALUE_MAX]; + property_get("sys.secpolicy.camera.disabled", value, "0"); + if (strcmp(value, "1") == 0) { + // Camera is disabled by DevicePolicyManager. + ALOGI("Camera is disabled. connect X (pid %d) rejected", callingPid); + return NULL; + } + + int facing = -1; + int deviceVersion = getDeviceVersion(cameraId, &facing); + switch(deviceVersion) { case CAMERA_DEVICE_API_VERSION_1_0: - client = new CameraClient(this, cameraClient, cameraId, - info.facing, callingPid, getpid()); + ALOGE("Camera id %d uses HALv1, doesn't support ProCamera", cameraId); + return NULL; break; case CAMERA_DEVICE_API_VERSION_2_0: - client = new Camera2Client(this, cameraClient, cameraId, - info.facing, callingPid, getpid()); + client = new ProClient(this, cameraCb, cameraId, + facing, callingPid, getpid()); break; + case -1: + ALOGE("Invalid camera id %d", cameraId); + return NULL; default: ALOGE("Unknown camera device HAL version: %d", deviceVersion); return NULL; @@ -212,23 +296,27 @@ sp CameraService::connect( return NULL; } - cameraClient->asBinder()->linkToDeath(this); + mProClientList[cameraId].push(client); + + cameraCb->asBinder()->linkToDeath(this); - mClient[cameraId] = client; LOG1("CameraService::connect X (id %d, this pid is %d)", cameraId, getpid()); return client; + + + return NULL; } -void CameraService::removeClient(const sp& cameraClient) { +void CameraService::removeClientByRemote(const wp& remoteBinder) { int callingPid = getCallingPid(); - LOG1("CameraService::removeClient E (pid %d)", callingPid); + LOG1("CameraService::removeClientByRemote E (pid %d)", callingPid); // Declare this before the lock to make absolutely sure the // destructor won't be called with the lock held. Mutex::Autolock lock(mServiceLock); int outIndex; - sp client = findClientUnsafe(cameraClient->asBinder(), outIndex); + sp client = findClientUnsafe(remoteBinder, outIndex); if (client != 0) { // Found our camera, clear and leave. @@ -236,9 +324,50 @@ void CameraService::removeClient(const sp& cameraClient) { mClient[outIndex].clear(); client->unlinkToDeath(this); + } else { + + sp clientPro = findProClientUnsafe(remoteBinder); + + if (clientPro != NULL) { + // Found our camera, clear and leave. + LOG1("removeClient: clear pro %p", clientPro.get()); + + clientPro->getRemoteCallback()->asBinder()->unlinkToDeath(this); + } + } + + LOG1("CameraService::removeClientByRemote X (pid %d)", callingPid); +} + +sp CameraService::findProClientUnsafe( + const wp& cameraCallbacksRemote) +{ + sp clientPro; + + for (int i = 0; i < mNumberOfCameras; ++i) { + Vector removeIdx; + + for (size_t j = 0; j < mProClientList[i].size(); ++j) { + wp cl = mProClientList[i][j]; + + sp clStrong = cl.promote(); + if (clStrong != NULL && clStrong->getRemote() == cameraCallbacksRemote) { + clientPro = clStrong; + break; + } else if (clStrong == NULL) { + // mark to clean up dead ptr + removeIdx.push(j); + } + } + + // remove stale ptrs (in reverse so the indices dont change) + for (ssize_t j = (ssize_t)removeIdx.size() - 1; j >= 0; --j) { + mProClientList[i].removeAt(removeIdx[j]); + } + } - LOG1("CameraService::removeClient X (pid %d)", callingPid); + return clientPro; } sp CameraService::findClientUnsafe( @@ -252,7 +381,7 @@ sp CameraService::findClientUnsafe( if (mClient[i] == 0) continue; // Promote mClient. It can fail if we are called from this path: - // Client::~Client() -> disconnect() -> removeClient(). + // Client::~Client() -> disconnect() -> removeClientByRemote(). client = mClient[i].promote(); // Clean up stale client entry @@ -282,12 +411,12 @@ Mutex* CameraService::getClientLockById(int cameraId) { return &mClientLock[cameraId]; } -sp CameraService::getClientByRemote( +sp CameraService::getClientByRemote( const wp& cameraClient) { // Declare this before the lock to make absolutely sure the // destructor won't be called with the lock held. - sp client; + sp client; Mutex::Autolock lock(mServiceLock); @@ -302,6 +431,7 @@ status_t CameraService::onTransact( // Permission checks switch (code) { case BnCameraService::CONNECT: + case BnCameraService::CONNECT_PRO: const int pid = getCallingPid(); const int self_pid = getpid(); if (pid != self_pid) { @@ -390,17 +520,15 @@ void CameraService::playSound(sound_kind kind) { CameraService::Client::Client(const sp& cameraService, const sp& cameraClient, - int cameraId, int cameraFacing, int clientPid, int servicePid) { + int cameraId, int cameraFacing, int clientPid, int servicePid) : + CameraService::BasicClient(cameraService, cameraClient->asBinder(), + cameraId, cameraFacing, + clientPid, servicePid) +{ int callingPid = getCallingPid(); LOG1("Client::Client E (pid %d, id %d)", callingPid, cameraId); - mCameraService = cameraService; mCameraClient = cameraClient; - mCameraId = cameraId; - mCameraFacing = cameraFacing; - mClientPid = clientPid; - mServicePid = servicePid; - mDestructionStarted = false; cameraService->setCameraBusy(cameraId); cameraService->loadSound(); @@ -409,12 +537,37 @@ CameraService::Client::Client(const sp& cameraService, // tear down the client CameraService::Client::~Client() { + mDestructionStarted = true; + mCameraService->releaseSound(); // unconditionally disconnect. function is idempotent Client::disconnect(); } +CameraService::BasicClient::BasicClient(const sp& cameraService, + const sp& remoteCallback, + int cameraId, int cameraFacing, + int clientPid, int servicePid) +{ + mCameraService = cameraService; + mRemoteCallback = remoteCallback; + mCameraId = cameraId; + mCameraFacing = cameraFacing; + mClientPid = clientPid; + mServicePid = servicePid; + + mDestructionStarted = false; +} + +CameraService::BasicClient::~BasicClient() { + mDestructionStarted = true; +} + +void CameraService::BasicClient::disconnect() { + mCameraService->removeClientByRemote(mRemoteCallback); +} + // ---------------------------------------------------------------------------- Mutex* CameraService::Client::getClientLockFromCookie(void* user) { @@ -439,11 +592,96 @@ CameraService::Client* CameraService::Client::getClientFromCookie(void* user) { // NOTE: function is idempotent void CameraService::Client::disconnect() { - mCameraService->removeClient(mCameraClient); + BasicClient::disconnect(); mCameraService->setCameraFree(mCameraId); } // ---------------------------------------------------------------------------- +// IProCamera +// ---------------------------------------------------------------------------- + +CameraService::ProClient::ProClient(const sp& cameraService, + const sp& remoteCallback, + int cameraId, + int cameraFacing, + int clientPid, + int servicePid) + : CameraService::BasicClient(cameraService, remoteCallback->asBinder(), + cameraId, cameraFacing, + clientPid, servicePid) +{ + mRemoteCallback = remoteCallback; +} + +CameraService::ProClient::~ProClient() { + mDestructionStarted = true; + + ProClient::disconnect(); +} + +status_t CameraService::ProClient::connect(const sp& callbacks) { + ALOGE("%s: not implemented yet", __FUNCTION__); + + return INVALID_OPERATION; +} + +void CameraService::ProClient::disconnect() { + BasicClient::disconnect(); +} + +status_t CameraService::ProClient::initialize(camera_module_t* module) +{ + ALOGW("%s: not implemented yet", __FUNCTION__); + return OK; +} + +status_t CameraService::ProClient::exclusiveTryLock() { + ALOGE("%s: not implemented yet", __FUNCTION__); + return INVALID_OPERATION; +} + +status_t CameraService::ProClient::exclusiveLock() { + ALOGE("%s: not implemented yet", __FUNCTION__); + return INVALID_OPERATION; +} + +status_t CameraService::ProClient::exclusiveUnlock() { + ALOGE("%s: not implemented yet", __FUNCTION__); + return INVALID_OPERATION; +} + +bool CameraService::ProClient::hasExclusiveLock() { + ALOGE("%s: not implemented yet", __FUNCTION__); + return false; +} + +status_t CameraService::ProClient::submitRequest(camera_metadata_t* request, bool streaming) { + ALOGE("%s: not implemented yet", __FUNCTION__); + + free_camera_metadata(request); + + return INVALID_OPERATION; +} + +status_t CameraService::ProClient::cancelRequest(int requestId) { + ALOGE("%s: not implemented yet", __FUNCTION__); + + return INVALID_OPERATION; +} + +status_t CameraService::ProClient::requestStream(int streamId) { + ALOGE("%s: not implemented yet", __FUNCTION__); + + return INVALID_OPERATION; +} + +status_t CameraService::ProClient::cancelStream(int streamId) { + ALOGE("%s: not implemented yet", __FUNCTION__); + + return INVALID_OPERATION; +} + +// ---------------------------------------------------------------------------- static const int kDumpLockRetries = 50; static const int kDumpLockSleep = 60000; @@ -569,7 +807,7 @@ status_t CameraService::dump(int fd, const Vector& args) { ALOGV("java clients' binder died"); - sp cameraClient = getClientByRemote(who); + sp cameraClient = getClientByRemote(who); if (cameraClient == 0) { ALOGV("java clients' binder death already cleaned up (normal case)"); diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h index 41365a0..9e0f62a 100644 --- a/services/camera/libcameraservice/CameraService.h +++ b/services/camera/libcameraservice/CameraService.h @@ -18,6 +18,7 @@ #ifndef ANDROID_SERVERS_CAMERA_CAMERASERVICE_H #define ANDROID_SERVERS_CAMERA_CAMERASERVICE_H +#include #include #include #include @@ -40,27 +41,32 @@ class CameraService : friend class BinderService; public: class Client; + class BasicClient; + + // Implementation of BinderService static char const* getServiceName() { return "media.camera"; } CameraService(); virtual ~CameraService(); + ///////////////////////////////////////////////////////////////////// + // ICameraService virtual int32_t getNumberOfCameras(); virtual status_t getCameraInfo(int cameraId, struct CameraInfo* cameraInfo); virtual sp connect(const sp& cameraClient, int cameraId); - virtual void removeClient(const sp& cameraClient); - // returns plain pointer of client. Note that mClientLock should be acquired to - // prevent the client from destruction. The result can be NULL. - virtual Client* getClientByIdUnsafe(int cameraId); - virtual Mutex* getClientLockById(int cameraId); - - virtual sp getClientByRemote(const wp& cameraClient); + virtual sp + connect(const sp& cameraCb, int cameraId); - virtual status_t dump(int fd, const Vector& args); + // Extra permissions checks virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags); - virtual void onFirstRef(); + + virtual status_t dump(int fd, const Vector& args); + + ///////////////////////////////////////////////////////////////////// + // Client functionality + virtual void removeClientByRemote(const wp& remoteBinder); enum sound_kind { SOUND_SHUTTER = 0, @@ -72,7 +78,53 @@ public: void playSound(sound_kind kind); void releaseSound(); - class Client : public BnCamera + + ///////////////////////////////////////////////////////////////////// + // CameraClient functionality + + // returns plain pointer of client. Note that mClientLock should be acquired to + // prevent the client from destruction. The result can be NULL. + virtual Client* getClientByIdUnsafe(int cameraId); + virtual Mutex* getClientLockById(int cameraId); + + class BasicClient : public virtual RefBase { + public: + virtual status_t initialize(camera_module_t *module) = 0; + + virtual void disconnect() = 0; + + wp getRemote() { + return mRemoteCallback; + } + + protected: + BasicClient(const sp& cameraService, + const sp& remoteCallback, + int cameraId, + int cameraFacing, + int clientPid, + int servicePid); + + virtual ~BasicClient(); + + // the instance is in the middle of destruction. When this is set, + // the instance should not be accessed from callback. + // CameraService's mClientLock should be acquired to access this. + // - subclasses should set this to true in their destructors. + bool mDestructionStarted; + + // these are initialized in the constructor. + sp mCameraService; // immutable after constructor + int mCameraId; // immutable after constructor + int mCameraFacing; // immutable after constructor + pid_t mClientPid; + pid_t mServicePid; // immutable after constructor + + // - The app-side Binder interface to receive callbacks from us + wp mRemoteCallback; // immutable after constructor + }; + + class Client : public BnCamera, public BasicClient { public: // ICamera interface (see ICamera for details) @@ -112,38 +164,82 @@ public: return mCameraClient; } - virtual status_t initialize(camera_module_t *module) = 0; - - virtual status_t dump(int fd, const Vector& args) = 0; - protected: static Mutex* getClientLockFromCookie(void* user); // convert client from cookie. Client lock should be acquired before getting Client. static Client* getClientFromCookie(void* user); - // the instance is in the middle of destruction. When this is set, - // the instance should not be accessed from callback. - // CameraService's mClientLock should be acquired to access this. - bool mDestructionStarted; + // Initialized in constructor - // these are initialized in the constructor. - sp mCameraService; // immutable after constructor + // - The app-side Binder interface to receive callbacks from us sp mCameraClient; - int mCameraId; // immutable after constructor - int mCameraFacing; // immutable after constructor - pid_t mClientPid; - pid_t mServicePid; // immutable after constructor + }; + + class ProClient : public BnProCameraUser, public BasicClient { + public: + ProClient(const sp& cameraService, + const sp& remoteCallback, + int cameraId, + int cameraFacing, + int clientPid, + int servicePid); + + virtual ~ProClient(); + + const sp& getRemoteCallback() { + return mRemoteCallback; + } + + // BasicClient implementation + virtual status_t initialize(camera_module_t *module); + + /*** + IProCamera implementation + ***/ + + + virtual status_t connect( + const sp& callbacks); + virtual void disconnect(); + + virtual status_t exclusiveTryLock(); + virtual status_t exclusiveLock(); + virtual status_t exclusiveUnlock(); + + virtual bool hasExclusiveLock(); + + // Note that the callee gets a copy of the metadata. + virtual int submitRequest(camera_metadata_t* metadata, + bool streaming = false); + virtual status_t cancelRequest(int requestId); + + virtual status_t requestStream(int streamId); + virtual status_t cancelStream(int streamId); + + protected: + sp mRemoteCallback; }; private: + + // Delay-load the Camera HAL module + virtual void onFirstRef(); + + virtual sp getClientByRemote(const wp& cameraClient); + Mutex mServiceLock; wp mClient[MAX_CAMERAS]; // protected by mServiceLock Mutex mClientLock[MAX_CAMERAS]; // prevent Client destruction inside callbacks int mNumberOfCameras; + typedef wp weak_pro_client_ptr; + Vector mProClientList[MAX_CAMERAS]; + // needs to be called with mServiceLock held sp findClientUnsafe(const wp& cameraClient, int& outIndex); + sp findProClientUnsafe( + const wp& cameraCallbacksRemote); // atomics to record whether the hardware is allocated to some client. volatile int32_t mBusy[MAX_CAMERAS]; @@ -161,6 +257,9 @@ private: // IBinder::DeathRecipient implementation virtual void binderDied(const wp &who); + + // Helpers + int getDeviceVersion(int cameraId, int* facing); }; } // namespace android -- cgit v1.1