DisplayManager: Add color sampling hardware query

Add calls to SurfaceControl JNI interface that allow
the DisplayManager to drive color histogram functionality.

Fixes: 112756444

Test: Boot
Test: additional test in 'atest FrameworksServicesTests:DisplayManagerServiceTest'
Change-Id: Ifa46dab53b09db62da79ad82e9687d9155ddc6da
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 9d61f02..1af9cde 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -16,6 +16,7 @@
 
 package android.hardware.display;
 
+import android.annotation.Nullable;
 import android.hardware.SensorManager;
 import android.os.Handler;
 import android.os.PowerManager;
@@ -195,6 +196,44 @@
     public abstract void onOverlayChanged();
 
     /**
+     * Get the attributes available for display color sampling.
+     * @param displayId id of the display to collect the sample from.
+     *
+     * @return The attributes the display supports, or null if sampling is not supported.
+     */
+    @Nullable
+    public abstract DisplayedContentSamplingAttributes getDisplayedContentSamplingAttributes(
+            int displayId);
+
+    /**
+     * Enable or disable the collection of color samples.
+     *
+     * @param displayId id of the display to collect the sample from.
+     * @param componentMask a bitmask of the color channels to collect samples for, or zero for all
+     *                      available.
+     * @param maxFrames maintain a ringbuffer of the last maxFrames.
+     * @param enable True to enable, False to disable.
+     *
+     * @return True if sampling was enabled, false if failure.
+     */
+    public abstract boolean setDisplayedContentSamplingEnabled(
+            int displayId, boolean enable, int componentMask, int maxFrames);
+
+    /**
+     * Accesses the color histogram statistics of displayed frames on devices that support sampling.
+     *
+     * @param displayId id of the display to collect the sample from
+     * @param maxFrames limit the statistics to the last maxFrames number of frames.
+     * @param timestamp discard statistics that were collected prior to timestamp, where timestamp
+     *                  is given as CLOCK_MONOTONIC.
+     * @return The statistics representing a histogram of the color distribution of the frames
+     *         displayed on-screen, or null if sampling is not supported.
+    */
+    @Nullable
+    public abstract DisplayedContentSample getDisplayedContentSample(
+            int displayId, long maxFrames, long timestamp);
+
+    /**
      * Describes the requested power state of the display.
      *
      * This object is intended to describe the general characteristics of the
diff --git a/core/java/android/hardware/display/DisplayedContentSample.java b/core/java/android/hardware/display/DisplayedContentSample.java
new file mode 100644
index 0000000..0610377
--- /dev/null
+++ b/core/java/android/hardware/display/DisplayedContentSample.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.display;
+
+/**
+ * @hide
+ */
+public final class DisplayedContentSample {
+    private long mNumFrames;
+    private long[] mSamplesComponent0;
+    private long[] mSamplesComponent1;
+    private long[] mSamplesComponent2;
+    private long[] mSamplesComponent3;
+
+    /**
+     * Construct an object representing a color histogram of pixels that were displayed on screen.
+     *
+     * @param numFrames The number of frames represented by this sample.
+     * @param mSamplesComponent0 is a histogram counting how many times a pixel of a given value
+     * was displayed onscreen for FORMAT_COMPONENT_0. The buckets of the histogram are evenly
+     * weighted, the number of buckets is device specific.
+     * eg, for RGBA_8888, if sampleComponent0 is {10, 6, 4, 1} this means that 10 red pixels were
+     * displayed onscreen in range 0x00->0x3F, 6 red pixels were displayed onscreen in range
+     * 0x40->0x7F, etc.
+     * @param mSamplesComponent1 is the same sample definition as sampleComponent0, but for the
+     * second component of format.
+     * @param mSamplesComponent2 is the same sample definition as sampleComponent0, but for the
+     * third component of format.
+     * @param mSamplesComponent3 is the same sample definition as sampleComponent0, but for the
+     * fourth component of format.
+     */
+    public DisplayedContentSample(long numFrames,
+            long[] sampleComponent0,
+            long[] sampleComponent1,
+            long[] sampleComponent2,
+            long[] sampleComponent3) {
+        mNumFrames = numFrames;
+        mSamplesComponent0 = sampleComponent0;
+        mSamplesComponent1 = sampleComponent1;
+        mSamplesComponent2 = sampleComponent2;
+        mSamplesComponent3 = sampleComponent3;
+    }
+
+    public enum ColorComponent {
+        CHANNEL0,
+        CHANNEL1,
+        CHANNEL2,
+        CHANNEL3,
+    }
+
+    /**
+     * Returns a color histogram according to component channel.
+     *
+     * @param component the component to return, according to the PixelFormat ordering
+     * (eg, for RGBA, CHANNEL0 is R, CHANNEL1 is G, etc).
+     *
+     * @return an evenly weighted histogram counting how many times a pixel was
+     *         displayed onscreen that fell into the corresponding bucket, with the first entry
+     *         corresponding to the normalized 0.0 value, and the last corresponding to the 1.0
+     *         value for that PixelFormat component.
+     */
+    public long[] getSampleComponent(ColorComponent component) {
+        switch (component) {
+            case CHANNEL0: return mSamplesComponent0;
+            case CHANNEL1: return mSamplesComponent1;
+            case CHANNEL2: return mSamplesComponent2;
+            case CHANNEL3: return mSamplesComponent3;
+            default: throw new ArrayIndexOutOfBoundsException();
+        }
+    }
+
+    /**
+     * Return the number of frames this sample was collected over.
+     *
+     * @return  the number of frames that this sample was collected over.
+     */
+    public long getNumFrames() {
+        return mNumFrames;
+    }
+}
diff --git a/core/java/android/hardware/display/DisplayedContentSamplingAttributes.java b/core/java/android/hardware/display/DisplayedContentSamplingAttributes.java
new file mode 100644
index 0000000..aad68d9
--- /dev/null
+++ b/core/java/android/hardware/display/DisplayedContentSamplingAttributes.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.display;
+
+/**
+ * @hide
+ */
+public final class DisplayedContentSamplingAttributes {
+    private int mPixelFormat;
+    private int mDataspace;
+    private int mComponentMask;
+
+    /* Creates the attributes reported by the display hardware about what capabilities
+     * are present.
+     *
+     * NOTE: the format and ds constants must match the values from graphics/common/x.x/types.hal
+     * @param format the format that the display hardware samples in.
+     * @param ds the dataspace in use when sampling.
+     * @param componentMask a mask of which of the format components are supported.
+    */
+    public DisplayedContentSamplingAttributes(int format, int ds, int componentMask) {
+        mPixelFormat = format;
+        mDataspace = ds;
+        mComponentMask = componentMask;
+    }
+
+    /* Returns the pixel format that the display hardware uses when sampling.
+     *
+     * NOTE: the returned constant matches the values from graphics/common/x.x/types.hal
+     * @return the format that the samples were collected in.
+     */
+    public int getPixelFormat() {
+        return mPixelFormat;
+    }
+
+    /* Returns the dataspace that the display hardware uses when sampling.
+     *
+     * NOTE: the returned constant matches the values from graphics/common/x.x/types.hal
+     * @return the dataspace that the samples were collected in.
+     */
+    public int getDataspace() {
+        return mDataspace;
+    }
+
+    /* Returns a mask of which components can be collected by the sampling engine.
+     *
+     * @return a mask of the components which are supported by the engine. The lowest
+     * bit corresponds to the lowest component (ie, 0x1 corresponds to A for RGBA).
+     */
+    public int getComponentMask() {
+        return mComponentMask;
+    }
+}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index ab01085..a006e5d 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -36,6 +36,8 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
+import android.hardware.display.DisplayedContentSample;
+import android.hardware.display.DisplayedContentSamplingAttributes;
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -129,6 +131,12 @@
             int width, int height);
     private static native SurfaceControl.PhysicalDisplayInfo[] nativeGetDisplayConfigs(
             IBinder displayToken);
