diff --git a/nearby/framework/java/android/nearby/DataElement.java b/nearby/framework/java/android/nearby/DataElement.java index 4592c33a90..02548cb312 100644 --- a/nearby/framework/java/android/nearby/DataElement.java +++ b/nearby/framework/java/android/nearby/DataElement.java @@ -55,7 +55,9 @@ public final class DataElement implements Parcelable { DataType.ACCOUNT_KEY_DATA, DataType.CONNECTION_STATUS, DataType.BATTERY, - DataType.SCAN_MODE + DataType.SCAN_MODE, + DataType.TEST_DE_BEGIN, + DataType.TEST_DE_END }) public @interface DataType { int BLE_SERVICE_DATA = 100; @@ -74,6 +76,10 @@ public final class DataElement implements Parcelable { int ACCOUNT_KEY_DATA = 9; int CONNECTION_STATUS = 10; int BATTERY = 11; + // Reserves test DE ranges from {@link DataElement.DataType#TEST_DE_BEGIN} + // to {@link DataElement.DataType#TEST_DE_END}, inclusive. + int TEST_DE_BEGIN = 256; + int TEST_DE_END = 260; } /** @@ -108,6 +114,14 @@ public final class DataElement implements Parcelable { || mKey == DataType.PROVISIONED_IDENTITY; } + /** + * @return {@code true} if this is test data element type. + * @hide + */ + public static boolean isTestDeType(int type) { + return type >= DataType.TEST_DE_BEGIN && type <= DataType.TEST_DE_END; + } + /** * Constructs a {@link DataElement}. */ diff --git a/nearby/service/java/com/android/server/nearby/provider/ChreDiscoveryProvider.java b/nearby/service/java/com/android/server/nearby/provider/ChreDiscoveryProvider.java index d0d0de0371..611f47564c 100644 --- a/nearby/service/java/com/android/server/nearby/provider/ChreDiscoveryProvider.java +++ b/nearby/service/java/com/android/server/nearby/provider/ChreDiscoveryProvider.java @@ -20,10 +20,6 @@ import static android.nearby.ScanRequest.SCAN_TYPE_NEARBY_PRESENCE; import static com.android.server.nearby.NearbyService.TAG; -import static service.proto.Blefilter.DataElement.ElementType.DE_BATTERY_STATUS; -import static service.proto.Blefilter.DataElement.ElementType.DE_CONNECTION_STATUS; -import static service.proto.Blefilter.DataElement.ElementType.DE_FAST_PAIR_ACCOUNT_KEY; - import android.annotation.Nullable; import android.content.BroadcastReceiver; import android.content.Context; @@ -40,9 +36,11 @@ import android.nearby.ScanFilter; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; +import com.android.server.nearby.NearbyConfiguration; import com.google.protobuf.ByteString; +import java.util.Arrays; import java.util.Collections; import java.util.concurrent.Executor; @@ -52,13 +50,17 @@ import service.proto.Blefilter; public class ChreDiscoveryProvider extends AbstractDiscoveryProvider { // Nanoapp ID reserved for Nearby Presence. /** @hide */ - @VisibleForTesting public static final long NANOAPP_ID = 0x476f6f676c001031L; + @VisibleForTesting + public static final long NANOAPP_ID = 0x476f6f676c001031L; /** @hide */ - @VisibleForTesting public static final int NANOAPP_MESSAGE_TYPE_FILTER = 3; + @VisibleForTesting + public static final int NANOAPP_MESSAGE_TYPE_FILTER = 3; /** @hide */ - @VisibleForTesting public static final int NANOAPP_MESSAGE_TYPE_FILTER_RESULT = 4; + @VisibleForTesting + public static final int NANOAPP_MESSAGE_TYPE_FILTER_RESULT = 4; /** @hide */ - @VisibleForTesting public static final int NANOAPP_MESSAGE_TYPE_CONFIG = 5; + @VisibleForTesting + public static final int NANOAPP_MESSAGE_TYPE_CONFIG = 5; private static final int FP_ACCOUNT_KEY_LENGTH = 16; @@ -67,6 +69,7 @@ public class ChreDiscoveryProvider extends AbstractDiscoveryProvider { private boolean mChreStarted = false; private Blefilter.BleFilters mFilters = null; private Context mContext; + private NearbyConfiguration mNearbyConfiguration; private final IntentFilter mIntentFilter; private final BroadcastReceiver mScreenBroadcastReceiver = @@ -93,6 +96,7 @@ public class ChreDiscoveryProvider extends AbstractDiscoveryProvider { /** Initialize the CHRE discovery provider. */ public void init() { mChreCommunication.start(mChreCallback, Collections.singleton(NANOAPP_ID)); + mNearbyConfiguration = new NearbyConfiguration(); } @Override @@ -138,6 +142,9 @@ public class ChreDiscoveryProvider extends AbstractDiscoveryProvider { for (DataElement dataElement : presenceScanFilter.getExtendedProperties()) { if (dataElement.getKey() == DataElement.DataType.ACCOUNT_KEY_DATA) { filterBuilder.addDataElement(toProtoDataElement(dataElement)); + } else if (mNearbyConfiguration.isTestAppSupported() + && DataElement.isTestDeType(dataElement.getKey())) { + filterBuilder.addDataElement(toProtoDataElement(dataElement)); } } if (!presenceScanFilter.getPresenceActions().isEmpty()) { @@ -153,7 +160,8 @@ public class ChreDiscoveryProvider extends AbstractDiscoveryProvider { private Blefilter.PublicateCertificate toProtoPublicCredential(PublicCredential credential) { Log.d(TAG, String.format("Returns a PublicCertificate with authenticity key size %d and" - + " encrypted metadata key tag size %d", credential.getAuthenticityKey().length, + + " encrypted metadata key tag size %d", + credential.getAuthenticityKey().length, credential.getEncryptedMetadataKeyTag().length)); return Blefilter.PublicateCertificate.newBuilder() .setAuthenticityKey(ByteString.copyFrom(credential.getAuthenticityKey())) @@ -164,12 +172,13 @@ public class ChreDiscoveryProvider extends AbstractDiscoveryProvider { private Blefilter.DataElement toProtoDataElement(DataElement dataElement) { return Blefilter.DataElement.newBuilder() - .setKey( - Blefilter.DataElement.ElementType - .DE_FAST_PAIR_ACCOUNT_KEY) - .setValue(ByteString.copyFrom(dataElement.getValue())) - .setValueLength(FP_ACCOUNT_KEY_LENGTH) - .build(); + .setKey(Arrays.stream(Blefilter.DataElement.ElementType.values()) + .filter(p -> p.getNumber() == dataElement.getKey()) + .findFirst() + .get()) + .setValue(ByteString.copyFrom(dataElement.getValue())) + .setValueLength(dataElement.getValue().length) + .build(); } private void sendFilters(Blefilter.BleFilters filters) { @@ -258,10 +267,10 @@ public class ChreDiscoveryProvider extends AbstractDiscoveryProvider { } PresenceDevice.Builder presenceDeviceBuilder = new PresenceDevice.Builder( - String.valueOf(filterResult.hashCode()), - salt, - secretId, - encryptedMetaData) + String.valueOf(filterResult.hashCode()), + salt, + secretId, + encryptedMetaData) .setRssi(filterResult.getRssi()) .addMedium(NearbyDevice.Medium.BLE); // Data Elements reported from nanoapp added to Data Elements. @@ -306,11 +315,11 @@ public class ChreDiscoveryProvider extends AbstractDiscoveryProvider { PublicCredential publicCredential = new PublicCredential.Builder( - secretId, - authenticityKey, - publicKey, - encryptedMetaData, - encryptedMetaDataTag) + secretId, + authenticityKey, + publicKey, + encryptedMetaData, + encryptedMetaDataTag) .build(); NearbyDeviceParcelable device = @@ -353,6 +362,16 @@ public class ChreDiscoveryProvider extends AbstractDiscoveryProvider { element.getValue().substring(0, endIndex).toByteArray())); break; default: + if (mNearbyConfiguration.isTestAppSupported() + && DataElement.isTestDeType(element.getKey().getNumber())) { + presenceDeviceBuilder.addExtendedProperty( + new DataElement(Arrays.stream( + Blefilter.DataElement.ElementType.values()) + .filter(p -> p.getNumber() == element.getKey().getNumber()) + .findFirst() + .get().getNumber(), + element.getValue().substring(0, endIndex).toByteArray())); + } break; } } diff --git a/nearby/service/proto/src/presence/blefilter.proto b/nearby/service/proto/src/presence/blefilter.proto index 6e1ba6d73e..9b760c15bf 100644 --- a/nearby/service/proto/src/presence/blefilter.proto +++ b/nearby/service/proto/src/presence/blefilter.proto @@ -58,10 +58,20 @@ message PublicCredential { message DataElement { enum ElementType { + option allow_alias = true; + DE_NONE = 0; DE_FAST_PAIR_ACCOUNT_KEY = 9; DE_CONNECTION_STATUS = 10; DE_BATTERY_STATUS = 11; + // Reserves Test DEs. + DE_TEST_BEGIN = 256; + DE_TEST_1 = 256; + DE_TEST_2 = 257; + DE_TEST_3 = 258; + DE_TEST_4 = 259; + DE_TEST_5 = 260; + DE_TEST_END = 260; } optional ElementType key = 1; diff --git a/nearby/tests/unit/src/com/android/server/nearby/provider/ChreDiscoveryProviderTest.java b/nearby/tests/unit/src/com/android/server/nearby/provider/ChreDiscoveryProviderTest.java index 270de52237..d06a4470c5 100644 --- a/nearby/tests/unit/src/com/android/server/nearby/provider/ChreDiscoveryProviderTest.java +++ b/nearby/tests/unit/src/com/android/server/nearby/provider/ChreDiscoveryProviderTest.java @@ -16,6 +16,12 @@ 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; @@ -25,10 +31,12 @@ 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; @@ -59,11 +67,20 @@ public class ChreDiscoveryProviderTest { 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 = @@ -113,6 +130,14 @@ public class ChreDiscoveryProviderTest { @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}; @@ -120,6 +145,7 @@ public class ChreDiscoveryProviderTest { 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 expectedExtendedProperties = new ArrayList<>(); expectedExtendedProperties.add(new DataElement(DATA_TYPE_CONNECTION_STATUS_KEY, @@ -169,6 +195,41 @@ public class ChreDiscoveryProviderTest { .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(); @@ -187,6 +248,158 @@ public class ChreDiscoveryProviderTest { List 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 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 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 {