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.apf
17 
18 import android.net.apf.ApfMdnsUtils.extractOffloadReplyRule
19 import android.net.nsd.OffloadEngine
20 import android.net.nsd.OffloadServiceInfo
21 import android.net.nsd.OffloadServiceInfo.Key
22 import android.os.Build
23 import androidx.test.filters.SmallTest
24 import com.android.net.module.util.NetworkStackConstants.TYPE_A
25 import com.android.net.module.util.NetworkStackConstants.TYPE_AAAA
26 import com.android.net.module.util.NetworkStackConstants.TYPE_PTR
27 import com.android.net.module.util.NetworkStackConstants.TYPE_SRV
28 import com.android.net.module.util.NetworkStackConstants.TYPE_TXT
29 import com.android.testutils.DevSdkIgnoreRule
30 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
31 import com.android.testutils.DevSdkIgnoreRunner
32 import java.io.IOException
33 import kotlin.test.assertContentEquals
34 import kotlin.test.assertFailsWith
35 import kotlin.test.assertTrue
36 import org.junit.Rule
37 import org.junit.Test
38 import org.junit.runner.RunWith
39 
40 /**
41  * Tests for Apf mDNS utility functions.
42  */
43 @RunWith(DevSdkIgnoreRunner::class)
44 @SmallTest
45 @IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
46 class ApfMdnsUtilsTest {
47     @get:Rule
48     val ignoreRule = DevSdkIgnoreRule()
49 
50     private val testServiceName1 = "NsdChat"
51     private val testServiceName2 = "NsdCall"
52     private val testServiceType = "_http._tcp.local"
53     private val testSubType = "tsub"
54     private val testHostName = "Android.local"
55     private val testRawPacket1 = byteArrayOf(1, 2, 3, 4, 5)
56     private val testRawPacket2 = byteArrayOf(6, 7, 8, 9)
57     private val encodedFullServiceName1 = intArrayOf(
58             7, 'N'.code, 'S'.code, 'D'.code, 'C'.code, 'H'.code, 'A'.code, 'T'.code,
59             5, '_'.code, 'H'.code, 'T'.code, 'T'.code, 'P'.code,
60             4, '_'.code, 'T'.code, 'C'.code, 'P'.code,
61             5, 'L'.code, 'O'.code, 'C'.code, 'A'.code, 'L'.code,
<lambda>null62             0, 0).map { it.toByte() }.toByteArray()
63     private val encodedFullServiceName2 = intArrayOf(
64             7, 'N'.code, 'S'.code, 'D'.code, 'C'.code, 'A'.code, 'L'.code, 'L'.code,
65             5, '_'.code, 'H'.code, 'T'.code, 'T'.code, 'P'.code,
66             4, '_'.code, 'T'.code, 'C'.code, 'P'.code,
67             5, 'L'.code, 'O'.code, 'C'.code, 'A'.code, 'L'.code,
<lambda>null68             0, 0).map { it.toByte() }.toByteArray()
69     private val encodedServiceType = intArrayOf(
70             5, '_'.code, 'H'.code, 'T'.code, 'T'.code, 'P'.code,
71             4, '_'.code, 'T'.code, 'C'.code, 'P'.code,
72             5, 'L'.code, 'O'.code, 'C'.code, 'A'.code, 'L'.code,
<lambda>null73             0, 0).map { it.toByte() }.toByteArray()
74     private val encodedServiceTypeWithSub1 = intArrayOf(
75             4, 'T'.code, 'S'.code, 'U'.code, 'B'.code,
76             4, '_'.code, 'S'.code, 'U'.code, 'B'.code,
77             5, '_'.code, 'H'.code, 'T'.code, 'T'.code, 'P'.code,
78             4, '_'.code, 'T'.code, 'C'.code, 'P'.code,
79             5, 'L'.code, 'O'.code, 'C'.code, 'A'.code, 'L'.code,
<lambda>null80             0, 0).map { it.toByte() }.toByteArray()
81     private val encodedServiceTypeWithWildCard = intArrayOf(
82             0xff,
83             4, '_'.code, 'S'.code, 'U'.code, 'B'.code,
84             5, '_'.code, 'H'.code, 'T'.code, 'T'.code, 'P'.code,
85             4, '_'.code, 'T'.code, 'C'.code, 'P'.code,
86             5, 'L'.code, 'O'.code, 'C'.code, 'A'.code, 'L'.code,
<lambda>null87             0, 0).map { it.toByte() }.toByteArray()
88     private val encodedTestHostName = intArrayOf(
89             7, 'A'.code, 'N'.code, 'D'.code, 'R'.code, 'O'.code, 'I'.code, 'D'.code,
90             5, 'L'.code, 'O'.code, 'C'.code, 'A'.code, 'L'.code,
<lambda>null91             0, 0).map { it.toByte() }.toByteArray()
92 
93     @Test
testExtractOffloadReplyRule_noPriorityReturnsEmptySetnull94     fun testExtractOffloadReplyRule_noPriorityReturnsEmptySet() {
95         val info = createOffloadServiceInfo(Int.MAX_VALUE)
96         val rules = extractOffloadReplyRule(listOf(info))
97         assertTrue(rules.isEmpty())
98     }
99 
100     @Test
testExtractOffloadReplyRule_extractRulesWithValidPrioritynull101     fun testExtractOffloadReplyRule_extractRulesWithValidPriority() {
102         val info1 = createOffloadServiceInfo(10)
103         val info2 = createOffloadServiceInfo(
104                 11,
105                 testServiceName2,
106                 listOf("a", "b", "c", "d"),
107                 testRawPacket2
108         )
109         val rules = extractOffloadReplyRule(listOf(info2, info1))
110         val expectedResult = listOf(
111                 MdnsOffloadRule(
112                         listOf(
113                                 MdnsOffloadRule.Matcher(encodedServiceType, TYPE_PTR),
114                                 MdnsOffloadRule.Matcher(encodedServiceTypeWithSub1, TYPE_PTR),
115                                 MdnsOffloadRule.Matcher(encodedFullServiceName1, TYPE_SRV),
116                                 MdnsOffloadRule.Matcher(encodedFullServiceName1, TYPE_TXT),
117                                 MdnsOffloadRule.Matcher(encodedTestHostName, TYPE_A),
118                                 MdnsOffloadRule.Matcher(encodedTestHostName, TYPE_AAAA),
119 
120                         ),
121                         testRawPacket1,
122                 ),
123                 MdnsOffloadRule(
124                         listOf(
125                                 MdnsOffloadRule.Matcher(encodedServiceTypeWithWildCard, TYPE_PTR),
126                                 MdnsOffloadRule.Matcher(encodedFullServiceName2, TYPE_SRV),
127                                 MdnsOffloadRule.Matcher(encodedFullServiceName2, TYPE_TXT),
128 
129                         ),
130                         null,
131                 )
132         )
133         assertContentEquals(expectedResult, rules)
134     }
135 
136     @Test
testExtractOffloadReplyRule_longLabelThrowsExceptionnull137     fun testExtractOffloadReplyRule_longLabelThrowsException() {
138         val info = createOffloadServiceInfo(10, "a".repeat(256))
139         assertFailsWith<IOException> { extractOffloadReplyRule(listOf(info)) }
140     }
141 
createOffloadServiceInfonull142     private fun createOffloadServiceInfo(
143             priority: Int,
144             serviceName: String = testServiceName1,
145             subTypes: List<String> = listOf(testSubType),
146             rawPacket1: ByteArray = testRawPacket1
147     ): OffloadServiceInfo = OffloadServiceInfo(
148             Key(serviceName, testServiceType),
149             subTypes,
150             testHostName,
151             rawPacket1,
152             priority,
153             OffloadEngine.OFFLOAD_TYPE_REPLY.toLong()
154         )
155 }
156