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 17 18 package com.android.server.wifi.rtt; 19 20 import static org.hamcrest.core.IsEqual.equalTo; 21 import static org.junit.Assert.assertFalse; 22 import static org.junit.Assert.assertTrue; 23 import static org.mockito.ArgumentMatchers.any; 24 import static org.mockito.ArgumentMatchers.anyInt; 25 import static org.mockito.ArgumentMatchers.eq; 26 import static org.mockito.Mockito.verify; 27 import static org.mockito.Mockito.verifyNoMoreInteractions; 28 import static org.mockito.Mockito.when; 29 30 import android.hardware.wifi.V1_0.IWifiRttController; 31 import android.hardware.wifi.V1_0.RttBw; 32 import android.hardware.wifi.V1_0.RttCapabilities; 33 import android.hardware.wifi.V1_0.RttConfig; 34 import android.hardware.wifi.V1_0.RttPeerType; 35 import android.hardware.wifi.V1_0.RttPreamble; 36 import android.hardware.wifi.V1_0.RttResult; 37 import android.hardware.wifi.V1_0.RttStatus; 38 import android.hardware.wifi.V1_0.RttType; 39 import android.hardware.wifi.V1_0.WifiChannelWidthInMhz; 40 import android.hardware.wifi.V1_0.WifiStatus; 41 import android.hardware.wifi.V1_0.WifiStatusCode; 42 import android.net.MacAddress; 43 import android.net.wifi.rtt.RangingRequest; 44 45 import com.android.server.wifi.HalDeviceManager; 46 47 import org.hamcrest.core.IsNull; 48 import org.junit.Before; 49 import org.junit.Rule; 50 import org.junit.Test; 51 import org.junit.rules.ErrorCollector; 52 import org.mockito.ArgumentCaptor; 53 import org.mockito.Mock; 54 import org.mockito.MockitoAnnotations; 55 56 import java.util.ArrayList; 57 import java.util.List; 58 59 /** 60 * Unit test harness for the RttNative class. 61 */ 62 public class RttNativeTest { 63 private RttNative mDut; 64 private WifiStatus mStatusSuccess; 65 66 private ArgumentCaptor<ArrayList> mRttConfigCaptor = ArgumentCaptor.forClass(ArrayList.class); 67 private ArgumentCaptor<List> mRttResultCaptor = ArgumentCaptor.forClass(List.class); 68 private ArgumentCaptor<HalDeviceManager.ManagerStatusListener> mHdmStatusListener = 69 ArgumentCaptor.forClass(HalDeviceManager.ManagerStatusListener.class); 70 private ArgumentCaptor<IWifiRttController.getCapabilitiesCallback> mGetCapCbCatpr = 71 ArgumentCaptor.forClass(IWifiRttController.getCapabilitiesCallback.class); 72 73 @Rule 74 public ErrorCollector collector = new ErrorCollector(); 75 76 @Mock 77 public RttServiceImpl mockRttServiceImpl; 78 79 @Mock 80 public HalDeviceManager mockHalDeviceManager; 81 82 @Mock 83 public IWifiRttController mockRttController; 84 85 @Before 86 public void setUp() throws Exception { 87 MockitoAnnotations.initMocks(this); 88 89 when(mockHalDeviceManager.isStarted()).thenReturn(true); 90 when(mockHalDeviceManager.createRttController()).thenReturn(mockRttController); 91 92 mStatusSuccess = new WifiStatus(); 93 mStatusSuccess.code = WifiStatusCode.SUCCESS; 94 when(mockRttController.registerEventCallback(any())).thenReturn(mStatusSuccess); 95 when(mockRttController.rangeRequest(anyInt(), any(ArrayList.class))).thenReturn( 96 mStatusSuccess); 97 when(mockRttController.rangeCancel(anyInt(), any(ArrayList.class))).thenReturn( 98 mStatusSuccess); 99 100 mDut = new RttNative(mockRttServiceImpl, mockHalDeviceManager); 101 mDut.start(null); 102 verify(mockHalDeviceManager).registerStatusListener(mHdmStatusListener.capture(), any()); 103 verify(mockRttController).registerEventCallback(any()); 104 verify(mockRttServiceImpl).enableIfPossible(); 105 verify(mockRttController).getCapabilities(mGetCapCbCatpr.capture()); 106 // will override capabilities (just call cb again) for specific tests 107 mGetCapCbCatpr.getValue().onValues(mStatusSuccess, getFullRttCapabilities()); 108 assertTrue(mDut.isReady()); 109 } 110 111 /** 112 * Validate successful ranging flow. 113 */ 114 @Test 115 public void testRangeRequest() throws Exception { 116 int cmdId = 55; 117 RangingRequest request = RttTestUtils.getDummyRangingRequest((byte) 0); 118 119 // (1) issue range request 120 mDut.rangeRequest(cmdId, request, true); 121 122 // (2) verify HAL call and parameters 123 verify(mockRttController).rangeRequest(eq(cmdId), mRttConfigCaptor.capture()); 124 125 // verify contents of HAL request (hard codes knowledge from getDummyRangingRequest()). 126 ArrayList<RttConfig> halRequest = mRttConfigCaptor.getValue(); 127 128 collector.checkThat("number of entries", halRequest.size(), 129 equalTo(request.mRttPeers.size())); 130 131 RttConfig rttConfig = halRequest.get(0); 132 collector.checkThat("entry 0: MAC", rttConfig.addr, 133 equalTo(MacAddress.fromString("00:01:02:03:04:00").toByteArray())); 134 collector.checkThat("entry 0: rtt type", rttConfig.type, equalTo(RttType.TWO_SIDED)); 135 collector.checkThat("entry 0: peer type", rttConfig.peer, equalTo(RttPeerType.AP)); 136 collector.checkThat("entry 0: lci", rttConfig.mustRequestLci, equalTo(true)); 137 collector.checkThat("entry 0: lcr", rttConfig.mustRequestLcr, equalTo(true)); 138 139 rttConfig = halRequest.get(1); 140 collector.checkThat("entry 1: MAC", rttConfig.addr, 141 equalTo(MacAddress.fromString("0A:0B:0C:0D:0E:00").toByteArray())); 142 collector.checkThat("entry 1: rtt type", rttConfig.type, equalTo(RttType.ONE_SIDED)); 143 collector.checkThat("entry 1: peer type", rttConfig.peer, equalTo(RttPeerType.AP)); 144 collector.checkThat("entry 1: lci", rttConfig.mustRequestLci, equalTo(true)); 145 collector.checkThat("entry 1: lcr", rttConfig.mustRequestLcr, equalTo(true)); 146 147 rttConfig = halRequest.get(2); 148 collector.checkThat("entry 2: MAC", rttConfig.addr, 149 equalTo(MacAddress.fromString("08:09:08:07:06:05").toByteArray())); 150 collector.checkThat("entry 2: rtt type", rttConfig.type, equalTo(RttType.TWO_SIDED)); 151 collector.checkThat("entry 2: peer type", rttConfig.peer, equalTo(RttPeerType.NAN)); 152 collector.checkThat("entry 2: lci", rttConfig.mustRequestLci, equalTo(false)); 153 collector.checkThat("entry 2: lcr", rttConfig.mustRequestLcr, equalTo(false)); 154 155 verifyNoMoreInteractions(mockRttController, mockRttServiceImpl); 156 } 157 158 /** 159 * Validate ranging request with a mix of Repsonders with and without IEEE 802.11mc support, 160 * from a non- privileged context. 161 */ 162 @Test 163 public void testRangeRequestNotPrivilegedNo80211mcSupportMixed() throws Exception { 164 int cmdId = 66; 165 166 // the request has 3 responders: first AP support 802.11mc, second AP does not, third is 167 // Aware (which supports 802.11mc by default) 168 RangingRequest request = RttTestUtils.getDummyRangingRequest((byte) 0); 169 170 // (1) issue range request 171 mDut.rangeRequest(cmdId, request, false); 172 173 // (2) verify HAL call and parameters 174 verify(mockRttController).rangeRequest(eq(cmdId), mRttConfigCaptor.capture()); 175 176 // verify contents of HAL request (hard codes knowledge from getDummyRangingRequest()). 177 ArrayList<RttConfig> halRequest = mRttConfigCaptor.getValue(); 178 179 collector.checkThat("number of entries", halRequest.size(), equalTo(2)); 180 181 RttConfig rttConfig = halRequest.get(0); 182 collector.checkThat("entry 0: MAC", rttConfig.addr, 183 equalTo(MacAddress.fromString("00:01:02:03:04:00").toByteArray())); 184 collector.checkThat("entry 0: rtt type", rttConfig.type, equalTo(RttType.TWO_SIDED)); 185 collector.checkThat("entry 0: peer type", rttConfig.peer, equalTo(RttPeerType.AP)); 186 collector.checkThat("entry 0: lci", rttConfig.mustRequestLci, equalTo(false)); 187 collector.checkThat("entry 0: lcr", rttConfig.mustRequestLcr, equalTo(false)); 188 189 rttConfig = halRequest.get(1); 190 collector.checkThat("entry 1: MAC", rttConfig.addr, 191 equalTo(MacAddress.fromString("08:09:08:07:06:05").toByteArray())); 192 collector.checkThat("entry 1: rtt type", rttConfig.type, equalTo(RttType.TWO_SIDED)); 193 collector.checkThat("entry 1: peer type", rttConfig.peer, equalTo(RttPeerType.NAN)); 194 collector.checkThat("entry 1: lci", rttConfig.mustRequestLci, equalTo(false)); 195 collector.checkThat("entry 1: lcr", rttConfig.mustRequestLcr, equalTo(false)); 196 197 verifyNoMoreInteractions(mockRttController, mockRttServiceImpl); 198 } 199 200 /** 201 * Validate successful ranging flow - with privileges access but with limited capabilities: 202 * - No single-sided RTT 203 * - No LCI/LCR 204 * - Limited BW 205 * - Limited Preamble 206 */ 207 @Test 208 public void testRangeRequestWithLimitedCapabilities() throws Exception { 209 int cmdId = 55; 210 RangingRequest request = RttTestUtils.getDummyRangingRequest((byte) 0); 211 212 // update capabilities to a limited set 213 RttCapabilities cap = getFullRttCapabilities(); 214 cap.rttOneSidedSupported = false; 215 cap.lciSupported = false; 216 cap.lcrSupported = false; 217 cap.bwSupport = RttBw.BW_10MHZ | RttBw.BW_160MHZ; 218 cap.preambleSupport = RttPreamble.LEGACY; 219 mGetCapCbCatpr.getValue().onValues(mStatusSuccess, cap); 220 221 // Note: request 1: BW = 40MHz --> 10MHz, Preamble = HT (since 40MHz) -> Legacy 222 223 // (1) issue range request 224 mDut.rangeRequest(cmdId, request, true); 225 226 // (2) verify HAL call and parameters 227 verify(mockRttController).rangeRequest(eq(cmdId), mRttConfigCaptor.capture()); 228 229 // verify contents of HAL request (hard codes knowledge from getDummyRangingRequest()). 230 ArrayList<RttConfig> halRequest = mRttConfigCaptor.getValue(); 231 232 collector.checkThat("number of entries", halRequest.size(), equalTo(2)); 233 234 RttConfig rttConfig = halRequest.get(0); 235 collector.checkThat("entry 0: MAC", rttConfig.addr, 236 equalTo(MacAddress.fromString("00:01:02:03:04:00").toByteArray())); 237 collector.checkThat("entry 0: rtt type", rttConfig.type, equalTo(RttType.TWO_SIDED)); 238 collector.checkThat("entry 0: peer type", rttConfig.peer, equalTo(RttPeerType.AP)); 239 collector.checkThat("entry 0: lci", rttConfig.mustRequestLci, equalTo(false)); 240 collector.checkThat("entry 0: lcr", rttConfig.mustRequestLcr, equalTo(false)); 241 collector.checkThat("entry 0: channel.width", rttConfig.channel.width, equalTo( 242 WifiChannelWidthInMhz.WIDTH_40)); 243 collector.checkThat("entry 0: bw", rttConfig.bw, equalTo(RttBw.BW_10MHZ)); 244 collector.checkThat("entry 0: preamble", rttConfig.preamble, equalTo(RttPreamble.LEGACY)); 245 246 rttConfig = halRequest.get(1); 247 collector.checkThat("entry 1: MAC", rttConfig.addr, 248 equalTo(MacAddress.fromString("08:09:08:07:06:05").toByteArray())); 249 collector.checkThat("entry 1: rtt type", rttConfig.type, equalTo(RttType.TWO_SIDED)); 250 collector.checkThat("entry 1: peer type", rttConfig.peer, equalTo(RttPeerType.NAN)); 251 collector.checkThat("entry 1: lci", rttConfig.mustRequestLci, equalTo(false)); 252 collector.checkThat("entry 1: lcr", rttConfig.mustRequestLcr, equalTo(false)); 253 254 verifyNoMoreInteractions(mockRttController, mockRttServiceImpl); 255 } 256 257 /** 258 * Validate successful ranging flow - with privileges access but with limited capabilities: 259 * - Very limited BW 260 * - Very limited Preamble 261 */ 262 @Test 263 public void testRangeRequestWithLimitedCapabilitiesNoOverlap() throws Exception { 264 int cmdId = 55; 265 RangingRequest request = RttTestUtils.getDummyRangingRequest((byte) 0); 266 267 // update capabilities to a limited set 268 RttCapabilities cap = getFullRttCapabilities(); 269 cap.bwSupport = RttBw.BW_80MHZ; 270 cap.preambleSupport = RttPreamble.VHT; 271 mGetCapCbCatpr.getValue().onValues(mStatusSuccess, cap); 272 273 // Note: request 1: BW = 40MHz --> no overlap -> dropped 274 // Note: request 2: BW = 160MHz --> 160MHz, preamble = VHT (since 160MHz) -> no overlap, 275 // dropped 276 277 // (1) issue range request 278 mDut.rangeRequest(cmdId, request, true); 279 280 // (2) verify HAL call and parameters 281 verify(mockRttController).rangeRequest(eq(cmdId), mRttConfigCaptor.capture()); 282 283 // verify contents of HAL request (hard codes knowledge from getDummyRangingRequest()). 284 ArrayList<RttConfig> halRequest = mRttConfigCaptor.getValue(); 285 286 collector.checkThat("number of entries", halRequest.size(), equalTo(1)); 287 288 RttConfig rttConfig = halRequest.get(0); 289 collector.checkThat("entry 0: MAC", rttConfig.addr, 290 equalTo(MacAddress.fromString("08:09:08:07:06:05").toByteArray())); 291 collector.checkThat("entry 0: rtt type", rttConfig.type, equalTo(RttType.TWO_SIDED)); 292 collector.checkThat("entry 0: peer type", rttConfig.peer, equalTo(RttPeerType.NAN)); 293 collector.checkThat("entry 0: lci", rttConfig.mustRequestLci, equalTo(false)); 294 collector.checkThat("entry 0: lcr", rttConfig.mustRequestLcr, equalTo(false)); 295 296 verifyNoMoreInteractions(mockRttController, mockRttServiceImpl); 297 } 298 299 /** 300 * Validate ranging request with all Repsonders without IEEE 802.11mc support, from a non- 301 * privileged context. 302 */ 303 @Test 304 public void testRangeRequestNotPrivilegedNo80211mcSupportForAny() throws Exception { 305 int cmdId = 77; 306 RangingRequest request = RttTestUtils.getDummyRangingRequestNo80211mcSupport((byte) 0); 307 308 // (1) issue range request 309 mDut.rangeRequest(cmdId, request, false); 310 311 // (2) verify immediate result callback (empty result set) 312 verify(mockRttServiceImpl).onRangingResults(eq(cmdId), mRttResultCaptor.capture()); 313 314 collector.checkThat("Result set", mRttResultCaptor.getValue().size(), equalTo(0)); 315 316 verifyNoMoreInteractions(mockRttController, mockRttServiceImpl); 317 } 318 319 /** 320 * Validate no range request when Wi-Fi is down 321 */ 322 @Test 323 public void testWifiDown() throws Exception { 324 int cmdId = 55; 325 RangingRequest request = RttTestUtils.getDummyRangingRequest((byte) 0); 326 327 // (1) configure Wi-Fi down and send a status change indication 328 when(mockHalDeviceManager.isStarted()).thenReturn(false); 329 mHdmStatusListener.getValue().onStatusChanged(); 330 verify(mockRttServiceImpl).disable(); 331 assertFalse(mDut.isReady()); 332 333 // (2) issue range request 334 mDut.rangeRequest(cmdId, request, true); 335 336 verifyNoMoreInteractions(mockRttServiceImpl, mockRttController); 337 } 338 339 /** 340 * Validate ranging cancel flow. 341 */ 342 @Test 343 public void testRangeCancel() throws Exception { 344 int cmdId = 66; 345 ArrayList<byte[]> macAddresses = new ArrayList<>(); 346 byte[] mac1 = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05}; 347 byte[] mac2 = {0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}; 348 macAddresses.add(mac1); 349 macAddresses.add(mac2); 350 351 // (1) issue cancel request 352 mDut.rangeCancel(cmdId, macAddresses); 353 354 // (2) verify HAL call and parameters 355 verify(mockRttController).rangeCancel(cmdId, macAddresses); 356 357 verifyNoMoreInteractions(mockRttController); 358 } 359 360 /** 361 * Validate correct result conversion from HAL to framework. 362 */ 363 @Test 364 public void testRangeResults() throws Exception { 365 int cmdId = 55; 366 ArrayList<RttResult> results = new ArrayList<>(); 367 RttResult res = new RttResult(); 368 res.addr[0] = 5; 369 res.addr[1] = 6; 370 res.addr[2] = 7; 371 res.addr[3] = 8; 372 res.addr[4] = 9; 373 res.addr[5] = 10; 374 res.status = RttStatus.SUCCESS; 375 res.distanceInMm = 1500; 376 res.timeStampInUs = 666; 377 results.add(res); 378 379 // (1) have HAL call native with results 380 mDut.onResults(cmdId, results); 381 382 // (2) verify call to framework 383 verify(mockRttServiceImpl).onRangingResults(eq(cmdId), mRttResultCaptor.capture()); 384 385 // verify contents of the framework results 386 List<RttResult> rttR = mRttResultCaptor.getValue(); 387 388 collector.checkThat("number of entries", rttR.size(), equalTo(1)); 389 390 RttResult rttResult = rttR.get(0); 391 collector.checkThat("status", rttResult.status, 392 equalTo(RttStatus.SUCCESS)); 393 collector.checkThat("mac", rttResult.addr, 394 equalTo(MacAddress.fromString("05:06:07:08:09:0A").toByteArray())); 395 collector.checkThat("distanceCm", rttResult.distanceInMm, equalTo(1500)); 396 collector.checkThat("timestamp", rttResult.timeStampInUs, equalTo(666L)); 397 398 verifyNoMoreInteractions(mockRttController, mockRttServiceImpl); 399 } 400 401 /** 402 * Validate correct cleanup when a null array of results is provided by HAL. 403 */ 404 @Test 405 public void testRangeResultsNullArray() { 406 int cmdId = 66; 407 408 mDut.onResults(cmdId, null); 409 verify(mockRttServiceImpl).onRangingResults(eq(cmdId), mRttResultCaptor.capture()); 410 411 collector.checkThat("number of entries", mRttResultCaptor.getValue().size(), equalTo(0)); 412 } 413 414 /** 415 * Validate correct cleanup when an array of results containing null entries is provided by HAL. 416 */ 417 @Test 418 public void testRangeResultsSomeNulls() { 419 int cmdId = 77; 420 421 ArrayList<RttResult> results = new ArrayList<>(); 422 results.add(null); 423 results.add(new RttResult()); 424 results.add(null); 425 results.add(null); 426 results.add(new RttResult()); 427 results.add(null); 428 429 mDut.onResults(cmdId, results); 430 verify(mockRttServiceImpl).onRangingResults(eq(cmdId), mRttResultCaptor.capture()); 431 432 List<RttResult> rttR = mRttResultCaptor.getValue(); 433 collector.checkThat("number of entries", rttR.size(), equalTo(2)); 434 for (int i = 0; i < rttR.size(); ++i) { 435 collector.checkThat("entry", rttR.get(i), IsNull.notNullValue()); 436 } 437 } 438 439 // Utilities 440 441 /** 442 * Return an RttCapabilities structure with all features enabled and support for all 443 * preambles and bandwidths. The purpose is to enable any request. The returned structure can 444 * then be modified to disable specific features. 445 */ 446 RttCapabilities getFullRttCapabilities() { 447 RttCapabilities cap = new RttCapabilities(); 448 449 cap.rttOneSidedSupported = true; 450 cap.rttFtmSupported = true; 451 cap.lciSupported = true; 452 cap.lcrSupported = true; 453 cap.responderSupported = true; // unused 454 cap.preambleSupport = RttPreamble.LEGACY | RttPreamble.HT | RttPreamble.VHT; 455 cap.bwSupport = 456 RttBw.BW_5MHZ | RttBw.BW_10MHZ | RttBw.BW_20MHZ | RttBw.BW_40MHZ | RttBw.BW_80MHZ 457 | RttBw.BW_160MHZ; 458 cap.mcVersion = 1; // unused 459 460 return cap; 461 } 462 } 463