EGL: GLES: Implement GL_EXT_protected_textures

Implement EGL_EXT_protected_content Images
Add protected member to Images and Textures
Add error when creating objects if not supported or
does't match native buffer
When creating siblings pass protected state
Add extension caps
Add Validation
Add GetTexParameter and SetTextparameter
Add protected to Texture and state
Expand tests for images and textures

Test: angle_end2end_test --gtest_filter=EGLProtectedContentTest

Bug: angleproject:3965
Change-Id: I35a89b4e80bba6d9b6831c68e71630eef304dacb
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2802852
Commit-Queue: Mohan Maiya <m.maiya@samsung.com>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
diff --git a/scripts/code_generation_hashes/GL_EGL_WGL_loader.json b/scripts/code_generation_hashes/GL_EGL_WGL_loader.json
index 15c5653..cc8e30a 100644
--- a/scripts/code_generation_hashes/GL_EGL_WGL_loader.json
+++ b/scripts/code_generation_hashes/GL_EGL_WGL_loader.json
@@ -12,7 +12,7 @@
   "scripts/gl_angle_ext.xml":
     "08f74b35d908b7c02b45fdf45572c434",
   "scripts/registry_xml.py":
-    "3fc0ede1891f7de9338993dea77df9e8",
+    "44a6f8c6713b39de5427da86b6c64595",
   "scripts/wgl.xml":
     "c36001431919e1c435f1215a85f7e1db",
   "src/libEGL/egl_loader_autogen.cpp":
diff --git a/scripts/code_generation_hashes/GL_EGL_entry_points.json b/scripts/code_generation_hashes/GL_EGL_entry_points.json
index b48e96b..437f092 100644
--- a/scripts/code_generation_hashes/GL_EGL_entry_points.json
+++ b/scripts/code_generation_hashes/GL_EGL_entry_points.json
@@ -16,7 +16,7 @@
   "scripts/gl_angle_ext.xml":
     "08f74b35d908b7c02b45fdf45572c434",
   "scripts/registry_xml.py":
-    "3fc0ede1891f7de9338993dea77df9e8",
+    "44a6f8c6713b39de5427da86b6c64595",
   "scripts/wgl.xml":
     "c36001431919e1c435f1215a85f7e1db",
   "src/common/entry_points_enum_autogen.cpp":
@@ -42,7 +42,7 @@
   "src/libANGLE/Context_gles_3_2_autogen.h":
     "48567dca16fd881dfe6d61fee0e3106f",
   "src/libANGLE/Context_gles_ext_autogen.h":
-    "dbf7c9716e46e29b44db01f215f0808a",
+    "1e78e832b23a36d8b0eb290ebfd95812",
   "src/libANGLE/capture/capture_gles_1_0_autogen.cpp":
     "7ec7ef8f779b809a45d74b97502c419b",
   "src/libANGLE/capture/capture_gles_1_0_autogen.h":
@@ -66,7 +66,7 @@
   "src/libANGLE/capture/capture_gles_ext_autogen.cpp":
     "b027d574c21f369aee0fb0117f453935",
   "src/libANGLE/capture/capture_gles_ext_autogen.h":
-    "46440404fc9219fbe709e564f26d57a6",
+    "42d26ead58cf52c8bbf2701589bc68aa",
   "src/libANGLE/capture/frame_capture_replay_autogen.cpp":
     "e0a3c284b986e2a712589b6f3523d79c",
   "src/libANGLE/capture/frame_capture_utils_autogen.cpp":
@@ -88,7 +88,7 @@
   "src/libANGLE/validationES3_autogen.h":
     "7435b9caddf8787b937c71a54dda96e1",
   "src/libANGLE/validationESEXT_autogen.h":
-    "498a3fcf388dd8edbe38b25aaa1621cc",
+    "b2d36e26a63cd92fbb26d289f1aecb9b",
   "src/libANGLE/validationGL1_autogen.h":
     "439f8ea26dc37ee6608100f4c6f9205c",
   "src/libANGLE/validationGL2_autogen.h":
@@ -162,17 +162,17 @@
   "src/libGLESv2/entry_points_gles_3_2_autogen.h":
     "647f932a299cdb4726b60bbba059f0d2",
   "src/libGLESv2/entry_points_gles_ext_autogen.cpp":
-    "dda68d8198adeb87c19f5fce8691a0b9",
+    "df737a111291710513eda6f38d391b26",
   "src/libGLESv2/entry_points_gles_ext_autogen.h":
-    "95374bec6b53708d019455309ea887ec",
+    "5be077957c8cf2dbf10be897d56ef4ed",
   "src/libGLESv2/libGLESv2_autogen.cpp":
-    "3805edff53f627f34ce6f55f064e473c",
+    "e6fd37cfcb725f9d3a25cc9c55a1ea40",
   "src/libGLESv2/libGLESv2_autogen.def":
-    "b49ee14e4c440282d86e5ab4acbaaff5",
+    "7f8231e286656964204c1f303e91dfc9",
   "src/libGLESv2/libGLESv2_no_capture_autogen.def":
-    "2303964798f342d2858dc44ba9f02a2e",
+    "e243621778233766d10df30029d4e2c7",
   "src/libGLESv2/libGLESv2_with_capture_autogen.def":
-    "b74c9e193df59dc0f06ff45308f8fb51",
+    "cce23e17f23ef38ee99869b6bc3d8729",
   "src/libOpenCL/libOpenCL_autogen.cpp":
     "10849978c910dc1af5dd4f0c815d1581"
 }
\ No newline at end of file
diff --git a/scripts/code_generation_hashes/GLenum_value_to_string_map.json b/scripts/code_generation_hashes/GLenum_value_to_string_map.json
index 391746c..e582af0 100644
--- a/scripts/code_generation_hashes/GLenum_value_to_string_map.json
+++ b/scripts/code_generation_hashes/GLenum_value_to_string_map.json
@@ -6,9 +6,9 @@
   "scripts/gl_angle_ext.xml":
     "08f74b35d908b7c02b45fdf45572c434",
   "scripts/registry_xml.py":
-    "3fc0ede1891f7de9338993dea77df9e8",
+    "44a6f8c6713b39de5427da86b6c64595",
   "src/libANGLE/capture/gl_enum_utils_autogen.cpp":
-    "335140f98374899f9e1303b23b4eddc1",
+    "9f432722dc11c315b6920d311d8df70e",
   "src/libANGLE/capture/gl_enum_utils_autogen.h":
     "fb0bb7f506f6082ea3b2c3fa384d2739"
 }
\ No newline at end of file
diff --git a/scripts/code_generation_hashes/proc_table.json b/scripts/code_generation_hashes/proc_table.json
index d3527fc..5e3272c 100644
--- a/scripts/code_generation_hashes/proc_table.json
+++ b/scripts/code_generation_hashes/proc_table.json
@@ -12,7 +12,7 @@
   "scripts/gl_angle_ext.xml":
     "08f74b35d908b7c02b45fdf45572c434",
   "scripts/registry_xml.py":
-    "3fc0ede1891f7de9338993dea77df9e8",
+    "44a6f8c6713b39de5427da86b6c64595",
   "scripts/wgl.xml":
     "c36001431919e1c435f1215a85f7e1db",
   "src/libGL/proc_table_wgl_autogen.cpp":
diff --git a/scripts/registry_xml.py b/scripts/registry_xml.py
index 470a7bb..ef68956 100644
--- a/scripts/registry_xml.py
+++ b/scripts/registry_xml.py
@@ -90,6 +90,7 @@
     "GL_EXT_multisampled_render_to_texture2",
     "GL_EXT_occlusion_query_boolean",
     "GL_EXT_primitive_bounding_box",
+    "GL_EXT_protected_textures",
     "GL_EXT_read_format_bgra",
     "GL_EXT_robustness",
     "GL_EXT_semaphore",
diff --git a/src/common/android_util.cpp b/src/common/android_util.cpp
index 436b1b7..a08ec9e 100644
--- a/src/common/android_util.cpp
+++ b/src/common/android_util.cpp
@@ -334,13 +334,15 @@
                                       int *width,
                                       int *height,
                                       int *depth,
-                                      int *pixelFormat)
+                                      int *pixelFormat,
+                                      uint64_t *usage)
 {
     *width       = buffer->width;
     *height      = buffer->height;
     *depth       = static_cast<int>(buffer->layerCount);
     *height      = buffer->height;
     *pixelFormat = buffer->format;
+    *usage       = buffer->usage;
 }
 
 GLenum NativePixelFormatToGLInternalFormat(int pixelFormat)
diff --git a/src/common/android_util.h b/src/common/android_util.h
index aed640a..eee60ba 100644
--- a/src/common/android_util.h
+++ b/src/common/android_util.h
@@ -42,7 +42,8 @@
                                       int *width,
                                       int *height,
                                       int *depth,
-                                      int *pixelFormat);
+                                      int *pixelFormat,
+                                      uint64_t *usage);
 GLenum NativePixelFormatToGLInternalFormat(int pixelFormat);
 int GLInternalFormatToNativePixelFormat(GLenum internalFormat);
 
diff --git a/src/libANGLE/Caps.cpp b/src/libANGLE/Caps.cpp
index 5ee0d94..974b3fd 100644
--- a/src/libANGLE/Caps.cpp
+++ b/src/libANGLE/Caps.cpp
@@ -1097,6 +1097,7 @@
         map["GL_EXT_primitive_bounding_box"] = esOnlyExtension(&Extensions::primitiveBoundingBoxEXT);
         map["GL_ANGLE_relaxed_vertex_attribute_type"] = esOnlyExtension(&Extensions::relaxedVertexAttributeTypeANGLE);
         map["GL_ANGLE_yuv_internal_format"] = enableableExtension(&Extensions::yuvInternalFormatANGLE);
+        map["GL_EXT_protected_textures"] = enableableExtension(&Extensions::protectedTexturesEXT);
         // GLES1 extensions
         map["GL_OES_point_size_array"] = enableableExtension(&Extensions::pointSizeArrayOES);
         map["GL_OES_texture_cube_map"] = enableableExtension(&Extensions::textureCubeMapOES);
diff --git a/src/libANGLE/Caps.h b/src/libANGLE/Caps.h
index aeb7c34..46140d1 100644
--- a/src/libANGLE/Caps.h
+++ b/src/libANGLE/Caps.h
@@ -741,6 +741,9 @@
 
     // GL_ANGLE_yuv_internal_format
     bool yuvInternalFormatANGLE = false;
+
+    // GL_EXT_protected_textures
+    bool protectedTexturesEXT = false;
 };
 
 // Pointer to a boolean memeber of the Extensions struct
diff --git a/src/libANGLE/Context.cpp b/src/libANGLE/Context.cpp
index 45e8dac..bb8479d 100644
--- a/src/libANGLE/Context.cpp
+++ b/src/libANGLE/Context.cpp
@@ -6,7 +6,6 @@
 
 // Context.cpp: Implements the gl::Context class, managing all GL state and performing
 // rendering operations. It is the GLES2 specific implementation of EGLContext.
-
 #include "libANGLE/Context.h"
 #include "libANGLE/Context.inl.h"
 
@@ -3559,6 +3558,14 @@
         ASSERT(supportedExtensions.textureCompressionASTCHDRKHR);
     }
 
+    // GL_KHR_protected_textures
+    // If EGL_KHR_protected_content is not supported then GL_EXT_protected_texture
+    // can not be supported.
+    if (!mDisplay->getExtensions().protectedContentEXT)
+    {
+        supportedExtensions.protectedTexturesEXT = false;
+    }
+
     // GL_ANGLE_get_tex_level_parameter is implemented in the frontend
     supportedExtensions.getTexLevelParameterANGLE = true;
 
diff --git a/src/libANGLE/Context_gles_ext_autogen.h b/src/libANGLE/Context_gles_ext_autogen.h
index c7c3a83..0c49174 100644
--- a/src/libANGLE/Context_gles_ext_autogen.h
+++ b/src/libANGLE/Context_gles_ext_autogen.h
@@ -184,6 +184,7 @@
     /* GL_EXT_multisampled_render_to_texture2 */                                                   \
     /* GL_EXT_occlusion_query_boolean */                                                           \
     /* GL_EXT_primitive_bounding_box */                                                            \
+    /* GL_EXT_protected_textures */                                                                \
     /* GL_EXT_read_format_bgra */                                                                  \
     /* GL_EXT_robustness */                                                                        \
     /* GL_EXT_sRGB */                                                                              \
