1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 *i 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 com.android.server.net; 18 19 import static android.net.NetworkStats.SET_DEFAULT; 20 import static android.net.NetworkStats.SET_FOREGROUND; 21 import static android.net.NetworkStats.TAG_NONE; 22 import static android.net.netstats.NetworkStatsDataMigrationUtils.PREFIX_UID; 23 import static android.net.netstats.NetworkStatsDataMigrationUtils.PREFIX_UID_TAG; 24 import static android.net.netstats.NetworkStatsDataMigrationUtils.PREFIX_XT; 25 import static android.text.format.DateUtils.HOUR_IN_MILLIS; 26 27 import static com.android.server.ConnectivityStatsLog.NETWORK_STATS_RECORDER_FILE_OPERATED__RECORDER_PREFIX__PREFIX_UID; 28 import static com.android.server.ConnectivityStatsLog.NETWORK_STATS_RECORDER_FILE_OPERATED__RECORDER_PREFIX__PREFIX_UIDTAG; 29 import static com.android.server.ConnectivityStatsLog.NETWORK_STATS_RECORDER_FILE_OPERATED__RECORDER_PREFIX__PREFIX_XT; 30 import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2; 31 32 import static org.mockito.Mockito.any; 33 import static org.mockito.Mockito.anyLong; 34 import static org.mockito.Mockito.doThrow; 35 import static org.mockito.Mockito.mock; 36 import static org.mockito.Mockito.never; 37 import static org.mockito.Mockito.reset; 38 import static org.mockito.Mockito.times; 39 import static org.mockito.Mockito.verify; 40 41 import android.annotation.NonNull; 42 import android.net.NetworkIdentity; 43 import android.net.NetworkIdentitySet; 44 import android.net.NetworkStats; 45 import android.net.NetworkStatsCollection; 46 import android.os.DropBoxManager; 47 48 import androidx.test.filters.SmallTest; 49 50 import com.android.internal.util.FileRotator; 51 import com.android.metrics.NetworkStatsMetricsLogger; 52 import com.android.testutils.DevSdkIgnoreRule; 53 import com.android.testutils.DevSdkIgnoreRunner; 54 55 import libcore.testing.io.TestIoUtils; 56 57 import org.junit.Before; 58 import org.junit.Test; 59 import org.junit.runner.RunWith; 60 import org.mockito.Mock; 61 import org.mockito.MockitoAnnotations; 62 63 import java.io.DataOutputStream; 64 import java.io.File; 65 import java.io.FileOutputStream; 66 import java.io.IOException; 67 68 @RunWith(DevSdkIgnoreRunner.class) 69 @SmallTest 70 @DevSdkIgnoreRule.IgnoreUpTo(SC_V2) 71 public final class NetworkStatsRecorderTest { 72 private static final String TAG = NetworkStatsRecorderTest.class.getSimpleName(); 73 74 private static final String TEST_PREFIX = "test"; 75 private static final int TEST_UID1 = 1234; 76 private static final int TEST_UID2 = 1235; 77 78 @Mock private DropBoxManager mDropBox; 79 @Mock private NetworkStats.NonMonotonicObserver mObserver; 80 81 @Before setUp()82 public void setUp() { 83 MockitoAnnotations.initMocks(this); 84 } 85 buildRecorder(FileRotator rotator, boolean wipeOnError)86 private NetworkStatsRecorder buildRecorder(FileRotator rotator, boolean wipeOnError) { 87 return new NetworkStatsRecorder(rotator, mObserver, mDropBox, TEST_PREFIX, 88 HOUR_IN_MILLIS, false /* includeTags */, wipeOnError, 89 false /* useFastDataInput */, null /* baseDir */); 90 } 91 92 @Test testWipeOnError()93 public void testWipeOnError() throws Exception { 94 final FileRotator rotator = mock(FileRotator.class); 95 final NetworkStatsRecorder wipeOnErrorRecorder = buildRecorder(rotator, true); 96 97 // Assuming that the rotator gets an exception happened when read data. 98 doThrow(new IOException()).when(rotator).readMatching(any(), anyLong(), anyLong()); 99 wipeOnErrorRecorder.getOrLoadPartialLocked(Long.MIN_VALUE, Long.MAX_VALUE); 100 // Verify that the files will be deleted. 101 verify(rotator, times(1)).deleteAll(); 102 reset(rotator); 103 104 final NetworkStatsRecorder noWipeOnErrorRecorder = buildRecorder(rotator, false); 105 doThrow(new IOException()).when(rotator).readMatching(any(), anyLong(), anyLong()); 106 noWipeOnErrorRecorder.getOrLoadPartialLocked(Long.MIN_VALUE, Long.MAX_VALUE); 107 // Verify that the rotator won't delete files. 108 verify(rotator, never()).deleteAll(); 109 } 110 111 @Test testFileReadingMetrics_empty()112 public void testFileReadingMetrics_empty() { 113 final NetworkStatsCollection collection = new NetworkStatsCollection(30); 114 final NetworkStatsMetricsLogger.Dependencies deps = 115 mock(NetworkStatsMetricsLogger.Dependencies.class); 116 final NetworkStatsMetricsLogger logger = new NetworkStatsMetricsLogger(deps); 117 logger.logRecorderFileReading(PREFIX_XT, 888, null /* statsDir */, collection, 118 false /* useFastDataInput */); 119 verify(deps).writeRecorderFileReadingStats( 120 NETWORK_STATS_RECORDER_FILE_OPERATED__RECORDER_PREFIX__PREFIX_XT, 121 1 /* readIndex */, 122 888 /* readLatencyMillis */, 123 0 /* fileCount */, 124 0 /* totalFileSize */, 125 0 /* keys */, 126 0 /* uids */, 127 0 /* totalHistorySize */, 128 false /* useFastDataInput */ 129 ); 130 131 // Write second time, verify the index increases. 132 logger.logRecorderFileReading(PREFIX_XT, 567, null /* statsDir */, collection, 133 true /* useFastDataInput */); 134 verify(deps).writeRecorderFileReadingStats( 135 NETWORK_STATS_RECORDER_FILE_OPERATED__RECORDER_PREFIX__PREFIX_XT, 136 2 /* readIndex */, 137 567 /* readLatencyMillis */, 138 0 /* fileCount */, 139 0 /* totalFileSize */, 140 0 /* keys */, 141 0 /* uids */, 142 0 /* totalHistorySize */, 143 true /* useFastDataInput */ 144 ); 145 } 146 147 @Test testFileReadingMetrics()148 public void testFileReadingMetrics() { 149 final NetworkStatsCollection collection = new NetworkStatsCollection(30); 150 final NetworkStats.Entry entry = new NetworkStats.Entry(); 151 final NetworkIdentitySet identSet = new NetworkIdentitySet(); 152 identSet.add(new NetworkIdentity.Builder().build()); 153 // Empty entries will be skipped, put some ints to make sure they can be recorded. 154 entry.rxBytes = 1; 155 156 collection.recordData(identSet, TEST_UID1, SET_DEFAULT, TAG_NONE, 0, 60, entry); 157 collection.recordData(identSet, TEST_UID2, SET_DEFAULT, TAG_NONE, 0, 60, entry); 158 collection.recordData(identSet, TEST_UID2, SET_FOREGROUND, TAG_NONE, 30, 60, entry); 159 160 final NetworkStatsMetricsLogger.Dependencies deps = 161 mock(NetworkStatsMetricsLogger.Dependencies.class); 162 final NetworkStatsMetricsLogger logger = new NetworkStatsMetricsLogger(deps); 163 logger.logRecorderFileReading(PREFIX_UID, 123, null /* statsDir */, collection, 164 false /* useFastDataInput */); 165 verify(deps).writeRecorderFileReadingStats( 166 NETWORK_STATS_RECORDER_FILE_OPERATED__RECORDER_PREFIX__PREFIX_UID, 167 1 /* readIndex */, 168 123 /* readLatencyMillis */, 169 0 /* fileCount */, 170 0 /* totalFileSize */, 171 3 /* keys */, 172 2 /* uids */, 173 5 /* totalHistorySize */, 174 false /* useFastDataInput */ 175 ); 176 } 177 178 @Test testFileReadingMetrics_fileAttributes()179 public void testFileReadingMetrics_fileAttributes() throws IOException { 180 final NetworkStatsCollection collection = new NetworkStatsCollection(30); 181 182 // Create files for testing. Only the first and the third files should be counted, 183 // with total 26 (each char takes 2 bytes) bytes in the content. 184 final File statsDir = TestIoUtils.createTemporaryDirectory(getClass().getSimpleName()); 185 write(statsDir, "uid_tag.1024-2048", "wanted"); 186 write(statsDir, "uid_tag.1024-2048.backup", ""); 187 write(statsDir, "uid_tag.2048-", "wanted2"); 188 write(statsDir, "uid.2048-4096", "unwanted"); 189 write(statsDir, "uid.2048-4096.backup", "unwanted2"); 190 191 final NetworkStatsMetricsLogger.Dependencies deps = 192 mock(NetworkStatsMetricsLogger.Dependencies.class); 193 final NetworkStatsMetricsLogger logger = new NetworkStatsMetricsLogger(deps); 194 logger.logRecorderFileReading(PREFIX_UID_TAG, 678, statsDir, collection, 195 false /* useFastDataInput */); 196 verify(deps).writeRecorderFileReadingStats( 197 NETWORK_STATS_RECORDER_FILE_OPERATED__RECORDER_PREFIX__PREFIX_UIDTAG, 198 1 /* readIndex */, 199 678 /* readLatencyMillis */, 200 2 /* fileCount */, 201 26 /* totalFileSize */, 202 0 /* keys */, 203 0 /* uids */, 204 0 /* totalHistorySize */, 205 false /* useFastDataInput */ 206 ); 207 } 208 write(@onNull File baseDir, @NonNull String name, @NonNull String value)209 private void write(@NonNull File baseDir, @NonNull String name, 210 @NonNull String value) throws IOException { 211 final DataOutputStream out = new DataOutputStream( 212 new FileOutputStream(new File(baseDir, name))); 213 out.writeChars(value); 214 out.close(); 215 } 216 } 217