summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--camera/Camera.cpp4
-rw-r--r--camera/ICameraService.cpp11
-rw-r--r--include/camera/Camera.h2
-rw-r--r--include/camera/ICameraService.h2
-rwxr-xr-xmedia/libstagefright/CameraSource.cpp2
-rw-r--r--services/camera/libcameraservice/CameraService.cpp149
-rw-r--r--services/camera/libcameraservice/CameraService.h15
7 files changed, 143 insertions, 42 deletions
diff --git a/camera/Camera.cpp b/camera/Camera.cpp
index d43cb0b..b81fe86 100644
--- a/camera/Camera.cpp
+++ b/camera/Camera.cpp
@@ -116,13 +116,13 @@ status_t Camera::getCameraInfo(int cameraId,
return cs->getCameraInfo(cameraId, cameraInfo);
}
-sp<Camera> Camera::connect(int cameraId)
+sp<Camera> Camera::connect(int cameraId, bool force, bool keep)
{
ALOGV("connect");
sp<Camera> c = new Camera();
const sp<ICameraService>& cs = getCameraService();
if (cs != 0) {
- c->mCamera = cs->connect(c, cameraId);
+ c->mCamera = cs->connect(c, cameraId, force, keep);
}
if (c->mCamera != 0) {
c->mCamera->asBinder()->linkToDeath(c);
diff --git a/camera/ICameraService.cpp b/camera/ICameraService.cpp
index 85f1a29..c74298a 100644
--- a/camera/ICameraService.cpp
+++ b/camera/ICameraService.cpp
@@ -56,12 +56,15 @@ public:
}
// connect to camera service
- virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient, int cameraId)
+ virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient, int cameraId,
+ bool force, bool keep)
{
Parcel data, reply;
data.writeInterfaceToken(ICameraService::getInterfaceDescriptor());
data.writeStrongBinder(cameraClient->asBinder());
data.writeInt32(cameraId);
+ data.writeInt32(force);
+ data.writeInt32(keep);
remote()->transact(BnCameraService::CONNECT, data, &reply);
return interface_cast<ICamera>(reply.readStrongBinder());
}
@@ -93,7 +96,10 @@ status_t BnCameraService::onTransact(
case CONNECT: {
CHECK_INTERFACE(ICameraService, data, reply);
sp<ICameraClient> cameraClient = interface_cast<ICameraClient>(data.readStrongBinder());
- sp<ICamera> camera = connect(cameraClient, data.readInt32());
+ const int cameraId = data.readInt32();
+ const int force = data.readInt32();
+ const int keep = data.readInt32();
+ sp<ICamera> camera = connect(cameraClient, cameraId, force, keep);
reply->writeStrongBinder(camera->asBinder());
return NO_ERROR;
} break;
@@ -105,4 +111,3 @@ status_t BnCameraService::onTransact(
// ----------------------------------------------------------------------------
}; // namespace android
-
diff --git a/include/camera/Camera.h b/include/camera/Camera.h
index 234e165..3fedea0 100644
--- a/include/camera/Camera.h
+++ b/include/camera/Camera.h
@@ -72,7 +72,7 @@ public:
static int32_t getNumberOfCameras();
static status_t getCameraInfo(int cameraId,
struct CameraInfo* cameraInfo);
- static sp<Camera> connect(int cameraId);
+ static sp<Camera> connect(int cameraId, bool force, bool keep);
virtual ~Camera();
void init();
diff --git a/include/camera/ICameraService.h b/include/camera/ICameraService.h
index 7d70c1e..97e3169 100644
--- a/include/camera/ICameraService.h
+++ b/include/camera/ICameraService.h
@@ -42,7 +42,7 @@ public:
virtual status_t getCameraInfo(int cameraId,
struct CameraInfo* cameraInfo) = 0;
virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient,
- int cameraId) = 0;
+ int cameraId, bool force, bool keep) = 0;
};
// ----------------------------------------------------------------------------
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index 2df5528..0d67800 100755
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -182,7 +182,7 @@ status_t CameraSource::isCameraAvailable(
int32_t cameraId) {
if (camera == 0) {
- mCamera = Camera::connect(cameraId);
+ mCamera = Camera::connect(cameraId, false, false);
if (mCamera == 0) return -EBUSY;
mCameraFlags &= ~FLAGS_HOT_CAMERA;
} else {
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index adf1d49..22836e3 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -21,6 +21,7 @@
#include <stdio.h>
#include <sys/types.h>
#include <pthread.h>
+#include <time.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
@@ -33,6 +34,7 @@
#include <hardware/hardware.h>
#include <media/AudioSystem.h>
#include <media/mediaplayer.h>
+#include <utils/Condition.h>
#include <utils/Errors.h>
#include <utils/Log.h>
#include <utils/String16.h>
@@ -42,6 +44,8 @@
namespace android {
+#define WAIT_RELEASE_TIMEOUT 250 // 250ms
+
// ----------------------------------------------------------------------------
// Logging support -- this is for debugging only
// Use "adb shell dumpsys media.camera -v 1" to change it.
@@ -64,6 +68,13 @@ static int getCallingUid() {
return IPCThreadState::self()->getCallingUid();
}
+static long long getTimeInMs() {
+ struct timeval t;
+ t.tv_sec = t.tv_usec = 0;
+ gettimeofday(&t, NULL);
+ return t.tv_sec * 1000LL + t.tv_usec / 1000;
+}
+
// ----------------------------------------------------------------------------
// This is ugly and only safe if we never re-create the CameraService, but
@@ -131,7 +142,7 @@ status_t CameraService::getCameraInfo(int cameraId,
}
sp<ICamera> CameraService::connect(
- const sp<ICameraClient>& cameraClient, int cameraId) {
+ const sp<ICameraClient>& cameraClient, int cameraId, bool force, bool keep) {
int callingPid = getCallingPid();
sp<CameraHardwareInterface> hardware = NULL;
@@ -157,27 +168,73 @@ sp<ICamera> CameraService::connect(
return NULL;
}
+ if (keep && !checkCallingPermission(String16("android.permission.KEEP_CAMERA"))) {
+ ALOGE("connect X (pid %d) rejected (no KEEP_CAMERA permission).", callingPid);
+ return NULL;
+ }
+
Mutex::Autolock lock(mServiceLock);
- if (mClient[cameraId] != 0) {
- client = mClient[cameraId].promote();
- if (client != 0) {
- if (cameraClient->asBinder() == client->getCameraClient()->asBinder()) {
- LOG1("CameraService::connect X (pid %d) (the same client)",
- callingPid);
- return client;
- } else {
- ALOGW("CameraService::connect X (pid %d) rejected (existing client).",
- callingPid);
- return NULL;
- }
+ // Check if there is an existing client.
+ client = mClient[cameraId].promote();
+ if (client != 0 &&
+ cameraClient->asBinder() == client->getCameraClient()->asBinder()) {
+ LOG1("connect X (pid %d) (the same client)", callingPid);
+ return client;
+ }
+
+ if (!force) {
+ if (mClient[cameraId].promote() != 0) {
+ ALOGW("connect X (pid %d) rejected (existing client).", callingPid);
+ return NULL;
}
mClient[cameraId].clear();
- }
+ if (mBusy[cameraId]) {
+ ALOGW("connect X (pid %d) rejected (camera %d is still busy).",
+ callingPid, cameraId);
+ return NULL;
+ }
+ } else { // force == true
+ int i = 0;
+ long long start_time = getTimeInMs();
+ while (i < mNumberOfCameras) {
+ if (getTimeInMs() - start_time >= 3000LL) {
+ ALOGE("connect X (pid %d) rejected (timeout 3s)", callingPid);
+ return NULL;
+ }
- if (mBusy[cameraId]) {
- ALOGW("CameraService::connect X (pid %d) rejected"
- " (camera %d is still busy).", callingPid, cameraId);
- return NULL;
+ client = mClient[i].promote();
+ if (client != 0) {
+ if (client->keep()) {
+ ALOGW("connect X (pid %d) rejected (existing client wants to keeps the camera)",
+ callingPid);
+ return NULL;
+ } else {
+ ALOGW("New client (pid %d, id=%d). Disconnect the existing client (id=%d).",
+ callingPid, cameraId, i);
+ // Do not hold mServiceLock because disconnect will try to get it.
+ mServiceLock.unlock();
+ client->notifyCallback(CAMERA_MSG_ERROR, CAMERA_ERROR_RELEASED, 0, &i);
+ client->waitRelease(WAIT_RELEASE_TIMEOUT);
+ client->disconnectInternal(false);
+ mServiceLock.lock();
+ // Restart from the first client because a new client may have connected
+ // when mServiceLock is unlocked.
+ i = 0;
+ continue;
+ }
+ }
+
+ if (mBusy[i]) {
+ // Give the client a chance to release the hardware.
+ mServiceLock.unlock();
+ usleep(10 * 1000);
+ mServiceLock.lock();
+ i = 0; // Restart from the first client
+ continue;
+ }
+
+ i++;
+ }
}
struct camera_info info;
@@ -195,9 +252,15 @@ sp<ICamera> CameraService::connect(
return NULL;
}
- client = new Client(this, cameraClient, hardware, cameraId, info.facing, callingPid);
+ client = new Client(this, cameraClient, hardware, cameraId, info.facing,
+ callingPid, keep);
+ // We need to clear the hardware here. After the destructor of mServiceLock
+ // finishes, a new client may connect and disconnect this client. If this
+ // reference is not cleared, the destructor of CameraHardwareInterface
+ // cannot run. The new client will not be able to connect.
+ hardware.clear();
mClient[cameraId] = client;
- LOG1("CameraService::connect X");
+ LOG1("CameraService::connect X (id %d)", cameraId);
return client;
}
@@ -331,9 +394,9 @@ void CameraService::playSound(sound_kind kind) {
CameraService::Client::Client(const sp<CameraService>& cameraService,
const sp<ICameraClient>& cameraClient,
const sp<CameraHardwareInterface>& hardware,
- int cameraId, int cameraFacing, int clientPid) {
+ int cameraId, int cameraFacing, int clientPid, bool keep) {
int callingPid = getCallingPid();
- LOG1("Client::Client E (pid %d)", callingPid);
+ LOG1("Client::Client E (pid %d, id %d)", callingPid, cameraId);
mCameraService = cameraService;
mCameraClient = cameraClient;
@@ -341,6 +404,7 @@ CameraService::Client::Client(const sp<CameraService>& cameraService,
mCameraId = cameraId;
mCameraFacing = cameraFacing;
mClientPid = clientPid;
+ mKeep = keep;
mMsgEnabled = 0;
mSurface = 0;
mPreviewWindow = 0;
@@ -359,7 +423,7 @@ CameraService::Client::Client(const sp<CameraService>& cameraService,
mPlayShutterSound = true;
cameraService->setCameraBusy(cameraId);
cameraService->loadSound();
- LOG1("Client::Client X (pid %d)", callingPid);
+ LOG1("Client::Client X (pid %d, id %d)", callingPid, cameraId);
}
// tear down the client
@@ -468,18 +532,24 @@ static void disconnectWindow(const sp<ANativeWindow>& window) {
}
void CameraService::Client::disconnect() {
+ disconnectInternal(true);
+}
+
+void CameraService::Client::disconnectInternal(bool needCheckPid) {
int callingPid = getCallingPid();
- LOG1("disconnect E (pid %d)", callingPid);
+ LOG1("disconnectInternal E (pid %d)", callingPid);
Mutex::Autolock lock(mLock);
- if (checkPid() != NO_ERROR) {
- ALOGW("different client - don't disconnect");
- return;
- }
+ if (needCheckPid) {
+ if (checkPid() != NO_ERROR) {
+ ALOGW("different client - don't disconnect");
+ return;
+ }
- if (mClientPid <= 0) {
- LOG1("camera is unlocked (mClientPid = %d), don't tear down hardware", mClientPid);
- return;
+ if (mClientPid <= 0) {
+ LOG1("camera is unlocked (mClientPid = %d), don't tear down hardware", mClientPid);
+ return;
+ }
}
// Make sure disconnect() is done once and once only, whether it is called
@@ -506,8 +576,16 @@ void CameraService::Client::disconnect() {
mCameraService->removeClient(mCameraClient);
mCameraService->setCameraFree(mCameraId);
+ mReleaseCondition.signal();
+
+ LOG1("disconnectInternal X (pid %d)", callingPid);
+}
- LOG1("disconnect X (pid %d)", callingPid);
+void CameraService::Client::waitRelease(int ms) {
+ Mutex::Autolock lock(mLock);
+ if (mHardware != 0) {
+ mReleaseCondition.waitRelative(mLock, ms * 1000000);
+ }
}
// ----------------------------------------------------------------------------
@@ -874,6 +952,9 @@ status_t CameraService::Client::sendCommand(int32_t cmd, int32_t arg1, int32_t a
return OK;
} else if (cmd == CAMERA_CMD_PLAY_RECORDING_SOUND) {
mCameraService->playSound(SOUND_RECORDING);
+ } else if (cmd == CAMERA_CMD_PING) {
+ // If mHardware is 0, checkPidAndHardware will return error.
+ return OK;
}
return mHardware->sendCommand(cmd, arg1, arg2);
@@ -1217,6 +1298,10 @@ int CameraService::Client::getOrientation(int degrees, bool mirror) {
return -1;
}
+// Whether the client wants to keep the camera from taking
+bool CameraService::Client::keep() const {
+ return mKeep;
+}
// ----------------------------------------------------------------------------
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index bad41f5..457c79b 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -46,7 +46,8 @@ public:
virtual int32_t getNumberOfCameras();
virtual status_t getCameraInfo(int cameraId,
struct CameraInfo* cameraInfo);
- virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient, int cameraId);
+ virtual sp<ICamera> connect(const sp<ICameraClient>& cameraClient, int cameraId,
+ bool force, bool keep);
virtual void removeClient(const sp<ICameraClient>& cameraClient);
virtual sp<Client> getClientById(int cameraId);
@@ -114,7 +115,8 @@ private:
const sp<CameraHardwareInterface>& hardware,
int cameraId,
int cameraFacing,
- int clientPid);
+ int clientPid,
+ bool keep);
~Client();
// return our camera client
@@ -172,12 +174,19 @@ private:
const sp<IBinder>& binder,
const sp<ANativeWindow>& window);
+ void disconnectInternal(bool needCheckPid);
+ bool keep() const;
+ void waitRelease(int ms);
+
+
// these are initialized in the constructor.
sp<CameraService> mCameraService; // immutable after constructor
sp<ICameraClient> mCameraClient;
int mCameraId; // immutable after constructor
int mCameraFacing; // immutable after constructor
pid_t mClientPid;
+ // Client wants to keep the camera from taking by other clients.
+ bool mKeep;
sp<CameraHardwareInterface> mHardware; // cleared after disconnect()
int mPreviewCallbackFlag;
int mOrientation; // Current display orientation
@@ -185,6 +194,8 @@ private:
// Ensures atomicity among the public methods
mutable Mutex mLock;
+ // This will get notified when the hardware is released.
+ Condition mReleaseCondition;
// This is a binder of Surface or SurfaceTexture.
sp<IBinder> mSurface;
sp<ANativeWindow> mPreviewWindow;