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

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

Merge "Added puller impl for mobile data transfered per uid by proc_state" into main

parents 7d02126b ac7a6110
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -214,6 +214,7 @@ java_library_static {
        "backup_flags_lib",
        "policy_flags_lib",
        "net_flags_lib",
        "stats_flags_lib",
    ],
    javac_shard_size: 50,
    javacflags: [
+19 −0
Original line number Diff line number Diff line
@@ -472,6 +472,8 @@ import com.android.server.pm.pkg.SELinuxUtil;
import com.android.server.pm.snapshot.PackageDataSnapshot;
import com.android.server.power.stats.BatteryStatsImpl;
import com.android.server.sdksandbox.SdkSandboxManagerLocal;
import com.android.server.stats.pull.StatsPullAtomService;
import com.android.server.stats.pull.StatsPullAtomServiceInternal;
import com.android.server.uri.GrantUri;
import com.android.server.uri.NeededUriGrants;
import com.android.server.uri.UriGrantsManagerInternal;
@@ -1308,6 +1310,8 @@ public class ActivityManagerService extends IActivityManager.Stub
     */
    final BatteryStatsService mBatteryStatsService;
    StatsPullAtomServiceInternal mStatsPullAtomServiceInternal;
    /**
     * Information about component usage
     */
@@ -16554,6 +16558,21 @@ public class ActivityManagerService extends IActivityManager.Stub
                final @ProcessCapability int capability) {
        mBatteryStatsService.noteUidProcessState(uid, state);
        mAppOpsService.updateUidProcState(uid, state, capability);
        if (StatsPullAtomService.ENABLE_MOBILE_DATA_STATS_AGGREGATED_PULLER) {
            try {
                if (mStatsPullAtomServiceInternal == null) {
                    mStatsPullAtomServiceInternal = LocalServices.getService(
                            StatsPullAtomServiceInternal.class);
                }
                if (mStatsPullAtomServiceInternal != null) {
                    mStatsPullAtomServiceInternal.noteUidProcessState(uid, state);
                } else {
                    Slog.d(TAG, "StatsPullAtomService not ready yet");
                }
            } catch (Exception e) {
                Slog.e(TAG, "Exception during logging uid proc state change event", e);
            }
        }
        if (mTrackingAssociations) {
            for (int i1=0, N1=mAssociations.size(); i1<N1; i1++) {
                ArrayMap<ComponentName, SparseArray<ArrayMap<String, Association>>> targetComponents
+12 −0
Original line number Diff line number Diff line
aconfig_declarations {
    name: "stats_flags",
    package: "com.android.server.stats",
    srcs: [
        "stats_flags.aconfig",
    ],
}

java_aconfig_library {
    name: "stats_flags_lib",
    aconfig_declarations: "stats_flags",
}
+285 −0
Original line number Diff line number Diff line
/*
 * Copyright 2024 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.
 */

package com.android.server.stats.pull;

import android.app.ActivityManager;
import android.app.StatsManager;
import android.app.usage.NetworkStatsManager;
import android.net.NetworkStats;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Trace;
import android.util.ArrayMap;
import android.util.Slog;
import android.util.SparseIntArray;
import android.util.StatsEvent;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.FrameworkStatsLog;

import java.util.List;
import java.util.Map;

/**
 * Aggregates Mobile Data Usage by process state per uid
 */
class AggregatedMobileDataStatsPuller {
    private static final String TAG = "AggregatedMobileDataStatsPuller";

    private static final boolean DEBUG = false;

    private static class UidProcState {

        private final int mUid;
        private final int mState;

        UidProcState(int uid, int state) {
            mUid = uid;
            mState = state;
        }

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

        @Override
        public int hashCode() {
            int result = mUid;
            result = 31 * result + mState;
            return result;
        }

        public int getUid() {
            return mUid;
        }

        public int getState() {
            return mState;
        }

    }

    private static class MobileDataStats {
        private long mRxPackets = 0;
        private long mTxPackets = 0;
        private long mRxBytes = 0;
        private long mTxBytes = 0;

        public long getRxPackets() {
            return mRxPackets;
        }

        public long getTxPackets() {
            return mTxPackets;
        }

        public long getRxBytes() {
            return mRxBytes;
        }

        public long getTxBytes() {
            return mTxBytes;
        }

        public void addRxPackets(long rxPackets) {
            mRxPackets += rxPackets;
        }

        public void addTxPackets(long txPackets) {
            mTxPackets += txPackets;
        }

        public void addRxBytes(long rxBytes) {
            mRxBytes += rxBytes;
        }

        public void addTxBytes(long txBytes) {
            mTxBytes += txBytes;
        }

        public boolean isEmpty() {
            return mRxPackets == 0 && mTxPackets == 0 && mRxBytes == 0 && mTxBytes == 0;
        }
    }

    private final Object mLock = new Object();
    @GuardedBy("mLock")
    private final Map<UidProcState, MobileDataStats> mUidStats;

    private final SparseIntArray mUidPreviousState;

    private NetworkStats mLastMobileUidStats = new NetworkStats(0, -1);

    private final NetworkStatsManager mNetworkStatsManager;

    private final Handler mMobileDataStatsHandler;

    AggregatedMobileDataStatsPuller(NetworkStatsManager networkStatsManager) {
        mUidStats = new ArrayMap<>();
        mUidPreviousState = new SparseIntArray();

        mNetworkStatsManager = networkStatsManager;

        if (mNetworkStatsManager != null) {
            updateNetworkStats(mNetworkStatsManager);
        }

        HandlerThread mMobileDataStatsHandlerThread = new HandlerThread("MobileDataStatsHandler");
        mMobileDataStatsHandlerThread.start();
        mMobileDataStatsHandler = new Handler(mMobileDataStatsHandlerThread.getLooper());
    }

    public void noteUidProcessState(int uid, int state, long unusedElapsedRealtime,
                                    long unusedUptime) {
        mMobileDataStatsHandler.post(
                () -> {
                    noteUidProcessStateImpl(uid, state);
                });
    }

    public int pullDataBytesTransfer(List<StatsEvent> data) {
        synchronized (mLock) {
            return pullDataBytesTransferLocked(data);
        }
    }

    @GuardedBy("mLock")
    private MobileDataStats getUidStatsForPreviousStateLocked(int uid) {
        final int previousState = mUidPreviousState.get(uid, ActivityManager.PROCESS_STATE_UNKNOWN);
        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);
        MobileDataStats stats;
        if (mUidStats.containsKey(statsKey)) {
            stats = mUidStats.get(statsKey);
        } else {
            stats = new MobileDataStats();
            mUidStats.put(statsKey, stats);
        }
        return stats;
    }

    private void noteUidProcessStateImpl(int uid, int state) {
        // noteUidProcessStateLocked can be called back to back several times while
        // the updateNetworkStatsLocked 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
        // reasons:
        // - app was just started
        // - app was started before the ActivityManagerService
        // as result stats would be created with state == ActivityManager.PROCESS_STATE_UNKNOWN
        if (mNetworkStatsManager != null) {
            updateNetworkStats(mNetworkStatsManager);
        } else {
            Slog.w(TAG, "noteUidProcessStateLocked() can not get mNetworkStatsManager");
        }
        mUidPreviousState.put(uid, state);
    }

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

        final NetworkStats latestStats = networkStatsManager.getMobileUidStats();
        if (isEmpty(latestStats)) {
            if (DEBUG) {
                Slog.w(TAG, "getMobileUidStats() failed");
                Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
            }
            return;
        }
        NetworkStats delta = latestStats.subtract(mLastMobileUidStats);
        mLastMobileUidStats = latestStats;

        if (!isEmpty(delta)) {
            updateNetworkStatsDelta(delta);
        } else if (DEBUG) {
            Slog.w(TAG, "updateNetworkStats() no delta");
        }
        if (DEBUG) {
            Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
        }
    }

    private void updateNetworkStatsDelta(NetworkStats delta) {
        synchronized (mLock) {
            for (NetworkStats.Entry entry : delta) {
                if (entry.getRxPackets() == 0 && entry.getTxPackets() == 0) {
                    continue;
                }
                MobileDataStats stats = getUidStatsForPreviousStateLocked(entry.getUid());
                stats.addTxBytes(entry.getTxBytes());
                stats.addRxBytes(entry.getRxBytes());
                stats.addTxPackets(entry.getTxPackets());
                stats.addRxPackets(entry.getRxPackets());
            }
        }
    }

    @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();
                pulledData.add(FrameworkStatsLog.buildStatsEvent(
                        FrameworkStatsLog.MOBILE_BYTES_TRANSFER_BY_PROC_STATE,
                        uidStats.getKey().getUid(),
                        ActivityManager.processStateAmToProto(uidStats.getKey().getState()),
                        stats.getRxBytes(),
                        stats.getRxPackets(),
                        stats.getTxBytes(),
                        stats.getTxPackets()));
            }
        }
        if (DEBUG) {
            Slog.d(TAG,
                    "pullDataBytesTransferLocked() done. results count " + pulledData.size());
        }
        if (!pulledData.isEmpty()) {
            return StatsManager.PULL_SUCCESS;
        }
        return StatsManager.PULL_SKIP;
    }

    private static boolean isEmpty(NetworkStats stats) {
        long totalRxPackets = 0;
        long totalTxPackets = 0;
        for (NetworkStats.Entry entry : stats) {
            if (entry.getRxPackets() == 0 && entry.getTxPackets() == 0) {
                continue;
            }
            totalRxPackets += entry.getRxPackets();
            totalTxPackets += entry.getTxPackets();
            // at least one non empty entry located
            break;
        }
        final long totalPackets = totalRxPackets + totalTxPackets;
        return totalPackets == 0;
    }
}
+58 −1
Original line number Diff line number Diff line
@@ -59,6 +59,7 @@ import static com.android.internal.util.FrameworkStatsLog.TIME_ZONE_DETECTOR_STA
import static com.android.internal.util.FrameworkStatsLog.TIME_ZONE_DETECTOR_STATE__DETECTION_MODE__TELEPHONY;
import static com.android.internal.util.FrameworkStatsLog.TIME_ZONE_DETECTOR_STATE__DETECTION_MODE__UNKNOWN;
import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem;
import static com.android.server.stats.Flags.addMobileBytesTransferByProcStatePuller;
import static com.android.server.stats.pull.IonMemoryUtil.readProcessSystemIonHeapSizesFromDebugfs;
import static com.android.server.stats.pull.IonMemoryUtil.readSystemIonHeapSizeFromDebugfs;
import static com.android.server.stats.pull.ProcfsMemoryUtil.getProcessCmdlines;
@@ -409,6 +410,15 @@ public class StatsPullAtomService extends SystemService {
    @GuardedBy("mKeystoreLock")
    private IKeystoreMetrics mIKeystoreMetrics;

    private AggregatedMobileDataStatsPuller mAggregatedMobileDataStatsPuller = null;

    /**
     * Whether or not to enable the new puller with aggregation by process state per uid on a
     * system server side.
     */
    public static final boolean ENABLE_MOBILE_DATA_STATS_AGGREGATED_PULLER =
                addMobileBytesTransferByProcStatePuller();

    // Puller locks
    private final Object mDataBytesTransferLock = new Object();
    private final Object mBluetoothBytesTransferLock = new Object();
@@ -469,6 +479,20 @@ public class StatsPullAtomService extends SystemService {
        mContext = context;
    }

    private final class StatsPullAtomServiceInternalImpl extends StatsPullAtomServiceInternal {

        @Override
        public void noteUidProcessState(int uid, int state) {
            if (ENABLE_MOBILE_DATA_STATS_AGGREGATED_PULLER
                    && mAggregatedMobileDataStatsPuller != null) {
                final long elapsedRealtime = SystemClock.elapsedRealtime();
                final long uptime = SystemClock.uptimeMillis();
                mAggregatedMobileDataStatsPuller.noteUidProcessState(uid, state, elapsedRealtime,
                        uptime);
            }
        }
    }

    private native void initializeNativePullers();

    /**
@@ -486,6 +510,11 @@ public class StatsPullAtomService extends SystemService {
            }
            try {
                switch (atomTag) {
                    case FrameworkStatsLog.MOBILE_BYTES_TRANSFER_BY_PROC_STATE:
                        if (ENABLE_MOBILE_DATA_STATS_AGGREGATED_PULLER
                                && mAggregatedMobileDataStatsPuller != null) {
                            return mAggregatedMobileDataStatsPuller.pullDataBytesTransfer(data);
                        }
                    case FrameworkStatsLog.WIFI_BYTES_TRANSFER:
                    case FrameworkStatsLog.WIFI_BYTES_TRANSFER_BY_FG_BG:
                    case FrameworkStatsLog.MOBILE_BYTES_TRANSFER:
@@ -776,7 +805,10 @@ public class StatsPullAtomService extends SystemService {

    @Override
    public void onStart() {
        // no op
        if (ENABLE_MOBILE_DATA_STATS_AGGREGATED_PULLER) {
            LocalServices.addService(StatsPullAtomServiceInternal.class,
                    new StatsPullAtomServiceInternalImpl());
        }
    }

    @Override
@@ -811,6 +843,9 @@ public class StatsPullAtomService extends SystemService {
        mStatsSubscriptionsListener = new StatsSubscriptionsListener(mSubscriptionManager);
        mStorageManager = (StorageManager) mContext.getSystemService(StorageManager.class);
        mNetworkStatsManager = mContext.getSystemService(NetworkStatsManager.class);

        initMobileDataStatsPuller();

        // Initialize DiskIO
        mStoragedUidIoStatsReader = new StoragedUidIoStatsReader();

@@ -972,6 +1007,18 @@ public class StatsPullAtomService extends SystemService {
        registerCachedAppsHighWatermarkPuller();
    }

    private void initMobileDataStatsPuller() {
        if (DEBUG) {
            Slog.d(TAG,
                    "ENABLE_MOBILE_DATA_STATS_AGGREGATED_PULLER = "
                            + ENABLE_MOBILE_DATA_STATS_AGGREGATED_PULLER);
        }
        if (ENABLE_MOBILE_DATA_STATS_AGGREGATED_PULLER) {
            mAggregatedMobileDataStatsPuller =
                    new AggregatedMobileDataStatsPuller(mNetworkStatsManager);
        }
    }

    private void initAndRegisterNetworkStatsPullers() {
        if (DEBUG) {
            Slog.d(TAG, "Registering NetworkStats pullers with statsd");
@@ -1013,6 +1060,9 @@ public class StatsPullAtomService extends SystemService {
        registerWifiBytesTransferBackground();
        registerMobileBytesTransfer();
        registerMobileBytesTransferBackground();
        if (ENABLE_MOBILE_DATA_STATS_AGGREGATED_PULLER) {
            registerMobileBytesTransferByProcState();
        }
        registerBytesTransferByTagAndMetered();
        registerDataUsageBytesTransfer();
        registerOemManagedBytesTransfer();
@@ -1021,6 +1071,13 @@ public class StatsPullAtomService extends SystemService {
        }
    }

    private void registerMobileBytesTransferByProcState() {
        final int tagId = FrameworkStatsLog.MOBILE_BYTES_TRANSFER_BY_PROC_STATE;
        PullAtomMetadata metadata =
                new PullAtomMetadata.Builder().setAdditiveFields(new int[] {3, 4, 5, 6}).build();
        mStatsManager.setPullAtomCallback(tagId, metadata, DIRECT_EXECUTOR, mStatsCallbackImpl);
    }

    private void initAndRegisterDeferredPullers() {
        mUwbManager = mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_UWB)
            ? mContext.getSystemService(UwbManager.class) : null;
Loading