Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright (C) 2017 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 package com.android.server.cts;
     17 
     18 import android.service.NetworkIdentityProto;
     19 import android.service.NetworkInterfaceProto;
     20 import android.service.NetworkStatsCollectionKeyProto;
     21 import android.service.NetworkStatsCollectionStatsProto;
     22 import android.service.NetworkStatsHistoryBucketProto;
     23 import android.service.NetworkStatsHistoryProto;
     24 import android.service.NetworkStatsRecorderProto;
     25 import android.service.NetworkStatsServiceDumpProto;
     26 
     27 import com.android.tradefed.log.LogUtil.CLog;
     28 
     29 import java.util.List;
     30 import java.util.function.Function;
     31 import java.util.function.Predicate;
     32 
     33 /**
     34  * Test for "dumpsys netstats --proto"
     35  *
     36  * Note most of the logic here is just heuristics.
     37  *
     38  * Usage:
     39 
     40   cts-tradefed run cts --skip-device-info --skip-preconditions \
     41       --skip-system-status-check \
     42        com.android.compatibility.common.tradefed.targetprep.NetworkConnectivityChecker \
     43        -a armeabi-v7a -m CtsIncidentHostTestCases -t com.android.server.cts.NetstatsIncidentTest
     44 
     45  */
     46 public class NetstatsIncidentTest extends ProtoDumpTestCase {
     47     private static final String DEVICE_SIDE_TEST_APK = "CtsNetStatsApp.apk";
     48     private static final String DEVICE_SIDE_TEST_PACKAGE = "com.android.server.cts.netstats";
     49     private static final String FEATURE_WIFI = "android.hardware.wifi";
     50 
     51     @Override
     52     protected void tearDown() throws Exception {
     53         getDevice().uninstallPackage(DEVICE_SIDE_TEST_PACKAGE);
     54 
     55         super.tearDown();
     56     }
     57 
     58 
     59     private void assertPositive(String name, long value) {
     60         if (value > 0) return;
     61         fail(name + " expected to be positive, but was: " + value);
     62     }
     63 
     64     private void assertNotNegative(String name, long value) {
     65         if (value >= 0) return;
     66         fail(name + " expected to be zero or positive, but was: " + value);
     67     }
     68 
     69     private void assertGreaterOrEqual(long greater, long lesser) {
     70         assertTrue("" + greater + " expected to be greater than or equal to " + lesser,
     71                 greater >= lesser);
     72     }
     73 
     74     /**
     75      * Parse the output of "dumpsys netstats --proto" and make sure all the values are probable.
     76      */
     77     public void testSanityCheck() throws Exception {
     78 
     79         final long st = System.currentTimeMillis();
     80 
     81         installPackage(DEVICE_SIDE_TEST_APK, /* grantPermissions= */ true);
     82 
     83         // Find the package UID.
     84         final int uid = Integer.parseInt(execCommandAndGetFirstGroup(
     85                 "dumpsys package " + DEVICE_SIDE_TEST_PACKAGE, "userId=(\\d+)"));
     86 
     87         CLog.i("Start time: " + st);
     88         CLog.i("App UID: " + uid);
     89 
     90         // Run the device side test which makes some network requests.
     91         runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, null, null);
     92 
     93         // Make some more activity.
     94         getDevice().executeShellCommand("ping -s 100 -c 10 -i 0  www.android.com");
     95 
     96         // Force refresh the output.
     97         getDevice().executeShellCommand("dumpsys netstats --poll");
     98 
     99         NetworkStatsServiceDumpProto dump = getDump(NetworkStatsServiceDumpProto.parser(),
    100                 "dumpsys netstats --proto");
    101 
    102         CLog.d("First dump:\n" + dump.toString());
    103 
    104         // Basic sanity check.
    105         checkInterfaces(dump.getActiveInterfacesList());
    106         checkInterfaces(dump.getActiveUidInterfacesList());
    107 
    108         checkStats(dump.getDevStats(), /*withUid=*/ false, /*withTag=*/ false);
    109         checkStats(dump.getXtStats(), /*withUid=*/ false, /*withTag=*/ false);
    110         checkStats(dump.getUidStats(), /*withUid=*/ true, /*withTag=*/ false);
    111         checkStats(dump.getUidTagStats(), /*withUid=*/ true, /*withTag=*/ true);
    112 
    113         // Remember the original values.
    114         final Predicate<NetworkStatsCollectionKeyProto> uidFilt = key -> key.getUid() == uid;
    115         final Predicate<NetworkStatsCollectionKeyProto> tagFilt =
    116                 key -> (key.getTag() == 123123123) && (key.getUid() == uid);
    117 
    118         final long devRxPackets = sum(dump.getDevStats(), st, b -> b.getRxPackets());
    119         final long devRxBytes = sum(dump.getDevStats(), st, b -> b.getRxBytes());
    120         final long devTxPackets = sum(dump.getDevStats(), st, b -> b.getTxPackets());
    121         final long devTxBytes = sum(dump.getDevStats(), st, b -> b.getTxBytes());
    122 
    123         final long xtRxPackets = sum(dump.getXtStats(), st, b -> b.getRxPackets());
    124         final long xtRxBytes = sum(dump.getXtStats(), st, b -> b.getRxBytes());
    125         final long xtTxPackets = sum(dump.getXtStats(), st, b -> b.getTxPackets());
    126         final long xtTxBytes = sum(dump.getXtStats(), st, b -> b.getTxBytes());
    127 
    128         final long uidRxPackets = sum(dump.getUidStats(), st, uidFilt, b -> b.getRxPackets());
    129         final long uidRxBytes = sum(dump.getUidStats(), st, uidFilt, b -> b.getRxBytes());
    130         final long uidTxPackets = sum(dump.getUidStats(), st, uidFilt, b -> b.getTxPackets());
    131         final long uidTxBytes = sum(dump.getUidStats(), st, uidFilt, b -> b.getTxBytes());
    132 
    133         final long tagRxPackets = sum(dump.getUidTagStats(), st, tagFilt, b -> b.getRxPackets());
    134         final long tagRxBytes = sum(dump.getUidTagStats(), st, tagFilt, b -> b.getRxBytes());
    135         final long tagTxPackets = sum(dump.getUidTagStats(), st, tagFilt, b -> b.getTxPackets());
    136         final long tagTxBytes = sum(dump.getUidTagStats(), st, tagFilt, b -> b.getTxBytes());
    137 
    138         // Run again to make some more activity.
    139         runDeviceTests(DEVICE_SIDE_TEST_PACKAGE,
    140                 "com.android.server.cts.netstats.NetstatsDeviceTest",
    141                 "testDoNetworkWithoutTagging");
    142 
    143         getDevice().executeShellCommand("dumpsys netstats --poll");
    144         dump = getDump(NetworkStatsServiceDumpProto.parser(), "dumpsys netstats --proto");
    145 
    146         CLog.d("Second dump:\n" + dump.toString());
    147 
    148         final long devRxPackets2 = sum(dump.getDevStats(), st, b -> b.getRxPackets());
    149         final long devRxBytes2 = sum(dump.getDevStats(), st, b -> b.getRxBytes());
    150         final long devTxPackets2 = sum(dump.getDevStats(), st, b -> b.getTxPackets());
    151         final long devTxBytes2 = sum(dump.getDevStats(), st, b -> b.getTxBytes());
    152 
    153         final long xtRxPackets2 = sum(dump.getXtStats(), st, b -> b.getRxPackets());
    154         final long xtRxBytes2 = sum(dump.getXtStats(), st, b -> b.getRxBytes());
    155         final long xtTxPackets2 = sum(dump.getXtStats(), st, b -> b.getTxPackets());
    156         final long xtTxBytes2 = sum(dump.getXtStats(), st, b -> b.getTxBytes());
    157 
    158         final long uidRxPackets2 = sum(dump.getUidStats(), st, uidFilt, b -> b.getRxPackets());
    159         final long uidRxBytes2 = sum(dump.getUidStats(), st, uidFilt, b -> b.getRxBytes());
    160         final long uidTxPackets2 = sum(dump.getUidStats(), st, uidFilt, b -> b.getTxPackets());
    161         final long uidTxBytes2 = sum(dump.getUidStats(), st, uidFilt, b -> b.getTxBytes());
    162 
    163         final long tagRxPackets2 = sum(dump.getUidTagStats(), st, tagFilt, b -> b.getRxPackets());
    164         final long tagRxBytes2 = sum(dump.getUidTagStats(), st, tagFilt, b -> b.getRxBytes());
    165         final long tagTxPackets2 = sum(dump.getUidTagStats(), st, tagFilt, b -> b.getTxPackets());
    166         final long tagTxBytes2 = sum(dump.getUidTagStats(), st, tagFilt, b -> b.getTxBytes());
    167 
    168         // At least 1 packet, 100 bytes sent.
    169         assertGreaterOrEqual(uidTxPackets2, uidTxPackets + 1);
    170         assertGreaterOrEqual(uidTxBytes2, uidTxBytes + 100);
    171 
    172 //        assertGreaterOrEqual(tagTxPackets2, tagTxPackets + 1);
    173 //        assertGreaterOrEqual(tagTxBytes2, tagTxBytes + 100);
    174 
    175         // At least 2 packets, 100 bytes sent.
    176         assertGreaterOrEqual(uidRxPackets2, uidRxPackets + 2);
    177         assertGreaterOrEqual(uidRxBytes2, uidRxBytes + 100);
    178 
    179 //        assertGreaterOrEqual(tagRxPackets2, tagRxPackets + 2);
    180 //        assertGreaterOrEqual(tagRxBytes2, tagRxBytes + 100);
    181 
    182         // Run again to make some more activity.
    183         runDeviceTests(DEVICE_SIDE_TEST_PACKAGE,
    184                 "com.android.server.cts.netstats.NetstatsDeviceTest",
    185                 "testDoNetworkWithTagging");
    186 
    187         getDevice().executeShellCommand("dumpsys netstats --poll");
    188         dump = getDump(NetworkStatsServiceDumpProto.parser(), "dumpsys netstats --proto");
    189 
    190         CLog.d("Second dump:\n" + dump.toString());
    191 
    192         final long devRxPackets3 = sum(dump.getDevStats(), st, b -> b.getRxPackets());
    193         final long devRxBytes3 = sum(dump.getDevStats(), st, b -> b.getRxBytes());
    194         final long devTxPackets3 = sum(dump.getDevStats(), st, b -> b.getTxPackets());
    195         final long devTxBytes3 = sum(dump.getDevStats(), st, b -> b.getTxBytes());
    196 
    197         final long xtRxPackets3 = sum(dump.getXtStats(), st, b -> b.getRxPackets());
    198         final long xtRxBytes3 = sum(dump.getXtStats(), st, b -> b.getRxBytes());
    199         final long xtTxPackets3 = sum(dump.getXtStats(), st, b -> b.getTxPackets());
    200         final long xtTxBytes3 = sum(dump.getXtStats(), st, b -> b.getTxBytes());
    201 
    202         final long uidRxPackets3 = sum(dump.getUidStats(), st, uidFilt, b -> b.getRxPackets());
    203         final long uidRxBytes3 = sum(dump.getUidStats(), st, uidFilt, b -> b.getRxBytes());
    204         final long uidTxPackets3 = sum(dump.getUidStats(), st, uidFilt, b -> b.getTxPackets());
    205         final long uidTxBytes3 = sum(dump.getUidStats(), st, uidFilt, b -> b.getTxBytes());
    206 
    207         final long tagRxPackets3 = sum(dump.getUidTagStats(), st, tagFilt, b -> b.getRxPackets());
    208         final long tagRxBytes3 = sum(dump.getUidTagStats(), st, tagFilt, b -> b.getRxBytes());
    209         final long tagTxPackets3 = sum(dump.getUidTagStats(), st, tagFilt, b -> b.getTxPackets());
    210         final long tagTxBytes3 = sum(dump.getUidTagStats(), st, tagFilt, b -> b.getTxBytes());
    211 
    212         // At least 1 packet, 100 bytes sent.
    213         assertGreaterOrEqual(uidTxPackets3, uidTxPackets2 + 1);
    214         assertGreaterOrEqual(uidTxBytes3, uidTxBytes2 + 100);
    215 
    216         assertGreaterOrEqual(tagTxPackets3, tagTxPackets2 + 1);
    217         assertGreaterOrEqual(tagTxBytes3, tagTxBytes2 + 100);
    218 
    219         // At least 2 packets, 100 bytes sent.
    220         assertGreaterOrEqual(uidRxPackets3, uidRxPackets2 + 2);
    221         assertGreaterOrEqual(uidRxBytes3, uidRxBytes2 + 100);
    222 
    223         assertGreaterOrEqual(tagRxPackets3, tagRxPackets2 + 2);
    224         assertGreaterOrEqual(tagRxBytes3, tagRxBytes2 + 100);
    225     }
    226 
    227     private long sum(NetworkStatsRecorderProto recorder,
    228             long startTime,
    229             Function<NetworkStatsHistoryBucketProto, Long> func) {
    230         return sum(recorder, startTime, key -> true, func);
    231     }
    232 
    233     private long sum(NetworkStatsRecorderProto recorder,
    234             long startTime,
    235             Predicate<NetworkStatsCollectionKeyProto> filter,
    236             Function<NetworkStatsHistoryBucketProto, Long> func) {
    237 
    238         long total = 0;
    239         for (NetworkStatsCollectionStatsProto stats
    240                 : recorder.getCompleteHistory().getStatsList()) {
    241             if (!filter.test(stats.getKey())) {
    242                 continue;
    243             }
    244             for (NetworkStatsHistoryBucketProto bucket : stats.getHistory().getBucketsList()) {
    245                 if (startTime < bucket.getBucketStartMs()) {
    246                     continue;
    247                 }
    248                 total += func.apply(bucket);
    249             }
    250         }
    251         return total;
    252     }
    253 
    254     private void checkInterfaces(List<NetworkInterfaceProto> interfaces) throws Exception{
    255         /* Example:
    256     active_interfaces=[
    257       NetworkInterfaceProto {
    258         interface=wlan0
    259         identities=NetworkIdentitySetProto {
    260           identities=[
    261             NetworkIdentityProto {
    262               type=1
    263               subscriber_id=
    264               network_id="wifiap"
    265               roaming=false
    266               metered=false
    267             }
    268           ]
    269         }
    270       }
    271     ]
    272          */
    273         assertTrue("There must be at least one network device",
    274                 interfaces.size() > 0);
    275 
    276         boolean allRoaming = true;
    277         boolean allMetered = true;
    278 
    279         for (NetworkInterfaceProto iface : interfaces) {
    280             assertTrue("Missing interface name", !iface.getInterface().isEmpty());
    281 
    282             assertPositive("# identities", iface.getIdentities().getIdentitiesList().size());
    283 
    284             for (NetworkIdentityProto iden : iface.getIdentities().getIdentitiesList()) {
    285                 allRoaming &= iden.getRoaming();
    286                 allMetered &= iden.getMetered();
    287 
    288                 // TODO Can we check the other fields too?  type, subscriber_id, and network_id.
    289             }
    290         }
    291         assertFalse("There must be at least one non-roaming interface during CTS", allRoaming);
    292         if (hasWiFiFeature()) {
    293             assertFalse("There must be at least one non-metered interface during CTS", allMetered);
    294         }
    295     }
    296 
    297     private void checkStats(NetworkStatsRecorderProto recorder, boolean withUid, boolean withTag) {
    298         /*
    299          * Example:
    300     dev_stats=NetworkStatsRecorderProto {
    301       pending_total_bytes=136
    302       complete_history=NetworkStatsCollectionProto {
    303         stats=[
    304           NetworkStatsCollectionStatsProto {
    305             key=NetworkStatsCollectionKeyProto {
    306               identity=NetworkIdentitySetProto {
    307                 identities=[
    308                   NetworkIdentityProto {
    309                     type=1
    310                     subscriber_id=
    311                     network_id="wifiap"
    312                     roaming=false
    313                     metered=false
    314                   }
    315                 ]
    316               }
    317               uid=-1
    318               set=-1
    319               tag=0
    320             }
    321             history=NetworkStatsHistoryProto {
    322               bucket_duration_ms=3600000
    323               buckets=[
    324                 NetworkStatsHistoryBucketProto {
    325                   bucket_start_ms=2273694336
    326                   rx_bytes=2142
    327                   rx_packets=10
    328                   tx_bytes=1568
    329                   tx_packets=12
    330                   operations=0
    331                 }
    332                 NetworkStatsHistoryBucketProto {
    333                   bucket_start_ms=3196682880
    334                   rx_bytes=2092039
    335                   rx_packets=1987
    336                   tx_bytes=236735
    337                   tx_packets=1750
    338                   operations=0
    339                 }
    340          */
    341 
    342         assertNotNegative("Pending bytes", recorder.getPendingTotalBytes());
    343 
    344         for (NetworkStatsCollectionStatsProto stats : recorder.getCompleteHistory().getStatsList()) {
    345 
    346             final NetworkStatsCollectionKeyProto key = stats.getKey();
    347 
    348             // TODO Check the key.
    349 
    350             final NetworkStatsHistoryProto hist = stats.getHistory();
    351 
    352             assertPositive("duration", hist.getBucketDurationMs());
    353 
    354             // Subtract one hour from duration to compensate for possible DTS.
    355             final long minInterval = hist.getBucketDurationMs() - (60 * 60 * 1000);
    356 
    357             NetworkStatsHistoryBucketProto prev = null;
    358             for (NetworkStatsHistoryBucketProto bucket : hist.getBucketsList()) {
    359 
    360                 // Make sure the start time is increasing by at least the "duration",
    361                 // except we subtract duration from one our to compensate possible DTS.
    362 
    363                 if (prev != null) {
    364                     assertTrue(
    365                             String.format("Last start=%d, current start=%d, diff=%d, duration=%d",
    366                                     prev.getBucketStartMs(), bucket.getBucketStartMs(),
    367                                     (bucket.getBucketStartMs() - prev.getBucketStartMs()),
    368                                     minInterval),
    369                             (bucket.getBucketStartMs() - prev.getBucketStartMs()) >=
    370                                     minInterval);
    371                 }
    372                 assertNotNegative("RX bytes", bucket.getRxBytes());
    373                 assertNotNegative("RX packets", bucket.getRxPackets());
    374                 assertNotNegative("TX bytes", bucket.getTxBytes());
    375                 assertNotNegative("TX packets", bucket.getTxPackets());
    376 
    377                 assertTrue(
    378                         String.format("# of bytes %d too small for # of packets %d",
    379                                 bucket.getRxBytes(), bucket.getRxPackets()),
    380                         bucket.getRxBytes() >= bucket.getRxPackets());
    381                 assertTrue(
    382                         String.format("# of bytes %d too small for # of packets %d",
    383                                 bucket.getTxBytes(), bucket.getTxPackets()),
    384                         bucket.getTxBytes() >= bucket.getTxPackets());
    385             }
    386         }
    387 
    388         // TODO Make sure test app's UID actually shows up.
    389     }
    390 
    391     private boolean hasWiFiFeature() throws Exception {
    392         final String commandOutput = getDevice().executeShellCommand("pm list features");
    393         return commandOutput.contains(FEATURE_WIFI);
    394     }
    395 
    396     // Currently only verifies that privacy filtering is done properly.
    397     static void verifyNetworkStatsServiceDumpProto(NetworkStatsServiceDumpProto dump, final int filterLevel) throws Exception {
    398         if (filterLevel == PRIVACY_AUTO) {
    399             for (NetworkInterfaceProto nip : dump.getActiveInterfacesList()) {
    400                 verifyNetworkInterfaceProto(nip, filterLevel);
    401             }
    402             for (NetworkInterfaceProto nip : dump.getActiveUidInterfacesList()) {
    403                 verifyNetworkInterfaceProto(nip, filterLevel);
    404             }
    405         }
    406     }
    407 
    408     private static void verifyNetworkInterfaceProto(NetworkInterfaceProto nip, final int filterLevel) throws Exception {
    409         for (NetworkIdentityProto ni : nip.getIdentities().getIdentitiesList()) {
    410             if (filterLevel == PRIVACY_AUTO) {
    411                 assertTrue(ni.getSubscriberId().isEmpty());
    412                 assertTrue(ni.getNetworkId().isEmpty());
    413             }
    414         }
    415     }
    416 }
    417