Add SF side tunnel mode listener

This CL adds a TunnelModeStateReporter, which monitors if there are
any layers with a sideband stream in SurfaceFlinger. If any of the
layers have a sideband stream, it informs all the registered listeners
that tunnel mode is enabled. When no layers have a sideband stream, it
notifies the listeners that tunnel mode is disabled.

Bug: 171457637
Test: atest TunnelModeStateListenerTest
Test: atest TunnelModeStateReporterTest
Change-Id: Ie54b34dbd9b6253d7142e9b0f690c8469374604d
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 53721cf..71e18a9 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -824,6 +824,36 @@
         return error;
     }
 
+    virtual status_t addTunnelModeEnabledListener(
+            const sp<gui::ITunnelModeEnabledListener>& listener) {
+        Parcel data, reply;
+        SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor());
+        SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(listener));
+
+        const status_t error =
+                remote()->transact(BnSurfaceComposer::ADD_TUNNEL_MODE_ENABLED_LISTENER, data,
+                                   &reply);
+        if (error != NO_ERROR) {
+            ALOGE("addTunnelModeEnabledListener: Failed to transact");
+        }
+        return error;
+    }
+
+    virtual status_t removeTunnelModeEnabledListener(
+            const sp<gui::ITunnelModeEnabledListener>& listener) {
+        Parcel data, reply;
+        SAFE_PARCEL(data.writeInterfaceToken, ISurfaceComposer::getInterfaceDescriptor());
+        SAFE_PARCEL(data.writeStrongBinder, IInterface::asBinder(listener));
+
+        const status_t error =
+                remote()->transact(BnSurfaceComposer::REMOVE_TUNNEL_MODE_ENABLED_LISTENER, data,
+                                   &reply);
+        if (error != NO_ERROR) {
+            ALOGE("removeTunnelModeEnabledListener: Failed to transact");
+        }
+        return error;
+    }
+
     status_t setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
                                         ui::DisplayModeId defaultMode, bool allowGroupSwitching,
                                         float primaryRefreshRateMin, float primaryRefreshRateMax,
@@ -1740,6 +1770,26 @@
             }
             return removeFpsListener(listener);
         }
+        case ADD_TUNNEL_MODE_ENABLED_LISTENER: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            sp<gui::ITunnelModeEnabledListener> listener;
+            status_t result = data.readNullableStrongBinder(&listener);
+            if (result != NO_ERROR) {
+                ALOGE("addTunnelModeEnabledListener: Failed to read listener");
+                return result;
+            }
+            return addTunnelModeEnabledListener(listener);
+        }
+        case REMOVE_TUNNEL_MODE_ENABLED_LISTENER: {
+            CHECK_INTERFACE(ISurfaceComposer, data, reply);
+            sp<gui::ITunnelModeEnabledListener> listener;
+            status_t result = data.readNullableStrongBinder(&listener);
+            if (result != NO_ERROR) {
+                ALOGE("removeTunnelModeEnabledListener: Failed to read listener");
+                return result;
+            }
+            return removeTunnelModeEnabledListener(listener);
+        }
         case SET_DESIRED_DISPLAY_MODE_SPECS: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
             sp<IBinder> displayToken = data.readStrongBinder();
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index aa93808..80ff653 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -2077,6 +2077,16 @@
     return ComposerService::getComposerService()->removeFpsListener(listener);
 }
 
