Added test handler class and the main activity.

- CameraAnalyzerActivity is the main activity controlling the app.
- Java class CameraTests is the base class for tests in Java
- C++ class ImageTestHandler is the base class for handling tests
- JNI files for communicating between these files are also included.

Change-Id: Ifeea0eb2adb1e11bb6616d72ed67dff089256887
diff --git a/apps/CtsVerifier/include/colorchecker/imagetesthandler.h b/apps/CtsVerifier/include/colorchecker/imagetesthandler.h
new file mode 100644
index 0000000..d66a1d2
--- /dev/null
+++ b/apps/CtsVerifier/include/colorchecker/imagetesthandler.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef IMAGETESTHANDLER_H
+#define IMAGETESTHANDLER_H
+
+#include "vec2.h"
+#include "vec3.h"
+
+class ImageTestHandler{
+  public:
+    ImageTestHandler() {
+        initDebugImage();
+    }
+    ImageTestHandler(int debugHeight, int debugWidth) {
+        initDebugImage(debugHeight, debugWidth);
+    }
+    virtual ~ImageTestHandler() { delete[] mDebugOutput; }
+
+    virtual void processData() {}
+
+    int getDebugWidth() const { return mDebugWidth; }
+    int getDebugHeight() const { return mDebugHeight; }
+    const unsigned char* debug_output() const { return mDebugOutput; }
+    void copyDebugImage(int inputHeight, int inputWidth,
+                        const unsigned char* inputImage);
+
+    void drawPoint(const Vec2i &point, const Vec3i &color);
+    void drawPoint(int row, int column, const Vec3i &color);
+    void drawLine(int angle, int radius, const Vec3i &color);
+
+  protected:
+    void clearDebugImage();
+
+  private:
+    void initDebugImage();
+    void initDebugImage(int debugHeight, int debugWidth);
+
+    unsigned char* mDebugOutput;
+    int mDebugWidth;
+    int mDebugHeight;
+};
+
+#endif
diff --git a/apps/CtsVerifier/jni/cameraanalyzer/com_android_cts_verifier_camera_analyzer_CameraTests.cpp b/apps/CtsVerifier/jni/cameraanalyzer/com_android_cts_verifier_camera_analyzer_CameraTests.cpp
new file mode 100644
index 0000000..51fd0c5
--- /dev/null
+++ b/apps/CtsVerifier/jni/cameraanalyzer/com_android_cts_verifier_camera_analyzer_CameraTests.cpp
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_NDEBUG 0
+
+#define LOG_TAG "CameraTestsJNI"
+#include <utils/Log.h>
+#include "com_android_cts_verifier_camera_analyzer_CameraTests.h"
+#include "android/bitmap.h"
+#include "testingimage.h"
+#include "imagetesthandler.h"
+
+#include <string.h>
+
+jlong Java_com_android_cts_verifier_camera_analyzer_CameraTests_findNative(
+        JNIEnv*      env,
+        jobject      thiz,
+        jobject      inputBitmap) {
+
+    ALOGV("JNI findNative starts!");
+
+    // Verify that we can handle the input bitmap
+    AndroidBitmapInfo inputInfo;
+    AndroidBitmap_getInfo(env, inputBitmap, &inputInfo);
+    if (inputInfo.format != ANDROID_BITMAP_FORMAT_RGBA_8888 &&
+        inputInfo.format != ANDROID_BITMAP_FORMAT_RGB_565) {
+        LOGE("Only RGBA_8888 and RGB_565 bitmaps are supported, type was %d.",
+             inputInfo.format);
+    }
+
+    // Get some references to the fields and class type of ColorChecker
+    jclass thizCls = env->GetObjectClass(thiz);
+    ALOGV("ColorChecker field and classes reference finished!");
+
+    // Get raw inputs and outputs ready
+    uint8_t *inputBuffer = NULL;
+    int result = AndroidBitmap_lockPixels(
+            env,
+            inputBitmap,
+            reinterpret_cast<void**>(&inputBuffer));
+
+    if (result != ANDROID_BITMAP_RESUT_SUCCESS) {
+        LOGE("Unable to lock input bitmap");
+    }
+
+    uint8_t *outputImage = NULL;
+    int outputWidth, outputHeight;
+
+    ALOGV("Input and output images created!");
+
+    // Find the color checker
+    bool success;
+    uint8_t *inputBufferRGBA = NULL;
+    int inputStride;
+    bool freeInputRGBA = false;
+    switch (inputInfo.format) {
+        case ANDROID_BITMAP_FORMAT_RGB_565: {
+            // First convert to RGBA_8888
+            inputBufferRGBA = new uint8_t[inputInfo.width *
+                                          inputInfo.height *
+                                          4];
+            inputStride = inputInfo.width * 4;
+            uint8_t *outP = inputBufferRGBA;
+            for (int y = 0; y < inputInfo.height; y++ ) {
+                uint16_t *inP = (uint16_t*)(&inputBuffer[y * inputInfo.stride]);
+                for (int x = 0; x < inputInfo.width; x++) {
+                    *(outP++) = ( ((*inP) >> 0) & 0x001F) << 3;
+                    *(outP++) = ( ((*inP) >> 5) & 0x003F) << 2;
+                    *(outP++) = ( ((*inP) >> 11) & 0x001F) << 3;
+                    outP++;
+                    inP++;
+                }
+            }
+            freeInputRGBA = true;
+
+            ALOGV("RGB_565 Format with width, height and stride as %d, %d, %d",
+                 inputInfo.width, inputInfo.height, inputStride);
+            break;
+        }
+        case ANDROID_BITMAP_FORMAT_RGBA_8888: {
+            // Already in RGBA
+            inputBufferRGBA = inputBuffer;
+            inputStride = inputInfo.stride;
+            ALOGV("RGB_8888 Format with width, height and stride as %d, %d, %d",
+                 inputInfo.width, inputInfo.height, inputStride);
+            break;
+        }
+    }
+
+    TestingImage *input_testing_image =
+            new TestingImage(inputBufferRGBA, inputInfo.height, inputInfo.width,
+                             4, inputStride, 120, 160);
+
+    long lp = (long)input_testing_image;
+
+    result = AndroidBitmap_unlockPixels(env, inputBitmap);
+    if (result != ANDROID_BITMAP_RESUT_SUCCESS) {
+        LOGE("Unable to unlock input bitmap");
+    }
+
+    if (freeInputRGBA) {
+        ALOGV("Deleteing inputbufferRGBA");
+        delete[] inputBufferRGBA;
+    }
+
+    return lp;
+    ALOGV("Input format switched!");
+}
+
+jlong Java_com_android_cts_verifier_camera_analyzer_CameraTests_createImageTestHandler(
+        JNIEnv*      env,
+        jobject      thiz,
+        jint         debugHeight,
+        jint         debugWidth) {
+
+    ImageTestHandler* testHandler =
+            new ImageTestHandler(debugHeight, debugWidth);
+    long handlerAddress = (long)testHandler;
+    return handlerAddress;
+}
+
+void Java_com_android_cts_verifier_camera_analyzer_CameraTests_cleanUpHandler(
+        JNIEnv*      env,
+        jobject      thiz,
+        jlong        inputHandlerAddress) {
+
+    ImageTestHandler* testHandler = (ImageTestHandler*) (long) inputHandlerAddress;
+    delete testHandler;
+}
+
+void Java_com_android_cts_verifier_camera_analyzer_CameraTests_displayHandlerDebugOutput(
+        JNIEnv*      env,
+        jobject      thiz,
+        jlong        inputHandlerAddress) {
+
+    jclass thizCls = env->GetObjectClass(thiz);
+    jfieldID outputId = env->GetFieldID(thizCls, "mDebugOutput",
+                                        "Landroid/graphics/Bitmap;");
+
+    ImageTestHandler* testHandler = (ImageTestHandler*) (long) inputHandlerAddress;
+    uint8_t *outputImage =  new uint8_t[testHandler->getDebugHeight() *
+                                        testHandler->getDebugWidth() * 4];
+
+    const unsigned char *debugoutput = testHandler->debug_output();
+    memcpy(outputImage, debugoutput, testHandler->getDebugHeight() *
+            testHandler->getDebugWidth() * 4);
+
+    int outputWidth = testHandler->getDebugWidth();
+    int outputHeight = testHandler->getDebugHeight();
+    bool success = false;
+
+    if (outputImage == NULL) {
+        ALOGV("output Image is null!");
+    } else {
+        ALOGV("output Image is ready!");
+    }
+
+    // Create debug bitmap from output image data
+    if (outputImage != NULL) {
+        // Get method handles, create inputs to createBitmap
+        jclass bitmapClass =
+                env->FindClass("android/graphics/Bitmap");
+        jclass bitmapConfigClass =
+                env->FindClass("android/graphics/Bitmap$Config");
+
+        jmethodID createBitmap = env->GetStaticMethodID(
+            bitmapClass, "createBitmap",
+            "(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;");
+
+        jmethodID getConfig = env->GetStaticMethodID(
+            bitmapConfigClass, "valueOf",
+            "(Ljava/lang/String;)Landroid/graphics/Bitmap$Config;");
+
+        // Create bitmap config and bitmap
+        jstring bitmapConfigValue = env->NewStringUTF("ARGB_8888");
+        jobject rgbaConfig = env->CallStaticObjectMethod(bitmapConfigClass,
+                                                         getConfig,
+                                                         bitmapConfigValue);
+        jobject outputBitmap = env->CallStaticObjectMethod(bitmapClass,
+                                                           createBitmap,
+                                                           outputWidth,
+                                                           outputHeight,
+                                                           rgbaConfig);
+        // Copy output image into it
+        uint8_t *outputBuffer;
+        int result = AndroidBitmap_lockPixels(
+                env,
+                outputBitmap,
+                reinterpret_cast<void**>(&outputBuffer) );
+
+        if (result != ANDROID_BITMAP_RESUT_SUCCESS) {
+            LOGE("Unable to lock output bitmap");
+        }
+
+        memcpy(outputBuffer, outputImage, outputWidth * outputHeight * 4);
+
+        result = AndroidBitmap_unlockPixels(env, outputBitmap);
+        if (result != ANDROID_BITMAP_RESUT_SUCCESS) {
+            LOGE("Unable to unlock output bitmap");
+        }
+
+        // Write new Bitmap reference into mDebugOutput class member
+        env->SetObjectField(thiz, outputId, outputBitmap);
+        ALOGV("Copied to outputBitmap");
+        delete [] outputImage;
+        env->DeleteLocalRef(outputBitmap);
+        env->DeleteLocalRef(rgbaConfig);
+        env->DeleteLocalRef(bitmapClass);
+        env->DeleteLocalRef(bitmapConfigClass);
+    }
+}
diff --git a/apps/CtsVerifier/jni/cameraanalyzer/com_android_cts_verifier_camera_analyzer_CameraTests.h b/apps/CtsVerifier/jni/cameraanalyzer/com_android_cts_verifier_camera_analyzer_CameraTests.h
new file mode 100644
index 0000000..e071dc1
--- /dev/null
+++ b/apps/CtsVerifier/jni/cameraanalyzer/com_android_cts_verifier_camera_analyzer_CameraTests.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef JNI_CAMERAANALYZER_CAMERATESTS_H
+#define JNI_CAMERAANALYZER_CAMERATESTS_H
+
+#include <jni.h>
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+JNIEXPORT jlong JNICALL
+Java_com_android_cts_verifier_camera_analyzer_CameraTests_findNative(
+        JNIEnv *env,
+        jobject thiz,
+        jobject inputBitmap);
+
+JNIEXPORT jlong JNICALL
+Java_com_android_cts_verifier_camera_analyzer_CameraTests_createImageTestHandler(
+        JNIEnv*      env,
+        jobject      thiz,
+        jint         debugHeight,
+        jint         debugWidth);
+
+JNIEXPORT void JNICALL
+Java_com_android_cts_verifier_camera_analyzer_CameraTests_cleanUpHandler(
+        JNIEnv*      env,
+        jobject      thiz,
+        jlong        inputHandlerAddress);
+
+JNIEXPORT void JNICALL
+Java_com_android_cts_verifier_camera_analyzer_CameraTests_displayHandlerDebugOutput(
+        JNIEnv*      env,
+        jobject      thiz,
+        jlong        inputHandlerAddress);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/apps/CtsVerifier/lib/colorchecker/imagetesthandler.cpp b/apps/CtsVerifier/lib/colorchecker/imagetesthandler.cpp
new file mode 100644
index 0000000..1c5bc17
--- /dev/null
+++ b/apps/CtsVerifier/lib/colorchecker/imagetesthandler.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_NDEBUG 0
+
+#define LOG_TAG "ImageTestHandler"
+#include <utils/Log.h>
+#include <utils/Timers.h>
+#include <cmath>
+
+#include "vec2.h"
+#include "vec3.h"
+#include "imagetesthandler.h"
+
+void ImageTestHandler::initDebugImage() {
+    mDebugOutput = NULL;
+}
+
+// Initializes the  debug image with a given height and width.
+void ImageTestHandler::initDebugImage(int debugHeight,
+                                      int debugWidth) {
+    mDebugOutput = NULL;
+    mDebugOutput = new unsigned char[debugHeight * debugWidth * 4];
+    memset(mDebugOutput, 0, debugHeight * debugWidth * 4);
+
+    mDebugHeight = debugHeight;
+    mDebugWidth = debugWidth;
+}
+
+// Copies an existing image to the debug image.
+void ImageTestHandler::copyDebugImage(int inputHeight, int inputWidth,
+                                      const unsigned char* inputImage) {
+    if ((inputHeight == mDebugHeight) && (inputWidth == mDebugWidth)) {
+        ALOGV("Copying debug images");
+        memcpy(mDebugOutput, inputImage, mDebugHeight * mDebugWidth * 4);
+    }
+}
+
+void ImageTestHandler::clearDebugImage() {
+    if (mDebugOutput != NULL) {
+        delete[] mDebugOutput;
+        mDebugOutput = new unsigned char[mDebugHeight * mDebugWidth * 4];
+        memset(mDebugOutput, 0, mDebugHeight * mDebugWidth * 4);
+    }
+}
+
+
+// Draws a point of a given color.
+void ImageTestHandler::drawPoint(int row, int column, const Vec3i &color) {
+    if ((row >= 0) && (column >= 0) &&
+        (column < mDebugWidth) && (row < mDebugHeight)) {
+        mDebugOutput[(row*mDebugWidth + column) * 4] = color.r();
+        mDebugOutput[(row*mDebugWidth + column) * 4+1] = color.g();
+        mDebugOutput[(row*mDebugWidth + column) * 4+2] = color.b();
+        mDebugOutput[(row*mDebugWidth + column) * 4+3] = 255;
+    }
+}
+
+// Draws a point in Vec2 format of a given color.
+void ImageTestHandler::drawPoint(const Vec2i &point, const Vec3i &color) {
+    drawPoint((int) point.y(), (int) point.x(), color);
+}
+
+// Draws a line of a given color.
+void ImageTestHandler::drawLine(int angle, int radius, const Vec3i &color) {
+    const int r = color.r();
+    const int g = color.g();
+    const int b = color.b();
+    const int a = 255;
+
+    int shiftedMin = -113;
+    int shiftedMax = 83;
+
+    float radiusDouble = static_cast<float>(radius);
+
+    float angleRad = static_cast<float>(angle) * M_PI / 180.0;
+
+    //ALOGV("draw line for (%d, %d)", angle, radius);
+    for (int i = shiftedMin; i <= shiftedMax; ++i) {
+        float j;
+
+        assert(angle != 0);
+        j = (i - radiusDouble / sin(angleRad)) * tan(angleRad);
+        float x = (static_cast<float>(i) + j) / sqrt(2.0);
+        float y = (j - static_cast<float>(i)) / sqrt(2.0);
+
+        drawPoint(x, y, color);
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/analyzer/CameraAnalyzerActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/analyzer/CameraAnalyzerActivity.java
new file mode 100644
index 0000000..8cce754
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/analyzer/CameraAnalyzerActivity.java
@@ -0,0 +1,480 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.verifier.camera.analyzer;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import android.app.Activity;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.ImageFormat;
+import android.hardware.Camera;
+import android.hardware.Camera.CameraInfo;
+import android.hardware.Camera.Size;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.text.Html;
+import android.text.method.ScrollingMovementMethod;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.Button;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+import android.content.Context;
+
+import java.io.IOException;
+import java.lang.Thread;
+import java.util.List;
+
+/**
+ * Controls the UI activities of the camera quality test app. It is created
+ * as soon as the app started. Users can launch different quality tests with
+ * the buttons in the UI. This class will manage the threading for different
+ * tests. Also it will provide debug output or debug text results for tests.
+ */
+public class CameraAnalyzerActivity extends PassFailButtons.Activity {
+
+    private static final String TAG = "CameraAnalyzer";
+    private SurfaceView mCameraView;
+    private ImageView mResultView;
+    private Button mFindCheckerButton;
+    private Button mExposureCompensationButton;
+    private Button mWhiteBalanceButton;
+    private Button mAutoLockButton;
+    private Button mMeteringButton;
+    private ListView mTestList;
+    private TwoColumnAdapter mAdapter;
+
+    private Camera mCamera;
+    private int mCameraIdx = 0;
+    private boolean mIsCameraOpen = false;
+
+    private PowerManager mPowerManager;
+    private PowerManager.WakeLock mWakeLock;
+
+    private boolean mProcessingPicture = false;
+    private boolean mTestInProgress = false;
+    private final Object mProcessingTest = new Object();
+    private CameraTests mCurrentTest = null;
+
+    private long mCheckerCenterAddress;
+    private long mCheckerRadiusAddress;
+
+    private String mResultReport = "";
+    static final String[] TESTS = new String[] {"Test1", "Test2"};
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.ca_main);
+        setPassFailButtonClickListeners();
+        setInfoResources(R.string.camera_analyzer, R.string.ca_info, -1);
+
+        mFindCheckerButton = (Button) findViewById(R.id.findcheckerboardbutton);
+        mExposureCompensationButton = (Button) findViewById(R.id.exposurecompensationbutton);
+        mWhiteBalanceButton = (Button) findViewById(R.id.whitebalancebutton);
+        mAutoLockButton = (Button) findViewById(R.id.lockbutton);
+        mMeteringButton = (Button) findViewById(R.id.meteringbutton);
+        mCameraView = (SurfaceView) findViewById(R.id.cameraview);
+        mResultView = (ImageView) findViewById(R.id.resultview);
+        mTestList = (ListView) findViewById(R.id.ca_tests);
+        mAdapter = new TwoColumnAdapter(this);
+
+        // Initialize the list view.
+        initializeAdapter();
+        mTestList.setAdapter(mAdapter);
+        mTestList.setOnItemClickListener(mListListener);
+
+        mFindCheckerButton.setOnClickListener(mFindCheckerListener);
+        mExposureCompensationButton.setOnClickListener(mExposureCompensationListener);
+        mWhiteBalanceButton.setOnClickListener(mWhiteBalanceListener);
+        mAutoLockButton.setOnClickListener(mAutoLockListener);
+        mMeteringButton.setOnClickListener(mMeteringListener);
+        mCameraView.getHolder().addCallback(mSurfaceChangeListener);
+
+        // Disables all test buttons except the color checker test one.
+        // They will be enabled after the color checker is located.
+        mExposureCompensationButton.setEnabled(false);
+        mWhiteBalanceButton.setEnabled(false);
+        mAutoLockButton.setEnabled(false);
+        mMeteringButton.setEnabled(false);
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+
+        openCamera(mCameraIdx);
+        Camera.Parameters params = mCamera.getParameters();
+        params.setPictureFormat(ImageFormat.JPEG);
+        params.setPictureSize(640, 480);
+        mCamera.setParameters(params);
+        Log.v(TAG, "Set resolution to 640*480");
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        CameraTests.getCamera().release();
+        mIsCameraOpen = false;
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        MenuInflater inflater = getMenuInflater();
+        inflater.inflate(R.menu.ca_menu, menu);
+
+        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
+        int cameraCount = Camera.getNumberOfCameras();
+        for (int camIdx = 0; camIdx < cameraCount; ++camIdx) {
+            MenuItem cameraMenuItem = menu.add(0, camIdx, Menu.NONE,
+                                               String.format("Open Camera %d", camIdx));
+        }
+      return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        if (item.getItemId() != mCameraIdx) {
+            mCameraIdx = item.getItemId();
+            new SwitchCameraTask().execute(mCameraIdx);
+        }
+        return false;
+    }
+
+    private class SwitchCameraTask extends AsyncTask<Integer, Void, Void> {
+        @Override
+        protected Void doInBackground(Integer... camIdx) {
+            if (mTestInProgress) {
+                synchronized (mProcessingTest) {
+                    try{
+                        Log.v(TAG, "Waiting for test to finish");
+                        mProcessingTest.wait();
+                    } catch (InterruptedException e){
+                         Log.v(TAG, "test wait fails!");
+                    }
+                }
+            }
+
+            openCamera((int)camIdx[0]);
+            return null;
+        }
+    }
+
+    private synchronized void openCamera(int camIdx) {
+        if (mIsCameraOpen) {
+            CameraTests.getCamera().release();
+            Log.v(TAG, "Releasing the cameratests camera");
+        }
+        try {
+            mCamera = Camera.open(camIdx);
+            mIsCameraOpen = true;
+        } catch (RuntimeException e) {
+            throw new RuntimeException("Failed to open the camera", e);
+        }
+
+        try {
+            mCamera.setPreviewDisplay(mCameraView.getHolder());
+        } catch (IOException e) {
+            throw new RuntimeException("Unable to connect camera to display: " + e);
+        }
+        mCamera.startPreview();
+        CameraTests.setCamera(mCamera);
+        //ColorCheckerTest.getSingletonTest().updateCamera();
+        //WhiteBalanceTest.getSingletonTest().updateCamera();
+        //ExposureCompensationTest.getSingletonTest().updateCamera();
+        //MeteringTest.getSingletonTest().updateCamera();
+        //AutoLockTest.getSingletonTest().updateCamera();
+    }
+
+    public Camera getCameraInstance() {
+        return mCamera;
+    }
+
+    public void disableAll() {
+        mExposureCompensationButton.setEnabled(false);
+        mWhiteBalanceButton.setEnabled(false);
+        mAutoLockButton.setEnabled(false);
+        mMeteringButton.setEnabled(false);
+        mFindCheckerButton.setEnabled(false);
+    }
+
+    public void enableAll() {
+        mExposureCompensationButton.setEnabled(true);
+        mWhiteBalanceButton.setEnabled(true);
+        mAutoLockButton.setEnabled(true);
+        mMeteringButton.setEnabled(true);
+        mFindCheckerButton.setEnabled(true);
+    }
+
+    /**
+     * Provides an abstraction for the Camera tests. The camera tests will
+     * run in the background and the results will be shown in the UI thread
+     * after the tests are processed.
+     */
+    private class DebugOutputProcessingTask extends AsyncTask<Integer,
+                                                              Integer,
+                                                              Integer> {
+        @Override
+        protected Integer doInBackground(Integer... cameraTestIndex) {
+            Log.v(TAG, "Do in Background started!");
+
+            mPowerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
+            mWakeLock = mPowerManager.newWakeLock(
+                    PowerManager.SCREEN_DIM_WAKE_LOCK, "CameraQualityTest");
+            mWakeLock.acquire();
+
+            mTestInProgress = true;
+
+            // Processes the camera tests one by one and publishes their
+            // debug output or debug text results after each test is done.
+            mCurrentTest.run((int)cameraTestIndex[0]);
+            publishProgress(cameraTestIndex);
+
+            Log.v(TAG, "Do in Background thread returns!");
+            return cameraTestIndex[0];
+        }
+
+        @Override
+        protected void onProgressUpdate(Integer... cameraTestIndex) {
+            Log.v(TAG, "Prepare to get debug output!");
+
+            // Copies the debug output image or text results to the UI.
+            mResultView.setImageBitmap(mCurrentTest.getDebugOutput());
+            mResultReport += (mCurrentTest.getTestName() + mCurrentTest.getDebugText());
+            mAdapter.notifyDataSetChanged();
+        }
+
+        @Override
+        protected void onPostExecute(Integer cameraTestIndex) {
+
+            // If the test is to find the color checker, copy the memory
+            // address of the found color checker centers and radius to the
+            // CameraTests class' static fields.
+            if (mCurrentTest.copyCheckerAddress()) {
+                mCheckerCenterAddress = CameraTests.getCheckerCenter();
+                mCheckerRadiusAddress = CameraTests.getCheckerRadius();
+            }
+
+            if (mCurrentTest.copyCheckerAddress() ||
+                !mCurrentTest.getTestName().contains("Color Checker")) {
+                // Enables the button of all other tests after the color checker
+                // is found. Now the user can start all other available tests.
+                enableAll();
+            }
+
+            mWakeLock.release();
+            mTestInProgress = false;
+            synchronized (mProcessingTest) {
+                mProcessingTest.notifyAll();
+            }
+
+        }
+    }
+
+    // Creates and runs a new test to find color checker in the captured image.
+    // It is invoked when users press the Find color checker button in the UI.
+    private View.OnClickListener mFindCheckerListener = new View.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            Log.v(TAG, "Running new color checker finding tests!");
+            /*ColorCheckerTest colorCheckerTest = ColorCheckerTest.getSingletonTest();
+
+            mCurrentTest = colorCheckerTest;
+            initializeAdapter();*/
+        }
+    };
+
+    // Creates and runs a new test to test the exposure compensation function.
+    // It is invoked when users press the Exposure Compensation Test button
+    // in the UI.
+    private View.OnClickListener mExposureCompensationListener = new View.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            Log.v(TAG, "Running new exposure compensation tests!");
+
+            /*ExposureCompensationTest exposureCompensationTest =
+                    ExposureCompensationTest.getSingletonTest();
+
+            mCurrentTest = exposureCompensationTest;
+            initializeAdapter();*/
+
+            // Loads the memory address of the checker centers and radius
+            // from the this class and set the two values for the new test.
+            //ExposureCompensationTest.setCheckerAddress(mCheckerCenterAddress,
+            //                                       mCheckerRadiusAddress);
+        }
+    };
+
+    // Creates and runs a new test to test the white balance function.
+    // It is invoked when users press the White Balance Test button in the UI.
+    private View.OnClickListener mWhiteBalanceListener = new View.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            Log.v(TAG, "Running new white balance tests!");
+
+            /*WhiteBalanceTest whiteBalanceTest = WhiteBalanceTest.getSingletonTest();
+
+            mCurrentTest = whiteBalanceTest;
+            initializeAdapter();*/
+
+            // Loads the memory address of the checker centers and radius
+            // from the this class and set the two values for the new test.
+            //WhiteBalanceTest.setCheckerAddress(mCheckerCenterAddress, mCheckerRadiusAddress);
+        }
+    };
+
+    // Creates and runs a new test to test the camera metering function.
+    // It is invoked when users press the Metering Test button in the UI.
+    private View.OnClickListener mMeteringListener = new View.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            Log.v(TAG, "Running new metering tests!");
+
+            /*MeteringTest meteringTest = MeteringTest.getSingletonTest();
+
+            mCurrentTest = meteringTest;
+            initializeAdapter();*/
+
+            // Loads the memory address of the checker centers and radius
+            // from the this class and set the two values for the new test.
+            //MeteringTest.setCheckerAddress(mCheckerCenterAddress, mCheckerRadiusAddress);
+        }
+    };
+
+    // Creates and runs new tests to test the camera auto exposure lock.
+    // It is invoked when users press the AWB and AE Lock Test button
+    // in the UI.
+    private View.OnClickListener mAutoLockListener = new View.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            Log.v(TAG, "Running New auto exposure/wb lock tests!");
+
+            // Loads the memory address of the checker centers and radius
+            // from the this class and set the two values for the new test.
+            //AutoLockTest.setCheckerAddress(mCheckerCenterAddress, mCheckerRadiusAddress);
+
+            // Construct all base case test scenearios for the Auto Lock test.
+            // Detailed documentation on each test can be found in native code.
+            /*AutoLockTest autoLockTest = AutoLockTest.getSingletonTest();
+            autoLockTest.setActivity(CameraAnalyzerActivity.this);
+
+            mCurrentTest = autoLockTest;
+            initializeAdapter();*/
+
+        }
+    };
+
+    // Creates a list listner that launches the experiment with the user's click
+    private AdapterView.OnItemClickListener mListListener = new AdapterView.OnItemClickListener() {
+        @Override
+        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+            Log.v(TAG, String.format("Item %d selected!", position));
+            if (!mTestInProgress) {
+                DebugOutputProcessingTask captureTask = new DebugOutputProcessingTask();
+                disableAll();
+                captureTask.execute(position);
+            }
+        }
+    };
+
+    private SurfaceHolder.Callback mSurfaceChangeListener =
+            new SurfaceHolder.Callback() {
+
+        // Sets the aspect ratio of the camera preview to 4:3
+        @Override
+        public void surfaceChanged(SurfaceHolder holder,
+                                   int format,
+                                   int width,
+                                   int height) {
+            int x = mCameraView.getWidth();
+            int y = mCameraView.getHeight();
+            Log.v(TAG, String.format("Measures are %d, %d", x, y));
+
+            if ( x > 4.0 / 3.0 * y) {
+                android.view.ViewGroup.LayoutParams lp = mCameraView.getLayoutParams();
+                lp.height =  y;
+                lp.width = (int)(4.0 / 3.0 * lp.height);
+                Log.v(TAG, String.format("params are %d, %d", lp.width, lp.height));
+                mCameraView.setLayoutParams(lp);
+            }
+
+            try {
+                mCamera.setPreviewDisplay(mCameraView.getHolder());
+            } catch (IOException e) {
+                throw new RuntimeException("Unable to connect camera to display: " + e);
+            }
+            CameraTests.setCameraView(mCameraView);
+            mCamera.startPreview();
+        }
+
+        @Override
+        public void surfaceCreated(SurfaceHolder holder) {}
+
+        @Override
+        public void surfaceDestroyed(SurfaceHolder holder) {}
+    };
+
+    @Override
+    public String getTestDetails() {
+        return mResultReport;
+    }
+
+    class TwoColumnAdapter extends ArrayAdapter<String> {
+        TwoColumnAdapter(Context context) {
+            super(context, R.layout.ca_row);
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            LayoutInflater inflater = getLayoutInflater();
+            View row = inflater.inflate(R.layout.ca_row, parent, false);
+            TextView nameField = (TextView) row.findViewById(R.id.caTestName);
+            TextView resultField = (TextView) row.findViewById(R.id.caTestResult);
+            if (mCurrentTest != null) {
+                nameField.setText(mCurrentTest.getTestName(position));
+                resultField.setText(mCurrentTest.getResult(position));
+            }
+            return row;
+        }
+    }
+
+    private void initializeAdapter() {
+        mAdapter.clear();
+        if (mCurrentTest != null) {
+            for (int i = 0; i < mCurrentTest.getNumTests(); ++i) {
+                mAdapter.add(mCurrentTest.getTestName(i));
+            }
+        }
+    }
+
+    public int getCameraIdx() {
+        return mCameraIdx;
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/analyzer/CameraTests.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/analyzer/CameraTests.java
new file mode 100644
index 0000000..7cea762
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/analyzer/CameraTests.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.camera.analyzer;
+
+import android.graphics.Bitmap;
+import android.hardware.Camera;
+import android.os.Environment;
+import android.util.Log;
+import android.view.SurfaceView;
+
+import java.io.FileOutputStream;
+import java.io.File;
+import java.lang.Runnable;
+
+/**
+ * Provides an abstraction for all camera tests and allows communication
+ * between camera test classes with the UI thread. This base class provides
+ * functions to contruct and access debug output images. It can access and
+ * set the pointer address of checkerboard centers and radius. It also provides
+ * native methods to convert an image shot by the camera into a native
+ * character array. Another native method it provides is to create a native
+ * test handler with desired debug height and width.
+ */
+public abstract class CameraTests{
+
+    private static final String TAG = "CameraTests";
+
+    /** Memory address of the color checker centers. */
+    private static long sCheckerCenterAddress = 0;
+    /** Memory address of the color checker radius. */
+    private static long sCheckerRadiusAddress = 0;
+    /** The surface view linked with the camera preview. */
+    private static SurfaceView sCameraView;
+    /** Image debug output. */
+    private Bitmap mDebugOutput;
+    /** Shared camera instance. */
+    protected static Camera mTestCamera = null;
+
+    /**
+     * Constructs the base CameraTests class.
+     */
+    public CameraTests() {}
+
+    /**
+     * Returns debug Bitmap image. In the test to find the color checker,
+     * the debug image will be the captured image with a matched color checker
+     * overlay on top. In the exposure compensation test, the debug image
+     * will be the response curve of the camera.
+     * @return A low-resolution Bitmap to be displayed in the UI.
+     */
+    public Bitmap getDebugOutput() {
+        return mDebugOutput;
+    }
+
+    public String getDebugText() {
+        return "";
+    }
+
+    public abstract String getTestName();
+
+    /**
+     * Gets the detailed report for CTS output.
+     */
+    public String getResultText(){
+        return "Details not available \n";
+    }
+
+    /**
+     * Provides a polymorphism to start the run() method for all child classes.
+     */
+    public abstract void run(int index);
+
+    public SurfaceView getCameraView() {
+        return sCameraView;
+    }
+
+    public static void setCameraView(SurfaceView cameraView) {
+        sCameraView = cameraView;
+    }
+
+    /**
+     * Refreshes the camera instance when the activity opens a new camera.
+     */
+    public static void setCamera(Camera newCamera) {
+        mTestCamera = newCamera;
+    }
+
+    public static Camera getCamera() {
+        return mTestCamera;
+    }
+
+    /**
+     * Sets the memory address of the checker centers and checker radius.
+     *
+     * @param inputCenterAddress the new memory address of
+     *                           the color checker centers
+     * @param inputRadiusAddress the new memory address of
+     *                           the color checker radius
+     */
+    public static void setCheckerAddress(long inputCenterAddress, long inputRadiusAddress) {
+        sCheckerCenterAddress = inputCenterAddress;
+        sCheckerRadiusAddress = inputRadiusAddress;
+    }
+
+    /**
+     * Provides polymorphism to indicate whether the checker memory addresses
+     * should be copied.
+     *
+     * @return <code>true</code> if the class invoking the method needs to
+     *                           update the memory address of the color checker
+     *                           centers and radius;
+     *         <code>false</code> if the class invoking the method does NOT
+     *                           update the memory address of the color checker
+     *                           centers and radius.
+     */
+    public boolean copyCheckerAddress() {
+        return false;
+    }
+
+    public void cleanUp() {
+    }
+
+    public static long getCheckerCenter() {
+        return sCheckerCenterAddress;
+    }
+
+    public static long getCheckerRadius() {
+        return sCheckerRadiusAddress;
+    }
+
+    public abstract String getTestName(int index);
+
+    public abstract String getResult(int index);
+
+    public abstract int getNumTests();
+
+    /**
+     * Provides a native method to convert the input Bitmap of the captured
+     * image into a native character array and constructs a native image class
+     * with this character array. This method currently supports conversion
+     * of Bitmaps in the formats of RGB_565 and RGB_8888.
+     *
+     * @param input the input Bitmap, which is decoded from the camrea data.
+     *
+     * @return the memory address of the native image class which contains
+     * the character array.
+     */
+    public native long findNative(Bitmap input);
+
+    /**
+     * Provides a native method to create a native image handler class. The
+     * native image handler class is the base class for all test classes and
+     * contains the debug output as a character array.
+     *
+     * @param outputHeight the desired height for the debug output
+     * @param outputWidth the desired width for the debug output
+     *
+     * @return the memory address of the native test handler class.
+     */
+    public native long createImageTestHandler(int outputHeight, int outputWidth);
+
+    /**
+     * Provides a native method to clean up the memory taken by the handler.
+     *
+     * @param handlerAddress the memory address of the native test handler,
+     * which contains the debug output.
+     */
+    public native void cleanUpHandler(long handlerAddress);
+
+    /**
+     * Provides a native method to convert the native debug output from
+     * character array back to Bitmap and copy it to the debug output of this
+     * class.
+     *
+     * @param handlerAddress the memory address of the native test handler,
+     * which contains the debug output.
+     */
+    public native void displayHandlerDebugOutput(long handlerAddress);
+
+    static {
+        System.loadLibrary("cameraanalyzer");
+    }
+}