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

Commit 765f583a authored by Junyu Lai's avatar Junyu Lai
Browse files

[MS46] Remove INetworkStatsSession usage from NetworkCycleDataLoader

Use public API instead. Also, since the android.app.usage.NetworkStats
is not friendly for test injection, add several util methods for
test injection.

Test: make RunSettingsLibRoboTests \ ROBOTEST_FILTER=NetworkCycleDataLoaderTest
Bug: 204830222
Change-Id: I1b8328010803050b5e1f176b47654ae18e8e1a0b
parent caaa2364
Loading
Loading
Loading
Loading
+37 −25
Original line number Diff line number Diff line
@@ -16,23 +16,16 @@

package com.android.settingslib.net;

import static android.net.NetworkStatsHistory.FIELD_RX_BYTES;
import static android.net.NetworkStatsHistory.FIELD_TX_BYTES;

import android.annotation.NonNull;
import android.app.usage.NetworkStats;
import android.app.usage.NetworkStatsManager;
import android.content.Context;
import android.net.INetworkStatsService;
import android.net.INetworkStatsSession;
import android.net.NetworkPolicy;
import android.net.NetworkPolicyManager;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
import android.net.TrafficStats;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.text.format.DateUtils;
import android.util.Pair;
import android.util.Range;

import androidx.annotation.VisibleForTesting;
import androidx.loader.content.AsyncTaskLoader;
@@ -52,8 +45,6 @@ public abstract class NetworkCycleDataLoader<D> extends AsyncTaskLoader<D> {
    protected final NetworkTemplate mNetworkTemplate;
    private final NetworkPolicy mPolicy;
    private final ArrayList<Long> mCycles;
    @VisibleForTesting
    final INetworkStatsService mNetworkStatsService;

    protected NetworkCycleDataLoader(Builder<?> builder) {
        super(builder.mContext);
@@ -61,8 +52,6 @@ public abstract class NetworkCycleDataLoader<D> extends AsyncTaskLoader<D> {
        mCycles = builder.mCycles;
        mNetworkStatsManager = (NetworkStatsManager)
            builder.mContext.getSystemService(Context.NETWORK_STATS_SERVICE);
        mNetworkStatsService = INetworkStatsService.Stub.asInterface(
            ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
        final NetworkPolicyEditor policyEditor =
            new NetworkPolicyEditor(NetworkPolicyManager.from(builder.mContext));
        policyEditor.read();
@@ -112,23 +101,20 @@ public abstract class NetworkCycleDataLoader<D> extends AsyncTaskLoader<D> {

    @VisibleForTesting
    void loadFourWeeksData() {
        if (mNetworkTemplate == null) return;
        final NetworkStats stats = mNetworkStatsManager.queryDetailsForDevice(
                mNetworkTemplate, Long.MIN_VALUE, Long.MAX_VALUE);
        try {
            final INetworkStatsSession networkSession = mNetworkStatsService.openSession();
            final NetworkStatsHistory networkHistory = networkSession.getHistoryForNetwork(
                mNetworkTemplate, FIELD_RX_BYTES | FIELD_TX_BYTES);
            final long historyStart = networkHistory.getStart();
            final long historyEnd = networkHistory.getEnd();

            long cycleEnd = historyEnd;
            while (cycleEnd > historyStart) {
            final Range<Long> historyTimeRange = getTimeRangeOf(stats);

            long cycleEnd = historyTimeRange.getUpper();
            while (cycleEnd > historyTimeRange.getLower()) {
                final long cycleStart = cycleEnd - (DateUtils.WEEK_IN_MILLIS * 4);
                recordUsage(cycleStart, cycleEnd);
                cycleEnd = cycleStart;
            }

            TrafficStats.closeQuietly(networkSession);
        } catch (RemoteException e) {
            throw new RuntimeException(e);
        } catch (IllegalArgumentException e) {
            // Empty history, ignore.
        }
    }

@@ -169,6 +155,32 @@ public abstract class NetworkCycleDataLoader<D> extends AsyncTaskLoader<D> {
        return bytes;
    }

    @NonNull
    @VisibleForTesting
    Range getTimeRangeOf(@NonNull NetworkStats stats) {
        long start = Long.MAX_VALUE;
        long end = Long.MIN_VALUE;
        while (hasNextBucket(stats)) {
            final NetworkStats.Bucket bucket = getNextBucket(stats);
            start = Math.min(start, bucket.getStartTimeStamp());
            end = Math.max(end, bucket.getEndTimeStamp());
        }
        return new Range(start, end);
    }

    @VisibleForTesting
    boolean hasNextBucket(@NonNull NetworkStats stats) {
        return stats.hasNextBucket();
    }

    @NonNull
    @VisibleForTesting
    NetworkStats.Bucket getNextBucket(@NonNull NetworkStats stats) {
        NetworkStats.Bucket bucket = new NetworkStats.Bucket();
        stats.getNextBucket(bucket);
        return bucket;
    }

    @VisibleForTesting(otherwise = VisibleForTesting.NONE)
    public ArrayList<Long> getCycles() {
        return mCycles;
+57 −18
Original line number Diff line number Diff line
@@ -16,24 +16,24 @@

package com.android.settingslib.net;

import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.nullable;
import static android.app.usage.NetworkStats.Bucket.UID_ALL;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThrows;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.annotation.NonNull;
import android.app.usage.NetworkStats;
import android.app.usage.NetworkStatsManager;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.INetworkStatsService;
import android.net.INetworkStatsSession;
import android.net.NetworkPolicy;
import android.net.NetworkPolicyManager;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
import android.os.RemoteException;
import android.text.format.DateUtils;
import android.util.Range;

@@ -49,6 +49,8 @@ import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;

@RunWith(RobolectricTestRunner.class)
public class NetworkCycleDataLoaderTest {
@@ -63,8 +65,6 @@ public class NetworkCycleDataLoaderTest {
    private NetworkPolicy mPolicy;
    @Mock
    private Iterator<Range<ZonedDateTime>> mIterator;
    @Mock
    private INetworkStatsService mNetworkStatsService;

    private NetworkCycleDataTestLoader mLoader;

@@ -132,20 +132,24 @@ public class NetworkCycleDataLoaderTest {
        verify(mLoader).recordUsage(nowInMs, nowInMs);
    }

    private NetworkStats.Bucket makeMockBucket(int uid, long rxBytes, long txBytes,
            long start, long end) {
        NetworkStats.Bucket ret = mock(NetworkStats.Bucket.class);
        when(ret.getUid()).thenReturn(uid);
        when(ret.getRxBytes()).thenReturn(rxBytes);
        when(ret.getTxBytes()).thenReturn(txBytes);
        when(ret.getStartTimeStamp()).thenReturn(start);
        when(ret.getEndTimeStamp()).thenReturn(end);
        return ret;
    }

    @Test
    public void loadFourWeeksData_shouldRecordUsageForLast4Weeks() throws RemoteException {
    public void loadFourWeeksData_shouldRecordUsageForLast4Weeks() {
        mLoader = spy(new NetworkCycleDataTestLoader(mContext));
        ReflectionHelpers.setField(mLoader, "mNetworkStatsService", mNetworkStatsService);
        final INetworkStatsSession networkSession = mock(INetworkStatsSession.class);
        when(mNetworkStatsService.openSession()).thenReturn(networkSession);
        final NetworkStatsHistory networkHistory = mock(NetworkStatsHistory.class);
        when(networkSession.getHistoryForNetwork(nullable(NetworkTemplate.class), anyInt()))
            .thenReturn(networkHistory);
        final long now = System.currentTimeMillis();
        final long fourWeeksAgo = now - (DateUtils.WEEK_IN_MILLIS * 4);
        final long twoDaysAgo = now - (DateUtils.DAY_IN_MILLIS * 2);
        when(networkHistory.getStart()).thenReturn(twoDaysAgo);
        when(networkHistory.getEnd()).thenReturn(now);
        mLoader.addBucket(makeMockBucket(UID_ALL, 123, 456, twoDaysAgo, now));

        mLoader.loadFourWeeksData();

@@ -173,10 +177,31 @@ public class NetworkCycleDataLoaderTest {
        verify(mLoader).recordUsage(thirtyDaysAgo, twentyDaysAgo);
    }

    @Test
    public void getTimeRangeOf() {
        mLoader = spy(new NetworkCycleDataTestLoader(mContext));
        // If empty, new Range(MAX_VALUE, MIN_VALUE) will be constructed. Hence, the function
        // should throw.
        assertThrows(IllegalArgumentException.class,
                () -> mLoader.getTimeRangeOf(mock(NetworkStats.class)));

        mLoader.addBucket(makeMockBucket(UID_ALL, 123, 456, 0, 10));
        // Feed the function with unused NetworkStats. The actual data injection is
        // done by addBucket.
        assertEquals(new Range(0L, 10L), mLoader.getTimeRangeOf(mock(NetworkStats.class)));

        mLoader.addBucket(makeMockBucket(UID_ALL, 123, 456, 0, 10));
        mLoader.addBucket(makeMockBucket(UID_ALL, 123, 456, 30, 40));
        mLoader.addBucket(makeMockBucket(UID_ALL, 123, 456, 10, 25));
        assertEquals(new Range(0L, 40L), mLoader.getTimeRangeOf(mock(NetworkStats.class)));
    }

    public class NetworkCycleDataTestLoader extends NetworkCycleDataLoader<List<NetworkCycleData>> {
        private final Queue<NetworkStats.Bucket> mMockedBuckets = new LinkedBlockingQueue<>();

        private NetworkCycleDataTestLoader(Context context) {
            super(NetworkCycleDataLoader.builder(mContext));
            super(NetworkCycleDataLoader.builder(mContext)
                    .setNetworkTemplate(mock(NetworkTemplate.class)));
            mContext = context;
        }

@@ -188,5 +213,19 @@ public class NetworkCycleDataLoaderTest {
        List<NetworkCycleData> getCycleUsage() {
            return null;
        }

        public void addBucket(NetworkStats.Bucket bucket) {
            mMockedBuckets.add(bucket);
        }

        @Override
        public boolean hasNextBucket(@NonNull NetworkStats unused) {
            return !mMockedBuckets.isEmpty();
        }

        @Override
        public NetworkStats.Bucket getNextBucket(@NonNull NetworkStats unused) {
            return mMockedBuckets.remove();
        }
    }
}