diff --git a/src/libANGLE/ErrorStrings.h b/src/libANGLE/ErrorStrings.h
index 8356518..df5ea13 100644
--- a/src/libANGLE/ErrorStrings.h
+++ b/src/libANGLE/ErrorStrings.h
@@ -567,6 +567,7 @@
 MSG kProgramPipelineDoesNotExist = "Program pipeline does not exist.";
 MSG kNotAllStagesOfSeparableProgramUsed = "A program object is active for at least one, but not all of the shader stages that were present when the program was linked.";
 MSG kProgramPipelineLinkFailed = "Program pipeline link failed";
+MSG kProtectedTexturesExtensionRequired = "GL_EXT_protected_textures not enabled.";
 
 // clang-format on
 
diff --git a/src/libANGLE/FramebufferAttachment.h b/src/libANGLE/FramebufferAttachment.h
index 013a1e0..7ebf5f5 100644
--- a/src/libANGLE/FramebufferAttachment.h
+++ b/src/libANGLE/FramebufferAttachment.h
@@ -212,6 +212,7 @@
                               GLenum binding,
                               const ImageIndex &imageIndex) const                          = 0;
     virtual bool isYUV() const                                                             = 0;
+    virtual bool hasProtectedContent() const                                               = 0;
 
     virtual void onAttach(const Context *context, rx::Serial framebufferSerial) = 0;
     virtual void onDetach(const Context *context, rx::Serial framebufferSerial) = 0;
diff --git a/src/libANGLE/Image.cpp b/src/libANGLE/Image.cpp
index d741286..3bf8fdd 100644
--- a/src/libANGLE/Image.cpp
+++ b/src/libANGLE/Image.cpp
@@ -135,6 +135,11 @@
     return mTargetOf.get() && mTargetOf->isYUV();
 }
 
+bool ImageSibling::hasProtectedContent() const
+{
+    return mTargetOf.get() && mTargetOf->hasProtectedContent();
+}
+
 void ImageSibling::notifySiblings(angle::SubjectMessage message)
 {
     if (mTargetOf.get())
@@ -203,6 +208,11 @@
     return mImplementation->isYUV();
 }
 
+bool ExternalImageSibling::hasProtectedContent() const
+{
+    return mImplementation->hasProtectedContent();
+}
+
 void ExternalImageSibling::onAttach(const gl::Context *context, rx::Serial framebufferSerial) {}
 
 void ExternalImageSibling::onDetach(const gl::Context *context, rx::Serial framebufferSerial) {}
@@ -249,7 +259,8 @@
       samples(),
       sourceType(target),
       colorspace(
-          static_cast<EGLenum>(attribs.get(EGL_GL_COLORSPACE, EGL_GL_COLORSPACE_DEFAULT_EXT)))
+          static_cast<EGLenum>(attribs.get(EGL_GL_COLORSPACE, EGL_GL_COLORSPACE_DEFAULT_EXT))),
+      hasProtectedContent(static_cast<bool>(attribs.get(EGL_PROTECTED_CONTENT_EXT, EGL_FALSE)))
 {}
 
 ImageState::~ImageState() {}
@@ -414,6 +425,11 @@
     return mState.samples;
 }
 
+bool Image::hasProtectedContent() const
+{
+    return mState.hasProtectedContent;
+}
+
 rx::ImageImpl *Image::getImplementation() const
 {
     return mImplementation;
@@ -426,6 +442,8 @@
         ExternalImageSibling *externalSibling = rx::GetAs<ExternalImageSibling>(mState.source);
         ANGLE_TRY(externalSibling->initialize(display));
 
+        mState.hasProtectedContent = externalSibling->hasProtectedContent();
+
         // Only external siblings can be YUV
         mState.yuv = externalSibling->isYUV();
     }
diff --git a/src/libANGLE/Image.h b/src/libANGLE/Image.h
index 9924ebe..c61f183 100644
--- a/src/libANGLE/Image.h
+++ b/src/libANGLE/Image.h
@@ -52,6 +52,7 @@
                       GLenum binding,
                       const gl::ImageIndex &imageIndex) const override;
     bool isYUV() const override;
+    bool hasProtectedContent() const override;
 
   protected:
     // Set the image target of this sibling
@@ -99,6 +100,7 @@
                       const gl::ImageIndex &imageIndex) const override;
     bool isTextureable(const gl::Context *context) const;
     bool isYUV() const override;
+    bool hasProtectedContent() const override;
 
     void onAttach(const gl::Context *context, rx::Serial framebufferSerial) override;
     void onDetach(const gl::Context *context, rx::Serial framebufferSerial) override;
@@ -137,6 +139,7 @@
     size_t samples;
     EGLenum sourceType;
     EGLenum colorspace;
+    bool hasProtectedContent;
 };
 
 class Image final : public RefCountObject, public LabeledObject
@@ -162,6 +165,7 @@
     size_t getHeight() const;
     bool isLayered() const;
     size_t getSamples() const;
+    bool hasProtectedContent() const;
 
     Error initialize(const Display *display);
 
diff --git a/src/libANGLE/MemoryObject.cpp b/src/libANGLE/MemoryObject.cpp
index c854faf..a137f26 100644
--- a/src/libANGLE/MemoryObject.cpp
+++ b/src/libANGLE/MemoryObject.cpp
@@ -18,7 +18,8 @@
     : RefCountObject(factory->generateSerial(), id),
       mImplementation(factory->createMemoryObject()),
       mImmutable(false),
-      mDedicatedMemory(false)
+      mDedicatedMemory(false),
+      mProtectedMemory(false)
 {}
 
 MemoryObject::~MemoryObject() {}
@@ -35,6 +36,13 @@
     return angle::Result::Continue;
 }
 
+angle::Result MemoryObject::setProtectedMemory(const Context *context, bool protectedMemory)
+{
+    ANGLE_TRY(mImplementation->setProtectedMemory(context, protectedMemory));
+    mProtectedMemory = protectedMemory;
+    return angle::Result::Continue;
+}
+
 angle::Result MemoryObject::importFd(Context *context,
                                      GLuint64 size,
                                      HandleType handleType,
diff --git a/src/libANGLE/MemoryObject.h b/src/libANGLE/MemoryObject.h
index 3e36262..0507f7e 100644
--- a/src/libANGLE/MemoryObject.h
+++ b/src/libANGLE/MemoryObject.h
@@ -38,6 +38,8 @@
 
     angle::Result setDedicatedMemory(const Context *context, bool dedicatedMemory);
     bool isDedicatedMemory() const { return mDedicatedMemory; }
+    angle::Result setProtectedMemory(const Context *context, bool protectedMemory);
+    bool isProtectedMemory() const { return mProtectedMemory; }
 
     angle::Result importFd(Context *context, GLuint64 size, HandleType handleType, GLint fd);
     angle::Result importZirconHandle(Context *context,
@@ -50,6 +52,7 @@
 
     bool mImmutable;
     bool mDedicatedMemory;
+    bool mProtectedMemory;
 };
 
 }  // namespace gl
diff --git a/src/libANGLE/Renderbuffer.cpp b/src/libANGLE/Renderbuffer.cpp
index 946f4d8..93f86bc 100644
--- a/src/libANGLE/Renderbuffer.cpp
+++ b/src/libANGLE/Renderbuffer.cpp
@@ -34,6 +34,7 @@
       mFormat(GL_RGBA4),
       mSamples(0),
       mMultisamplingMode(MultisamplingMode::Regular),
+      mHasProtectedContent(false),
       mInitState(InitState::Initialized)
 {}
 
@@ -76,12 +77,18 @@
                                MultisamplingMode multisamplingMode,
                                InitState initState)
 {
-    mWidth             = width;
-    mHeight            = height;
-    mFormat            = format;
-    mSamples           = samples;
-    mMultisamplingMode = multisamplingMode;
-    mInitState         = InitState::MayNeedInit;
+    mWidth               = width;
+    mHeight              = height;
+    mFormat              = format;
+    mSamples             = samples;
+    mMultisamplingMode   = multisamplingMode;
+    mInitState           = InitState::MayNeedInit;
+    mHasProtectedContent = false;
+}
+
+void RenderbufferState::setProtectedContent(bool hasProtectedContent)
+{
+    mHasProtectedContent = hasProtectedContent;
 }
 
 // Renderbuffer implementation.
@@ -164,6 +171,8 @@
     mState.update(static_cast<GLsizei>(image->getWidth()), static_cast<GLsizei>(image->getHeight()),
                   Format(image->getFormat()), 0, MultisamplingMode::Regular,
                   image->sourceInitState());
+    mState.setProtectedContent(image->hasProtectedContent());
+
     onStateChange(angle::SubjectMessage::SubjectChanged);
 
     return angle::Result::Continue;
diff --git a/src/libANGLE/Renderbuffer.h b/src/libANGLE/Renderbuffer.h
index 8e6e612..4380a71 100644
--- a/src/libANGLE/Renderbuffer.h
+++ b/src/libANGLE/Renderbuffer.h
@@ -44,6 +44,7 @@
     GLsizei getSamples() const;
     MultisamplingMode getMultisamplingMode() const;
     InitState getInitState() const;
+    void setProtectedContent(bool hasProtectedContent);
 
   private:
     friend class Renderbuffer;
@@ -60,6 +61,7 @@
     Format mFormat;
     GLsizei mSamples;
     MultisamplingMode mMultisamplingMode;
+    bool mHasProtectedContent;
 
     // For robust resource init.
     InitState mInitState;
diff --git a/src/libANGLE/Surface.h b/src/libANGLE/Surface.h
index 5ccf9e6..f79a634 100644
--- a/src/libANGLE/Surface.h
+++ b/src/libANGLE/Surface.h
@@ -133,7 +133,7 @@
     EGLint getHorizontalResolution() const;
     EGLint getVerticalResolution() const;
     EGLenum getMultisampleResolve() const;
-    bool hasProtectedContent() const;
+    bool hasProtectedContent() const override;
 
     gl::Texture *getBoundTexture() const { return mTexture; }
 
diff --git a/src/libANGLE/Texture.cpp b/src/libANGLE/Texture.cpp
index 7843abb..55186c0 100644
--- a/src/libANGLE/Texture.cpp
+++ b/src/libANGLE/Texture.cpp
@@ -144,6 +144,7 @@
       mImmutableFormat(false),
       mImmutableLevels(0),
       mUsage(GL_NONE),
+      mHasProtectedContent(false),
       mImageDescs((IMPLEMENTATION_MAX_TEXTURE_LEVELS + 1) * (type == TextureType::CubeMap ? 6 : 1)),
       mCropRect(0, 0, 0, 0),
       mGenerateMipmapHint(GL_FALSE),
@@ -1065,6 +1066,16 @@
     return mState.mUsage;
 }
 
+void Texture::setProtectedContent(Context *context, bool hasProtectedContent)
+{
+    mState.mHasProtectedContent = hasProtectedContent;
+}
+
+bool Texture::hasProtectedContent() const
+{
+    return mState.mHasProtectedContent;
+}
+
 const TextureState &Texture::getTextureState() const
 {
     return mState;
@@ -1735,6 +1746,7 @@
     Extents size(surface->getWidth(), surface->getHeight(), 1);
     ImageDesc desc(size, surface->getBindTexImageFormat(), InitState::Initialized);
     mState.setImageDesc(NonCubeTextureTypeToTarget(mState.mType), 0, desc);
+    mState.mHasProtectedContent = surface->hasProtectedContent();
     signalDirtyStorage(InitState::Initialized);
     return angle::Result::Continue;
 }
@@ -1748,6 +1760,7 @@
     // Erase the image info for level 0
     ASSERT(mState.mType == TextureType::_2D || mState.mType == TextureType::Rectangle);
     mState.clearImageDesc(NonCubeTextureTypeToTarget(mState.mType), 0);
+    mState.mHasProtectedContent = false;
     signalDirtyStorage(InitState::Initialized);
     return angle::Result::Continue;
 }
@@ -1838,6 +1851,7 @@
     mState.clearImageDescs();
     mState.setImageDesc(NonCubeTextureTypeToTarget(type), 0,
                         ImageDesc(size, imageTarget->getFormat(), initState));
+    mState.mHasProtectedContent = imageTarget->hasProtectedContent();
     signalDirtyStorage(initState);
 
     return angle::Result::Continue;
diff --git a/src/libANGLE/Texture.h b/src/libANGLE/Texture.h
index e563127..2a03fdf 100644
--- a/src/libANGLE/Texture.h
+++ b/src/libANGLE/Texture.h
@@ -143,6 +143,7 @@
     const SwizzleState &getSwizzleState() const { return mSwizzleState; }
     const SamplerState &getSamplerState() const { return mSamplerState; }
     GLenum getUsage() const { return mUsage; }
