1 /*
2  * Copyright (C) 2024 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 android.net.util
17 
18 import android.net.ip.ConnectivityPacketTracker
19 import android.os.HandlerThread
20 import androidx.test.filters.SmallTest
21 import com.android.testutils.DevSdkIgnoreRunner
22 import com.android.testutils.FunctionalUtils.ThrowingSupplier
23 import com.android.testutils.assertThrows
24 import com.android.testutils.visibleOnHandlerThread
25 import kotlin.test.assertEquals
26 import kotlin.test.assertTrue
27 import org.junit.After
28 import org.junit.Before
29 import org.junit.Test
30 import org.mockito.ArgumentMatchers.any
31 import org.mockito.ArgumentMatchers.anyInt
32 import org.mockito.ArgumentMatchers.eq
33 import org.mockito.Mockito
34 import org.mockito.Mockito.clearInvocations
35 import org.mockito.Mockito.doReturn
36 import org.mockito.Mockito.mock
37 import org.mockito.Mockito.timeout
38 import org.mockito.Mockito.verify
39 import org.mockito.Mockito.verifyZeroInteractions
40 
41 /**
42  * Test for RawPacketTracker.
43  */
44 @SmallTest
45 @DevSdkIgnoreRunner.MonitorThreadLeak
46 class RawPacketTrackerTest {
47     companion object {
48         private const val TEST_TIMEOUT_MS: Long = 1000
49         private const val TEST_MAX_CAPTURE_TIME_MS: Long = 1000
50         private const val TAG = "RawPacketTrackerTest"
51     }
52 
53     private val deps = mock(RawPacketTracker.Dependencies::class.java)
54     private val tracker = mock(ConnectivityPacketTracker::class.java)
55     private val ifaceName = "lo"
<lambda>null56     private val handlerThread by lazy {
57         HandlerThread("$TAG-handler-thread").apply { start() }
58     }
59     private lateinit var rawTracker: RawPacketTracker
60 
61     @Before
setUpnull62     fun setUp() {
63         doReturn(handlerThread).`when`(deps).createHandlerThread()
64         doReturn(handlerThread.looper).`when`(deps).getLooper(any())
65         doReturn(tracker).`when`(deps).createPacketTracker(any(), any(), anyInt())
66         rawTracker = RawPacketTracker(deps)
67     }
68 
69     @After
tearDownnull70     fun tearDown() {
71         Mockito.framework().clearInlineMocks()
72         handlerThread.quitSafely()
73         handlerThread.join()
74     }
75 
76     @Test
testStartCapturenull77     fun testStartCapture() {
78         // start capturing
79         startCaptureOnHandler(ifaceName)
80         verifySetCapture(true, 1)
81 
82         assertTrue(rawTracker.handler.hasMessages(RawPacketTracker.CMD_STOP_CAPTURE))
83     }
84 
85     @Test
testInvalidStartCapturenull86     fun testInvalidStartCapture() {
87         // start capturing with negative timeout
88         assertThrows(IllegalArgumentException::class.java) {
89             startCaptureOnHandler(ifaceName, -1)
90         }
91     }
92 
93     @Test
testStopCapturenull94     fun testStopCapture() {
95         // start capturing
96         startCaptureOnHandler(ifaceName)
97         // simulate capture status for stop capturing
98         verifySetCapture(true, 1)
99 
100         // stop capturing
101         stopCaptureOnHandler(ifaceName)
102         verifySetCapture(false, 1)
103         verifyZeroInteractions(tracker)
104     }
105 
106     @Test
testDuplicatedStartAndStopnull107     fun testDuplicatedStartAndStop() {
108         // start capture with a long timeout
109         startCaptureOnHandler(ifaceName, 10_000)
110         verifySetCapture(true, 1)
111 
112         // start capturing for multiple times
113         for (i in 1..10) {
114             assertThrows(RuntimeException::class.java) {
115                 startCaptureOnHandler(ifaceName)
116             }
117         }
118 
119         // expect no duplicated start capture
120         verifySetCapture(true, 0)
121 
122         // stop capturing for multiple times
123         stopCaptureOnHandler(ifaceName)
124         verifySetCapture(false, 1)
125         for (i in 1..10) {
126             assertThrows(RuntimeException::class.java) {
127                 stopCaptureOnHandler(ifaceName)
128             }
129         }
130 
131         verifySetCapture(false, 0)
132         verifyZeroInteractions(tracker)
133     }
134 
135     @Test
testMatchedPacketCountnull136     fun testMatchedPacketCount() {
137         val matchedPkt = "12345"
138         val notMatchedPkt = "54321"
139 
140         // simulate get matched packet count
141         doReturn(1).`when`(tracker).getMatchedPacketCount(matchedPkt)
142         // simulate get not matched packet count
143         doReturn(0).`when`(tracker).getMatchedPacketCount(notMatchedPkt)
144 
145         // start capture
146         startCaptureOnHandler(ifaceName)
147 
148         assertEquals(1, getMatchedPktCntOnHandler(ifaceName, matchedPkt))
149         assertEquals(0, getMatchedPktCntOnHandler(ifaceName, notMatchedPkt))
150 
151         // for non-existed interface
152         val nonExistedIface = "non-existed-iface"
153         assertThrows(RuntimeException::class.java) {
154             getMatchedPktCntOnHandler(nonExistedIface, matchedPkt)
155             getMatchedPktCntOnHandler(nonExistedIface, notMatchedPkt)
156         }
157 
158         // stop capture
159         stopCaptureOnHandler(ifaceName)
160 
161         // expect no matched packet after stop capturing
162         assertThrows(RuntimeException::class.java) {
163             getMatchedPktCntOnHandler(ifaceName, matchedPkt)
164             getMatchedPktCntOnHandler(ifaceName, notMatchedPkt)
165         }
166     }
167 
startCaptureOnHandlernull168     private fun startCaptureOnHandler(
169         ifaceName: String, maxCaptureTime: Long = TEST_MAX_CAPTURE_TIME_MS
170     ) {
171         visibleOnHandlerThread(rawTracker.handler) {
172             rawTracker.startCapture(ifaceName, maxCaptureTime)
173         }
174     }
175 
stopCaptureOnHandlernull176     private fun stopCaptureOnHandler(ifaceName: String) {
177         visibleOnHandlerThread(rawTracker.handler) {
178             rawTracker.stopCapture(ifaceName)
179         }
180     }
181 
getMatchedPktCntOnHandlernull182     private fun getMatchedPktCntOnHandler(ifaceName: String, packetPattern: String): Int {
183         return visibleOnHandlerThread(rawTracker.handler, ThrowingSupplier {
184             rawTracker.getMatchedPacketCount(ifaceName, packetPattern)
185         })
186     }
187 
verifySetCapturenull188     private fun verifySetCapture(
189         isCapture: Boolean,
190         receiveCnt: Int
191     ) {
192         verify(tracker, timeout(TEST_TIMEOUT_MS).times(receiveCnt)).setCapture(eq(isCapture))
193         clearInvocations<Any>(tracker)
194     }
195 }