blob: 84902d22be1e32312ea2f869add4a71693d26856 [file] [log] [blame]
/*
* Copyright (C) 2021 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.Manifest.permission.UWB_PRIVILEGED;
import static android.uwb.UwbManager.AdapterStateCallback.STATE_ENABLED_ACTIVE;
import static android.uwb.UwbManager.AdapterStateCallback.STATE_ENABLED_INACTIVE;
import static com.android.server.uwb.UwbServiceImpl.SETTINGS_SATELLITE_MODE_ENABLED;
import static com.android.server.uwb.UwbServiceImpl.SETTINGS_SATELLITE_MODE_RADIOS;
import static com.android.server.uwb.UwbSettingsStore.SETTINGS_FIRST_TOGGLE_DONE;
import static com.android.server.uwb.UwbSettingsStore.SETTINGS_TOGGLE_STATE;
import static com.android.server.uwb.UwbTestUtils.MAX_DATA_SIZE;
import static com.google.common.truth.Truth.assertThat;
import static com.google.uwb.support.fira.FiraParams.PACS_PROFILE_SERVICE_ID;
import static com.google.uwb.support.fira.FiraParams.RANGE_DATA_NTF_CONFIG_ENABLE_PROXIMITY_LEVEL_TRIG;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.AttributionSource;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.database.ContentObserver;
import android.os.Handler;
import android.os.IBinder;
import android.os.PersistableBundle;
import android.os.Process;
import android.os.UserManager;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.Settings;
import android.test.suitebuilder.annotation.SmallTest;
import android.uwb.IOnUwbActivityEnergyInfoListener;
import android.uwb.IUwbAdapterStateCallbacks;
import android.uwb.IUwbAdfProvisionStateCallbacks;
import android.uwb.IUwbRangingCallbacks;
import android.uwb.IUwbVendorUciCallback;
import android.uwb.SessionHandle;
import android.uwb.UwbAddress;
import androidx.test.runner.AndroidJUnit4;
import com.android.modules.utils.build.SdkLevel;
import com.android.server.uwb.UwbServiceCore.InitializationFailureListener;
import com.android.server.uwb.data.UwbUciConstants;
import com.android.server.uwb.jni.NativeUwbManager;
import com.android.server.uwb.multchip.UwbMultichipData;
import com.android.server.uwb.pm.ProfileManager;
import com.android.uwb.flags.FeatureFlags;
import com.android.uwb.flags.Flags;
import com.google.uwb.support.fira.FiraRangingReconfigureParams;
import com.google.uwb.support.multichip.ChipInfoParams;
import com.google.uwb.support.profile.ServiceProfile;
import com.google.uwb.support.profile.UuidBundleWrapper;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
/**
* Tests for {@link UwbServiceImpl}.
*/
@RunWith(AndroidJUnit4.class)
@SmallTest
@Presubmit
public class UwbServiceImplTest {
private static final int UID = 343453;
private static final String PACKAGE_NAME = "com.uwb.test";
private static final String DEFAULT_CHIP_ID = "defaultChipId";
private static final ChipInfoParams DEFAULT_CHIP_INFO_PARAMS =
ChipInfoParams.createBuilder().setChipId(DEFAULT_CHIP_ID).build();
private static final AttributionSource ATTRIBUTION_SOURCE =
new AttributionSource.Builder(UID).setPackageName(PACKAGE_NAME).build();
@Mock private UwbServiceCore mUwbServiceCore;
@Mock private Context mContext;
@Mock private UwbInjector mUwbInjector;
@Mock private UwbSettingsStore mUwbSettingsStore;
@Mock private NativeUwbManager mNativeUwbManager;
@Mock private UwbMultichipData mUwbMultichipData;
@Mock private ProfileManager mProfileManager;
@Mock(answer = Answers.RETURNS_DEEP_STUBS) private UserManager mUserManager;
@Mock private DeviceConfigFacade mDeviceConfigFacade;
@Mock private UwbCountryCode mUwbCountryCode;
@Mock private UciLogModeStore mUciLogModeStore;
@Captor private ArgumentCaptor<IUwbRangingCallbacks> mRangingCbCaptor;
@Captor private ArgumentCaptor<BroadcastReceiver> mApmModeBroadcastReceiver;
@Captor private ArgumentCaptor<ContentObserver> mSatelliteModeContentObserver;
@Captor private ArgumentCaptor<BroadcastReceiver> mUserRestrictionReceiver;
@Captor private ArgumentCaptor<InitializationFailureListener> mInitializationFailureListener;
private UwbServiceImpl mUwbServiceImpl;
private TestLooper mTestLooper;
@Mock private FeatureFlags mFeatureFlags;
@Rule
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
private void createUwbServiceImpl() {
mUwbServiceImpl = new UwbServiceImpl(mContext, mUwbInjector);
}
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mTestLooper = new TestLooper();
when(mUwbInjector.getUwbSettingsStore()).thenReturn(mUwbSettingsStore);
when(mUwbSettingsStore.get(SETTINGS_TOGGLE_STATE)).thenReturn(true);
when(mUwbSettingsStore.get(SETTINGS_FIRST_TOGGLE_DONE)).thenReturn(false);
when(mUwbInjector.getDeviceConfigFacade()).thenReturn(mDeviceConfigFacade);
when(mDeviceConfigFacade.isUwbDisabledUntilFirstToggle()).thenReturn(false);
when(mUwbMultichipData.getChipInfos()).thenReturn(List.of(DEFAULT_CHIP_INFO_PARAMS));
when(mUwbMultichipData.getDefaultChipId()).thenReturn(DEFAULT_CHIP_ID);
when(mUwbInjector.getUwbServiceCore()).thenReturn(mUwbServiceCore);
when(mUwbInjector.getMultichipData()).thenReturn(mUwbMultichipData);
when(mUwbInjector.getGlobalSettingsInt(Settings.Global.AIRPLANE_MODE_ON, 0)).thenReturn(0);
when(mUwbInjector.getGlobalSettingsString(Settings.Global.AIRPLANE_MODE_RADIOS))
.thenReturn("cell,bluetooth,uwb,wifi,wimax");
when(mUwbInjector.getGlobalSettingsInt(SETTINGS_SATELLITE_MODE_ENABLED, 0)).thenReturn(0);
when(mUwbInjector.getGlobalSettingsString(SETTINGS_SATELLITE_MODE_RADIOS))
.thenReturn("cell,bluetooth,nfc,uwb,wifi");
when(mUwbInjector.getNativeUwbManager()).thenReturn(mNativeUwbManager);
when(mUwbInjector.getUserManager()).thenReturn(mUserManager);
when(mUwbInjector.getFeatureFlags()).thenReturn(mFeatureFlags);
when(mUwbInjector.getUwbCountryCode()).thenReturn(mUwbCountryCode);
when(mUwbInjector.getUciLogModeStore()).thenReturn(mUciLogModeStore);
when(mUserManager.getUserRestrictions().getBoolean(anyString())).thenReturn(false);
when(mUwbServiceCore.getHandler()).thenReturn(new Handler(mTestLooper.getLooper()));
createUwbServiceImpl();
verify(mUwbServiceCore).addInitializationFailureListener(
mInitializationFailureListener.capture());
verify(mContext).registerReceiver(
mApmModeBroadcastReceiver.capture(),
argThat(i -> i.getAction(0).equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)),
any(), any());
if (SdkLevel.isAtLeastU()) {
verify(mUwbInjector).registerContentObserver(
eq(Settings.Global.getUriFor(SETTINGS_SATELLITE_MODE_ENABLED)), anyBoolean(),
mSatelliteModeContentObserver.capture());
}
verify(mContext).registerReceiver(
mUserRestrictionReceiver.capture(),
argThat(i -> i.getAction(0).equals(UserManager.ACTION_USER_RESTRICTIONS_CHANGED)),
any(), any());
}
@Test
public void testRegisterAdapterStateCallbacks() throws Exception {
final IUwbAdapterStateCallbacks cb = mock(IUwbAdapterStateCallbacks.class);
mUwbServiceImpl.registerAdapterStateCallbacks(cb);
verify(mUwbServiceCore).registerAdapterStateCallbacks(cb);
}
@Test
public void testUnregisterAdapterStateCallbacks() throws Exception {
final IUwbAdapterStateCallbacks cb = mock(IUwbAdapterStateCallbacks.class);
mUwbServiceImpl.unregisterAdapterStateCallbacks(cb);
verify(mUwbServiceCore).unregisterAdapterStateCallbacks(cb);
}
@Test
public void testGetTimestampResolutionNanos() throws Exception {
final long timestamp = 34L;
when(mUwbServiceCore.getTimestampResolutionNanos()).thenReturn(timestamp);
assertThat(mUwbServiceImpl.getTimestampResolutionNanos(/* chipId= */ null))
.isEqualTo(timestamp);
verify(mUwbServiceCore).getTimestampResolutionNanos();
}
@Test
public void testGetTimestampResolutionNanos_validChipId() throws Exception {
final long timestamp = 34L;
when(mUwbServiceCore.getTimestampResolutionNanos()).thenReturn(timestamp);
assertThat(mUwbServiceImpl.getTimestampResolutionNanos(DEFAULT_CHIP_ID))
.isEqualTo(timestamp);
verify(mUwbServiceCore).getTimestampResolutionNanos();
}
@Test
public void testGetTimestampResolutionNanos_invalidChipId() {
assertThrows(IllegalArgumentException.class,
() -> mUwbServiceImpl.getTimestampResolutionNanos("invalidChipId"));
}
@Test
public void testGetSpecificationInfo_nullChipId() throws Exception {
final PersistableBundle specification = new PersistableBundle();
when(mUwbServiceCore.getSpecificationInfo(anyString())).thenReturn(specification);
assertThat(mUwbServiceImpl.getSpecificationInfo(/* chipId= */ null))
.isEqualTo(specification);
verify(mUwbServiceCore).getSpecificationInfo(DEFAULT_CHIP_ID);
}
@Test
public void testGetSpecificationInfo_validChipId() throws Exception {
final PersistableBundle specification = new PersistableBundle();
when(mUwbServiceCore.getSpecificationInfo(anyString())).thenReturn(specification);
assertThat(mUwbServiceImpl.getSpecificationInfo(DEFAULT_CHIP_ID))
.isEqualTo(specification);
verify(mUwbServiceCore).getSpecificationInfo(DEFAULT_CHIP_ID);
}
@Test
public void testGetSpecificationInfo_invalidChipId() {
assertThrows(IllegalArgumentException.class,
() -> mUwbServiceImpl.getSpecificationInfo("invalidChipId"));
}
@Test
public void testOpenRanging_nullChipId() throws Exception {
final SessionHandle sessionHandle = mock(SessionHandle.class);
final IUwbRangingCallbacks cb = mock(IUwbRangingCallbacks.class);
final PersistableBundle parameters = new PersistableBundle();
final IBinder cbBinder = mock(IBinder.class);
when(cb.asBinder()).thenReturn(cbBinder);
mUwbServiceImpl.openRanging(
ATTRIBUTION_SOURCE, sessionHandle, cb, parameters, /* chipId= */ null);
verify(mUwbServiceCore).openRanging(
eq(ATTRIBUTION_SOURCE), eq(sessionHandle), mRangingCbCaptor.capture(),
eq(parameters), eq(DEFAULT_CHIP_ID));
assertThat(mRangingCbCaptor.getValue()).isNotNull();
}
@Test
public void testStartRanging() throws Exception {
final SessionHandle sessionHandle = mock(SessionHandle.class);
final PersistableBundle parameters = new PersistableBundle();
mUwbServiceImpl.startRanging(sessionHandle, parameters);
verify(mUwbServiceCore).startRanging(sessionHandle, parameters);
}
@Test
public void testReconfigureRanging() throws Exception {
final SessionHandle sessionHandle = mock(SessionHandle.class);
final FiraRangingReconfigureParams parameters =
new FiraRangingReconfigureParams.Builder()
.setBlockStrideLength(6)
.setRangeDataNtfConfig(RANGE_DATA_NTF_CONFIG_ENABLE_PROXIMITY_LEVEL_TRIG)
.setRangeDataProximityFar(6)
.setRangeDataProximityNear(4)
.build();
mUwbServiceImpl.reconfigureRanging(sessionHandle, parameters.toBundle());
verify(mUwbServiceCore).reconfigureRanging(eq(sessionHandle),
argThat((x) -> x.getInt("update_block_stride_length") == 6));
}
@Test
public void testStopRanging() throws Exception {
final SessionHandle sessionHandle = mock(SessionHandle.class);
mUwbServiceImpl.stopRanging(sessionHandle);
verify(mUwbServiceCore).stopRanging(sessionHandle);
}
@Test
public void testCloseRanging() throws Exception {
final SessionHandle sessionHandle = mock(SessionHandle.class);
mUwbServiceImpl.closeRanging(sessionHandle);
verify(mUwbServiceCore).closeRanging(sessionHandle);
}
@Test
public void testThrowSecurityExceptionWhenCalledWithoutUwbPrivilegedPermission()
throws Exception {
doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
eq(UWB_PRIVILEGED), any());
final IUwbAdapterStateCallbacks cb = mock(IUwbAdapterStateCallbacks.class);
try {
mUwbServiceImpl.registerAdapterStateCallbacks(cb);
fail();
} catch (SecurityException e) { /* pass */ }
}
@Test
public void testThrowSecurityExceptionWhenSetUwbEnabledCalledWithoutUwbPrivilegedPermission()
throws Exception {
doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
eq(UWB_PRIVILEGED), any());
try {
mUwbServiceImpl.setEnabled(true);
fail();
} catch (SecurityException e) { /* pass */ }
}
@Test
public void testThrowSecurityExceptionWhenOpenRangingCalledWithoutUwbRangingPermission()
throws Exception {
doThrow(new SecurityException()).when(mUwbInjector).enforceUwbRangingPermissionForPreflight(
any());
final SessionHandle sessionHandle = mock(SessionHandle.class);
final IUwbRangingCallbacks cb = mock(IUwbRangingCallbacks.class);
final PersistableBundle parameters = new PersistableBundle();
final IBinder cbBinder = mock(IBinder.class);
when(cb.asBinder()).thenReturn(cbBinder);
try {
mUwbServiceImpl.openRanging(
ATTRIBUTION_SOURCE, sessionHandle, cb, parameters, /* chipId= */ null);
fail();
} catch (SecurityException e) { /* pass */ }
}
@Test
public void testInitialize() throws Exception {
when(mUwbSettingsStore.get(SETTINGS_TOGGLE_STATE)).thenReturn(true);
mUwbServiceImpl.initialize();
verify(mUwbServiceCore).setEnabled(true);
when(mUwbSettingsStore.get(SETTINGS_TOGGLE_STATE)).thenReturn(false);
mUwbServiceImpl.initialize();
verify(mUwbServiceCore).setEnabled(false);
}
@Test
public void testInitializeWithUwbDisabledUntilFirstToggleFlagOn() throws Exception {
when(mDeviceConfigFacade.isUwbDisabledUntilFirstToggle()).thenReturn(true);
when(mUwbSettingsStore.get(SETTINGS_TOGGLE_STATE)).thenReturn(true);
when(mUwbSettingsStore.get(SETTINGS_FIRST_TOGGLE_DONE)).thenReturn(false);
mUwbServiceImpl.initialize();
verify(mUwbServiceCore).setEnabled(false);
when(mUwbSettingsStore.get(SETTINGS_FIRST_TOGGLE_DONE)).thenReturn(true);
mUwbServiceImpl.initialize();
verify(mUwbServiceCore).setEnabled(true);
}
@Test
public void testToggleStatePersistenceToSharedPrefs() throws Exception {
mUwbServiceImpl.setEnabled(true);
verify(mUwbSettingsStore).put(SETTINGS_TOGGLE_STATE, true);
verify(mUwbServiceCore).setEnabled(true);
when(mUwbSettingsStore.get(SETTINGS_TOGGLE_STATE)).thenReturn(false);
mUwbServiceImpl.setEnabled(false);
verify(mUwbSettingsStore).put(SETTINGS_TOGGLE_STATE, false);
verify(mUwbServiceCore).setEnabled(false);
}
@Test
public void testToggleStatePersistenceToSharedPrefsWithUwbDisabledUntilFirstToggleFlagOn()
throws Exception {
when(mDeviceConfigFacade.isUwbDisabledUntilFirstToggle()).thenReturn(true);
mUwbServiceImpl.setEnabled(true);
verify(mUwbSettingsStore).put(SETTINGS_TOGGLE_STATE, true);
verify(mUwbSettingsStore).put(SETTINGS_FIRST_TOGGLE_DONE, true);
verify(mUwbServiceCore).setEnabled(true);
when(mUwbSettingsStore.get(SETTINGS_TOGGLE_STATE)).thenReturn(false);
mUwbServiceImpl.setEnabled(false);
verify(mUwbSettingsStore).put(SETTINGS_TOGGLE_STATE, false);
verify(mUwbSettingsStore, times(2)).put(SETTINGS_FIRST_TOGGLE_DONE, true);
verify(mUwbServiceCore).setEnabled(false);
}
@Test
public void testToggleStatePersistenceToSharedPrefsWhenApmModeOn() throws Exception {
when(mUwbInjector.getGlobalSettingsInt(Settings.Global.AIRPLANE_MODE_ON, 0)).thenReturn(1);
mUwbServiceImpl.setEnabled(true);
verify(mUwbSettingsStore).put(SETTINGS_TOGGLE_STATE, true);
verify(mUwbServiceCore).setEnabled(false);
when(mUwbSettingsStore.get(SETTINGS_TOGGLE_STATE)).thenReturn(false);
mUwbServiceImpl.setEnabled(false);
verify(mUwbSettingsStore).put(SETTINGS_TOGGLE_STATE, false);
verify(mUwbServiceCore, times(2)).setEnabled(false);
}
@Test
public void testToggleStateReadFromSharedPrefsOnInitialization() throws Exception {
when(mUwbServiceCore.getAdapterState()).thenReturn(STATE_ENABLED_ACTIVE);
assertThat(mUwbServiceImpl.getAdapterState()).isEqualTo(STATE_ENABLED_ACTIVE);
verify(mUwbServiceCore).getAdapterState();
when(mUwbServiceCore.getAdapterState()).thenReturn(STATE_ENABLED_INACTIVE);
assertThat(mUwbServiceImpl.getAdapterState()).isEqualTo(STATE_ENABLED_INACTIVE);
verify(mUwbServiceCore, times(2)).getAdapterState();
}
@Test
public void testApmModeToggle() throws Exception {
mUwbServiceImpl.setEnabled(true);
verify(mUwbSettingsStore).put(SETTINGS_TOGGLE_STATE, true);
verify(mUwbServiceCore).setEnabled(true);
// Toggle on
when(mUwbInjector.getGlobalSettingsInt(Settings.Global.AIRPLANE_MODE_ON, 0)).thenReturn(1);
mApmModeBroadcastReceiver.getValue().onReceive(
mContext, new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED));
verify(mUwbServiceCore).setEnabled(false);
// Toggle off
when(mUwbInjector.getGlobalSettingsInt(Settings.Global.AIRPLANE_MODE_ON, 0)).thenReturn(0);
mApmModeBroadcastReceiver.getValue().onReceive(
mContext, new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED));
verify(mUwbServiceCore, times(2)).setEnabled(true);
}
@Test
public void testApmModeSetEnabledWhenUwbRadioNotSetInAndroidUAndHigher() throws Exception {
assumeTrue(SdkLevel.isAtLeastU()); // Test should only run on U+ devices.
when(mUwbInjector.getGlobalSettingsString(Settings.Global.AIRPLANE_MODE_RADIOS))
.thenReturn("cell,bluetooth,wifi,wimax");
// Recreate UwbServiceImpl to ensure we don't register APM broadcast receiver.
clearInvocations(mContext);
createUwbServiceImpl();
// apm radio setting should be honored on android U+ devices.
// Verify that we did not re-register the APM broadcast listener.
verify(mContext, never()).registerReceiver(
any(), argThat(i -> i.getAction(0).equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)),
any(), any());
mUwbServiceImpl.setEnabled(true);
verify(mUwbSettingsStore).put(SETTINGS_TOGGLE_STATE, true);
verify(mUwbServiceCore).setEnabled(true);
clearInvocations(mUwbServiceCore, mUwbSettingsStore);
// Toggle APM on
when(mUwbInjector.getGlobalSettingsInt(Settings.Global.AIRPLANE_MODE_ON, 0)).thenReturn(1);
mUwbServiceImpl.setEnabled(true);
verify(mUwbSettingsStore).put(SETTINGS_TOGGLE_STATE, true);
verify(mUwbServiceCore).setEnabled(true);
clearInvocations(mUwbServiceCore, mUwbSettingsStore);
// Toggle APM off
when(mUwbInjector.getGlobalSettingsInt(Settings.Global.AIRPLANE_MODE_ON, 0)).thenReturn(0);
mUwbServiceImpl.setEnabled(true);
verify(mUwbSettingsStore).put(SETTINGS_TOGGLE_STATE, true);
verify(mUwbServiceCore).setEnabled(true);
}
@Test
public void testApmModeSetEnabledWhenUwbRadioNotSetInAndroidT() throws Exception {
// Test should only run on T devices.
assumeTrue(SdkLevel.isAtLeastT() && !SdkLevel.isAtLeastU());
when(mUwbInjector.getGlobalSettingsString(Settings.Global.AIRPLANE_MODE_RADIOS))
.thenReturn("cell,bluetooth,wifi,wimax");
// Recreate UwbServiceImpl to ensure we do register APM broadcast receiver.
clearInvocations(mContext);
createUwbServiceImpl();
// apm radio setting should be ignored on android T devices.
// Verify that we did re-register the APM broadcast listener.
verify(mContext).registerReceiver(
mApmModeBroadcastReceiver.capture(),
argThat(i -> i.getAction(0).equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)),
any(), any());
mUwbServiceImpl.setEnabled(true);
verify(mUwbSettingsStore).put(SETTINGS_TOGGLE_STATE, true);
verify(mUwbServiceCore).setEnabled(true);
clearInvocations(mUwbServiceCore, mUwbSettingsStore);
// Toggle APM on (ignored by uwb stack)
when(mUwbInjector.getGlobalSettingsInt(Settings.Global.AIRPLANE_MODE_ON, 0)).thenReturn(1);
mUwbServiceImpl.setEnabled(true);
verify(mUwbSettingsStore).put(SETTINGS_TOGGLE_STATE, true);
verify(mUwbServiceCore).setEnabled(false);
clearInvocations(mUwbServiceCore, mUwbSettingsStore);
// Toggle APM off (ignored by uwb stack)
when(mUwbInjector.getGlobalSettingsInt(Settings.Global.AIRPLANE_MODE_ON, 0)).thenReturn(0);
mUwbServiceImpl.setEnabled(true);
verify(mUwbSettingsStore).put(SETTINGS_TOGGLE_STATE, true);
verify(mUwbServiceCore).setEnabled(true);
}
@Test
public void testSatelliteModeToggle() throws Exception {
assumeTrue(SdkLevel.isAtLeastU()); // Test should only run on U+ devices.
mUwbServiceImpl.setEnabled(true);
verify(mUwbSettingsStore).put(SETTINGS_TOGGLE_STATE, true);
verify(mUwbServiceCore).setEnabled(true);
// Toggle satellite on
when(mUwbInjector.getGlobalSettingsInt(SETTINGS_SATELLITE_MODE_ENABLED, 0)).thenReturn(1);
mSatelliteModeContentObserver.getValue().onChange(false);
verify(mUwbServiceCore).setEnabled(false);
// Toggle satellite off
when(mUwbInjector.getGlobalSettingsInt(SETTINGS_SATELLITE_MODE_ENABLED, 0)).thenReturn(0);
mSatelliteModeContentObserver.getValue().onChange(false);
verify(mUwbServiceCore, times(2)).setEnabled(true);
}
@Test
public void testSatelliteModeSetEnabledWhenUwbRadioNotSet() throws Exception {
assumeTrue(SdkLevel.isAtLeastU()); // Test should only run on U+ devices.
when(mUwbInjector.getGlobalSettingsString(SETTINGS_SATELLITE_MODE_RADIOS))
.thenReturn("cell,bluetooth,nfc,wifi");
mUwbServiceImpl.setEnabled(true);
verify(mUwbSettingsStore).put(SETTINGS_TOGGLE_STATE, true);
verify(mUwbServiceCore).setEnabled(true);
clearInvocations(mUwbServiceCore, mUwbSettingsStore);
// Toggle satellite on (ignored by uwb stack)
when(mUwbInjector.getGlobalSettingsInt(SETTINGS_SATELLITE_MODE_ENABLED, 0)).thenReturn(1);
mUwbServiceImpl.setEnabled(true);
verify(mUwbSettingsStore).put(SETTINGS_TOGGLE_STATE, true);
verify(mUwbServiceCore).setEnabled(true);
clearInvocations(mUwbServiceCore, mUwbSettingsStore);
// Toggle satellite off (ignored by uwb stack)
when(mUwbInjector.getGlobalSettingsInt(SETTINGS_SATELLITE_MODE_ENABLED, 0)).thenReturn(0);
mUwbServiceImpl.setEnabled(true);
verify(mUwbSettingsStore).put(SETTINGS_TOGGLE_STATE, true);
verify(mUwbServiceCore).setEnabled(true);
}
@Test
public void testUserRestrictionChanged() throws Exception {
assumeTrue(SdkLevel.isAtLeastU()); // Test should only run on U+ devices.
mUwbServiceImpl.setEnabled(true);
// User restriction changes to disallow UWB
when(mUserManager.getUserRestrictions().getBoolean(anyString())).thenReturn(true);
mUserRestrictionReceiver.getValue().onReceive(
mContext, new Intent(UserManager.ACTION_USER_RESTRICTIONS_CHANGED));
verify(mUwbServiceCore).setEnabled(false);
// User restriction changes to allow UWB
when(mUserManager.getUserRestrictions().getBoolean(anyString())).thenReturn(true);
mUserRestrictionReceiver.getValue().onReceive(
mContext, new Intent(UserManager.ACTION_USER_RESTRICTIONS_CHANGED));
verify(mUwbServiceCore, times(1)).setEnabled(true);
}
@Test
public void testToggleFromRootedShellWhenApmModeOn() throws Exception {
BinderUtil.setUid(Process.ROOT_UID);
when(mUwbInjector.getGlobalSettingsInt(Settings.Global.AIRPLANE_MODE_ON, 0)).thenReturn(1);
mUwbServiceImpl.setEnabled(true);
verify(mUwbSettingsStore).put(SETTINGS_TOGGLE_STATE, true);
verify(mUwbServiceCore).setEnabled(true);
when(mUwbSettingsStore.get(SETTINGS_TOGGLE_STATE)).thenReturn(false);
mUwbServiceImpl.setEnabled(false);
verify(mUwbSettingsStore).put(SETTINGS_TOGGLE_STATE, false);
verify(mUwbServiceCore).setEnabled(false);
}
@Test
public void testHandleInitializationFailure() throws Exception {
mUwbServiceImpl.setEnabled(true);
verify(mUwbSettingsStore).put(SETTINGS_TOGGLE_STATE, true);
verify(mUwbServiceCore).setEnabled(true);
// Trigger failure callback.
mInitializationFailureListener.getValue().onFailure();
// Move time forward.
mTestLooper.moveTimeForward(UwbServiceImpl.INITIALIZATION_RETRY_TIMEOUT_MS);
// Verify UWB is re-enabled.
verify(mUwbServiceCore).setEnabled(true);
verify(mUwbServiceCore).removeInitializationFailureListener(
mInitializationFailureListener.getValue());
}
@Test
public void testGetDefaultChipId() {
assertEquals(DEFAULT_CHIP_ID, mUwbServiceImpl.getDefaultChipId());
}
@Test
public void testThrowSecurityExceptionWhenGetDefaultChipIdWithoutUwbPrivilegedPermission()
throws Exception {
doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
eq(UWB_PRIVILEGED), any());
try {
mUwbServiceImpl.getDefaultChipId();
fail();
} catch (SecurityException e) { /* pass */ }
}
@Test
public void testGetChipIds() {
List<String> chipIds = mUwbServiceImpl.getChipIds();
assertThat(chipIds).containsExactly(DEFAULT_CHIP_ID);
}
@Test
public void testThrowSecurityExceptionWhenGetChipIdsWithoutUwbPrivilegedPermission()
throws Exception {
doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
eq(UWB_PRIVILEGED), any());
try {
mUwbServiceImpl.getChipIds();
fail();
} catch (SecurityException e) { /* pass */ }
}
@Test
public void testGetChipInfos() {
List<PersistableBundle> chipInfos = mUwbServiceImpl.getChipInfos();
assertThat(chipInfos).hasSize(1);
ChipInfoParams chipInfoParams = ChipInfoParams.fromBundle(chipInfos.get(0));
assertThat(chipInfoParams.getChipId()).isEqualTo(DEFAULT_CHIP_ID);
assertThat(chipInfoParams.getPositionX()).isEqualTo(0.);
assertThat(chipInfoParams.getPositionY()).isEqualTo(0.);
assertThat(chipInfoParams.getPositionZ()).isEqualTo(0.);
}
@Test
public void testThrowSecurityExceptionWhenGetChipInfosWithoutUwbPrivilegedPermission()
throws Exception {
doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
eq(UWB_PRIVILEGED), any());
try {
mUwbServiceImpl.getChipInfos();
fail();
} catch (SecurityException e) { /* pass */ }
}
@Test
public void testAddControlee() throws Exception {
final SessionHandle sessionHandle = mock(SessionHandle.class);
final PersistableBundle parameters = new PersistableBundle();
mUwbServiceImpl.addControlee(sessionHandle, parameters);
verify(mUwbServiceCore).addControlee(sessionHandle, parameters);
}
@Test
public void testRemoveControlee() throws Exception {
final SessionHandle sessionHandle = mock(SessionHandle.class);
final PersistableBundle parameters = new PersistableBundle();
mUwbServiceImpl.removeControlee(sessionHandle, parameters);
verify(mUwbServiceCore).removeControlee(sessionHandle, parameters);
}
@Test
public void testAddServiceProfile() {
UUID serviceInstanceID = new UUID(100, 50);
when(mUwbInjector.getProfileManager()).thenReturn(mProfileManager);
when(mProfileManager.addServiceProfile(anyInt()))
.thenReturn(Optional.of(serviceInstanceID));
PersistableBundle bundle = new ServiceProfile.Builder()
.setServiceID(PACS_PROFILE_SERVICE_ID)
.build()
.toBundle();
assertEquals(ServiceProfile.fromBundle(bundle).getServiceID(),
PACS_PROFILE_SERVICE_ID);
PersistableBundle statusBundle = mUwbServiceImpl.addServiceProfile(bundle);
verify(mProfileManager).addServiceProfile(1);
assertEquals(UuidBundleWrapper.fromBundle(statusBundle).getServiceInstanceID().get(),
serviceInstanceID);
}
@Test
public void testGetAdfCertificateAndInfo() throws Exception {
final PersistableBundle parameters = new PersistableBundle();
try {
mUwbServiceImpl.getAdfCertificateAndInfo(parameters);
fail();
} catch (IllegalStateException e) { /* pass */ }
}
@Test
public void testGetAdfProvisioningAuthorities() throws Exception {
final PersistableBundle parameters = new PersistableBundle();
try {
mUwbServiceImpl.getAdfProvisioningAuthorities(parameters);
fail();
} catch (IllegalStateException e) { /* pass */ }
}
@Test
public void testGetAllServiceProfiles() throws Exception {
try {
mUwbServiceImpl.getAllServiceProfiles();
fail();
} catch (IllegalStateException e) { /* pass */ }
}
@Test
public void testProvisionProfileAdfByScript() throws Exception {
final PersistableBundle parameters = new PersistableBundle();
final IUwbAdfProvisionStateCallbacks cb = mock(IUwbAdfProvisionStateCallbacks.class);
try {
mUwbServiceImpl.provisionProfileAdfByScript(parameters, cb);
fail();
} catch (IllegalStateException e) { /* pass */ }
}
@Test
public void testRegisterVendorExtensionCallback() throws Exception {
final IUwbVendorUciCallback cb = mock(IUwbVendorUciCallback.class);
mUwbServiceImpl.registerVendorExtensionCallback(cb);
verify(mUwbServiceCore).registerVendorExtensionCallback(cb);
}
@Test
public void testUnregisterVendorExtensionCallback() throws Exception {
final IUwbVendorUciCallback cb = mock(IUwbVendorUciCallback.class);
mUwbServiceImpl.unregisterVendorExtensionCallback(cb);
verify(mUwbServiceCore).unregisterVendorExtensionCallback(cb);
}
@Test
public void testRemoveProfileAdf() throws Exception {
final PersistableBundle parameters = new PersistableBundle();
try {
mUwbServiceImpl.removeProfileAdf(parameters);
fail();
} catch (IllegalStateException e) { /* pass */ }
}
@Test
public void testRemoveServiceProfile() throws Exception {
UUID serviceInstanceID = new UUID(100, 50);
when(mUwbInjector.getProfileManager()).thenReturn(mProfileManager);
when(mProfileManager.removeServiceProfile(any()))
.thenReturn(UwbUciConstants.STATUS_CODE_OK);
UuidBundleWrapper uuidBundleWrapper = new UuidBundleWrapper.Builder()
.setServiceInstanceID(Optional.of(serviceInstanceID))
.build();
int status = mUwbServiceImpl.removeServiceProfile(uuidBundleWrapper.toBundle());
verify(mProfileManager).removeServiceProfile(any());
assertEquals(status, UwbUciConstants.STATUS_CODE_OK);
}
@Test
public void testResume() throws Exception {
final SessionHandle sessionHandle = mock(SessionHandle.class);
PersistableBundle bundle = new PersistableBundle();
mUwbServiceImpl.resume(sessionHandle, bundle);
verify(mUwbServiceCore).resume(sessionHandle, bundle);
}
@Test
public void testPause() throws Exception {
final SessionHandle sessionHandle = mock(SessionHandle.class);
PersistableBundle bundle = new PersistableBundle();
mUwbServiceImpl.pause(sessionHandle, bundle);
verify(mUwbServiceCore).pause(sessionHandle, bundle);
}
@Test
public void testSendData() throws Exception {
final SessionHandle sessionHandle = mock(SessionHandle.class);
final UwbAddress mUwbAddress = mock(UwbAddress.class);
final PersistableBundle parameters = new PersistableBundle();
final byte[] data = new byte[] {1, 3, 5, 7, 11, 13};
mUwbServiceImpl.sendData(sessionHandle, mUwbAddress, parameters, data);
verify(mUwbServiceCore).sendData(sessionHandle, mUwbAddress, parameters, data);
}
@Test
public void testThrowSecurityExceptionWhenSendDataWithoutUwbPrivilegedPermission()
throws Exception {
final SessionHandle sessionHandle = mock(SessionHandle.class);
final UwbAddress mUwbAddress = mock(UwbAddress.class);
final PersistableBundle parameters = new PersistableBundle();
final byte[] data = new byte[] {1, 3, 5, 7, 11, 13};
doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
eq(UWB_PRIVILEGED), any());
try {
mUwbServiceImpl.sendData(sessionHandle, mUwbAddress, parameters, data);
fail();
} catch (SecurityException e) { /* pass */ }
}
@Test
public void testSetDataTransferPhaseConfig() throws Exception {
assumeTrue(SdkLevel.isAtLeastV()); // Test should only run on V+ devices.
when(mFeatureFlags.dataTransferPhaseConfig()).thenReturn(true);
final SessionHandle sessionHandle = mock(SessionHandle.class);
PersistableBundle bundle = new PersistableBundle();
mUwbServiceImpl.setDataTransferPhaseConfig(sessionHandle, bundle);
verify(mUwbServiceCore).setDataTransferPhaseConfig(sessionHandle, bundle);
}
@Test
public void testSendVendorUciMessage() throws Exception {
final int mt = 1;
final int gid = 0;
final int oid = 0;
mUwbServiceImpl.sendVendorUciMessage(mt, gid, oid, null);
verify(mUwbServiceCore).sendVendorUciMessage(mt, gid, oid, null,
mUwbInjector.getMultichipData().getDefaultChipId());
}
@Test
public void testRangingRoundsUpdateDtTag() throws Exception {
assumeTrue(SdkLevel.isAtLeastU()); // Test should only run on U+ devices.
final SessionHandle sessionHandle = mock(SessionHandle.class);
final PersistableBundle parameters = new PersistableBundle();
mUwbServiceImpl.updateRangingRoundsDtTag(sessionHandle, parameters);
verify(mUwbServiceCore).rangingRoundsUpdateDtTag(sessionHandle, parameters);
}
@Test
@RequiresFlagsEnabled(Flags.FLAG_QUERY_TIMESTAMP_MICROS)
public void testQueryDataSize() throws Exception {
assumeTrue(SdkLevel.isAtLeastV()); // Test should only run on V+ devices.
final SessionHandle sessionHandle = mock(SessionHandle.class);
final PersistableBundle parameters = new PersistableBundle();
when(mUwbServiceCore.queryMaxDataSizeBytes(sessionHandle)).thenReturn(MAX_DATA_SIZE);
assertThat(mUwbServiceImpl.queryMaxDataSizeBytes(sessionHandle)).isEqualTo(MAX_DATA_SIZE);
verify(mUwbServiceCore).queryMaxDataSizeBytes(sessionHandle);
}
@Test
@RequiresFlagsEnabled(Flags.FLAG_HYBRID_SESSION_SUPPORT)
public void testSetHybridSessionControllerConfiguration() throws Exception {
assumeTrue(SdkLevel.isAtLeastV()); // Test should only run on V+ devices.
final SessionHandle sessionHandle = mock(SessionHandle.class);
final PersistableBundle parameters = new PersistableBundle();
mUwbServiceImpl.setHybridSessionControllerConfiguration(sessionHandle, parameters);
verify(mUwbServiceCore).setHybridSessionControllerConfiguration(sessionHandle, parameters);
}
@Test
@RequiresFlagsEnabled(Flags.FLAG_HYBRID_SESSION_SUPPORT)
public void testSetHybridSessionControleeConfiguration() {
assumeTrue(SdkLevel.isAtLeastV()); // Test should only run on V+ devices.
SessionHandle sessionHandle = mock(SessionHandle.class);
PersistableBundle params = mock(PersistableBundle.class);
mUwbServiceImpl.setHybridSessionControleeConfiguration(sessionHandle, params);
verify(mUwbServiceCore).setHybridSessionControleeConfiguration(sessionHandle, params);
}
@Test
public void testGetUwbActivityEnergyInfoAsync() throws Exception {
final IOnUwbActivityEnergyInfoListener listener = mock(
IOnUwbActivityEnergyInfoListener.class);
mUwbServiceImpl.getUwbActivityEnergyInfoAsync(listener);
verify(mUwbServiceCore).reportUwbActivityEnergyInfo(listener);
}
@Test
public void testGetUwbActivityEnergyInfoAsyncSecurityException() throws Exception {
final IOnUwbActivityEnergyInfoListener listener = mock(
IOnUwbActivityEnergyInfoListener.class);
doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
eq(UWB_PRIVILEGED), any());
try {
mUwbServiceImpl.getUwbActivityEnergyInfoAsync(listener);
fail();
} catch (SecurityException e) { /* pass */ }
}
}