diff options
-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 aa92efb..83b6986 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 0c66b86..36b4b45 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 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/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 @@ 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 85f1a29..c74298a 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 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; |