+    private static native DisplayedContentSamplingAttributes
+            nativeGetDisplayedContentSamplingAttributes(IBinder displayToken);
+    private static native boolean nativeSetDisplayedContentSamplingEnabled(IBinder displayToken,
+            boolean enable, int componentMask, int maxFrames);
+    private static native DisplayedContentSample nativeGetDisplayedContentSample(
+            IBinder displayToken, long numFrames, long timestamp);
     private static native int nativeGetActiveConfig(IBinder displayToken);
     private static native boolean nativeSetActiveConfig(IBinder displayToken, int id);
     private static native int[] nativeGetDisplayColorModes(IBinder displayToken);
@@ -1164,6 +1172,45 @@
         return nativeGetActiveConfig(displayToken);
     }
 
+    /**
+     * @hide
+     */
+    public static DisplayedContentSamplingAttributes getDisplayedContentSamplingAttributes(
+            IBinder displayToken) {
+        if (displayToken == null) {
+            throw new IllegalArgumentException("displayToken must not be null");
+        }
+        return nativeGetDisplayedContentSamplingAttributes(displayToken);
+    }
+
+    /**
+     * @hide
+     */
+    public static boolean setDisplayedContentSamplingEnabled(
+            IBinder displayToken, boolean enable, int componentMask, int maxFrames) {
+        if (displayToken == null) {
+            throw new IllegalArgumentException("displayToken must not be null");
+        }
+        final int maxColorComponents = 4;
+        if ((componentMask >> maxColorComponents) != 0) {
+            throw new IllegalArgumentException("invalid componentMask when enabling sampling");
+        }
+        return nativeSetDisplayedContentSamplingEnabled(
+                displayToken, enable, componentMask, maxFrames);
+    }
+
+    /**
+     * @hide
+     */
+    public static DisplayedContentSample getDisplayedContentSample(
+            IBinder displayToken, long maxFrames, long timestamp) {
+        if (displayToken == null) {
+            throw new IllegalArgumentException("displayToken must not be null");
+        }
+        return nativeGetDisplayedContentSample(displayToken, maxFrames, timestamp);
+    }
+
+
     public static boolean setActiveConfig(IBinder displayToken, int id) {
         if (displayToken == null) {
             throw new IllegalArgumentException("displayToken must not be null");
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index ea6e017..c745c16 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -37,6 +37,7 @@
 #include <stdio.h>
 #include <system/graphics.h>
 #include <ui/DisplayInfo.h>
+#include <ui/DisplayedFrameStats.h>
 #include <ui/FrameStats.h>
 #include <ui/GraphicTypes.h>
 #include <ui/HdrCapabilities.h>
@@ -97,6 +98,16 @@
     jmethodID builder;
 } gGraphicBufferClassInfo;
 
+static struct {
+    jclass clazz;
+    jmethodID ctor;
+} gDisplayedContentSampleClassInfo;
+
+static struct {
+    jclass clazz;
+    jmethodID ctor;
+} gDisplayedContentSamplingAttributesClassInfo;
+
 // ----------------------------------------------------------------------------
 
 static jlong nativeCreateTransaction(JNIEnv* env, jclass clazz) {
@@ -398,6 +409,73 @@
     return javaObjectForIBinder(env, token);
 }
 
+static jobject nativeGetDisplayedContentSamplingAttributes(JNIEnv* env, jclass clazz,
+        jobject tokenObj) {
+    sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
+
+    ui::PixelFormat format;
+    ui::Dataspace dataspace;
+    uint8_t componentMask;
+    status_t err = SurfaceComposerClient::getDisplayedContentSamplingAttributes(
+            token, &format, &dataspace, &componentMask);
+    if (err != OK) {
+        return nullptr;
+    }
+    return env->NewObject(gDisplayedContentSamplingAttributesClassInfo.clazz,
+                          gDisplayedContentSamplingAttributesClassInfo.ctor,
+                          format, dataspace, componentMask);
+}
+
+static jboolean nativeSetDisplayedContentSamplingEnabled(JNIEnv* env, jclass clazz,
+        jobject tokenObj, jboolean enable, jint componentMask, jint maxFrames) {
+    sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
+    return SurfaceComposerClient::setDisplayContentSamplingEnabled(
+            token, enable, componentMask, maxFrames);
+}
+
+static jobject nativeGetDisplayedContentSample(JNIEnv* env, jclass clazz, jobject tokenObj,
+    jlong maxFrames, jlong timestamp) {
+    sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
+
+    DisplayedFrameStats stats;
+    status_t err = SurfaceComposerClient::getDisplayedContentSample(
+            token, maxFrames, timestamp, &stats);
+    if (err != OK) {
+        return nullptr;
+    }
+
+    jlongArray histogramComponent0 = env->NewLongArray(stats.component_0_sample.size());
+    jlongArray histogramComponent1 = env->NewLongArray(stats.component_1_sample.size());
+    jlongArray histogramComponent2 = env->NewLongArray(stats.component_2_sample.size());
+    jlongArray histogramComponent3 = env->NewLongArray(stats.component_3_sample.size());
+    if ((histogramComponent0 == nullptr) ||
+        (histogramComponent1 == nullptr) ||
+        (histogramComponent2 == nullptr) ||
+        (histogramComponent3 == nullptr)) {
+        return JNI_FALSE;
+    }
+
+    env->SetLongArrayRegion(histogramComponent0, 0,
+            stats.component_0_sample.size(),
+            reinterpret_cast<jlong*>(stats.component_0_sample.data()));
+    env->SetLongArrayRegion(histogramComponent1, 0,
+            stats.component_1_sample.size(),
+            reinterpret_cast<jlong*>(stats.component_1_sample.data()));
+    env->SetLongArrayRegion(histogramComponent2, 0,
+            stats.component_2_sample.size(),
+            reinterpret_cast<jlong*>(stats.component_2_sample.data()));
+    env->SetLongArrayRegion(histogramComponent3, 0,
+            stats.component_3_sample.size(),
+            reinterpret_cast<jlong*>(stats.component_3_sample.data()));
+    return env->NewObject(gDisplayedContentSampleClassInfo.clazz,
+                          gDisplayedContentSampleClassInfo.ctor,
+                          stats.numFrames,
+                          histogramComponent0,
+                          histogramComponent1,
+                          histogramComponent2,
+                          histogramComponent3);
+}
+
 static jobject nativeCreateDisplay(JNIEnv* env, jclass clazz, jstring nameObj,
         jboolean secure) {
     ScopedUtfChars name(env, nameObj);
@@ -955,6 +1033,14 @@
             (void*)nativeCaptureLayers },
     {"nativeSetInputWindowInfo", "(JJLandroid/view/InputWindowHandle;)V",
      (void*)nativeSetInputWindowInfo },
+    {"nativeGetDisplayedContentSamplingAttributes",
+            "(Landroid/os/IBinder;)Landroid/hardware/display/DisplayedContentSamplingAttributes;",
+            (void*)nativeGetDisplayedContentSamplingAttributes },
+    {"nativeSetDisplayedContentSamplingEnabled", "(Landroid/os/IBinder;ZII)Z",
+            (void*)nativeSetDisplayedContentSamplingEnabled },
+    {"nativeGetDisplayedContentSample",
+            "(Landroid/os/IBinder;JJ)Landroid/hardware/display/DisplayedContentSample;",
+            (void*)nativeGetDisplayedContentSample },
 };
 
 int register_android_view_SurfaceControl(JNIEnv* env)
