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
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;