diff options
author | 2012-02-23 19:01:00 -0800 | |
---|---|---|
committer | 2012-03-15 14:50:48 +0800 | |
commit | a1c41e13b521cdd611a8fc46e43b5ae0a2c00238 (patch) | |
tree | 48877fa91b94c62482519f040e966997a9d214d2 | |
parent | 5571ffdd9d419e3b1e050501e6f8dbfb04159b5d (diff) |
Add a new camera open API that allows taking the ownership.
The purpose is to let face unlock always get the camera
successfully. What happened was the camera applications may
have opened the camera in onResume under the lock screen.
This API lets face unlock take the camera from the camera
application. A new permission will be added, so other
applicatoins won't be able to take the camera from the face
unlock.
bug:5584464
Change-Id: Ib3d9dcbc2161815b68db42327dc01148453704c6
-rw-r--r-- | core/java/android/hardware/Camera.java | 84 | ||||
-rw-r--r-- | core/jni/android_hardware_Camera.cpp | 25 | ||||
-rw-r--r-- | include/camera/Camera.h | 2 | ||||
-rw-r--r-- | include/camera/ICameraService.h | 2 | ||||
-rw-r--r-- | libs/camera/Camera.cpp | 4 | ||||
-rw-r--r-- | libs/camera/ICameraService.cpp | 11 | ||||
-rwxr-xr-x | media/libstagefright/CameraSource.cpp | 2 | ||||
-rw-r--r-- | services/camera/libcameraservice/CameraService.cpp | 149 | ||||
-rw-r--r-- | services/camera/libcameraservice/CameraService.h | 15 |
9 files changed, 240 insertions, 54 deletions
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index aa92efb0c932..83b69869314b 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -236,6 +236,50 @@ public class Camera { /** * 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 @@ public class Camera { * @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 @@ public class Camera { 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 @@ public class Camera { mEventHandler = null; } - native_setup(new WeakReference<Camera>(this), cameraId); + native_setup(new WeakReference<Camera>(this), cameraId, force, keep); } /** @@ -312,7 +356,8 @@ public class Camera { 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 @@ public class Camera { } /** + * 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 class Camera { 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 0c66b867c8dc..36b4b45bf921 100644 --- a/core/jni/android_hardware_Camera.cpp +++ b/core/jni/android_hardware_Camera.cpp @@ -457,9 +457,9 @@ static void android_hardware_Camera_getCameraInfo(JNIEnv *env, jobject thiz, // 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 @@ static jstring android_hardware_Camera_getParameters(JNIEnv *env, jobject thiz) 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 void android_hardware_Camera_enableFocusMoveCallback(JNIEnv *env, jobject } } +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 @@ static JNINativeMethod camMethods[] = { "(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 @@ static JNINativeMethod camMethods[] = { { "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 234e1657eef8..3fedea0f6b67 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 7d70c1e48ac4..97e3169875d2 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/libs/camera/Camera.cpp b/libs/camera/Camera.cpp index d43cb0bb434f..b81fe8623237 100644 --- a/libs/camera/Camera.cpp +++ b/libs/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/libs/camera/ICameraService.cpp b/libs/camera/ICameraService.cpp index 85f1a29feb1a..c74298a84bb9 100644 --- a/libs/camera/ICameraService.cpp +++ b/libs/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/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp index 2df55282f35f..0d67800ad1bd 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 adf1d4913af7..22836e313fad 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 bad41f5a1e78..457c79b2aa45 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; |