1 /* 2 * Copyright (C) 2018 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 17 package android.app.usage; 18 19 import static android.net.ConnectivityManager.TYPE_MOBILE; 20 import static android.net.ConnectivityManager.TYPE_WIFI; 21 import static android.net.NetworkStats.DEFAULT_NETWORK_NO; 22 import static android.net.NetworkStats.METERED_NO; 23 import static android.net.NetworkStats.METERED_YES; 24 import static android.net.NetworkStats.ROAMING_NO; 25 import static android.net.NetworkStats.SET_ALL; 26 import static android.net.NetworkStats.SET_DEFAULT; 27 import static android.net.NetworkStats.TAG_NONE; 28 import static android.net.NetworkStatsHistory.FIELD_ALL; 29 import static android.net.NetworkTemplate.MATCH_MOBILE; 30 import static android.net.NetworkTemplate.MATCH_WIFI; 31 32 import static junit.framework.Assert.assertEquals; 33 import static junit.framework.Assert.assertFalse; 34 import static junit.framework.Assert.assertTrue; 35 36 import static org.mockito.ArgumentMatchers.any; 37 import static org.mockito.ArgumentMatchers.anyInt; 38 import static org.mockito.ArgumentMatchers.anyLong; 39 import static org.mockito.ArgumentMatchers.anyString; 40 import static org.mockito.ArgumentMatchers.eq; 41 import static org.mockito.Mockito.reset; 42 import static org.mockito.Mockito.times; 43 import static org.mockito.Mockito.verify; 44 import static org.mockito.Mockito.when; 45 46 import android.net.INetworkStatsService; 47 import android.net.INetworkStatsSession; 48 import android.net.NetworkStats.Entry; 49 import android.net.NetworkStatsHistory; 50 import android.net.NetworkTemplate; 51 import android.os.Build; 52 import android.os.RemoteException; 53 54 import androidx.test.InstrumentationRegistry; 55 import androidx.test.filters.SmallTest; 56 57 import com.android.testutils.DevSdkIgnoreRule; 58 import com.android.testutils.DevSdkIgnoreRunner; 59 60 import org.junit.Before; 61 import org.junit.Test; 62 import org.junit.runner.RunWith; 63 import org.mockito.Mock; 64 import org.mockito.MockitoAnnotations; 65 import org.mockito.invocation.InvocationOnMock; 66 67 import java.util.Set; 68 69 @RunWith(DevSdkIgnoreRunner.class) 70 @SmallTest 71 @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.S_V2) 72 public class NetworkStatsManagerTest { 73 private static final String TEST_SUBSCRIBER_ID = "subid"; 74 75 private @Mock INetworkStatsService mService; 76 private @Mock INetworkStatsSession mStatsSession; 77 78 private NetworkStatsManager mManager; 79 80 // TODO: change to NetworkTemplate.MATCH_MOBILE once internal constant rename is merged to aosp. 81 private static final int MATCH_MOBILE_ALL = 1; 82 83 @Before setUp()84 public void setUp() { 85 MockitoAnnotations.initMocks(this); 86 mManager = new NetworkStatsManager(InstrumentationRegistry.getContext(), mService); 87 } 88 89 @Test testQueryDetails()90 public void testQueryDetails() throws RemoteException { 91 final long startTime = 1; 92 final long endTime = 100; 93 final int uid1 = 10001; 94 final int uid2 = 10002; 95 final int uid3 = 10003; 96 97 Entry uid1Entry1 = new Entry("if1", uid1, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 98 DEFAULT_NETWORK_NO, 100, 10, 200, 20, 0); 99 100 Entry uid1Entry2 = new Entry("if2", uid1, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 101 DEFAULT_NETWORK_NO, 100, 10, 200, 20, 0); 102 103 Entry uid2Entry1 = new Entry("if1", uid2, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 104 DEFAULT_NETWORK_NO, 150, 10, 250, 20, 0); 105 106 Entry uid2Entry2 = new Entry("if2", uid2, SET_DEFAULT, TAG_NONE, METERED_NO, ROAMING_NO, 107 DEFAULT_NETWORK_NO, 150, 10, 250, 20, 0); 108 109 NetworkStatsHistory history1 = new NetworkStatsHistory(10, 2); 110 history1.recordData(10, 20, uid1Entry1); 111 history1.recordData(20, 30, uid1Entry2); 112 113 NetworkStatsHistory history2 = new NetworkStatsHistory(10, 2); 114 history1.recordData(30, 40, uid2Entry1); 115 history1.recordData(35, 45, uid2Entry2); 116 117 118 when(mService.openSessionForUsageStats(anyInt(), anyString())).thenReturn(mStatsSession); 119 when(mStatsSession.getRelevantUids()).thenReturn(new int[] { uid1, uid2, uid3 }); 120 121 when(mStatsSession.getHistoryIntervalForUid(any(NetworkTemplate.class), 122 eq(uid1), eq(SET_ALL), eq(TAG_NONE), 123 eq(FIELD_ALL), eq(startTime), eq(endTime))) 124 .then((InvocationOnMock inv) -> { 125 NetworkTemplate template = inv.getArgument(0); 126 assertEquals(MATCH_MOBILE_ALL, template.getMatchRule()); 127 assertEquals(TEST_SUBSCRIBER_ID, template.getSubscriberId()); 128 return history1; 129 }); 130 131 when(mStatsSession.getHistoryIntervalForUid(any(NetworkTemplate.class), 132 eq(uid2), eq(SET_ALL), eq(TAG_NONE), 133 eq(FIELD_ALL), eq(startTime), eq(endTime))) 134 .then((InvocationOnMock inv) -> { 135 NetworkTemplate template = inv.getArgument(0); 136 assertEquals(MATCH_MOBILE_ALL, template.getMatchRule()); 137 assertEquals(TEST_SUBSCRIBER_ID, template.getSubscriberId()); 138 return history2; 139 }); 140 141 142 NetworkStats stats = mManager.queryDetails( 143 TYPE_MOBILE, TEST_SUBSCRIBER_ID, startTime, endTime); 144 145 NetworkStats.Bucket bucket = new NetworkStats.Bucket(); 146 147 // First 2 buckets exactly match entry timings 148 assertTrue(stats.getNextBucket(bucket)); 149 assertEquals(10, bucket.getStartTimeStamp()); 150 assertEquals(20, bucket.getEndTimeStamp()); 151 assertBucketMatches(uid1Entry1, bucket); 152 153 assertTrue(stats.getNextBucket(bucket)); 154 assertEquals(20, bucket.getStartTimeStamp()); 155 assertEquals(30, bucket.getEndTimeStamp()); 156 assertBucketMatches(uid1Entry2, bucket); 157 158 // 30 -> 40: contains uid2Entry1 and half of uid2Entry2 159 assertTrue(stats.getNextBucket(bucket)); 160 assertEquals(30, bucket.getStartTimeStamp()); 161 assertEquals(40, bucket.getEndTimeStamp()); 162 assertEquals(225, bucket.getRxBytes()); 163 assertEquals(15, bucket.getRxPackets()); 164 assertEquals(375, bucket.getTxBytes()); 165 assertEquals(30, bucket.getTxPackets()); 166 167 // 40 -> 50: contains half of uid2Entry2 168 assertTrue(stats.getNextBucket(bucket)); 169 assertEquals(40, bucket.getStartTimeStamp()); 170 assertEquals(50, bucket.getEndTimeStamp()); 171 assertEquals(75, bucket.getRxBytes()); 172 assertEquals(5, bucket.getRxPackets()); 173 assertEquals(125, bucket.getTxBytes()); 174 assertEquals(10, bucket.getTxPackets()); 175 176 assertFalse(stats.hasNextBucket()); 177 } 178 runQueryDetailsAndCheckTemplate(int networkType, String subscriberId, NetworkTemplate expectedTemplate)179 private void runQueryDetailsAndCheckTemplate(int networkType, String subscriberId, 180 NetworkTemplate expectedTemplate) throws RemoteException { 181 final long startTime = 1; 182 final long endTime = 100; 183 final int uid1 = 10001; 184 final int uid2 = 10002; 185 186 reset(mStatsSession); 187 when(mService.openSessionForUsageStats(anyInt(), anyString())).thenReturn(mStatsSession); 188 when(mStatsSession.getRelevantUids()).thenReturn(new int[] { uid1, uid2 }); 189 when(mStatsSession.getHistoryIntervalForUid(any(NetworkTemplate.class), 190 anyInt(), anyInt(), anyInt(), anyInt(), anyLong(), anyLong())) 191 .thenReturn(new NetworkStatsHistory(10, 0)); 192 NetworkStats stats = mManager.queryDetails( 193 networkType, subscriberId, startTime, endTime); 194 195 verify(mStatsSession, times(1)).getHistoryIntervalForUid( 196 eq(expectedTemplate), 197 eq(uid1), eq(SET_ALL), 198 eq(TAG_NONE), 199 eq(FIELD_ALL), eq(startTime), eq(endTime)); 200 201 verify(mStatsSession, times(1)).getHistoryIntervalForUid( 202 eq(expectedTemplate), 203 eq(uid2), eq(SET_ALL), 204 eq(TAG_NONE), 205 eq(FIELD_ALL), eq(startTime), eq(endTime)); 206 207 assertFalse(stats.hasNextBucket()); 208 } 209 210 @Test testNetworkTemplateWhenRunningQueryDetails_NoSubscriberId()211 public void testNetworkTemplateWhenRunningQueryDetails_NoSubscriberId() throws RemoteException { 212 runQueryDetailsAndCheckTemplate(TYPE_MOBILE, null /* subscriberId */, 213 new NetworkTemplate.Builder(MATCH_MOBILE).setMeteredness(METERED_YES).build()); 214 runQueryDetailsAndCheckTemplate(TYPE_WIFI, "" /* subscriberId */, 215 new NetworkTemplate.Builder(MATCH_WIFI).build()); 216 runQueryDetailsAndCheckTemplate(TYPE_WIFI, null /* subscriberId */, 217 new NetworkTemplate.Builder(MATCH_WIFI).build()); 218 } 219 220 @Test testNetworkTemplateWhenRunningQueryDetails_MergedCarrierWifi()221 public void testNetworkTemplateWhenRunningQueryDetails_MergedCarrierWifi() 222 throws RemoteException { 223 runQueryDetailsAndCheckTemplate(TYPE_WIFI, TEST_SUBSCRIBER_ID, 224 new NetworkTemplate.Builder(MATCH_WIFI) 225 .setSubscriberIds(Set.of(TEST_SUBSCRIBER_ID)).build()); 226 } 227 228 @Test testQueryTaggedSummary()229 public void testQueryTaggedSummary() throws Exception { 230 final long startTime = 1; 231 final long endTime = 100; 232 233 reset(mStatsSession); 234 when(mService.openSessionForUsageStats(anyInt(), anyString())).thenReturn(mStatsSession); 235 when(mStatsSession.getTaggedSummaryForAllUid(any(NetworkTemplate.class), 236 anyLong(), anyLong())) 237 .thenReturn(new android.net.NetworkStats(0, 0)); 238 final NetworkTemplate template = new NetworkTemplate.Builder(MATCH_MOBILE) 239 .setMeteredness(NetworkStats.Bucket.METERED_YES).build(); 240 NetworkStats stats = mManager.queryTaggedSummary(template, startTime, endTime); 241 242 verify(mStatsSession, times(1)).getTaggedSummaryForAllUid( 243 eq(template), eq(startTime), eq(endTime)); 244 245 assertFalse(stats.hasNextBucket()); 246 } 247 248 249 @Test testQueryDetailsForDevice()250 public void testQueryDetailsForDevice() throws Exception { 251 final long startTime = 1; 252 final long endTime = 100; 253 254 reset(mStatsSession); 255 when(mService.openSessionForUsageStats(anyInt(), anyString())).thenReturn(mStatsSession); 256 when(mStatsSession.getHistoryIntervalForNetwork(any(NetworkTemplate.class), 257 anyInt(), anyLong(), anyLong())) 258 .thenReturn(new NetworkStatsHistory(10, 0)); 259 final NetworkTemplate template = new NetworkTemplate.Builder(MATCH_MOBILE) 260 .setMeteredness(NetworkStats.Bucket.METERED_YES).build(); 261 NetworkStats stats = mManager.queryDetailsForDevice(template, startTime, endTime); 262 263 verify(mStatsSession, times(1)).getHistoryIntervalForNetwork( 264 eq(template), eq(FIELD_ALL), eq(startTime), eq(endTime)); 265 266 assertFalse(stats.hasNextBucket()); 267 } 268 assertBucketMatches(Entry expected, NetworkStats.Bucket actual)269 private void assertBucketMatches(Entry expected, NetworkStats.Bucket actual) { 270 assertEquals(expected.uid, actual.getUid()); 271 assertEquals(expected.rxBytes, actual.getRxBytes()); 272 assertEquals(expected.rxPackets, actual.getRxPackets()); 273 assertEquals(expected.txBytes, actual.getTxBytes()); 274 assertEquals(expected.txPackets, actual.getTxPackets()); 275 } 276 } 277