Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8b365021 authored by Matt Buckley's avatar Matt Buckley Committed by Android (Google) Code Review
Browse files

Merge "Update SessionLayerMap to detect change sets" into main

parents 71ac6605 ee4111dc
Loading
Loading
Loading
Loading
+35 −21
Original line number Diff line number Diff line
@@ -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
@@ -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;
    }

@@ -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
+42 −15
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
#pragma once

#include <log/log.h>
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
@@ -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()) {
@@ -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;
                }
@@ -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;
                }
@@ -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");
@@ -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
+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
+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
+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