1 /*
2 * Copyright (C) 2023 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.server
18
19 import android.content.Intent
20 import android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED
21 import android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED
22 import android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED
23 import android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED
24 import android.net.InetAddresses
25 import android.net.LinkProperties
26 import android.os.Build
27 import android.os.Build.VERSION_CODES
28 import androidx.test.filters.SmallTest
29 import com.android.testutils.DevSdkIgnoreRule
30 import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter
31 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
32 import com.android.testutils.DevSdkIgnoreRunner
33 import com.android.testutils.visibleOnHandlerThread
34 import org.junit.Rule
35 import org.junit.Test
36 import org.junit.runner.RunWith
37 import org.mockito.ArgumentMatchers.anyBoolean
38 import org.mockito.ArgumentMatchers.anyInt
39 import org.mockito.ArgumentMatchers.eq
40 import org.mockito.Mockito.atLeastOnce
41 import org.mockito.Mockito.doReturn
42 import org.mockito.Mockito.inOrder
43 import org.mockito.Mockito.never
44 import org.mockito.Mockito.verify
45
46 internal val LOCAL_DNS = InetAddresses.parseNumericAddress("224.0.1.2")
47 internal val NON_LOCAL_DNS = InetAddresses.parseNumericAddress("76.76.75.75")
48
49 private const val IFNAME_1 = "wlan1"
50 private const val IFNAME_2 = "wlan2"
51 private const val PORT_53 = 53
52 private const val PROTOCOL_TCP = 6
53 private const val PROTOCOL_UDP = 17
54
<lambda>null55 private val lpWithNoLocalDns = LinkProperties().apply {
56 addDnsServer(NON_LOCAL_DNS)
57 interfaceName = IFNAME_1
58 }
59
<lambda>null60 private val lpWithLocalDns = LinkProperties().apply {
61 addDnsServer(LOCAL_DNS)
62 interfaceName = IFNAME_2
63 }
64
65 @DevSdkIgnoreRunner.MonitorThreadLeak
66 @RunWith(DevSdkIgnoreRunner::class)
67 @SmallTest
68 @IgnoreUpTo(Build.VERSION_CODES.S_V2) // Bpf only supports in T+.
69 class CSBpfNetMapsTest : CSTest() {
70 @get:Rule
71 val ignoreRule = DevSdkIgnoreRule()
72
73 @IgnoreAfter(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
74 @Test
testCSTrackDataSaverBeforeVnull75 fun testCSTrackDataSaverBeforeV() {
76 val inOrder = inOrder(bpfNetMaps)
77 mockDataSaverStatus(RESTRICT_BACKGROUND_STATUS_WHITELISTED)
78 inOrder.verify(bpfNetMaps).setDataSaverEnabled(true)
79 mockDataSaverStatus(RESTRICT_BACKGROUND_STATUS_DISABLED)
80 inOrder.verify(bpfNetMaps).setDataSaverEnabled(false)
81 mockDataSaverStatus(RESTRICT_BACKGROUND_STATUS_ENABLED)
82 inOrder.verify(bpfNetMaps).setDataSaverEnabled(true)
83 }
84
85 // Data Saver Status is updated from platform code in V+.
86 @IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
87 @Test
testCSTrackDataSaverAboveUnull88 fun testCSTrackDataSaverAboveU() {
89 listOf(RESTRICT_BACKGROUND_STATUS_WHITELISTED, RESTRICT_BACKGROUND_STATUS_ENABLED,
90 RESTRICT_BACKGROUND_STATUS_DISABLED).forEach {
91 mockDataSaverStatus(it)
92 verify(bpfNetMaps, never()).setDataSaverEnabled(anyBoolean())
93 }
94 }
95
96 @IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
97 @Test
testLocalPrefixesUpdatedInBpfMapnull98 fun testLocalPrefixesUpdatedInBpfMap() {
99 // Connect Wi-Fi network with non-local dns.
100 val wifiAgent = Agent(nc = defaultNc(), lp = lpWithNoLocalDns)
101 wifiAgent.connect()
102
103 // Verify that block rule is added to BpfMap for local prefixes.
104 verify(bpfNetMaps, atLeastOnce()).addLocalNetAccess(any(), eq(IFNAME_1),
105 any(), eq(0), eq(0), eq(false))
106
107 wifiAgent.disconnect()
108 val cellAgent = Agent(nc = defaultNc(), lp = lpWithLocalDns)
109 cellAgent.connect()
110
111 // Verify that block rule is removed from BpfMap for local prefixes.
112 verify(bpfNetMaps, atLeastOnce()).removeLocalNetAccess(any(), eq(IFNAME_1),
113 any(), eq(0), eq(0))
114
115 cellAgent.disconnect()
116 }
117
118 @IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
119 @Test
testLocalDnsNotUpdatedInBpfMapnull120 fun testLocalDnsNotUpdatedInBpfMap() {
121 // Connect Wi-Fi network with non-local dns.
122 val wifiAgent = Agent(nc = defaultNc(), lp = lpWithNoLocalDns)
123 wifiAgent.connect()
124
125 // Verify that No allow rule is added to BpfMap since there is no local dns.
126 verify(bpfNetMaps, never()).addLocalNetAccess(any(), any(), any(), any(), any(),
127 eq(true))
128
129 wifiAgent.disconnect()
130 val cellAgent = Agent(nc = defaultNc(), lp = lpWithLocalDns)
131 cellAgent.connect()
132
133 // Verify that No allow rule from port 53 is removed on network change
134 // because no dns was added
135 verify(bpfNetMaps, never()).removeLocalNetAccess(eq(192), eq(IFNAME_1),
136 eq(NON_LOCAL_DNS), any(), eq(PORT_53))
137
138 cellAgent.disconnect()
139 }
140
141 @IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM)
142 @Test
testLocalDnsUpdatedInBpfMapnull143 fun testLocalDnsUpdatedInBpfMap() {
144 // Connect Wi-Fi network with one local Dns.
145 val wifiAgent = Agent(nc = defaultNc(), lp = lpWithLocalDns)
146 wifiAgent.connect()
147
148 // Verify that allow rule is added to BpfMap for local dns at port 53,
149 // for TCP(=6) protocol
150 verify(bpfNetMaps, atLeastOnce()).addLocalNetAccess(eq(192), eq(IFNAME_2),
151 eq(LOCAL_DNS), eq(PROTOCOL_TCP), eq(PORT_53), eq(true))
152 // And for UDP(=17) protocol
153 verify(bpfNetMaps, atLeastOnce()).addLocalNetAccess(eq(192), eq(IFNAME_2),
154 eq(LOCAL_DNS), eq(PROTOCOL_UDP), eq(PORT_53), eq(true))
155
156 wifiAgent.disconnect()
157 val cellAgent = Agent(nc = defaultNc(), lp = lpWithNoLocalDns)
158 cellAgent.connect()
159
160 // Verify that allow rule is removed for local dns on network change,
161 // for TCP(=6) protocol
162 verify(bpfNetMaps, atLeastOnce()).removeLocalNetAccess(eq(192), eq(IFNAME_2),
163 eq(LOCAL_DNS), eq(PROTOCOL_TCP), eq(PORT_53))
164 // And for UDP(=17) protocol
165 verify(bpfNetMaps, atLeastOnce()).removeLocalNetAccess(eq(192), eq(IFNAME_2),
166 eq(LOCAL_DNS), eq(PROTOCOL_UDP), eq(PORT_53))
167
168 cellAgent.disconnect()
169 }
170
mockDataSaverStatusnull171 private fun mockDataSaverStatus(status: Int) {
172 doReturn(status).`when`(context.networkPolicyManager).getRestrictBackgroundStatus(anyInt())
173 // While the production code dispatches the intent on the handler thread,
174 // The test would dispatch the intent in the caller thread. Make it dispatch
175 // on the handler thread to match production behavior.
176 visibleOnHandlerThread(csHandler) {
177 context.sendBroadcast(Intent(ACTION_RESTRICT_BACKGROUND_CHANGED))
178 }
179 waitForIdle()
180 }
181 }
182