IVGCVSW-6073 Add protected mode to ArmNN CreationOptions

 * Adds logic to the Runtime to activate protected mode
 * Adds ProtectedContentAllocation backend capability to ClBackend
   It's not fully activated yet because the CustomAllocator is
   missing. Will print an error message and won't register the backend
   but won't fail.
 * Extends IBackendInternal with an UseCustomAllocator function.
 * Adds related unit tests

Signed-off-by: Jan Eilers <jan.eilers@arm.com>
Change-Id: I64f465c5800eb104aa90db1bbf772a4148b5072f
diff --git a/src/armnn/Runtime.cpp b/src/armnn/Runtime.cpp
index f16d186..c2b7486 100644
--- a/src/armnn/Runtime.cpp
+++ b/src/armnn/Runtime.cpp
@@ -6,6 +6,7 @@
 
 #include <armnn/Version.hpp>
 #include <armnn/BackendRegistry.hpp>
+#include <armnn/BackendHelper.hpp>
 #include <armnn/Logging.hpp>
 #include <armnn/utility/Timer.hpp>
 
@@ -282,6 +283,32 @@
             auto backend = factoryFun();
             ARMNN_ASSERT(backend.get() != nullptr);
 
+            // If the runtime is created in protected mode only add backends that support this mode
+            if (options.m_ProtectedMode)
+            {
+                // check if backend supports ProtectedMode
+                using BackendCapability = BackendOptions::BackendOption;
+                BackendCapability protectedContentCapability {"ProtectedContentAllocation", true};
+                if (!HasCapability(protectedContentCapability, id))
+                {
+                    // Protected Content Allocation is not supported by the backend
+                    // backend should not be registered
+                    ARMNN_LOG(warning) << "Backend "
+                                       << id
+                                       << " is not registered as does not support protected content allocation \n";
+                    continue;
+                }
+                std::string err;
+                if (!backend->UseCustomMemoryAllocator(err))
+                {
+                    ARMNN_LOG(error) << "The backend "
+                                     << id
+                                     << " reported an error when entering protected mode. Backend won't be used."
+                                     << " ErrorMsg: " << err;
+                    continue;
+                }
+            }
+
             auto context = backend->CreateBackendContext(options);
 
             // backends are allowed to return nullptrs if they
diff --git a/src/armnn/test/OptimizerTests.cpp b/src/armnn/test/OptimizerTests.cpp
index d4e2d49..19bd581 100644
--- a/src/armnn/test/OptimizerTests.cpp
+++ b/src/armnn/test/OptimizerTests.cpp
@@ -139,6 +139,153 @@
     Connect(layer, output, lstmTensorInfo3, 3, 0);
 }
 