@@ -1009,6 +1095,18 @@
     gGraphicBufferClassInfo.builder = GetStaticMethodIDOrDie(env, graphicsBufferClazz,
             "createFromExisting", "(IIIIJ)Landroid/graphics/GraphicBuffer;");
 
+    jclass displayedContentSampleClazz = FindClassOrDie(env,
+            "android/hardware/display/DisplayedContentSample");
+    gDisplayedContentSampleClassInfo.clazz = MakeGlobalRefOrDie(env, displayedContentSampleClazz);
+    gDisplayedContentSampleClassInfo.ctor = GetMethodIDOrDie(env,
+            displayedContentSampleClazz, "<init>", "(J[J[J[J[J)V");
+
+    jclass displayedContentSamplingAttributesClazz = FindClassOrDie(env,
+            "android/hardware/display/DisplayedContentSamplingAttributes");
+    gDisplayedContentSamplingAttributesClassInfo.clazz = MakeGlobalRefOrDie(env,
+            displayedContentSamplingAttributesClazz);
+    gDisplayedContentSamplingAttributesClassInfo.ctor = GetMethodIDOrDie(env,
+            displayedContentSamplingAttributesClazz, "<init>", "(III)V");
     return err;
 }
 
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 360a7d1..cf8d21b 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -46,6 +46,8 @@
 import android.hardware.display.DisplayManagerInternal;
 import android.hardware.display.DisplayManagerInternal.DisplayTransactionListener;
 import android.hardware.display.DisplayViewport;
