WifiAwareServiceImplTest.java revision c29acea6ceda3aa4ee537c05ce7d05dac2655cf9
1/* 2 * Copyright (C) 2016 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 17package com.android.server.wifi.aware; 18 19import static org.junit.Assert.assertArrayEquals; 20import static org.junit.Assert.assertEquals; 21import static org.junit.Assert.assertTrue; 22import static org.mockito.Matchers.any; 23import static org.mockito.Matchers.anyInt; 24import static org.mockito.Matchers.anyString; 25import static org.mockito.Matchers.eq; 26import static org.mockito.Mockito.inOrder; 27import static org.mockito.Mockito.mock; 28import static org.mockito.Mockito.verify; 29import static org.mockito.Mockito.when; 30 31import android.content.Context; 32import android.content.pm.PackageManager; 33import android.net.wifi.RttManager; 34import android.net.wifi.aware.ConfigRequest; 35import android.net.wifi.aware.IWifiAwareDiscoverySessionCallback; 36import android.net.wifi.aware.IWifiAwareEventCallback; 37import android.net.wifi.aware.PublishConfig; 38import android.net.wifi.aware.SubscribeConfig; 39import android.net.wifi.aware.WifiAwareCharacteristics; 40import android.os.IBinder; 41import android.os.Looper; 42import android.test.suitebuilder.annotation.SmallTest; 43import android.util.SparseArray; 44import android.util.SparseIntArray; 45 46import org.junit.Before; 47import org.junit.Test; 48import org.mockito.ArgumentCaptor; 49import org.mockito.InOrder; 50import org.mockito.Mock; 51import org.mockito.MockitoAnnotations; 52 53import java.lang.reflect.Field; 54 55/** 56 * Unit test harness for WifiAwareStateManager. 57 */ 58@SmallTest 59public class WifiAwareServiceImplTest { 60 private static final int MAX_LENGTH = 255; 61 62 private WifiAwareServiceImplSpy mDut; 63 private int mDefaultUid = 1500; 64 65 @Mock 66 private Context mContextMock; 67 @Mock 68 private PackageManager mPackageManagerMock; 69 @Mock 70 private WifiAwareStateManager mAwareStateManagerMock; 71 @Mock 72 private IBinder mBinderMock; 73 @Mock 74 private IWifiAwareEventCallback mCallbackMock; 75 @Mock 76 private IWifiAwareDiscoverySessionCallback mSessionCallbackMock; 77 78 /** 79 * Using instead of spy to avoid native crash failures - possibly due to 80 * spy's copying of state. 81 */ 82 private class WifiAwareServiceImplSpy extends WifiAwareServiceImpl { 83 public int fakeUid; 84 85 WifiAwareServiceImplSpy(Context context) { 86 super(context); 87 } 88 89 /** 90 * Return the fake UID instead of the real one: pseudo-spy 91 * implementation. 92 */ 93 @Override 94 public int getMockableCallingUid() { 95 return fakeUid; 96 } 97 } 98 99 /** 100 * Initializes mocks. 101 */ 102 @Before 103 public void setup() throws Exception { 104 MockitoAnnotations.initMocks(this); 105 106 when(mContextMock.getApplicationContext()).thenReturn(mContextMock); 107 when(mContextMock.getPackageManager()).thenReturn(mPackageManagerMock); 108 when(mPackageManagerMock.hasSystemFeature(PackageManager.FEATURE_WIFI_AWARE)) 109 .thenReturn(true); 110 when(mAwareStateManagerMock.getCharacteristics()).thenReturn(getCharacteristics()); 111 112 installMockAwareStateManager(); 113 114 mDut = new WifiAwareServiceImplSpy(mContextMock); 115 mDut.fakeUid = mDefaultUid; 116 } 117 118 /** 119 * Validate start() function: passes a valid looper. 120 */ 121 @Test 122 public void testStart() { 123 mDut.start(); 124 125 verify(mAwareStateManagerMock).start(eq(mContextMock), any(Looper.class)); 126 } 127 128 /** 129 * Validate enableUsage() function 130 */ 131 @Test 132 public void testEnableUsage() { 133 mDut.enableUsage(); 134 135 verify(mAwareStateManagerMock).enableUsage(); 136 } 137 138 /** 139 * Validate disableUsage() function 140 */ 141 @Test 142 public void testDisableUsage() throws Exception { 143 mDut.enableUsage(); 144 doConnect(); 145 mDut.disableUsage(); 146 147 verify(mAwareStateManagerMock).disableUsage(); 148 } 149 150 /** 151 * Validate isUsageEnabled() function 152 */ 153 @Test 154 public void testIsUsageEnabled() { 155 mDut.isUsageEnabled(); 156 157 verify(mAwareStateManagerMock).isUsageEnabled(); 158 } 159 160 161 /** 162 * Validate connect() - returns and uses a client ID. 163 */ 164 @Test 165 public void testConnect() { 166 doConnect(); 167 } 168 169 /** 170 * Validate connect() when a non-null config is passed. 171 */ 172 @Test 173 public void testConnectWithConfig() { 174 ConfigRequest configRequest = new ConfigRequest.Builder().setMasterPreference(55).build(); 175 String callingPackage = "com.google.somePackage"; 176 177 mDut.connect(mBinderMock, callingPackage, mCallbackMock, 178 configRequest, false); 179 180 verify(mAwareStateManagerMock).connect(anyInt(), anyInt(), anyInt(), 181 eq(callingPackage), eq(mCallbackMock), eq(configRequest), eq(false)); 182 } 183 184 /** 185 * Validate disconnect() - correct pass-through args. 186 * 187 * @throws Exception 188 */ 189 @Test 190 public void testDisconnect() throws Exception { 191 int clientId = doConnect(); 192 193 mDut.disconnect(clientId, mBinderMock); 194 195 verify(mAwareStateManagerMock).disconnect(clientId); 196 validateInternalStateCleanedUp(clientId); 197 } 198 199 /** 200 * Validate that security exception thrown when attempting operation using 201 * an invalid client ID. 202 */ 203 @Test(expected = SecurityException.class) 204 public void testFailOnInvalidClientId() { 205 mDut.disconnect(-1, mBinderMock); 206 } 207 208 /** 209 * Validate that security exception thrown when attempting operation using a 210 * client ID which was already cleared-up. 211 */ 212 @Test(expected = SecurityException.class) 213 public void testFailOnClearedUpClientId() throws Exception { 214 int clientId = doConnect(); 215 216 mDut.disconnect(clientId, mBinderMock); 217 218 verify(mAwareStateManagerMock).disconnect(clientId); 219 validateInternalStateCleanedUp(clientId); 220 221 mDut.disconnect(clientId, mBinderMock); 222 } 223 224 /** 225 * Validate that trying to use a client ID from a UID which is different 226 * from the one that created it fails - and that the internal state is not 227 * modified so that a valid call (from the correct UID) will subsequently 228 * succeed. 229 */ 230 @Test 231 public void testFailOnAccessClientIdFromWrongUid() throws Exception { 232 int clientId = doConnect(); 233 234 mDut.fakeUid = mDefaultUid + 1; 235 236 /* 237 * Not using thrown.expect(...) since want to test that subsequent 238 * access works. 239 */ 240 boolean failsAsExpected = false; 241 try { 242 mDut.disconnect(clientId, mBinderMock); 243 } catch (SecurityException e) { 244 failsAsExpected = true; 245 } 246 247 mDut.fakeUid = mDefaultUid; 248 249 PublishConfig publishConfig = new PublishConfig.Builder().setServiceName("valid.value") 250 .build(); 251 mDut.publish(clientId, publishConfig, mSessionCallbackMock); 252 253 verify(mAwareStateManagerMock).publish(clientId, publishConfig, mSessionCallbackMock); 254 assertTrue("SecurityException for invalid access from wrong UID thrown", failsAsExpected); 255 } 256 257 /** 258 * Validates that on binder death we get a disconnect(). 259 */ 260 @Test 261 public void testBinderDeath() throws Exception { 262 ArgumentCaptor<IBinder.DeathRecipient> deathRecipient = ArgumentCaptor 263 .forClass(IBinder.DeathRecipient.class); 264 265 int clientId = doConnect(); 266 267 verify(mBinderMock).linkToDeath(deathRecipient.capture(), eq(0)); 268 deathRecipient.getValue().binderDied(); 269 verify(mAwareStateManagerMock).disconnect(clientId); 270 validateInternalStateCleanedUp(clientId); 271 } 272 273 /** 274 * Validates that sequential connect() calls return increasing client IDs. 275 */ 276 @Test 277 public void testClientIdIncrementing() { 278 int loopCount = 100; 279 280 InOrder inOrder = inOrder(mAwareStateManagerMock); 281 ArgumentCaptor<Integer> clientIdCaptor = ArgumentCaptor.forClass(Integer.class); 282 283 int prevId = 0; 284 for (int i = 0; i < loopCount; ++i) { 285 mDut.connect(mBinderMock, "", mCallbackMock, null, false); 286 inOrder.verify(mAwareStateManagerMock).connect(clientIdCaptor.capture(), anyInt(), 287 anyInt(), anyString(), eq(mCallbackMock), any(ConfigRequest.class), eq(false)); 288 int id = clientIdCaptor.getValue(); 289 if (i != 0) { 290 assertTrue("Client ID incrementing", id > prevId); 291 } 292 prevId = id; 293 } 294 } 295 296 /** 297 * Validate terminateSession() - correct pass-through args. 298 */ 299 @Test 300 public void testTerminateSession() { 301 int sessionId = 1024; 302 int clientId = doConnect(); 303 304 mDut.terminateSession(clientId, sessionId); 305 306 verify(mAwareStateManagerMock).terminateSession(clientId, sessionId); 307 } 308 309 /** 310 * Validate publish() - correct pass-through args. 311 */ 312 @Test 313 public void testPublish() { 314 PublishConfig publishConfig = new PublishConfig.Builder().setServiceName("something.valid") 315 .build(); 316 int clientId = doConnect(); 317 IWifiAwareDiscoverySessionCallback mockCallback = mock( 318 IWifiAwareDiscoverySessionCallback.class); 319 320 mDut.publish(clientId, publishConfig, mockCallback); 321 322 verify(mAwareStateManagerMock).publish(clientId, publishConfig, mockCallback); 323 } 324 325 /** 326 * Validate that publish() verifies the input PublishConfig and fails on an invalid service 327 * name. 328 */ 329 @Test(expected = IllegalArgumentException.class) 330 public void testPublishInvalidServiceName() { 331 doBadPublishConfiguration("Including invalid characters - spaces", null, null); 332 } 333 334 /** 335 * Validate that publish() verifies the input PublishConfig and fails on a "very long" 336 * service name. 337 */ 338 @Test(expected = IllegalArgumentException.class) 339 public void testPublishServiceNameTooLong() { 340 byte[] byteArray = new byte[MAX_LENGTH + 1]; 341 for (int i = 0; i < MAX_LENGTH + 1; ++i) { 342 byteArray[i] = 'a'; 343 } 344 doBadPublishConfiguration(new String(byteArray), null, null); 345 } 346 347 /** 348 * Validate that publish() verifies the input PublishConfig and fails on a "very long" ssi. 349 */ 350 @Test(expected = IllegalArgumentException.class) 351 public void testPublishSsiTooLong() { 352 doBadPublishConfiguration("correctservicename", new byte[MAX_LENGTH + 1], null); 353 } 354 355 /** 356 * Validate that publish() verifies the input PublishConfig and fails on a "very long" match 357 * filter. 358 */ 359 @Test(expected = IllegalArgumentException.class) 360 public void testPublishMatchFilterTooLong() { 361 doBadPublishConfiguration("correctservicename", null, new byte[MAX_LENGTH + 1]); 362 } 363 364 /** 365 * Validate updatePublish() - correct pass-through args. 366 */ 367 @Test 368 public void testUpdatePublish() { 369 int sessionId = 1232; 370 PublishConfig publishConfig = new PublishConfig.Builder().setServiceName("something.valid") 371 .build(); 372 int clientId = doConnect(); 373 374 mDut.updatePublish(clientId, sessionId, publishConfig); 375 376 verify(mAwareStateManagerMock).updatePublish(clientId, sessionId, publishConfig); 377 } 378 379 /** 380 * Validate updatePublish() error checking. 381 */ 382 @Test(expected = IllegalArgumentException.class) 383 public void testUpdatePublishInvalid() { 384 int sessionId = 1232; 385 PublishConfig publishConfig = new PublishConfig.Builder() 386 .setServiceName("something with spaces").build(); 387 int clientId = doConnect(); 388 389 mDut.updatePublish(clientId, sessionId, publishConfig); 390 391 verify(mAwareStateManagerMock).updatePublish(clientId, sessionId, publishConfig); 392 } 393 394 /** 395 * Validate subscribe() - correct pass-through args. 396 */ 397 @Test 398 public void testSubscribe() { 399 SubscribeConfig subscribeConfig = new SubscribeConfig.Builder() 400 .setServiceName("something.valid").build(); 401 int clientId = doConnect(); 402 IWifiAwareDiscoverySessionCallback mockCallback = mock( 403 IWifiAwareDiscoverySessionCallback.class); 404 405 mDut.subscribe(clientId, subscribeConfig, mockCallback); 406 407 verify(mAwareStateManagerMock).subscribe(clientId, subscribeConfig, mockCallback); 408 } 409 410 /** 411 * Validate that subscribe() verifies the input SubscribeConfig and fails on an invalid service 412 * name. 413 */ 414 @Test(expected = IllegalArgumentException.class) 415 public void testSubscribeInvalidServiceName() { 416 doBadSubscribeConfiguration("Including invalid characters - spaces", null, null); 417 } 418 419 /** 420 * Validate that subscribe() verifies the input SubscribeConfig and fails on a "very long" 421 * service name. 422 */ 423 @Test(expected = IllegalArgumentException.class) 424 public void testSubscribeServiceNameTooLong() { 425 byte[] byteArray = new byte[MAX_LENGTH + 1]; 426 for (int i = 0; i < MAX_LENGTH + 1; ++i) { 427 byteArray[i] = 'a'; 428 } 429 doBadSubscribeConfiguration(new String(byteArray), null, null); 430 } 431 432 /** 433 * Validate that subscribe() verifies the input SubscribeConfig and fails on a "very long" ssi. 434 */ 435 @Test(expected = IllegalArgumentException.class) 436 public void testSubscribeSsiTooLong() { 437 doBadSubscribeConfiguration("correctservicename", new byte[MAX_LENGTH + 1], null); 438 } 439 440 /** 441 * Validate that subscribe() verifies the input SubscribeConfig and fails on a "very long" match 442 * filter. 443 */ 444 @Test(expected = IllegalArgumentException.class) 445 public void testSubscribeMatchFilterTooLong() { 446 doBadSubscribeConfiguration("correctservicename", null, new byte[MAX_LENGTH + 1]); 447 } 448 449 /** 450 * Validate updateSubscribe() error checking. 451 */ 452 @Test(expected = IllegalArgumentException.class) 453 public void testUpdateSubscribeInvalid() { 454 int sessionId = 1232; 455 SubscribeConfig subscribeConfig = new SubscribeConfig.Builder() 456 .setServiceName("something.valid") 457 .setServiceSpecificInfo(new byte[MAX_LENGTH + 1]).build(); 458 int clientId = doConnect(); 459 460 mDut.updateSubscribe(clientId, sessionId, subscribeConfig); 461 462 verify(mAwareStateManagerMock).updateSubscribe(clientId, sessionId, subscribeConfig); 463 } 464 465 /** 466 * Validate updateSubscribe() validates configuration. 467 */ 468 @Test 469 public void testUpdateSubscribe() { 470 int sessionId = 1232; 471 SubscribeConfig subscribeConfig = new SubscribeConfig.Builder() 472 .setServiceName("something.valid").build(); 473 int clientId = doConnect(); 474 475 mDut.updateSubscribe(clientId, sessionId, subscribeConfig); 476 477 verify(mAwareStateManagerMock).updateSubscribe(clientId, sessionId, subscribeConfig); 478 } 479 480 /** 481 * Validate sendMessage() - correct pass-through args. 482 */ 483 @Test 484 public void testSendMessage() { 485 int sessionId = 2394; 486 int peerId = 2032; 487 byte[] message = new byte[MAX_LENGTH]; 488 int messageId = 2043; 489 int clientId = doConnect(); 490 491 mDut.sendMessage(clientId, sessionId, peerId, message, messageId, 0); 492 493 verify(mAwareStateManagerMock).sendMessage(clientId, sessionId, peerId, message, messageId, 494 0); 495 } 496 497 /** 498 * Validate sendMessage() validates that message length is correct. 499 */ 500 @Test(expected = IllegalArgumentException.class) 501 public void testSendMessageTooLong() { 502 int sessionId = 2394; 503 int peerId = 2032; 504 byte[] message = new byte[MAX_LENGTH + 1]; 505 int messageId = 2043; 506 int clientId = doConnect(); 507 508 mDut.sendMessage(clientId, sessionId, peerId, message, messageId, 0); 509 510 verify(mAwareStateManagerMock).sendMessage(clientId, sessionId, peerId, message, messageId, 511 0); 512 } 513 514 /** 515 * Validate startRanging() - correct pass-through args 516 */ 517 @Test 518 public void testStartRanging() { 519 int clientId = doConnect(); 520 int sessionId = 65345; 521 RttManager.ParcelableRttParams params = 522 new RttManager.ParcelableRttParams(new RttManager.RttParams[1]); 523 524 ArgumentCaptor<RttManager.RttParams[]> paramsCaptor = 525 ArgumentCaptor.forClass(RttManager.RttParams[].class); 526 527 int rangingId = mDut.startRanging(clientId, sessionId, params); 528 529 verify(mAwareStateManagerMock).startRanging(eq(clientId), eq(sessionId), 530 paramsCaptor.capture(), eq(rangingId)); 531 532 assertArrayEquals(paramsCaptor.getValue(), params.mParams); 533 } 534 535 /** 536 * Validates that sequential startRanging() calls return increasing ranging IDs. 537 */ 538 @Test 539 public void testRangingIdIncrementing() { 540 int loopCount = 100; 541 int clientId = doConnect(); 542 int sessionId = 65345; 543 RttManager.ParcelableRttParams params = 544 new RttManager.ParcelableRttParams(new RttManager.RttParams[1]); 545 546 int prevRangingId = 0; 547 for (int i = 0; i < loopCount; ++i) { 548 int rangingId = mDut.startRanging(clientId, sessionId, params); 549 if (i != 0) { 550 assertTrue("Client ID incrementing", rangingId > prevRangingId); 551 } 552 prevRangingId = rangingId; 553 } 554 } 555 556 /** 557 * Validates that startRanging() requires a non-empty list 558 */ 559 @Test(expected = IllegalArgumentException.class) 560 public void testStartRangingZeroArgs() { 561 int clientId = doConnect(); 562 int sessionId = 65345; 563 RttManager.ParcelableRttParams params = 564 new RttManager.ParcelableRttParams(new RttManager.RttParams[0]); 565 566 ArgumentCaptor<RttManager.RttParams[]> paramsCaptor = 567 ArgumentCaptor.forClass(RttManager.RttParams[].class); 568 569 int rangingId = mDut.startRanging(clientId, sessionId, params); 570 } 571 572 /* 573 * Tests of internal state of WifiAwareServiceImpl: very limited (not usually 574 * a good idea). However, these test that the internal state is cleaned-up 575 * appropriately. Alternatively would cause issues with memory leaks or 576 * information leak between sessions. 577 */ 578 579 private void validateInternalStateCleanedUp(int clientId) throws Exception { 580 int uidEntry = getInternalStateUid(clientId); 581 assertEquals(-1, uidEntry); 582 583 IBinder.DeathRecipient dr = getInternalStateDeathRecipient(clientId); 584 assertEquals(null, dr); 585 } 586 587 /* 588 * Utilities 589 */ 590 591 private void doBadPublishConfiguration(String serviceName, byte[] ssi, byte[] matchFilter) 592 throws IllegalArgumentException { 593 PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(serviceName) 594 .setServiceSpecificInfo(ssi).setMatchFilter(matchFilter).build(); 595 int clientId = doConnect(); 596 IWifiAwareDiscoverySessionCallback mockCallback = mock( 597 IWifiAwareDiscoverySessionCallback.class); 598 599 mDut.publish(clientId, publishConfig, mockCallback); 600 601 verify(mAwareStateManagerMock).publish(clientId, publishConfig, mockCallback); 602 } 603 604 private void doBadSubscribeConfiguration(String serviceName, byte[] ssi, byte[] matchFilter) 605 throws IllegalArgumentException { 606 SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().setServiceName(serviceName) 607 .setServiceSpecificInfo(ssi).setMatchFilter(matchFilter).build(); 608 int clientId = doConnect(); 609 IWifiAwareDiscoverySessionCallback mockCallback = mock( 610 IWifiAwareDiscoverySessionCallback.class); 611 612 mDut.subscribe(clientId, subscribeConfig, mockCallback); 613 614 verify(mAwareStateManagerMock).subscribe(clientId, subscribeConfig, mockCallback); 615 } 616 617 private int doConnect() { 618 String callingPackage = "com.google.somePackage"; 619 620 mDut.connect(mBinderMock, callingPackage, mCallbackMock, null, false); 621 622 ArgumentCaptor<Integer> clientId = ArgumentCaptor.forClass(Integer.class); 623 verify(mAwareStateManagerMock).connect(clientId.capture(), anyInt(), anyInt(), 624 eq(callingPackage), eq(mCallbackMock), eq(new ConfigRequest.Builder().build()), 625 eq(false)); 626 627 return clientId.getValue(); 628 } 629 630 private void installMockAwareStateManager() 631 throws Exception { 632 Field field = WifiAwareStateManager.class.getDeclaredField("sAwareStateManagerSingleton"); 633 field.setAccessible(true); 634 field.set(null, mAwareStateManagerMock); 635 } 636 637 private static WifiAwareCharacteristics getCharacteristics() { 638 WifiAwareNative.Capabilities cap = new WifiAwareNative.Capabilities(); 639 cap.maxConcurrentAwareClusters = 1; 640 cap.maxPublishes = 2; 641 cap.maxSubscribes = 2; 642 cap.maxServiceNameLen = MAX_LENGTH; 643 cap.maxMatchFilterLen = MAX_LENGTH; 644 cap.maxTotalMatchFilterLen = 255; 645 cap.maxServiceSpecificInfoLen = MAX_LENGTH; 646 cap.maxVsaDataLen = 255; 647 cap.maxMeshDataLen = 255; 648 cap.maxNdiInterfaces = 1; 649 cap.maxNdpSessions = 1; 650 cap.maxAppInfoLen = 255; 651 cap.maxQueuedTransmitMessages = 6; 652 return cap.toPublicCharacteristics(); 653 } 654 655 private int getInternalStateUid(int clientId) throws Exception { 656 Field field = WifiAwareServiceImpl.class.getDeclaredField("mUidByClientId"); 657 field.setAccessible(true); 658 @SuppressWarnings("unchecked") 659 SparseIntArray uidByClientId = (SparseIntArray) field.get(mDut); 660 661 return uidByClientId.get(clientId, -1); 662 } 663 664 private IBinder.DeathRecipient getInternalStateDeathRecipient(int clientId) throws Exception { 665 Field field = WifiAwareServiceImpl.class.getDeclaredField("mDeathRecipientsByClientId"); 666 field.setAccessible(true); 667 @SuppressWarnings("unchecked") 668 SparseArray<IBinder.DeathRecipient> deathRecipientsByClientId = 669 (SparseArray<IBinder.DeathRecipient>) field.get(mDut); 670 671 return deathRecipientsByClientId.get(clientId); 672 } 673} 674