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/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;
+}
 
 // ----------------------------------------------------------------------------