• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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 com.android.server.uwb;
18 
19 import static android.uwb.RangingSession.Callback.REASON_LOCAL_REQUEST;
20 
21 import static com.android.server.uwb.UwbShellCommand.DEFAULT_CCC_OPEN_RANGING_PARAMS;
22 import static com.android.server.uwb.UwbShellCommand.DEFAULT_FIRA_OPEN_SESSION_PARAMS;
23 
24 import static com.google.common.truth.Truth.assertThat;
25 import static com.google.uwb.support.fira.FiraParams.RangeDataNtfConfigCapabilityFlag.HAS_RANGE_DATA_NTF_CONFIG_DISABLE;
26 import static com.google.uwb.support.fira.FiraParams.RangeDataNtfConfigCapabilityFlag.HAS_RANGE_DATA_NTF_CONFIG_ENABLE;
27 
28 import static org.mockito.ArgumentMatchers.anyInt;
29 import static org.mockito.Mockito.any;
30 import static org.mockito.Mockito.clearInvocations;
31 import static org.mockito.Mockito.doAnswer;
32 import static org.mockito.Mockito.eq;
33 import static org.mockito.Mockito.mock;
34 import static org.mockito.Mockito.never;
35 import static org.mockito.Mockito.validateMockitoUsage;
36 import static org.mockito.Mockito.verify;
37 import static org.mockito.Mockito.when;
38 
39 import android.annotation.NonNull;
40 import android.annotation.Nullable;
41 import android.content.AttributionSource;
42 import android.content.Context;
43 import android.os.Binder;
44 import android.os.PersistableBundle;
45 import android.os.Process;
46 import android.util.Pair;
47 import android.uwb.IUwbRangingCallbacks;
48 import android.uwb.RangingMeasurement;
49 import android.uwb.RangingReport;
50 import android.uwb.SessionHandle;
51 import android.uwb.UwbManager;
52 import android.uwb.UwbTestUtils;
53 
54 import androidx.test.filters.SmallTest;
55 import androidx.test.runner.AndroidJUnit4;
56 
57 import com.google.uwb.support.base.Params;
58 import com.google.uwb.support.ccc.CccOpenRangingParams;
59 import com.google.uwb.support.ccc.CccSpecificationParams;
60 import com.google.uwb.support.ccc.CccStartRangingParams;
61 import com.google.uwb.support.fira.FiraOpenSessionParams;
62 import com.google.uwb.support.fira.FiraSpecificationParams;
63 import com.google.uwb.support.generic.GenericSpecificationParams;
64 
65 import org.junit.After;
66 import org.junit.Before;
67 import org.junit.Test;
68 import org.junit.runner.RunWith;
69 import org.mockito.ArgumentCaptor;
70 import org.mockito.Mock;
71 import org.mockito.MockitoAnnotations;
72 
73 import java.io.FileDescriptor;
74 import java.util.EnumSet;
75 import java.util.List;
76 import java.util.concurrent.FutureTask;
77 
78 /**
79  * Unit tests for {@link com.android.server.uwb.UwbShellCommand}.
80  */
81 @RunWith(AndroidJUnit4.class)
82 @SmallTest
83 public class UwbShellCommandTest {
84     private static final String TEST_PACKAGE = "com.android.test";
85 
86     @Mock UwbInjector mUwbInjector;
87     @Mock UwbServiceImpl mUwbService;
88     @Mock UwbCountryCode mUwbCountryCode;
89     @Mock Context mContext;
90     @Mock UwbServiceCore mUwbServiceCore;
91 
92     UwbShellCommand mUwbShellCommand;
93 
94     @Before
setUp()95     public void setUp() throws Exception {
96         MockitoAnnotations.initMocks(this);
97 
98         when(mUwbInjector.getUwbCountryCode()).thenReturn(mUwbCountryCode);
99         when(mUwbInjector.getUwbServiceCore()).thenReturn(mUwbServiceCore);
100         doAnswer(invocation -> {
101             FutureTask t = invocation.getArgument(0);
102             t.run();
103             return t.get();
104         }).when(mUwbInjector).runTaskOnSingleThreadExecutor(any(FutureTask.class), anyInt());
105         GenericSpecificationParams params = new GenericSpecificationParams.Builder()
106                 .setCccSpecificationParams(mock(CccSpecificationParams.class))
107                 .setFiraSpecificationParams(
108                         new FiraSpecificationParams.Builder()
109                                 .setSupportedChannels(List.of(9))
110                                 .setRangeDataNtfConfigCapabilities(
111                                         EnumSet.of(
112                                                 HAS_RANGE_DATA_NTF_CONFIG_DISABLE,
113                                                 HAS_RANGE_DATA_NTF_CONFIG_ENABLE))
114                                 .build())
115                 .build();
116         when(mUwbServiceCore.getCachedSpecificationParams(any())).thenReturn(params);
117 
118         mUwbShellCommand = new UwbShellCommand(mUwbInjector, mUwbService, mContext);
119 
120         // by default emulate shell uid.
121         BinderUtil.setUid(Process.SHELL_UID);
122     }
123 
124     @After
tearDown()125     public void tearDown() throws Exception {
126         mUwbShellCommand.reset();
127         validateMockitoUsage();
128     }
129 
130     @Test
testStatus()131     public void testStatus() throws Exception {
132         when(mUwbService.getAdapterState())
133                 .thenReturn(UwbManager.AdapterStateCallback.STATE_ENABLED_ACTIVE);
134 
135         // unrooted shell.
136         mUwbShellCommand.exec(
137                 new Binder(), new FileDescriptor(), new FileDescriptor(), new FileDescriptor(),
138                 new String[]{"status"});
139         verify(mUwbService).getAdapterState();
140     }
141 
142     @Test
testForceSetCountryCode()143     public void testForceSetCountryCode() throws Exception {
144         // not allowed for unrooted shell.
145         mUwbShellCommand.exec(
146                 new Binder(), new FileDescriptor(), new FileDescriptor(), new FileDescriptor(),
147                 new String[]{"force-country-code", "enabled", "US"});
148         verify(mUwbCountryCode, never()).setOverrideCountryCode(any());
149         assertThat(mUwbShellCommand.getErrPrintWriter().toString().isEmpty()).isFalse();
150 
151         BinderUtil.setUid(Process.ROOT_UID);
152 
153         // rooted shell.
154         mUwbShellCommand.exec(
155                 new Binder(), new FileDescriptor(), new FileDescriptor(), new FileDescriptor(),
156                 new String[]{"force-country-code", "enabled", "US"});
157         verify(mUwbCountryCode).setOverrideCountryCode(any());
158 
159     }
160 
161     @Test
testForceClearCountryCode()162     public void testForceClearCountryCode() throws Exception {
163         // not allowed for unrooted shell.
164         mUwbShellCommand.exec(
165                 new Binder(), new FileDescriptor(), new FileDescriptor(), new FileDescriptor(),
166                 new String[]{"force-country-code", "disabled"});
167         verify(mUwbCountryCode, never()).setOverrideCountryCode(any());
168         assertThat(mUwbShellCommand.getErrPrintWriter().toString().isEmpty()).isFalse();
169 
170         BinderUtil.setUid(Process.ROOT_UID);
171 
172         // rooted shell.
173         mUwbShellCommand.exec(
174                 new Binder(), new FileDescriptor(), new FileDescriptor(), new FileDescriptor(),
175                 new String[]{"force-country-code", "disabled"});
176         verify(mUwbCountryCode).clearOverrideCountryCode();
177     }
178 
179     @Test
testGetCountryCode()180     public void testGetCountryCode() throws Exception {
181         mUwbShellCommand.exec(
182                 new Binder(), new FileDescriptor(), new FileDescriptor(), new FileDescriptor(),
183                 new String[]{"get-country-code"});
184         verify(mUwbCountryCode).getCountryCode();
185     }
186 
187     @Test
testEnableUwb()188     public void testEnableUwb() throws Exception {
189         mUwbShellCommand.exec(
190                 new Binder(), new FileDescriptor(), new FileDescriptor(), new FileDescriptor(),
191                 new String[]{"enable-uwb"});
192         verify(mUwbService).setEnabled(true);
193     }
194 
195     @Test
testDisableUwb()196     public void testDisableUwb() throws Exception {
197         mUwbShellCommand.exec(
198                 new Binder(), new FileDescriptor(), new FileDescriptor(), new FileDescriptor(),
199                 new String[]{"disable-uwb"});
200         verify(mUwbService).setEnabled(false);
201     }
202 
203     private static class MutableCb {
204         @Nullable public IUwbRangingCallbacks cb;
205     }
206 
triggerAndVerifyRangingStart( String[] rangingStartCmd, @NonNull Params openRangingParams)207     private Pair<IUwbRangingCallbacks, SessionHandle> triggerAndVerifyRangingStart(
208             String[] rangingStartCmd, @NonNull Params openRangingParams) throws Exception {
209         return triggerAndVerifyRangingStart(rangingStartCmd, openRangingParams, null);
210     }
211 
triggerAndVerifyRangingStart( String[] rangingStartCmd, @NonNull Params openRangingParams, @Nullable Params startRangingParams)212     private Pair<IUwbRangingCallbacks, SessionHandle> triggerAndVerifyRangingStart(
213             String[] rangingStartCmd, @NonNull Params openRangingParams, @Nullable Params
214             startRangingParams) throws Exception {
215         final MutableCb cbCaptor = new MutableCb();
216         doAnswer(invocation -> {
217             cbCaptor.cb = invocation.getArgument(2);
218             cbCaptor.cb.onRangingOpened(invocation.getArgument(1));
219             return true;
220         }).when(mUwbService).openRanging(any(), any(), any(), any(), any());
221         doAnswer(invocation -> {
222             cbCaptor.cb.onRangingStarted(invocation.getArgument(0), new PersistableBundle());
223             return true;
224         }).when(mUwbService).startRanging(any(), any());
225 
226         mUwbShellCommand.exec(
227                 new Binder(), new FileDescriptor(), new FileDescriptor(), new FileDescriptor(),
228                 rangingStartCmd);
229 
230         ArgumentCaptor<SessionHandle> sessionHandleCaptor =
231                 ArgumentCaptor.forClass(SessionHandle.class);
232         ArgumentCaptor<PersistableBundle> paramsCaptor =
233                 ArgumentCaptor.forClass(PersistableBundle.class);
234 
235         verify(mUwbService).openRanging(
236                 eq(new AttributionSource.Builder(Process.SHELL_UID)
237                         .setPackageName(UwbShellCommand.SHELL_PACKAGE_NAME)
238                         .build()),
239                 sessionHandleCaptor.capture(), any(), paramsCaptor.capture(), any());
240         // PersistableBundle does not implement equals, so use toString equals.
241         assertThat(paramsCaptor.getValue().toString())
242                 .isEqualTo(openRangingParams.toBundle().toString());
243 
244         verify(mUwbService).startRanging(
245                 eq(sessionHandleCaptor.getValue()), paramsCaptor.capture());
246         assertThat(paramsCaptor.getValue().toString())
247                 .isEqualTo(startRangingParams != null
248                         ? startRangingParams.toBundle().toString()
249                         : new PersistableBundle().toString());
250 
251         return Pair.create(cbCaptor.cb, sessionHandleCaptor.getValue());
252     }
253 
triggerAndVerifyRangingStop( String[] rangingStopCmd, IUwbRangingCallbacks cb, SessionHandle sessionHandle)254     private void triggerAndVerifyRangingStop(
255             String[] rangingStopCmd, IUwbRangingCallbacks cb, SessionHandle sessionHandle)
256             throws Exception {
257         doAnswer(invocation -> {
258             cb.onRangingStopped(sessionHandle, REASON_LOCAL_REQUEST, new PersistableBundle());
259             return true;
260         }).when(mUwbService).stopRanging(any());
261         doAnswer(invocation -> {
262             cb.onRangingClosed(
263                     sessionHandle, REASON_LOCAL_REQUEST,
264                     new PersistableBundle());
265             return true;
266         }).when(mUwbService).closeRanging(any());
267 
268         mUwbShellCommand.exec(
269                 new Binder(), new FileDescriptor(), new FileDescriptor(), new FileDescriptor(),
270                 rangingStopCmd);
271 
272         verify(mUwbService).stopRanging(sessionHandle);
273         verify(mUwbService).closeRanging(sessionHandle);
274     }
275 
getCccStartRangingParamsFromOpenRangingParams( @onNull CccOpenRangingParams openRangingParams)276     private CccStartRangingParams getCccStartRangingParamsFromOpenRangingParams(
277             @NonNull CccOpenRangingParams openRangingParams) {
278         return new CccStartRangingParams.Builder()
279                 .setSessionId(openRangingParams.getSessionId())
280                 .setRanMultiplier(openRangingParams.getRanMultiplier())
281                 .build();
282     }
283 
284     @Test
testStartFiraRanging()285     public void testStartFiraRanging() throws Exception {
286         triggerAndVerifyRangingStart(
287                 new String[]{"start-fira-ranging-session"},
288                 DEFAULT_FIRA_OPEN_SESSION_PARAMS.build());
289     }
290 
291     @Test
testStartFiraRangingUsesUniqueSessionHandle()292     public void testStartFiraRangingUsesUniqueSessionHandle() throws Exception {
293         FiraOpenSessionParams.Builder openSessionParamsBuilder =
294                 new FiraOpenSessionParams.Builder(DEFAULT_FIRA_OPEN_SESSION_PARAMS);
295 
296         openSessionParamsBuilder.setSessionId(1);
297         Pair<IUwbRangingCallbacks, SessionHandle> cbAndSessionHandle1 =
298                 triggerAndVerifyRangingStart(
299                         new String[]{"start-fira-ranging-session", "-i", "1"},
300                         openSessionParamsBuilder.build());
301         clearInvocations(mUwbService);
302 
303         openSessionParamsBuilder.setSessionId(2);
304         Pair<IUwbRangingCallbacks, SessionHandle> cbAndSessionHandle2 =
305                 triggerAndVerifyRangingStart(
306                         new String[]{"start-fira-ranging-session", "-i", "2"},
307                         openSessionParamsBuilder.build());
308         assertThat(cbAndSessionHandle1.second).isNotEqualTo(cbAndSessionHandle2.second);
309     }
310 
311     @Test
testStartFiraRangingWithNonDefaultParams()312     public void testStartFiraRangingWithNonDefaultParams() throws Exception {
313         FiraOpenSessionParams.Builder openSessionParamsBuilder =
314                 new FiraOpenSessionParams.Builder(DEFAULT_FIRA_OPEN_SESSION_PARAMS);
315         openSessionParamsBuilder.setSessionId(5);
316         triggerAndVerifyRangingStart(
317                 new String[]{"start-fira-ranging-session", "-i", "5"},
318                 openSessionParamsBuilder.build());
319     }
320 
321     @Test
testStartFiraRangingWithBothInterleavingAndAoaResultReq()322     public void testStartFiraRangingWithBothInterleavingAndAoaResultReq() throws Exception {
323         // Both AOA result req and interleaving are not allowed in the same command.
324         assertThat(mUwbShellCommand.exec(
325                 new Binder(), new FileDescriptor(), new FileDescriptor(), new FileDescriptor(),
326                 new String[]{"start-fira-ranging-session", "-i", "5", "-z", "4,5,6", "-e",
327                         "enabled"})).isEqualTo(-1);
328     }
329 
getRangingMeasurement()330     private RangingMeasurement getRangingMeasurement() {
331         return new RangingMeasurement.Builder()
332                 .setStatus(RangingMeasurement.RANGING_STATUS_SUCCESS)
333                 .setElapsedRealtimeNanos(67)
334                 .setDistanceMeasurement(UwbTestUtils.getDistanceMeasurement())
335                 .setAngleOfArrivalMeasurement(UwbTestUtils.getAngleOfArrivalMeasurement())
336                 .setRemoteDeviceAddress(UwbTestUtils.getUwbAddress(true))
337                 .build();
338     }
339 
340     @Test
testRangingReportFiraRanging()341     public void testRangingReportFiraRanging() throws Exception {
342         Pair<IUwbRangingCallbacks, SessionHandle> cbAndSessionHandle =
343                 triggerAndVerifyRangingStart(
344                         new String[]{"start-fira-ranging-session"},
345                         DEFAULT_FIRA_OPEN_SESSION_PARAMS.build());
346         int sessionId = DEFAULT_FIRA_OPEN_SESSION_PARAMS.build().getSessionId();
347         cbAndSessionHandle.first.onRangingResult(
348                 cbAndSessionHandle.second,
349                 new RangingReport.Builder().addMeasurement(getRangingMeasurement()).build());
350         mUwbShellCommand.exec(
351                 new Binder(), new FileDescriptor(), new FileDescriptor(), new FileDescriptor(),
352                 new String[]{"get-ranging-session-reports", String.valueOf(sessionId)});
353     }
354 
355     @Test
testRangingReportAllFiraRanging()356     public void testRangingReportAllFiraRanging() throws Exception {
357         Pair<IUwbRangingCallbacks, SessionHandle> cbAndSessionHandle =
358                 triggerAndVerifyRangingStart(
359                         new String[]{"start-fira-ranging-session"},
360                         DEFAULT_FIRA_OPEN_SESSION_PARAMS.build());
361         cbAndSessionHandle.first.onRangingResult(
362                 cbAndSessionHandle.second,
363                 new RangingReport.Builder().addMeasurement(getRangingMeasurement()).build());
364         mUwbShellCommand.exec(
365                 new Binder(), new FileDescriptor(), new FileDescriptor(), new FileDescriptor(),
366                 new String[]{"get-all-ranging-session-reports"});
367     }
368 
369     @Test
testStopFiraRanging()370     public void testStopFiraRanging() throws Exception {
371         Pair<IUwbRangingCallbacks, SessionHandle> cbAndSessionHandle =
372                 triggerAndVerifyRangingStart(
373                         new String[]{"start-fira-ranging-session"},
374                         DEFAULT_FIRA_OPEN_SESSION_PARAMS.build());
375         int sessionId = DEFAULT_FIRA_OPEN_SESSION_PARAMS.build().getSessionId();
376         triggerAndVerifyRangingStop(
377                 new String[]{"stop-ranging-session", String.valueOf(sessionId)},
378                 cbAndSessionHandle.first, cbAndSessionHandle.second);
379     }
380 
381     @Test
testStartCccRanging()382     public void testStartCccRanging() throws Exception {
383         CccOpenRangingParams openSessionParams = DEFAULT_CCC_OPEN_RANGING_PARAMS.build();
384         triggerAndVerifyRangingStart(
385                 new String[]{"start-ccc-ranging-session"},
386                 openSessionParams,
387                 getCccStartRangingParamsFromOpenRangingParams(openSessionParams));
388     }
389 
390     @Test
testStartCccRangingWithNonDefaultParams()391     public void testStartCccRangingWithNonDefaultParams() throws Exception {
392         CccOpenRangingParams.Builder openSessionParamsBuilder =
393                 new CccOpenRangingParams.Builder(DEFAULT_CCC_OPEN_RANGING_PARAMS);
394         openSessionParamsBuilder.setSessionId(5);
395         CccOpenRangingParams openSessionParams = openSessionParamsBuilder.build();
396         triggerAndVerifyRangingStart(
397                 new String[]{"start-ccc-ranging-session", "-i", "5"},
398                 openSessionParams,
399                 getCccStartRangingParamsFromOpenRangingParams(openSessionParams));
400     }
401 
402     @Test
testStopCccRanging()403     public void testStopCccRanging() throws Exception {
404         CccOpenRangingParams openSessionParams = DEFAULT_CCC_OPEN_RANGING_PARAMS.build();
405         Pair<IUwbRangingCallbacks, SessionHandle> cbAndSessionHandle =
406                 triggerAndVerifyRangingStart(
407                         new String[]{"start-ccc-ranging-session"},
408                         openSessionParams,
409                         getCccStartRangingParamsFromOpenRangingParams(openSessionParams));
410         int sessionId = openSessionParams.getSessionId();
411         triggerAndVerifyRangingStop(
412                 new String[]{"stop-ranging-session", String.valueOf(sessionId)},
413                 cbAndSessionHandle.first, cbAndSessionHandle.second);
414     }
415 
416     @Test
testStopAllRanging()417     public void testStopAllRanging() throws Exception {
418         CccOpenRangingParams openSessionParams = DEFAULT_CCC_OPEN_RANGING_PARAMS.build();
419         Pair<IUwbRangingCallbacks, SessionHandle> cbAndSessionHandle =
420                 triggerAndVerifyRangingStart(
421                         new String[]{"start-ccc-ranging-session"},
422                         openSessionParams,
423                         getCccStartRangingParamsFromOpenRangingParams(openSessionParams));
424         triggerAndVerifyRangingStop(
425                 new String[]{"stop-all-ranging-sessions"},
426                 cbAndSessionHandle.first, cbAndSessionHandle.second);
427     }
428 }
429