+import android.hardware.display.DisplayedContentSample;
+import android.hardware.display.DisplayedContentSamplingAttributes;
 import android.hardware.display.IDisplayManager;
 import android.hardware.display.IDisplayManagerCallback;
 import android.hardware.display.IVirtualDisplayCallback;
@@ -1241,6 +1243,29 @@
         }
     }
 
+    @VisibleForTesting
+    DisplayedContentSamplingAttributes getDisplayedContentSamplingAttributesInternal(
+            int displayId) {
+        IBinder displayToken = SurfaceControl.getBuiltInDisplay(displayId);
+        return SurfaceControl.getDisplayedContentSamplingAttributes(displayToken);
+    }
+
+    @VisibleForTesting
+    boolean setDisplayedContentSamplingEnabledInternal(
+            int displayId, boolean enable, int componentMask, int maxFrames) {
+        IBinder displayToken = SurfaceControl.getBuiltInDisplay(displayId);
+        return SurfaceControl.setDisplayedContentSamplingEnabled(
+                displayToken, enable, componentMask, maxFrames);
+    }
+
+    @VisibleForTesting
+    DisplayedContentSample getDisplayedContentSampleInternal(int displayId,
+            long maxFrames, long timestamp) {
+        IBinder displayToken = SurfaceControl.getBuiltInDisplay(displayId);
+        return SurfaceControl.getDisplayedContentSample(
+            displayToken, maxFrames, timestamp);
+    }
+
     private void clearViewportsLocked() {
         mViewports.clear();
     }
