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

Commit 7d2ddbee authored by Prabir Pradhan's avatar Prabir Pradhan Committed by Automerger Merge Worker
Browse files

Merge changes from topics "breakdown-usage-by-uid",...

Merge changes from topics "breakdown-usage-by-uid", "notify-device-interaction" into udc-qpr-dev am: e632c7cb

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/native/+/23353721



Change-Id: Ia13d27f3d4ca7cb84be2d70976a2f324596111cf
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents 21f2fc5e e632c7cb
Loading
Loading
Loading
Loading
+72 −5
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ namespace android {

using android::base::StringPrintf;
using std::chrono::nanoseconds;
using std::chrono_literals::operator""ns;

namespace {

@@ -72,10 +73,19 @@ class : public InputDeviceMetricsLogger {
                     ftl::enum_string(src).c_str(), durMillis);
        }

        ALOGD_IF(DEBUG, "    Uid breakdown:");

        std::vector<int32_t> uids;
        std::vector<int32_t> durationsPerUid;
        for (auto& [uid, dur] : report.uidBreakdown) {
            uids.push_back(uid);
            int32_t durMillis = std::chrono::duration_cast<std::chrono::milliseconds>(dur).count();
            durationsPerUid.push_back(durMillis);
            ALOGD_IF(DEBUG, "        - uid: %d\t duration: %dms", uid, durMillis);
        }
        util::stats_write(util::INPUTDEVICE_USAGE_REPORTED, identifier.vendor, identifier.product,
                          identifier.version, linuxBusToInputDeviceBusEnum(identifier.bus),
                          durationMillis, sources, durationsPerSource, /*uids=*/empty,
                          /*usage_durations_per_uid=*/empty);
                          durationMillis, sources, durationsPerSource, uids, durationsPerUid);
    }
} sStatsdLogger;

@@ -248,7 +258,11 @@ void InputDeviceMetricsCollector::notifyPointerCaptureChanged(

void InputDeviceMetricsCollector::notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp,
                                                          const std::set<int32_t>& uids) {
    // TODO: Implement.
    std::set<Uid> typeSafeUids;
    for (auto uid : uids) {
        typeSafeUids.emplace(uid);
    }
    mInteractionsQueue.push(DeviceId{deviceId}, timestamp, typeSafeUids);
}

void InputDeviceMetricsCollector::dump(std::string& dump) {
@@ -309,17 +323,33 @@ void InputDeviceMetricsCollector::onInputDeviceUsage(DeviceId deviceId, nanoseco
    }
}

void InputDeviceMetricsCollector::onInputDeviceInteraction(const Interaction& interaction) {
    auto activeSessionIt = mActiveUsageSessions.find(std::get<DeviceId>(interaction));
    if (activeSessionIt == mActiveUsageSessions.end()) {
        return;
    }

    activeSessionIt->second.recordInteraction(interaction);
}

