1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.hardware.cts;
18 
19 import android.content.pm.PackageManager;
20 import android.graphics.BitmapFactory;
21 import android.graphics.ImageFormat;
22 import android.graphics.Rect;
23 import android.hardware.Camera;
24 import android.hardware.Camera.Area;
25 import android.hardware.Camera.CameraInfo;
26 import android.hardware.Camera.ErrorCallback;
27 import android.hardware.Camera.Face;
28 import android.hardware.Camera.FaceDetectionListener;
29 import android.hardware.Camera.Parameters;
30 import android.hardware.Camera.PictureCallback;
31 import android.hardware.Camera.ShutterCallback;
32 import android.hardware.Camera.Size;
33 import android.hardware.cts.helpers.CameraUtils;
34 import android.media.CamcorderProfile;
35 import android.media.ExifInterface;
36 import android.media.MediaRecorder;
37 import android.os.Build;
38 import android.os.ConditionVariable;
39 import android.os.Looper;
40 import android.os.SystemClock;
41 import android.test.MoreAsserts;
42 import android.test.UiThreadTest;
43 import android.test.suitebuilder.annotation.LargeTest;
44 import android.util.Log;
45 import android.view.SurfaceHolder;
46 
47 import androidx.test.rule.ActivityTestRule;
48 
49 import junit.framework.Assert;
50 import junit.framework.AssertionFailedError;
51 
52 import org.junit.After;
53 import org.junit.Before;
54 import org.junit.Rule;
55 import org.junit.Test;
56 
57 import java.io.File;
58 import java.io.FileOutputStream;
59 import java.io.IOException;
60 import java.text.ParseException;
61 import java.text.ParsePosition;
62 import java.text.SimpleDateFormat;
63 import java.util.ArrayList;
64 import java.util.Arrays;
65 import java.util.Date;
66 import java.util.Iterator;
67 import java.util.List;
68 import java.util.TimeZone;
69 
70 import com.android.compatibility.common.util.WindowUtil;
71 
72 /**
73  * This test case must run with hardware. It can't be tested in emulator
74  */
75 @LargeTest
76 public class CameraTest extends Assert {
77     private static final String TAG = "CameraTest";
78     private static final String PACKAGE = "android.hardware.cts";
79     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
80     private String mRecordingPath = null;
81     private String mJpegPath = null;
82     private byte[] mJpegData;
83 
84     private static final int PREVIEW_CALLBACK_NOT_RECEIVED = 0;
85     private static final int PREVIEW_CALLBACK_RECEIVED = 1;
86     private static final int PREVIEW_CALLBACK_DATA_NULL = 2;
87     private static final int PREVIEW_CALLBACK_INVALID_FRAME_SIZE = 3;
88     private int mPreviewCallbackResult = PREVIEW_CALLBACK_NOT_RECEIVED;
89 
90     private boolean mShutterCallbackResult = false;
91     private boolean mRawPictureCallbackResult = false;
92     private boolean mJpegPictureCallbackResult = false;
93     private static final int NO_ERROR = -1;
94     private int mCameraErrorCode = NO_ERROR;
95     private boolean mAutoFocusSucceeded = false;
96 
97     private static final int WAIT_FOR_COMMAND_TO_COMPLETE = 5000;  // Milliseconds.
98     private static final int WAIT_FOR_FOCUS_TO_COMPLETE = 5000;
99     private static final int WAIT_FOR_SNAPSHOT_TO_COMPLETE = 5000;
100 
101     private static final int FOCUS_AREA = 0;
102     private static final int METERING_AREA = 1;
103 
104     private static final int AUTOEXPOSURE_LOCK = 0;
105     private static final int AUTOWHITEBALANCE_LOCK = 1;
106 
107     // For external camera recording
108     private static final int VIDEO_BIT_RATE_IN_BPS = 128000;
109 
110     // Some exif tags that are not defined by ExifInterface but supported.
111     private static final String TAG_DATETIME_DIGITIZED = "DateTimeDigitized";
112     private static final String TAG_SUBSEC_TIME = "SubSecTime";
113     private static final String TAG_SUBSEC_TIME_ORIG = "SubSecTimeOriginal";
114     private static final String TAG_SUBSEC_TIME_DIG = "SubSecTimeDigitized";
115 
116     private PreviewCallback mPreviewCallback = new PreviewCallback();
117     private TestShutterCallback mShutterCallback = new TestShutterCallback();
118     private RawPictureCallback mRawPictureCallback = new RawPictureCallback();
119     private JpegPictureCallback mJpegPictureCallback = new JpegPictureCallback();
120     private TestErrorCallback mErrorCallback = new TestErrorCallback();
121     private AutoFocusCallback mAutoFocusCallback = new AutoFocusCallback();
122     private AutoFocusMoveCallback mAutoFocusMoveCallback = new AutoFocusMoveCallback();
123 
124     private Looper mLooper = null;
125     private final ConditionVariable mPreviewDone = new ConditionVariable();
126     private final ConditionVariable mFocusDone = new ConditionVariable();
127     private final ConditionVariable mSnapshotDone = new ConditionVariable();
128     private int[] mCameraIds;
129 
130     Camera mCamera;
131     boolean mIsExternalCamera;
132 
133     @Rule
134     public ActivityTestRule<CameraCtsActivity> mActivityRule =
135             new ActivityTestRule<>(CameraCtsActivity.class);
136 
137     @Before
setUp()138     public void setUp() throws Exception {
139         // Some of the tests run on the UI thread. In case some of the operations take a long time to complete,
140         // wait for window to receive focus. This ensure that the focus event from input flinger has been handled,
141         // and avoids getting ANR.
142         WindowUtil.waitForFocus(mActivityRule.getActivity());
143         mCameraIds = CameraUtils.deriveCameraIdsUnderTest();
144     }
145 
146     @After
tearDown()147     public void tearDown() throws Exception {
148         if (mCamera != null) {
149             mCamera.release();
150             mCamera = null;
151         }
152     }
153 
154     /*
155      * Initializes the message looper so that the Camera object can
156      * receive the callback messages.
157      */
initializeMessageLooper(final int cameraId)158     private void initializeMessageLooper(final int cameraId) throws IOException {
159         LooperInfo looperInfo = new LooperInfo();
160         initializeMessageLooper(cameraId, mErrorCallback, looperInfo);
161         mIsExternalCamera = looperInfo.isExternalCamera;
162         mCamera = looperInfo.camera;
163         mLooper = looperInfo.looper;
164     }
165 
166     private final class LooperInfo {
167         Camera camera = null;
168         Looper looper = null;
169         boolean isExternalCamera = false;
170     };
171 
172     /*
173      * Initializes the message looper so that the Camera object can
174      * receive the callback messages.
175      */
initializeMessageLooper(final int cameraId, final ErrorCallback errorCallback, LooperInfo looperInfo)176     private void initializeMessageLooper(final int cameraId, final ErrorCallback errorCallback,
177             LooperInfo looperInfo) throws IOException {
178         final ConditionVariable startDone = new ConditionVariable();
179         final CameraCtsActivity activity = mActivityRule.getActivity();
180         new Thread() {
181             @Override
182             public void run() {
183                 Log.v(TAG, "start loopRun for cameraId " + cameraId);
184                 // Set up a looper to be used by camera.
185                 Looper.prepare();
186                 // Save the looper so that we can terminate this thread
187                 // after we are done with it.
188                 looperInfo.looper = Looper.myLooper();
189                 try {
190                     looperInfo.isExternalCamera = CameraUtils.isExternal(
191                             activity.getApplicationContext(), cameraId);
192                 } catch (Exception e) {
193                     Log.e(TAG, "Unable to query external camera!" + e);
194                 }
195 
196                 try {
197                     looperInfo.camera = Camera.open(cameraId);
198                     looperInfo.camera.setErrorCallback(errorCallback);
199                 } catch (RuntimeException e) {
200                     Log.e(TAG, "Fail to open camera id " + cameraId + ": " + e);
201                 }
202                 Log.v(TAG, "camera" + cameraId + " is opened");
203                 startDone.open();
204                 Looper.loop(); // Blocks forever until Looper.quit() is called.
205                 if (VERBOSE) Log.v(TAG, "initializeMessageLooper: quit.");
206             }
207         }.start();
208 
209         Log.v(TAG, "start waiting for looper");
210         if (!startDone.block(WAIT_FOR_COMMAND_TO_COMPLETE)) {
211             Log.v(TAG, "initializeMessageLooper: start timeout");
212             fail("initializeMessageLooper: start timeout");
213         }
214         assertNotNull("Fail to open camera " + cameraId, looperInfo.camera);
215         looperInfo.camera.setPreviewDisplay(activity.getSurfaceView().getHolder());
216         File parent = activity.getPackageManager().isInstantApp()
217                 ? activity.getFilesDir()
218                 : activity.getExternalFilesDir(null);
219 
220         mJpegPath = parent.getPath() + "/test.jpg";
221         mRecordingPath = parent.getPath() + "/test_video.mp4";
222     }
223 
224     /*
225      * Terminates the message looper thread.
226      */
terminateMessageLooper()227     private void terminateMessageLooper() throws Exception {
228         terminateMessageLooper(false);
229     }
230 
231     /*
232      * Terminates the message looper thread, optionally allowing evict error
233      */
terminateMessageLooper(boolean allowEvict)234     private void terminateMessageLooper(boolean allowEvict) throws Exception {
235         LooperInfo looperInfo = new LooperInfo();
236         looperInfo.camera = mCamera;
237         looperInfo.looper = mLooper;
238         terminateMessageLooper(allowEvict, mCameraErrorCode, looperInfo);
239         mCamera = null;
240     }
241 
242     /*
243      * Terminates the message looper thread, optionally allowing evict error
244      */
terminateMessageLooper(final boolean allowEvict, final int errorCode, final LooperInfo looperInfo)245     private void terminateMessageLooper(final boolean allowEvict, final int errorCode,
246             final LooperInfo looperInfo) throws Exception {
247         looperInfo.looper.quit();
248         // Looper.quit() is asynchronous. The looper may still has some
249         // preview callbacks in the queue after quit is called. The preview
250         // callback still uses the camera object (setHasPreviewCallback).
251         // After camera is released, RuntimeException will be thrown from
252         // the method. So we need to join the looper thread here.
253         looperInfo.looper.getThread().join();
254         looperInfo.camera.release();
255         looperInfo.camera = null;
256         if (allowEvict) {
257             assertTrue("Got unexpected camera error callback.",
258                     (NO_ERROR == errorCode ||
259                     Camera.CAMERA_ERROR_EVICTED == errorCode));
260         } else {
261             assertEquals("Got camera error callback.", NO_ERROR, errorCode);
262         }
263     }
264 
265     // Align 'x' to 'to', which should be a power of 2
align(int x, int to)266     private static int align(int x, int to) {
267         return (x + (to-1)) & ~(to - 1);
268     }
calculateBufferSize(int width, int height, int format, int bpp)269     private static int calculateBufferSize(int width, int height,
270                                            int format, int bpp) {
271 
272         if (VERBOSE) {
273             Log.v(TAG, "calculateBufferSize: w=" + width + ",h=" + height
274             + ",f=" + format + ",bpp=" + bpp);
275         }
276 
277         if (format == ImageFormat.YV12) {
278             /*
279             http://developer.android.com/reference/android/graphics/ImageFormat.html#YV12
280             */
281 
282             int stride = align(width, 16);
283 
284             int y_size = stride * height;
285             int c_stride = align(stride/2, 16);
286             int c_size = c_stride * height/2;
287             int size = y_size + c_size * 2;
288 
289             if (VERBOSE) {
290                 Log.v(TAG, "calculateBufferSize: YV12 size= " + size);
291             }
292 
293             return size;
294 
295         }
296         else {
297             return width * height * bpp / 8;
298         }
299     }
300 
301     //Implement the previewCallback
302     private final class PreviewCallback
303             implements android.hardware.Camera.PreviewCallback {
onPreviewFrame(byte [] data, Camera camera)304         public void onPreviewFrame(byte [] data, Camera camera) {
305             if (data == null) {
306                 mPreviewCallbackResult = PREVIEW_CALLBACK_DATA_NULL;
307                 mPreviewDone.open();
308                 return;
309             }
310             Size size = camera.getParameters().getPreviewSize();
311             int format = camera.getParameters().getPreviewFormat();
312             int bitsPerPixel = ImageFormat.getBitsPerPixel(format);
313             if (calculateBufferSize(size.width, size.height,
314                     format, bitsPerPixel) != data.length) {
315                 Log.e(TAG, "Invalid frame size " + data.length + ". width=" + size.width
316                         + ". height=" + size.height + ". bitsPerPixel=" + bitsPerPixel);
317                 mPreviewCallbackResult = PREVIEW_CALLBACK_INVALID_FRAME_SIZE;
318                 mPreviewDone.open();
319                 return;
320             }
321             mPreviewCallbackResult = PREVIEW_CALLBACK_RECEIVED;
322             mCamera.stopPreview();
323             if (VERBOSE) Log.v(TAG, "notify the preview callback");
324             mPreviewDone.open();
325             if (VERBOSE) Log.v(TAG, "Preview callback stop");
326         }
327     }
328 
329     //Implement the shutterCallback
330     private final class TestShutterCallback implements ShutterCallback {
onShutter()331         public void onShutter() {
332             mShutterCallbackResult = true;
333             if (VERBOSE) Log.v(TAG, "onShutter called");
334         }
335     }
336 
337     //Implement the RawPictureCallback
338     private final class RawPictureCallback implements PictureCallback {
onPictureTaken(byte [] rawData, Camera camera)339         public void onPictureTaken(byte [] rawData, Camera camera) {
340             mRawPictureCallbackResult = true;
341             if (VERBOSE) Log.v(TAG, "RawPictureCallback callback");
342         }
343     }
344 
345     // Implement the JpegPictureCallback
346     private final class JpegPictureCallback implements PictureCallback {
onPictureTaken(byte[] rawData, Camera camera)347         public void onPictureTaken(byte[] rawData, Camera camera) {
348             try {
349                 mJpegData = rawData;
350                 if (rawData != null) {
351                     // try to store the picture on the SD card
352                     File rawoutput = new File(mJpegPath);
353                     FileOutputStream outStream = new FileOutputStream(rawoutput);
354                     outStream.write(rawData);
355                     outStream.close();
356                     mJpegPictureCallbackResult = true;
357 
358                     if (VERBOSE) {
359                         Log.v(TAG, "JpegPictureCallback rawDataLength = " + rawData.length);
360                     }
361                 } else {
362                     mJpegPictureCallbackResult = false;
363                 }
364                 mSnapshotDone.open();
365                 if (VERBOSE) Log.v(TAG, "Jpeg Picture callback");
366             } catch (IOException e) {
367                 // no need to fail here; callback worked fine
368                 Log.w(TAG, "Error writing picture to sd card.");
369             }
370         }
371     }
372 
373     // Implement the ErrorCallback
374     private final class TestErrorCallback implements ErrorCallback {
onError(int error, Camera camera)375         public void onError(int error, Camera camera) {
376             Log.e(TAG, "Got camera error=" + error);
377             mCameraErrorCode = error;
378         }
379     }
380 
381     // parent independent version of TestErrorCallback
382     private static final class TestErrorCallbackI implements ErrorCallback {
383         private int mCameraErrorCode = NO_ERROR;
onError(int error, Camera camera)384         public void onError(int error, Camera camera) {
385             Log.e(TAG, "Got camera error=" + error);
386             mCameraErrorCode = error;
387         }
388     }
389 
390     private final class AutoFocusCallback
391             implements android.hardware.Camera.AutoFocusCallback {
onAutoFocus(boolean success, Camera camera)392         public void onAutoFocus(boolean success, Camera camera) {
393             mAutoFocusSucceeded = success;
394             Log.v(TAG, "AutoFocusCallback success=" + success);
395             mFocusDone.open();
396         }
397     }
398 
399     private final class AutoFocusMoveCallback
400             implements android.hardware.Camera.AutoFocusMoveCallback {
401         @Override
onAutoFocusMoving(boolean start, Camera camera)402         public void onAutoFocusMoving(boolean start, Camera camera) {
403         }
404     }
405 
waitForPreviewDone()406     private void waitForPreviewDone() {
407         if (VERBOSE) Log.v(TAG, "Wait for preview callback");
408         if (!mPreviewDone.block(WAIT_FOR_COMMAND_TO_COMPLETE)) {
409             // timeout could be expected or unexpected. The caller will decide.
410             Log.v(TAG, "waitForPreviewDone: timeout");
411         }
412         mPreviewDone.close();
413     }
414 
waitForFocusDone()415     private boolean waitForFocusDone() {
416         boolean result = mFocusDone.block(WAIT_FOR_FOCUS_TO_COMPLETE);
417         if (!result) {
418             // timeout could be expected or unexpected. The caller will decide.
419             Log.v(TAG, "waitForFocusDone: timeout");
420         }
421         mFocusDone.close();
422         return result;
423     }
424 
waitForSnapshotDone()425     private void waitForSnapshotDone() {
426         if (!mSnapshotDone.block(WAIT_FOR_SNAPSHOT_TO_COMPLETE)) {
427             // timeout could be expected or unexpected. The caller will decide.
428             Log.v(TAG, "waitForSnapshotDone: timeout");
429         }
430         mSnapshotDone.close();
431     }
432 
checkPreviewCallback()433     private void checkPreviewCallback() throws Exception {
434         if (VERBOSE) Log.v(TAG, "check preview callback");
435         mCamera.startPreview();
436         waitForPreviewDone();
437         mCamera.setPreviewCallback(null);
438     }
439 
440     /**
441      * Start preview and wait for the first preview callback, which indicates the
442      * preview becomes active.
443      */
blockingStartPreview()444     private void blockingStartPreview() {
445         mCamera.setPreviewCallback(new SimplePreviewStreamCb(/*Id*/0));
446         mCamera.startPreview();
447         waitForPreviewDone();
448         mCamera.setPreviewCallback(null);
449     }
450 
451     /*
452      * Test case 1: Take a picture and verify all the callback
453      * functions are called properly.
454      */
455     @UiThreadTest
456     @Test
testTakePicture()457     public void testTakePicture() throws Exception {
458         for (int id : mCameraIds) {
459             Log.v(TAG, "Camera id=" + id);
460             initializeMessageLooper(id);
461             mCamera.startPreview();
462             subtestTakePictureByCamera(false, 0, 0);
463             terminateMessageLooper();
464         }
465     }
466 
subtestTakePictureByCamera(boolean isVideoSnapshot, int videoWidth, int videoHeight)467     private void subtestTakePictureByCamera(boolean isVideoSnapshot,
468             int videoWidth, int videoHeight) throws Exception {
469         int videoSnapshotMinArea =
470                 videoWidth * videoHeight; // Temporary until new API definitions
471 
472         Size pictureSize = mCamera.getParameters().getPictureSize();
473         mCamera.autoFocus(mAutoFocusCallback);
474         assertTrue(waitForFocusDone());
475         mJpegData = null;
476         mCamera.takePicture(mShutterCallback, mRawPictureCallback, mJpegPictureCallback);
477         waitForSnapshotDone();
478         assertTrue("Shutter callback not received", mShutterCallbackResult);
479         assertTrue("Raw picture callback not received", mRawPictureCallbackResult);
480         assertTrue("Jpeg picture callback not recieved", mJpegPictureCallbackResult);
481         assertNotNull(mJpegData);
482         BitmapFactory.Options bmpOptions = new BitmapFactory.Options();
483         bmpOptions.inJustDecodeBounds = true;
484         BitmapFactory.decodeByteArray(mJpegData, 0, mJpegData.length, bmpOptions);
485         if (!isVideoSnapshot) {
486             assertEquals(pictureSize.width, bmpOptions.outWidth);
487             assertEquals(pictureSize.height, bmpOptions.outHeight);
488         } else {
489             int realArea = bmpOptions.outWidth * bmpOptions.outHeight;
490             if (VERBOSE) Log.v(TAG, "Video snapshot is " +
491                     bmpOptions.outWidth + " x " + bmpOptions.outHeight +
492                     ", video size is " + videoWidth + " x " + videoHeight);
493             assertTrue ("Video snapshot too small! Expected at least " +
494                     videoWidth + " x " + videoHeight + " (" +
495                     videoSnapshotMinArea/1000000. + " MP)",
496                     realArea >= videoSnapshotMinArea);
497         }
498     }
499 
500     @UiThreadTest
501     @Test
testPreviewCallback()502     public void testPreviewCallback() throws Exception {
503         for (int id : mCameraIds) {
504             Log.v(TAG, "Camera id=" + id);
505             testPreviewCallbackByCamera(id);
506         }
507     }
508 
testPreviewCallbackByCamera(int cameraId)509     private void testPreviewCallbackByCamera(int cameraId) throws Exception {
510         initializeMessageLooper(cameraId);
511         mCamera.setPreviewCallback(mPreviewCallback);
512         checkPreviewCallback();
513         terminateMessageLooper();
514         assertEquals(PREVIEW_CALLBACK_RECEIVED, mPreviewCallbackResult);
515 
516         mPreviewCallbackResult = PREVIEW_CALLBACK_NOT_RECEIVED;
517         initializeMessageLooper(cameraId);
518         checkPreviewCallback();
519         terminateMessageLooper();
520         assertEquals(PREVIEW_CALLBACK_NOT_RECEIVED, mPreviewCallbackResult);
521 
522         // Test all preview sizes.
523         initializeMessageLooper(cameraId);
524         Parameters parameters = mCamera.getParameters();
525 
526         for (Size size: parameters.getSupportedPreviewSizes()) {
527             mPreviewCallbackResult = PREVIEW_CALLBACK_NOT_RECEIVED;
528             mCamera.setPreviewCallback(mPreviewCallback);
529             parameters.setPreviewSize(size.width, size.height);
530             Size pictureSize = getPictureSizeForPreview(size, parameters);
531             parameters.setPictureSize(pictureSize.width, pictureSize.height);
532             mCamera.setParameters(parameters);
533             assertEquals(size, mCamera.getParameters().getPreviewSize());
534             checkPreviewCallback();
535             assertEquals(PREVIEW_CALLBACK_RECEIVED, mPreviewCallbackResult);
536             try {
537                 // Wait for a while to throw away the remaining preview frames.
538                 Thread.sleep(1000);
539             } catch(Exception e) {
540                 // ignore
541             }
542             mPreviewDone.close();
543         }
544         terminateMessageLooper();
545     }
546 
547     @UiThreadTest
548     @Test
testStabilizationOneShotPreviewCallback()549     public void testStabilizationOneShotPreviewCallback() throws Exception {
550         for (int id : mCameraIds) {
551             Log.v(TAG, "Camera id=" + id);
552             testStabilizationOneShotPreviewCallbackByCamera(id);
553         }
554     }
555 
testStabilizationOneShotPreviewCallbackByCamera(int cameraId)556     private void testStabilizationOneShotPreviewCallbackByCamera(int cameraId) throws Exception {
557         initializeMessageLooper(cameraId);
558         Parameters params = mCamera.getParameters();
559         if(!params.isVideoStabilizationSupported()) {
560             terminateMessageLooper();
561             return;
562         }
563         //Check whether we can support preview callbacks along with stabilization
564         params.setVideoStabilization(true);
565         mCamera.setParameters(params);
566         mCamera.setOneShotPreviewCallback(mPreviewCallback);
567         checkPreviewCallback();
568         terminateMessageLooper();
569         assertEquals(PREVIEW_CALLBACK_RECEIVED, mPreviewCallbackResult);
570     }
571 
572     @UiThreadTest
573     @Test
testSetOneShotPreviewCallback()574     public void testSetOneShotPreviewCallback() throws Exception {
575         for (int id : mCameraIds) {
576             Log.v(TAG, "Camera id=" + id);
577             testSetOneShotPreviewCallbackByCamera(id);
578         }
579     }
580 
testSetOneShotPreviewCallbackByCamera(int cameraId)581     private void testSetOneShotPreviewCallbackByCamera(int cameraId) throws Exception {
582         initializeMessageLooper(cameraId);
583         mCamera.setOneShotPreviewCallback(mPreviewCallback);
584         checkPreviewCallback();
585         terminateMessageLooper();
586         assertEquals(PREVIEW_CALLBACK_RECEIVED, mPreviewCallbackResult);
587 
588         mPreviewCallbackResult = PREVIEW_CALLBACK_NOT_RECEIVED;
589         initializeMessageLooper(cameraId);
590         checkPreviewCallback();
591         terminateMessageLooper();
592         assertEquals(PREVIEW_CALLBACK_NOT_RECEIVED, mPreviewCallbackResult);
593     }
594 
595     @UiThreadTest
596     @Test
testSetPreviewDisplay()597     public void testSetPreviewDisplay() throws Exception {
598         for (int id : mCameraIds) {
599             Log.v(TAG, "Camera id=" + id);
600             testSetPreviewDisplayByCamera(id);
601         }
602     }
603 
testSetPreviewDisplayByCamera(int cameraId)604     private void testSetPreviewDisplayByCamera(int cameraId) throws Exception {
605         SurfaceHolder holder = mActivityRule.getActivity().getSurfaceView().getHolder();
606         initializeMessageLooper(cameraId);
607 
608         // Check the order: startPreview->setPreviewDisplay.
609         mCamera.setOneShotPreviewCallback(mPreviewCallback);
610         mCamera.startPreview();
611         mCamera.setPreviewDisplay(holder);
612         waitForPreviewDone();
613         terminateMessageLooper();
614         assertEquals(PREVIEW_CALLBACK_RECEIVED, mPreviewCallbackResult);
615 
616         // Check the order: setPreviewDisplay->startPreview.
617         initializeMessageLooper(cameraId);
618         mPreviewCallbackResult = PREVIEW_CALLBACK_NOT_RECEIVED;
619         mCamera.setOneShotPreviewCallback(mPreviewCallback);
620         mCamera.setPreviewDisplay(holder);
621         mCamera.startPreview();
622         waitForPreviewDone();
623         mCamera.stopPreview();
624         assertEquals(PREVIEW_CALLBACK_RECEIVED, mPreviewCallbackResult);
625 
626         // Check the order: setting preview display to null->startPreview->
627         // setPreviewDisplay.
628         mPreviewCallbackResult = PREVIEW_CALLBACK_NOT_RECEIVED;
629         mCamera.setOneShotPreviewCallback(mPreviewCallback);
630         mCamera.setPreviewDisplay(null);
631         mCamera.startPreview();
632         mCamera.setPreviewDisplay(holder);
633         waitForPreviewDone();
634         terminateMessageLooper();
635         assertEquals(PREVIEW_CALLBACK_RECEIVED, mPreviewCallbackResult);
636     }
637 
638     @UiThreadTest
639     @Test
testDisplayOrientation()640     public void testDisplayOrientation() throws Exception {
641         for (int id : mCameraIds) {
642             Log.v(TAG, "Camera id=" + id);
643             testDisplayOrientationByCamera(id);
644         }
645     }
646 
testDisplayOrientationByCamera(int cameraId)647     private void testDisplayOrientationByCamera(int cameraId) throws Exception {
648         initializeMessageLooper(cameraId);
649 
650         // Check valid arguments.
651         mCamera.setDisplayOrientation(0);
652         mCamera.setDisplayOrientation(90);
653         mCamera.setDisplayOrientation(180);
654         mCamera.setDisplayOrientation(270);
655 
656         // Check invalid arguments.
657         try {
658             mCamera.setDisplayOrientation(45);
659             fail("Should throw exception for invalid arguments");
660         } catch (RuntimeException ex) {
661             // expected
662         }
663 
664         // Start preview.
665         mCamera.startPreview();
666 
667         // Check setting orientation during preview is allowed.
668         mCamera.setDisplayOrientation(90);
669         mCamera.setDisplayOrientation(180);
670         mCamera.setDisplayOrientation(270);
671         mCamera.setDisplayOrientation(00);
672 
673         terminateMessageLooper();
674     }
675 
676     @UiThreadTest
677     @Test
testParameters()678     public void testParameters() throws Exception {
679         for (int id : mCameraIds) {
680             Log.v(TAG, "Camera id=" + id);
681             testParametersByCamera(id);
682         }
683     }
684 
testParametersByCamera(int cameraId)685     private void testParametersByCamera(int cameraId) throws Exception {
686         initializeMessageLooper(cameraId);
687         // we can get parameters just by getxxx method due to the private constructor
688         Parameters pSet = mCamera.getParameters();
689         assertParameters(pSet);
690         terminateMessageLooper();
691     }
692 
693     // Also test Camera.Parameters
assertParameters(Parameters parameters)694     private void assertParameters(Parameters parameters) {
695         // Parameters constants
696         final int PICTURE_FORMAT = ImageFormat.JPEG;
697         final int PREVIEW_FORMAT = ImageFormat.NV21;
698 
699         // Before setting Parameters
700         final int origPictureFormat = parameters.getPictureFormat();
701         final int origPictureWidth = parameters.getPictureSize().width;
702         final int origPictureHeight = parameters.getPictureSize().height;
703         final int origPreviewFormat = parameters.getPreviewFormat();
704         final int origPreviewWidth = parameters.getPreviewSize().width;
705         final int origPreviewHeight = parameters.getPreviewSize().height;
706         final int origPreviewFrameRate = parameters.getPreviewFrameRate();
707 
708         assertTrue(origPictureWidth > 0);
709         assertTrue(origPictureHeight > 0);
710         assertTrue(origPreviewWidth > 0);
711         assertTrue(origPreviewHeight > 0);
712         assertTrue(origPreviewFrameRate > 0);
713 
714         // The default preview format must be yuv420 (NV21).
715         assertEquals(ImageFormat.NV21, origPreviewFormat);
716 
717         // The default picture format must be Jpeg.
718         assertEquals(ImageFormat.JPEG, origPictureFormat);
719 
720         // If camera supports flash, the default flash mode must be off.
721         String flashMode = parameters.getFlashMode();
722         assertTrue(flashMode == null || flashMode.equals(parameters.FLASH_MODE_OFF));
723         String wb = parameters.getWhiteBalance();
724         assertTrue(wb == null || wb.equals(parameters.WHITE_BALANCE_AUTO));
725         String effect = parameters.getColorEffect();
726         assertTrue(effect == null || effect.equals(parameters.EFFECT_NONE));
727 
728         // Some parameters must be supported.
729         List<Size> previewSizes = parameters.getSupportedPreviewSizes();
730         List<Size> pictureSizes = parameters.getSupportedPictureSizes();
731         List<Integer> previewFormats = parameters.getSupportedPreviewFormats();
732         List<Integer> pictureFormats = parameters.getSupportedPictureFormats();
733         List<Integer> frameRates = parameters.getSupportedPreviewFrameRates();
734         List<String> focusModes = parameters.getSupportedFocusModes();
735         String focusMode = parameters.getFocusMode();
736         float focalLength = parameters.getFocalLength();
737         float horizontalViewAngle = parameters.getHorizontalViewAngle();
738         float verticalViewAngle = parameters.getVerticalViewAngle();
739         int jpegQuality = parameters.getJpegQuality();
740         int jpegThumnailQuality = parameters.getJpegThumbnailQuality();
741         assertTrue(previewSizes != null && previewSizes.size() != 0);
742         assertTrue(pictureSizes != null && pictureSizes.size() != 0);
743         assertTrue(previewFormats != null && previewFormats.size() >= 2);
744         assertTrue(previewFormats.contains(ImageFormat.NV21));
745         assertTrue(previewFormats.contains(ImageFormat.YV12));
746         assertTrue(pictureFormats != null && pictureFormats.size() != 0);
747         assertTrue(frameRates != null && frameRates.size() != 0);
748         assertTrue(focusModes != null && focusModes.size() != 0);
749         assertNotNull(focusMode);
750         // The default focus mode must be auto if it exists.
751         if (focusModes.contains(Parameters.FOCUS_MODE_AUTO)) {
752             assertEquals(Parameters.FOCUS_MODE_AUTO, focusMode);
753         }
754 
755         if (mIsExternalCamera) {
756             // External camera by default reports -1.0, but don't fail if
757             // the HAL implementation somehow chooses to report this information.
758             assertTrue(focalLength == -1.0 || focalLength > 0);
759             assertTrue(horizontalViewAngle == -1.0 ||
760                     (horizontalViewAngle > 0 && horizontalViewAngle <= 360));
761             assertTrue(verticalViewAngle == -1.0 ||
762                     (verticalViewAngle > 0 && verticalViewAngle <= 360));
763         } else {
764             assertTrue(focalLength > 0);
765             assertTrue(horizontalViewAngle > 0 && horizontalViewAngle <= 360);
766             assertTrue(verticalViewAngle > 0 && verticalViewAngle <= 360);
767         }
768 
769         Size previewSize = previewSizes.get(0);
770         Size pictureSize = pictureSizes.get(0);
771         assertTrue(jpegQuality >= 1 && jpegQuality <= 100);
772         assertTrue(jpegThumnailQuality >= 1 && jpegThumnailQuality <= 100);
773 
774         // If a parameter is supported, both getXXX and getSupportedXXX have to
775         // be non null.
776         if (parameters.getWhiteBalance() != null) {
777             assertNotNull(parameters.getSupportedWhiteBalance());
778         }
779         if (parameters.getSupportedWhiteBalance() != null) {
780             assertNotNull(parameters.getWhiteBalance());
781         }
782         if (parameters.getColorEffect() != null) {
783             assertNotNull(parameters.getSupportedColorEffects());
784         }
785         if (parameters.getSupportedColorEffects() != null) {
786             assertNotNull(parameters.getColorEffect());
787         }
788         if (parameters.getAntibanding() != null) {
789             assertNotNull(parameters.getSupportedAntibanding());
790         }
791         if (parameters.getSupportedAntibanding() != null) {
792             assertNotNull(parameters.getAntibanding());
793         }
794         if (parameters.getSceneMode() != null) {
795             assertNotNull(parameters.getSupportedSceneModes());
796         }
797         if (parameters.getSupportedSceneModes() != null) {
798             assertNotNull(parameters.getSceneMode());
799         }
800         if (parameters.getFlashMode() != null) {
801             assertNotNull(parameters.getSupportedFlashModes());
802         }
803         if (parameters.getSupportedFlashModes() != null) {
804             assertNotNull(parameters.getFlashMode());
805         }
806 
807         // Check if the sizes value contain invalid characters.
808         assertNoLetters(parameters.get("preview-size-values"), "preview-size-values");
809         assertNoLetters(parameters.get("picture-size-values"), "picture-size-values");
810         assertNoLetters(parameters.get("jpeg-thumbnail-size-values"),
811                 "jpeg-thumbnail-size-values");
812 
813         // Set the parameters.
814         parameters.setPictureFormat(PICTURE_FORMAT);
815         assertEquals(PICTURE_FORMAT, parameters.getPictureFormat());
816         parameters.setPictureSize(pictureSize.width, pictureSize.height);
817         assertEquals(pictureSize.width, parameters.getPictureSize().width);
818         assertEquals(pictureSize.height, parameters.getPictureSize().height);
819         parameters.setPreviewFormat(PREVIEW_FORMAT);
820         assertEquals(PREVIEW_FORMAT, parameters.getPreviewFormat());
821         parameters.setPreviewFrameRate(frameRates.get(0));
822         assertEquals(frameRates.get(0).intValue(), parameters.getPreviewFrameRate());
823         parameters.setPreviewSize(previewSize.width, previewSize.height);
824         assertEquals(previewSize.width, parameters.getPreviewSize().width);
825         assertEquals(previewSize.height, parameters.getPreviewSize().height);
826 
827         mCamera.setParameters(parameters);
828         Parameters paramActual = mCamera.getParameters();
829 
830         assertTrue(isValidPixelFormat(paramActual.getPictureFormat()));
831         assertEquals(pictureSize.width, paramActual.getPictureSize().width);
832         assertEquals(pictureSize.height, paramActual.getPictureSize().height);
833         assertTrue(isValidPixelFormat(paramActual.getPreviewFormat()));
834         assertEquals(previewSize.width, paramActual.getPreviewSize().width);
835         assertEquals(previewSize.height, paramActual.getPreviewSize().height);
836         assertTrue(paramActual.getPreviewFrameRate() > 0);
837 
838         checkExposureCompensation(parameters);
839         checkPreferredPreviewSizeForVideo(parameters);
840     }
841 
checkPreferredPreviewSizeForVideo(Parameters parameters)842     private void checkPreferredPreviewSizeForVideo(Parameters parameters) {
843         List<Size> videoSizes = parameters.getSupportedVideoSizes();
844         Size preferredPreviewSize = parameters.getPreferredPreviewSizeForVideo();
845 
846         // If getSupportedVideoSizes() returns null,
847         // getPreferredPreviewSizeForVideo() will return null;
848         // otherwise, if getSupportedVideoSizes() does not return null,
849         // getPreferredPreviewSizeForVideo() will not return null.
850         if (videoSizes == null) {
851             assertNull(preferredPreviewSize);
852         } else {
853             assertNotNull(preferredPreviewSize);
854         }
855 
856         // If getPreferredPreviewSizeForVideo() returns null,
857         // getSupportedVideoSizes() will return null;
858         // otherwise, if getPreferredPreviewSizeForVideo() does not return null,
859         // getSupportedVideoSizes() will not return null.
860         if (preferredPreviewSize == null) {
861             assertNull(videoSizes);
862         } else {
863             assertNotNull(videoSizes);
864         }
865 
866         if (videoSizes != null) {  // implies: preferredPreviewSize != null
867             // If getSupportedVideoSizes() does not return null,
868             // the returned list will contain at least one size.
869             assertTrue(videoSizes.size() > 0);
870 
871             // In addition, getPreferredPreviewSizeForVideo() returns a size
872             // that is among the supported preview sizes.
873             List<Size> previewSizes = parameters.getSupportedPreviewSizes();
874             assertNotNull(previewSizes);
875             assertTrue(previewSizes.size() > 0);
876             assertTrue(previewSizes.contains(preferredPreviewSize));
877         }
878     }
879 
checkExposureCompensation(Parameters parameters)880     private void checkExposureCompensation(Parameters parameters) {
881         assertEquals(0, parameters.getExposureCompensation());
882         int max = parameters.getMaxExposureCompensation();
883         int min = parameters.getMinExposureCompensation();
884         float step = parameters.getExposureCompensationStep();
885         if (max == 0 && min == 0) {
886             assertEquals(0f, step, 0.000001f);
887             return;
888         }
889         assertTrue(step > 0);
890         assertTrue(max >= 0);
891         assertTrue(min <= 0);
892     }
893 
isValidPixelFormat(int format)894     private boolean isValidPixelFormat(int format) {
895         return (format == ImageFormat.RGB_565) || (format == ImageFormat.NV21)
896                 || (format == ImageFormat.JPEG) || (format == ImageFormat.YUY2);
897     }
898 
899     @UiThreadTest
900     @Test
testJpegThumbnailSize()901     public void testJpegThumbnailSize() throws Exception {
902         for (int id : mCameraIds) {
903             Log.v(TAG, "Camera id=" + id);
904             initializeMessageLooper(id);
905             testJpegThumbnailSizeByCamera(false, 0, 0);
906             terminateMessageLooper();
907         }
908     }
909 
testJpegThumbnailSizeByCamera(boolean recording, int recordingWidth, int recordingHeight)910     private void testJpegThumbnailSizeByCamera(boolean recording,
911             int recordingWidth, int recordingHeight) throws Exception {
912         // Thumbnail size parameters should have valid values.
913         Parameters p = mCamera.getParameters();
914         Size size = p.getJpegThumbnailSize();
915         assertTrue(size.width > 0 && size.height > 0);
916         List<Size> sizes = p.getSupportedJpegThumbnailSizes();
917         assertTrue(sizes.size() >= 2);
918         assertTrue(sizes.contains(size));
919         assertTrue(sizes.contains(mCamera.new Size(0, 0)));
920         Size pictureSize = p.getPictureSize();
921 
922         // Test if the thumbnail size matches the setting.
923         if (!recording) mCamera.startPreview();
924         mCamera.takePicture(mShutterCallback, mRawPictureCallback, mJpegPictureCallback);
925         waitForSnapshotDone();
926         assertTrue(mJpegPictureCallbackResult);
927         ExifInterface exif = new ExifInterface(mJpegPath);
928         assertTrue(exif.hasThumbnail());
929         byte[] thumb = exif.getThumbnail();
930         BitmapFactory.Options bmpOptions = new BitmapFactory.Options();
931         bmpOptions.inJustDecodeBounds = true;
932         BitmapFactory.decodeByteArray(thumb, 0, thumb.length, bmpOptions);
933         if (!recording) {
934             assertEquals(size.width, bmpOptions.outWidth);
935             assertEquals(size.height, bmpOptions.outHeight);
936         } else {
937             assertTrue(bmpOptions.outWidth >= recordingWidth ||
938                     bmpOptions.outWidth == size.width);
939             assertTrue(bmpOptions.outHeight >= recordingHeight ||
940                     bmpOptions.outHeight == size.height);
941         }
942 
943         // Test no thumbnail case.
944         p.setJpegThumbnailSize(0, 0);
945         mCamera.setParameters(p);
946         Size actual = mCamera.getParameters().getJpegThumbnailSize();
947         assertEquals(0, actual.width);
948         assertEquals(0, actual.height);
949         if (!recording) mCamera.startPreview();
950         mCamera.takePicture(mShutterCallback, mRawPictureCallback, mJpegPictureCallback);
951         waitForSnapshotDone();
952         assertTrue(mJpegPictureCallbackResult);
953         exif = new ExifInterface(mJpegPath);
954         assertFalse(exif.hasThumbnail());
955         // Primary image should still be valid for no thumbnail capture.
956         BitmapFactory.decodeFile(mJpegPath, bmpOptions);
957         if (!recording) {
958             assertTrue("Jpeg primary image size should match requested size",
959                     bmpOptions.outWidth == pictureSize.width &&
960                     bmpOptions.outHeight == pictureSize.height);
961         } else {
962             assertTrue(bmpOptions.outWidth >= recordingWidth ||
963                     bmpOptions.outWidth == size.width);
964             assertTrue(bmpOptions.outHeight >= recordingHeight ||
965                     bmpOptions.outHeight == size.height);
966         }
967 
968         assertNotNull("Jpeg primary image data should be decodable",
969                 BitmapFactory.decodeFile(mJpegPath));
970     }
971 
972     @UiThreadTest
973     @Test
testJpegExif()974     public void testJpegExif() throws Exception {
975         for (int id : mCameraIds) {
976             Log.v(TAG, "Camera id=" + id);
977             initializeMessageLooper(id);
978             testJpegExifByCamera(false);
979             terminateMessageLooper();
980         }
981     }
982 
testJpegExifByCamera(boolean recording)983     private void testJpegExifByCamera(boolean recording) throws Exception {
984         if (!recording) mCamera.startPreview();
985         // Get current time in milliseconds, removing the millisecond part
986         long captureStartTime = System.currentTimeMillis() / 1000 * 1000;
987         mCamera.takePicture(mShutterCallback, mRawPictureCallback, mJpegPictureCallback);
988         waitForSnapshotDone();
989 
990         Camera.Parameters parameters = mCamera.getParameters();
991         double focalLength = parameters.getFocalLength();
992 
993         // Test various exif tags.
994         ExifInterface exif = new ExifInterface(mJpegPath);
995         StringBuffer failedCause = new StringBuffer("Jpeg exif test failed:\n");
996         boolean extraExiftestPassed = checkExtraExifTagsSucceeds(failedCause, exif);
997 
998         if (VERBOSE) Log.v(TAG, "Testing exif tag TAG_DATETIME");
999         String datetime = exif.getAttribute(ExifInterface.TAG_DATETIME);
1000         assertNotNull(datetime);
1001         assertTrue(datetime.length() == 19); // EXIF spec is "yyyy:MM:dd HH:mm:ss".
1002         // Datetime should be local time.
1003         SimpleDateFormat exifDateFormat = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
1004         try {
1005             Date exifDateTime = exifDateFormat.parse(datetime);
1006             long captureFinishTime = exifDateTime.getTime();
1007             long timeDelta = captureFinishTime - captureStartTime;
1008             assertTrue(String.format("Snapshot delay (%d ms) is not in range of [0, %d]", timeDelta,
1009                     WAIT_FOR_SNAPSHOT_TO_COMPLETE),
1010                     timeDelta >= 0 && timeDelta <= WAIT_FOR_SNAPSHOT_TO_COMPLETE);
1011         } catch (ParseException e) {
1012             fail(String.format("Invalid string value on exif tag TAG_DATETIME: %s", datetime));
1013         }
1014         checkGpsDataNull(exif);
1015         double exifFocalLength = exif.getAttributeDouble(ExifInterface.TAG_FOCAL_LENGTH, -1);
1016         assertEquals(focalLength, exifFocalLength, 0.001);
1017         // Test image width and height exif tags. They should match the jpeg.
1018         assertBitmapAndJpegSizeEqual(mJpegData, exif);
1019 
1020         // Test gps exif tags.
1021         if (VERBOSE) Log.v(TAG, "Testing exif GPS tags");
1022         testGpsExifValues(parameters, 37.736071, -122.441983, 21, 1199145600,
1023             "GPS NETWORK HYBRID ARE ALL FINE.");
1024         testGpsExifValues(parameters, 0.736071, 0.441983, 1, 1199145601, "GPS");
1025         testGpsExifValues(parameters, -89.736071, -179.441983, 100000, 1199145602, "NETWORK");
1026 
1027         // Test gps tags do not exist after calling removeGpsData. Also check if
1028         // image width and height exif match the jpeg when jpeg rotation is set.
1029         if (VERBOSE) Log.v(TAG, "Testing exif GPS tag removal");
1030         if (!recording) mCamera.startPreview();
1031         parameters.removeGpsData();
1032         parameters.setRotation(90); // For testing image width and height exif.
1033         mCamera.setParameters(parameters);
1034         mCamera.takePicture(mShutterCallback, mRawPictureCallback, mJpegPictureCallback);
1035         waitForSnapshotDone();
1036         exif = new ExifInterface(mJpegPath);
1037         checkGpsDataNull(exif);
1038         assertBitmapAndJpegSizeEqual(mJpegData, exif);
1039         // Reset the rotation to prevent from affecting other tests.
1040         parameters.setRotation(0);
1041         mCamera.setParameters(parameters);
1042     }
1043 
1044     /**
1045      * Correctness check of some extra exif tags.
1046      * <p>
1047      * Check some extra exif tags without asserting the check failures
1048      * immediately. When a failure is detected, the failure cause is logged,
1049      * the rest of the tests are still executed. The caller can assert with the
1050      * failure cause based on the returned test status.
1051      * </p>
1052      *
1053      * @param logBuf Log failure cause to this StringBuffer if there is
1054      * any failure.
1055      * @param exif The exif data associated with a jpeg image being tested.
1056      * @return true if no test failure is found, false if there is any failure.
1057      */
checkExtraExifTagsSucceeds(StringBuffer logBuf, ExifInterface exif)1058     private boolean checkExtraExifTagsSucceeds(StringBuffer logBuf, ExifInterface exif) {
1059         if (logBuf == null || exif == null) {
1060             throw new IllegalArgumentException("failureCause and exif shouldn't be null");
1061         }
1062 
1063         if (VERBOSE) Log.v(TAG, "Testing extra exif tags");
1064         boolean allTestsPassed = true;
1065         boolean passedSoFar = true;
1066         String failureMsg;
1067 
1068         // TAG_EXPOSURE_TIME
1069         // ExifInterface API gives exposure time value in the form of float instead of rational
1070         String exposureTime = exif.getAttribute(ExifInterface.TAG_EXPOSURE_TIME);
1071         passedSoFar = expectNotNull("Exif TAG_EXPOSURE_TIME is null!", logBuf, exposureTime);
1072         if (passedSoFar) {
1073             double exposureTimeValue = Double.parseDouble(exposureTime);
1074             failureMsg = "Exif exposure time " + exposureTime + " should be a positive value";
1075             passedSoFar = expectTrue(failureMsg, logBuf, exposureTimeValue > 0);
1076         }
1077         allTestsPassed = allTestsPassed && passedSoFar;
1078 
1079         // TAG_APERTURE
1080         // ExifInterface API gives aperture value in the form of float instead of rational
1081         String aperture = exif.getAttribute(ExifInterface.TAG_APERTURE);
1082         passedSoFar = expectNotNull("Exif TAG_APERTURE is null!", logBuf, aperture);
1083         if (passedSoFar) {
1084             double apertureValue = Double.parseDouble(aperture);
1085             passedSoFar = expectTrue("Exif TAG_APERTURE value " + aperture + " should be positive!",
1086                     logBuf, apertureValue > 0);
1087         }
1088         allTestsPassed = allTestsPassed && passedSoFar;
1089 
1090         // TAG_FLASH
1091         String flash = exif.getAttribute(ExifInterface.TAG_FLASH);
1092         passedSoFar = expectNotNull("Exif TAG_FLASH is null!", logBuf, flash);
1093         allTestsPassed = allTestsPassed && passedSoFar;
1094 
1095         // TAG_WHITE_BALANCE
1096         String whiteBalance = exif.getAttribute(ExifInterface.TAG_WHITE_BALANCE);
1097         passedSoFar = expectNotNull("Exif TAG_WHITE_BALANCE is null!", logBuf, whiteBalance);
1098         allTestsPassed = allTestsPassed && passedSoFar;
1099 
1100         // TAG_MAKE
1101         String make = exif.getAttribute(ExifInterface.TAG_MAKE);
1102         passedSoFar = expectNotNull("Exif TAG_MAKE is null!", logBuf, make);
1103         if (passedSoFar) {
1104             passedSoFar = expectTrue("Exif TAG_MODEL value: " + make
1105                     + " should match build manufacturer: " + Build.MANUFACTURER, logBuf,
1106                     make.equals(Build.MANUFACTURER));
1107         }
1108         allTestsPassed = allTestsPassed && passedSoFar;
1109 
1110         // TAG_MODEL
1111         String model = exif.getAttribute(ExifInterface.TAG_MODEL);
1112         passedSoFar = expectNotNull("Exif TAG_MODEL is null!", logBuf, model);
1113         if (passedSoFar) {
1114             passedSoFar = expectTrue("Exif TAG_MODEL value: " + model
1115                     + " should match build manufacturer: " + Build.MODEL, logBuf,
1116                     model.equals(Build.MODEL));
1117         }
1118         allTestsPassed = allTestsPassed && passedSoFar;
1119 
1120         // TAG_ISO
1121         int iso = exif.getAttributeInt(ExifInterface.TAG_ISO, -1);
1122         passedSoFar = expectTrue("Exif ISO value " + iso + " is invalid", logBuf, iso > 0);
1123         allTestsPassed = allTestsPassed && passedSoFar;
1124 
1125         // TAG_DATETIME_DIGITIZED (a.k.a Create time for digital cameras).
1126         String digitizedTime = exif.getAttribute(TAG_DATETIME_DIGITIZED);
1127         passedSoFar = expectNotNull("Exif TAG_DATETIME_DIGITIZED is null!", logBuf, digitizedTime);
1128         if (passedSoFar) {
1129             String datetime = exif.getAttribute(ExifInterface.TAG_DATETIME);
1130             passedSoFar = expectNotNull("Exif TAG_DATETIME is null!", logBuf, datetime);
1131             if (passedSoFar) {
1132                 passedSoFar = expectTrue("dataTime should match digitizedTime", logBuf,
1133                         digitizedTime.equals(datetime));
1134             }
1135         }
1136         allTestsPassed = allTestsPassed && passedSoFar;
1137 
1138         /**
1139          * TAG_SUBSEC_TIME. Since the sub second tag strings are truncated to at
1140          * most 9 digits in ExifInterface implementation, use getAttributeInt to
1141          * sanitize it. When the default value -1 is returned, it means that
1142          * this exif tag either doesn't exist or is a non-numerical invalid
1143          * string. Same rule applies to the rest of sub second tags.
1144          */
1145         int subSecTime = exif.getAttributeInt(TAG_SUBSEC_TIME, -1);
1146         passedSoFar = expectTrue(
1147                 "Exif TAG_SUBSEC_TIME value is null or invalid!", logBuf, subSecTime > 0);
1148         allTestsPassed = allTestsPassed && passedSoFar;
1149 
1150         // TAG_SUBSEC_TIME_ORIG
1151         int subSecTimeOrig = exif.getAttributeInt(TAG_SUBSEC_TIME_ORIG, -1);
1152         passedSoFar = expectTrue(
1153                 "Exif TAG_SUBSEC_TIME_ORIG value is null or invalid!", logBuf, subSecTimeOrig > 0);
1154         allTestsPassed = allTestsPassed && passedSoFar;
1155 
1156         // TAG_SUBSEC_TIME_DIG
1157         int subSecTimeDig = exif.getAttributeInt(TAG_SUBSEC_TIME_DIG, -1);
1158         passedSoFar = expectTrue(
1159                 "Exif TAG_SUBSEC_TIME_DIG value is null or invalid!", logBuf, subSecTimeDig > 0);
1160         allTestsPassed = allTestsPassed && passedSoFar;
1161 
1162         return allTestsPassed;
1163     }
1164 
1165     /**
1166      * Check if object is null and log failure msg.
1167      *
1168      * @param msg Failure msg.
1169      * @param logBuffer StringBuffer to log the failure msg.
1170      * @param obj Object to test.
1171      * @return true if object is not null, otherwise return false.
1172      */
expectNotNull(String msg, StringBuffer logBuffer, Object obj)1173     private boolean expectNotNull(String msg, StringBuffer logBuffer, Object obj)
1174     {
1175         if (obj == null) {
1176             logBuffer.append(msg + "\n");
1177         }
1178         return (obj != null);
1179     }
1180 
1181     /**
1182      * Check if condition is false and log failure msg.
1183      *
1184      * @param msg Failure msg.
1185      * @param logBuffer StringBuffer to log the failure msg.
1186      * @param condition Condition to test.
1187      * @return The value of the condition.
1188      */
expectTrue(String msg, StringBuffer logBuffer, boolean condition)1189     private boolean expectTrue(String msg, StringBuffer logBuffer, boolean condition) {
1190         if (!condition) {
1191             logBuffer.append(msg + "\n");
1192         }
1193         return condition;
1194     }
1195 
assertBitmapAndJpegSizeEqual(byte[] jpegData, ExifInterface exif)1196     private void assertBitmapAndJpegSizeEqual(byte[] jpegData, ExifInterface exif) {
1197         int exifWidth = exif.getAttributeInt(ExifInterface.TAG_IMAGE_WIDTH, 0);
1198         int exifHeight = exif.getAttributeInt(ExifInterface.TAG_IMAGE_LENGTH, 0);
1199         assertTrue(exifWidth != 0 && exifHeight != 0);
1200         BitmapFactory.Options bmpOptions = new BitmapFactory.Options();
1201         bmpOptions.inJustDecodeBounds = true;
1202         BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length, bmpOptions);
1203         assertEquals(bmpOptions.outWidth, exifWidth);
1204         assertEquals(bmpOptions.outHeight, exifHeight);
1205     }
1206 
testGpsExifValues(Parameters parameters, double latitude, double longitude, double altitude, long timestamp, String method)1207     private void testGpsExifValues(Parameters parameters, double latitude,
1208             double longitude, double altitude, long timestamp, String method)
1209             throws IOException {
1210         mCamera.startPreview();
1211         parameters.setGpsLatitude(latitude);
1212         parameters.setGpsLongitude(longitude);
1213         parameters.setGpsAltitude(altitude);
1214         parameters.setGpsTimestamp(timestamp);
1215         parameters.setGpsProcessingMethod(method);
1216         mCamera.setParameters(parameters);
1217         mCamera.takePicture(mShutterCallback, mRawPictureCallback, mJpegPictureCallback);
1218         waitForSnapshotDone();
1219         ExifInterface exif = new ExifInterface(mJpegPath);
1220         assertNotNull(exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE));
1221         assertNotNull(exif.getAttribute(ExifInterface.TAG_GPS_LONGITUDE));
1222         assertNotNull(exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE_REF));
1223         assertNotNull(exif.getAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF));
1224         assertNotNull(exif.getAttribute(ExifInterface.TAG_GPS_TIMESTAMP));
1225         assertNotNull(exif.getAttribute(ExifInterface.TAG_GPS_DATESTAMP));
1226         assertEquals(method, exif.getAttribute(ExifInterface.TAG_GPS_PROCESSING_METHOD));
1227         float[] latLong = new float[2];
1228         assertTrue(exif.getLatLong(latLong));
1229         assertEquals((float)latitude, latLong[0], 0.0001f);
1230         assertEquals((float)longitude, latLong[1], 0.0001f);
1231         assertEquals(altitude, exif.getAltitude(-1), 1);
1232         assertEquals(timestamp, getGpsDateTimeFromJpeg(exif) / 1000);
1233     }
1234 
getGpsDateTimeFromJpeg(ExifInterface exif)1235     private long getGpsDateTimeFromJpeg(ExifInterface exif) {
1236         String date = exif.getAttribute(ExifInterface.TAG_GPS_DATESTAMP);
1237         String time = exif.getAttribute(ExifInterface.TAG_GPS_TIMESTAMP);
1238         if (date == null || time == null) return -1;
1239 
1240         String dateTimeString = date + ' ' + time;
1241         ParsePosition pos = new ParsePosition(0);
1242         try {
1243             SimpleDateFormat formatter = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
1244             formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
1245 
1246             Date datetime = formatter.parse(dateTimeString, pos);
1247             if (datetime == null) return -1;
1248             return datetime.getTime();
1249         } catch (IllegalArgumentException ex) {
1250             return -1;
1251         }
1252     }
1253 
checkGpsDataNull(ExifInterface exif)1254     private void checkGpsDataNull(ExifInterface exif) {
1255         assertNull(exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE));
1256         assertNull(exif.getAttribute(ExifInterface.TAG_GPS_LONGITUDE));
1257         assertNull(exif.getAttribute(ExifInterface.TAG_GPS_LATITUDE_REF));
1258         assertNull(exif.getAttribute(ExifInterface.TAG_GPS_LONGITUDE_REF));
1259         assertNull(exif.getAttribute(ExifInterface.TAG_GPS_TIMESTAMP));
1260         assertNull(exif.getAttribute(ExifInterface.TAG_GPS_DATESTAMP));
1261         assertNull(exif.getAttribute(ExifInterface.TAG_GPS_PROCESSING_METHOD));
1262     }
1263 
1264     @UiThreadTest
1265     @Test
testLockUnlock()1266     public void testLockUnlock() throws Exception {
1267         for (int id : mCameraIds) {
1268             Log.v(TAG, "Camera id=" + id);
1269             testLockUnlockByCamera(id);
1270         }
1271     }
1272 
testLockUnlockByCamera(int cameraId)1273     private void testLockUnlockByCamera(int cameraId) throws Exception {
1274         initializeMessageLooper(cameraId);
1275         Camera.Parameters parameters = mCamera.getParameters();
1276         SurfaceHolder surfaceHolder;
1277         surfaceHolder = mActivityRule.getActivity().getSurfaceView().getHolder();
1278         CamcorderProfile profile = null; // Used for built-in camera
1279         Camera.Size videoSize = null; // Used for external camera
1280         int frameRate = -1; // Used for external camera
1281 
1282         // Set the preview size.
1283         if (mIsExternalCamera) {
1284             videoSize = setupExternalCameraRecord(parameters);
1285             frameRate = parameters.getPreviewFrameRate();
1286         } else {
1287             profile = CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_LOW);
1288             setPreviewSizeByProfile(parameters, profile);
1289         }
1290 
1291         mCamera.setParameters(parameters);
1292         mCamera.setPreviewDisplay(surfaceHolder);
1293         mCamera.startPreview();
1294         mCamera.lock();  // Locking again from the same process has no effect.
1295         try {
1296             if (mIsExternalCamera) {
1297                 recordVideoBySize(videoSize, frameRate, surfaceHolder);
1298             } else {
1299                 recordVideo(profile, surfaceHolder);
1300             }
1301             fail("Recording should not succeed because camera is locked.");
1302         } catch (Exception e) {
1303             // expected
1304         }
1305 
1306         mCamera.unlock();  // Unlock the camera so media recorder can use it.
1307         try {
1308             mCamera.setParameters(parameters);
1309             fail("setParameters should not succeed because camera is unlocked.");
1310         } catch (RuntimeException e) {
1311             // expected
1312         }
1313 
1314         if (mIsExternalCamera) {
1315             recordVideoBySize(videoSize, frameRate, surfaceHolder);
1316         } else {
1317             recordVideo(profile, surfaceHolder);  // should not throw exception
1318         }
1319 
1320         // Media recorder already releases the camera so the test application
1321         // can lock and use the camera now.
1322         mCamera.lock();  // should not fail
1323         mCamera.setParameters(parameters);  // should not fail
1324         terminateMessageLooper();
1325     }
1326 
setupExternalCameraRecord(Parameters parameters)1327     private Camera.Size setupExternalCameraRecord(Parameters parameters) {
1328         Camera.Size videoSize = parameters.getPreferredPreviewSizeForVideo();
1329         assertNotNull(videoSize);
1330         parameters.setPreviewSize(videoSize.width, videoSize.height);
1331         return videoSize;
1332     }
1333 
setPreviewSizeByProfile(Parameters parameters, CamcorderProfile profile)1334     private void setPreviewSizeByProfile(Parameters parameters, CamcorderProfile profile) {
1335         if (parameters.getSupportedVideoSizes() == null) {
1336             parameters.setPreviewSize(profile.videoFrameWidth,
1337                     profile.videoFrameHeight);
1338         } else {  // Driver supports separates outputs for preview and video.
1339             List<Size> sizes = parameters.getSupportedPreviewSizes();
1340             Size preferred = parameters.getPreferredPreviewSizeForVideo();
1341             int product = preferred.width * preferred.height;
1342             for (Size size: sizes) {
1343                 if (size.width * size.height <= product) {
1344                     parameters.setPreviewSize(size.width, size.height);
1345                     break;
1346                 }
1347             }
1348         }
1349     }
1350 
recordVideoBySize(Camera.Size size, int frameRate, SurfaceHolder holder)1351     private void recordVideoBySize(Camera.Size size, int frameRate,
1352             SurfaceHolder holder) throws Exception {
1353         MediaRecorder recorder = new MediaRecorder();
1354         try {
1355             // Pass the camera from the test application to media recorder.
1356             recorder.setCamera(mCamera);
1357             recorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
1358             recorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
1359             recorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
1360             recorder.setVideoEncodingBitRate(VIDEO_BIT_RATE_IN_BPS);
1361             recorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);
1362             recorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
1363             recorder.setVideoSize(size.width, size.height);
1364             recorder.setVideoFrameRate(frameRate);
1365             recorder.setOutputFile(mRecordingPath);
1366             recorder.setPreviewDisplay(holder.getSurface());
1367             recorder.prepare();
1368             recorder.start();
1369 
1370             // Apps can use the camera after start since API level 13.
1371             Parameters parameters = mCamera.getParameters();
1372             if (parameters.isZoomSupported()) {
1373                if (parameters.getMaxZoom() > 0) {
1374                    parameters.setZoom(1);
1375                    mCamera.setParameters(parameters);
1376                    parameters.setZoom(0);
1377                    mCamera.setParameters(parameters);
1378                }
1379             }
1380             if (parameters.isSmoothZoomSupported()) {
1381                 if (parameters.getMaxZoom() > 0) {
1382                     ZoomListener zoomListener = new ZoomListener();
1383                     mCamera.setZoomChangeListener(zoomListener);
1384                     mCamera.startSmoothZoom(1);
1385                     assertTrue(zoomListener.mZoomDone.block(1000));
1386                 }
1387             }
1388 
1389             try {
1390                 mCamera.unlock();
1391                 fail("unlock should not succeed during recording.");
1392             } catch(RuntimeException e) {
1393                 // expected
1394             }
1395 
1396             Thread.sleep(2000);
1397             recorder.stop();
1398         } finally {
1399             recorder.release();
1400         }
1401     }
1402 
recordVideo(CamcorderProfile profile, SurfaceHolder holder)1403     private void recordVideo(CamcorderProfile profile,
1404             SurfaceHolder holder) throws Exception {
1405         MediaRecorder recorder = new MediaRecorder();
1406         try {
1407             // Pass the camera from the test application to media recorder.
1408             recorder.setCamera(mCamera);
1409             recorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
1410             recorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
1411             recorder.setProfile(profile);
1412             recorder.setOutputFile(mRecordingPath);
1413             recorder.setPreviewDisplay(holder.getSurface());
1414             recorder.prepare();
1415             recorder.start();
1416 
1417             // Apps can use the camera after start since API level 13.
1418             Parameters parameters = mCamera.getParameters();
1419             if (parameters.isZoomSupported()) {
1420                if (parameters.getMaxZoom() > 0) {
1421                    parameters.setZoom(1);
1422                    mCamera.setParameters(parameters);
1423                    parameters.setZoom(0);
1424                    mCamera.setParameters(parameters);
1425                }
1426             }
1427             if (parameters.isSmoothZoomSupported()) {
1428                 if (parameters.getMaxZoom() > 0) {
1429                     ZoomListener zoomListener = new ZoomListener();
1430                     mCamera.setZoomChangeListener(zoomListener);
1431                     mCamera.startSmoothZoom(1);
1432                     assertTrue(zoomListener.mZoomDone.block(1000));
1433                 }
1434             }
1435 
1436             try {
1437                 mCamera.unlock();
1438                 fail("unlock should not succeed during recording.");
1439             } catch(RuntimeException e) {
1440                 // expected
1441             }
1442 
1443             Thread.sleep(2000);
1444             recorder.stop();
1445         } finally {
1446             recorder.release();
1447         }
1448     }
1449 
1450     @UiThreadTest
1451     @Test
testPreviewCallbackWithBuffer()1452     public void testPreviewCallbackWithBuffer() throws Exception {
1453         for (int id : mCameraIds) {
1454             Log.v(TAG, "Camera id=" + id);
1455             testPreviewCallbackWithBufferByCamera(id);
1456         }
1457     }
1458 
testPreviewCallbackWithBufferByCamera(int cameraId)1459     private void testPreviewCallbackWithBufferByCamera(int cameraId) throws Exception {
1460         initializeMessageLooper(cameraId);
1461         SurfaceHolder surfaceHolder;
1462         surfaceHolder = mActivityRule.getActivity().getSurfaceView().getHolder();
1463         mCamera.setPreviewDisplay(surfaceHolder);
1464         Parameters parameters = mCamera.getParameters();
1465         PreviewCallbackWithBuffer callback = new PreviewCallbackWithBuffer();
1466 
1467         // Test all preview sizes.
1468         for (Size size: parameters.getSupportedPreviewSizes()) {
1469             Size pictureSize = getPictureSizeForPreview(size, parameters);
1470             parameters.setPictureSize(pictureSize.width, pictureSize.height);
1471             parameters.setPreviewSize(size.width, size.height);
1472             mCamera.setParameters(parameters);
1473             assertEquals(size, mCamera.getParameters().getPreviewSize());
1474             callback.mNumCbWithBuffer1 = 0;
1475             callback.mNumCbWithBuffer2 = 0;
1476             callback.mNumCbWithBuffer3 = 0;
1477             int format = mCamera.getParameters().getPreviewFormat();
1478             int bitsPerPixel = ImageFormat.getBitsPerPixel(format);
1479             callback.mBuffer1 = new byte[size.width * size.height * bitsPerPixel / 8];
1480             callback.mBuffer2 = new byte[size.width * size.height * bitsPerPixel / 8];
1481             callback.mBuffer3 = new byte[size.width * size.height * bitsPerPixel / 8];
1482 
1483             // Test if we can get the preview callbacks with specified buffers.
1484             mCamera.addCallbackBuffer(callback.mBuffer1);
1485             mCamera.addCallbackBuffer(callback.mBuffer2);
1486             mCamera.setPreviewCallbackWithBuffer(callback);
1487             mCamera.startPreview();
1488             waitForPreviewDone();
1489             assertFalse(callback.mPreviewDataNull);
1490             assertFalse(callback.mInvalidData);
1491             assertEquals(1, callback.mNumCbWithBuffer1);
1492             assertEquals(1, callback.mNumCbWithBuffer2);
1493             assertEquals(0, callback.mNumCbWithBuffer3);
1494 
1495             // Test if preview callback with buffer still works during preview.
1496             mCamera.addCallbackBuffer(callback.mBuffer3);
1497             waitForPreviewDone();
1498             assertFalse(callback.mPreviewDataNull);
1499             assertFalse(callback.mInvalidData);
1500             assertEquals(1, callback.mNumCbWithBuffer1);
1501             assertEquals(1, callback.mNumCbWithBuffer2);
1502             assertEquals(1, callback.mNumCbWithBuffer3);
1503             mCamera.setPreviewCallbackWithBuffer(null);
1504             mCamera.stopPreview();
1505         }
1506         terminateMessageLooper();
1507     }
1508 
1509     private final class PreviewCallbackWithBuffer
1510             implements android.hardware.Camera.PreviewCallback {
1511         public int mNumCbWithBuffer1, mNumCbWithBuffer2, mNumCbWithBuffer3;
1512         public byte[] mBuffer1, mBuffer2, mBuffer3;
1513         public boolean mPreviewDataNull, mInvalidData;
onPreviewFrame(byte[] data, Camera camera)1514         public void onPreviewFrame(byte[] data, Camera camera) {
1515             if (data == null) {
1516                 Log.e(TAG, "Preview data is null!");
1517                 mPreviewDataNull = true;
1518                 mPreviewDone.open();
1519                 return;
1520             }
1521             if (data == mBuffer1) {
1522                 mNumCbWithBuffer1++;
1523             } else if (data == mBuffer2) {
1524                 mNumCbWithBuffer2++;
1525             } else if (data == mBuffer3) {
1526                 mNumCbWithBuffer3++;
1527             } else {
1528                 Log.e(TAG, "Invalid byte array.");
1529                 mInvalidData = true;
1530                 mPreviewDone.open();
1531                 return;
1532             }
1533 
1534             if ((mNumCbWithBuffer1 == 1 && mNumCbWithBuffer2 == 1)
1535                     || mNumCbWithBuffer3 == 1) {
1536                 mPreviewDone.open();
1537             }
1538         }
1539     }
1540 
1541     @UiThreadTest
1542     @Test
testImmediateZoom()1543     public void testImmediateZoom() throws Exception {
1544         for (int id : mCameraIds) {
1545             Log.v(TAG, "Camera id=" + id);
1546             testImmediateZoomByCamera(id);
1547         }
1548     }
1549 
testImmediateZoomByCamera(int id)1550     private void testImmediateZoomByCamera(int id) throws Exception {
1551         initializeMessageLooper(id);
1552 
1553         Parameters parameters = mCamera.getParameters();
1554         if (!parameters.isZoomSupported()) {
1555             terminateMessageLooper();
1556             return;
1557         }
1558 
1559         // Test the zoom parameters.
1560         assertEquals(0, parameters.getZoom());  // default zoom should be 0.
1561         for (Size size: parameters.getSupportedPreviewSizes()) {
1562             parameters = mCamera.getParameters();
1563             Size pictureSize = getPictureSizeForPreview(size, parameters);
1564             parameters.setPreviewSize(size.width, size.height);
1565             parameters.setPictureSize(pictureSize.width, pictureSize.height);
1566             mCamera.setParameters(parameters);
1567             parameters = mCamera.getParameters();
1568             int maxZoom = parameters.getMaxZoom();
1569             assertTrue(maxZoom >= 0);
1570 
1571             // Zoom ratios should be sorted from small to large.
1572             List<Integer> ratios = parameters.getZoomRatios();
1573             assertEquals(maxZoom + 1, ratios.size());
1574             assertEquals(100, ratios.get(0).intValue());
1575             for (int i = 0; i < ratios.size() - 1; i++) {
1576                 assertTrue(ratios.get(i) < ratios.get(i + 1));
1577             }
1578             blockingStartPreview();
1579 
1580             // Test each zoom step.
1581             for (int i = 0; i <= maxZoom; i++) {
1582                 parameters.setZoom(i);
1583                 mCamera.setParameters(parameters);
1584                 assertEquals(i, mCamera.getParameters().getZoom());
1585             }
1586 
1587             // It should throw exception if an invalid value is passed.
1588             try {
1589                 parameters.setZoom(maxZoom + 1);
1590                 mCamera.setParameters(parameters);
1591                 fail("setZoom should throw exception.");
1592             } catch (RuntimeException e) {
1593                 // expected
1594             }
1595             assertEquals(maxZoom, mCamera.getParameters().getZoom());
1596 
1597             mCamera.takePicture(mShutterCallback, mRawPictureCallback,
1598                                 mJpegPictureCallback);
1599             waitForSnapshotDone();
1600         }
1601 
1602         terminateMessageLooper();
1603     }
1604 
1605     @UiThreadTest
1606     @Test
1607     public void testSmoothZoom() throws Exception {
1608         for (int id : mCameraIds) {
1609             Log.v(TAG, "Camera id=" + id);
1610             testSmoothZoomByCamera(id);
1611         }
1612     }
1613 
1614     private void testSmoothZoomByCamera(int id) throws Exception {
1615         initializeMessageLooper(id);
1616 
1617         Parameters parameters = mCamera.getParameters();
1618         if (!parameters.isSmoothZoomSupported()) {
1619             terminateMessageLooper();
1620             return;
1621         }
1622         assertTrue(parameters.isZoomSupported());
1623 
1624         ZoomListener zoomListener = new ZoomListener();
1625         mCamera.setZoomChangeListener(zoomListener);
1626         mCamera.startPreview();
1627         waitForPreviewDone();
1628 
1629         // Immediate zoom should not generate callbacks.
1630         int maxZoom = parameters.getMaxZoom();
1631         parameters.setZoom(maxZoom);
1632         mCamera.setParameters(parameters);
1633         assertEquals(maxZoom, mCamera.getParameters().getZoom());
1634         parameters.setZoom(0);
1635         mCamera.setParameters(parameters);
1636         assertEquals(0, mCamera.getParameters().getZoom());
1637         assertFalse(zoomListener.mZoomDone.block(500));
1638 
1639         // Nothing will happen if zoom is not moving.
1640         mCamera.stopSmoothZoom();
1641 
1642         // It should not generate callbacks if zoom value is not changed.
1643         mCamera.startSmoothZoom(0);
1644         assertFalse(zoomListener.mZoomDone.block(500));
1645         assertEquals(0, mCamera.getParameters().getZoom());
1646 
1647         // Test startSmoothZoom.
1648         mCamera.startSmoothZoom(maxZoom);
1649         assertEquals(true, zoomListener.mZoomDone.block(5000));
1650         assertEquals(maxZoom, mCamera.getParameters().getZoom());
1651         assertEquals(maxZoom, zoomListener.mValues.size());
1652         for(int i = 0; i < maxZoom; i++) {
1653             int value = zoomListener.mValues.get(i);
1654             boolean stopped = zoomListener.mStopped.get(i);
1655             // Make sure we get all the zoom values in order.
1656             assertEquals(i + 1, value);
1657             // All "stopped" except the last should be false.
1658             assertEquals(i == maxZoom - 1, stopped);
1659         }
1660 
1661         // Test startSmoothZoom. Make sure we get all the callbacks.
1662         if (maxZoom > 1) {
1663             zoomListener.mValues.clear();
1664             zoomListener.mStopped.clear();
1665             Log.e(TAG, "zoomListener.mStopped = " + zoomListener.mStopped);
1666             zoomListener.mZoomDone.close();
1667             mCamera.startSmoothZoom(maxZoom / 2);
1668             assertTrue(zoomListener.mZoomDone.block(5000));
1669             assertEquals(maxZoom / 2, mCamera.getParameters().getZoom());
1670             assertEquals(maxZoom - (maxZoom / 2), zoomListener.mValues.size());
1671             for(int i = 0; i < zoomListener.mValues.size(); i++) {
1672                 int value = zoomListener.mValues.get(i);
1673                 boolean stopped = zoomListener.mStopped.get(i);
1674                 // Make sure we get all the zoom values in order.
1675                 assertEquals(maxZoom - 1 - i, value);
1676                 // All "stopped" except the last should be false.
1677                 assertEquals(i == zoomListener.mValues.size() - 1, stopped);
1678             }
1679         }
1680 
1681         // It should throw exception if an invalid value is passed.
1682         try {
1683             mCamera.startSmoothZoom(maxZoom + 1);
1684             fail("startSmoothZoom should throw exception.");
1685         } catch (IllegalArgumentException e) {
1686             // expected
1687         }
1688 
1689         // Test stopSmoothZoom.
1690         zoomListener.mValues.clear();
1691         zoomListener.mStopped.clear();
1692         zoomListener.mZoomDone.close();
1693         parameters.setZoom(0);
1694         mCamera.setParameters(parameters);
1695         assertEquals(0, mCamera.getParameters().getZoom());
1696         mCamera.startSmoothZoom(maxZoom);
1697         mCamera.stopSmoothZoom();
1698         assertTrue(zoomListener.mZoomDone.block(5000));
1699         assertEquals(zoomListener.mValues.size(), mCamera.getParameters().getZoom());
1700         for(int i = 0; i < zoomListener.mValues.size() - 1; i++) {
1701             int value = zoomListener.mValues.get(i);
1702             boolean stopped = zoomListener.mStopped.get(i);
1703             // Make sure we get all the callbacks in order (except the last).
1704             assertEquals(i + 1, value);
1705             // All "stopped" except the last should be false. stopSmoothZoom has been called. So the
1706             // last "stopped" can be true or false.
1707             if (i != zoomListener.mValues.size() - 1) {
1708                 assertFalse(stopped);
1709             }
1710         }
1711 
1712         terminateMessageLooper();
1713     }
1714 
1715     private final class ZoomListener
1716             implements android.hardware.Camera.OnZoomChangeListener {
1717         public ArrayList<Integer> mValues = new ArrayList<Integer>();
1718         public ArrayList<Boolean> mStopped = new ArrayList<Boolean>();
1719         public final ConditionVariable mZoomDone = new ConditionVariable();
1720 
1721         public void onZoomChange(int value, boolean stopped, Camera camera) {
1722             mValues.add(value);
1723             mStopped.add(stopped);
1724             if (stopped) {
1725                 mZoomDone.open();
1726             }
1727         }
1728     }
1729 
1730     @UiThreadTest
1731     @Test
1732     public void testFocusDistances() throws Exception {
1733         for (int id : mCameraIds) {
1734             Log.v(TAG, "Camera id=" + id);
1735             testFocusDistancesByCamera(id);
1736         }
1737     }
1738 
1739     private void testFocusDistancesByCamera(int cameraId) throws Exception {
1740         initializeMessageLooper(cameraId);
1741         blockingStartPreview();
1742 
1743         Parameters parameters = mCamera.getParameters();
1744 
1745         // Test every supported focus mode.
1746         for (String focusMode: parameters.getSupportedFocusModes()) {
1747             parameters.setFocusMode(focusMode);
1748             mCamera.setParameters(parameters);
1749             parameters = mCamera.getParameters();
1750             assertEquals(focusMode, parameters.getFocusMode());
1751             checkFocusDistances(parameters);
1752             if (Parameters.FOCUS_MODE_AUTO.equals(focusMode)
1753                     || Parameters.FOCUS_MODE_MACRO.equals(focusMode)
1754                     || Parameters.FOCUS_MODE_CONTINUOUS_VIDEO.equals(focusMode)
1755                     || Parameters.FOCUS_MODE_CONTINUOUS_PICTURE.equals(focusMode)) {
1756                 Log.v(TAG, "Focus mode=" + focusMode);
1757                 mCamera.autoFocus(mAutoFocusCallback);
1758                 assertTrue(waitForFocusDone());
1759                 parameters = mCamera.getParameters();
1760                 checkFocusDistances(parameters);
1761                 float[] initialFocusDistances = new float[3];
1762                 parameters.getFocusDistances(initialFocusDistances);
1763 
1764                 // Focus position should not change after autoFocus call.
1765                 // Continuous autofocus should have stopped. Sleep some time and
1766                 // check. Make sure continuous autofocus is not working. If the
1767                 // focus mode is auto or macro, it is no harm to do the extra
1768                 // test.
1769                 Thread.sleep(500);
1770                 parameters = mCamera.getParameters();
1771                 float[] currentFocusDistances = new float[3];
1772                 parameters.getFocusDistances(currentFocusDistances);
1773                 assertEquals(initialFocusDistances, currentFocusDistances);
1774 
1775                 // Focus position should not change after stopping preview.
1776                 mCamera.stopPreview();
1777                 parameters = mCamera.getParameters();
1778                 parameters.getFocusDistances(currentFocusDistances);
1779                 assertEquals(initialFocusDistances, currentFocusDistances);
1780 
1781                 // Focus position should not change after taking a picture.
1782                 mCamera.startPreview();
1783                 mCamera.takePicture(mShutterCallback, mRawPictureCallback, mJpegPictureCallback);
1784                 waitForSnapshotDone();
1785                 parameters = mCamera.getParameters();
1786                 parameters.getFocusDistances(currentFocusDistances);
1787                 assertEquals(initialFocusDistances, currentFocusDistances);
1788                 mCamera.startPreview();
1789             }
1790         }
1791 
1792         // Test if the method throws exception if the argument is invalid.
1793         try {
1794             parameters.getFocusDistances(null);
1795             fail("getFocusDistances should not accept null.");
1796         } catch (IllegalArgumentException e) {
1797             // expected
1798         }
1799 
1800         try {
1801             parameters.getFocusDistances(new float[2]);
1802             fail("getFocusDistances should not accept a float array with two elements.");
1803         } catch (IllegalArgumentException e) {
1804             // expected
1805         }
1806 
1807         try {
1808             parameters.getFocusDistances(new float[4]);
1809             fail("getFocusDistances should not accept a float array with four elements.");
1810         } catch (IllegalArgumentException e) {
1811             // expected
1812         }
1813         terminateMessageLooper();
1814     }
1815 
1816     private void checkFocusDistances(Parameters parameters) {
1817         float[] distances = new float[3];
1818         parameters.getFocusDistances(distances);
1819 
1820         // Focus distances should be greater than 0.
1821         assertTrue(distances[Parameters.FOCUS_DISTANCE_NEAR_INDEX] > 0);
1822         assertTrue(distances[Parameters.FOCUS_DISTANCE_OPTIMAL_INDEX] > 0);
1823         assertTrue(distances[Parameters.FOCUS_DISTANCE_FAR_INDEX] > 0);
1824 
1825         // Make sure far focus distance >= optimal focus distance >= near focus distance.
1826         assertTrue(distances[Parameters.FOCUS_DISTANCE_FAR_INDEX] >=
1827                    distances[Parameters.FOCUS_DISTANCE_OPTIMAL_INDEX]);
1828         assertTrue(distances[Parameters.FOCUS_DISTANCE_OPTIMAL_INDEX] >=
1829                    distances[Parameters.FOCUS_DISTANCE_NEAR_INDEX]);
1830 
1831         // Far focus distance should be infinity in infinity focus mode.
1832         if (Parameters.FOCUS_MODE_INFINITY.equals(parameters.getFocusMode())) {
1833             assertEquals(Float.POSITIVE_INFINITY,
1834                          distances[Parameters.FOCUS_DISTANCE_FAR_INDEX]);
1835         }
1836     }
1837 
1838     @UiThreadTest
1839     @Test
testCancelAutofocus()1840     public void testCancelAutofocus() throws Exception {
1841         for (int id : mCameraIds) {
1842             Log.v(TAG, "Camera id=" + id);
1843             testCancelAutofocusByCamera(id);
1844         }
1845     }
1846 
testCancelAutofocusByCamera(int cameraId)1847     private void testCancelAutofocusByCamera(int cameraId) throws Exception {
1848         initializeMessageLooper(cameraId);
1849         Parameters parameters = mCamera.getParameters();
1850         List<String> focusModes = parameters.getSupportedFocusModes();
1851 
1852         if (focusModes.contains(Parameters.FOCUS_MODE_AUTO)) {
1853             parameters.setFocusMode(Parameters.FOCUS_MODE_AUTO);
1854         } else if (focusModes.contains(Parameters.FOCUS_MODE_MACRO)) {
1855             parameters.setFocusMode(Parameters.FOCUS_MODE_MACRO);
1856         } else {
1857             terminateMessageLooper();
1858             return;
1859         }
1860 
1861         mCamera.setParameters(parameters);
1862 
1863         // Valid to call outside of preview; should just reset lens or
1864         // be a no-op.
1865         mCamera.cancelAutoFocus();
1866 
1867         mCamera.startPreview();
1868 
1869         // No op if autofocus is not in progress.
1870         mCamera.cancelAutoFocus();
1871 
1872         // Try to cancel autofocus immediately.
1873         mCamera.autoFocus(mAutoFocusCallback);
1874         mCamera.cancelAutoFocus();
1875         checkFocusDistanceNotChanging();
1876 
1877         // Try to cancel autofocus after it starts for some time.
1878         mCamera.autoFocus(mAutoFocusCallback);
1879         Thread.sleep(500);
1880         mCamera.cancelAutoFocus();
1881         checkFocusDistanceNotChanging();
1882 
1883         // Try to cancel autofocus after it completes. It should be no op.
1884         mCamera.autoFocus(mAutoFocusCallback);
1885         assertTrue(waitForFocusDone());
1886         mCamera.cancelAutoFocus();
1887 
1888         // Test the case calling cancelAutoFocus and release in a row.
1889         mCamera.autoFocus(mAutoFocusCallback);
1890         mCamera.cancelAutoFocus();
1891         mCamera.release();
1892 
1893         // Ensure the camera can be opened if release is called right after AF.
1894         mCamera = Camera.open(cameraId);
1895         mCamera.setPreviewDisplay(mActivityRule.getActivity().getSurfaceView().getHolder());
1896         mCamera.startPreview();
1897         mCamera.autoFocus(mAutoFocusCallback);
1898         mCamera.release();
1899 
1900         terminateMessageLooper();
1901     }
1902 
checkFocusDistanceNotChanging()1903     private void checkFocusDistanceNotChanging() throws Exception {
1904         float[] distances1 = new float[3];
1905         float[] distances2 = new float[3];
1906         Parameters parameters = mCamera.getParameters();
1907         parameters.getFocusDistances(distances1);
1908         Thread.sleep(100);
1909         parameters = mCamera.getParameters();
1910         parameters.getFocusDistances(distances2);
1911         assertEquals(distances1[Parameters.FOCUS_DISTANCE_NEAR_INDEX],
1912                      distances2[Parameters.FOCUS_DISTANCE_NEAR_INDEX]);
1913         assertEquals(distances1[Parameters.FOCUS_DISTANCE_OPTIMAL_INDEX],
1914                      distances2[Parameters.FOCUS_DISTANCE_OPTIMAL_INDEX]);
1915         assertEquals(distances1[Parameters.FOCUS_DISTANCE_FAR_INDEX],
1916                      distances2[Parameters.FOCUS_DISTANCE_FAR_INDEX]);
1917     }
1918 
1919     @UiThreadTest
1920     @Test
testMultipleCameras()1921     public void testMultipleCameras() throws Exception {
1922         if (CameraUtils.getOverrideCameraId() != null) {
1923             // A single camera is being tested. Skip.
1924             return;
1925         }
1926 
1927         int nCameras = Camera.getNumberOfCameras();
1928         Log.v(TAG, "total " + nCameras + " cameras");
1929         assertTrue(nCameras >= 0);
1930 
1931         boolean backCameraExist = false;
1932         CameraInfo info = new CameraInfo();
1933         for (int i = 0; i < nCameras; i++) {
1934             Camera.getCameraInfo(i, info);
1935             if (info.facing == CameraInfo.CAMERA_FACING_BACK) {
1936                 backCameraExist = true;
1937                 break;
1938             }
1939         }
1940         // Make sure original open still works. It must return a back-facing
1941         // camera.
1942         mCamera = Camera.open();
1943         if (mCamera != null) {
1944             mCamera.release();
1945             assertTrue(backCameraExist);
1946         } else {
1947             assertFalse(backCameraExist);
1948         }
1949 
1950         for (int id = -1; id <= nCameras; id++) {
1951             Log.v(TAG, "testing camera #" + id);
1952 
1953             boolean isBadId = (id < 0 || id >= nCameras);
1954 
1955             try {
1956                 Camera.getCameraInfo(id, info);
1957                 if (isBadId) {
1958                     fail("getCameraInfo should not accept bad cameraId (" + id + ")");
1959                 }
1960             } catch (RuntimeException e) {
1961                 if (!isBadId) throw e;
1962             }
1963 
1964             int facing = info.facing;
1965             int orientation = info.orientation;
1966             assertTrue(facing == CameraInfo.CAMERA_FACING_BACK ||
1967                        facing == CameraInfo.CAMERA_FACING_FRONT);
1968             assertTrue(orientation == 0 || orientation == 90 ||
1969                        orientation == 180 || orientation == 270);
1970 
1971             Camera camera = null;
1972             try {
1973                 camera = Camera.open(id);
1974                 if (isBadId) {
1975                     fail("open() should not accept bad cameraId (" + id + ")");
1976                 }
1977             } catch (RuntimeException e) {
1978                 if (!isBadId) throw e;
1979             } finally {
1980                 if (camera != null) {
1981                     camera.release();
1982                 }
1983             }
1984         }
1985     }
1986 
1987     @UiThreadTest
1988     @Test(timeout=60*60*1000) // timeout = 60 mins for long running tests
testPreviewPictureSizesCombination()1989     public void testPreviewPictureSizesCombination() throws Exception {
1990         for (int id : mCameraIds) {
1991             Log.v(TAG, "Camera id=" + id);
1992             testPreviewPictureSizesCombinationByCamera(id);
1993         }
1994     }
1995 
1996     // API exception on QCIF size. QCIF size along with anything larger than
1997     // 1920x1080 on either width/height is not guaranteed to be supported.
isWaivedCombination(Size previewSize, Size pictureSize)1998     private boolean isWaivedCombination(Size previewSize, Size pictureSize) {
1999         Size QCIF = mCamera.new Size(176, 144);
2000         Size FULL_HD = mCamera.new Size(1920, 1080);
2001         if (previewSize.equals(QCIF) && (pictureSize.width > FULL_HD.width ||
2002                 pictureSize.height > FULL_HD.height)) {
2003             return true;
2004         }
2005         if (pictureSize.equals(QCIF) && (previewSize.width > FULL_HD.width ||
2006                 previewSize.height > FULL_HD.height)) {
2007             return true;
2008         }
2009         return false;
2010     }
2011 
testPreviewPictureSizesCombinationByCamera(int cameraId)2012     private void testPreviewPictureSizesCombinationByCamera(int cameraId) throws Exception {
2013         initializeMessageLooper(cameraId);
2014         Parameters parameters = mCamera.getParameters();
2015         PreviewCbForPreviewPictureSizesCombination callback =
2016             new PreviewCbForPreviewPictureSizesCombination();
2017 
2018         // Test all combination of preview sizes and picture sizes.
2019         for (Size previewSize: parameters.getSupportedPreviewSizes()) {
2020             for (Size pictureSize: parameters.getSupportedPictureSizes()) {
2021                 Log.v(TAG, "Test previewSize=(" + previewSize.width + "," +
2022                         previewSize.height + ") pictureSize=(" +
2023                         pictureSize.width + "," + pictureSize.height + ")");
2024                 mPreviewCallbackResult = PREVIEW_CALLBACK_NOT_RECEIVED;
2025                 mCamera.setPreviewCallback(callback);
2026                 callback.expectedPreviewSize = previewSize;
2027                 parameters.setPreviewSize(previewSize.width, previewSize.height);
2028                 parameters.setPictureSize(pictureSize.width, pictureSize.height);
2029                 try {
2030                     mCamera.setParameters(parameters);
2031                 } catch (RuntimeException e) {
2032                     if (isWaivedCombination(previewSize, pictureSize)) {
2033                         Log.i(TAG, String.format("Preview %dx%d and still %dx%d combination is" +
2034                                 "waived", previewSize.width, previewSize.height,
2035                                 pictureSize.width, pictureSize.height));
2036                         continue;
2037                     }
2038                     throw e;
2039                 }
2040 
2041                 assertEquals(previewSize, mCamera.getParameters().getPreviewSize());
2042                 assertEquals(pictureSize, mCamera.getParameters().getPictureSize());
2043 
2044                 // Check if the preview size is the same as requested.
2045                 try {
2046                     mCamera.startPreview();
2047                 } catch (RuntimeException e) {
2048                     if (isWaivedCombination(previewSize, pictureSize)) {
2049                         Log.i(TAG, String.format("Preview %dx%d and still %dx%d combination is" +
2050                                 "waived", previewSize.width, previewSize.height,
2051                                 pictureSize.width, pictureSize.height));
2052                         continue;
2053                     }
2054                     throw e;
2055                 }
2056                 waitForPreviewDone();
2057                 assertEquals(PREVIEW_CALLBACK_RECEIVED, mPreviewCallbackResult);
2058 
2059                 // Check if the picture size is the same as requested.
2060                 mCamera.takePicture(mShutterCallback, mRawPictureCallback, mJpegPictureCallback);
2061                 waitForSnapshotDone();
2062                 assertTrue(mJpegPictureCallbackResult);
2063                 assertNotNull(mJpegData);
2064                 BitmapFactory.Options bmpOptions = new BitmapFactory.Options();
2065                 bmpOptions.inJustDecodeBounds = true;
2066                 BitmapFactory.decodeByteArray(mJpegData, 0, mJpegData.length, bmpOptions);
2067                 assertEquals(pictureSize.width, bmpOptions.outWidth);
2068                 assertEquals(pictureSize.height, bmpOptions.outHeight);
2069             }
2070         }
2071         terminateMessageLooper();
2072     }
2073 
2074     private final class PreviewCbForPreviewPictureSizesCombination
2075             implements android.hardware.Camera.PreviewCallback {
2076         public Size expectedPreviewSize;
onPreviewFrame(byte[] data, Camera camera)2077         public void onPreviewFrame(byte[] data, Camera camera) {
2078             if (data == null) {
2079                 mPreviewCallbackResult = PREVIEW_CALLBACK_DATA_NULL;
2080                 mPreviewDone.open();
2081                 return;
2082             }
2083             Size size = camera.getParameters().getPreviewSize();
2084             int format = camera.getParameters().getPreviewFormat();
2085             int bitsPerPixel = ImageFormat.getBitsPerPixel(format);
2086             if (!expectedPreviewSize.equals(size) ||
2087                     calculateBufferSize(size.width, size.height,
2088                         format, bitsPerPixel) != data.length) {
2089                 Log.e(TAG, "Expected preview width=" + expectedPreviewSize.width + ", height="
2090                         + expectedPreviewSize.height + ". Actual width=" + size.width + ", height="
2091                         + size.height);
2092                 Log.e(TAG, "Frame data length=" + data.length + ". bitsPerPixel=" + bitsPerPixel);
2093                 mPreviewCallbackResult = PREVIEW_CALLBACK_INVALID_FRAME_SIZE;
2094                 mPreviewDone.open();
2095                 return;
2096             }
2097             camera.setPreviewCallback(null);
2098             mPreviewCallbackResult = PREVIEW_CALLBACK_RECEIVED;
2099             mPreviewDone.open();
2100         }
2101     }
2102 
2103     @UiThreadTest
2104     @Test
testPreviewFpsRange()2105     public void testPreviewFpsRange() throws Exception {
2106         for (int id : mCameraIds) {
2107             Log.v(TAG, "Camera id=" + id);
2108             testPreviewFpsRangeByCamera(id);
2109         }
2110     }
2111 
testPreviewFpsRangeByCamera(int cameraId)2112     private void testPreviewFpsRangeByCamera(int cameraId) throws Exception {
2113         initializeMessageLooper(cameraId);
2114 
2115         // Test if the parameters exists and minimum fps <= maximum fps.
2116         final int INTERVAL_ERROR_THRESHOLD = 10;
2117         int[] defaultFps = new int[2];
2118         Parameters parameters = mCamera.getParameters();
2119         parameters.getPreviewFpsRange(defaultFps);
2120         List<int[]> fpsList = parameters.getSupportedPreviewFpsRange();
2121         assertTrue(fpsList.size() > 0);
2122         boolean found = false;
2123         for(int[] fps: fpsList) {
2124             assertTrue(fps[Parameters.PREVIEW_FPS_MIN_INDEX] > 0);
2125             assertTrue(fps[Parameters.PREVIEW_FPS_MIN_INDEX] <=
2126                        fps[Parameters.PREVIEW_FPS_MAX_INDEX]);
2127             if (!found && Arrays.equals(defaultFps, fps)) {
2128                 found = true;
2129             }
2130         }
2131         assertTrue("Preview fps range must be in the supported list.", found);
2132 
2133         // Test if the list is properly sorted.
2134         for (int i = 0; i < fpsList.size() - 1; i++) {
2135             int minFps1 = fpsList.get(i)[Parameters.PREVIEW_FPS_MIN_INDEX];
2136             int maxFps1 = fpsList.get(i)[Parameters.PREVIEW_FPS_MAX_INDEX];
2137             int minFps2 = fpsList.get(i + 1)[Parameters.PREVIEW_FPS_MIN_INDEX];
2138             int maxFps2 = fpsList.get(i + 1)[Parameters.PREVIEW_FPS_MAX_INDEX];
2139             assertTrue(maxFps1 < maxFps2
2140                     || (maxFps1 == maxFps2 && minFps1 < minFps2));
2141         }
2142 
2143         // Test if the actual fps is within fps range.
2144         Size size = parameters.getPreviewSize();
2145         int format = mCamera.getParameters().getPreviewFormat();
2146         int bitsPerPixel = ImageFormat.getBitsPerPixel(format);
2147         byte[] buffer1 = new byte[size.width * size.height * bitsPerPixel / 8];
2148         byte[] buffer2 = new byte[size.width * size.height * bitsPerPixel / 8];
2149         byte[] buffer3 = new byte[size.width * size.height * bitsPerPixel / 8];
2150         FpsRangePreviewCb callback = new FpsRangePreviewCb();
2151         int[] readBackFps = new int[2];
2152         for (int[] fps: fpsList) {
2153             parameters = mCamera.getParameters();
2154             parameters.setPreviewFpsRange(fps[Parameters.PREVIEW_FPS_MIN_INDEX],
2155                                           fps[Parameters.PREVIEW_FPS_MAX_INDEX]);
2156             callback.reset(fps[Parameters.PREVIEW_FPS_MIN_INDEX] / 1000.0,
2157                            fps[Parameters.PREVIEW_FPS_MAX_INDEX] / 1000.0);
2158             mCamera.setParameters(parameters);
2159             parameters = mCamera.getParameters();
2160             parameters.getPreviewFpsRange(readBackFps);
2161             MoreAsserts.assertEquals(fps, readBackFps);
2162             mCamera.addCallbackBuffer(buffer1);
2163             mCamera.addCallbackBuffer(buffer2);
2164             mCamera.addCallbackBuffer(buffer3);
2165             mCamera.setPreviewCallbackWithBuffer(callback);
2166             mCamera.startPreview();
2167             try {
2168                 // Test the frame rate for a while.
2169                 Thread.sleep(3000);
2170             } catch(Exception e) {
2171                 // ignore
2172             }
2173             mCamera.stopPreview();
2174             // See if any frame duration violations occurred during preview run
2175             AssertionFailedError e = callback.getDurationException();
2176             if (e != null) throw(e);
2177             int numIntervalError = callback.getNumIntervalError();
2178             if (numIntervalError > INTERVAL_ERROR_THRESHOLD) {
2179                 fail(String.format(
2180                         "Too many preview callback frame intervals out of bounds: " +
2181                                 "Count is %d, limit is %d",
2182                         numIntervalError, INTERVAL_ERROR_THRESHOLD));
2183             }
2184         }
2185 
2186         // Test the invalid fps cases.
2187         parameters = mCamera.getParameters();
2188         parameters.setPreviewFpsRange(-1, -1);
2189         try {
2190             mCamera.setParameters(parameters);
2191             fail("Should throw an exception if fps range is negative.");
2192         } catch (RuntimeException e) {
2193             // expected
2194         }
2195         parameters.setPreviewFpsRange(10, 5);
2196         try {
2197             mCamera.setParameters(parameters);
2198             fail("Should throw an exception if fps range is invalid.");
2199         } catch (RuntimeException e) {
2200             // expected
2201         }
2202 
2203         terminateMessageLooper();
2204     }
2205 
2206     private final class FpsRangePreviewCb
2207             implements android.hardware.Camera.PreviewCallback {
2208         private double mMinFps, mMaxFps, mMaxFrameInterval, mMinFrameInterval;
2209         // An array storing the arrival time of the frames in the last second.
2210         private ArrayList<Long> mFrames = new ArrayList<Long>();
2211         private long firstFrameArrivalTime;
2212         private AssertionFailedError mDurationException = null;
2213         private int numIntervalError;
2214 
2215         public void reset(double minFps, double maxFps) {
2216             this.mMinFps = minFps;
2217             this.mMaxFps = maxFps;
2218             mMaxFrameInterval = 1000.0 / mMinFps;
2219             mMinFrameInterval = 1000.0 / mMaxFps;
2220             Log.v(TAG, "Min fps=" + mMinFps + ". Max fps=" + mMaxFps
2221                     + ". Min frame interval=" + mMinFrameInterval
2222                     + ". Max frame interval=" + mMaxFrameInterval);
2223             mFrames.clear();
2224             firstFrameArrivalTime = 0;
2225             mDurationException = null;
2226             numIntervalError = 0;
2227         }
2228 
2229         // This method tests if the actual fps is between minimum and maximum.
2230         // It also tests if the frame interval is too long.
2231         public void onPreviewFrame(byte[] data, android.hardware.Camera camera) {
2232             long arrivalTime = SystemClock.elapsedRealtime();
2233             camera.addCallbackBuffer(data);
2234             if (firstFrameArrivalTime == 0) firstFrameArrivalTime = arrivalTime;
2235 
2236             // Remove the frames that arrived before the last second.
2237             Iterator<Long> it = mFrames.iterator();
2238             while(it.hasNext()) {
2239                 long time = it.next();
2240                 if (arrivalTime - time > 1000 && mFrames.size() > 2) {
2241                     it.remove();
2242                 } else {
2243                     break;
2244                 }
2245             }
2246 
2247             // Start the test after one second.
2248             if (arrivalTime - firstFrameArrivalTime > 1000) {
2249                 assertTrue(mFrames.size() >= 2);
2250 
2251                 // Check the frame interval and fps. The interval check
2252                 // considers the time variance passing frames from the camera
2253                 // hardware to the callback. It should be a constant time, not a
2254                 // ratio. The fps check is more strict because individual
2255                 // variance is averaged out.
2256 
2257                 // Check if the frame interval is too large or too small.
2258                 // x100 = percent, intervalMargin should be bigger than
2259                 // fpsMargin considering that fps will be in the order of 10.
2260                 double intervalMargin = 0.9;
2261                 if (mIsExternalCamera) {
2262                     intervalMargin = 0.8;
2263                 }
2264                 long lastArrivalTime = mFrames.get(mFrames.size() - 1);
2265                 double interval = arrivalTime - lastArrivalTime;
2266                 if (VERBOSE) Log.v(TAG, "Frame interval=" + interval);
2267 
2268                 try {
2269                     if (interval > mMaxFrameInterval * (1.0 + intervalMargin) ||
2270                             interval < mMinFrameInterval * (1.0 - intervalMargin)) {
2271                         Log.i(TAG, "Bad frame interval=" + interval + "ms. Out out range " +
2272                                 mMinFrameInterval * (1.0 - intervalMargin) + "/" +
2273                                 mMaxFrameInterval * (1.0 + intervalMargin));
2274                         numIntervalError++;
2275                     }
2276                     // Check if the fps is within range.
2277                     double fpsMargin = 0.5; // x100 = percent
2278                     if (mIsExternalCamera) {
2279                         fpsMargin = 0.6;
2280                     }
2281                     double avgInterval = (double)(arrivalTime - mFrames.get(0))
2282                             / mFrames.size();
2283                     double fps = 1000.0 / avgInterval;
2284                     assertTrue("Actual fps (" + fps + ") should be larger " +
2285                             "than min fps (" + mMinFps + ")",
2286                             fps >= mMinFps * (1.0 - fpsMargin));
2287                     assertTrue("Actual fps (" + fps + ") should be smaller" +
2288                             "than max fps (" + mMaxFps + ")",
2289                             fps <= mMaxFps * (1.0 + fpsMargin));
2290                 } catch (AssertionFailedError e) {
2291                     // Need to throw this only in the test body, instead of in
2292                     // the callback
2293                     if (mDurationException == null) {
2294                         mDurationException = e;
2295                     }
2296                 }
2297             }
2298             // Add the arrival time of this frame to the list.
2299             mFrames.add(arrivalTime);
2300         }
2301 
getDurationException()2302         public AssertionFailedError getDurationException() {
2303             return mDurationException;
2304         }
getNumIntervalError()2305         public int getNumIntervalError() {
2306             return numIntervalError;
2307         }
2308     }
2309 
assertEquals(Size expected, Size actual)2310     private void assertEquals(Size expected, Size actual) {
2311         assertEquals(expected.width, actual.width);
2312         assertEquals(expected.height, actual.height);
2313     }
2314 
assertEquals(float[] expected, float[] actual)2315     private void assertEquals(float[] expected, float[] actual) {
2316         assertEquals(expected.length, actual.length);
2317         for (int i = 0; i < expected.length; i++) {
2318             assertEquals(expected[i], actual[i], 0.000001f);
2319         }
2320     }
2321 
assertNoLetters(String value, String key)2322     private void assertNoLetters(String value, String key) {
2323         for (int i = 0; i < value.length(); i++) {
2324             char c = value.charAt(i);
2325             assertFalse("Parameter contains invalid characters. key,value=("
2326                     + key + "," + value + ")",
2327                     Character.isLetter(c) && c != 'x');
2328         }
2329     }
2330 
2331     @UiThreadTest
2332     @Test
testSceneMode()2333     public void testSceneMode() throws Exception {
2334         for (int id : mCameraIds) {
2335             Log.v(TAG, "Camera id=" + id);
2336             testSceneModeByCamera(id);
2337         }
2338     }
2339 
2340     private class SceneModeSettings {
2341         public String mScene, mFlash, mFocus, mWhiteBalance;
2342         public List<String> mSupportedFlash, mSupportedFocus, mSupportedWhiteBalance;
2343 
SceneModeSettings(Parameters parameters)2344         public SceneModeSettings(Parameters parameters) {
2345             mScene = parameters.getSceneMode();
2346             mFlash = parameters.getFlashMode();
2347             mFocus = parameters.getFocusMode();
2348             mWhiteBalance = parameters.getWhiteBalance();
2349             mSupportedFlash = parameters.getSupportedFlashModes();
2350             mSupportedFocus = parameters.getSupportedFocusModes();
2351             mSupportedWhiteBalance = parameters.getSupportedWhiteBalance();
2352         }
2353     }
2354 
testSceneModeByCamera(int cameraId)2355     private void testSceneModeByCamera(int cameraId) throws Exception {
2356         initializeMessageLooper(cameraId);
2357         Parameters parameters = mCamera.getParameters();
2358         List<String> supportedSceneModes = parameters.getSupportedSceneModes();
2359         if (supportedSceneModes != null) {
2360             assertEquals(Parameters.SCENE_MODE_AUTO, parameters.getSceneMode());
2361             SceneModeSettings autoSceneMode = new SceneModeSettings(parameters);
2362 
2363             // Store all scene mode affected settings.
2364             SceneModeSettings[] settings = new SceneModeSettings[supportedSceneModes.size()];
2365             for (int i = 0; i < supportedSceneModes.size(); i++) {
2366                 parameters.setSceneMode(supportedSceneModes.get(i));
2367                 mCamera.setParameters(parameters);
2368                 parameters = mCamera.getParameters();
2369                 settings[i] = new SceneModeSettings(parameters);
2370             }
2371 
2372             // Make sure scene mode settings are consistent before preview and
2373             // after preview.
2374             blockingStartPreview();
2375             for (int i = 0; i < supportedSceneModes.size(); i++) {
2376                 String sceneMode = supportedSceneModes.get(i);
2377                 parameters.setSceneMode(sceneMode);
2378                 mCamera.setParameters(parameters);
2379                 parameters = mCamera.getParameters();
2380 
2381                 // In auto scene mode, camera HAL will not remember the previous
2382                 // flash, focus, and white-balance. It will just take values set
2383                 // by parameters. But the supported flash, focus, and
2384                 // white-balance should still be restored in auto scene mode.
2385                 if (!Parameters.SCENE_MODE_AUTO.equals(sceneMode)) {
2386                     assertEquals("Flash is inconsistent in scene mode " + sceneMode,
2387                             settings[i].mFlash, parameters.getFlashMode());
2388                     assertEquals("Focus is inconsistent in scene mode " + sceneMode,
2389                             settings[i].mFocus, parameters.getFocusMode());
2390                     assertEquals("White balance is inconsistent in scene mode " + sceneMode,
2391                             settings[i].mWhiteBalance, parameters.getWhiteBalance());
2392                 }
2393                 assertEquals("Suppported flash modes are inconsistent in scene mode " + sceneMode,
2394                         settings[i].mSupportedFlash, parameters.getSupportedFlashModes());
2395                 assertEquals("Suppported focus modes are inconsistent in scene mode " + sceneMode,
2396                         settings[i].mSupportedFocus, parameters.getSupportedFocusModes());
2397                 assertEquals("Suppported white balance are inconsistent in scene mode " + sceneMode,
2398                         settings[i].mSupportedWhiteBalance, parameters.getSupportedWhiteBalance());
2399             }
2400 
2401             for (int i = 0; i < settings.length; i++) {
2402                 if (Parameters.SCENE_MODE_AUTO.equals(settings[i].mScene)) continue;
2403 
2404                 // Both the setting and the supported settings may change. It is
2405                 // allowed to have more than one supported settings in scene
2406                 // modes. For example, in night scene mode, supported flash
2407                 // modes can have on and off.
2408                 if (autoSceneMode.mSupportedFlash != null) {
2409                     assertTrue(settings[i].mSupportedFlash.contains(settings[i].mFlash));
2410                     for (String mode: settings[i].mSupportedFlash) {
2411                         assertTrue(autoSceneMode.mSupportedFlash.contains(mode));
2412                     }
2413                 }
2414                 if (autoSceneMode.mSupportedFocus != null) {
2415                     assertTrue(settings[i].mSupportedFocus.contains(settings[i].mFocus));
2416                     for (String mode: settings[i].mSupportedFocus) {
2417                         assertTrue(autoSceneMode.mSupportedFocus.contains(mode));
2418                     }
2419                 }
2420                 if (autoSceneMode.mSupportedWhiteBalance != null) {
2421                     assertTrue(settings[i].mSupportedWhiteBalance.contains(settings[i].mWhiteBalance));
2422                     for (String mode: settings[i].mSupportedWhiteBalance) {
2423                         assertTrue(autoSceneMode.mSupportedWhiteBalance.contains(mode));
2424                     }
2425                 }
2426             }
2427         }
2428         terminateMessageLooper();
2429     }
2430 
2431     @UiThreadTest
2432     @Test
testInvalidParameters()2433     public void testInvalidParameters() throws Exception {
2434         for (int id : mCameraIds) {
2435             Log.v(TAG, "Camera id=" + id);
2436             testInvalidParametersByCamera(id);
2437         }
2438     }
2439 
testInvalidParametersByCamera(int cameraId)2440     private void testInvalidParametersByCamera(int cameraId) throws Exception {
2441         initializeMessageLooper(cameraId);
2442         // Test flash mode.
2443         Parameters parameters = mCamera.getParameters();
2444         List<String> list = parameters.getSupportedFlashModes();
2445         if (list != null && list.size() > 0) {
2446             String original = parameters.getFlashMode();
2447             parameters.setFlashMode("invalid");
2448             try {
2449                 mCamera.setParameters(parameters);
2450                 fail("Should throw exception for invalid parameters");
2451             } catch (RuntimeException e) {
2452                 // expected
2453             }
2454             parameters = mCamera.getParameters();
2455             assertEquals(original, parameters.getFlashMode());
2456         }
2457 
2458         // Test focus mode.
2459         String originalFocus = parameters.getFocusMode();
2460         parameters.setFocusMode("invalid");
2461         try {
2462             mCamera.setParameters(parameters);
2463             fail("Should throw exception for invalid parameters");
2464         } catch (RuntimeException e) {
2465             // expected
2466         }
2467         parameters = mCamera.getParameters();
2468         assertEquals(originalFocus, parameters.getFocusMode());
2469 
2470         // Test preview size.
2471         Size originalSize = parameters.getPreviewSize();
2472         parameters.setPreviewSize(-1, -1);
2473         try {
2474             mCamera.setParameters(parameters);
2475             fail("Should throw exception for invalid parameters");
2476         } catch (RuntimeException e) {
2477             // expected
2478         }
2479         parameters = mCamera.getParameters();
2480         assertEquals(originalSize, parameters.getPreviewSize());
2481 
2482         terminateMessageLooper();
2483     }
2484 
2485     @UiThreadTest
2486     @Test
testGetParameterDuringFocus()2487     public void testGetParameterDuringFocus() throws Exception {
2488         for (int id : mCameraIds) {
2489             Log.v(TAG, "Camera id=" + id);
2490             testGetParameterDuringFocusByCamera(id);
2491         }
2492     }
2493 
testGetParameterDuringFocusByCamera(int cameraId)2494     private void testGetParameterDuringFocusByCamera(int cameraId) throws Exception {
2495         initializeMessageLooper(cameraId);
2496         mCamera.startPreview();
2497         Parameters parameters = mCamera.getParameters();
2498         for (String focusMode: parameters.getSupportedFocusModes()) {
2499             if (focusMode.equals(parameters.FOCUS_MODE_AUTO)
2500                     || focusMode.equals(parameters.FOCUS_MODE_MACRO)) {
2501                 parameters.setFocusMode(focusMode);
2502                 mCamera.setParameters(parameters);
2503                 mCamera.autoFocus(mAutoFocusCallback);
2504                 // This should not crash or throw exception.
2505                 mCamera.getParameters();
2506                 waitForFocusDone();
2507 
2508 
2509                 mCamera.autoFocus(mAutoFocusCallback);
2510                 // Add a small delay to make sure focus has started.
2511                 Thread.sleep(100);
2512                 // This should not crash or throw exception.
2513                 mCamera.getParameters();
2514                 waitForFocusDone();
2515             }
2516         }
2517         terminateMessageLooper();
2518     }
2519 
2520     @UiThreadTest
2521     @Test
testPreviewFormats()2522     public void testPreviewFormats() throws Exception {
2523         for (int id : mCameraIds) {
2524             Log.v(TAG, "Camera id=" + id);
2525             testPreviewFormatsByCamera(id);
2526         }
2527     }
2528 
testPreviewFormatsByCamera(int cameraId)2529     private void testPreviewFormatsByCamera(int cameraId) throws Exception {
2530         initializeMessageLooper(cameraId);
2531         Parameters parameters = mCamera.getParameters();
2532         for (int format: parameters.getSupportedPreviewFormats()) {
2533             Log.v(TAG, "Test preview format " + format);
2534             parameters.setPreviewFormat(format);
2535             mCamera.setParameters(parameters);
2536             mCamera.setOneShotPreviewCallback(mPreviewCallback);
2537             mCamera.startPreview();
2538             waitForPreviewDone();
2539             assertEquals(PREVIEW_CALLBACK_RECEIVED, mPreviewCallbackResult);
2540         }
2541         terminateMessageLooper();
2542     }
2543 
2544     @UiThreadTest
2545     @Test
testMultiCameraRelease()2546     public void testMultiCameraRelease() throws Exception {
2547         if (CameraUtils.getOverrideCameraId() != null) {
2548             // A single camera is being tested. Skip.
2549             return;
2550         }
2551 
2552         // Verify that multiple cameras exist, and that they can be opened at the same time
2553         if (VERBOSE) Log.v(TAG, "testMultiCameraRelease: Checking pre-conditions.");
2554         int nCameras = Camera.getNumberOfCameras();
2555         if (nCameras < 2) {
2556             Log.i(TAG, "Test multi-camera release: Skipping test because only 1 camera available");
2557             return;
2558         }
2559 
2560         Camera testCamera0 = Camera.open(0);
2561         Camera testCamera1 = null;
2562         try {
2563             testCamera1 = Camera.open(1);
2564         } catch (RuntimeException e) {
2565             // Can't open two cameras at once
2566             Log.i(TAG, "testMultiCameraRelease: Skipping test because only 1 camera "+
2567                   "could be opened at once. Second open threw: " + e);
2568             testCamera0.release();
2569             return;
2570         }
2571         testCamera0.release();
2572         testCamera1.release();
2573 
2574         LooperInfo looperInfo0 = new LooperInfo();
2575         LooperInfo looperInfo1 = new LooperInfo();
2576 
2577         ConditionVariable previewDone0 = new ConditionVariable();
2578         ConditionVariable previewDone1 = new ConditionVariable();
2579         // Open both cameras camera
2580         if (VERBOSE) Log.v(TAG, "testMultiCameraRelease: Opening cameras 0 and 1");
2581         TestErrorCallbackI errorCallback0 = new TestErrorCallbackI();
2582         TestErrorCallbackI errorCallback1 = new TestErrorCallbackI();
2583         initializeMessageLooper(0, errorCallback0, looperInfo0);
2584         initializeMessageLooper(1, errorCallback1, looperInfo1);
2585 
2586         SimplePreviewStreamCbI callback0 = new SimplePreviewStreamCbI(0, previewDone0);
2587         looperInfo0.camera.setPreviewCallback(callback0);
2588         if (VERBOSE) Log.v(TAG, "testMultiCameraRelease: Starting preview on camera 0");
2589         looperInfo0.camera.startPreview();
2590         // Run preview for a bit
2591         for (int f = 0; f < 100; f++) {
2592             previewDone0.close();
2593             assertTrue("testMultiCameraRelease: First camera preview timed out on frame " + f + "!",
2594                        previewDone0.block( WAIT_FOR_COMMAND_TO_COMPLETE));
2595         }
2596         if (VERBOSE) Log.v(TAG, "testMultiCameraRelease: Stopping preview on camera 0");
2597         looperInfo0.camera.stopPreview();
2598 
2599         // Preview surface should be released though!
2600         looperInfo0.camera.setPreviewDisplay(null);
2601 
2602         SimplePreviewStreamCbI callback1 = new SimplePreviewStreamCbI(1, previewDone1);
2603         looperInfo1.camera.setPreviewCallback(callback1);
2604         if (VERBOSE) Log.v(TAG, "testMultiCameraRelease: Starting preview on camera 1");
2605         looperInfo1.camera.startPreview();
2606         for (int f = 0; f < 100; f++) {
2607             previewDone1.close();
2608             assertTrue("testMultiCameraRelease: Second camera preview timed out on frame " + f + "!",
2609                        previewDone1.block( WAIT_FOR_COMMAND_TO_COMPLETE));
2610             if (f == 50) {
2611                 // Release first camera mid-preview, should cause no problems
2612                 if (VERBOSE) Log.v(TAG, "testMultiCameraRelease: Releasing camera 0");
2613                 looperInfo0.camera.release();
2614             }
2615         }
2616         if (VERBOSE) Log.v(TAG, "testMultiCameraRelease: Stopping preview on camera 1");
2617         looperInfo1.camera.stopPreview();
2618 
2619         looperInfo0.looper.quit();
2620         terminateMessageLooper(true, errorCallback1.mCameraErrorCode, looperInfo1);
2621     }
2622 
2623     // This callback just signals on the condition variable, making it useful for checking that
2624     // preview callbacks don't stop unexpectedly
2625     private final class SimplePreviewStreamCb
2626             implements android.hardware.Camera.PreviewCallback {
2627         private int mId;
SimplePreviewStreamCb(int id)2628         public SimplePreviewStreamCb(int id) {
2629             mId = id;
2630         }
onPreviewFrame(byte[] data, android.hardware.Camera camera)2631         public void onPreviewFrame(byte[] data, android.hardware.Camera camera) {
2632             if (VERBOSE) Log.v(TAG, "Preview frame callback, id " + mId + ".");
2633             mPreviewDone.open();
2634         }
2635     }
2636 
2637     // Parent independent version of SimplePreviewStreamCb
2638     private static final class SimplePreviewStreamCbI
2639             implements android.hardware.Camera.PreviewCallback {
2640         private int mId;
2641         private ConditionVariable mPreviewDone = null;
SimplePreviewStreamCbI(int id, ConditionVariable previewDone)2642         public SimplePreviewStreamCbI(int id, ConditionVariable previewDone) {
2643             mId = id;
2644             mPreviewDone = previewDone;
2645         }
onPreviewFrame(byte[] data, android.hardware.Camera camera)2646         public void onPreviewFrame(byte[] data, android.hardware.Camera camera) {
2647             if (VERBOSE) Log.v(TAG, "Preview frame callback, id " + mId + ".");
2648             mPreviewDone.open();
2649         }
2650     }
2651 
2652     @UiThreadTest
2653     @Test
testFocusAreas()2654     public void testFocusAreas() throws Exception {
2655         for (int id : mCameraIds) {
2656             Log.v(TAG, "Camera id=" + id);
2657 
2658             initializeMessageLooper(id);
2659             Parameters parameters = mCamera.getParameters();
2660             int maxNumFocusAreas = parameters.getMaxNumFocusAreas();
2661             assertTrue(maxNumFocusAreas >= 0);
2662             if (maxNumFocusAreas > 0) {
2663                 List<String> focusModes = parameters.getSupportedFocusModes();
2664                 assertTrue(focusModes.contains(Parameters.FOCUS_MODE_AUTO));
2665                 testAreas(FOCUS_AREA, maxNumFocusAreas);
2666             }
2667             terminateMessageLooper();
2668         }
2669     }
2670 
2671     @UiThreadTest
2672     @Test
testMeteringAreas()2673     public void testMeteringAreas() throws Exception {
2674         for (int id : mCameraIds) {
2675             Log.v(TAG, "Camera id=" + id);
2676             initializeMessageLooper(id);
2677             Parameters parameters = mCamera.getParameters();
2678             int maxNumMeteringAreas = parameters.getMaxNumMeteringAreas();
2679             assertTrue(maxNumMeteringAreas >= 0);
2680             if (maxNumMeteringAreas > 0) {
2681                 testAreas(METERING_AREA, maxNumMeteringAreas);
2682             }
2683             terminateMessageLooper();
2684         }
2685     }
2686 
testAreas(int type, int maxNumAreas)2687     private void testAreas(int type, int maxNumAreas) throws Exception {
2688         mCamera.startPreview();
2689 
2690         // Test various valid cases.
2691         testValidAreas(type, null);                                  // the default area
2692         testValidAreas(type, makeAreas(-1000, -1000, 1000, 1000, 1)); // biggest area
2693         testValidAreas(type, makeAreas(-500, -500, 500, 500, 1000)); // medium area & biggest weight
2694         testValidAreas(type, makeAreas(0, 0, 1, 1, 1));              // smallest area
2695 
2696         ArrayList<Area> areas = new ArrayList();
2697         if (maxNumAreas > 1) {
2698             // Test overlapped areas.
2699             testValidAreas(type, makeAreas(-250, -250, 250, 250, 1, 0, 0, 500, 500, 2));
2700             // Test completely disjoint areas.
2701             testValidAreas(type, makeAreas(-250, -250, 0, 0, 1, 900, 900, 1000, 1000, 1));
2702             // Test the maximum number of areas.
2703             testValidAreas(type, makeAreas(-1000, -1000, 1000, 1000, 1000, maxNumAreas));
2704         }
2705 
2706         // Test various invalid cases.
2707         testInvalidAreas(type, makeAreas(-1001, -1000, 1000, 1000, 1));    // left should >= -1000
2708         testInvalidAreas(type, makeAreas(-1000, -1001, 1000, 1000, 1));    // top should >= -1000
2709         testInvalidAreas(type, makeAreas(-1000, -1000, 1001, 1000, 1));    // right should <= 1000
2710         testInvalidAreas(type, makeAreas(-1000, -1000, 1000, 1001, 1));    // bottom should <= 1000
2711         testInvalidAreas(type, makeAreas(-1000, -1000, 1000, 1000, 0));    // weight should >= 1
2712         testInvalidAreas(type, makeAreas(-1000, -1000, 1000, 1001, 1001)); // weight should <= 1000
2713         testInvalidAreas(type, makeAreas(500, -1000, 500, 1000, 1));       // left should < right
2714         testInvalidAreas(type, makeAreas(-1000, 500, 1000, 500, 1));       // top should < bottom
2715         testInvalidAreas(type, makeAreas(500, -1000, 499, 1000, 1));       // left should < right
2716         testInvalidAreas(type, makeAreas(-1000, 500, 100, 499, 1));        // top should < bottom
2717         testInvalidAreas(type, makeAreas(-250, -250, 250, 250, -1));       // weight should >= 1
2718         // Test when the number of areas exceeds maximum.
2719         testInvalidAreas(type, makeAreas(-1000, -1000, 1000, 1000, 1000, maxNumAreas + 1));
2720     }
2721 
makeAreas(int left, int top, int right, int bottom, int weight)2722     private static ArrayList<Area> makeAreas(int left, int top, int right, int bottom, int weight) {
2723         ArrayList<Area> areas = new ArrayList<Area>();
2724         areas.add(new Area(new Rect(left, top, right, bottom), weight));
2725         return areas;
2726     }
2727 
makeAreas(int left, int top, int right, int bottom, int weight, int number)2728     private static ArrayList<Area> makeAreas(int left, int top, int right, int bottom,
2729             int weight, int number) {
2730         ArrayList<Area> areas = new ArrayList<Area>();
2731         for (int i = 0; i < number; i++) {
2732             areas.add(new Area(new Rect(left, top, right, bottom), weight));
2733         }
2734         return areas;
2735     }
2736 
makeAreas(int left1, int top1, int right1, int bottom1, int weight1, int left2, int top2, int right2, int bottom2, int weight2)2737     private static ArrayList<Area> makeAreas(int left1, int top1, int right1,
2738             int bottom1, int weight1, int left2, int top2, int right2,
2739             int bottom2, int weight2) {
2740         ArrayList<Area> areas = new ArrayList<Area>();
2741         areas.add(new Area(new Rect(left1, top1, right1, bottom1), weight1));
2742         areas.add(new Area(new Rect(left2, top2, right2, bottom2), weight2));
2743         return areas;
2744     }
2745 
testValidAreas(int areaType, ArrayList<Area> areas)2746     private void testValidAreas(int areaType, ArrayList<Area> areas) {
2747         if (areaType == FOCUS_AREA) {
2748             testValidFocusAreas(areas);
2749         } else {
2750             testValidMeteringAreas(areas);
2751         }
2752     }
2753 
testInvalidAreas(int areaType, ArrayList<Area> areas)2754     private void testInvalidAreas(int areaType, ArrayList<Area> areas) {
2755         if (areaType == FOCUS_AREA) {
2756             testInvalidFocusAreas(areas);
2757         } else {
2758             testInvalidMeteringAreas(areas);
2759         }
2760     }
2761 
testValidFocusAreas(ArrayList<Area> areas)2762     private void testValidFocusAreas(ArrayList<Area> areas) {
2763         Parameters parameters = mCamera.getParameters();
2764         parameters.setFocusAreas(areas);
2765         mCamera.setParameters(parameters);
2766         parameters = mCamera.getParameters();
2767         assertEquals(areas, parameters.getFocusAreas());
2768         mCamera.autoFocus(mAutoFocusCallback);
2769         waitForFocusDone();
2770     }
2771 
testInvalidFocusAreas(ArrayList<Area> areas)2772     private void testInvalidFocusAreas(ArrayList<Area> areas) {
2773         Parameters parameters = mCamera.getParameters();
2774         List<Area> originalAreas = parameters.getFocusAreas();
2775         try {
2776             parameters.setFocusAreas(areas);
2777             mCamera.setParameters(parameters);
2778             fail("Should throw exception when focus area is invalid.");
2779         } catch (RuntimeException e) {
2780             parameters = mCamera.getParameters();
2781             assertEquals(originalAreas, parameters.getFocusAreas());
2782         }
2783     }
2784 
testValidMeteringAreas(ArrayList<Area> areas)2785     private void testValidMeteringAreas(ArrayList<Area> areas) {
2786         Parameters parameters = mCamera.getParameters();
2787         parameters.setMeteringAreas(areas);
2788         mCamera.setParameters(parameters);
2789         parameters = mCamera.getParameters();
2790         assertEquals(areas, parameters.getMeteringAreas());
2791     }
2792 
testInvalidMeteringAreas(ArrayList<Area> areas)2793     private void testInvalidMeteringAreas(ArrayList<Area> areas) {
2794         Parameters parameters = mCamera.getParameters();
2795         List<Area> originalAreas = parameters.getMeteringAreas();
2796         try {
2797             parameters.setMeteringAreas(areas);
2798             mCamera.setParameters(parameters);
2799             fail("Should throw exception when metering area is invalid.");
2800         } catch (RuntimeException e) {
2801             parameters = mCamera.getParameters();
2802             assertEquals(originalAreas, parameters.getMeteringAreas());
2803         }
2804     }
2805 
2806     // Apps should be able to call startPreview in jpeg callback.
2807     @UiThreadTest
2808     @Test
testJpegCallbackStartPreview()2809     public void testJpegCallbackStartPreview() throws Exception {
2810         for (int id : mCameraIds) {
2811             Log.v(TAG, "Camera id=" + id);
2812             testJpegCallbackStartPreviewByCamera(id);
2813         }
2814     }
2815 
testJpegCallbackStartPreviewByCamera(int cameraId)2816     private void testJpegCallbackStartPreviewByCamera(int cameraId) throws Exception {
2817         initializeMessageLooper(cameraId);
2818         mCamera.startPreview();
2819         mCamera.takePicture(mShutterCallback, mRawPictureCallback, new JpegStartPreviewCallback());
2820         waitForSnapshotDone();
2821         terminateMessageLooper();
2822         assertTrue(mJpegPictureCallbackResult);
2823     }
2824 
2825     private final class JpegStartPreviewCallback implements PictureCallback {
onPictureTaken(byte[] rawData, Camera camera)2826         public void onPictureTaken(byte[] rawData, Camera camera) {
2827             try {
2828                 camera.startPreview();
2829                 mJpegPictureCallbackResult = true;
2830             } catch (Exception e) {
2831             }
2832             mSnapshotDone.open();
2833         }
2834     }
2835 
2836     @UiThreadTest
2837     @Test
testRecordingHint()2838     public void testRecordingHint() throws Exception {
2839         for (int id : mCameraIds) {
2840             Log.v(TAG, "Camera id=" + id);
2841             testRecordingHintByCamera(id);
2842         }
2843     }
2844 
testRecordingHintByCamera(int cameraId)2845     private void testRecordingHintByCamera(int cameraId) throws Exception {
2846         initializeMessageLooper(cameraId);
2847         Parameters parameters = mCamera.getParameters();
2848 
2849         SurfaceHolder holder = mActivityRule.getActivity().getSurfaceView().getHolder();
2850         CamcorderProfile profile = null; // for built-in camera
2851         Camera.Size videoSize = null; // for external camera
2852         int frameRate = -1; // for external camera
2853 
2854         if (mIsExternalCamera) {
2855             videoSize = setupExternalCameraRecord(parameters);
2856             frameRate = parameters.getPreviewFrameRate();
2857         } else {
2858             profile = CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_LOW);
2859             setPreviewSizeByProfile(parameters, profile);
2860         }
2861 
2862 
2863         // Test recording videos and taking pictures when the hint is off and on.
2864         for (int i = 0; i < 2; i++) {
2865             parameters.setRecordingHint(i == 0 ? false : true);
2866             mCamera.setParameters(parameters);
2867             mCamera.startPreview();
2868             if (mIsExternalCamera) {
2869                 recordVideoSimpleBySize(videoSize, frameRate, holder);
2870             } else {
2871                 recordVideoSimple(profile, holder);
2872             }
2873             mCamera.takePicture(mShutterCallback, mRawPictureCallback, mJpegPictureCallback);
2874             waitForSnapshotDone();
2875             assertTrue(mJpegPictureCallbackResult);
2876         }
2877 
2878         // Can change recording hint when the preview is active.
2879         mCamera.startPreview();
2880         parameters.setRecordingHint(false);
2881         mCamera.setParameters(parameters);
2882         parameters.setRecordingHint(true);
2883         mCamera.setParameters(parameters);
2884         terminateMessageLooper();
2885     }
2886 
recordVideoSimpleBySize(Camera.Size size, int frameRate, SurfaceHolder holder)2887     private void recordVideoSimpleBySize(Camera.Size size, int frameRate,
2888             SurfaceHolder holder) throws Exception {
2889         mCamera.unlock();
2890         MediaRecorder recorder = new MediaRecorder();
2891         try {
2892             recorder.setCamera(mCamera);
2893             recorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
2894             recorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
2895             recorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
2896             recorder.setVideoEncodingBitRate(VIDEO_BIT_RATE_IN_BPS);
2897             recorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);
2898             recorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
2899             recorder.setVideoSize(size.width, size.height);
2900             recorder.setVideoFrameRate(frameRate);
2901             recorder.setOutputFile(mRecordingPath);
2902             recorder.setPreviewDisplay(holder.getSurface());
2903             recorder.prepare();
2904             recorder.start();
2905             Thread.sleep(2000);
2906             recorder.stop();
2907         } finally {
2908             recorder.release();
2909             mCamera.lock();
2910         }
2911     }
2912 
recordVideoSimple(CamcorderProfile profile, SurfaceHolder holder)2913     private void recordVideoSimple(CamcorderProfile profile,
2914             SurfaceHolder holder) throws Exception {
2915         mCamera.unlock();
2916         MediaRecorder recorder = new MediaRecorder();
2917         try {
2918             recorder.setCamera(mCamera);
2919             recorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
2920             recorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
2921             recorder.setProfile(profile);
2922             recorder.setOutputFile(mRecordingPath);
2923             recorder.setPreviewDisplay(holder.getSurface());
2924             recorder.prepare();
2925             recorder.start();
2926             Thread.sleep(2000);
2927             recorder.stop();
2928         } finally {
2929             recorder.release();
2930             mCamera.lock();
2931         }
2932     }
2933 
2934     @UiThreadTest
2935     @Test
testAutoExposureLock()2936     public void testAutoExposureLock() throws Exception {
2937         for (int id : mCameraIds) {
2938             Log.v(TAG, "Camera id=" + id);
2939             initializeMessageLooper(id);
2940             Parameters parameters = mCamera.getParameters();
2941             boolean aeLockSupported = parameters.isAutoExposureLockSupported();
2942             if (aeLockSupported) {
2943                 subtestLockCommon(AUTOEXPOSURE_LOCK);
2944                 subtestLockAdditionalAE();
2945             }
2946             terminateMessageLooper();
2947         }
2948     }
2949 
2950     @UiThreadTest
2951     @Test
testAutoWhiteBalanceLock()2952     public void testAutoWhiteBalanceLock() throws Exception {
2953         for (int id : mCameraIds) {
2954             Log.v(TAG, "Camera id=" + id);
2955             initializeMessageLooper(id);
2956             Parameters parameters = mCamera.getParameters();
2957             boolean awbLockSupported = parameters.isAutoWhiteBalanceLockSupported();
2958             if (awbLockSupported) {
2959                 subtestLockCommon(AUTOWHITEBALANCE_LOCK);
2960                 subtestLockAdditionalAWB();
2961             }
2962             terminateMessageLooper();
2963         }
2964     }
2965 
2966     @UiThreadTest
2967     @Test
test3ALockInteraction()2968     public void test3ALockInteraction() throws Exception {
2969         for (int id : mCameraIds) {
2970             Log.v(TAG, "Camera id=" + id);
2971             initializeMessageLooper(id);
2972             Parameters parameters = mCamera.getParameters();
2973             boolean locksSupported =
2974                     parameters.isAutoWhiteBalanceLockSupported() &&
2975                     parameters.isAutoExposureLockSupported();
2976             if (locksSupported) {
2977                 subtestLockInteractions();
2978             }
2979             terminateMessageLooper();
2980         }
2981     }
2982 
subtestLockCommon(int type)2983     private void subtestLockCommon(int type) {
2984         // Verify lock is not set on open()
2985         assert3ALockState("Lock not released after open()", type, false);
2986 
2987         // Verify lock can be set, unset before preview
2988         set3ALockState(true, type);
2989         assert3ALockState("Lock could not be set before 1st preview!",
2990                 type, true);
2991 
2992         set3ALockState(false, type);
2993         assert3ALockState("Lock could not be unset before 1st preview!",
2994                 type, false);
2995 
2996         // Verify preview start does not set lock
2997         mCamera.startPreview();
2998         assert3ALockState("Lock state changed by preview start!", type, false);
2999 
3000         // Verify lock can be set, unset during preview
3001         set3ALockState(true, type);
3002         assert3ALockState("Lock could not be set during preview!", type, true);
3003 
3004         set3ALockState(false, type);
3005         assert3ALockState("Lock could not be unset during preview!",
3006                 type, false);
3007 
3008         // Verify lock is not cleared by stop preview
3009         set3ALockState(true, type);
3010         mCamera.stopPreview();
3011         assert3ALockState("Lock was cleared by stopPreview!", type, true);
3012 
3013         // Verify that preview start does not clear lock
3014         set3ALockState(true, type);
3015         mCamera.startPreview();
3016         assert3ALockState("Lock state changed by preview start!", type, true);
3017 
3018         // Verify that taking a picture does not clear the lock
3019         set3ALockState(true, type);
3020         mCamera.takePicture(mShutterCallback, mRawPictureCallback,
3021                 mJpegPictureCallback);
3022         waitForSnapshotDone();
3023         assert3ALockState("Lock state was cleared by takePicture!", type, true);
3024 
3025         mCamera.startPreview();
3026         Parameters parameters = mCamera.getParameters();
3027         for (String focusMode: parameters.getSupportedFocusModes()) {
3028             // TODO: Test this for other focus modes as well, once agreement is
3029             // reached on which ones it should apply to
3030             if (!Parameters.FOCUS_MODE_AUTO.equals(focusMode) ) {
3031                 continue;
3032             }
3033 
3034             parameters.setFocusMode(focusMode);
3035             mCamera.setParameters(parameters);
3036 
3037             // Verify that autoFocus does not change the lock
3038             set3ALockState(false, type);
3039             mCamera.autoFocus(mAutoFocusCallback);
3040             assert3ALockState("Lock was set by autoFocus in mode: " + focusMode, type, false);
3041             assertTrue(waitForFocusDone());
3042             assert3ALockState("Lock was set by autoFocus in mode: " + focusMode, type, false);
3043 
3044             // Verify that cancelAutoFocus does not change the lock
3045             mCamera.cancelAutoFocus();
3046             assert3ALockState("Lock was set by cancelAutoFocus!", type, false);
3047 
3048             // Verify that autoFocus does not change the lock
3049             set3ALockState(true, type);
3050             mCamera.autoFocus(mAutoFocusCallback);
3051             assert3ALockState("Lock was cleared by autoFocus in mode: " + focusMode, type, true);
3052             assertTrue(waitForFocusDone());
3053             assert3ALockState("Lock was cleared by autoFocus in mode: " + focusMode, type, true);
3054 
3055             // Verify that cancelAutoFocus does not change the lock
3056             mCamera.cancelAutoFocus();
3057             assert3ALockState("Lock was cleared by cancelAutoFocus!", type, true);
3058         }
3059         mCamera.stopPreview();
3060     }
3061 
subtestLockAdditionalAE()3062     private void subtestLockAdditionalAE() {
3063         // Verify that exposure compensation can be used while
3064         // AE lock is active
3065         mCamera.startPreview();
3066         Parameters parameters = mCamera.getParameters();
3067         parameters.setAutoExposureLock(true);
3068         mCamera.setParameters(parameters);
3069         parameters.setExposureCompensation(parameters.getMaxExposureCompensation());
3070         mCamera.setParameters(parameters);
3071         parameters = mCamera.getParameters();
3072         assertTrue("Could not adjust exposure compensation with AE locked!",
3073                 parameters.getExposureCompensation() ==
3074                 parameters.getMaxExposureCompensation() );
3075 
3076         parameters.setExposureCompensation(parameters.getMinExposureCompensation());
3077         mCamera.setParameters(parameters);
3078         parameters = mCamera.getParameters();
3079         assertTrue("Could not adjust exposure compensation with AE locked!",
3080                 parameters.getExposureCompensation() ==
3081                 parameters.getMinExposureCompensation() );
3082         mCamera.stopPreview();
3083     }
3084 
subtestLockAdditionalAWB()3085     private void subtestLockAdditionalAWB() {
3086         // Verify that switching AWB modes clears AWB lock
3087         mCamera.startPreview();
3088         Parameters parameters = mCamera.getParameters();
3089         String firstWb = null;
3090         for ( String wbMode: parameters.getSupportedWhiteBalance() ) {
3091             if (firstWb == null) {
3092                 firstWb = wbMode;
3093             }
3094             parameters.setWhiteBalance(firstWb);
3095             mCamera.setParameters(parameters);
3096             parameters.setAutoWhiteBalanceLock(true);
3097             mCamera.setParameters(parameters);
3098 
3099             parameters.setWhiteBalance(wbMode);
3100             mCamera.setParameters(parameters);
3101 
3102             if (firstWb == wbMode) {
3103                 assert3ALockState("AWB lock was cleared when WB mode was unchanged!",
3104                         AUTOWHITEBALANCE_LOCK, true);
3105             } else {
3106                 assert3ALockState("Changing WB mode did not clear AWB lock!",
3107                         AUTOWHITEBALANCE_LOCK, false);
3108             }
3109         }
3110         mCamera.stopPreview();
3111     }
3112 
subtestLockInteractions()3113     private void subtestLockInteractions() {
3114         // Verify that toggling AE does not change AWB lock state
3115         set3ALockState(false, AUTOWHITEBALANCE_LOCK);
3116         set3ALockState(false, AUTOEXPOSURE_LOCK);
3117 
3118         set3ALockState(true, AUTOEXPOSURE_LOCK);
3119         assert3ALockState("Changing AE lock affected AWB lock!",
3120                 AUTOWHITEBALANCE_LOCK, false);
3121 
3122         set3ALockState(false, AUTOEXPOSURE_LOCK);
3123         assert3ALockState("Changing AE lock affected AWB lock!",
3124                 AUTOWHITEBALANCE_LOCK, false);
3125 
3126         set3ALockState(true, AUTOWHITEBALANCE_LOCK);
3127 
3128         set3ALockState(true, AUTOEXPOSURE_LOCK);
3129         assert3ALockState("Changing AE lock affected AWB lock!",
3130                 AUTOWHITEBALANCE_LOCK, true);
3131 
3132         set3ALockState(false, AUTOEXPOSURE_LOCK);
3133         assert3ALockState("Changing AE lock affected AWB lock!",
3134                 AUTOWHITEBALANCE_LOCK, true);
3135 
3136         // Verify that toggling AWB does not change AE lock state
3137         set3ALockState(false, AUTOWHITEBALANCE_LOCK);
3138         set3ALockState(false, AUTOEXPOSURE_LOCK);
3139 
3140         set3ALockState(true, AUTOWHITEBALANCE_LOCK);
3141         assert3ALockState("Changing AWB lock affected AE lock!",
3142                 AUTOEXPOSURE_LOCK, false);
3143 
3144         set3ALockState(false, AUTOWHITEBALANCE_LOCK);
3145         assert3ALockState("Changing AWB lock affected AE lock!",
3146                 AUTOEXPOSURE_LOCK, false);
3147 
3148         set3ALockState(true, AUTOEXPOSURE_LOCK);
3149 
3150         set3ALockState(true, AUTOWHITEBALANCE_LOCK);
3151         assert3ALockState("Changing AWB lock affected AE lock!",
3152                 AUTOEXPOSURE_LOCK, true);
3153 
3154         set3ALockState(false, AUTOWHITEBALANCE_LOCK);
3155         assert3ALockState("Changing AWB lock affected AE lock!",
3156                 AUTOEXPOSURE_LOCK, true);
3157     }
3158 
assert3ALockState(String msg, int type, boolean state)3159     private void assert3ALockState(String msg, int type, boolean state) {
3160         Parameters parameters = mCamera.getParameters();
3161         switch (type) {
3162             case AUTOEXPOSURE_LOCK:
3163                 assertTrue(msg, state == parameters.getAutoExposureLock());
3164                 break;
3165             case AUTOWHITEBALANCE_LOCK:
3166                 assertTrue(msg, state == parameters.getAutoWhiteBalanceLock());
3167                 break;
3168             default:
3169                 assertTrue("Unknown lock type " + type, false);
3170                 break;
3171         }
3172     }
3173 
set3ALockState(boolean state, int type)3174     private void set3ALockState(boolean state, int type) {
3175         Parameters parameters = mCamera.getParameters();
3176         switch (type) {
3177             case AUTOEXPOSURE_LOCK:
3178                 parameters.setAutoExposureLock(state);
3179                 break;
3180             case AUTOWHITEBALANCE_LOCK:
3181                 parameters.setAutoWhiteBalanceLock(state);
3182                 break;
3183             default:
3184                 assertTrue("Unknown lock type "+type, false);
3185                 break;
3186         }
3187         mCamera.setParameters(parameters);
3188     }
3189 
3190     @UiThreadTest
3191     @Test
testFaceDetection()3192     public void testFaceDetection() throws Exception {
3193         for (int id : mCameraIds) {
3194             Log.v(TAG, "Camera id=" + id);
3195             testFaceDetectionByCamera(id);
3196         }
3197     }
3198 
testFaceDetectionByCamera(int cameraId)3199     private void testFaceDetectionByCamera(int cameraId) throws Exception {
3200         final int FACE_DETECTION_TEST_DURATION = 3000;
3201         initializeMessageLooper(cameraId);
3202         mCamera.startPreview();
3203         Parameters parameters = mCamera.getParameters();
3204         int maxNumOfFaces = parameters.getMaxNumDetectedFaces();
3205         assertTrue(maxNumOfFaces >= 0);
3206         if (maxNumOfFaces == 0) {
3207             try {
3208                 mCamera.startFaceDetection();
3209                 fail("Should throw an exception if face detection is not supported.");
3210             } catch (IllegalArgumentException e) {
3211                 // expected
3212             }
3213             terminateMessageLooper();
3214             return;
3215         }
3216 
3217         mCamera.startFaceDetection();
3218         try {
3219             mCamera.startFaceDetection();
3220             fail("Starting face detection twice should throw an exception");
3221         } catch (RuntimeException e) {
3222             // expected
3223         }
3224         FaceListener listener = new FaceListener();
3225         mCamera.setFaceDetectionListener(listener);
3226         // Sleep some time so the camera has chances to detect faces.
3227         Thread.sleep(FACE_DETECTION_TEST_DURATION);
3228         // The face callback runs in another thread. Release the camera and stop
3229         // the looper. So we do not access the face array from two threads at
3230         // the same time.
3231         terminateMessageLooper();
3232 
3233         // Check if the optional fields are supported.
3234         boolean optionalFieldSupported = false;
3235         Face firstFace = null;
3236         for (Face[] faces: listener.mFacesArray) {
3237             for (Face face: faces) {
3238                 if (face != null) firstFace = face;
3239             }
3240         }
3241         if (firstFace != null) {
3242             if (firstFace.id != -1 || firstFace.leftEye != null
3243                     || firstFace.rightEye != null || firstFace.mouth != null) {
3244                 optionalFieldSupported = true;
3245             }
3246         }
3247 
3248         // Verify the faces array.
3249         for (Face[] faces: listener.mFacesArray) {
3250             testFaces(faces, maxNumOfFaces, optionalFieldSupported);
3251         }
3252 
3253         // After taking a picture, face detection should be started again.
3254         // Also make sure autofocus move callback is supported.
3255         initializeMessageLooper(cameraId);
3256         mCamera.setAutoFocusMoveCallback(mAutoFocusMoveCallback);
3257         mCamera.startPreview();
3258         mCamera.startFaceDetection();
3259         mCamera.takePicture(mShutterCallback, mRawPictureCallback, mJpegPictureCallback);
3260         waitForSnapshotDone();
3261         mCamera.startPreview();
3262         mCamera.startFaceDetection();
3263         terminateMessageLooper();
3264     }
3265 
3266     private class FaceListener implements FaceDetectionListener {
3267         public ArrayList<Face[]> mFacesArray = new ArrayList<Face[]>();
3268 
3269         @Override
onFaceDetection(Face[] faces, Camera camera)3270         public void onFaceDetection(Face[] faces, Camera camera) {
3271             mFacesArray.add(faces);
3272         }
3273     }
3274 
testFaces(Face[] faces, int maxNumOfFaces, boolean optionalFieldSupported)3275     private void testFaces(Face[] faces, int maxNumOfFaces,
3276             boolean optionalFieldSupported) {
3277         Rect bounds = new Rect(-1000, -1000, 1000, 1000);
3278         assertNotNull(faces);
3279         assertTrue(faces.length <= maxNumOfFaces);
3280         for (int i = 0; i < faces.length; i++) {
3281             Face face = faces[i];
3282             Rect rect = face.rect;
3283             // Check the bounds.
3284             assertNotNull(rect);
3285             assertTrue(rect.width() > 0);
3286             assertTrue(rect.height() > 0);
3287             assertTrue("Coordinates out of bounds. rect=" + rect,
3288                     bounds.contains(rect) || Rect.intersects(bounds, rect));
3289 
3290             // Check the score.
3291             assertTrue(face.score >= 1 && face.score <= 100);
3292 
3293             // Check id, left eye, right eye, and the mouth.
3294             // Optional fields should be all valid or none of them.
3295             if (!optionalFieldSupported) {
3296                 assertEquals(-1, face.id);
3297                 assertNull(face.leftEye);
3298                 assertNull(face.rightEye);
3299                 assertNull(face.mouth);
3300             } else {
3301                 assertTrue(face.id != -1);
3302                 assertNotNull(face.leftEye);
3303                 assertNotNull(face.rightEye);
3304                 assertNotNull(face.mouth);
3305                 assertTrue(bounds.contains(face.leftEye.x, face.leftEye.y));
3306                 assertTrue(bounds.contains(face.rightEye.x, face.rightEye.y));
3307                 assertTrue(bounds.contains(face.mouth.x, face.mouth.y));
3308                 // ID should be unique.
3309                 if (i != faces.length - 1) {
3310                     assertTrue(face.id != faces[i + 1].id);
3311                 }
3312             }
3313         }
3314     }
3315 
3316     @UiThreadTest
3317     @Test(timeout=60*60*1000) // timeout = 60 mins for long running tests
testVideoSnapshot()3318     public void testVideoSnapshot() throws Exception {
3319         for (int id : mCameraIds) {
3320             Log.v(TAG, "Camera id=" + id);
3321             testVideoSnapshotByCamera(id, /*recordingHint*/false);
3322             testVideoSnapshotByCamera(id, /*recordingHint*/true);
3323         }
3324     }
3325 
3326     private static final int[] mCamcorderProfileList = {
3327         CamcorderProfile.QUALITY_2160P,
3328         CamcorderProfile.QUALITY_1080P,
3329         CamcorderProfile.QUALITY_480P,
3330         CamcorderProfile.QUALITY_720P,
3331         CamcorderProfile.QUALITY_CIF,
3332         CamcorderProfile.QUALITY_HIGH,
3333         CamcorderProfile.QUALITY_LOW,
3334         CamcorderProfile.QUALITY_QCIF,
3335         CamcorderProfile.QUALITY_QVGA,
3336     };
3337 
testVideoSnapshotByCamera(int cameraId, boolean recordingHint)3338     private void testVideoSnapshotByCamera(int cameraId, boolean recordingHint) throws Exception {
3339         initializeMessageLooper(cameraId);
3340         Camera.Parameters parameters = mCamera.getParameters();
3341         terminateMessageLooper();
3342         if (!parameters.isVideoSnapshotSupported()) {
3343             return;
3344         }
3345 
3346         if (mIsExternalCamera) {
3347             return;
3348         }
3349 
3350         SurfaceHolder holder = mActivityRule.getActivity().getSurfaceView().getHolder();
3351 
3352         for (int profileId: mCamcorderProfileList) {
3353             if (!CamcorderProfile.hasProfile(cameraId, profileId)) {
3354                 continue;
3355             }
3356             initializeMessageLooper(cameraId);
3357             // Set the preview size.
3358             CamcorderProfile profile = CamcorderProfile.get(cameraId,
3359                     profileId);
3360             setPreviewSizeByProfile(parameters, profile);
3361 
3362             // Set the biggest picture size.
3363             Size biggestSize = mCamera.new Size(-1, -1);
3364             for (Size size: parameters.getSupportedPictureSizes()) {
3365                 if (biggestSize.width < size.width) {
3366                     biggestSize = size;
3367                 }
3368             }
3369             parameters.setPictureSize(biggestSize.width, biggestSize.height);
3370             parameters.setRecordingHint(recordingHint);
3371 
3372             mCamera.setParameters(parameters);
3373             mCamera.startPreview();
3374             mCamera.unlock();
3375             MediaRecorder recorder = new MediaRecorder();
3376             try {
3377                 recorder.setCamera(mCamera);
3378                 recorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
3379                 recorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
3380                 recorder.setProfile(profile);
3381                 recorder.setOutputFile(mRecordingPath);
3382                 recorder.setPreviewDisplay(holder.getSurface());
3383                 recorder.prepare();
3384                 recorder.start();
3385                 subtestTakePictureByCamera(true,
3386                         profile.videoFrameWidth, profile.videoFrameHeight);
3387                 testJpegExifByCamera(true);
3388                 testJpegThumbnailSizeByCamera(true,
3389                         profile.videoFrameWidth, profile.videoFrameHeight);
3390                 Thread.sleep(2000);
3391                 recorder.stop();
3392             } finally {
3393                 recorder.release();
3394                 mCamera.lock();
3395             }
3396             mCamera.stopPreview();
3397             terminateMessageLooper();
3398         }
3399     }
3400 
3401     @Test
testPreviewCallbackWithPicture()3402     public void testPreviewCallbackWithPicture() throws Exception {
3403         for (int id : mCameraIds) {
3404             Log.v(TAG, "Camera id=" + id);
3405             testPreviewCallbackWithPictureByCamera(id);
3406         }
3407     }
3408 
testPreviewCallbackWithPictureByCamera(int cameraId)3409     private void testPreviewCallbackWithPictureByCamera(int cameraId)
3410             throws Exception {
3411         initializeMessageLooper(cameraId);
3412 
3413         SimplePreviewStreamCb callback = new SimplePreviewStreamCb(1);
3414         mCamera.setPreviewCallback(callback);
3415 
3416         Log.v(TAG, "Starting preview");
3417         mCamera.startPreview();
3418 
3419         // Wait until callbacks are flowing
3420         for (int i = 0; i < 30; i++) {
3421             assertTrue("testPreviewCallbackWithPicture: Not receiving preview callbacks!",
3422                     mPreviewDone.block( WAIT_FOR_COMMAND_TO_COMPLETE ) );
3423             mPreviewDone.close();
3424         }
3425 
3426         // Now take a picture
3427         Log.v(TAG, "Taking picture now");
3428 
3429         Size pictureSize = mCamera.getParameters().getPictureSize();
3430         mCamera.takePicture(mShutterCallback, mRawPictureCallback,
3431                 mJpegPictureCallback);
3432 
3433         waitForSnapshotDone();
3434 
3435         assertTrue("Shutter callback not received", mShutterCallbackResult);
3436         assertTrue("Raw picture callback not received", mRawPictureCallbackResult);
3437         assertTrue("Jpeg picture callback not received", mJpegPictureCallbackResult);
3438         assertNotNull(mJpegData);
3439         BitmapFactory.Options bmpOptions = new BitmapFactory.Options();
3440         bmpOptions.inJustDecodeBounds = true;
3441         BitmapFactory.decodeByteArray(mJpegData, 0, mJpegData.length, bmpOptions);
3442         assertEquals(pictureSize.width, bmpOptions.outWidth);
3443         assertEquals(pictureSize.height, bmpOptions.outHeight);
3444 
3445         // Restart preview, confirm callbacks still happen
3446         Log.v(TAG, "Restarting preview");
3447         mCamera.startPreview();
3448 
3449         for (int i = 0; i < 30; i++) {
3450             assertTrue("testPreviewCallbackWithPicture: Not receiving preview callbacks!",
3451                     mPreviewDone.block( WAIT_FOR_COMMAND_TO_COMPLETE ) );
3452             mPreviewDone.close();
3453         }
3454 
3455         mCamera.stopPreview();
3456 
3457         terminateMessageLooper();
3458     }
3459 
3460     @Test
testEnableShutterSound()3461     public void testEnableShutterSound() throws Exception {
3462         for (int id : mCameraIds) {
3463             Log.v(TAG, "Camera id=" + id);
3464             testEnableShutterSoundByCamera(id);
3465         }
3466     }
3467 
testEnableShutterSoundByCamera(int id)3468     private void testEnableShutterSoundByCamera(int id) throws Exception {
3469         CameraInfo info = new CameraInfo();
3470 
3471         Camera.getCameraInfo(id, info);
3472 
3473         initializeMessageLooper(id);
3474 
3475         boolean result;
3476         Log.v(TAG, "testEnableShutterSoundByCamera: canDisableShutterSound: " +
3477                 info.canDisableShutterSound);
3478         result = mCamera.enableShutterSound(false);
3479         assertTrue(result == info.canDisableShutterSound);
3480         result = mCamera.enableShutterSound(true);
3481         assertTrue(result);
3482 
3483         terminateMessageLooper();
3484     }
3485 
3486     @Test
testCameraExternalConnected()3487     public void testCameraExternalConnected() {
3488         if (mActivityRule.getActivity().getPackageManager().
3489                 hasSystemFeature(PackageManager.FEATURE_CAMERA_EXTERNAL) ) {
3490             int nCameras = Camera.getNumberOfCameras();
3491             assertTrue("Devices with external camera support must have a camera connected for " +
3492                     "testing",
3493                     nCameras > 0);
3494             for (int id = 0; id < nCameras; id++) {
3495                 try {
3496                     Camera c = Camera.open(id);
3497                     c.release();
3498                 } catch (Throwable e) {
3499                     throw new AssertionError("Devices with external camera support must " +
3500                             "have all listed cameras be connected and openable for testing", e);
3501                 }
3502             }
3503         }
3504     }
3505 
getPictureSizeForPreview(Size previewSize, Parameters parameters)3506     private Size getPictureSizeForPreview(Size previewSize, Parameters parameters) {
3507         Size QCIF = mCamera.new Size(176, 144);
3508         Size defaultPicSize = parameters.getPictureSize();
3509         List<Size> supportedPicSizes = parameters.getSupportedPictureSizes();
3510         Size smallestPicSize = defaultPicSize;
3511         for (Size size: supportedPicSizes) {
3512             if (smallestPicSize.width > size.width) {
3513                 smallestPicSize = size;
3514             }
3515         }
3516 
3517         if (previewSize.equals(QCIF)) {
3518             return smallestPicSize;
3519         } else {
3520             return defaultPicSize;
3521         }
3522     }
3523 }
3524