+
+class MockLayerSupport : public LayerSupportBase
+{
+public:
+    bool IsInputSupported(const TensorInfo& /*input*/,
+                          Optional<std::string&> /*reasonIfUnsupported = EmptyOptional()*/) const override
+    {
+        return true;
+    }
+
+    bool IsOutputSupported(const TensorInfo& /*input*/,
+                           Optional<std::string&> /*reasonIfUnsupported = EmptyOptional()*/) const override
+    {
+        return true;
+    }
+
+    bool IsActivationSupported(const TensorInfo& /*input0*/,
+                               const TensorInfo& /*output*/,
+                               const ActivationDescriptor& /*descriptor*/,
+                               Optional<std::string&> /*reasonIfUnsupported = EmptyOptional()*/) const override
+    {
+        return true;
+    }
+};
+
+template <typename NamePolicy>
+class MockBackend : public IBackendInternal
+{
+public:
+    MockBackend() :
+            m_BackendCapabilities(NamePolicy::GetIdStatic(), {{"NullCapability", false}}),
+            m_CustomAllocator(false) {};
+    MockBackend(const BackendCapabilities& capabilities) :
+            m_BackendCapabilities(capabilities),
+            m_CustomAllocator(false) {};
+    ~MockBackend() = default;
+
+    static const BackendId& GetIdStatic()
+    {
+        return NamePolicy::GetIdStatic();
+    }
+    const BackendId& GetId() const override
+    {
+        return GetIdStatic();
+    }
+
+    IBackendInternal::IMemoryManagerUniquePtr CreateMemoryManager() const override
+    {
+        return nullptr;
+    };
+
+    IBackendInternal::IWorkloadFactoryPtr
+        CreateWorkloadFactory(const IBackendInternal::IMemoryManagerSharedPtr&) const override
+    {
+        return nullptr;
+    }
+
+    IBackendInternal::IBackendContextPtr CreateBackendContext(const IRuntime::CreationOptions&) const override
+    {
+        return nullptr;
+    }
+
+    IBackendInternal::Optimizations GetOptimizations() const override
+    {
+        return {};
+    }
+    IBackendInternal::ILayerSupportSharedPtr GetLayerSupport() const override
+    {
+        return std::make_shared<MockLayerSupport>();
+    }
+
+    OptimizationViews OptimizeSubgraphView(const SubgraphView&) const override
+    {
+        return {};
+    };
+
+    BackendCapabilities GetCapabilities() const override
+    {
+        return m_BackendCapabilities;
+    };
+
+    virtual bool UseCustomMemoryAllocator(armnn::Optional<std::string&> errMsg) override
+    {
+        IgnoreUnused(errMsg);
+        m_CustomAllocator = true;
+        return m_CustomAllocator;
+    }
+
+    BackendCapabilities m_BackendCapabilities;
+    bool m_CustomAllocator;
+};
+
+template <typename NamePolicy>
+class NoProtectedModeMockBackend : public IBackendInternal
+{
+public:
+    NoProtectedModeMockBackend() : m_BackendCapabilities(NamePolicy::GetIdStatic(), {{"NullCapability", false}}) {};
+    NoProtectedModeMockBackend(const BackendCapabilities& capabilities) : m_BackendCapabilities(capabilities) {};
+    ~NoProtectedModeMockBackend() = default;
+
+    static const BackendId& GetIdStatic()
+    {
+        return NamePolicy::GetIdStatic();
+    }
+    const BackendId& GetId() const override
+    {
+        return GetIdStatic();
+    }
+
+    IBackendInternal::IMemoryManagerUniquePtr CreateMemoryManager() const override
+    {
+        return nullptr;
+    };
+
+    IBackendInternal::IWorkloadFactoryPtr
+        CreateWorkloadFactory(const IBackendInternal::IMemoryManagerSharedPtr&) const override
+    {
+        return nullptr;
+    }
+
+    IBackendInternal::IBackendContextPtr CreateBackendContext(const IRuntime::CreationOptions&) const override
+    {
+        return nullptr;
+    }
+
+    IBackendInternal::Optimizations GetOptimizations() const override
+    {
+        return {};
+    }
+    IBackendInternal::ILayerSupportSharedPtr GetLayerSupport() const override
+    {
+        return std::make_shared<MockLayerSupport>();
+    }
+
+    OptimizationViews OptimizeSubgraphView(const SubgraphView&) const override
+    {
+        return {};
+    };
+
+    BackendCapabilities GetCapabilities() const override
+    {
+        return m_BackendCapabilities;
+    };
+
+    BackendCapabilities m_BackendCapabilities;
+};
+
 }    // namespace
 
 TEST_SUITE("Optimizer")
@@ -543,77 +690,6 @@
     CHECK_NOTHROW(graph.InferTensorInfos());
 }
 
