Update EVS app and manager for HAL revisions

The HAL contract, particuarly with respect to opening and closing
devices has changed and the manager and app have been revised
accordingly.  The previous "test" has been removed as it has been
superceeded by the VTS test for the HAL.

NOTE:  This change was originally developed and +2'd on the master
branch as ag/2021133.  However cherry picking to oc-dev is broken, so
this will be submitted directly to oc-dev and auto-merged back to
master.

Test: build locally against updated EVS HAL
Change-Id: I2378d622079a2326dc7d1432adca72bcf112e193
diff --git a/car_product/build/car_base.mk b/car_product/build/car_base.mk
index 6544dc1..3e8d776 100644
--- a/car_product/build/car_base.mk
+++ b/car_product/build/car_base.mk
@@ -82,6 +82,11 @@
     wifi-service \
     A2dpSinkService \
 
+# EVS resources
+PRODUCT_PACKAGES += android.hardware.automotive.evs@1.0-service
+PRODUCT_PACKAGES += android.automotive.evs.manager@1.0
+PRODUCT_PACKAGES += evs_app
+
 ifeq ($(TARGET_USES_CAR_FUTURE_FEATURES),true)
 PRODUCT_PACKAGES += android.hardware.automotive.vehicle@2.1-service
 else
diff --git a/evs/app/Android.mk b/evs/app/Android.mk
index 0c38672..1978616 100644
--- a/evs/app/Android.mk
+++ b/evs/app/Android.mk
@@ -6,6 +6,8 @@
 LOCAL_SRC_FILES := \
     evs_app.cpp \
     EvsStateControl.cpp \
+    StreamHandler.cpp \
+    ConfigManager.cpp \
 
 LOCAL_C_INCLUDES += \
     frameworks/base/include \
@@ -15,6 +17,7 @@
     libcutils \
     liblog \
     libutils \
+    libui \
     libhwbinder \
     libhidlbase \
     libhidltransport \
@@ -25,6 +28,9 @@
     android.hardware.automotive.evs@1.0 \
     android.hardware.automotive.vehicle@2.0 \
 
+LOCAL_STATIC_LIBRARIES := \
+    libjsoncpp \
+
 LOCAL_STRIP_MODULE := keep_symbols
 
 LOCAL_MODULE:= evs_app