+    bool hasProtectedContent() const { return mHasProtectedContent; }
     GLenum getDepthStencilTextureMode() const { return mDepthStencilTextureMode; }
     bool isStencilMode() const { return mDepthStencilTextureMode == GL_STENCIL_INDEX; }
 
@@ -224,6 +225,9 @@
     // From GL_ANGLE_texture_usage
     GLenum mUsage;
 
+    // GL_EXT_protected_textures
+    bool mHasProtectedContent;
+
     std::vector<ImageDesc> mImageDescs;
 
     // GLES1 emulation: Texture crop rectangle
@@ -328,6 +332,9 @@
     void setUsage(const Context *context, GLenum usage);
     GLenum getUsage() const;
 
+    void setProtectedContent(Context *context, bool hasProtectedContent);
+    bool hasProtectedContent() const override;
+
     const TextureState &getState() const { return mState; }
 
     void setBorderColor(const Context *context, const ColorGeneric &color);
diff --git a/src/libANGLE/capture/capture_gles_ext_autogen.h b/src/libANGLE/capture/capture_gles_ext_autogen.h
index 346d18d..d316650 100644
--- a/src/libANGLE/capture/capture_gles_ext_autogen.h
+++ b/src/libANGLE/capture/capture_gles_ext_autogen.h
@@ -1273,6 +1273,8 @@
                                                   GLfloat maxZ,
                                                   GLfloat maxW);
 
+// GL_EXT_protected_textures
+
 // GL_EXT_read_format_bgra
 
 // GL_EXT_robustness
diff --git a/src/libANGLE/capture/gl_enum_utils_autogen.cpp b/src/libANGLE/capture/gl_enum_utils_autogen.cpp
index 77168cb..cb4564b 100644
--- a/src/libANGLE/capture/gl_enum_utils_autogen.cpp
+++ b/src/libANGLE/capture/gl_enum_utils_autogen.cpp
@@ -535,6 +535,8 @@
                     return "GL_CONTEXT_FLAG_DEBUG_BIT";
                 case 0x4:
                     return "GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT";
+                case 0x10:
+                    return "GL_CONTEXT_FLAG_PROTECTED_CONTENT_BIT_EXT";
                 default:
                     return UnknownGLenumToString(value);
             }
@@ -1173,6 +1175,8 @@
                     return "GL_POINT_SIZE_ARRAY_BUFFER_BINDING_OES";
                 case 0x8BE7:
                     return "GL_SAMPLER_EXTERNAL_2D_Y2Y_EXT";
+                case 0x8BFA:
+                    return "GL_TEXTURE_PROTECTED_EXT";
                 case 0x8C10:
                     return "GL_TEXTURE_RED_TYPE";
                 case 0x8C11:
diff --git a/src/libANGLE/queryutils.cpp b/src/libANGLE/queryutils.cpp
index 020779b..7cc9211 100644
--- a/src/libANGLE/queryutils.cpp
+++ b/src/libANGLE/queryutils.cpp
@@ -369,6 +369,9 @@
             *params = CastFromGLintStateValue<ParamType>(
                 pname, texture->getRequiredTextureImageUnits(context));
             break;
+        case GL_TEXTURE_PROTECTED_EXT:
+            *params = CastFromGLintStateValue<ParamType>(pname, texture->hasProtectedContent());
+            break;
         default:
             UNREACHABLE();
             break;
@@ -479,6 +482,9 @@
             texture->setInitState(ConvertToBool(params[0]) ? InitState::Initialized
                                                            : InitState::MayNeedInit);
             break;
+        case GL_TEXTURE_PROTECTED_EXT:
+            texture->setProtectedContent(context, (params[0] == GL_TRUE));
+            break;
         default:
             UNREACHABLE();
             break;
@@ -2193,6 +2199,10 @@
             ANGLE_TRY(memoryObject->setDedicatedMemory(context, ConvertToBool(params[0])));
             break;
 
+        case GL_PROTECTED_MEMORY_OBJECT_EXT:
+            ANGLE_TRY(memoryObject->setProtectedMemory(context, ConvertToBool(params[0])));
+            break;
+
         default:
             UNREACHABLE();
     }
@@ -2208,6 +2218,10 @@
             *params = memoryObject->isDedicatedMemory();
             break;
 
+        case GL_PROTECTED_MEMORY_OBJECT_EXT:
+            *params = memoryObject->isProtectedMemory();
+            break;
+
         default:
             UNREACHABLE();
     }
diff --git a/src/libANGLE/renderer/ImageImpl.h b/src/libANGLE/renderer/ImageImpl.h
index f5dbcad..44eb007 100644
--- a/src/libANGLE/renderer/ImageImpl.h
+++ b/src/libANGLE/renderer/ImageImpl.h
@@ -40,6 +40,7 @@
     virtual bool isRenderable(const gl::Context *context) const = 0;
     virtual bool isTexturable(const gl::Context *context) const = 0;
     virtual bool isYUV() const                                  = 0;
+    virtual bool hasProtectedContent() const                    = 0;
     virtual gl::Extents getSize() const                         = 0;
     virtual size_t getSamples() const                           = 0;
 };
diff --git a/src/libANGLE/renderer/MemoryObjectImpl.h b/src/libANGLE/renderer/MemoryObjectImpl.h
index 9bb4154..ce37dd3 100644
--- a/src/libANGLE/renderer/MemoryObjectImpl.h
+++ b/src/libANGLE/renderer/MemoryObjectImpl.h
@@ -30,6 +30,7 @@
     virtual void onDestroy(const gl::Context *context) = 0;
 
     virtual angle::Result setDedicatedMemory(const gl::Context *context, bool dedicatedMemory) = 0;
+    virtual angle::Result setProtectedMemory(const gl::Context *context, bool protectedMemory) = 0;
 
     virtual angle::Result importFd(gl::Context *context,
                                    GLuint64 size,
diff --git a/src/libANGLE/renderer/d3d/d3d11/ExternalImageSiblingImpl11.cpp b/src/libANGLE/renderer/d3d/d3d11/ExternalImageSiblingImpl11.cpp
index c0481f8..f8e5ea8 100644
--- a/src/libANGLE/renderer/d3d/d3d11/ExternalImageSiblingImpl11.cpp
+++ b/src/libANGLE/renderer/d3d/d3d11/ExternalImageSiblingImpl11.cpp
@@ -81,6 +81,11 @@
     return mYUV;
 }
 
+bool ExternalImageSiblingImpl11::hasProtectedContent() const
+{
+    return mHasProtectedContent;
+}
+
 gl::Extents ExternalImageSiblingImpl11::getSize() const
 {
     return gl::Extents(mWidth, mHeight, 1);
diff --git a/src/libANGLE/renderer/d3d/d3d11/ExternalImageSiblingImpl11.h b/src/libANGLE/renderer/d3d/d3d11/ExternalImageSiblingImpl11.h
index a163bca..45dd42c 100644
--- a/src/libANGLE/renderer/d3d/d3d11/ExternalImageSiblingImpl11.h
+++ b/src/libANGLE/renderer/d3d/d3d11/ExternalImageSiblingImpl11.h
@@ -30,6 +30,7 @@
     bool isRenderable(const gl::Context *context) const override;
     bool isTexturable(const gl::Context *context) const override;
     bool isYUV() const override;
+    bool hasProtectedContent() const override;
     gl::Extents getSize() const override;
     size_t getSamples() const override;
 
@@ -51,15 +52,16 @@
 
     TextureHelper11 mTexture;
 
-    gl::Format mFormat   = gl::Format::Invalid();
-    bool mIsRenderable   = false;
-    bool mIsTexturable   = false;
-    bool mIsTextureArray = false;
-    bool mYUV            = false;
-    EGLint mWidth        = 0;
-    EGLint mHeight       = 0;
-    GLsizei mSamples     = 0;
-    UINT mArraySlice     = 0;
+    gl::Format mFormat        = gl::Format::Invalid();
+    bool mIsRenderable        = false;
+    bool mIsTexturable        = false;
+    bool mIsTextureArray      = false;
+    bool mYUV                 = false;
+    bool mHasProtectedContent = false;
+    EGLint mWidth             = 0;
+    EGLint mHeight            = 0;
+    GLsizei mSamples          = 0;
+    UINT mArraySlice          = 0;
 
     std::unique_ptr<RenderTargetD3D> mRenderTarget;
 };
diff --git a/src/libANGLE/renderer/gl/MemoryObjectGL.cpp b/src/libANGLE/renderer/gl/MemoryObjectGL.cpp
index 7807860..509c3ef 100644
--- a/src/libANGLE/renderer/gl/MemoryObjectGL.cpp
+++ b/src/libANGLE/renderer/gl/MemoryObjectGL.cpp
@@ -40,6 +40,16 @@
     return angle::Result::Continue;
 }
 
