1 /*
2  * Copyright (C) 2019 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 com.android.networkstack.metrics
18 
19 import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
20 import android.net.captiveportal.CaptivePortalProbeResult
21 import android.net.metrics.ValidationProbeEvent
22 import android.net.util.DataStallUtils.DATA_STALL_EVALUATION_TYPE_DNS
23 import android.telephony.TelephonyManager
24 import androidx.test.filters.SmallTest
25 import androidx.test.runner.AndroidJUnit4
26 import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
27 import com.android.dx.mockito.inline.extended.ExtendedMockito.verify
28 import com.android.server.connectivity.nano.CellularData
29 import com.android.server.connectivity.nano.DataStallEventProto
30 import com.android.server.connectivity.nano.DnsEvent
31 import com.google.protobuf.nano.MessageNano
32 import java.util.Arrays
33 import org.junit.Assert.assertEquals
34 import org.junit.Test
35 import org.junit.runner.RunWith
36 import org.mockito.ArgumentMatchers.eq
37 
38 @RunWith(AndroidJUnit4::class)
39 @SmallTest
40 class DataStallStatsUtilsTest {
41     private val TEST_ELAPSED_TIME_MS = 123456789L
42     private val TEST_MCCMNC = "123456"
43     private val TEST_SIGNAL_STRENGTH = -100
44     private val RETURN_CODE_DNS_TIMEOUT = 255
45 
46     @Test
testProbeResultToEnumnull47     fun testProbeResultToEnum() {
48         assertEquals(DataStallStatsUtils.probeResultToEnum(null), DataStallEventProto.INVALID)
49         // Metrics cares only http response code.
50         assertEquals(DataStallStatsUtils.probeResultToEnum(
51                 CaptivePortalProbeResult.failed(ValidationProbeEvent.PROBE_HTTP)),
52                 DataStallEventProto.INVALID)
53         assertEquals(DataStallStatsUtils.probeResultToEnum(
54                 CaptivePortalProbeResult.failed(ValidationProbeEvent.PROBE_HTTPS)),
55                 DataStallEventProto.INVALID)
56         assertEquals(DataStallStatsUtils.probeResultToEnum(
57                 CaptivePortalProbeResult.success(ValidationProbeEvent.PROBE_HTTP)),
58                 DataStallEventProto.VALID)
59         assertEquals(DataStallStatsUtils.probeResultToEnum(
60                 CaptivePortalProbeResult.success(ValidationProbeEvent.PROBE_HTTP)),
61                 DataStallEventProto.VALID)
62         assertEquals(DataStallStatsUtils.probeResultToEnum(CaptivePortalProbeResult.PARTIAL),
63                 DataStallEventProto.PARTIAL)
64         assertEquals(DataStallStatsUtils.probeResultToEnum(CaptivePortalProbeResult(
65                 CaptivePortalProbeResult.PORTAL_CODE, ValidationProbeEvent.PROBE_HTTP)),
66                 DataStallEventProto.PORTAL)
67         assertEquals(DataStallStatsUtils.probeResultToEnum(CaptivePortalProbeResult(
68                 CaptivePortalProbeResult.PORTAL_CODE, ValidationProbeEvent.PROBE_HTTPS)),
69                 DataStallEventProto.PORTAL)
70     }
71 
72     @Test
testWritenull73     fun testWrite() {
74         val session = mockitoSession().spyStatic(NetworkStackStatsLog::class.java).startMocking()
75         val stats = DataStallDetectionStats.Builder()
76                 .setEvaluationType(DATA_STALL_EVALUATION_TYPE_DNS)
77                 .setNetworkType(TRANSPORT_CELLULAR)
78                 .setCellData(TelephonyManager.NETWORK_TYPE_LTE /* radioType */,
79                         true /* roaming */,
80                         TEST_MCCMNC /* networkMccmnc */,
81                         TEST_MCCMNC /* simMccmnc */,
82                         TEST_SIGNAL_STRENGTH /* signalStrength */)
83                 .setTcpFailRate(90)
84                 .setTcpSentSinceLastRecv(10)
85         generateTimeoutDnsEvent(stats, count = 5)
86         DataStallStatsUtils.write(stats.build(), CaptivePortalProbeResult.PARTIAL)
87 
88         verify { NetworkStackStatsLog.write(
89                 eq(NetworkStackStatsLog.DATA_STALL_EVENT),
90                 eq(DATA_STALL_EVALUATION_TYPE_DNS),
91                 eq(DataStallEventProto.PARTIAL),
92                 eq(TRANSPORT_CELLULAR),
93                 eq(DataStallDetectionStats.emptyWifiInfoIfNull(null)),
94                 eq(makeTestCellDataNano()),
95                 eq(makeTestDnsTimeoutNano(5)),
96                 eq(90) /* tcpFailRate */,
97                 eq(10) /* tcpSentSinceLastRecv */) }
98 
99         session.finishMocking()
100     }
101 
makeTestDnsTimeoutNanonull102     private fun makeTestDnsTimeoutNano(timeoutCount: Int): ByteArray? {
103         // Make an expected nano dns message.
104         val event = DnsEvent()
105         event.dnsReturnCode = IntArray(timeoutCount)
106         event.dnsTime = LongArray(timeoutCount)
107         Arrays.fill(event.dnsReturnCode, RETURN_CODE_DNS_TIMEOUT)
108         Arrays.fill(event.dnsTime, TEST_ELAPSED_TIME_MS)
109         return MessageNano.toByteArray(event)
110     }
111 
makeTestCellDataNanonull112     private fun makeTestCellDataNano(): ByteArray? {
113         // Make an expected nano cell data message.
114         val data = CellularData()
115         data.ratType = DataStallEventProto.RADIO_TECHNOLOGY_LTE
116         data.networkMccmnc = TEST_MCCMNC
117         data.simMccmnc = TEST_MCCMNC
118         data.isRoaming = true
119         data.signalStrength = TEST_SIGNAL_STRENGTH
120         return MessageNano.toByteArray(data)
121     }
122 
generateTimeoutDnsEventnull123     private fun generateTimeoutDnsEvent(stats: DataStallDetectionStats.Builder, count: Int) {
124         repeat(count) {
125             stats.addDnsEvent(RETURN_CODE_DNS_TIMEOUT, TEST_ELAPSED_TIME_MS)
126         }
127     }
128 }
129