SF - plumbing game mode for metrics (Part 1)

This change adds a game mode in the Layer metadata that gets updated every
time a game app comes to the foreground. The gameMode is then set on the
Layer based on the metadata and reparenting.

The game mode will then be
updated in TimeStats as a dimension (Part 2, in a separate CL later).

Bug: 186025682
Test: libsurfaceflinger_unittest:GameModeTest

Change-Id: I09deffc01d1b318cc08d0475611289dec055c190
diff --git a/aidl/gui/android/view/LayerMetadataKey.aidl b/aidl/gui/android/view/LayerMetadataKey.aidl
index a1d8ce5..d6ca3db 100644
--- a/aidl/gui/android/view/LayerMetadataKey.aidl
+++ b/aidl/gui/android/view/LayerMetadataKey.aidl
@@ -26,4 +26,5 @@
     METADATA_ACCESSIBILITY_ID = 5,
     METADATA_OWNER_PID = 6,
     METADATA_DEQUEUE_TIME = 7,
+    METADATA_GAME_MODE = 8,
 }
diff --git a/libs/gui/LayerMetadata.cpp b/libs/gui/LayerMetadata.cpp
index 634d8b7..189d51a 100644
--- a/libs/gui/LayerMetadata.cpp
+++ b/libs/gui/LayerMetadata.cpp
@@ -136,6 +136,8 @@
             return StringPrintf("ownerPID%s%d", separator, getInt32(key, 0));
         case view::LayerMetadataKey::METADATA_DEQUEUE_TIME:
             return StringPrintf("dequeueTime%s%" PRId64, separator, *getInt64(key));
+        case view::LayerMetadataKey::METADATA_GAME_MODE:
+            return StringPrintf("gameMode%s%d", separator, getInt32(key, 0));
         default:
             return StringPrintf("%d%s%dbytes", key, separator,
                                 static_cast<int>(mMap.at(key).size()));
diff --git a/libs/gui/include/gui/LayerMetadata.h b/libs/gui/include/gui/LayerMetadata.h
index 41982c2..de14b3d 100644
--- a/libs/gui/include/gui/LayerMetadata.h
+++ b/libs/gui/include/gui/LayerMetadata.h
@@ -29,7 +29,8 @@
     METADATA_MOUSE_CURSOR = 4,
     METADATA_ACCESSIBILITY_ID = 5,
     METADATA_OWNER_PID = 6,
-    METADATA_DEQUEUE_TIME = 7
+    METADATA_DEQUEUE_TIME = 7,
+    METADATA_GAME_MODE = 8
 };
 
 struct LayerMetadata : public Parcelable {
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index a7c8704..d78a822 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -1651,12 +1651,25 @@
     return count;
 }
 
+void Layer::setGameModeForTree(int parentGameMode) {
+    int gameMode = parentGameMode;
+    auto& currentState = getCurrentState();
+    if (currentState.metadata.has(METADATA_GAME_MODE)) {
+        gameMode = currentState.metadata.getInt32(METADATA_GAME_MODE, 0);
+    }
+    setGameMode(gameMode);
+    for (const sp<Layer>& child : mCurrentChildren) {
+        child->setGameModeForTree(gameMode);
+    }
+}
+
 void Layer::addChild(const sp<Layer>& layer) {
     mChildrenChanged = true;
     setTransactionFlags(eTransactionNeeded);
 
     mCurrentChildren.add(layer);
     layer->setParent(this);
+    layer->setGameModeForTree(mGameMode);
     updateTreeHasFrameRateVote();
 }
 
@@ -1668,6 +1681,7 @@
     const auto removeResult = mCurrentChildren.remove(layer);
 
     updateTreeHasFrameRateVote();
+    layer->setGameModeForTree(0);
     layer->updateTreeHasFrameRateVote();
 
     return removeResult;
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 66d7018..af26045 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -847,6 +847,13 @@
      */
     bool hasInputInfo() const;
 
+    // Sets the parent's gameMode for this layer and all its children. Parent's gameMode is applied
+    // only to layers that do not have the GAME_MODE_METADATA set by WMShell. Any layer(along with
+    // its children) that has the metadata set will use the gameMode from the metadata.
+    void setGameModeForTree(int parentGameMode);
+    void setGameMode(int gameMode) { mGameMode = gameMode; };
+    int getGameMode() const { return mGameMode; }
+
     virtual uid_t getOwnerUid() const { return mOwnerUid; }
 
     pid_t getOwnerPid() { return mOwnerPid; }
@@ -1089,6 +1096,10 @@
     // shadow radius is the set shadow radius, otherwise its the parent's shadow radius.
     float mEffectiveShadowRadius = 0.f;
 