void InputDeviceMetricsCollector::reportCompletedSessions() {
    const auto currentTime = mLogger.getCurrentTime();
    // Process all pending interactions.
    for (auto interaction = mInteractionsQueue.pop(); interaction;
         interaction = mInteractionsQueue.pop()) {
        onInputDeviceInteraction(*interaction);
    }

    const auto currentTime = mLogger.getCurrentTime();
    std::vector<DeviceId> completedUsageSessions;

    // Process usages for all active session to determine if any sessions have expired.
    for (auto& [deviceId, activeSession] : mActiveUsageSessions) {
        if (activeSession.checkIfCompletedAt(currentTime)) {
            completedUsageSessions.emplace_back(deviceId);
        }
    }

    // Close out and log all expired usage sessions.
    for (DeviceId deviceId : completedUsageSessions) {
        const auto infoIt = mLoggedDeviceInfos.find(deviceId);
        LOG_ALWAYS_FATAL_IF(infoIt == mLoggedDeviceInfos.end());
@@ -351,6 +381,23 @@ void InputDeviceMetricsCollector::ActiveSession::recordUsage(nanoseconds eventTi
    mDeviceSession.end = eventTime;
}

void InputDeviceMetricsCollector::ActiveSession::recordInteraction(const Interaction& interaction) {
    const auto sessionExpiryTime = mDeviceSession.end + mUsageSessionTimeout;
    const auto timestamp = std::get<nanoseconds>(interaction);
    if (timestamp >= sessionExpiryTime) {
        // This interaction occurred after the device's current active session is set to expire.
        // Ignore it.
        return;
    }

    for (Uid uid : std::get<std::set<Uid>>(interaction)) {
        auto [activeUidIt, inserted] = mActiveSessionsByUid.try_emplace(uid, timestamp, timestamp);
        if (!inserted) {
            activeUidIt->second.end = timestamp;
        }
    }
}

bool InputDeviceMetricsCollector::ActiveSession::checkIfCompletedAt(nanoseconds timestamp) {
    const auto sessionExpiryTime = timestamp - mUsageSessionTimeout;
    std::vector<InputDeviceUsageSource> completedSourceSessionsForDevice;
@@ -365,6 +412,21 @@ bool InputDeviceMetricsCollector::ActiveSession::checkIfCompletedAt(nanoseconds
        mSourceUsageBreakdown.emplace_back(source, session.end - session.start);
        mActiveSessionsBySource.erase(it);
    }

    std::vector<Uid> completedUidSessionsForDevice;
    for (auto& [uid, session] : mActiveSessionsByUid) {
        if (session.end <= sessionExpiryTime) {
            completedUidSessionsForDevice.emplace_back(uid);
        }
    }
    for (Uid uid : completedUidSessionsForDevice) {
        auto it = mActiveSessionsByUid.find(uid);
        const auto& [_, session] = *it;
        mUidUsageBreakdown.emplace_back(uid, session.end - session.start);
        mActiveSessionsByUid.erase(it);
    }

    // This active session has expired if there are no more active source sessions tracked.
    return mActiveSessionsBySource.empty();
}

@@ -377,7 +439,12 @@ InputDeviceMetricsCollector::ActiveSession::finishSession() {
    }
    mActiveSessionsBySource.clear();

    return {deviceUsageDuration, mSourceUsageBreakdown};
    for (const auto& [uid, uidSession] : mActiveSessionsByUid) {
        mUidUsageBreakdown.emplace_back(uid, uidSession.end - uidSession.start);
    }
    mActiveSessionsByUid.clear();

    return {deviceUsageDuration, mSourceUsageBreakdown, mUidUsageBreakdown};
}

} // namespace android
+22 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@

#include "InputListener.h"
#include "NotifyArgs.h"
#include "SyncQueue.h"

#include <ftl/mixins.h>
#include <input/InputDevice.h>
@@ -98,9 +99,14 @@ public:
    using SourceUsageBreakdown =
            std::vector<std::pair<InputDeviceUsageSource, std::chrono::nanoseconds /*duration*/>>;

    // Describes the breakdown of an input device usage session by the UIDs that it interacted with.
    using UidUsageBreakdown =
            std::vector<std::pair<int32_t /*uid*/, std::chrono::nanoseconds /*duration*/>>;

    struct DeviceUsageReport {
        std::chrono::nanoseconds usageDuration;
        SourceUsageBreakdown sourceBreakdown;
        UidUsageBreakdown uidBreakdown;
    };

    virtual void logInputDeviceUsageReported(const InputDeviceIdentifier&,
@@ -146,13 +152,25 @@ private:
        return std::to_string(ftl::to_underlying(id));
    }

    // Type-safe wrapper for a UID.
    struct Uid : ftl::Constructible<Uid, std::int32_t>, ftl::Equatable<Uid>, ftl::Orderable<Uid> {
        using Constructible::Constructible;
    };
    static inline std::string toString(const Uid& src) {
        return std::to_string(ftl::to_underlying(src));
    }

    std::map<DeviceId, InputDeviceInfo> mLoggedDeviceInfos;

    using Interaction = std::tuple<DeviceId, std::chrono::nanoseconds, std::set<Uid>>;
    SyncQueue<Interaction> mInteractionsQueue;

    class ActiveSession {
    public:
        explicit ActiveSession(std::chrono::nanoseconds usageSessionTimeout,
                               std::chrono::nanoseconds startTime);
        void recordUsage(std::chrono::nanoseconds eventTime, InputDeviceUsageSource source);
        void recordInteraction(const Interaction&);
        bool checkIfCompletedAt(std::chrono::nanoseconds timestamp);
        InputDeviceMetricsLogger::DeviceUsageReport finishSession();

@@ -167,6 +185,9 @@ private:

        std::map<InputDeviceUsageSource, UsageSession> mActiveSessionsBySource{};
        InputDeviceMetricsLogger::SourceUsageBreakdown mSourceUsageBreakdown{};

        std::map<Uid, UsageSession> mActiveSessionsByUid{};
        InputDeviceMetricsLogger::UidUsageBreakdown mUidUsageBreakdown{};
    };

    // The input devices that currently have active usage sessions.
