Loading services/surfaceflinger/PowerAdvisor/SessionLayerMap.cpp +35 −21 Original line number Diff line number Diff line Loading @@ -15,50 +15,47 @@ */ #include "SessionLayerMap.h" #include <android/binder_libbinder.h> namespace android::adpf { void SessionLayerMap::notifySessionsDied(std::vector<int32_t>& sessionIds) { for (int id : sessionIds) { auto&& iter = mSessions.find(id); if (iter != mSessions.end()) { mSessions.erase(iter); } void SessionLayerMap::notifySessionsDied(std::vector<int32_t>& sessions) { for (int session : sessions) { mSessions.entries.erase(session); // We don't create update entries for dying elements mSessions.updates.erase(session); } } void SessionLayerMap::notifyLayersDied(std::vector<int32_t>& layers) { for (auto&& layer : layers) { auto&& iter = mLayers.find(layer); if (iter != mLayers.end()) { mLayers.erase(iter); } mLayers.entries.erase(layer); // We don't create update entries for dying elements mLayers.updates.erase(layer); } } bool SessionLayerMap::bindSessionIDToLayers(int sessionId, const std::vector<int32_t>& layerIds) { // If there is no association, just drop from map if (layerIds.empty()) { mSessions.erase(sessionId); mSessions.entries.erase(sessionId); return false; } // Ensure session exists if (!mSessions.contains(sessionId)) { mSessions.emplace(sessionId, MappedType(sessionId, mLayers)); if (!mSessions.entries.contains(sessionId)) { mSessions.entries.emplace(sessionId, MappedType(sessionId, mSessions, mLayers)); } MappedType& session = mSessions.at(sessionId); MappedType& session = mSessions.entries.at(sessionId); std::set<int32_t> newLinks; // For each incoming link for (auto&& layerId : layerIds) { auto&& iter = mLayers.find(layerId); auto&& iter = mLayers.entries.find(layerId); // If it's not in the map, add it if (iter == mLayers.end()) { mLayers.emplace(layerId, MappedType(layerId, mSessions)); if (iter == mLayers.entries.end()) { mLayers.entries.emplace(layerId, MappedType(layerId, mLayers, mSessions)); } // Make a ref to it in the session's new association map Loading @@ -71,9 +68,9 @@ bool SessionLayerMap::bindSessionIDToLayers(int sessionId, const std::vector<int void SessionLayerMap::getAssociatedSessions(int32_t layerId, std::vector<int32_t>& sessionIdsOut) { sessionIdsOut.clear(); auto&& iter = mLayers.find(layerId); auto&& iter = mLayers.entries.find(layerId); if (iter == mLayers.end()) { if (iter == mLayers.entries.end()) { return; } Loading @@ -82,12 +79,29 @@ void SessionLayerMap::getAssociatedSessions(int32_t layerId, std::vector<int32_t iter->second.mLinks.end()); } bool SessionLayerMap::isLayerRelevant(int32_t layer) { return mLayers.entries.contains(layer); } void SessionLayerMap::getCurrentlyRelevantLayers( std::unordered_set<int32_t>& currentlyRelevantLayers) { currentlyRelevantLayers.clear(); for (auto&& layer : mLayers) { for (auto&& layer : mLayers.entries) { currentlyRelevantLayers.insert(layer.first); } } void SessionLayerMap::getUpdatedItems(std::set<int32_t>& updatedLayers, std::set<int32_t>& updatedSessions) { updatedLayers.swap(mLayers.updates); updatedSessions.swap(mSessions.updates); mLayers.updates.clear(); mSessions.updates.clear(); }; bool SessionLayerMap::isIdle() { return mLayers.entries.empty() && mLayers.updates.empty() && mSessions.entries.empty() && mSessions.updates.empty(); } } // namespace android::adpf No newline at end of file services/surfaceflinger/PowerAdvisor/SessionLayerMap.h +42 −15 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ #pragma once #include <log/log.h> #include <map> #include <set> #include <unordered_map> #include <unordered_set> Loading @@ -34,21 +35,42 @@ public: bool bindSessionIDToLayers(int sessionId, const std::vector<int32_t>& layerIds); // Get the set of sessions that are mapped to a specific layer id void getAssociatedSessions(int32_t layerId, std::vector<int32_t>& sessionIdsOut); // Get the set of layers that are currently being tracked // Get a copy of the whole set of layers that are currently being tracked void getCurrentlyRelevantLayers(std::unordered_set<int32_t>& currentlyRelevantLayers); // Find out whether a given layer should be tracked bool isLayerRelevant(int32_t layer); // Get the session associations of any layers that changed since the last time we called this std::map<int32_t, int32_t> getLayerMappingUpdates(); // Gets the set of items that changed void getUpdatedItems(std::set<int32_t>& updatedLayers, std::set<int32_t>& updatedSessions); // Is there any active mapping between sessions and layers currently bool isIdle(); private: struct MappedType; struct EntrySet { std::set<int32_t> updates; std::unordered_map<int32_t, MappedType> entries; }; EntrySet mSessions; EntrySet mLayers; struct MappedType { MappedType(int32_t id, std::unordered_map<int32_t, MappedType>& otherList) : mId(id), mOtherList(otherList) {}; MappedType(int32_t id, EntrySet& sameSet, EntrySet& otherSet) : mId(id), mMyEntrySet(sameSet), mOtherEntrySet(otherSet) {}; MappedType() = delete; ~MappedType() { swapLinks({}); } // Replace the set of associated IDs for this mapped type with a different set of IDs, // updating only associations which have changed between the two sets void swapLinks(std::set<int32_t>&& incoming) { if (mLinks == incoming) { return; } auto&& oldIter = mLinks.begin(); auto&& newIter = incoming.begin(); bool isChanged = false; // Dump all outdated values and insert new ones while (oldIter != mLinks.end() || newIter != incoming.end()) { Loading @@ -56,6 +78,7 @@ private: // We should have already ensured what we're linking to exists if (oldIter == mLinks.end() || (newIter != incoming.end() && *newIter < *oldIter)) { addRemoteAssociation(*newIter); isChanged = true; ++newIter; continue; } Loading @@ -63,6 +86,7 @@ private: // If there is a value in the old set but not the new set if (newIter == incoming.end() || (oldIter != mLinks.end() && *oldIter < *newIter)) { dropRemoteAssociation(*oldIter); isChanged = true; ++oldIter; continue; } Loading @@ -74,26 +98,31 @@ private: continue; } } if (isChanged) { mMyEntrySet.updates.insert(mId); mLinks.swap(incoming); } } void addRemoteAssociation(int32_t other) { auto&& iter = mOtherList.find(other); if (iter != mOtherList.end()) { auto&& iter = mOtherEntrySet.entries.find(other); if (iter != mOtherEntrySet.entries.end()) { iter->second.mLinks.insert(mId); mOtherEntrySet.updates.insert(iter->first); } else { ALOGE("Existing entry in SessionLayerMap, link failed"); } } void dropRemoteAssociation(int32_t other) { auto&& iter = mOtherList.find(other); if (iter != mOtherList.end()) { auto&& iter = mOtherEntrySet.entries.find(other); if (iter != mOtherEntrySet.entries.end()) { iter->second.mLinks.erase(mId); mOtherEntrySet.updates.insert(iter->first); // If the item has no links, drop them from the map if (iter->second.mLinks.empty()) { // This only erases them from the map, not from general tracking mOtherList.erase(iter); // This does not drop them from general tracking, nor make them "dead" mOtherEntrySet.entries.erase(iter); } } else { ALOGE("Missing entry in SessionLayerMap, unlinking failed"); Loading @@ -102,11 +131,9 @@ private: int32_t mId; std::set<int> mLinks; std::unordered_map<int32_t, MappedType>& mOtherList; EntrySet& mMyEntrySet; EntrySet& mOtherEntrySet; }; std::unordered_map<int32_t, MappedType> mSessions; std::unordered_map<int32_t, MappedType> mLayers; }; } // namespace android::adpf services/surfaceflinger/tests/unittests/SessionLayerMapTest.cpp 0 → 100644 +157 −0 Original line number Diff line number Diff line /* * Copyright 2025 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 "SessionLayerMapTest" #include "PowerAdvisor/SessionLayerMap.h" #include "SessionManagerTestUtils.h" #include <gmock/gmock.h> #include <gtest/gtest.h> namespace android::adpf { class SessionLayerMapTest : public testing::Test { public: void SetUp() override; void checkAssociations(); protected: std::map<size_t, std::vector<int>> mSessionAssociations{{0, {0, 1, 2, 3}}, {1, {1, 2}}, {2, {2, 3}}, {3, {3}}, {4, {4, 5}}, {5, {4}}, {6, {5}}}; std::unique_ptr<SessionLayerMap> mSessionLayerMap; }; void SessionLayerMapTest::SetUp() { mSessionLayerMap = std::make_unique<SessionLayerMap>(); for (auto&& association : mSessionAssociations) { mSessionLayerMap->bindSessionIDToLayers(static_cast<int32_t>(association.first), association.second); } } void SessionLayerMapTest::checkAssociations() { auto remapped = invertAssociations(mSessionAssociations); for (auto&& layerEntry : remapped) { std::vector<int32_t> out; mSessionLayerMap->getAssociatedSessions(static_cast<int32_t>(layerEntry.first), out); EXPECT_EQ(layerEntry.second, out); } } TEST_F(SessionLayerMapTest, testElementsExist) { checkAssociations(); } TEST_F(SessionLayerMapTest, testLayersRemoved) { std::vector<int> layersToRemove = {3, 4}; mSessionLayerMap->notifyLayersDied(layersToRemove); dropTestLayers(mSessionAssociations, layersToRemove); checkAssociations(); } TEST_F(SessionLayerMapTest, testSessionsRemoved) { std::vector<int> sessionsToRemove = {0, 2, 4}; mSessionLayerMap->notifySessionsDied(sessionsToRemove); dropTestSessions(mSessionAssociations, sessionsToRemove); checkAssociations(); } TEST_F(SessionLayerMapTest, testRelevancyMakesSense) { std::map<size_t, std::vector<int>> toInsert{{80, {20, 21, 22}}, {81, {20}}, {82, {21, 24}}, {83, {24, 25}}, {84, {25, 26}}}; for (auto& items : toInsert) { mSessionLayerMap->bindSessionIDToLayers((int32_t)items.first, items.second); mSessionAssociations[items.first] = items.second; } std::unordered_set<int32_t> startingRelevance; mSessionLayerMap->getCurrentlyRelevantLayers(startingRelevance); std::set<int> uniqueLayerSet = getUniqueLayers(mSessionAssociations); std::set<int> relevantLayerSet; relevantLayerSet.insert(startingRelevance.begin(), startingRelevance.end()); // All unique layers should appear exactly once in the relevance set EXPECT_EQ(uniqueLayerSet, relevantLayerSet); std::vector<int32_t> sessionsToDrop{83, 84}; std::vector<int32_t> layersToDrop{1, 2}; // This should dump 4 layers: 1, 2, 25 and 26 dropTestSessions(mSessionAssociations, sessionsToDrop); dropTestLayers(mSessionAssociations, layersToDrop); mSessionLayerMap->notifyLayersDied(layersToDrop); mSessionLayerMap->notifySessionsDied(sessionsToDrop); std::unordered_set<int32_t> updatedRelevance; mSessionLayerMap->getCurrentlyRelevantLayers(updatedRelevance); std::set<int> updatedUniqueLayerSet = getUniqueLayers(mSessionAssociations); std::set<int> updatedRelevantLayerSet; updatedRelevantLayerSet.insert(updatedRelevance.begin(), updatedRelevance.end()); EXPECT_EQ(updatedUniqueLayerSet, updatedRelevantLayerSet); // Verify that we actually dumped four layers EXPECT_EQ(updatedRelevantLayerSet.size(), relevantLayerSet.size() - 4); } TEST_F(SessionLayerMapTest, testDiffsMakeSense) { std::set<int32_t> gotLayers, gotSessions; std::set<int32_t> expectedSessions{0, 1, 2, 3, 4, 5, 6}; std::set<int32_t> expectedLayers{0, 1, 2, 3, 4, 5}; mSessionLayerMap->getUpdatedItems(gotLayers, gotSessions); EXPECT_EQ(expectedLayers, gotLayers); EXPECT_EQ(expectedSessions, gotSessions); expectedSessions = std::set<int32_t>{2}; expectedLayers = std::set<int32_t>{4}; // Add an arbitrary binding mSessionLayerMap->bindSessionIDToLayers(2, {2, 3, 4}); mSessionLayerMap->getUpdatedItems(gotLayers, gotSessions); EXPECT_EQ(expectedLayers, gotLayers); EXPECT_EQ(expectedSessions, gotSessions); std::vector<int32_t> sessionsToDrop{4, 6}; std::vector<int32_t> layersToDrop{3}; dropTestSessions(mSessionAssociations, sessionsToDrop); dropTestLayers(mSessionAssociations, layersToDrop); mSessionLayerMap->notifyLayersDied(layersToDrop); mSessionLayerMap->notifySessionsDied(sessionsToDrop); // This should only contain updates for things associated with layers or sessions that dropped // Dropped layers are not included, because we don't create updates for dead objects expectedSessions = std::set<int32_t>{0, 2, 3}; expectedLayers = std::set<int32_t>{4, 5}; mSessionLayerMap->getUpdatedItems(gotLayers, gotSessions); EXPECT_EQ(expectedLayers, gotLayers); EXPECT_EQ(expectedSessions, gotSessions); } } // namespace android::adpf services/surfaceflinger/tests/unittests/SessionManagerTestUtils.cpp 0 → 100644 +67 −0 Original line number Diff line number Diff line /* * Copyright 2025 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. */ #include "PowerAdvisor/SessionManager.h" #include <gmock/gmock.h> #include <gtest/gtest.h> namespace android::adpf { std::map<size_t, std::vector<int>> invertAssociations(std::map<size_t, std::vector<int>>& input) { std::map<size_t, std::vector<int>> inverted; for (auto&& association : input) { for (auto&& layerId : association.second) { inverted[(size_t)layerId].push_back((int)association.first); } } for (auto&& association : inverted) { std::sort(association.second.begin(), association.second.end()); } return inverted; } std::set<int> getUniqueLayers(std::map<size_t, std::vector<int>>& sessions) { std::set<int> uniqueLayers; for (auto&& association : sessions) { uniqueLayers.insert(association.second.begin(), association.second.end()); } return uniqueLayers; } std::set<int> getUniqueSessions(std::map<size_t, std::vector<int>>& sessions) { std::set<int> uniqueSessions; for (auto&& association : sessions) { uniqueSessions.insert(static_cast<int32_t>(association.first)); } return uniqueSessions; } void dropTestSessions(std::map<size_t, std::vector<int>>& sessions, std::vector<int>& sessionIds) { for (auto&& sessionId : sessionIds) { sessions.erase((size_t)sessionId); } } void dropTestLayers(std::map<size_t, std::vector<int>>& sessions, std::vector<int>& layerIds) { for (auto&& association : sessions) { std::erase_if(association.second, [&](int elem) { return std::find(layerIds.begin(), layerIds.end(), elem) != layerIds.end(); }); } } } // namespace android::adpf No newline at end of file services/surfaceflinger/tests/unittests/SessionManagerTestUtils.h 0 → 100644 +38 −0 Original line number Diff line number Diff line /* * Copyright 2025 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. */ #include "PowerAdvisor/SessionManager.h" #include <gmock/gmock.h> #include <gtest/gtest.h> namespace android::adpf { // Turns the session -> layer map into a layer -> session map std::map<size_t, std::vector<int>> invertAssociations(std::map<size_t, std::vector<int>>& sessions); // Ensures the map associations line up with those expected bool validateAssociations( std::map<size_t, std::vector<int>>& sessions, SessionLayerMap& map); // Get the set of unique layer IDs in a given test session mapping std::set<int> getUniqueLayers(std::map<size_t, std::vector<int>>& sessions); // Get the set of unique session IDs in a given test session mapping std::set<int> getUniqueSessions(std::map<size_t, std::vector<int>>& sessions); // Drops a set of layer IDs from the test session mapping void dropTestLayers(std::map<size_t, std::vector<int>>& sessions, std::vector<int>& layerIds); // Drops a set of session IDs from the test session mapping void dropTestSessions(std::map<size_t, std::vector<int>>& sessions, std::vector<int>& sessionIds); }; // namespace android::adpf No newline at end of file Loading
services/surfaceflinger/PowerAdvisor/SessionLayerMap.cpp +35 −21 Original line number Diff line number Diff line Loading @@ -15,50 +15,47 @@ */ #include "SessionLayerMap.h" #include <android/binder_libbinder.h> namespace android::adpf { void SessionLayerMap::notifySessionsDied(std::vector<int32_t>& sessionIds) { for (int id : sessionIds) { auto&& iter = mSessions.find(id); if (iter != mSessions.end()) { mSessions.erase(iter); } void SessionLayerMap::notifySessionsDied(std::vector<int32_t>& sessions) { for (int session : sessions) { mSessions.entries.erase(session); // We don't create update entries for dying elements mSessions.updates.erase(session); } } void SessionLayerMap::notifyLayersDied(std::vector<int32_t>& layers) { for (auto&& layer : layers) { auto&& iter = mLayers.find(layer); if (iter != mLayers.end()) { mLayers.erase(iter); } mLayers.entries.erase(layer); // We don't create update entries for dying elements mLayers.updates.erase(layer); } } bool SessionLayerMap::bindSessionIDToLayers(int sessionId, const std::vector<int32_t>& layerIds) { // If there is no association, just drop from map if (layerIds.empty()) { mSessions.erase(sessionId); mSessions.entries.erase(sessionId); return false; } // Ensure session exists if (!mSessions.contains(sessionId)) { mSessions.emplace(sessionId, MappedType(sessionId, mLayers)); if (!mSessions.entries.contains(sessionId)) { mSessions.entries.emplace(sessionId, MappedType(sessionId, mSessions, mLayers)); } MappedType& session = mSessions.at(sessionId); MappedType& session = mSessions.entries.at(sessionId); std::set<int32_t> newLinks; // For each incoming link for (auto&& layerId : layerIds) { auto&& iter = mLayers.find(layerId); auto&& iter = mLayers.entries.find(layerId); // If it's not in the map, add it if (iter == mLayers.end()) { mLayers.emplace(layerId, MappedType(layerId, mSessions)); if (iter == mLayers.entries.end()) { mLayers.entries.emplace(layerId, MappedType(layerId, mLayers, mSessions)); } // Make a ref to it in the session's new association map Loading @@ -71,9 +68,9 @@ bool SessionLayerMap::bindSessionIDToLayers(int sessionId, const std::vector<int void SessionLayerMap::getAssociatedSessions(int32_t layerId, std::vector<int32_t>& sessionIdsOut) { sessionIdsOut.clear(); auto&& iter = mLayers.find(layerId); auto&& iter = mLayers.entries.find(layerId); if (iter == mLayers.end()) { if (iter == mLayers.entries.end()) { return; } Loading @@ -82,12 +79,29 @@ void SessionLayerMap::getAssociatedSessions(int32_t layerId, std::vector<int32_t iter->second.mLinks.end()); } bool SessionLayerMap::isLayerRelevant(int32_t layer) { return mLayers.entries.contains(layer); } void SessionLayerMap::getCurrentlyRelevantLayers( std::unordered_set<int32_t>& currentlyRelevantLayers) { currentlyRelevantLayers.clear(); for (auto&& layer : mLayers) { for (auto&& layer : mLayers.entries) { currentlyRelevantLayers.insert(layer.first); } } void SessionLayerMap::getUpdatedItems(std::set<int32_t>& updatedLayers, std::set<int32_t>& updatedSessions) { updatedLayers.swap(mLayers.updates); updatedSessions.swap(mSessions.updates); mLayers.updates.clear(); mSessions.updates.clear(); }; bool SessionLayerMap::isIdle() { return mLayers.entries.empty() && mLayers.updates.empty() && mSessions.entries.empty() && mSessions.updates.empty(); } } // namespace android::adpf No newline at end of file
services/surfaceflinger/PowerAdvisor/SessionLayerMap.h +42 −15 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ #pragma once #include <log/log.h> #include <map> #include <set> #include <unordered_map> #include <unordered_set> Loading @@ -34,21 +35,42 @@ public: bool bindSessionIDToLayers(int sessionId, const std::vector<int32_t>& layerIds); // Get the set of sessions that are mapped to a specific layer id void getAssociatedSessions(int32_t layerId, std::vector<int32_t>& sessionIdsOut); // Get the set of layers that are currently being tracked // Get a copy of the whole set of layers that are currently being tracked void getCurrentlyRelevantLayers(std::unordered_set<int32_t>& currentlyRelevantLayers); // Find out whether a given layer should be tracked bool isLayerRelevant(int32_t layer); // Get the session associations of any layers that changed since the last time we called this std::map<int32_t, int32_t> getLayerMappingUpdates(); // Gets the set of items that changed void getUpdatedItems(std::set<int32_t>& updatedLayers, std::set<int32_t>& updatedSessions); // Is there any active mapping between sessions and layers currently bool isIdle(); private: struct MappedType; struct EntrySet { std::set<int32_t> updates; std::unordered_map<int32_t, MappedType> entries; }; EntrySet mSessions; EntrySet mLayers; struct MappedType { MappedType(int32_t id, std::unordered_map<int32_t, MappedType>& otherList) : mId(id), mOtherList(otherList) {}; MappedType(int32_t id, EntrySet& sameSet, EntrySet& otherSet) : mId(id), mMyEntrySet(sameSet), mOtherEntrySet(otherSet) {}; MappedType() = delete; ~MappedType() { swapLinks({}); } // Replace the set of associated IDs for this mapped type with a different set of IDs, // updating only associations which have changed between the two sets void swapLinks(std::set<int32_t>&& incoming) { if (mLinks == incoming) { return; } auto&& oldIter = mLinks.begin(); auto&& newIter = incoming.begin(); bool isChanged = false; // Dump all outdated values and insert new ones while (oldIter != mLinks.end() || newIter != incoming.end()) { Loading @@ -56,6 +78,7 @@ private: // We should have already ensured what we're linking to exists if (oldIter == mLinks.end() || (newIter != incoming.end() && *newIter < *oldIter)) { addRemoteAssociation(*newIter); isChanged = true; ++newIter; continue; } Loading @@ -63,6 +86,7 @@ private: // If there is a value in the old set but not the new set if (newIter == incoming.end() || (oldIter != mLinks.end() && *oldIter < *newIter)) { dropRemoteAssociation(*oldIter); isChanged = true; ++oldIter; continue; } Loading @@ -74,26 +98,31 @@ private: continue; } } if (isChanged) { mMyEntrySet.updates.insert(mId); mLinks.swap(incoming); } } void addRemoteAssociation(int32_t other) { auto&& iter = mOtherList.find(other); if (iter != mOtherList.end()) { auto&& iter = mOtherEntrySet.entries.find(other); if (iter != mOtherEntrySet.entries.end()) { iter->second.mLinks.insert(mId); mOtherEntrySet.updates.insert(iter->first); } else { ALOGE("Existing entry in SessionLayerMap, link failed"); } } void dropRemoteAssociation(int32_t other) { auto&& iter = mOtherList.find(other); if (iter != mOtherList.end()) { auto&& iter = mOtherEntrySet.entries.find(other); if (iter != mOtherEntrySet.entries.end()) { iter->second.mLinks.erase(mId); mOtherEntrySet.updates.insert(iter->first); // If the item has no links, drop them from the map if (iter->second.mLinks.empty()) { // This only erases them from the map, not from general tracking mOtherList.erase(iter); // This does not drop them from general tracking, nor make them "dead" mOtherEntrySet.entries.erase(iter); } } else { ALOGE("Missing entry in SessionLayerMap, unlinking failed"); Loading @@ -102,11 +131,9 @@ private: int32_t mId; std::set<int> mLinks; std::unordered_map<int32_t, MappedType>& mOtherList; EntrySet& mMyEntrySet; EntrySet& mOtherEntrySet; }; std::unordered_map<int32_t, MappedType> mSessions; std::unordered_map<int32_t, MappedType> mLayers; }; } // namespace android::adpf
services/surfaceflinger/tests/unittests/SessionLayerMapTest.cpp 0 → 100644 +157 −0 Original line number Diff line number Diff line /* * Copyright 2025 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 "SessionLayerMapTest" #include "PowerAdvisor/SessionLayerMap.h" #include "SessionManagerTestUtils.h" #include <gmock/gmock.h> #include <gtest/gtest.h> namespace android::adpf { class SessionLayerMapTest : public testing::Test { public: void SetUp() override; void checkAssociations(); protected: std::map<size_t, std::vector<int>> mSessionAssociations{{0, {0, 1, 2, 3}}, {1, {1, 2}}, {2, {2, 3}}, {3, {3}}, {4, {4, 5}}, {5, {4}}, {6, {5}}}; std::unique_ptr<SessionLayerMap> mSessionLayerMap; }; void SessionLayerMapTest::SetUp() { mSessionLayerMap = std::make_unique<SessionLayerMap>(); for (auto&& association : mSessionAssociations) { mSessionLayerMap->bindSessionIDToLayers(static_cast<int32_t>(association.first), association.second); } } void SessionLayerMapTest::checkAssociations() { auto remapped = invertAssociations(mSessionAssociations); for (auto&& layerEntry : remapped) { std::vector<int32_t> out; mSessionLayerMap->getAssociatedSessions(static_cast<int32_t>(layerEntry.first), out); EXPECT_EQ(layerEntry.second, out); } } TEST_F(SessionLayerMapTest, testElementsExist) { checkAssociations(); } TEST_F(SessionLayerMapTest, testLayersRemoved) { std::vector<int> layersToRemove = {3, 4}; mSessionLayerMap->notifyLayersDied(layersToRemove); dropTestLayers(mSessionAssociations, layersToRemove); checkAssociations(); } TEST_F(SessionLayerMapTest, testSessionsRemoved) { std::vector<int> sessionsToRemove = {0, 2, 4}; mSessionLayerMap->notifySessionsDied(sessionsToRemove); dropTestSessions(mSessionAssociations, sessionsToRemove); checkAssociations(); } TEST_F(SessionLayerMapTest, testRelevancyMakesSense) { std::map<size_t, std::vector<int>> toInsert{{80, {20, 21, 22}}, {81, {20}}, {82, {21, 24}}, {83, {24, 25}}, {84, {25, 26}}}; for (auto& items : toInsert) { mSessionLayerMap->bindSessionIDToLayers((int32_t)items.first, items.second); mSessionAssociations[items.first] = items.second; } std::unordered_set<int32_t> startingRelevance; mSessionLayerMap->getCurrentlyRelevantLayers(startingRelevance); std::set<int> uniqueLayerSet = getUniqueLayers(mSessionAssociations); std::set<int> relevantLayerSet; relevantLayerSet.insert(startingRelevance.begin(), startingRelevance.end()); // All unique layers should appear exactly once in the relevance set EXPECT_EQ(uniqueLayerSet, relevantLayerSet); std::vector<int32_t> sessionsToDrop{83, 84}; std::vector<int32_t> layersToDrop{1, 2}; // This should dump 4 layers: 1, 2, 25 and 26 dropTestSessions(mSessionAssociations, sessionsToDrop); dropTestLayers(mSessionAssociations, layersToDrop); mSessionLayerMap->notifyLayersDied(layersToDrop); mSessionLayerMap->notifySessionsDied(sessionsToDrop); std::unordered_set<int32_t> updatedRelevance; mSessionLayerMap->getCurrentlyRelevantLayers(updatedRelevance); std::set<int> updatedUniqueLayerSet = getUniqueLayers(mSessionAssociations); std::set<int> updatedRelevantLayerSet; updatedRelevantLayerSet.insert(updatedRelevance.begin(), updatedRelevance.end()); EXPECT_EQ(updatedUniqueLayerSet, updatedRelevantLayerSet); // Verify that we actually dumped four layers EXPECT_EQ(updatedRelevantLayerSet.size(), relevantLayerSet.size() - 4); } TEST_F(SessionLayerMapTest, testDiffsMakeSense) { std::set<int32_t> gotLayers, gotSessions; std::set<int32_t> expectedSessions{0, 1, 2, 3, 4, 5, 6}; std::set<int32_t> expectedLayers{0, 1, 2, 3, 4, 5}; mSessionLayerMap->getUpdatedItems(gotLayers, gotSessions); EXPECT_EQ(expectedLayers, gotLayers); EXPECT_EQ(expectedSessions, gotSessions); expectedSessions = std::set<int32_t>{2}; expectedLayers = std::set<int32_t>{4}; // Add an arbitrary binding mSessionLayerMap->bindSessionIDToLayers(2, {2, 3, 4}); mSessionLayerMap->getUpdatedItems(gotLayers, gotSessions); EXPECT_EQ(expectedLayers, gotLayers); EXPECT_EQ(expectedSessions, gotSessions); std::vector<int32_t> sessionsToDrop{4, 6}; std::vector<int32_t> layersToDrop{3}; dropTestSessions(mSessionAssociations, sessionsToDrop); dropTestLayers(mSessionAssociations, layersToDrop); mSessionLayerMap->notifyLayersDied(layersToDrop); mSessionLayerMap->notifySessionsDied(sessionsToDrop); // This should only contain updates for things associated with layers or sessions that dropped // Dropped layers are not included, because we don't create updates for dead objects expectedSessions = std::set<int32_t>{0, 2, 3}; expectedLayers = std::set<int32_t>{4, 5}; mSessionLayerMap->getUpdatedItems(gotLayers, gotSessions); EXPECT_EQ(expectedLayers, gotLayers); EXPECT_EQ(expectedSessions, gotSessions); } } // namespace android::adpf
services/surfaceflinger/tests/unittests/SessionManagerTestUtils.cpp 0 → 100644 +67 −0 Original line number Diff line number Diff line /* * Copyright 2025 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. */ #include "PowerAdvisor/SessionManager.h" #include <gmock/gmock.h> #include <gtest/gtest.h> namespace android::adpf { std::map<size_t, std::vector<int>> invertAssociations(std::map<size_t, std::vector<int>>& input) { std::map<size_t, std::vector<int>> inverted; for (auto&& association : input) { for (auto&& layerId : association.second) { inverted[(size_t)layerId].push_back((int)association.first); } } for (auto&& association : inverted) { std::sort(association.second.begin(), association.second.end()); } return inverted; } std::set<int> getUniqueLayers(std::map<size_t, std::vector<int>>& sessions) { std::set<int> uniqueLayers; for (auto&& association : sessions) { uniqueLayers.insert(association.second.begin(), association.second.end()); } return uniqueLayers; } std::set<int> getUniqueSessions(std::map<size_t, std::vector<int>>& sessions) { std::set<int> uniqueSessions; for (auto&& association : sessions) { uniqueSessions.insert(static_cast<int32_t>(association.first)); } return uniqueSessions; } void dropTestSessions(std::map<size_t, std::vector<int>>& sessions, std::vector<int>& sessionIds) { for (auto&& sessionId : sessionIds) { sessions.erase((size_t)sessionId); } } void dropTestLayers(std::map<size_t, std::vector<int>>& sessions, std::vector<int>& layerIds) { for (auto&& association : sessions) { std::erase_if(association.second, [&](int elem) { return std::find(layerIds.begin(), layerIds.end(), elem) != layerIds.end(); }); } } } // namespace android::adpf No newline at end of file
services/surfaceflinger/tests/unittests/SessionManagerTestUtils.h 0 → 100644 +38 −0 Original line number Diff line number Diff line /* * Copyright 2025 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. */ #include "PowerAdvisor/SessionManager.h" #include <gmock/gmock.h> #include <gtest/gtest.h> namespace android::adpf { // Turns the session -> layer map into a layer -> session map std::map<size_t, std::vector<int>> invertAssociations(std::map<size_t, std::vector<int>>& sessions); // Ensures the map associations line up with those expected bool validateAssociations( std::map<size_t, std::vector<int>>& sessions, SessionLayerMap& map); // Get the set of unique layer IDs in a given test session mapping std::set<int> getUniqueLayers(std::map<size_t, std::vector<int>>& sessions); // Get the set of unique session IDs in a given test session mapping std::set<int> getUniqueSessions(std::map<size_t, std::vector<int>>& sessions); // Drops a set of layer IDs from the test session mapping void dropTestLayers(std::map<size_t, std::vector<int>>& sessions, std::vector<int>& layerIds); // Drops a set of session IDs from the test session mapping void dropTestSessions(std::map<size_t, std::vector<int>>& sessions, std::vector<int>& sessionIds); }; // namespace android::adpf No newline at end of file