blob: 1d0e412fe12796db1ad4ce8ab4bc1539c19c691f [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.UwbSettingsStore.SETTINGS_TOGGLE_STATE;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
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.os.IBinder;
import android.os.PersistableBundle;
import android.platform.test.annotations.Presubmit;
import android.provider.Settings;
import android.test.suitebuilder.annotation.SmallTest;
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.server.uwb.jni.NativeUwbManager;
import com.android.server.uwb.multchip.UwbMultichipData;
import com.google.uwb.support.multichip.ChipInfoParams;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.List;
/**
* 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;
@Captor private ArgumentCaptor<IUwbRangingCallbacks> mRangingCbCaptor;
@Captor private ArgumentCaptor<IUwbRangingCallbacks> mRangingCbCaptor2;
@Captor private ArgumentCaptor<IBinder.DeathRecipient> mClientDeathCaptor;
@Captor private ArgumentCaptor<IBinder.DeathRecipient> mUwbServiceCoreDeathCaptor;
@Captor private ArgumentCaptor<BroadcastReceiver> mApmModeBroadcastReceiver;
private UwbServiceImpl mUwbServiceImpl;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
when(mUwbInjector.getUwbSettingsStore()).thenReturn(mUwbSettingsStore);
when(mUwbSettingsStore.get(SETTINGS_TOGGLE_STATE)).thenReturn(true);
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.getSettingsInt(Settings.Global.AIRPLANE_MODE_ON, 0)).thenReturn(0);
when(mUwbInjector.getNativeUwbManager()).thenReturn(mNativeUwbManager);
mUwbServiceImpl = new UwbServiceImpl(mContext, mUwbInjector);
verify(mContext).registerReceiver(
mApmModeBroadcastReceiver.capture(),
argThat(i -> i.getAction(0).equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)));
}
@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() throws Exception {
final PersistableBundle specification = new PersistableBundle();
when(mUwbServiceCore.getSpecificationInfo()).thenReturn(specification);
assertThat(mUwbServiceImpl.getSpecificationInfo(/* chipId= */ null))
.isEqualTo(specification);
verify(mUwbServiceCore).getSpecificationInfo();
}
@Test
public void testGetSpecificationInfo_validChipId() throws Exception {
final PersistableBundle specification = new PersistableBundle();
when(mUwbServiceCore.getSpecificationInfo()).thenReturn(specification);
assertThat(mUwbServiceImpl.getSpecificationInfo(DEFAULT_CHIP_ID))
.isEqualTo(specification);
verify(mUwbServiceCore).getSpecificationInfo();
}
@Test
public void testGetSpecificationInfo_invalidChipId() {
assertThrows(IllegalArgumentException.class,
() -> mUwbServiceImpl.getSpecificationInfo("invalidChipId"));
}
@Test
public void testOpenRanging() throws Exception {
final SessionHandle sessionHandle = new SessionHandle(5);
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));
assertThat(mRangingCbCaptor.getValue()).isNotNull();
}
@Test
public void testStartRanging() throws Exception {
final SessionHandle sessionHandle = new SessionHandle(5);
final PersistableBundle parameters = new PersistableBundle();
mUwbServiceImpl.startRanging(sessionHandle, parameters);
verify(mUwbServiceCore).startRanging(sessionHandle, parameters);
}
@Test
public void testReconfigureRanging() throws Exception {
final SessionHandle sessionHandle = new SessionHandle(5);
final PersistableBundle parameters = new PersistableBundle();
mUwbServiceImpl.reconfigureRanging(sessionHandle, parameters);
verify(mUwbServiceCore).reconfigureRanging(sessionHandle, parameters);
}
@Test
public void testStopRanging() throws Exception {
final SessionHandle sessionHandle = new SessionHandle(5);
mUwbServiceImpl.stopRanging(sessionHandle);
verify(mUwbServiceCore).stopRanging(sessionHandle);
}
@Test
public void testCloseRanging() throws Exception {
final SessionHandle sessionHandle = new SessionHandle(5);
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 = new SessionHandle(5);
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 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 testToggleStatePersistenceToSharedPrefsWhenApmModeOn() throws Exception {
when(mUwbInjector.getSettingsInt(Settings.Global.AIRPLANE_MODE_ON, 0)).thenReturn(1);
mUwbServiceImpl.setEnabled(true);
verify(mUwbSettingsStore).put(SETTINGS_TOGGLE_STATE, true);
verify(mUwbServiceCore).setEnabled(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.getSettingsInt(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.getSettingsInt(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 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 = new SessionHandle(5);
final PersistableBundle parameters = new PersistableBundle();
try {
mUwbServiceImpl.addControlee(sessionHandle, parameters);
fail();
} catch (IllegalStateException e) { /* pass */ }
}
@Test
public void testRemoveControlee() throws Exception {
final SessionHandle sessionHandle = new SessionHandle(5);
final PersistableBundle parameters = new PersistableBundle();
try {
mUwbServiceImpl.removeControlee(sessionHandle, parameters);
fail();
} catch (IllegalStateException e) { /* pass */ }
}
@Test
public void testAddServiceProfile() throws Exception {
final PersistableBundle parameters = new PersistableBundle();
try {
mUwbServiceImpl.addServiceProfile(parameters);
fail();
} catch (IllegalStateException e) { /* pass */ }
}
@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);
try {
mUwbServiceImpl.registerVendorExtensionCallback(cb);
fail();
} catch (IllegalStateException e) { /* pass */ }
}
@Test
public void testUnregisterVendorExtensionCallback() throws Exception {
final IUwbVendorUciCallback cb = mock(IUwbVendorUciCallback.class);
try {
mUwbServiceImpl.unregisterVendorExtensionCallback(cb);
fail();
} catch (IllegalStateException e) { /* pass */ }
}
@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 {
final PersistableBundle parameters = new PersistableBundle();
try {
mUwbServiceImpl.removeServiceProfile(parameters);
fail();
} catch (IllegalStateException e) { /* pass */ }
}
@Test
public void testResume() throws Exception {
final SessionHandle sessionHandle = new SessionHandle(5);
final PersistableBundle parameters = new PersistableBundle();
try {
mUwbServiceImpl.resume(sessionHandle, parameters);
fail();
} catch (IllegalStateException e) { /* pass */ }
}
@Test
public void testPause() throws Exception {
final SessionHandle sessionHandle = new SessionHandle(5);
final PersistableBundle parameters = new PersistableBundle();
try {
mUwbServiceImpl.pause(sessionHandle, parameters);
fail();
} catch (IllegalStateException e) { /* pass */ }
}
@Test
public void testSendData() throws Exception {
final SessionHandle sessionHandle = new SessionHandle(5);
final UwbAddress mUwbAddress = mock(UwbAddress.class);
final PersistableBundle parameters = new PersistableBundle();
try {
mUwbServiceImpl.sendData(sessionHandle, mUwbAddress, parameters, null);
fail();
} catch (IllegalStateException e) { /* pass */ }
}
@Test
public void testSendVendorUciMessage() throws Exception {
final int gid = 0;
final int oid = 0;
try {
mUwbServiceImpl.sendVendorUciMessage(gid, oid, null);
fail();
} catch (IllegalStateException e) { /* pass */ }
}
}