diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java
index 31d759ea92e6262d8d38456d456dde217f357938..18080e4478fc9bc289c52bc5acbff1018bf9d237 100644
--- a/core/java/android/view/DisplayEventReceiver.java
+++ b/core/java/android/view/DisplayEventReceiver.java
@@ -286,6 +286,16 @@ public abstract class DisplayEventReceiver {
             long renderPeriod) {
     }
 
+    /**
+     * Called when a display hdcp levels change event is received.
+     *
+     * @param physicalDisplayId Stable display ID that uniquely describes a (display, port) pair.
+     * @param connectedLevel the new connected HDCP level
+     * @param maxLevel the maximum HDCP level
+     */
+    public void onHdcpLevelsChanged(long physicalDisplayId, int connectedLevel, int maxLevel) {
+    }
+
     /**
      * Represents a mapping between a UID and an override frame rate
      */
@@ -374,4 +384,11 @@ public abstract class DisplayEventReceiver {
         onFrameRateOverridesChanged(timestampNanos, physicalDisplayId, overrides);
     }
 
+    // Called from native code.
+    @SuppressWarnings("unused")
+    private void dispatchHdcpLevelsChanged(long physicalDisplayId, int connectedLevel,
+            int maxLevel) {
+        onHdcpLevelsChanged(physicalDisplayId, connectedLevel, maxLevel);
+    }
+
 }
diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp
index fef8ad7499f7ac8856da112e664201e1e2b53c79..f007cc5a23bd1cfbd8a4c011b4f980b73d5ac60d 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -41,6 +41,7 @@ static struct {
     jmethodID dispatchHotplugConnectionError;
     jmethodID dispatchModeChanged;
     jmethodID dispatchFrameRateOverrides;
+    jmethodID dispatchHdcpLevelsChanged;
 
     struct {
         jclass clazz;
@@ -96,6 +97,8 @@ private:
     void dispatchFrameRateOverrides(nsecs_t timestamp, PhysicalDisplayId displayId,
                                     std::vector<FrameRateOverride> overrides) override;
     void dispatchNullEvent(nsecs_t timestamp, PhysicalDisplayId displayId) override {}
+    void dispatchHdcpLevelsChanged(PhysicalDisplayId displayId, int connectedLevel,
+                                   int maxLevel) override;
 };
 
 NativeDisplayEventReceiver::NativeDisplayEventReceiver(JNIEnv* env, jobject receiverWeak,
@@ -294,6 +297,22 @@ void NativeDisplayEventReceiver::dispatchFrameRateOverrides(
     mMessageQueue->raiseAndClearException(env, "dispatchModeChanged");
 }
 
+void NativeDisplayEventReceiver::dispatchHdcpLevelsChanged(PhysicalDisplayId displayId,
+                                                           int connectedLevel, int maxLevel) {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+
+    ScopedLocalRef<jobject> receiverObj(env, GetReferent(env, mReceiverWeakGlobal));
+    if (receiverObj.get()) {
+        ALOGV("receiver %p ~ Invoking hdcp levels changed handler.", this);
+        env->CallVoidMethod(receiverObj.get(),
+                            gDisplayEventReceiverClassInfo.dispatchHdcpLevelsChanged,
+                            displayId.value, connectedLevel, maxLevel);
+        ALOGV("receiver %p ~ Returned from hdcp levels changed handler.", this);
+    }
+
+    mMessageQueue->raiseAndClearException(env, "dispatchHdcpLevelsChanged");
+}
+
 static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak, jobject vsyncEventDataWeak,
                         jobject messageQueueObj, jint vsyncSource, jint eventRegistration,
                         jlong layerHandle) {
@@ -385,6 +404,9 @@ int register_android_view_DisplayEventReceiver(JNIEnv* env) {
             GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz,
                              "dispatchFrameRateOverrides",
                              "(JJ[Landroid/view/DisplayEventReceiver$FrameRateOverride;)V");
+    gDisplayEventReceiverClassInfo.dispatchHdcpLevelsChanged =
+            GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchHdcpLevelsChanged",
+                             "(JII)V");
 
     jclass frameRateOverrideClazz =
             FindClassOrDie(env, "android/view/DisplayEventReceiver$FrameRateOverride");
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 22898a65c5defe0179e6efd45892dc6ea0d0478d..25576ce9efd6174a022059cbcb5c4497eaff9c06 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -25,6 +25,7 @@ import android.content.Context;
 import android.content.res.Resources;
 import android.hardware.display.DisplayManagerInternal.DisplayOffloadSession;
 import android.hardware.sidekick.SidekickInternal;
+import android.media.MediaDrm;
 import android.os.Build;
 import android.os.Handler;
 import android.os.IBinder;
@@ -242,6 +243,10 @@ final class LocalDisplayAdapter extends DisplayAdapter {
         private SurfaceControl.DisplayMode mActiveSfDisplayMode;
         // The active display vsync period in SurfaceFlinger
         private float mActiveRenderFrameRate;
+        // The current HDCP level supported by the display, 0 indicates unset
+        // values are defined in hardware/interfaces/drm/aidl/android/hardware/drm/HdcpLevel.aidl
+        private int mConnectedHdcpLevel;
+
         private DisplayEventReceiver.FrameRateOverride[] mFrameRateOverrides =
                 new DisplayEventReceiver.FrameRateOverride[0];
 
@@ -675,8 +680,9 @@ final class LocalDisplayAdapter extends DisplayAdapter {
                 mInfo.yDpi = mActiveSfDisplayMode.yDpi;
                 mInfo.deviceProductInfo = mStaticDisplayInfo.deviceProductInfo;
 
-                // Assume that all built-in displays that have secure output (eg. HDCP) also
-                // support compositing from gralloc protected buffers.
+                if (mConnectedHdcpLevel != 0) {
+                    mStaticDisplayInfo.secure = mConnectedHdcpLevel >= MediaDrm.HDCP_V1;
+                }
                 if (mStaticDisplayInfo.secure) {
                     mInfo.flags = DisplayDeviceInfo.FLAG_SECURE
                             | DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS;
@@ -1093,6 +1099,12 @@ final class LocalDisplayAdapter extends DisplayAdapter {
             }
         }
 
+        public void onHdcpLevelsChangedLocked(int connectedLevel, int maxLevel) {
+            if (updateHdcpLevelsLocked(connectedLevel, maxLevel)) {
+                updateDeviceInfoLocked();
+            }
+        }
+
         public boolean updateActiveModeLocked(int activeSfModeId, float renderFrameRate) {
             if (mActiveSfDisplayMode.id == activeSfModeId
                     && mActiveRenderFrameRate == renderFrameRate) {
@@ -1118,6 +1130,22 @@ final class LocalDisplayAdapter extends DisplayAdapter {
             return true;
         }
 
+        public boolean updateHdcpLevelsLocked(int connectedLevel, int maxLevel) {
+            if (connectedLevel > maxLevel) {
+                Slog.w(TAG, "HDCP connected level: " + connectedLevel
+                        + " is larger than max level: " + maxLevel
+                        + ", ignoring request.");
+                return false;
+            }
+
+            if (mConnectedHdcpLevel == connectedLevel) {
+                return false;
+            }
+
+            mConnectedHdcpLevel = connectedLevel;
+            return true;
+        }
+
         public void requestColorModeLocked(int colorMode) {
             if (mActiveColorMode == colorMode) {
                 return;
@@ -1387,6 +1415,7 @@ final class LocalDisplayAdapter extends DisplayAdapter {
                 long renderPeriod);
         void onFrameRateOverridesChanged(long timestampNanos, long physicalDisplayId,
                 DisplayEventReceiver.FrameRateOverride[] overrides);
+        void onHdcpLevelsChanged(long physicalDisplayId, int connectedLevel, int maxLevel);
 
     }
 
@@ -1420,6 +1449,11 @@ final class LocalDisplayAdapter extends DisplayAdapter {
                 DisplayEventReceiver.FrameRateOverride[] overrides) {
             mListener.onFrameRateOverridesChanged(timestampNanos, physicalDisplayId, overrides);
         }
+
+        @Override
+        public void onHdcpLevelsChanged(long physicalDisplayId, int connectedLevel, int maxLevel) {
+            mListener.onHdcpLevelsChanged(physicalDisplayId, connectedLevel, maxLevel);
+        }
     }
 
     private final class LocalDisplayEventListener implements DisplayEventListener {
@@ -1489,6 +1523,26 @@ final class LocalDisplayAdapter extends DisplayAdapter {
                 device.onFrameRateOverridesChanged(overrides);
             }
         }
+
+        @Override
+        public void onHdcpLevelsChanged(long physicalDisplayId, int connectedLevel, int maxLevel) {
+            if (DEBUG) {
+                Slog.d(TAG, "onHdcpLevelsChanged(physicalDisplayId=" + physicalDisplayId
+                        + ", connectedLevel=" + connectedLevel + ", maxLevel=" + maxLevel + ")");
+            }
+            synchronized (getSyncRoot()) {
+                LocalDisplayDevice device = mDevices.get(physicalDisplayId);
+                if (device == null) {
+                    if (DEBUG) {
+                        Slog.d(TAG, "Received hdcp levels change for unhandled physical display: "
+                                + "physicalDisplayId=" + physicalDisplayId);
+                    }
+                    return;
+                }
+
+                device.onHdcpLevelsChangedLocked(connectedLevel, maxLevel);
+            }
+        }
     }
 
     @VisibleForTesting