@@ -2331,5 +2356,25 @@
                 }
             }
         }
+
+        @Override
+        public DisplayedContentSamplingAttributes getDisplayedContentSamplingAttributes(
+                int displayId) {
+            return getDisplayedContentSamplingAttributesInternal(displayId);
+        }
+
+        @Override
+        public boolean setDisplayedContentSamplingEnabled(
+                int displayId, boolean enable, int componentMask, int maxFrames) {
+            return setDisplayedContentSamplingEnabledInternal(
+                    displayId, enable, componentMask, maxFrames);
+        }
+
+        @Override
+        public DisplayedContentSample getDisplayedContentSample(int displayId,
+                long maxFrames, long timestamp) {
+            return getDisplayedContentSampleInternal(displayId, maxFrames, timestamp);
+        }
+
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index b421280..e9bfa8f 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -18,11 +18,21 @@
 
 import static com.android.server.display.VirtualDisplayAdapter.UNIQUE_ID_PREFIX;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
 import android.content.Context;
 import android.hardware.display.BrightnessConfiguration;
 import android.hardware.display.Curve;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayViewport;
+import android.hardware.display.DisplayedContentSample;
+import android.hardware.display.DisplayedContentSamplingAttributes;
 import android.hardware.display.IVirtualDisplayCallback;
 import android.hardware.input.InputManagerInternal;
 import android.os.Handler;
@@ -31,9 +41,9 @@
 import android.view.DisplayInfo;
 import android.view.SurfaceControl;
 
-import androidx.test.runner.AndroidJUnit4;
 import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
 
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
@@ -49,17 +59,8 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import java.util.Arrays;
 import java.util.List;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.mockito.Mockito.mock;
+import java.util.stream.LongStream;
 
 @SmallTest
 @RunWith(AndroidJUnit4.class)
@@ -397,6 +398,43 @@
         displayManager.validateBrightnessConfiguration(null);
     }
 
+    /**
+     * Tests that collection of display color sampling results are sensible.
+     */
+    @Test
+    public void testDisplayedContentSampling() {
+        DisplayManagerService displayManager =
+                new DisplayManagerService(mContext, mShortMockedInjector);
+        registerDefaultDisplays(displayManager);
+
+        DisplayDeviceInfo ddi = displayManager.getDisplayDeviceInfoInternal(0);
+        assertNotNull(ddi);
+
+        DisplayedContentSamplingAttributes attr =
+                displayManager.getDisplayedContentSamplingAttributesInternal(0);
+        if (attr == null) return; //sampling not supported on device, skip remainder of test.
+
+        boolean enabled = displayManager.setDisplayedContentSamplingEnabledInternal(0, true, 0, 0);
+        assertTrue(!enabled);
+
+        displayManager.setDisplayedContentSamplingEnabledInternal(0, false, 0, 0);
+        DisplayedContentSample sample = displayManager.getDisplayedContentSampleInternal(0, 0, 0);
+        assertNotNull(sample);
+
+        long numPixels = ddi.width * ddi.height * sample.getNumFrames();
+        long[] samples = sample.getSampleComponent(DisplayedContentSample.ColorComponent.CHANNEL0);
+        assertTrue(samples.length == 0 || LongStream.of(samples).sum() == numPixels);
+
+        samples = sample.getSampleComponent(DisplayedContentSample.ColorComponent.CHANNEL1);
+        assertTrue(samples.length == 0 || LongStream.of(samples).sum() == numPixels);
+
+        samples = sample.getSampleComponent(DisplayedContentSample.ColorComponent.CHANNEL2);
+        assertTrue(samples.length == 0 || LongStream.of(samples).sum() == numPixels);
+
+        samples = sample.getSampleComponent(DisplayedContentSample.ColorComponent.CHANNEL3);
+        assertTrue(samples.length == 0 || LongStream.of(samples).sum() == numPixels);
+    }
+
     private void registerDefaultDisplays(DisplayManagerService displayManager) {
         Handler handler = displayManager.getDisplayHandler();
         // Would prefer to call displayManager.onStart() directly here but it performs binderService