+angle::Result MemoryObjectGL::setProtectedMemory(const gl::Context *context, bool protectedMemory)
+{
+    const FunctionsGL *functions = GetFunctionsGL(context);
+
+    GLint params = gl::ConvertToGLBoolean(protectedMemory);
+    ANGLE_GL_TRY(context, functions->memoryObjectParameterivEXT(
+                              mMemoryObject, GL_PROTECTED_MEMORY_OBJECT_EXT, &params));
+    return angle::Result::Continue;
+}
+
 angle::Result MemoryObjectGL::importFd(gl::Context *context,
                                        GLuint64 size,
                                        gl::HandleType handleType,
diff --git a/src/libANGLE/renderer/gl/MemoryObjectGL.h b/src/libANGLE/renderer/gl/MemoryObjectGL.h
index 5c36478..cfcb7d5 100644
--- a/src/libANGLE/renderer/gl/MemoryObjectGL.h
+++ b/src/libANGLE/renderer/gl/MemoryObjectGL.h
@@ -22,6 +22,7 @@
     void onDestroy(const gl::Context *context) override;
 
     angle::Result setDedicatedMemory(const gl::Context *context, bool dedicatedMemory) override;
+    angle::Result setProtectedMemory(const gl::Context *context, bool protectedMemory) override;
 
     angle::Result importFd(gl::Context *context,
                            GLuint64 size,
diff --git a/src/libANGLE/renderer/gl/egl/DmaBufImageSiblingEGL.cpp b/src/libANGLE/renderer/gl/egl/DmaBufImageSiblingEGL.cpp
index 476035e..f435942 100644
--- a/src/libANGLE/renderer/gl/egl/DmaBufImageSiblingEGL.cpp
+++ b/src/libANGLE/renderer/gl/egl/DmaBufImageSiblingEGL.cpp
@@ -74,13 +74,14 @@
 namespace rx
 {
 DmaBufImageSiblingEGL::DmaBufImageSiblingEGL(const egl::AttributeMap &attribs)
-    : mAttribs(attribs), mFormat(GL_NONE), mYUV(false)
+    : mAttribs(attribs), mFormat(GL_NONE), mYUV(false), mHasProtectedContent(false)
 {
     ASSERT(mAttribs.contains(EGL_WIDTH));
     mSize.width = mAttribs.getAsInt(EGL_WIDTH);
     ASSERT(mAttribs.contains(EGL_HEIGHT));
-    mSize.height = mAttribs.getAsInt(EGL_HEIGHT);
-    mSize.depth  = 1;
+    mSize.height         = mAttribs.getAsInt(EGL_HEIGHT);
+    mSize.depth          = 1;
+    mHasProtectedContent = mAttribs.getAsInt(EGL_PROTECTED_CONTENT_EXT, EGL_FALSE) != 0;
 
     int fourCCFormat = mAttribs.getAsInt(EGL_LINUX_DRM_FOURCC_EXT);
     mFormat          = gl::Format(FourCCFormatToGLInternalFormat(fourCCFormat, &mYUV));
@@ -113,6 +114,11 @@
     return mYUV;
 }
 
+bool DmaBufImageSiblingEGL::hasProtectedContent() const
+{
+    return mHasProtectedContent;
+}
+
 gl::Extents DmaBufImageSiblingEGL::getSize() const
 {
     return mSize;
@@ -132,6 +138,7 @@
 {
     EGLenum kForwardedAttribs[] = {EGL_WIDTH,
                                    EGL_HEIGHT,
+                                   EGL_PROTECTED_CONTENT_EXT,
                                    EGL_LINUX_DRM_FOURCC_EXT,
                                    EGL_DMA_BUF_PLANE0_FD_EXT,
                                    EGL_DMA_BUF_PLANE0_OFFSET_EXT,
diff --git a/src/libANGLE/renderer/gl/egl/DmaBufImageSiblingEGL.h b/src/libANGLE/renderer/gl/egl/DmaBufImageSiblingEGL.h
index 033ec5c..f367f6b 100644
--- a/src/libANGLE/renderer/gl/egl/DmaBufImageSiblingEGL.h
+++ b/src/libANGLE/renderer/gl/egl/DmaBufImageSiblingEGL.h
@@ -28,6 +28,7 @@
     bool isRenderable(const gl::Context *context) const override;
     bool isTexturable(const gl::Context *context) const override;
     bool isYUV() const override;
+    bool hasProtectedContent() const override;
     gl::Extents getSize() const override;
     size_t getSamples() const override;
 
@@ -40,6 +41,7 @@
     gl::Extents mSize;
     gl::Format mFormat;
     bool mYUV;
+    bool mHasProtectedContent;
 };
 
 }  // namespace rx
diff --git a/src/libANGLE/renderer/gl/egl/android/NativeBufferImageSiblingAndroid.cpp b/src/libANGLE/renderer/gl/egl/android/NativeBufferImageSiblingAndroid.cpp
index fac8e83..3bc7070 100644
--- a/src/libANGLE/renderer/gl/egl/android/NativeBufferImageSiblingAndroid.cpp
+++ b/src/libANGLE/renderer/gl/egl/android/NativeBufferImageSiblingAndroid.cpp
@@ -21,11 +21,13 @@
 egl::Error NativeBufferImageSiblingAndroid::initialize(const egl::Display *display)
 {
     int pixelFormat = 0;
+    uint64_t usage  = 0;
     angle::android::GetANativeWindowBufferProperties(
         angle::android::ClientBufferToANativeWindowBuffer(mBuffer), &mSize.width, &mSize.height,
-        &mSize.depth, &pixelFormat);
+        &mSize.depth, &pixelFormat, &usage);
     mFormat = gl::Format(angle::android::NativePixelFormatToGLInternalFormat(pixelFormat));
     mYUV    = angle::android::NativePixelFormatIsYUV(pixelFormat);
+    mHasProtectedContent = (usage & EGL_NATIVE_BUFFER_USAGE_PROTECTED_BIT_ANDROID) != 0;
 
     return egl::NoError();
 }
@@ -50,6 +52,11 @@
     return mYUV;
 }
 
+bool NativeBufferImageSiblingAndroid::hasProtectedContent() const
+{
+    return mHasProtectedContent;
+}
+
 gl::Extents NativeBufferImageSiblingAndroid::getSize() const
 {
     return mSize;
diff --git a/src/libANGLE/renderer/gl/egl/android/NativeBufferImageSiblingAndroid.h b/src/libANGLE/renderer/gl/egl/android/NativeBufferImageSiblingAndroid.h
index 5c5fda2..d549411 100644
--- a/src/libANGLE/renderer/gl/egl/android/NativeBufferImageSiblingAndroid.h
+++ b/src/libANGLE/renderer/gl/egl/android/NativeBufferImageSiblingAndroid.h
@@ -28,6 +28,7 @@
     bool isRenderable(const gl::Context *context) const override;
     bool isTexturable(const gl::Context *context) const override;
     bool isYUV() const override;
+    bool hasProtectedContent() const override;
     gl::Extents getSize() const override;
     size_t getSamples() const override;
 
@@ -39,6 +40,7 @@
     gl::Extents mSize;
     gl::Format mFormat;
     bool mYUV;
+    bool mHasProtectedContent;
 };
 
 }  // namespace rx
diff --git a/src/libANGLE/renderer/metal/ImageMtl.h b/src/libANGLE/renderer/metal/ImageMtl.h
index 5d61518..7772966 100644
--- a/src/libANGLE/renderer/metal/ImageMtl.h
+++ b/src/libANGLE/renderer/metal/ImageMtl.h
@@ -37,6 +37,7 @@
     size_t getSamples() const override;
 
     bool isYUV() const override;
+    bool hasProtectedContent() const override;
 
     const mtl::TextureRef &getTexture() const { return mNativeTexture; }
     const mtl::Format &getFormatMtl() const { return mFormat; }
diff --git a/src/libANGLE/renderer/metal/ImageMtl.mm b/src/libANGLE/renderer/metal/ImageMtl.mm
index 73738d5..2090ac7 100644
--- a/src/libANGLE/renderer/metal/ImageMtl.mm
+++ b/src/libANGLE/renderer/metal/ImageMtl.mm
@@ -118,6 +118,11 @@
     return false;
 }
 
+bool TextureImageSiblingMtl::hasProtectedContent() const
+{
+    return false;
+}
+
 // ImageMtl implementation
 ImageMtl::ImageMtl(const egl::ImageState &state, const gl::Context *context) : ImageImpl(state) {}
 
diff --git a/src/libANGLE/renderer/vulkan/MemoryObjectVk.cpp b/src/libANGLE/renderer/vulkan/MemoryObjectVk.cpp
index b4ccca9..6012cde 100644
--- a/src/libANGLE/renderer/vulkan/MemoryObjectVk.cpp
+++ b/src/libANGLE/renderer/vulkan/MemoryObjectVk.cpp
@@ -101,6 +101,12 @@
     return angle::Result::Continue;
 }
 
+angle::Result MemoryObjectVk::setProtectedMemory(const gl::Context *context, bool protectedMemory)
+{
+    mProtectedMemory = protectedMemory;
+    return angle::Result::Continue;
+}
+
 angle::Result MemoryObjectVk::importFd(gl::Context *context,
                                        GLuint64 size,
                                        gl::HandleType handleType,
@@ -204,11 +210,12 @@
     // ANGLE_external_objects_flags allows create flags to be specified by the application instead
     // of getting defaulted to zero.  Note that the GL enum values constituting the bits of
     // |createFlags| are identical to their corresponding Vulkan value.
-    ANGLE_TRY(image->initExternal(contextVk, type, vkExtents, vkFormat, 1, imageUsageFlags,
-                                  createFlags, vk::ImageLayout::Undefined,
-                                  &externalMemoryImageCreateInfo, gl::LevelIndex(0),
-                                  static_cast<uint32_t>(levels), layerCount,
-                                  contextVk->isRobustResourceInitEnabled(), nullptr, false));
+    bool hasProtectedContent = mProtectedMemory;
+    ANGLE_TRY(image->initExternal(
+        contextVk, type, vkExtents, vkFormat, 1, imageUsageFlags, createFlags,
+        vk::ImageLayout::Undefined, &externalMemoryImageCreateInfo, gl::LevelIndex(0),
+        static_cast<uint32_t>(levels), layerCount, contextVk->isRobustResourceInitEnabled(),
+        nullptr, hasProtectedContent));
 
     VkMemoryRequirements externalMemoryRequirements;
     image->getImage().getMemoryRequirements(renderer->getDevice(), &externalMemoryRequirements);
@@ -252,7 +259,7 @@
     ASSERT(offset == 0);
     ASSERT(externalMemoryRequirements.size == mSize);
 
-    VkMemoryPropertyFlags flags = 0;
+    VkMemoryPropertyFlags flags = hasProtectedContent ? VK_MEMORY_PROPERTY_PROTECTED_BIT : 0;
     ANGLE_TRY(image->initExternalMemory(contextVk, renderer->getMemoryProperties(),
                                         externalMemoryRequirements, nullptr, importMemoryInfo,
                                         renderer->getQueueFamilyIndex(), flags));
diff --git a/src/libANGLE/renderer/vulkan/MemoryObjectVk.h b/src/libANGLE/renderer/vulkan/MemoryObjectVk.h
index cf5d65a..d94fbb2 100644
--- a/src/libANGLE/renderer/vulkan/MemoryObjectVk.h
+++ b/src/libANGLE/renderer/vulkan/MemoryObjectVk.h
@@ -24,6 +24,7 @@
     void onDestroy(const gl::Context *context) override;
 
     angle::Result setDedicatedMemory(const gl::Context *context, bool dedicatedMemory) override;
+    angle::Result setProtectedMemory(const gl::Context *context, bool protectedMemory) override;
 
     angle::Result importFd(gl::Context *context,
                            GLuint64 size,
@@ -52,6 +53,7 @@
 
     // Imported memory object was a dedicated allocation.
     bool mDedicatedMemory = false;
+    bool mProtectedMemory = false;
 
     GLuint64 mSize             = 0;
     gl::HandleType mHandleType = gl::HandleType::InvalidEnum;
diff --git a/src/libANGLE/renderer/vulkan/TextureVk.cpp b/src/libANGLE/renderer/vulkan/TextureVk.cpp
index 6cf6ba1..e0be967 100644
--- a/src/libANGLE/renderer/vulkan/TextureVk.cpp
+++ b/src/libANGLE/renderer/vulkan/TextureVk.cpp
@@ -1008,10 +1008,10 @@
         std::unique_ptr<vk::RefCounted<vk::ImageHelper>> stagingImage;
         stagingImage = std::make_unique<vk::RefCounted<vk::ImageHelper>>();
 
-        ANGLE_TRY(
-            stagingImage->get().init2DStaging(contextVk, false, renderer->getMemoryProperties(),
-                                              gl::Extents(sourceBox.width, sourceBox.height, 1),
-                                              destFormat, kTransferStagingImageFlags, layerCount));
+        ANGLE_TRY(stagingImage->get().init2DStaging(
+            contextVk, mState.hasProtectedContent(), renderer->getMemoryProperties(),
+            gl::Extents(sourceBox.width, sourceBox.height, 1), destFormat,
+            kTransferStagingImageFlags, layerCount));
 
         access.onImageTransferWrite(gl::LevelIndex(0), 1, 0, layerCount, VK_IMAGE_ASPECT_COLOR_BIT,
                                     &stagingImage->get());
@@ -1159,10 +1159,10 @@
         std::unique_ptr<vk::RefCounted<vk::ImageHelper>> stagingImage;
         stagingImage = std::make_unique<vk::RefCounted<vk::ImageHelper>>();
 
-        ANGLE_TRY(
-            stagingImage->get().init2DStaging(contextVk, false, renderer->getMemoryProperties(),
-                                              gl::Extents(sourceBox.width, sourceBox.height, 1),
-                                              destFormat, kDrawStagingImageFlags, layerCount));
+        ANGLE_TRY(stagingImage->get().init2DStaging(
+            contextVk, mState.hasProtectedContent(), renderer->getMemoryProperties(),
+            gl::Extents(sourceBox.width, sourceBox.height, 1), destFormat, kDrawStagingImageFlags,
+            layerCount));
 
         params.destOffset[0] = 0;
         params.destOffset[1] = 0;
@@ -1987,10 +1987,10 @@
     const uint32_t levelCount = srcImage->getLevelCount();
     const uint32_t layerCount = srcImage->getLayerCount();
 
-    ANGLE_TRY(stagingImage->get().initStaging(contextVk, false, renderer->getMemoryProperties(),
-                                              srcImage->getType(), srcImage->getExtents(),
-                                              srcImage->getFormat(), srcImage->getSamples(),
-                                              kTransferStagingImageFlags, levelCount, layerCount));
+    ANGLE_TRY(stagingImage->get().initStaging(
+        contextVk, mState.hasProtectedContent(), renderer->getMemoryProperties(),
+        srcImage->getType(), srcImage->getExtents(), srcImage->getFormat(), srcImage->getSamples(),
+        kTransferStagingImageFlags, levelCount, layerCount));
 
     // Copy the src image wholly into the staging image
     const VkImageAspectFlags aspectFlags = srcImage->getAspectFlags();
@@ -2196,8 +2196,8 @@
 
         // Create the implicit multisampled image.
         ANGLE_TRY(multisampledImage->initImplicitMultisampledRenderToTexture(
-            contextVk, false, renderer->getMemoryProperties(), mState.getType(), samples, *mImage,
-            useRobustInit));
+            contextVk, mState.hasProtectedContent(), renderer->getMemoryProperties(),
+            mState.getType(), samples, *mImage, useRobustInit));
     }
 
     // Don't flush staged updates here. We'll handle that in FramebufferVk so it can defer clears.
@@ -2793,17 +2793,28 @@
     gl_vk::GetExtentsAndLayerCount(mState.getType(), firstLevelExtents, &vkExtent, &layerCount);
     GLint samples = mState.getBaseLevelDesc().samples ? mState.getBaseLevelDesc().samples : 1;
 
+    if (mState.hasProtectedContent())
+    {
+        mImageCreateFlags |= VK_IMAGE_CREATE_PROTECTED_BIT;
+    }
+
     bool imageFormatListEnabled = false;
