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 package com.android.server.wifi.p2p; 17 18 import static org.junit.Assert.*; 19 import static org.mockito.Matchers.*; 20 import static org.mockito.Mockito.doAnswer; 21 import static org.mockito.Mockito.mock; 22 import static org.mockito.Mockito.never; 23 import static org.mockito.Mockito.times; 24 import static org.mockito.Mockito.verify; 25 26 import android.app.test.MockAnswerUtil.AnswerWithArguments; 27 import android.hardware.wifi.supplicant.V1_0.ISupplicantP2pIfaceCallback; 28 import android.hardware.wifi.supplicant.V1_0.WpsConfigMethods; 29 import android.net.wifi.WpsInfo; 30 import android.net.wifi.p2p.WifiP2pConfig; 31 import android.net.wifi.p2p.WifiP2pDevice; 32 import android.net.wifi.p2p.WifiP2pGroup; 33 import android.net.wifi.p2p.WifiP2pProvDiscEvent; 34 import android.net.wifi.p2p.nsd.WifiP2pServiceResponse; 35 36 import com.android.server.wifi.util.NativeUtil; 37 38 import org.junit.Assert.*; 39 import org.junit.Before; 40 import org.junit.Test; 41 import org.mockito.ArgumentCaptor; 42 import org.mockito.MockitoAnnotations; 43 44 import java.util.ArrayList; 45 import java.util.HashSet; 46 import java.util.List; 47 48 49 /** 50 * Unit tests for SupplicantP2pIfaceCallback 51 */ 52 public class SupplicantP2pIfaceCallbackTest { 53 private static final String TAG = "SupplicantP2pIfaceCallbackTest"; 54 55 private String mIface = "test_p2p0"; 56 private WifiP2pMonitor mMonitor; 57 private SupplicantP2pIfaceCallback mDut; 58 59 private byte[] mDeviceAddressInvalid1 = { 0x00 }; 60 private byte[] mDeviceAddressInvalid2 = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66 }; 61 private byte[] mDeviceAddress1Bytes = { 0x00, 0x11, 0x22, 0x33, 0x44, 0x55 }; 62 private String mDeviceAddress1String = "00:11:22:33:44:55"; 63 private byte[] mDeviceAddress2Bytes = { 0x01, 0x12, 0x23, 0x34, 0x45, 0x56 }; 64 private String mDeviceAddress2String = "01:12:23:34:45:56"; 65 private byte[] mDeviceInfoBytes = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 }; 66 private static final byte[] DEVICE_ADDRESS = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 }; 67 68 private class SupplicantP2pIfaceCallbackSpy extends SupplicantP2pIfaceCallback { 69 SupplicantP2pIfaceCallbackSpy(String iface, WifiP2pMonitor monitor) { 70 super(iface, monitor); 71 } 72 } 73 74 @Before 75 public void setUp() throws Exception { 76 MockitoAnnotations.initMocks(this); 77 mMonitor = mock(WifiP2pMonitor.class); 78 mDut = new SupplicantP2pIfaceCallbackSpy(mIface, mMonitor); 79 } 80 81 /** 82 * Sunny day scenario for onDeviceFound call. 83 */ 84 @Test 85 public void testOnDeviceFound_success() throws Exception { 86 byte[] fakePrimaryDeviceTypeBytes = { 0x01, 0x02, 0x03 }; 87 String fakePrimaryDeviceTypeString = "010203"; 88 String fakeDeviceName = "test device name"; 89 short fakeConfigMethods = 0x1234; 90 byte fakeCapabilities = 123; 91 int fakeGroupCapabilities = 456; 92 93 doAnswer(new AnswerWithArguments() { 94 public void answer(String iface, WifiP2pDevice device) { 95 // NOTE: mDeviceAddress1Bytes seems to be ignored by 96 // legacy implementation of WifiP2pDevice. 97 assertEquals(iface, mIface); 98 assertEquals(device.deviceName, fakeDeviceName); 99 assertEquals(device.primaryDeviceType, fakePrimaryDeviceTypeString); 100 assertEquals(device.deviceCapability, fakeCapabilities); 101 assertEquals(device.groupCapability, fakeGroupCapabilities); 102 assertEquals(device.wpsConfigMethodsSupported, fakeConfigMethods); 103 assertEquals(device.deviceAddress, mDeviceAddress2String); 104 assertEquals(device.status, WifiP2pDevice.AVAILABLE); 105 } 106 }) 107 .when(mMonitor).broadcastP2pDeviceFound( 108 anyString(), any(WifiP2pDevice.class)); 109 110 mDut.onDeviceFound( 111 mDeviceAddress1Bytes, mDeviceAddress2Bytes, 112 fakePrimaryDeviceTypeBytes, 113 fakeDeviceName, fakeConfigMethods, 114 fakeCapabilities, fakeGroupCapabilities, 115 mDeviceInfoBytes); 116 117 mDut.onDeviceFound( 118 mDeviceAddress1Bytes, mDeviceAddress2Bytes, 119 fakePrimaryDeviceTypeBytes, 120 fakeDeviceName, fakeConfigMethods, 121 fakeCapabilities, fakeGroupCapabilities, 122 null); 123 124 // Make sure we issued a broadcast each time. 125 verify(mMonitor, times(2)).broadcastP2pDeviceFound( 126 anyString(), any(WifiP2pDevice.class)); 127 } 128 129 /** 130 * Failing scenarios for onDeviceFound call. 131 */ 132 @Test 133 public void testOnDeviceFound_invalidArguments() throws Exception { 134 byte[] fakePrimaryDeviceTypeBytes = { 0x01, 0x02, 0x03 }; 135 String fakePrimaryDeviceTypeString = "010203"; 136 String fakeDeviceName = "test device name"; 137 short fakeConfigMethods = 0x1234; 138 byte fakeCapabilities = 123; 139 int fakeGroupCapabilities = 456; 140 141 mDut.onDeviceFound( 142 mDeviceAddress2Bytes, null, 143 fakePrimaryDeviceTypeBytes, 144 fakeDeviceName, fakeConfigMethods, 145 fakeCapabilities, fakeGroupCapabilities, 146 mDeviceInfoBytes); 147 verify(mMonitor, never()).broadcastP2pDeviceFound( 148 anyString(), any(WifiP2pDevice.class)); 149 150 151 mDut.onDeviceFound( 152 mDeviceAddress1Bytes, mDeviceAddress2Bytes, 153 null, 154 fakeDeviceName, fakeConfigMethods, 155 fakeCapabilities, fakeGroupCapabilities, 156 mDeviceInfoBytes); 157 verify(mMonitor, never()).broadcastP2pDeviceFound( 158 anyString(), any(WifiP2pDevice.class)); 159 160 161 mDut.onDeviceFound( 162 mDeviceAddress1Bytes, mDeviceAddress2Bytes, 163 fakePrimaryDeviceTypeBytes, 164 null, fakeConfigMethods, 165 fakeCapabilities, fakeGroupCapabilities, 166 mDeviceInfoBytes); 167 verify(mMonitor, never()).broadcastP2pDeviceFound( 168 anyString(), any(WifiP2pDevice.class)); 169 170 171 mDut.onDeviceFound( 172 mDeviceAddress1Bytes, mDeviceAddressInvalid1, 173 fakePrimaryDeviceTypeBytes, 174 null, fakeConfigMethods, 175 fakeCapabilities, fakeGroupCapabilities, 176 mDeviceInfoBytes); 177 verify(mMonitor, never()).broadcastP2pDeviceFound( 178 anyString(), any(WifiP2pDevice.class)); 179 180 181 mDut.onDeviceFound( 182 mDeviceAddress1Bytes, mDeviceAddressInvalid2, 183 fakePrimaryDeviceTypeBytes, 184 null, fakeConfigMethods, 185 fakeCapabilities, fakeGroupCapabilities, 186 mDeviceInfoBytes); 187 verify(mMonitor, never()).broadcastP2pDeviceFound( 188 anyString(), any(WifiP2pDevice.class)); 189 } 190 191 /** 192 * Sunny day scenario for onDeviceLost call. 193 */ 194 @Test 195 public void testOnDeviceLost_success() throws Exception { 196 doAnswer(new AnswerWithArguments() { 197 public void answer(String iface, WifiP2pDevice device) { 198 assertEquals(iface, mIface); 199 assertEquals(device.deviceAddress, mDeviceAddress1String); 200 assertEquals(device.status, WifiP2pDevice.UNAVAILABLE); 201 } 202 }) 203 .when(mMonitor).broadcastP2pDeviceLost( 204 anyString(), any(WifiP2pDevice.class)); 205 206 mDut.onDeviceLost(mDeviceAddress1Bytes); 207 208 // Make sure we issued a broadcast each time. 209 verify(mMonitor, times(1)).broadcastP2pDeviceLost( 210 anyString(), any(WifiP2pDevice.class)); 211 } 212 213 /** 214 * Failing scenarios for onDeviceLost call. 215 */ 216 @Test 217 public void testOnDeviceLost_invalidArguments() throws Exception { 218 mDut.onDeviceLost(null); 219 verify(mMonitor, never()).broadcastP2pDeviceLost( 220 anyString(), any(WifiP2pDevice.class)); 221 222 mDut.onDeviceLost(mDeviceAddressInvalid1); 223 verify(mMonitor, never()).broadcastP2pDeviceLost( 224 anyString(), any(WifiP2pDevice.class)); 225 226 mDut.onDeviceLost(mDeviceAddressInvalid2); 227 verify(mMonitor, never()).broadcastP2pDeviceLost( 228 anyString(), any(WifiP2pDevice.class)); 229 } 230 231 /** 232 * Sunny day scenario for onGoNegotiationRequest call. 233 */ 234 @Test 235 public void testOnGoNegotiationRequest_success() throws Exception { 236 HashSet<Integer> setups = new HashSet<Integer>(); 237 238 doAnswer(new AnswerWithArguments() { 239 public void answer(String iface, WifiP2pConfig config) { 240 assertEquals(iface, mIface); 241 assertNotNull(config.wps); 242 setups.add(config.wps.setup); 243 assertEquals(config.deviceAddress, mDeviceAddress1String); 244 } 245 }) 246 .when(mMonitor).broadcastP2pGoNegotiationRequest( 247 anyString(), any(WifiP2pConfig.class)); 248 249 mDut.onGoNegotiationRequest(mDeviceAddress1Bytes, 250 (short)ISupplicantP2pIfaceCallback.WpsDevPasswordId.USER_SPECIFIED); 251 assertTrue(setups.contains(WpsInfo.DISPLAY)); 252 253 mDut.onGoNegotiationRequest(mDeviceAddress1Bytes, 254 (short)ISupplicantP2pIfaceCallback.WpsDevPasswordId.PUSHBUTTON); 255 assertTrue(setups.contains(WpsInfo.PBC)); 256 257 mDut.onGoNegotiationRequest(mDeviceAddress1Bytes, 258 (short)ISupplicantP2pIfaceCallback.WpsDevPasswordId.REGISTRAR_SPECIFIED); 259 assertTrue(setups.contains(WpsInfo.KEYPAD)); 260 261 // Invalid should default to PBC 262 setups.clear(); 263 mDut.onGoNegotiationRequest(mDeviceAddress1Bytes, (short)0xffff); 264 assertTrue(setups.contains(WpsInfo.PBC)); 265 } 266 267 /** 268 * Failing scenarios for onGoNegotiationRequest call. 269 */ 270 @Test 271 public void testOnGoNegotiationRequest_invalidArguments() throws Exception { 272 mDut.onGoNegotiationRequest(null, (short)0); 273 verify(mMonitor, never()).broadcastP2pDeviceLost( 274 anyString(), any(WifiP2pDevice.class)); 275 276 mDut.onGoNegotiationRequest(mDeviceAddressInvalid1, (short)0); 277 verify(mMonitor, never()).broadcastP2pDeviceLost( 278 anyString(), any(WifiP2pDevice.class)); 279 280 mDut.onGoNegotiationRequest(mDeviceAddressInvalid2, (short)0); 281 verify(mMonitor, never()).broadcastP2pDeviceLost( 282 anyString(), any(WifiP2pDevice.class)); 283 } 284 285 /** 286 * Sunny day scenario for onGroupStarted call. 287 */ 288 @Test 289 public void testOnGroupStarted_success() throws Exception { 290 String fakeName = "group name"; 291 String fakePassphrase = "secret"; 292 ArrayList<Byte> fakeSsidBytesList = new ArrayList<Byte>() {{ 293 add((byte)0x30); 294 add((byte)0x31); 295 add((byte)0x32); 296 add((byte)0x33); 297 }}; 298 String fakeSsidString = "0123"; 299 HashSet<String> passwords = new HashSet<String>(); 300 301 doAnswer(new AnswerWithArguments() { 302 public void answer(String iface, WifiP2pGroup group) { 303 assertEquals(iface, mIface); 304 assertNotNull(group.getOwner()); 305 assertEquals(group.getOwner().deviceAddress, mDeviceAddress1String); 306 assertEquals(group.getNetworkId(), WifiP2pGroup.PERSISTENT_NET_ID); 307 passwords.add(group.getPassphrase()); 308 assertEquals(group.getInterface(), fakeName); 309 assertEquals(group.getNetworkName(), fakeSsidString); 310 } 311 }) 312 .when(mMonitor).broadcastP2pGroupStarted( 313 anyString(), any(WifiP2pGroup.class)); 314 315 mDut.onGroupStarted( 316 fakeName, true, fakeSsidBytesList, 1, null, fakePassphrase, 317 mDeviceAddress1Bytes, true); 318 assertTrue(passwords.contains(fakePassphrase)); 319 320 mDut.onGroupStarted( 321 fakeName, true, fakeSsidBytesList, 1, null, null, 322 mDeviceAddress1Bytes, true); 323 assertTrue(passwords.contains(null)); 324 325 verify(mMonitor, times(2)).broadcastP2pGroupStarted( 326 anyString(), any(WifiP2pGroup.class)); 327 } 328 329 /** 330 * Failing scenarios for onGroupStarted call. 331 */ 332 @Test 333 public void testOnGroupStarted_invalidArguments() throws Exception { 334 String fakeName = "group name"; 335 String fakePassphrase = "secret"; 336 ArrayList<Byte> fakeSsidBytesList = new ArrayList<Byte>() {{ 337 add((byte)0x30); 338 add((byte)0x31); 339 add((byte)0x32); 340 add((byte)0x33); 341 }}; 342 String fakeSsidString = "0123"; 343 344 mDut.onGroupStarted( 345 null, true, fakeSsidBytesList, 1, null, fakePassphrase, 346 mDeviceAddress1Bytes, true); 347 verify(mMonitor, never()).broadcastP2pGroupStarted( 348 anyString(), any(WifiP2pGroup.class)); 349 350 mDut.onGroupStarted( 351 fakeName, true, null, 1, null, fakePassphrase, 352 mDeviceAddress1Bytes, true); 353 verify(mMonitor, never()).broadcastP2pGroupStarted( 354 anyString(), any(WifiP2pGroup.class)); 355 356 mDut.onGroupStarted( 357 fakeName, true, fakeSsidBytesList, 1, null, fakePassphrase, 358 null, true); 359 verify(mMonitor, never()).broadcastP2pGroupStarted( 360 anyString(), any(WifiP2pGroup.class)); 361 } 362 363 /** 364 * Test provision discovery callback. 365 */ 366 @Test 367 public void testOnProvisionDiscoveryCompleted() throws Exception { 368 byte[] p2pDeviceAddr = DEVICE_ADDRESS; 369 boolean isRequest = false; 370 byte status = ISupplicantP2pIfaceCallback.P2pProvDiscStatusCode.SUCCESS; 371 short configMethods = WpsConfigMethods.DISPLAY; 372 String generatedPin = "12345678"; 373 374 ArgumentCaptor<WifiP2pProvDiscEvent> discEventCaptor = 375 ArgumentCaptor.forClass(WifiP2pProvDiscEvent.class); 376 mDut.onProvisionDiscoveryCompleted( 377 p2pDeviceAddr, isRequest, status, configMethods, generatedPin); 378 verify(mMonitor).broadcastP2pProvisionDiscoveryEnterPin( 379 anyString(), discEventCaptor.capture()); 380 assertEquals(WifiP2pProvDiscEvent.ENTER_PIN, discEventCaptor.getValue().event); 381 382 configMethods = WpsConfigMethods.KEYPAD; 383 mDut.onProvisionDiscoveryCompleted( 384 p2pDeviceAddr, isRequest, status, configMethods, generatedPin); 385 verify(mMonitor).broadcastP2pProvisionDiscoveryShowPin( 386 anyString(), discEventCaptor.capture()); 387 assertEquals(WifiP2pProvDiscEvent.SHOW_PIN, discEventCaptor.getValue().event); 388 assertEquals(generatedPin, discEventCaptor.getValue().pin); 389 390 isRequest = true; 391 configMethods = WpsConfigMethods.KEYPAD; 392 mDut.onProvisionDiscoveryCompleted( 393 p2pDeviceAddr, isRequest, status, configMethods, generatedPin); 394 verify(mMonitor, times(2)).broadcastP2pProvisionDiscoveryEnterPin( 395 anyString(), discEventCaptor.capture()); 396 assertEquals(WifiP2pProvDiscEvent.ENTER_PIN, discEventCaptor.getValue().event); 397 398 configMethods = WpsConfigMethods.DISPLAY; 399 mDut.onProvisionDiscoveryCompleted( 400 p2pDeviceAddr, isRequest, status, configMethods, generatedPin); 401 verify(mMonitor, times(2)).broadcastP2pProvisionDiscoveryShowPin( 402 anyString(), discEventCaptor.capture()); 403 assertEquals(WifiP2pProvDiscEvent.SHOW_PIN, discEventCaptor.getValue().event); 404 assertEquals(generatedPin, discEventCaptor.getValue().pin); 405 406 isRequest = false; 407 configMethods = WpsConfigMethods.PUSHBUTTON; 408 mDut.onProvisionDiscoveryCompleted( 409 p2pDeviceAddr, isRequest, status, configMethods, generatedPin); 410 verify(mMonitor).broadcastP2pProvisionDiscoveryPbcResponse( 411 anyString(), discEventCaptor.capture()); 412 assertEquals(WifiP2pProvDiscEvent.PBC_RSP, discEventCaptor.getValue().event); 413 414 isRequest = true; 415 mDut.onProvisionDiscoveryCompleted( 416 p2pDeviceAddr, isRequest, status, configMethods, generatedPin); 417 verify(mMonitor).broadcastP2pProvisionDiscoveryPbcRequest( 418 anyString(), discEventCaptor.capture()); 419 assertEquals(WifiP2pProvDiscEvent.PBC_REQ, discEventCaptor.getValue().event); 420 } 421 422 /** 423 * Test staAuth with device address, should trigger ApStaConnected broadcast 424 */ 425 @Test 426 public void testStaAuth_success() { 427 // Trigger onStaAuthorized callback, ensure wifimonitor broadcast is sent with WifiP2pDevice 428 // using the p2pDeviceAddress 429 ArgumentCaptor<WifiP2pDevice> p2pDeviceCaptor = 430 ArgumentCaptor.forClass(WifiP2pDevice.class); 431 mDut.onStaAuthorized(mDeviceAddress1Bytes, mDeviceAddress2Bytes); 432 verify(mMonitor).broadcastP2pApStaConnected(any(String.class), p2pDeviceCaptor.capture()); 433 assertEquals(mDeviceAddress2String, p2pDeviceCaptor.getValue().deviceAddress); 434 } 435 436 /** 437 * Test staAuth without device address, should trigger ApStaConnected broadcast using srcAddress 438 */ 439 @Test 440 public void testStaAuth_noDeviceAddress_success() { 441 // Trigger onStaAuthorized callback, using a zero'd p2pDeviceAddress, ensure wifimonitor 442 // broadcast is sent with WifiP2pDevice using the srcAddress 443 ArgumentCaptor<WifiP2pDevice> p2pDeviceCaptor = 444 ArgumentCaptor.forClass(WifiP2pDevice.class); 445 mDut.onStaAuthorized(mDeviceAddress1Bytes, NativeUtil.ANY_MAC_BYTES); 446 verify(mMonitor).broadcastP2pApStaConnected(any(String.class), p2pDeviceCaptor.capture()); 447 assertEquals(mDeviceAddress1String, p2pDeviceCaptor.getValue().deviceAddress); 448 } 449 450 // TLVS hex data encoded as a hex string. 451 // Taken directly from an observed supplicant service response event 452 private static final String SERV_DISC_RESP_TLVS = "1d00010100076578616d706c650b5f6166706f766572" 453 + "746370c00c001001001e000101000b5f6166706f766572746370c00c000c01074578616d706c65c0273c" 454 + "00010100096d797072696e746572045f697070c00c00100109747874766572733d311a70646c3d617070" 455 + "6c69636174696f6e2f706f73747363726970741900010100045f697070c00c000c01094d795072696e74" 456 + "6572c0275f000201000a757569643a36383539646564652d383537342d353961622d393333322d313233" 457 + "3435363738393031313a3a75726e3a736368656d61732d75706e702d6f72673a736572766963653a436f" 458 + "6e6e656374696f6e4d616e616765723a3159000201000a757569643a36383539646564652d383537342d" 459 + "353961622d393333322d3132333435363738393031313a3a75726e3a736368656d61732d75706e702d6f" 460 + "72673a736572766963653a41565472616e73706f72743a315a000201000a757569643a36383539646564" 461 + "652d383537342d353961622d393333322d3132333435363738393031313a3a75726e3a736368656d6173" 462 + "2d75706e702d6f72673a6465766963653a4d6564696152656e64657265723a313e000201000a75756964" 463 + "3a36383539646564652d383537342d353961622d393333322d3132333435363738393031313a3a75706e" 464 + "703a726f6f746465766963652d000201000a757569643a36383539646564652d383537342d353961622d" 465 + "393333322d313233343536373839303131"; 466 467 /** 468 * Pretty basic onServiceDiscoveryResponse callback test. 469 * Mocks the callback event, passing some observed real data to it, and ensures that it returns 470 * a non-null WifiP2pServiceResponse list. 471 */ 472 @Test 473 public void testOnServiceDiscoveryResponseCompleted_success() throws Exception { 474 ArrayList<Byte> tlvs = NativeUtil.byteArrayToArrayList(hexStr2Bin(SERV_DISC_RESP_TLVS)); 475 ArgumentCaptor<List<WifiP2pServiceResponse>> respListCaptor = 476 ArgumentCaptor.forClass(List.class); 477 mDut.onServiceDiscoveryResponse( 478 mDeviceAddress1Bytes, 479 (short) 10 /* unused updateIndicator value */, 480 tlvs); 481 verify(mMonitor).broadcastP2pServiceDiscoveryResponse(anyString(), 482 respListCaptor.capture()); 483 assertNotNull(respListCaptor.getValue()); 484 } 485 486 /** 487 * Converts hex string to byte array. 488 * 489 * @param hex hex string. if invalid, return null. 490 * @return binary data. 491 */ 492 private static byte[] hexStr2Bin(String hex) { 493 int sz = hex.length() / 2; 494 byte[] b = new byte[hex.length() / 2]; 495 for (int i = 0; i < sz; i++) { 496 try { 497 b[i] = (byte) Integer.parseInt(hex.substring(i * 2, i * 2 + 2), 16); 498 } catch (Exception e) { 499 return null; 500 } 501 } 502 return b; 503 } 504 } 505