1/*
2 * Copyright (C) 2012 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
17package com.android.mediaframeworktest.stress;
18
19import com.android.mediaframeworktest.MediaFrameworkTest;
20
21import java.io.BufferedWriter;
22import java.io.File;
23import java.io.FilenameFilter;
24import java.io.FileWriter;
25import java.io.FileNotFoundException;
26import java.io.FileOutputStream;
27import java.io.IOException;
28import java.io.Writer;
29import java.util.concurrent.Semaphore;
30import java.util.concurrent.TimeUnit;
31
32import android.hardware.Camera;
33import android.hardware.Camera.PictureCallback;
34import android.hardware.Camera.ShutterCallback;
35import android.os.Environment;
36import android.os.Handler;
37import android.os.Looper;
38import android.test.ActivityInstrumentationTestCase2;
39import android.test.suitebuilder.annotation.LargeTest;
40import android.util.Log;
41import android.view.SurfaceHolder;
42import com.android.mediaframeworktest.CameraStressTestRunner;
43
44import junit.framework.Assert;
45
46/**
47 * Junit / Instrumentation test case for the camera zoom api
48 *
49 * adb shell am instrument
50 *  -e class com.android.mediaframeworktest.stress.CameraStressTest
51 *  -w com.android.mediaframeworktest/.CameraStressTestRunner
52 */
53public class CameraStressTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
54    private String TAG = "CameraStressTest";
55    private Camera mCamera;
56
57    private static final int NUMBER_OF_ZOOM_LOOPS = 100;
58    private static final long WAIT_GENERIC = 3 * 1000; // 3 seconds
59    private static final long WAIT_TIMEOUT = 10 * 1000; // 10 seconds
60    private static final long WAIT_ZOOM_ANIMATION = 5 * 1000; // 5 seconds
61    private static final String CAMERA_STRESS_OUTPUT =
62            "/sdcard/cameraStressOutput.txt";
63    private static final int CAMERA_ID = 0;
64    private final CameraErrorCallback mCameraErrorCallback = new CameraErrorCallback();
65
66    private Thread mLooperThread;
67    private Handler mHandler;
68
69    public CameraStressTest() {
70        super("com.android.mediaframeworktest", MediaFrameworkTest.class);
71    }
72
73    protected void setUp() throws Exception {
74        final Semaphore sem = new Semaphore(0);
75        mLooperThread = new Thread() {
76            @Override
77            public void run() {
78                Log.v(TAG, "starting looper");
79                Looper.prepare();
80                mHandler = new Handler();
81                sem.release();
82                Looper.loop();
83                Log.v(TAG, "quit looper");
84            }
85        };
86        mLooperThread.start();
87        if (!sem.tryAcquire(WAIT_TIMEOUT, TimeUnit.MILLISECONDS)) {
88            fail("Failed to start the looper.");
89        }
90        getActivity();
91        super.setUp();
92    }
93
94    @Override
95    protected void tearDown() throws Exception {
96        if (mHandler != null) {
97            mHandler.getLooper().quit();
98            mHandler = null;
99        }
100        if (mLooperThread != null) {
101            mLooperThread.join(WAIT_TIMEOUT);
102            if (mLooperThread.isAlive()) {
103                fail("Failed to stop the looper.");
104            }
105            mLooperThread = null;
106        }
107
108        super.tearDown();
109    }
110
111    private void runOnLooper(final Runnable command) throws InterruptedException {
112        final Semaphore sem = new Semaphore(0);
113        mHandler.post(new Runnable() {
114            @Override
115            public void run() {
116                try {
117                    command.run();
118                } finally {
119                    sem.release();
120                }
121            }
122        });
123        if (!sem.tryAcquire(WAIT_TIMEOUT, TimeUnit.MILLISECONDS)) {
124            fail("Failed to run the command on the looper.");
125        }
126    }
127
128    private final class CameraErrorCallback implements android.hardware.Camera.ErrorCallback {
129        public void onError(int error, android.hardware.Camera camera) {
130            if (error == android.hardware.Camera.CAMERA_ERROR_SERVER_DIED) {
131                assertTrue("Camera test mediaserver died", false);
132            }
133        }
134    }
135
136    private ShutterCallback shutterCallback = new ShutterCallback() {
137        @Override
138        public void onShutter() {
139            Log.v(TAG, "Shutter");
140        }
141    };
142
143    private PictureCallback rawCallback = new PictureCallback() {
144        @Override
145        public void onPictureTaken(byte[] data, Camera camera) {
146            Log.v(TAG, "Raw picture taken");
147        }
148    };
149
150    private PictureCallback jpegCallback = new PictureCallback() {
151        @Override
152        public void onPictureTaken(byte[] data, Camera camera) {
153            FileOutputStream fos = null;
154
155            try {
156                Log.v(TAG, "JPEG picture taken");
157                fos = new FileOutputStream(String.format("%s/zoom-test-%d.jpg",
158                        Environment.getExternalStorageDirectory(), System.currentTimeMillis()));
159                fos.write(data);
160            }
161            catch (FileNotFoundException e) {
162                Log.v(TAG, "File not found: " + e.toString());
163            }
164            catch (IOException e) {
165                Log.v(TAG, "Error accessing file: " + e.toString());
166            }
167            finally {
168                try {
169                    if (fos != null) {
170                        fos.close();
171                    }
172                }
173                catch (IOException e) {
174                    Log.v(TAG, "Error closing file: " + e.toString());
175                }
176            }
177        }
178    };
179
180    // Helper method for cleaning up pics taken during testStressCameraZoom
181    private void cleanupZoomImages() {
182        try {
183            File sdcard = Environment.getExternalStorageDirectory();
184            File[] zoomImages = null;
185
186            FilenameFilter filter = new FilenameFilter() {
187                public boolean accept(File dir, String name) {
188                    return name.startsWith("zoom-test-");
189                }
190            };
191
192            zoomImages = sdcard.listFiles(filter);
193
194            for (File f : zoomImages) {
195                f.delete();
196            }
197        }
198        catch (SecurityException e) {
199            Log.v(TAG, "Security manager access violation: " + e.toString());
200        }
201    }
202
203    // Test case for stressing the camera zoom in/out feature
204    @LargeTest
205    public void testStressCameraZoom() throws Exception {
206        SurfaceHolder mSurfaceHolder;
207        mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
208        File stressOutFile = new File(CAMERA_STRESS_OUTPUT);
209        Writer output = new BufferedWriter(new FileWriter(stressOutFile, true));
210        output.write("Camera zoom stress:\n");
211        output.write("Total number of loops: " +  NUMBER_OF_ZOOM_LOOPS + "\n");
212
213        try {
214            Log.v(TAG, "Start preview");
215            output.write("No of loop: ");
216
217            mCamera = Camera.open(CAMERA_ID);
218            Camera.Parameters params = mCamera.getParameters();
219            mCamera.release();
220
221            if (!params.isSmoothZoomSupported() && !params.isZoomSupported()) {
222                Log.v(TAG, "Device camera does not support zoom");
223                assertTrue("Camera zoom stress test", false);
224            }
225            else {
226                Log.v(TAG, "Device camera does support zoom");
227
228                int nextZoomLevel = 0;
229
230                for (int i = 0; i < NUMBER_OF_ZOOM_LOOPS; i++) {
231                    runOnLooper(new Runnable() {
232                        @Override
233                        public void run() {
234                            mCamera = Camera.open(CAMERA_ID);
235                        }
236                    });
237
238                    mCamera.setErrorCallback(mCameraErrorCallback);
239                    mCamera.setPreviewDisplay(mSurfaceHolder);
240                    mCamera.startPreview();
241                    Thread.sleep(WAIT_GENERIC);
242
243                    params = mCamera.getParameters();
244                    int currentZoomLevel = params.getZoom();
245
246                    if (nextZoomLevel >= params.getMaxZoom()) {
247                        nextZoomLevel = 0;
248                    }
249                    ++nextZoomLevel;
250
251                    if (params.isSmoothZoomSupported()) {
252                        mCamera.startSmoothZoom(nextZoomLevel);
253                    }
254                    else {
255                        params.setZoom(nextZoomLevel);
256                        mCamera.setParameters(params);
257                    }
258                    Log.v(TAG, "Zooming from " + currentZoomLevel + " to " + nextZoomLevel);
259
260                    // sleep allows for zoom animation to finish
261                    Thread.sleep(WAIT_ZOOM_ANIMATION);
262
263                    // take picture
264                    mCamera.takePicture(shutterCallback, rawCallback, jpegCallback);
265                    Thread.sleep(WAIT_GENERIC);
266                    mCamera.stopPreview();
267                    mCamera.release();
268                    output.write(" ," + i);
269                }
270            }
271
272            cleanupZoomImages();
273        }
274        catch (Exception e) {
275            assertTrue("Camera zoom stress test Exception", false);
276            Log.v(TAG, e.toString());
277        }
278        output.write("\n\n");
279        output.close();
280    }
281}
282