+status_t SurfaceComposerClient::addTunnelModeEnabledListener(
+        const sp<gui::ITunnelModeEnabledListener>& listener) {
+    return ComposerService::getComposerService()->addTunnelModeEnabledListener(listener);
+}
+
+status_t SurfaceComposerClient::removeTunnelModeEnabledListener(
+        const sp<gui::ITunnelModeEnabledListener>& listener) {
+    return ComposerService::getComposerService()->removeTunnelModeEnabledListener(listener);
+}
+
 bool SurfaceComposerClient::getDisplayBrightnessSupport(const sp<IBinder>& displayToken) {
     bool support = false;
     ComposerService::getComposerService()->getDisplayBrightnessSupport(displayToken, &support);
diff --git a/libs/gui/aidl/android/gui/ITunnelModeEnabledListener.aidl b/libs/gui/aidl/android/gui/ITunnelModeEnabledListener.aidl
new file mode 100644
index 0000000..2a89cca
--- /dev/null
+++ b/libs/gui/aidl/android/gui/ITunnelModeEnabledListener.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2021 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.gui;
+
+/** @hide */
+oneway interface ITunnelModeEnabledListener {
+
+    /**
+     * Called when tunnel mode status has changed. Tunnel mode is:
+     *  - enabled when there is a sideband stream attached to one of the layers in
+     *    surface flinger
+     *  - disabled when there is no layer with a sideband stream
+     */
+    void onTunnelModeEnabledChanged(boolean enabled);
+}
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index cb04689..439d90a 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -21,6 +21,7 @@
 #include <android/gui/IHdrLayerInfoListener.h>
 #include <android/gui/IScreenCaptureListener.h>
 #include <android/gui/ITransactionTraceListener.h>
+#include <android/gui/ITunnelModeEnabledListener.h>
 #include <binder/IBinder.h>
 #include <binder/IInterface.h>
 #include <gui/FrameTimelineInfo.h>
@@ -377,6 +378,21 @@
      */
     virtual status_t removeFpsListener(const sp<gui::IFpsListener>& listener) = 0;
 
+    /* Registers a listener to receive tunnel mode enabled updates from SurfaceFlinger.
+     *
+     * Requires ACCESS_SURFACE_FLINGER permission.
+     */
+    virtual status_t addTunnelModeEnabledListener(
+            const sp<gui::ITunnelModeEnabledListener>& listener) = 0;
+
+    /*
+     * Removes a listener that was receiving tunnel mode enabled updates from SurfaceFlinger.
+     *
+     * Requires ACCESS_SURFACE_FLINGER permission.
+     */
+    virtual status_t removeTunnelModeEnabledListener(
+            const sp<gui::ITunnelModeEnabledListener>& listener) = 0;
+
     /* Sets the refresh rate boundaries for the display.
      *
      * The primary refresh rate range represents display manager's general guidance on the display
@@ -607,6 +623,8 @@
         ADD_HDR_LAYER_INFO_LISTENER,
         REMOVE_HDR_LAYER_INFO_LISTENER,
         ON_PULL_ATOM,
+        ADD_TUNNEL_MODE_ENABLED_LISTENER,
+        REMOVE_TUNNEL_MODE_ENABLED_LISTENER,
         // Always append new enum to the end.
     };
 
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 0940e9d..2582882 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -50,6 +50,7 @@
 class ISurfaceComposerClient;
 class IGraphicBufferProducer;
 class IRegionSamplingListener;
+class ITunnelModeEnabledListener;
 class Region;
 
 struct SurfaceControlStats {
@@ -610,6 +611,10 @@
     static status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener);
     static status_t addFpsListener(int32_t taskId, const sp<gui::IFpsListener>& listener);
     static status_t removeFpsListener(const sp<gui::IFpsListener>& listener);
+    static status_t addTunnelModeEnabledListener(
+            const sp<gui::ITunnelModeEnabledListener>& listener);
+    static status_t removeTunnelModeEnabledListener(
+            const sp<gui::ITunnelModeEnabledListener>& listener);
 
 private:
     virtual void onFirstRef();
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index ea8c295..b8d34c3 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -845,6 +845,16 @@
         return NO_ERROR;
     }
     status_t removeFpsListener(const sp<gui::IFpsListener>& /*listener*/) { return NO_ERROR; }