-    ANGLE_TRY(mImage->initExternal(
-        contextVk, mState.getType(), vkExtent, format, samples, mImageUsageFlags, mImageCreateFlags,
-        vk::ImageLayout::Undefined, nullptr, gl::LevelIndex(firstLevel), levelCount, layerCount,
-        contextVk->isRobustResourceInitEnabled(), &imageFormatListEnabled, false));
+    ANGLE_TRY(mImage->initExternal(contextVk, mState.getType(), vkExtent, format, samples,
+                                   mImageUsageFlags, mImageCreateFlags, vk::ImageLayout::Undefined,
+                                   nullptr, gl::LevelIndex(firstLevel), levelCount, layerCount,
+                                   contextVk->isRobustResourceInitEnabled(),
+                                   &imageFormatListEnabled, mState.hasProtectedContent()));
 
     mRequiresMutableStorage = (mImageCreateFlags & VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT) != 0;
 
-    const VkMemoryPropertyFlags flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
+    VkMemoryPropertyFlags flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
+    if (mState.hasProtectedContent())
+    {
+        flags |= VK_MEMORY_PROPERTY_PROTECTED_BIT;
+    }
 
-    ANGLE_TRY(mImage->initMemory(contextVk, false, renderer->getMemoryProperties(), flags));
+    ANGLE_TRY(mImage->initMemory(contextVk, mState.hasProtectedContent(),
+                                 renderer->getMemoryProperties(), flags));
 
     const uint32_t viewLevelCount =
         mState.getImmutableFormat() ? getMipLevelCount(ImageMipLevels::EnabledLevels) : levelCount;
diff --git a/src/libANGLE/renderer/vulkan/android/DisplayVkAndroid.cpp b/src/libANGLE/renderer/vulkan/android/DisplayVkAndroid.cpp
index 78a4924..1d5afab 100644
--- a/src/libANGLE/renderer/vulkan/android/DisplayVkAndroid.cpp
+++ b/src/libANGLE/renderer/vulkan/android/DisplayVkAndroid.cpp
@@ -98,8 +98,8 @@
     switch (target)
     {
         case EGL_NATIVE_BUFFER_ANDROID:
-            return HardwareBufferImageSiblingVkAndroid::ValidateHardwareBuffer(mRenderer,
-                                                                               clientBuffer);
+            return HardwareBufferImageSiblingVkAndroid::ValidateHardwareBuffer(
+                mRenderer, clientBuffer, attribs);
 
         default:
             return DisplayVk::validateImageClientBuffer(context, target, clientBuffer, attribs);
diff --git a/src/libANGLE/renderer/vulkan/android/HardwareBufferImageSiblingVkAndroid.cpp b/src/libANGLE/renderer/vulkan/android/HardwareBufferImageSiblingVkAndroid.cpp
index 8d15976..9843193 100644
--- a/src/libANGLE/renderer/vulkan/android/HardwareBufferImageSiblingVkAndroid.cpp
+++ b/src/libANGLE/renderer/vulkan/android/HardwareBufferImageSiblingVkAndroid.cpp
@@ -87,8 +87,10 @@
 HardwareBufferImageSiblingVkAndroid::~HardwareBufferImageSiblingVkAndroid() {}
 
 // Static
-egl::Error HardwareBufferImageSiblingVkAndroid::ValidateHardwareBuffer(RendererVk *renderer,
-                                                                       EGLClientBuffer buffer)
+egl::Error HardwareBufferImageSiblingVkAndroid::ValidateHardwareBuffer(
+    RendererVk *renderer,
+    EGLClientBuffer buffer,
+    const egl::AttributeMap &attribs)
 {
     struct ANativeWindowBuffer *windowBuffer =
         angle::android::ClientBufferToANativeWindowBuffer(buffer);
@@ -133,6 +135,23 @@
         }
     }
 
+    if (attribs.getAsInt(EGL_PROTECTED_CONTENT_EXT, EGL_FALSE) == EGL_TRUE)
+    {
+        int width       = 0;
+        int height      = 0;
+        int depth       = 0;
+        int pixelFormat = 0;
+        uint64_t usage  = 0;
+        angle::android::GetANativeWindowBufferProperties(windowBuffer, &width, &height, &depth,
+                                                         &pixelFormat, &usage);
+        if ((usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT) == 0)
+        {
+            return egl::EglBadAccess()
+                   << "EGL_PROTECTED_CONTENT_EXT attribute does not match protected state "
+                      "of EGLCleintBuffer.";
+        }
+    }
+
     return egl::NoError();
 }
 
@@ -191,7 +210,7 @@
 
     int pixelFormat = 0;
     angle::android::GetANativeWindowBufferProperties(windowBuffer, &mSize.width, &mSize.height,
-                                                     &mSize.depth, &pixelFormat);
+                                                     &mSize.depth, &pixelFormat, &mUsage);
     GLenum internalFormat = angle::android::NativePixelFormatToGLInternalFormat(pixelFormat);
     mFormat               = gl::Format(internalFormat);
 
@@ -259,12 +278,17 @@
     bool robustInitEnabled = false;
 
     mImage->setTilingMode(imageTilingMode);
+    VkImageCreateFlags imageCreateFlags = vk::kVkImageCreateFlagsNone;
+    if (hasProtectedContent())
+    {
+        imageCreateFlags |= VK_IMAGE_CREATE_PROTECTED_BIT;
+    }
     ANGLE_TRY(mImage->initExternal(
         displayVk, gl::TextureType::_2D, vkExtents,
         bufferFormatProperties.format == VK_FORMAT_UNDEFINED ? externalVkFormat : vkFormat, 1,
-        usage, vk::kVkImageCreateFlagsNone, vk::ImageLayout::ExternalPreInitialized,
+        usage, imageCreateFlags, vk::ImageLayout::ExternalPreInitialized,
         &externalMemoryImageCreateInfo, gl::LevelIndex(0), 1, 1, robustInitEnabled, nullptr,
-        false));
+        hasProtectedContent()));
 
     VkImportAndroidHardwareBufferInfoANDROID importHardwareBufferInfo = {};
     importHardwareBufferInfo.sType  = VK_STRUCTURE_TYPE_IMPORT_ANDROID_HARDWARE_BUFFER_INFO_ANDROID;
@@ -281,7 +305,8 @@
     externalMemoryRequirements.alignment            = 0;
     externalMemoryRequirements.memoryTypeBits       = bufferProperties.memoryTypeBits;
 
-    VkMemoryPropertyFlags flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
+    VkMemoryPropertyFlags flags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
+                                  (hasProtectedContent() ? VK_MEMORY_PROPERTY_PROTECTED_BIT : 0);
     if (bufferFormatProperties.format == VK_FORMAT_UNDEFINED)
     {
         // Note from Vulkan spec: Since GL_OES_EGL_image_external does not require the same sampling
@@ -360,6 +385,11 @@
     return mYUV;
 }
 
+bool HardwareBufferImageSiblingVkAndroid::hasProtectedContent() const
+{
+    return ((mUsage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT) != 0);
+}
+
 gl::Extents HardwareBufferImageSiblingVkAndroid::getSize() const
 {
     return mSize;
diff --git a/src/libANGLE/renderer/vulkan/android/HardwareBufferImageSiblingVkAndroid.h b/src/libANGLE/renderer/vulkan/android/HardwareBufferImageSiblingVkAndroid.h
index 39f5ed5..7f55bca 100644
--- a/src/libANGLE/renderer/vulkan/android/HardwareBufferImageSiblingVkAndroid.h
+++ b/src/libANGLE/renderer/vulkan/android/HardwareBufferImageSiblingVkAndroid.h
@@ -21,7 +21,9 @@
     HardwareBufferImageSiblingVkAndroid(EGLClientBuffer buffer);
     ~HardwareBufferImageSiblingVkAndroid() override;
 
-    static egl::Error ValidateHardwareBuffer(RendererVk *renderer, EGLClientBuffer buffer);
+    static egl::Error ValidateHardwareBuffer(RendererVk *renderer,
+                                             EGLClientBuffer buffer,
+                                             const egl::AttributeMap &attribs);
 
     egl::Error initialize(const egl::Display *display) override;
     void onDestroy(const egl::Display *display) override;
@@ -31,6 +33,7 @@
     bool isRenderable(const gl::Context *context) const override;
     bool isTexturable(const gl::Context *context) const override;
     bool isYUV() const override;
+    bool hasProtectedContent() const override;
     gl::Extents getSize() const override;
     size_t getSamples() const override;
 
@@ -49,6 +52,7 @@
     bool mRenderable;
     bool mTextureable;
     bool mYUV;
+    uint64_t mUsage;
     size_t mSamples;
 
     vk::ImageHelper *mImage;
diff --git a/src/libANGLE/renderer/vulkan/vk_caps_utils.cpp b/src/libANGLE/renderer/vulkan/vk_caps_utils.cpp
index 778e5bc..c6c9f50 100644
--- a/src/libANGLE/renderer/vulkan/vk_caps_utils.cpp
+++ b/src/libANGLE/renderer/vulkan/vk_caps_utils.cpp
@@ -1072,6 +1072,9 @@
     // GL_ANGLE_yuv_internal_format
     mNativeExtensions.yuvInternalFormatANGLE =
         getFeatures().supportsYUVSamplerConversion.enabled && vk::CanSupportYuvInternalFormat(this);
+
+    // GL_EXT_protected_textures
+    mNativeExtensions.protectedTexturesEXT = mFeatures.supportsProtectedMemory.enabled;
 }
 
 namespace vk
diff --git a/src/libANGLE/validationEGL.cpp b/src/libANGLE/validationEGL.cpp
index b5f0414..4e6bbb7 100644
--- a/src/libANGLE/validationEGL.cpp
+++ b/src/libANGLE/validationEGL.cpp
@@ -3240,6 +3240,16 @@
                 return false;
             }
 
+            bool protectedContentAttrib =
+                (attributes.getAsInt(EGL_PROTECTED_CONTENT_EXT, EGL_FALSE) != EGL_FALSE);
+            if (protectedContentAttrib != texture->hasProtectedContent())
+            {
+                val->setError(EGL_BAD_PARAMETER,
+                              "EGL_PROTECTED_CONTENT_EXT attribute does not match protected state "
+                              "of target.");
+                return false;
+            }
+
             ANGLE_VALIDATION_TRY(ValidateCreateImageMipLevelCommon(val, context, texture, level));
         }
         break;
@@ -3301,6 +3311,16 @@
                               "zero.");
                 return false;
             }
+
+            bool protectedContentAttrib =
+                (attributes.getAsInt(EGL_PROTECTED_CONTENT_EXT, EGL_FALSE) != EGL_FALSE);
+            if (protectedContentAttrib != texture->hasProtectedContent())
+            {
+                val->setError(EGL_BAD_PARAMETER,
+                              "EGL_PROTECTED_CONTENT_EXT attribute does not match protected state "
+                              "of target.");
+                return false;
+            }
         }
         break;
 
@@ -3355,6 +3375,16 @@
                 return false;
             }
 
+            bool protectedContentAttrib =
+                (attributes.getAsInt(EGL_PROTECTED_CONTENT_EXT, EGL_FALSE) != EGL_FALSE);
+            if (protectedContentAttrib != texture->hasProtectedContent())
+            {
+                val->setError(EGL_BAD_PARAMETER,
+                              "EGL_PROTECTED_CONTENT_EXT attribute does not match protected state "
+                              "of target.");
+                return false;
+            }
+
             ANGLE_VALIDATION_TRY(ValidateCreateImageMipLevelCommon(val, context, texture, level));
         }
         break;
@@ -3396,6 +3426,16 @@
                 val->setError(EGL_BAD_PARAMETER, "target renderbuffer cannot be multisampled.");
                 return false;
             }
+
+            bool protectedContentAttrib =
+                (attributes.getAsInt(EGL_PROTECTED_CONTENT_EXT, EGL_FALSE) != EGL_FALSE);
+            if (protectedContentAttrib != renderbuffer->hasProtectedContent())
+            {
+                val->setError(EGL_BAD_ACCESS,
+                              "EGL_PROTECTED_CONTENT_EXT attribute does not match protected state "
+                              "of target.");
+                return false;
+            }
         }
         break;
 
diff --git a/src/libANGLE/validationES.cpp b/src/libANGLE/validationES.cpp
index 9765a9d..98606f6 100644
--- a/src/libANGLE/validationES.cpp
+++ b/src/libANGLE/validationES.cpp
@@ -4583,6 +4583,13 @@
         return false;
     }
 
+    if (imageObject->hasProtectedContent() != context->getState().hasProtectedContent())
+    {
+        context->validationError(GL_INVALID_OPERATION,
+                                 "Mismatch between Image and Context Protected Content state");
+        return false;
+    }
+
     return true;
 }
 
