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