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

Commit 489e8b02 authored by Remi NGUYEN VAN's avatar Remi NGUYEN VAN
Browse files

Allow null subscriberId in NetworkStatsManager.

Use a MATCH_MOBILE_WILDCARD template to avoid filtering by
subscriberId when querying statistics from NetworkStatsService.

Bug: 74038898
Change-Id: I4b39e7031416cb33b23d89aa36ff0f774eaa942f
Fixes: 74038898
Test: runtest frameworks-net, CTS tests pass
parent 6455e31b
Loading
Loading
Loading
Loading
+2 −4
Original line number Diff line number Diff line
@@ -24,7 +24,6 @@ import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
import android.net.TrafficStats;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.IntArray;
import android.util.Log;

@@ -98,9 +97,8 @@ public final class NetworkStats implements AutoCloseable {

    /** @hide */
    NetworkStats(Context context, NetworkTemplate template, int flags, long startTimestamp,
            long endTimestamp) throws RemoteException, SecurityException {
        final INetworkStatsService statsService = INetworkStatsService.Stub.asInterface(
                ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
            long endTimestamp, INetworkStatsService statsService)
            throws RemoteException, SecurityException {
        // Open network stats session
        mSession = statsService.openSessionForUsageStats(flags, context.getOpPackageName());
        mCloseGuard.open("close");
+25 −15
Original line number Diff line number Diff line
@@ -37,6 +37,8 @@ import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
import android.util.Log;

import com.android.internal.annotations.VisibleForTesting;

/**
 * Provides access to network usage history and statistics. Usage data is collected in
 * discrete bins of time called 'Buckets'. See {@link NetworkStats.Bucket} for details.
@@ -107,9 +109,15 @@ public class NetworkStatsManager {
     * {@hide}
     */
    public NetworkStatsManager(Context context) throws ServiceNotFoundException {
        this(context, INetworkStatsService.Stub.asInterface(
                ServiceManager.getServiceOrThrow(Context.NETWORK_STATS_SERVICE)));
    }

    /** @hide */
    @VisibleForTesting
    public NetworkStatsManager(Context context, INetworkStatsService service) {
        mContext = context;
        mService = INetworkStatsService.Stub.asInterface(
                ServiceManager.getServiceOrThrow(Context.NETWORK_STATS_SERVICE));
        mService = service;
        setPollOnOpen(true);
    }

@@ -135,7 +143,8 @@ public class NetworkStatsManager {
    public Bucket querySummaryForDevice(NetworkTemplate template,
            long startTime, long endTime) throws SecurityException, RemoteException {
        Bucket bucket = null;
        NetworkStats stats = new NetworkStats(mContext, template, mFlags, startTime, endTime);
        NetworkStats stats = new NetworkStats(mContext, template, mFlags, startTime, endTime,
                mService);
        bucket = stats.getDeviceSummaryForNetwork();

        stats.close();
@@ -208,7 +217,7 @@ public class NetworkStatsManager {
        }

        NetworkStats stats;
        stats = new NetworkStats(mContext, template, mFlags, startTime, endTime);
        stats = new NetworkStats(mContext, template, mFlags, startTime, endTime, mService);
        stats.startSummaryEnumeration();

        stats.close();
@@ -245,7 +254,7 @@ public class NetworkStatsManager {
        }

        NetworkStats result;
        result = new NetworkStats(mContext, template, mFlags, startTime, endTime);
        result = new NetworkStats(mContext, template, mFlags, startTime, endTime, mService);
        result.startSummaryEnumeration();

        return result;
@@ -295,7 +304,7 @@ public class NetworkStatsManager {

        NetworkStats result;
        try {
            result = new NetworkStats(mContext, template, mFlags, startTime, endTime);
            result = new NetworkStats(mContext, template, mFlags, startTime, endTime, mService);
            result.startHistoryEnumeration(uid, tag);
        } catch (RemoteException e) {
            Log.e(TAG, "Error while querying stats for uid=" + uid + " tag=" + tag, e);
@@ -341,7 +350,7 @@ public class NetworkStatsManager {
        }

        NetworkStats result;
        result = new NetworkStats(mContext, template, mFlags, startTime, endTime);
        result = new NetworkStats(mContext, template, mFlags, startTime, endTime, mService);
        result.startUserUidEnumeration();
        return result;
    }
@@ -451,20 +460,21 @@ public class NetworkStatsManager {
    }

    private static NetworkTemplate createTemplate(int networkType, String subscriberId) {
        NetworkTemplate template = null;
        final NetworkTemplate template;
        switch (networkType) {
            case ConnectivityManager.TYPE_MOBILE: {
                template = NetworkTemplate.buildTemplateMobileAll(subscriberId);
                } break;
            case ConnectivityManager.TYPE_WIFI: {
            case ConnectivityManager.TYPE_MOBILE:
                template = subscriberId == null
                        ? NetworkTemplate.buildTemplateMobileWildcard()
                        : NetworkTemplate.buildTemplateMobileAll(subscriberId);
                break;
            case ConnectivityManager.TYPE_WIFI:
                template = NetworkTemplate.buildTemplateWifiWildcard();
                } break;
            default: {
                break;
            default:
                throw new IllegalArgumentException("Cannot create template for network type "
                        + networkType + ", subscriberId '"
                        + NetworkIdentity.scrubSubscriberId(subscriberId) + "'.");
        }
        }
        return template;
    }

+213 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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 android.app.usage;

import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.net.ConnectivityManager;
import android.net.INetworkStatsService;
import android.net.INetworkStatsSession;
import android.net.NetworkStats.Entry;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
import android.os.RemoteException;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;

@RunWith(AndroidJUnit4.class)
@SmallTest
public class NetworkStatsManagerTest {

    private @Mock INetworkStatsService mService;
    private @Mock INetworkStatsSession mStatsSession;

    private NetworkStatsManager mManager;

    // TODO: change to NetworkTemplate.MATCH_MOBILE once internal constant rename is merged to aosp.
    private static final int MATCH_MOBILE_ALL = 1;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        mManager = new NetworkStatsManager(InstrumentationRegistry.getContext(), mService);
    }

    @Test
    public void testQueryDetails() throws RemoteException {
        final String subscriberId = "subid";
        final long startTime = 1;
        final long endTime = 100;
        final int uid1 = 10001;
        final int uid2 = 10002;
        final int uid3 = 10003;

        Entry uid1Entry1 = new Entry("if1", uid1,
                android.net.NetworkStats.SET_DEFAULT, android.net.NetworkStats.TAG_NONE,
                100, 10, 200, 20, 0);

        Entry uid1Entry2 = new Entry(
                "if2", uid1,
                android.net.NetworkStats.SET_DEFAULT, android.net.NetworkStats.TAG_NONE,
                100, 10, 200, 20, 0);

        Entry uid2Entry1 = new Entry("if1", uid2,
                android.net.NetworkStats.SET_DEFAULT, android.net.NetworkStats.TAG_NONE,
                150, 10, 250, 20, 0);

        Entry uid2Entry2 = new Entry(
                "if2", uid2,
                android.net.NetworkStats.SET_DEFAULT, android.net.NetworkStats.TAG_NONE,
                150, 10, 250, 20, 0);

        NetworkStatsHistory history1 = new NetworkStatsHistory(10, 2);
        history1.recordData(10, 20, uid1Entry1);
        history1.recordData(20, 30, uid1Entry2);

        NetworkStatsHistory history2 = new NetworkStatsHistory(10, 2);
        history1.recordData(30, 40, uid2Entry1);
        history1.recordData(35, 45, uid2Entry2);


        when(mService.openSessionForUsageStats(anyInt(), anyString())).thenReturn(mStatsSession);
        when(mStatsSession.getRelevantUids()).thenReturn(new int[] { uid1, uid2, uid3 });

        when(mStatsSession.getHistoryIntervalForUid(any(NetworkTemplate.class),
                eq(uid1), eq(android.net.NetworkStats.SET_ALL),
                eq(android.net.NetworkStats.TAG_NONE),
                eq(NetworkStatsHistory.FIELD_ALL), eq(startTime), eq(endTime)))
                .then((InvocationOnMock inv) -> {
                    NetworkTemplate template = inv.getArgument(0);
                    assertEquals(MATCH_MOBILE_ALL, template.getMatchRule());
                    assertEquals(subscriberId, template.getSubscriberId());
                    return history1;
                });

        when(mStatsSession.getHistoryIntervalForUid(any(NetworkTemplate.class),
                eq(uid2), eq(android.net.NetworkStats.SET_ALL),
                eq(android.net.NetworkStats.TAG_NONE),
                eq(NetworkStatsHistory.FIELD_ALL), eq(startTime), eq(endTime)))
                .then((InvocationOnMock inv) -> {
                    NetworkTemplate template = inv.getArgument(0);
                    assertEquals(MATCH_MOBILE_ALL, template.getMatchRule());
                    assertEquals(subscriberId, template.getSubscriberId());
                    return history2;
                });


        NetworkStats stats = mManager.queryDetails(
                ConnectivityManager.TYPE_MOBILE, subscriberId, startTime, endTime);

        NetworkStats.Bucket bucket = new NetworkStats.Bucket();

        // First 2 buckets exactly match entry timings
        assertTrue(stats.getNextBucket(bucket));
        assertEquals(10, bucket.getStartTimeStamp());
        assertEquals(20, bucket.getEndTimeStamp());
        assertBucketMatches(uid1Entry1, bucket);

        assertTrue(stats.getNextBucket(bucket));
        assertEquals(20, bucket.getStartTimeStamp());
        assertEquals(30, bucket.getEndTimeStamp());
        assertBucketMatches(uid1Entry2, bucket);

        // 30 -> 40: contains uid2Entry1 and half of uid2Entry2
        assertTrue(stats.getNextBucket(bucket));
        assertEquals(30, bucket.getStartTimeStamp());
        assertEquals(40, bucket.getEndTimeStamp());
        assertEquals(225, bucket.getRxBytes());
        assertEquals(15, bucket.getRxPackets());
        assertEquals(375, bucket.getTxBytes());
        assertEquals(30, bucket.getTxPackets());

        // 40 -> 50: contains half of uid2Entry2
        assertTrue(stats.getNextBucket(bucket));
        assertEquals(40, bucket.getStartTimeStamp());
        assertEquals(50, bucket.getEndTimeStamp());
        assertEquals(75, bucket.getRxBytes());
        assertEquals(5, bucket.getRxPackets());
        assertEquals(125, bucket.getTxBytes());
        assertEquals(10, bucket.getTxPackets());

        assertFalse(stats.hasNextBucket());
    }

    @Test
    public void testQueryDetails_NoSubscriberId() throws RemoteException {
        final long startTime = 1;
        final long endTime = 100;
        final int uid1 = 10001;
        final int uid2 = 10002;

        when(mService.openSessionForUsageStats(anyInt(), anyString())).thenReturn(mStatsSession);
        when(mStatsSession.getRelevantUids()).thenReturn(new int[] { uid1, uid2 });

        NetworkStats stats = mManager.queryDetails(
                ConnectivityManager.TYPE_MOBILE, null, startTime, endTime);

        when(mStatsSession.getHistoryIntervalForUid(any(NetworkTemplate.class),
                anyInt(), anyInt(), anyInt(), anyInt(), anyLong(), anyLong()))
                .thenReturn(new NetworkStatsHistory(10, 0));

        verify(mStatsSession, times(1)).getHistoryIntervalForUid(
                argThat((NetworkTemplate t) ->
                        // No subscriberId: MATCH_MOBILE_WILDCARD template
                        t.getMatchRule() == NetworkTemplate.MATCH_MOBILE_WILDCARD),
                eq(uid1), eq(android.net.NetworkStats.SET_ALL),
                eq(android.net.NetworkStats.TAG_NONE),
                eq(NetworkStatsHistory.FIELD_ALL), eq(startTime), eq(endTime));

        verify(mStatsSession, times(1)).getHistoryIntervalForUid(
                argThat((NetworkTemplate t) ->
                        // No subscriberId: MATCH_MOBILE_WILDCARD template
                        t.getMatchRule() == NetworkTemplate.MATCH_MOBILE_WILDCARD),
                eq(uid2), eq(android.net.NetworkStats.SET_ALL),
                eq(android.net.NetworkStats.TAG_NONE),
                eq(NetworkStatsHistory.FIELD_ALL), eq(startTime), eq(endTime));

        assertFalse(stats.hasNextBucket());
    }

    private void assertBucketMatches(Entry expected,
            NetworkStats.Bucket actual) {
        assertEquals(expected.uid, actual.getUid());
        assertEquals(expected.rxBytes, actual.getRxBytes());
        assertEquals(expected.rxPackets, actual.getRxPackets());
        assertEquals(expected.txBytes, actual.getTxBytes());
        assertEquals(expected.txPackets, actual.getTxPackets());
    }
}