@@ -4621,6 +4628,13 @@
         return false;
     }
 
+    if (imageObject->hasProtectedContent() != context->getState().hasProtectedContent())
+    {
+        context->validationError(GL_INVALID_OPERATION,
+                                 "Mismatch between Image and Context Protected Content state");
+        return false;
+    }
+
     return true;
 }
 
@@ -6549,6 +6563,14 @@
             }
             break;
 
+        case GL_TEXTURE_PROTECTED_EXT:
+            if (!context->getExtensions().protectedTexturesEXT)
+            {
+                context->validationError(GL_INVALID_ENUM, kProtectedTexturesExtensionRequired);
+                return false;
+            }
+            break;
+
         default:
             context->validationError(GL_INVALID_ENUM, kEnumNotSupported);
             return false;
@@ -7251,7 +7273,20 @@
                                          kRobustResourceInitializationExtensionRequired);
                 return false;
             }
+            break;
 
+        case GL_TEXTURE_PROTECTED_EXT:
+            if (!context->getExtensions().protectedTexturesEXT)
+            {
+                context->validationError(GL_INVALID_ENUM, kProtectedTexturesExtensionRequired);
+                return false;
+            }
+            if (ConvertToBool(params[0]) != context->getState().hasProtectedContent())
+            {
+                context->validationError(GL_INVALID_OPERATION,
+                                         "Protected Texture must match Protected Context");
+                return false;
+            }
             break;
 
         default:
diff --git a/src/libANGLE/validationESEXT.cpp b/src/libANGLE/validationESEXT.cpp
index e567816..44797ac 100644
--- a/src/libANGLE/validationESEXT.cpp
+++ b/src/libANGLE/validationESEXT.cpp
@@ -72,6 +72,14 @@
         case GL_DEDICATED_MEMORY_OBJECT_EXT:
             return true;
 
+        case GL_PROTECTED_MEMORY_OBJECT_EXT:
+            if (!context->getExtensions().protectedTexturesEXT)
+            {
+                context->validationError(GL_INVALID_OPERATION, kExtensionNotEnabled);
+                return false;
+            }
+            return true;
+
         default:
             return false;
     }
diff --git a/src/libANGLE/validationESEXT_autogen.h b/src/libANGLE/validationESEXT_autogen.h
index 869ef4d..f0feb1a 100644
--- a/src/libANGLE/validationESEXT_autogen.h
+++ b/src/libANGLE/validationESEXT_autogen.h
@@ -1052,6 +1052,8 @@
                                      GLfloat maxZ,
                                      GLfloat maxW);
 
+// GL_EXT_protected_textures
+
 // GL_EXT_read_format_bgra
 
 // GL_EXT_robustness
diff --git a/src/libGLESv2/entry_points_gles_ext_autogen.cpp b/src/libGLESv2/entry_points_gles_ext_autogen.cpp
index 10bfbbe..b0d9937 100644
--- a/src/libGLESv2/entry_points_gles_ext_autogen.cpp
+++ b/src/libGLESv2/entry_points_gles_ext_autogen.cpp
@@ -5483,6 +5483,8 @@
     }
 }
 
+// GL_EXT_protected_textures
+
 // GL_EXT_read_format_bgra
 
 // GL_EXT_robustness
diff --git a/src/libGLESv2/entry_points_gles_ext_autogen.h b/src/libGLESv2/entry_points_gles_ext_autogen.h
index 169cdc3..146b62b 100644
--- a/src/libGLESv2/entry_points_gles_ext_autogen.h
+++ b/src/libGLESv2/entry_points_gles_ext_autogen.h
@@ -893,6 +893,8 @@
                                                          GLfloat maxZ,
                                                          GLfloat maxW);
 
+// GL_EXT_protected_textures
+
 // GL_EXT_read_format_bgra
 
 // GL_EXT_robustness
diff --git a/src/libGLESv2/libGLESv2_autogen.cpp b/src/libGLESv2/libGLESv2_autogen.cpp
index b7467c9..dfbf718 100644
--- a/src/libGLESv2/libGLESv2_autogen.cpp
+++ b/src/libGLESv2/libGLESv2_autogen.cpp
@@ -4227,6 +4227,8 @@
     return GL_PrimitiveBoundingBoxEXT(minX, minY, minZ, minW, maxX, maxY, maxZ, maxW);
 }
 
+// GL_EXT_protected_textures
+
 // GL_EXT_read_format_bgra
 
 // GL_EXT_robustness
diff --git a/src/libGLESv2/libGLESv2_autogen.def b/src/libGLESv2/libGLESv2_autogen.def
index 8d73795..91b0f32 100644
--- a/src/libGLESv2/libGLESv2_autogen.def
+++ b/src/libGLESv2/libGLESv2_autogen.def
@@ -716,6 +716,8 @@
     ; GL_EXT_primitive_bounding_box
     glPrimitiveBoundingBoxEXT
 
+    ; GL_EXT_protected_textures
+
     ; GL_EXT_read_format_bgra
 
     ; GL_EXT_robustness
diff --git a/src/libGLESv2/libGLESv2_no_capture_autogen.def b/src/libGLESv2/libGLESv2_no_capture_autogen.def
index ee6dfb3..b5d4a5e 100644
--- a/src/libGLESv2/libGLESv2_no_capture_autogen.def
+++ b/src/libGLESv2/libGLESv2_no_capture_autogen.def
@@ -716,6 +716,8 @@
     ; GL_EXT_primitive_bounding_box
     glPrimitiveBoundingBoxEXT
 
+    ; GL_EXT_protected_textures
+
     ; GL_EXT_read_format_bgra
 
     ; GL_EXT_robustness
diff --git a/src/libGLESv2/libGLESv2_with_capture_autogen.def b/src/libGLESv2/libGLESv2_with_capture_autogen.def
index 5a72263..5d684a6 100644
--- a/src/libGLESv2/libGLESv2_with_capture_autogen.def
+++ b/src/libGLESv2/libGLESv2_with_capture_autogen.def
@@ -716,6 +716,8 @@
     ; GL_EXT_primitive_bounding_box
     glPrimitiveBoundingBoxEXT
 
+    ; GL_EXT_protected_textures
+
     ; GL_EXT_read_format_bgra
 
     ; GL_EXT_robustness
diff --git a/src/tests/egl_tests/EGLProtectedContentTest.cpp b/src/tests/egl_tests/EGLProtectedContentTest.cpp
index e98cef9..9137461 100644
--- a/src/tests/egl_tests/EGLProtectedContentTest.cpp
+++ b/src/tests/egl_tests/EGLProtectedContentTest.cpp
@@ -63,6 +63,8 @@
                             clientVersion,
                             EGL_SURFACE_TYPE,
                             (EGL_PBUFFER_BIT | EGL_WINDOW_BIT),
+                            EGL_BIND_TO_TEXTURE_RGBA,
+                            EGL_TRUE,
                             EGL_NONE};
 
         EGLint count = 0;
@@ -88,10 +90,27 @@
 
     bool createPbufferSurface(EGLBoolean isProtected, EGLConfig config, EGLSurface *surface)
     {
-        bool result               = false;
-        EGLint attribsProtected[] = {
-            EGL_WIDTH, kWidth, EGL_HEIGHT, kHeight, EGL_PROTECTED_CONTENT_EXT, EGL_TRUE, EGL_NONE};
-        EGLint attribsUnProtected[] = {EGL_WIDTH, kWidth, EGL_HEIGHT, kHeight, EGL_NONE};
+        bool result                 = false;
+        EGLint attribsProtected[]   = {EGL_WIDTH,
+                                     kWidth,
+                                     EGL_HEIGHT,
+                                     kHeight,
+                                     EGL_TEXTURE_FORMAT,
+                                     EGL_TEXTURE_RGBA,
+                                     EGL_TEXTURE_TARGET,
+                                     EGL_TEXTURE_2D,
+                                     EGL_PROTECTED_CONTENT_EXT,
+                                     EGL_TRUE,
+                                     EGL_NONE};
+        EGLint attribsUnProtected[] = {EGL_WIDTH,
+                                       kWidth,
+                                       EGL_HEIGHT,
+                                       kHeight,
+                                       EGL_TEXTURE_FORMAT,
+                                       EGL_TEXTURE_RGBA,
+                                       EGL_TEXTURE_TARGET,
+                                       EGL_TEXTURE_2D,
+                                       EGL_NONE};
 
         *surface = eglCreatePbufferSurface(mDisplay, config,
                                            (isProtected ? attribsProtected : attribsUnProtected));
@@ -116,8 +135,231 @@
         return result;
     }
 
