Merge "Adding the interface file for the prebuilt computepipe graph execution library."
diff --git a/computepipe/runner/include/MemHandle.h b/computepipe/runner/include/MemHandle.h
index 19c5c73..bd9198d 100644
--- a/computepipe/runner/include/MemHandle.h
+++ b/computepipe/runner/include/MemHandle.h
@@ -24,16 +24,18 @@
 
 class MemHandle {
   public:
+    /* Retrieve stream Id */
+    virtual int getStreamId() const = 0;
     /* Retrieve packet type */
-    virtual proto::PacketType getType() = 0;
+    virtual proto::PacketType getType() const = 0;
     /* Retrieve packet time stamp */
-    virtual uint64_t getTimeStamp() = 0;
+    virtual uint64_t getTimeStamp() const = 0;
     /* Get size */
-    virtual uint32_t getSize() = 0;
+    virtual uint32_t getSize() const = 0;
     /* Get data, raw pointer. Only implemented for copy semantics */
-    virtual const char* getData() = 0;
+    virtual const char* getData() const = 0;
     /* Get native handle. data with zero copy semantics */
-    virtual native_handle_t getNativeHandle() = 0;
+    virtual native_handle_t getNativeHandle() const = 0;
 
     virtual ~MemHandle() {
     }
diff --git a/computepipe/runner/stream_manager/Factory.cpp b/computepipe/runner/stream_manager/Factory.cpp
index 9a82028..d77016a 100644
--- a/computepipe/runner/stream_manager/Factory.cpp
+++ b/computepipe/runner/stream_manager/Factory.cpp
@@ -31,7 +31,7 @@
     const proto::OutputConfig& config, std::function<Status(std::shared_ptr<MemHandle>)>& cb,
     uint32_t maxPackets) {
     std::unique_ptr<SemanticManager> semanticManager =
-        std::make_unique<SemanticManager>(config.stream_name(), config.type());
+        std::make_unique<SemanticManager>(config.stream_name(), config.stream_id(), config.type());
     if (semanticManager->setIpcDispatchCallback(cb) != SUCCESS) {
         return nullptr;
     }
diff --git a/computepipe/runner/stream_manager/SemanticManager.cpp b/computepipe/runner/stream_manager/SemanticManager.cpp
index 2f57dbf..a874b96 100644
--- a/computepipe/runner/stream_manager/SemanticManager.cpp
+++ b/computepipe/runner/stream_manager/SemanticManager.cpp
@@ -10,34 +10,39 @@
 namespace runner {
 namespace stream_manager {
 
-proto::PacketType SemanticHandle::getType() {
+proto::PacketType SemanticHandle::getType() const {
     return mType;
 }
 
-uint64_t SemanticHandle::getTimeStamp() {
+uint64_t SemanticHandle::getTimeStamp() const {
     return mTimestamp;
 }
 
-uint32_t SemanticHandle::getSize() {
+uint32_t SemanticHandle::getSize() const {
     return mSize;
 }
 
-const char* SemanticHandle::getData() {
+const char* SemanticHandle::getData() const {
     return mData;
 }
 
-native_handle_t SemanticHandle::getNativeHandle() {
+native_handle_t SemanticHandle::getNativeHandle() const {
     native_handle_t temp;
     temp.numFds = 0;
     temp.numInts = 0;
     return temp;
 }
 
-Status SemanticHandle::setMemInfo(const char* data, uint32_t size, uint64_t timestamp,
+int SemanticHandle::getStreamId() const {
+    return mStreamId;
+}
+
+Status SemanticHandle::setMemInfo(int streamId, const char* data, uint32_t size, uint64_t timestamp,
                                   const proto::PacketType& type) {
     if (data == nullptr || size == 0 || size > kMaxSemanticDataSize) {
         return INVALID_ARGUMENT;
     }
+    mStreamId = streamId;
     mData = (char*)malloc(size);
     if (!mData) {
         return NO_MEMORY;
@@ -138,7 +143,7 @@
         return INTERNAL_ERROR;
     }
     auto memHandle = std::make_shared<SemanticHandle>();
-    auto status = memHandle->setMemInfo(data, size, timestamp, mType);
+    auto status = memHandle->setMemInfo(mStreamId, data, size, timestamp, mType);
     if (status != SUCCESS) {
         return status;
     }
@@ -146,8 +151,8 @@
     return SUCCESS;
 }
 
-SemanticManager::SemanticManager(std::string name, const proto::PacketType& type)
-    : StreamManager(name, type) {
+SemanticManager::SemanticManager(std::string name, int streamId, const proto::PacketType& type)
+    : StreamManager(name, type), mStreamId(streamId) {
 }
 }  // namespace stream_manager
 }  // namespace runner
diff --git a/computepipe/runner/stream_manager/SemanticManager.h b/computepipe/runner/stream_manager/SemanticManager.h
index c738718..687e0b9 100644
--- a/computepipe/runner/stream_manager/SemanticManager.h
+++ b/computepipe/runner/stream_manager/SemanticManager.h
@@ -31,17 +31,17 @@
 class SemanticHandle : public MemHandle {
   public:
     static constexpr uint32_t kMaxSemanticDataSize = 1024;
-    proto::PacketType getType() override;
-    /* Retrieve packet time stamp */
-    uint64_t getTimeStamp() override;
-    /* Get size */
-    uint32_t getSize() override;
-    /* Get data, raw pointer. Only implemented for copy semantics */
-    const char* getData() override;
-    /* Get native handle. data with zero copy semantics */
-    native_handle_t getNativeHandle() override;
+    /**
+     * Override mem handle methods
+     */
+    int getStreamId() const override;
+    proto::PacketType getType() const override;
+    uint64_t getTimeStamp() const override;
+    uint32_t getSize() const override;
+    const char* getData() const override;
+    native_handle_t getNativeHandle() const override;
     /* set info for the memory. Make a copy */
-    Status setMemInfo(const char* data, uint32_t size, uint64_t timestamp,
+    Status setMemInfo(int streamId, const char* data, uint32_t size, uint64_t timestamp,
                       const proto::PacketType& type);
     /* Destroy local copy */
     ~SemanticHandle();
@@ -51,6 +51,7 @@
     uint32_t mSize;
     uint64_t mTimestamp;
     proto::PacketType mType;
+    int mStreamId;
 };
 
 class SemanticManager : public StreamManager, StreamManagerInit {
@@ -69,11 +70,12 @@
     Status handleStopWithFlushPhase(const RunnerEvent& e) override;
     Status handleStopImmediatePhase(const RunnerEvent& e) override;
 
-    explicit SemanticManager(std::string name, const proto::PacketType& type);
+    explicit SemanticManager(std::string name, int streamId, const proto::PacketType& type);
     ~SemanticManager() = default;
 
   private:
     std::mutex mStateLock;
+    int mStreamId;
     std::function<Status(const std::shared_ptr<MemHandle>&)> mDispatchCallback = nullptr;
 };
 }  // namespace stream_manager
diff --git a/computepipe/runner/utils/Android.bp b/computepipe/runner/utils/Android.bp
index 74c6656..a297b47 100644
--- a/computepipe/runner/utils/Android.bp
+++ b/computepipe/runner/utils/Android.bp
@@ -39,7 +39,11 @@
         "libprotobuf-cpp-lite",
     ],
 
-    visibility:["//packages/services/Car/computepipe/runner:__subpackages__"],
+    visibility:[
+        "//packages/services/Car/computepipe/runner:__subpackages__",
+        "//packages/services/Car/computepipe/tests:__subpackages__",
+    ],
+
     srcs: [
         "InterfaceImpl.cc",
         "PipeOptionsConverter.cc",
diff --git a/computepipe/runner/utils/InterfaceImpl.cc b/computepipe/runner/utils/InterfaceImpl.cc
index 67b7bc6..df57d99 100644
--- a/computepipe/runner/utils/InterfaceImpl.cc
+++ b/computepipe/runner/utils/InterfaceImpl.cc
@@ -12,13 +12,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.c
 
+#define LOG_TAG "RunnerIpcInterface"
+
 #include "InterfaceImpl.h"
 
 #include "OutputConfig.pb.h"
 #include "PacketDescriptor.pb.h"
 #include "PipeOptionsConverter.h"
 
-#define LOG_TAG "RunnerIpcInterface"
 #include <aidl/android/automotive/computepipe/runner/PacketDescriptor.h>
 #include <aidl/android/automotive/computepipe/runner/PacketDescriptorPacketType.h>
 #include <android-base/logging.h>
@@ -73,13 +74,16 @@
     iface->clientDied();
 }
 
-Status ToAidlPacketType(proto::PacketType type, PacketDescriptorPacketType& outType) {
+Status ToAidlPacketType(proto::PacketType type, PacketDescriptorPacketType* outType) {
+    if (outType == nullptr) {
+        return Status::INTERNAL_ERROR;
+    }
     switch (type) {
         case proto::SEMANTIC_DATA:
-            outType = PacketDescriptorPacketType::SEMANTIC_DATA;
+            *outType = PacketDescriptorPacketType::SEMANTIC_DATA;
             return Status::SUCCESS;
         case proto::PIXEL_DATA:
-            outType = PacketDescriptorPacketType::PIXEL_DATA;
+            *outType = PacketDescriptorPacketType::PIXEL_DATA;
             return Status::SUCCESS;
         default:
             LOG(ERROR) << "unknown packet type " << type;
@@ -97,7 +101,7 @@
         LOG(ERROR) << "Bad streamId";
         return Status::INVALID_ARGUMENT;
     }
-    Status status = ToAidlPacketType(packetHandle->getType(), desc.type);
+    Status status = ToAidlPacketType(packetHandle->getType(), &desc.type);
     if (status != SUCCESS) {
         return status;
     }
diff --git a/computepipe/runner/utils/RunnerInterface.cc b/computepipe/runner/utils/RunnerInterface.cc
index 66c2bea..0ff7aaf 100644
--- a/computepipe/runner/utils/RunnerInterface.cc
+++ b/computepipe/runner/utils/RunnerInterface.cc
@@ -16,12 +16,12 @@
 
 #include "RunnerInterface.h"
 
-#include <thread>
-
 #include <aidl/android/automotive/computepipe/registry/IPipeRegistration.h>
 #include <android/binder_manager.h>
 #include <android-base/logging.h>
 
+#include <thread>
+
 namespace android {
 namespace automotive {
 namespace computepipe {
@@ -45,7 +45,7 @@
         return Status::INVALID_ARGUMENT;
     }
 
-    mPipeRunner = std::make_shared<InterfaceImpl>(mGraphOptions, mRunnerInterfaceCallbacks);
+    mPipeRunner = ndk::SharedRefBase::make<InterfaceImpl>(mGraphOptions, mRunnerInterfaceCallbacks);
     std::thread t(&RunnerInterface::tryRegisterPipeRunner, this);
     t.detach();
     return Status::SUCCESS;
diff --git a/computepipe/tests/Android.bp b/computepipe/tests/Android.bp
index 18919d2..ec26177 100644
--- a/computepipe/tests/Android.bp
+++ b/computepipe/tests/Android.bp
@@ -41,14 +41,14 @@
     test_suites: ["device-tests"],
     srcs: [
         "PipeQueryTest.cpp",
-        "FakeRunner.cpp",
+	"FakeRunner.cpp",
     ],
     static_libs: [
         "libgtest",
         "libgmock",
     ],
     header_libs: [
-        "computepipe_router_headers",
+      "computepipe_router_headers",
     ],
     shared_libs: [
         "libbinder",
diff --git a/computepipe/tests/runner/client_interface/Android.bp b/computepipe/tests/runner/client_interface/Android.bp
new file mode 100644
index 0000000..6ee8295
--- /dev/null
+++ b/computepipe/tests/runner/client_interface/Android.bp
@@ -0,0 +1,40 @@
+// 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: "clientinterface_test",
+    test_suites: ["device-tests"],
+    srcs: [
+        "ClientInterfaceTest.cc",
+    ],
+    static_libs: [
+        "libgtest",
+        "libgmock",
+        "libcomputepipeprotos",
+    ],
+    header_libs: [
+        "computepipe_router_headers",
+        "computepipe_runner_includes",
+    ],
+    include_dirs: ["packages/services/Car/computepipe"],
+    shared_libs: [
+        "libbinder_ndk",
+        "libutils",
+        "libRunnerInterface",
+        "android.automotive.computepipe.registry-ndk_platform",
+        "android.automotive.computepipe.runner-ndk_platform",
+        "android.automotive.computepipe.runner-ndk_platform",
+        "libprotobuf-cpp-lite",
+    ],
+}
diff --git a/computepipe/tests/runner/client_interface/ClientInterfaceTest.cc b/computepipe/tests/runner/client_interface/ClientInterfaceTest.cc
new file mode 100644
index 0000000..a26be99
--- /dev/null
+++ b/computepipe/tests/runner/client_interface/ClientInterfaceTest.cc
@@ -0,0 +1,362 @@
+// 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.
+
+#include <aidl/android/automotive/computepipe/registry/BnClientInfo.h>
+#include <aidl/android/automotive/computepipe/registry/IPipeQuery.h>
+#include <aidl/android/automotive/computepipe/registry/IPipeRegistration.h>
+#include <aidl/android/automotive/computepipe/runner/BnPipeStateCallback.h>
+#include <aidl/android/automotive/computepipe/runner/BnPipeStream.h>
+#include <aidl/android/automotive/computepipe/runner/PipeState.h>
+
+#include <android/binder_manager.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <utility>
+
+#include "ConfigurationCommand.pb.h"
+#include "ControlCommand.pb.h"
+#include "MemHandle.h"
+#include "MockMemHandle.h"
+#include "Options.pb.h"
+#include "runner/utils/RunnerInterface.h"
+#include "runner/utils/RunnerInterfaceCallbacks.h"
+#include "types/Status.h"
+
+namespace android {
+namespace automotive {
+namespace computepipe {
+namespace {
+
+using ::aidl::android::automotive::computepipe::registry::BnClientInfo;
+using ::aidl::android::automotive::computepipe::registry::IPipeQuery;
+using ::aidl::android::automotive::computepipe::runner::BnPipeStateCallback;
+using ::aidl::android::automotive::computepipe::runner::BnPipeStream;
+using ::aidl::android::automotive::computepipe::runner::IPipeRunner;
+using ::aidl::android::automotive::computepipe::runner::IPipeStateCallback;
+using ::aidl::android::automotive::computepipe::runner::PacketDescriptor;
+using ::aidl::android::automotive::computepipe::runner::PipeState;
+using ::ndk::ScopedAStatus;
+using ::ndk::SharedRefBase;
+using ::testing::AtLeast;
+using ::testing::Return;
+
+const char kRegistryInterfaceName[] = "router";
+
+class RunnerCallbacks {
+  public:
+    Status ControlCommandCallback(const proto::ControlCommand& command) {
+        mLastControlCommand = command;
+        return mStatus;
+    }
+
+    Status ConfigurationCommandCallback(const proto::ConfigurationCommand& command) {
+        mLastConfigurationCommand = command;
+        return mStatus;
+    }
+
+    Status ReleasePacketNotification(const std::shared_ptr<MemHandle>& packet) {
+        mLastPacket = packet;
+        return mStatus;
+    }
+
+    runner_utils::RunnerInterfaceCallbacks GetCallbackObject() {
+        using std::placeholders::_1;
+        std::function<Status(const proto::ControlCommand&)> controlCb =
+            std::bind(&RunnerCallbacks::ControlCommandCallback, this, _1);
+        std::function<Status(const proto::ConfigurationCommand&)> configCb =
+            std::bind(&RunnerCallbacks::ConfigurationCommandCallback, this, _1);
+        std::function<Status(const std::shared_ptr<MemHandle>&)> packetCb =
+            std::bind(&RunnerCallbacks::ReleasePacketNotification, this, _1);
+        return runner_utils::RunnerInterfaceCallbacks(controlCb, configCb, packetCb);
+    }
+
+    void SetReturnStatus(Status status) {
+        mStatus = status;
+    }
+
+    proto::ControlCommand mLastControlCommand;
+    proto::ConfigurationCommand mLastConfigurationCommand;
+    std::shared_ptr<MemHandle> mLastPacket = nullptr;
+    Status mStatus = Status::SUCCESS;
+};
+
+class StateChangeCallback : public BnPipeStateCallback {
+  public:
+    ScopedAStatus handleState(PipeState state) {
+        mState = state;
+        return ScopedAStatus::ok();
+    }
+    PipeState mState = PipeState::RESET;
+};
+
+class StreamCallback : public BnPipeStream {
+  public:
+    ScopedAStatus deliverPacket(const PacketDescriptor& in_packet) override {
+        data = in_packet.data;
+        timestamp = in_packet.sourceTimeStampMillis;
+        return ScopedAStatus::ok();
+    }
+    std::string data;
+    uint64_t timestamp;
+};
+
+class ClientInfo : public BnClientInfo {
+  public:
+    ScopedAStatus getClientId(int32_t* _aidl_return) {
+        if (_aidl_return) {
+            *_aidl_return = 0;
+            return ScopedAStatus::ok();
+        }
+        return ScopedAStatus::fromExceptionCode(EX_TRANSACTION_FAILED);
+    }
+};
+
+class ClientInterface : public ::testing::Test {
+  protected:
+    void SetUp() override {
+        const std::string graphName = "graph1";
+        proto::Options options;
+        options.set_graph_name(graphName);
+        mRunnerInterface = std::make_unique<runner_utils::RunnerInterface>(
+            options, mCallbacks.GetCallbackObject());
+
+        // Register the instance with router.
+        EXPECT_EQ(mRunnerInterface->init(), Status::SUCCESS);
+
+        // Init is not a blocking call, so sleep for 3 seconds to allow the runner to register with
+        // router.
+        sleep(3);
+
+        // Retrieve router query instance from service manager.
+        std::string instanceName =
+            std::string() + IPipeQuery::descriptor + "/" + kRegistryInterfaceName;
+        ndk::SpAIBinder binder(AServiceManager_getService(instanceName.c_str()));
+        ASSERT_TRUE(binder.get() != nullptr);
+        std::shared_ptr<IPipeQuery> queryService = IPipeQuery::fromBinder(binder);
+
+        // Retrieve pipe runner instance from the router.
+        std::shared_ptr<ClientInfo> clientInfo = ndk::SharedRefBase::make<ClientInfo>();
+        ASSERT_TRUE(queryService->getPipeRunner(graphName, clientInfo, &mPipeRunner).isOk());
+    }
+
+    RunnerCallbacks mCallbacks;
+    std::shared_ptr<runner_utils::RunnerInterface> mRunnerInterface = nullptr;
+    std::shared_ptr<IPipeRunner> mPipeRunner = nullptr;
+};
+
+TEST_F(ClientInterface, TestSetConfiguration) {
+    // Configure runner to return success.
+    mCallbacks.SetReturnStatus(Status::SUCCESS);
+
+    // Initialize pipe runner.
+    std::shared_ptr<StateChangeCallback> stateCallback =
+        ndk::SharedRefBase::make<StateChangeCallback>();
+    EXPECT_TRUE(mPipeRunner->init(stateCallback).isOk());
+
+    // Test that set input source returns ok status.
+    EXPECT_TRUE(mPipeRunner->setPipeInputSource(1).isOk());
+    EXPECT_EQ(mCallbacks.mLastConfigurationCommand.has_set_input_source(), true);
+    EXPECT_EQ(mCallbacks.mLastConfigurationCommand.set_input_source().source_id(), 1);
+
+    // Test that set offload option returns ok status.
+    EXPECT_TRUE(mPipeRunner->setPipeOffloadOptions(5).isOk());
+    EXPECT_EQ(mCallbacks.mLastConfigurationCommand.has_set_offload_offload(), true);
+    EXPECT_EQ(mCallbacks.mLastConfigurationCommand.set_offload_offload().offload_option_id(), 5);
+
+    // Test that set termination option returns ok status.
+    EXPECT_TRUE(mPipeRunner->setPipeTermination(3).isOk());
+    EXPECT_EQ(mCallbacks.mLastConfigurationCommand.has_set_termination_option(), true);
+    EXPECT_EQ(mCallbacks.mLastConfigurationCommand.set_termination_option().termination_option_id(),
+              3);
+
+    // Test that set output callback returns ok status.
+    std::shared_ptr<StreamCallback> streamCb = ndk::SharedRefBase::make<StreamCallback>();
+    EXPECT_TRUE(mPipeRunner->setPipeOutputConfig(0, 10, streamCb).isOk());
+    EXPECT_EQ(mCallbacks.mLastConfigurationCommand.has_set_output_stream(), true);
+    EXPECT_EQ(mCallbacks.mLastConfigurationCommand.set_output_stream().stream_id(), 0);
+    EXPECT_EQ(mCallbacks.mLastConfigurationCommand.set_output_stream().max_inflight_packets_count(),
+              10);
+
+    // Release runner here. This should remove registry entry from router registry.
+    mRunnerInterface.reset();
+}
+
+TEST_F(ClientInterface, TestSetConfigurationError) {
+    ScopedAStatus status;
+
+    // Configure runner to return error.
+    mCallbacks.SetReturnStatus(Status::INTERNAL_ERROR);
+
+    // Initialize pipe runner.
+    std::shared_ptr<StateChangeCallback> stateCallback =
+        ndk::SharedRefBase::make<StateChangeCallback>();
+    EXPECT_TRUE(mPipeRunner->init(stateCallback).isOk());
+
+    // Test that set input source returns error status.
+    status = mPipeRunner->setPipeInputSource(1);
+    EXPECT_EQ(status.getExceptionCode(), EX_TRANSACTION_FAILED);
+    EXPECT_EQ(mCallbacks.mLastConfigurationCommand.has_set_input_source(), true);
+    EXPECT_EQ(mCallbacks.mLastConfigurationCommand.set_input_source().source_id(), 1);
+
+    // Test that set offload option returns error status.
+    status = mPipeRunner->setPipeOffloadOptions(5);
+    EXPECT_EQ(status.getExceptionCode(), EX_TRANSACTION_FAILED);
+    EXPECT_EQ(mCallbacks.mLastConfigurationCommand.has_set_offload_offload(), true);
+    EXPECT_EQ(mCallbacks.mLastConfigurationCommand.set_offload_offload().offload_option_id(), 5);
+
+    // Test that set termination option returns error status.
+    status = mPipeRunner->setPipeTermination(3);
+    EXPECT_EQ(status.getExceptionCode(), EX_TRANSACTION_FAILED);
+    EXPECT_EQ(mCallbacks.mLastConfigurationCommand.has_set_termination_option(), true);
+    EXPECT_EQ(mCallbacks.mLastConfigurationCommand.set_termination_option().termination_option_id(),
+              3);
+
+    // Test that set output callback returns error status.
+    std::shared_ptr<StreamCallback> streamCb = ndk::SharedRefBase::make<StreamCallback>();
+    status = mPipeRunner->setPipeOutputConfig(0, 10, streamCb);
+    EXPECT_EQ(status.getExceptionCode(), EX_TRANSACTION_FAILED);
+    EXPECT_EQ(mCallbacks.mLastConfigurationCommand.has_set_output_stream(), true);
+    EXPECT_EQ(mCallbacks.mLastConfigurationCommand.set_output_stream().stream_id(), 0);
+    EXPECT_EQ(mCallbacks.mLastConfigurationCommand.set_output_stream().max_inflight_packets_count(),
+              10);
+
+    // Release runner here. This should remove registry entry from router registry.
+    mRunnerInterface.reset();
+}
+
+TEST_F(ClientInterface, TestControlCommands) {
+    // Configure runner to return success.
+    mCallbacks.SetReturnStatus(Status::SUCCESS);
+
+    // Initialize pipe runner.
+    std::shared_ptr<StateChangeCallback> stateCallback =
+        ndk::SharedRefBase::make<StateChangeCallback>();
+    EXPECT_TRUE(mPipeRunner->init(stateCallback).isOk());
+
+    // Test that apply-configs api returns ok status.
+    EXPECT_TRUE(mPipeRunner->applyPipeConfigs().isOk());
+    EXPECT_EQ(mCallbacks.mLastControlCommand.has_apply_configs(), true);
+
+    // Test that set start graph api returns ok status.
+    EXPECT_TRUE(mPipeRunner->startPipe().isOk());
+    EXPECT_EQ(mCallbacks.mLastControlCommand.has_start_graph(), true);
+
+    // Test that set stop graph api returns ok status.
+    EXPECT_TRUE(mPipeRunner->stopPipe().isOk());
+    EXPECT_EQ(mCallbacks.mLastControlCommand.has_stop_graph(), true);
+
+    // Release runner here. This should remove registry entry from router registry.
+    mRunnerInterface.reset();
+}
+
+TEST_F(ClientInterface, TestControlCommandsFailure) {
+    ScopedAStatus status;
+
+    // Configure runner to return error status.
+    mCallbacks.SetReturnStatus(Status::INTERNAL_ERROR);
+
+    // Initialize pipe runner.
+    std::shared_ptr<StateChangeCallback> stateCallback =
+        ndk::SharedRefBase::make<StateChangeCallback>();
+    EXPECT_TRUE(mPipeRunner->init(stateCallback).isOk());
+
+    // Test that apply-configs api returns error status.
+    status = mPipeRunner->applyPipeConfigs();
+    EXPECT_EQ(status.getExceptionCode(), EX_TRANSACTION_FAILED);
+    EXPECT_EQ(mCallbacks.mLastControlCommand.has_apply_configs(), true);
+
+    // Test that start graph api returns error status.
+    status = mPipeRunner->startPipe();
+    EXPECT_EQ(status.getExceptionCode(), EX_TRANSACTION_FAILED);
+    EXPECT_EQ(mCallbacks.mLastControlCommand.has_start_graph(), true);
+
+    // Test that stop graph api returns error status.
+    status = mPipeRunner->stopPipe();
+    EXPECT_EQ(status.getExceptionCode(), EX_TRANSACTION_FAILED);
+    EXPECT_EQ(mCallbacks.mLastControlCommand.has_stop_graph(), true);
+
+    // Release runner here. This should remove registry entry from router registry.
+    mRunnerInterface.reset();
+}
+
+TEST_F(ClientInterface, TestFailureWithoutInit) {
+    mCallbacks.SetReturnStatus(Status::SUCCESS);
+
+    // Pipe runner is not initalized here, test that a configuration command returns error status.
+    ScopedAStatus status;
+    status = mPipeRunner->setPipeInputSource(1);
+    EXPECT_EQ(status.getExceptionCode(), EX_ILLEGAL_STATE);
+    EXPECT_EQ(mCallbacks.mLastConfigurationCommand.has_set_input_source(), false);
+
+    // Test that a control command returns error status.
+    status = mPipeRunner->applyPipeConfigs();
+    EXPECT_EQ(status.getExceptionCode(), EX_ILLEGAL_STATE);
+    EXPECT_EQ(mCallbacks.mLastControlCommand.has_apply_configs(), false);
+}
+
+TEST_F(ClientInterface, TestStateChangeNotification) {
+    // Create RunnerInterface instance.
+    mCallbacks.SetReturnStatus(Status::SUCCESS);
+
+    // Initialize pipe runner.
+    std::shared_ptr<StateChangeCallback> stateCallback =
+        ndk::SharedRefBase::make<StateChangeCallback>();
+    EXPECT_TRUE(mPipeRunner->init(stateCallback).isOk());
+
+    // Test that when runner interface is notified of a new state, client callback is invoked with
+    // correct state.
+    GraphState state = RUNNING;
+    EXPECT_EQ(mRunnerInterface->stateUpdateNotification(state), Status::SUCCESS);
+    EXPECT_EQ(stateCallback->mState, PipeState::RUNNING);
+}
+
+TEST_F(ClientInterface, TestPacketDelivery) {
+    // Configure runner to return success state.
+    mCallbacks.SetReturnStatus(Status::SUCCESS);
+
+    // Initialize pipe runner.
+    std::shared_ptr<StateChangeCallback> stateCallback =
+        ndk::SharedRefBase::make<StateChangeCallback>();
+    EXPECT_TRUE(mPipeRunner->init(stateCallback).isOk());
+
+    // Set callback for stream id 0.
+    std::shared_ptr<StreamCallback> streamCb = ndk::SharedRefBase::make<StreamCallback>();
+    EXPECT_TRUE(mPipeRunner->setPipeOutputConfig(0, 10, streamCb).isOk());
+    EXPECT_EQ(mCallbacks.mLastConfigurationCommand.has_set_output_stream(), true);
+    EXPECT_EQ(mCallbacks.mLastConfigurationCommand.set_output_stream().stream_id(), 0);
+    EXPECT_EQ(mCallbacks.mLastConfigurationCommand.set_output_stream().max_inflight_packets_count(),
+              10);
+
+    // Send a packet to client and verify the packet.
+    std::shared_ptr<tests::MockMemHandle> packet = std::make_unique<tests::MockMemHandle>();
+    uint64_t timestamp = 100;
+    const std::string testData = "Test String.";
+    EXPECT_CALL(*packet, getType()).Times(AtLeast(1))
+        .WillRepeatedly(Return(proto::PacketType::SEMANTIC_DATA));
+    EXPECT_CALL(*packet, getTimeStamp()).Times(AtLeast(1))
+        .WillRepeatedly(Return(timestamp));
+    EXPECT_CALL(*packet, getSize()).Times(AtLeast(1))
+        .WillRepeatedly(Return(testData.size()));
+    EXPECT_CALL(*packet, getData()).Times(AtLeast(1))
+        .WillRepeatedly(Return(testData.c_str()));
+    EXPECT_EQ(mRunnerInterface->newPacketNotification(
+        0, static_cast<std::shared_ptr<MemHandle>>(packet)), Status::SUCCESS);
+    EXPECT_EQ(streamCb->data, packet->getData());
+    EXPECT_EQ(streamCb->timestamp, packet->getTimeStamp());
+}
+
+}  // namespace
+}  // namespace computepipe
+}  // namespace automotive
+}  // namespace android
diff --git a/computepipe/tests/runner/client_interface/MockMemHandle.h b/computepipe/tests/runner/client_interface/MockMemHandle.h
new file mode 100644
index 0000000..0bff0e5
--- /dev/null
+++ b/computepipe/tests/runner/client_interface/MockMemHandle.h
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+#ifndef COMPUTEPIPE_TESTS_FAKEMEMHANDLE_H_
+#define COMPUTEPIPE_TESTS_FAKEMEMHANDLE_H_
+
+#include <gmock/gmock.h>
+#include <string>
+
+#include "MemHandle.h"
+
+namespace android {
+namespace automotive {
+namespace computepipe {
+namespace tests {
+
+class MockMemHandle : public MemHandle {
+  public:
+    MOCK_METHOD0(getType, proto::PacketType());
+    MOCK_METHOD0(getTimeStamp, uint64_t());
+    MOCK_METHOD0(getSize, uint32_t());
+    MOCK_METHOD0(getData, const char*());
+    MOCK_METHOD0(getNativeHandle, native_handle_t());
+};
+
+}  // namespace tests
+}  // namespace computepipe
+}  // namespace automotive
+}  // namespace android
+
+#endif  // COMPUTEPIPE_TESTS_FAKEMEMHANDLE_H_