diff --git a/evs/app/ConfigManager.cpp b/evs/app/ConfigManager.cpp
new file mode 100644
index 0000000..48966d9
--- /dev/null
+++ b/evs/app/ConfigManager.cpp
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+#include "ConfigManager.h"
+
+#include "json/json.h"
+
+#include <fstream>
+#include <math.h>
+#include <assert.h>
+
+
+static const float kDegreesToRadians = M_PI / 180.0f;
+
+
+static float normalizeToPlusMinus180degrees(float theta) {
+    const float wraps = floor((theta+180.0f) / 360.0f);
+    return theta - wraps*360.0f;
+}
+
+
+static bool readChildNodeAsFloat(const char* groupName,
+                                 const Json::Value& parentNode,
+                                 const char* childName,
+                                 float* value) {
+    // Must have a place to put the value!
+    assert(value);
+
+    Json::Value childNode = parentNode[childName];
+    if (!childNode.isNumeric()) {
+        printf("Missing or invalid field %s in record %s", childName, groupName);
+        return false;
+    }
+
+    *value = childNode.asFloat();
+    return true;
+}
+
+
+static bool ReadChildNodeAsUint(const char* groupName,
+                                const Json::Value& parentNode,
+                                const char* childName,
+                                unsigned* value) {
+    // Must have a place to put the value!
+    assert(value);
+
+    Json::Value childNode = parentNode[childName];
+    if (!childNode.isNumeric()) {
+        printf("Missing or invalid field %s in record %s", childName, groupName);
+        return false;
+    }
+
+    *value = childNode.asUInt();
+    return true;
+}
+
+
+bool ConfigManager::initialize(const char* configFileName)
+{
+    bool complete = true;
+
+    // Set up a stream to read in the input file
+    std::ifstream configStream(configFileName);
+
+    // Parse the stream into JSON objects
+    Json::Reader reader;
+    Json::Value rootNode;
+    bool parseOk = reader.parse(configStream, rootNode, false /* don't need comments */);
+    if (!parseOk) {
+        printf("Failed to read configuration file %s\n", configFileName);
+        printf("%s\n", reader.getFormatedErrorMessages().c_str());
+        return false;
+    }
+
+
+    //
+    // Read car information
+    //
+    {
+        Json::Value car = rootNode["car"];
+        if (!car.isObject()) {
+            printf("Invalid configuration format -- we expect a car description\n");
+            return false;
+        }
+        complete &= readChildNodeAsFloat("car", car, "width",       &mCarWidth);
+        complete &= readChildNodeAsFloat("car", car, "wheelBase",   &mWheelBase);
+        complete &= readChildNodeAsFloat("car", car, "frontExtent", &mFrontExtent);
+        complete &= readChildNodeAsFloat("car", car, "rearExtent",  &mRearExtent);
+    }
+
+
+    //
+    // Read display layout information
+    //
+    {
+        Json::Value displayNode = rootNode["display"];
+        if (!displayNode.isObject()) {
+            printf("Invalid configuration format -- we expect a display description\n");
+            return false;
+        }
+        complete &= ReadChildNodeAsUint("display", displayNode, "width",      &mPixelWidth);
+        complete &= ReadChildNodeAsUint("display", displayNode, "height",     &mPixelHeight);
+        complete &= readChildNodeAsFloat("display", displayNode, "frontRange", &mFrontRangeInCarSpace);
+        complete &= readChildNodeAsFloat("display", displayNode, "rearRange",  &mRearRangeInCarSpace);
+    }
+
+
+    //
+    // Car top view texture properties for top down view
+    //
+    {
+        Json::Value graphicNode = rootNode["graphic"];
+        if (!graphicNode.isObject()) {
+            printf("Invalid configuration format -- we expect a graphic description\n");
+            return false;
+        }
+        complete &= readChildNodeAsFloat("graphic", graphicNode, "frontPixel", &mCarGraphicFrontPixel);
+        complete &= readChildNodeAsFloat("display", graphicNode, "rearPixel",  &mCarGraphicRearPixel);
+    }
+
+
+    //
+    // Read camera information
+    // NOTE:  Missing positions and angles are not reported, but instead default to zero
+    //
+    {
+        Json::Value cameraArray = rootNode["cameras"];
+        if (!cameraArray.isArray()) {
+            printf("Invalid configuration format -- we expect an array of cameras\n");
+            return false;
+        }
+
+        mCameras.reserve(cameraArray.size());
+        for (auto&& node: cameraArray) {
+            // Get data from the configuration file
+            Json::Value nameNode = node.get("cameraId", "MISSING");
+            const char *cameraId = nameNode.asCString();
+            printf("Loading camera %s\n", cameraId);
+
+            Json::Value usageNode = node.get("function", "");
+            const char *function = usageNode.asCString();
+
+            float yaw   = node.get("yaw", 0).asFloat();
+            float pitch = node.get("pitch", 0).asFloat();
+            float hfov  = node.get("hfov", 0).asFloat();
+            float vfov  = node.get("vfov", 0).asFloat();
+
+            // Wrap the direction angles to be in the 180deg to -180deg range
+            // Rotate 180 in yaw if necessary to flip the pitch into the +/-90degree range
+            pitch = normalizeToPlusMinus180degrees(pitch);
+            if (pitch > 90.0f) {
+                yaw += 180.0f;
+                pitch = 180.0f - pitch;
+            }
+            if (pitch < -90.0f) {
+                yaw += 180.0f;
+                pitch = -180.0f + pitch;
+            }
+            yaw = normalizeToPlusMinus180degrees(yaw);
+
+            // Range check the FOV values to ensure they are postive and less than 180degrees
+            if (hfov > 179.0f) {
+                printf("Pathological horizontal field of view %f clamped to 179 degrees\n", hfov);
+                hfov = 179.0f;
+            }
+            if (hfov < 1.0f) {
+                printf("Pathological horizontal field of view %f clamped to 1 degree\n", hfov);
+                hfov = 1.0f;
+            }
+            if (vfov > 179.0f) {
+                printf("Pathological horizontal field of view %f clamped to 179 degrees\n", vfov);
+                vfov = 179.0f;
+            }
+            if (vfov < 1.0f) {
+                printf("Pathological horizontal field of view %f clamped to 1 degree\n", vfov);
+                vfov = 1.0f;
+            }
+
+            // Store the camera info (converting degrees to radians in the process)
+            CameraInfo info;
+            info.position[0] = node.get("x", 0).asFloat();
+            info.position[1] = node.get("y", 0).asFloat();
+            info.position[2] = node.get("z", 0).asFloat();
+            info.yaw         = yaw   * kDegreesToRadians;
+            info.pitch       = pitch * kDegreesToRadians;
+            info.hfov        = hfov  * kDegreesToRadians;
+            info.vfov        = vfov  * kDegreesToRadians;
+            info.cameraId    = cameraId;
+            info.function    = function;
+
+            mCameras.push_back(info);
+        }
+    }
+
+    // If we got this far, we were successful as long as we found all our child fields
+    return complete;
+}
diff --git a/evs/app/ConfigManager.h b/evs/app/ConfigManager.h
new file mode 100644
index 0000000..1b61146
--- /dev/null
+++ b/evs/app/ConfigManager.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2017 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 CONFIG_MANAGER_H
+#define CONFIG_MANAGER_H
+
+#include <vector>
+#include <string>
+
+
+class ConfigManager {
+public:
+    struct CameraInfo {
+        std::string cameraId = 0;   // The name of the camera from the point of view of the HAL
+        std::string function = 0;   // The expected use for this camera ("reverse", "left", "right")
+        float position[3] = {0};    // x, y, z -> right, fwd, up in the units of car space
+        float yaw   = 0;    // radians positive to the left (right hand rule about global z axis)
+        float pitch = 0;    // positive upward (ie: right hand rule about local x axis)
+        float hfov  = 0;    // radians
+        float vfov  = 0;    // radians
+    };
+
+    bool initialize(const char* configFileName);
+
+    // World space dimensions of the car
+    float getCarWidth() const   { return mCarWidth; };
+    float getCarLength() const  { return mWheelBase + mFrontExtent + mRearExtent; };
+    float getWheelBase() const  { return mWheelBase; };
+
+    // Car space (world space centered on the rear axel) edges of the car
+    float getFrontLocation() const  { return mWheelBase + mFrontExtent; };
+    float getRearLocation() const   { return -mRearExtent; };
+    float getRightLocation() const  { return mCarWidth*0.5f; };
+    float getLeftLocation() const   { return -mCarWidth*0.5f; };
+
+    // Where are the edges of the top down display in car space?
+    float getDisplayTopLocation() const {
+        // From the rear axel (origin) to the front bumper, and then beyond by the front range
+        return mWheelBase + mFrontExtent + mFrontRangeInCarSpace;
+    };
+    float getDisplayBottomLocation() const {
+        // From the rear axel (origin) to the back bumper, and then beyond by the back range
+        return -mRearExtent - mRearRangeInCarSpace;
+    };
+    float getDisplayRightLocation(float aspectRatio) const   {
+        // Given the display aspect ratio (width over height), how far can we see to the right?
+        return (getDisplayTopLocation() - getDisplayBottomLocation()) * 0.5f / aspectRatio;
+    };
+    float getDisplayLeftLocation(float aspectRatio) const {
+        // Given the display aspect ratio (width over height), how far can we see to the left?
+        return -getDisplayRightLocation(aspectRatio);
+    };
+
+    // At which texel (vertically in the image) are the front and rear bumpers of the car?
+    float carGraphicFrontPixel() const      { return mCarGraphicFrontPixel; };
+    float carGraphicRearPixel() const       { return mCarGraphicRearPixel; };
+
+    const std::vector<CameraInfo>& getCameras() const   { return mCameras; };
+
+private:
+    // Camera information
+    std::vector<CameraInfo> mCameras;
+
+    // Car body information (assumes front wheel steering and origin at center of rear axel)
+    // Note that units aren't specified and don't matter as long as all length units are consistent
+    // within the JSON file from which we parse.  That is, if everything is in meters, that's fine.
+    // Everything in mm?  That's fine too.
+    float mCarWidth;
+    float mWheelBase;
+    float mFrontExtent;
+    float mRearExtent;
+
+    // Display information
+    unsigned mPixelWidth;
+    unsigned mPixelHeight;
+    float    mFrontRangeInCarSpace;     // How far the display extends in front of the car
+    float    mRearRangeInCarSpace;      // How far the display extends behind the car
+
+    // Top view car image information
+    float mCarGraphicFrontPixel;    // How many pixels from the top of the image does the car start
+    float mCarGraphicRearPixel;     // How many pixels from the top of the image does the car end
+};
+
+#endif // CONFIG_MANAGER_H
\ No newline at end of file
diff --git a/evs/app/EvsStateControl.cpp b/evs/app/EvsStateControl.cpp
index 8624246..abed1e5 100644
--- a/evs/app/EvsStateControl.cpp
+++ b/evs/app/EvsStateControl.cpp
@@ -34,7 +34,8 @@
 
 EvsStateControl::EvsStateControl(android::sp <IVehicle>       pVnet,
                                  android::sp <IEvsEnumerator> pEvs,
-                                 android::sp <IEvsDisplay>    pDisplay) :
+                                 android::sp <IEvsDisplay>    pDisplay,
+                                 const ConfigManager&         config) :
     mVehicle(pVnet),
     mEvs(pEvs),
     mDisplay(pDisplay),
@@ -51,22 +52,37 @@
 
     // Build our set of cameras for the states we support
     ALOGD("Requesting camera list");