-class MockLayerSupport : public LayerSupportBase
-{
-public:
-    bool IsInputSupported(const TensorInfo& /*input*/,
-                          Optional<std::string&> /*reasonIfUnsupported = EmptyOptional()*/) const override
-    {
-        return true;
-    }
-
-    bool IsOutputSupported(const TensorInfo& /*input*/,
-                           Optional<std::string&> /*reasonIfUnsupported = EmptyOptional()*/) const override
-    {
-        return true;
-    }
-
-    bool IsActivationSupported(const TensorInfo& /*input0*/,
-                               const TensorInfo& /*output*/,
-                               const ActivationDescriptor& /*descriptor*/,
-                               Optional<std::string&> /*reasonIfUnsupported = EmptyOptional()*/) const override
-    {
-        return true;
-    }
-};
-
-template <typename NamePolicy>
-class MockBackend : public IBackendInternal
-{
-public:
-    MockBackend()  = default;
-    ~MockBackend() = default;
-
-    static const BackendId& GetIdStatic()
-    {
-        return NamePolicy::GetIdStatic();
-    }
-    const BackendId& GetId() const override
-    {
-        return GetIdStatic();
-    }
-
-    IBackendInternal::IMemoryManagerUniquePtr CreateMemoryManager() const override
-    {
-        return nullptr;
-    };
-
-    IBackendInternal::IWorkloadFactoryPtr
-        CreateWorkloadFactory(const IBackendInternal::IMemoryManagerSharedPtr&) const override
-    {
-        return nullptr;
-    }
-
-    IBackendInternal::IBackendContextPtr CreateBackendContext(const IRuntime::CreationOptions&) const override
-    {
-        return nullptr;
-    }
-
-    IBackendInternal::Optimizations GetOptimizations() const override
-    {
-        return {};
-    }
-    IBackendInternal::ILayerSupportSharedPtr GetLayerSupport() const override
-    {
-        return std::make_shared<MockLayerSupport>();
-    }
-
-    OptimizationViews OptimizeSubgraphView(const SubgraphView&) const override
-    {
-        return {};
-    };
-};
-
 TEST_CASE("BackendCapabilityTest")
 {
     BackendId backendId = "MockBackend";
@@ -848,4 +924,132 @@
                              &IsLayerOfType<armnn::OutputLayer>,
                              &IsLayerOfType<armnn::OutputLayer>));
 }
+} // Optimizer TestSuite
+
+TEST_SUITE("Runtime")
+{
+// This test really belongs into RuntimeTests.cpp but it requires all sort of MockBackends which are
+// already defined here
+TEST_CASE("RuntimeProtectedModeOption")
+{
+    using namespace armnn;
+
+    struct MockPolicy
+    {
+        static const BackendId& GetIdStatic()
+        {
+            static BackendId id = "MockBackend";
+            return id;
+        }
+    };
+
+    struct ProtectedPolicy
+    {
+        static const BackendId& GetIdStatic()
+        {
+            static BackendId id = "MockBackendProtectedContent";
+            return id;
+        }
+    };
+
+    struct SillyPolicy
+    {
+        static const BackendId& GetIdStatic()
+        {
+            static BackendId id = "SillyMockBackend";
+            return id;
+        }
+    };
+
+    BackendCapabilities mockBackendCapabilities("MockBackend",
+                                                {
+                                                        {"ProtectedContentAllocation", false}
+                                                });
+    BackendCapabilities mockProtectedBackendCapabilities("MockBackendProtectedContent",
+                                                         {
+                                                                 {"ProtectedContentAllocation", true}
+                                                         });
+
+    auto& backendRegistry = BackendRegistryInstance();
+
+    // clean up from previous test runs
+    std::vector<BackendId> mockBackends = {"MockBackend", "MockBackendProtectedContent", "SillyMockBackend"};
+    for (auto& backend : mockBackends)
+    {
+        backendRegistry.Deregister(backend);
+    }
+
+    // Create a bunch of MockBackends with different capabilities
+    // 1. Doesn't support protected mode even though it knows about this capability
+    backendRegistry.Register("MockBackend", [mockBackendCapabilities]()
+    {
+        return std::make_unique<MockBackend<MockPolicy>>(mockBackendCapabilities);
+    });
+    // 2. Supports protected mode and has it implemented correctly
+    backendRegistry.Register("MockBackendProtectedContent", [mockProtectedBackendCapabilities]()
+    {
+        return std::make_unique<MockBackend<ProtectedPolicy>>(mockProtectedBackendCapabilities);
+    });
+    // 3. Claims to support protected mode but doesn't have the UseCustomMemoryAllocator function implemented
+    backendRegistry.Register("SillyMockBackend", [mockProtectedBackendCapabilities]()
+    {
+        return std::make_unique<NoProtectedModeMockBackend<SillyPolicy>>(mockProtectedBackendCapabilities);
+    });
+
+    // Creates a runtime that is not in protected mode
+    {
+        IRuntime::CreationOptions creationOptions;
+        creationOptions.m_ProtectedMode = false;
+
+        IRuntimePtr run = IRuntime::Create(creationOptions);
+
+        const armnn::BackendIdSet supportedDevices = run->GetDeviceSpec().GetSupportedBackends();
+        // Both MockBackends that are registered should show up in the runtimes supported backends list
+        for (auto& backend : mockBackends)
+        {
+            CHECK(std::find(supportedDevices.cbegin(), supportedDevices.cend(), backend) != supportedDevices.cend());
+        }
+    }
+
+    // If the runtime is in protected mode only backends that support protected content should be added
+    {
+        IRuntime::CreationOptions creationOptions;
+        creationOptions.m_ProtectedMode = true;
+
+        IRuntimePtr run = IRuntime::Create(creationOptions);
+
+        const armnn::BackendIdSet supportedDevices = run->GetDeviceSpec().GetSupportedBackends();
+        // Only the MockBackends that claims support for protected content should show up in the
+        // runtimes supported backends list
+        CHECK(std::find(supportedDevices.cbegin(),
+                        supportedDevices.cend(),
+                        "MockBackendProtectedContent") != supportedDevices.cend());
+        CHECK(std::find(supportedDevices.cbegin(),
+                        supportedDevices.cend(),
+                        "MockBackend") == supportedDevices.cend());
+        CHECK(std::find(supportedDevices.cbegin(),
+                        supportedDevices.cend(),
+                        "SillyMockBackend") == supportedDevices.cend());
+    }
+
+    // If the runtime is in protected mode only backends that support protected content should be added
+    {
+        IRuntime::CreationOptions creationOptions;
+        creationOptions.m_ProtectedMode = true;
+
+        IRuntimePtr run = IRuntime::Create(creationOptions);
+
+        const armnn::BackendIdSet supportedDevices = run->GetDeviceSpec().GetSupportedBackends();
+        // Only the MockBackend that claims support for protected content should show up in the
+        // runtimes supported backends list
+                CHECK(std::find(supportedDevices.cbegin(),
+                                supportedDevices.cend(),
+                                "MockBackendProtectedContent") != supportedDevices.cend());
+
+                CHECK(std::find(supportedDevices.cbegin(),
+                                supportedDevices.cend(),
+                                "MockBackend") == supportedDevices.cend());
+    }
+
+}
 }
