/*
**
** 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()
{
    // 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());
        params.getPictureSize(&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->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:
            client->handleShutter();
            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.
    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;
        }
    }
    memcpy(mPreviewBuffer->base(),
           (uint8_t *)heap->base() + offset, size);

    sp<MemoryBase> frame = new MemoryBase(mPreviewBuffer, 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
