1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.nearby.common.bluetooth.fastpair; 18 19 import static com.android.server.nearby.common.bluetooth.fastpair.Constants.BLUETOOTH_ADDRESS_LENGTH; 20 import static com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic.ActionOverBleFlag.ADDITIONAL_DATA_CHARACTERISTIC; 21 import static com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic.ActionOverBleFlag.DEVICE_ACTION; 22 import static com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic.KeyBasedPairingRequestFlag.PROVIDER_INITIATES_BONDING; 23 import static com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic.KeyBasedPairingRequestFlag.REQUEST_DEVICE_NAME; 24 import static com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic.KeyBasedPairingRequestFlag.REQUEST_DISCOVERABLE; 25 import static com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic.KeyBasedPairingRequestFlag.REQUEST_RETROACTIVE_PAIR; 26 import static com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic.Request.ADDITIONAL_DATA_TYPE_INDEX; 27 28 import static com.google.common.truth.Truth.assertThat; 29 30 import static org.junit.Assert.assertThrows; 31 import static org.mockito.ArgumentMatchers.any; 32 import static org.mockito.ArgumentMatchers.anyLong; 33 import static org.mockito.Mockito.inOrder; 34 import static org.mockito.Mockito.when; 35 36 import android.platform.test.annotations.Presubmit; 37 38 import androidx.annotation.Nullable; 39 import androidx.core.util.Consumer; 40 import androidx.test.core.app.ApplicationProvider; 41 import androidx.test.ext.junit.runners.AndroidJUnit4; 42 import androidx.test.filters.SdkSuppress; 43 import androidx.test.filters.SmallTest; 44 45 import com.android.server.nearby.common.bluetooth.BluetoothException; 46 import com.android.server.nearby.common.bluetooth.BluetoothGattException; 47 import com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.AdditionalDataCharacteristic.AdditionalDataType; 48 import com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService.KeyBasedPairingCharacteristic.Request; 49 import com.android.server.nearby.common.bluetooth.gatt.BluetoothGattConnection; 50 import com.android.server.nearby.common.bluetooth.testability.android.bluetooth.BluetoothAdapter; 51 import com.android.server.nearby.common.bluetooth.util.BluetoothOperationExecutor; 52 import com.android.server.nearby.intdefs.NearbyEventIntDefs; 53 54 import com.google.common.collect.ImmutableSet; 55 import com.google.common.io.BaseEncoding; 56 57 import org.junit.Before; 58 import org.junit.Test; 59 import org.junit.runner.RunWith; 60 import org.mockito.InOrder; 61 import org.mockito.Mock; 62 import org.mockito.MockitoAnnotations; 63 64 import java.security.GeneralSecurityException; 65 import java.time.Duration; 66 import java.util.Arrays; 67 68 /** 69 * Unit tests for {@link HandshakeHandler}. 70 */ 71 @Presubmit 72 @SmallTest 73 @RunWith(AndroidJUnit4.class) 74 public class HandshakeHandlerTest { 75 76 public static final byte[] PUBLIC_KEY = 77 BaseEncoding.base64().decode( 78 "d2JTfvfdS6u7LmGfMOmco3C7ra3lW1k17AOly0LrBydDZURacfTY" 79 + "IMmo5K1ejfD9e8b6qHsDTNzselhifi10kQ=="); 80 private static final String SEEKER_ADDRESS = "A1:A2:A3:A4:A5:A6"; 81 private static final String PROVIDER_BLE_ADDRESS = "11:22:33:44:55:66"; 82 /** 83 * The random-resolvable private address (RPA) is sometimes used when advertising over BLE, to 84 * hide the static public address (otherwise, the Fast Pair device would b 85 * identifiable/trackable whenever it's BLE advertising). 86 */ 87 private static final String RANDOM_PRIVATE_ADDRESS = "BB:BB:BB:BB:BB:1E"; 88 private static final byte[] SHARED_SECRET = 89 BaseEncoding.base16().decode("0123456789ABCDEF0123456789ABCDEF"); 90 91 @Mock EventLoggerWrapper mEventLoggerWrapper; 92 @Mock BluetoothGattConnection mBluetoothGattConnection; 93 @Mock BluetoothGattConnection.ChangeObserver mChangeObserver; 94 @Mock private Consumer<Integer> mRescueFromError; 95 96 @Before setUp()97 public void setUp() { 98 MockitoAnnotations.initMocks(this); 99 } 100 101 @Test 102 @SdkSuppress(minSdkVersion = 32, codeName = "T") handshakeGattError_noRetryError_failed()103 public void handshakeGattError_noRetryError_failed() throws BluetoothException { 104 HandshakeHandler.KeyBasedPairingRequest keyBasedPairingRequest = 105 new HandshakeHandler.KeyBasedPairingRequest.Builder() 106 .setVerificationData(BluetoothAddress.decode(PROVIDER_BLE_ADDRESS)) 107 .build(); 108 BluetoothGattException exception = 109 new BluetoothGattException("Exception for no retry", 257); 110 when(mChangeObserver.waitForUpdate(anyLong())).thenThrow(exception); 111 GattConnectionManager gattConnectionManager = 112 createGattConnectionManager(Preferences.builder(), () -> {}); 113 gattConnectionManager.setGattConnection(mBluetoothGattConnection); 114 when(mBluetoothGattConnection.enableNotification(any(), any())) 115 .thenReturn(mChangeObserver); 116 InOrder inOrder = inOrder(mEventLoggerWrapper); 117 118 assertThrows( 119 BluetoothGattException.class, 120 () -> 121 getHandshakeHandler(gattConnectionManager, address -> address) 122 .doHandshakeWithRetryAndSignalLostCheck( 123 PUBLIC_KEY, 124 keyBasedPairingRequest, 125 mRescueFromError)); 126 127 inOrder.verify(mEventLoggerWrapper).setCurrentEvent( 128 NearbyEventIntDefs.EventCode.SECRET_HANDSHAKE_GATT_COMMUNICATION); 129 inOrder.verify(mEventLoggerWrapper).logCurrentEventFailed(exception); 130 inOrder.verifyNoMoreInteractions(); 131 } 132 133 @Test 134 @SdkSuppress(minSdkVersion = 32, codeName = "T") handshakeGattError_retryAndNoCount_throwException()135 public void handshakeGattError_retryAndNoCount_throwException() throws BluetoothException { 136 HandshakeHandler.KeyBasedPairingRequest keyBasedPairingRequest = 137 new HandshakeHandler.KeyBasedPairingRequest.Builder() 138 .setVerificationData(BluetoothAddress.decode(PROVIDER_BLE_ADDRESS)) 139 .build(); 140 BluetoothGattException exception = new BluetoothGattException("Exception for retry", 133); 141 when(mChangeObserver.waitForUpdate(anyLong())).thenThrow(exception); 142 GattConnectionManager gattConnectionManager = 143 createGattConnectionManager(Preferences.builder(), () -> {}); 144 gattConnectionManager.setGattConnection(mBluetoothGattConnection); 145 when(mBluetoothGattConnection.enableNotification(any(), any())) 146 .thenReturn(mChangeObserver); 147 InOrder inOrder = inOrder(mEventLoggerWrapper); 148 149 HandshakeHandler.HandshakeException handshakeException = 150 assertThrows( 151 HandshakeHandler.HandshakeException.class, 152 () -> getHandshakeHandler(gattConnectionManager, address -> address) 153 .doHandshakeWithRetryAndSignalLostCheck( 154 PUBLIC_KEY, keyBasedPairingRequest, mRescueFromError)); 155 156 inOrder.verify(mEventLoggerWrapper) 157 .setCurrentEvent(NearbyEventIntDefs.EventCode.SECRET_HANDSHAKE_GATT_COMMUNICATION); 158 inOrder.verify(mEventLoggerWrapper).logCurrentEventFailed(exception); 159 inOrder.verify(mEventLoggerWrapper) 160 .setCurrentEvent(NearbyEventIntDefs.EventCode.SECRET_HANDSHAKE_GATT_COMMUNICATION); 161 inOrder.verify(mEventLoggerWrapper).logCurrentEventFailed(exception); 162 inOrder.verify(mEventLoggerWrapper) 163 .setCurrentEvent(NearbyEventIntDefs.EventCode.SECRET_HANDSHAKE_GATT_COMMUNICATION); 164 inOrder.verify(mEventLoggerWrapper).logCurrentEventFailed(exception); 165 inOrder.verify(mEventLoggerWrapper) 166 .setCurrentEvent(NearbyEventIntDefs.EventCode.SECRET_HANDSHAKE_GATT_COMMUNICATION); 167 inOrder.verify(mEventLoggerWrapper).logCurrentEventFailed(exception); 168 inOrder.verifyNoMoreInteractions(); 169 assertThat(handshakeException.getOriginalException()).isEqualTo(exception); 170 } 171 172 @Test 173 @SdkSuppress(minSdkVersion = 32, codeName = "T") handshakeGattError_noRetryOnTimeout_throwException()174 public void handshakeGattError_noRetryOnTimeout_throwException() throws BluetoothException { 175 HandshakeHandler.KeyBasedPairingRequest keyBasedPairingRequest = 176 new HandshakeHandler.KeyBasedPairingRequest.Builder() 177 .setVerificationData(BluetoothAddress.decode(PROVIDER_BLE_ADDRESS)) 178 .build(); 179 BluetoothOperationExecutor.BluetoothOperationTimeoutException exception = 180 new BluetoothOperationExecutor.BluetoothOperationTimeoutException("Test timeout"); 181 when(mChangeObserver.waitForUpdate(anyLong())).thenThrow(exception); 182 GattConnectionManager gattConnectionManager = 183 createGattConnectionManager(Preferences.builder(), () -> {}); 184 gattConnectionManager.setGattConnection(mBluetoothGattConnection); 185 when(mBluetoothGattConnection.enableNotification(any(), any())) 186 .thenReturn(mChangeObserver); 187 InOrder inOrder = inOrder(mEventLoggerWrapper); 188 189 assertThrows( 190 HandshakeHandler.HandshakeException.class, 191 () -> 192 new HandshakeHandler( 193 gattConnectionManager, 194 PROVIDER_BLE_ADDRESS, 195 Preferences.builder().setRetrySecretHandshakeTimeout(false).build(), 196 mEventLoggerWrapper, 197 address -> address) 198 .doHandshakeWithRetryAndSignalLostCheck( 199 PUBLIC_KEY, keyBasedPairingRequest, mRescueFromError)); 200 201 inOrder.verify(mEventLoggerWrapper) 202 .setCurrentEvent(NearbyEventIntDefs.EventCode.SECRET_HANDSHAKE_GATT_COMMUNICATION); 203 inOrder.verify(mEventLoggerWrapper).logCurrentEventFailed(exception); 204 inOrder.verifyNoMoreInteractions(); 205 } 206 207 @Test 208 @SdkSuppress(minSdkVersion = 32, codeName = "T") handshakeGattError_signalLost()209 public void handshakeGattError_signalLost() throws BluetoothException { 210 HandshakeHandler.KeyBasedPairingRequest keyBasedPairingRequest = 211 new HandshakeHandler.KeyBasedPairingRequest.Builder() 212 .setVerificationData(BluetoothAddress.decode(PROVIDER_BLE_ADDRESS)) 213 .build(); 214 BluetoothGattException exception = new BluetoothGattException("Exception for retry", 133); 215 when(mChangeObserver.waitForUpdate(anyLong())).thenThrow(exception); 216 GattConnectionManager gattConnectionManager = 217 createGattConnectionManager(Preferences.builder(), () -> {}); 218 gattConnectionManager.setGattConnection(mBluetoothGattConnection); 219 when(mBluetoothGattConnection.enableNotification(any(), any())) 220 .thenReturn(mChangeObserver); 221 InOrder inOrder = inOrder(mEventLoggerWrapper); 222 223 SignalLostException signalLostException = 224 assertThrows( 225 SignalLostException.class, 226 () -> getHandshakeHandler(gattConnectionManager, address -> null) 227 .doHandshakeWithRetryAndSignalLostCheck( 228 PUBLIC_KEY, keyBasedPairingRequest, mRescueFromError)); 229 230 inOrder.verify(mEventLoggerWrapper) 231 .setCurrentEvent(NearbyEventIntDefs.EventCode.SECRET_HANDSHAKE_GATT_COMMUNICATION); 232 inOrder.verify(mEventLoggerWrapper).logCurrentEventFailed(exception); 233 assertThat(signalLostException).hasCauseThat().isEqualTo(exception); 234 } 235 236 @Test 237 @SdkSuppress(minSdkVersion = 32, codeName = "T") handshakeGattError_addressRotate()238 public void handshakeGattError_addressRotate() throws BluetoothException { 239 HandshakeHandler.KeyBasedPairingRequest keyBasedPairingRequest = 240 new HandshakeHandler.KeyBasedPairingRequest.Builder() 241 .setVerificationData(BluetoothAddress.decode(PROVIDER_BLE_ADDRESS)) 242 .build(); 243 BluetoothGattException exception = new BluetoothGattException("Exception for retry", 133); 244 when(mChangeObserver.waitForUpdate(anyLong())).thenThrow(exception); 245 GattConnectionManager gattConnectionManager = 246 createGattConnectionManager(Preferences.builder(), () -> {}); 247 gattConnectionManager.setGattConnection(mBluetoothGattConnection); 248 when(mBluetoothGattConnection.enableNotification(any(), any())) 249 .thenReturn(mChangeObserver); 250 InOrder inOrder = inOrder(mEventLoggerWrapper); 251 252 SignalRotatedException signalRotatedException = 253 assertThrows( 254 SignalRotatedException.class, 255 () -> getHandshakeHandler( 256 gattConnectionManager, address -> "AA:BB:CC:DD:EE:FF") 257 .doHandshakeWithRetryAndSignalLostCheck( 258 PUBLIC_KEY, keyBasedPairingRequest, mRescueFromError)); 259 260 inOrder.verify(mEventLoggerWrapper).setCurrentEvent( 261 NearbyEventIntDefs.EventCode.SECRET_HANDSHAKE_GATT_COMMUNICATION); 262 inOrder.verify(mEventLoggerWrapper).logCurrentEventFailed(exception); 263 assertThat(signalRotatedException.getNewAddress()).isEqualTo("AA:BB:CC:DD:EE:FF"); 264 assertThat(signalRotatedException).hasCauseThat().isEqualTo(exception); 265 } 266 267 @Test 268 @SdkSuppress(minSdkVersion = 32, codeName = "T") constructBytes_setRetroactiveFlag_decodeCorrectly()269 public void constructBytes_setRetroactiveFlag_decodeCorrectly() throws 270 GeneralSecurityException { 271 HandshakeHandler.KeyBasedPairingRequest keyBasedPairingRequest = 272 new HandshakeHandler.KeyBasedPairingRequest.Builder() 273 .setVerificationData(BluetoothAddress.decode(PROVIDER_BLE_ADDRESS)) 274 .addFlag(REQUEST_RETROACTIVE_PAIR) 275 .setSeekerPublicAddress(BluetoothAddress.decode(SEEKER_ADDRESS)) 276 .build(); 277 278 byte[] encryptedRawMessage = 279 AesEcbSingleBlockEncryption.encrypt( 280 SHARED_SECRET, keyBasedPairingRequest.getBytes()); 281 HandshakeRequest handshakeRequest = 282 new HandshakeRequest(SHARED_SECRET, encryptedRawMessage); 283 284 assertThat(handshakeRequest.getType()) 285 .isEqualTo(HandshakeRequest.Type.KEY_BASED_PAIRING_REQUEST); 286 assertThat(handshakeRequest.requestRetroactivePair()).isTrue(); 287 assertThat(handshakeRequest.getVerificationData()) 288 .isEqualTo(BluetoothAddress.decode(PROVIDER_BLE_ADDRESS)); 289 assertThat(handshakeRequest.getSeekerPublicAddress()) 290 .isEqualTo(BluetoothAddress.decode(SEEKER_ADDRESS)); 291 assertThat(handshakeRequest.requestDeviceName()).isFalse(); 292 assertThat(handshakeRequest.requestDiscoverable()).isFalse(); 293 assertThat(handshakeRequest.requestProviderInitialBonding()).isFalse(); 294 } 295 296 @Test 297 @SdkSuppress(minSdkVersion = 32, codeName = "T") getTimeout_notOverShortRetryMaxSpentTime_getShort()298 public void getTimeout_notOverShortRetryMaxSpentTime_getShort() { 299 Preferences preferences = Preferences.builder().build(); 300 301 assertThat(getHandshakeHandler(/* getEnable128BitCustomGattCharacteristicsId= */ true) 302 .getTimeoutMs( 303 preferences.getSecretHandshakeShortTimeoutRetryMaxSpentTimeMs() 304 - 1)) 305 .isEqualTo(preferences.getSecretHandshakeShortTimeoutMs()); 306 } 307 308 @Test 309 @SdkSuppress(minSdkVersion = 32, codeName = "T") getTimeout_overShortRetryMaxSpentTime_getLong()310 public void getTimeout_overShortRetryMaxSpentTime_getLong() { 311 Preferences preferences = Preferences.builder().build(); 312 313 assertThat(getHandshakeHandler(/* getEnable128BitCustomGattCharacteristicsId= */ true) 314 .getTimeoutMs( 315 preferences.getSecretHandshakeShortTimeoutRetryMaxSpentTimeMs() 316 + 1)) 317 .isEqualTo(preferences.getSecretHandshakeLongTimeoutMs()); 318 } 319 320 @Test 321 @SdkSuppress(minSdkVersion = 32, codeName = "T") getTimeout_retryNotEnabled_getOrigin()322 public void getTimeout_retryNotEnabled_getOrigin() { 323 Preferences preferences = Preferences.builder().build(); 324 325 assertThat( 326 new HandshakeHandler( 327 createGattConnectionManager(Preferences.builder(), () -> {}), 328 PROVIDER_BLE_ADDRESS, 329 Preferences.builder() 330 .setRetryGattConnectionAndSecretHandshake(false).build(), 331 mEventLoggerWrapper, 332 /* fastPairSignalChecker= */ null) 333 .getTimeoutMs(0)) 334 .isEqualTo(Duration.ofSeconds( 335 preferences.getGattOperationTimeoutSeconds()).toMillis()); 336 } 337 338 @Test 339 @SdkSuppress(minSdkVersion = 32, codeName = "T") triggersActionOverBle_notCrash()340 public void triggersActionOverBle_notCrash() { 341 HandshakeHandler.ActionOverBle.Builder actionOverBleBuilder = 342 new HandshakeHandler.ActionOverBle.Builder() 343 .addFlag( 344 Constants.FastPairService.KeyBasedPairingCharacteristic 345 .ActionOverBleFlag.ADDITIONAL_DATA_CHARACTERISTIC) 346 .setVerificationData(BluetoothAddress.decode(RANDOM_PRIVATE_ADDRESS)) 347 .setAdditionalDataType(AdditionalDataType.PERSONALIZED_NAME) 348 .setEvent(0, 0) 349 .setEventAdditionalData(new byte[]{1}) 350 .getThis(); 351 HandshakeHandler.ActionOverBle actionOverBle = actionOverBleBuilder.build(); 352 assertThat(actionOverBle.getBytes().length).isEqualTo(16); 353 assertThat( 354 Arrays.equals( 355 Arrays.copyOfRange(actionOverBle.getBytes(), 0, 12), 356 new byte[]{ 357 (byte) 16, (byte) -64, (byte) -69, (byte) -69, 358 (byte) -69, (byte) -69, (byte) -69, (byte) 30, 359 (byte) 0, (byte) 0, (byte) 1, (byte) 1})) 360 .isTrue(); 361 } 362 createGattConnectionManager( Preferences.Builder prefs, ToggleBluetoothTask toggleBluetooth)363 private GattConnectionManager createGattConnectionManager( 364 Preferences.Builder prefs, ToggleBluetoothTask toggleBluetooth) { 365 return new GattConnectionManager( 366 ApplicationProvider.getApplicationContext(), 367 prefs.build(), 368 new EventLoggerWrapper(null), 369 BluetoothAdapter.getDefaultAdapter(), 370 toggleBluetooth, 371 PROVIDER_BLE_ADDRESS, 372 new TimingLogger("GattConnectionManager", prefs.build()), 373 /* fastPairSignalChecker= */ null, 374 /* setMtu= */ false); 375 } 376 getHandshakeHandler( GattConnectionManager gattConnectionManager, @Nullable FastPairConnection.FastPairSignalChecker fastPairSignalChecker)377 private HandshakeHandler getHandshakeHandler( 378 GattConnectionManager gattConnectionManager, 379 @Nullable FastPairConnection.FastPairSignalChecker fastPairSignalChecker) { 380 return new HandshakeHandler( 381 gattConnectionManager, 382 PROVIDER_BLE_ADDRESS, 383 Preferences.builder() 384 .setGattConnectionAndSecretHandshakeNoRetryGattError(ImmutableSet.of(257)) 385 .setRetrySecretHandshakeTimeout(true) 386 .build(), 387 mEventLoggerWrapper, 388 fastPairSignalChecker); 389 } 390 getHandshakeHandler( boolean getEnable128BitCustomGattCharacteristicsId)391 private HandshakeHandler getHandshakeHandler( 392 boolean getEnable128BitCustomGattCharacteristicsId) { 393 return new HandshakeHandler( 394 createGattConnectionManager(Preferences.builder(), () -> {}), 395 PROVIDER_BLE_ADDRESS, 396 Preferences.builder() 397 .setGattOperationTimeoutSeconds(5) 398 .setEnable128BitCustomGattCharacteristicsId( 399 getEnable128BitCustomGattCharacteristicsId) 400 .build(), 401 mEventLoggerWrapper, 402 /* fastPairSignalChecker= */ null); 403 } 404 405 private static class HandshakeRequest { 406 407 /** 408 * 16 bytes data: 1-byte for type, 1-byte for flags, 6-bytes for provider's BLE address, 8 409 * bytes optional data. 410 * 411 * @see {go/fast-pair-spec-handshake-message1} 412 */ 413 private final byte[] mDecryptedMessage; 414 HandshakeRequest(byte[] key, byte[] encryptedPairingRequest)415 HandshakeRequest(byte[] key, byte[] encryptedPairingRequest) 416 throws GeneralSecurityException { 417 mDecryptedMessage = AesEcbSingleBlockEncryption.decrypt(key, encryptedPairingRequest); 418 } 419 420 /** 421 * Gets the type of this handshake request. Currently, we have 2 types: 0x00 for Key-based 422 * Pairing Request and 0x10 for Action Request. 423 */ getType()424 public Type getType() { 425 return Type.valueOf(mDecryptedMessage[Request.TYPE_INDEX]); 426 } 427 428 /** 429 * Gets verification data of this handshake request. 430 * Currently, we use Provider's BLE address. 431 */ getVerificationData()432 public byte[] getVerificationData() { 433 return Arrays.copyOfRange( 434 mDecryptedMessage, 435 Request.VERIFICATION_DATA_INDEX, 436 Request.VERIFICATION_DATA_INDEX + Request.VERIFICATION_DATA_LENGTH); 437 } 438 439 /** Gets Seeker's public address of the handshake request. */ getSeekerPublicAddress()440 public byte[] getSeekerPublicAddress() { 441 return Arrays.copyOfRange( 442 mDecryptedMessage, 443 Request.SEEKER_PUBLIC_ADDRESS_INDEX, 444 Request.SEEKER_PUBLIC_ADDRESS_INDEX + BLUETOOTH_ADDRESS_LENGTH); 445 } 446 447 /** Checks whether the Seeker request discoverability from flags byte. */ requestDiscoverable()448 public boolean requestDiscoverable() { 449 return (getFlags() & REQUEST_DISCOVERABLE) != 0; 450 } 451 452 /** 453 * Checks whether the Seeker requests that the Provider shall initiate bonding from 454 * flags byte. 455 */ requestProviderInitialBonding()456 public boolean requestProviderInitialBonding() { 457 return (getFlags() & PROVIDER_INITIATES_BONDING) != 0; 458 } 459 460 /** Checks whether the Seeker requests that the Provider shall notify the existing name. */ requestDeviceName()461 public boolean requestDeviceName() { 462 return (getFlags() & REQUEST_DEVICE_NAME) != 0; 463 } 464 465 /** Checks whether this is for retroactively writing account key. */ requestRetroactivePair()466 public boolean requestRetroactivePair() { 467 return (getFlags() & REQUEST_RETROACTIVE_PAIR) != 0; 468 } 469 470 /** Gets the flags of this handshake request. */ getFlags()471 private byte getFlags() { 472 return mDecryptedMessage[Request.FLAGS_INDEX]; 473 } 474 475 /** Checks whether the Seeker requests a device action. */ requestDeviceAction()476 public boolean requestDeviceAction() { 477 return (getFlags() & DEVICE_ACTION) != 0; 478 } 479 480 /** 481 * Checks whether the Seeker requests an action which will be followed by an additional 482 * data. 483 */ requestFollowedByAdditionalData()484 public boolean requestFollowedByAdditionalData() { 485 return (getFlags() & ADDITIONAL_DATA_CHARACTERISTIC) != 0; 486 } 487 488 /** Gets the {@link AdditionalDataType} of this handshake request. */ 489 @AdditionalDataType getAdditionalDataType()490 public int getAdditionalDataType() { 491 if (!requestFollowedByAdditionalData() 492 || mDecryptedMessage.length <= ADDITIONAL_DATA_TYPE_INDEX) { 493 return AdditionalDataType.UNKNOWN; 494 } 495 return mDecryptedMessage[ADDITIONAL_DATA_TYPE_INDEX]; 496 } 497 498 /** Enumerates the handshake message types. */ 499 public enum Type { 500 KEY_BASED_PAIRING_REQUEST(Request.TYPE_KEY_BASED_PAIRING_REQUEST), 501 ACTION_OVER_BLE(Request.TYPE_ACTION_OVER_BLE), 502 UNKNOWN((byte) 0xFF); 503 504 private final byte mValue; 505 Type(byte type)506 Type(byte type) { 507 mValue = type; 508 } 509 valueOf(byte value)510 public static Type valueOf(byte value) { 511 for (Type type : Type.values()) { 512 if (type.getValue() == value) { 513 return type; 514 } 515 } 516 return UNKNOWN; 517 } 518 getValue()519 public byte getValue() { 520 return mValue; 521 } 522 } 523 } 524 } 525