+
+    status_t addTunnelModeEnabledListener(const sp<gui::ITunnelModeEnabledListener>& /*listener*/) {
+        return NO_ERROR;
+    }
+
+    status_t removeTunnelModeEnabledListener(
+            const sp<gui::ITunnelModeEnabledListener>& /*listener*/) {
+        return NO_ERROR;
+    }
+
     status_t setDesiredDisplayModeSpecs(const sp<IBinder>& /*displayToken*/,
                                         ui::DisplayModeId /*defaultMode*/,
                                         bool /*allowGroupSwitching*/,
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index f20bfe1..e669e45 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -188,6 +188,7 @@
         "SurfaceInterceptor.cpp",
         "SurfaceTracing.cpp",
         "TransactionCallbackInvoker.cpp",
+        "TunnelModeEnabledReporter.cpp",
     ],
 }
 
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 0e222ab..881ee5b 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -130,6 +130,7 @@
 #include "SurfaceFlingerProperties.h"
 #include "SurfaceInterceptor.h"
 #include "TimeStats/TimeStats.h"
+#include "TunnelModeEnabledReporter.h"
 #include "android-base/parseint.h"
 #include "android-base/stringprintf.h"
 #include "android-base/strings.h"
@@ -1468,6 +1469,26 @@
     return NO_ERROR;
 }
 
