Adding the interface file for the prebuilt computepipe graph execution
library.

Android runner will use this c interface to interact with the prebuilt
graph execution library.

Change-Id: Ifa974c7a1e4b8859ebac7eff1ff8520e365a4319
Fix: 146518209, 146518511, 145230095
Test: Unit tests added to verify function calls into the prebuilt.
diff --git a/computepipe/runner/graph/Android.bp b/computepipe/runner/graph/Android.bp
new file mode 100644
index 0000000..589c00f
--- /dev/null
+++ b/computepipe/runner/graph/Android.bp
@@ -0,0 +1,48 @@
+// Copyright (C) 2019 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.
+
+cc_library {
+    name: "computepipe_prebuilt_graph",
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+        "-Wno-unused-parameter",
+    ],
+
+    export_include_dirs: ["include"],
+    static_libs: [
+        "computepipe_runner_component",
+        "libcomputepipeprotos",
+    ],
+
+    header_libs: ["computepipe_runner_includes"],
+    include_dirs: [
+          "packages/services/Car/computepipe",
+          "packages/services/Car/computepipe/runner/graph",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "libdl",
+        "liblog",
+        "libutils",
+        "libprotobuf-cpp-lite",
+    ],
+
+    srcs: [
+        "PrebuiltGraph.cpp",
+    ],
+}
diff --git a/computepipe/runner/graph/PrebuiltGraph.cpp b/computepipe/runner/graph/PrebuiltGraph.cpp
new file mode 100644
index 0000000..eef3113
--- /dev/null
+++ b/computepipe/runner/graph/PrebuiltGraph.cpp
@@ -0,0 +1,369 @@
+// Copyright (C) 2020 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 "PrebuiltGraph.h"
+
+#include <android-base/logging.h>
+#include <dlfcn.h>
+
+#include <functional>
+#include <iostream>
+#include <mutex>
+#include <shared_mutex>
+#include <string>
+#include <vector>
+
+#include "ClientConfig.pb.h"
+#include "RunnerComponent.h"
+#include "prebuilt_interface.h"
+#include "types/Status.h"
+
+namespace android {
+namespace automotive {
+namespace computepipe {
+namespace graph {
+
+#define LOAD_FUNCTION(name)                                                        \
+    {                                                                              \
+        std::string func_name = std::string("PrebuiltComputepipeRunner_") + #name; \
+        mPrebuiltGraphInstance->mFn##name =                                        \
+            dlsym(mPrebuiltGraphInstance->mHandle, func_name.c_str());             \
+        if (mPrebuiltGraphInstance->mFn##name == nullptr) {                        \
+            initialized = false;                                                   \
+            LOG(ERROR) << std::string(dlerror()) << std::endl;                     \
+        }                                                                          \
+    }
+
+std::mutex PrebuiltGraph::mCreationMutex;
+PrebuiltGraph* PrebuiltGraph::mPrebuiltGraphInstance = nullptr;
+
+// Function to confirm that there would be no further changes to the graph configuration. This
+// needs to be called before starting the graph.
+Status PrebuiltGraph::handleConfigPhase(const runner::ClientConfig& e) {
+    if (mGraphState.load() == PrebuiltGraphState::UNINITIALIZED) {
+        return Status::ILLEGAL_STATE;
+    }
+
+    // handleConfigPhase is a blocking call, so abort call is pointless for this RunnerEvent.
+    if (e.isAborted()) {
+        return Status::INVALID_ARGUMENT;
+    } else if (e.isTransitionComplete()) {
+        return Status::SUCCESS;
+    }
+
+    std::string config = e.getSerializedClientConfig();
+    auto mappedFn =
+        (PrebuiltComputepipeRunner_ErrorCode(*)(const unsigned char*, size_t))mFnUpdateGraphConfig;
+    PrebuiltComputepipeRunner_ErrorCode errorCode =
+        mappedFn(reinterpret_cast<const unsigned char*>(config.c_str()), config.length());
+    if (errorCode != PrebuiltComputepipeRunner_ErrorCode::SUCCESS) {
+        return static_cast<Status>(static_cast<int>(errorCode));
+    }
+
+    // Set the pixel stream callback function. The same function will be called for all requested
+    // pixel output streams.
+    if (mEngineInterface) {
+        auto pixelCallbackFn = (PrebuiltComputepipeRunner_ErrorCode(*)(
+            void (*)(void* cookie, int, int64_t, const uint8_t* pixels, int width, int height,
+                     int step, int format)))mFnSetOutputPixelStreamCallback;
+        PrebuiltComputepipeRunner_ErrorCode errorCode =
+            pixelCallbackFn(PrebuiltGraph::OutputPixelStreamCallbackFunction);
+        if (errorCode != PrebuiltComputepipeRunner_ErrorCode::SUCCESS) {
+            return static_cast<Status>(static_cast<int>(errorCode));
+        }
+
+        // Set the serialized stream callback function. The same callback function will be invoked
+        // for all requested serialized output streams.
+        auto streamCallbackFn = (PrebuiltComputepipeRunner_ErrorCode(*)(void (*)(
+            void* cookie, int, int64_t, const unsigned char*, size_t)))mFnSetOutputStreamCallback;
+        errorCode = streamCallbackFn(PrebuiltGraph::OutputStreamCallbackFunction);
+        if (errorCode != PrebuiltComputepipeRunner_ErrorCode::SUCCESS) {
+            return static_cast<Status>(static_cast<int>(errorCode));
+        }
+
+        // Set the callback function for when the graph terminates.
+        auto terminationCallback = (PrebuiltComputepipeRunner_ErrorCode(*)(
+            void (*)(void* cookie, const unsigned char*, size_t)))mFnSetGraphTerminationCallback;
+        errorCode = terminationCallback(PrebuiltGraph::GraphTerminationCallbackFunction);
+        if (errorCode != PrebuiltComputepipeRunner_ErrorCode::SUCCESS) {
+            return static_cast<Status>(static_cast<int>(errorCode));
+        }
+    }
+
+    return Status::SUCCESS;
+}
+
+// Starts the graph.
+Status PrebuiltGraph::handleExecutionPhase(const runner::RunnerEvent& e) {
+    if (mGraphState.load() != PrebuiltGraphState::STOPPED) {
+        return Status::ILLEGAL_STATE;
+    }
+
+    if (e.isAborted()) {
+        // Starting the graph is a blocking call and cannot be aborted in between.
+        return Status::INVALID_ARGUMENT;
+    } else if (e.isTransitionComplete()) {
+        return Status::SUCCESS;
+    }
+
+    auto mappedFn = (PrebuiltComputepipeRunner_ErrorCode(*)(void*, bool))mFnStartGraphExecution;
+    PrebuiltComputepipeRunner_ErrorCode errorCode =
+        mappedFn(reinterpret_cast<void*>(this), /* debuggingEnabled =*/false);
+    if (errorCode == PrebuiltComputepipeRunner_ErrorCode::SUCCESS) {
+        mGraphState.store(PrebuiltGraphState::RUNNING);
+    }
+    return static_cast<Status>(static_cast<int>(errorCode));
+}
+
+// Stops the graph while letting the graph flush output packets in flight.
+Status PrebuiltGraph::handleStopWithFlushPhase(const runner::RunnerEvent& e) {
+    if (mGraphState.load() != PrebuiltGraphState::RUNNING) {
+        return Status::ILLEGAL_STATE;
+    }
+
+    if (e.isAborted()) {
+        return Status::INVALID_ARGUMENT;
+    } else if (e.isTransitionComplete()) {
+        return Status::SUCCESS;
+    }
+
+    return StopGraphExecution(/* flushOutputFrames = */ true);
+}
+
+// Stops the graph and cancels all the output packets.
+Status PrebuiltGraph::handleStopImmediatePhase(const runner::RunnerEvent& e) {
+    if (mGraphState.load() != PrebuiltGraphState::RUNNING) {
+        return Status::ILLEGAL_STATE;
+    }
+
+    if (e.isAborted()) {
+        return Status::INVALID_ARGUMENT;
+    } else if (e.isTransitionComplete()) {
+        return Status::SUCCESS;
+    }
+
+    return StopGraphExecution(/* flushOutputFrames = */ false);
+}
+
+Status PrebuiltGraph::handleResetPhase(const runner::RunnerEvent& e) {
+    if (mGraphState.load() != PrebuiltGraphState::STOPPED) {
+        return Status::ILLEGAL_STATE;
+    }
+
+    if (e.isAborted()) {
+        return Status::INVALID_ARGUMENT;
+    } else if (e.isTransitionComplete()) {
+        return Status::SUCCESS;
+    }
+
+    auto mappedFn = (PrebuiltComputepipeRunner_ErrorCode(*)())mFnResetGraph;
+    mappedFn();
+    return Status::SUCCESS;
+}
+
+PrebuiltGraph* PrebuiltGraph::GetPrebuiltGraphFromLibrary(
+    const std::string& prebuilt_library, std::shared_ptr<PrebuiltEngineInterface> engineInterface) {
+    std::unique_lock<std::mutex> lock(PrebuiltGraph::mCreationMutex);
+    if (mPrebuiltGraphInstance != nullptr) {
+        mPrebuiltGraphInstance = new PrebuiltGraph();
+    }
+    if (mPrebuiltGraphInstance->mGraphState.load() != PrebuiltGraphState::UNINITIALIZED) {
+        return mPrebuiltGraphInstance;
+    }
+    mPrebuiltGraphInstance->mHandle = dlopen(prebuilt_library.c_str(), RTLD_NOW);
+
+    if (mPrebuiltGraphInstance->mHandle) {
+        bool initialized = true;
+
+        // Load config and version number first.
+        const unsigned char* (*getVersionFn)() = (const unsigned char* (*)())dlsym(
+            mPrebuiltGraphInstance->mHandle, "PrebuiltComputepipeRunner_GetVersion");
+        if (getVersionFn != nullptr) {
+            mPrebuiltGraphInstance->mGraphVersion =
+                std::string(reinterpret_cast<const char*>(getVersionFn()));
+        } else {
+            LOG(ERROR) << std::string(dlerror());
+            initialized = false;
+        }
+
+        const unsigned char* (*getSupportedGraphConfigsFn)() = (const unsigned char* (*)())dlsym(
+            mPrebuiltGraphInstance->mHandle, "PrebuiltComputepipeRunner_GetSupportedGraphConfigs");
+        if (getSupportedGraphConfigsFn != nullptr) {
+            initialized &= mPrebuiltGraphInstance->mGraphConfig.ParseFromString(
+                std::string(reinterpret_cast<const char*>(getSupportedGraphConfigsFn())));
+        } else {
+            LOG(ERROR) << std::string(dlerror());
+            initialized = false;
+        }
+
+        // Null callback interface is not acceptable.
+        if (initialized && engineInterface == nullptr) {
+            initialized = false;
+        }
+
+        LOAD_FUNCTION(GetErrorCode);
+        LOAD_FUNCTION(GetErrorMessage);
+        LOAD_FUNCTION(ResetGraph);
+        LOAD_FUNCTION(UpdateGraphConfig);
+        LOAD_FUNCTION(SetInputStreamData);
+        LOAD_FUNCTION(SetInputStreamPixelData);
+        LOAD_FUNCTION(SetOutputStreamCallback);
+        LOAD_FUNCTION(SetOutputPixelStreamCallback);
+        LOAD_FUNCTION(SetGraphTerminationCallback);
+        LOAD_FUNCTION(StartGraphExecution);
+        LOAD_FUNCTION(StopGraphExecution);
+        LOAD_FUNCTION(GetDebugInfo);
+
+        // This is the only way to create this object and there is already a
+        // lock around object creation, so no need to hold the graphState lock
+        // here.
+        if (initialized) {
+            mPrebuiltGraphInstance->mGraphState.store(PrebuiltGraphState::STOPPED);
+            mPrebuiltGraphInstance->mEngineInterface = engineInterface;
+        }
+    }
+
+    return mPrebuiltGraphInstance;
+}
+
+PrebuiltGraph::~PrebuiltGraph() {
+    if (mHandle) {
+        dlclose(mHandle);
+    }
+}
+
+Status PrebuiltGraph::GetStatus() const {
+    if (mGraphState.load() == PrebuiltGraphState::UNINITIALIZED) {
+        return Status::ILLEGAL_STATE;
+    }
+
+    auto mappedFn = (PrebuiltComputepipeRunner_ErrorCode(*)())mFnGetErrorCode;
+    PrebuiltComputepipeRunner_ErrorCode errorCode = mappedFn();
+    return static_cast<Status>(static_cast<int>(errorCode));
+}
+
+std::string PrebuiltGraph::GetErrorMessage() const {
+    if (mGraphState.load() == PrebuiltGraphState::UNINITIALIZED) {
+        return "Graph has not been initialized";
+    }
+    auto mappedFn =
+        (PrebuiltComputepipeRunner_ErrorCode(*)(unsigned char*, size_t, size_t*))mFnGetErrorMessage;
+    size_t errorMessageSize = 0;
+
+    PrebuiltComputepipeRunner_ErrorCode errorCode = mappedFn(nullptr, 0, &errorMessageSize);
+    std::vector<unsigned char> errorMessage(errorMessageSize);
+
+    errorCode = mappedFn(&errorMessage[0], errorMessage.size(), &errorMessageSize);
+    if (errorCode != PrebuiltComputepipeRunner_ErrorCode::SUCCESS) {
+        return "Unable to get error message from the graph.";
+    }
+
+    return std::string(reinterpret_cast<char*>(&errorMessage[0]),
+                       reinterpret_cast<char*>(&errorMessage[0]) + errorMessage.size());
+}
+
+Status PrebuiltGraph::SetInputStreamData(int streamIndex, int64_t timestamp,
+                                         const std::string& streamData) {
+    if (mGraphState.load() == PrebuiltGraphState::UNINITIALIZED) {
+        return Status::ILLEGAL_STATE;
+    }
+    auto mappedFn = (PrebuiltComputepipeRunner_ErrorCode(*)(int, int64_t, const unsigned char*,
+                                                            size_t))mFnSetInputStreamData;
+    PrebuiltComputepipeRunner_ErrorCode errorCode =
+        mappedFn(streamIndex, timestamp, reinterpret_cast<const unsigned char*>(streamData.c_str()),
+                 streamData.length());
+    return static_cast<Status>(static_cast<int>(errorCode));
+}
+
+Status PrebuiltGraph::SetInputStreamPixelData(int streamIndex, int64_t timestamp,
+                                              const uint8_t* pixels, int width, int height,
+                                              int step, PixelFormat format) {
+    if (mGraphState.load() == PrebuiltGraphState::UNINITIALIZED) {
+        return Status::ILLEGAL_STATE;
+    }
+
+    auto mappedFn = (PrebuiltComputepipeRunner_ErrorCode(*)(
+        int, int64_t, const uint8_t*, int, int, int,
+        PrebuiltComputepipeRunner_PixelDataFormat))mFnSetInputStreamPixelData;
+    PrebuiltComputepipeRunner_ErrorCode errorCode =
+        mappedFn(streamIndex, timestamp, pixels, width, height, step,
+                 static_cast<PrebuiltComputepipeRunner_PixelDataFormat>(static_cast<int>(format)));
+    return static_cast<Status>(static_cast<int>(errorCode));
+}
+
+Status PrebuiltGraph::StopGraphExecution(bool flushOutputFrames) {
+    auto mappedFn = (PrebuiltComputepipeRunner_ErrorCode(*)(bool))mFnStopGraphExecution;
+    PrebuiltComputepipeRunner_ErrorCode errorCode = mappedFn(flushOutputFrames);
+    if (errorCode == PrebuiltComputepipeRunner_ErrorCode::SUCCESS) {
+        mGraphState.store(flushOutputFrames ? PrebuiltGraphState::FLUSHING
+                                            : PrebuiltGraphState::STOPPED);
+    }
+    return static_cast<Status>(static_cast<int>(errorCode));
+}
+
+std::string PrebuiltGraph::GetDebugInfo() {
+    if (mGraphState.load() == PrebuiltGraphState::UNINITIALIZED) {
+        return "";
+    }
+    auto mappedFn =
+        (PrebuiltComputepipeRunner_ErrorCode(*)(unsigned char*, size_t, size_t*))mFnGetDebugInfo;
+
+    size_t debugInfoSize = 0;
+    PrebuiltComputepipeRunner_ErrorCode errorCode = mappedFn(nullptr, 0, &debugInfoSize);
+    std::vector<unsigned char> debugInfo(debugInfoSize);
+
+    errorCode = mappedFn(&debugInfo[0], debugInfo.size(), &debugInfoSize);
+    if (errorCode != PrebuiltComputepipeRunner_ErrorCode::SUCCESS) {
+        return "";
+    }
+
+    return std::string(reinterpret_cast<char*>(&debugInfo[0]),
+                       reinterpret_cast<char*>(&debugInfo[0]) + debugInfo.size());
+}
+
+void PrebuiltGraph::OutputStreamCallbackFunction(void* cookie, int streamIndex, int64_t timestamp,
+                                                 const unsigned char* data, size_t data_size) {
+    PrebuiltGraph* graph = reinterpret_cast<PrebuiltGraph*>(cookie);
+    CHECK(graph);
+    graph->mEngineInterface->DispatchSerializedData(streamIndex, timestamp,
+                                                    std::string(data, data + data_size));
+}
+
+void PrebuiltGraph::OutputPixelStreamCallbackFunction(void* cookie, int streamIndex,
+                                                      int64_t timestamp, const uint8_t* pixels,
+                                                      int width, int height, int step, int format) {
+    PrebuiltGraph* graph = reinterpret_cast<PrebuiltGraph*>(cookie);
+    CHECK(graph);
+    graph->mEngineInterface->DispatchPixelData(streamIndex, timestamp, pixels, width, height, step,
+                                               static_cast<PixelFormat>(format));
+}
+
+void PrebuiltGraph::GraphTerminationCallbackFunction(void* cookie,
+                                                     const unsigned char* termination_message,
+                                                     size_t termination_message_size) {
+    PrebuiltGraph* graph = reinterpret_cast<PrebuiltGraph*>(cookie);
+    CHECK(graph);
+    std::string errorMessage = "";
+    if (termination_message != nullptr && termination_message_size > 0) {
+        std::string(termination_message, termination_message + termination_message_size);
+    }
+    graph->mGraphState.store(PrebuiltGraphState::STOPPED);
+    graph->mEngineInterface->DispatchGraphTerminationMessage(graph->GetStatus(),
+                                                             std::move(errorMessage));
+}
+
+}  // namespace graph
+}  // namespace computepipe
+}  // namespace automotive
+}  // namespace android
diff --git a/computepipe/runner/graph/include/PrebuiltEngineInterface.h b/computepipe/runner/graph/include/PrebuiltEngineInterface.h
new file mode 100644
index 0000000..197ef8b
--- /dev/null
+++ b/computepipe/runner/graph/include/PrebuiltEngineInterface.h
@@ -0,0 +1,43 @@
+// Copyright (C) 2020 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 COMPUTEPIPE_RUNNER_GRAPH_INCLUDE_PREBUILTENGINEINTERFACE_H_
+#define COMPUTEPIPE_RUNNER_GRAPH_INCLUDE_PREBUILTENGINEINTERFACE_H_
+
+#include <functional>
+
+#include "types/Status.h"
+
+namespace android {
+namespace automotive {
+namespace computepipe {
+namespace graph {
+
+class PrebuiltEngineInterface {
+  public:
+    virtual ~PrebuiltEngineInterface() = default;
+
+    virtual void DispatchPixelData(int streamId, int64_t timestamp, const uint8_t* pixels,
+                                   int width, int height, int step, PixelFormat format) = 0;
+
+    virtual void DispatchSerializedData(int streamId, int64_t timestamp, std::string&&) = 0;
+
+    virtual void DispatchGraphTerminationMessage(Status, std::string&&) = 0;
+};
+
+}  // namespace graph
+}  // namespace computepipe
+}  // namespace automotive
+}  // namespace android
+
+#endif  // COMPUTEPIPE_RUNNER_GRAPH_INCLUDE_PREBUILTENGINEINTERFACE_H_
diff --git a/computepipe/runner/graph/include/PrebuiltGraph.h b/computepipe/runner/graph/include/PrebuiltGraph.h
new file mode 100644
index 0000000..f96a8ad
--- /dev/null
+++ b/computepipe/runner/graph/include/PrebuiltGraph.h
@@ -0,0 +1,152 @@
+// Copyright (C) 2020 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 COMPUTEPIPE_RUNNER_GRAPH_INCLUDE_PREBUILTGRAPH_H_
+#define COMPUTEPIPE_RUNNER_GRAPH_INCLUDE_PREBUILTGRAPH_H_
+
+#include <functional>
+#include <shared_mutex>
+#include <string>
+
+#include "ClientConfig.pb.h"
+#include "Options.pb.h"
+#include "PrebuiltEngineInterface.h"
+#include "RunnerComponent.h"
+#include "types/Status.h"
+
+namespace android {
+namespace automotive {
+namespace computepipe {
+namespace graph {
+
+enum PrebuiltGraphState {
+    RUNNING = 0,
+    UNINITIALIZED,
+    FLUSHING,
+    STOPPED,
+};
+
+// PrebuiltGraph is a singleton class. This is because the underlying functions that it implements
+// are C functions that carry around state.
+class PrebuiltGraph : public runner::RunnerComponentInterface {
+  private:
+    // Private constructor
+    PrebuiltGraph() {
+    }
+
+  public:
+    ~PrebuiltGraph();
+
+    // No copy or move constructors or operators are available.
+    PrebuiltGraph(const PrebuiltGraph&) = delete;
+    PrebuiltGraph& operator=(const PrebuiltGraph&) = delete;
+    PrebuiltGraph(PrebuiltGraph&&) = delete;
+    PrebuiltGraph& operator=(PrebuiltGraph&&) = delete;
+
+    // Override RunnerComponent interface functions for applying configs,
+    // starting the graph and stopping the graph.
+    Status handleConfigPhase(const runner::ClientConfig& e) override;
+    Status handleExecutionPhase(const runner::RunnerEvent& e) override;
+    Status handleStopWithFlushPhase(const runner::RunnerEvent& e) override;
+    Status handleStopImmediatePhase(const runner::RunnerEvent& e) override;
+    Status handleResetPhase(const runner::RunnerEvent& e) override;
+
+    static PrebuiltGraph* GetPrebuiltGraphFromLibrary(
+        const std::string& prebuiltLib, std::shared_ptr<PrebuiltEngineInterface> engineInterface);
+
+    PrebuiltGraphState GetGraphState() const {
+        return mGraphState;
+    }
+
+    Status GetStatus() const;
+
+    std::string GetErrorMessage() const;
+
+    // Gets the supported graph config options.
+    const proto::Options& GetSupportedGraphConfigs() const {
+        return mGraphConfig;
+    }
+
+    // Sets input stream data. The string is expected to be a serialized proto
+    // the definition of which is known to the graph.
+    Status SetInputStreamData(int streamIndex, int64_t timestamp, const std::string& streamData);
+
+    // Sets pixel data to the specified input stream index.
+    Status SetInputStreamPixelData(int streamIndex, int64_t timestamp, const uint8_t* pixels,
+                                   int width, int height, int step, PixelFormat format);
+
+    // Collects debugging and profiling information for the graph. The graph
+    // needs to be started with debugging enabled in order to get valid info.
+    std::string GetDebugInfo();
+
+  private:
+    // Starts the graph execution.
+    Status StartGraphExecution(bool debuggingEnabled);
+
+    // Stops the graph execution.
+    Status StopGraphExecution(bool flushOutputFrames);
+
+    // Callback functions. The class has a C++ function callback interface while it deals with pure
+    // C functions underneath that do not have object context. We need to have these static
+    // functions that need to be passed to the C interface.
+    static void OutputPixelStreamCallbackFunction(void* cookie, int streamIndex, int64_t timestamp,
+                                                  const uint8_t* pixels, int width, int height,
+                                                  int step, int format);
+    static void OutputStreamCallbackFunction(void* cookie, int streamIndex, int64_t timestamp,
+                                             const unsigned char* data, size_t dataSize);
+    static void GraphTerminationCallbackFunction(void* cookie,
+                                                 const unsigned char* terminationMessage,
+                                                 size_t terminationMessageSize);
+
+    // Cached callback interface that is passed in from the runner.
+    std::shared_ptr<PrebuiltEngineInterface> mEngineInterface;
+
+    static std::mutex mCreationMutex;
+    static PrebuiltGraph* mPrebuiltGraphInstance;
+
+    // Even though mutexes are generally preferred over atomics, the only varialble in this class
+    // that changes after initialization is graph state and that is the only vairable that needs
+    // to be guarded. The prebuilt is internally assumed to be thread safe, so that concurrent
+    // calls into the library will automatically be handled in a thread safe manner by the it.
+    std::atomic<PrebuiltGraphState> mGraphState = PrebuiltGraphState::UNINITIALIZED;
+
+    // Dynamic library handle
+    void* mHandle;
+
+    // Repeated function calls need not be made to get the graph version and the config is this is
+    // constant through the operation of the graph. These values are just cached as strings.
+    std::string mGraphVersion;
+    proto::Options mGraphConfig;
+
+    // Cached functions from the dynamic library.
+    void* mFnGetErrorCode;
+    void* mFnGetErrorMessage;
+    void* mFnUpdateGraphConfig;
+    void* mFnResetGraph;
+    void* mFnSetInputStreamData;
+    void* mFnSetInputStreamPixelData;
+    void* mFnSetOutputStreamCallback;
+    void* mFnSetOutputPixelStreamCallback;
+    void* mFnSetGraphTerminationCallback;
+    void* mFnStartGraphExecution;
+    void* mFnStopGraphExecution;
+    void* mFnGetDebugInfo;
+};
+
+}  // namespace graph
+}  // namespace computepipe
+}  // namespace automotive
+}  // namespace android
+
+#endif  // COMPUTEPIPE_RUNNER_GRAPH_INCLUDE_PREBUILTGRAPH_H_
diff --git a/computepipe/runner/include/prebuilt_interface.h b/computepipe/runner/include/prebuilt_interface.h
new file mode 100644
index 0000000..0210775
--- /dev/null
+++ b/computepipe/runner/include/prebuilt_interface.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2019 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 COMPUTEPIPE_RUNNER_INCLUDE_PREBUILT_INTERFACE_H_
+#define COMPUTEPIPE_RUNNER_INCLUDE_PREBUILT_INTERFACE_H_
+
+#include <cstddef>
+#include <cstdint>
+
+#define COMPUTEPIPE_RUNNER(a) PrebuiltComputepipeRunner_##a
+
+extern "C" {
+
+// Enum value to report the error code for function calls.
+enum PrebuiltComputepipeRunner_ErrorCode {
+    SUCCESS = 0,
+    INTERNAL_ERROR,
+    INVALID_ARGUMENT,
+    ILLEGAL_STATE,
+    NO_MEMORY,
+    FATAL_ERROR,
+    ERROR_CODE_MAX,
+};
+
+enum PrebuiltComputepipeRunner_PixelDataFormat {
+    RGB = 0,
+    RGBA = 1,
+    GRAY = 2,
+    PIXEL_DATA_FORMAT_MAX = 3,
+};
+
+// Gets the version of the library. The runner should check if the version of
+// the prebuilt matches the version of android runner for which it was built
+// and fail out if needed.
+const unsigned char* COMPUTEPIPE_RUNNER(GetVersion)();
+
+// Gets the error code. This API is necessary because the graph execution is
+// asynchronous and even if the function calls to execute the graph succeed, it
+// could fail at a later point in the execution of the graph.
+PrebuiltComputepipeRunner_ErrorCode COMPUTEPIPE_RUNNER(GetErrorCode)();
+
+// Gets the graph error message from the graph. The return value is not the
+// graph error code but the error code returned if the call to the function
+// fails.
+PrebuiltComputepipeRunner_ErrorCode COMPUTEPIPE_RUNNER(GetErrorMessage)(
+    unsigned char* error_msg_buffer, size_t error_msg_buffer_size, size_t* error_msg_size);
+
+// Gets the supported graph config options.
+const unsigned char* COMPUTEPIPE_RUNNER(GetSupportedGraphConfigs)();
+
+// Sets the graph configuration or updates it if an incomplete config is passed.
+PrebuiltComputepipeRunner_ErrorCode COMPUTEPIPE_RUNNER(UpdateGraphConfig)(
+    const unsigned char* graph_config, size_t graph_config_size);
+
+// Sets the stream contents. This can only be used after the graph has started
+// running successfully. The contents of this stream are typically a serialized
+// proto and would be deserialized and fed into the graph.
+PrebuiltComputepipeRunner_ErrorCode COMPUTEPIPE_RUNNER(SetInputStreamData)(
+    int stream_index, int64_t timestamp, const unsigned char* stream_data, size_t stream_data_size);
+
+// Sets the pixel data as stream contents. This can be set only after the graph
+// has started running successfully. Pixel data should be copied within this
+// function as there are no guarantess on the lifetime of the pixel data beyond
+// the return of this function call.
+PrebuiltComputepipeRunner_ErrorCode COMPUTEPIPE_RUNNER(SetInputStreamPixelData)(
+    int stream_index, int64_t timestamp, const uint8_t* pixels, int width, int height, int step,
+    int format);
+
+// Sets a callback function for when a packet is generated. Note that a c-style
+// function needs to be passed as no object context is being passed around here.
+// The runner would be responsible for using the buffer provided in the callback
+// immediately or copying it as there are no guarantees on its lifetime beyond
+// the return of the callback.
+PrebuiltComputepipeRunner_ErrorCode COMPUTEPIPE_RUNNER(SetOutputStreamCallback)(
+    void (*streamCallback)(void* cookie, int stream_index, int64_t timestamp,
+                           const unsigned char* data, size_t data_size));
+
+// Sets a callback function for when new pixel data is generated. C-style
+// function pointers need to passed as no object context is being passed around.
+// The runner would be responsible for immediately copying out the data. The
+// prebuilt is expected to pass contiguous data.
+PrebuiltComputepipeRunner_ErrorCode COMPUTEPIPE_RUNNER(SetOutputPixelStreamCallback)(
+    void (*streamCallback)(void* cookie, int stream_index, int64_t timestamp, const uint8_t* pixels,
+                           int width, int height, int step, int format));
+
+// Sets a callback function for when the graph terminates.
+PrebuiltComputepipeRunner_ErrorCode COMPUTEPIPE_RUNNER(SetGraphTerminationCallback)(
+    void (*terminationCallback)(void* cookie, const unsigned char* termination_message,
+                                size_t termination_message_size));
+
+// Starts the graph execution. Debugging can be enabled which will enable
+// profiling. The profiling info can be obtained by calling getDebugInfo once
+// the graph execution has stopped.
+PrebuiltComputepipeRunner_ErrorCode COMPUTEPIPE_RUNNER(StartGraphExecution)(void* cookie,
+                                                                            bool debugging_enabled);
+
+// Stops the graph execution.
+PrebuiltComputepipeRunner_ErrorCode COMPUTEPIPE_RUNNER(StopGraphExecution)(bool flushOutputFrames);
+
+// Resets the graph completely. Should be called only after graph execution has been stopped.
+void COMPUTEPIPE_RUNNER(ResetGraph)();
+
+// Get debugging/profiling information. The function outputs the size of
+// profiling information string and if the buffer size is larger than or equal
+// to the size, then it copies it over to the buffer. Debugging info will be
+// empty if the graph is started without debugging support.
+PrebuiltComputepipeRunner_ErrorCode COMPUTEPIPE_RUNNER(GetDebugInfo)(unsigned char* debug_info,
+                                                                     size_t debug_info_buffer_size,
+                                                                     size_t* debug_info_size);
+}
+#endif  // COMPUTEPIPE_RUNNER_INCLUDE_PREBUILT_INTERFACE_H_
diff --git a/computepipe/tests/runner/Android.bp b/computepipe/tests/runner/Android.bp
index a3edb37..144f42b 100644
--- a/computepipe/tests/runner/Android.bp
+++ b/computepipe/tests/runner/Android.bp
@@ -21,7 +21,7 @@
     static_libs: [
         "libgtest",
         "libgmock",
-	"libcomputepipeprotos",
+	  "libcomputepipeprotos",
     ],
     shared_libs: [
         "libcomputepipe_stream_manager",
diff --git a/computepipe/tests/runner/graph/Android.bp b/computepipe/tests/runner/graph/Android.bp
new file mode 100644
index 0000000..e8aad96
--- /dev/null
+++ b/computepipe/tests/runner/graph/Android.bp
@@ -0,0 +1,44 @@
+// Copyright (C) 2020 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.
+
+cc_test {
+    name: "computepipe_prebuilt_graph_test",
+    test_suites: ["device-tests"],
+    srcs: [
+        "EnumConversionTest.cpp",
+        "PrebuiltGraphTest.cpp",
+    ],
+    static_libs: [
+        "computepipe_prebuilt_graph",
+        "computepipe_runner_component",
+        "libgtest",
+        "libgmock",
+	      "libcomputepipeprotos",
+    ],
+    shared_libs: [
+        "libstubgraphimpl",
+	      "libprotobuf-cpp-full",
+        "liblog",
+        "libdl",
+        "libbase",
+    ],
+    header_libs: [
+        "computepipe_runner_includes",
+    ],
+    include_dirs: [
+        "packages/services/Car/computepipe",
+        "packages/services/Car/computepipe/runner/graph",
+    ],
+
+}
diff --git a/computepipe/tests/runner/graph/EnumConversionTest.cpp b/computepipe/tests/runner/graph/EnumConversionTest.cpp
new file mode 100644
index 0000000..bbb9408
--- /dev/null
+++ b/computepipe/tests/runner/graph/EnumConversionTest.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2020 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 "gmock/gmock-matchers.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "prebuilt_interface.h"
+#include "types/Status.h"
+
+namespace android {
+namespace automotive {
+namespace computepipe {
+namespace graph {
+namespace {
+
+TEST(EnumConversionTest, StatusToErrorCodeEnums) {
+    EXPECT_EQ(static_cast<int>(PrebuiltComputepipeRunner_ErrorCode::SUCCESS),
+              static_cast<int>(Status::SUCCESS));
+    EXPECT_EQ(static_cast<int>(PrebuiltComputepipeRunner_ErrorCode::INTERNAL_ERROR),
+              static_cast<int>(Status::INTERNAL_ERROR));
+    EXPECT_EQ(PrebuiltComputepipeRunner_ErrorCode::INVALID_ARGUMENT,
+              static_cast<int>(Status::INVALID_ARGUMENT));
+    EXPECT_EQ(PrebuiltComputepipeRunner_ErrorCode::ILLEGAL_STATE,
+              static_cast<int>(Status::ILLEGAL_STATE));
+    EXPECT_EQ(PrebuiltComputepipeRunner_ErrorCode::NO_MEMORY, static_cast<int>(Status::NO_MEMORY));
+    EXPECT_EQ(PrebuiltComputepipeRunner_ErrorCode::FATAL_ERROR,
+              static_cast<int>(Status::FATAL_ERROR));
+    EXPECT_EQ(PrebuiltComputepipeRunner_ErrorCode::ERROR_CODE_MAX,
+              static_cast<int>(Status::STATUS_MAX));
+}
+enum PrebuiltComputepipeRunner_PixelDataFormat {
+    RGB = 0,
+    RGBA = 1,
+    GRAY = 2,
+    PIXEL_DATA_FORMAT_MAX = 3,
+};
+TEST(EnumConversionTest, PixelFormatEnums) {
+    EXPECT_EQ(static_cast<int>(PrebuiltComputepipeRunner_PixelDataFormat::RGB),
+              static_cast<int>(PixelFormat::RGB));
+    EXPECT_EQ(static_cast<int>(PrebuiltComputepipeRunner_PixelDataFormat::RGBA),
+              static_cast<int>(PixelFormat::RGBA));
+    EXPECT_EQ(PrebuiltComputepipeRunner_PixelDataFormat::GRAY, static_cast<int>(PixelFormat::GRAY));
+    EXPECT_EQ(PrebuiltComputepipeRunner_PixelDataFormat::PIXEL_DATA_FORMAT_MAX,
+              static_cast<int>(PixelFormat::PIXELFORMAT_MAX));
+}
+
+}  // namespace
+}  // namespace graph
+}  // namespace computepipe
+}  // namespace automotive
+}  // namespace android
diff --git a/computepipe/tests/runner/graph/PrebuiltGraphTest.cpp b/computepipe/tests/runner/graph/PrebuiltGraphTest.cpp
new file mode 100644
index 0000000..6a77b7e
--- /dev/null
+++ b/computepipe/tests/runner/graph/PrebuiltGraphTest.cpp
@@ -0,0 +1,226 @@
+/*
+ * Copyright 2020 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 <string>
+
+#include "ClientConfig.pb.h"
+#include "PrebuiltEngineInterface.h"
+#include "PrebuiltGraph.h"
+#include "RunnerComponent.h"
+#include "gmock/gmock-matchers.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "types/Status.h"
+
+using ::android::automotive::computepipe::runner::ClientConfig;
+using ::android::automotive::computepipe::runner::RunnerComponentInterface;
+using ::android::automotive::computepipe::runner::RunnerEvent;
+using ::testing::HasSubstr;
+
+namespace android {
+namespace automotive {
+namespace computepipe {
+namespace graph {
+namespace {
+
+// Barebones implementation of the PrebuiltEngineInterface. This implementation should suffice for
+// basic cases. More complicated use cases might need their own implementation of it.
+typedef std::function<void(int, int64_t, const uint8_t*, int, int, int, PixelFormat)> PixelCallback;
+typedef std::function<void(int, int64_t, std::string&&)> SerializedStreamCallback;
+typedef std::function<void(Status, std::string&&)> GraphTerminationCallback;
+class PrebuiltEngineInterfaceImpl : public PrebuiltEngineInterface {
+  private:
+    PixelCallback mPixelCallbackFn;
+    SerializedStreamCallback mSerializedStreamCallbackFn;
+    GraphTerminationCallback mGraphTerminationCallbackFn;
+
+  public:
+    virtual ~PrebuiltEngineInterfaceImpl() = default;
+
+    void DispatchPixelData(int streamId, int64_t timestamp, const uint8_t* pixels, int width,
+                           int height, int step, PixelFormat format) override {
+        mPixelCallbackFn(streamId, timestamp, pixels, width, height, step, format);
+    }
+
+    void DispatchSerializedData(int streamId, int64_t timestamp, std::string&& data) override {
+        mSerializedStreamCallbackFn(streamId, timestamp, std::move(data));
+    }
+
+    void DispatchGraphTerminationMessage(Status status, std::string&& msg) override {
+        mGraphTerminationCallbackFn(status, std::move(msg));
+    }
+
+    void SetPixelCallback(PixelCallback callback) {
+        mPixelCallbackFn = callback;
+    }
+
+    void SetSerializedStreamCallback(SerializedStreamCallback callback) {
+        mSerializedStreamCallbackFn = callback;
+    }
+
+    void SetGraphTerminationCallback(GraphTerminationCallback callback) {
+        mGraphTerminationCallbackFn = callback;
+    }
+};
+
+// The stub graph implementation is a passthrough implementation that does not run
+// any graph and returns success for all implementations. The only useful things that
+// it does for the tests are
+//
+//    1. Stores the name of the function last visited and returns that with GetErrorMessage call
+//    2. When an input stream is set, it immediately returns an output callback with the same input
+//       data and timestamp. Similar callback is issued for when input stream pixel data is set too
+//
+// The above two properties are used to test that the prebuilt graph wrapper calls the correct
+// functions and callbacks are issued as expected. These tests do not test the internals of the
+// graph themselves and such tests must be written along with the graph implementation.
+TEST(PrebuiltGraphTest, FunctionMappingFromLibraryIsSuccessful) {
+    PrebuiltEngineInterfaceImpl callback;
+    std::shared_ptr<PrebuiltEngineInterface> engineInterface =
+        std::static_pointer_cast<PrebuiltEngineInterface, PrebuiltEngineInterfaceImpl>(
+            std::make_shared<PrebuiltEngineInterfaceImpl>(callback));
+    PrebuiltGraph* graph =
+        PrebuiltGraph::GetPrebuiltGraphFromLibrary("libstubgraphimpl.so", engineInterface);
+    ASSERT_TRUE(graph);
+    EXPECT_NE(graph->GetGraphState(), PrebuiltGraphState::UNINITIALIZED);
+}
+
+TEST(PrebuiltGraphTest, GraphConfigurationIssuesCorrectFunctionCalls) {
+    PrebuiltEngineInterfaceImpl callback;
+    std::shared_ptr<PrebuiltEngineInterface> engineInterface =
+        std::static_pointer_cast<PrebuiltEngineInterface, PrebuiltEngineInterfaceImpl>(
+            std::make_shared<PrebuiltEngineInterfaceImpl>(callback));
+    PrebuiltGraph* graph =
+        PrebuiltGraph::GetPrebuiltGraphFromLibrary("libstubgraphimpl.so", engineInterface);
+    ASSERT_TRUE(graph);
+    ASSERT_NE(graph->GetGraphState(), PrebuiltGraphState::UNINITIALIZED);
+
+    graph->GetSupportedGraphConfigs();
+    std::string functionVisited = graph->GetErrorMessage();
+    EXPECT_THAT(functionVisited, HasSubstr("GetSupportedGraphConfigs"));
+
+    std::map<int, int> maxOutputPacketsPerStream;
+    ClientConfig e(0, 0, 0, maxOutputPacketsPerStream);
+    e.setPhaseState(runner::PhaseState::ENTRY);
+    EXPECT_EQ(graph->handleConfigPhase(e), Status::SUCCESS);
+    functionVisited = graph->GetErrorMessage();
+
+    EXPECT_EQ(graph->GetStatus(), Status::SUCCESS);
+    functionVisited = graph->GetErrorMessage();
+    EXPECT_THAT(functionVisited, HasSubstr("GetErrorCode"));
+}
+
+TEST(PrebuiltGraphTest, GraphOperationEndToEndIsSuccessful) {
+    bool graphHasTerminated = false;
+    int numOutputStreamCallbacksReceived[4] = {0, 0, 0, 0};
+
+    PrebuiltEngineInterfaceImpl callback;
+    callback.SetGraphTerminationCallback(
+        [&graphHasTerminated](Status, std::string) { graphHasTerminated = true; });
+
+    // Add multiple pixel stream callback functions to see if all of them register.
+    callback.SetPixelCallback([&numOutputStreamCallbacksReceived](int streamIndex, int64_t,
+                                                                  const uint8_t*, int, int, int,
+                                                                  PixelFormat) {
+        ASSERT_TRUE(streamIndex == 0 || streamIndex == 1);
+        numOutputStreamCallbacksReceived[streamIndex]++;
+    });
+
+    // Add multiple stream callback functions to see if all of them register.
+    callback.SetSerializedStreamCallback(
+        [&numOutputStreamCallbacksReceived](int streamIndex, int64_t, std::string&&) {
+            ASSERT_TRUE(streamIndex == 2 || streamIndex == 3);
+            numOutputStreamCallbacksReceived[streamIndex]++;
+        });
+
+    std::shared_ptr<PrebuiltEngineInterface> engineInterface =
+        std::static_pointer_cast<PrebuiltEngineInterface, PrebuiltEngineInterfaceImpl>(
+            std::make_shared<PrebuiltEngineInterfaceImpl>(callback));
+
+    PrebuiltGraph* graph =
+        PrebuiltGraph::GetPrebuiltGraphFromLibrary("libstubgraphimpl.so", engineInterface);
+
+    ASSERT_NE(graph->GetGraphState(), PrebuiltGraphState::UNINITIALIZED);
+
+    graph->GetSupportedGraphConfigs();
+    std::string functionVisited = graph->GetErrorMessage();
+    EXPECT_THAT(functionVisited, HasSubstr("GetSupportedGraphConfigs"));
+
+    std::map<int, int> maxOutputPacketsPerStream;
+    ClientConfig e(0, 0, 0, maxOutputPacketsPerStream);
+    e.setPhaseState(runner::PhaseState::ENTRY);
+    EXPECT_EQ(graph->handleConfigPhase(e), Status::SUCCESS);
+    functionVisited = graph->GetErrorMessage();
+
+    EXPECT_EQ(graph->handleExecutionPhase(e), Status::SUCCESS);
+    functionVisited = graph->GetErrorMessage();
+    EXPECT_THAT(functionVisited, HasSubstr("StartGraphExecution"));
+
+    EXPECT_EQ(graph->SetInputStreamPixelData(
+                  /*streamIndex =*/0, /*timestamp =*/0, /*pixels =*/nullptr,
+                  /*width = */ 0, /*height =*/0, /*step =*/0, PixelFormat::RGB),
+              Status::SUCCESS);
+    EXPECT_EQ(graph->SetInputStreamPixelData(
+                  /*streamIndex =*/0, /*timestamp =*/0, /*pixels =*/nullptr,
+                  /*width = */ 0, /*height =*/0, /*step =*/0, PixelFormat::RGB),
+              Status::SUCCESS);
+    EXPECT_EQ(graph->SetInputStreamPixelData(
+                  /*streamIndex =*/0, /*timestamp =*/0, /*pixels =*/nullptr,
+                  /*width = */ 0, /*height =*/0, /*step =*/0, PixelFormat::RGB),
+              Status::SUCCESS);
+    EXPECT_EQ(graph->SetInputStreamPixelData(
+                  /*streamIndex =*/1, /*timestamp =*/0, /*pixels =*/nullptr,
+                  /*width = */ 0, /*height =*/0, /*step =*/0, PixelFormat::RGB),
+              Status::SUCCESS);
+    EXPECT_EQ(graph->SetInputStreamPixelData(
+                  /*streamIndex =*/1, /*timestamp =*/0, /*pixels =*/nullptr,
+                  /*width = */ 0, /*height =*/0, /*step =*/0, PixelFormat::RGB),
+              Status::SUCCESS);
+    functionVisited = graph->GetErrorMessage();
+    EXPECT_THAT(functionVisited, HasSubstr("SetInputStreamPixelData"));
+
+    EXPECT_EQ(graph->SetInputStreamData(/*streamIndex =*/2, /* timestamp =*/0, /* data =*/""),
+              Status::SUCCESS);
+    EXPECT_EQ(graph->SetInputStreamData(/*streamIndex =*/2, /* timestamp =*/0, /* data =*/""),
+              Status::SUCCESS);
+    EXPECT_EQ(graph->SetInputStreamData(/*streamIndex =*/2, /* timestamp =*/0, /* data =*/""),
+              Status::SUCCESS);
+    EXPECT_EQ(graph->SetInputStreamData(/*streamIndex =*/3, /* timestamp =*/0, /* data =*/""),
+              Status::SUCCESS);
+    EXPECT_EQ(graph->SetInputStreamData(/*streamIndex =*/3, /* timestamp =*/0, /* data =*/""),
+              Status::SUCCESS);
+    functionVisited = graph->GetErrorMessage();
+    EXPECT_THAT(functionVisited, HasSubstr("SetInputStreamData"));
+
+    EXPECT_EQ(numOutputStreamCallbacksReceived[0], 3);
+    EXPECT_EQ(numOutputStreamCallbacksReceived[1], 2);
+    EXPECT_EQ(numOutputStreamCallbacksReceived[2], 3);
+    EXPECT_EQ(numOutputStreamCallbacksReceived[3], 2);
+
+    EXPECT_FALSE(graphHasTerminated);
+    EXPECT_EQ(graph->handleStopImmediatePhase(e), Status::SUCCESS);
+
+    EXPECT_EQ(graph->handleResetPhase(e), Status::SUCCESS);
+    functionVisited = graph->GetErrorMessage();
+    EXPECT_THAT(functionVisited, HasSubstr("ResetGraph"));
+
+    EXPECT_TRUE(graphHasTerminated);
+}
+
+}  // namespace
+}  // namespace graph
+}  // namespace computepipe
+}  // namespace automotive
+}  // namespace android
diff --git a/computepipe/tests/runner/graph/stubgraph/Android.bp b/computepipe/tests/runner/graph/stubgraph/Android.bp
new file mode 100644
index 0000000..d01b69f
--- /dev/null
+++ b/computepipe/tests/runner/graph/stubgraph/Android.bp
@@ -0,0 +1,41 @@
+// Copyright (C) 2020 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.
+
+cc_prebuilt_library_shared {
+    name: "libstubgraphimpl",
+    target: {
+        android_arm64: {
+            srcs: ["arm64/libstubgraphimpl.so"],
+        },
+        android_arm: {
+            srcs: ["arm/libstubgraphimpl.so"],
+        },
+        android_x86: {
+            srcs: ["x86/libstubgraphimpl.so"],
+        },
+        android_x86_64: {
+            srcs: ["x86_64/libstubgraphimpl.so"],
+        },
+    },
+
+    shared_libs: [
+        "libc",
+        "libdl",
+        "liblog",
+        "libm"
+    ],
+    strip: {
+        keep_symbols: true,
+    }
+}
diff --git a/computepipe/tests/runner/graph/stubgraph/arm/libstubgraphimpl.so b/computepipe/tests/runner/graph/stubgraph/arm/libstubgraphimpl.so
new file mode 100755
index 0000000..458b8ac
--- /dev/null
+++ b/computepipe/tests/runner/graph/stubgraph/arm/libstubgraphimpl.so
Binary files differ
diff --git a/computepipe/tests/runner/graph/stubgraph/arm64/libstubgraphimpl.so b/computepipe/tests/runner/graph/stubgraph/arm64/libstubgraphimpl.so
new file mode 100755
index 0000000..f6f6b3a
--- /dev/null
+++ b/computepipe/tests/runner/graph/stubgraph/arm64/libstubgraphimpl.so
Binary files differ
diff --git a/computepipe/tests/runner/graph/stubgraph/x86/libstubgraphimpl.so b/computepipe/tests/runner/graph/stubgraph/x86/libstubgraphimpl.so
new file mode 100755
index 0000000..6f71651
--- /dev/null
+++ b/computepipe/tests/runner/graph/stubgraph/x86/libstubgraphimpl.so
Binary files differ
diff --git a/computepipe/tests/runner/graph/stubgraph/x86_64/libstubgraphimpl.so b/computepipe/tests/runner/graph/stubgraph/x86_64/libstubgraphimpl.so
new file mode 100755
index 0000000..e4e7e75
--- /dev/null
+++ b/computepipe/tests/runner/graph/stubgraph/x86_64/libstubgraphimpl.so
Binary files differ
diff --git a/computepipe/types/Status.h b/computepipe/types/Status.h
index 4bd126f..af05c6a 100644
--- a/computepipe/types/Status.h
+++ b/computepipe/types/Status.h
@@ -26,6 +26,14 @@
     ILLEGAL_STATE,
     NO_MEMORY,
     FATAL_ERROR,
+    STATUS_MAX,
+};
+
+enum PixelFormat {
+    RGB = 0,
+    RGBA,
+    GRAY,
+    PIXELFORMAT_MAX,
 };
 
 }  // namespace computepipe