-    mEvs->getCameraList([this]
+    mEvs->getCameraList([this, &config]
                         (hidl_vec<CameraDesc> cameraList) {
                             ALOGI("Camera list callback received %zu cameras",
                                   cameraList.size());
-                            for(auto&& cam: cameraList) {
-                                if ((cam.hints & UsageHint::USAGE_HINT_REVERSE) != 0) {
-                                    mCameraInfo[State::REVERSE] = cam;
-                                }
-                                if ((cam.hints & UsageHint::USAGE_HINT_RIGHT_TURN) != 0) {
-                                    mCameraInfo[State::RIGHT] = cam;
-                                }
-                                if ((cam.hints & UsageHint::USAGE_HINT_LEFT_TURN) != 0) {
-                                    mCameraInfo[State::LEFT] = cam;
-                                }
-
+                            for (auto&& cam: cameraList) {
                                 ALOGD("Found camera %s", cam.cameraId.c_str());
+                                bool cameraConfigFound = false;
+
+                                // Check our configuration for information about this camera
+                                // Note that a camera can have a compound function string
+                                // such that a camera can be "right/reverse" and be used for both.
+                                for (auto&& info: config.getCameras()) {
+                                    if (cam.cameraId == info.cameraId) {
+                                        // We found a match!
+                                        if (info.function.find("reverse") != std::string::npos) {
+                                            mCameraInfo[State::REVERSE] = info;
+                                        }
+                                        if (info.function.find("right") != std::string::npos) {
+                                            mCameraInfo[State::RIGHT] = info;
+                                        }
+                                        if (info.function.find("left") != std::string::npos) {
+                                            mCameraInfo[State::LEFT] = info;
+                                        }
+                                        cameraConfigFound = true;
+                                        break;
+                                    }
+                                }
+                                if (!cameraConfigFound) {
+                                    ALOGW("No config information for hardware camera %s",
+                                          cam.cameraId.c_str());
+                                }
                             }
                         }
     );
@@ -155,9 +171,6 @@
 bool EvsStateControl::configureEvsPipeline(State desiredState) {
     ALOGD("configureEvsPipeline");
 
-    // Protect access to mCurrentState which is shared with the deliverFrame callback
-    std::unique_lock<std::mutex> lock(mAccessLock);
-
     if (mCurrentState == desiredState) {
         // Nothing to do here...
         return true;
@@ -173,13 +186,13 @@
 
         // Yup, we need to change cameras, so close the previous one, if necessary.
         if (mCurrentCamera != nullptr) {
-            mEvs->closeCamera(mCurrentCamera);
+            mCurrentStreamHandler->blockingStopStream();
+            mCurrentStreamHandler = nullptr;
             mCurrentCamera = nullptr;
         }
 
         // Now do we need a new camera?
         if (!mCameraInfo[desiredState].cameraId.empty()) {
-
             // Need a new camera, so open it
             ALOGD("Open camera %s", mCameraInfo[desiredState].cameraId.c_str());
             mCurrentCamera = mEvs->openCamera(mCameraInfo[desiredState].cameraId);
@@ -196,16 +209,16 @@
             ALOGD("Turning off the display");
             mDisplay->setDisplayState(DisplayState::NOT_VISIBLE);
         } else {
+            // Create the stream handler object to receive and forward the video frames
+            mCurrentStreamHandler = new StreamHandler(mCurrentCamera, mDisplay);
+
             // Start the camera stream
             ALOGD("Starting camera stream");
-            mCurrentCamera->startVideoStream(this);
+            mCurrentStreamHandler->startStream();
 
             // Activate the display
             ALOGD("Arming the display");
             mDisplay->setDisplayState(DisplayState::VISIBLE_ON_NEXT_FRAME);
-
-            // TODO:  Detect and exit if we encounter a stalled stream or unresponsive driver?
-            // Consider using a timer and watching for frame arrival?
         }
     }
 
@@ -215,55 +228,3 @@
 
     return true;
 }
-
-
-Return<void> EvsStateControl::deliverFrame(const BufferDesc& buffer) {
-    ALOGD("Received a frame from the camera (%p)", buffer.memHandle.getNativeHandle());
-
-    if (buffer.memHandle == nullptr) {
-        // This is the end of the stream.  Transition back to the "off" state.
-        std::unique_lock<std::mutex> lock(mAccessLock);
-        mCurrentState = State::OFF;
-        lock.unlock();
-    } else {
-        // Get the output buffer we'll use to display the imagery
-        BufferDesc tgtBuffer = {};
-        mDisplay->getTargetBuffer([&tgtBuffer]
-                                  (const BufferDesc& buff) {
-                                      tgtBuffer = buff;
-                                      ALOGD("Got output buffer (%p) with id %d cloned as (%p)",
-                                            buff.memHandle.getNativeHandle(),
-                                            tgtBuffer.bufferId,
-                                            tgtBuffer.memHandle.getNativeHandle());
-                                  }
-        );
-
-        if (tgtBuffer.memHandle == nullptr) {
-            ALOGE("Didn't get requested output buffer -- skipping this frame.");
-        } else {
-            // TODO:  Copy the contents of the of bufferHandle into tgtBuffer
-            // TODO:  Add a bit of overlay graphics?
-            // TODO:  Use OpenGL to render from texture?
-
-            // Send the target buffer back for display
-            ALOGD("Calling returnTargetBufferForDisplay (%p)", tgtBuffer.memHandle.getNativeHandle());
-            Return<EvsResult> result = mDisplay->returnTargetBufferForDisplay(tgtBuffer);
-            if (!result.isOk()) {
-                ALOGE("Error making the remote function call.  HIDL said %s",
-                      result.description().c_str());
-            }
-            if (result != EvsResult::OK) {
-                ALOGE("We encountered error %d when returning a buffer to the display!",
-                      (EvsResult)result);
-            }
-        }
-
-        // Send the camera buffer back now that we're done with it
-        ALOGD("Calling doneWithFrame");
-        mCurrentCamera->doneWithFrame(buffer);
-
-        ALOGD("Frame handling complete");
-    }
-
-    return Void();
-}
diff --git a/evs/app/EvsStateControl.h b/evs/app/EvsStateControl.h
index dbe6d28..d8ce9e6 100644
--- a/evs/app/EvsStateControl.h
+++ b/evs/app/EvsStateControl.h
@@ -21,7 +21,9 @@
 #include <android/hardware/automotive/evs/1.0/IEvsEnumerator.h>
 #include <android/hardware/automotive/evs/1.0/IEvsDisplay.h>
 #include <android/hardware/automotive/evs/1.0/IEvsCamera.h>
-#include <android/hardware/automotive/evs/1.0/IEvsCameraStream.h>
+
+#include "StreamHandler.h"
+#include "ConfigManager.h"
 
 
 using namespace ::android::hardware::automotive::evs::V1_0;
@@ -33,13 +35,12 @@
 using ::android::sp;
 
 
-class EvsStateControl : public IEvsCameraStream {
+class EvsStateControl {
 public:
     EvsStateControl(android::sp <IVehicle>       pVnet,
                     android::sp <IEvsEnumerator> pEvs,
-                    android::sp <IEvsDisplay>    pDisplay);
-
-    bool configureForVehicleState();
+                    android::sp <IEvsDisplay>    pDisplay,
+                    const ConfigManager&         config);
 
     enum State {
         REVERSE = 0,
@@ -49,13 +50,12 @@
         NUM_STATES  // Must come last
     };
 
+    bool configureForVehicleState();
+
 private:
     StatusCode invokeGet(VehiclePropValue *pRequestedPropValue);
     bool configureEvsPipeline(State desiredState);
 
-    // Methods from ::android::hardware::automotive::evs::V1_0::ICarCameraStream follow.
-    Return<void> deliverFrame(const BufferDesc& buffer)  override;
-
     sp<IVehicle>                mVehicle;
     sp<IEvsEnumerator>          mEvs;
     sp<IEvsDisplay>             mDisplay;
@@ -63,11 +63,11 @@
     VehiclePropValue            mGearValue;
     VehiclePropValue            mTurnSignalValue;
 
-    CameraDesc                  mCameraInfo[State::NUM_STATES];
+    ConfigManager::CameraInfo   mCameraInfo[State::NUM_STATES];
     State                       mCurrentState;
     sp<IEvsCamera>              mCurrentCamera;
 
-    std::mutex                  mAccessLock;
+    sp<StreamHandler>           mCurrentStreamHandler;
 };
 
 
diff --git a/evs/test/StreamHandler.cpp b/evs/app/StreamHandler.cpp
similarity index 90%
rename from evs/test/StreamHandler.cpp
rename to evs/app/StreamHandler.cpp
index ab4f445..f420466 100644
--- a/evs/test/StreamHandler.cpp
+++ b/evs/app/StreamHandler.cpp
@@ -35,19 +35,9 @@
 static const unsigned kBytesPerPixel = 4;   // assuming 4 byte RGBx pixels
 
 
-StreamHandler::StreamHandler(android::sp <IEvsCamera>  pCamera,  CameraDesc  cameraInfo,
-                             android::sp <IEvsDisplay> pDisplay, DisplayDesc displayInfo) :
+StreamHandler::StreamHandler(android::sp<IEvsCamera> pCamera, android::sp<IEvsDisplay> pDisplay) :
     mCamera(pCamera),
-    mCameraInfo(cameraInfo),
-    mDisplay(pDisplay),
-    mDisplayInfo(displayInfo) {
-
-    // Post a warning message if resolutions don't match since we handle it, but only
-    // with simple/ugly results in copyBufferContents below.
-    if ((mDisplayInfo.defaultHorResolution != cameraInfo.defaultHorResolution) ||
-        (mDisplayInfo.defaultVerResolution != cameraInfo.defaultVerResolution)) {
-        ALOGW("Camera and Display resolutions don't match -- images will be clipped");
-    }
+    mDisplay(pDisplay) {
 }
 
 
@@ -100,26 +90,18 @@
 Return<void> StreamHandler::deliverFrame(const BufferDesc& bufferArg) {
     ALOGD("Received a frame from the camera (%p)", bufferArg.memHandle.getNativeHandle());
 
+    // Local flag we use to keep track of when the stream is stopping
+    bool timeToStop = false;
+
     // TODO:  Why do we get a gralloc crash if we don't clone the buffer here?
     BufferDesc buffer(bufferArg);
     ALOGD("Clone the received frame as %p", buffer.memHandle.getNativeHandle());
 
     if (buffer.memHandle.getNativeHandle() == nullptr) {
-        printf("Got end of stream notification\n");
-
-        // Signal that the last frame has been received and the stream is stopped
-        mLock.lock();
-        mRunning = false;
-        mLock.unlock();
-        mSignal.notify_all();
-
+        // Signal that the last frame has been received and that the stream should stop
+        timeToStop = true;
         ALOGI("End of stream signaled");
     } else {
-        // Quick and dirty so that we can monitor frame delivery for testing
-        mLock.lock();
-        mFramesReceived++;
-        mLock.unlock();
-
         // Get the output buffer we'll use to display the imagery
         BufferDesc tgtBuffer = {};
         mDisplay->getTargetBuffer([&tgtBuffer]
@@ -183,6 +165,18 @@
         ALOGD("Frame handling complete");
     }
 
+
+    // Update our received frame count and notify anybody who cares that things have changed
+    mLock.lock();
+    if (timeToStop) {
+        mRunning = false;
+    } else {
+        mFramesReceived++;
+    }
+    mLock.unlock();
+    mSignal.notify_all();
+
+
     return Void();
 }
 
diff --git a/evs/test/StreamHandler.h b/evs/app/StreamHandler.h
similarity index 89%
rename from evs/test/StreamHandler.h
rename to evs/app/StreamHandler.h
index 327c743..c8f8865 100644
--- a/evs/test/StreamHandler.h
+++ b/evs/app/StreamHandler.h
@@ -31,8 +31,8 @@
 
 class StreamHandler : public IEvsCameraStream {
 public:
-    StreamHandler(android::sp <IEvsCamera>  pCamera,  CameraDesc  cameraInfo,
-                  android::sp <IEvsDisplay> pDisplay, DisplayDesc displayInfo);
+    StreamHandler(android::sp <IEvsCamera>  pCamera,
+                  android::sp <IEvsDisplay> pDisplay);
 
     void startStream();
     void asyncStopStream();
@@ -53,9 +53,7 @@
     void unregisterBufferHelper(const BufferDesc& buffer);
 
     android::sp <IEvsCamera>    mCamera;
-    CameraDesc                  mCameraInfo;
     android::sp <IEvsDisplay>   mDisplay;
-    DisplayDesc                 mDisplayInfo;
 
     std::mutex                  mLock;
     std::condition_variable     mSignal;
diff --git a/evs/app/config.json b/evs/app/config.json
new file mode 100644
index 0000000..791dc4e
--- /dev/null
+++ b/evs/app/config.json
@@ -0,0 +1,73 @@
+{
+  "car" : {
+    "width"  : 76.7,
+    "wheelBase" : 117.9,
+    "frontExtent" : 44.7,
+    "rearExtent" : 40
+  },
+  "display" : {
+    "width" : 640,
+    "height" : 480,
+    "frontRange" : 100,
+    "rearRange" : 100
+  },
+  "graphic" : {
+    "frontPixel" : 23,
+    "rearPixel" : 223
+  },
+  "cameras" : [
+    {
+      "name" : "rightFront",
+      "x" : 36.0,
+      "y" : 90.0,
+      "z" : 36,
+      "yaw" : -45,
+      "pitch" : -25,
+      "hfov" : 60,
+      "vfov" : 40
+    },
+    {
+      "name" : "rightRear",
+      "function" : "right",
+      "x" : 36.0,
+      "y" : -10,
+      "z" : 36,
+      "yaw" : -135,
+      "pitch" : -25,
+      "hfov" : 60,
+      "vfov" : 40
+    },
+    {
+      "name" : "left",
+      "function" : "left",
+      "x" : -36.0,
+      "y" : 80,
+      "z" : 30,
+      "yaw" : 90,
+      "pitch" : -45,
+      "hfov" : 90,
+      "vfov" : 90
+    },
+    {
+      "name" : "front",
+      "x" : 0.0,
+      "y" : 100.0,
+      "z" : 48,
+      "yaw" : 0,
+      "pitch" : -10,
+      "hfov" : 60,
+      "vfov" : 42
+    },
+    {
+      "name" : "rear",
+      "function" : "rear",
+      "x" : 0.0,
+      "y" : -40,
+      "z" : 30,
+      "yaw" : 180,
+      "pitch" : -45,
+      "hfov" : 90,
+      "vfov" : 60
+    }
+  ]
+}
\ No newline at end of file
diff --git a/evs/app/evs_app.cpp b/evs/app/evs_app.cpp
index 82ad52c..d17119e 100644
--- a/evs/app/evs_app.cpp
+++ b/evs/app/evs_app.cpp
@@ -32,6 +32,7 @@
 
 #include "EvsStateControl.h"
 #include "EvsVehicleListener.h"
+#include "ConfigManager.h"
 
 
 // libhidl:
@@ -48,6 +49,10 @@
 {
     printf("EVS app starting\n");
 
+    // Load our configuration information
+    ConfigManager config;
+    config.initialize("config.json");
+
     // Set thread pool size to one to avoid concurrent events from the HAL.
     // This pool will handle the EvsCameraStream callbacks.
     // Note:  This _will_ run in parallel with the EvsListener run() loop below which
@@ -109,7 +114,7 @@
 
     // Configure ourselves for the current vehicle state at startup
     ALOGI("Constructing state controller");
-    EvsStateControl *pStateController = new EvsStateControl(pVnet, pEvs, pDisplay);
+    EvsStateControl *pStateController = new EvsStateControl(pVnet, pEvs, pDisplay, config);
     if (!pStateController->configureForVehicleState()) {
         ALOGE("Initial configuration failed.  Exiting.");
         return 1;
diff --git a/evs/manager/Enumerator.cpp b/evs/manager/Enumerator.cpp
index cbaafed..7bfe6f0 100644
--- a/evs/manager/Enumerator.cpp
+++ b/evs/manager/Enumerator.cpp
@@ -52,8 +52,8 @@
     sp<HalCamera> hwCamera;
     for (auto &&cam : mCameras) {
         bool match = false;
-        cam->getHwCamera()->getId([cameraId, &match](hidl_string id) {
-                                      if (id == cameraId) {
+        cam->getHwCamera()->getCameraInfo([cameraId, &match](CameraDesc desc) {
+                                      if (desc.cameraId == cameraId) {
                                           match = true;
                                       }
                                   }
@@ -106,7 +106,6 @@
     }
 
     // All our client cameras are actually VirtualCamera objects
-    // TODO (b/33492405):  This will likely crash until pointers make proper round trips
     sp<VirtualCamera> virtualCamera = reinterpret_cast<VirtualCamera*>(clientCamera.get());
 
     // Find the parent camera that backs this virtual camera
@@ -119,10 +118,7 @@
 
     // Did we just remove the last client of this camera?
     if (halCamera->getClientCount() == 0) {
-        // Close the hardware camera before we go any further
-        mHwEnumerator->closeCamera(halCamera->getHwCamera());
-
-        // Take this now closed camera out of our list
+        // Take this now unused camera out of our list
         // NOTE:  This should drop our last reference to the camera, resulting in its
         //        destruction.
         mCameras.remove(halCamera);
@@ -135,22 +131,21 @@
 Return<sp<IEvsDisplay>> Enumerator::openDisplay() {
     ALOGD("openDisplay");
 
-    // If we already have a display active, then this request must be denied
-    sp<IEvsDisplay> pActiveDisplay = mActiveDisplay.promote();
-    if (pActiveDisplay != nullptr) {
-        ALOGW("Rejecting openDisplay request because the display is already in use.");
-        return nullptr;
-    } else {
-        // Request exclusive access to the EVS display
-        ALOGI("Acquiring EVS Display");
-        pActiveDisplay = mHwEnumerator->openDisplay();
-        if (pActiveDisplay == nullptr) {
-            ALOGE("EVS Display unavailable");
-        }
-
-        mActiveDisplay = pActiveDisplay;
-        return pActiveDisplay;
+    // We simply keep track of the most recently opened display instance.
+    // In the underlying layers we expect that a new open will cause the previous
+    // object to be destroyed.  This avoids any race conditions associated with
+    // create/destroy order and provides a cleaner restart sequence if the previous owner
+    // is non-responsive for some reason.
+    // Request exclusive access to the EVS display
+    sp<IEvsDisplay> pActiveDisplay = mHwEnumerator->openDisplay();
+    if (pActiveDisplay == nullptr) {
+        ALOGE("EVS Display unavailable");
     }
+
+    // Remember (via weak pointer) who we think the most recently opened display is so that
+    // we can proxy state requests from other callers to it.
+    mActiveDisplay = pActiveDisplay;
+    return pActiveDisplay;
 }
 
 
@@ -190,9 +185,6 @@
 }
 
 
-// TODO(b/31632518):  Need to get notification when our client dies so we can close the camera.
-
-
 } // namespace implementation
 } // namespace V1_0
 } // namespace evs
diff --git a/evs/manager/HalCamera.cpp b/evs/manager/HalCamera.cpp
index b98a53c..9e33717 100644
--- a/evs/manager/HalCamera.cpp
+++ b/evs/manager/HalCamera.cpp
@@ -18,6 +18,7 @@
 
 #include "HalCamera.h"
 #include "VirtualCamera.h"
+#include "Enumerator.h"
 
 #include <ui/GraphicBufferAllocator.h>
 #include <ui/GraphicBufferMapper.h>
diff --git a/evs/manager/HalCamera.h b/evs/manager/HalCamera.h
index 8a0af12..099666a 100644
--- a/evs/manager/HalCamera.h
+++ b/evs/manager/HalCamera.h
@@ -24,6 +24,7 @@
 #include <thread>
 #include <list>
 
+
 using namespace ::android::hardware::automotive::evs::V1_0;
 using ::android::hardware::Return;
 using ::android::hardware::hidl_handle;
@@ -35,8 +36,7 @@
 namespace implementation {
 
 
-// From VirtualCamera.h
-class VirtualCamera;
+class VirtualCamera;    // From VirtualCamera.h
 
 
 // This class wraps the actual hardware IEvsCamera objects.  There is a one to many
@@ -71,7 +71,7 @@
         STOPPED,
         RUNNING,
         STOPPING,
-    }                               mStreamState    = STOPPED;
+    }                               mStreamState = STOPPED;
 
     struct FrameRecord {
         uint32_t    frameId;
diff --git a/evs/manager/VirtualCamera.cpp b/evs/manager/VirtualCamera.cpp
index 1dbc20a..652caf6 100644
--- a/evs/manager/VirtualCamera.cpp
+++ b/evs/manager/VirtualCamera.cpp
@@ -18,6 +18,7 @@
 
 #include "VirtualCamera.h"
 #include "HalCamera.h"
+#include "Enumerator.h"
 
 #include <ui/GraphicBufferAllocator.h>
 #include <ui/GraphicBufferMapper.h>
@@ -30,10 +31,22 @@
 namespace implementation {
 
 
-// If the client dies before closing the camera, this is our chance to clean up...
+VirtualCamera::VirtualCamera(sp<HalCamera> halCamera) :
+    mHalCamera(halCamera) {
+}
+
+
 VirtualCamera::~VirtualCamera() {
-    // If we have been connected to a camera device we have some cleanup to do...
-    if (mHalCamera != nullptr) {
+    shutdown();
+}
+
+
+void VirtualCamera::shutdown() {
+    // In normal operation, the stream should already be stopped by the time we get here
+    if (mStreamState != STOPPED) {
+        // Note that if we hit this case, no terminating frame will be sent to the client,
+        // but they're probably already dead anyway.
+        ALOGW("Virtual camera being shutdown while stream is running");
         mStreamState = STOPPED;
 
         if (mFramesHeld.size() > 0) {
@@ -46,18 +59,10 @@
             }
             mFramesHeld.clear();
         }
-
-        // Give the underlying hardware camera the heads up that it might be time to stop
-        mHalCamera->clientStreamEnding();
-
-        // Disconnect ourselves from the underlying hardware camera so it can adjust it's resources
-        mHalCamera->disownVirtualCamera(this);
     }
-}
 
-
-void VirtualCamera::shutdown() {
-    mHalCamera=nullptr;
+    // Drop our reference to our associated hardware camera
+    mHalCamera = nullptr;
 }
 
 
@@ -95,9 +100,9 @@
 
 
 // Methods from ::android::hardware::automotive::evs::V1_0::IEvsCamera follow.
-Return<void> VirtualCamera::getId(getId_cb id_cb) {
+Return<void> VirtualCamera::getCameraInfo(getCameraInfo_cb info_cb) {
     // Straight pass through to hardware layer
-    return mHalCamera->getHwCamera()->getId(id_cb);
+    return mHalCamera->getHwCamera()->getCameraInfo(info_cb);
 }
 
 
diff --git a/evs/manager/VirtualCamera.h b/evs/manager/VirtualCamera.h
index 5e474f1..22da798 100644
--- a/evs/manager/VirtualCamera.h
+++ b/evs/manager/VirtualCamera.h
@@ -37,8 +37,7 @@
 namespace implementation {
 
 
-// From HalCamera.h
-class HalCamera;
+class HalCamera;        // From HalCamera.h
 
 
 // This class represents an EVS camera to the client application.  As such it presents
@@ -46,28 +45,28 @@
 // IEvsCameraStream object.
 class VirtualCamera : public IEvsCamera {
 public:
-    explicit VirtualCamera(sp<HalCamera> halCamera) : mHalCamera(halCamera) {};
+    explicit VirtualCamera(sp<HalCamera> halCamera);
     virtual ~VirtualCamera();
-    void              shutdown();
+    void                shutdown();
 
-    sp<HalCamera>     getHalCamera() { return mHalCamera; };
-    unsigned          getAllowedBuffers() { return mFramesAllowed; };
-    bool              isStreaming()  { return mStreamState == RUNNING; }
+    sp<HalCamera>       getHalCamera()      { return mHalCamera; };
+    unsigned            getAllowedBuffers() { return mFramesAllowed; };
+    bool                isStreaming()       { return mStreamState == RUNNING; }
 
     // Proxy to receive frames and forward them to the client's stream
-    bool              deliverFrame(const BufferDesc& buffer);
+    bool                deliverFrame(const BufferDesc& buffer);
 
     // Methods from ::android::hardware::automotive::evs::V1_0::IEvsCamera follow.
-    Return<void>      getId(getId_cb id_cb)  override;
-    Return<EvsResult> setMaxFramesInFlight(uint32_t bufferCount)  override;
-    Return<EvsResult> startVideoStream(const ::android::sp<IEvsCameraStream>& stream)  override;
-    Return<void>      doneWithFrame(const BufferDesc& buffer)  override;
-    Return<void>      stopVideoStream()  override;
-    Return<int32_t>   getExtendedInfo(uint32_t opaqueIdentifier)  override;
-    Return<EvsResult> setExtendedInfo(uint32_t opaqueIdentifier, int32_t opaqueValue)  override;
+    Return<void>        getCameraInfo(getCameraInfo_cb _hidl_cb)  override;
+    Return<EvsResult>   setMaxFramesInFlight(uint32_t bufferCount) override;
+    Return<EvsResult>   startVideoStream(const ::android::sp<IEvsCameraStream>& stream) override;
+    Return<void>        doneWithFrame(const BufferDesc& buffer) override;
+    Return<void>        stopVideoStream() override;
+    Return<int32_t>     getExtendedInfo(uint32_t opaqueIdentifier) override;
+    Return<EvsResult>   setExtendedInfo(uint32_t opaqueIdentifier, int32_t opaqueValue) override;
 
 private:
-    sp<HalCamera>           mHalCamera;
+    sp<HalCamera>           mHalCamera;     // The low level camera interface that backs this proxy
     sp<IEvsCameraStream>    mStream;
 
     std::deque<BufferDesc>  mFramesHeld;
diff --git a/evs/test/Android.mk b/evs/test/Android.mk
deleted file mode 100644
index 4a9eb47..0000000
--- a/evs/test/Android.mk
+++ /dev/null
@@ -1,31 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-##################################
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
-    evs_test.cpp \
-    EvsStateControl.cpp \
-    StreamHandler.cpp \
-
-LOCAL_C_INCLUDES += \
-    frameworks/base/include \
-    packages/services/Car/evs/test \
-
-LOCAL_SHARED_LIBRARIES := \
-    android.hardware.automotive.evs@1.0 \
-    liblog \
-    libutils \
-    libui \
-    libhidlbase \
-    libhidltransport \
-
-LOCAL_STRIP_MODULE := keep_symbols
-
-LOCAL_MODULE:= evs_test
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
-LOCAL_CFLAGS += -O0 -ggdb
-
-include $(BUILD_EXECUTABLE)
diff --git a/evs/test/EvsStateControl.cpp b/evs/test/EvsStateControl.cpp
deleted file mode 100644
index de8a53a..0000000
--- a/evs/test/EvsStateControl.cpp
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright (C) 2016 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_TAG "EvsTest"
-
-#include "EvsStateControl.h"
-
-#include <stdio.h>
-#include <string.h>
-
-#include <android/log.h>
-
-
-EvsStateControl::EvsStateControl(android::sp <IEvsEnumerator> pEnumerator,
-                                 android::sp <IEvsDisplay> pDisplay) {
-
-    // Store the pointers to the HAL interfaces we use to assemble the EVS pipeline
-    mEnumerator    = pEnumerator;
-    mDisplay       = pDisplay;
-
-    // Initialize our current state so we know we have no cameras active
-    mCurrentState = State::OFF;
-
-    // Build our set of cameras for the states we support
-    ALOGD("Requesting camera list");
-    mEnumerator->getCameraList([this]
-                               (hidl_vec<CameraDesc> cameraList) {
-                                   ALOGI("Camera list callback received %zu cameras",
-                                         cameraList.size());
-                                   for(auto&& cam: cameraList) {
-                                       if ((cam.hints & UsageHint::USAGE_HINT_REVERSE) != 0) {
-                                           mCameraInfo[State::REVERSE] = cam;
-                                           ALOGD("Use for REVERSE...");
-                                       }
-                                       if ((cam.hints & UsageHint::USAGE_HINT_RIGHT_TURN) != 0) {
-                                           mCameraInfo[State::RIGHT] = cam;
-                                           ALOGD("Use for RIGHT...");
-                                       }
-                                       if ((cam.hints & UsageHint::USAGE_HINT_LEFT_TURN) != 0) {
-                                           mCameraInfo[State::LEFT] = cam;
-                                           ALOGD("Use for LEFT...");
-                                       }
-
-                                       ALOGD("Found camera %s", cam.cameraId.c_str());
-                                   }
-                               }
-    );
-
-    // Record information about our display device
-    mDisplay->getDisplayInfo([this]
-                             (DisplayDesc desc) {
-                                 mDisplayInfo = desc;
-                                 ALOGD("Found %dx%d display",
-                                       desc.defaultHorResolution,
-                                       desc.defaultVerResolution);
-                             }
-    );
-
-    ALOGD("State controller ready");
-}
-
-
-bool EvsStateControl::configureEvsPipeline(State desiredState) {
-    ALOGD("configureEvsPipeline");
-
-    if (mCurrentState == desiredState) {
-        // Nothing to do here...
-        return true;
-    }
-
-    // See if we actually have to change cameras
-    if (mCameraInfo[mCurrentState].cameraId != mCameraInfo[desiredState].cameraId) {
-        ALOGI("Camera change required");
-        ALOGD("  Current cameraId (%d) = %s", mCurrentState,
-              mCameraInfo[mCurrentState].cameraId.c_str());
-        ALOGD("  Desired cameraId (%d) = %s", desiredState,
-              mCameraInfo[desiredState].cameraId.c_str());
-
-        // Yup, we need to change cameras, so close the previous one, if necessary.
-        if (mCurrentCamera != nullptr) {
-            mCurrentStreamHandler->blockingStopStream();
-            mCurrentStreamHandler = nullptr;
-
-            mEnumerator->closeCamera(mCurrentCamera);
-            mCurrentCamera = nullptr;
-        }
-
-        // Now do we need a new camera?
-        if (!mCameraInfo[desiredState].cameraId.empty()) {
-            // Need a new camera, so open it
-            ALOGD("Open camera %s", mCameraInfo[desiredState].cameraId.c_str());
-            mCurrentCamera = mEnumerator->openCamera(mCameraInfo[desiredState].cameraId);
-
-            // If we didn't get the camera we asked for, we need to bail out and try again later
-            if (mCurrentCamera == nullptr) {
-                ALOGE("Failed to open EVS camera.  Skipping state change.");
-                return false;
-            }
-        }
-
-        // Now set the display state based on whether we have a camera feed to show
-        if (mCurrentCamera == nullptr) {
-            ALOGD("Turning off the display");
-            mDisplay->setDisplayState(DisplayState::NOT_VISIBLE);
-        } else {
-            // Create the stream handler object to receive and forward the video frames
-            mCurrentStreamHandler = new StreamHandler(mCurrentCamera, mCameraInfo[desiredState],
-                                                      mDisplay, mDisplayInfo);
-
-            // Start the camera stream
-            ALOGD("Starting camera stream");
-            mCurrentStreamHandler->startStream();
-
-            // Activate the display
-            ALOGD("Arming the display");
-            mDisplay->setDisplayState(DisplayState::VISIBLE_ON_NEXT_FRAME);
-        }
-    }
-
-    // Record our current state
-    ALOGI("Activated state %d.", desiredState);
-    mCurrentState = desiredState;
-
-    return true;
-}
diff --git a/evs/test/EvsStateControl.h b/evs/test/EvsStateControl.h
deleted file mode 100644
index 06de3b2..0000000
--- a/evs/test/EvsStateControl.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2016 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 CAR_EVS_APP_EVSSTATECONTROL_H
-#define CAR_EVS_APP_EVSSTATECONTROL_H
-
-#include <android/hardware/automotive/evs/1.0/IEvsCamera.h>
-#include <android/hardware/automotive/evs/1.0/IEvsEnumerator.h>
-#include <android/hardware/automotive/evs/1.0/IEvsDisplay.h>
-
-#include "StreamHandler.h"
-
-using namespace ::android::hardware::automotive::evs::V1_0;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::hidl_handle;
-using ::android::sp;
-
-
-class EvsStateControl {
-public:
-    EvsStateControl(android::sp <IEvsEnumerator> pEnumerator,
-                    android::sp <IEvsDisplay> pDisplay);
-
-    enum State {
-        REVERSE = 0,
-        LEFT,
-        RIGHT,
-        OFF,
-        NUM_STATES  // Must come last
-    };
-
-    bool configureEvsPipeline(State desiredState);
-
-    unsigned getFramesReceived() {
-        return mCurrentStreamHandler.get() ? mCurrentStreamHandler->getFramesReceived() : 0;
-    }
-    unsigned getFramesCompleted() {
-        return mCurrentStreamHandler.get() ? mCurrentStreamHandler->getFramesCompleted() : 0;
-    }
-
-private:
-    android::sp <IEvsEnumerator>    mEnumerator;
-
-    State                           mCurrentState;
-
-    sp<IEvsCamera>                  mCurrentCamera;
-    CameraDesc                      mCameraInfo[State::NUM_STATES];
-
-    sp<IEvsDisplay>                 mDisplay;
-    DisplayDesc                     mDisplayInfo;
-
-    sp<StreamHandler>               mCurrentStreamHandler;
-};
-
-
-#endif //CAR_EVS_APP_EVSSTATECONTROL_H
diff --git a/evs/test/evs_test.cpp b/evs/test/evs_test.cpp
deleted file mode 100644
index 772dc2f..0000000
--- a/evs/test/evs_test.cpp
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * Copyright (C) 2016 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_TAG "EvsTest"
-
-#include <stdio.h>
-#include <string.h>
-
-#include <hidl/HidlTransportSupport.h>
-#include <utils/Errors.h>
-#include <utils/StrongPointer.h>
-#include <utils/Log.h>
-
-#include <android/log.h>
-#include <android/hardware/automotive/evs/1.0/IEvsCamera.h>
-#include <android/hardware/automotive/evs/1.0/IEvsEnumerator.h>
-#include <android/hardware/automotive/evs/1.0/IEvsCameraStream.h>
-#include <android/hardware/automotive/evs/1.0/IEvsDisplay.h>
-
-#include <hwbinder/ProcessState.h>
-
-#include "EvsStateControl.h"
-
-
-// libhidl:
-using android::hardware::configureRpcThreadpool;
-using android::hardware::joinRpcThreadpool;
-
-
-// TODO:  How should we configure these values to target appropriate hardware?
-const static char kDirectEnumeratorName[]  = "EvsEnumeratorHw-Mock";
-const static char kManagedEnumeratorName[] = "EvsSharedEnumerator";
-
-
-// Timing expectations for EVS performance are called out in the EVS Vehicle Camera HAL
-// design document available internally at go/aae-evs
-static const unsigned kMaxTimeToFirstFrame  = 500;  // units of ms
-static const unsigned kMaxTimeBetweenFrames = 100;  // units of ms;
-
-static const unsigned kTestTimeInReverse = 1;       // units of seconds;
-static const unsigned kTestTimeInLeft    = 3;       // units of seconds;
-static const unsigned kTestTimeInRight   = 3;       // units of seconds;
-static const unsigned kTestTimeInOff     = 1;       // units of seconds;
-
-constexpr unsigned expectedFrames(unsigned testTimeSec) {
-    unsigned minTime = (testTimeSec * 1000) - kMaxTimeToFirstFrame;
-    unsigned requiredFrames = minTime / kMaxTimeBetweenFrames;
-    return requiredFrames;
-}
-
-
-bool VerifyDisplayState(DisplayState expectedState, DisplayState actualState) {
-    if (expectedState != actualState) {
-        printf("ERROR:  DisplayState should be %d, but is %d instead.\n",
-               expectedState, actualState);
-        return false;
-    } else {
-        return true;
-    }
-}
-
-
-// Main entry point
-int main(int argc, char** argv)
-{
-    const char* serviceName = kManagedEnumeratorName;
-    if (argc > 1) {
-        if (strcmp(argv[1], "-t") == 0) {
-            serviceName = kDirectEnumeratorName;
-        } else if (strcmp(argv[1], "-m") == 0) {
-            serviceName = kManagedEnumeratorName;
-        } else if ((strcmp(argv[1], "-s") == 0) && argc > 2) {
-            serviceName = argv[2];
-        } else {
-            printf("Usage:  %s [mode]\n", argv[0]);
-            printf("  were mode is one of:\n");
-            printf("  -t  connect directly to the EVS HAL mock implementation.\n");
-            printf("  -m  connect to the shared EVS manager.\n");
-            printf("  -s <service name>  connect to the named service.\n");
-            printf("  the default option is the shared EVS manager.\n");
-            return 1;
-        }
-    }
-
-    printf("EVS test starting for %s\n", serviceName);
-
-    // Get the EVS enumerator service
-    sp<IEvsEnumerator> pEnumerator = IEvsEnumerator::getService(serviceName);
-    if (pEnumerator.get() == nullptr) {
-        printf("getService returned NULL, exiting\n");
-        return 1;
-    }
-    DisplayState displayState = pEnumerator->getDisplayState();
-    if (!VerifyDisplayState(DisplayState::NOT_OPEN, displayState)) {
-        return 1;
-    }
-
-    // Request exclusive access to the EVS display
-    sp<IEvsDisplay> pDisplay = pEnumerator->openDisplay();
-    if (pDisplay.get() == nullptr) {
-        printf("EVS Display unavailable, exiting\n");
-        return 1;
-    }
-    displayState = pEnumerator->getDisplayState();
-    if (!VerifyDisplayState(DisplayState::NOT_VISIBLE, displayState)) {
-        return 1;
-    }
-
-    // Construct our view state controller
-    EvsStateControl stateController(pEnumerator, pDisplay);
-
-    // Set thread pool size to one to avoid concurrent events from the HAL.
-    // Note:  The pool _will_ run in parallel with the main thread logic below which
-    // implements the test actions.
-    configureRpcThreadpool(1, false /* callerWillJoin */);
-
-    // Run our test sequence
-    printf("Reverse...\n");
-    stateController.configureEvsPipeline(EvsStateControl::State::REVERSE);
-    sleep(kTestTimeInReverse);
-
-    // Make sure we get the expected EVS Display State
-    displayState = pEnumerator->getDisplayState();
-    printf("EVS Display State is %d\n", displayState);
-    if (displayState != pDisplay->getDisplayState()) {
-        printf("ERROR:  DisplayState mismatch.\n");
-        return 1;
-    }
-    if (!VerifyDisplayState(DisplayState::VISIBLE, displayState)) {
-        printf("Display didn't enter visible state within %d second\n", kTestTimeInReverse);
-        return 1;
-    }
-
-    // Make sure that we got at least the minimum required number of frames delivered while the
-    // stream was running assuming a maximum startup time and a minimum frame rate.
-    unsigned framesSent = stateController.getFramesReceived();
-    unsigned framesDone = stateController.getFramesCompleted();
-    printf("In the first %d second of reverse, we got %d frames delivered, and %d completed\n",
-           kTestTimeInReverse, framesSent, framesDone);
-    if (framesSent < expectedFrames(kTestTimeInReverse)) {
-        printf("Warning: we got only %d of the required minimum %d frames in the first %d second.",
-               framesSent, expectedFrames(kTestTimeInReverse), kTestTimeInReverse);
-    }
-
-    printf("Left...\n");
-    stateController.configureEvsPipeline(EvsStateControl::State::LEFT);
-    sleep(3);
-    framesSent = stateController.getFramesReceived();
-    framesDone = stateController.getFramesCompleted();
-    printf("in %d seconds of Left, we got %d frames delivered, and %d completed\n",
-           kTestTimeInLeft, framesSent, framesDone);
-
-    printf("Right...\n");
-    stateController.configureEvsPipeline(EvsStateControl::State::RIGHT);
-    sleep(kTestTimeInRight);
-    framesSent = stateController.getFramesReceived();
-    framesDone = stateController.getFramesCompleted();
-    printf("in %d seconds of Right, we got %d frames delivered, and %d completed\n",
-           kTestTimeInRight, framesSent, framesDone);
-
-    printf("Off...\n");
-    stateController.configureEvsPipeline(EvsStateControl::State::OFF);
-    sleep(kTestTimeInOff);
-    displayState = pEnumerator->getDisplayState();
-    if (!VerifyDisplayState(DisplayState::NOT_VISIBLE, displayState)) {
-        printf("Display didn't turn off within 1 second.\n");
-        return 1;
-    }
-
-    framesSent = stateController.getFramesReceived();
-    framesDone = stateController.getFramesCompleted();
-    printf("in %d seconds of Off, we got %d frames delivered, and %d completed\n",
-           kTestTimeInOff, framesSent, framesDone);
-
-    // Explicitly release our resources while still in main
-    printf("Exiting...\n");
-
-    pEnumerator->closeDisplay(pDisplay);
-    displayState = pEnumerator->getDisplayState();
-    if (!VerifyDisplayState(DisplayState::NOT_OPEN, displayState)) {
-        printf("Display didn't report closed after shutdown.\n");
-        return 1;
-    }
-
-    pDisplay = nullptr;
-    pEnumerator = nullptr;
-
-    return 0;
-}