@@ -177,6 +198,7 @@ private:
    using SourceProvider = std::function<std::set<InputDeviceUsageSource>(const InputDeviceInfo&)>;
    void onInputDeviceUsage(DeviceId deviceId, std::chrono::nanoseconds eventTime,
                            const SourceProvider& getSources);
    void onInputDeviceInteraction(const Interaction&);
    void reportCompletedSessions();
};

+150 −2
Original line number Diff line number Diff line
@@ -341,8 +341,9 @@ protected:
    TestInputListener mTestListener;
    InputDeviceMetricsCollector mMetricsCollector{mTestListener, *this, USAGE_TIMEOUT};

    void assertUsageLogged(const InputDeviceIdentifier& identifier, nanoseconds duration,
                           std::optional<SourceUsageBreakdown> sourceBreakdown = {}) {
    void assertUsageLogged(InputDeviceIdentifier identifier, nanoseconds duration,
                           std::optional<SourceUsageBreakdown> sourceBreakdown = {},
                           std::optional<UidUsageBreakdown> uidBreakdown = {}) {
        ASSERT_GE(mLoggedUsageSessions.size(), 1u);
        const auto& [loggedIdentifier, report] = *mLoggedUsageSessions.begin();
        ASSERT_EQ(identifier, loggedIdentifier);
@@ -350,6 +351,9 @@ protected:
        if (sourceBreakdown) {
            ASSERT_EQ(sourceBreakdown, report.sourceBreakdown);
        }
        if (uidBreakdown) {
            ASSERT_EQ(uidBreakdown, report.uidBreakdown);
        }
        mLoggedUsageSessions.erase(mLoggedUsageSessions.begin());
    }

@@ -357,6 +361,8 @@ protected:

    void setCurrentTime(nanoseconds time) { mCurrentTime = time; }

    nsecs_t currentTime() const { return mCurrentTime.count(); }

    NotifyMotionArgs generateMotionArgs(int32_t deviceId,
                                        uint32_t source = AINPUT_SOURCE_TOUCHSCREEN,
                                        std::vector<ToolType> toolTypes = {ToolType::FINGER}) {
@@ -622,4 +628,146 @@ TEST_F(InputDeviceMetricsCollectorTest, BreakdownUsageBySource_MultiSourceEvent)
    ASSERT_NO_FATAL_FAILURE(assertUsageNotLogged());
}

TEST_F(InputDeviceMetricsCollectorTest, UidsNotTrackedWhenThereIsNoActiveSession) {
    mMetricsCollector.notifyInputDevicesChanged({/*id=*/0, {generateTestDeviceInfo()}});

    // Notify interaction with UIDs before the device is used.
    mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), /*uids=*/{1});

    // Use the device.
    setCurrentTime(TIME + 100ns);
    mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
    setCurrentTime(TIME + 200ns);
    mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));

    // Notify interaction for the wrong device.
    mMetricsCollector.notifyDeviceInteraction(DEVICE_ID_2, currentTime(), /*uids=*/{42});

    // Notify interaction after usage session would have expired.
    // This interaction should not be tracked.
    setCurrentTime(TIME + 200ns + USAGE_TIMEOUT);
    mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), /*uids=*/{2, 3});

    // Use the device again, by starting a new usage session.
    setCurrentTime(TIME + 300ns + USAGE_TIMEOUT);
    mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));

    // The first usage session is logged.
    static const UidUsageBreakdown emptyBreakdown;
    ASSERT_NO_FATAL_FAILURE(assertUsageLogged(getIdentifier(), 100ns, /*sourceBreakdown=*/{},
                                              /*uidBreakdown=*/emptyBreakdown));

    ASSERT_NO_FATAL_FAILURE(assertUsageNotLogged());
}

TEST_F(InputDeviceMetricsCollectorTest, BreakdownUsageByUid) {
    mMetricsCollector.notifyInputDevicesChanged({/*id=*/0, {generateTestDeviceInfo()}});
    UidUsageBreakdown expectedUidBreakdown;

    mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
    mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), /*uids=*/{1});

    setCurrentTime(TIME + 100ns);
    mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
    mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), /*uids=*/{1, 2});
    setCurrentTime(TIME + 200ns);
    mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
    mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), /*uids=*/{1, 2, 3});

    expectedUidBreakdown.emplace_back(1, 200ns);
    expectedUidBreakdown.emplace_back(2, 100ns);
    expectedUidBreakdown.emplace_back(3, 0ns);

    // Remove the device to force the usage session to be logged.
    mMetricsCollector.notifyInputDevicesChanged({});
    ASSERT_NO_FATAL_FAILURE(assertUsageLogged(getIdentifier(), 200ns, /*sourceBreakdown=*/{},
                                              expectedUidBreakdown));

    ASSERT_NO_FATAL_FAILURE(assertUsageNotLogged());
}

