Merge "Add GET_SIGNATURES to getPackageArchiveInfo test" into ics-mr0
diff --git a/tests/tests/hardware/src/android/hardware/cts/CameraTest.java b/tests/tests/hardware/src/android/hardware/cts/CameraTest.java
index 3d3cd1b..fcf3a34 100644
--- a/tests/tests/hardware/src/android/hardware/cts/CameraTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/CameraTest.java
@@ -23,6 +23,8 @@
 import android.hardware.Camera.Area;
 import android.hardware.Camera.CameraInfo;
 import android.hardware.Camera.ErrorCallback;
+import android.hardware.Camera.Face;
+import android.hardware.Camera.FaceDetectionListener;
 import android.hardware.Camera.Parameters;
 import android.hardware.Camera.PictureCallback;
 import android.hardware.Camera.ShutterCallback;
@@ -342,20 +344,20 @@
         int nCameras = Camera.getNumberOfCameras();
         for (int id = 0; id < nCameras; id++) {
             Log.v(TAG, "Camera id=" + id);
-            testTakePictureByCamera(id);
+            initializeMessageLooper(id);
+            mCamera.startPreview();
+            testTakePictureByCamera();
+            terminateMessageLooper();
         }
     }
 
-    private void testTakePictureByCamera(int cameraId) throws Exception {
-        initializeMessageLooper(cameraId);
+    private void testTakePictureByCamera() throws Exception {
         Size pictureSize = mCamera.getParameters().getPictureSize();
-        mCamera.startPreview();
         mCamera.autoFocus(mAutoFocusCallback);
         assertTrue(waitForFocusDone());
         mJpegData = null;
         mCamera.takePicture(mShutterCallback, mRawPictureCallback, mJpegPictureCallback);
         waitForSnapshotDone();
-        terminateMessageLooper();
         assertTrue("Shutter callback not received", mShutterCallbackResult);
         assertTrue("Raw picture callback not received", mRawPictureCallbackResult);
         assertTrue("Jpeg picture callback not recieved", mJpegPictureCallbackResult);
@@ -808,12 +810,13 @@
         int nCameras = Camera.getNumberOfCameras();
         for (int id = 0; id < nCameras; id++) {
             Log.v(TAG, "Camera id=" + id);
-            testJpegThumbnailSizeByCamera(id);
+            initializeMessageLooper(id);
+            testJpegThumbnailSizeByCamera(false);
+            terminateMessageLooper();
         }
     }
 
-    private void testJpegThumbnailSizeByCamera(int cameraId) throws Exception {
-        initializeMessageLooper(cameraId);
+    private void testJpegThumbnailSizeByCamera(boolean recording) throws Exception {
         // Thumbnail size parameters should have valid values.
         Parameters p = mCamera.getParameters();
         Size size = p.getJpegThumbnailSize();
@@ -824,7 +827,7 @@
         assertTrue(sizes.contains(mCamera.new Size(0, 0)));
 
         // Test if the thumbnail size matches the setting.
-        mCamera.startPreview();
+        if (!recording) mCamera.startPreview();
         mCamera.takePicture(mShutterCallback, mRawPictureCallback, mJpegPictureCallback);
         waitForSnapshotDone();
         assertTrue(mJpegPictureCallbackResult);
@@ -843,14 +846,12 @@
         Size actual = mCamera.getParameters().getJpegThumbnailSize();
         assertEquals(0, actual.width);
         assertEquals(0, actual.height);
-        mCamera.startPreview();
+        if (!recording) mCamera.startPreview();
         mCamera.takePicture(mShutterCallback, mRawPictureCallback, mJpegPictureCallback);
         waitForSnapshotDone();
         assertTrue(mJpegPictureCallbackResult);
         exif = new ExifInterface(JPEG_PATH);
         assertFalse(exif.hasThumbnail());
-
-        terminateMessageLooper();
     }
 
     @UiThreadTest
@@ -858,15 +859,16 @@
         int nCameras = Camera.getNumberOfCameras();
         for (int id = 0; id < nCameras; id++) {
             Log.v(TAG, "Camera id=" + id);
-            testJpegExifByCamera(id);
+            initializeMessageLooper(id);
+            testJpegExifByCamera(false);
+            terminateMessageLooper();
         }
     }
 
-    private void testJpegExifByCamera(int cameraId) throws Exception {
-        initializeMessageLooper(cameraId);
+    private void testJpegExifByCamera(boolean recording) throws Exception {
         Camera.Parameters parameters = mCamera.getParameters();
-        mCamera.startPreview();
-        double focalLength = (double)parameters.getFocalLength();
+        if (!recording) mCamera.startPreview();
+        double focalLength = parameters.getFocalLength();
         mCamera.takePicture(mShutterCallback, mRawPictureCallback, mJpegPictureCallback);
         waitForSnapshotDone();
         ExifInterface exif = new ExifInterface(JPEG_PATH);
@@ -876,8 +878,7 @@
         assertTrue(exif.getAttributeInt(ExifInterface.TAG_IMAGE_WIDTH, 0) != 0);
         assertTrue(exif.getAttributeInt(ExifInterface.TAG_IMAGE_LENGTH, 0) != 0);
         checkGpsDataNull(exif);
-        double exifFocalLength = (double)exif.getAttributeDouble(
-                ExifInterface.TAG_FOCAL_LENGTH, -1);
+        double exifFocalLength = exif.getAttributeDouble(ExifInterface.TAG_FOCAL_LENGTH, -1);
         assertEquals(focalLength, exifFocalLength, 0.001);
 
         // Test gps exif tags.
@@ -887,14 +888,13 @@
         testGpsExifValues(parameters, -89.736071, -179.441983, 100000, 1199145602, "NETWORK");
 
         // Test gps tags do not exist after calling removeGpsData.
-        mCamera.startPreview();
+        if (!recording) mCamera.startPreview();
         parameters.removeGpsData();
         mCamera.setParameters(parameters);
         mCamera.takePicture(mShutterCallback, mRawPictureCallback, mJpegPictureCallback);
         waitForSnapshotDone();
         exif = new ExifInterface(JPEG_PATH);
         checkGpsDataNull(exif);
-        terminateMessageLooper();
     }
 
     private void testGpsExifValues(Parameters parameters, double latitude,
@@ -1386,11 +1386,42 @@
             assertEquals(focusMode, parameters.getFocusMode());
             checkFocusDistances(parameters);
             if (Parameters.FOCUS_MODE_AUTO.equals(focusMode)
-                    || Parameters.FOCUS_MODE_MACRO.equals(focusMode)) {
+                    || Parameters.FOCUS_MODE_MACRO.equals(focusMode)
+                    || Parameters.FOCUS_MODE_CONTINUOUS_VIDEO.equals(focusMode)
+                    || Parameters.FOCUS_MODE_CONTINUOUS_PICTURE.equals(focusMode)) {
+                Log.v(TAG, "Focus mode=" + focusMode);
                 mCamera.autoFocus(mAutoFocusCallback);
                 assertTrue(waitForFocusDone());
                 parameters = mCamera.getParameters();
                 checkFocusDistances(parameters);
+                float[] initialFocusDistances = new float[3];
+                parameters.getFocusDistances(initialFocusDistances);
+
+                // Focus position should not change after autoFocus call.
+                // Continuous autofocus should have stopped. Sleep some time and
+                // check. Make sure continuous autofocus is not working. If the
+                // focus mode is auto or macro, it is no harm to do the extra
+                // test.
+                Thread.sleep(500);
+                parameters = mCamera.getParameters();
+                float[] currentFocusDistances = new float[3];
+                parameters.getFocusDistances(currentFocusDistances);
+                assertEquals(initialFocusDistances, currentFocusDistances);
+
+                // Focus position should not change after stopping preview.
+                mCamera.stopPreview();
+                parameters = mCamera.getParameters();
+                parameters.getFocusDistances(currentFocusDistances);
+                assertEquals(initialFocusDistances, currentFocusDistances);
+
+                // Focus position should not change after taking a picture.
+                mCamera.startPreview();
+                mCamera.takePicture(mShutterCallback, mRawPictureCallback, mJpegPictureCallback);
+                waitForSnapshotDone();
+                parameters = mCamera.getParameters();
+                parameters.getFocusDistances(currentFocusDistances);
+                assertEquals(initialFocusDistances, currentFocusDistances);
+                mCamera.startPreview();
             }
         }
 
@@ -1865,6 +1896,13 @@
         assertEquals(expected.height, actual.height);
     }
 
+    private void assertEquals(float[] expected, float[] actual) {
+        assertEquals(expected.length, actual.length);
+        for (int i = 0; i < expected.length; i++) {
+            assertEquals(expected[i], actual[i], 0.000001f);
+        }
+    }
+
     private void assertNoLetters(String value, String key) {
         for (int i = 0; i < value.length(); i++) {
             char c = value.charAt(i);
@@ -2675,4 +2713,171 @@
         mCamera.setParameters(parameters);
     }
 
+    @UiThreadTest
+    public void testFaceDetection() throws Exception {
+        int nCameras = Camera.getNumberOfCameras();
+        for (int id = 0; id < nCameras; id++) {
+            Log.v(TAG, "Camera id=" + id);
+            testFaceDetectionByCamera(id);
+        }
+    }
+
+    private void testFaceDetectionByCamera(int cameraId) throws Exception {
+        final int FACE_DETECTION_TEST_DURATION = 3000;
+        initializeMessageLooper(cameraId);
+        mCamera.startPreview();
+        Parameters parameters = mCamera.getParameters();
+        int maxNumOfFaces = parameters.getMaxNumDetectedFaces();
+        assertTrue(maxNumOfFaces >= 0);
+        if (maxNumOfFaces == 0) {
+            try {
+                mCamera.startFaceDetection();
+                fail("Should throw an exception if face detection is not supported.");
+            } catch (IllegalArgumentException e) {
+                // expected
+            }
+            terminateMessageLooper();
+            return;
+        }
+
+        mCamera.startFaceDetection();
+        try {
+            mCamera.startFaceDetection();
+            fail("Starting face detection twice should throw an exception");
+        } catch (RuntimeException e) {
+            // expected
+        }
+        FaceListener listener = new FaceListener();
+        mCamera.setFaceDetectionListener(listener);
+        // Sleep some time so the camera has chances to detect faces.
+        Thread.sleep(FACE_DETECTION_TEST_DURATION);
+        // The face callback runs in another thread. Release the camera and stop
+        // the looper. So we do not access the face array from two threads at
+        // the same time.
+        terminateMessageLooper();
+
+        // Check if the optional fields are supported.
+        boolean optionalFieldSupported = false;
+        Face firstFace = null;
+        for (Face[] faces: listener.mFacesArray) {
+            for (Face face: faces) {
+                if (face != null) firstFace = face;
+            }
+        }
+        if (firstFace != null) {
+            if (firstFace.id != -1 || firstFace.leftEye != null
+                    || firstFace.rightEye != null || firstFace.mouth != null) {
+                optionalFieldSupported = true;
+            }
+        }
+
+        // Verify the faces array.
+        for (Face[] faces: listener.mFacesArray) {
+            testFaces(faces, maxNumOfFaces, optionalFieldSupported);
+        }
+    }
+
+    private class FaceListener implements FaceDetectionListener {
+        public ArrayList<Face[]> mFacesArray = new ArrayList<Face[]>();
+
+        @Override
+        public void onFaceDetection(Face[] faces, Camera camera) {
+            mFacesArray.add(faces);
+        }
+    }
+
+    private void testFaces(Face[] faces, int maxNumOfFaces,
+            boolean optionalFieldSupported) {
+        Rect bounds = new Rect(-1000, -1000, 1000, 1000);
+        assertNotNull(faces);
+        assertTrue(faces.length <= maxNumOfFaces);
+        for (int i = 0; i < faces.length; i++) {
+            Face face = faces[i];
+            Rect rect = face.rect;
+            // Check the bounds.
+            assertNotNull(rect);
+            assertTrue(rect.width() > 0);
+            assertTrue(rect.height() > 0);
+            assertTrue("Coordinates out of bounds. rect=" + rect,
+                    bounds.contains(rect) || Rect.intersects(bounds, rect));
+
+            // Check the score.
+            assertTrue(face.score >= 1 && face.score <= 100);
+
+            // Check id, left eye, right eye, and the mouth.
+            // Optional fields should be all valid or none of them.
+            if (!optionalFieldSupported) {
+                assertEquals(-1, face.id);
+                assertNull(face.leftEye);
+                assertNull(face.rightEye);
+                assertNull(face.mouth);
+            } else {
+                assertTrue(face.id != -1);
+                assertNotNull(face.leftEye);
+                assertNotNull(face.rightEye);
+                assertNotNull(face.mouth);
+                assertTrue(bounds.contains(face.leftEye.x, face.leftEye.y));
+                assertTrue(bounds.contains(face.rightEye.x, face.rightEye.y));
+                assertTrue(bounds.contains(face.mouth.x, face.mouth.y));
+                // ID should be unique.
+                if (i != faces.length - 1) {
+                    assertTrue(face.id != faces[i + 1].id);
+                }
+            }
+        }
+    }
+
+    @UiThreadTest
+    public void testVideoSnapshot() throws Exception {
+        int nCameras = Camera.getNumberOfCameras();
+        for (int id = 0; id < nCameras; id++) {
+            Log.v(TAG, "Camera id=" + id);
+            testVideoSnapshotByCamera(id);
+        }
+    }
+
+    private void testVideoSnapshotByCamera(int cameraId) throws Exception {
+        initializeMessageLooper(cameraId);
+        Camera.Parameters parameters = mCamera.getParameters();
+        if (!parameters.isVideoSnapshotSupported()) return;
+
+        SurfaceHolder holder = getActivity().getSurfaceView().getHolder();
+
+        // Set the preview size.
+        CamcorderProfile profile = CamcorderProfile.get(cameraId,
+                CamcorderProfile.QUALITY_LOW);
+        setPreviewSizeByProfile(parameters, profile);
+
+        // Set the biggest picture size.
+        Size biggestSize = mCamera.new Size(-1, -1);
+        for (Size size: parameters.getSupportedPictureSizes()) {
+            if (biggestSize.width < size.width) {
+                biggestSize = size;
+            }
+        }
+        parameters.setPictureSize(biggestSize.width, biggestSize.height);
+
+        mCamera.setParameters(parameters);
+        mCamera.startPreview();
+        mCamera.unlock();
+        MediaRecorder recorder = new MediaRecorder();
+        try {
+            recorder.setCamera(mCamera);
+            recorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
+            recorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
+            recorder.setProfile(profile);
+            recorder.setOutputFile("/dev/null");
+            recorder.setPreviewDisplay(holder.getSurface());
+            recorder.prepare();
+            recorder.start();
+            testTakePictureByCamera();
+            testJpegExifByCamera(true);
+            testJpegThumbnailSizeByCamera(true);
+            recorder.stop();
+        } finally {
+            recorder.release();
+            mCamera.lock();
+        }
+        terminateMessageLooper();
+    }
 }