1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.net;
18
19import static android.net.ConnectivityManager.TYPE_MOBILE;
20import static android.net.NetworkStats.SET_DEFAULT;
21import static android.net.NetworkStats.TAG_NONE;
22import static android.net.NetworkStats.UID_ALL;
23import static android.net.NetworkTemplate.buildTemplateMobileAll;
24import static android.text.format.DateUtils.HOUR_IN_MILLIS;
25import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
26
27import android.content.res.Resources;
28import android.net.NetworkIdentity;
29import android.net.NetworkStats;
30import android.net.NetworkTemplate;
31import android.os.Process;
32import android.os.UserHandle;
33import android.telephony.TelephonyManager;
34import android.test.AndroidTestCase;
35import android.test.MoreAsserts;
36import android.test.suitebuilder.annotation.MediumTest;
37
38import com.android.frameworks.servicestests.R;
39
40import java.io.ByteArrayInputStream;
41import java.io.ByteArrayOutputStream;
42import java.io.DataOutputStream;
43import java.io.File;
44import java.io.FileOutputStream;
45import java.io.InputStream;
46import java.io.OutputStream;
47
48import libcore.io.IoUtils;
49import libcore.io.Streams;
50
51/**
52 * Tests for {@link NetworkStatsCollection}.
53 */
54@MediumTest
55public class NetworkStatsCollectionTest extends AndroidTestCase {
56
57    private static final String TEST_FILE = "test.bin";
58    private static final String TEST_IMSI = "310260000000000";
59
60    @Override
61    public void setUp() throws Exception {
62        super.setUp();
63
64        // ignore any device overlay while testing
65        NetworkTemplate.forceAllNetworkTypes();
66    }
67
68    public void testReadLegacyNetwork() throws Exception {
69        final File testFile = new File(getContext().getFilesDir(), TEST_FILE);
70        stageFile(R.raw.netstats_v1, testFile);
71
72        final NetworkStatsCollection collection = new NetworkStatsCollection(30 * MINUTE_IN_MILLIS);
73        collection.readLegacyNetwork(testFile);
74
75        // verify that history read correctly
76        assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
77                636016770L, 709306L, 88038768L, 518836L, NetworkStatsAccess.Level.DEVICE);
78
79        // now export into a unified format
80        final ByteArrayOutputStream bos = new ByteArrayOutputStream();
81        collection.write(new DataOutputStream(bos));
82
83        // clear structure completely
84        collection.reset();
85        assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
86                0L, 0L, 0L, 0L, NetworkStatsAccess.Level.DEVICE);
87
88        // and read back into structure, verifying that totals are same
89        collection.read(new ByteArrayInputStream(bos.toByteArray()));
90        assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
91                636016770L, 709306L, 88038768L, 518836L, NetworkStatsAccess.Level.DEVICE);
92    }
93
94    public void testReadLegacyUid() throws Exception {
95        final File testFile = new File(getContext().getFilesDir(), TEST_FILE);
96        stageFile(R.raw.netstats_uid_v4, testFile);
97
98        final NetworkStatsCollection collection = new NetworkStatsCollection(30 * MINUTE_IN_MILLIS);
99        collection.readLegacyUid(testFile, false);
100
101        // verify that history read correctly
102        assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
103                637076152L, 711413L, 88343717L, 521022L, NetworkStatsAccess.Level.DEVICE);
104
105        // now export into a unified format
106        final ByteArrayOutputStream bos = new ByteArrayOutputStream();
107        collection.write(new DataOutputStream(bos));
108
109        // clear structure completely
110        collection.reset();
111        assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
112                0L, 0L, 0L, 0L, NetworkStatsAccess.Level.DEVICE);
113
114        // and read back into structure, verifying that totals are same
115        collection.read(new ByteArrayInputStream(bos.toByteArray()));
116        assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
117                637076152L, 711413L, 88343717L, 521022L, NetworkStatsAccess.Level.DEVICE);
118    }
119
120    public void testReadLegacyUidTags() throws Exception {
121        final File testFile = new File(getContext().getFilesDir(), TEST_FILE);
122        stageFile(R.raw.netstats_uid_v4, testFile);
123
124        final NetworkStatsCollection collection = new NetworkStatsCollection(30 * MINUTE_IN_MILLIS);
125        collection.readLegacyUid(testFile, true);
126
127        // verify that history read correctly
128        assertSummaryTotalIncludingTags(collection, buildTemplateMobileAll(TEST_IMSI),
129                77017831L, 100995L, 35436758L, 92344L);
130
131        // now export into a unified format
132        final ByteArrayOutputStream bos = new ByteArrayOutputStream();
133        collection.write(new DataOutputStream(bos));
134
135        // clear structure completely
136        collection.reset();
137        assertSummaryTotalIncludingTags(collection, buildTemplateMobileAll(TEST_IMSI),
138                0L, 0L, 0L, 0L);
139
140        // and read back into structure, verifying that totals are same
141        collection.read(new ByteArrayInputStream(bos.toByteArray()));
142        assertSummaryTotalIncludingTags(collection, buildTemplateMobileAll(TEST_IMSI),
143                77017831L, 100995L, 35436758L, 92344L);
144    }
145
146    public void testStartEndAtomicBuckets() throws Exception {
147        final NetworkStatsCollection collection = new NetworkStatsCollection(HOUR_IN_MILLIS);
148
149        // record empty data straddling between buckets
150        final NetworkStats.Entry entry = new NetworkStats.Entry();
151        entry.rxBytes = 32;
152        collection.recordData(null, UID_ALL, SET_DEFAULT, TAG_NONE, 30 * MINUTE_IN_MILLIS,
153                90 * MINUTE_IN_MILLIS, entry);
154
155        // assert that we report boundary in atomic buckets
156        assertEquals(0, collection.getStartMillis());
157        assertEquals(2 * HOUR_IN_MILLIS, collection.getEndMillis());
158    }
159
160    public void testAccessLevels() throws Exception {
161        final NetworkStatsCollection collection = new NetworkStatsCollection(HOUR_IN_MILLIS);
162        final NetworkStats.Entry entry = new NetworkStats.Entry();
163        final NetworkIdentitySet identSet = new NetworkIdentitySet();
164        identSet.add(new NetworkIdentity(TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
165                TEST_IMSI, null, false, true));
166
167        int myUid = Process.myUid();
168        int otherUidInSameUser = Process.myUid() + 1;
169        int uidInDifferentUser = Process.myUid() + UserHandle.PER_USER_RANGE;
170
171        // Record one entry for the current UID.
172        entry.rxBytes = 32;
173        collection.recordData(identSet, myUid, SET_DEFAULT, TAG_NONE, 0, 60 * MINUTE_IN_MILLIS,
174                entry);
175
176        // Record one entry for another UID in this user.
177        entry.rxBytes = 64;
178        collection.recordData(identSet, otherUidInSameUser, SET_DEFAULT, TAG_NONE, 0,
179                60 * MINUTE_IN_MILLIS, entry);
180
181        // Record one entry for the system UID.
182        entry.rxBytes = 128;
183        collection.recordData(identSet, Process.SYSTEM_UID, SET_DEFAULT, TAG_NONE, 0,
184                60 * MINUTE_IN_MILLIS, entry);
185
186        // Record one entry for a UID in a different user.
187        entry.rxBytes = 256;
188        collection.recordData(identSet, uidInDifferentUser, SET_DEFAULT, TAG_NONE, 0,
189                60 * MINUTE_IN_MILLIS, entry);
190
191        // Verify the set of relevant UIDs for each access level.
192        MoreAsserts.assertEquals(new int[] { myUid },
193                collection.getRelevantUids(NetworkStatsAccess.Level.DEFAULT));
194        MoreAsserts.assertEquals(new int[] { Process.SYSTEM_UID, myUid, otherUidInSameUser },
195                collection.getRelevantUids(NetworkStatsAccess.Level.USER));
196        MoreAsserts.assertEquals(
197                new int[] { Process.SYSTEM_UID, myUid, otherUidInSameUser, uidInDifferentUser },
198                collection.getRelevantUids(NetworkStatsAccess.Level.DEVICE));
199
200        // Verify security check in getHistory.
201        assertNotNull(collection.getHistory(buildTemplateMobileAll(TEST_IMSI), myUid, SET_DEFAULT,
202                TAG_NONE, 0, NetworkStatsAccess.Level.DEFAULT));
203        try {
204            collection.getHistory(buildTemplateMobileAll(TEST_IMSI), otherUidInSameUser,
205                    SET_DEFAULT, TAG_NONE, 0, NetworkStatsAccess.Level.DEFAULT);
206            fail("Should have thrown SecurityException for accessing different UID");
207        } catch (SecurityException e) {
208            // expected
209        }
210
211        // Verify appropriate aggregation in getSummary.
212        assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI), 32, 0, 0, 0,
213                NetworkStatsAccess.Level.DEFAULT);
214        assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI), 32 + 64 + 128, 0, 0, 0,
215                NetworkStatsAccess.Level.USER);
216        assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI), 32 + 64 + 128 + 256, 0, 0,
217                0, NetworkStatsAccess.Level.DEVICE);
218    }
219
220    /**
221     * Copy a {@link Resources#openRawResource(int)} into {@link File} for
222     * testing purposes.
223     */
224    private void stageFile(int rawId, File file) throws Exception {
225        new File(file.getParent()).mkdirs();
226        InputStream in = null;
227        OutputStream out = null;
228        try {
229            in = getContext().getResources().openRawResource(rawId);
230            out = new FileOutputStream(file);
231            Streams.copy(in, out);
232        } finally {
233            IoUtils.closeQuietly(in);
234            IoUtils.closeQuietly(out);
235        }
236    }
237
238    private static void assertSummaryTotal(NetworkStatsCollection collection,
239            NetworkTemplate template, long rxBytes, long rxPackets, long txBytes, long txPackets,
240            @NetworkStatsAccess.Level int accessLevel) {
241        final NetworkStats.Entry entry = collection.getSummary(
242                template, Long.MIN_VALUE, Long.MAX_VALUE, accessLevel)
243                .getTotal(null);
244        assertEntry(entry, rxBytes, rxPackets, txBytes, txPackets);
245    }
246
247    private static void assertSummaryTotalIncludingTags(NetworkStatsCollection collection,
248            NetworkTemplate template, long rxBytes, long rxPackets, long txBytes, long txPackets) {
249        final NetworkStats.Entry entry = collection.getSummary(
250                template, Long.MIN_VALUE, Long.MAX_VALUE, NetworkStatsAccess.Level.DEVICE)
251                .getTotalIncludingTags(null);
252        assertEntry(entry, rxBytes, rxPackets, txBytes, txPackets);
253    }
254
255    private static void assertEntry(
256            NetworkStats.Entry entry, long rxBytes, long rxPackets, long txBytes, long txPackets) {
257        assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes);
258        assertEquals("unexpected rxPackets", rxPackets, entry.rxPackets);
259        assertEquals("unexpected txBytes", txBytes, entry.txBytes);
260        assertEquals("unexpected txPackets", txPackets, entry.txPackets);
261    }
262}
263