23c21baf27
copyFrameAndPostCopiedFrame was not holding a lock while it accessed the preview heap. If the client process is torn down while the heap is accessed, the memcpy could access memory that was deallocated. This patch creates a local sp reference to the preview heap while holding the lock, then releases the lock. This should prevent the heap from being pulled out from underneath us.
1344 lines
40 KiB
C++
1344 lines
40 KiB
C++
/*
|
|
**
|
|
** Copyright (C) 2008, The Android Open Source Project
|
|
** Copyright (C) 2008 HTC Inc.
|
|
**
|
|
** 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_NDEBUG 0
|
|
#define LOG_TAG "CameraService"
|
|
#include <utils/Log.h>
|
|
|
|
#include <binder/IServiceManager.h>
|
|
#include <binder/IPCThreadState.h>
|
|
#include <utils/String16.h>
|
|
#include <utils/Errors.h>
|
|
#include <binder/MemoryBase.h>
|
|
#include <binder/MemoryHeapBase.h>
|
|
#include <ui/ICameraService.h>
|
|
|
|
#include <media/mediaplayer.h>
|
|
#include <media/AudioSystem.h>
|
|
#include "CameraService.h"
|
|
|
|
#include <cutils/atomic.h>
|
|
|
|
namespace android {
|
|
|
|
extern "C" {
|
|
#include <stdio.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <pthread.h>
|
|
#include <signal.h>
|
|
}
|
|
|
|
// When you enable this, as well as DEBUG_REFS=1 and
|
|
// DEBUG_REFS_ENABLED_BY_DEFAULT=0 in libutils/RefBase.cpp, this will track all
|
|
// references to the CameraService::Client in order to catch the case where the
|
|
// client is being destroyed while a callback from the CameraHardwareInterface
|
|
// is outstanding. This is a serious bug because if we make another call into
|
|
// CameraHardwreInterface that itself triggers a callback, we will deadlock.
|
|
|
|
#define DEBUG_CLIENT_REFERENCES 0
|
|
|
|
#define PICTURE_TIMEOUT seconds(5)
|
|
|
|
#define DEBUG_DUMP_PREVIEW_FRAME_TO_FILE 0 /* n-th frame to write */
|
|
#define DEBUG_DUMP_JPEG_SNAPSHOT_TO_FILE 0
|
|
#define DEBUG_DUMP_YUV_SNAPSHOT_TO_FILE 0
|
|
#define DEBUG_DUMP_POSTVIEW_SNAPSHOT_TO_FILE 0
|
|
|
|
#if DEBUG_DUMP_PREVIEW_FRAME_TO_FILE
|
|
static int debug_frame_cnt;
|
|
#endif
|
|
|
|
static int getCallingPid() {
|
|
return IPCThreadState::self()->getCallingPid();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
void CameraService::instantiate() {
|
|
defaultServiceManager()->addService(
|
|
String16("media.camera"), new CameraService());
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
CameraService::CameraService() :
|
|
BnCameraService()
|
|
{
|
|
LOGI("CameraService started: pid=%d", getpid());
|
|
mUsers = 0;
|
|
}
|
|
|
|
CameraService::~CameraService()
|
|
{
|
|
if (mClient != 0) {
|
|
LOGE("mClient was still connected in destructor!");
|
|
}
|
|
}
|
|
|
|
sp<ICamera> CameraService::connect(const sp<ICameraClient>& cameraClient)
|
|
{
|
|
int callingPid = getCallingPid();
|
|
LOGD("CameraService::connect E (pid %d, client %p)", callingPid,
|
|
cameraClient->asBinder().get());
|
|
|
|
Mutex::Autolock lock(mServiceLock);
|
|
sp<Client> client;
|
|
if (mClient != 0) {
|
|
sp<Client> currentClient = mClient.promote();
|
|
if (currentClient != 0) {
|
|
sp<ICameraClient> currentCameraClient(currentClient->getCameraClient());
|
|
if (cameraClient->asBinder() == currentCameraClient->asBinder()) {
|
|
// This is the same client reconnecting...
|
|
LOGD("CameraService::connect X (pid %d, same client %p) is reconnecting...",
|
|
callingPid, cameraClient->asBinder().get());
|
|
return currentClient;
|
|
} else {
|
|
// It's another client... reject it
|
|
LOGD("CameraService::connect X (pid %d, new client %p) rejected. "
|
|
"(old pid %d, old client %p)",
|
|
callingPid, cameraClient->asBinder().get(),
|
|
currentClient->mClientPid, currentCameraClient->asBinder().get());
|
|
if (kill(currentClient->mClientPid, 0) == -1 && errno == ESRCH) {
|
|
LOGD("The old client is dead!");
|
|
}
|
|
return client;
|
|
}
|
|
} else {
|
|
// can't promote, the previous client has died...
|
|
LOGD("New client (pid %d) connecting, old reference was dangling...",
|
|
callingPid);
|
|
mClient.clear();
|
|
}
|
|
}
|
|
|
|
if (mUsers > 0) {
|
|
LOGD("Still have client, rejected");
|
|
return client;
|
|
}
|
|
|
|
// create a new Client object
|
|
client = new Client(this, cameraClient, callingPid);
|
|
mClient = client;
|
|
#if DEBUG_CLIENT_REFERENCES
|
|
// Enable tracking for this object, and track increments and decrements of
|
|
// the refcount.
|
|
client->trackMe(true, true);
|
|
#endif
|
|
LOGD("CameraService::connect X");
|
|
return client;
|
|
}
|
|
|
|
void CameraService::removeClient(const sp<ICameraClient>& cameraClient)
|
|
{
|
|
int callingPid = getCallingPid();
|
|
|
|
// Declare this outside the lock to make absolutely sure the
|
|
// destructor won't be called with the lock held.
|
|
sp<Client> client;
|
|
|
|
Mutex::Autolock lock(mServiceLock);
|
|
|
|
if (mClient == 0) {
|
|
// This happens when we have already disconnected.
|
|
LOGD("removeClient (pid %d): already disconnected", callingPid);
|
|
return;
|
|
}
|
|
|
|
// Promote mClient. It can fail if we are called from this path:
|
|
// Client::~Client() -> disconnect() -> removeClient().
|
|
client = mClient.promote();
|
|
if (client == 0) {
|
|
LOGD("removeClient (pid %d): no more strong reference", callingPid);
|
|
mClient.clear();
|
|
return;
|
|
}
|
|
|
|
if (cameraClient->asBinder() != client->getCameraClient()->asBinder()) {
|
|
// ugh! that's not our client!!
|
|
LOGW("removeClient (pid %d): mClient doesn't match!", callingPid);
|
|
} else {
|
|
// okay, good, forget about mClient
|
|
mClient.clear();
|
|
}
|
|
|
|
LOGD("removeClient (pid %d) done", callingPid);
|
|
}
|
|
|
|
// The reason we need this count is a new CameraService::connect() request may
|
|
// come in while the previous Client's destructor has not been run or is still
|
|
// running. If the last strong reference of the previous Client is gone but
|
|
// destructor has not been run, we should not allow the new Client to be created
|
|
// because we need to wait for the previous Client to tear down the hardware
|
|
// first.
|
|
void CameraService::incUsers() {
|
|
android_atomic_inc(&mUsers);
|
|
}
|
|
|
|
void CameraService::decUsers() {
|
|
android_atomic_dec(&mUsers);
|
|
}
|
|
|
|
static sp<MediaPlayer> newMediaPlayer(const char *file)
|
|
{
|
|
sp<MediaPlayer> mp = new MediaPlayer();
|
|
if (mp->setDataSource(file) == NO_ERROR) {
|
|
mp->setAudioStreamType(AudioSystem::ENFORCED_AUDIBLE);
|
|
mp->prepare();
|
|
} else {
|
|
mp.clear();
|
|
LOGE("Failed to load CameraService sounds.");
|
|
}
|
|
return mp;
|
|
}
|
|
|
|
CameraService::Client::Client(const sp<CameraService>& cameraService,
|
|
const sp<ICameraClient>& cameraClient, pid_t clientPid)
|
|
{
|
|
int callingPid = getCallingPid();
|
|
LOGD("Client::Client E (pid %d)", callingPid);
|
|
mCameraService = cameraService;
|
|
mCameraClient = cameraClient;
|
|
mClientPid = clientPid;
|
|
mHardware = openCameraHardware();
|
|
mUseOverlay = mHardware->useOverlay();
|
|
|
|
mHardware->setCallbacks(notifyCallback,
|
|
dataCallback,
|
|
dataCallbackTimestamp,
|
|
mCameraService.get());
|
|
|
|
// Enable zoom, error, and focus messages by default
|
|
mHardware->enableMsgType(CAMERA_MSG_ERROR |
|
|
CAMERA_MSG_ZOOM |
|
|
CAMERA_MSG_FOCUS);
|
|
|
|
mMediaPlayerClick = newMediaPlayer("/system/media/audio/ui/camera_click.ogg");
|
|
mMediaPlayerBeep = newMediaPlayer("/system/media/audio/ui/VideoRecord.ogg");
|
|
mOverlayW = 0;
|
|
mOverlayH = 0;
|
|
|
|
// Callback is disabled by default
|
|
mPreviewCallbackFlag = FRAME_CALLBACK_FLAG_NOOP;
|
|
cameraService->incUsers();
|
|
LOGD("Client::Client X (pid %d)", callingPid);
|
|
}
|
|
|
|
status_t CameraService::Client::checkPid()
|
|
{
|
|
int callingPid = getCallingPid();
|
|
if (mClientPid == callingPid) return NO_ERROR;
|
|
LOGW("Attempt to use locked camera (client %p) from different process "
|
|
" (old pid %d, new pid %d)",
|
|
getCameraClient()->asBinder().get(), mClientPid, callingPid);
|
|
return -EBUSY;
|
|
}
|
|
|
|
status_t CameraService::Client::lock()
|
|
{
|
|
int callingPid = getCallingPid();
|
|
LOGD("lock from pid %d (mClientPid %d)", callingPid, mClientPid);
|
|
Mutex::Autolock _l(mLock);
|
|
// lock camera to this client if the the camera is unlocked
|
|
if (mClientPid == 0) {
|
|
mClientPid = callingPid;
|
|
return NO_ERROR;
|
|
}
|
|
// returns NO_ERROR if the client already owns the camera, -EBUSY otherwise
|
|
return checkPid();
|
|
}
|
|
|
|
status_t CameraService::Client::unlock()
|
|
{
|
|
int callingPid = getCallingPid();
|
|
LOGD("unlock from pid %d (mClientPid %d)", callingPid, mClientPid);
|
|
Mutex::Autolock _l(mLock);
|
|
// allow anyone to use camera
|
|
status_t result = checkPid();
|
|
if (result == NO_ERROR) {
|
|
mClientPid = 0;
|
|
LOGD("clear mCameraClient (pid %d)", callingPid);
|
|
// we need to remove the reference so that when app goes
|
|
// away, the reference count goes to 0.
|
|
mCameraClient.clear();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
status_t CameraService::Client::connect(const sp<ICameraClient>& client)
|
|
{
|
|
int callingPid = getCallingPid();
|
|
|
|
// connect a new process to the camera
|
|
LOGD("Client::connect E (pid %d, client %p)", callingPid, client->asBinder().get());
|
|
|
|
// I hate this hack, but things get really ugly when the media recorder
|
|
// service is handing back the camera to the app. The ICameraClient
|
|
// destructor will be called during the same IPC, making it look like
|
|
// the remote client is trying to disconnect. This hack temporarily
|
|
// sets the mClientPid to an invalid pid to prevent the hardware from
|
|
// being torn down.
|
|
{
|
|
|
|
// hold a reference to the old client or we will deadlock if the client is
|
|
// in the same process and we hold the lock when we remove the reference
|
|
sp<ICameraClient> oldClient;
|
|
{
|
|
Mutex::Autolock _l(mLock);
|
|
if (mClientPid != 0 && checkPid() != NO_ERROR) {
|
|
LOGW("Tried to connect to locked camera (old pid %d, new pid %d)",
|
|
mClientPid, callingPid);
|
|
return -EBUSY;
|
|
}
|
|
oldClient = mCameraClient;
|
|
|
|
// did the client actually change?
|
|
if ((mCameraClient != NULL) && (client->asBinder() == mCameraClient->asBinder())) {
|
|
LOGD("Connect to the same client");
|
|
return NO_ERROR;
|
|
}
|
|
|
|
mCameraClient = client;
|
|
mClientPid = -1;
|
|
mPreviewCallbackFlag = FRAME_CALLBACK_FLAG_NOOP;
|
|
LOGD("Connect to the new client (pid %d, client %p)",
|
|
callingPid, mCameraClient->asBinder().get());
|
|
}
|
|
|
|
}
|
|
// the old client destructor is called when oldClient goes out of scope
|
|
// now we set the new PID to lock the interface again
|
|
mClientPid = callingPid;
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
#if HAVE_ANDROID_OS
|
|
static void *unregister_surface(void *arg)
|
|
{
|
|
ISurface *surface = (ISurface *)arg;
|
|
surface->unregisterBuffers();
|
|
IPCThreadState::self()->flushCommands();
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
CameraService::Client::~Client()
|
|
{
|
|
int callingPid = getCallingPid();
|
|
|
|
// tear down client
|
|
LOGD("Client::~Client E (pid %d, client %p)",
|
|
callingPid, getCameraClient()->asBinder().get());
|
|
if (mSurface != 0 && !mUseOverlay) {
|
|
#if HAVE_ANDROID_OS
|
|
pthread_t thr;
|
|
// We unregister the buffers in a different thread because binder does
|
|
// not let us make sychronous transactions in a binder destructor (that
|
|
// is, upon our reaching a refcount of zero.)
|
|
pthread_create(&thr, NULL,
|
|
unregister_surface,
|
|
mSurface.get());
|
|
pthread_join(thr, NULL);
|
|
#else
|
|
mSurface->unregisterBuffers();
|
|
#endif
|
|
}
|
|
|
|
if (mMediaPlayerBeep.get() != NULL) {
|
|
mMediaPlayerBeep->disconnect();
|
|
mMediaPlayerBeep.clear();
|
|
}
|
|
if (mMediaPlayerClick.get() != NULL) {
|
|
mMediaPlayerClick->disconnect();
|
|
mMediaPlayerClick.clear();
|
|
}
|
|
|
|
// make sure we tear down the hardware
|
|
mClientPid = callingPid;
|
|
disconnect();
|
|
LOGD("Client::~Client X (pid %d)", mClientPid);
|
|
}
|
|
|
|
void CameraService::Client::disconnect()
|
|
{
|
|
int callingPid = getCallingPid();
|
|
|
|
LOGD("Client::disconnect() E (pid %d client %p)",
|
|
callingPid, getCameraClient()->asBinder().get());
|
|
|
|
Mutex::Autolock lock(mLock);
|
|
if (mClientPid <= 0) {
|
|
LOGD("camera is unlocked (mClientPid = %d), don't tear down hardware", mClientPid);
|
|
return;
|
|
}
|
|
if (checkPid() != NO_ERROR) {
|
|
LOGD("Different client - don't disconnect");
|
|
return;
|
|
}
|
|
|
|
// Make sure disconnect() is done once and once only, whether it is called
|
|
// from the user directly, or called by the destructor.
|
|
if (mHardware == 0) return;
|
|
|
|
LOGD("hardware teardown");
|
|
// Before destroying mHardware, we must make sure it's in the
|
|
// idle state.
|
|
mHardware->stopPreview();
|
|
// Cancel all picture callbacks.
|
|
mHardware->disableMsgType(CAMERA_MSG_SHUTTER |
|
|
CAMERA_MSG_POSTVIEW_FRAME |
|
|
CAMERA_MSG_RAW_IMAGE |
|
|
CAMERA_MSG_COMPRESSED_IMAGE);
|
|
mHardware->cancelPicture();
|
|
// Turn off remaining messages.
|
|
mHardware->disableMsgType(CAMERA_MSG_ALL_MSGS);
|
|
// Release the hardware resources.
|
|
mHardware->release();
|
|
// Release the held overlay resources.
|
|
if (mUseOverlay)
|
|
{
|
|
mOverlayRef = 0;
|
|
}
|
|
mHardware.clear();
|
|
|
|
mCameraService->removeClient(mCameraClient);
|
|
mCameraService->decUsers();
|
|
|
|
LOGD("Client::disconnect() X (pid %d)", callingPid);
|
|
}
|
|
|
|
// pass the buffered ISurface to the camera service
|
|
status_t CameraService::Client::setPreviewDisplay(const sp<ISurface>& surface)
|
|
{
|
|
LOGD("setPreviewDisplay(%p) (pid %d)",
|
|
((surface == NULL) ? NULL : surface.get()), getCallingPid());
|
|
Mutex::Autolock lock(mLock);
|
|
status_t result = checkPid();
|
|
if (result != NO_ERROR) return result;
|
|
|
|
Mutex::Autolock surfaceLock(mSurfaceLock);
|
|
result = NO_ERROR;
|
|
// asBinder() is safe on NULL (returns NULL)
|
|
if (surface->asBinder() != mSurface->asBinder()) {
|
|
if (mSurface != 0) {
|
|
LOGD("clearing old preview surface %p", mSurface.get());
|
|
if ( !mUseOverlay)
|
|
{
|
|
mSurface->unregisterBuffers();
|
|
}
|
|
else
|
|
{
|
|
// Force the destruction of any previous overlay
|
|
sp<Overlay> dummy;
|
|
mHardware->setOverlay( dummy );
|
|
}
|
|
}
|
|
mSurface = surface;
|
|
mOverlayRef = 0;
|
|
// If preview has been already started, set overlay or register preview
|
|
// buffers now.
|
|
if (mHardware->previewEnabled()) {
|
|
if (mUseOverlay) {
|
|
result = setOverlay();
|
|
} else if (mSurface != 0) {
|
|
result = registerPreviewBuffers();
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// set the preview callback flag to affect how the received frames from
|
|
// preview are handled.
|
|
void CameraService::Client::setPreviewCallbackFlag(int callback_flag)
|
|
{
|
|
LOGV("setPreviewCallbackFlag (pid %d)", getCallingPid());
|
|
Mutex::Autolock lock(mLock);
|
|
if (checkPid() != NO_ERROR) return;
|
|
mPreviewCallbackFlag = callback_flag;
|
|
|
|
if(mUseOverlay) {
|
|
if(mPreviewCallbackFlag & FRAME_CALLBACK_FLAG_ENABLE_MASK)
|
|
mHardware->enableMsgType(CAMERA_MSG_PREVIEW_FRAME);
|
|
else
|
|
mHardware->disableMsgType(CAMERA_MSG_PREVIEW_FRAME);
|
|
}
|
|
}
|
|
|
|
// start preview mode
|
|
status_t CameraService::Client::startCameraMode(camera_mode mode)
|
|
{
|
|
int callingPid = getCallingPid();
|
|
|
|
LOGD("startCameraMode(%d) (pid %d)", mode, callingPid);
|
|
|
|
/* we cannot call into mHardware with mLock held because
|
|
* mHardware has callbacks onto us which acquire this lock
|
|
*/
|
|
|
|
Mutex::Autolock lock(mLock);
|
|
status_t result = checkPid();
|
|
if (result != NO_ERROR) return result;
|
|
|
|
if (mHardware == 0) {
|
|
LOGE("mHardware is NULL, returning.");
|
|
return INVALID_OPERATION;
|
|
}
|
|
|
|
switch(mode) {
|
|
case CAMERA_RECORDING_MODE:
|
|
if (mSurface == 0) {
|
|
LOGE("setPreviewDisplay must be called before startRecordingMode.");
|
|
return INVALID_OPERATION;
|
|
}
|
|
return startRecordingMode();
|
|
|
|
default: // CAMERA_PREVIEW_MODE
|
|
if (mSurface == 0) {
|
|
LOGD("mSurface is not set yet.");
|
|
}
|
|
return startPreviewMode();
|
|
}
|
|
}
|
|
|
|
status_t CameraService::Client::startRecordingMode()
|
|
{
|
|
LOGD("startRecordingMode (pid %d)", getCallingPid());
|
|
|
|
status_t ret = UNKNOWN_ERROR;
|
|
|
|
// if preview has not been started, start preview first
|
|
if (!mHardware->previewEnabled()) {
|
|
ret = startPreviewMode();
|
|
if (ret != NO_ERROR) {
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
// if recording has been enabled, nothing needs to be done
|
|
if (mHardware->recordingEnabled()) {
|
|
return NO_ERROR;
|
|
}
|
|
|
|
// start recording mode
|
|
ret = mHardware->startRecording();
|
|
if (ret != NO_ERROR) {
|
|
LOGE("mHardware->startRecording() failed with status %d", ret);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
status_t CameraService::Client::setOverlay()
|
|
{
|
|
LOGD("setOverlay");
|
|
int w, h;
|
|
CameraParameters params(mHardware->getParameters());
|
|
params.getPreviewSize(&w, &h);
|
|
|
|
if ( w != mOverlayW || h != mOverlayH )
|
|
{
|
|
// Force the destruction of any previous overlay
|
|
sp<Overlay> dummy;
|
|
mHardware->setOverlay( dummy );
|
|
mOverlayRef = 0;
|
|
}
|
|
|
|
status_t ret = NO_ERROR;
|
|
if (mSurface != 0) {
|
|
if (mOverlayRef.get() == NULL) {
|
|
|
|
// FIXME:
|
|
// Surfaceflinger may hold onto the previous overlay reference for some
|
|
// time after we try to destroy it. retry a few times. In the future, we
|
|
// should make the destroy call block, or possibly specify that we can
|
|
// wait in the createOverlay call if the previous overlay is in the
|
|
// process of being destroyed.
|
|
for (int retry = 0; retry < 50; ++retry) {
|
|
mOverlayRef = mSurface->createOverlay(w, h, OVERLAY_FORMAT_DEFAULT);
|
|
if (mOverlayRef != NULL) break;
|
|
LOGD("Overlay create failed - retrying");
|
|
usleep(20000);
|
|
}
|
|
if ( mOverlayRef.get() == NULL )
|
|
{
|
|
LOGE("Overlay Creation Failed!");
|
|
return -EINVAL;
|
|
}
|
|
ret = mHardware->setOverlay(new Overlay(mOverlayRef));
|
|
}
|
|
} else {
|
|
ret = mHardware->setOverlay(NULL);
|
|
}
|
|
if (ret != NO_ERROR) {
|
|
LOGE("mHardware->setOverlay() failed with status %d\n", ret);
|
|
}
|
|
|
|
mOverlayW = w;
|
|
mOverlayH = h;
|
|
|
|
return ret;
|
|
}
|
|
|
|
status_t CameraService::Client::registerPreviewBuffers()
|
|
{
|
|
int w, h;
|
|
CameraParameters params(mHardware->getParameters());
|
|
params.getPreviewSize(&w, &h);
|
|
|
|
uint32_t transform = 0;
|
|
if (params.getOrientation() ==
|
|
CameraParameters::CAMERA_ORIENTATION_PORTRAIT) {
|
|
LOGV("portrait mode");
|
|
transform = ISurface::BufferHeap::ROT_90;
|
|
}
|
|
ISurface::BufferHeap buffers(w, h, w, h,
|
|
PIXEL_FORMAT_YCbCr_420_SP,
|
|
transform,
|
|
0,
|
|
mHardware->getPreviewHeap());
|
|
|
|
status_t ret = mSurface->registerBuffers(buffers);
|
|
if (ret != NO_ERROR) {
|
|
LOGE("registerBuffers failed with status %d", ret);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
status_t CameraService::Client::startPreviewMode()
|
|
{
|
|
LOGD("startPreviewMode (pid %d)", getCallingPid());
|
|
|
|
// if preview has been enabled, nothing needs to be done
|
|
if (mHardware->previewEnabled()) {
|
|
return NO_ERROR;
|
|
}
|
|
|
|
// start preview mode
|
|
#if DEBUG_DUMP_PREVIEW_FRAME_TO_FILE
|
|
debug_frame_cnt = 0;
|
|
#endif
|
|
status_t ret = NO_ERROR;
|
|
|
|
if (mUseOverlay) {
|
|
// If preview display has been set, set overlay now.
|
|
if (mSurface != 0) {
|
|
ret = setOverlay();
|
|
}
|
|
if (ret != NO_ERROR) return ret;
|
|
ret = mHardware->startPreview();
|
|
} else {
|
|
mHardware->enableMsgType(CAMERA_MSG_PREVIEW_FRAME);
|
|
ret = mHardware->startPreview();
|
|
if (ret != NO_ERROR) return ret;
|
|
// If preview display has been set, register preview buffers now.
|
|
if (mSurface != 0) {
|
|
// Unregister here because the surface registered with raw heap.
|
|
mSurface->unregisterBuffers();
|
|
ret = registerPreviewBuffers();
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
status_t CameraService::Client::startPreview()
|
|
{
|
|
LOGD("startPreview (pid %d)", getCallingPid());
|
|
|
|
return startCameraMode(CAMERA_PREVIEW_MODE);
|
|
}
|
|
|
|
status_t CameraService::Client::startRecording()
|
|
{
|
|
LOGD("startRecording (pid %d)", getCallingPid());
|
|
|
|
if (mMediaPlayerBeep.get() != NULL) {
|
|
mMediaPlayerBeep->seekTo(0);
|
|
mMediaPlayerBeep->start();
|
|
}
|
|
|
|
mHardware->enableMsgType(CAMERA_MSG_VIDEO_FRAME);
|
|
|
|
return startCameraMode(CAMERA_RECORDING_MODE);
|
|
}
|
|
|
|
// stop preview mode
|
|
void CameraService::Client::stopPreview()
|
|
{
|
|
LOGD("stopPreview (pid %d)", getCallingPid());
|
|
|
|
Mutex::Autolock lock(mLock);
|
|
if (checkPid() != NO_ERROR) return;
|
|
|
|
if (mHardware == 0) {
|
|
LOGE("mHardware is NULL, returning.");
|
|
return;
|
|
}
|
|
|
|
mHardware->stopPreview();
|
|
mHardware->disableMsgType(CAMERA_MSG_PREVIEW_FRAME);
|
|
LOGD("stopPreview(), hardware stopped OK");
|
|
|
|
if (mSurface != 0 && !mUseOverlay) {
|
|
mSurface->unregisterBuffers();
|
|
}
|
|
mPreviewBuffer.clear();
|
|
}
|
|
|
|
// stop recording mode
|
|
void CameraService::Client::stopRecording()
|
|
{
|
|
LOGD("stopRecording (pid %d)", getCallingPid());
|
|
|
|
Mutex::Autolock lock(mLock);
|
|
if (checkPid() != NO_ERROR) return;
|
|
|
|
if (mHardware == 0) {
|
|
LOGE("mHardware is NULL, returning.");
|
|
return;
|
|
}
|
|
|
|
if (mMediaPlayerBeep.get() != NULL) {
|
|
mMediaPlayerBeep->seekTo(0);
|
|
mMediaPlayerBeep->start();
|
|
}
|
|
|
|
mHardware->stopRecording();
|
|
mHardware->disableMsgType(CAMERA_MSG_VIDEO_FRAME);
|
|
LOGD("stopRecording(), hardware stopped OK");
|
|
|
|
mPreviewBuffer.clear();
|
|
}
|
|
|
|
// release a recording frame
|
|
void CameraService::Client::releaseRecordingFrame(const sp<IMemory>& mem)
|
|
{
|
|
Mutex::Autolock lock(mLock);
|
|
if (checkPid() != NO_ERROR) return;
|
|
|
|
if (mHardware == 0) {
|
|
LOGE("mHardware is NULL, returning.");
|
|
return;
|
|
}
|
|
|
|
mHardware->releaseRecordingFrame(mem);
|
|
}
|
|
|
|
bool CameraService::Client::previewEnabled()
|
|
{
|
|
Mutex::Autolock lock(mLock);
|
|
if (mHardware == 0) return false;
|
|
return mHardware->previewEnabled();
|
|
}
|
|
|
|
bool CameraService::Client::recordingEnabled()
|
|
{
|
|
Mutex::Autolock lock(mLock);
|
|
if (mHardware == 0) return false;
|
|
return mHardware->recordingEnabled();
|
|
}
|
|
|
|
// Safely retrieves a strong pointer to the client during a hardware callback.
|
|
sp<CameraService::Client> CameraService::Client::getClientFromCookie(void* user)
|
|
{
|
|
sp<Client> client = 0;
|
|
CameraService *service = static_cast<CameraService*>(user);
|
|
if (service != NULL) {
|
|
Mutex::Autolock ourLock(service->mServiceLock);
|
|
if (service->mClient != 0) {
|
|
client = service->mClient.promote();
|
|
if (client == 0) {
|
|
LOGE("getClientFromCookie: client appears to have died");
|
|
service->mClient.clear();
|
|
}
|
|
} else {
|
|
LOGE("getClientFromCookie: got callback but client was NULL");
|
|
}
|
|
}
|
|
return client;
|
|
}
|
|
|
|
|
|
#if DEBUG_DUMP_JPEG_SNAPSHOT_TO_FILE || \
|
|
DEBUG_DUMP_YUV_SNAPSHOT_TO_FILE || \
|
|
DEBUG_DUMP_PREVIEW_FRAME_TO_FILE
|
|
static void dump_to_file(const char *fname,
|
|
uint8_t *buf, uint32_t size)
|
|
{
|
|
int nw, cnt = 0;
|
|
uint32_t written = 0;
|
|
|
|
LOGD("opening file [%s]\n", fname);
|
|
int fd = open(fname, O_RDWR | O_CREAT);
|
|
if (fd < 0) {
|
|
LOGE("failed to create file [%s]: %s", fname, strerror(errno));
|
|
return;
|
|
}
|
|
|
|
LOGD("writing %d bytes to file [%s]\n", size, fname);
|
|
while (written < size) {
|
|
nw = ::write(fd,
|
|
buf + written,
|
|
size - written);
|
|
if (nw < 0) {
|
|
LOGE("failed to write to file [%s]: %s",
|
|
fname, strerror(errno));
|
|
break;
|
|
}
|
|
written += nw;
|
|
cnt++;
|
|
}
|
|
LOGD("done writing %d bytes to file [%s] in %d passes\n",
|
|
size, fname, cnt);
|
|
::close(fd);
|
|
}
|
|
#endif
|
|
|
|
status_t CameraService::Client::autoFocus()
|
|
{
|
|
LOGD("autoFocus (pid %d)", getCallingPid());
|
|
|
|
Mutex::Autolock lock(mLock);
|
|
status_t result = checkPid();
|
|
if (result != NO_ERROR) return result;
|
|
|
|
if (mHardware == 0) {
|
|
LOGE("mHardware is NULL, returning.");
|
|
return INVALID_OPERATION;
|
|
}
|
|
|
|
return mHardware->autoFocus();
|
|
}
|
|
|
|
status_t CameraService::Client::cancelAutoFocus()
|
|
{
|
|
LOGD("cancelAutoFocus (pid %d)", getCallingPid());
|
|
|
|
Mutex::Autolock lock(mLock);
|
|
status_t result = checkPid();
|
|
if (result != NO_ERROR) return result;
|
|
|
|
if (mHardware == 0) {
|
|
LOGE("mHardware is NULL, returning.");
|
|
return INVALID_OPERATION;
|
|
}
|
|
|
|
return mHardware->cancelAutoFocus();
|
|
}
|
|
|
|
// take a picture - image is returned in callback
|
|
status_t CameraService::Client::takePicture()
|
|
{
|
|
LOGD("takePicture (pid %d)", getCallingPid());
|
|
|
|
Mutex::Autolock lock(mLock);
|
|
status_t result = checkPid();
|
|
if (result != NO_ERROR) return result;
|
|
|
|
if (mHardware == 0) {
|
|
LOGE("mHardware is NULL, returning.");
|
|
return INVALID_OPERATION;
|
|
}
|
|
|
|
mHardware->enableMsgType(CAMERA_MSG_SHUTTER |
|
|
CAMERA_MSG_POSTVIEW_FRAME |
|
|
CAMERA_MSG_RAW_IMAGE |
|
|
CAMERA_MSG_COMPRESSED_IMAGE);
|
|
|
|
return mHardware->takePicture();
|
|
}
|
|
|
|
// snapshot taken
|
|
void CameraService::Client::handleShutter(
|
|
image_rect_type *size // The width and height of yuv picture for
|
|
// registerBuffer. If this is NULL, use the picture
|
|
// size from parameters.
|
|
)
|
|
{
|
|
// Play shutter sound.
|
|
if (mMediaPlayerClick.get() != NULL) {
|
|
mMediaPlayerClick->seekTo(0);
|
|
mMediaPlayerClick->start();
|
|
}
|
|
|
|
// Screen goes black after the buffer is unregistered.
|
|
if (mSurface != 0 && !mUseOverlay) {
|
|
mSurface->unregisterBuffers();
|
|
}
|
|
|
|
sp<ICameraClient> c = mCameraClient;
|
|
if (c != NULL) {
|
|
c->notifyCallback(CAMERA_MSG_SHUTTER, 0, 0);
|
|
}
|
|
mHardware->disableMsgType(CAMERA_MSG_SHUTTER);
|
|
|
|
// It takes some time before yuvPicture callback to be called.
|
|
// Register the buffer for raw image here to reduce latency.
|
|
if (mSurface != 0 && !mUseOverlay) {
|
|
int w, h;
|
|
CameraParameters params(mHardware->getParameters());
|
|
uint32_t transform = 0;
|
|
if (params.getOrientation() == CameraParameters::CAMERA_ORIENTATION_PORTRAIT) {
|
|
LOGV("portrait mode");
|
|
transform = ISurface::BufferHeap::ROT_90;
|
|
}
|
|
|
|
if (size == NULL) {
|
|
params.getPictureSize(&w, &h);
|
|
} else {
|
|
w = size->width;
|
|
h = size->height;
|
|
w &= ~1;
|
|
h &= ~1;
|
|
LOGD("Snapshot image width=%d, height=%d", w, h);
|
|
}
|
|
ISurface::BufferHeap buffers(w, h, w, h,
|
|
PIXEL_FORMAT_YCbCr_420_SP, transform, 0, mHardware->getRawHeap());
|
|
|
|
mSurface->registerBuffers(buffers);
|
|
}
|
|
}
|
|
|
|
// preview callback - frame buffer update
|
|
void CameraService::Client::handlePreviewData(const sp<IMemory>& mem)
|
|
{
|
|
ssize_t offset;
|
|
size_t size;
|
|
sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
|
|
|
|
#if DEBUG_HEAP_LEAKS && 0 // debugging
|
|
if (gWeakHeap == NULL) {
|
|
if (gWeakHeap != heap) {
|
|
LOGD("SETTING PREVIEW HEAP");
|
|
heap->trackMe(true, true);
|
|
gWeakHeap = heap;
|
|
}
|
|
}
|
|
#endif
|
|
#if DEBUG_DUMP_PREVIEW_FRAME_TO_FILE
|
|
{
|
|
if (debug_frame_cnt++ == DEBUG_DUMP_PREVIEW_FRAME_TO_FILE) {
|
|
dump_to_file("/data/preview.yuv",
|
|
(uint8_t *)heap->base() + offset, size);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (!mUseOverlay)
|
|
{
|
|
Mutex::Autolock surfaceLock(mSurfaceLock);
|
|
if (mSurface != NULL) {
|
|
mSurface->postBuffer(offset);
|
|
}
|
|
}
|
|
|
|
// local copy of the callback flags
|
|
int flags = mPreviewCallbackFlag;
|
|
|
|
// is callback enabled?
|
|
if (!(flags & FRAME_CALLBACK_FLAG_ENABLE_MASK)) {
|
|
// If the enable bit is off, the copy-out and one-shot bits are ignored
|
|
LOGV("frame callback is diabled");
|
|
return;
|
|
}
|
|
|
|
// hold a strong pointer to the client
|
|
sp<ICameraClient> c = mCameraClient;
|
|
|
|
// clear callback flags if no client or one-shot mode
|
|
if ((c == NULL) || (mPreviewCallbackFlag & FRAME_CALLBACK_FLAG_ONE_SHOT_MASK)) {
|
|
LOGV("Disable preview callback");
|
|
mPreviewCallbackFlag &= ~(FRAME_CALLBACK_FLAG_ONE_SHOT_MASK |
|
|
FRAME_CALLBACK_FLAG_COPY_OUT_MASK |
|
|
FRAME_CALLBACK_FLAG_ENABLE_MASK);
|
|
// TODO: Shouldn't we use this API for non-overlay hardware as well?
|
|
if (mUseOverlay)
|
|
mHardware->disableMsgType(CAMERA_MSG_PREVIEW_FRAME);
|
|
}
|
|
|
|
// Is the received frame copied out or not?
|
|
if (flags & FRAME_CALLBACK_FLAG_COPY_OUT_MASK) {
|
|
LOGV("frame is copied");
|
|
copyFrameAndPostCopiedFrame(c, heap, offset, size);
|
|
} else {
|
|
LOGV("frame is forwarded");
|
|
c->dataCallback(CAMERA_MSG_PREVIEW_FRAME, mem);
|
|
}
|
|
}
|
|
|
|
// picture callback - postview image ready
|
|
void CameraService::Client::handlePostview(const sp<IMemory>& mem)
|
|
{
|
|
#if DEBUG_DUMP_POSTVIEW_SNAPSHOT_TO_FILE // for testing pursposes only
|
|
{
|
|
ssize_t offset;
|
|
size_t size;
|
|
sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
|
|
dump_to_file("/data/postview.yuv",
|
|
(uint8_t *)heap->base() + offset, size);
|
|
}
|
|
#endif
|
|
|
|
sp<ICameraClient> c = mCameraClient;
|
|
if (c != NULL) {
|
|
c->dataCallback(CAMERA_MSG_POSTVIEW_FRAME, mem);
|
|
}
|
|
mHardware->disableMsgType(CAMERA_MSG_POSTVIEW_FRAME);
|
|
}
|
|
|
|
// picture callback - raw image ready
|
|
void CameraService::Client::handleRawPicture(const sp<IMemory>& mem)
|
|
{
|
|
ssize_t offset;
|
|
size_t size;
|
|
sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
|
|
#if DEBUG_HEAP_LEAKS && 0 // debugging
|
|
gWeakHeap = heap; // debugging
|
|
#endif
|
|
|
|
//LOGV("handleRawPicture(%d, %d)", offset, size);
|
|
#if DEBUG_DUMP_YUV_SNAPSHOT_TO_FILE // for testing pursposes only
|
|
dump_to_file("/data/photo.yuv",
|
|
(uint8_t *)heap->base() + offset, size);
|
|
#endif
|
|
|
|
// Put the YUV version of the snapshot in the preview display.
|
|
if (mSurface != 0 && !mUseOverlay) {
|
|
mSurface->postBuffer(offset);
|
|
}
|
|
|
|
sp<ICameraClient> c = mCameraClient;
|
|
if (c != NULL) {
|
|
c->dataCallback(CAMERA_MSG_RAW_IMAGE, mem);
|
|
}
|
|
mHardware->disableMsgType(CAMERA_MSG_RAW_IMAGE);
|
|
}
|
|
|
|
// picture callback - compressed picture ready
|
|
void CameraService::Client::handleCompressedPicture(const sp<IMemory>& mem)
|
|
{
|
|
#if DEBUG_DUMP_JPEG_SNAPSHOT_TO_FILE // for testing pursposes only
|
|
{
|
|
ssize_t offset;
|
|
size_t size;
|
|
sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
|
|
dump_to_file("/data/photo.jpg",
|
|
(uint8_t *)heap->base() + offset, size);
|
|
}
|
|
#endif
|
|
|
|
sp<ICameraClient> c = mCameraClient;
|
|
if (c != NULL) {
|
|
c->dataCallback(CAMERA_MSG_COMPRESSED_IMAGE, mem);
|
|
}
|
|
mHardware->disableMsgType(CAMERA_MSG_COMPRESSED_IMAGE);
|
|
}
|
|
|
|
void CameraService::Client::notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2, void* user)
|
|
{
|
|
LOGV("notifyCallback(%d)", msgType);
|
|
|
|
sp<Client> client = getClientFromCookie(user);
|
|
if (client == 0) {
|
|
return;
|
|
}
|
|
|
|
switch (msgType) {
|
|
case CAMERA_MSG_SHUTTER:
|
|
// ext1 is the dimension of the yuv picture.
|
|
client->handleShutter((image_rect_type *)ext1);
|
|
break;
|
|
default:
|
|
sp<ICameraClient> c = client->mCameraClient;
|
|
if (c != NULL) {
|
|
c->notifyCallback(msgType, ext1, ext2);
|
|
}
|
|
break;
|
|
}
|
|
|
|
#if DEBUG_CLIENT_REFERENCES
|
|
if (client->getStrongCount() == 1) {
|
|
LOGE("++++++++++++++++ (NOTIFY CALLBACK) THIS WILL CAUSE A LOCKUP!");
|
|
client->printRefs();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void CameraService::Client::dataCallback(int32_t msgType, const sp<IMemory>& dataPtr, void* user)
|
|
{
|
|
LOGV("dataCallback(%d)", msgType);
|
|
|
|
sp<Client> client = getClientFromCookie(user);
|
|
if (client == 0) {
|
|
return;
|
|
}
|
|
|
|
sp<ICameraClient> c = client->mCameraClient;
|
|
if (dataPtr == NULL) {
|
|
LOGE("Null data returned in data callback");
|
|
if (c != NULL) {
|
|
c->notifyCallback(CAMERA_MSG_ERROR, UNKNOWN_ERROR, 0);
|
|
c->dataCallback(msgType, NULL);
|
|
}
|
|
return;
|
|
}
|
|
|
|
switch (msgType) {
|
|
case CAMERA_MSG_PREVIEW_FRAME:
|
|
client->handlePreviewData(dataPtr);
|
|
break;
|
|
case CAMERA_MSG_POSTVIEW_FRAME:
|
|
client->handlePostview(dataPtr);
|
|
break;
|
|
case CAMERA_MSG_RAW_IMAGE:
|
|
client->handleRawPicture(dataPtr);
|
|
break;
|
|
case CAMERA_MSG_COMPRESSED_IMAGE:
|
|
client->handleCompressedPicture(dataPtr);
|
|
break;
|
|
default:
|
|
if (c != NULL) {
|
|
c->dataCallback(msgType, dataPtr);
|
|
}
|
|
break;
|
|
}
|
|
|
|
#if DEBUG_CLIENT_REFERENCES
|
|
if (client->getStrongCount() == 1) {
|
|
LOGE("++++++++++++++++ (DATA CALLBACK) THIS WILL CAUSE A LOCKUP!");
|
|
client->printRefs();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void CameraService::Client::dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType,
|
|
const sp<IMemory>& dataPtr, void* user)
|
|
{
|
|
LOGV("dataCallbackTimestamp(%d)", msgType);
|
|
|
|
sp<Client> client = getClientFromCookie(user);
|
|
if (client == 0) {
|
|
return;
|
|
}
|
|
sp<ICameraClient> c = client->mCameraClient;
|
|
|
|
if (dataPtr == NULL) {
|
|
LOGE("Null data returned in data with timestamp callback");
|
|
if (c != NULL) {
|
|
c->notifyCallback(CAMERA_MSG_ERROR, UNKNOWN_ERROR, 0);
|
|
c->dataCallbackTimestamp(0, msgType, NULL);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (c != NULL) {
|
|
c->dataCallbackTimestamp(timestamp, msgType, dataPtr);
|
|
}
|
|
|
|
#if DEBUG_CLIENT_REFERENCES
|
|
if (client->getStrongCount() == 1) {
|
|
LOGE("++++++++++++++++ (DATA CALLBACK TIMESTAMP) THIS WILL CAUSE A LOCKUP!");
|
|
client->printRefs();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// set preview/capture parameters - key/value pairs
|
|
status_t CameraService::Client::setParameters(const String8& params)
|
|
{
|
|
LOGD("setParameters(%s)", params.string());
|
|
|
|
Mutex::Autolock lock(mLock);
|
|
status_t result = checkPid();
|
|
if (result != NO_ERROR) return result;
|
|
|
|
if (mHardware == 0) {
|
|
LOGE("mHardware is NULL, returning.");
|
|
return INVALID_OPERATION;
|
|
}
|
|
|
|
CameraParameters p(params);
|
|
return mHardware->setParameters(p);
|
|
}
|
|
|
|
// get preview/capture parameters - key/value pairs
|
|
String8 CameraService::Client::getParameters() const
|
|
{
|
|
Mutex::Autolock lock(mLock);
|
|
|
|
if (mHardware == 0) {
|
|
LOGE("mHardware is NULL, returning.");
|
|
return String8();
|
|
}
|
|
|
|
String8 params(mHardware->getParameters().flatten());
|
|
LOGD("getParameters(%s)", params.string());
|
|
return params;
|
|
}
|
|
|
|
status_t CameraService::Client::sendCommand(int32_t cmd, int32_t arg1, int32_t arg2)
|
|
{
|
|
LOGD("sendCommand (pid %d)", getCallingPid());
|
|
Mutex::Autolock lock(mLock);
|
|
status_t result = checkPid();
|
|
if (result != NO_ERROR) return result;
|
|
|
|
if (mHardware == 0) {
|
|
LOGE("mHardware is NULL, returning.");
|
|
return INVALID_OPERATION;
|
|
}
|
|
|
|
return mHardware->sendCommand(cmd, arg1, arg2);
|
|
}
|
|
|
|
void CameraService::Client::copyFrameAndPostCopiedFrame(const sp<ICameraClient>& client,
|
|
const sp<IMemoryHeap>& heap, size_t offset, size_t size)
|
|
{
|
|
LOGV("copyFrameAndPostCopiedFrame");
|
|
// It is necessary to copy out of pmem before sending this to
|
|
// the callback. For efficiency, reuse the same MemoryHeapBase
|
|
// provided it's big enough. Don't allocate the memory or
|
|
// perform the copy if there's no callback.
|
|
|
|
// hold the lock while we grab a reference to the preview buffer
|
|
sp<MemoryHeapBase> previewBuffer;
|
|
{
|
|
Mutex::Autolock lock(mLock);
|
|
if (mPreviewBuffer == 0) {
|
|
mPreviewBuffer = new MemoryHeapBase(size, 0, NULL);
|
|
} else if (size > mPreviewBuffer->virtualSize()) {
|
|
mPreviewBuffer.clear();
|
|
mPreviewBuffer = new MemoryHeapBase(size, 0, NULL);
|
|
}
|
|
if (mPreviewBuffer == 0) {
|
|
LOGE("failed to allocate space for preview buffer");
|
|
return;
|
|
}
|
|
previewBuffer = mPreviewBuffer;
|
|
}
|
|
memcpy(previewBuffer->base(),
|
|
(uint8_t *)heap->base() + offset, size);
|
|
|
|
sp<MemoryBase> frame = new MemoryBase(previewBuffer, 0, size);
|
|
if (frame == 0) {
|
|
LOGE("failed to allocate space for frame callback");
|
|
return;
|
|
}
|
|
client->dataCallback(CAMERA_MSG_PREVIEW_FRAME, frame);
|
|
}
|
|
|
|
status_t CameraService::dump(int fd, const Vector<String16>& args)
|
|
{
|
|
const size_t SIZE = 256;
|
|
char buffer[SIZE];
|
|
String8 result;
|
|
if (checkCallingPermission(String16("android.permission.DUMP")) == false) {
|
|
snprintf(buffer, SIZE, "Permission Denial: "
|
|
"can't dump CameraService from pid=%d, uid=%d\n",
|
|
getCallingPid(),
|
|
IPCThreadState::self()->getCallingUid());
|
|
result.append(buffer);
|
|
write(fd, result.string(), result.size());
|
|
} else {
|
|
AutoMutex lock(&mServiceLock);
|
|
if (mClient != 0) {
|
|
sp<Client> currentClient = mClient.promote();
|
|
sprintf(buffer, "Client (%p) PID: %d\n",
|
|
currentClient->getCameraClient()->asBinder().get(),
|
|
currentClient->mClientPid);
|
|
result.append(buffer);
|
|
write(fd, result.string(), result.size());
|
|
currentClient->mHardware->dump(fd, args);
|
|
} else {
|
|
result.append("No camera client yet.\n");
|
|
write(fd, result.string(), result.size());
|
|
}
|
|
}
|
|
return NO_ERROR;
|
|
}
|
|
|
|
|
|
status_t CameraService::onTransact(
|
|
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
|
|
{
|
|
// permission checks...
|
|
switch (code) {
|
|
case BnCameraService::CONNECT:
|
|
IPCThreadState* ipc = IPCThreadState::self();
|
|
const int pid = ipc->getCallingPid();
|
|
const int self_pid = getpid();
|
|
if (pid != self_pid) {
|
|
// we're called from a different process, do the real check
|
|
if (!checkCallingPermission(
|
|
String16("android.permission.CAMERA")))
|
|
{
|
|
const int uid = ipc->getCallingUid();
|
|
LOGE("Permission Denial: "
|
|
"can't use the camera pid=%d, uid=%d", pid, uid);
|
|
return PERMISSION_DENIED;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
status_t err = BnCameraService::onTransact(code, data, reply, flags);
|
|
|
|
#if DEBUG_HEAP_LEAKS
|
|
LOGD("+++ onTransact err %d code %d", err, code);
|
|
|
|
if (err == UNKNOWN_TRANSACTION || err == PERMISSION_DENIED) {
|
|
// the 'service' command interrogates this binder for its name, and then supplies it
|
|
// even for the debugging commands. that means we need to check for it here, using
|
|
// ISurfaceComposer (since we delegated the INTERFACE_TRANSACTION handling to
|
|
// BnSurfaceComposer before falling through to this code).
|
|
|
|
LOGD("+++ onTransact code %d", code);
|
|
|
|
CHECK_INTERFACE(ICameraService, data, reply);
|
|
|
|
switch(code) {
|
|
case 1000:
|
|
{
|
|
if (gWeakHeap != 0) {
|
|
sp<IMemoryHeap> h = gWeakHeap.promote();
|
|
IMemoryHeap *p = gWeakHeap.unsafe_get();
|
|
LOGD("CHECKING WEAK REFERENCE %p (%p)", h.get(), p);
|
|
if (h != 0)
|
|
h->printRefs();
|
|
bool attempt_to_delete = data.readInt32() == 1;
|
|
if (attempt_to_delete) {
|
|
// NOT SAFE!
|
|
LOGD("DELETING WEAK REFERENCE %p (%p)", h.get(), p);
|
|
if (p) delete p;
|
|
}
|
|
return NO_ERROR;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
#endif // DEBUG_HEAP_LEAKS
|
|
|
|
return err;
|
|
}
|
|
|
|
}; // namespace android
|