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

Commit 9b3c8529 authored by Vova Sharaienko's avatar Vova Sharaienko Committed by Android (Google) Code Review
Browse files

Merge "MobileBytesTransferByProcState: used alternative stats query approach" into main

parents 0dd7ab61 630d2f6c
Loading
Loading
Loading
Loading
+119 −24
Original line number Diff line number Diff line
@@ -16,14 +16,28 @@

package com.android.server.stats.pull;

import static android.net.NetworkStats.METERED_YES;
import static android.net.NetworkTemplate.MATCH_MOBILE;
import static android.provider.Settings.Global.NETSTATS_UID_BUCKET_DURATION;

import static com.android.server.stats.Flags.useNetworkStatsQuerySummary;
import static com.android.server.stats.pull.netstats.NetworkStatsUtils.fromPublicNetworkStats;

import static java.util.concurrent.TimeUnit.HOURS;
import static java.util.concurrent.TimeUnit.MICROSECONDS;

import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.StatsManager;
import android.app.usage.NetworkStatsManager;
import android.content.Context;
import android.net.NetworkStats;
import android.net.NetworkTemplate;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.SystemClock;
import android.os.Trace;
import android.provider.Settings;
import android.util.ArrayMap;
import android.util.Slog;
import android.util.SparseIntArray;
@@ -32,6 +46,7 @@ import android.util.StatsEvent;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.selinux.RateLimiter;
import com.android.server.stats.pull.netstats.NetworkStatsAccumulator;