TEST_F(InputDeviceMetricsCollectorTest, BreakdownUsageByUid_TracksMultipleSessionsForUid) {
    mMetricsCollector.notifyInputDevicesChanged({/*id=*/0, {generateTestDeviceInfo()}});
    UidUsageBreakdown expectedUidBreakdown;

    mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
    mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), /*uids=*/{1, 2});
    setCurrentTime(TIME + 100ns);
    mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
    mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), /*uids=*/{1, 2});

    setCurrentTime(TIME + 200ns);
    mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
    mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), /*uids=*/{1});

    setCurrentTime(TIME + 300ns);
    mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
    mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), /*uids=*/{1, 3});
    setCurrentTime(TIME + 400ns);
    mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
    mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), /*uids=*/{1, 3});

    setCurrentTime(TIME + 200ns + USAGE_TIMEOUT);
    expectedUidBreakdown.emplace_back(2, 100ns);
    mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
    mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), /*uids=*/{4});

    setCurrentTime(TIME + 300ns + USAGE_TIMEOUT);
    mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
    mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), /*uids=*/{1, 4});

    setCurrentTime(TIME + 400ns + USAGE_TIMEOUT);
    expectedUidBreakdown.emplace_back(3, 100ns);
    mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
    mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), /*uids=*/{2, 3});

    setCurrentTime(TIME + 500ns + USAGE_TIMEOUT);
    mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
    mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), /*uids=*/{3});

    // Remove the device to force the usage session to be logged.
    mMetricsCollector.notifyInputDevicesChanged({});
    expectedUidBreakdown.emplace_back(1, 300ns + USAGE_TIMEOUT);
    expectedUidBreakdown.emplace_back(2, 0ns);
    expectedUidBreakdown.emplace_back(3, 100ns);
    expectedUidBreakdown.emplace_back(4, 100ns);
    ASSERT_NO_FATAL_FAILURE(assertUsageLogged(getIdentifier(), 500ns + USAGE_TIMEOUT,
                                              /*sourceBreakdown=*/{}, expectedUidBreakdown));

    ASSERT_NO_FATAL_FAILURE(assertUsageNotLogged());
}

TEST_F(InputDeviceMetricsCollectorTest, BreakdownUsageByUid_TracksUidsByDevice) {
    mMetricsCollector.notifyInputDevicesChanged(
            {/*id=*/0, {generateTestDeviceInfo(DEVICE_ID), generateTestDeviceInfo(DEVICE_ID_2)}});
    UidUsageBreakdown expectedUidBreakdown1;
    UidUsageBreakdown expectedUidBreakdown2;

    mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
    mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), /*uids=*/{1, 2});

    setCurrentTime(TIME + 100ns);
    mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID_2));
    mMetricsCollector.notifyDeviceInteraction(DEVICE_ID_2, currentTime(), /*uids=*/{1, 3});

    setCurrentTime(TIME + 200ns);
    mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
    mMetricsCollector.notifyDeviceInteraction(DEVICE_ID, currentTime(), /*uids=*/{1, 2});
    mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID_2));
    mMetricsCollector.notifyDeviceInteraction(DEVICE_ID_2, currentTime(), /*uids=*/{1, 3});

    setCurrentTime(TIME + 200ns + USAGE_TIMEOUT);
    expectedUidBreakdown1.emplace_back(1, 200ns);
    expectedUidBreakdown1.emplace_back(2, 200ns);
    expectedUidBreakdown2.emplace_back(1, 100ns);
    expectedUidBreakdown2.emplace_back(3, 100ns);
    mMetricsCollector.notifyMotion(generateMotionArgs(DEVICE_ID));
    ASSERT_NO_FATAL_FAILURE(assertUsageLogged(getIdentifier(DEVICE_ID), 200ns,
                                              /*sourceBreakdown=*/{}, expectedUidBreakdown1));
    ASSERT_NO_FATAL_FAILURE(assertUsageLogged(getIdentifier(DEVICE_ID_2), 100ns,
                                              /*sourceBreakdown=*/{}, expectedUidBreakdown2));

    ASSERT_NO_FATAL_FAILURE(assertUsageNotLogged());
}

} // namespace android