blob: 3d5504ee9dc40b3630dc040e7bded0b936b97669 [file] [log] [blame]
/*
* Copyright (C) 2020 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 com.android.server.uwb;
import static android.uwb.RangingSession.Callback.REASON_LOCAL_REQUEST;
import static com.android.server.uwb.UwbShellCommand.DEFAULT_CCC_OPEN_RANGING_PARAMS;
import static com.android.server.uwb.UwbShellCommand.DEFAULT_FIRA_OPEN_SESSION_PARAMS;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.validateMockitoUsage;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.AttributionSource;
import android.content.Context;
import android.os.Binder;
import android.os.PersistableBundle;
import android.os.Process;
import android.util.Pair;
import android.uwb.IUwbRangingCallbacks;
import android.uwb.RangingMeasurement;
import android.uwb.RangingReport;
import android.uwb.SessionHandle;
import android.uwb.UwbManager;
import android.uwb.UwbTestUtils;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.google.uwb.support.base.Params;
import com.google.uwb.support.ccc.CccOpenRangingParams;
import com.google.uwb.support.ccc.CccStartRangingParams;
import com.google.uwb.support.fira.FiraOpenSessionParams;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.io.FileDescriptor;
/**
* Unit tests for {@link com.android.server.uwb.UwbShellCommand}.
*/
@RunWith(AndroidJUnit4.class)
@SmallTest
public class UwbShellCommandTest {
private static final String TEST_PACKAGE = "com.android.test";
@Mock UwbInjector mUwbInjector;
@Mock UwbServiceImpl mUwbService;
@Mock UwbCountryCode mUwbCountryCode;
@Mock Context mContext;
UwbShellCommand mUwbShellCommand;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
when(mUwbInjector.getUwbCountryCode()).thenReturn(mUwbCountryCode);
mUwbShellCommand = new UwbShellCommand(mUwbInjector, mUwbService, mContext);
// by default emulate shell uid.
BinderUtil.setUid(Process.SHELL_UID);
}
@After
public void tearDown() throws Exception {
mUwbShellCommand.reset();
validateMockitoUsage();
}
@Test
public void testStatus() throws Exception {
when(mUwbService.getAdapterState())
.thenReturn(UwbManager.AdapterStateCallback.STATE_ENABLED_ACTIVE);
// unrooted shell.
mUwbShellCommand.exec(
new Binder(), new FileDescriptor(), new FileDescriptor(), new FileDescriptor(),
new String[]{"status"});
verify(mUwbService).getAdapterState();
}
@Test
public void testForceSetCountryCode() throws Exception {
// not allowed for unrooted shell.
mUwbShellCommand.exec(
new Binder(), new FileDescriptor(), new FileDescriptor(), new FileDescriptor(),
new String[]{"force-country-code", "enabled", "US"});
verify(mUwbCountryCode, never()).setOverrideCountryCode(any());
assertThat(mUwbShellCommand.getErrPrintWriter().toString().isEmpty()).isFalse();
BinderUtil.setUid(Process.ROOT_UID);
// rooted shell.
mUwbShellCommand.exec(
new Binder(), new FileDescriptor(), new FileDescriptor(), new FileDescriptor(),
new String[]{"force-country-code", "enabled", "US"});
verify(mUwbCountryCode).setOverrideCountryCode(any());
}
@Test
public void testForceClearCountryCode() throws Exception {
// not allowed for unrooted shell.
mUwbShellCommand.exec(
new Binder(), new FileDescriptor(), new FileDescriptor(), new FileDescriptor(),
new String[]{"force-country-code", "disabled"});
verify(mUwbCountryCode, never()).setOverrideCountryCode(any());
assertThat(mUwbShellCommand.getErrPrintWriter().toString().isEmpty()).isFalse();
BinderUtil.setUid(Process.ROOT_UID);
// rooted shell.
mUwbShellCommand.exec(
new Binder(), new FileDescriptor(), new FileDescriptor(), new FileDescriptor(),
new String[]{"force-country-code", "disabled"});
verify(mUwbCountryCode).clearOverrideCountryCode();
}
@Test
public void testGetCountryCode() throws Exception {
mUwbShellCommand.exec(
new Binder(), new FileDescriptor(), new FileDescriptor(), new FileDescriptor(),
new String[]{"get-country-code"});
verify(mUwbCountryCode).getCountryCode();
}
@Test
public void testEnableUwb() throws Exception {
mUwbShellCommand.exec(
new Binder(), new FileDescriptor(), new FileDescriptor(), new FileDescriptor(),
new String[]{"enable-uwb"});
verify(mUwbService).setEnabled(true);
}
@Test
public void testDisableUwb() throws Exception {
mUwbShellCommand.exec(
new Binder(), new FileDescriptor(), new FileDescriptor(), new FileDescriptor(),
new String[]{"disable-uwb"});
verify(mUwbService).setEnabled(false);
}
private static class MutableCb {
@Nullable public IUwbRangingCallbacks cb;
}
private Pair<IUwbRangingCallbacks, SessionHandle> triggerAndVerifyRangingStart(
String[] rangingStartCmd, @NonNull Params openRangingParams) throws Exception {
return triggerAndVerifyRangingStart(rangingStartCmd, openRangingParams, null);
}
private Pair<IUwbRangingCallbacks, SessionHandle> triggerAndVerifyRangingStart(
String[] rangingStartCmd, @NonNull Params openRangingParams, @Nullable Params
startRangingParams) throws Exception {
final MutableCb cbCaptor = new MutableCb();
doAnswer(invocation -> {
cbCaptor.cb = invocation.getArgument(2);
cbCaptor.cb.onRangingOpened(invocation.getArgument(1));
return true;
}).when(mUwbService).openRanging(any(), any(), any(), any(), any());
doAnswer(invocation -> {
cbCaptor.cb.onRangingStarted(invocation.getArgument(0), new PersistableBundle());
return true;
}).when(mUwbService).startRanging(any(), any());
mUwbShellCommand.exec(
new Binder(), new FileDescriptor(), new FileDescriptor(), new FileDescriptor(),
rangingStartCmd);
ArgumentCaptor<SessionHandle> sessionHandleCaptor =
ArgumentCaptor.forClass(SessionHandle.class);
ArgumentCaptor<PersistableBundle> paramsCaptor =
ArgumentCaptor.forClass(PersistableBundle.class);
verify(mUwbService).openRanging(
eq(new AttributionSource.Builder(Process.SHELL_UID)
.setPackageName(UwbShellCommand.SHELL_PACKAGE_NAME)
.build()),
sessionHandleCaptor.capture(), any(), paramsCaptor.capture(), any());
// PersistableBundle does not implement equals, so use toString equals.
assertThat(paramsCaptor.getValue().toString())
.isEqualTo(openRangingParams.toBundle().toString());
verify(mUwbService).startRanging(
eq(sessionHandleCaptor.getValue()), paramsCaptor.capture());
assertThat(paramsCaptor.getValue().toString())
.isEqualTo(startRangingParams != null
? startRangingParams.toBundle().toString()
: new PersistableBundle().toString());
return Pair.create(cbCaptor.cb, sessionHandleCaptor.getValue());
}
private void triggerAndVerifyRangingStop(
String[] rangingStopCmd, IUwbRangingCallbacks cb, SessionHandle sessionHandle)
throws Exception {
doAnswer(invocation -> {
cb.onRangingStopped(sessionHandle, REASON_LOCAL_REQUEST, new PersistableBundle());
return true;
}).when(mUwbService).stopRanging(any());
doAnswer(invocation -> {
cb.onRangingClosed(
sessionHandle, REASON_LOCAL_REQUEST,
new PersistableBundle());
return true;
}).when(mUwbService).closeRanging(any());
mUwbShellCommand.exec(
new Binder(), new FileDescriptor(), new FileDescriptor(), new FileDescriptor(),
rangingStopCmd);
verify(mUwbService).stopRanging(sessionHandle);
verify(mUwbService).closeRanging(sessionHandle);
}
private CccStartRangingParams getCccStartRangingParamsFromOpenRangingParams(
@NonNull CccOpenRangingParams openRangingParams) {
return new CccStartRangingParams.Builder()
.setSessionId(openRangingParams.getSessionId())
.setRanMultiplier(openRangingParams.getRanMultiplier())
.build();
}
@Test
public void testStartFiraRanging() throws Exception {
triggerAndVerifyRangingStart(
new String[]{"start-fira-ranging-session"},
DEFAULT_FIRA_OPEN_SESSION_PARAMS.build());
}
@Test
public void testStartFiraRangingWithNonDefaultParams() throws Exception {
FiraOpenSessionParams.Builder openSessionParamsBuilder =
new FiraOpenSessionParams.Builder(DEFAULT_FIRA_OPEN_SESSION_PARAMS);
openSessionParamsBuilder.setSessionId(5);
triggerAndVerifyRangingStart(
new String[]{"start-fira-ranging-session", "-i", "5"},
openSessionParamsBuilder.build());
}
@Test
public void testStartFiraRangingWithBothInterleavingAndAoaResultReq() throws Exception {
// Both AOA result req and interleaving are not allowed in the same command.
assertThat(mUwbShellCommand.exec(
new Binder(), new FileDescriptor(), new FileDescriptor(), new FileDescriptor(),
new String[]{"start-fira-ranging-session", "-i", "5", "-z", "4,5,6", "-e",
"enabled"})).isEqualTo(-1);
}
private RangingMeasurement getRangingMeasurement() {
return new RangingMeasurement.Builder()
.setStatus(RangingMeasurement.RANGING_STATUS_SUCCESS)
.setElapsedRealtimeNanos(67)
.setDistanceMeasurement(UwbTestUtils.getDistanceMeasurement())
.setAngleOfArrivalMeasurement(UwbTestUtils.getAngleOfArrivalMeasurement())
.setRemoteDeviceAddress(UwbTestUtils.getUwbAddress(true))
.build();
}
@Test
public void testRangingReportFiraRanging() throws Exception {
Pair<IUwbRangingCallbacks, SessionHandle> cbAndSessionHandle =
triggerAndVerifyRangingStart(
new String[]{"start-fira-ranging-session"},
DEFAULT_FIRA_OPEN_SESSION_PARAMS.build());
int sessionId = DEFAULT_FIRA_OPEN_SESSION_PARAMS.build().getSessionId();
cbAndSessionHandle.first.onRangingResult(
cbAndSessionHandle.second,
new RangingReport.Builder().addMeasurement(getRangingMeasurement()).build());
mUwbShellCommand.exec(
new Binder(), new FileDescriptor(), new FileDescriptor(), new FileDescriptor(),
new String[]{"get-ranging-session-reports", String.valueOf(sessionId)});
}
@Test
public void testRangingReportAllFiraRanging() throws Exception {
Pair<IUwbRangingCallbacks, SessionHandle> cbAndSessionHandle =
triggerAndVerifyRangingStart(
new String[]{"start-fira-ranging-session"},
DEFAULT_FIRA_OPEN_SESSION_PARAMS.build());
cbAndSessionHandle.first.onRangingResult(
cbAndSessionHandle.second,
new RangingReport.Builder().addMeasurement(getRangingMeasurement()).build());
mUwbShellCommand.exec(
new Binder(), new FileDescriptor(), new FileDescriptor(), new FileDescriptor(),
new String[]{"get-all-ranging-session-reports"});
}
@Test
public void testStopFiraRanging() throws Exception {
Pair<IUwbRangingCallbacks, SessionHandle> cbAndSessionHandle =
triggerAndVerifyRangingStart(
new String[]{"start-fira-ranging-session"},
DEFAULT_FIRA_OPEN_SESSION_PARAMS.build());
int sessionId = DEFAULT_FIRA_OPEN_SESSION_PARAMS.build().getSessionId();
triggerAndVerifyRangingStop(
new String[]{"stop-ranging-session", String.valueOf(sessionId)},
cbAndSessionHandle.first, cbAndSessionHandle.second);
}
@Test
public void testStartCccRanging() throws Exception {
CccOpenRangingParams openSessionParams = DEFAULT_CCC_OPEN_RANGING_PARAMS.build();
triggerAndVerifyRangingStart(
new String[]{"start-ccc-ranging-session"},
openSessionParams,
getCccStartRangingParamsFromOpenRangingParams(openSessionParams));
}
@Test
public void testStartCccRangingWithNonDefaultParams() throws Exception {
CccOpenRangingParams.Builder openSessionParamsBuilder =
new CccOpenRangingParams.Builder(DEFAULT_CCC_OPEN_RANGING_PARAMS);
openSessionParamsBuilder.setSessionId(5);
CccOpenRangingParams openSessionParams = openSessionParamsBuilder.build();
triggerAndVerifyRangingStart(
new String[]{"start-ccc-ranging-session", "-i", "5"},
openSessionParams,
getCccStartRangingParamsFromOpenRangingParams(openSessionParams));
}
@Test
public void testStopCccRanging() throws Exception {
CccOpenRangingParams openSessionParams = DEFAULT_CCC_OPEN_RANGING_PARAMS.build();
Pair<IUwbRangingCallbacks, SessionHandle> cbAndSessionHandle =
triggerAndVerifyRangingStart(
new String[]{"start-ccc-ranging-session"},
openSessionParams,
getCccStartRangingParamsFromOpenRangingParams(openSessionParams));
int sessionId = openSessionParams.getSessionId();
triggerAndVerifyRangingStop(
new String[]{"stop-ranging-session", String.valueOf(sessionId)},
cbAndSessionHandle.first, cbAndSessionHandle.second);
}
@Test
public void testStopAllRanging() throws Exception {
CccOpenRangingParams openSessionParams = DEFAULT_CCC_OPEN_RANGING_PARAMS.build();
Pair<IUwbRangingCallbacks, SessionHandle> cbAndSessionHandle =
triggerAndVerifyRangingStart(
new String[]{"start-ccc-ranging-session"},
openSessionParams,
getCccStartRangingParamsFromOpenRangingParams(openSessionParams));
triggerAndVerifyRangingStop(
new String[]{"stop-all-ranging-sessions"},
cbAndSessionHandle.first, cbAndSessionHandle.second);
}
}