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