Merge "Add unit tests" into tm-mainline-prod
diff --git a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/Event.java b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/Event.java
index 0b50dfd..7a0548b 100644
--- a/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/Event.java
+++ b/nearby/service/java/com/android/server/nearby/common/bluetooth/fastpair/Event.java
@@ -133,14 +133,11 @@
Event that = (Event) o;
return this.mEventCode == that.getEventCode()
&& this.mTimestamp == that.getTimestamp()
- && (this.mProfile == null
- ? that.getProfile() == null : this.mProfile.equals(that.getProfile()))
&& (this.mBluetoothDevice == null
- ? that.getBluetoothDevice() == null :
- this.mBluetoothDevice.equals(that.getBluetoothDevice()))
- && (this.mException == null
- ? that.getException() == null :
- this.mException.equals(that.getException()));
+ ? that.getBluetoothDevice() == null :
+ this.mBluetoothDevice.equals(that.getBluetoothDevice()))
+ && (this.mProfile == null
+ ? that.getProfile() == null : this.mProfile.equals(that.getProfile()));
}
return false;
}
@@ -150,7 +147,6 @@
return Objects.hash(mEventCode, mTimestamp, mProfile, mBluetoothDevice, mException);
}
-
/**
* Builder
*/
diff --git a/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/BytesTest.java b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/BytesTest.java
new file mode 100644
index 0000000..2e46ef9
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/BytesTest.java
@@ -0,0 +1,59 @@
+/*
+ * 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.common.bluetooth.fastpair;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.filters.SdkSuppress;
+
+import junit.framework.TestCase;
+
+import java.nio.ByteOrder;
+import java.util.Arrays;
+
+/**
+ * Unit tests for {@link Bytes}.
+ */
+public class BytesTest extends TestCase {
+
+ private static final Bytes.Value VALUE1 =
+ new Bytes.Value(new byte[]{1, 2}, ByteOrder.BIG_ENDIAN);
+ private static final Bytes.Value VALUE2 =
+ new Bytes.Value(new byte[]{1, 2}, ByteOrder.BIG_ENDIAN);
+ private static final Bytes.Value VALUE3 =
+ new Bytes.Value(new byte[]{1, 3}, ByteOrder.BIG_ENDIAN);
+
+ @SdkSuppress(minSdkVersion = 32, codeName = "T")
+ public void testEquals_asExpected() {
+ assertThat(VALUE1.equals(VALUE2)).isTrue();
+ }
+
+ @SdkSuppress(minSdkVersion = 32, codeName = "T")
+ public void testNotEquals_asExpected() {
+ assertThat(VALUE1.equals(VALUE3)).isFalse();
+ }
+
+ @SdkSuppress(minSdkVersion = 32, codeName = "T")
+ public void testGetBytes_asExpected() {
+ assertThat(Arrays.equals(VALUE1.getBytes(ByteOrder.BIG_ENDIAN), new byte[]{1, 2})).isTrue();
+ }
+
+ @SdkSuppress(minSdkVersion = 32, codeName = "T")
+ public void testToString() {
+ assertThat(VALUE1.toString()).isEqualTo("0102");
+ }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/ConstantsTest.java b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/ConstantsTest.java
index f7ffa24..6684fbc 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/ConstantsTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/ConstantsTest.java
@@ -33,6 +33,8 @@
import com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic;
import com.android.server.nearby.common.bluetooth.gatt.BluetoothGattConnection;
+import com.google.common.collect.ImmutableList;
+
import junit.framework.TestCase;
import org.mockito.Mock;
@@ -44,6 +46,8 @@
*/
public class ConstantsTest extends TestCase {
+ private static final int PASSKEY = 32689;
+
@Mock
private BluetoothGattConnection mMockGattConnection;
@@ -78,4 +82,62 @@
assertThat(KeyBasedPairingCharacteristic.getId(mMockGattConnection))
.isEqualTo(OLD_KEY_BASE_PAIRING_CHARACTERISTICS);
}
+
+ @SdkSuppress(minSdkVersion = 32, codeName = "T")
+ public void test_accountKeyCharacteristic_notCrash() throws BluetoothException {
+ Constants.FastPairService.AccountKeyCharacteristic.getId(mMockGattConnection);
+ }
+
+ @SdkSuppress(minSdkVersion = 32, codeName = "T")
+ public void test_additionalDataCharacteristic_notCrash() throws BluetoothException {
+ Constants.FastPairService.AdditionalDataCharacteristic.getId(mMockGattConnection);
+ }
+
+ @SdkSuppress(minSdkVersion = 32, codeName = "T")
+ public void test_nameCharacteristic_notCrash() throws BluetoothException {
+ Constants.FastPairService.NameCharacteristic.getId(mMockGattConnection);
+ }
+
+ @SdkSuppress(minSdkVersion = 32, codeName = "T")
+ public void test_passKeyCharacteristic_encryptDecryptSuccessfully()
+ throws java.security.GeneralSecurityException {
+ byte[] secret = AesEcbSingleBlockEncryption.generateKey();
+
+ Constants.FastPairService.PasskeyCharacteristic.getId(mMockGattConnection);
+ assertThat(
+ Constants.FastPairService.PasskeyCharacteristic.decrypt(
+ Constants.FastPairService.PasskeyCharacteristic.Type.SEEKER,
+ secret,
+ Constants.FastPairService.PasskeyCharacteristic.encrypt(
+ Constants.FastPairService.PasskeyCharacteristic.Type.SEEKER,
+ secret,
+ PASSKEY))
+ ).isEqualTo(PASSKEY);
+ }
+
+ @SdkSuppress(minSdkVersion = 32, codeName = "T")
+ public void test_beaconActionsCharacteristic_notCrash() throws BluetoothException {
+ Constants.FastPairService.BeaconActionsCharacteristic.getId(mMockGattConnection);
+ for (byte b : ImmutableList.of(
+ (byte) Constants.FastPairService.BeaconActionsCharacteristic.BeaconActionType
+ .READ_BEACON_PARAMETERS,
+ (byte) Constants.FastPairService.BeaconActionsCharacteristic.BeaconActionType
+ .READ_PROVISIONING_STATE,
+ (byte) Constants.FastPairService.BeaconActionsCharacteristic.BeaconActionType
+ .SET_EPHEMERAL_IDENTITY_KEY,
+ (byte) Constants.FastPairService.BeaconActionsCharacteristic.BeaconActionType
+ .CLEAR_EPHEMERAL_IDENTITY_KEY,
+ (byte) Constants.FastPairService.BeaconActionsCharacteristic.BeaconActionType
+ .READ_EPHEMERAL_IDENTITY_KEY,
+ (byte) Constants.FastPairService.BeaconActionsCharacteristic.BeaconActionType
+ .RING,
+ (byte) Constants.FastPairService.BeaconActionsCharacteristic.BeaconActionType
+ .READ_RINGING_STATE,
+ (byte) Constants.FastPairService.BeaconActionsCharacteristic.BeaconActionType
+ .UNKNOWN
+ )) {
+ assertThat(Constants.FastPairService.BeaconActionsCharacteristic
+ .valueOf(b)).isEqualTo(b);
+ }
+ }
}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/CreateBondExceptionTest.java b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/CreateBondExceptionTest.java
new file mode 100644
index 0000000..052e696
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/CreateBondExceptionTest.java
@@ -0,0 +1,44 @@
+/*
+ * 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.common.bluetooth.fastpair;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.filters.SdkSuppress;
+
+import com.android.server.nearby.common.bluetooth.BluetoothException;
+import com.android.server.nearby.intdefs.FastPairEventIntDefs;
+
+import junit.framework.TestCase;
+
+/**
+ * Unit tests for {@link CreateBondException}.
+ */
+public class CreateBondExceptionTest extends TestCase {
+
+ private static final String FORMAT = "FORMAT";
+ private static final int REASON = 0;
+ private static final CreateBondException EXCEPTION = new CreateBondException(
+ FastPairEventIntDefs.CreateBondErrorCode.INCORRECT_VARIANT, REASON, FORMAT);
+
+ @SdkSuppress(minSdkVersion = 32, codeName = "T")
+ public void test_getter_asExpected() throws BluetoothException {
+ assertThat(EXCEPTION.getErrorCode()).isEqualTo(
+ FastPairEventIntDefs.CreateBondErrorCode.INCORRECT_VARIANT);
+ assertThat(EXCEPTION.getReason()).isSameInstanceAs(REASON);
+ }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/DeviceIntentReceiverTest.java b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/DeviceIntentReceiverTest.java
new file mode 100644
index 0000000..94bf111
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/DeviceIntentReceiverTest.java
@@ -0,0 +1,58 @@
+/*
+ * 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.common.bluetooth.fastpair;
+
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import android.bluetooth.BluetoothDevice;
+import android.content.Context;
+import android.content.Intent;
+
+import androidx.test.filters.SdkSuppress;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import junit.framework.TestCase;
+
+import org.mockito.Mock;
+
+/**
+ * Unit tests for {@link DeviceIntentReceiver}.
+ */
+public class DeviceIntentReceiverTest extends TestCase {
+ @Mock Preferences mPreferences;
+ @Mock BluetoothDevice mBluetoothDevice;
+
+ private DeviceIntentReceiver mDeviceIntentReceiver;
+ private Intent mIntent;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ initMocks(this);
+
+ Context context = InstrumentationRegistry.getInstrumentation().getContext();
+ mDeviceIntentReceiver = DeviceIntentReceiver.oneShotReceiver(
+ context, mPreferences, mBluetoothDevice);
+
+ mIntent = new Intent().putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
+ }
+
+ @SdkSuppress(minSdkVersion = 32, codeName = "T")
+ public void test_onReceive_notCrash() throws Exception {
+ mDeviceIntentReceiver.onReceive(mIntent);
+ }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/EventTest.java b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/EventTest.java
index 28e925f..1b63ad0 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/EventTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/EventTest.java
@@ -58,6 +58,13 @@
.setBluetoothDevice(BLUETOOTH_DEVICE)
.setProfile(PROFILE)
.build();
+ assertThat(event.hasBluetoothDevice()).isTrue();
+ assertThat(event.hasProfile()).isTrue();
+ assertThat(event.isFailure()).isTrue();
+ assertThat(event.toString()).isEqualTo(
+ "Event{eventCode=1120, timestamp=1234, profile=1, "
+ + "bluetoothDevice=11:22:33:44:55:66, "
+ + "exception=java.lang.Exception: Test exception}");
Parcel parcel = Parcel.obtain();
event.writeToParcel(parcel, event.describeContents());
@@ -70,5 +77,8 @@
assertThat(result.getEventCode()).isEqualTo(event.getEventCode());
assertThat(result.getBluetoothDevice()).isEqualTo(event.getBluetoothDevice());
assertThat(result.getProfile()).isEqualTo(event.getProfile());
+ assertThat(result.equals(event)).isTrue();
+
+ assertThat(Event.CREATOR.newArray(10)).isNotEmpty();
}
}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/HandshakeHandlerTest.java b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/HandshakeHandlerTest.java
new file mode 100644
index 0000000..5763d69
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/HandshakeHandlerTest.java
@@ -0,0 +1,493 @@
+/*
+ * 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.common.bluetooth.fastpair;
+
+import static com.android.server.nearby.common.bluetooth.fastpair.Constants.BLUETOOTH_ADDRESS_LENGTH;
+import static com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic.ActionOverBleFlag.ADDITIONAL_DATA_CHARACTERISTIC;
+import static com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic.ActionOverBleFlag.DEVICE_ACTION;
+import static com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic.KeyBasedPairingRequestFlag.PROVIDER_INITIATES_BONDING;
+import static com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic.KeyBasedPairingRequestFlag.REQUEST_DEVICE_NAME;
+import static com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic.KeyBasedPairingRequestFlag.REQUEST_DISCOVERABLE;
+import static com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic.KeyBasedPairingRequestFlag.REQUEST_RETROACTIVE_PAIR;
+import static com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic.Request.ADDITIONAL_DATA_TYPE_INDEX;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.when;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.annotation.Nullable;
+import androidx.core.util.Consumer;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.filters.SmallTest;
+
+import com.android.server.nearby.common.bluetooth.BluetoothException;
+import com.android.server.nearby.common.bluetooth.BluetoothGattException;
+import com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.AdditionalDataCharacteristic.AdditionalDataType;
+import com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic.Request;
+import com.android.server.nearby.common.bluetooth.gatt.BluetoothGattConnection;
+import com.android.server.nearby.common.bluetooth.testability.android.bluetooth.BluetoothAdapter;
+import com.android.server.nearby.common.bluetooth.util.BluetoothOperationExecutor;
+import com.android.server.nearby.intdefs.NearbyEventIntDefs;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.io.BaseEncoding;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.security.GeneralSecurityException;
+import java.time.Duration;
+import java.util.Arrays;
+
+/**
+ * Unit tests for {@link HandshakeHandler}.
+ */
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class HandshakeHandlerTest {
+
+ public static final byte[] PUBLIC_KEY =
+ BaseEncoding.base64().decode(
+ "d2JTfvfdS6u7LmGfMOmco3C7ra3lW1k17AOly0LrBydDZURacfTY"
+ + "IMmo5K1ejfD9e8b6qHsDTNzselhifi10kQ==");
+ private static final String SEEKER_ADDRESS = "A1:A2:A3:A4:A5:A6";
+ private static final String PROVIDER_BLE_ADDRESS = "11:22:33:44:55:66";
+ private static final byte[] SHARED_SECRET =
+ BaseEncoding.base16().decode("0123456789ABCDEF0123456789ABCDEF");
+
+ @Mock EventLoggerWrapper mEventLoggerWrapper;
+ @Mock BluetoothGattConnection mBluetoothGattConnection;
+ @Mock BluetoothGattConnection.ChangeObserver mChangeObserver;
+ @Mock private Consumer<Integer> mRescueFromError;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = 32, codeName = "T")
+ public void handshakeGattError_noRetryError_failed() throws BluetoothException {
+ HandshakeHandler.KeyBasedPairingRequest keyBasedPairingRequest =
+ new HandshakeHandler.KeyBasedPairingRequest.Builder()
+ .setVerificationData(BluetoothAddress.decode(PROVIDER_BLE_ADDRESS))
+ .build();
+ BluetoothGattException exception =
+ new BluetoothGattException("Exception for no retry", 257);
+ when(mChangeObserver.waitForUpdate(anyLong())).thenThrow(exception);
+ GattConnectionManager gattConnectionManager =
+ createGattConnectionManager(Preferences.builder(), () -> {});
+ gattConnectionManager.setGattConnection(mBluetoothGattConnection);
+ when(mBluetoothGattConnection.enableNotification(any(), any()))
+ .thenReturn(mChangeObserver);
+ InOrder inOrder = inOrder(mEventLoggerWrapper);
+
+ assertThrows(
+ BluetoothGattException.class,
+ () ->
+ getHandshakeHandler(gattConnectionManager, address -> address)
+ .doHandshakeWithRetryAndSignalLostCheck(
+ PUBLIC_KEY,
+ keyBasedPairingRequest,
+ mRescueFromError));
+
+ inOrder.verify(mEventLoggerWrapper).setCurrentEvent(
+ NearbyEventIntDefs.EventCode.SECRET_HANDSHAKE_GATT_COMMUNICATION);
+ inOrder.verify(mEventLoggerWrapper).logCurrentEventFailed(exception);
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = 32, codeName = "T")
+ public void handshakeGattError_retryAndNoCount_throwException() throws BluetoothException {
+ HandshakeHandler.KeyBasedPairingRequest keyBasedPairingRequest =
+ new HandshakeHandler.KeyBasedPairingRequest.Builder()
+ .setVerificationData(BluetoothAddress.decode(PROVIDER_BLE_ADDRESS))
+ .build();
+ BluetoothGattException exception = new BluetoothGattException("Exception for retry", 133);
+ when(mChangeObserver.waitForUpdate(anyLong())).thenThrow(exception);
+ GattConnectionManager gattConnectionManager =
+ createGattConnectionManager(Preferences.builder(), () -> {});
+ gattConnectionManager.setGattConnection(mBluetoothGattConnection);
+ when(mBluetoothGattConnection.enableNotification(any(), any()))
+ .thenReturn(mChangeObserver);
+ InOrder inOrder = inOrder(mEventLoggerWrapper);
+
+ HandshakeHandler.HandshakeException handshakeException =
+ assertThrows(
+ HandshakeHandler.HandshakeException.class,
+ () -> getHandshakeHandler(gattConnectionManager, address -> address)
+ .doHandshakeWithRetryAndSignalLostCheck(
+ PUBLIC_KEY, keyBasedPairingRequest, mRescueFromError));
+
+ inOrder.verify(mEventLoggerWrapper)
+ .setCurrentEvent(NearbyEventIntDefs.EventCode.SECRET_HANDSHAKE_GATT_COMMUNICATION);
+ inOrder.verify(mEventLoggerWrapper).logCurrentEventFailed(exception);
+ inOrder.verify(mEventLoggerWrapper)
+ .setCurrentEvent(NearbyEventIntDefs.EventCode.SECRET_HANDSHAKE_GATT_COMMUNICATION);
+ inOrder.verify(mEventLoggerWrapper).logCurrentEventFailed(exception);
+ inOrder.verify(mEventLoggerWrapper)
+ .setCurrentEvent(NearbyEventIntDefs.EventCode.SECRET_HANDSHAKE_GATT_COMMUNICATION);
+ inOrder.verify(mEventLoggerWrapper).logCurrentEventFailed(exception);
+ inOrder.verify(mEventLoggerWrapper)
+ .setCurrentEvent(NearbyEventIntDefs.EventCode.SECRET_HANDSHAKE_GATT_COMMUNICATION);
+ inOrder.verify(mEventLoggerWrapper).logCurrentEventFailed(exception);
+ inOrder.verifyNoMoreInteractions();
+ assertThat(handshakeException.getOriginalException()).isEqualTo(exception);
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = 32, codeName = "T")
+ public void handshakeGattError_noRetryOnTimeout_throwException() throws BluetoothException {
+ HandshakeHandler.KeyBasedPairingRequest keyBasedPairingRequest =
+ new HandshakeHandler.KeyBasedPairingRequest.Builder()
+ .setVerificationData(BluetoothAddress.decode(PROVIDER_BLE_ADDRESS))
+ .build();
+ BluetoothOperationExecutor.BluetoothOperationTimeoutException exception =
+ new BluetoothOperationExecutor.BluetoothOperationTimeoutException("Test timeout");
+ when(mChangeObserver.waitForUpdate(anyLong())).thenThrow(exception);
+ GattConnectionManager gattConnectionManager =
+ createGattConnectionManager(Preferences.builder(), () -> {});
+ gattConnectionManager.setGattConnection(mBluetoothGattConnection);
+ when(mBluetoothGattConnection.enableNotification(any(), any()))
+ .thenReturn(mChangeObserver);
+ InOrder inOrder = inOrder(mEventLoggerWrapper);
+
+ assertThrows(
+ HandshakeHandler.HandshakeException.class,
+ () ->
+ new HandshakeHandler(
+ gattConnectionManager,
+ PROVIDER_BLE_ADDRESS,
+ Preferences.builder().setRetrySecretHandshakeTimeout(false).build(),
+ mEventLoggerWrapper,
+ address -> address)
+ .doHandshakeWithRetryAndSignalLostCheck(
+ PUBLIC_KEY, keyBasedPairingRequest, mRescueFromError));
+
+ inOrder.verify(mEventLoggerWrapper)
+ .setCurrentEvent(NearbyEventIntDefs.EventCode.SECRET_HANDSHAKE_GATT_COMMUNICATION);
+ inOrder.verify(mEventLoggerWrapper).logCurrentEventFailed(exception);
+ inOrder.verifyNoMoreInteractions();
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = 32, codeName = "T")
+ public void handshakeGattError_signalLost() throws BluetoothException {
+ HandshakeHandler.KeyBasedPairingRequest keyBasedPairingRequest =
+ new HandshakeHandler.KeyBasedPairingRequest.Builder()
+ .setVerificationData(BluetoothAddress.decode(PROVIDER_BLE_ADDRESS))
+ .build();
+ BluetoothGattException exception = new BluetoothGattException("Exception for retry", 133);
+ when(mChangeObserver.waitForUpdate(anyLong())).thenThrow(exception);
+ GattConnectionManager gattConnectionManager =
+ createGattConnectionManager(Preferences.builder(), () -> {});
+ gattConnectionManager.setGattConnection(mBluetoothGattConnection);
+ when(mBluetoothGattConnection.enableNotification(any(), any()))
+ .thenReturn(mChangeObserver);
+ InOrder inOrder = inOrder(mEventLoggerWrapper);
+
+ SignalLostException signalLostException =
+ assertThrows(
+ SignalLostException.class,
+ () -> getHandshakeHandler(gattConnectionManager, address -> null)
+ .doHandshakeWithRetryAndSignalLostCheck(
+ PUBLIC_KEY, keyBasedPairingRequest, mRescueFromError));
+
+ inOrder.verify(mEventLoggerWrapper)
+ .setCurrentEvent(NearbyEventIntDefs.EventCode.SECRET_HANDSHAKE_GATT_COMMUNICATION);
+ inOrder.verify(mEventLoggerWrapper).logCurrentEventFailed(exception);
+ assertThat(signalLostException).hasCauseThat().isEqualTo(exception);
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = 32, codeName = "T")
+ public void handshakeGattError_addressRotate() throws BluetoothException {
+ HandshakeHandler.KeyBasedPairingRequest keyBasedPairingRequest =
+ new HandshakeHandler.KeyBasedPairingRequest.Builder()
+ .setVerificationData(BluetoothAddress.decode(PROVIDER_BLE_ADDRESS))
+ .build();
+ BluetoothGattException exception = new BluetoothGattException("Exception for retry", 133);
+ when(mChangeObserver.waitForUpdate(anyLong())).thenThrow(exception);
+ GattConnectionManager gattConnectionManager =
+ createGattConnectionManager(Preferences.builder(), () -> {});
+ gattConnectionManager.setGattConnection(mBluetoothGattConnection);
+ when(mBluetoothGattConnection.enableNotification(any(), any()))
+ .thenReturn(mChangeObserver);
+ InOrder inOrder = inOrder(mEventLoggerWrapper);
+
+ SignalRotatedException signalRotatedException =
+ assertThrows(
+ SignalRotatedException.class,
+ () -> getHandshakeHandler(
+ gattConnectionManager, address -> "AA:BB:CC:DD:EE:FF")
+ .doHandshakeWithRetryAndSignalLostCheck(
+ PUBLIC_KEY, keyBasedPairingRequest, mRescueFromError));
+
+ inOrder.verify(mEventLoggerWrapper).setCurrentEvent(
+ NearbyEventIntDefs.EventCode.SECRET_HANDSHAKE_GATT_COMMUNICATION);
+ inOrder.verify(mEventLoggerWrapper).logCurrentEventFailed(exception);
+ assertThat(signalRotatedException.getNewAddress()).isEqualTo("AA:BB:CC:DD:EE:FF");
+ assertThat(signalRotatedException).hasCauseThat().isEqualTo(exception);
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = 32, codeName = "T")
+ public void constructBytes_setRetroactiveFlag_decodeCorrectly() throws
+ GeneralSecurityException {
+ HandshakeHandler.KeyBasedPairingRequest keyBasedPairingRequest =
+ new HandshakeHandler.KeyBasedPairingRequest.Builder()
+ .setVerificationData(BluetoothAddress.decode(PROVIDER_BLE_ADDRESS))
+ .addFlag(REQUEST_RETROACTIVE_PAIR)
+ .setSeekerPublicAddress(BluetoothAddress.decode(SEEKER_ADDRESS))
+ .build();
+
+ byte[] encryptedRawMessage =
+ AesEcbSingleBlockEncryption.encrypt(
+ SHARED_SECRET, keyBasedPairingRequest.getBytes());
+ HandshakeRequest handshakeRequest =
+ new HandshakeRequest(SHARED_SECRET, encryptedRawMessage);
+
+ assertThat(handshakeRequest.getType())
+ .isEqualTo(HandshakeRequest.Type.KEY_BASED_PAIRING_REQUEST);
+ assertThat(handshakeRequest.requestRetroactivePair()).isTrue();
+ assertThat(handshakeRequest.getVerificationData())
+ .isEqualTo(BluetoothAddress.decode(PROVIDER_BLE_ADDRESS));
+ assertThat(handshakeRequest.getSeekerPublicAddress())
+ .isEqualTo(BluetoothAddress.decode(SEEKER_ADDRESS));
+ assertThat(handshakeRequest.requestDeviceName()).isFalse();
+ assertThat(handshakeRequest.requestDiscoverable()).isFalse();
+ assertThat(handshakeRequest.requestProviderInitialBonding()).isFalse();
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = 32, codeName = "T")
+ public void getTimeout_notOverShortRetryMaxSpentTime_getShort() {
+ Preferences preferences = Preferences.builder().build();
+
+ assertThat(getHandshakeHandler(/* getEnable128BitCustomGattCharacteristicsId= */ true)
+ .getTimeoutMs(
+ preferences.getSecretHandshakeShortTimeoutRetryMaxSpentTimeMs()
+ - 1))
+ .isEqualTo(preferences.getSecretHandshakeShortTimeoutMs());
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = 32, codeName = "T")
+ public void getTimeout_overShortRetryMaxSpentTime_getLong() {
+ Preferences preferences = Preferences.builder().build();
+
+ assertThat(getHandshakeHandler(/* getEnable128BitCustomGattCharacteristicsId= */ true)
+ .getTimeoutMs(
+ preferences.getSecretHandshakeShortTimeoutRetryMaxSpentTimeMs()
+ + 1))
+ .isEqualTo(preferences.getSecretHandshakeLongTimeoutMs());
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = 32, codeName = "T")
+ public void getTimeout_retryNotEnabled_getOrigin() {
+ Preferences preferences = Preferences.builder().build();
+
+ assertThat(
+ new HandshakeHandler(
+ createGattConnectionManager(Preferences.builder(), () -> {}),
+ PROVIDER_BLE_ADDRESS,
+ Preferences.builder()
+ .setRetryGattConnectionAndSecretHandshake(false).build(),
+ mEventLoggerWrapper,
+ /* fastPairSignalChecker= */ null)
+ .getTimeoutMs(0))
+ .isEqualTo(Duration.ofSeconds(
+ preferences.getGattOperationTimeoutSeconds()).toMillis());
+ }
+
+ private GattConnectionManager createGattConnectionManager(
+ Preferences.Builder prefs, ToggleBluetoothTask toggleBluetooth) {
+ return new GattConnectionManager(
+ ApplicationProvider.getApplicationContext(),
+ prefs.build(),
+ new EventLoggerWrapper(null),
+ BluetoothAdapter.getDefaultAdapter(),
+ toggleBluetooth,
+ PROVIDER_BLE_ADDRESS,
+ new TimingLogger("GattConnectionManager", prefs.build()),
+ /* fastPairSignalChecker= */ null,
+ /* setMtu= */ false);
+ }
+
+ private HandshakeHandler getHandshakeHandler(
+ GattConnectionManager gattConnectionManager,
+ @Nullable FastPairConnection.FastPairSignalChecker fastPairSignalChecker) {
+ return new HandshakeHandler(
+ gattConnectionManager,
+ PROVIDER_BLE_ADDRESS,
+ Preferences.builder()
+ .setGattConnectionAndSecretHandshakeNoRetryGattError(ImmutableSet.of(257))
+ .setRetrySecretHandshakeTimeout(true)
+ .build(),
+ mEventLoggerWrapper,
+ fastPairSignalChecker);
+ }
+
+ private HandshakeHandler getHandshakeHandler(
+ boolean getEnable128BitCustomGattCharacteristicsId) {
+ return new HandshakeHandler(
+ createGattConnectionManager(Preferences.builder(), () -> {}),
+ PROVIDER_BLE_ADDRESS,
+ Preferences.builder()
+ .setGattOperationTimeoutSeconds(5)
+ .setEnable128BitCustomGattCharacteristicsId(
+ getEnable128BitCustomGattCharacteristicsId)
+ .build(),
+ mEventLoggerWrapper,
+ /* fastPairSignalChecker= */ null);
+ }
+
+ private static class HandshakeRequest {
+
+ /**
+ * 16 bytes data: 1-byte for type, 1-byte for flags, 6-bytes for provider's BLE address, 8
+ * bytes optional data.
+ *
+ * @see {go/fast-pair-spec-handshake-message1}
+ */
+ private final byte[] mDecryptedMessage;
+
+ HandshakeRequest(byte[] key, byte[] encryptedPairingRequest)
+ throws GeneralSecurityException {
+ mDecryptedMessage = AesEcbSingleBlockEncryption.decrypt(key, encryptedPairingRequest);
+ }
+
+ /**
+ * Gets the type of this handshake request. Currently, we have 2 types: 0x00 for Key-based
+ * Pairing Request and 0x10 for Action Request.
+ */
+ public Type getType() {
+ return Type.valueOf(mDecryptedMessage[Request.TYPE_INDEX]);
+ }
+
+ /**
+ * Gets verification data of this handshake request.
+ * Currently, we use Provider's BLE address.
+ */
+ public byte[] getVerificationData() {
+ return Arrays.copyOfRange(
+ mDecryptedMessage,
+ Request.VERIFICATION_DATA_INDEX,
+ Request.VERIFICATION_DATA_INDEX + Request.VERIFICATION_DATA_LENGTH);
+ }
+
+ /** Gets Seeker's public address of the handshake request. */
+ public byte[] getSeekerPublicAddress() {
+ return Arrays.copyOfRange(
+ mDecryptedMessage,
+ Request.SEEKER_PUBLIC_ADDRESS_INDEX,
+ Request.SEEKER_PUBLIC_ADDRESS_INDEX + BLUETOOTH_ADDRESS_LENGTH);
+ }
+
+ /** Checks whether the Seeker request discoverability from flags byte. */
+ public boolean requestDiscoverable() {
+ return (getFlags() & REQUEST_DISCOVERABLE) != 0;
+ }
+
+ /**
+ * Checks whether the Seeker requests that the Provider shall initiate bonding from
+ * flags byte.
+ */
+ public boolean requestProviderInitialBonding() {
+ return (getFlags() & PROVIDER_INITIATES_BONDING) != 0;
+ }
+
+ /** Checks whether the Seeker requests that the Provider shall notify the existing name. */
+ public boolean requestDeviceName() {
+ return (getFlags() & REQUEST_DEVICE_NAME) != 0;
+ }
+
+ /** Checks whether this is for retroactively writing account key. */
+ public boolean requestRetroactivePair() {
+ return (getFlags() & REQUEST_RETROACTIVE_PAIR) != 0;
+ }
+
+ /** Gets the flags of this handshake request. */
+ private byte getFlags() {
+ return mDecryptedMessage[Request.FLAGS_INDEX];
+ }
+
+ /** Checks whether the Seeker requests a device action. */
+ public boolean requestDeviceAction() {
+ return (getFlags() & DEVICE_ACTION) != 0;
+ }
+
+ /**
+ * Checks whether the Seeker requests an action which will be followed by an additional
+ * data.
+ */
+ public boolean requestFollowedByAdditionalData() {
+ return (getFlags() & ADDITIONAL_DATA_CHARACTERISTIC) != 0;
+ }
+
+ /** Gets the {@link AdditionalDataType} of this handshake request. */
+ @AdditionalDataType
+ public int getAdditionalDataType() {
+ if (!requestFollowedByAdditionalData()
+ || mDecryptedMessage.length <= ADDITIONAL_DATA_TYPE_INDEX) {
+ return AdditionalDataType.UNKNOWN;
+ }
+ return mDecryptedMessage[ADDITIONAL_DATA_TYPE_INDEX];
+ }
+
+ /** Enumerates the handshake message types. */
+ public enum Type {
+ KEY_BASED_PAIRING_REQUEST(Request.TYPE_KEY_BASED_PAIRING_REQUEST),
+ ACTION_OVER_BLE(Request.TYPE_ACTION_OVER_BLE),
+ UNKNOWN((byte) 0xFF);
+
+ private final byte mValue;
+
+ Type(byte type) {
+ mValue = type;
+ }
+
+ public static Type valueOf(byte value) {
+ for (Type type : Type.values()) {
+ if (type.getValue() == value) {
+ return type;
+ }
+ }
+ return UNKNOWN;
+ }
+
+ public byte getValue() {
+ return mValue;
+ }
+ }
+ }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/LtvTest.java b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/LtvTest.java
new file mode 100644
index 0000000..81a5d92
--- /dev/null
+++ b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/fastpair/LtvTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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.common.bluetooth.fastpair;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SdkSuppress;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Unit tests for {@link Ltv}.
+ */
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class LtvTest {
+
+ @Test
+ @SdkSuppress(minSdkVersion = 32, codeName = "T")
+ public void testParseEmpty_throwsException() throws Ltv.ParseException {
+ assertThrows(Ltv.ParseException.class,
+ () -> Ltv.parse(new byte[]{0}));
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = 32, codeName = "T")
+ public void testParse_finishesSuccessfully() throws Ltv.ParseException {
+ assertThat(Ltv.parse(new byte[]{3, 4, 5, 6})).isNotEmpty();
+ }
+}
diff --git a/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothGattWrapperTest.java b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothGattWrapperTest.java
index ef37323..199146d 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothGattWrapperTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/common/bluetooth/testability/android/bluetooth/BluetoothGattWrapperTest.java
@@ -170,7 +170,7 @@
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void testDisconnect_callsWrapped() {
doNothing().when(mBluetoothGatt).disconnect();
- mBluetoothGatt.disconnect();
+ mBluetoothGattWrapper.disconnect();
verify(mBluetoothGatt).disconnect();
}
@@ -178,7 +178,7 @@
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void testClose_callsWrapped() {
doNothing().when(mBluetoothGatt).close();
- mBluetoothGatt.close();
+ mBluetoothGattWrapper.close();
verify(mBluetoothGatt).close();
}
}