OSDN Git Service

[AWARE] Move manager tests from service to manager directory
authorEtan Cohen <etancohen@google.com>
Thu, 17 Nov 2016 01:59:41 +0000 (17:59 -0800)
committerEtan Cohen <etancohen@google.com>
Wed, 23 Nov 2016 18:28:09 +0000 (10:28 -0800)
Move operation - no functional tests. Only editorial changes:
- Remove (no longer) necessary import statements
- Removed reference to a constant sitting on the service side.
  Replaced with locally defined constant.

(cherry pick of commit 7ba5ad3c53dac62e56cd12dbe2054ce8374b6b92)

Bug: 32954930
Test: unit-tests pass
Change-Id: Ibe463fa73c27fc67996460eb91ea0222437927a6

wifi/tests/src/android/net/wifi/aware/TlvBufferUtilsTest.java [new file with mode: 0644]
wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java [new file with mode: 0644]

diff --git a/wifi/tests/src/android/net/wifi/aware/TlvBufferUtilsTest.java b/wifi/tests/src/android/net/wifi/aware/TlvBufferUtilsTest.java
new file mode 100644 (file)
index 0000000..4b6957b
--- /dev/null
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.aware;
+
+import static org.hamcrest.core.IsEqual.equalTo;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ErrorCollector;
+
+/**
+ * Unit test harness for TlvBufferUtils class.
+ */
+@SmallTest
+public class TlvBufferUtilsTest {
+    @Rule
+    public ErrorCollector collector = new ErrorCollector();
+
+    /*
+     * TlvBufferUtils Tests
+     */
+
+    @Test
+    public void testTlvBuild() {
+        TlvBufferUtils.TlvConstructor tlv11 = new TlvBufferUtils.TlvConstructor(1, 1);
+        tlv11.allocate(15);
+        tlv11.putByte(0, (byte) 2);
+        tlv11.putByteArray(2, new byte[] {
+                0, 1, 2 });
+
+        collector.checkThat("tlv11-correct-construction",
+                tlv11.getArray(), equalTo(new byte[]{0, 1, 2, 2, 3, 0, 1, 2}));
+
+        LvBufferUtils.LvConstructor tlv01 = new LvBufferUtils.LvConstructor(1);
+        tlv01.allocate(15);
+        tlv01.putByte((byte) 2);
+        tlv01.putByteArray(2, new byte[] {
+                0, 1, 2 });
+
+        collector.checkThat("tlv01-correct-construction",
+                tlv01.getArray(), equalTo(new byte[] {1, 2, 3, 0, 1, 2 }));
+
+        collector.checkThat("tlv11-valid",
+                TlvBufferUtils.isValid(tlv11.getArray(), 1, 1),
+                equalTo(true));
+        collector.checkThat("tlv01-valid",
+                LvBufferUtils.isValid(tlv01.getArray(), 1),
+                equalTo(true));
+    }
+
+    @Test
+    public void testTlvIterate() {
+        final String ascii = "ABC";
+        final String nonAscii = "何かもっと複雑な";
+
+        TlvBufferUtils.TlvConstructor tlv22 = new TlvBufferUtils.TlvConstructor(2, 2);
+        tlv22.allocate(18);
+        tlv22.putInt(0, 2);
+        tlv22.putShort(2, (short) 3);
+        tlv22.putZeroLengthElement(55);
+
+        TlvBufferUtils.TlvIterable tlv22It = new TlvBufferUtils.TlvIterable(2, 2, tlv22.getArray());
+        int count = 0;
+        for (TlvBufferUtils.TlvElement tlv : tlv22It) {
+            if (count == 0) {
+                collector.checkThat("tlv22-correct-iteration-mType", tlv.type, equalTo(0));
+                collector.checkThat("tlv22-correct-iteration-mLength", tlv.length, equalTo(4));
+                collector.checkThat("tlv22-correct-iteration-DATA", tlv.getInt(), equalTo(2));
+            } else if (count == 1) {
+                collector.checkThat("tlv22-correct-iteration-mType", tlv.type, equalTo(2));
+                collector.checkThat("tlv22-correct-iteration-mLength", tlv.length, equalTo(2));
+                collector.checkThat("tlv22-correct-iteration-DATA", (int) tlv.getShort(),
+                        equalTo(3));
+            } else if (count == 2) {
+                collector.checkThat("tlv22-correct-iteration-mType", tlv.type, equalTo(55));
+                collector.checkThat("tlv22-correct-iteration-mLength", tlv.length, equalTo(0));
+            } else {
+                collector.checkThat("Invalid number of iterations in loop - tlv22", true,
+                        equalTo(false));
+            }
+            ++count;
+        }
+        if (count != 3) {
+            collector.checkThat("Invalid number of iterations outside loop - tlv22", true,
+                    equalTo(false));
+        }
+
+        TlvBufferUtils.TlvConstructor tlv02 = new TlvBufferUtils.TlvConstructor(0, 2);
+        tlv02.allocate(100);
+        tlv02.putByte(0, (byte) 2);
+        tlv02.putString(0, ascii);
+        tlv02.putString(0, nonAscii);
+
+        TlvBufferUtils.TlvIterable tlv02It = new TlvBufferUtils.TlvIterable(0, 2, tlv02.getArray());
+        count = 0;
+        for (TlvBufferUtils.TlvElement tlv : tlv02It) {
+            if (count == 0) {
+                collector.checkThat("tlv02-correct-iteration-mLength", tlv.length, equalTo(1));
+                collector.checkThat("tlv02-correct-iteration-DATA", (int) tlv.getByte(),
+                        equalTo(2));
+            } else if (count == 1) {
+                collector.checkThat("tlv02-correct-iteration-mLength", tlv.length,
+                        equalTo(ascii.length()));
+                collector.checkThat("tlv02-correct-iteration-DATA", tlv.getString().equals(ascii),
+                        equalTo(true));
+            } else if (count == 2) {
+                collector.checkThat("tlv02-correct-iteration-mLength", tlv.length,
+                        equalTo(nonAscii.getBytes().length));
+                collector.checkThat("tlv02-correct-iteration-DATA",
+                        tlv.getString().equals(nonAscii), equalTo(true));
+            } else {
+                collector.checkThat("Invalid number of iterations in loop - tlv02", true,
+                        equalTo(false));
+            }
+            ++count;
+        }
+        collector.checkThat("Invalid number of iterations outside loop - tlv02", count,
+                equalTo(3));
+
+        collector.checkThat("tlv22-valid",
+                TlvBufferUtils.isValid(tlv22.getArray(), 2, 2),
+                equalTo(true));
+        collector.checkThat("tlv02-valid",
+                LvBufferUtils.isValid(tlv02.getArray(), 2),
+                equalTo(true));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testTlvInvalidSizeT1L0() {
+        TlvBufferUtils.TlvConstructor tlv10 = new TlvBufferUtils.TlvConstructor(1, 0);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testTlvInvalidSizeTm3L2() {
+        TlvBufferUtils.TlvConstructor tlv10 = new TlvBufferUtils.TlvConstructor(-3, 2);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testTlvInvalidSizeT1Lm2() {
+        TlvBufferUtils.TlvConstructor tlv10 = new TlvBufferUtils.TlvConstructor(1, -2);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testTlvInvalidSizeT1L3() {
+        TlvBufferUtils.TlvConstructor tlv10 = new TlvBufferUtils.TlvConstructor(1, 3);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testTlvInvalidSizeT3L1() {
+        TlvBufferUtils.TlvConstructor tlv10 = new TlvBufferUtils.TlvConstructor(3, 1);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testTlvItInvalidSizeT1L0() {
+        final byte[] dummy = {
+                0, 1, 2 };
+        final int dummyLength = 3;
+        TlvBufferUtils.TlvIterable tlvIt10 = new TlvBufferUtils.TlvIterable(1, 0, dummy);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testTlvItInvalidSizeTm3L2() {
+        final byte[] dummy = {
+                0, 1, 2 };
+        final int dummyLength = 3;
+        TlvBufferUtils.TlvIterable tlvIt10 = new TlvBufferUtils.TlvIterable(-3, 2, dummy);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testTlvItInvalidSizeT1Lm2() {
+        final byte[] dummy = {
+                0, 1, 2 };
+        final int dummyLength = 3;
+        TlvBufferUtils.TlvIterable tlvIt10 = new TlvBufferUtils.TlvIterable(1, -2, dummy);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testTlvItInvalidSizeT1L3() {
+        final byte[] dummy = {
+                0, 1, 2 };
+        final int dummyLength = 3;
+        TlvBufferUtils.TlvIterable tlvIt10 = new TlvBufferUtils.TlvIterable(1, 3, dummy);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testTlvItInvalidSizeT3L1() {
+        final byte[] dummy = {
+                0, 1, 2 };
+        final int dummyLength = 3;
+        TlvBufferUtils.TlvIterable tlvIt10 = new TlvBufferUtils.TlvIterable(3, 1, dummy);
+    }
+
+    /**
+     * Validate that a malformed byte array fails the TLV validity test.
+     */
+    @Test
+    public void testTlvInvalidByteArray() {
+        LvBufferUtils.LvConstructor tlv01 = new LvBufferUtils.LvConstructor(1);
+        tlv01.allocate(15);
+        tlv01.putByte((byte) 2);
+        tlv01.putByteArray(2, new byte[]{0, 1, 2});
+
+        byte[] array = tlv01.getArray();
+        array[0] = 10;
+
+        collector.checkThat("tlv01-invalid",
+                LvBufferUtils.isValid(array, 1), equalTo(false));
+    }
+}
diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
new file mode 100644 (file)
index 0000000..b30ecf7
--- /dev/null
@@ -0,0 +1,1054 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.aware;
+
+import static org.hamcrest.core.IsEqual.equalTo;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.isNull;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.net.wifi.RttManager;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.test.TestLooper;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Base64;
+
+import libcore.util.HexEncoding;
+
+import org.json.JSONObject;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ErrorCollector;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Unit test harness for WifiAwareManager class.
+ */
+@SmallTest
+public class WifiAwareManagerTest {
+    private WifiAwareManager mDut;
+    private TestLooper mMockLooper;
+    private Handler mMockLooperHandler;
+
+    @Rule
+    public ErrorCollector collector = new ErrorCollector();
+
+    @Mock
+    public Context mockContext;
+
+    @Mock
+    public WifiAwareAttachCallback mockCallback;
+
+    @Mock
+    public WifiAwareDiscoverySessionCallback mockSessionCallback;
+
+    @Mock
+    public IWifiAwareManager mockAwareService;
+
+    @Mock
+    public WifiAwarePublishDiscoverySession mockPublishSession;
+
+    @Mock
+    public WifiAwareSubscribeDiscoverySession mockSubscribeSession;
+
+    @Mock
+    public RttManager.RttListener mockRttListener;
+
+    private static final int AWARE_STATUS_ERROR = -1;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+
+        mDut = new WifiAwareManager(mockContext, mockAwareService);
+        mMockLooper = new TestLooper();
+        mMockLooperHandler = new Handler(mMockLooper.getLooper());
+    }
+
+    /*
+     * Straight pass-through tests
+     */
+
+    /**
+     * Validate pass-through of enableUsage() API.
+     */
+    @Test
+    public void testEnableUsage() throws Exception {
+        mDut.enableUsage();
+
+        verify(mockAwareService).enableUsage();
+    }
+
+    /**
+     * Validate pass-through of disableUsage() API.
+     */
+    @Test
+    public void testDisableUsage() throws Exception {
+        mDut.disableUsage();
+
+        verify(mockAwareService).disableUsage();
+    }
+
+    /**
+     * Validate pass-through of isUsageEnabled() API.
+     */
+    @Test
+    public void testIsUsageEnable() throws Exception {
+        mDut.isAvailable();
+
+        verify(mockAwareService).isUsageEnabled();
+    }
+
+    /**
+     * Validate pass-through of getCharacteristics() API.
+     */
+    @Test
+    public void testGetCharacteristics() throws Exception {
+        mDut.getCharacteristics();
+
+        verify(mockAwareService).getCharacteristics();
+    }
+
+    /*
+     * WifiAwareEventCallbackProxy Tests
+     */
+
+    /**
+     * Validate the successful connect flow: (1) connect + success (2) publish, (3) disconnect
+     * (4) try publishing on old session (5) connect again
+     */
+    @Test
+    public void testConnectFlow() throws Exception {
+        final int clientId = 4565;
+
+        InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockAwareService);
+        ArgumentCaptor<IWifiAwareEventCallback> clientProxyCallback = ArgumentCaptor
+                .forClass(IWifiAwareEventCallback.class);
+        ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass(
+                WifiAwareSession.class);
+        ArgumentCaptor<IBinder> binder = ArgumentCaptor.forClass(IBinder.class);
+
+        // (1) connect + success
+        mDut.attach(mockCallback, mMockLooperHandler);
+        inOrder.verify(mockAwareService).connect(binder.capture(), anyString(),
+                clientProxyCallback.capture(), (ConfigRequest) isNull(), eq(false));
+        clientProxyCallback.getValue().onConnectSuccess(clientId);
+        mMockLooper.dispatchAll();
+        inOrder.verify(mockCallback).onAttached(sessionCaptor.capture());
+        WifiAwareSession session = sessionCaptor.getValue();
+
+        // (2) publish - should succeed
+        PublishConfig publishConfig = new PublishConfig.Builder().build();
+        session.publish(publishConfig, mockSessionCallback, mMockLooperHandler);
+        inOrder.verify(mockAwareService).publish(eq(clientId), eq(publishConfig),
+                any(IWifiAwareDiscoverySessionCallback.class));
+
+        // (3) disconnect
+        session.destroy();
+        inOrder.verify(mockAwareService).disconnect(eq(clientId), eq(binder.getValue()));
+
+        // (4) try publishing again - fails silently
+        session.publish(new PublishConfig.Builder().build(), mockSessionCallback,
+                mMockLooperHandler);
+
+        // (5) connect
+        mDut.attach(mockCallback, mMockLooperHandler);
+        inOrder.verify(mockAwareService).connect(binder.capture(), anyString(),
+                any(IWifiAwareEventCallback.class), (ConfigRequest) isNull(), eq(false));
+
+        verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService);
+    }
+
+    /**
+     * Validate the failed connect flow: (1) connect + failure, (2) connect + success (3) subscribe
+     */
+    @Test
+    public void testConnectFailure() throws Exception {
+        final int clientId = 4565;
+        final int reason = AWARE_STATUS_ERROR;
+
+        InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockAwareService);
+        ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass(
+                WifiAwareSession.class);
+        ArgumentCaptor<IWifiAwareEventCallback> clientProxyCallback = ArgumentCaptor
+                .forClass(IWifiAwareEventCallback.class);
+
+        // (1) connect + failure
+        mDut.attach(mockCallback, mMockLooperHandler);
+        inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(),
+                clientProxyCallback.capture(), (ConfigRequest) isNull(), eq(false));
+        clientProxyCallback.getValue().onConnectFail(reason);
+        mMockLooper.dispatchAll();
+        inOrder.verify(mockCallback).onAttachFailed();
+
+        // (2) connect + success
+        mDut.attach(mockCallback, mMockLooperHandler);
+        inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(),
+                clientProxyCallback.capture(), (ConfigRequest) isNull(), eq(false));
+        clientProxyCallback.getValue().onConnectSuccess(clientId);
+        mMockLooper.dispatchAll();
+        inOrder.verify(mockCallback).onAttached(sessionCaptor.capture());
+        WifiAwareSession session = sessionCaptor.getValue();
+
+        // (4) subscribe: should succeed
+        SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
+        session.subscribe(subscribeConfig, mockSessionCallback, mMockLooperHandler);
+        inOrder.verify(mockAwareService).subscribe(eq(clientId), eq(subscribeConfig),
+                any(IWifiAwareDiscoverySessionCallback.class));
+
+        verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService);
+    }
+
+    /**
+     * Validate that can call connect to create multiple sessions: (1) connect
+     * + success, (2) try connect again
+     */
+    @Test
+    public void testInvalidConnectSequence() throws Exception {
+        final int clientId = 4565;
+
+        InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockAwareService);
+        ArgumentCaptor<IWifiAwareEventCallback> clientProxyCallback = ArgumentCaptor
+                .forClass(IWifiAwareEventCallback.class);
+
+        // (1) connect + success
+        mDut.attach(mockCallback, mMockLooperHandler);
+        inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(),
+                clientProxyCallback.capture(), (ConfigRequest) isNull(), eq(false));
+        clientProxyCallback.getValue().onConnectSuccess(clientId);
+        mMockLooper.dispatchAll();
+        inOrder.verify(mockCallback).onAttached(any(WifiAwareSession.class));
+
+        // (2) connect + success
+        mDut.attach(mockCallback, mMockLooperHandler);
+        inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(),
+                clientProxyCallback.capture(), (ConfigRequest) isNull(), eq(false));
+        clientProxyCallback.getValue().onConnectSuccess(clientId + 1);
+        mMockLooper.dispatchAll();
+        inOrder.verify(mockCallback).onAttached(any(WifiAwareSession.class));
+
+        verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService);
+    }
+
+    /*
+     * WifiAwareDiscoverySessionCallbackProxy Tests
+     */
+
+    /**
+     * Validate the publish flow: (0) connect + success, (1) publish, (2)
+     * success creates session, (3) pass through everything, (4) update publish
+     * through session, (5) terminate locally, (6) try another command -
+     * ignored.
+     */
+    @Test
+    public void testPublishFlow() throws Exception {
+        final int clientId = 4565;
+        final int sessionId = 123;
+        final ConfigRequest configRequest = new ConfigRequest.Builder().build();
+        final PublishConfig publishConfig = new PublishConfig.Builder().build();
+        final WifiAwareManager.PeerHandle peerHandle = new WifiAwareManager.PeerHandle(873);
+        final String string1 = "hey from here...";
+        final String string2 = "some other arbitrary string...";
+        final int messageId = 2123;
+        final int reason = AWARE_STATUS_ERROR;
+
+        InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockAwareService,
+                mockPublishSession);
+        ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass(
+                WifiAwareSession.class);
+        ArgumentCaptor<IWifiAwareEventCallback> clientProxyCallback = ArgumentCaptor
+                .forClass(IWifiAwareEventCallback.class);
+        ArgumentCaptor<IWifiAwareDiscoverySessionCallback> sessionProxyCallback = ArgumentCaptor
+                .forClass(IWifiAwareDiscoverySessionCallback.class);
+        ArgumentCaptor<WifiAwarePublishDiscoverySession> publishSession = ArgumentCaptor
+                .forClass(WifiAwarePublishDiscoverySession.class);
+        ArgumentCaptor<WifiAwareManager.PeerHandle> peerIdCaptor = ArgumentCaptor.forClass(
+                WifiAwareManager.PeerHandle.class);
+
+        // (0) connect + success
+        mDut.attach(mMockLooperHandler, configRequest, mockCallback, null);
+        inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(),
+                clientProxyCallback.capture(), eq(configRequest), eq(false));
+        clientProxyCallback.getValue().onConnectSuccess(clientId);
+        mMockLooper.dispatchAll();
+        inOrder.verify(mockCallback).onAttached(sessionCaptor.capture());
+        WifiAwareSession session = sessionCaptor.getValue();
+
+        // (1) publish
+        session.publish(publishConfig, mockSessionCallback, mMockLooperHandler);
+        inOrder.verify(mockAwareService).publish(eq(clientId), eq(publishConfig),
+                sessionProxyCallback.capture());
+
+        // (2) publish session created
+        sessionProxyCallback.getValue().onSessionStarted(sessionId);
+        mMockLooper.dispatchAll();
+        inOrder.verify(mockSessionCallback).onPublishStarted(publishSession.capture());
+
+        // (3) ...
+        publishSession.getValue().sendMessage(peerHandle, messageId, string1.getBytes());
+        sessionProxyCallback.getValue().onMatch(peerHandle.peerId, string1.getBytes(),
+                string2.getBytes());
+        sessionProxyCallback.getValue().onMessageReceived(peerHandle.peerId, string1.getBytes());
+        sessionProxyCallback.getValue().onMessageSendFail(messageId, reason);
+        sessionProxyCallback.getValue().onMessageSendSuccess(messageId);
+        mMockLooper.dispatchAll();
+
+        inOrder.verify(mockAwareService).sendMessage(eq(clientId), eq(sessionId),
+                eq(peerHandle.peerId), eq(string1.getBytes()), eq(messageId), eq(0));
+        inOrder.verify(mockSessionCallback).onServiceDiscovered(peerIdCaptor.capture(),
+                eq(string1.getBytes()), eq(string2.getBytes()));
+        assertEquals(((WifiAwareManager.PeerHandle) peerIdCaptor.getValue()).peerId,
+                peerHandle.peerId);
+        inOrder.verify(mockSessionCallback).onMessageReceived(peerIdCaptor.capture(),
+                eq(string1.getBytes()));
+        assertEquals(((WifiAwareManager.PeerHandle) peerIdCaptor.getValue()).peerId,
+                peerHandle.peerId);
+        inOrder.verify(mockSessionCallback).onMessageSendFailed(eq(messageId));
+        inOrder.verify(mockSessionCallback).onMessageSendSucceeded(eq(messageId));
+
+        // (4) update publish
+        publishSession.getValue().updatePublish(publishConfig);
+        sessionProxyCallback.getValue().onSessionConfigFail(reason);
+        mMockLooper.dispatchAll();
+        inOrder.verify(mockAwareService).updatePublish(eq(clientId), eq(sessionId),
+                eq(publishConfig));
+        inOrder.verify(mockSessionCallback).onSessionConfigFailed();
+
+        // (5) terminate
+        publishSession.getValue().destroy();
+        mMockLooper.dispatchAll();
+        inOrder.verify(mockAwareService).terminateSession(clientId, sessionId);
+
+        // (6) try an update (nothing)
+        publishSession.getValue().updatePublish(publishConfig);
+        mMockLooper.dispatchAll();
+
+        verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService,
+                mockPublishSession);
+    }
+
+    /**
+     * Validate race condition of session terminate and session action: (1)
+     * connect, (2) publish success + terminate, (3) update.
+     */
+    @Test
+    public void testPublishRemoteTerminate() throws Exception {
+        final int clientId = 4565;
+        final int sessionId = 123;
+        final ConfigRequest configRequest = new ConfigRequest.Builder().build();
+        final PublishConfig publishConfig = new PublishConfig.Builder().build();
+        final int reason = WifiAwareDiscoverySessionCallback.TERMINATE_REASON_DONE;
+
+        InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockAwareService,
+                mockPublishSession);
+        ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass(
+                WifiAwareSession.class);
+        ArgumentCaptor<IWifiAwareEventCallback> clientProxyCallback = ArgumentCaptor
+                .forClass(IWifiAwareEventCallback.class);
+        ArgumentCaptor<IWifiAwareDiscoverySessionCallback> sessionProxyCallback = ArgumentCaptor
+                .forClass(IWifiAwareDiscoverySessionCallback.class);
+        ArgumentCaptor<WifiAwarePublishDiscoverySession> publishSession = ArgumentCaptor
+                .forClass(WifiAwarePublishDiscoverySession.class);
+
+        // (1) connect successfully
+        mDut.attach(mMockLooperHandler, configRequest, mockCallback, null);
+        inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(),
+                clientProxyCallback.capture(), eq(configRequest), eq(false));
+        clientProxyCallback.getValue().onConnectSuccess(clientId);
+        mMockLooper.dispatchAll();
+        inOrder.verify(mockCallback).onAttached(sessionCaptor.capture());
+        WifiAwareSession session = sessionCaptor.getValue();
+
+        // (2) publish: successfully - then terminated
+        session.publish(publishConfig, mockSessionCallback, mMockLooperHandler);
+        inOrder.verify(mockAwareService).publish(eq(clientId), eq(publishConfig),
+                sessionProxyCallback.capture());
+        sessionProxyCallback.getValue().onSessionStarted(sessionId);
+        sessionProxyCallback.getValue().onSessionTerminated(reason);
+        mMockLooper.dispatchAll();
+        inOrder.verify(mockSessionCallback).onPublishStarted(publishSession.capture());
+        inOrder.verify(mockSessionCallback).onSessionTerminated(reason);
+
+        // (3) failure when trying to update: NOP
+        publishSession.getValue().updatePublish(publishConfig);
+
+        verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService,
+                mockPublishSession);
+    }
+
+    /**
+     * Validate the subscribe flow: (0) connect + success, (1) subscribe, (2)
+     * success creates session, (3) pass through everything, (4) update
+     * subscribe through session, (5) terminate locally, (6) try another command
+     * - ignored.
+     */
+    @Test
+    public void testSubscribeFlow() throws Exception {
+        final int clientId = 4565;
+        final int sessionId = 123;
+        final ConfigRequest configRequest = new ConfigRequest.Builder().build();
+        final SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
+        final WifiAwareManager.PeerHandle peerHandle = new WifiAwareManager.PeerHandle(873);
+        final String string1 = "hey from here...";
+        final String string2 = "some other arbitrary string...";
+        final int messageId = 2123;
+        final int reason = AWARE_STATUS_ERROR;
+
+        InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockAwareService,
+                mockSubscribeSession);
+        ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass(
+                WifiAwareSession.class);
+        ArgumentCaptor<IWifiAwareEventCallback> clientProxyCallback = ArgumentCaptor
+                .forClass(IWifiAwareEventCallback.class);
+        ArgumentCaptor<IWifiAwareDiscoverySessionCallback> sessionProxyCallback = ArgumentCaptor
+                .forClass(IWifiAwareDiscoverySessionCallback.class);
+        ArgumentCaptor<WifiAwareSubscribeDiscoverySession> subscribeSession = ArgumentCaptor
+                .forClass(WifiAwareSubscribeDiscoverySession.class);
+        ArgumentCaptor<WifiAwareManager.PeerHandle> peerIdCaptor = ArgumentCaptor.forClass(
+                WifiAwareManager.PeerHandle.class);
+
+        // (0) connect + success
+        mDut.attach(mMockLooperHandler, configRequest, mockCallback, null);
+        inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(),
+                clientProxyCallback.capture(), eq(configRequest), eq(false));
+        clientProxyCallback.getValue().onConnectSuccess(clientId);
+        mMockLooper.dispatchAll();
+        inOrder.verify(mockCallback).onAttached(sessionCaptor.capture());
+        WifiAwareSession session = sessionCaptor.getValue();
+
+        // (1) subscribe
+        session.subscribe(subscribeConfig, mockSessionCallback, mMockLooperHandler);
+        inOrder.verify(mockAwareService).subscribe(eq(clientId), eq(subscribeConfig),
+                sessionProxyCallback.capture());
+
+        // (2) subscribe session created
+        sessionProxyCallback.getValue().onSessionStarted(sessionId);
+        mMockLooper.dispatchAll();
+        inOrder.verify(mockSessionCallback).onSubscribeStarted(subscribeSession.capture());
+
+        // (3) ...
+        subscribeSession.getValue().sendMessage(peerHandle, messageId, string1.getBytes());
+        sessionProxyCallback.getValue().onMatch(peerHandle.peerId, string1.getBytes(),
+                string2.getBytes());
+        sessionProxyCallback.getValue().onMessageReceived(peerHandle.peerId, string1.getBytes());
+        sessionProxyCallback.getValue().onMessageSendFail(messageId, reason);
+        sessionProxyCallback.getValue().onMessageSendSuccess(messageId);
+        mMockLooper.dispatchAll();
+
+        inOrder.verify(mockAwareService).sendMessage(eq(clientId), eq(sessionId),
+                eq(peerHandle.peerId), eq(string1.getBytes()), eq(messageId), eq(0));
+        inOrder.verify(mockSessionCallback).onServiceDiscovered(peerIdCaptor.capture(),
+                eq(string1.getBytes()), eq(string2.getBytes()));
+        assertEquals(((WifiAwareManager.PeerHandle) peerIdCaptor.getValue()).peerId,
+                peerHandle.peerId);
+        inOrder.verify(mockSessionCallback).onMessageReceived(peerIdCaptor.capture(),
+                eq(string1.getBytes()));
+        assertEquals(((WifiAwareManager.PeerHandle) peerIdCaptor.getValue()).peerId,
+                peerHandle.peerId);
+        inOrder.verify(mockSessionCallback).onMessageSendFailed(eq(messageId));
+        inOrder.verify(mockSessionCallback).onMessageSendSucceeded(eq(messageId));
+
+        // (4) update subscribe
+        subscribeSession.getValue().updateSubscribe(subscribeConfig);
+        sessionProxyCallback.getValue().onSessionConfigFail(reason);
+        mMockLooper.dispatchAll();
+        inOrder.verify(mockAwareService).updateSubscribe(eq(clientId), eq(sessionId),
+                eq(subscribeConfig));
+        inOrder.verify(mockSessionCallback).onSessionConfigFailed();
+
+        // (5) terminate
+        subscribeSession.getValue().destroy();
+        mMockLooper.dispatchAll();
+        inOrder.verify(mockAwareService).terminateSession(clientId, sessionId);
+
+        // (6) try an update (nothing)
+        subscribeSession.getValue().updateSubscribe(subscribeConfig);
+        mMockLooper.dispatchAll();
+
+        verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService,
+                mockSubscribeSession);
+    }
+
+    /**
+     * Validate race condition of session terminate and session action: (1)
+     * connect, (2) subscribe success + terminate, (3) update.
+     */
+    @Test
+    public void testSubscribeRemoteTerminate() throws Exception {
+        final int clientId = 4565;
+        final int sessionId = 123;
+        final ConfigRequest configRequest = new ConfigRequest.Builder().build();
+        final SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
+        final int reason = WifiAwareDiscoverySessionCallback.TERMINATE_REASON_DONE;
+
+        InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockAwareService,
+                mockSubscribeSession);
+        ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass(
+                WifiAwareSession.class);
+        ArgumentCaptor<IWifiAwareEventCallback> clientProxyCallback = ArgumentCaptor
+                .forClass(IWifiAwareEventCallback.class);
+        ArgumentCaptor<IWifiAwareDiscoverySessionCallback> sessionProxyCallback = ArgumentCaptor
+                .forClass(IWifiAwareDiscoverySessionCallback.class);
+        ArgumentCaptor<WifiAwareSubscribeDiscoverySession> subscribeSession = ArgumentCaptor
+                .forClass(WifiAwareSubscribeDiscoverySession.class);
+
+        // (1) connect successfully
+        mDut.attach(mMockLooperHandler, configRequest, mockCallback, null);
+        inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(),
+                clientProxyCallback.capture(), eq(configRequest), eq(false));
+        clientProxyCallback.getValue().onConnectSuccess(clientId);
+        mMockLooper.dispatchAll();
+        inOrder.verify(mockCallback).onAttached(sessionCaptor.capture());
+        WifiAwareSession session = sessionCaptor.getValue();
+
+        // (2) subscribe: successfully - then terminated
+        session.subscribe(subscribeConfig, mockSessionCallback, mMockLooperHandler);
+        inOrder.verify(mockAwareService).subscribe(eq(clientId), eq(subscribeConfig),
+                sessionProxyCallback.capture());
+        sessionProxyCallback.getValue().onSessionStarted(sessionId);
+        sessionProxyCallback.getValue().onSessionTerminated(reason);
+        mMockLooper.dispatchAll();
+        inOrder.verify(mockSessionCallback).onSubscribeStarted(subscribeSession.capture());
+        inOrder.verify(mockSessionCallback).onSessionTerminated(reason);
+
+        // (3) failure when trying to update: NOP
+        subscribeSession.getValue().updateSubscribe(subscribeConfig);
+
+        verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService,
+                mockSubscribeSession);
+    }
+
+    /*
+     * ConfigRequest Tests
+     */
+
+    @Test
+    public void testConfigRequestBuilderDefaults() {
+        ConfigRequest configRequest = new ConfigRequest.Builder().build();
+
+        collector.checkThat("mClusterHigh", ConfigRequest.CLUSTER_ID_MAX,
+                equalTo(configRequest.mClusterHigh));
+        collector.checkThat("mClusterLow", ConfigRequest.CLUSTER_ID_MIN,
+                equalTo(configRequest.mClusterLow));
+        collector.checkThat("mMasterPreference", 0,
+                equalTo(configRequest.mMasterPreference));
+        collector.checkThat("mSupport5gBand", false, equalTo(configRequest.mSupport5gBand));
+    }
+
+    @Test
+    public void testConfigRequestBuilder() {
+        final int clusterHigh = 100;
+        final int clusterLow = 5;
+        final int masterPreference = 55;
+        final boolean supportBand5g = true;
+
+        ConfigRequest configRequest = new ConfigRequest.Builder().setClusterHigh(clusterHigh)
+                .setClusterLow(clusterLow).setMasterPreference(masterPreference)
+                .setSupport5gBand(supportBand5g)
+                .build();
+
+        collector.checkThat("mClusterHigh", clusterHigh, equalTo(configRequest.mClusterHigh));
+        collector.checkThat("mClusterLow", clusterLow, equalTo(configRequest.mClusterLow));
+        collector.checkThat("mMasterPreference", masterPreference,
+                equalTo(configRequest.mMasterPreference));
+        collector.checkThat("mSupport5gBand", supportBand5g, equalTo(configRequest.mSupport5gBand));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testConfigRequestBuilderMasterPrefNegative() {
+        ConfigRequest.Builder builder = new ConfigRequest.Builder();
+        builder.setMasterPreference(-1);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testConfigRequestBuilderMasterPrefReserved1() {
+        new ConfigRequest.Builder().setMasterPreference(1);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testConfigRequestBuilderMasterPrefReserved255() {
+        new ConfigRequest.Builder().setMasterPreference(255);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testConfigRequestBuilderMasterPrefTooLarge() {
+        new ConfigRequest.Builder().setMasterPreference(256);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testConfigRequestBuilderClusterLowNegative() {
+        new ConfigRequest.Builder().setClusterLow(-1);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testConfigRequestBuilderClusterHighNegative() {
+        new ConfigRequest.Builder().setClusterHigh(-1);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testConfigRequestBuilderClusterLowAboveMax() {
+        new ConfigRequest.Builder().setClusterLow(ConfigRequest.CLUSTER_ID_MAX + 1);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testConfigRequestBuilderClusterHighAboveMax() {
+        new ConfigRequest.Builder().setClusterHigh(ConfigRequest.CLUSTER_ID_MAX + 1);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testConfigRequestBuilderClusterLowLargerThanHigh() {
+        new ConfigRequest.Builder().setClusterLow(100).setClusterHigh(5).build();
+    }
+
+    @Test
+    public void testConfigRequestParcel() {
+        final int clusterHigh = 189;
+        final int clusterLow = 25;
+        final int masterPreference = 177;
+        final boolean supportBand5g = true;
+
+        ConfigRequest configRequest = new ConfigRequest.Builder().setClusterHigh(clusterHigh)
+                .setClusterLow(clusterLow).setMasterPreference(masterPreference)
+                .setSupport5gBand(supportBand5g)
+                .build();
+
+        Parcel parcelW = Parcel.obtain();
+        configRequest.writeToParcel(parcelW, 0);
+        byte[] bytes = parcelW.marshall();
+        parcelW.recycle();
+
+        Parcel parcelR = Parcel.obtain();
+        parcelR.unmarshall(bytes, 0, bytes.length);
+        parcelR.setDataPosition(0);
+        ConfigRequest rereadConfigRequest = ConfigRequest.CREATOR.createFromParcel(parcelR);
+
+        assertEquals(configRequest, rereadConfigRequest);
+    }
+
+    /*
+     * SubscribeConfig Tests
+     */
+
+    @Test
+    public void testSubscribeConfigBuilderDefaults() {
+        SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
+
+        collector.checkThat("mServiceName", subscribeConfig.mServiceName, equalTo(null));
+        collector.checkThat("mSubscribeType", subscribeConfig.mSubscribeType,
+                equalTo(SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE));
+        collector.checkThat("mSubscribeCount", subscribeConfig.mSubscribeCount, equalTo(0));
+        collector.checkThat("mTtlSec", subscribeConfig.mTtlSec, equalTo(0));
+        collector.checkThat("mMatchStyle", subscribeConfig.mMatchStyle,
+                equalTo(SubscribeConfig.MATCH_STYLE_ALL));
+        collector.checkThat("mEnableTerminateNotification",
+                subscribeConfig.mEnableTerminateNotification, equalTo(true));
+    }
+
+    @Test
+    public void testSubscribeConfigBuilder() {
+        final String serviceName = "some_service_or_other";
+        final String serviceSpecificInfo = "long arbitrary string with some info";
+        final byte[] matchFilter = {
+                0, 1, 16, 1, 22 };
+        final int subscribeType = SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE;
+        final int subscribeCount = 10;
+        final int subscribeTtl = 15;
+        final int matchStyle = SubscribeConfig.MATCH_STYLE_FIRST_ONLY;
+        final boolean enableTerminateNotification = false;
+
+        SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().setServiceName(serviceName)
+                .setServiceSpecificInfo(serviceSpecificInfo.getBytes()).setMatchFilter(matchFilter)
+                .setSubscribeType(subscribeType)
+                .setSubscribeCount(subscribeCount).setTtlSec(subscribeTtl).setMatchStyle(matchStyle)
+                .setTerminateNotificationEnabled(enableTerminateNotification).build();
+
+        collector.checkThat("mServiceName", serviceName.getBytes(),
+                equalTo(subscribeConfig.mServiceName));
+        collector.checkThat("mServiceSpecificInfo",
+                serviceSpecificInfo.getBytes(), equalTo(subscribeConfig.mServiceSpecificInfo));
+        collector.checkThat("mMatchFilter", matchFilter, equalTo(subscribeConfig.mMatchFilter));
+        collector.checkThat("mSubscribeType", subscribeType,
+                equalTo(subscribeConfig.mSubscribeType));
+        collector.checkThat("mSubscribeCount", subscribeCount,
+                equalTo(subscribeConfig.mSubscribeCount));
+        collector.checkThat("mTtlSec", subscribeTtl, equalTo(subscribeConfig.mTtlSec));
+        collector.checkThat("mMatchStyle", matchStyle, equalTo(subscribeConfig.mMatchStyle));
+        collector.checkThat("mEnableTerminateNotification", enableTerminateNotification,
+                equalTo(subscribeConfig.mEnableTerminateNotification));
+    }
+
+    @Test
+    public void testSubscribeConfigParcel() {
+        final String serviceName = "some_service_or_other";
+        final String serviceSpecificInfo = "long arbitrary string with some info";
+        final byte[] matchFilter = {
+                0, 1, 16, 1, 22 };
+        final int subscribeType = SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE;
+        final int subscribeCount = 10;
+        final int subscribeTtl = 15;
+        final int matchStyle = SubscribeConfig.MATCH_STYLE_FIRST_ONLY;
+        final boolean enableTerminateNotification = true;
+
+        SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().setServiceName(serviceName)
+                .setServiceSpecificInfo(serviceSpecificInfo.getBytes()).setMatchFilter(matchFilter)
+                .setSubscribeType(subscribeType)
+                .setSubscribeCount(subscribeCount).setTtlSec(subscribeTtl).setMatchStyle(matchStyle)
+                .setTerminateNotificationEnabled(enableTerminateNotification).build();
+
+        Parcel parcelW = Parcel.obtain();
+        subscribeConfig.writeToParcel(parcelW, 0);
+        byte[] bytes = parcelW.marshall();
+        parcelW.recycle();
+
+        Parcel parcelR = Parcel.obtain();
+        parcelR.unmarshall(bytes, 0, bytes.length);
+        parcelR.setDataPosition(0);
+        SubscribeConfig rereadSubscribeConfig = SubscribeConfig.CREATOR.createFromParcel(parcelR);
+
+        assertEquals(subscribeConfig, rereadSubscribeConfig);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testSubscribeConfigBuilderBadSubscribeType() {
+        new SubscribeConfig.Builder().setSubscribeType(10);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testSubscribeConfigBuilderNegativeCount() {
+        new SubscribeConfig.Builder().setSubscribeCount(-1);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testSubscribeConfigBuilderNegativeTtl() {
+        new SubscribeConfig.Builder().setTtlSec(-100);
+    }
+
+    /**
+     * Validate that a bad match style configuration throws an exception.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testSubscribeConfigBuilderBadMatchStyle() {
+        new SubscribeConfig.Builder().setMatchStyle(10);
+    }
+
+    /*
+     * PublishConfig Tests
+     */
+
+    @Test
+    public void testPublishConfigBuilderDefaults() {
+        PublishConfig publishConfig = new PublishConfig.Builder().build();
+
+        collector.checkThat("mServiceName", publishConfig.mServiceName, equalTo(null));
+        collector.checkThat("mPublishType", publishConfig.mPublishType,
+                equalTo(PublishConfig.PUBLISH_TYPE_UNSOLICITED));
+        collector.checkThat("mPublishCount", publishConfig.mPublishCount, equalTo(0));
+        collector.checkThat("mTtlSec", publishConfig.mTtlSec, equalTo(0));
+        collector.checkThat("mEnableTerminateNotification",
+                publishConfig.mEnableTerminateNotification, equalTo(true));
+    }
+
+    @Test
+    public void testPublishConfigBuilder() {
+        final String serviceName = "some_service_or_other";
+        final String serviceSpecificInfo = "long arbitrary string with some info";
+        final byte[] matchFilter = {
+                0, 1, 16, 1, 22 };
+        final int publishType = PublishConfig.PUBLISH_TYPE_SOLICITED;
+        final int publishCount = 10;
+        final int publishTtl = 15;
+        final boolean enableTerminateNotification = false;
+
+        PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(serviceName)
+                .setServiceSpecificInfo(serviceSpecificInfo.getBytes()).setMatchFilter(matchFilter)
+                .setPublishType(publishType)
+                .setPublishCount(publishCount).setTtlSec(publishTtl)
+                .setTerminateNotificationEnabled(enableTerminateNotification).build();
+
+        collector.checkThat("mServiceName", serviceName.getBytes(),
+                equalTo(publishConfig.mServiceName));
+        collector.checkThat("mServiceSpecificInfo",
+                serviceSpecificInfo.getBytes(), equalTo(publishConfig.mServiceSpecificInfo));
+        collector.checkThat("mMatchFilter", matchFilter, equalTo(publishConfig.mMatchFilter));
+        collector.checkThat("mPublishType", publishType, equalTo(publishConfig.mPublishType));
+        collector.checkThat("mPublishCount", publishCount, equalTo(publishConfig.mPublishCount));
+        collector.checkThat("mTtlSec", publishTtl, equalTo(publishConfig.mTtlSec));
+        collector.checkThat("mEnableTerminateNotification", enableTerminateNotification,
+                equalTo(publishConfig.mEnableTerminateNotification));
+    }
+
+    @Test
+    public void testPublishConfigParcel() {
+        final String serviceName = "some_service_or_other";
+        final String serviceSpecificInfo = "long arbitrary string with some info";
+        final byte[] matchFilter = {
+                0, 1, 16, 1, 22 };
+        final int publishType = PublishConfig.PUBLISH_TYPE_SOLICITED;
+        final int publishCount = 10;
+        final int publishTtl = 15;
+        final boolean enableTerminateNotification = false;
+
+        PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(serviceName)
+                .setServiceSpecificInfo(serviceSpecificInfo.getBytes()).setMatchFilter(matchFilter)
+                .setPublishType(publishType)
+                .setPublishCount(publishCount).setTtlSec(publishTtl)
+                .setTerminateNotificationEnabled(enableTerminateNotification).build();
+
+        Parcel parcelW = Parcel.obtain();
+        publishConfig.writeToParcel(parcelW, 0);
+        byte[] bytes = parcelW.marshall();
+        parcelW.recycle();
+
+        Parcel parcelR = Parcel.obtain();
+        parcelR.unmarshall(bytes, 0, bytes.length);
+        parcelR.setDataPosition(0);
+        PublishConfig rereadPublishConfig = PublishConfig.CREATOR.createFromParcel(parcelR);
+
+        assertEquals(publishConfig, rereadPublishConfig);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testPublishConfigBuilderBadPublishType() {
+        new PublishConfig.Builder().setPublishType(5);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testPublishConfigBuilderNegativeCount() {
+        new PublishConfig.Builder().setPublishCount(-4);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testPublishConfigBuilderNegativeTtl() {
+        new PublishConfig.Builder().setTtlSec(-10);
+    }
+
+    /*
+     * Ranging tests
+     */
+
+    /**
+     * Validate ranging + success flow: (1) connect, (2) create a (publish) session, (3) start
+     * ranging, (4) ranging success callback, (5) ranging aborted callback ignored (since
+     * listener removed).
+     */
+    @Test
+    public void testRangingCallbacks() throws Exception {
+        final int clientId = 4565;
+        final int sessionId = 123;
+        final int rangingId = 3482;
+        final ConfigRequest configRequest = new ConfigRequest.Builder().build();
+        final PublishConfig publishConfig = new PublishConfig.Builder().build();
+        final RttManager.RttParams rttParams = new RttManager.RttParams();
+        rttParams.deviceType = RttManager.RTT_PEER_NAN;
+        rttParams.bssid = Integer.toString(1234);
+        final RttManager.RttResult rttResults = new RttManager.RttResult();
+        rttResults.distance = 10;
+
+        when(mockAwareService.startRanging(anyInt(), anyInt(),
+                any(RttManager.ParcelableRttParams.class))).thenReturn(rangingId);
+
+        InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockAwareService,
+                mockPublishSession, mockRttListener);
+        ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass(
+                WifiAwareSession.class);
+        ArgumentCaptor<IWifiAwareEventCallback> clientProxyCallback = ArgumentCaptor
+                .forClass(IWifiAwareEventCallback.class);
+        ArgumentCaptor<IWifiAwareDiscoverySessionCallback> sessionProxyCallback = ArgumentCaptor
+                .forClass(IWifiAwareDiscoverySessionCallback.class);
+        ArgumentCaptor<WifiAwarePublishDiscoverySession> publishSession = ArgumentCaptor
+                .forClass(WifiAwarePublishDiscoverySession.class);
+        ArgumentCaptor<RttManager.ParcelableRttParams> rttParamCaptor = ArgumentCaptor
+                .forClass(RttManager.ParcelableRttParams.class);
+        ArgumentCaptor<RttManager.RttResult[]> rttResultsCaptor = ArgumentCaptor
+                .forClass(RttManager.RttResult[].class);
+
+        // (1) connect successfully
+        mDut.attach(mMockLooperHandler, configRequest, mockCallback, null);
+        inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(),
+                clientProxyCallback.capture(), eq(configRequest), eq(false));
+        clientProxyCallback.getValue().onConnectSuccess(clientId);
+        mMockLooper.dispatchAll();
+        inOrder.verify(mockCallback).onAttached(sessionCaptor.capture());
+        WifiAwareSession session = sessionCaptor.getValue();
+
+        // (2) publish successfully
+        session.publish(publishConfig, mockSessionCallback, mMockLooperHandler);
+        inOrder.verify(mockAwareService).publish(eq(clientId), eq(publishConfig),
+                sessionProxyCallback.capture());
+        sessionProxyCallback.getValue().onSessionStarted(sessionId);
+        mMockLooper.dispatchAll();
+        inOrder.verify(mockSessionCallback).onPublishStarted(publishSession.capture());
+
+        // (3) start ranging
+        publishSession.getValue().startRanging(new RttManager.RttParams[]{rttParams},
+                mockRttListener);
+        inOrder.verify(mockAwareService).startRanging(eq(clientId), eq(sessionId),
+                rttParamCaptor.capture());
+        collector.checkThat("RttParams.deviceType", rttParams.deviceType,
+                equalTo(rttParamCaptor.getValue().mParams[0].deviceType));
+        collector.checkThat("RttParams.bssid", rttParams.bssid,
+                equalTo(rttParamCaptor.getValue().mParams[0].bssid));
+
+        // (4) ranging success callback
+        clientProxyCallback.getValue().onRangingSuccess(rangingId,
+                new RttManager.ParcelableRttResults(new RttManager.RttResult[] { rttResults }));
+        mMockLooper.dispatchAll();
+        inOrder.verify(mockRttListener).onSuccess(rttResultsCaptor.capture());
+        collector.checkThat("RttResult.distance", rttResults.distance,
+                equalTo(rttResultsCaptor.getValue()[0].distance));
+
+        // (5) ranging aborted callback (should be ignored since listener cleared on first callback)
+        clientProxyCallback.getValue().onRangingAborted(rangingId);
+        mMockLooper.dispatchAll();
+
+        verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService,
+                mockPublishSession, mockRttListener);
+    }
+
+    /*
+     * Data-path tests
+     */
+
+    /**
+     * Validate that correct network specifier is generated for client-based data-path.
+     */
+    @Test
+    public void testNetworkSpecifierWithClient() throws Exception {
+        final int clientId = 4565;
+        final int sessionId = 123;
+        final WifiAwareManager.PeerHandle peerHandle = new WifiAwareManager.PeerHandle(123412);
+        final int role = WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR;
+        final String token = "Some arbitrary token string - can really be anything";
+        final ConfigRequest configRequest = new ConfigRequest.Builder().build();
+        final PublishConfig publishConfig = new PublishConfig.Builder().build();
+
+        String tokenB64 = Base64.encodeToString(token.getBytes(), Base64.DEFAULT);
+
+        ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass(
+                WifiAwareSession.class);
+        ArgumentCaptor<IWifiAwareEventCallback> clientProxyCallback = ArgumentCaptor
+                .forClass(IWifiAwareEventCallback.class);
+        ArgumentCaptor<IWifiAwareDiscoverySessionCallback> sessionProxyCallback = ArgumentCaptor
+                .forClass(IWifiAwareDiscoverySessionCallback.class);
+        ArgumentCaptor<WifiAwarePublishDiscoverySession> publishSession = ArgumentCaptor
+                .forClass(WifiAwarePublishDiscoverySession.class);
+
+        InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockAwareService,
+                mockPublishSession, mockRttListener);
+
+        // (1) connect successfully
+        mDut.attach(mMockLooperHandler, configRequest, mockCallback, null);
+        inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(),
+                clientProxyCallback.capture(), eq(configRequest), eq(false));
+        clientProxyCallback.getValue().onConnectSuccess(clientId);
+        mMockLooper.dispatchAll();
+        inOrder.verify(mockCallback).onAttached(sessionCaptor.capture());
+        WifiAwareSession session = sessionCaptor.getValue();
+
+        // (2) publish successfully
+        session.publish(publishConfig, mockSessionCallback, mMockLooperHandler);
+        inOrder.verify(mockAwareService).publish(eq(clientId), eq(publishConfig),
+                sessionProxyCallback.capture());
+        sessionProxyCallback.getValue().onSessionStarted(sessionId);
+        mMockLooper.dispatchAll();
+        inOrder.verify(mockSessionCallback).onPublishStarted(publishSession.capture());
+
+        // (3) request a network specifier from the session
+        String networkSpecifier = publishSession.getValue().createNetworkSpecifier(role, peerHandle,
+                token.getBytes());
+
+        // validate format
+        JSONObject jsonObject = new JSONObject(networkSpecifier);
+        collector.checkThat("role", role,
+                equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_ROLE)));
+        collector.checkThat("client_id", clientId,
+                equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_CLIENT_ID)));
+        collector.checkThat("session_id", sessionId,
+                equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_SESSION_ID)));
+        collector.checkThat("peer_id", peerHandle.peerId,
+                equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_PEER_ID)));
+        collector.checkThat("token", tokenB64,
+                equalTo(jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_TOKEN)));
+
+        verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService,
+                mockPublishSession, mockRttListener);
+    }
+
+    /**
+     * Validate that correct network specifier is generated for a direct data-path (i.e.
+     * specifying MAC address as opposed to a client-based oqaque specification).
+     */
+    @Test
+    public void testNetworkSpecifierDirect() throws Exception {
+        final int clientId = 134;
+        final ConfigRequest configRequest = new ConfigRequest.Builder().build();
+        final byte[] someMac = HexEncoding.decode("000102030405".toCharArray(), false);
+        final int role = WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR;
+        final String token = "Some arbitrary token string - can really be anything";
+
+        String tokenB64 = Base64.encodeToString(token.getBytes(), Base64.DEFAULT);
+
+        ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass(
+                WifiAwareSession.class);
+        ArgumentCaptor<IWifiAwareEventCallback> clientProxyCallback = ArgumentCaptor
+                .forClass(IWifiAwareEventCallback.class);
+
+        InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockAwareService,
+                mockPublishSession, mockRttListener);
+
+        // (1) connect successfully
+        mDut.attach(mMockLooperHandler, configRequest, mockCallback, null);
+        inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(),
+                clientProxyCallback.capture(), eq(configRequest), eq(false));
+        clientProxyCallback.getValue().onConnectSuccess(clientId);
+        mMockLooper.dispatchAll();
+        inOrder.verify(mockCallback).onAttached(sessionCaptor.capture());
+        WifiAwareSession session = sessionCaptor.getValue();
+
+        /* (2) request a direct network specifier*/
+        String networkSpecifier = session.createNetworkSpecifier(role, someMac, token.getBytes());
+
+        /* validate format*/
+        JSONObject jsonObject = new JSONObject(networkSpecifier);
+        collector.checkThat("role", role,
+                equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_ROLE)));
+        collector.checkThat("client_id", clientId,
+                equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_CLIENT_ID)));
+        collector.checkThat("peer_mac", someMac, equalTo(HexEncoding.decode(
+                jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PEER_MAC).toCharArray(),
+                false)));
+        collector.checkThat("token", tokenB64,
+                equalTo(jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_TOKEN)));
+
+        verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService,
+                mockPublishSession, mockRttListener);
+    }
+}