blob: d06a4470c5b0e45d199cce40642a848fa7c5d968 [file] [log] [blame]
/*
* Copyright (C) 2022 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.nearby.provider;
import static android.Manifest.permission.READ_DEVICE_CONFIG;
import static android.Manifest.permission.WRITE_DEVICE_CONFIG;
import static android.provider.DeviceConfig.NAMESPACE_TETHERING;
import static com.android.server.nearby.NearbyConfiguration.NEARBY_SUPPORT_TEST_APP;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
import android.content.Context;
import android.hardware.location.NanoAppMessage;
import android.nearby.DataElement;
import android.nearby.NearbyDeviceParcelable;
import android.provider.DeviceConfig;
import androidx.test.filters.SdkSuppress;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.server.nearby.NearbyConfiguration;
import com.android.server.nearby.presence.PresenceDiscoveryResult;
import com.google.protobuf.ByteString;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
import service.proto.Blefilter;
public class ChreDiscoveryProviderTest {
@Mock AbstractDiscoveryProvider.Listener mListener;
@Mock ChreCommunication mChreCommunication;
@Captor ArgumentCaptor<ChreCommunication.ContextHubCommsCallback> mChreCallbackCaptor;
@Captor ArgumentCaptor<NearbyDeviceParcelable> mNearbyDevice;
private static final int DATA_TYPE_CONNECTION_STATUS_KEY = 10;
private static final int DATA_TYPE_BATTERY_KEY = 11;
private static final int DATA_TYPE_TX_POWER_KEY = 5;
private static final int DATA_TYPE_BLUETOOTH_ADDR_KEY = 101;
private static final int DATA_TYPE_FP_ACCOUNT_KEY = 9;
private static final int DATA_TYPE_BLE_SERVICE_DATA_KEY = 100;
private static final int DATA_TYPE_TEST_1_KEY = 256;
private static final int DATA_TYPE_TEST_2_KEY = 257;
private static final int DATA_TYPE_TEST_3_KEY = 258;
private static final int DATA_TYPE_TEST_4_KEY = 259;
private static final int DATA_TYPE_TEST_5_KEY = 260;
private ChreDiscoveryProvider mChreDiscoveryProvider;
@Before
public void setUp() {
InstrumentationRegistry.getInstrumentation().getUiAutomation()
.adoptShellPermissionIdentity(WRITE_DEVICE_CONFIG, READ_DEVICE_CONFIG);
MockitoAnnotations.initMocks(this);
Context context = InstrumentationRegistry.getInstrumentation().getContext();
mChreDiscoveryProvider =
new ChreDiscoveryProvider(context, mChreCommunication, new InLineExecutor());
}
@Test
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void testInit() {
mChreDiscoveryProvider.init();
verify(mChreCommunication).start(mChreCallbackCaptor.capture(), any());
mChreCallbackCaptor.getValue().started(true);
}
@Test
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void testOnNearbyDeviceDiscovered() {
Blefilter.PublicCredential credential =
Blefilter.PublicCredential.newBuilder()
.setSecretId(ByteString.copyFrom(new byte[] {1}))
.setAuthenticityKey(ByteString.copyFrom(new byte[2]))
.setPublicKey(ByteString.copyFrom(new byte[3]))
.setEncryptedMetadata(ByteString.copyFrom(new byte[4]))
.setEncryptedMetadataTag(ByteString.copyFrom(new byte[5]))
.build();
Blefilter.BleFilterResult result =
Blefilter.BleFilterResult.newBuilder()
.setTxPower(2)
.setRssi(1)
.setPublicCredential(credential)
.build();
Blefilter.BleFilterResults results =
Blefilter.BleFilterResults.newBuilder().addResult(result).build();
NanoAppMessage chre_message =
NanoAppMessage.createMessageToNanoApp(
ChreDiscoveryProvider.NANOAPP_ID,
ChreDiscoveryProvider.NANOAPP_MESSAGE_TYPE_FILTER_RESULT,
results.toByteArray());
mChreDiscoveryProvider.getController().setListener(mListener);
mChreDiscoveryProvider.init();
mChreDiscoveryProvider.onStart();
verify(mChreCommunication).start(mChreCallbackCaptor.capture(), any());
mChreCallbackCaptor.getValue().onMessageFromNanoApp(chre_message);
verify(mListener).onNearbyDeviceDiscovered(any());
}
@Test
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void testOnNearbyDeviceDiscoveredWithDataElements() {
// Disables the setting of test app support
boolean isSupportedTestApp = getDeviceConfigBoolean(
NEARBY_SUPPORT_TEST_APP, false /* defaultValue */);
if (isSupportedTestApp) {
DeviceConfig.setProperty(NAMESPACE_TETHERING, NEARBY_SUPPORT_TEST_APP, "false", false);
}
assertThat(new NearbyConfiguration().isTestAppSupported()).isFalse();
final byte [] connectionStatus = new byte[] {1, 2, 3};
final byte [] batteryStatus = new byte[] {4, 5, 6};
final byte [] txPower = new byte[] {2};
final byte [] bluetoothAddr = new byte[] {1, 2, 3, 4, 5, 6};
final byte [] fastPairAccountKey = new byte[16];
// First byte is length of service data, padding zeros should be thrown away.
final byte [] bleServiceData = new byte[] {5, 1, 2, 3, 4, 5, 0, 0, 0, 0};
final byte [] testData = new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
final List<DataElement> expectedExtendedProperties = new ArrayList<>();
expectedExtendedProperties.add(new DataElement(DATA_TYPE_CONNECTION_STATUS_KEY,
connectionStatus));
expectedExtendedProperties.add(new DataElement(DATA_TYPE_BATTERY_KEY, batteryStatus));
expectedExtendedProperties.add(new DataElement(DATA_TYPE_TX_POWER_KEY, txPower));
expectedExtendedProperties.add(
new DataElement(DATA_TYPE_BLUETOOTH_ADDR_KEY, bluetoothAddr));
expectedExtendedProperties.add(
new DataElement(DATA_TYPE_FP_ACCOUNT_KEY, fastPairAccountKey));
expectedExtendedProperties.add(
new DataElement(DATA_TYPE_BLE_SERVICE_DATA_KEY, new byte[] {1, 2, 3, 4, 5}));
Blefilter.PublicCredential credential =
Blefilter.PublicCredential.newBuilder()
.setSecretId(ByteString.copyFrom(new byte[] {1}))
.setAuthenticityKey(ByteString.copyFrom(new byte[2]))
.setPublicKey(ByteString.copyFrom(new byte[3]))
.setEncryptedMetadata(ByteString.copyFrom(new byte[4]))
.setEncryptedMetadataTag(ByteString.copyFrom(new byte[5]))
.build();
Blefilter.BleFilterResult result =
Blefilter.BleFilterResult.newBuilder()
.setTxPower(2)
.setRssi(1)
.setBluetoothAddress(ByteString.copyFrom(bluetoothAddr))
.setBleServiceData(ByteString.copyFrom(bleServiceData))
.setPublicCredential(credential)
.addDataElement(Blefilter.DataElement.newBuilder()
.setKey(
Blefilter.DataElement.ElementType
.DE_CONNECTION_STATUS)
.setValue(ByteString.copyFrom(connectionStatus))
.setValueLength(connectionStatus.length)
)
.addDataElement(Blefilter.DataElement.newBuilder()
.setKey(
Blefilter.DataElement.ElementType
.DE_BATTERY_STATUS)
.setValue(ByteString.copyFrom(batteryStatus))
.setValueLength(batteryStatus.length)
)
.addDataElement(Blefilter.DataElement.newBuilder()
.setKey(
Blefilter.DataElement.ElementType
.DE_FAST_PAIR_ACCOUNT_KEY)
.setValue(ByteString.copyFrom(fastPairAccountKey))
.setValueLength(fastPairAccountKey.length)
)
.addDataElement(Blefilter.DataElement.newBuilder()
.setKey(
Blefilter.DataElement.ElementType
.DE_TEST_1)
.setValue(ByteString.copyFrom(testData))
.setValueLength(testData.length)
)
.addDataElement(Blefilter.DataElement.newBuilder()
.setKey(
Blefilter.DataElement.ElementType
.DE_TEST_2)
.setValue(ByteString.copyFrom(testData))
.setValueLength(testData.length)
)
.addDataElement(Blefilter.DataElement.newBuilder()
.setKey(
Blefilter.DataElement.ElementType
.DE_TEST_3)
.setValue(ByteString.copyFrom(testData))
.setValueLength(testData.length)
)
.addDataElement(Blefilter.DataElement.newBuilder()
.setKey(
Blefilter.DataElement.ElementType
.DE_TEST_4)
.setValue(ByteString.copyFrom(testData))
.setValueLength(testData.length)
)
.addDataElement(Blefilter.DataElement.newBuilder()
.setKey(
Blefilter.DataElement.ElementType
.DE_TEST_5)
.setValue(ByteString.copyFrom(testData))
.setValueLength(testData.length)
)
.build();
Blefilter.BleFilterResults results =
Blefilter.BleFilterResults.newBuilder().addResult(result).build();
NanoAppMessage chre_message =
NanoAppMessage.createMessageToNanoApp(
ChreDiscoveryProvider.NANOAPP_ID,
ChreDiscoveryProvider.NANOAPP_MESSAGE_TYPE_FILTER_RESULT,
results.toByteArray());
mChreDiscoveryProvider.getController().setListener(mListener);
mChreDiscoveryProvider.init();
mChreDiscoveryProvider.onStart();
verify(mChreCommunication).start(mChreCallbackCaptor.capture(), any());
mChreCallbackCaptor.getValue().onMessageFromNanoApp(chre_message);
verify(mListener).onNearbyDeviceDiscovered(mNearbyDevice.capture());
List<DataElement> extendedProperties = PresenceDiscoveryResult
.fromDevice(mNearbyDevice.getValue()).getExtendedProperties();
assertThat(extendedProperties).containsExactlyElementsIn(expectedExtendedProperties);
// Reverts the setting of test app support
if (isSupportedTestApp) {
DeviceConfig.setProperty(NAMESPACE_TETHERING, NEARBY_SUPPORT_TEST_APP, "true", false);
assertThat(new NearbyConfiguration().isTestAppSupported()).isTrue();
}
}
@Test
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void testOnNearbyDeviceDiscoveredWithTestDataElements() {
// Enables the setting of test app support
boolean isSupportedTestApp = getDeviceConfigBoolean(
NEARBY_SUPPORT_TEST_APP, false /* defaultValue */);
if (!isSupportedTestApp) {
DeviceConfig.setProperty(NAMESPACE_TETHERING, NEARBY_SUPPORT_TEST_APP, "true", false);
}
assertThat(new NearbyConfiguration().isTestAppSupported()).isTrue();
final byte [] connectionStatus = new byte[] {1, 2, 3};
final byte [] batteryStatus = new byte[] {4, 5, 6};
final byte [] txPower = new byte[] {2};
final byte [] bluetoothAddr = new byte[] {1, 2, 3, 4, 5, 6};
final byte [] fastPairAccountKey = new byte[16];
// First byte is length of service data, padding zeros should be thrown away.
final byte [] bleServiceData = new byte[] {5, 1, 2, 3, 4, 5, 0, 0, 0, 0};
final byte [] testData = new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
final List<DataElement> expectedExtendedProperties = new ArrayList<>();
expectedExtendedProperties.add(new DataElement(DATA_TYPE_CONNECTION_STATUS_KEY,
connectionStatus));
expectedExtendedProperties.add(new DataElement(DATA_TYPE_BATTERY_KEY, batteryStatus));
expectedExtendedProperties.add(new DataElement(DATA_TYPE_TX_POWER_KEY, txPower));
expectedExtendedProperties.add(
new DataElement(DATA_TYPE_BLUETOOTH_ADDR_KEY, bluetoothAddr));
expectedExtendedProperties.add(
new DataElement(DATA_TYPE_FP_ACCOUNT_KEY, fastPairAccountKey));
expectedExtendedProperties.add(
new DataElement(DATA_TYPE_BLE_SERVICE_DATA_KEY, new byte[] {1, 2, 3, 4, 5}));
expectedExtendedProperties.add(
new DataElement(DATA_TYPE_TEST_1_KEY, testData));
expectedExtendedProperties.add(
new DataElement(DATA_TYPE_TEST_2_KEY, testData));
expectedExtendedProperties.add(
new DataElement(DATA_TYPE_TEST_3_KEY, testData));
expectedExtendedProperties.add(
new DataElement(DATA_TYPE_TEST_4_KEY, testData));
expectedExtendedProperties.add(
new DataElement(DATA_TYPE_TEST_5_KEY, testData));
Blefilter.PublicCredential credential =
Blefilter.PublicCredential.newBuilder()
.setSecretId(ByteString.copyFrom(new byte[] {1}))
.setAuthenticityKey(ByteString.copyFrom(new byte[2]))
.setPublicKey(ByteString.copyFrom(new byte[3]))
.setEncryptedMetadata(ByteString.copyFrom(new byte[4]))
.setEncryptedMetadataTag(ByteString.copyFrom(new byte[5]))
.build();
Blefilter.BleFilterResult result =
Blefilter.BleFilterResult.newBuilder()
.setTxPower(2)
.setRssi(1)
.setBluetoothAddress(ByteString.copyFrom(bluetoothAddr))
.setBleServiceData(ByteString.copyFrom(bleServiceData))
.setPublicCredential(credential)
.addDataElement(Blefilter.DataElement.newBuilder()
.setKey(
Blefilter.DataElement.ElementType
.DE_CONNECTION_STATUS)
.setValue(ByteString.copyFrom(connectionStatus))
.setValueLength(connectionStatus.length)
)
.addDataElement(Blefilter.DataElement.newBuilder()
.setKey(
Blefilter.DataElement.ElementType
.DE_BATTERY_STATUS)
.setValue(ByteString.copyFrom(batteryStatus))
.setValueLength(batteryStatus.length)
)
.addDataElement(Blefilter.DataElement.newBuilder()
.setKey(
Blefilter.DataElement.ElementType
.DE_FAST_PAIR_ACCOUNT_KEY)
.setValue(ByteString.copyFrom(fastPairAccountKey))
.setValueLength(fastPairAccountKey.length)
)
.addDataElement(Blefilter.DataElement.newBuilder()
.setKey(
Blefilter.DataElement.ElementType
.DE_TEST_1)
.setValue(ByteString.copyFrom(testData))
.setValueLength(testData.length)
)
.addDataElement(Blefilter.DataElement.newBuilder()
.setKey(
Blefilter.DataElement.ElementType
.DE_TEST_2)
.setValue(ByteString.copyFrom(testData))
.setValueLength(testData.length)
)
.addDataElement(Blefilter.DataElement.newBuilder()
.setKey(
Blefilter.DataElement.ElementType
.DE_TEST_3)
.setValue(ByteString.copyFrom(testData))
.setValueLength(testData.length)
)
.addDataElement(Blefilter.DataElement.newBuilder()
.setKey(
Blefilter.DataElement.ElementType
.DE_TEST_4)
.setValue(ByteString.copyFrom(testData))
.setValueLength(testData.length)
)
.addDataElement(Blefilter.DataElement.newBuilder()
.setKey(
Blefilter.DataElement.ElementType
.DE_TEST_5)
.setValue(ByteString.copyFrom(testData))
.setValueLength(testData.length)
)
.build();
Blefilter.BleFilterResults results =
Blefilter.BleFilterResults.newBuilder().addResult(result).build();
NanoAppMessage chre_message =
NanoAppMessage.createMessageToNanoApp(
ChreDiscoveryProvider.NANOAPP_ID,
ChreDiscoveryProvider.NANOAPP_MESSAGE_TYPE_FILTER_RESULT,
results.toByteArray());
mChreDiscoveryProvider.getController().setListener(mListener);
mChreDiscoveryProvider.init();
mChreDiscoveryProvider.onStart();
verify(mChreCommunication).start(mChreCallbackCaptor.capture(), any());
mChreCallbackCaptor.getValue().onMessageFromNanoApp(chre_message);
verify(mListener).onNearbyDeviceDiscovered(mNearbyDevice.capture());
List<DataElement> extendedProperties = PresenceDiscoveryResult
.fromDevice(mNearbyDevice.getValue()).getExtendedProperties();
assertThat(extendedProperties).containsExactlyElementsIn(expectedExtendedProperties);
// Reverts the setting of test app support
if (!isSupportedTestApp) {
DeviceConfig.setProperty(NAMESPACE_TETHERING, NEARBY_SUPPORT_TEST_APP, "false", false);
assertThat(new NearbyConfiguration().isTestAppSupported()).isFalse();
}
}
private boolean getDeviceConfigBoolean(final String name, final boolean defaultValue) {
final String value = getDeviceConfigProperty(name);
return value != null ? Boolean.parseBoolean(value) : defaultValue;
}
private String getDeviceConfigProperty(String name) {
return DeviceConfig.getProperty(DeviceConfig.NAMESPACE_TETHERING, name);
}
private static class InLineExecutor implements Executor {
@Override
public void execute(Runnable command) {
command.run();
}
}
}