Merge "Add a new camera open API that allows taking the ownership."
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index aa92efb..83b6986 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -236,6 +236,50 @@
/**
* Creates a new Camera object to access a particular hardware camera.
*
+ * <p>When <code>force</code> is set to false, this will throw an exception
+ * if the same camera is already opened by other clients. If true, the other
+ * client will be disconnected from the camera they opened. If the device
+ * can only support one camera running at a time, all camera-using clients
+ * will be disconnected from their cameras.
+ *
+ * <p>A camera being held by an application can be taken away by other
+ * applications at any time. Before the camera is taken, applications will
+ * get {@link #CAMERA_ERROR_RELEASED} and have some time to clean up. Apps
+ * receiving this callback must immediately stop video recording and then
+ * call {@link #release()} on their camera object. Otherwise, it will be
+ * released by the frameworks in a short time. After receiving
+ * CAMERA_ERROR_RELEASED, apps should not call any method except <code>
+ * release</code> and {@link #isReleased()}. After a camera is taken away,
+ * all methods will throw exceptions except <code>isReleased</code> and
+ * <code>release</code>. Apps can use <code>isReleased</code> to see if the
+ * camera has been taken away. If the camera is taken away, the apps can
+ * silently finish themselves or show a dialog.
+ *
+ * <p>Applications with android.permission.KEEP_CAMERA can request to keep
+ * the camera. That is, the camera will not be taken by other applications
+ * while it is opened. The permission can only be obtained by trusted
+ * platform applications, such as those implementing lock screen security
+ * features.
+ *
+ * @param cameraId the hardware camera to access, between 0 and
+ * {@link #getNumberOfCameras()}-1.
+ * @param force true to take the ownership from the existing client if the
+ * camera has been opened by other clients.
+ * @param keep true if the applications do not want other apps to take the
+ * camera. Only the apps with android.permission.KEEP_CAMERA can keep
+ * the camera.
+ * @return a new Camera object, connected, locked and ready for use.
+ * @hide
+ */
+ public static Camera open(int cameraId, boolean force, boolean keep) {
+ return new Camera(cameraId, force, keep);
+ }
+
+ /**
+ * Creates a new Camera object to access a particular hardware camera. If
+ * the same camera is opened by other applications, this will throw a
+ * RuntimeException.
+ *
* <p>You must call {@link #release()} when you are done using the camera,
* otherwise it will remain locked and be unavailable to other applications.
*
@@ -255,13 +299,13 @@
* @param cameraId the hardware camera to access, between 0 and
* {@link #getNumberOfCameras()}-1.
* @return a new Camera object, connected, locked and ready for use.
- * @throws RuntimeException if connection to the camera service fails (for
- * example, if the camera is in use by another process or device policy
- * manager has disabled the camera).
+ * @throws RuntimeException if opening the camera fails (for example, if the
+ * camera is in use by another process or device policy manager has
+ * disabled the camera).
* @see android.app.admin.DevicePolicyManager#getCameraDisabled(android.content.ComponentName)
*/
public static Camera open(int cameraId) {
- return new Camera(cameraId);
+ return new Camera(cameraId, false, false);
}
/**
@@ -276,13 +320,13 @@
for (int i = 0; i < numberOfCameras; i++) {
getCameraInfo(i, cameraInfo);
if (cameraInfo.facing == CameraInfo.CAMERA_FACING_BACK) {
- return new Camera(i);
+ return new Camera(i, false, false);
}
}
return null;
}
- Camera(int cameraId) {
+ Camera(int cameraId, boolean force, boolean keep) {
mShutterCallback = null;
mRawImageCallback = null;
mJpegCallback = null;
@@ -299,7 +343,7 @@
mEventHandler = null;
}
- native_setup(new WeakReference<Camera>(this), cameraId);
+ native_setup(new WeakReference<Camera>(this), cameraId, force, keep);
}
/**
@@ -312,7 +356,8 @@
release();
}
- private native final void native_setup(Object camera_this, int cameraId);
+ private native final void native_setup(Object camera_this, int cameraId,
+ boolean force, boolean keep);
private native final void native_release();
@@ -327,6 +372,18 @@
}
/**
+ * Whether the camera is released. When any camera method throws an
+ * exception, applications can use this to check whether the camera has been
+ * taken by other clients. If true, it means other clients have taken the
+ * camera. The applications can silently finish themselves or show a dialog.
+ *
+ * @return whether the camera is released.
+ * @see #open(int, boolean, boolean)
+ * @hide
+ */
+ public native final boolean isReleased();
+
+ /**
* Unlocks the camera to allow another process to access it.
* Normally, the camera is locked to the process with an active Camera
* object until {@link #release()} is called. To allow rapid handoff
@@ -1322,6 +1379,17 @@
public static final int CAMERA_ERROR_UNKNOWN = 1;
/**
+ * Camera was released because another client has opened the camera. The
+ * application should call {@link #release()} after getting this. The apps
+ * should not call any method except <code>release</code> and {@link #isReleased()}
+ * after this.
+ *
+ * @see Camera.ErrorCallback
+ * @hide
+ */
+ public static final int CAMERA_ERROR_RELEASED = 2;
+
+ /**
* Media server died. In this case, the application must release the
* Camera object and instantiate a new one.
* @see Camera.ErrorCallback
diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index 0c66b86..36b4b45 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -457,9 +457,9 @@
// connect to camera service
static void android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz,
- jobject weak_this, jint cameraId)
+ jobject weak_this, jint cameraId, jboolean force, jboolean keep)
{
- sp<Camera> camera = Camera::connect(cameraId);
+ sp<Camera> camera = Camera::connect(cameraId, force, keep);
if (camera == NULL) {
jniThrowRuntimeException(env, "Fail to connect to camera service");
@@ -700,7 +700,12 @@
sp<Camera> camera = get_native_camera(env, thiz, NULL);
if (camera == 0) return 0;
- return env->NewStringUTF(camera->getParameters().string());
+ String8 params8 = camera->getParameters();
+ if (params8.isEmpty()) {
+ jniThrowRuntimeException(env, "getParameters failed (empty parameters)");
+ return 0;
+ }
+ return env->NewStringUTF(params8.string());
}
static void android_hardware_Camera_reconnect(JNIEnv *env, jobject thiz)
@@ -816,6 +821,15 @@
}
}
+static bool android_hardware_Camera_isReleased(JNIEnv *env, jobject thiz)
+{
+ ALOGV("isReleased");
+ sp<Camera> camera = get_native_camera(env, thiz, NULL);
+ if (camera == 0) return true;
+
+ return (camera->sendCommand(CAMERA_CMD_PING, 0, 0) != NO_ERROR);
+}
+
//-------------------------------------------------
static JNINativeMethod camMethods[] = {
@@ -826,7 +840,7 @@
"(ILandroid/hardware/Camera$CameraInfo;)V",
(void*)android_hardware_Camera_getCameraInfo },
{ "native_setup",
- "(Ljava/lang/Object;I)V",
+ "(Ljava/lang/Object;IZZ)V",
(void*)android_hardware_Camera_native_setup },
{ "native_release",
"()V",
@@ -894,6 +908,9 @@
{ "enableFocusMoveCallback",
"(I)V",
(void *)android_hardware_Camera_enableFocusMoveCallback},
+ { "isReleased",
+ "()Z",
+ (void *)android_hardware_Camera_isReleased},
};
struct field {
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 @@
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 @@
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/libs/camera/Camera.cpp b/libs/camera/Camera.cpp
index d43cb0b..b81fe86 100644
--- a/libs/camera/Camera.cpp
+++ b/libs/camera/Camera.cpp
@@ -116,13 +116,13 @@
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/libs/camera/ICameraService.cpp b/libs/camera/ICameraService.cpp
index 85f1a29..c74298a 100644
--- a/libs/camera/ICameraService.cpp
+++ b/libs/camera/ICameraService.cpp
@@ -56,12 +56,15 @@
}
// 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 @@
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 @@
// ----------------------------------------------------------------------------
}; // namespace android
-
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index 2df55282..0d67800 100755
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -182,7 +182,7 @@
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 @@
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 @@
}
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 @@
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;
- }
- }
- mClient[cameraId].clear();
+ if (keep && !checkCallingPermission(String16("android.permission.KEEP_CAMERA"))) {
+ ALOGE("connect X (pid %d) rejected (no KEEP_CAMERA permission).", callingPid);
+ return NULL;
}
- if (mBusy[cameraId]) {
- ALOGW("CameraService::connect X (pid %d) rejected"
- " (camera %d is still busy).", callingPid, cameraId);
- return NULL;
+ Mutex::Autolock lock(mServiceLock);
+ // 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;
+ }
+
+ 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 @@
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 @@
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 @@
mCameraId = cameraId;
mCameraFacing = cameraFacing;
mClientPid = clientPid;
+ mKeep = keep;
mMsgEnabled = 0;
mSurface = 0;
mPreviewWindow = 0;
@@ -359,7 +423,7 @@
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 @@
}
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 @@
mCameraService->removeClient(mCameraClient);
mCameraService->setCameraFree(mCameraId);
+ mReleaseCondition.signal();
- LOG1("disconnect X (pid %d)", callingPid);
+ LOG1("disconnectInternal 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 @@
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 @@
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 @@
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 @@
const sp<CameraHardwareInterface>& hardware,
int cameraId,
int cameraFacing,
- int clientPid);
+ int clientPid,
+ bool keep);
~Client();
// return our camera client
@@ -172,12 +174,19 @@
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 @@
// 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;