-    void PbufferTest(bool isProtectedContext, bool isProtectedSurface);
-    void WindowTest(bool isProtectedContext, bool isProtectedSurface);
+    bool createImage(EGLBoolean isProtected,
+                     EGLContext context,
+                     EGLenum target,
+                     EGLClientBuffer buffer,
+                     EGLImage *image)
+    {
+        bool result                  = false;
+        EGLAttrib attribsProtected[] = {EGL_PROTECTED_CONTENT_EXT, EGL_TRUE, EGL_NONE};
+
+        *image = eglCreateImage(mDisplay, context, target, buffer,
+                                (isProtected ? attribsProtected : nullptr));
+        EXPECT_EGL_SUCCESS();
+        result = (*image != EGL_NO_SURFACE);
+        EXPECT_TRUE(result);
+        return result;
+    }
+
+    bool createTexture(EGLBoolean isProtected, GLuint *textureId)
+    {
+        bool result    = false;
+        GLuint texture = 0;
+        glGenTextures(1, &texture);
+        glBindTexture(GL_TEXTURE_2D, texture);
+        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kWidth, kHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE,
+                     nullptr);
+        EXPECT_GL_NO_ERROR();
+        if (isProtected)
+        {
+            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_PROTECTED_EXT, GL_TRUE);
+            // GL_INVALID_OPERATION expected when context is not protected too.
+            GLenum error = glGetError();
+            if (error == GL_INVALID_OPERATION)
+            {
+                return false;
+            }
+        }
+        glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, kWidth, kHeight);
+        EXPECT_GL_NO_ERROR();
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+        result = (texture != 0);
+        EXPECT_TRUE(result);
+        *textureId = texture;
+        return result;
+    }
+
+    bool createTextureFromImage(EGLImage image, GLuint *textureId)
+    {
+        bool result    = false;
+        GLuint texture = 0;
+        glGenTextures(1, &texture);
+        glBindTexture(GL_TEXTURE_2D, texture);
+        glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image);
+        EXPECT_GL_NO_ERROR();
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+        result = (texture != 0);
+        EXPECT_TRUE(result);
+        *textureId = texture;
+        return result;
+    }
+
+    bool createTextureFromPbuffer(EGLSurface pBuffer, GLuint *textureId)
+    {
+        bool result    = false;
+        GLuint texture = 0;
+        glGenTextures(1, &texture);
+        glBindTexture(GL_TEXTURE_2D, texture);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+        EXPECT_GL_NO_ERROR();
+        EXPECT_TRUE(texture != 0);
+        result = eglBindTexImage(mDisplay, pBuffer, EGL_BACK_BUFFER);
+        glViewport(0, 0, kWidth, kHeight);
+        *textureId = texture;
+        return result;
+    }
+
+    bool fillTexture(GLuint textureId, GLColor color)
+    {
+        GLuint pixels[kWidth * kHeight];
+        for (uint32_t i = 0; i < (kWidth * kHeight); i++)
+        {
+            pixels[i] = *(GLuint *)(color.data());
+        }
+        glBindTexture(GL_TEXTURE_2D, textureId);
+        glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, kWidth, kHeight, GL_RGBA, GL_UNSIGNED_BYTE,
+                        (void *)pixels);
+        EXPECT_GL_NO_ERROR();
+        return true;
+    }
+
+    bool renderTexture(GLuint textureId)
+    {
+        const char *kVertexShader   = R"(
+            precision highp float;
+            attribute vec4 position;
+            varying vec2 texcoord;
+
+            void main()
+            {
+                gl_Position = vec4(position.xy, 0.0, 1.0);
+                texcoord = (position.xy * 0.5) + 0.5;
+            }
+        )";
+        const char *kFragmentShader = R"(
+            precision highp float;
+            uniform sampler2D tex;
+            varying vec2 texcoord;
+
+            void main()
+            {
+                gl_FragColor = texture2D(tex, texcoord);
+            }
+        )";
+
+        GLuint program = CompileProgram(kVertexShader, kFragmentShader);
+        glUseProgram(program);
+        glBindTexture(GL_TEXTURE_2D, textureId);
+        glActiveTexture(GL_TEXTURE0);
+        GLint texture2DUniformLocation = glGetUniformLocation(program, "tex");
+        glUniform1i(texture2DUniformLocation, 0);
+        drawQuad(program, "position", 0.5f);
+        glDeleteProgram(program);
+        EXPECT_GL_NO_ERROR();
+        return true;
+    }
+
+    bool createRenderbuffer(GLuint *renderbuffer)
+    {
+        bool result   = false;
+        *renderbuffer = 0;
+        glGenRenderbuffers(1, renderbuffer);
+        glBindRenderbuffer(GL_RENDERBUFFER, *renderbuffer);
+        glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA, kWidth, kHeight);
+        EXPECT_GL_NO_ERROR();
+        result = (*renderbuffer != 0);
+        EXPECT_TRUE(result);
+        return result;
+    }
+
+    bool createRenderbufferFromImage(EGLImage image, GLuint *renderbuffer)
+    {
+        bool result   = false;
+        *renderbuffer = 0;
+        glGenRenderbuffers(1, renderbuffer);
+        glBindRenderbuffer(GL_RENDERBUFFER, *renderbuffer);
+        glEGLImageTargetRenderbufferStorageOES(GL_RENDERBUFFER, image);
+        EXPECT_GL_NO_ERROR();
+        result = (*renderbuffer != 0);
+        EXPECT_TRUE(result);
+        return result;
+    }
+
+    bool createAndroidClientBuffer(bool useProtected,
+                                   bool useRenderbuffer,
+                                   bool useTexture,
+                                   EGLClientBuffer *clientBuffer)
+    {
+        bool result = false;
+        EGLint nativeBufferUsage =
+            0 | (useProtected ? EGL_NATIVE_BUFFER_USAGE_PROTECTED_BIT_ANDROID : 0) |
+            (useRenderbuffer ? EGL_NATIVE_BUFFER_USAGE_RENDERBUFFER_BIT_ANDROID : 0) |
+            (useTexture ? EGL_NATIVE_BUFFER_USAGE_TEXTURE_BIT_ANDROID : 0);
+
+        EGLint attribs[] = {EGL_WIDTH,
+                            kWidth,
+                            EGL_HEIGHT,
+                            kHeight,
+                            EGL_RED_SIZE,
+                            8,
+                            EGL_GREEN_SIZE,
+                            8,
+                            EGL_BLUE_SIZE,
+                            8,
+                            EGL_ALPHA_SIZE,
+                            8,
+                            EGL_NATIVE_BUFFER_USAGE_ANDROID,
+                            nativeBufferUsage,
+                            EGL_NONE};
+
+        *clientBuffer = eglCreateNativeClientBufferANDROID(attribs);
+        EXPECT_EGL_SUCCESS();
+        result = (*clientBuffer != nullptr);
+        EXPECT_TRUE(result);
+        return result;
+    }
+
+    void pbufferTest(bool isProtectedContext, bool isProtectedSurface);
+    void windowTest(bool isProtectedContext, bool isProtectedSurface);
+    void textureTest(bool isProtectedContext, bool isProtectedTexture);
+    void textureFromImageTest(bool isProtectedContext, bool isProtectedTexture);
+    void textureFromPbufferTest(bool isProtectedContext, bool isProtectedTexture);
+    void textureFromAndroidNativeBufferTest(bool isProtectedContext, bool isProtectedTexture);
+
+    void checkSwapBuffersResult(const std::string color,
+                                bool isProtectedContext,
+                                bool isProtectedSurface)
+    {
+        std::this_thread::sleep_for(1s);
+        if (isProtectedContext)
+        {
+            if (isProtectedSurface)
+            {
+                std::cout << "Operator should see color: " << color << std::endl;
+            }
+            else
+            {
+                std::cout << "Operator should see color: BLACK" << std::endl;
+            }
+        }
+        else
+        {
+            if (isProtectedSurface)
+            {
+                std::cout << "Operator should see color: BLACK" << std::endl;
+            }
+            else
+            {
+                std::cout << "Operator should see color: " << color << std::endl;
+            }
+        }
+    }
 
     EGLDisplay mDisplay         = EGL_NO_DISPLAY;
     EGLint mMajorVersion        = 0;
@@ -125,15 +367,13 @@
     static const EGLint kHeight = 16;
 };
 
-void EGLProtectedContentTest::PbufferTest(bool isProtectedContext, bool isProtectedSurface)
+void EGLProtectedContentTest::pbufferTest(bool isProtectedContext, bool isProtectedSurface)
 {
-    if (isProtectedContext || isProtectedSurface)
-    {
-        ANGLE_SKIP_TEST_IF(!IsEGLDisplayExtensionEnabled(mDisplay, "EGL_EXT_protected_content"));
-    }
+    ANGLE_SKIP_TEST_IF(!IsEGLDisplayExtensionEnabled(mDisplay, "EGL_EXT_protected_content"));
 
     EGLConfig config = EGL_NO_CONFIG_KHR;
     EXPECT_TRUE(chooseConfig(&config));
+    ANGLE_SKIP_TEST_IF(config == EGL_NO_CONFIG_KHR);
 
     EGLContext context = EGL_NO_CONTEXT;
     EXPECT_TRUE(createContext(isProtectedContext, config, &context));
@@ -151,6 +391,7 @@
 
     glFinish();
     ASSERT_GL_NO_ERROR() << "glFinish failed";
+    EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
 
     EXPECT_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context));
     ASSERT_EGL_SUCCESS() << "eglMakeCurrent - uncurrent failed.";
@@ -165,18 +406,16 @@
 // Unprotected context with Unprotected PbufferSurface
 TEST_P(EGLProtectedContentTest, UnprotectedContextWithUnprotectedPbufferSurface)
 {
-    PbufferTest(false, false);
+    pbufferTest(false, false);
 }
 
-void EGLProtectedContentTest::WindowTest(bool isProtectedContext, bool isProtectedSurface)
+void EGLProtectedContentTest::windowTest(bool isProtectedContext, bool isProtectedSurface)
 {
-    if (isProtectedContext || isProtectedSurface)
-    {
-        ANGLE_SKIP_TEST_IF(!IsEGLDisplayExtensionEnabled(mDisplay, "EGL_EXT_protected_content"));
-    }
+    ANGLE_SKIP_TEST_IF(!IsEGLDisplayExtensionEnabled(mDisplay, "EGL_EXT_protected_content"));
 
     EGLConfig config = EGL_NO_CONFIG_KHR;
     EXPECT_TRUE(chooseConfig(&config));
+    ANGLE_SKIP_TEST_IF(config == EGL_NO_CONFIG_KHR);
 
     EGLContext context = EGL_NO_CONTEXT;
     EXPECT_TRUE(createContext(isProtectedContext, config, &context));
@@ -199,7 +438,7 @@
     ASSERT_GL_NO_ERROR() << "glClear failed";
     eglSwapBuffers(mDisplay, windowSurface);
     ASSERT_EGL_SUCCESS() << "eglSwapBuffers failed.";
-    std::this_thread::sleep_for(1s);
+    checkSwapBuffersResult("RED", isProtectedContext, isProtectedSurface);
 
     // Green
     glClearColor(0.0, 1.0, 0.0, 1.0);
@@ -207,7 +446,7 @@
     ASSERT_GL_NO_ERROR() << "glClear failed";
     eglSwapBuffers(mDisplay, windowSurface);
     ASSERT_EGL_SUCCESS() << "eglSwapBuffers failed.";
-    std::this_thread::sleep_for(1s);
+    checkSwapBuffersResult("GREEN", isProtectedContext, isProtectedSurface);
 
     // Blue
     glClearColor(0.0, 0.0, 1.0, 1.0);
@@ -215,7 +454,7 @@
     ASSERT_GL_NO_ERROR() << "glClear failed";
     eglSwapBuffers(mDisplay, windowSurface);
     ASSERT_EGL_SUCCESS() << "eglSwapBuffers failed.";
-    std::this_thread::sleep_for(1s);
+    checkSwapBuffersResult("BLUE", isProtectedContext, isProtectedSurface);
 
     EXPECT_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context));
     ASSERT_EGL_SUCCESS() << "eglMakeCurrent - uncurrent failed.";