diff --git a/src/backends/backendsCommon/test/DynamicBackendTests.hpp b/src/backends/backendsCommon/test/DynamicBackendTests.hpp
index cfcdf8e..f49c36e 100644
--- a/src/backends/backendsCommon/test/DynamicBackendTests.hpp
+++ b/src/backends/backendsCommon/test/DynamicBackendTests.hpp
@@ -560,7 +560,7 @@
 
     BackendVersion dynamicBackendVersion;
     CHECK_NOTHROW(dynamicBackendVersion = dynamicBackend->GetBackendVersion());
-    CHECK((dynamicBackendVersion == BackendVersion({ 1, 0 })));
+    CHECK((dynamicBackendVersion >= BackendVersion({ 1, 0 })));
 
     IBackendInternalUniquePtr dynamicBackendInstance1;
     CHECK_THROWS_AS(dynamicBackendInstance1 = dynamicBackend->GetBackend(), RuntimeException);
diff --git a/src/backends/cl/ClBackend.hpp b/src/backends/cl/ClBackend.hpp
index db03cfe..c742c0b 100644
--- a/src/backends/cl/ClBackend.hpp
+++ b/src/backends/cl/ClBackend.hpp
@@ -13,13 +13,14 @@
 const BackendCapabilities gpuAccCapabilities("GpuAcc",
                                              {
                                                      {"NonConstWeights", false},
-                                                     {"AsyncExecution", false}
+                                                     {"AsyncExecution", false},
+                                                     {"ProtectedContentAllocation", true}
                                              });
 
 class ClBackend : public IBackendInternal
 {
 public:
-    ClBackend()  = default;
+    ClBackend() : m_EnableCustomAllocator(false) {};
     ~ClBackend() = default;
 
     static const BackendId& GetIdStatic();
@@ -70,6 +71,18 @@
     {
         return gpuAccCapabilities;
     };
+
+    virtual bool UseCustomMemoryAllocator(armnn::Optional<std::string&> errMsg) override
+    {
+        IgnoreUnused(errMsg);
+
+        // Set flag to signal the backend to use a custom memory allocator
+        m_EnableCustomAllocator = true;
+
+        return m_EnableCustomAllocator;
+    }
+
+    bool m_EnableCustomAllocator;
 };
 
 } // namespace armnn