#include "core/internal/mediums/ble.h" #include "core/internal/mediums/ble_advertisement_header.h" #include "core/internal/mediums/bloom_filter.h" #include "core/internal/mediums/utils.h" #include "core/internal/mediums/uuid.h" #include "platform/synchronized.h" namespace location { namespace nearby { namespace connections { namespace mediums { namespace ble_v2 { template class ProcessOnLostRunnable : public Runnable { public: explicit ProcessOnLostRunnable(Ptr> ble_v2) : ble_v2_(ble_v2) {} void run() override { ble_v2_->processOnLostTimeout(); } private: Ptr> ble_v2_; }; template class OnAdvertisementFoundRunnable : public Runnable { public: OnAdvertisementFoundRunnable( Ptr> ble_v2, Ptr peripheral, ConstPtr advertisement_data) : ble_v2_(ble_v2), peripheral_(peripheral), advertisement_data_(advertisement_data) {} // This method is synchronized because it affects class state, but is called // from a separate thread that fires whenever a BLE advertisement is seen. void run() override { Synchronized s(ble_v2_->lock_.get()); ble_v2_->discovered_peripheral_tracker_->processFoundBleAdvertisement( peripheral_, advertisement_data_.release(), MakePtr(new typename BLEV2::GATTAdvertisementFetcherFacade( ble_v2_))); } private: Ptr> ble_v2_; Ptr peripheral_; ScopedPtr> advertisement_data_; }; } // namespace ble_v2 template const std::int32_t BLEV2::kNumAdvertisementSlots = 2; template const std::int32_t BLEV2::kMaxAdvertisementLength = 512; template const std::int32_t BLEV2::kDummyServiceIdLength = 512; template const char* BLEV2::kCopresenceServiceUuid = "0000FEF3-0000-1000-8000-00805F9B34FB"; template const std::int64_t BLEV2::kOnLostTimeoutMillis = 15000; template const std::int64_t BLEV2::kGattAdvertisementOperationTimeoutMillis = 5000; template const std::int64_t BLEV2::kMinConnectionAttemptRecoveryDurationMillis = 1000; template const std::int32_t BLEV2::kMaxConnectionAttemptRecoveryFuzzDurationMillis = 10000; template const std::uint32_t BLEV2::kDefaultMtu = 512; // These two values make up the base UUID we use when advertising a slot. The // base is an all zero Version-3 name-based UUID. To turn this into an // advertisement slot UUID, we simply OR the least significant bits with the // slot number. // // More info about the format can be found here: // https://en.wikipedia.org/wiki/Universally_unique_identifier#Versions_3_and_5_(namespace_name-based) template const std::int64_t BLEV2::kAdvertisementUuidMsb = 0x0000000000003000; template const std::int64_t BLEV2::kAdvertisementUuidLsb = 0x8000000000000000; template BLEV2::BLEV2(Ptr> bluetooth_radio) : lock_(Platform::createLock()), platform_thread_offloader_(Platform::createSingleThreadExecutor()), prng_(MakePtr(new Prng())), hash_utils_(Platform::createHashUtils()), bluetooth_radio_(bluetooth_radio), bluetooth_adapter_(Platform::createBluetoothAdapter()), ble_medium_(Platform::createBLEMediumV2()), scanning_info_(), discovered_peripheral_tracker_( new DiscoveredPeripheralTracker()), on_lost_executor_(Platform::createScheduledExecutor()), advertising_info_(), gatt_server_info_(), accepting_connections_info_() {} template BLEV2::~BLEV2() { Synchronized s(lock_.get()); on_lost_executor_->shutdown(); platform_thread_offloader_->shutdown(); stopAdvertising(); stopAdvertisementGattServer(); stopAcceptingConnections(); stopScanning(); // discovered_peripheral_tracker is a ScopedPtr member and will take care of // itself. } template bool BLEV2::isAvailable() { // This is purposefully left un-synchronized like its java counterpart. // Callers should be able to query this without waiting for other operations // to complete first and this should be safe to call after shutdown. We would // have made it static, but it relies on variables from the constructor (like // ble_medium_ and bluetooth_adapter_). return !ble_medium_.isNull() && !bluetooth_adapter_.isNull(); } // Returns true if currently scanning for BLE advertisements. template bool BLEV2::isAdvertising() { Synchronized s(lock_.get()); return !advertising_info_.isNull(); } // Starts BLE advertising, delivering additional information through a GATT // server. template bool BLEV2::startAdvertising( const string& service_id, ConstPtr advertisement_bytes, BLEMediumV2::PowerMode::Value power_mode, const string& fast_advertisement_service_uuid) { Synchronized s(lock_.get()); // Avoid leaks. ScopedPtr> scoped_advertisement_bytes( advertisement_bytes); if (service_id.empty() || scoped_advertisement_bytes.isNull()) { // logger.atSevere().log("Refusing to start BLE advertising because a null // parameter was passed in."); return false; } if (scoped_advertisement_bytes->size() > kMaxAdvertisementLength) { // logger.atSevere().log("Refusing to start BLE advertising because the // advertisement was too long. Expected at most %d bytes but received %d.", // kMaxAdvertisementLength, scoped_advertisement_bytes->size()); return false; } // Note: We don't include logic checking/using the fast_pair_model_id because // that is a java-only concept for now. if (isAdvertising()) { // logger.atSevere().log("Failed to BLE advertise because we're already // advertising."); return false; } if (!bluetooth_radio_->isEnabled()) { // logger.atSevere().log("Can't start BLE advertising because Bluetooth // isn't enabled."); return false; } if (!isAvailable()) { // logger.atSevere().log("Can't start BLE advertising because BLE is not // available."); return false; } // TODO(ahlee): Remove this check here and in the java code (redundant) // Stop any existing advertisement GATT servers. We don't stop it in // stopAdvertising() to avoid GATT issues with BLE sockets. if (isAdvertisementGattServerRunning()) { stopAdvertisementGattServer(); } // Start a GATT server to deliver the full advertisement data. If we fail to // advertise the header, we must shut this down before the method returns. bool is_fast_advertisement = !fast_advertisement_service_uuid.empty(); if (!is_fast_advertisement) { if (!startAdvertisementGattServer(service_id, scoped_advertisement_bytes.get())) { // logger.atSevere().log("Failed to to BLE advertise because the // advertisement GATT server failed to start"); return false; } } ScopedPtr> advertisement_header_bytes( createAdvertisementHeader(service_id, scoped_advertisement_bytes.get(), is_fast_advertisement)); if (advertisement_header_bytes.isNull()) { // logger.atSevere().log("Failed to to BLE advertise because we could not // create an advertisement header"); // We failed to start BLE advertising, so stop the advertisement GATT // server. stopAdvertisementGattServer(); return false; } ScopedPtr> advertisement( new BLEAdvertisementData()); advertisement->is_connectable = true; advertisement->tx_power_level = BLEAdvertisementData::UNSPECIFIED_TX_POWER_LEVEL; ScopedPtr> scan_response( new BLEAdvertisementData()); scan_response->is_connectable = true; scan_response->tx_power_level = BLEAdvertisementData::UNSPECIFIED_TX_POWER_LEVEL; scan_response->service_uuids.insert(kCopresenceServiceUuid); scan_response->service_data.insert(std::make_pair( kCopresenceServiceUuid, advertisement_header_bytes.release())); // Note: We don't use fast pair data because that is java-only for now. // TODO(ahlee): Fix this if check in the java code. if (is_fast_advertisement) { ScopedPtr> service_id_hash( generateServiceIdHash(BLEAdvertisement::Version::V2, service_id)); ScopedPtr> fast_advertisement_bytes( BLEAdvertisement::toBytes( BLEAdvertisement::Version::V2, BLEAdvertisement::SocketVersion::V2, service_id_hash.get(), scoped_advertisement_bytes.get())); if (fast_advertisement_bytes.isNull()) { // logger.atSevere().log("Failed to BLE advertise because we could not // create a fast advertisement for service UUID %s.", // fast_advertisement_service_uuid); // We shouldn't have started an advertisement GATT server in the first // place if we are using fast advertisements. However, to avoid careless // leaks, try shutting down the server anyway. stopAdvertisementGattServer(); return false; } advertisement->service_data.insert(std::make_pair( fast_advertisement_service_uuid, fast_advertisement_bytes.release())); scan_response->service_uuids.insert(fast_advertisement_service_uuid); } if (!ble_medium_->startAdvertising(ConstifyPtr(advertisement.release()), ConstifyPtr(scan_response.release()), power_mode)) { // If BLE advertising was not successful, stop the advertisement GATT // server. stopAdvertisementGattServer(); return false; } // logger.atVerbose().flog("Started BLE advertising with advertisement %s for // serviceID %s.", advertisement_header, service_id); advertising_info_ = MakePtr(new AdvertisingInfo(service_id)); return true; } template ConstPtr BLEV2::createAdvertisementHeader( const string& service_id, ConstPtr advertisement_bytes, bool is_fast_advertisement) { // Create a randomized dummy service ID to anonymize our header with. string dummy_service_id; dummy_service_id.reserve(kDummyServiceIdLength); for (int i = 0; i < kDummyServiceIdLength; i++) { dummy_service_id[i] = static_cast(prng_->nextInt32() & 0x000000FF); } // Put the service ID along with the dummy service ID into our bloom filter // Note: BloomFilter length should always match // BLEAdvertisementHeader::kServiceIdBloomFilterLength ScopedPtr>> bloom_filter(new BloomFilter<10>()); bloom_filter->add(dummy_service_id); // Only add the service ID to our bloom filter if it's not a fast // advertisement. Fast advertisements want discoverers to avoid reading our // GATT advertisement. if (!is_fast_advertisement) { bloom_filter->add(service_id); } // Create a hash seeded from dummy_service_id + advertisementBytes // // First, populate advertisement_bodies with the dummy_service_id and // advertisement_bytes. string advertisement_bodies; advertisement_bodies.reserve(dummy_service_id.size() + advertisement_bytes->size()); advertisement_bodies.append(dummy_service_id.data(), dummy_service_id.size()); advertisement_bodies.append(advertisement_bytes->getData(), advertisement_bytes->size()); // Then, generate the advertisement hash from the populated // advertisement_bodies string. ScopedPtr> advertisement_bodies_byte_array(MakeConstPtr( new ByteArray(advertisement_bodies.data(), advertisement_bodies.size()))); ScopedPtr> advertisement_hash( generateAdvertisementHash(advertisement_bodies_byte_array.get())); ScopedPtr> bloom_filter_bytes(bloom_filter->asBytes()); string ble_advertisement_header_string = BLEAdvertisementHeader::asString( BLEAdvertisementHeader::Version::V2, kNumAdvertisementSlots, bloom_filter_bytes.get(), advertisement_hash.get()); return MakeConstPtr(new ByteArray(ble_advertisement_header_string.data(), ble_advertisement_header_string.size())); } // Stops BLE advertising. template void BLEV2::stopAdvertising() { Synchronized s(lock_.get()); if (!isAdvertising()) { // logger.atDebug().log("Can't turn off BLE advertising because it never // started."); return; } ble_medium_->stopAdvertising(); // Reset advertising_info_to mark that we're no longer advertising. advertising_info_.destroy(); // Do NOT stop the advertisement GATT server here. Doing so will cause any // other existing GATT connections to stop receiving callbacks. This affects // our BLE sockets. Therefore, we only stop it in shutdown() and // startAdvertising(), where it is safe to do so. At those two points, we // shouldn't expect any BLE sockets to be connected. // logger.atVerbose().log("Turned BLE advertising off"); } // Returns true if currently scanning for BLE advertisements. template bool BLEV2::isScanning() { Synchronized s(lock_.get()); return !scanning_info_.isNull(); } // Starts scanning for BLE advertisements (if it is possible for the device). template bool BLEV2::startScanning( const string& service_id, Ptr discovered_peripheral_callback, BLEMediumV2::PowerMode::Value power_mode, const string& fast_advertisement_service_uuid) { Synchronized s(lock_.get()); // Avoid leaks. ScopedPtr> scoped_discovered_peripheral_callback(discovered_peripheral_callback); if (service_id.empty() || scoped_discovered_peripheral_callback.isNull()) { // logger.atSevere().log("Refusing to start BLE scanning because at least // one of workSource, serviceId, or discoveredPeripheralCallback is null."); return false; } if (isScanning()) { // logger.atSevere().log("Refusing to start BLE scanning because we are // already scanning."); return false; } if (!bluetooth_radio_->isEnabled()) { // logger.atSevere().log("Can't start BLE scanning because Bluetooth was // never turned on"); return false; } if (!isAvailable()) { // logger.atSevere().log("Can't start BLE scanning because BLE is not // available."); return false; } discovered_peripheral_tracker_->startTracking( service_id, scoped_discovered_peripheral_callback.release(), fast_advertisement_service_uuid); // Avoid leaks. ScopedPtr> scan_callback_facade( new ScanCallbackFacade(self_)); std::set service_uuids; service_uuids.insert(kCopresenceServiceUuid); if (!ble_medium_->startScanning(service_uuids, power_mode, scan_callback_facade.get())) { discovered_peripheral_tracker_->stopTracking(service_id); return false; } // logger.atVerbose().log("Started BLE scanning for serviceID %s.", // service_id); scanning_info_ = MakePtr(new ScanningInfo( service_id, scan_callback_facade.release(), createOnLostAlarm())); return true; } template void BLEV2::onAdvertisementFoundImpl( Ptr ble_peripheral, ConstPtr advertisement_data) { offloadFromPlatformThread( MakePtr(new ble_v2::OnAdvertisementFoundRunnable( self_, ble_peripheral, advertisement_data))); } // This method is synchronized because it affects class state, but is called // from a separate thread that has a recurring alarm running on it. template void BLEV2::processOnLostTimeout() { Synchronized s(lock_.get()); discovered_peripheral_tracker_->processLostGattAdvertisements(); } // Stops scanning for BLE advertisements. template void BLEV2::stopScanning() { Synchronized s(lock_.get()); if (!isScanning()) { // logger.atDebug().log("Can't turn off BLE scanning because we never // started scanning."); return; } scanning_info_->on_lost_alarm->cancel(); ble_medium_->stopScanning(); discovered_peripheral_tracker_->stopTracking(scanning_info_->service_id); // Reset our bundle of scanning state to mark that we're no longer scanning. scanning_info_.destroy(); } // TODO(b/112199086) Change to RecurringCancelableAlarm template Ptr> BLEV2::createOnLostAlarm() { return Ptr>(); } // Returns true if the device is currently accepting incoming BLE socket // connections. template bool BLEV2::isAcceptingConnections() { Synchronized s(lock_.get()); return !accepting_connections_info_.isNull(); } // Starts accepting incoming BLE socket connections. template bool BLEV2::startAcceptingConnections( const string& service_id, Ptr accepted_connection_callback) { Synchronized s(lock_.get()); // Avoid leaks. ScopedPtr> scoped_accepted_connection_callback(accepted_connection_callback); if (service_id.empty() || scoped_accepted_connection_callback.isNull()) { // logger.atSevere().log("Refusing to start accepting BLE connections // because at least one of serviceId or acceptedConnectionCallback is // null."); return false; } if (isAcceptingConnections()) { // logger.atSevere().log("Refusing to start accepting BLE connections for %s // because another BLE server socket is already in-progress.", service_id); return false; } if (!bluetooth_radio_->isEnabled()) { // logger.atSevere().log("Can't start accepting BLE connections for %s // because Bluetooth isn't enabled.", service_id); return false; } if (!isAvailable()) { // logger.atSevere().log("Can't start accepting BLE connections for %s // because BLE is not available.", service_id); return false; } // TODO(ahlee): Implement w/ the rest of the connecting logic. // Default to returning true and creating accepting_connections_info_ so we // can test the advertising and discovery flow fully. accepting_connections_info_ = MakePtr(new AcceptingConnectionsInfo(service_id)); return true; } // Stops accepting incoming BLE socket connections. template void BLEV2::stopAcceptingConnections() { Synchronized s(lock_.get()); if (!isAcceptingConnections()) { // logger.atDebug().log("Can't stop accepting BLE connections because it was // never started."); return; } ble_medium_->stopListeningForIncomingBLESockets(); // Reset our bundle of accepting connections state to mark that we're no // longer accepting connections. accepting_connections_info_.destroy(); } // Note: getGattConnectionBackoffPeriodMillis is only used in the java version // of reliablyConnect() for now. // Returns true if the advertisement GATT server is currently running. template bool BLEV2::isAdvertisementGattServerRunning() { return !gatt_server_info_.isNull(); } // Starts a GATT server to deliver additional advertisement data. Returns true // if the server was started successfully. template bool BLEV2::startAdvertisementGattServer( const string& service_id, ConstPtr advertisement) { // advertisement is not being wrapped in a ScopedPtr because ownership is not // passed on from startAdvertising(). if (isAdvertisementGattServerRunning()) { // logger.atSevere().log("Refusing to start an advertisement GATT server // because one is already running."); return false; } // Create a BleAdvertisement to wrap over the passed in advertisement. ScopedPtr> legacy_service_id_hash( generateServiceIdHash(BLEAdvertisement::Version::V1, service_id)); ScopedPtr> legacy_ble_advertisement_bytes( BLEAdvertisement::toBytes(BLEAdvertisement::Version::V1, BLEAdvertisement::SocketVersion::V1, legacy_service_id_hash.get(), advertisement)); if (legacy_ble_advertisement_bytes.isNull()) { // logger.atSevere().log("Refusing to start an advertisement GATT server // because creating a legacy BleAdvertisement with service ID %s failed.", // service_id); return false; } ScopedPtr> service_id_hash( generateServiceIdHash(BLEAdvertisement::Version::V2, service_id)); ScopedPtr> ble_advertisement_bytes( BLEAdvertisement::toBytes(BLEAdvertisement::Version::V2, BLEAdvertisement::SocketVersion::V2, service_id_hash.get(), advertisement)); if (ble_advertisement_bytes.isNull()) { // logger.atSevere().log("Refusing to start an advertisement GATT server // because creating a BleAdvertisement with service ID %s failed.", // service_id); return false; } return internalStartAdvertisementGattServer( legacy_ble_advertisement_bytes.release(), ble_advertisement_bytes.release()); } template bool BLEV2::internalStartAdvertisementGattServer( ConstPtr legacy_ble_advertisement_bytes, ConstPtr ble_advertisement_bytes) { // Avoid leaks. ScopedPtr> scoped_legacy_ble_advertisement_bytes( legacy_ble_advertisement_bytes); ScopedPtr> scoped_ble_advertisement_bytes( ble_advertisement_bytes); ScopedPtr> connection_lifecycle_callback( new ServerGATTConnectionLifecycleCallbackFacade(self_)); ScopedPtr> gatt_server( ble_medium_->startGATTServer(connection_lifecycle_callback.get())); if (gatt_server.isNull()) { // logger.atSevere().withCause(e).log("Unable to start an advertisement GATT // server."); return false; } if (!generateAdvertisementCharacteristic( /* slot= */ 0, scoped_legacy_ble_advertisement_bytes.release(), gatt_server.get())) { gatt_server->stop(); return false; } if (!generateAdvertisementCharacteristic( /* slot= */ 1, scoped_ble_advertisement_bytes.release(), gatt_server.get())) { gatt_server->stop(); return false; } // GattCharacteristic is not included in GATTServerInfo because we don't need // it after it's been updated. gatt_server_info_ = MakePtr(new GATTServerInfo( gatt_server.release(), connection_lifecycle_callback.release())); return true; } template bool BLEV2::generateAdvertisementCharacteristic( std::int32_t slot, ConstPtr advertisement, Ptr gatt_server) { // Avoid leaks. ScopedPtr> scoped_advertisement(advertisement); std::set permissions; permissions.insert(GATTCharacteristic::Permission::READ); std::set properties; properties.insert(GATTCharacteristic::Property::READ); Ptr gatt_characteristic(gatt_server->createCharacteristic( kCopresenceServiceUuid, generateAdvertisementUuid(slot), permissions, properties)); if (gatt_characteristic.isNull()) { // logger.atSevere().withCause(e).log("Unable to create and add a // characterstic to the gatt server for the advertisement."); return false; } if (!gatt_server->updateCharacteristic(gatt_characteristic, scoped_advertisement.release())) { // logger.atSevere().withCause(e).log("Unable to write a value to the GATT // characteristic."); return false; } return true; } // Note: In the java counterpart this in a utils class. // Generates a characteristic UUID for an advertisement at the given slot. template string BLEV2::generateAdvertisementUuid(std::int32_t slot) { return UUID(kAdvertisementUuidMsb, kAdvertisementUuidLsb | slot) .str(); } // Stops a GATT server used for additional advertisement data. template void BLEV2::stopAdvertisementGattServer() { Synchronized s(lock_.get()); if (!isAdvertisementGattServerRunning()) { // logger.atSevere().log("Unable to stop the advertisement GATT server // because it's not running."); return; } gatt_server_info_->gatt_server->stop(); gatt_server_info_.destroy(); } // Connects to a GATT server, reads advertisement data, and then disconnects // from the GATT server. This method blocks until all advertisements are read, // or a connection error occurs. template Ptr> BLEV2::processFetchGattAdvertisementsRequest( Ptr peripheral, std::int32_t num_slots, Ptr> advertisement_read_result) { Synchronized s(lock_.get()); if (advertisement_read_result.isNull()) { advertisement_read_result = MakeRefCountedPtr(new AdvertisementReadResult()); } if (peripheral.isNull()) { // logger.atSevere().log("Can't read from an advertisement GATT server // because ble peripheral is null."); return advertisement_read_result; } if (!bluetooth_radio_->isEnabled()) { // logger.atSevere().log("Can't read from an advertisement GATT server // because Bluetooth was never turned on."); return advertisement_read_result; } if (!isAvailable()) { // logger.atSevere().log("Can't read from an advertisement GATT server // because BLE is not available."); return advertisement_read_result; } return internalReadFromAdvertisementGattServer(peripheral, num_slots, advertisement_read_result); } template Ptr> BLEV2::internalReadFromAdvertisementGattServer( Ptr peripheral, std::int32_t num_slots, Ptr> advertisement_read_result) { // Attempt to connect and read some GATT characteristics. bool read_success = true; ScopedPtr> connection_lifecycle_callback( new ClientGATTConnectionLifecycleCallbackFacade(self_)); ScopedPtr> gatt_connection( ble_medium_->connectToGATTServer(peripheral, kDefaultMtu, BLEMediumV2::PowerMode::HIGH, connection_lifecycle_callback.get())); if (!gatt_connection.isNull() && gatt_connection->discoverServices()) { // Read all advertisements from all slots that we haven't read from yet. for (std::int32_t slot = 0; slot < num_slots; ++slot) { // Make sure we haven't already read this advertisement before. if (advertisement_read_result->hasAdvertisement(slot)) { continue; } // Make sure the characteristic even exists for this slot number. If the // characteristic doesn't exist, we shouldn't count the fetch as a // failure because there's nothing we could've done about a non-existent // characteristic. Ptr gatt_characteristic( gatt_connection->getCharacteristic(kCopresenceServiceUuid, generateAdvertisementUuid(slot))); if (/* !advertisementSlotExists()= */ gatt_characteristic.isNull()) { continue; } // Read advertisement data from the characteristic associated with this // slot. ScopedPtr> characteristic_value( gatt_connection->readCharacteristic(gatt_characteristic)); if (!characteristic_value.isNull()) { advertisement_read_result->addAdvertisement( slot, characteristic_value.release()); // logger.atVerbose().log("Successfully read advertisement at slot %d // on peripheral %s.", slot, peripheral); } else { // logger.atWarning().withCause(characteristicReadException).log("Can't // read advertisement for slot %d on peripheral %s.", slot, // peripheral); read_success = false; } // Whether or not we succeeded with this slot, we should try reading the // other slots to get as many advertisements as possible before // returning a success or failure. } gatt_connection->disconnect(); } else { // logger.atWarning().withCause(connectException).log("Can't connect to an // advertisement GATT server for peripheral %s.", peripheral); read_success = false; } advertisement_read_result->recordLastReadStatus(read_success); return advertisement_read_result; } template void BLEV2::offloadFromPlatformThread(Ptr runnable) { platform_thread_offloader_->execute(runnable); } template ConstPtr BLEV2::generateAdvertisementHash( ConstPtr advertisement_bytes) { return Utils::sha256Hash(hash_utils_.get(), advertisement_bytes, BLEAdvertisementHeader::kAdvertisementHashLength); } template ConstPtr BLEV2::generateServiceIdHash( BLEAdvertisement::Version::Value version, const string& service_id) { ScopedPtr> service_id_bytes( MakeConstPtr(new ByteArray(service_id.data(), service_id.size()))); switch (version) { case BLEAdvertisement::Version::V1: return Utils::legacySha256HashOnlyForPrinting( hash_utils_.get(), service_id_bytes.get(), BLEAdvertisement::kServiceIdHashLength); case BLEAdvertisement::Version::V2: // Fall through. case BLEAdvertisement::Version::UNKNOWN: // Fall through. default: // Use the latest known hashing scheme. return Utils::sha256Hash(hash_utils_.get(), service_id_bytes.get(), BLEAdvertisement::kServiceIdHashLength); } } } // namespace mediums } // namespace connections } // namespace nearby } // namespace location