@@ -232,15 +471,361 @@
 // Unprotected context with Unprotected WindowSurface
 TEST_P(EGLProtectedContentTest, UnprotectedContextWithUnprotectedWindowSurface)
 {
-    std::cout << "Operator should see RED, GREEN, BLUE on screen" << std::endl;
-    WindowTest(false, false);
+    windowTest(false, false);
 }
 
 // Protected context with Protected WindowSurface
 TEST_P(EGLProtectedContentTest, ProtectedContextWithProtectedWindowSurface)
 {
-    std::cout << "Operator should see RED, GREEN, BLUE on screen" << std::endl;
-    WindowTest(true, true);
+    windowTest(true, true);
+}
+
+void EGLProtectedContentTest::textureTest(bool isProtectedContext, bool isProtectedTexture)
+{
+    ANGLE_SKIP_TEST_IF(!IsEGLDisplayExtensionEnabled(mDisplay, "EGL_EXT_protected_content"));
+
+    bool isProtectedSurface = isProtectedTexture;
+
+    EGLConfig config = EGL_NO_CONFIG_KHR;
+    EXPECT_TRUE(chooseConfig(&config));
+    ANGLE_SKIP_TEST_IF(config == EGL_NO_CONFIG_KHR);
+
+    EGLContext context = EGL_NO_CONTEXT;
+    EXPECT_TRUE(createContext(isProtectedContext, config, &context));
+    ASSERT_EGL_SUCCESS() << "eglCreateContext failed.";
+
+    OSWindow *osWindow = OSWindow::New();
+    osWindow->initialize("ProtectedContentTest", kWidth, kHeight);
+    EGLSurface windowSurface          = EGL_NO_SURFACE;
+    EGLBoolean createWinSurfaceResult = createWindowSurface(
+        isProtectedSurface, config, osWindow->getNativeWindow(), &windowSurface);
+    EXPECT_TRUE(createWinSurfaceResult);
+    ASSERT_EGL_SUCCESS() << "eglCreateWindowSurface failed.";
+    glViewport(0, 0, kWidth, kHeight);
+
+    EXPECT_TRUE(eglMakeCurrent(mDisplay, windowSurface, windowSurface, context));
+    ASSERT_EGL_SUCCESS() << "eglMakeCurrent failed.";
+
+    if (IsGLExtensionEnabled("GL_EXT_protected_textures"))
+    {
+        GLuint texture = 0;
+        bool result    = createTexture(isProtectedTexture, &texture);
+        if (isProtectedTexture && !isProtectedContext)
+        {
+            std::cout << "Can't create protected Texture for Unprotected Context" << std::endl;
+            ASSERT_FALSE(result);
+        }
+        else
+        {
+            ASSERT_TRUE(result);
+
+            EXPECT_TRUE(fillTexture(texture, GLColor::red));
+            EXPECT_TRUE(renderTexture(texture));
+
+            eglSwapBuffers(mDisplay, windowSurface);
+            ASSERT_EGL_SUCCESS() << "eglSwapBuffers failed.";
+            checkSwapBuffersResult("RED", isProtectedContext, isProtectedSurface);
+
+            glBindTexture(GL_TEXTURE_2D, 0);
+            glDeleteTextures(1, &texture);
+        }
+    }
+    else
+    {
+        std::cout << "Skipping tests, GL_EXT_protected_textures not supported" << std::endl;
+    }
+
+    EXPECT_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context));
+    ASSERT_EGL_SUCCESS() << "eglMakeCurrent - uncurrent failed.";
+
+    eglDestroySurface(mDisplay, windowSurface);
+    windowSurface = EGL_NO_SURFACE;
+    osWindow->destroy();
+    OSWindow::Delete(&osWindow);
+
+    eglDestroyContext(mDisplay, context);
+    context = EGL_NO_CONTEXT;
+}
+
+// Unprotected context with unprotected texture
+TEST_P(EGLProtectedContentTest, UnprotectedContextWithUnprotectedTexture)
+{
+    textureTest(false, false);
+}
+
+// Protected context with protected texture
+TEST_P(EGLProtectedContentTest, ProtectedContextWithProtectedTexture)
+{
+    textureTest(true, true);
+}
+
+void EGLProtectedContentTest::textureFromImageTest(bool isProtectedContext, bool isProtectedTexture)
+{
+    ANGLE_SKIP_TEST_IF(!IsEGLDisplayExtensionEnabled(mDisplay, "EGL_EXT_protected_content"));
+
+    bool isProtectedSurface = isProtectedTexture;
+
+    EGLConfig config = EGL_NO_CONFIG_KHR;
+    EXPECT_TRUE(chooseConfig(&config));
+    ANGLE_SKIP_TEST_IF(config == EGL_NO_CONFIG_KHR);
+
+    EGLContext context = EGL_NO_CONTEXT;
+    EXPECT_TRUE(createContext(isProtectedContext, config, &context));
+    ASSERT_EGL_SUCCESS() << "eglCreateContext failed.";
+
+    OSWindow *osWindow = OSWindow::New();
+    osWindow->initialize("ProtectedContentTest", kWidth, kHeight);
+    EGLSurface windowSurface          = EGL_NO_SURFACE;
+    EGLBoolean createWinSurfaceResult = createWindowSurface(
+        isProtectedSurface, config, osWindow->getNativeWindow(), &windowSurface);
+    EXPECT_TRUE(createWinSurfaceResult);
+    ASSERT_EGL_SUCCESS() << "eglCreateWindowSurface failed.";
+
+    EXPECT_TRUE(eglMakeCurrent(mDisplay, windowSurface, windowSurface, context));
+    ASSERT_EGL_SUCCESS() << "eglMakeCurrent failed.";
+    glViewport(0, 0, kWidth, kHeight);
+
+    if (IsGLExtensionEnabled("GL_OES_EGL_image") &&
+        IsGLExtensionEnabled("GL_EXT_protected_textures"))
+    {
+        GLuint srcTexture = 0;
+        if (isProtectedTexture && !isProtectedContext)
+        {
+            std::cout << "Can't create protected Texture for Unprotected Context, Skipping"
+                      << std::endl;
+            ASSERT_FALSE(createTexture(isProtectedTexture, &srcTexture));
+        }
+        else
+        {
+            ASSERT_TRUE(createTexture(isProtectedTexture, &srcTexture));
+            EXPECT_TRUE(fillTexture(srcTexture, GLColor::red));
+
+            EGLImage image = EGL_NO_IMAGE;
+            EXPECT_TRUE(createImage(isProtectedTexture, context, EGL_GL_TEXTURE_2D,
+                                    (void *)(static_cast<intptr_t>(srcTexture)), &image));
+
+            GLuint dstTexture = 0;
+            EXPECT_TRUE(createTextureFromImage(image, &dstTexture));
+            EXPECT_TRUE(renderTexture(dstTexture));
+
+            eglSwapBuffers(mDisplay, windowSurface);
+            ASSERT_EGL_SUCCESS() << "eglSwapBuffers failed.";
+            checkSwapBuffersResult("RED", isProtectedContext, isProtectedSurface);
+
+            glBindTexture(GL_TEXTURE_2D, 0);
+            glDeleteTextures(1, &dstTexture);
+            glDeleteTextures(1, &srcTexture);
+
+            eglDestroyImage(mDisplay, image);
+            image = EGL_NO_IMAGE;
+        }
+    }
+    else
+    {
+        std::cout << "Skipping tests, GL_OES_EGL_image or GL_EXT_protected_textures not supported"
+                  << std::endl;
+    }
+
+    EXPECT_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context));
+    ASSERT_EGL_SUCCESS() << "eglMakeCurrent - uncurrent failed.";
+
+    eglDestroySurface(mDisplay, windowSurface);
+    windowSurface = EGL_NO_SURFACE;
+    osWindow->destroy();
+    OSWindow::Delete(&osWindow);
+
+    eglDestroyContext(mDisplay, context);
+    context = EGL_NO_CONTEXT;
+}
+
+// Unprotected context with unprotected texture from EGL image
+TEST_P(EGLProtectedContentTest, UnprotectedContextWithUnprotectedTextureFromImage)
+{
+    textureFromImageTest(false, false);
+}
+
+// Protected context with protected texture from EGL image
+TEST_P(EGLProtectedContentTest, ProtectedContextWithProtectedTextureFromImage)
+{
+    textureFromImageTest(true, true);
+}
+
+void EGLProtectedContentTest::textureFromPbufferTest(bool isProtectedContext,
+                                                     bool isProtectedTexture)
+{
+    ANGLE_SKIP_TEST_IF(!IsEGLDisplayExtensionEnabled(mDisplay, "EGL_EXT_protected_content"));
+
+    bool isProtectedSurface = isProtectedTexture;
+
+    EGLConfig config = EGL_NO_CONFIG_KHR;
+    EXPECT_TRUE(chooseConfig(&config));
+    ANGLE_SKIP_TEST_IF(config == EGL_NO_CONFIG_KHR);
+
+    EGLContext context = EGL_NO_CONTEXT;
+    EXPECT_TRUE(createContext(isProtectedContext, config, &context));
+    ASSERT_EGL_SUCCESS() << "eglCreateContext failed.";
+
+    EGLSurface pBufferSurface = EGL_NO_SURFACE;
+    EXPECT_TRUE(createPbufferSurface(isProtectedSurface, config, &pBufferSurface));
+    ASSERT_EGL_SUCCESS() << "eglCreatePbufferSurface failed.";
+
+    EXPECT_TRUE(eglMakeCurrent(mDisplay, pBufferSurface, pBufferSurface, context));
+    ASSERT_EGL_SUCCESS() << "eglMakeCurrent failed.";
+
+    glViewport(0, 0, kWidth, kHeight);
+    glClearColor(1.0, 0.0, 0.0, 1.0);
+    glClear(GL_COLOR_BUFFER_BIT);
+
+    glFinish();
+    ASSERT_GL_NO_ERROR() << "glFinish failed";
+
+    OSWindow *osWindow = OSWindow::New();
+    osWindow->initialize("ProtectedContentTest", kWidth, kHeight);
+    EGLSurface windowSurface          = EGL_NO_SURFACE;
+    EGLBoolean createWinSurfaceResult = createWindowSurface(
+        isProtectedSurface, config, osWindow->getNativeWindow(), &windowSurface);
+    EXPECT_TRUE(createWinSurfaceResult);
+    ASSERT_EGL_SUCCESS() << "eglCreateWindowSurface failed.";
+
+    EXPECT_TRUE(eglMakeCurrent(mDisplay, windowSurface, windowSurface, context));
+    ASSERT_EGL_SUCCESS() << "eglMakeCurrent failed.";
+    glViewport(0, 0, kWidth, kHeight);
+
+    if (IsGLExtensionEnabled("GL_EXT_protected_textures"))
+    {
+        GLuint texture = 0;
+        EXPECT_TRUE(createTextureFromPbuffer(pBufferSurface, &texture));
+        EXPECT_TRUE(renderTexture(texture));
+
+        eglSwapBuffers(mDisplay, windowSurface);
+        ASSERT_EGL_SUCCESS() << "eglSwapBuffers failed.";
+        checkSwapBuffersResult("RED", isProtectedContext, isProtectedTexture);
+
+        eglReleaseTexImage(mDisplay, pBufferSurface, EGL_BACK_BUFFER);
+        glDeleteTextures(1, &texture);
+    }
+    else
+    {
+        std::cout << "Skipping tests, GL_EXT_protected_textures not supported" << std::endl;
+    }
+
+    EXPECT_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context));
+    ASSERT_EGL_SUCCESS() << "eglMakeCurrent - uncurrent failed.";
+
+    eglDestroySurface(mDisplay, windowSurface);
+    windowSurface = EGL_NO_SURFACE;
+    osWindow->destroy();
+    OSWindow::Delete(&osWindow);
+
+    eglDestroySurface(mDisplay, pBufferSurface);
+    pBufferSurface = EGL_NO_SURFACE;
+
+    eglDestroyContext(mDisplay, context);
+    context = EGL_NO_CONTEXT;
+}
+
+// Unprotected context with unprotected texture from BindTex of PBufferSurface
+TEST_P(EGLProtectedContentTest, UnprotectedContextWithUnprotectedTextureFromPBuffer)
+{
+    textureFromPbufferTest(false, false);
+}
+
+// Protected context with protected texture from BindTex of PBufferSurface
+TEST_P(EGLProtectedContentTest, ProtectedContextWithProtectedTextureFromPbuffer)
+{
+    textureFromPbufferTest(true, true);
+}
+
+void EGLProtectedContentTest::textureFromAndroidNativeBufferTest(bool isProtectedContext,
+                                                                 bool isProtectedTexture)
+{
+    ANGLE_SKIP_TEST_IF(!IsEGLDisplayExtensionEnabled(mDisplay, "EGL_EXT_protected_content"));
+    ANGLE_SKIP_TEST_IF(
+        !IsEGLDisplayExtensionEnabled(mDisplay, "EGL_ANDROID_get_native_client_buffer"));
+    ANGLE_SKIP_TEST_IF(!IsEGLDisplayExtensionEnabled(mDisplay, "EGL_ANDROID_image_native_buffer"));
+
+    bool isProtectedSurface = isProtectedTexture;
+
+    EGLConfig config = EGL_NO_CONFIG_KHR;
+    EXPECT_TRUE(chooseConfig(&config));
+    ANGLE_SKIP_TEST_IF(config == EGL_NO_CONFIG_KHR);
+
+    EGLContext context = EGL_NO_CONTEXT;
+    EXPECT_TRUE(createContext(isProtectedContext, config, &context));
+    ASSERT_EGL_SUCCESS() << "eglCreateContext failed.";
+
+    OSWindow *osWindow = OSWindow::New();
+    osWindow->initialize("ProtectedContentTest", kWidth, kHeight);
+    EGLSurface windowSurface          = EGL_NO_SURFACE;
+    EGLBoolean createWinSurfaceResult = createWindowSurface(
+        isProtectedSurface, config, osWindow->getNativeWindow(), &windowSurface);
+    EXPECT_TRUE(createWinSurfaceResult);
+    ASSERT_EGL_SUCCESS() << "eglCreateWindowSurface failed.";
+
+    EXPECT_TRUE(eglMakeCurrent(mDisplay, windowSurface, windowSurface, context));
+    ASSERT_EGL_SUCCESS() << "eglMakeCurrent failed.";
+    glViewport(0, 0, kWidth, kHeight);
+
+    if (IsGLExtensionEnabled("GL_EXT_protected_textures"))
+    {
+        EGLClientBuffer clientBuffer = nullptr;
+        EXPECT_TRUE(createAndroidClientBuffer(isProtectedTexture, false, true, &clientBuffer));
+
+        EGLImage image = EGL_NO_IMAGE;
+        EXPECT_TRUE(createImage(isProtectedTexture, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
+                                clientBuffer, &image));
+
+        GLuint texture = 0;
+        if (isProtectedTexture && !isProtectedContext)
+        {
+            std::cout << "Can't create protected Texture for Unprotected Context, Skipping"
+                      << std::endl;
+            ASSERT_FALSE(createTextureFromImage(image, &texture));
+        }
+        else
+        {
+            EXPECT_TRUE(createTextureFromImage(image, &texture));
+            EXPECT_TRUE(fillTexture(texture, GLColor::red));
+            EXPECT_TRUE(renderTexture(texture));
+
+            eglSwapBuffers(mDisplay, windowSurface);
+            ASSERT_EGL_SUCCESS() << "eglSwapBuffers failed.";
+            checkSwapBuffersResult("RED", isProtectedContext, isProtectedTexture);
+
+            glBindTexture(GL_TEXTURE_2D, 0);
+            glDeleteTextures(1, &texture);
+
+            eglDestroyImage(mDisplay, image);
+            image = EGL_NO_IMAGE;
+        }
+    }
+    else
+    {
+        std::cout << "Skipping tests, GL_EXT_protected_textures not supported" << std::endl;
+    }
+
+    EXPECT_TRUE(eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, context));
+    ASSERT_EGL_SUCCESS() << "eglMakeCurrent - uncurrent failed.";
+
+    eglDestroySurface(mDisplay, windowSurface);
+    windowSurface = EGL_NO_SURFACE;
+    osWindow->destroy();
+    OSWindow::Delete(&osWindow);
+
+    eglDestroyContext(mDisplay, context);
+    context = EGL_NO_CONTEXT;
+}
+
+// Unprotected context with unprotected texture from EGL image from Android native buffer
+TEST_P(EGLProtectedContentTest, UnprotectedContextWithUnprotectedTextureFromAndroidNativeBuffer)
+{
+    textureFromAndroidNativeBufferTest(false, false);
+}
+
+// Protected context with protected texture from EGL image from Android native buffer
+TEST_P(EGLProtectedContentTest, ProtectedContextWithProtectedTextureFromAndroidNativeBuffer)
+{
+    textureFromAndroidNativeBufferTest(true, true);
 }
 
 ANGLE_INSTANTIATE_TEST(EGLProtectedContentTest,