1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.uwb;
18 
19 import static android.uwb.RangingSession.Callback.REASON_BAD_PARAMETERS;
20 
21 import static com.google.common.truth.Truth.assertThat;
22 
23 import static org.junit.Assert.assertEquals;
24 import static org.junit.Assert.assertFalse;
25 import static org.junit.Assert.assertTrue;
26 import static org.junit.Assert.fail;
27 import static org.junit.Assume.assumeTrue;
28 import static org.mockito.ArgumentMatchers.any;
29 import static org.mockito.ArgumentMatchers.anyInt;
30 import static org.mockito.ArgumentMatchers.eq;
31 import static org.mockito.Mockito.doAnswer;
32 import static org.mockito.Mockito.doNothing;
33 import static org.mockito.Mockito.mock;
34 import static org.mockito.Mockito.times;
35 import static org.mockito.Mockito.verify;
36 import static org.mockito.Mockito.when;
37 
38 import android.content.AttributionSource;
39 import android.os.PersistableBundle;
40 import android.os.Process;
41 import android.os.RemoteException;
42 
43 import androidx.test.ext.junit.runners.AndroidJUnit4;
44 import androidx.test.filters.SmallTest;
45 
46 import com.android.modules.utils.build.SdkLevel;
47 
48 import org.junit.Test;
49 import org.junit.runner.RunWith;
50 import org.mockito.ArgumentCaptor;
51 import org.mockito.invocation.InvocationOnMock;
52 import org.mockito.stubbing.Answer;
53 
54 import java.util.concurrent.Executor;
55 
56 /**
57  * Test of {@link RangingSession}.
58  */
59 @SmallTest
60 @RunWith(AndroidJUnit4.class)
61 public class RangingSessionTest {
62     private static final Executor EXECUTOR = UwbTestUtils.getExecutor();
63     private static final PersistableBundle PARAMS = new PersistableBundle();
64     private static final UwbAddress UWB_ADDRESS = UwbAddress.fromBytes(new byte[] {0x00, 0x56});
65     private static final @RangingSession.Callback.Reason int REASON =
66             RangingSession.Callback.REASON_GENERIC_ERROR;
67     private static final int UID = Process.myUid();
68     private static final String PACKAGE_NAME = "com.uwb.test";
69     private static final AttributionSource ATTRIBUTION_SOURCE =
70             new AttributionSource.Builder(UID).setPackageName(PACKAGE_NAME).build();
71     private static final int HANDLE_ID = 12;
72     private static final int PID = Process.myPid();
73     private static final int MAX_DATA_SIZE = 100;
74     public static final int STATUS_OK = 0;
75 
76     @Test
testOnRangingOpened_OnOpenSuccessCalled()77     public void testOnRangingOpened_OnOpenSuccessCalled() {
78         SessionHandle handle = new SessionHandle(HANDLE_ID, ATTRIBUTION_SOURCE, PID);
79         RangingSession.Callback callback = mock(RangingSession.Callback.class);
80         IUwbAdapter adapter = mock(IUwbAdapter.class);
81         RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle);
82         verifyOpenState(session, false);
83 
84         session.onRangingOpened();
85         verifyOpenState(session, true);
86 
87         // Verify that the onOpenSuccess callback was invoked
88         verify(callback, times(1)).onOpened(eq(session));
89         verify(callback, times(0)).onClosed(anyInt(), any());
90     }
91 
92     @Test
testOnRangingOpened_OnServiceDiscoveredConnectedCalled()93     public void testOnRangingOpened_OnServiceDiscoveredConnectedCalled() {
94         SessionHandle handle = new SessionHandle(HANDLE_ID, ATTRIBUTION_SOURCE, PID);
95         RangingSession.Callback callback = mock(RangingSession.Callback.class);
96         IUwbAdapter adapter = mock(IUwbAdapter.class);
97         RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle);
98         verifyOpenState(session, false);
99 
100         session.onRangingOpened();
101         verifyOpenState(session, true);
102 
103         // Verify that the onOpenSuccess callback was invoked
104         verify(callback, times(1)).onOpened(eq(session));
105         verify(callback, times(0)).onClosed(anyInt(), any());
106 
107         session.onServiceDiscovered(PARAMS);
108         verify(callback, times(1)).onServiceDiscovered(eq(PARAMS));
109 
110         session.onServiceConnected(PARAMS);
111         verify(callback, times(1)).onServiceConnected(eq(PARAMS));
112     }
113 
114 
115     @Test
testOnRangingOpened_CannotOpenClosedSession()116     public void testOnRangingOpened_CannotOpenClosedSession() {
117         SessionHandle handle = new SessionHandle(HANDLE_ID, ATTRIBUTION_SOURCE, PID);
118         RangingSession.Callback callback = mock(RangingSession.Callback.class);
119         IUwbAdapter adapter = mock(IUwbAdapter.class);
120         RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle);
121 
122         session.onRangingOpened();
123         verifyOpenState(session, true);
124         verify(callback, times(1)).onOpened(eq(session));
125         verify(callback, times(0)).onClosed(anyInt(), any());
126 
127         session.onRangingClosed(REASON, PARAMS);
128         verifyOpenState(session, false);
129         verify(callback, times(1)).onOpened(eq(session));
130         verify(callback, times(1)).onClosed(anyInt(), any());
131 
132         // Now invoke the ranging started callback and ensure the session remains closed
133         session.onRangingOpened();
134         verifyOpenState(session, false);
135         verify(callback, times(1)).onOpened(eq(session));
136         verify(callback, times(1)).onClosed(anyInt(), any());
137     }
138 
139     @Test
testOnRangingClosed_OnClosedCalledWhenSessionNotOpen()140     public void testOnRangingClosed_OnClosedCalledWhenSessionNotOpen() {
141         SessionHandle handle = new SessionHandle(HANDLE_ID, ATTRIBUTION_SOURCE, PID);
142         RangingSession.Callback callback = mock(RangingSession.Callback.class);
143         IUwbAdapter adapter = mock(IUwbAdapter.class);
144         RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle);
145         verifyOpenState(session, false);
146 
147         session.onRangingClosed(REASON, PARAMS);
148         verifyOpenState(session, false);
149 
150         // Verify that the onOpenSuccess callback was invoked
151         verify(callback, times(0)).onOpened(eq(session));
152         verify(callback, times(1)).onClosed(anyInt(), any());
153     }
154 
155     @Test
testOnRangingClosed_OnClosedCalled()156     public void testOnRangingClosed_OnClosedCalled() {
157         SessionHandle handle = new SessionHandle(HANDLE_ID, ATTRIBUTION_SOURCE, PID);
158         RangingSession.Callback callback = mock(RangingSession.Callback.class);
159         IUwbAdapter adapter = mock(IUwbAdapter.class);
160         RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle);
161         session.onRangingStarted(PARAMS);
162         session.onRangingClosed(REASON, PARAMS);
163         verify(callback, times(1)).onClosed(anyInt(), any());
164 
165         verifyOpenState(session, false);
166         session.onRangingClosed(REASON, PARAMS);
167         verify(callback, times(2)).onClosed(anyInt(), any());
168     }
169 
170     @Test
testOnRangingResult_OnReportReceivedCalled()171     public void testOnRangingResult_OnReportReceivedCalled() {
172         SessionHandle handle = new SessionHandle(HANDLE_ID, ATTRIBUTION_SOURCE, PID);
173         RangingSession.Callback callback = mock(RangingSession.Callback.class);
174         IUwbAdapter adapter = mock(IUwbAdapter.class);
175         RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle);
176         verifyOpenState(session, false);
177 
178         session.onRangingStarted(PARAMS);
179         verifyOpenState(session, true);
180 
181         RangingReport report = UwbTestUtils.getRangingReports(1);
182         session.onRangingResult(report);
183         verify(callback, times(1)).onReportReceived(eq(report));
184     }
185 
186     @Test
testStart_CannotStartIfAlreadyStarted()187     public void testStart_CannotStartIfAlreadyStarted() throws RemoteException {
188         SessionHandle handle = new SessionHandle(HANDLE_ID, ATTRIBUTION_SOURCE, PID);
189         RangingSession.Callback callback = mock(RangingSession.Callback.class);
190         IUwbAdapter adapter = mock(IUwbAdapter.class);
191         RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle);
192         doAnswer(new StartAnswer(session)).when(adapter).startRanging(any(), any());
193         session.onRangingOpened();
194 
195         session.start(PARAMS);
196         verify(callback, times(1)).onStarted(any());
197 
198         // Calling start again should throw an illegal state
199         verifyThrowIllegalState(() -> session.start(PARAMS));
200         verify(callback, times(1)).onStarted(any());
201     }
202 
203     @Test
testStop_CannotStopIfAlreadyStopped()204     public void testStop_CannotStopIfAlreadyStopped() throws RemoteException {
205         SessionHandle handle = new SessionHandle(HANDLE_ID, ATTRIBUTION_SOURCE, PID);
206         RangingSession.Callback callback = mock(RangingSession.Callback.class);
207         IUwbAdapter adapter = mock(IUwbAdapter.class);
208         RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle);
209         doAnswer(new StartAnswer(session)).when(adapter).startRanging(any(), any());
210         doAnswer(new StopAnswer(session)).when(adapter).stopRanging(any());
211         session.onRangingOpened();
212         session.start(PARAMS);
213 
214         verifyNoThrowIllegalState(session::stop);
215         verify(callback, times(1)).onStopped(anyInt(), any());
216 
217         // Calling stop again should throw an illegal state
218         verifyThrowIllegalState(session::stop);
219         verify(callback, times(1)).onStopped(anyInt(), any());
220     }
221 
222     @Test
testStop_CannotStopIfOpenFailed()223     public void testStop_CannotStopIfOpenFailed() throws RemoteException {
224         SessionHandle handle = new SessionHandle(HANDLE_ID, ATTRIBUTION_SOURCE, PID);
225         RangingSession.Callback callback = mock(RangingSession.Callback.class);
226         IUwbAdapter adapter = mock(IUwbAdapter.class);
227         RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle);
228         doAnswer(new StartAnswer(session)).when(adapter).startRanging(any(), any());
229         doAnswer(new StopAnswer(session)).when(adapter).stopRanging(any());
230         session.onRangingOpened();
231         session.start(PARAMS);
232 
233         verifyNoThrowIllegalState(() -> session.onRangingOpenFailed(REASON_BAD_PARAMETERS, PARAMS));
234         verify(callback, times(1)).onOpenFailed(
235                 REASON_BAD_PARAMETERS, PARAMS);
236 
237         // Calling stop again should throw an illegal state
238         verifyThrowIllegalState(session::stop);
239         verify(callback, times(0)).onStopped(anyInt(), any());
240     }
241 
242     @Test
testCallbacks_OnlyWhenOpened()243     public void testCallbacks_OnlyWhenOpened() throws RemoteException {
244         SessionHandle handle = new SessionHandle(HANDLE_ID, ATTRIBUTION_SOURCE, PID);
245         RangingSession.Callback callback = mock(RangingSession.Callback.class);
246         IUwbAdapter adapter = mock(IUwbAdapter.class);
247         RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle);
248         doAnswer(new OpenAnswer(session)).when(adapter).openRanging(
249                 any(), any(), any(), any(), any());
250         doAnswer(new StartAnswer(session)).when(adapter).startRanging(any(), any());
251         doAnswer(new ReconfigureAnswer(session)).when(adapter).reconfigureRanging(any(), any());
252         doAnswer(new PauseAnswer(session)).when(adapter).pause(any(), any());
253         doAnswer(new ResumeAnswer(session)).when(adapter).resume(any(), any());
254         doAnswer(new ControleeAddAnswer(session)).when(adapter).addControlee(any(), any());
255         doAnswer(new ControleeRemoveAnswer(session)).when(adapter).removeControlee(any(), any());
256         doAnswer(new DataSendAnswer(session)).when(adapter).sendData(any(), any(), any(), any());
257         doAnswer(new StopAnswer(session)).when(adapter).stopRanging(any());
258         doAnswer(new CloseAnswer(session)).when(adapter).closeRanging(any());
259         doAnswer(new DataTransferPhaseConfigAnswer(session)).when(adapter)
260                 .setDataTransferPhaseConfig(any(), any());
261         doAnswer(new HybridSessionControllerConfigurationAnswer(session)).when(adapter)
262                 .setHybridSessionControllerConfiguration(any(), any());
263         doAnswer(new HybridSessionControleeConfigurationAnswer(session)).when(adapter)
264                 .setHybridSessionControleeConfiguration(any(), any());
265 
266         verifyThrowIllegalState(() -> session.reconfigure(PARAMS));
267         verify(callback, times(0)).onReconfigured(any());
268         verifyOpenState(session, false);
269 
270         session.onRangingOpened();
271         verifyOpenState(session, true);
272         verify(callback, times(1)).onOpened(any());
273         verifyNoThrowIllegalState(() -> session.reconfigure(PARAMS));
274         verify(callback, times(1)).onReconfigured(any());
275         verifyThrowIllegalState(() -> session.pause(PARAMS));
276         verify(callback, times(0)).onPaused(any());
277         verifyThrowIllegalState(() -> session.resume(PARAMS));
278         verify(callback, times(0)).onResumed(any());
279         verifyNoThrowIllegalState(() -> session.addControlee(PARAMS));
280         verify(callback, times(1)).onControleeAdded(any());
281         verifyNoThrowIllegalState(() -> session.removeControlee(PARAMS));
282         verify(callback, times(1)).onControleeRemoved(any());
283         verifyThrowIllegalState(() -> session.sendData(
284                 UWB_ADDRESS, PARAMS, new byte[] {0x05, 0x1}));
285         verify(callback, times(0)).onDataSent(any(), any());
286 
287         session.onRangingStartFailed(REASON_BAD_PARAMETERS, PARAMS);
288         verifyOpenState(session, true);
289         verify(callback, times(1)).onStartFailed(
290                 REASON_BAD_PARAMETERS, PARAMS);
291 
292         session.onRangingStarted(PARAMS);
293         verifyOpenState(session, true);
294         verifyNoThrowIllegalState(() -> session.reconfigure(PARAMS));
295         verify(callback, times(2)).onReconfigured(any());
296         verifyNoThrowIllegalState(() -> session.reconfigure(null));
297         verify(callback, times(1)).onReconfigureFailed(
298                 eq(REASON_BAD_PARAMETERS), any());
299         verifyNoThrowIllegalState(() -> session.pause(PARAMS));
300         verify(callback, times(1)).onPaused(any());
301         verifyNoThrowIllegalState(() -> session.pause(null));
302         verify(callback, times(1)).onPauseFailed(
303                 eq(REASON_BAD_PARAMETERS), any());
304         verifyNoThrowIllegalState(() -> session.resume(PARAMS));
305         verify(callback, times(1)).onResumed(any());
306         verifyNoThrowIllegalState(() -> session.resume(null));
307         verify(callback, times(1)).onResumeFailed(
308                 eq(REASON_BAD_PARAMETERS), any());
309         verifyNoThrowIllegalState(() -> session.addControlee(PARAMS));
310         verify(callback, times(2)).onControleeAdded(any());
311         verifyNoThrowIllegalState(() -> session.addControlee(null));
312         verify(callback, times(1)).onControleeAddFailed(
313                 eq(REASON_BAD_PARAMETERS), any());
314         verifyNoThrowIllegalState(() -> session.removeControlee(PARAMS));
315         verify(callback, times(2)).onControleeRemoved(any());
316         verifyNoThrowIllegalState(() -> session.removeControlee(null));
317         verify(callback, times(1)).onControleeRemoveFailed(
318                 eq(REASON_BAD_PARAMETERS), any());
319         verifyNoThrowIllegalState(() -> session.sendData(
320                 UWB_ADDRESS, PARAMS, new byte[] {0x05, 0x1}));
321         verify(callback, times(1)).onDataSent(any(), any());
322         verifyNoThrowIllegalState(() -> session.sendData(
323                 null, PARAMS, new byte[] {0x05, 0x1}));
324         verify(callback, times(1)).onDataSendFailed(
325                 eq(null), eq(REASON_BAD_PARAMETERS), any());
326 
327         session.onDataReceived(UWB_ADDRESS, PARAMS, new byte[] {0x5, 0x7});
328         verify(callback, times(1)).onDataReceived(
329                 UWB_ADDRESS, PARAMS, new byte[] {0x5, 0x7});
330         session.onDataReceiveFailed(UWB_ADDRESS, REASON_BAD_PARAMETERS, PARAMS);
331         verify(callback, times(1)).onDataReceiveFailed(
332                 UWB_ADDRESS, REASON_BAD_PARAMETERS, PARAMS);
333 
334         session.onDataTransferPhaseConfigured(PARAMS);
335         verify(callback, times(1)).onDataTransferPhaseConfigured(any());
336         session.setDataTransferPhaseConfig(PARAMS);
337         verify(callback, times(2)).onDataTransferPhaseConfigured(any());
338 
339         session.onDataTransferPhaseConfigFailed(REASON_BAD_PARAMETERS, PARAMS);
340         verify(callback, times(1)).onDataTransferPhaseConfigFailed(
341                 eq(REASON_BAD_PARAMETERS), eq(PARAMS));
342         verifyNoThrowIllegalState(() -> session.setDataTransferPhaseConfig(null));
343         verify(callback, times(1)).onDataTransferPhaseConfigFailed(
344                 eq(REASON_BAD_PARAMETERS), eq(null));
345 
346         session.onHybridSessionControllerConfigured(PARAMS);
347         verify(callback, times(1)).onHybridSessionControllerConfigured(any());
348         session.setHybridSessionControllerConfiguration(PARAMS);
349         verify(callback, times(2)).onHybridSessionControllerConfigured(any());
350 
351         session.onHybridSessionControllerConfigurationFailed(REASON_BAD_PARAMETERS, PARAMS);
352         verify(callback, times(1)).onHybridSessionControllerConfigurationFailed(
353                 eq(REASON_BAD_PARAMETERS), eq(PARAMS));
354         verifyNoThrowIllegalState(() -> session.setHybridSessionControllerConfiguration(null));
355         verify(callback, times(1)).onHybridSessionControllerConfigurationFailed(
356                 eq(REASON_BAD_PARAMETERS), eq(null));
357 
358         session.onHybridSessionControleeConfigured(PARAMS);
359         verify(callback, times(1)).onHybridSessionControleeConfigured(any());
360         session.setHybridSessionControleeConfiguration(PARAMS);
361         verify(callback, times(2)).onHybridSessionControleeConfigured(any());
362 
363         session.onHybridSessionControleeConfigurationFailed(REASON_BAD_PARAMETERS, PARAMS);
364         verify(callback, times(1)).onHybridSessionControleeConfigurationFailed(
365                 eq(REASON_BAD_PARAMETERS), eq(PARAMS));
366         verifyNoThrowIllegalState(() -> session.setHybridSessionControleeConfiguration(null));
367         verify(callback, times(1)).onHybridSessionControleeConfigurationFailed(
368                 eq(REASON_BAD_PARAMETERS), eq(null));
369 
370         session.stop();
371         verifyOpenState(session, true);
372         verify(callback, times(1)).onStopped(REASON, PARAMS);
373 
374         verifyNoThrowIllegalState(() -> session.reconfigure(PARAMS));
375         verify(callback, times(3)).onReconfigured(any());
376         verifyThrowIllegalState(() -> session.pause(PARAMS));
377         verify(callback, times(1)).onPaused(any());
378         verifyThrowIllegalState(() -> session.resume(PARAMS));
379         verify(callback, times(1)).onResumed(any());
380         verifyNoThrowIllegalState(() -> session.addControlee(PARAMS));
381         verify(callback, times(3)).onControleeAdded(any());
382         verifyNoThrowIllegalState(() -> session.removeControlee(PARAMS));
383         verify(callback, times(3)).onControleeRemoved(any());
384         verifyThrowIllegalState(() -> session.sendData(
385                 UWB_ADDRESS, PARAMS, new byte[] {0x05, 0x1}));
386         verify(callback, times(1)).onDataSent(any(), any());
387 
388         session.close();
389         verifyOpenState(session, false);
390         verify(callback, times(1)).onClosed(REASON, PARAMS);
391 
392         verifyThrowIllegalState(() -> session.reconfigure(PARAMS));
393         verify(callback, times(3)).onReconfigured(any());
394         verifyThrowIllegalState(() -> session.pause(PARAMS));
395         verify(callback, times(1)).onPaused(any());
396         verifyThrowIllegalState(() -> session.resume(PARAMS));
397         verify(callback, times(1)).onResumed(any());
398         verifyThrowIllegalState(() -> session.addControlee(PARAMS));
399         verify(callback, times(3)).onControleeAdded(any());
400         verifyThrowIllegalState(() -> session.removeControlee(PARAMS));
401         verify(callback, times(3)).onControleeRemoved(any());
402         verifyThrowIllegalState(() -> session.sendData(
403                 UWB_ADDRESS, PARAMS, new byte[] {0x05, 0x1}));
404         verify(callback, times(1)).onDataSent(any(), any());
405     }
406 
407     @Test
testClose_NoCallbackUntilInvoked()408     public void testClose_NoCallbackUntilInvoked() throws RemoteException {
409         SessionHandle handle = new SessionHandle(HANDLE_ID, ATTRIBUTION_SOURCE, PID);
410         RangingSession.Callback callback = mock(RangingSession.Callback.class);
411         IUwbAdapter adapter = mock(IUwbAdapter.class);
412         RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle);
413         session.onRangingOpened();
414 
415         // Calling close multiple times should invoke closeRanging until the session receives
416         // the onClosed callback.
417         int totalCallsBeforeOnRangingClosed = 3;
418         for (int i = 1; i <= totalCallsBeforeOnRangingClosed; i++) {
419             session.close();
420             verifyOpenState(session, true);
421             verify(adapter, times(i)).closeRanging(handle);
422             verify(callback, times(0)).onClosed(anyInt(), any());
423         }
424 
425         // After onClosed is invoked, then the adapter should no longer be called for each call to
426         // the session's close.
427         final int totalCallsAfterOnRangingClosed = 2;
428         for (int i = 1; i <= totalCallsAfterOnRangingClosed; i++) {
429             session.onRangingClosed(REASON, PARAMS);
430             verifyOpenState(session, false);
431             verify(adapter, times(totalCallsBeforeOnRangingClosed)).closeRanging(handle);
432             verify(callback, times(i)).onClosed(anyInt(), any());
433         }
434     }
435 
436     @Test
testClose_OnClosedCalled()437     public void testClose_OnClosedCalled() throws RemoteException {
438         SessionHandle handle = new SessionHandle(HANDLE_ID, ATTRIBUTION_SOURCE, PID);
439         RangingSession.Callback callback = mock(RangingSession.Callback.class);
440         IUwbAdapter adapter = mock(IUwbAdapter.class);
441         RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle);
442         doAnswer(new CloseAnswer(session)).when(adapter).closeRanging(any());
443         session.onRangingOpened();
444 
445         session.close();
446         verify(callback, times(1)).onClosed(anyInt(), any());
447     }
448 
449     @Test
testClose_CannotInteractFurther()450     public void testClose_CannotInteractFurther() throws RemoteException {
451         SessionHandle handle = new SessionHandle(HANDLE_ID, ATTRIBUTION_SOURCE, PID);
452         RangingSession.Callback callback = mock(RangingSession.Callback.class);
453         IUwbAdapter adapter = mock(IUwbAdapter.class);
454         RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle);
455         doAnswer(new CloseAnswer(session)).when(adapter).closeRanging(any());
456         session.close();
457 
458         verifyThrowIllegalState(() -> session.start(PARAMS));
459         verifyThrowIllegalState(() -> session.reconfigure(PARAMS));
460         verifyThrowIllegalState(() -> session.stop());
461         verifyNoThrowIllegalState(() -> session.close());
462     }
463 
464     @Test
testQueryDataSize()465     public void testQueryDataSize() throws RemoteException {
466         SessionHandle handle = new SessionHandle(HANDLE_ID, ATTRIBUTION_SOURCE, PID);
467         RangingSession.Callback callback = mock(RangingSession.Callback.class);
468         IUwbAdapter adapter = mock(IUwbAdapter.class);
469         RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle);
470 
471         when(adapter.queryMaxDataSizeBytes(handle)).thenReturn(MAX_DATA_SIZE);
472 
473         session.onRangingStarted(PARAMS);
474         assertThat(session.queryMaxDataSizeBytes()).isEqualTo(MAX_DATA_SIZE);
475     }
476 
477     @Test
testOnRangingResult_OnReportReceivedCalledWhenOpen()478     public void testOnRangingResult_OnReportReceivedCalledWhenOpen() {
479         SessionHandle handle = new SessionHandle(HANDLE_ID, ATTRIBUTION_SOURCE, PID);
480         RangingSession.Callback callback = mock(RangingSession.Callback.class);
481         IUwbAdapter adapter = mock(IUwbAdapter.class);
482         RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle);
483 
484         assertFalse(session.isOpen());
485         session.onRangingStarted(PARAMS);
486         assertTrue(session.isOpen());
487 
488         // Verify that the onReportReceived callback was invoked
489         RangingReport report = UwbTestUtils.getRangingReports(1);
490         session.onRangingResult(report);
491         verify(callback, times(1)).onReportReceived(report);
492     }
493 
494     @Test
testOnRangingResult_OnReportReceivedNotCalledWhenNotOpen()495     public void testOnRangingResult_OnReportReceivedNotCalledWhenNotOpen() {
496         SessionHandle handle = new SessionHandle(HANDLE_ID, ATTRIBUTION_SOURCE, PID);
497         RangingSession.Callback callback = mock(RangingSession.Callback.class);
498         IUwbAdapter adapter = mock(IUwbAdapter.class);
499         RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle);
500 
501         assertFalse(session.isOpen());
502 
503         // Verify that the onReportReceived callback was invoked
504         RangingReport report = UwbTestUtils.getRangingReports(1);
505         session.onRangingResult(report);
506         verify(callback, times(0)).onReportReceived(report);
507     }
508 
509     @Test
testOnRangingRoundsUpdateDtTag()510     public void testOnRangingRoundsUpdateDtTag() throws RemoteException {
511         assumeTrue(SdkLevel.isAtLeastU()); // Test should only run on U+ devices.
512         SessionHandle handle = new SessionHandle(HANDLE_ID, ATTRIBUTION_SOURCE, PID);
513         RangingSession.Callback callback = mock(RangingSession.Callback.class);
514         IUwbAdapter adapter = mock(IUwbAdapter.class);
515         RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle);
516         PersistableBundle params = new PersistableBundle();
517         assertFalse(session.isOpen());
518 
519         session.onRangingOpened();
520         session.onRangingStarted(params);
521         session.updateRangingRoundsDtTag(params);
522 
523         verify(adapter, times(1)).updateRangingRoundsDtTag(handle, params);
524     }
525 
526     @Test
testOnRangingRoundsUpdateDtTagStatus()527     public void testOnRangingRoundsUpdateDtTagStatus() {
528         assumeTrue(SdkLevel.isAtLeastU()); // Test should only run on U+ devices.
529         SessionHandle handle = new SessionHandle(HANDLE_ID, ATTRIBUTION_SOURCE, PID);
530         RangingSession.Callback callback = mock(RangingSession.Callback.class);
531         IUwbAdapter adapter = mock(IUwbAdapter.class);
532         RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle);
533         PersistableBundle params = new PersistableBundle();
534         assertFalse(session.isOpen());
535 
536         session.onRangingOpened();
537         session.onRangingRoundsUpdateDtTagStatus(params);
538 
539         verify(callback, times(1)).onRangingRoundsUpdateDtTagStatus(params);
540     }
541 
542     @Test
testQueryMaxDataSizeBytes()543     public void testQueryMaxDataSizeBytes() throws RemoteException {
544         assumeTrue(SdkLevel.isAtLeastU()); // Test should only run on U+ devices.
545         SessionHandle handle = new SessionHandle(HANDLE_ID, ATTRIBUTION_SOURCE, PID);
546         RangingSession.Callback callback = mock(RangingSession.Callback.class);
547         IUwbAdapter adapter = mock(IUwbAdapter.class);
548         RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle);
549 
550         when(adapter.queryMaxDataSizeBytes(handle)).thenReturn(MAX_DATA_SIZE);
551 
552         // Confirm that queryMaxDataSizeBytes() throws an IllegalStateException when the ranging
553         // session is not open.
554         assertFalse(session.isOpen());
555         verifyThrowIllegalState(() -> session.queryMaxDataSizeBytes());
556 
557         // Confirm that queryMaxDataSizeBytes() returns a value when the ranging session has been
558         // opened.
559         session.onRangingOpened();
560         assertEquals(session.queryMaxDataSizeBytes(), MAX_DATA_SIZE);
561 
562         // Confirm that queryMaxDataSizeBytes() returns a value when the ranging session has been
563         // started.
564         session.onRangingStarted(PARAMS);
565         assertEquals(session.queryMaxDataSizeBytes(), MAX_DATA_SIZE);
566 
567         // Confirm that queryMaxDataSizeBytes() still returns a value, when the ranging session
568         // was stopped.
569         session.onRangingStopped(REASON, PARAMS);
570         assertEquals(session.queryMaxDataSizeBytes(), MAX_DATA_SIZE);
571 
572         // Confirm that queryMaxDataSizeBytes() throws an IllegalStateException when the ranging
573         // session has now been closed.
574         session.onRangingClosed(REASON, PARAMS);
575         verifyThrowIllegalState(() -> session.queryMaxDataSizeBytes());
576     }
577 
578     @Test
testSetHybridSessionControllerConfiguration_NotOpenSession_ThrowsException()579     public void testSetHybridSessionControllerConfiguration_NotOpenSession_ThrowsException()
580             throws RemoteException {
581         assumeTrue(SdkLevel.isAtLeastV()); // Test should only run on V+ devices.
582         // Mocking necessary objects and behaviors
583         SessionHandle sessionHandle = new SessionHandle(HANDLE_ID, ATTRIBUTION_SOURCE, PID);
584         RangingSession.Callback callback = mock(RangingSession.Callback.class);
585         IUwbAdapter adapter = mock(IUwbAdapter.class);
586         RangingSession rangingSession = new RangingSession(EXECUTOR, callback, adapter,
587                 sessionHandle);
588         assertFalse(rangingSession.isOpen());
589 
590         // Verify that an IllegalStateException is thrown when attempting to set the hybrid session
591         // controller configuration while the session is not open.
592         verifyThrowIllegalState(() ->
593                 rangingSession.setHybridSessionControllerConfiguration(PARAMS));
594     }
595 
596     @Test
testSetHybridSessionControllerConfiguration_OpenSession_Success()597     public void testSetHybridSessionControllerConfiguration_OpenSession_Success()
598             throws RemoteException {
599         assumeTrue(SdkLevel.isAtLeastV()); // Test should only run on V+ devices.
600         // Mocking necessary objects and behaviors
601         SessionHandle sessionHandle = new SessionHandle(HANDLE_ID, ATTRIBUTION_SOURCE, PID);
602         RangingSession.Callback callback = mock(RangingSession.Callback.class);
603         IUwbAdapter adapter = mock(IUwbAdapter.class);
604         RangingSession rangingSession = new RangingSession(EXECUTOR, callback, adapter,
605                 sessionHandle);
606         rangingSession.onRangingOpened();
607 
608         // Invoke the method being tested
609         rangingSession.setHybridSessionControllerConfiguration(PARAMS);
610 
611         // Verify that the adapter's method setHybridSessionControllerConfiguration() is called once
612         // with the correct parameters.
613         verify(adapter).setHybridSessionControllerConfiguration(sessionHandle, PARAMS);
614 
615         // Simulate the session being closed
616         rangingSession.onRangingClosed(REASON, PARAMS);
617 
618         // Verify that an IllegalStateException is thrown when attempting to set the configuration
619         // after the session is closed.
620         verifyThrowIllegalState(() ->
621                 rangingSession.setHybridSessionControllerConfiguration(PARAMS));
622     }
623 
624     @Test
testOnHybridSessionControllerConfigured_WhenSessionOpened_CallbackMethodCalled()625     public void testOnHybridSessionControllerConfigured_WhenSessionOpened_CallbackMethodCalled()
626             throws RemoteException {
627         assumeTrue(SdkLevel.isAtLeastV()); // Test should only run on V+ devices.
628         // Mocking necessary objects and behaviors
629         SessionHandle sessionHandle = new SessionHandle(HANDLE_ID, ATTRIBUTION_SOURCE, PID);
630         RangingSession.Callback callback = mock(RangingSession.Callback.class);
631         IUwbAdapter adapter = mock(IUwbAdapter.class);
632         RangingSession rangingSession = new RangingSession(EXECUTOR, callback, adapter,
633                 sessionHandle);
634 
635         // Simulate the session opening
636         rangingSession.onRangingOpened();
637         rangingSession.onHybridSessionControllerConfigured(PARAMS);
638 
639         // Verify that the callback method onHybridSessionControllerConfigured() is called once
640         // with the correct parameters.
641         verify(callback).onHybridSessionControllerConfigured(PARAMS);
642     }
643 
644     @Test
645     public void
testOnHybridSessionControllerConfigurationFailed_WhenSessionOpenedCallbackCalled()646             testOnHybridSessionControllerConfigurationFailed_WhenSessionOpenedCallbackCalled()
647             throws RemoteException {
648         assumeTrue(SdkLevel.isAtLeastV()); // Test should only run on V+ devices.
649         // Mocking necessary objects and behaviors
650         SessionHandle sessionHandle = new SessionHandle(HANDLE_ID, ATTRIBUTION_SOURCE, PID);
651         RangingSession.Callback callback = mock(RangingSession.Callback.class);
652         IUwbAdapter adapter = mock(IUwbAdapter.class);
653         RangingSession rangingSession = new RangingSession(EXECUTOR, callback, adapter,
654                 sessionHandle);
655 
656         // Simulate the session opening
657         rangingSession.onRangingOpened();
658         rangingSession.onHybridSessionControllerConfigurationFailed(REASON, PARAMS);
659 
660         // Verify that the callback method onHybridSessionControllerConfigurationFailed() is
661         // called once with the correct parameters.
662         verify(callback).onHybridSessionControllerConfigurationFailed(REASON, PARAMS);
663     }
664 
665     @Test
testSetHybridSessionControleeConfiguration_WhenSessionNotOpen_ThrowsException()666     public void testSetHybridSessionControleeConfiguration_WhenSessionNotOpen_ThrowsException()
667             throws RemoteException {
668         assumeTrue(SdkLevel.isAtLeastV()); // Test should only run on V+ devices.
669         // Mocking necessary objects and behaviors
670         SessionHandle sessionHandle = new SessionHandle(HANDLE_ID, ATTRIBUTION_SOURCE, PID);
671         RangingSession.Callback callback = mock(RangingSession.Callback.class);
672         IUwbAdapter adapter = mock(IUwbAdapter.class);
673         RangingSession rangingSession = new RangingSession(EXECUTOR, callback, adapter,
674                 sessionHandle);
675         assertFalse(rangingSession.isOpen());
676 
677         // Verify that an IllegalStateException is thrown when attempting to set the hybrid session
678         // controlee configuration while the session is not open.
679         verifyThrowIllegalState(() -> rangingSession.setHybridSessionControleeConfiguration(
680                 PARAMS));
681     }
682 
683     @Test
testSetHybridSessionControleeConfiguration_OpenSession_Success()684     public void testSetHybridSessionControleeConfiguration_OpenSession_Success()
685             throws RemoteException {
686         assumeTrue(SdkLevel.isAtLeastV()); // Test should only run on V+ devices.
687         // Mocking necessary objects and behaviors
688         SessionHandle sessionHandle = new SessionHandle(HANDLE_ID, ATTRIBUTION_SOURCE, PID);
689         RangingSession.Callback callback = mock(RangingSession.Callback.class);
690         IUwbAdapter adapter = mock(IUwbAdapter.class);
691         RangingSession rangingSession = new RangingSession(EXECUTOR, callback, adapter,
692                 sessionHandle);
693         rangingSession.onRangingOpened();
694 
695         // Invoke the method being tested
696         rangingSession.setHybridSessionControleeConfiguration(PARAMS);
697 
698         // Verify that the adapter's method setHybridSessionControleeConfiguration() is called once
699         // with the correct parameters.
700         verify(adapter).setHybridSessionControleeConfiguration(sessionHandle, PARAMS);
701 
702         // Simulate the session being closed
703         rangingSession.onRangingClosed(REASON, PARAMS);
704 
705         // Verify that an IllegalStateException is thrown when attempting to set the configuration
706         // after the session is closed.
707         verifyThrowIllegalState(() ->
708                 rangingSession.setHybridSessionControleeConfiguration(PARAMS));
709     }
710 
711     @Test
testOnHybridSessionControleeConfigured_WhenSessionOpened_CallbackMethodCalled()712     public void testOnHybridSessionControleeConfigured_WhenSessionOpened_CallbackMethodCalled()
713             throws RemoteException {
714         assumeTrue(SdkLevel.isAtLeastV()); // Test should only run on V+ devices.
715         // Mocking necessary objects and behaviors
716         SessionHandle sessionHandle = new SessionHandle(HANDLE_ID, ATTRIBUTION_SOURCE, PID);
717         RangingSession.Callback callback = mock(RangingSession.Callback.class);
718         IUwbAdapter adapter = mock(IUwbAdapter.class);
719         RangingSession rangingSession = new RangingSession(EXECUTOR, callback, adapter,
720                 sessionHandle);
721 
722         // Simulate the session opening
723         rangingSession.onRangingOpened();
724         rangingSession.onHybridSessionControleeConfigured(PARAMS);
725 
726         // Verify that the callback method onHybridSessionControleeConfigured() is called once
727         // with the correct parameters.
728         verify(callback).onHybridSessionControleeConfigured(PARAMS);
729     }
730 
731     @Test
732     public void
testOnHybridSessionControleeConfigurationFailed_WhenSessionOpenedCallbackCalled()733             testOnHybridSessionControleeConfigurationFailed_WhenSessionOpenedCallbackCalled()
734             throws RemoteException {
735         assumeTrue(SdkLevel.isAtLeastV()); // Test should only run on V+ devices.
736         // Mocking necessary objects and behaviors
737         SessionHandle sessionHandle = new SessionHandle(HANDLE_ID, ATTRIBUTION_SOURCE, PID);
738         RangingSession.Callback callback = mock(RangingSession.Callback.class);
739         IUwbAdapter adapter = mock(IUwbAdapter.class);
740         RangingSession rangingSession = new RangingSession(EXECUTOR, callback, adapter,
741                 sessionHandle);
742 
743         // Simulate the session opening
744         rangingSession.onRangingOpened();
745         rangingSession.onHybridSessionControleeConfigurationFailed(REASON, PARAMS);
746 
747         // Verify that the callback method onHybridSessionControleeConfigurationFailed() is
748         // called once with the correct parameters.
749         verify(callback).onHybridSessionControleeConfigurationFailed(REASON, PARAMS);
750     }
751 
752     @Test
testSetDataTransferPhaseConfig()753     public void testSetDataTransferPhaseConfig() throws RemoteException {
754         assumeTrue(SdkLevel.isAtLeastV()); // Test should only run on V+ devices.
755         SessionHandle handle = new SessionHandle(HANDLE_ID, ATTRIBUTION_SOURCE, PID);
756         RangingSession.Callback callback = mock(RangingSession.Callback.class);
757         IUwbAdapter adapter = mock(IUwbAdapter.class);
758         RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle);
759 
760         // Confirm that setDataTransferPhaseConfig() throws an IllegalStateException
761         // when the ranging session is not open.
762         assertFalse(session.isOpen());
763         verifyThrowIllegalState(() -> session.setDataTransferPhaseConfig(PARAMS));
764 
765         // Confirm that setDataTransferPhaseConfig() returns a value when the ranging
766         // session has been opened.
767         session.onRangingOpened();
768         verifyNoThrowIllegalState(() -> session.setDataTransferPhaseConfig(PARAMS));
769 
770         // Confirm that setDataTransferPhaseConfig() throws an IllegalStateException when the
771         // ranging session has now been closed.
772         session.onRangingClosed(REASON, PARAMS);
773         verifyThrowIllegalState(() -> session.setDataTransferPhaseConfig(PARAMS));
774     }
775 
776     @Test
testPoseUpdate()777     public void testPoseUpdate() throws RemoteException {
778         assumeTrue(SdkLevel.isAtLeastU()); // Test should only run on U+ devices.
779         SessionHandle handle = new SessionHandle(HANDLE_ID, ATTRIBUTION_SOURCE, PID);
780         RangingSession.Callback callback = mock(RangingSession.Callback.class);
781         IUwbAdapter adapter = mock(IUwbAdapter.Stub.class);
782         doNothing().when(adapter).updatePose(any(), any());
783         RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle);
784         assertFalse(session.isOpen());
785 
786         session.onRangingOpened();
787         session.updatePose(PARAMS);
788 
789         ArgumentCaptor<SessionHandle> shCaptor = ArgumentCaptor.forClass(SessionHandle.class);
790         ArgumentCaptor<PersistableBundle> bundleCaptor = ArgumentCaptor.forClass(
791                 PersistableBundle.class);
792 
793         verify(adapter, times(1))
794                 .updatePose(shCaptor.capture(), bundleCaptor.capture());
795         assertEquals(handle.getId(), shCaptor.getValue().getId());
796     }
797 
verifyOpenState(RangingSession session, boolean expected)798     private void verifyOpenState(RangingSession session, boolean expected) {
799         assertEquals(expected, session.isOpen());
800     }
801 
verifyThrowIllegalState(Runnable runnable)802     private void verifyThrowIllegalState(Runnable runnable) {
803         try {
804             runnable.run();
805             fail();
806         } catch (IllegalStateException e) {
807             // Pass
808         }
809     }
810 
verifyNoThrowIllegalState(Runnable runnable)811     private void verifyNoThrowIllegalState(Runnable runnable) {
812         try {
813             runnable.run();
814         } catch (IllegalStateException e) {
815             fail();
816         }
817     }
818 
819     abstract class AdapterAnswer implements Answer {
820         protected RangingSession mSession;
821 
AdapterAnswer(RangingSession session)822         protected AdapterAnswer(RangingSession session) {
823             mSession = session;
824         }
825     }
826 
827     class OpenAnswer extends AdapterAnswer {
OpenAnswer(RangingSession session)828         OpenAnswer(RangingSession session) {
829             super(session);
830         }
831 
832         @Override
answer(InvocationOnMock invocation)833         public Object answer(InvocationOnMock invocation) {
834             PersistableBundle argParams = invocation.getArgument(1);
835             if (argParams != null) {
836                 mSession.onRangingOpened();
837             } else {
838                 mSession.onRangingOpenFailed(REASON_BAD_PARAMETERS, PARAMS);
839             }
840             return null;
841         }
842     }
843 
844     class StartAnswer extends AdapterAnswer {
StartAnswer(RangingSession session)845         StartAnswer(RangingSession session) {
846             super(session);
847         }
848 
849         @Override
answer(InvocationOnMock invocation)850         public Object answer(InvocationOnMock invocation) {
851             PersistableBundle argParams = invocation.getArgument(1);
852             if (argParams != null) {
853                 mSession.onRangingStarted(PARAMS);
854             } else {
855                 mSession.onRangingStartFailed(REASON_BAD_PARAMETERS, PARAMS);
856             }
857             return null;
858         }
859     }
860 
861     class ReconfigureAnswer extends AdapterAnswer {
ReconfigureAnswer(RangingSession session)862         ReconfigureAnswer(RangingSession session) {
863             super(session);
864         }
865 
866         @Override
answer(InvocationOnMock invocation)867         public Object answer(InvocationOnMock invocation) {
868             PersistableBundle argParams = invocation.getArgument(1);
869             if (argParams != null) {
870                 mSession.onRangingReconfigured(PARAMS);
871             } else {
872                 mSession.onRangingReconfigureFailed(REASON_BAD_PARAMETERS, PARAMS);
873             }
874             return null;
875         }
876     }
877 
878     class PauseAnswer extends AdapterAnswer {
PauseAnswer(RangingSession session)879         PauseAnswer(RangingSession session) {
880             super(session);
881         }
882 
883         @Override
answer(InvocationOnMock invocation)884         public Object answer(InvocationOnMock invocation) {
885             PersistableBundle argParams = invocation.getArgument(1);
886             if (argParams != null) {
887                 mSession.onRangingPaused(PARAMS);
888             } else {
889                 mSession.onRangingPauseFailed(REASON_BAD_PARAMETERS, PARAMS);
890             }
891             return null;
892         }
893     }
894 
895     class ResumeAnswer extends AdapterAnswer {
ResumeAnswer(RangingSession session)896         ResumeAnswer(RangingSession session) {
897             super(session);
898         }
899 
900         @Override
answer(InvocationOnMock invocation)901         public Object answer(InvocationOnMock invocation) {
902             PersistableBundle argParams = invocation.getArgument(1);
903             if (argParams != null) {
904                 mSession.onRangingResumed(PARAMS);
905             } else {
906                 mSession.onRangingResumeFailed(REASON_BAD_PARAMETERS, PARAMS);
907             }
908             return null;
909         }
910     }
911 
912     class ControleeAddAnswer extends AdapterAnswer {
ControleeAddAnswer(RangingSession session)913         ControleeAddAnswer(RangingSession session) {
914             super(session);
915         }
916 
917         @Override
answer(InvocationOnMock invocation)918         public Object answer(InvocationOnMock invocation) {
919             PersistableBundle argParams = invocation.getArgument(1);
920             if (argParams != null) {
921                 mSession.onControleeAdded(PARAMS);
922             } else {
923                 mSession.onControleeAddFailed(REASON_BAD_PARAMETERS, PARAMS);
924             }
925             return null;
926         }
927     }
928 
929     class ControleeRemoveAnswer extends AdapterAnswer {
ControleeRemoveAnswer(RangingSession session)930         ControleeRemoveAnswer(RangingSession session) {
931             super(session);
932         }
933 
934         @Override
answer(InvocationOnMock invocation)935         public Object answer(InvocationOnMock invocation) {
936             PersistableBundle argParams = invocation.getArgument(1);
937             if (argParams != null) {
938                 mSession.onControleeRemoved(PARAMS);
939             } else {
940                 mSession.onControleeRemoveFailed(REASON_BAD_PARAMETERS, PARAMS);
941             }
942             return null;
943         }
944     }
945 
946     class DataSendAnswer extends AdapterAnswer {
DataSendAnswer(RangingSession session)947         DataSendAnswer(RangingSession session) {
948             super(session);
949         }
950 
951         @Override
answer(InvocationOnMock invocation)952         public Object answer(InvocationOnMock invocation) {
953             UwbAddress argParams = invocation.getArgument(1);
954             if (argParams != null) {
955                 mSession.onDataSent(UWB_ADDRESS, PARAMS);
956             } else {
957                 mSession.onDataSendFailed(null, REASON_BAD_PARAMETERS, PARAMS);
958             }
959             return null;
960         }
961     }
962 
963     class StopAnswer extends AdapterAnswer {
StopAnswer(RangingSession session)964         StopAnswer(RangingSession session) {
965             super(session);
966         }
967 
968         @Override
answer(InvocationOnMock invocation)969         public Object answer(InvocationOnMock invocation) {
970             mSession.onRangingStopped(REASON, PARAMS);
971             return null;
972         }
973     }
974 
975     class CloseAnswer extends AdapterAnswer {
CloseAnswer(RangingSession session)976         CloseAnswer(RangingSession session) {
977             super(session);
978         }
979 
980         @Override
answer(InvocationOnMock invocation)981         public Object answer(InvocationOnMock invocation) {
982             mSession.onRangingClosed(REASON, PARAMS);
983             return null;
984         }
985     }
986 
987     class DataTransferPhaseConfigAnswer extends AdapterAnswer {
DataTransferPhaseConfigAnswer(RangingSession session)988         DataTransferPhaseConfigAnswer(RangingSession session) {
989             super(session);
990         }
991 
992         @Override
answer(InvocationOnMock invocation)993         public Object answer(InvocationOnMock invocation) {
994             PersistableBundle argParams = invocation.getArgument(1);
995             System.out.println("AKJ: DataTransferPhaseConfigAnswer: argParams = " + argParams);
996             if (argParams != null) {
997                 mSession.onDataTransferPhaseConfigured(PARAMS);
998             } else {
999                 mSession.onDataTransferPhaseConfigFailed(REASON_BAD_PARAMETERS, null);
1000             }
1001             return null;
1002         }
1003     }
1004 
1005     class HybridSessionControllerConfigurationAnswer extends AdapterAnswer {
HybridSessionControllerConfigurationAnswer(RangingSession session)1006         HybridSessionControllerConfigurationAnswer(RangingSession session) {
1007             super(session);
1008         }
1009 
1010         @Override
answer(InvocationOnMock invocation)1011         public Object answer(InvocationOnMock invocation) {
1012             PersistableBundle argParams = invocation.getArgument(1);
1013             if (argParams != null) {
1014                 mSession.onHybridSessionControllerConfigured(PARAMS);
1015             } else {
1016                 mSession.onHybridSessionControllerConfigurationFailed(REASON_BAD_PARAMETERS, null);
1017             }
1018             return null;
1019         }
1020     }
1021 
1022     class HybridSessionControleeConfigurationAnswer extends AdapterAnswer {
HybridSessionControleeConfigurationAnswer(RangingSession session)1023         HybridSessionControleeConfigurationAnswer(RangingSession session) {
1024             super(session);
1025         }
1026 
1027         @Override
answer(InvocationOnMock invocation)1028         public Object answer(InvocationOnMock invocation) {
1029             PersistableBundle argParams = invocation.getArgument(1);
1030             if (argParams != null) {
1031                 mSession.onHybridSessionControleeConfigured(PARAMS);
1032             } else {
1033                 mSession.onHybridSessionControleeConfigurationFailed(REASON_BAD_PARAMETERS, null);
1034             }
1035             return null;
1036         }
1037     }
1038 }
1039