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