import java.time.Duration;
import java.util.List;
@@ -45,6 +60,8 @@ class AggregatedMobileDataStatsPuller {

    private static final boolean DEBUG = false;

    private static final long NETSTATS_UID_DEFAULT_BUCKET_DURATION_MS = HOURS.toMillis(2);

    private static class UidProcState {

        private final int mUid;
@@ -57,8 +74,12 @@ class AggregatedMobileDataStatsPuller {

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (!(o instanceof UidProcState key)) return false;
            if (this == o) {
                return true;
            }
            if (!(o instanceof UidProcState key)) {
                return false;
            }
            return mUid == key.mUid && mState == key.mState;
        }

@@ -76,7 +97,6 @@ class AggregatedMobileDataStatsPuller {
        public int getState() {
            return mState;
        }

    }

    private static class MobileDataStats {
@@ -128,7 +148,6 @@ class AggregatedMobileDataStatsPuller {

    // No reason to keep more dimensions than 3000. The 3000 is the hard top for the statsd metrics
    // dimensions guardrail. It also will keep the result binder transaction size capped to
    // approximately 220kB for 3000 atoms
    private static final int UID_STATS_MAX_SIZE = 3000;

    private final SparseIntArray mUidPreviousState;
@@ -141,17 +160,54 @@ class AggregatedMobileDataStatsPuller {

    private final RateLimiter mRateLimiter;

    AggregatedMobileDataStatsPuller(@NonNull NetworkStatsManager networkStatsManager) {
        if (DEBUG && Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
    private final Context mContext;

    private final NetworkStatsAccumulator mStatsAccumulator;

    /**
     * Polling NetworkStats is a heavy operation and it should be done sparingly. Atom pulls may
     * happen in bursts, but these should be infrequent. The poll rate limit ensures that data is
     * sufficiently fresh (i.e. not stale) while reducing system load during atom pull bursts.
     */
    private static final long NETSTATS_POLL_RATE_LIMIT_MS = 15000;
    private long mLastNetworkStatsPollTime = -NETSTATS_POLL_RATE_LIMIT_MS;

    AggregatedMobileDataStatsPuller(@NonNull Context context) {
        final boolean traceEnabled = Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER);
        if (traceEnabled) {
            Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, TAG + "-Init");
        }

        mRateLimiter = new RateLimiter(/* window= */ Duration.ofSeconds(1));
        mContext = context;

        mRateLimiter = new RateLimiter(/* window= */ Duration.ofSeconds(2));

        mUidStats = new ArrayMap<>();
        mUidPreviousState = new SparseIntArray();

        mNetworkStatsManager = networkStatsManager;
        final long elapsedMillisSinceBoot = SystemClock.elapsedRealtime();
        final long currentTimeMillis = MICROSECONDS.toMillis(SystemClock.currentTimeMicro());
        final long bootTimeMillis = currentTimeMillis - elapsedMillisSinceBoot;
        final long bucketDurationMillis =
                Settings.Global.getLong(
                        mContext.getContentResolver(),
                        NETSTATS_UID_BUCKET_DURATION,
                        NETSTATS_UID_DEFAULT_BUCKET_DURATION_MS);

        mNetworkStatsManager = mContext.getSystemService(NetworkStatsManager.class);

        if (useNetworkStatsQuerySummary()) {
            NetworkTemplate template =
                    new NetworkTemplate.Builder(MATCH_MOBILE).setMeteredness(METERED_YES).build();
            mStatsAccumulator =
                    new NetworkStatsAccumulator(
                            template,
                            false,
                            bucketDurationMillis,
                            bootTimeMillis - bucketDurationMillis);
        } else {
            mStatsAccumulator = null;
        }

        HandlerThread mMobileDataStatsHandlerThread = new HandlerThread("MobileDataStatsHandler");
        mMobileDataStatsHandlerThread.start();
@@ -163,7 +219,7 @@ class AggregatedMobileDataStatsPuller {
                        updateNetworkStats(mNetworkStatsManager);
                    });
        }
        if (DEBUG) {
        if (traceEnabled) {
            Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
        }
    }
@@ -191,14 +247,14 @@ class AggregatedMobileDataStatsPuller {
    @GuardedBy("mLock")
    private MobileDataStats getUidStatsForPreviousStateLocked(int uid) {
        final int previousState = mUidPreviousState.get(uid, ActivityManager.PROCESS_STATE_UNKNOWN);
        final UidProcState statsKey = new UidProcState(uid, previousState);
        if (mUidStats.containsKey(statsKey)) {
            return mUidStats.get(statsKey);
        } else {
            if (DEBUG && previousState == ActivityManager.PROCESS_STATE_UNKNOWN) {
                Slog.d(TAG, "getUidStatsForPreviousStateLocked() no prev state info for uid "
                        + uid + ". Tracking stats with ActivityManager.PROCESS_STATE_UNKNOWN");
            }

        final UidProcState statsKey = new UidProcState(uid, previousState);
        if (mUidStats.containsKey(statsKey)) {
            return mUidStats.get(statsKey);
        }
        if (mUidStats.size() < UID_STATS_MAX_SIZE) {
            MobileDataStats stats = new MobileDataStats();
@@ -215,11 +271,13 @@ class AggregatedMobileDataStatsPuller {
        // noteUidProcessStateImpl can be called back to back several times while
        // the updateNetworkStats loops over several stats for multiple uids
        // and during the first call in a batch of proc state change event it can
        // contain info for uid with unknown previous state yet which can happen due to a few
        // contain info for uid with unknown previous state yet which can happen due to
        // a few
        // reasons:
        // - app was just started
        // - app was started before the ActivityManagerService
        // as result stats would be created with state == ActivityManager.PROCESS_STATE_UNKNOWN
        // as result stats would be created with state ==
        // ActivityManager.PROCESS_STATE_UNKNOWN
        if (mNetworkStatsManager != null) {
            updateNetworkStats(mNetworkStatsManager);
        } else {
@@ -230,12 +288,44 @@ class AggregatedMobileDataStatsPuller {
        }
    }

    private NetworkStats getMobileUidStats(NetworkStatsManager networkStatsManager) {
        if (useNetworkStatsQuerySummary()) {
            // networkStatsManager.querySummary provides reduced data resolution
            // and higher latency for fresh data compared to networkStatsManager.getMobileUidStats.
            // On the other hand getMobileUidStats provides realtime data in the memory
            // with higher performance cost.
            // Assumption is the tradeoff is acceptable in regards to existing
            // atom MOBILE_BYTES_TRANSFER use case
            final long elapsedMillisSinceBoot = SystemClock.elapsedRealtime();
            if (elapsedMillisSinceBoot - mLastNetworkStatsPollTime >= NETSTATS_POLL_RATE_LIMIT_MS) {
                mLastNetworkStatsPollTime = elapsedMillisSinceBoot;
                Slog.d(TAG, "getMobileUidStats() forceUpdate");
                networkStatsManager.forceUpdate();
            }

            final long currentTimeMillis = MICROSECONDS.toMillis(SystemClock.currentTimeMicro());
            return mStatsAccumulator.queryStats(
                    currentTimeMillis,
                    (aTemplate, aIncludeTags, aStartTime, aEndTime) -> {
                        final android.app.usage.NetworkStats queryNonTaggedStats =
                                networkStatsManager.querySummary(aTemplate, aStartTime, aEndTime);
                        final NetworkStats nonTaggedStats =
                                fromPublicNetworkStats(queryNonTaggedStats);
                        queryNonTaggedStats.close();
                        return nonTaggedStats;
                    });
        } else {
            return networkStatsManager.getMobileUidStats();
        }
    }

    private void updateNetworkStats(NetworkStatsManager networkStatsManager) {
        if (DEBUG && Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
        final boolean traceEnabled = Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER);
        if (traceEnabled) {
            Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, TAG + "-updateNetworkStats");
        }

        final NetworkStats latestStats = networkStatsManager.getMobileUidStats();
        final NetworkStats latestStats = getMobileUidStats(networkStatsManager);
        if (isEmpty(latestStats)) {
            if (DEBUG) {
                Slog.w(TAG, "getMobileUidStats() failed");
@@ -243,6 +333,10 @@ class AggregatedMobileDataStatsPuller {
            }
            return;
        }
        if (DEBUG) {
            Slog.d(TAG, "latestStats: \n" + latestStats.toString());
        }

        NetworkStats delta = latestStats.subtract(mLastMobileUidStats);
        mLastMobileUidStats = latestStats;

@@ -251,18 +345,22 @@ class AggregatedMobileDataStatsPuller {
        } else if (DEBUG) {
            Slog.w(TAG, "updateNetworkStats() no delta");
        }
        if (DEBUG) {
        if (traceEnabled) {
            Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
        }
    }

    private void updateNetworkStatsDelta(NetworkStats delta) {
        if (DEBUG && Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
        final boolean traceEnabled = DEBUG && Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER);
        if (traceEnabled) {
            Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, TAG + "-updateNetworkStatsDelta");
        }
        synchronized (mLock) {
            for (NetworkStats.Entry entry : delta) {
                if (entry.getRxPackets() != 0 || entry.getTxPackets() != 0) {
                    if (DEBUG) {
                        Slog.d(TAG, entry.toString());
                    }
                    MobileDataStats stats = getUidStatsForPreviousStateLocked(entry.getUid());
                    if (stats != null) {
                        stats.addTxBytes(entry.getTxBytes());
@@ -273,16 +371,13 @@ class AggregatedMobileDataStatsPuller {
                }
            }
        }
        if (DEBUG) {
        if (traceEnabled) {
            Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
        }
    }

    @GuardedBy("mLock")
    private int pullDataBytesTransferLocked(List<StatsEvent> pulledData) {
        if (DEBUG) {
            Slog.d(TAG, "pullDataBytesTransferLocked() start");
        }
        for (Map.Entry<UidProcState, MobileDataStats> uidStats : mUidStats.entrySet()) {
            if (!uidStats.getValue().isEmpty()) {
                MobileDataStats stats = uidStats.getValue();
+1 −3
Original line number Diff line number Diff line
@@ -1071,9 +1071,7 @@ public class StatsPullAtomService extends SystemService {
                            + ENABLE_MOBILE_DATA_STATS_AGGREGATED_PULLER);
        }
        if (ENABLE_MOBILE_DATA_STATS_AGGREGATED_PULLER) {
            mAggregatedMobileDataStatsPuller =
                    new AggregatedMobileDataStatsPuller(
                            mContext.getSystemService(NetworkStatsManager.class));
            mAggregatedMobileDataStatsPuller = new AggregatedMobileDataStatsPuller(mContext);
        }
    }

+9 −1
Original line number Diff line number Diff line
@@ -46,3 +46,11 @@ flag {
    bug: "365731097"
    is_fixed_read_only: true
}

flag {
    name: "use_network_stats_query_summary"
    namespace: "statsd"
    description: "Use NetworkStats querySummary API for AggregatedMobileDataStatsPuller"
    bug: "398327159"
    is_fixed_read_only: true
}