Home | History | Annotate | Download | only in analyzer
      1 /*
      2  * Copyright (C) 2011 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 com.android.cts.verifier.camera.analyzer;
     18 
     19 import android.graphics.Bitmap;
     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.util.Log;
     26 import android.widget.ImageView;
     27 
     28 import java.io.FileOutputStream;
     29 import java.io.FileNotFoundException;
     30 import java.io.IOException;
     31 import java.util.ArrayList;
     32 import java.util.List;
     33 import java.util.Random;
     34 
     35 /**
     36  * Implements a test to verify whether the camera metering system works as
     37  * described in the API.
     38  *
     39  * The test consists two sub-categories. The first one has tests with only
     40  * one metering area defined. The second one has tests with two metering areas
     41  * defined. For each single sub-test, we use a random number generator to
     42  * decide where to put some of the metering areas to and how much weight should
     43  * be assigned to each area. For different tests, we use different ways to
     44  * define other metering areas and their weight, in order to cover all possible
     45  * fail cases. The metering areas are contrained to the grey squares in the
     46  * bottom of the color checker.
     47  */
     48 public class MeteringTest extends CameraTests {
     49 
     50     private static final String TAG = "MeteringTest";
     51 
     52     /** A long wait.*/
     53     private static final int LONG_SLEEP = 4000;
     54     /** Debug result in text. */
     55     private String mDebugText;
     56     /** Thread lock. */
     57     private final Object mProcessingImage = new Object();
     58     /** Memory address of the native test handler. */
     59     private long mTestHandler;
     60     /** The maximum number of metering area the device supports. */
     61     private int mMaxNumMeteringArea;
     62     /** The metering areas. */
     63     private List<Camera.Area> mGreyAreas;
     64     /** The coordinates of the grey squares on the color checker. */
     65     private int[] mGreyCoordinates = new int[24];
     66     /** Random number generator. */
     67     private final Random mRandomGenerator = new Random();
     68     /** Reference comparison result for tests. */
     69     private ArrayList<Boolean>  mReferenceCompareResults;
     70     /** Number of tests in the same instance. */
     71     private int mTestCount;
     72     /** Reference test logs. */
     73     private ArrayList<String> mReferenceLogs;
     74     /** Test result to show. */
     75     private int[] mTestResults;
     76     /** Number of tests. */
     77     private int mNumTests;
     78     /** Camera Parameters. */
     79     private Camera.Parameters mParams;
     80     /** Singleton test instance. */
     81     private static MeteringTest singletonTest = null;
     82 
     83     /** Constructs a <code>MeteringTest</code> instance with the given
     84      * camera pointer.
     85      */
     86     private MeteringTest() {
     87         super();
     88     }
     89 
     90     public void updateCamera() {
     91         // Looks up how many metering area the device supports.
     92         mParams = mTestCamera.getParameters();
     93         mMaxNumMeteringArea = mParams.getMaxNumMeteringAreas();
     94         Log.v(TAG, String.format("Maximum number if metering area is %d", mMaxNumMeteringArea));
     95         if (mMaxNumMeteringArea == 0) {
     96             mDebugText = "Custom Metering not supported!";
     97             Log.v(TAG, "Custom Metering not supported");
     98         }
     99     }
    100 
    101     public static synchronized MeteringTest getSingletonTest() {
    102         if (singletonTest == null) {
    103             Log.v(TAG, "Creating a new MeteringTest instance");
    104             singletonTest = new MeteringTest();
    105             singletonTest.initializeTest();
    106         }
    107         return singletonTest;
    108     }
    109 
    110     private void initializeTest() {
    111         // Creates a native metering test handler.
    112         mTestHandler = createMeteringTest();
    113         mDebugText = new String();
    114         mReferenceCompareResults = new ArrayList<Boolean>();
    115         mReferenceLogs = new ArrayList<String>();
    116         mNumTests = 3;
    117         mTestResults = new int[mNumTests];
    118         for (int i = 0; i < mNumTests; ++i) {
    119             mTestResults[i] = CameraTests.CAMERA_TEST_NOT_RUN;
    120         }
    121     }
    122 
    123     /**
    124      * Runs the metering test instance.
    125      */
    126     @Override
    127     public synchronized void run(int index) {
    128         if (index == 0) {
    129             run(1);
    130             run(2);
    131             return;
    132         }
    133         Log.v(TAG, "MeteringTest thread started!");
    134 
    135         // Finds the coordinates of the grey squares on the color checker.
    136         // The coordinate system has (-1000, -1000) on the upper left corner.
    137         // And (1000, 1000) on the bottom right corner.
    138         findGreyCoordinates(mGreyCoordinates, getCheckerCenter(), getCheckerRadius());
    139 
    140         if (mMaxNumMeteringArea > 0) {
    141             mTestCount = 0;
    142             // Runs the metering tests category by category.
    143             switch (index) {
    144                 case 1:
    145                     runOneAreaTest();
    146                     break;
    147                 case 2:
    148                     if (mMaxNumMeteringArea > 1) {
    149                         runTwoAreasTest();
    150                     }
    151                     break;
    152                 default:
    153                     break;
    154             }
    155         }
    156 
    157         mParams = mTestCamera.getParameters();
    158         mParams.setMeteringAreas(null);
    159         mTestCamera.setParameters(mParams);
    160 
    161         boolean[] testCompareResults = new boolean[2 * mTestCount];
    162 
    163         // Processes the image data taken so far and stores the test results.
    164         processMeteringTest(mTestHandler, testCompareResults);
    165         // Prepares debug output based on the test results.
    166         prepareDebugText(testCompareResults, index);
    167 
    168         mReferenceCompareResults.clear();
    169         mReferenceLogs.clear();
    170     }
    171 
    172     /**
    173      * Prepares the test results in HTML text string to show in the UI.
    174      *
    175      * If the test result is the same as the reference result, the text will be
    176      * shown in green. Otherwise it would be shown as red.
    177      *
    178      * @param testCompareResults the array storing the comparison results from
    179      * the data taken by the camera so far.
    180      */
    181     private void prepareDebugText(boolean[] testCompareResults, int index) {
    182         mDebugText = "";
    183         boolean groupTestPassed = true;
    184         for (int i = 0; i < mTestCount; ++i) {
    185               String testLog;
    186               boolean testPassed = true;
    187               testLog = mReferenceLogs.get(i);
    188               mDebugText += (testLog + "<br/>");
    189 
    190               if (testCompareResults[i * 2] == mReferenceCompareResults.get(i * 2)) {
    191                   mDebugText += String.format(
    192                       "Picture 1 equivalent to Picture 2 is %b \n",
    193                       testCompareResults[i * 2]);
    194               } else {
    195                   mDebugText += String.format(
    196                       "Picture 1 equivalent to Picture 2 is %b \n",
    197                       testCompareResults[i * 2]);
    198                   testPassed = false;
    199               }
    200 
    201               if (testCompareResults[i * 2 + 1] == mReferenceCompareResults.get(i * 2 + 1)) {
    202                   mDebugText += String.format(
    203                       "Picture 1 darker than Picture 2 is %b \n",
    204                       testCompareResults[i * 2 + 1]);
    205               } else {
    206                   mDebugText += String.format(
    207                       "Picture 1 darker than Picture 2 is %b \n",
    208                       testCompareResults[i * 2 + 1]);
    209                   testPassed = false;
    210               }
    211 
    212               if (testPassed) {
    213                   mDebugText += "Test passed! \n";
    214               } else {
    215                   mDebugText += "Test failed! \n";
    216                   groupTestPassed = false;
    217               }
    218               Log.v(TAG, String.format("%s", mDebugText));
    219          }
    220 
    221         if (groupTestPassed) {
    222             mTestResults[index] = CameraTests.CAMERA_TEST_SUCCESS;
    223         } else {
    224             mTestResults[index] = CameraTests.CAMERA_TEST_FAILURE;
    225         }
    226 
    227     }
    228 
    229     /**
    230      * Runs tests to check whether the metering functionalities work properly
    231      * when one metering area is added.
    232      */
    233     private void runOneAreaTest() {
    234         int weight1;
    235         int weight2;
    236         int square1;
    237         int square2;
    238 
    239         Log.v(TAG, "Running one area Test");
    240 
    241         // Test case 1: Two images have the same metering area. Image 1 has
    242         // a diffent weight than Image 2. The result images should be
    243         // identical.
    244         // Tests whether weight normalization works.
    245         square1 = mRandomGenerator.nextInt(6);
    246         weight1 = mRandomGenerator.nextInt(100) + 1;
    247         runSingleTest(square1, square1, weight1);
    248 
    249         square2 = square1;
    250         weight2 = mRandomGenerator.nextInt(100) + 901;
    251         runSingleTest(square2, square2, weight2);
    252         mReferenceCompareResults.add(true);
    253         mReferenceCompareResults.add(false);
    254         Log.v(TAG, String.format("Running test for %d square with weights %d, %d",
    255                                  square1, weight1, weight2));
    256         mReferenceLogs.add(String.format(
    257             "Running test for %d 1x1 square with weights %d, %d", square1, weight1, weight2));
    258         ++mTestCount;
    259 
    260         // Test case 2: Two images have different metering areas. Image 1 has
    261         // one of the grey squares as its metering area. Image 2 has a darker
    262         // grey square as its metering area. The weights for both images are
    263         // the same. Image 1 is expected to be darker than Image 2.
    264         // Tests whether metering on uni-brightness patches work.
    265         square1 = mRandomGenerator.nextInt(5);
    266         weight1 = mRandomGenerator.nextInt(1000) + 1;
    267         runSingleTest(square1, square1, weight1);
    268 
    269         square2 = mRandomGenerator.nextInt(6 - square1 - 1) + square1 + 1;
    270         weight2 = weight1;
    271         runSingleTest(square2, square2, weight2);
    272         mReferenceCompareResults.add(false);
    273         mReferenceCompareResults.add(true);
    274         mReferenceLogs.add(String.format(
    275             "Running test for %d, %d 1x1 square with weight %d", square1, square2, weight1));
    276         ++mTestCount;
    277 
    278         // Test case 3: Two images have different metering areas. Image one has
    279         // one of the grey squares as its metering area. Image 2 has a
    280         // rectangle which contains Image 1's metering area and the neighboring
    281         // darker grey square. The weights for both tests are the same. Image 1
    282         // is expected to be darker than Image 2.
    283         // Tests whether metering on patches with different brightness works.
    284         square1 = mRandomGenerator.nextInt(5);
    285         weight1 = mRandomGenerator.nextInt(1000) + 1;
    286         runSingleTest(square1, square1, weight1);
    287 
    288         square2 = square1;
    289         weight2 = weight1;
    290         runSingleTest(square2, square2 + 1, weight2);
    291         mReferenceCompareResults.add(false);
    292         mReferenceCompareResults.add(true);
    293         mReferenceLogs.add(String.format(
    294             "Running test for %d 1x1, 1x2 square with weight %d", square1, weight1));
    295         ++mTestCount;
    296 
    297         // Test case 4: Two images have different metering areas. Image one has
    298         // two neighboring grey squares as its metering area. Image 2 has two
    299         // darker neighboring grey squares as its metering area. Weights are
    300         // the same for both images. Image 1 is expected to be darker than
    301         // Image 2.
    302         // Tests whether metering on two mixed-brightness patches work.
    303         square1 = mRandomGenerator.nextInt(4);
    304         weight1 = mRandomGenerator.nextInt(1000) + 1;
    305         runSingleTest(square1, square1 + 1, weight1);
    306 
    307         square2 = mRandomGenerator.nextInt(5 - square1 - 1) + square1 + 1;
    308         weight2 = weight1;
    309         runSingleTest(square2, square2 + 1, weight2);
    310         mReferenceCompareResults.add(false);
    311         mReferenceCompareResults.add(true);
    312         mReferenceLogs.add(String.format(
    313             "Running test for %d, %d 1x2 square with weight %d", square1, square2, weight1));
    314         ++mTestCount;
    315 
    316         // Test case 5: Two images have different metering areas. Image one has
    317         // three neighboring grey squares as its metering area. Image 2 has
    318         // three darker neighboring grey squares as its metering area. Weights
    319         // are the same. Image 1 is expected to be darker than Image 2.
    320         // Tests whether metering on three mixed-brightness patches work.
    321         square1 = mRandomGenerator.nextInt(3);
    322         weight1 = mRandomGenerator.nextInt(1000) + 1;
    323         runSingleTest(square1, square1 + 2, weight1);
    324 
    325         square2 = mRandomGenerator.nextInt(4 - square1 - 1) + square1 + 1;
    326         weight2 = weight1;
    327         runSingleTest(square2, square2 + 2, weight2);
    328         mReferenceCompareResults.add(false);
    329         mReferenceCompareResults.add(true);
    330         mReferenceLogs.add(String.format(
    331             "Running test for %d, %d 1x3 square with weight %d", square1, square2, weight1));
    332         ++mTestCount;
    333     }
    334 
    335     /**
    336      * Runs metering tests to verify the functionalities when there are two
    337      * areas set as the metering area.
    338      */
    339     private void runTwoAreasTest() {
    340         int[] weight1 = new int[2];
    341         int[] weight2 = new int[2];
    342         int[] square1Start = new int[2];
    343         int[] square2Start = new int[2];
    344         int[] square1End = new int[2];
    345         int[] square2End = new int[2];
    346 
    347         Log.v(TAG, "Running two-area Test");
    348 
    349         // Test case 1: Image 1 has two metering areas. They are two adjacent
    350         // grey squares (each set as a metering area). The two areas have the
    351         // same weight. Image 2 has one metering area, which is the combination
    352         // of Image 1's two metering areas as a rectangle. The weight is the
    353         // same as that of Image 1's individual area. Image 1 is expected to
    354         // be equivalent to Image 2.
    355         // Tests whether having seperating a metering area into two will yield
    356         // the same result.
    357         square1Start[0] = mRandomGenerator.nextInt(5);
    358         square1End[0] = square1Start[0];
    359         weight1[0] = mRandomGenerator.nextInt(1000) + 1;
    360         square1Start[1] = square1Start[0] + 1;
    361         square1End[1] = square1Start[1];
    362         weight1[1] = weight1[0];
    363         runMultipleAreaTest(square1Start, square1End, weight1);
    364 
    365         square2Start[0] = square1Start[0];
    366         weight2[0] = weight1[0];
    367         runSingleTest(square2Start[0], square2Start[0] + 1, weight2[0]);
    368         mReferenceCompareResults.add(true);
    369         mReferenceCompareResults.add(false);
    370         mReferenceLogs.add(String.format(
    371             "Running test for %d, %d 1x1 square with weight %d",
    372             square1Start[0], square1Start[1], weight1[0]));
    373         ++mTestCount;
    374 
    375         // Test case 2: Image 1 has two metering areas. They are two random
    376         // grey squareson the color checker. The brighter square has a larger
    377         // weight than the darker square. Image 2 has the same two metering
    378         // areas as Image 1. The weights for both are equal to the weight of
    379         // the darker square in Image 1, which is smaller than the weight of
    380         // the brighter square in Image 1. Image 1 is expected to be darker
    381         // than Image 2.
    382         // Tests whether giving one of the two metering areas a different
    383         // weight would change the image in the correct way.
    384         square1Start[0] = mRandomGenerator.nextInt(4);
    385         square1End[0] = square1Start[0];
    386         weight1[0] = mRandomGenerator.nextInt(100) + 901;
    387         square1Start[1] = mRandomGenerator.nextInt(5 - square1Start[0] - 1) + square1Start[0] + 1;
    388         square1End[1] = square1Start[1];
    389         weight1[1] = mRandomGenerator.nextInt(100) + 1;
    390         runMultipleAreaTest(square1Start, square1End, weight1);
    391 
    392         square2Start[0] = square1Start[0];
    393         square2End[0] = square2Start[0];
    394         weight2[0] = weight1[1];
    395         square2Start[1] = square1Start[1];
    396         square2End[1] = square1End[1];
    397         weight2[1] = weight2[0];
    398         runMultipleAreaTest(square2Start, square2End, weight2);
    399         mReferenceCompareResults.add(false);
    400         mReferenceCompareResults.add(true);
    401         mReferenceLogs.add(String.format(
    402             "Running test for %d, %d 1x1 square with weight %d, %d",
    403             square1Start[0], square1Start[1], weight1[0], weight2[1]));
    404         ++mTestCount;
    405 
    406         // Test case 3: Image 1 has two metering areas. Both are set to the
    407         // same random grey square on the color checker. The weight for both
    408         // are the same. Image 2 has one meterig area, which is the same as
    409         // Image 1's chosen grey square. The weight for it is the same as
    410         // Image 1's weight for one metering area. Image 1 is expected to be
    411         // equivalent to Image 2.
    412         // Tests whether defining overlapping metering area works.
    413         square1Start[0] = mRandomGenerator.nextInt(6);
    414         square1End[0] = square1Start[0];
    415         weight1[0] = mRandomGenerator.nextInt(1000) + 1;
    416         square1Start[1] = square1Start[0];
    417         square1End[1] = square1Start[1];
    418         weight1[1] = weight1[0];
    419         runMultipleAreaTest(square1Start, square1End, weight1);
    420 
    421         square2Start[0] = square1Start[0];
    422         square2End[0] = square2Start[0];
    423         weight2[0] = weight1[0];
    424         runSingleTest(square2Start[0], square2End[0], weight2[0]);
    425         mReferenceCompareResults.add(true);
    426         mReferenceCompareResults.add(false);
    427         mReferenceLogs.add(String.format(
    428             "Running test for %d 1x1 square with weight %d,", square1Start[0], weight1[0]));
    429         ++mTestCount;
    430 
    431         // Test case 4: Image 1 has two metering areas. The first one is a
    432         // grey square on the color checker. The second one is a rectangle
    433         // containing the first metering area's grey square and its neighboring
    434         // darker square. The weights for both metering area are the same.
    435         // Image 2 has two metering areas. The first one is the same grey
    436         // square as Image 1's first metering area. The second one is the
    437         // neighboring darker grey square. The weight for the brighter square
    438         // is double the weight of Image 1's weights for each metering area.
    439         // The weight for the Image 2's darker grey square is the same as
    440         // Image 1's weight for each of its metering areas. Image 1 is expected
    441         // to be equivalent to Image 2.
    442         // Tests whether the weights for overlapping metering area add up.
    443         square1Start[0] = mRandomGenerator.nextInt(2);
    444         square1End[0] = square1Start[0];
    445         weight1[0] = mRandomGenerator.nextInt(500) + 1;
    446         square1Start[1] = square1Start[0];
    447         square1End[1] = square1Start[1] + 1;
    448         weight1[1] = weight1[0];
    449         runMultipleAreaTest(square1Start, square1End, weight1);
    450 
    451         square2Start[0] = square1Start[0];
    452         square2End[0] = square1End[0];
    453         weight2[0] = weight1[0] * 2;
    454         square2Start[1] = square2Start[0] + 1;
    455         square2End[1] = square2Start[1];
    456         weight2[1] = weight1[1];
    457         runMultipleAreaTest(square2Start, square2End, weight2);
    458         mReferenceCompareResults.add(true);
    459         mReferenceCompareResults.add(false);
    460         mReferenceLogs.add(String.format(
    461             "Running test for %d 1x2 1x1 and 1x2 square with weight %d,",
    462             square1Start[0], weight1[0]));
    463         ++mTestCount;
    464     }
    465 
    466     /**
    467      * Runs the metering test when multiple metering areas are defined.
    468      *
    469      * @param startIndex the array storing the index of the grey square where
    470      * one metering area starts
    471      * @param endIndex the array storing the index of the grey square where one
    472      * metering area ends.
    473      * @param weight the array storing the weight for each metering area.
    474      */
    475     private void runMultipleAreaTest(int[] startIndex, int[] endIndex, int[] weight) {
    476         int numAreas = startIndex.length;
    477         mParams = mTestCamera.getParameters();
    478         List<Camera.Area> meteringAreas = new ArrayList<Camera.Area>();
    479 
    480         for (int i = 0; i < numAreas; ++i) {
    481             meteringAreas.add(makeArea(startIndex[i], endIndex[i], weight[i]));
    482             Log.v(TAG, String.format("Add metering area for %d, %d, %d",
    483                                      startIndex[i], endIndex[i], weight[i]));
    484         }
    485         mParams.setMeteringAreas(meteringAreas);
    486         mTestCamera.setParameters(mParams);
    487         takePicture();
    488     }
    489 
    490     /**
    491      * Runs the metering test when one metering area is defined.
    492      *
    493      * @param startIndex the index of the grey square where the metering area
    494      * starts
    495      * @param endIndex the index of the grey square where the metering area
    496      * ends.
    497      * @param weight the weight for the metering area.
    498      */
    499     private void runSingleTest(int startIndex, int endIndex, int weight) {
    500         mParams = mTestCamera.getParameters();
    501         List<Camera.Area> meteringAreas = new ArrayList<Camera.Area>();
    502 
    503         Log.v(TAG, String.format("Single test for %d, %d, %d", startIndex, endIndex, weight));
    504         meteringAreas.add(makeArea(startIndex, endIndex, weight));
    505         mParams.setMeteringAreas(meteringAreas);
    506         mTestCamera.setParameters(mParams);
    507         takePicture();
    508     }
    509 
    510     /**
    511      * Takes picture with the camera instance linked to this test class.
    512      */
    513     private void takePicture() {
    514         // Waits for the metering to be stable
    515         try{
    516             Log.v(TAG, "Waiting for metering");
    517             Thread.sleep(LONG_SLEEP);
    518             Log.v(TAG, "END Waiting");
    519         } catch (InterruptedException e) {}
    520 
    521         mTestCamera.takePicture(null, null, null, mTestJpegListener);
    522 
    523         // Locks thread until picture is taken and ready for processing.
    524         synchronized (mProcessingImage) {
    525             try{
    526                 Log.v(TAG, "Start waiting for Image");
    527 
    528                 mProcessingImage.wait();
    529             } catch (InterruptedException e) {
    530                 Log.v(TAG, "Callback wait fails!");
    531             }
    532         }
    533     }
    534 
    535     /**
    536      * Constructs a <code>Camera.Area</code> object of the metering area.
    537      * Given the start and end index of one metering area, it takes the upper
    538      * left corner of the starting square and the bottom right corner of the
    539      * end square to construct an Area.
    540      *
    541      * @param startIndex the index of the grey square where the metering area
    542      * starts
    543      * @param endIndex the index of the grey square where the metering area
    544      * ends
    545      * @param weight the weight of this metering area.
    546      *
    547      * @return a <code>Camera.Area</code> object which represents this metering
    548      * area
    549      */
    550     private Camera.Area makeArea(int startIndex, int endIndex, int weight) {
    551         Rect areaRect = new Rect(mGreyCoordinates[startIndex * 4],
    552                                  mGreyCoordinates[startIndex * 4 + 1],
    553                                  mGreyCoordinates[endIndex * 4 + 2],
    554                                  mGreyCoordinates[endIndex * 4 + 3]);
    555         Camera.Area area = new Camera.Area(areaRect, weight);
    556 
    557         return area;
    558     }
    559 
    560     @Override
    561     public String getDebugText() {
    562         return mDebugText;
    563     }
    564 
    565     @Override
    566     public String getResultText() {
    567         return mDebugText;
    568     }
    569 
    570     @Override
    571     public String getTestName() {
    572         return "Metering Test: \n";
    573     }
    574 
    575     @Override
    576     public String getTestName(int index) {
    577         switch (index) {
    578             case 0:
    579                 return "Run all tests";
    580             case 1:
    581                 return "One metering area tests";
    582             case 2:
    583                 return "Multiple metering areas tests";
    584             default:
    585                 return "";
    586         }
    587     }
    588 
    589     @Override
    590     public int getResult(int index) {
    591         return mTestResults[index];
    592     }
    593 
    594     @Override
    595     public int getNumTests() {
    596         return mNumTests;
    597     }
    598 
    599     private Camera.PictureCallback mTestJpegListener = new Camera.PictureCallback() {
    600         public void onPictureTaken(byte[] data, Camera mCamera) {
    601             Log.v(TAG, "Shutter pressed down!");
    602             Bitmap inputImage;
    603             try {
    604                 FileOutputStream outStream = new FileOutputStream(
    605                     String.format("/sdcard/metering%d.jpg", System.currentTimeMillis()));
    606                 outStream.write(data);
    607                 outStream.close();
    608             } catch (FileNotFoundException e) {
    609             } catch (IOException e) {}
    610 
    611             // Decodes the input data of the camera.
    612             inputImage = BitmapFactory.decodeByteArray(data, 0, data.length);
    613 
    614             // Records the memory address of the native image class instance.
    615             long bufferAddress = findNative(inputImage);
    616             Log.v(TAG, "findNative method finishes");
    617 
    618             // Cleans up the memory taken by the bitmap.
    619             inputImage.recycle();
    620             data = null;
    621             inputImage = null;
    622             System.gc();
    623 
    624             // Add the image data to the native test handler.
    625             createMeteringClass(bufferAddress, mTestHandler,
    626                                 getCheckerCenter(), getCheckerRadius());
    627             mCamera.startPreview();
    628 
    629             // Releases thread lock after the image is processed.
    630             synchronized (mProcessingImage) {
    631                 mProcessingImage.notifyAll();
    632             }
    633         }
    634     };
    635 
    636     /**
    637      * Finds the coordinates of the grey squares on the color checker.
    638      * The coordinates are computed by using the checker center and radius.
    639      * The coordinates are converted to a system with (-1000, -1000) in the
    640      * upper left corner and (1000, 1000) in the  bottom right corner.
    641      *
    642      * @param greyCoordinates the array to store the coordinates of the grey
    643      * squares
    644      * @param checkerCenterAddress the memory address pointing to the vector
    645      * storing the color checker's centers.
    646      * @param checkerRadiusAddress the memory address pointing to the vetor
    647      * storing the color checker's radius.
    648      */
    649     private native void findGreyCoordinates(int[] greyCoordinates,
    650                                             long checkerCenterAddress, long checkerRadiusAddress);
    651 
    652     /**
    653      * Creates the native metering test handler with no debug image.
    654      *
    655      * @return the memory address pointing to the native test handler instance
    656      */
    657     private native long createMeteringTest();
    658 
    659     /**
    660      * Adds the data from the native image class instance to the native test
    661      * handler.
    662      *
    663      * @param bufferAddress the meory address of the native image class
    664      * @param handlerAddress the memory address of the native test handler
    665      * @param checkerCenterAddress the memory address of the checker cneters
    666      * @param checkerRadiusAddress the meory address of the checker radius
    667      */
    668     private native void createMeteringClass(long bufferAddress, long handlerAddress,
    669                                             long checkerCenterAddress,
    670                                             long checkerRadiusAddress);
    671 
    672     /**
    673      * Process the data stored in the native test handler and stores the
    674      * comparison results.
    675      *
    676      * @param handlerAddress the memory address of the native test handler
    677      * @param testCompareResults the boolean array to store the test comparison
    678      * results
    679      */
    680     private native void processMeteringTest(long handlerAddress, boolean[] testCompareResults);
    681 
    682     static {
    683         System.loadLibrary("cameraanalyzer");
    684     }
    685 }
    686