/* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "ProCamera2Client" #define ATRACE_TAG ATRACE_TAG_CAMERA //#define LOG_NDEBUG 0 #include <utils/Log.h> #include <utils/Trace.h> #include <cutils/properties.h> #include <gui/Surface.h> #include <gui/Surface.h> #include "camera2/Parameters.h" #include "ProCamera2Client.h" #include "camera2/ProFrameProcessor.h" #include "CameraDeviceBase.h" namespace android { using namespace camera2; // Interface used by CameraService ProCamera2Client::ProCamera2Client(const sp<CameraService>& cameraService, const sp<IProCameraCallbacks>& remoteCallback, const String16& clientPackageName, int cameraId, int cameraFacing, int clientPid, uid_t clientUid, int servicePid) : Camera2ClientBase(cameraService, remoteCallback, clientPackageName, cameraId, cameraFacing, clientPid, clientUid, servicePid) { ATRACE_CALL(); ALOGI("ProCamera %d: Opened", cameraId); mExclusiveLock = false; } status_t ProCamera2Client::initialize(camera_module_t *module) { ATRACE_CALL(); status_t res; res = Camera2ClientBase::initialize(module); if (res != OK) { return res; } String8 threadName; mFrameProcessor = new ProFrameProcessor(mDevice); threadName = String8::format("PC2-%d-FrameProc", mCameraId); mFrameProcessor->run(threadName.string()); mFrameProcessor->registerListener(FRAME_PROCESSOR_LISTENER_MIN_ID, FRAME_PROCESSOR_LISTENER_MAX_ID, /*listener*/this); return OK; } ProCamera2Client::~ProCamera2Client() { } status_t ProCamera2Client::exclusiveTryLock() { ATRACE_CALL(); ALOGV("%s", __FUNCTION__); Mutex::Autolock icl(mBinderSerializationLock); SharedCameraCallbacks::Lock l(mSharedCameraCallbacks); if (!mDevice.get()) return PERMISSION_DENIED; if (!mExclusiveLock) { mExclusiveLock = true; if (mRemoteCallback != NULL) { mRemoteCallback->onLockStatusChanged( IProCameraCallbacks::LOCK_ACQUIRED); } ALOGV("%s: exclusive lock acquired", __FUNCTION__); return OK; } // TODO: have a PERMISSION_DENIED case for when someone else owns the lock // don't allow recursive locking ALOGW("%s: exclusive lock already exists - recursive locking is not" "allowed", __FUNCTION__); return ALREADY_EXISTS; } status_t ProCamera2Client::exclusiveLock() { ATRACE_CALL(); ALOGV("%s", __FUNCTION__); Mutex::Autolock icl(mBinderSerializationLock); SharedCameraCallbacks::Lock l(mSharedCameraCallbacks); if (!mDevice.get()) return PERMISSION_DENIED; /** * TODO: this should asynchronously 'wait' until the lock becomes available * if another client already has an exclusive lock. * * once we have proper sharing support this will need to do * more than just return immediately */ if (!mExclusiveLock) { mExclusiveLock = true; if (mRemoteCallback != NULL) { mRemoteCallback->onLockStatusChanged(IProCameraCallbacks::LOCK_ACQUIRED); } ALOGV("%s: exclusive lock acquired", __FUNCTION__); return OK; } // don't allow recursive locking ALOGW("%s: exclusive lock already exists - recursive locking is not allowed" , __FUNCTION__); return ALREADY_EXISTS; } status_t ProCamera2Client::exclusiveUnlock() { ATRACE_CALL(); ALOGV("%s", __FUNCTION__); Mutex::Autolock icl(mBinderSerializationLock); SharedCameraCallbacks::Lock l(mSharedCameraCallbacks); // don't allow unlocking if we have no lock if (!mExclusiveLock) { ALOGW("%s: cannot unlock, no lock was held in the first place", __FUNCTION__); return BAD_VALUE; } mExclusiveLock = false; if (mRemoteCallback != NULL ) { mRemoteCallback->onLockStatusChanged( IProCameraCallbacks::LOCK_RELEASED); } ALOGV("%s: exclusive lock released", __FUNCTION__); return OK; } bool ProCamera2Client::hasExclusiveLock() { Mutex::Autolock icl(mBinderSerializationLock); return mExclusiveLock; } void ProCamera2Client::onExclusiveLockStolen() { ALOGV("%s: ProClient lost exclusivity (id %d)", __FUNCTION__, mCameraId); Mutex::Autolock icl(mBinderSerializationLock); SharedCameraCallbacks::Lock l(mSharedCameraCallbacks); if (mExclusiveLock && mRemoteCallback.get() != NULL) { mRemoteCallback->onLockStatusChanged( IProCameraCallbacks::LOCK_STOLEN); } mExclusiveLock = false; //TODO: we should not need to detach the device, merely reset it. detachDevice(); } status_t ProCamera2Client::submitRequest(camera_metadata_t* request, bool streaming) { ATRACE_CALL(); ALOGV("%s", __FUNCTION__); Mutex::Autolock icl(mBinderSerializationLock); if (!mDevice.get()) return DEAD_OBJECT; if (!mExclusiveLock) { return PERMISSION_DENIED; } CameraMetadata metadata(request); if (!enforceRequestPermissions(metadata)) { return PERMISSION_DENIED; } if (streaming) { return mDevice->setStreamingRequest(metadata); } else { return mDevice->capture(metadata); } // unreachable. thx gcc for a useless warning return OK; } status_t ProCamera2Client::cancelRequest(int requestId) { ATRACE_CALL(); ALOGV("%s", __FUNCTION__); Mutex::Autolock icl(mBinderSerializationLock); if (!mDevice.get()) return DEAD_OBJECT; if (!mExclusiveLock) { return PERMISSION_DENIED; } // TODO: implement ALOGE("%s: not fully implemented yet", __FUNCTION__); return INVALID_OPERATION; } status_t ProCamera2Client::deleteStream(int streamId) { ATRACE_CALL(); ALOGV("%s (streamId = 0x%x)", __FUNCTION__, streamId); status_t res; if ( (res = checkPid(__FUNCTION__) ) != OK) return res; Mutex::Autolock icl(mBinderSerializationLock); if (!mDevice.get()) return DEAD_OBJECT; mDevice->clearStreamingRequest(); status_t code; if ((code = mDevice->waitUntilDrained()) != OK) { ALOGE("%s: waitUntilDrained failed with code 0x%x", __FUNCTION__, code); } return mDevice->deleteStream(streamId); } status_t ProCamera2Client::createStream(int width, int height, int format, const sp<IGraphicBufferProducer>& bufferProducer, /*out*/ int* streamId) { if (streamId) { *streamId = -1; } ATRACE_CALL(); ALOGV("%s (w = %d, h = %d, f = 0x%x)", __FUNCTION__, width, height, format); status_t res; if ( (res = checkPid(__FUNCTION__) ) != OK) return res; Mutex::Autolock icl(mBinderSerializationLock); if (!mDevice.get()) return DEAD_OBJECT; sp<IBinder> binder; sp<ANativeWindow> window; if (bufferProducer != 0) { binder = bufferProducer->asBinder(); window = new Surface(bufferProducer); } return mDevice->createStream(window, width, height, format, /*size*/1, streamId); } // Create a request object from a template. // -- Caller owns the newly allocated metadata status_t ProCamera2Client::createDefaultRequest(int templateId, /*out*/ camera_metadata** request) { ATRACE_CALL(); ALOGV("%s (templateId = 0x%x)", __FUNCTION__, templateId); if (request) { *request = NULL; } status_t res; if ( (res = checkPid(__FUNCTION__) ) != OK) return res; Mutex::Autolock icl(mBinderSerializationLock); if (!mDevice.get()) return DEAD_OBJECT; CameraMetadata metadata; if ( (res = mDevice->createDefaultRequest(templateId, &metadata) ) == OK) { *request = metadata.release(); } return res; } status_t ProCamera2Client::getCameraInfo(int cameraId, /*out*/ camera_metadata** info) { if (cameraId != mCameraId) { return INVALID_OPERATION; } Mutex::Autolock icl(mBinderSerializationLock); if (!mDevice.get()) return DEAD_OBJECT; CameraMetadata deviceInfo = mDevice->info(); *info = deviceInfo.release(); return OK; } status_t ProCamera2Client::dump(int fd, const Vector<String16>& args) { String8 result; result.appendFormat("ProCamera2Client[%d] (%p) PID: %d, dump:\n", mCameraId, getRemoteCallback()->asBinder().get(), mClientPid); result.append(" State: "); // TODO: print dynamic/request section from most recent requests mFrameProcessor->dump(fd, args); return dumpDevice(fd, args); } // IProCameraUser interface void ProCamera2Client::detachDevice() { if (mDevice == 0) return; ALOGV("Camera %d: Stopping processors", mCameraId); mFrameProcessor->removeListener(FRAME_PROCESSOR_LISTENER_MIN_ID, FRAME_PROCESSOR_LISTENER_MAX_ID, /*listener*/this); mFrameProcessor->requestExit(); ALOGV("Camera %d: Waiting for threads", mCameraId); mFrameProcessor->join(); ALOGV("Camera %d: Disconnecting device", mCameraId); // WORKAROUND: HAL refuses to disconnect while there's streams in flight { mDevice->clearStreamingRequest(); status_t code; if ((code = mDevice->waitUntilDrained()) != OK) { ALOGE("%s: waitUntilDrained failed with code 0x%x", __FUNCTION__, code); } } Camera2ClientBase::detachDevice(); } /** Device-related methods */ void ProCamera2Client::onFrameAvailable(int32_t frameId, const CameraMetadata& frame) { ATRACE_CALL(); ALOGV("%s", __FUNCTION__); Mutex::Autolock icl(mBinderSerializationLock); SharedCameraCallbacks::Lock l(mSharedCameraCallbacks); if (mRemoteCallback != NULL) { CameraMetadata tmp(frame); camera_metadata_t* meta = tmp.release(); ALOGV("%s: meta = %p ", __FUNCTION__, meta); mRemoteCallback->onResultReceived(frameId, meta); tmp.acquire(meta); } } bool ProCamera2Client::enforceRequestPermissions(CameraMetadata& metadata) { const int pid = IPCThreadState::self()->getCallingPid(); const int selfPid = getpid(); camera_metadata_entry_t entry; /** * Mixin default important security values * - android.led.transmit = defaulted ON */ CameraMetadata staticInfo = mDevice->info(); entry = staticInfo.find(ANDROID_LED_AVAILABLE_LEDS); for(size_t i = 0; i < entry.count; ++i) { uint8_t led = entry.data.u8[i]; switch(led) { case ANDROID_LED_AVAILABLE_LEDS_TRANSMIT: { uint8_t transmitDefault = ANDROID_LED_TRANSMIT_ON; if (!metadata.exists(ANDROID_LED_TRANSMIT)) { metadata.update(ANDROID_LED_TRANSMIT, &transmitDefault, 1); } break; } } } // We can do anything! if (pid == selfPid) { return true; } /** * Permission check special fields in the request * - android.led.transmit = android.permission.CAMERA_DISABLE_TRANSMIT */ entry = metadata.find(ANDROID_LED_TRANSMIT); if (entry.count > 0 && entry.data.u8[0] != ANDROID_LED_TRANSMIT_ON) { String16 permissionString = String16("android.permission.CAMERA_DISABLE_TRANSMIT_LED"); if (!checkCallingPermission(permissionString)) { const int uid = IPCThreadState::self()->getCallingUid(); ALOGE("Permission Denial: " "can't disable transmit LED pid=%d, uid=%d", pid, uid); return false; } } return true; } } // namespace android