+    // Game mode for the layer. Set by WindowManagerShell, game mode is used in
+    // metrics(SurfaceFlingerStats).
+    int mGameMode = 0;
+
     // A list of regions on this layer that should have blurs.
     const std::vector<BlurRegion> getBlurRegions() const;
 };
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 881ee5b..3f75af2 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -4018,6 +4018,13 @@
     std::optional<nsecs_t> dequeueBufferTimestamp;
     if (what & layer_state_t::eMetadataChanged) {
         dequeueBufferTimestamp = s.metadata.getInt64(METADATA_DEQUEUE_TIME);
+        auto gameMode = s.metadata.getInt32(METADATA_GAME_MODE, -1);
+        if (gameMode != -1) {
+            // The transaction will be received on the Task layer and needs to be applied to all
+            // child layers. Child layers that are added at a later point will obtain the game mode
+            // info through addChild().
+            layer->setGameModeForTree(gameMode);
+        }
         if (layer->setMetadata(s.metadata)) flags |= eTraversalNeeded;
     }
     if (what & layer_state_t::eColorSpaceAgnosticChanged) {
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index 1b25a36..736ef30 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -57,6 +57,7 @@
         "FpsTest.cpp",
         "FramebufferSurfaceTest.cpp",
         "FrameTimelineTest.cpp",
+        "GameModeTest.cpp",
         "HWComposerTest.cpp",
         "OneShotTimerTest.cpp",
         "LayerHistoryTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/GameModeTest.cpp b/services/surfaceflinger/tests/unittests/GameModeTest.cpp
new file mode 100644
index 0000000..3fa1a2c
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/GameModeTest.cpp
@@ -0,0 +1,159 @@
+/*
+ * 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 "LibSurfaceFlingerUnittests"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <gui/LayerMetadata.h>
+#include <gui/SurfaceComposerClient.h>
+#include <log/log.h>
+
+#include "TestableSurfaceFlinger.h"
+#include "mock/DisplayHardware/MockComposer.h"
+#include "mock/MockEventThread.h"
+#include "mock/MockVsyncController.h"
+
+namespace android {
+
+using testing::_;
+using testing::Mock;
+using testing::Return;
+using FakeHwcDisplayInjector = TestableSurfaceFlinger::FakeHwcDisplayInjector;
+
+class GameModeTest : public testing::Test {
+public:
+    GameModeTest() {
+        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();
+        setupComposer();
+    }
+
+    ~GameModeTest() {
+        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());
+    }
+
+    sp<BufferStateLayer> createBufferStateLayer() {
+        sp<Client> client;
+        LayerCreationArgs args(mFlinger.flinger(), client, "buffer-state-layer", 100, 100, 0,
+                               LayerMetadata());
+        return new BufferStateLayer(args);
+    }
+
+    void 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));
+    }
+
+    void setupComposer() {
+        mComposer = new Hwc2::mock::Composer();
+        mFlinger.setupComposer(std::unique_ptr<Hwc2::Composer>(mComposer));
+
+        Mock::VerifyAndClear(mComposer);
+    }
+
+    // Mocks the behavior of applying a transaction from WMShell
+    void setGameModeMetadata(sp<Layer> layer, int gameMode) {
+        mLayerMetadata.setInt32(METADATA_GAME_MODE, gameMode);
+        layer->setMetadata(mLayerMetadata);
+        layer->setGameModeForTree(gameMode);
+    }
+
+    TestableSurfaceFlinger mFlinger;
+    Hwc2::mock::Composer* mComposer = nullptr;
+    client_cache_t mClientCache;
+    LayerMetadata mLayerMetadata;
+};
+
+TEST_F(GameModeTest, SetGameModeSetsForAllCurrentChildren) {
+    sp<BufferStateLayer> rootLayer = createBufferStateLayer();
+    sp<BufferStateLayer> childLayer1 = createBufferStateLayer();
+    sp<BufferStateLayer> childLayer2 = createBufferStateLayer();
+    rootLayer->addChild(childLayer1);
+    rootLayer->addChild(childLayer2);
+    rootLayer->setGameModeForTree(/*gameMode*/ 2);
+
+    EXPECT_EQ(rootLayer->getGameMode(), 2);
+    EXPECT_EQ(childLayer1->getGameMode(), 2);
+    EXPECT_EQ(childLayer2->getGameMode(), 2);
+}
+
+TEST_F(GameModeTest, AddChildAppliesGameModeFromParent) {
+    sp<BufferStateLayer> rootLayer = createBufferStateLayer();
+    sp<BufferStateLayer> childLayer = createBufferStateLayer();
+    rootLayer->setGameModeForTree(/*gameMode*/ 2);
+    rootLayer->addChild(childLayer);
+
+    EXPECT_EQ(rootLayer->getGameMode(), 2);
+    EXPECT_EQ(childLayer->getGameMode(), 2);
+}
+
+TEST_F(GameModeTest, RemoveChildResetsGameMode) {
+    sp<BufferStateLayer> rootLayer = createBufferStateLayer();
+    sp<BufferStateLayer> childLayer = createBufferStateLayer();
+    rootLayer->setGameModeForTree(/*gameMode*/ 2);
+    rootLayer->addChild(childLayer);
+
+    EXPECT_EQ(rootLayer->getGameMode(), 2);
+    EXPECT_EQ(childLayer->getGameMode(), 2);
+
+    rootLayer->removeChild(childLayer);
+    EXPECT_EQ(childLayer->getGameMode(), 0);
+}
+
+TEST_F(GameModeTest, ReparentingDoesNotOverrideMetadata) {
+    sp<BufferStateLayer> rootLayer = createBufferStateLayer();
+    sp<BufferStateLayer> childLayer1 = createBufferStateLayer();
+    sp<BufferStateLayer> childLayer2 = createBufferStateLayer();
+    rootLayer->setGameModeForTree(/*gameMode*/ 1);
+    rootLayer->addChild(childLayer1);
+
+    setGameModeMetadata(childLayer2, /*gameMode*/ 2);
+    rootLayer->addChild(childLayer2);
+
+    EXPECT_EQ(rootLayer->getGameMode(), 1);
+    EXPECT_EQ(childLayer1->getGameMode(), 1);
+    EXPECT_EQ(childLayer2->getGameMode(), 2);
+
+    rootLayer->removeChild(childLayer2);
+    EXPECT_EQ(childLayer2->getGameMode(), 2);
+}
+} // namespace android
\ No newline at end of file