+status_t SurfaceFlinger::addTunnelModeEnabledListener(
+        const sp<gui::ITunnelModeEnabledListener>& listener) {
+    if (!listener) {
+        return BAD_VALUE;
+    }
+
+    mTunnelModeEnabledReporter->addListener(listener);
+    return NO_ERROR;
+}
+
+status_t SurfaceFlinger::removeTunnelModeEnabledListener(
+        const sp<gui::ITunnelModeEnabledListener>& listener) {
+    if (!listener) {
+        return BAD_VALUE;
+    }
+
+    mTunnelModeEnabledReporter->removeListener(listener);
+    return NO_ERROR;
+}
+
 status_t SurfaceFlinger::getDisplayBrightnessSupport(const sp<IBinder>& displayToken,
                                                      bool* outSupport) const {
     if (!displayToken || !outSupport) {
@@ -2191,6 +2212,10 @@
         if (mFpsReporter) {
             mFpsReporter->dispatchLayerFps();
         }
+
+        if (mTunnelModeEnabledReporter) {
+            mTunnelModeEnabledReporter->updateTunnelModeStatus();
+        }
         hdrInfoListeners.reserve(mHdrLayerInfoListeners.size());
         for (auto& [key, value] : mHdrLayerInfoListeners) {
             if (value && value->hasListeners()) {
@@ -3067,6 +3092,7 @@
     mRegionSamplingThread =
             new RegionSamplingThread(*this, RegionSamplingThread::EnvironmentTimingTunables());
     mFpsReporter = new FpsReporter(*mFrameTimeline, *this);
+    mTunnelModeEnabledReporter = new TunnelModeEnabledReporter(*this);
     // Dispatch a mode change request for the primary display on scheduler
     // initialization, so that the EventThreads always contain a reference to a
     // prior configuration.
@@ -5072,6 +5098,8 @@
         case GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES:
         case SET_DISPLAY_CONTENT_SAMPLING_ENABLED:
         case GET_DISPLAYED_CONTENT_SAMPLE:
+        case ADD_TUNNEL_MODE_ENABLED_LISTENER:
+        case REMOVE_TUNNEL_MODE_ENABLED_LISTENER:
         case NOTIFY_POWER_BOOST:
         case SET_GLOBAL_SHADOW_SETTINGS:
         case ACQUIRE_FRAME_RATE_FLEXIBILITY_TOKEN: {
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index cb8d312..c382005 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -90,6 +90,7 @@
 class Client;
 class EventThread;
 class FpsReporter;
+class TunnelModeEnabledReporter;
 class HdrLayerInfoReporter;
 class HWComposer;
 struct SetInputWindowsListener;
@@ -359,6 +360,7 @@
     friend class BufferStateLayer;
     friend class Client;
     friend class FpsReporter;
+    friend class TunnelModeEnabledReporter;
     friend class Layer;
     friend class MonitoredProducer;
     friend class RefreshRateOverlay;
@@ -673,6 +675,10 @@
     status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) override;
     status_t addFpsListener(int32_t taskId, const sp<gui::IFpsListener>& listener) override;
     status_t removeFpsListener(const sp<gui::IFpsListener>& listener) override;
+    status_t addTunnelModeEnabledListener(
+            const sp<gui::ITunnelModeEnabledListener>& listener) override;
+    status_t removeTunnelModeEnabledListener(
+            const sp<gui::ITunnelModeEnabledListener>& listener) override;
     status_t setDesiredDisplayModeSpecs(const sp<IBinder>& displayToken,
                                         ui::DisplayModeId displayModeId, bool allowGroupSwitching,
                                         float primaryRefreshRateMin, float primaryRefreshRateMax,
@@ -1368,6 +1374,7 @@
     bool mLumaSampling = true;
     sp<RegionSamplingThread> mRegionSamplingThread;
     sp<FpsReporter> mFpsReporter;
+    sp<TunnelModeEnabledReporter> mTunnelModeEnabledReporter;
     ui::DisplayPrimaries mInternalDisplayPrimaries;
 
     const float mInternalDisplayDensity;
diff --git a/services/surfaceflinger/TunnelModeEnabledReporter.cpp b/services/surfaceflinger/TunnelModeEnabledReporter.cpp
new file mode 100644
index 0000000..1b3ddf7
--- /dev/null
+++ b/services/surfaceflinger/TunnelModeEnabledReporter.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2021 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "TunnelModeEnabledReporter"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
+#include <algorithm>
+
+#include "Layer.h"
+#include "SurfaceFlinger.h"
+#include "TunnelModeEnabledReporter.h"
+
+namespace android {
+
+TunnelModeEnabledReporter::TunnelModeEnabledReporter(SurfaceFlinger& flinger) : mFlinger(flinger) {}
+
+void TunnelModeEnabledReporter::updateTunnelModeStatus() {
+    bool tunnelModeEnabled = false;
+    mFlinger.mCurrentState.traverse([&](Layer* layer) {
+        auto& currentState = layer->getCurrentState();
+        if (currentState.sidebandStream != nullptr) {
+            tunnelModeEnabled = true;
+            return;
+        }
+    });
+    dispatchTunnelModeEnabled(tunnelModeEnabled);
+}
+
+void TunnelModeEnabledReporter::dispatchTunnelModeEnabled(bool tunnelModeEnabled) {
+    std::vector<sp<gui::ITunnelModeEnabledListener>> localListeners;
+    {
+        std::scoped_lock lock(mMutex);
+        if (mTunnelModeEnabled == tunnelModeEnabled) {
+            return;
+        }
+        mTunnelModeEnabled = tunnelModeEnabled;
+
+        std::transform(mListeners.begin(), mListeners.end(), std::back_inserter(localListeners),
+                       [](const std::pair<wp<IBinder>, sp<gui::ITunnelModeEnabledListener>>&
+                                  entry) { return entry.second; });
+    }
+
+    for (sp<gui::ITunnelModeEnabledListener>& listener : localListeners) {
+        listener->onTunnelModeEnabledChanged(tunnelModeEnabled);
+    }
+}
+
+void TunnelModeEnabledReporter::binderDied(const wp<IBinder>& who) {
+    std::scoped_lock lock(mMutex);
+    mListeners.erase(who);
+}
+
+void TunnelModeEnabledReporter::addListener(const sp<gui::ITunnelModeEnabledListener>& listener) {
+    sp<IBinder> asBinder = IInterface::asBinder(listener);
+    asBinder->linkToDeath(this);
+    bool tunnelModeEnabled = false;
+    {
+        std::scoped_lock lock(mMutex);
+        mListeners.emplace(wp<IBinder>(asBinder), listener);
+        tunnelModeEnabled = mTunnelModeEnabled;
+    }
+    listener->onTunnelModeEnabledChanged(tunnelModeEnabled);
+}
+
+void TunnelModeEnabledReporter::removeListener(
+        const sp<gui::ITunnelModeEnabledListener>& listener) {
+    std::lock_guard lock(mMutex);
+    mListeners.erase(wp<IBinder>(IInterface::asBinder(listener)));
+}
+
+} // namespace android
diff --git a/services/surfaceflinger/TunnelModeEnabledReporter.h b/services/surfaceflinger/TunnelModeEnabledReporter.h
new file mode 100644
index 0000000..d55507a
--- /dev/null
+++ b/services/surfaceflinger/TunnelModeEnabledReporter.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2021 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.
+ */
+
+#pragma once
+
+#include <android-base/thread_annotations.h>
+#include <android/gui/ITunnelModeEnabledListener.h>
+#include <binder/IBinder.h>
+
+#include <unordered_map>
+
+namespace android {
+
+class Layer;
+class SurfaceFlinger;
+
+class TunnelModeEnabledReporter : public IBinder::DeathRecipient {
+public:
+    TunnelModeEnabledReporter(SurfaceFlinger& flinger);
+
+    // Checks if there is a tunnel mode enabled state change and if so, dispatches the updated
+    // tunnel mode enabled/disabled state to the registered listeners
+    // This method performs layer stack traversals, so mStateLock must be held when calling this
+    // method.
+    void updateTunnelModeStatus();
+
+    // Dispatches tunnelModeEnabled to all registered listeners
+    void dispatchTunnelModeEnabled(bool tunnelModeEnabled);
+
+    // Override for IBinder::DeathRecipient
+    void binderDied(const wp<IBinder>&) override;
+
+    // Registers a TunnelModeEnabled listener
+    void addListener(const sp<gui::ITunnelModeEnabledListener>& listener);
+
+    // Deregisters a TunnelModeEnabled listener
+    void removeListener(const sp<gui::ITunnelModeEnabledListener>& listener);
+
+private:
+    mutable std::mutex mMutex;
+    struct WpHash {
+        size_t operator()(const wp<IBinder>& p) const {
+            return std::hash<IBinder*>()(p.unsafe_get());
+        }
+    };
+
+    SurfaceFlinger& mFlinger;
+    std::unordered_map<wp<IBinder>, sp<gui::ITunnelModeEnabledListener>, WpHash> mListeners
+            GUARDED_BY(mMutex);
+    bool mTunnelModeEnabled GUARDED_BY(mMutex) = false;
+};
+
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index b33434e..1b25a36 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -86,6 +86,7 @@
         "TransactionApplicationTest.cpp",
         "TransactionFrameTracerTest.cpp",
         "TransactionSurfaceFrameTest.cpp",
+        "TunnelModeEnabledReporterTest.cpp",
         "StrongTypingTest.cpp",
         "VSyncDispatchTimerQueueTest.cpp",
         "VSyncDispatchRealtimeTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index d004b9d..a551248 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -276,6 +276,7 @@
     static void setLayerSidebandStream(const sp<Layer>& layer,
                                        const sp<NativeHandle>& sidebandStream) {
         layer->mDrawingState.sidebandStream = sidebandStream;
+        layer->mCurrentState.sidebandStream = sidebandStream;
         layer->mSidebandStream = sidebandStream;
         layer->editCompositionState()->sidebandStream = sidebandStream;
     }
diff --git a/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp b/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp
new file mode 100644
index 0000000..d7d7ea7
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/TunnelModeEnabledReporterTest.cpp
@@ -0,0 +1,194 @@
+/*
+ * Copyright 2021 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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "TunnelModeEnabledReporterTest"
+
+#include <android/gui/BnTunnelModeEnabledListener.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <gui/LayerMetadata.h>
+
+#include "BufferStateLayer.h"
+#include "TestableSurfaceFlinger.h"
+#include "TunnelModeEnabledReporter.h"
+#include "mock/DisplayHardware/MockComposer.h"
+#include "mock/MockEventThread.h"
+
+namespace android {
+
+using testing::_;
+using testing::Mock;
+using testing::Return;
+
+using android::Hwc2::IComposer;
+using android::Hwc2::IComposerClient;
+
+using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
+
+constexpr int DEFAULT_SIDEBAND_STREAM = 51;
+
+struct TestableTunnelModeEnabledListener : public gui::BnTunnelModeEnabledListener {
+    TestableTunnelModeEnabledListener() {}
+
+    bool mTunnelModeEnabled = false;
+
+    binder::Status onTunnelModeEnabledChanged(bool tunnelModeEnabled) override {
+        mTunnelModeEnabled = tunnelModeEnabled;
+        return binder::Status::ok();
+    }
+};
+
+class TunnelModeEnabledReporterTest : public testing::Test {
+public:
+    TunnelModeEnabledReporterTest();
+    ~TunnelModeEnabledReporterTest() override;
+
+protected:
+    static constexpr uint32_t WIDTH = 100;
+    static constexpr uint32_t HEIGHT = 100;
+    static constexpr uint32_t LAYER_FLAGS = 0;
+
+    void setupScheduler();
+    void setupComposer(uint32_t virtualDisplayCount);
+    sp<BufferStateLayer> createBufferStateLayer(LayerMetadata metadata);
+
+    TestableSurfaceFlinger mFlinger;
+    Hwc2::mock::Composer* mComposer = nullptr;
+    sp<TestableTunnelModeEnabledListener> mTunnelModeEnabledListener =
+            new TestableTunnelModeEnabledListener();
+    sp<TunnelModeEnabledReporter> mTunnelModeEnabledReporter =
+            new TunnelModeEnabledReporter(*(mFlinger.flinger()));
+};
+
+TunnelModeEnabledReporterTest::TunnelModeEnabledReporterTest() {
+    const ::testing::TestInfo* const test_info =
+            ::testing::UnitTest::GetInstance()->current_test_info();
+    ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
+    setupScheduler();
+    mFlinger.setupComposer(std::make_unique<Hwc2::mock::Composer>());
+    mTunnelModeEnabledReporter->dispatchTunnelModeEnabled(false);
+}
+
+TunnelModeEnabledReporterTest::~TunnelModeEnabledReporterTest() {
+    const ::testing::TestInfo* const test_info =
+            ::testing::UnitTest::GetInstance()->current_test_info();
+    ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name());
+    mTunnelModeEnabledReporter->dispatchTunnelModeEnabled(false);
+    mTunnelModeEnabledReporter->removeListener(mTunnelModeEnabledListener);
+}
+
+sp<BufferStateLayer> TunnelModeEnabledReporterTest::createBufferStateLayer(
+        LayerMetadata metadata = {}) {
+    sp<Client> client;
+    LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", WIDTH, HEIGHT,
+                           LAYER_FLAGS, metadata);
+    return new BufferStateLayer(args);
+}
+
+void TunnelModeEnabledReporterTest::setupScheduler() {
+    auto eventThread = std::make_unique<mock::EventThread>();
+    auto sfEventThread = std::make_unique<mock::EventThread>();
+
+    EXPECT_CALL(*eventThread, registerDisplayEventConnection(_));
+    EXPECT_CALL(*eventThread, createEventConnection(_, _))
+            .WillOnce(Return(new EventThreadConnection(eventThread.get(), /*callingUid=*/0,
+                                                       ResyncCallback())));
+
+    EXPECT_CALL(*sfEventThread, registerDisplayEventConnection(_));
+    EXPECT_CALL(*sfEventThread, createEventConnection(_, _))
+            .WillOnce(Return(new EventThreadConnection(sfEventThread.get(), /*callingUid=*/0,
+                                                       ResyncCallback())));
+
+    auto vsyncController = std::make_unique<mock::VsyncController>();
+    auto vsyncTracker = std::make_unique<mock::VSyncTracker>();
+
+    EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+    EXPECT_CALL(*vsyncTracker, currentPeriod())
+            .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_VSYNC_PERIOD));
+    EXPECT_CALL(*vsyncTracker, nextAnticipatedVSyncTimeFrom(_)).WillRepeatedly(Return(0));
+    mFlinger.setupScheduler(std::move(vsyncController), std::move(vsyncTracker),
+                            std::move(eventThread), std::move(sfEventThread));
+}
+
+namespace {
+
+TEST_F(TunnelModeEnabledReporterTest, callsAddedListeners) {
+    mTunnelModeEnabledReporter->addListener(mTunnelModeEnabledListener);
+
+    bool expectedTunnelModeEnabled = false;
+    mTunnelModeEnabledReporter->dispatchTunnelModeEnabled(expectedTunnelModeEnabled);
+    EXPECT_EQ(expectedTunnelModeEnabled, mTunnelModeEnabledListener->mTunnelModeEnabled);
+
+    expectedTunnelModeEnabled = true;
+    mTunnelModeEnabledReporter->dispatchTunnelModeEnabled(expectedTunnelModeEnabled);
+    EXPECT_EQ(expectedTunnelModeEnabled, mTunnelModeEnabledListener->mTunnelModeEnabled);
+
+    mTunnelModeEnabledReporter->removeListener(mTunnelModeEnabledListener);
+
+    mTunnelModeEnabledReporter->dispatchTunnelModeEnabled(false);
+    EXPECT_EQ(expectedTunnelModeEnabled, mTunnelModeEnabledListener->mTunnelModeEnabled);
+}
+
+TEST_F(TunnelModeEnabledReporterTest, callsNewListenerImmediately) {
+    bool expectedTunnelModeEnabled = false;
+    mTunnelModeEnabledReporter->dispatchTunnelModeEnabled(expectedTunnelModeEnabled);
+
+    mTunnelModeEnabledReporter->addListener(mTunnelModeEnabledListener);
+    EXPECT_EQ(expectedTunnelModeEnabled, mTunnelModeEnabledListener->mTunnelModeEnabled);
+}
+
+TEST_F(TunnelModeEnabledReporterTest, callsNewListenerWithFreshInformation) {
+    sp<Layer> layer = createBufferStateLayer();
+    sp<NativeHandle> stream =
+            NativeHandle::create(reinterpret_cast<native_handle_t*>(DEFAULT_SIDEBAND_STREAM),
+                                 false);
+    mFlinger.setLayerSidebandStream(layer, stream);
+    mFlinger.mutableCurrentState().layersSortedByZ.add(layer);
+    mTunnelModeEnabledReporter->updateTunnelModeStatus();
+    mTunnelModeEnabledReporter->addListener(mTunnelModeEnabledListener);
+    EXPECT_EQ(true, mTunnelModeEnabledListener->mTunnelModeEnabled);
+    mTunnelModeEnabledReporter->removeListener(mTunnelModeEnabledListener);
+
+    mFlinger.mutableCurrentState().layersSortedByZ.remove(layer);
+    mTunnelModeEnabledReporter->updateTunnelModeStatus();
+    mTunnelModeEnabledReporter->addListener(mTunnelModeEnabledListener);
+    EXPECT_EQ(false, mTunnelModeEnabledListener->mTunnelModeEnabled);
+}
+
+TEST_F(TunnelModeEnabledReporterTest, layerWithSidebandStreamTriggersUpdate) {
+    mTunnelModeEnabledReporter->addListener(mTunnelModeEnabledListener);
+    EXPECT_EQ(false, mTunnelModeEnabledListener->mTunnelModeEnabled);
+
+    sp<Layer> simpleLayer = createBufferStateLayer();
+    sp<Layer> layerWithSidebandStream = createBufferStateLayer();
+    sp<NativeHandle> stream =
+            NativeHandle::create(reinterpret_cast<native_handle_t*>(DEFAULT_SIDEBAND_STREAM),
+                                 false);
+    mFlinger.setLayerSidebandStream(layerWithSidebandStream, stream);
+
+    mFlinger.mutableCurrentState().layersSortedByZ.add(simpleLayer);
+    mFlinger.mutableCurrentState().layersSortedByZ.add(layerWithSidebandStream);
+    mTunnelModeEnabledReporter->updateTunnelModeStatus();
+    EXPECT_EQ(true, mTunnelModeEnabledListener->mTunnelModeEnabled);
+
+    mFlinger.mutableCurrentState().layersSortedByZ.remove(layerWithSidebandStream);
+    mTunnelModeEnabledReporter->updateTunnelModeStatus();
+    EXPECT_EQ(false, mTunnelModeEnabledListener->mTunnelModeEnabled);
+}
+
+} // namespace
+} // namespace android