mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-16 15:36:12 -04:00
nearby sdk refactor
PiperOrigin-RevId: 425434260
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
# Copyright 2020 Google LLC
|
||||
#
|
||||
# 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
|
||||
#
|
||||
# https://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.
|
||||
licenses(["notice"])
|
||||
|
||||
cc_library(
|
||||
name = "types",
|
||||
hdrs = [
|
||||
"atomic_boolean.h",
|
||||
"atomic_reference.h",
|
||||
"bluetooth_adapter.h",
|
||||
"cancelable.h",
|
||||
"condition_variable.h",
|
||||
"count_down_latch.h",
|
||||
"crypto.h",
|
||||
"executor.h",
|
||||
"future.h",
|
||||
"input_file.h",
|
||||
"listenable_future.h",
|
||||
"log_message.h",
|
||||
"mutex.h",
|
||||
"output_file.h",
|
||||
"scheduled_executor.h",
|
||||
"settable_future.h",
|
||||
"submittable_executor.h",
|
||||
"system_clock.h",
|
||||
],
|
||||
visibility = [
|
||||
"//internal/platform:__pkg__",
|
||||
"//internal/platform/implementation:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
"//internal/platform:base",
|
||||
"@com_google_absl//absl/base:core_headers",
|
||||
"@com_google_absl//absl/strings",
|
||||
"@com_google_absl//absl/time",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "comm",
|
||||
hdrs = [
|
||||
"ble.h",
|
||||
"ble_v2.h",
|
||||
"bluetooth_adapter.h",
|
||||
"bluetooth_classic.h",
|
||||
"server_sync.h",
|
||||
"webrtc.h",
|
||||
"wifi.h",
|
||||
"wifi_lan.h",
|
||||
],
|
||||
visibility = [
|
||||
"//connections/implementation:__subpackages__",
|
||||
"//internal/platform:__pkg__",
|
||||
"//internal/platform/implementation:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
"//connections/implementation/proto:offline_wire_formats_portable_proto",
|
||||
"//internal/platform:base",
|
||||
"//internal/platform:cancellation_flag",
|
||||
"@com_google_absl//absl/strings",
|
||||
"@com_google_absl//absl/types:optional",
|
||||
"//webrtc/api:libjingle_peerconnection_api",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "platform",
|
||||
hdrs = [
|
||||
"platform.h",
|
||||
],
|
||||
visibility = [
|
||||
"//googlemac/iPhone/Shared/Nearby/Connections:__subpackages__",
|
||||
"//internal/platform:__pkg__",
|
||||
"//internal/platform/implementation:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
":comm",
|
||||
":types",
|
||||
"//internal/platform:base",
|
||||
"@com_google_absl//absl/strings",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#ifndef PLATFORM_API_ATOMIC_BOOLEAN_H_
|
||||
#define PLATFORM_API_ATOMIC_BOOLEAN_H_
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace api {
|
||||
|
||||
// A boolean value that may be updated atomically.
|
||||
class AtomicBoolean {
|
||||
public:
|
||||
virtual ~AtomicBoolean() = default;
|
||||
|
||||
// Atomically read and return current value.
|
||||
virtual bool Get() const = 0;
|
||||
|
||||
// Atomically exchange original value with a new one. Return previous value.
|
||||
virtual bool Set(bool value) = 0;
|
||||
};
|
||||
|
||||
} // namespace api
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // PLATFORM_API_ATOMIC_BOOLEAN_H_
|
||||
@@ -0,0 +1,40 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#ifndef PLATFORM_API_ATOMIC_REFERENCE_H_
|
||||
#define PLATFORM_API_ATOMIC_REFERENCE_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace api {
|
||||
|
||||
// Type that allows 32-bit atomic reads and writes.
|
||||
class AtomicUint32 {
|
||||
public:
|
||||
virtual ~AtomicUint32() = default;
|
||||
|
||||
// Atomically reads and returns stored value.
|
||||
virtual std::uint32_t Get() const = 0;
|
||||
|
||||
// Atomically stores value.
|
||||
virtual void Set(std::uint32_t value) = 0;
|
||||
};
|
||||
|
||||
} // namespace api
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // PLATFORM_API_ATOMIC_REFERENCE_H_
|
||||
@@ -0,0 +1,125 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#ifndef PLATFORM_API_BLE_H_
|
||||
#define PLATFORM_API_BLE_H_
|
||||
|
||||
#include "internal/platform/implementation/bluetooth_classic.h"
|
||||
#include "internal/platform/byte_array.h"
|
||||
#include "internal/platform/cancellation_flag.h"
|
||||
#include "internal/platform/input_stream.h"
|
||||
#include "internal/platform/output_stream.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace api {
|
||||
|
||||
// Opaque wrapper over a BLE peripheral. Must contain enough data about a
|
||||
// particular BLE device to connect to its GATT server.
|
||||
class BlePeripheral {
|
||||
public:
|
||||
virtual ~BlePeripheral() = default;
|
||||
|
||||
virtual std::string GetName() const = 0;
|
||||
|
||||
virtual ByteArray GetAdvertisementBytes(
|
||||
const std::string& service_id) const = 0;
|
||||
};
|
||||
|
||||
class BleSocket {
|
||||
public:
|
||||
virtual ~BleSocket() = default;
|
||||
|
||||
// Returns the InputStream of the BleSocket.
|
||||
// On error, returned stream will report Exception::kIo on any operation.
|
||||
//
|
||||
// The returned object is not owned by the caller, and can be invalidated once
|
||||
// the BleSocket object is destroyed.
|
||||
virtual InputStream& GetInputStream() = 0;
|
||||
|
||||
// Returns the OutputStream of the BleSocket.
|
||||
// On error, returned stream will report Exception::kIo on any operation.
|
||||
//
|
||||
// The returned object is not owned by the caller, and can be invalidated once
|
||||
// the BleSocket object is destroyed.
|
||||
virtual OutputStream& GetOutputStream() = 0;
|
||||
|
||||
// Conforms to the same contract as
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothSocket.html#close().
|
||||
//
|
||||
// Returns Exception::kIo on error, Exception::kSuccess otherwise.
|
||||
virtual Exception Close() = 0;
|
||||
|
||||
// Returns valid BlePeripheral pointer if there is a connection, and
|
||||
// nullptr otherwise.
|
||||
virtual BlePeripheral* GetRemotePeripheral() = 0;
|
||||
};
|
||||
|
||||
// Container of operations that can be performed over the BLE medium.
|
||||
class BleMedium {
|
||||
public:
|
||||
virtual ~BleMedium() = default;
|
||||
|
||||
virtual bool StartAdvertising(
|
||||
const std::string& service_id, const ByteArray& advertisement_bytes,
|
||||
const std::string& fast_advertisement_service_uuid) = 0;
|
||||
virtual bool StopAdvertising(const std::string& service_id) = 0;
|
||||
|
||||
// Callback that is invoked when a discovered peripheral is found or lost.
|
||||
struct DiscoveredPeripheralCallback {
|
||||
std::function<void(BlePeripheral& peripheral, const std::string& service_id,
|
||||
bool fast_advertisement)>
|
||||
peripheral_discovered_cb =
|
||||
DefaultCallback<BlePeripheral&, const std::string&, bool>();
|
||||
std::function<void(BlePeripheral& peripheral,
|
||||
const std::string& service_id)>
|
||||
peripheral_lost_cb =
|
||||
DefaultCallback<BlePeripheral&, const std::string&>();
|
||||
};
|
||||
|
||||
// Returns true once the BLE scan has been initiated.
|
||||
virtual bool StartScanning(const std::string& service_id,
|
||||
const std::string& fast_advertisement_service_uuid,
|
||||
DiscoveredPeripheralCallback callback) = 0;
|
||||
|
||||
// Returns true once BLE scanning for service_id is well and truly stopped;
|
||||
// after this returns, there must be no more invocations of the
|
||||
// DiscoveredPeripheralCallback passed in to StartScanning() for service_id.
|
||||
virtual bool StopScanning(const std::string& service_id) = 0;
|
||||
|
||||
// Callback that is invoked when a new connection is accepted.
|
||||
struct AcceptedConnectionCallback {
|
||||
std::function<void(BleSocket& socket, const std::string& service_id)>
|
||||
accepted_cb = DefaultCallback<BleSocket&, const std::string&>();
|
||||
};
|
||||
|
||||
// Returns true once BLE socket connection requests to service_id can be
|
||||
// accepted.
|
||||
virtual bool StartAcceptingConnections(
|
||||
const std::string& service_id, AcceptedConnectionCallback callback) = 0;
|
||||
virtual bool StopAcceptingConnections(const std::string& service_id) = 0;
|
||||
|
||||
// Connects to a BLE peripheral.
|
||||
// On success, returns a new BleSocket.
|
||||
// On error, returns nullptr.
|
||||
virtual std::unique_ptr<BleSocket> Connect(
|
||||
BlePeripheral& peripheral, const std::string& service_id,
|
||||
CancellationFlag* cancellation_flag) = 0;
|
||||
};
|
||||
|
||||
} // namespace api
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // PLATFORM_API_BLE_H_
|
||||
@@ -0,0 +1,406 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#ifndef PLATFORM_API_BLE_V2_H_
|
||||
#define PLATFORM_API_BLE_V2_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/types/optional.h"
|
||||
#include "internal/platform/byte_array.h"
|
||||
#include "internal/platform/exception.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace api {
|
||||
namespace ble_v2 {
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/le/AdvertiseData
|
||||
//
|
||||
// Bundle of data found in a BLE advertisement.
|
||||
//
|
||||
// All service UUIDs will conform to the 16-bit Bluetooth base UUID,
|
||||
// 0000xxxx-0000-1000-8000-00805F9B34FB. This makes it possible to store two
|
||||
// byte service UUIDs in the advertisement.
|
||||
struct BleAdvertisementData {
|
||||
using TxPowerLevel = int8_t;
|
||||
|
||||
static const TxPowerLevel kUnspecifiedTxPowerLevel =
|
||||
std::numeric_limits<TxPowerLevel>::min();
|
||||
|
||||
bool is_connectable;
|
||||
// When set to kUnspecifiedTxPowerLevel, TX power should not be included in
|
||||
// the advertisement data.
|
||||
TxPowerLevel tx_power_level;
|
||||
// When set to an empty string, local name should not be included in the
|
||||
// advertisement data.
|
||||
std::string local_name;
|
||||
// When set to an empty vector, the set of 16-bit service class UUIDs should
|
||||
// not be included in the advertisement data.
|
||||
std::set<std::string> service_uuids;
|
||||
// Maps service UUIDs to their service data.
|
||||
std::map<std::string, ByteArray> service_data;
|
||||
};
|
||||
|
||||
// Opaque wrapper over a BLE peripheral. Must be able to uniquely identify a
|
||||
// peripheral so that we can connect to its GATT server.
|
||||
class BlePeripheral {
|
||||
public:
|
||||
virtual ~BlePeripheral() {}
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothDevice#getAddress()
|
||||
//
|
||||
// This should be the MAC address when possible. If the implementation is
|
||||
// unable to retrieve that, any unique identifier should suffice.
|
||||
virtual std::string GetId() const = 0;
|
||||
};
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothGattCharacteristic
|
||||
//
|
||||
// Representation of a GATT characteristic.
|
||||
class GattCharacteristic {
|
||||
public:
|
||||
virtual ~GattCharacteristic() {}
|
||||
|
||||
// Possible permissions of a GATT characteristic.
|
||||
enum class Permission {
|
||||
kUnknown = 0,
|
||||
kRead = 1,
|
||||
kWrite = 2,
|
||||
kLast,
|
||||
};
|
||||
|
||||
// Possible properties of a GATT characteristic.
|
||||
enum class Property {
|
||||
kUnknown = 0,
|
||||
kRead = 1,
|
||||
kWrite = 2,
|
||||
kIndicate = 3,
|
||||
kLast,
|
||||
};
|
||||
|
||||
// Returns the UUID of this characteristic.
|
||||
virtual std::string GetUuid() = 0;
|
||||
|
||||
// Returns the UUID of the containing GATT service.
|
||||
virtual std::string GetServiceUuid() = 0;
|
||||
};
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothGatt
|
||||
//
|
||||
// Representation of a client GATT connection to a remote GATT server.
|
||||
class ClientGattConnection {
|
||||
public:
|
||||
virtual ~ClientGattConnection() {}
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#getDevice()
|
||||
//
|
||||
// Retrieves the BLE peripheral that this connection is tied to.
|
||||
virtual BlePeripheral& GetPeripheral() = 0;
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#discoverServices()
|
||||
//
|
||||
// Discovers all available services and characteristics on this connection.
|
||||
// Returns whether or not discovery finished successfully.
|
||||
//
|
||||
// This function should block until discovery has finished.
|
||||
virtual bool DiscoverServices() = 0;
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#getService(java.util.UUID)
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothGattService.html#getCharacteristic(java.util.UUID)
|
||||
//
|
||||
// Retrieves a GATT characteristic. On error, does not return a value.
|
||||
//
|
||||
// DiscoverServices() should be called before this method to fetch all
|
||||
// available services and characteristics first.
|
||||
//
|
||||
// It is okay for duplicate services to exist, as long as the specified
|
||||
// characteristic UUID is unique among all services of the same UUID.
|
||||
virtual absl::optional<GattCharacteristic> GetCharacteristic(
|
||||
absl::string_view service_uuid,
|
||||
absl::string_view characteristic_uuid) = 0;
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#readCharacteristic(android.bluetooth.BluetoothGattCharacteristic)
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothGattCharacteristic.html#getValue()
|
||||
//
|
||||
// Reads a GATT characteristic. No value is returned upon error.
|
||||
virtual absl::optional<ByteArray> ReadCharacteristic(
|
||||
const GattCharacteristic& characteristic) = 0;
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothGattCharacteristic.html#setValue(byte[])
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#writeCharacteristic(android.bluetooth.BluetoothGattCharacteristic)
|
||||
//
|
||||
// Sends a remote characteristic write request to the server and returns
|
||||
// whether or not it was successful.
|
||||
virtual bool WriteCharacteristic(const GattCharacteristic& characteristic,
|
||||
const ByteArray& value) = 0;
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#disconnect()
|
||||
//
|
||||
// Disconnects a GATT connection.
|
||||
virtual void Disconnect() = 0;
|
||||
};
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothGattServer
|
||||
//
|
||||
// Representation of a server GATT connection to a remote GATT client.
|
||||
class ServerGattConnection {
|
||||
public:
|
||||
virtual ~ServerGattConnection() {}
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothGattCharacteristic.html#setValue(byte[])
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothGattServer.html#notifyCharacteristicChanged(android.bluetooth.BluetoothDevice,%20android.bluetooth.BluetoothGattCharacteristic,%20boolean)
|
||||
//
|
||||
// Sends a notification (via indication) to the client that a characteristic
|
||||
// has changed with the given value. Returns whether or not it was successful.
|
||||
//
|
||||
// The value sent does not have to reflect the locally stored characteristic
|
||||
// value. To update the local value, call GattServer::UpdateCharacteristic.
|
||||
virtual bool SendCharacteristic(const GattCharacteristic& characteristic,
|
||||
const ByteArray& value) = 0;
|
||||
};
|
||||
|
||||
// Callback for asynchronous events on the client side of a GATT connection.
|
||||
class ClientGattConnectionLifeCycleCallback {
|
||||
public:
|
||||
virtual ~ClientGattConnectionLifeCycleCallback() {}
|
||||
|
||||
// Called when the client is disconnected from the GATT server.
|
||||
virtual void OnDisconnected(ClientGattConnection* connection) = 0;
|
||||
};
|
||||
|
||||
// Callback for asynchronous events on the server side of a GATT connection.
|
||||
class ServerGattConnectionLifeCycleCallback {
|
||||
public:
|
||||
virtual ~ServerGattConnectionLifeCycleCallback() {}
|
||||
|
||||
// Called when a remote peripheral connected to us and subscribed to one of
|
||||
// our characteristics.
|
||||
virtual void OnCharacteristicSubscription(
|
||||
ServerGattConnection* connection,
|
||||
const GattCharacteristic& characteristic) = 0;
|
||||
|
||||
// Called when a remote peripheral unsubscribed from one of our
|
||||
// characteristics.
|
||||
virtual void OnCharacteristicUnsubscription(
|
||||
ServerGattConnection* connection,
|
||||
const GattCharacteristic& characteristic) = 0;
|
||||
};
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothGattServer
|
||||
//
|
||||
// Representation of a BLE GATT server.
|
||||
class GattServer {
|
||||
public:
|
||||
virtual ~GattServer() {}
|
||||
|
||||
// Creates a characteristic and adds it to the GATT server under the given
|
||||
// characteristic and service UUIDs. Returns no value upon error.
|
||||
//
|
||||
// Characteristics of the same service UUID should be put under one
|
||||
// service rather than many services with the same UUID.
|
||||
//
|
||||
// If the INDICATE property is included, the characteristic should include the
|
||||
// official Bluetooth Client Characteristic Configuration descriptor with UUID
|
||||
// 0x2902 and a WRITE permission. This allows remote clients to write to this
|
||||
// descriptor and subscribe for characteristic changes. For more information
|
||||
// about this descriptor, please go to:
|
||||
// https://www.bluetooth.com/specifications/Gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.Gatt.client_characteristic_configuration.xml
|
||||
virtual absl::optional<GattCharacteristic> CreateCharacteristic(
|
||||
absl::string_view service_uuid, absl::string_view characteristic_uuid,
|
||||
const std::set<GattCharacteristic::Permission>& permissions,
|
||||
const std::set<GattCharacteristic::Property>& properties) = 0;
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothGattCharacteristic.html#setValue(byte[])
|
||||
//
|
||||
// Locally updates the value of a characteristic and returns whether or not it
|
||||
// was successful.
|
||||
// Takes ownership of (and is responsible for destroying) the passed-in
|
||||
// 'value'.
|
||||
virtual bool UpdateCharacteristic(const GattCharacteristic& characteristic,
|
||||
const ByteArray& value) = 0;
|
||||
|
||||
// Stops a GATT server.
|
||||
virtual void Stop() = 0;
|
||||
};
|
||||
|
||||
// A BLE socket representation.
|
||||
class BleSocket {
|
||||
public:
|
||||
virtual ~BleSocket() {}
|
||||
|
||||
// Returns the remote BLE peripheral tied to this socket.
|
||||
virtual BlePeripheral& GetRemotePeripheral() = 0;
|
||||
|
||||
// Writes a message on the socket and blocks until finished. Returns
|
||||
// Exception::kIo upon error, and Exception::kSuccess otherwise.
|
||||
virtual Exception Write(const ByteArray& message) = 0;
|
||||
|
||||
// Closes the socket and blocks until finished. Returns Exception::kIo upon
|
||||
// error, and Exception::kSuccess otherwise.
|
||||
virtual Exception Close() = 0;
|
||||
};
|
||||
|
||||
// Callback for asynchronous events on a BleSocket object.
|
||||
class BleSocketLifeCycleCallback {
|
||||
public:
|
||||
virtual ~BleSocketLifeCycleCallback() {}
|
||||
|
||||
// Called when a message arrives on a socket.
|
||||
virtual void OnMessageReceived(BleSocket* socket,
|
||||
const ByteArray& message) = 0;
|
||||
|
||||
// Called when a socket gets disconnected.
|
||||
virtual void OnDisconnected(BleSocket* socket) = 0;
|
||||
};
|
||||
|
||||
// Callback for asynchronous events on the server side of a BleSocket object.
|
||||
class ServerBleSocketLifeCycleCallback : public BleSocketLifeCycleCallback {
|
||||
public:
|
||||
~ServerBleSocketLifeCycleCallback() override {}
|
||||
|
||||
// Called when a new incoming socket has been established.
|
||||
virtual void OnSocketEstablished(BleSocket* socket) = 0;
|
||||
};
|
||||
|
||||
// The main BLE medium used inside of Nearby. This serves as the entry point for
|
||||
// all BLE and GATT related operations.
|
||||
class BleMedium {
|
||||
public:
|
||||
using Mtu = uint32_t;
|
||||
|
||||
virtual ~BleMedium() {}
|
||||
|
||||
// Coarse representation of power settings throughout all BLE operations.
|
||||
enum class PowerMode {
|
||||
kUnknown = 0,
|
||||
kLow = 1,
|
||||
kHigh = 2,
|
||||
kLast,
|
||||
};
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/le/BluetoothLeAdvertiser.html#startAdvertising(android.bluetooth.le.AdvertiseSettings,%20android.bluetooth.le.AdvertiseData,%20android.bluetooth.le.AdvertiseData,%20android.bluetooth.le.AdvertiseCallback)
|
||||
//
|
||||
// Starts BLE advertising and returns whether or not it was successful.
|
||||
//
|
||||
// Power mode should be interpreted in the following way:
|
||||
// LOW:
|
||||
// - Advertising interval = ~1000ms
|
||||
// - TX power = low
|
||||
// HIGH:
|
||||
// - Advertising interval = ~100ms
|
||||
// - TX power = high
|
||||
virtual bool StartAdvertising(const BleAdvertisementData& advertisement_data,
|
||||
const BleAdvertisementData& scan_response,
|
||||
PowerMode power_mode) = 0;
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/le/BluetoothLeAdvertiser.html#stopAdvertising(android.bluetooth.le.AdvertiseCallback)
|
||||
//
|
||||
// Stops advertising.
|
||||
virtual void StopAdvertising() = 0;
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/le/ScanCallback
|
||||
//
|
||||
// Callback for BLE scan results.
|
||||
class ScanCallback {
|
||||
public:
|
||||
virtual ~ScanCallback() {}
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/le/ScanCallback.html#onScanResult(int,%20android.bluetooth.le.ScanResult)
|
||||
//
|
||||
// Called when a BLE advertisement is discovered.
|
||||
//
|
||||
// The passed in advertisement_data is the merged combination of both
|
||||
// advertisement data and scan response.
|
||||
//
|
||||
// Every discovery of an advertisement should be reported, even if the
|
||||
// advertisement was discovered before.
|
||||
//
|
||||
// Ownership of the BleAdvertisementData transfers to the caller at this
|
||||
// point.
|
||||
virtual void OnAdvertisementFound(
|
||||
BlePeripheral* peripheral,
|
||||
const BleAdvertisementData& advertisement_data) = 0;
|
||||
};
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/le/BluetoothLeScanner.html#startScan(java.util.List%3Candroid.bluetooth.le.ScanFilter%3E,%20android.bluetooth.le.ScanSettings,%20android.bluetooth.le.ScanCallback)
|
||||
//
|
||||
// Starts scanning and returns whether or not it was successful.
|
||||
//
|
||||
// Power mode should be interpreted in the following way:
|
||||
// LOW:
|
||||
// - Scan window = ~512ms
|
||||
// - Scan interval = ~5120ms
|
||||
// HIGH:
|
||||
// - Scan window = ~4096ms
|
||||
// - Scan interval = ~4096ms
|
||||
virtual bool StartScanning(const std::set<std::string>& service_uuids,
|
||||
PowerMode power_mode,
|
||||
const ScanCallback& scan_callback) = 0;
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/le/BluetoothLeScanner.html#stopScan(android.bluetooth.le.ScanCallback)
|
||||
//
|
||||
// Stops scanning.
|
||||
virtual void StopScanning() = 0;
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothManager#openGattServer(android.content.Context,%20android.bluetooth.BluetoothGattServerCallback)
|
||||
//
|
||||
// Starts a GATT server. Returns a nullptr upon error.
|
||||
virtual std::unique_ptr<GattServer> StartGattServer(
|
||||
const ServerGattConnectionLifeCycleCallback& callback) = 0;
|
||||
|
||||
// Starts listening for incoming BLE sockets and returns false upon error.
|
||||
virtual bool StartListeningForIncomingBleSockets(
|
||||
const ServerBleSocketLifeCycleCallback& callback) = 0;
|
||||
|
||||
// Stops listening for incoming BLE sockets.
|
||||
virtual void StopListeningForIncomingBleSockets() = 0;
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#connectGatt(android.content.Context,%20boolean,%20android.bluetooth.BluetoothGattCallback)
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#requestConnectionPriority(int)
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothGatt.html#requestMtu(int)
|
||||
//
|
||||
// Connects to a GATT server and negotiates the specified connection
|
||||
// parameters. Returns nullptr upon error.
|
||||
//
|
||||
// Both connection interval and MTU can be negotiated on a best-effort basis.
|
||||
//
|
||||
// Power mode should be interpreted in the following way:
|
||||
// LOW:
|
||||
// - Connection interval = ~11.25ms - 15ms
|
||||
// HIGH:
|
||||
// - Connection interval = ~100ms - 125ms
|
||||
virtual std::unique_ptr<ClientGattConnection> ConnectToGattServer(
|
||||
BlePeripheral* peripheral, Mtu mtu, PowerMode power_mode,
|
||||
const ClientGattConnectionLifeCycleCallback& callback) = 0;
|
||||
|
||||
// Establishes a BLE socket to the specified remote peripheral. Returns
|
||||
// nullptr on error.
|
||||
virtual std::unique_ptr<BleSocket> EstablishBleSocket(
|
||||
BlePeripheral* peripheral,
|
||||
const BleSocketLifeCycleCallback& callback) = 0;
|
||||
};
|
||||
|
||||
} // namespace ble_v2
|
||||
} // namespace api
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // PLATFORM_API_BLE_V2_H_
|
||||
@@ -0,0 +1,75 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#ifndef PLATFORM_API_BLUETOOTH_ADAPTER_H_
|
||||
#define PLATFORM_API_BLUETOOTH_ADAPTER_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace api {
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html
|
||||
class BluetoothAdapter {
|
||||
public:
|
||||
virtual ~BluetoothAdapter() = default;
|
||||
|
||||
// Eligible statuses of the BluetoothAdapter.
|
||||
enum class Status {
|
||||
kDisabled,
|
||||
kEnabled,
|
||||
};
|
||||
|
||||
// Synchronously sets the status of the BluetoothAdapter to 'status', and
|
||||
// returns true if the operation was a success.
|
||||
virtual bool SetStatus(Status status) = 0;
|
||||
// Returns true if the BluetoothAdapter's current status is
|
||||
// Status::Value::kEnabled.
|
||||
virtual bool IsEnabled() const = 0;
|
||||
|
||||
// Scan modes of a BluetoothAdapter, as described at
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#getScanMode().
|
||||
enum class ScanMode {
|
||||
kUnknown,
|
||||
kNone,
|
||||
kConnectable,
|
||||
kConnectableDiscoverable,
|
||||
};
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#getScanMode()
|
||||
//
|
||||
// Returns ScanMode::kUnknown on error.
|
||||
virtual ScanMode GetScanMode() const = 0;
|
||||
// Synchronously sets the scan mode of the adapter, and returns true if the
|
||||
// operation was a success.
|
||||
virtual bool SetScanMode(ScanMode scan_mode) = 0;
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#getName()
|
||||
// Returns an empty string on error
|
||||
virtual std::string GetName() const = 0;
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#setName(java.lang.String)
|
||||
virtual bool SetName(absl::string_view name) = 0;
|
||||
|
||||
// Returns BT MAC address assigned to this adapter.
|
||||
virtual std::string GetMacAddress() const = 0;
|
||||
};
|
||||
|
||||
} // namespace api
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // PLATFORM_API_BLUETOOTH_ADAPTER_H_
|
||||
@@ -0,0 +1,162 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#ifndef PLATFORM_API_BLUETOOTH_CLASSIC_H_
|
||||
#define PLATFORM_API_BLUETOOTH_CLASSIC_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "internal/platform/byte_array.h"
|
||||
#include "internal/platform/cancellation_flag.h"
|
||||
#include "internal/platform/exception.h"
|
||||
#include "internal/platform/input_stream.h"
|
||||
#include "internal/platform/listeners.h"
|
||||
#include "internal/platform/output_stream.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace api {
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html.
|
||||
class BluetoothDevice {
|
||||
public:
|
||||
virtual ~BluetoothDevice() = default;
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#getName()
|
||||
virtual std::string GetName() const = 0;
|
||||
|
||||
// Returns BT MAC address assigned to this device.
|
||||
virtual std::string GetMacAddress() const = 0;
|
||||
};
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothSocket.html.
|
||||
class BluetoothSocket {
|
||||
public:
|
||||
virtual ~BluetoothSocket() = default;
|
||||
|
||||
// NOTE:
|
||||
// It is an undefined behavior if GetInputStream() or GetOutputStream() is
|
||||
// called for a not-connected BluetoothSocket, i.e. any object that is not
|
||||
// returned by BluetoothClassicMedium::ConnectToService() for client side or
|
||||
// BluetoothServerSocket::Accept() for server side of connection.
|
||||
|
||||
// Returns the InputStream of this connected BluetoothSocket.
|
||||
virtual InputStream& GetInputStream() = 0;
|
||||
|
||||
// Returns the OutputStream of this connected BluetoothSocket.
|
||||
virtual OutputStream& GetOutputStream() = 0;
|
||||
|
||||
// Closes both input and output streams, marks Socket as closed.
|
||||
// After this call object should be treated as not connected.
|
||||
// Returns Exception::kIo on error, Exception::kSuccess otherwise.
|
||||
virtual Exception Close() = 0;
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothSocket.html#getRemoteDevice()
|
||||
// Returns valid BluetoothDevice pointer if there is a connection, and
|
||||
// nullptr otherwise.
|
||||
virtual BluetoothDevice* GetRemoteDevice() = 0;
|
||||
};
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothServerSocket.html.
|
||||
class BluetoothServerSocket {
|
||||
public:
|
||||
virtual ~BluetoothServerSocket() = default;
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothServerSocket.html#accept()
|
||||
//
|
||||
// Blocks until either:
|
||||
// - at least one incoming connection request is available, or
|
||||
// - ServerSocket is closed.
|
||||
// On success, returns connected socket, ready to exchange data.
|
||||
// Returns nullptr on error.
|
||||
// Once error is reported, it is permanent, and ServerSocket has to be closed.
|
||||
virtual std::unique_ptr<BluetoothSocket> Accept() = 0;
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothServerSocket.html#close()
|
||||
//
|
||||
// Returns Exception::kIo on error, Exception::kSuccess otherwise.
|
||||
virtual Exception Close() = 0;
|
||||
};
|
||||
|
||||
// Container of operations that can be performed over the Bluetooth Classic
|
||||
// medium.
|
||||
class BluetoothClassicMedium {
|
||||
public:
|
||||
virtual ~BluetoothClassicMedium() = default;
|
||||
|
||||
struct DiscoveryCallback {
|
||||
// BluetoothDevice is a proxy object created as a result of BT discovery.
|
||||
// Its lifetime spans between calls to device_discovered_cb and
|
||||
// device_lost_cb.
|
||||
// It is safe to use BluetoothDevice in device_discovered_cb() callback
|
||||
// and at any time afterwards, until device_lost_cb() is called.
|
||||
// It is not safe to use BluetoothDevice after returning from
|
||||
// device_lost_cb() callback.
|
||||
std::function<void(BluetoothDevice& device)> device_discovered_cb =
|
||||
DefaultCallback<BluetoothDevice&>();
|
||||
std::function<void(BluetoothDevice& device)> device_name_changed_cb =
|
||||
DefaultCallback<BluetoothDevice&>();
|
||||
std::function<void(BluetoothDevice& device)> device_lost_cb =
|
||||
DefaultCallback<BluetoothDevice&>();
|
||||
};
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#startDiscovery()
|
||||
//
|
||||
// Returns true once the process of discovery has been initiated.
|
||||
virtual bool StartDiscovery(DiscoveryCallback discovery_callback) = 0;
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#cancelDiscovery()
|
||||
//
|
||||
// Returns true once discovery is well and truly stopped; after this returns,
|
||||
// there must be no more invocations of the DiscoveryCallback passed in to
|
||||
// StartDiscovery().
|
||||
virtual bool StopDiscovery() = 0;
|
||||
|
||||
// A combination of
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#createInsecureRfcommSocketToServiceRecord
|
||||
// followed by
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothSocket.html#connect().
|
||||
//
|
||||
// service_uuid is the canonical textual representation
|
||||
// (https://en.wikipedia.org/wiki/Universally_unique_identifier#Format) of a
|
||||
// type 3 name-based
|
||||
// (https://en.wikipedia.org/wiki/Universally_unique_identifier#Versions_3_and_5_(namespace_name-based))
|
||||
// UUID.
|
||||
//
|
||||
// On success, returns a new BluetoothSocket.
|
||||
// On error, returns nullptr.
|
||||
virtual std::unique_ptr<BluetoothSocket> ConnectToService(
|
||||
BluetoothDevice& remote_device, const std::string& service_uuid,
|
||||
CancellationFlag* cancellation_flag) = 0;
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#listenUsingInsecureRfcommWithServiceRecord
|
||||
//
|
||||
// service_uuid is the canonical textual representation
|
||||
// (https://en.wikipedia.org/wiki/Universally_unique_identifier#Format) of a
|
||||
// type 3 name-based
|
||||
// (https://en.wikipedia.org/wiki/Universally_unique_identifier#Versions_3_and_5_(namespace_name-based))
|
||||
// UUID.
|
||||
//
|
||||
// Returns nullptr error.
|
||||
virtual std::unique_ptr<BluetoothServerSocket> ListenForService(
|
||||
const std::string& service_name, const std::string& service_uuid) = 0;
|
||||
|
||||
virtual BluetoothDevice* GetRemoteDevice(const std::string& mac_address) = 0;
|
||||
};
|
||||
|
||||
} // namespace api
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // PLATFORM_API_BLUETOOTH_CLASSIC_H_
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#ifndef PLATFORM_API_CANCELABLE_H_
|
||||
#define PLATFORM_API_CANCELABLE_H_
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace api {
|
||||
|
||||
// An interface to provide a cancellation mechanism for objects that represent
|
||||
// long-running operations.
|
||||
class Cancelable {
|
||||
public:
|
||||
virtual ~Cancelable() = default;
|
||||
|
||||
virtual bool Cancel() = 0;
|
||||
};
|
||||
|
||||
} // namespace api
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // PLATFORM_API_CANCELABLE_H_
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#ifndef PLATFORM_API_CONDITION_VARIABLE_H_
|
||||
#define PLATFORM_API_CONDITION_VARIABLE_H_
|
||||
|
||||
#include "absl/time/clock.h"
|
||||
#include "internal/platform/exception.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace api {
|
||||
|
||||
// The ConditionVariable class is a synchronization primitive that can be used
|
||||
// to block a thread, or multiple threads at the same time, until another thread
|
||||
// both modifies a shared variable (the condition), and notifies the
|
||||
// ConditionVariable.
|
||||
class ConditionVariable {
|
||||
public:
|
||||
virtual ~ConditionVariable() {}
|
||||
|
||||
// Notifies all the waiters that condition state has changed.
|
||||
virtual void Notify() = 0;
|
||||
|
||||
// Waits indefinitely for Notify to be called.
|
||||
// May return prematurely in case of interrupt, if supported by platform.
|
||||
// Returns kSuccess, or kInterrupted on interrupt.
|
||||
virtual Exception Wait() = 0;
|
||||
|
||||
// Waits while timeout has not expired for Notify to be called.
|
||||
// May return prematurely in case of interrupt, if supported by platform.
|
||||
// Returns kSuccess, or kInterrupted on interrupt.
|
||||
virtual Exception Wait(absl::Duration timeout) = 0;
|
||||
};
|
||||
|
||||
} // namespace api
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // PLATFORM_API_CONDITION_VARIABLE_H_
|
||||
@@ -0,0 +1,45 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#ifndef PLATFORM_API_COUNT_DOWN_LATCH_H_
|
||||
#define PLATFORM_API_COUNT_DOWN_LATCH_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "absl/time/time.h"
|
||||
#include "internal/platform/exception.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace api {
|
||||
|
||||
// A synchronization aid that allows one or more threads to wait until a set of
|
||||
// operations being performed in other threads completes.
|
||||
//
|
||||
// https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CountDownLatch.html
|
||||
class CountDownLatch {
|
||||
public:
|
||||
virtual ~CountDownLatch() = default;
|
||||
|
||||
virtual Exception Await() = 0; // throws Exception::kInterrupted
|
||||
virtual ExceptionOr<bool> Await(
|
||||
absl::Duration timeout) = 0; // throws Exception::kInterrupted
|
||||
virtual void CountDown() = 0;
|
||||
};
|
||||
|
||||
} // namespace api
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // PLATFORM_API_COUNT_DOWN_LATCH_H_
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#ifndef PLATFORM_API_CRYPTO_H_
|
||||
#define PLATFORM_API_CRYPTO_H_
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "internal/platform/byte_array.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
|
||||
// A provider of standard hashing algorithms.
|
||||
class Crypto {
|
||||
public:
|
||||
// Initialize global crypto state.
|
||||
static void Init();
|
||||
// Return MD5 hash of input.
|
||||
static ByteArray Md5(absl::string_view input);
|
||||
// Return SHA256 hash of input.
|
||||
static ByteArray Sha256(absl::string_view input);
|
||||
};
|
||||
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // PLATFORM_API_CRYPTO_H_
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#ifndef PLATFORM_API_EXECUTOR_H_
|
||||
#define PLATFORM_API_EXECUTOR_H_
|
||||
|
||||
#include "internal/platform/runnable.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace api {
|
||||
|
||||
int GetCurrentTid();
|
||||
|
||||
// This abstract class is the superclass of all classes representing an
|
||||
// Executor.
|
||||
class Executor {
|
||||
public:
|
||||
// Before returning from destructor, executor must wait for all pending
|
||||
// jobs to finish.
|
||||
virtual ~Executor() = default;
|
||||
// https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executor.html#execute-java.lang.Runnable-
|
||||
virtual void Execute(Runnable&& runnable) = 0;
|
||||
|
||||
// https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html#shutdown--
|
||||
virtual void Shutdown() = 0;
|
||||
};
|
||||
|
||||
} // namespace api
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // PLATFORM_API_EXECUTOR_H_
|
||||
@@ -0,0 +1,46 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#ifndef PLATFORM_API_FUTURE_H_
|
||||
#define PLATFORM_API_FUTURE_H_
|
||||
|
||||
#include "absl/time/clock.h"
|
||||
#include "internal/platform/exception.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace api {
|
||||
|
||||
// A Future represents the result of an asynchronous computation.
|
||||
//
|
||||
// https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Future.html
|
||||
template <typename T>
|
||||
class Future {
|
||||
public:
|
||||
virtual ~Future() = default;
|
||||
|
||||
// throws Exception::kInterrupted, Exception::kExecution
|
||||
virtual ExceptionOr<T> Get() = 0;
|
||||
|
||||
// throws Exception::kInterrupted, Exception::kExecution
|
||||
// throws Exception::kTimeout if timeout is exceeded while waiting for
|
||||
// result.
|
||||
virtual ExceptionOr<T> Get(absl::Duration timeout) = 0;
|
||||
};
|
||||
|
||||
} // namespace api
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // PLATFORM_API_FUTURE_H_
|
||||
@@ -0,0 +1,132 @@
|
||||
# Copyright 2020 Google LLC
|
||||
#
|
||||
# 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
|
||||
#
|
||||
# https://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.
|
||||
licenses(["notice"])
|
||||
|
||||
cc_library(
|
||||
name = "types",
|
||||
testonly = True,
|
||||
srcs = [
|
||||
"log_message.cc",
|
||||
"scheduled_executor.cc",
|
||||
"system_clock.cc",
|
||||
],
|
||||
hdrs = [
|
||||
"atomic_boolean.h",
|
||||
"atomic_reference.h",
|
||||
"condition_variable.h",
|
||||
"log_message.h",
|
||||
"multi_thread_executor.h",
|
||||
"mutex.h",
|
||||
"pipe.h",
|
||||
"scheduled_executor.h",
|
||||
"single_thread_executor.h",
|
||||
],
|
||||
visibility = ["//visibility:private"],
|
||||
deps = [
|
||||
"//base",
|
||||
"//base:stringprintf",
|
||||
"//internal/platform:base",
|
||||
"//internal/platform:util",
|
||||
"//internal/platform/implementation:platform",
|
||||
"//internal/platform/implementation:types",
|
||||
"//internal/platform/implementation/shared:count_down_latch",
|
||||
"//internal/platform/implementation/shared:posix_mutex",
|
||||
"@com_google_absl//absl/base:core_headers",
|
||||
"@com_google_absl//absl/synchronization",
|
||||
"@com_google_absl//absl/time",
|
||||
"@com_google_nisaba//nisaba/port:thread_pool",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "comm",
|
||||
testonly = True,
|
||||
srcs = [
|
||||
"ble.cc",
|
||||
"bluetooth_adapter.cc",
|
||||
"bluetooth_classic.cc",
|
||||
"webrtc.cc",
|
||||
"wifi_lan.cc",
|
||||
],
|
||||
hdrs = [
|
||||
"ble.h",
|
||||
"bluetooth_adapter.h",
|
||||
"bluetooth_classic.h",
|
||||
"webrtc.h",
|
||||
"wifi_lan.h",
|
||||
],
|
||||
visibility = ["//visibility:private"],
|
||||
deps = [
|
||||
":types",
|
||||
"//internal/platform:base",
|
||||
"//internal/platform:cancellation_flag",
|
||||
"//internal/platform:logging",
|
||||
"//internal/platform:test_util",
|
||||
"//internal/platform/implementation:comm",
|
||||
"//internal/platform/implementation/shared:count_down_latch",
|
||||
"@com_google_absl//absl/base:core_headers",
|
||||
"@com_google_absl//absl/container:flat_hash_map",
|
||||
"@com_google_absl//absl/container:flat_hash_set",
|
||||
"@com_google_absl//absl/strings",
|
||||
"@com_google_absl//absl/strings:str_format",
|
||||
"@com_google_absl//absl/synchronization",
|
||||
"//webrtc/api:create_peerconnection_factory", #buildcleaner: keep
|
||||
"//webrtc/api:libjingle_peerconnection_api",
|
||||
"//webrtc/api/task_queue:default_task_queue_factory",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "crypto",
|
||||
testonly = True,
|
||||
srcs = [
|
||||
"crypto.cc",
|
||||
],
|
||||
visibility = ["//visibility:private"],
|
||||
deps = [
|
||||
"//internal/platform:base",
|
||||
"//internal/platform/implementation:types",
|
||||
"@com_google_absl//absl/strings",
|
||||
"@boringssl//:crypto",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "g3",
|
||||
testonly = True,
|
||||
srcs = [
|
||||
"platform.cc",
|
||||
],
|
||||
visibility = [
|
||||
"//connections:__subpackages__",
|
||||
"//internal/analytics:__subpackages__",
|
||||
"//internal/platform:__subpackages__",
|
||||
"//internal/proto/analytics:__subpackages__",
|
||||
],
|
||||
deps = [
|
||||
":comm",
|
||||
":crypto", # build_cleaner: keep
|
||||
":types",
|
||||
"//internal/platform:test_util",
|
||||
"//internal/platform/implementation:comm",
|
||||
"//internal/platform/implementation:platform",
|
||||
"//internal/platform/implementation:types",
|
||||
"//internal/platform/implementation/shared:count_down_latch",
|
||||
"//internal/platform/implementation/shared:file",
|
||||
"@com_google_absl//absl/base:core_headers",
|
||||
"@com_google_absl//absl/memory",
|
||||
"@com_google_absl//absl/strings",
|
||||
"@com_google_absl//absl/time",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#ifndef PLATFORM_IMPL_G3_ATOMIC_BOOLEAN_H_
|
||||
#define PLATFORM_IMPL_G3_ATOMIC_BOOLEAN_H_
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "internal/platform/implementation/atomic_boolean.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace g3 {
|
||||
|
||||
// See documentation in
|
||||
// cpp/platform/api/atomic_boolean.h
|
||||
class AtomicBoolean : public api::AtomicBoolean {
|
||||
public:
|
||||
explicit AtomicBoolean(bool initial_value) : value_(initial_value) {}
|
||||
~AtomicBoolean() override = default;
|
||||
|
||||
bool Get() const override { return value_.load(); }
|
||||
bool Set(bool value) override { return value_.exchange(value); }
|
||||
|
||||
private:
|
||||
std::atomic_bool value_;
|
||||
};
|
||||
|
||||
} // namespace g3
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // PLATFORM_IMPL_G3_ATOMIC_BOOLEAN_H_
|
||||
@@ -0,0 +1,43 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#ifndef PLATFORM_IMPL_G3_ATOMIC_REFERENCE_H_
|
||||
#define PLATFORM_IMPL_G3_ATOMIC_REFERENCE_H_
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
|
||||
#include "internal/platform/implementation/atomic_reference.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace g3 {
|
||||
|
||||
class AtomicUint32 : public api::AtomicUint32 {
|
||||
public:
|
||||
explicit AtomicUint32(std::int32_t value) : value_(value) {}
|
||||
~AtomicUint32() override = default;
|
||||
|
||||
std::uint32_t Get() const override { return value_; }
|
||||
void Set(std::uint32_t value) override { value_ = value; }
|
||||
|
||||
private:
|
||||
std::atomic<std::uint32_t> value_;
|
||||
};
|
||||
|
||||
} // namespace g3
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // PLATFORM_IMPL_G3_ATOMIC_REFERENCE_H_
|
||||
@@ -0,0 +1,378 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#include "internal/platform/implementation/g3/ble.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "internal/platform/implementation/ble.h"
|
||||
#include "internal/platform/cancellation_flag_listener.h"
|
||||
#include "internal/platform/logging.h"
|
||||
#include "internal/platform/medium_environment.h"
|
||||
#include "internal/platform/implementation/shared/count_down_latch.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace g3 {
|
||||
|
||||
BleSocket::~BleSocket() {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
DoClose();
|
||||
}
|
||||
|
||||
void BleSocket::Connect(BleSocket& other) {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
remote_socket_ = &other;
|
||||
input_ = other.output_;
|
||||
}
|
||||
|
||||
InputStream& BleSocket::GetInputStream() {
|
||||
auto* remote_socket = GetRemoteSocket();
|
||||
CHECK(remote_socket != nullptr);
|
||||
return remote_socket->GetLocalInputStream();
|
||||
}
|
||||
|
||||
OutputStream& BleSocket::GetOutputStream() { return GetLocalOutputStream(); }
|
||||
|
||||
BleSocket* BleSocket::GetRemoteSocket() {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
return remote_socket_;
|
||||
}
|
||||
|
||||
bool BleSocket::IsConnected() const {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
return IsConnectedLocked();
|
||||
}
|
||||
|
||||
bool BleSocket::IsClosed() const {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
return closed_;
|
||||
}
|
||||
|
||||
Exception BleSocket::Close() {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
DoClose();
|
||||
return {Exception::kSuccess};
|
||||
}
|
||||
|
||||
BlePeripheral* BleSocket::GetRemotePeripheral() {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
return peripheral_;
|
||||
}
|
||||
|
||||
void BleSocket::DoClose() {
|
||||
if (!closed_) {
|
||||
remote_socket_ = nullptr;
|
||||
output_->GetOutputStream().Close();
|
||||
output_->GetInputStream().Close();
|
||||
if (IsConnectedLocked()) {
|
||||
input_->GetOutputStream().Close();
|
||||
input_->GetInputStream().Close();
|
||||
}
|
||||
closed_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool BleSocket::IsConnectedLocked() const { return input_ != nullptr; }
|
||||
|
||||
InputStream& BleSocket::GetLocalInputStream() {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
return output_->GetInputStream();
|
||||
}
|
||||
|
||||
OutputStream& BleSocket::GetLocalOutputStream() {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
return output_->GetOutputStream();
|
||||
}
|
||||
|
||||
std::unique_ptr<api::BleSocket> BleServerSocket::Accept(
|
||||
BlePeripheral* peripheral) {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
if (closed_) return {};
|
||||
while (pending_sockets_.empty()) {
|
||||
cond_.Wait(&mutex_);
|
||||
if (closed_) break;
|
||||
}
|
||||
if (closed_) return {};
|
||||
auto* remote_socket =
|
||||
pending_sockets_.extract(pending_sockets_.begin()).value();
|
||||
CHECK(remote_socket);
|
||||
auto local_socket = std::make_unique<BleSocket>(peripheral);
|
||||
local_socket->Connect(*remote_socket);
|
||||
remote_socket->Connect(*local_socket);
|
||||
cond_.SignalAll();
|
||||
return local_socket;
|
||||
}
|
||||
|
||||
bool BleServerSocket::Connect(BleSocket& socket) {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
if (closed_) return false;
|
||||
if (socket.IsConnected()) {
|
||||
NEARBY_LOG(ERROR,
|
||||
"Failed to connect to Ble server socket: already connected");
|
||||
return true; // already connected.
|
||||
}
|
||||
// add client socket to the pending list
|
||||
pending_sockets_.emplace(&socket);
|
||||
cond_.SignalAll();
|
||||
while (!socket.IsConnected()) {
|
||||
cond_.Wait(&mutex_);
|
||||
if (closed_) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void BleServerSocket::SetCloseNotifier(std::function<void()> notifier) {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
close_notifier_ = std::move(notifier);
|
||||
}
|
||||
|
||||
BleServerSocket::~BleServerSocket() {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
DoClose();
|
||||
}
|
||||
|
||||
Exception BleServerSocket::Close() {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
return DoClose();
|
||||
}
|
||||
|
||||
Exception BleServerSocket::DoClose() {
|
||||
bool should_notify = !closed_;
|
||||
closed_ = true;
|
||||
if (should_notify) {
|
||||
cond_.SignalAll();
|
||||
if (close_notifier_) {
|
||||
auto notifier = std::move(close_notifier_);
|
||||
mutex_.Unlock();
|
||||
// Notifier may contain calls to public API, and may cause deadlock, if
|
||||
// mutex_ is held during the call.
|
||||
notifier();
|
||||
mutex_.Lock();
|
||||
}
|
||||
}
|
||||
return {Exception::kSuccess};
|
||||
}
|
||||
|
||||
BleMedium::BleMedium(api::BluetoothAdapter& adapter)
|
||||
: adapter_(static_cast<BluetoothAdapter*>(&adapter)) {
|
||||
adapter_->SetBleMedium(this);
|
||||
auto& env = MediumEnvironment::Instance();
|
||||
env.RegisterBleMedium(*this);
|
||||
}
|
||||
|
||||
BleMedium::~BleMedium() {
|
||||
adapter_->SetBleMedium(nullptr);
|
||||
auto& env = MediumEnvironment::Instance();
|
||||
env.UnregisterBleMedium(*this);
|
||||
|
||||
StopAdvertising(advertising_info_.service_id);
|
||||
StopScanning(scanning_info_.service_id);
|
||||
|
||||
accept_loops_runner_.Shutdown();
|
||||
NEARBY_LOG(INFO, "BleMedium dtor advertising_accept_thread_running_ = %d",
|
||||
acceptance_thread_running_.load());
|
||||
// If acceptance thread is still running, wait to finish.
|
||||
if (acceptance_thread_running_) {
|
||||
while (acceptance_thread_running_) {
|
||||
shared::CountDownLatch latch(1);
|
||||
close_accept_loops_runner_.Execute([&latch]() { latch.CountDown(); });
|
||||
latch.Await();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool BleMedium::StartAdvertising(
|
||||
const std::string& service_id, const ByteArray& advertisement_bytes,
|
||||
const std::string& fast_advertisement_service_uuid) {
|
||||
NEARBY_LOGS(INFO) << "G3 Ble StartAdvertising: service_id=" << service_id
|
||||
<< ", advertisement bytes=" << advertisement_bytes.data()
|
||||
<< "(" << advertisement_bytes.size() << "),"
|
||||
<< " fast advertisement service uuid="
|
||||
<< fast_advertisement_service_uuid;
|
||||
auto& env = MediumEnvironment::Instance();
|
||||
auto& peripheral = adapter_->GetPeripheral();
|
||||
peripheral.SetAdvertisementBytes(service_id, advertisement_bytes);
|
||||
bool fast_advertisement = !fast_advertisement_service_uuid.empty();
|
||||
env.UpdateBleMediumForAdvertising(*this, peripheral, service_id,
|
||||
fast_advertisement, true);
|
||||
|
||||
absl::MutexLock lock(&mutex_);
|
||||
if (server_socket_ != nullptr) server_socket_.release();
|
||||
server_socket_ = std::make_unique<BleServerSocket>();
|
||||
|
||||
acceptance_thread_running_.exchange(true);
|
||||
accept_loops_runner_.Execute([&env, this, service_id]() mutable {
|
||||
if (!accept_loops_runner_.InShutdown()) {
|
||||
while (true) {
|
||||
auto client_socket =
|
||||
server_socket_->Accept(&(this->adapter_->GetPeripheral()));
|
||||
if (client_socket == nullptr) break;
|
||||
env.CallBleAcceptedConnectionCallback(*this, *(client_socket.release()),
|
||||
service_id);
|
||||
}
|
||||
}
|
||||
acceptance_thread_running_.exchange(false);
|
||||
});
|
||||
advertising_info_.service_id = service_id;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BleMedium::StopAdvertising(const std::string& service_id) {
|
||||
NEARBY_LOGS(INFO) << "G3 Ble StopAdvertising: service_id=" << service_id;
|
||||
{
|
||||
absl::MutexLock lock(&mutex_);
|
||||
if (advertising_info_.Empty()) {
|
||||
NEARBY_LOGS(INFO) << "G3 Ble StopAdvertising: Can't stop advertising "
|
||||
"because we never started advertising.";
|
||||
return false;
|
||||
}
|
||||
advertising_info_.Clear();
|
||||
}
|
||||
|
||||
auto& env = MediumEnvironment::Instance();
|
||||
env.UpdateBleMediumForAdvertising(*this, adapter_->GetPeripheral(),
|
||||
service_id, /*fast_advertisement=*/false,
|
||||
/*enabled=*/false);
|
||||
accept_loops_runner_.Shutdown();
|
||||
if (server_socket_ == nullptr) {
|
||||
NEARBY_LOGS(ERROR) << "G3 Ble StopAdvertising: Failed to find Ble Server "
|
||||
"socket: service_id="
|
||||
<< service_id;
|
||||
// Fall through for server socket not found.
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!server_socket_->Close().Ok()) {
|
||||
NEARBY_LOGS(INFO)
|
||||
<< "G3 Ble StopAdvertising: Failed to close Ble server socket for "
|
||||
<< service_id;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BleMedium::StartScanning(
|
||||
const std::string& service_id,
|
||||
const std::string& fast_advertisement_service_uuid,
|
||||
DiscoveredPeripheralCallback callback) {
|
||||
NEARBY_LOGS(INFO) << "G3 Ble StartScanning: service_id=" << service_id;
|
||||
auto& env = MediumEnvironment::Instance();
|
||||
env.UpdateBleMediumForScanning(*this, service_id,
|
||||
fast_advertisement_service_uuid,
|
||||
std::move(callback), true);
|
||||
{
|
||||
absl::MutexLock lock(&mutex_);
|
||||
scanning_info_.service_id = service_id;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BleMedium::StopScanning(const std::string& service_id) {
|
||||
NEARBY_LOGS(INFO) << "G3 Ble StopScanning: service_id=" << service_id;
|
||||
{
|
||||
absl::MutexLock lock(&mutex_);
|
||||
if (scanning_info_.Empty()) {
|
||||
NEARBY_LOGS(INFO) << "G3 Ble StopDiscovery: Can't stop scanning because "
|
||||
"we never started scanning.";
|
||||
return false;
|
||||
}
|
||||
scanning_info_.Clear();
|
||||
}
|
||||
|
||||
auto& env = MediumEnvironment::Instance();
|
||||
env.UpdateBleMediumForScanning(*this, service_id, {}, {}, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BleMedium::StartAcceptingConnections(const std::string& service_id,
|
||||
AcceptedConnectionCallback callback) {
|
||||
NEARBY_LOGS(INFO) << "G3 Ble StartAcceptingConnections: service_id="
|
||||
<< service_id;
|
||||
auto& env = MediumEnvironment::Instance();
|
||||
env.UpdateBleMediumForAcceptedConnection(*this, service_id, callback);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BleMedium::StopAcceptingConnections(const std::string& service_id) {
|
||||
NEARBY_LOGS(INFO) << "G3 Ble StopAcceptingConnections: service_id="
|
||||
<< service_id;
|
||||
auto& env = MediumEnvironment::Instance();
|
||||
env.UpdateBleMediumForAcceptedConnection(*this, service_id, {});
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<api::BleSocket> BleMedium::Connect(
|
||||
api::BlePeripheral& remote_peripheral, const std::string& service_id,
|
||||
CancellationFlag* cancellation_flag) {
|
||||
NEARBY_LOG(INFO,
|
||||
"G3 Ble Connect [self]: medium=%p, adapter=%p, peripheral=%p, "
|
||||
"service_id=%s",
|
||||
this, &GetAdapter(), &GetAdapter().GetPeripheral(),
|
||||
service_id.c_str());
|
||||
// First, find an instance of remote medium, that exposed this peripheral.
|
||||
auto& adapter = static_cast<BlePeripheral&>(remote_peripheral).GetAdapter();
|
||||
auto* medium = static_cast<BleMedium*>(adapter.GetBleMedium());
|
||||
|
||||
if (!medium) return {}; // Can't find medium. Bail out.
|
||||
|
||||
BleServerSocket* remote_server_socket = nullptr;
|
||||
NEARBY_LOG(INFO,
|
||||
"G3 Ble Connect [peer]: medium=%p, adapter=%p, peripheral=%p, "
|
||||
"service_id=%s",
|
||||
medium, &adapter, &remote_peripheral, service_id.c_str());
|
||||
// Then, find our server socket context in this medium.
|
||||
{
|
||||
absl::MutexLock medium_lock(&medium->mutex_);
|
||||
remote_server_socket = medium->server_socket_.get();
|
||||
if (remote_server_socket == nullptr) {
|
||||
NEARBY_LOGS(ERROR)
|
||||
<< "G3 Ble Connect: Failed to find Ble Server socket: service_id="
|
||||
<< service_id;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
if (cancellation_flag->Cancelled()) {
|
||||
NEARBY_LOGS(ERROR) << "G3 BLE Connect: Has been cancelled: "
|
||||
"service_id="
|
||||
<< service_id;
|
||||
return {};
|
||||
}
|
||||
|
||||
CancellationFlagListener listener(cancellation_flag, [this]() {
|
||||
NEARBY_LOGS(INFO) << "G3 BLE Cancel Connect.";
|
||||
if (server_socket_ != nullptr) server_socket_->Close();
|
||||
});
|
||||
|
||||
BlePeripheral peripheral = static_cast<BlePeripheral&>(remote_peripheral);
|
||||
auto socket = std::make_unique<BleSocket>(&peripheral);
|
||||
// Finally, Request to connect to this socket.
|
||||
if (!remote_server_socket->Connect(*socket)) {
|
||||
NEARBY_LOGS(ERROR) << "G3 Ble Connect: Failed to connect to existing Ble "
|
||||
"Server socket: service_id="
|
||||
<< service_id;
|
||||
return {};
|
||||
}
|
||||
|
||||
NEARBY_LOG(INFO, "G3 Ble Connect: connected: socket=%p", socket.get());
|
||||
return socket;
|
||||
}
|
||||
|
||||
} // namespace g3
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -0,0 +1,228 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#ifndef PLATFORM_IMPL_G3_BLE_H_
|
||||
#define PLATFORM_IMPL_G3_BLE_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "absl/container/flat_hash_map.h"
|
||||
#include "absl/container/flat_hash_set.h"
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "internal/platform/implementation/ble.h"
|
||||
#include "internal/platform/byte_array.h"
|
||||
#include "internal/platform/input_stream.h"
|
||||
#include "internal/platform/output_stream.h"
|
||||
#include "internal/platform/implementation/g3/bluetooth_adapter.h"
|
||||
#include "internal/platform/implementation/g3/bluetooth_classic.h"
|
||||
#include "internal/platform/implementation/g3/multi_thread_executor.h"
|
||||
#include "internal/platform/implementation/g3/pipe.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace g3 {
|
||||
|
||||
class BleMedium;
|
||||
|
||||
class BleSocket : public api::BleSocket {
|
||||
public:
|
||||
BleSocket() = default;
|
||||
explicit BleSocket(BlePeripheral* peripheral) : peripheral_(peripheral) {}
|
||||
~BleSocket() override;
|
||||
|
||||
// Connect to another BleSocket, to form a functional low-level channel.
|
||||
// from this point on, and until Close is called, connection exists.
|
||||
void Connect(BleSocket& other) ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Returns the InputStream of this connected BleSocket.
|
||||
InputStream& GetInputStream() override ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Returns the OutputStream of this connected BleSocket.
|
||||
// This stream is for local side to write.
|
||||
OutputStream& GetOutputStream() override ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Returns address of a remote BleSocket or nullptr.
|
||||
BleSocket* GetRemoteSocket() ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Returns true if connection exists to the (possibly closed) remote socket.
|
||||
bool IsConnected() const ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Returns true if socket is closed.
|
||||
bool IsClosed() const ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Returns Exception::kIo on error, Exception::kSuccess otherwise.
|
||||
Exception Close() override ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Returns valid BlePeripheral pointer if there is a connection, and
|
||||
// nullptr otherwise.
|
||||
BlePeripheral* GetRemotePeripheral() override ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
private:
|
||||
void DoClose() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
// Returns true if connection exists to the (possibly closed) remote socket.
|
||||
bool IsConnectedLocked() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
// Returns InputStream of our side of a connection.
|
||||
// This is what the remote side is supposed to read from.
|
||||
// This is a helper for GetInputStream() method.
|
||||
InputStream& GetLocalInputStream() ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Returns OutputStream of our side of a connection.
|
||||
// This is what the local size is supposed to write to.
|
||||
// This is a helper for GetOutputStream() method.
|
||||
OutputStream& GetLocalOutputStream() ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Output pipe is initialized by constructor, it remains always valid, until
|
||||
// it is closed. it represents output part of a local socket. Input part of a
|
||||
// local socket comes from the peer socket, after connection.
|
||||
std::shared_ptr<Pipe> output_{new Pipe};
|
||||
std::shared_ptr<Pipe> input_;
|
||||
mutable absl::Mutex mutex_;
|
||||
BlePeripheral* peripheral_;
|
||||
BleSocket* remote_socket_ ABSL_GUARDED_BY(mutex_) = nullptr;
|
||||
bool closed_ ABSL_GUARDED_BY(mutex_) = false;
|
||||
};
|
||||
|
||||
class BleServerSocket {
|
||||
public:
|
||||
~BleServerSocket();
|
||||
|
||||
// Blocks until either:
|
||||
// - at least one incoming connection request is available, or
|
||||
// - ServerSocket is closed.
|
||||
// On success, returns connected socket, ready to exchange data.
|
||||
// Returns nullptr on error.
|
||||
// Once error is reported, it is permanent, and ServerSocket has to be closed.
|
||||
//
|
||||
// Called by the server side of a connection.
|
||||
// Returns BleSocket to the server side.
|
||||
// If not null, returned socket is connected to its remote (client-side) peer.
|
||||
std::unique_ptr<api::BleSocket> Accept(BlePeripheral* peripheral)
|
||||
ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Blocks until either:
|
||||
// - connection is available, or
|
||||
// - server socket is closed, or
|
||||
// - error happens.
|
||||
//
|
||||
// Called by the client side of a connection.
|
||||
// Returns true, if socket is successfully connected.
|
||||
bool Connect(BleSocket& socket) ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Called by the server side of a connection before passing ownership of
|
||||
// BleServerSocker to user, to track validity of a pointer to this
|
||||
// server socket,
|
||||
void SetCloseNotifier(std::function<void()> notifier)
|
||||
ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Returns Exception::kIo on error, Exception::kSuccess otherwise.
|
||||
// Calls close_notifier if it was previously set, and marks socket as closed.
|
||||
Exception Close() ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
private:
|
||||
Exception DoClose() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
absl::Mutex mutex_;
|
||||
absl::CondVar cond_;
|
||||
absl::flat_hash_set<BleSocket*> pending_sockets_ ABSL_GUARDED_BY(mutex_);
|
||||
std::function<void()> close_notifier_ ABSL_GUARDED_BY(mutex_);
|
||||
bool closed_ ABSL_GUARDED_BY(mutex_) = false;
|
||||
};
|
||||
|
||||
// Container of operations that can be performed over the BLE medium.
|
||||
class BleMedium : public api::BleMedium {
|
||||
public:
|
||||
explicit BleMedium(api::BluetoothAdapter& adapter);
|
||||
~BleMedium() override;
|
||||
|
||||
// Returns true once the Ble advertising has been initiated.
|
||||
bool StartAdvertising(
|
||||
const std::string& service_id, const ByteArray& advertisement_bytes,
|
||||
const std::string& fast_advertisement_service_uuid) override
|
||||
ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
bool StopAdvertising(const std::string& service_id) override
|
||||
ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Returns true once the Ble scanning has been initiated.
|
||||
bool StartScanning(const std::string& service_id,
|
||||
const std::string& fast_advertisement_service_uuid,
|
||||
DiscoveredPeripheralCallback callback) override
|
||||
ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Returns true once Ble scanning for service_id is well and truly
|
||||
// stopped; after this returns, there must be no more invocations of the
|
||||
// DiscoveredPeripheralCallback passed in to StartScanning() for service_id.
|
||||
bool StopScanning(const std::string& service_id) override
|
||||
ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Returns true once Ble socket connection requests to service_id can be
|
||||
// accepted.
|
||||
bool StartAcceptingConnections(const std::string& service_id,
|
||||
AcceptedConnectionCallback callback) override
|
||||
ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
bool StopAcceptingConnections(const std::string& service_id) override
|
||||
ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Connects to existing remote Ble peripheral.
|
||||
//
|
||||
// On success, returns a new BleSocket.
|
||||
// On error, returns nullptr.
|
||||
std::unique_ptr<api::BleSocket> Connect(
|
||||
api::BlePeripheral& remote_peripheral, const std::string& service_id,
|
||||
CancellationFlag* cancellation_flag) override ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
BluetoothAdapter& GetAdapter() { return *adapter_; }
|
||||
|
||||
private:
|
||||
static constexpr int kMaxConcurrentAcceptLoops = 5;
|
||||
|
||||
struct AdvertisingInfo {
|
||||
bool Empty() const { return service_id.empty(); }
|
||||
void Clear() { service_id.clear(); }
|
||||
|
||||
std::string service_id;
|
||||
};
|
||||
|
||||
struct ScanningInfo {
|
||||
bool Empty() const { return service_id.empty(); }
|
||||
void Clear() { service_id.clear(); }
|
||||
|
||||
std::string service_id;
|
||||
};
|
||||
|
||||
absl::Mutex mutex_;
|
||||
BluetoothAdapter* adapter_; // Our device adapter; read-only.
|
||||
|
||||
// A thread pool dedicated to running all the accept loops from
|
||||
// StartAdvertising().
|
||||
MultiThreadExecutor accept_loops_runner_{kMaxConcurrentAcceptLoops};
|
||||
std::atomic_bool acceptance_thread_running_ = false;
|
||||
|
||||
// A thread pool dedicated to wait to complete the accept_loops_runner_.
|
||||
MultiThreadExecutor close_accept_loops_runner_{kMaxConcurrentAcceptLoops};
|
||||
|
||||
// A server socket is established when start advertising.
|
||||
std::unique_ptr<BleServerSocket> server_socket_;
|
||||
AdvertisingInfo advertising_info_ ABSL_GUARDED_BY(mutex_);
|
||||
ScanningInfo scanning_info_ ABSL_GUARDED_BY(mutex_);
|
||||
};
|
||||
|
||||
} // namespace g3
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // PLATFORM_IMPL_G3_BLE_H_
|
||||
@@ -0,0 +1,135 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#include "internal/platform/implementation/g3/bluetooth_adapter.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "internal/platform/medium_environment.h"
|
||||
#include "internal/platform/prng.h"
|
||||
#include "internal/platform/implementation/g3/bluetooth_classic.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace g3 {
|
||||
|
||||
BlePeripheral::BlePeripheral(BluetoothAdapter* adapter) : adapter_(*adapter) {}
|
||||
|
||||
std::string BlePeripheral::GetName() const { return adapter_.GetName(); }
|
||||
|
||||
ByteArray BlePeripheral::GetAdvertisementBytes(
|
||||
const std::string& service_id) const {
|
||||
return advertisement_bytes_;
|
||||
}
|
||||
|
||||
void BlePeripheral::SetAdvertisementBytes(
|
||||
const std::string& service_id, const ByteArray& advertisement_bytes) {
|
||||
advertisement_bytes_ = advertisement_bytes;
|
||||
}
|
||||
|
||||
BluetoothDevice::BluetoothDevice(BluetoothAdapter* adapter)
|
||||
: adapter_(*adapter) {}
|
||||
|
||||
std::string BluetoothDevice::GetName() const { return adapter_.GetName(); }
|
||||
std::string BluetoothDevice::GetMacAddress() const {
|
||||
return adapter_.GetMacAddress();
|
||||
}
|
||||
|
||||
BluetoothAdapter::BluetoothAdapter() {
|
||||
std::string mac_address;
|
||||
mac_address.resize(6);
|
||||
int64_t raw_mac_addr = Prng().NextInt64();
|
||||
mac_address[0] = static_cast<char>(raw_mac_addr >> 40);
|
||||
mac_address[1] = static_cast<char>(raw_mac_addr >> 32);
|
||||
mac_address[2] = static_cast<char>(raw_mac_addr >> 24);
|
||||
mac_address[3] = static_cast<char>(raw_mac_addr >> 16);
|
||||
mac_address[4] = static_cast<char>(raw_mac_addr >> 8);
|
||||
mac_address[5] = static_cast<char>(raw_mac_addr >> 0);
|
||||
SetMacAddress(mac_address);
|
||||
}
|
||||
|
||||
BluetoothAdapter::~BluetoothAdapter() { SetStatus(Status::kDisabled); }
|
||||
|
||||
void BluetoothAdapter::SetBluetoothClassicMedium(
|
||||
api::BluetoothClassicMedium* medium) {
|
||||
bluetooth_classic_medium_ = medium;
|
||||
}
|
||||
|
||||
void BluetoothAdapter::SetBleMedium(api::BleMedium* medium) {
|
||||
ble_medium_ = medium;
|
||||
}
|
||||
|
||||
bool BluetoothAdapter::SetStatus(Status status) {
|
||||
BluetoothAdapter::ScanMode mode;
|
||||
bool enabled = status == Status::kEnabled;
|
||||
std::string name;
|
||||
{
|
||||
absl::MutexLock lock(&mutex_);
|
||||
enabled_ = enabled;
|
||||
name = name_;
|
||||
mode = mode_;
|
||||
}
|
||||
auto& env = MediumEnvironment::Instance();
|
||||
env.OnBluetoothAdapterChangedState(*this, device_, name, enabled, mode);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BluetoothAdapter::IsEnabled() const {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
return enabled_;
|
||||
}
|
||||
|
||||
BluetoothAdapter::ScanMode BluetoothAdapter::GetScanMode() const {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
return mode_;
|
||||
}
|
||||
|
||||
bool BluetoothAdapter::SetScanMode(BluetoothAdapter::ScanMode mode) {
|
||||
bool enabled;
|
||||
std::string name;
|
||||
{
|
||||
absl::MutexLock lock(&mutex_);
|
||||
mode_ = mode;
|
||||
name = name_;
|
||||
enabled = enabled_;
|
||||
}
|
||||
auto& env = MediumEnvironment::Instance();
|
||||
env.OnBluetoothAdapterChangedState(*this, device_, std::move(name), enabled,
|
||||
mode);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string BluetoothAdapter::GetName() const {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
return name_;
|
||||
}
|
||||
|
||||
bool BluetoothAdapter::SetName(absl::string_view name) {
|
||||
BluetoothAdapter::ScanMode mode;
|
||||
bool enabled;
|
||||
{
|
||||
absl::MutexLock lock(&mutex_);
|
||||
name_ = name;
|
||||
enabled = enabled_;
|
||||
mode = mode_;
|
||||
}
|
||||
auto& env = MediumEnvironment::Instance();
|
||||
env.OnBluetoothAdapterChangedState(*this, device_, std::string(name), enabled,
|
||||
mode);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace g3
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -0,0 +1,142 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#ifndef PLATFORM_IMPL_G3_BLUETOOTH_ADAPTER_H_
|
||||
#define PLATFORM_IMPL_G3_BLUETOOTH_ADAPTER_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "absl/base/thread_annotations.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "internal/platform/implementation/ble.h"
|
||||
#include "internal/platform/implementation/bluetooth_adapter.h"
|
||||
#include "internal/platform/implementation/bluetooth_classic.h"
|
||||
#include "internal/platform/implementation/g3/single_thread_executor.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace g3 {
|
||||
|
||||
// BluetoothDevice and BluetoothAdapter have a mutual dependency.
|
||||
class BluetoothAdapter;
|
||||
|
||||
// Opaque wrapper over a Ble peripheral. Must contain enough data about a
|
||||
// particular Ble device to connect to its GATT server.
|
||||
class BlePeripheral : public api::BlePeripheral {
|
||||
public:
|
||||
~BlePeripheral() override = default;
|
||||
|
||||
std::string GetName() const override;
|
||||
ByteArray GetAdvertisementBytes(const std::string& service_id) const override;
|
||||
void SetAdvertisementBytes(const std::string& service_id,
|
||||
const ByteArray& advertisement_bytes);
|
||||
BluetoothAdapter& GetAdapter() { return adapter_; }
|
||||
|
||||
private:
|
||||
// Only BluetoothAdapter may instantiate BlePeripheral.
|
||||
friend class BluetoothAdapter;
|
||||
|
||||
explicit BlePeripheral(BluetoothAdapter* adapter);
|
||||
|
||||
BluetoothAdapter& adapter_;
|
||||
ByteArray advertisement_bytes_;
|
||||
};
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html.
|
||||
class BluetoothDevice : public api::BluetoothDevice {
|
||||
public:
|
||||
~BluetoothDevice() override = default;
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#getName()
|
||||
std::string GetName() const override;
|
||||
std::string GetMacAddress() const override;
|
||||
BluetoothAdapter& GetAdapter() { return adapter_; }
|
||||
|
||||
private:
|
||||
// Only BluetoothAdapter may instantiate BluetoothDevice.
|
||||
friend class BluetoothAdapter;
|
||||
|
||||
explicit BluetoothDevice(BluetoothAdapter* adapter);
|
||||
|
||||
BluetoothAdapter& adapter_;
|
||||
};
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html
|
||||
class BluetoothAdapter : public api::BluetoothAdapter {
|
||||
public:
|
||||
using Status = api::BluetoothAdapter::Status;
|
||||
using ScanMode = api::BluetoothAdapter::ScanMode;
|
||||
|
||||
BluetoothAdapter();
|
||||
~BluetoothAdapter() override;
|
||||
|
||||
// Synchronously sets the status of the BluetoothAdapter to 'status', and
|
||||
// returns true if the operation was a success.
|
||||
bool SetStatus(Status status) override ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Returns true if the BluetoothAdapter's current status is
|
||||
// Status::Value::kEnabled.
|
||||
bool IsEnabled() const override ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#getScanMode()
|
||||
//
|
||||
// Returns ScanMode::kUnknown on error.
|
||||
ScanMode GetScanMode() const override ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Synchronously sets the scan mode of the adapter, and returns true if the
|
||||
// operation was a success.
|
||||
bool SetScanMode(ScanMode mode) override ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#getName()
|
||||
// Returns an empty string on error
|
||||
std::string GetName() const override ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#setName(java.lang.String)
|
||||
bool SetName(absl::string_view name) override ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Returns BT MAC address assigned to this adapter.
|
||||
std::string GetMacAddress() const override { return mac_address_; }
|
||||
|
||||
BluetoothDevice& GetDevice() { return device_; }
|
||||
|
||||
void SetBluetoothClassicMedium(api::BluetoothClassicMedium* medium);
|
||||
api::BluetoothClassicMedium* GetBluetoothClassicMedium() {
|
||||
return bluetooth_classic_medium_;
|
||||
}
|
||||
|
||||
BlePeripheral& GetPeripheral() { return peripheral_; }
|
||||
|
||||
void SetBleMedium(api::BleMedium* medium);
|
||||
api::BleMedium* GetBleMedium() { return ble_medium_; }
|
||||
|
||||
void SetMacAddress(std::string& mac_address) { mac_address_ = mac_address; }
|
||||
|
||||
private:
|
||||
mutable absl::Mutex mutex_;
|
||||
BluetoothDevice device_{this};
|
||||
BlePeripheral peripheral_{this};
|
||||
api::BluetoothClassicMedium* bluetooth_classic_medium_ = nullptr;
|
||||
api::BleMedium* ble_medium_ = nullptr;
|
||||
std::string mac_address_;
|
||||
ScanMode mode_ ABSL_GUARDED_BY(mutex_) = ScanMode::kNone;
|
||||
std::string name_ ABSL_GUARDED_BY(mutex_) = "unknown G3 BT device";
|
||||
bool enabled_ ABSL_GUARDED_BY(mutex_) = false;
|
||||
};
|
||||
|
||||
} // namespace g3
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // PLATFORM_IMPL_G3_BLUETOOTH_ADAPTER_H_
|
||||
@@ -0,0 +1,278 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#include "internal/platform/implementation/g3/bluetooth_classic.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "internal/platform/implementation/bluetooth_classic.h"
|
||||
#include "internal/platform/cancellation_flag_listener.h"
|
||||
#include "internal/platform/logging.h"
|
||||
#include "internal/platform/medium_environment.h"
|
||||
#include "internal/platform/implementation/g3/bluetooth_adapter.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace g3 {
|
||||
|
||||
BluetoothSocket::~BluetoothSocket() {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
DoClose();
|
||||
}
|
||||
|
||||
void BluetoothSocket::Connect(BluetoothSocket& other) {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
remote_socket_ = &other;
|
||||
input_ = other.output_;
|
||||
}
|
||||
|
||||
bool BluetoothSocket::IsConnected() const {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
return IsConnectedLocked();
|
||||
}
|
||||
|
||||
bool BluetoothSocket::IsClosed() const {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
return closed_;
|
||||
}
|
||||
|
||||
bool BluetoothSocket::IsConnectedLocked() const { return input_ != nullptr; }
|
||||
|
||||
InputStream& BluetoothSocket::GetInputStream() {
|
||||
auto* remote_socket = GetRemoteSocket();
|
||||
CHECK(remote_socket != nullptr);
|
||||
return remote_socket->GetLocalInputStream();
|
||||
}
|
||||
|
||||
OutputStream& BluetoothSocket::GetOutputStream() {
|
||||
return GetLocalOutputStream();
|
||||
}
|
||||
|
||||
InputStream& BluetoothSocket::GetLocalInputStream() {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
return output_->GetInputStream();
|
||||
}
|
||||
|
||||
OutputStream& BluetoothSocket::GetLocalOutputStream() {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
return output_->GetOutputStream();
|
||||
}
|
||||
|
||||
Exception BluetoothSocket::Close() {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
DoClose();
|
||||
return {Exception::kSuccess};
|
||||
}
|
||||
|
||||
void BluetoothSocket::DoClose() {
|
||||
if (!closed_) {
|
||||
remote_socket_ = nullptr;
|
||||
output_->GetOutputStream().Close();
|
||||
output_->GetInputStream().Close();
|
||||
input_->GetOutputStream().Close();
|
||||
input_->GetInputStream().Close();
|
||||
closed_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
BluetoothSocket* BluetoothSocket::GetRemoteSocket() {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
return remote_socket_;
|
||||
}
|
||||
|
||||
BluetoothDevice* BluetoothSocket::GetRemoteDevice() {
|
||||
BluetoothAdapter* remote_adapter = nullptr;
|
||||
{
|
||||
absl::MutexLock lock(&mutex_);
|
||||
if (remote_socket_ == nullptr || remote_socket_->adapter_ == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
remote_adapter = remote_socket_->adapter_;
|
||||
}
|
||||
return remote_adapter ? &remote_adapter->GetDevice() : nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<api::BluetoothSocket> BluetoothServerSocket::Accept() {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
while (!closed_ && pending_sockets_.empty()) {
|
||||
cond_.Wait(&mutex_);
|
||||
}
|
||||
// whether or not we were running in the wait loop, return early if closed.
|
||||
if (closed_) return {};
|
||||
auto* remote_socket =
|
||||
pending_sockets_.extract(pending_sockets_.begin()).value();
|
||||
CHECK(remote_socket);
|
||||
auto local_socket = std::make_unique<BluetoothSocket>(adapter_);
|
||||
local_socket->Connect(*remote_socket);
|
||||
remote_socket->Connect(*local_socket);
|
||||
cond_.SignalAll();
|
||||
return local_socket;
|
||||
}
|
||||
|
||||
bool BluetoothServerSocket::Connect(BluetoothSocket& socket) {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
if (closed_) return false;
|
||||
if (socket.IsConnected()) {
|
||||
NEARBY_LOGS(ERROR)
|
||||
<< "Failed to connect to BT server socket: already connected";
|
||||
return true; // already connected.
|
||||
}
|
||||
// add client socket to the pending list
|
||||
pending_sockets_.emplace(&socket);
|
||||
cond_.SignalAll();
|
||||
while (!socket.IsConnected()) {
|
||||
cond_.Wait(&mutex_);
|
||||
if (closed_) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void BluetoothServerSocket::SetCloseNotifier(std::function<void()> notifier) {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
close_notifier_ = std::move(notifier);
|
||||
}
|
||||
|
||||
BluetoothServerSocket::~BluetoothServerSocket() {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
DoClose();
|
||||
}
|
||||
|
||||
Exception BluetoothServerSocket::Close() {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
return DoClose();
|
||||
}
|
||||
|
||||
Exception BluetoothServerSocket::DoClose() {
|
||||
bool should_notify = !closed_;
|
||||
closed_ = true;
|
||||
if (should_notify) {
|
||||
cond_.SignalAll();
|
||||
if (close_notifier_) {
|
||||
auto notifier = std::move(close_notifier_);
|
||||
mutex_.Unlock();
|
||||
// Notifier may contain calls to public API, and may cause deadlock, if
|
||||
// mutex_ is held during the call.
|
||||
notifier();
|
||||
mutex_.Lock();
|
||||
}
|
||||
}
|
||||
return {Exception::kSuccess};
|
||||
}
|
||||
|
||||
BluetoothClassicMedium::BluetoothClassicMedium(api::BluetoothAdapter& adapter)
|
||||
// TODO(apolyudov): implement and use downcast<> with static assertions.
|
||||
: adapter_(static_cast<BluetoothAdapter*>(&adapter)) {
|
||||
adapter_->SetBluetoothClassicMedium(this);
|
||||
auto& env = MediumEnvironment::Instance();
|
||||
env.RegisterBluetoothMedium(*this, GetAdapter());
|
||||
}
|
||||
|
||||
BluetoothClassicMedium::~BluetoothClassicMedium() {
|
||||
adapter_->SetBluetoothClassicMedium(nullptr);
|
||||
auto& env = MediumEnvironment::Instance();
|
||||
env.UnregisterBluetoothMedium(*this);
|
||||
}
|
||||
|
||||
bool BluetoothClassicMedium::StartDiscovery(DiscoveryCallback callback) {
|
||||
auto& env = MediumEnvironment::Instance();
|
||||
env.UpdateBluetoothMedium(*this, std::move(callback));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BluetoothClassicMedium::StopDiscovery() {
|
||||
auto& env = MediumEnvironment::Instance();
|
||||
env.UpdateBluetoothMedium(*this, {});
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<api::BluetoothSocket> BluetoothClassicMedium::ConnectToService(
|
||||
api::BluetoothDevice& remote_device, const std::string& service_uuid,
|
||||
CancellationFlag* cancellation_flag) {
|
||||
NEARBY_LOGS(INFO) << "G3 ConnectToService [self]: medium=" << this
|
||||
<< ", adapter=" << &GetAdapter()
|
||||
<< ", device=" << &GetAdapter().GetDevice();
|
||||
// First, find an instance of remote medium, that exposed this device.
|
||||
auto& adapter = static_cast<BluetoothDevice&>(remote_device).GetAdapter();
|
||||
auto* medium =
|
||||
static_cast<BluetoothClassicMedium*>(adapter.GetBluetoothClassicMedium());
|
||||
|
||||
if (!medium) return {}; // Adapter is not bound to medium. Bail out.
|
||||
|
||||
BluetoothServerSocket* server_socket = nullptr;
|
||||
NEARBY_LOGS(INFO) << "G3 ConnectToService [peer]: medium=" << medium
|
||||
<< ", adapter=" << &adapter << ", device=" << &remote_device
|
||||
<< ", uuid=" << service_uuid.c_str();
|
||||
// Then, find our server socket context in this medium.
|
||||
{
|
||||
absl::MutexLock medium_lock(&medium->mutex_);
|
||||
auto item = medium->sockets_.find(service_uuid);
|
||||
server_socket = item != sockets_.end() ? item->second : nullptr;
|
||||
if (server_socket == nullptr) {
|
||||
NEARBY_LOGS(ERROR) << "Failed to find BT Server socket: uuid="
|
||||
<< service_uuid;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
if (cancellation_flag->Cancelled()) {
|
||||
NEARBY_LOGS(ERROR) << "G3 Bluetooth Connect: Has been cancelled: "
|
||||
"service_uuid="
|
||||
<< service_uuid;
|
||||
return {};
|
||||
}
|
||||
|
||||
CancellationFlagListener listener(cancellation_flag, [&server_socket]() {
|
||||
NEARBY_LOGS(INFO) << "G3 Bluetooth Cancel Connect.";
|
||||
if (server_socket != nullptr) server_socket->Close();
|
||||
});
|
||||
|
||||
auto socket = std::make_unique<BluetoothSocket>(&GetAdapter());
|
||||
// Finally, Request to connect to this socket.
|
||||
if (!server_socket->Connect(*socket)) {
|
||||
NEARBY_LOGS(ERROR)
|
||||
<< "Failed to connect to existing BT Server socket: uuid="
|
||||
<< service_uuid;
|
||||
return {};
|
||||
}
|
||||
NEARBY_LOGS(INFO) << "G3 ConnectToService: connected: socket="
|
||||
<< socket.get();
|
||||
return socket;
|
||||
}
|
||||
|
||||
std::unique_ptr<api::BluetoothServerSocket>
|
||||
BluetoothClassicMedium::ListenForService(const std::string& service_name,
|
||||
const std::string& service_uuid) {
|
||||
auto socket = std::make_unique<BluetoothServerSocket>(GetAdapter());
|
||||
socket->SetCloseNotifier([this, uuid = service_uuid]() {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
sockets_.erase(uuid);
|
||||
});
|
||||
NEARBY_LOGS(INFO) << "Adding service: medium=" << this
|
||||
<< ", uuid=" << service_uuid;
|
||||
absl::MutexLock lock(&mutex_);
|
||||
sockets_.emplace(service_uuid, socket.get());
|
||||
return socket;
|
||||
}
|
||||
|
||||
api::BluetoothDevice* BluetoothClassicMedium::GetRemoteDevice(
|
||||
const std::string& mac_address) {
|
||||
auto& env = MediumEnvironment::Instance();
|
||||
return env.FindBluetoothDevice(mac_address);
|
||||
}
|
||||
|
||||
} // namespace g3
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -0,0 +1,238 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#ifndef PLATFORM_IMPL_G3_BLUETOOTH_CLASSIC_H_
|
||||
#define PLATFORM_IMPL_G3_BLUETOOTH_CLASSIC_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "absl/container/flat_hash_map.h"
|
||||
#include "absl/container/flat_hash_set.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "internal/platform/implementation/bluetooth_classic.h"
|
||||
#include "internal/platform/byte_array.h"
|
||||
#include "internal/platform/exception.h"
|
||||
#include "internal/platform/input_stream.h"
|
||||
#include "internal/platform/listeners.h"
|
||||
#include "internal/platform/output_stream.h"
|
||||
#include "internal/platform/implementation/g3/bluetooth_adapter.h"
|
||||
#include "internal/platform/implementation/g3/pipe.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace g3 {
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothSocket.html.
|
||||
class BluetoothSocket : public api::BluetoothSocket {
|
||||
public:
|
||||
BluetoothSocket() = default;
|
||||
explicit BluetoothSocket(BluetoothAdapter* adapter) : adapter_(adapter) {}
|
||||
~BluetoothSocket() override;
|
||||
|
||||
// Connects to another BluetoothSocket, to form a functional low-level
|
||||
// channel. From this point on, and until Close is called, connection exists.
|
||||
void Connect(BluetoothSocket& other);
|
||||
|
||||
// NOTE:
|
||||
// It is an undefined behavior if GetInputStream() or GetOutputStream() is
|
||||
// called for a not-connected BluetoothSocket, i.e. any object that is not
|
||||
// returned by BluetoothClassicMedium::ConnectToService() for client side or
|
||||
// BluetoothServerSocket::Accept() for server side of connection.
|
||||
|
||||
// Returns the InputStream of this connected BluetoothSocket.
|
||||
InputStream& GetInputStream() override;
|
||||
|
||||
// Returns the OutputStream of this connected BluetoothSocket.
|
||||
// This stream is for local side to write.
|
||||
OutputStream& GetOutputStream() override;
|
||||
|
||||
// Returns address of a remote BluetoothSocket or nullptr.
|
||||
BluetoothSocket* GetRemoteSocket() ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Returns true if connection exists to the (possibly closed) remote socket.
|
||||
bool IsConnected() const ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Returns true if socket is closed.
|
||||
bool IsClosed() const ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Closes both input and output streams, marks Socket as closed.
|
||||
// After this call object should be treated as not connected.
|
||||
// Returns Exception::kIo on error, Exception::kSuccess otherwise.
|
||||
Exception Close() override ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothSocket.html#getRemoteDevice()
|
||||
// Returns valid BluetoothDevice pointer if there is a connection, and
|
||||
// nullptr otherwise.
|
||||
BluetoothDevice* GetRemoteDevice() override ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
private:
|
||||
void DoClose() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
// Returns true if connection exists to the (possibly closed) remote socket.
|
||||
bool IsConnectedLocked() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
// Returns InputStream of our side of a connection.
|
||||
// This is what the remote side is supposed to read from.
|
||||
// This is a helper for GetInputStream() method.
|
||||
InputStream& GetLocalInputStream() ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Returns OutputStream of our side of a connection.
|
||||
// This is what the local size is supposed to write to.
|
||||
// This is a helper for GetOutputStream() method.
|
||||
OutputStream& GetLocalOutputStream() ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Output pipe is initialized by constructor, it remains always valid, until
|
||||
// it is closed. it represents output part of a local socket. Input part of a
|
||||
// local socket comes from the peer socket, after connection.
|
||||
std::shared_ptr<Pipe> output_{new Pipe};
|
||||
std::shared_ptr<Pipe> input_;
|
||||
mutable absl::Mutex mutex_;
|
||||
BluetoothAdapter* adapter_ = nullptr; // Our Adapter. Read only.
|
||||
BluetoothSocket* remote_socket_ ABSL_GUARDED_BY(mutex_) = nullptr;
|
||||
bool closed_ ABSL_GUARDED_BY(mutex_) = false;
|
||||
};
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothServerSocket.html.
|
||||
class BluetoothServerSocket : public api::BluetoothServerSocket {
|
||||
public:
|
||||
explicit BluetoothServerSocket(BluetoothAdapter& adapter)
|
||||
: adapter_(&adapter) {}
|
||||
~BluetoothServerSocket() override;
|
||||
|
||||
// Blocks until either:
|
||||
// - at least one incoming connection request is available, or
|
||||
// - ServerSocket is closed.
|
||||
// On success, returns connected socket, ready to exchange data.
|
||||
// Returns nullptr on error.
|
||||
// Once error is reported, it is permanent, and ServerSocket has to be closed.
|
||||
//
|
||||
// Called by the server side of a connection.
|
||||
// Returns BluetoothSocket to the server side.
|
||||
// If not null, returned socket is connected to its remote (client-side) peer.
|
||||
std::unique_ptr<api::BluetoothSocket> Accept() override
|
||||
ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Blocks until either:
|
||||
// - connection is available, or
|
||||
// - server socket is closed, or
|
||||
// - error happens.
|
||||
//
|
||||
// Called by the client side of a connection.
|
||||
// socket is an initialized BluetoothSocket, associated with a client
|
||||
// BluetoothAdapter.
|
||||
// Returns true, if socket is successfully connected.
|
||||
bool Connect(BluetoothSocket& socket) ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Called by the server side of a connection before passing ownership of
|
||||
// BluetoothServerSocker to user, to track validity of a pointer to this
|
||||
// server socket,
|
||||
void SetCloseNotifier(std::function<void()> notifier)
|
||||
ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Returns Exception::kIo on error, Exception::kSuccess otherwise.
|
||||
// Calls close_notifier if it was previously set, and marks socket as closed.
|
||||
Exception Close() override ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
private:
|
||||
Exception DoClose() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
absl::Mutex mutex_;
|
||||
absl::CondVar cond_;
|
||||
BluetoothAdapter* adapter_ = nullptr; // Our Adapter. Read only.
|
||||
absl::flat_hash_set<BluetoothSocket*> pending_sockets_
|
||||
ABSL_GUARDED_BY(mutex_);
|
||||
std::function<void()> close_notifier_ ABSL_GUARDED_BY(mutex_);
|
||||
bool closed_ ABSL_GUARDED_BY(mutex_) = false;
|
||||
};
|
||||
|
||||
// Container of operations that can be performed over the Bluetooth Classic
|
||||
// medium.
|
||||
class BluetoothClassicMedium : public api::BluetoothClassicMedium {
|
||||
public:
|
||||
explicit BluetoothClassicMedium(api::BluetoothAdapter& adapter);
|
||||
~BluetoothClassicMedium() override;
|
||||
|
||||
// NOTE(DiscoveryCallback):
|
||||
// BluetoothDevice is a proxy object created as a result of BT discovery.
|
||||
// Its lifetime spans between calls to device_discovered_cb and
|
||||
// device_lost_cb.
|
||||
// It is safe to use BluetoothDevice in device_discovered_cb() callback
|
||||
// and at any time afterwards, until device_lost_cb() is called.
|
||||
// It is not safe to use BluetoothDevice after returning from
|
||||
// device_lost_cb() callback.
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#startDiscovery()
|
||||
//
|
||||
// Returns true once the process of discovery has been initiated.
|
||||
bool StartDiscovery(DiscoveryCallback callback) override
|
||||
ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#cancelDiscovery()
|
||||
//
|
||||
// Returns true once discovery is well and truly stopped; after this returns,
|
||||
// there must be no more invocations of the DiscoveryCallback passed in to
|
||||
// StartDiscovery().
|
||||
bool StopDiscovery() override ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Connects to existing remote BT service.
|
||||
//
|
||||
// A combination of
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#createInsecureRfcommSocketToServiceRecord
|
||||
// followed by
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothSocket.html#connect().
|
||||
//
|
||||
// service_uuid is the canonical textual representation
|
||||
// (https://en.wikipedia.org/wiki/Universally_unique_identifier#Format) of a
|
||||
// type 3 name-based
|
||||
// (https://en.wikipedia.org/wiki/Universally_unique_identifier#Versions_3_and_5_(namespace_name-based))
|
||||
// UUID.
|
||||
//
|
||||
// On success, returns a new BluetoothSocket.
|
||||
// On error, returns nullptr.
|
||||
std::unique_ptr<api::BluetoothSocket> ConnectToService(
|
||||
api::BluetoothDevice& remote_device, const std::string& service_uuid,
|
||||
CancellationFlag* cancellation_flag) override ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
BluetoothAdapter& GetAdapter() { return *adapter_; }
|
||||
|
||||
// Creates BT service, and begins listening for remote attempts to connect.
|
||||
//
|
||||
// https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#listenUsingInsecureRfcommWithServiceRecord
|
||||
//
|
||||
// service_uuid is the canonical textual representation
|
||||
// (https://en.wikipedia.org/wiki/Universally_unique_identifier#Format) of a
|
||||
// type 3 name-based
|
||||
// (https://en.wikipedia.org/wiki/Universally_unique_identifier#Versions_3_and_5_(namespace_name-based))
|
||||
// UUID.
|
||||
//
|
||||
// Returns nullptr on error.
|
||||
std::unique_ptr<api::BluetoothServerSocket> ListenForService(
|
||||
const std::string& service_name, const std::string& service_uuid) override
|
||||
ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
api::BluetoothDevice* GetRemoteDevice(
|
||||
const std::string& mac_address) override;
|
||||
|
||||
private:
|
||||
absl::Mutex mutex_;
|
||||
BluetoothAdapter* adapter_; // Our device adapter; read-only.
|
||||
absl::flat_hash_map<std::string, BluetoothServerSocket*> sockets_
|
||||
ABSL_GUARDED_BY(mutex_);
|
||||
};
|
||||
|
||||
} // namespace g3
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // PLATFORM_IMPL_G3_BLUETOOTH_CLASSIC_H_
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#ifndef PLATFORM_IMPL_G3_CONDITION_VARIABLE_H_
|
||||
#define PLATFORM_IMPL_G3_CONDITION_VARIABLE_H_
|
||||
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "internal/platform/implementation/condition_variable.h"
|
||||
#include "internal/platform/exception.h"
|
||||
#include "internal/platform/implementation/g3/mutex.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace g3 {
|
||||
|
||||
class ConditionVariable : public api::ConditionVariable {
|
||||
public:
|
||||
explicit ConditionVariable(g3::Mutex* mutex) : mutex_(&mutex->mutex_) {}
|
||||
~ConditionVariable() override = default;
|
||||
|
||||
Exception Wait() override {
|
||||
cond_var_.Wait(mutex_);
|
||||
return {Exception::kSuccess};
|
||||
}
|
||||
Exception Wait(absl::Duration timeout) override {
|
||||
cond_var_.WaitWithTimeout(mutex_, timeout);
|
||||
return {Exception::kSuccess};
|
||||
}
|
||||
void Notify() override { cond_var_.SignalAll(); }
|
||||
|
||||
private:
|
||||
absl::Mutex* mutex_;
|
||||
absl::CondVar cond_var_;
|
||||
};
|
||||
|
||||
} // namespace g3
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // PLATFORM_IMPL_G3_CONDITION_VARIABLE_H_
|
||||
@@ -0,0 +1,53 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#include "internal/platform/implementation/crypto.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "internal/platform/byte_array.h"
|
||||
#include "src/include/openssl/digest.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
|
||||
// Initialize global crypto state.
|
||||
void Crypto::Init() {}
|
||||
|
||||
static ByteArray Hash(absl::string_view input, const EVP_MD* algo) {
|
||||
unsigned int md_out_size = EVP_MAX_MD_SIZE;
|
||||
uint8_t digest_buffer[EVP_MAX_MD_SIZE];
|
||||
if (input.empty()) return {};
|
||||
|
||||
if (!EVP_Digest(input.data(), input.size(), digest_buffer, &md_out_size, algo,
|
||||
nullptr))
|
||||
return {};
|
||||
|
||||
return ByteArray{reinterpret_cast<char*>(digest_buffer), md_out_size};
|
||||
}
|
||||
|
||||
// Return MD5 hash of input.
|
||||
ByteArray Crypto::Md5(absl::string_view input) {
|
||||
return Hash(input, EVP_md5());
|
||||
}
|
||||
|
||||
// Return SHA256 hash of input.
|
||||
ByteArray Crypto::Sha256(absl::string_view input) {
|
||||
return Hash(input, EVP_sha256());
|
||||
}
|
||||
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -0,0 +1,74 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#include "internal/platform/implementation/g3/log_message.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "base/stringprintf.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace g3 {
|
||||
|
||||
api::LogMessage::Severity g_min_log_severity = api::LogMessage::Severity::kInfo;
|
||||
|
||||
inline absl::LogSeverity ConvertSeverity(api::LogMessage::Severity severity) {
|
||||
switch (severity) {
|
||||
// api::LogMessage::Severity kVerbose and kInfo is mapped to
|
||||
// absl::LogSeverity kInfo since absl::LogSeverity doesn't have kVerbose
|
||||
// level.
|
||||
case api::LogMessage::Severity::kVerbose:
|
||||
case api::LogMessage::Severity::kInfo:
|
||||
return absl::LogSeverity::kInfo;
|
||||
case api::LogMessage::Severity::kWarning:
|
||||
return absl::LogSeverity::kWarning;
|
||||
case api::LogMessage::Severity::kError:
|
||||
return absl::LogSeverity::kError;
|
||||
case api::LogMessage::Severity::kFatal:
|
||||
return absl::LogSeverity::kFatal;
|
||||
}
|
||||
}
|
||||
|
||||
LogMessage::LogMessage(const char* file, int line, Severity severity)
|
||||
: log_streamer_(ConvertSeverity(severity), file, line) {}
|
||||
|
||||
LogMessage::~LogMessage() = default;
|
||||
|
||||
void LogMessage::Print(const char* format, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
std::string result;
|
||||
StringAppendV(&result, format, ap);
|
||||
log_streamer_.stream() << result;
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
std::ostream& LogMessage::Stream() { return log_streamer_.stream(); }
|
||||
|
||||
} // namespace g3
|
||||
|
||||
namespace api {
|
||||
|
||||
void LogMessage::SetMinLogSeverity(Severity severity) {
|
||||
g3::g_min_log_severity = severity;
|
||||
}
|
||||
|
||||
bool LogMessage::ShouldCreateLogMessage(Severity severity) {
|
||||
return severity >= g3::g_min_log_severity;
|
||||
}
|
||||
|
||||
} // namespace api
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#ifndef PLATFORM_IMPL_G3_LOG_MESSAGE_H_
|
||||
#define PLATFORM_IMPL_G3_LOG_MESSAGE_H_
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "internal/platform/implementation/log_message.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace g3 {
|
||||
|
||||
// See documentation in
|
||||
// cpp/platform/api/log_message.h
|
||||
class LogMessage : public api::LogMessage {
|
||||
public:
|
||||
LogMessage(const char* file, int line, Severity severity);
|
||||
~LogMessage() override;
|
||||
|
||||
void Print(const char* format, ...) override;
|
||||
|
||||
std::ostream& Stream() override;
|
||||
|
||||
private:
|
||||
google::LogMessage log_streamer_;
|
||||
};
|
||||
|
||||
} // namespace g3
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // PLATFORM_IMPL_G3_LOG_MESSAGE_H_
|
||||
@@ -0,0 +1,66 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#ifndef PLATFORM_IMPL_G3_MULTI_THREAD_EXECUTOR_H_
|
||||
#define PLATFORM_IMPL_G3_MULTI_THREAD_EXECUTOR_H_
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "absl/time/clock.h"
|
||||
#include "internal/platform/implementation/submittable_executor.h"
|
||||
#include "internal/platform/implementation/shared/count_down_latch.h"
|
||||
#include "nisaba/port/thread_pool.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace g3 {
|
||||
|
||||
// An Executor that reuses a fixed number of threads operating off a shared
|
||||
// unbounded queue.
|
||||
class MultiThreadExecutor : public api::SubmittableExecutor {
|
||||
public:
|
||||
explicit MultiThreadExecutor(int max_parallelism)
|
||||
: thread_pool_(max_parallelism) {
|
||||
thread_pool_.StartWorkers();
|
||||
}
|
||||
void Execute(Runnable&& runnable) override {
|
||||
if (!shutdown_) {
|
||||
thread_pool_.Schedule(std::move(runnable));
|
||||
}
|
||||
}
|
||||
bool DoSubmit(Runnable&& runnable) override {
|
||||
if (shutdown_) return false;
|
||||
thread_pool_.Schedule(std::move(runnable));
|
||||
return true;
|
||||
}
|
||||
void Shutdown() override { DoShutdown(); }
|
||||
~MultiThreadExecutor() override { DoShutdown(); }
|
||||
|
||||
void ScheduleAfter(absl::Duration delay, Runnable&& runnable) {
|
||||
if (shutdown_) return;
|
||||
thread_pool_.ScheduleAt(absl::Now() + delay, std::move(runnable));
|
||||
}
|
||||
bool InShutdown() const { return shutdown_; }
|
||||
|
||||
private:
|
||||
void DoShutdown() { shutdown_ = true; }
|
||||
std::atomic_bool shutdown_ = false;
|
||||
ThreadPool thread_pool_;
|
||||
};
|
||||
|
||||
} // namespace g3
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // PLATFORM_IMPL_G3_MULTI_THREAD_EXECUTOR_H_
|
||||
@@ -0,0 +1,61 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#ifndef PLATFORM_IMPL_G3_MUTEX_H_
|
||||
#define PLATFORM_IMPL_G3_MUTEX_H_
|
||||
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "internal/platform/implementation/mutex.h"
|
||||
#include "internal/platform/implementation/shared/posix_mutex.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace g3 {
|
||||
|
||||
class ABSL_LOCKABLE Mutex : public api::Mutex {
|
||||
public:
|
||||
explicit Mutex(bool check) : check_(check) {}
|
||||
~Mutex() override = default;
|
||||
Mutex(Mutex&&) = delete;
|
||||
Mutex& operator=(Mutex&&) = delete;
|
||||
Mutex(const Mutex&) = delete;
|
||||
Mutex& operator=(const Mutex&) = delete;
|
||||
|
||||
void Lock() ABSL_EXCLUSIVE_LOCK_FUNCTION() override {
|
||||
mutex_.Lock();
|
||||
if (!check_) mutex_.ForgetDeadlockInfo();
|
||||
}
|
||||
void Unlock() ABSL_UNLOCK_FUNCTION() override { mutex_.Unlock(); }
|
||||
|
||||
private:
|
||||
friend class ConditionVariable;
|
||||
absl::Mutex mutex_;
|
||||
bool check_;
|
||||
};
|
||||
|
||||
class ABSL_LOCKABLE RecursiveMutex : public posix::Mutex {
|
||||
public:
|
||||
~RecursiveMutex() override = default;
|
||||
RecursiveMutex() = default;
|
||||
RecursiveMutex(RecursiveMutex&&) = delete;
|
||||
RecursiveMutex& operator=(RecursiveMutex&&) = delete;
|
||||
RecursiveMutex(const RecursiveMutex&) = delete;
|
||||
RecursiveMutex& operator=(const RecursiveMutex&) = delete;
|
||||
};
|
||||
|
||||
} // namespace g3
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // PLATFORM_IMPL_G3_MUTEX_H_
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#ifndef PLATFORM_IMPL_G3_PIPE_H_
|
||||
#define PLATFORM_IMPL_G3_PIPE_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "internal/platform/base_pipe.h"
|
||||
#include "internal/platform/implementation/g3/condition_variable.h"
|
||||
#include "internal/platform/implementation/g3/mutex.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace g3 {
|
||||
|
||||
class Pipe : public BasePipe {
|
||||
public:
|
||||
Pipe() {
|
||||
auto mutex = std::make_unique<g3::Mutex>(/*check=*/true);
|
||||
auto cond = std::make_unique<g3::ConditionVariable>(mutex.get());
|
||||
Setup(std::move(mutex), std::move(cond));
|
||||
}
|
||||
~Pipe() override = default;
|
||||
Pipe(Pipe&&) = delete;
|
||||
Pipe& operator=(Pipe&&) = delete;
|
||||
};
|
||||
|
||||
} // namespace g3
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // PLATFORM_IMPL_G3_PIPE_H_
|
||||
@@ -0,0 +1,172 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#include "internal/platform/implementation/platform.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
#include "absl/memory/memory.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/time/time.h"
|
||||
#include "internal/platform/implementation/atomic_boolean.h"
|
||||
#include "internal/platform/implementation/atomic_reference.h"
|
||||
#include "internal/platform/implementation/ble_v2.h"
|
||||
#include "internal/platform/implementation/bluetooth_adapter.h"
|
||||
#include "internal/platform/implementation/bluetooth_classic.h"
|
||||
#include "internal/platform/implementation/condition_variable.h"
|
||||
#include "internal/platform/implementation/shared/count_down_latch.h"
|
||||
#include "internal/platform/implementation/log_message.h"
|
||||
#include "internal/platform/implementation/mutex.h"
|
||||
#include "internal/platform/implementation/scheduled_executor.h"
|
||||
#include "internal/platform/implementation/server_sync.h"
|
||||
#include "internal/platform/implementation/submittable_executor.h"
|
||||
#include "internal/platform/implementation/webrtc.h"
|
||||
#include "internal/platform/implementation/wifi.h"
|
||||
#include "internal/platform/medium_environment.h"
|
||||
#include "internal/platform/implementation/g3/atomic_boolean.h"
|
||||
#include "internal/platform/implementation/g3/atomic_reference.h"
|
||||
#include "internal/platform/implementation/g3/ble.h"
|
||||
#include "internal/platform/implementation/g3/bluetooth_adapter.h"
|
||||
#include "internal/platform/implementation/g3/bluetooth_classic.h"
|
||||
#include "internal/platform/implementation/g3/condition_variable.h"
|
||||
#include "internal/platform/implementation/g3/log_message.h"
|
||||
#include "internal/platform/implementation/g3/multi_thread_executor.h"
|
||||
#include "internal/platform/implementation/g3/mutex.h"
|
||||
#include "internal/platform/implementation/g3/scheduled_executor.h"
|
||||
#include "internal/platform/implementation/g3/single_thread_executor.h"
|
||||
#include "internal/platform/implementation/g3/webrtc.h"
|
||||
#include "internal/platform/implementation/g3/wifi_lan.h"
|
||||
#include "internal/platform/implementation/shared/file.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace api {
|
||||
|
||||
namespace {
|
||||
std::string GetPayloadPath(PayloadId payload_id) {
|
||||
return absl::StrCat("/tmp/", payload_id);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int GetCurrentTid() {
|
||||
const LiveThread* my = Thread_GetMyLiveThread();
|
||||
return LiveThread_Pthread_TID(my);
|
||||
}
|
||||
|
||||
std::unique_ptr<SubmittableExecutor>
|
||||
ImplementationPlatform::CreateSingleThreadExecutor() {
|
||||
return absl::make_unique<g3::SingleThreadExecutor>();
|
||||
}
|
||||
|
||||
std::unique_ptr<SubmittableExecutor>
|
||||
ImplementationPlatform::CreateMultiThreadExecutor(int max_concurrency) {
|
||||
return absl::make_unique<g3::MultiThreadExecutor>(max_concurrency);
|
||||
}
|
||||
|
||||
std::unique_ptr<ScheduledExecutor>
|
||||
ImplementationPlatform::CreateScheduledExecutor() {
|
||||
return absl::make_unique<g3::ScheduledExecutor>();
|
||||
}
|
||||
|
||||
std::unique_ptr<AtomicUint32> ImplementationPlatform::CreateAtomicUint32(
|
||||
std::uint32_t value) {
|
||||
return absl::make_unique<g3::AtomicUint32>(value);
|
||||
}
|
||||
|
||||
std::unique_ptr<BluetoothAdapter>
|
||||
ImplementationPlatform::CreateBluetoothAdapter() {
|
||||
return absl::make_unique<g3::BluetoothAdapter>();
|
||||
}
|
||||
|
||||
std::unique_ptr<CountDownLatch> ImplementationPlatform::CreateCountDownLatch(
|
||||
std::int32_t count) {
|
||||
return absl::make_unique<shared::CountDownLatch>(count);
|
||||
}
|
||||
|
||||
std::unique_ptr<AtomicBoolean> ImplementationPlatform::CreateAtomicBoolean(
|
||||
bool initial_value) {
|
||||
return absl::make_unique<g3::AtomicBoolean>(initial_value);
|
||||
}
|
||||
|
||||
std::unique_ptr<InputFile> ImplementationPlatform::CreateInputFile(
|
||||
PayloadId payload_id, std::int64_t total_size) {
|
||||
return absl::make_unique<shared::InputFile>(GetPayloadPath(payload_id),
|
||||
total_size);
|
||||
}
|
||||
|
||||
std::unique_ptr<OutputFile> ImplementationPlatform::CreateOutputFile(
|
||||
PayloadId payload_id) {
|
||||
return absl::make_unique<shared::OutputFile>(GetPayloadPath(payload_id));
|
||||
}
|
||||
|
||||
std::unique_ptr<LogMessage> ImplementationPlatform::CreateLogMessage(
|
||||
const char* file, int line, LogMessage::Severity severity) {
|
||||
return absl::make_unique<g3::LogMessage>(file, line, severity);
|
||||
}
|
||||
|
||||
std::unique_ptr<BluetoothClassicMedium>
|
||||
ImplementationPlatform::CreateBluetoothClassicMedium(
|
||||
api::BluetoothAdapter& adapter) {
|
||||
return absl::make_unique<g3::BluetoothClassicMedium>(adapter);
|
||||
}
|
||||
|
||||
std::unique_ptr<BleMedium> ImplementationPlatform::CreateBleMedium(
|
||||
api::BluetoothAdapter& adapter) {
|
||||
return absl::make_unique<g3::BleMedium>(adapter);
|
||||
}
|
||||
|
||||
std::unique_ptr<ble_v2::BleMedium> ImplementationPlatform::CreateBleV2Medium(
|
||||
api::BluetoothAdapter& adapter) {
|
||||
return std::unique_ptr<ble_v2::BleMedium>();
|
||||
}
|
||||
|
||||
std::unique_ptr<ServerSyncMedium>
|
||||
ImplementationPlatform::CreateServerSyncMedium() {
|
||||
return std::unique_ptr<ServerSyncMedium>(/*new ServerSyncMediumImpl()*/);
|
||||
}
|
||||
|
||||
std::unique_ptr<WifiMedium> ImplementationPlatform::CreateWifiMedium() {
|
||||
return std::unique_ptr<WifiMedium>();
|
||||
}
|
||||
|
||||
std::unique_ptr<WifiLanMedium> ImplementationPlatform::CreateWifiLanMedium() {
|
||||
return absl::make_unique<g3::WifiLanMedium>();
|
||||
}
|
||||
|
||||
std::unique_ptr<WebRtcMedium> ImplementationPlatform::CreateWebRtcMedium() {
|
||||
if (MediumEnvironment::Instance().GetEnvironmentConfig().webrtc_enabled) {
|
||||
return absl::make_unique<g3::WebRtcMedium>();
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<Mutex> ImplementationPlatform::CreateMutex(Mutex::Mode mode) {
|
||||
if (mode == Mutex::Mode::kRecursive)
|
||||
return absl::make_unique<g3::RecursiveMutex>();
|
||||
else
|
||||
return absl::make_unique<g3::Mutex>(mode == Mutex::Mode::kRegular);
|
||||
}
|
||||
|
||||
std::unique_ptr<ConditionVariable>
|
||||
ImplementationPlatform::CreateConditionVariable(Mutex* mutex) {
|
||||
return std::unique_ptr<ConditionVariable>(
|
||||
new g3::ConditionVariable(static_cast<g3::Mutex*>(mutex)));
|
||||
}
|
||||
|
||||
} // namespace api
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -0,0 +1,79 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#include "internal/platform/implementation/g3/scheduled_executor.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
|
||||
#include "absl/time/clock.h"
|
||||
#include "internal/platform/implementation/cancelable.h"
|
||||
#include "internal/platform/runnable.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace g3 {
|
||||
|
||||
namespace {
|
||||
|
||||
class ScheduledCancelable : public api::Cancelable {
|
||||
public:
|
||||
bool Cancel() override {
|
||||
Status expected = kNotRun;
|
||||
while (expected == kNotRun) {
|
||||
if (status_.compare_exchange_strong(expected, kCanceled)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool MarkExecuted() {
|
||||
Status expected = kNotRun;
|
||||
while (expected == kNotRun) {
|
||||
if (status_.compare_exchange_strong(expected, kExecuted)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
enum Status {
|
||||
kNotRun,
|
||||
kExecuted,
|
||||
kCanceled,
|
||||
};
|
||||
std::atomic<Status> status_ = kNotRun;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
std::shared_ptr<api::Cancelable> ScheduledExecutor::Schedule(
|
||||
Runnable&& runnable, absl::Duration delay) {
|
||||
auto scheduled_cancelable = std::make_shared<ScheduledCancelable>();
|
||||
if (executor_.InShutdown()) {
|
||||
return scheduled_cancelable;
|
||||
}
|
||||
executor_.ScheduleAfter(
|
||||
delay, [this, scheduled_cancelable, runnable(std::move(runnable))]() {
|
||||
if (!executor_.InShutdown() && scheduled_cancelable->MarkExecuted()) {
|
||||
runnable();
|
||||
}
|
||||
});
|
||||
return scheduled_cancelable;
|
||||
}
|
||||
|
||||
} // namespace g3
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -0,0 +1,54 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#ifndef PLATFORM_IMPL_G3_SCHEDULED_EXECUTOR_H_
|
||||
#define PLATFORM_IMPL_G3_SCHEDULED_EXECUTOR_H_
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
|
||||
#include "absl/time/clock.h"
|
||||
#include "internal/platform/implementation/cancelable.h"
|
||||
#include "internal/platform/implementation/scheduled_executor.h"
|
||||
#include "internal/platform/runnable.h"
|
||||
#include "internal/platform/implementation/g3/single_thread_executor.h"
|
||||
#include "nisaba/port/thread_pool.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace g3 {
|
||||
|
||||
// An Executor that reuses a fixed number of threads operating off a shared
|
||||
// unbounded queue.
|
||||
class ScheduledExecutor final : public api::ScheduledExecutor {
|
||||
public:
|
||||
ScheduledExecutor() = default;
|
||||
~ScheduledExecutor() override { executor_.Shutdown(); }
|
||||
|
||||
void Execute(Runnable&& runnable) override {
|
||||
executor_.Execute(std::move(runnable));
|
||||
}
|
||||
std::shared_ptr<api::Cancelable> Schedule(Runnable&& runnable,
|
||||
absl::Duration delay) override;
|
||||
void Shutdown() override { executor_.Shutdown(); }
|
||||
|
||||
private:
|
||||
SingleThreadExecutor executor_;
|
||||
};
|
||||
|
||||
} // namespace g3
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // PLATFORM_IMPL_G3_SCHEDULED_EXECUTOR_H_
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#ifndef PLATFORM_IMPL_G3_SINGLE_THREAD_EXECUTOR_H_
|
||||
#define PLATFORM_IMPL_G3_SINGLE_THREAD_EXECUTOR_H_
|
||||
|
||||
#include "internal/platform/implementation/g3/multi_thread_executor.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace g3 {
|
||||
|
||||
// An Executor that uses a single worker thread operating off an unbounded
|
||||
// queue.
|
||||
class SingleThreadExecutor final : public MultiThreadExecutor {
|
||||
public:
|
||||
SingleThreadExecutor() : MultiThreadExecutor(1) {}
|
||||
~SingleThreadExecutor() override = default;
|
||||
};
|
||||
|
||||
} // namespace g3
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // PLATFORM_IMPL_G3_SINGLE_THREAD_EXECUTOR_H_
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#include "internal/platform/implementation/system_clock.h"
|
||||
|
||||
#include "absl/time/clock.h"
|
||||
#include "internal/platform/exception.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
|
||||
absl::Time SystemClock::ElapsedRealtime() { return absl::Now(); }
|
||||
Exception SystemClock::Sleep(absl::Duration duration) {
|
||||
absl::SleepFor(duration);
|
||||
return {Exception::kSuccess};
|
||||
}
|
||||
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -0,0 +1,97 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#include "internal/platform/implementation/g3/webrtc.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "internal/platform/medium_environment.h"
|
||||
#include "webrtc/api/task_queue/default_task_queue_factory.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace g3 {
|
||||
|
||||
WebRtcSignalingMessenger::WebRtcSignalingMessenger(
|
||||
absl::string_view self_id, const connections::LocationHint& location_hint)
|
||||
: self_id_(self_id), location_hint_(location_hint) {}
|
||||
|
||||
bool WebRtcSignalingMessenger::SendMessage(absl::string_view peer_id,
|
||||
const ByteArray& message) {
|
||||
auto& env = MediumEnvironment::Instance();
|
||||
env.SendWebRtcSignalingMessage(peer_id, message);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WebRtcSignalingMessenger::StartReceivingMessages(
|
||||
OnSignalingMessageCallback on_message_callback,
|
||||
OnSignalingCompleteCallback on_complete_callback) {
|
||||
auto& env = MediumEnvironment::Instance();
|
||||
env.RegisterWebRtcSignalingMessenger(self_id_, on_message_callback,
|
||||
on_complete_callback);
|
||||
return true;
|
||||
}
|
||||
|
||||
void WebRtcSignalingMessenger::StopReceivingMessages() {
|
||||
auto& env = MediumEnvironment::Instance();
|
||||
env.UnregisterWebRtcSignalingMessenger(self_id_);
|
||||
}
|
||||
|
||||
WebRtcMedium::~WebRtcMedium() { single_thread_executor_.Shutdown(); }
|
||||
|
||||
const std::string WebRtcMedium::GetDefaultCountryCode() { return "US"; }
|
||||
|
||||
void WebRtcMedium::CreatePeerConnection(
|
||||
webrtc::PeerConnectionObserver* observer, PeerConnectionCallback callback) {
|
||||
auto& env = MediumEnvironment::Instance();
|
||||
if (!env.GetUseValidPeerConnection()) {
|
||||
callback(nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
webrtc::PeerConnectionInterface::RTCConfiguration rtc_config;
|
||||
rtc_config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan;
|
||||
webrtc::PeerConnectionDependencies dependencies(observer);
|
||||
|
||||
std::unique_ptr<rtc::Thread> signaling_thread = rtc::Thread::Create();
|
||||
signaling_thread->SetName("signaling_thread", nullptr);
|
||||
RTC_CHECK(signaling_thread->Start()) << "Failed to start thread";
|
||||
|
||||
webrtc::PeerConnectionFactoryDependencies factory_dependencies;
|
||||
factory_dependencies.task_queue_factory =
|
||||
webrtc::CreateDefaultTaskQueueFactory();
|
||||
factory_dependencies.signaling_thread = signaling_thread.release();
|
||||
|
||||
rtc::scoped_refptr<webrtc::PeerConnectionInterface> peer_connection =
|
||||
webrtc::CreateModularPeerConnectionFactory(
|
||||
std::move(factory_dependencies))
|
||||
->CreatePeerConnection(rtc_config, std::move(dependencies));
|
||||
|
||||
single_thread_executor_.Execute(
|
||||
[&env, callback = std::move(callback),
|
||||
peer_connection = std::move(peer_connection)]() {
|
||||
absl::SleepFor(env.GetPeerConnectionLatency());
|
||||
callback(peer_connection);
|
||||
});
|
||||
}
|
||||
|
||||
std::unique_ptr<api::WebRtcSignalingMessenger>
|
||||
WebRtcMedium::GetSignalingMessenger(
|
||||
absl::string_view self_id, const connections::LocationHint& location_hint) {
|
||||
return std::make_unique<WebRtcSignalingMessenger>(self_id, location_hint);
|
||||
}
|
||||
|
||||
} // namespace g3
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -0,0 +1,81 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#ifndef PLATFORM_IMPL_G3_WEBRTC_H_
|
||||
#define PLATFORM_IMPL_G3_WEBRTC_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "internal/platform/implementation/webrtc.h"
|
||||
#include "internal/platform/implementation/g3/single_thread_executor.h"
|
||||
#include "webrtc/api/peer_connection_interface.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace g3 {
|
||||
|
||||
class WebRtcSignalingMessenger : public api::WebRtcSignalingMessenger {
|
||||
public:
|
||||
using OnSignalingMessageCallback =
|
||||
api::WebRtcSignalingMessenger::OnSignalingMessageCallback;
|
||||
using OnSignalingCompleteCallback =
|
||||
api::WebRtcSignalingMessenger::OnSignalingCompleteCallback;
|
||||
|
||||
explicit WebRtcSignalingMessenger(
|
||||
absl::string_view self_id,
|
||||
const connections::LocationHint& location_hint);
|
||||
~WebRtcSignalingMessenger() override = default;
|
||||
|
||||
bool SendMessage(absl::string_view peer_id,
|
||||
const ByteArray& message) override;
|
||||
bool StartReceivingMessages(
|
||||
OnSignalingMessageCallback on_message_callback,
|
||||
OnSignalingCompleteCallback on_complete_callback) override;
|
||||
void StopReceivingMessages() override;
|
||||
|
||||
private:
|
||||
std::string self_id_;
|
||||
connections::LocationHint location_hint_;
|
||||
};
|
||||
|
||||
class WebRtcMedium : public api::WebRtcMedium {
|
||||
public:
|
||||
using PeerConnectionCallback = api::WebRtcMedium::PeerConnectionCallback;
|
||||
|
||||
WebRtcMedium() = default;
|
||||
~WebRtcMedium() override;
|
||||
|
||||
const std::string GetDefaultCountryCode() override;
|
||||
|
||||
// Creates and returns a new webrtc::PeerConnectionInterface object via
|
||||
// |callback|.
|
||||
void CreatePeerConnection(webrtc::PeerConnectionObserver* observer,
|
||||
PeerConnectionCallback callback) override;
|
||||
|
||||
// Returns a signaling messenger for sending WebRTC signaling messages.
|
||||
std::unique_ptr<api::WebRtcSignalingMessenger> GetSignalingMessenger(
|
||||
absl::string_view self_id,
|
||||
const connections::LocationHint& location_hint) override;
|
||||
|
||||
private:
|
||||
// Executor for handling calls to create a peer connection.
|
||||
SingleThreadExecutor single_thread_executor_;
|
||||
};
|
||||
|
||||
} // namespace g3
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // PLATFORM_IMPL_G3_WEBRTC_H_
|
||||
@@ -0,0 +1,371 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#include "internal/platform/implementation/g3/wifi_lan.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "internal/platform/implementation/wifi_lan.h"
|
||||
#include "internal/platform/cancellation_flag_listener.h"
|
||||
#include "internal/platform/logging.h"
|
||||
#include "internal/platform/medium_environment.h"
|
||||
#include "internal/platform/nsd_service_info.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace g3 {
|
||||
|
||||
WifiLanSocket::~WifiLanSocket() {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
DoClose();
|
||||
}
|
||||
|
||||
void WifiLanSocket::Connect(WifiLanSocket& other) {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
remote_socket_ = &other;
|
||||
input_ = other.output_;
|
||||
}
|
||||
|
||||
InputStream& WifiLanSocket::GetInputStream() {
|
||||
auto* remote_socket = GetRemoteSocket();
|
||||
CHECK(remote_socket != nullptr);
|
||||
return remote_socket->GetLocalInputStream();
|
||||
}
|
||||
|
||||
OutputStream& WifiLanSocket::GetOutputStream() {
|
||||
return GetLocalOutputStream();
|
||||
}
|
||||
|
||||
WifiLanSocket* WifiLanSocket::GetRemoteSocket() {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
return remote_socket_;
|
||||
}
|
||||
|
||||
bool WifiLanSocket::IsConnected() const {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
return IsConnectedLocked();
|
||||
}
|
||||
|
||||
bool WifiLanSocket::IsClosed() const {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
return closed_;
|
||||
}
|
||||
|
||||
Exception WifiLanSocket::Close() {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
DoClose();
|
||||
return {Exception::kSuccess};
|
||||
}
|
||||
|
||||
void WifiLanSocket::DoClose() {
|
||||
if (!closed_) {
|
||||
remote_socket_ = nullptr;
|
||||
output_->GetOutputStream().Close();
|
||||
output_->GetInputStream().Close();
|
||||
input_->GetOutputStream().Close();
|
||||
input_->GetInputStream().Close();
|
||||
closed_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool WifiLanSocket::IsConnectedLocked() const { return input_ != nullptr; }
|
||||
|
||||
InputStream& WifiLanSocket::GetLocalInputStream() {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
return output_->GetInputStream();
|
||||
}
|
||||
|
||||
OutputStream& WifiLanSocket::GetLocalOutputStream() {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
return output_->GetOutputStream();
|
||||
}
|
||||
|
||||
std::string WifiLanServerSocket::GetName(const std::string& ip_address,
|
||||
int port) {
|
||||
std::string dot_delimited_string;
|
||||
if (!ip_address.empty()) {
|
||||
for (auto byte : ip_address) {
|
||||
if (!dot_delimited_string.empty())
|
||||
absl::StrAppend(&dot_delimited_string, ".");
|
||||
absl::StrAppend(&dot_delimited_string, absl::StrFormat("%d", byte));
|
||||
}
|
||||
}
|
||||
std::string out = absl::StrCat(dot_delimited_string, ":", port);
|
||||
return out;
|
||||
}
|
||||
|
||||
std::unique_ptr<api::WifiLanSocket> WifiLanServerSocket::Accept() {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
while (!closed_ && pending_sockets_.empty()) {
|
||||
cond_.Wait(&mutex_);
|
||||
}
|
||||
// whether or not we were running in the wait loop, return early if closed.
|
||||
if (closed_) return {};
|
||||
auto* remote_socket =
|
||||
pending_sockets_.extract(pending_sockets_.begin()).value();
|
||||
CHECK(remote_socket);
|
||||
|
||||
auto local_socket = std::make_unique<WifiLanSocket>();
|
||||
local_socket->Connect(*remote_socket);
|
||||
remote_socket->Connect(*local_socket);
|
||||
cond_.SignalAll();
|
||||
return local_socket;
|
||||
}
|
||||
|
||||
bool WifiLanServerSocket::Connect(WifiLanSocket& socket) {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
if (closed_) return false;
|
||||
if (socket.IsConnected()) {
|
||||
NEARBY_LOGS(ERROR)
|
||||
<< "Failed to connect to WifiLan server socket: already connected";
|
||||
return true; // already connected.
|
||||
}
|
||||
// add client socket to the pending list
|
||||
pending_sockets_.insert(&socket);
|
||||
cond_.SignalAll();
|
||||
while (!socket.IsConnected()) {
|
||||
cond_.Wait(&mutex_);
|
||||
if (closed_) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void WifiLanServerSocket::SetCloseNotifier(std::function<void()> notifier) {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
close_notifier_ = std::move(notifier);
|
||||
}
|
||||
|
||||
WifiLanServerSocket::~WifiLanServerSocket() {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
DoClose();
|
||||
}
|
||||
|
||||
Exception WifiLanServerSocket::Close() {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
return DoClose();
|
||||
}
|
||||
|
||||
Exception WifiLanServerSocket::DoClose() {
|
||||
bool should_notify = !closed_;
|
||||
closed_ = true;
|
||||
if (should_notify) {
|
||||
cond_.SignalAll();
|
||||
if (close_notifier_) {
|
||||
auto notifier = std::move(close_notifier_);
|
||||
mutex_.Unlock();
|
||||
// Notifier may contain calls to public API, and may cause deadlock, if
|
||||
// mutex_ is held during the call.
|
||||
notifier();
|
||||
mutex_.Lock();
|
||||
}
|
||||
}
|
||||
return {Exception::kSuccess};
|
||||
}
|
||||
|
||||
WifiLanMedium::WifiLanMedium() {
|
||||
auto& env = MediumEnvironment::Instance();
|
||||
env.RegisterWifiLanMedium(*this);
|
||||
}
|
||||
|
||||
WifiLanMedium::~WifiLanMedium() {
|
||||
auto& env = MediumEnvironment::Instance();
|
||||
env.UnregisterWifiLanMedium(*this);
|
||||
}
|
||||
|
||||
bool WifiLanMedium::StartAdvertising(const NsdServiceInfo& nsd_service_info) {
|
||||
std::string service_type = nsd_service_info.GetServiceType();
|
||||
NEARBY_LOGS(INFO) << "G3 WifiLan StartAdvertising: nsd_service_info="
|
||||
<< &nsd_service_info
|
||||
<< ", service_name=" << nsd_service_info.GetServiceName()
|
||||
<< ", service_type=" << service_type;
|
||||
{
|
||||
absl::MutexLock lock(&mutex_);
|
||||
if (advertising_info_.Existed(service_type)) {
|
||||
NEARBY_LOGS(INFO)
|
||||
<< "G3 WifiLan StartAdvertising: Can't start advertising because "
|
||||
"service_type="
|
||||
<< service_type << ", has started already.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
auto& env = MediumEnvironment::Instance();
|
||||
env.UpdateWifiLanMediumForAdvertising(*this, nsd_service_info,
|
||||
/*enabled=*/true);
|
||||
{
|
||||
absl::MutexLock lock(&mutex_);
|
||||
advertising_info_.Add(service_type);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WifiLanMedium::StopAdvertising(const NsdServiceInfo& nsd_service_info) {
|
||||
std::string service_type = nsd_service_info.GetServiceType();
|
||||
NEARBY_LOGS(INFO) << "G3 WifiLan StopAdvertising: nsd_service_info="
|
||||
<< &nsd_service_info
|
||||
<< ", service_name=" << nsd_service_info.GetServiceName()
|
||||
<< ", service_type=" << service_type;
|
||||
{
|
||||
absl::MutexLock lock(&mutex_);
|
||||
if (!advertising_info_.Existed(service_type)) {
|
||||
NEARBY_LOGS(INFO)
|
||||
<< "G3 WifiLan StopAdvertising: Can't stop advertising because "
|
||||
"we never started advertising for service_type="
|
||||
<< service_type;
|
||||
return false;
|
||||
}
|
||||
advertising_info_.Remove(service_type);
|
||||
}
|
||||
auto& env = MediumEnvironment::Instance();
|
||||
env.UpdateWifiLanMediumForAdvertising(*this, nsd_service_info,
|
||||
/*enabled=*/false);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WifiLanMedium::StartDiscovery(const std::string& service_type,
|
||||
DiscoveredServiceCallback callback) {
|
||||
NEARBY_LOGS(INFO) << "G3 WifiLan StartDiscovery: service_type="
|
||||
<< service_type;
|
||||
{
|
||||
absl::MutexLock lock(&mutex_);
|
||||
if (discovering_info_.Existed(service_type)) {
|
||||
NEARBY_LOGS(INFO)
|
||||
<< "G3 WifiLan StartDiscovery: Can't start discovery because "
|
||||
"service_type="
|
||||
<< service_type << " has started already.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
auto& env = MediumEnvironment::Instance();
|
||||
env.UpdateWifiLanMediumForDiscovery(*this, std::move(callback), service_type,
|
||||
true);
|
||||
{
|
||||
absl::MutexLock lock(&mutex_);
|
||||
discovering_info_.Add(service_type);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WifiLanMedium::StopDiscovery(const std::string& service_type) {
|
||||
NEARBY_LOGS(INFO) << "G3 WifiLan StopDiscovery: service_type="
|
||||
<< service_type;
|
||||
{
|
||||
absl::MutexLock lock(&mutex_);
|
||||
if (!discovering_info_.Existed(service_type)) {
|
||||
NEARBY_LOGS(INFO)
|
||||
<< "G3 WifiLan StopDiscovery: Can't stop discovering because we "
|
||||
"never started discovering.";
|
||||
return false;
|
||||
}
|
||||
discovering_info_.Remove(service_type);
|
||||
}
|
||||
auto& env = MediumEnvironment::Instance();
|
||||
env.UpdateWifiLanMediumForDiscovery(*this, {}, service_type, false);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<api::WifiLanSocket> WifiLanMedium::ConnectToService(
|
||||
const NsdServiceInfo& remote_service_info,
|
||||
CancellationFlag* cancellation_flag) {
|
||||
std::string service_type = remote_service_info.GetServiceType();
|
||||
NEARBY_LOGS(INFO) << "G3 WifiLan ConnectToService [self]: medium=" << this
|
||||
<< ", service_type=" << service_type;
|
||||
return ConnectToService(remote_service_info.GetIPAddress(),
|
||||
remote_service_info.GetPort(), cancellation_flag);
|
||||
}
|
||||
|
||||
std::unique_ptr<api::WifiLanSocket> WifiLanMedium::ConnectToService(
|
||||
const std::string& ip_address, int port,
|
||||
CancellationFlag* cancellation_flag) {
|
||||
std::string socket_name = WifiLanServerSocket::GetName(ip_address, port);
|
||||
NEARBY_LOGS(INFO) << "G3 WifiLan ConnectToService [self]: medium=" << this
|
||||
<< ", ip address + port=" << socket_name;
|
||||
// First, find an instance of remote medium, that exposed this service.
|
||||
auto& env = MediumEnvironment::Instance();
|
||||
auto* remote_medium =
|
||||
static_cast<WifiLanMedium*>(env.GetWifiLanMedium(ip_address, port));
|
||||
if (!remote_medium) {
|
||||
return {};
|
||||
}
|
||||
|
||||
WifiLanServerSocket* server_socket = nullptr;
|
||||
NEARBY_LOGS(INFO) << "G3 WifiLan ConnectToService [peer]: medium="
|
||||
<< remote_medium
|
||||
<< ", remote ip address + port=" << socket_name;
|
||||
// Then, find our server socket context in this medium.
|
||||
{
|
||||
absl::MutexLock medium_lock(&remote_medium->mutex_);
|
||||
auto item = remote_medium->server_sockets_.find(socket_name);
|
||||
server_socket = item != server_sockets_.end() ? item->second : nullptr;
|
||||
if (server_socket == nullptr) {
|
||||
NEARBY_LOGS(ERROR)
|
||||
<< "G3 WifiLan Failed to find WifiLan Server socket: socket_name="
|
||||
<< socket_name;
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
if (cancellation_flag->Cancelled()) {
|
||||
NEARBY_LOGS(ERROR) << "G3 WifiLan Connect: Has been cancelled: socket_name="
|
||||
<< socket_name;
|
||||
return {};
|
||||
}
|
||||
|
||||
CancellationFlagListener listener(cancellation_flag, [&server_socket]() {
|
||||
NEARBY_LOGS(INFO) << "G3 WifiLan Cancel Connect.";
|
||||
if (server_socket != nullptr) {
|
||||
server_socket->Close();
|
||||
}
|
||||
});
|
||||
|
||||
auto socket = std::make_unique<WifiLanSocket>();
|
||||
// Finally, Request to connect to this socket.
|
||||
if (!server_socket->Connect(*socket)) {
|
||||
NEARBY_LOGS(ERROR) << "G3 WifiLan Failed to connect to existing WifiLan "
|
||||
"Server socket: name="
|
||||
<< socket_name;
|
||||
return {};
|
||||
}
|
||||
NEARBY_LOGS(INFO) << "G3 WifiLan ConnectToService: connected: socket="
|
||||
<< socket.get();
|
||||
return socket;
|
||||
}
|
||||
|
||||
std::unique_ptr<api::WifiLanServerSocket> WifiLanMedium::ListenForService(
|
||||
int port) {
|
||||
auto& env = MediumEnvironment::Instance();
|
||||
auto server_socket = std::make_unique<WifiLanServerSocket>();
|
||||
server_socket->SetIPAddress(env.GetFakeIPAddress());
|
||||
server_socket->SetPort(port == 0 ? env.GetFakePort() : port);
|
||||
std::string socket_name = WifiLanServerSocket::GetName(
|
||||
server_socket->GetIPAddress(), server_socket->GetPort());
|
||||
server_socket->SetCloseNotifier([this, socket_name]() {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
server_sockets_.erase(socket_name);
|
||||
});
|
||||
NEARBY_LOGS(INFO) << "G3 WifiLan Adding server socket: medium=" << this
|
||||
<< ", socket_name=" << socket_name;
|
||||
absl::MutexLock lock(&mutex_);
|
||||
server_sockets_.insert({socket_name, server_socket.get()});
|
||||
return server_socket;
|
||||
}
|
||||
|
||||
} // namespace g3
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -0,0 +1,285 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#ifndef PLATFORM_IMPL_G3_WIFI_LAN_H_
|
||||
#define PLATFORM_IMPL_G3_WIFI_LAN_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "absl/container/flat_hash_map.h"
|
||||
#include "absl/container/flat_hash_set.h"
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "internal/platform/implementation/wifi_lan.h"
|
||||
#include "internal/platform/byte_array.h"
|
||||
#include "internal/platform/input_stream.h"
|
||||
#include "internal/platform/nsd_service_info.h"
|
||||
#include "internal/platform/output_stream.h"
|
||||
#include "internal/platform/implementation/g3/multi_thread_executor.h"
|
||||
#include "internal/platform/implementation/g3/pipe.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace g3 {
|
||||
|
||||
class WifiLanMedium;
|
||||
|
||||
class WifiLanSocket : public api::WifiLanSocket {
|
||||
public:
|
||||
WifiLanSocket() = default;
|
||||
~WifiLanSocket() override;
|
||||
|
||||
// Connect to another WifiLanSocket, to form a functional low-level channel.
|
||||
// from this point on, and until Close is called, connection exists.
|
||||
void Connect(WifiLanSocket& other) ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Returns the InputStream of this connected WifiLanSocket.
|
||||
InputStream& GetInputStream() override ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Returns the OutputStream of this connected WifiLanSocket.
|
||||
// This stream is for local side to write.
|
||||
OutputStream& GetOutputStream() override ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Returns address of a remote WifiLanSocket or nullptr.
|
||||
WifiLanSocket* GetRemoteSocket() ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Returns true if connection exists to the (possibly closed) remote socket.
|
||||
bool IsConnected() const ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Returns true if socket is closed.
|
||||
bool IsClosed() const ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Returns Exception::kIo on error, Exception::kSuccess otherwise.
|
||||
Exception Close() override ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
private:
|
||||
void DoClose() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
// Returns true if connection exists to the (possibly closed) remote socket.
|
||||
bool IsConnectedLocked() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
// Returns InputStream of our side of a connection.
|
||||
// This is what the remote side is supposed to read from.
|
||||
// This is a helper for GetInputStream() method.
|
||||
InputStream& GetLocalInputStream() ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Returns OutputStream of our side of a connection.
|
||||
// This is what the local size is supposed to write to.
|
||||
// This is a helper for GetOutputStream() method.
|
||||
OutputStream& GetLocalOutputStream() ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Output pipe is initialized by constructor, it remains always valid, until
|
||||
// it is closed. it represents output part of a local socket. Input part of a
|
||||
// local socket comes from the peer socket, after connection.
|
||||
std::shared_ptr<Pipe> output_{new Pipe};
|
||||
std::shared_ptr<Pipe> input_;
|
||||
mutable absl::Mutex mutex_;
|
||||
WifiLanSocket* remote_socket_ ABSL_GUARDED_BY(mutex_) = nullptr;
|
||||
bool closed_ ABSL_GUARDED_BY(mutex_) = false;
|
||||
};
|
||||
|
||||
class WifiLanServerSocket : public api::WifiLanServerSocket {
|
||||
public:
|
||||
static std::string GetName(const std::string& ip_address, int port);
|
||||
|
||||
~WifiLanServerSocket() override;
|
||||
|
||||
// Gets ip address.
|
||||
std::string GetIPAddress() const override ABSL_LOCKS_EXCLUDED(mutex_) {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
return ip_address_;
|
||||
}
|
||||
|
||||
// Sets the ip address.
|
||||
void SetIPAddress(const std::string& ip_address) ABSL_LOCKS_EXCLUDED(mutex_) {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
ip_address_ = ip_address;
|
||||
}
|
||||
|
||||
// Gets the port.
|
||||
int GetPort() const override ABSL_LOCKS_EXCLUDED(mutex_) {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
return port_;
|
||||
}
|
||||
|
||||
// Sets the port.
|
||||
void SetPort(int port) ABSL_LOCKS_EXCLUDED(mutex_) {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
port_ = port;
|
||||
}
|
||||
|
||||
// Blocks until either:
|
||||
// - at least one incoming connection request is available, or
|
||||
// - ServerSocket is closed.
|
||||
// On success, returns connected socket, ready to exchange data.
|
||||
// Returns nullptr on error.
|
||||
// Once error is reported, it is permanent, and ServerSocket has to be closed.
|
||||
//
|
||||
// Called by the server side of a connection.
|
||||
// Returns WifiLanSocket to the server side.
|
||||
// If not null, returned socket is connected to its remote (client-side) peer.
|
||||
std::unique_ptr<api::WifiLanSocket> Accept() override
|
||||
ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Blocks until either:
|
||||
// - connection is available, or
|
||||
// - server socket is closed, or
|
||||
// - error happens.
|
||||
//
|
||||
// Called by the client side of a connection.
|
||||
// Returns true, if socket is successfully connected.
|
||||
bool Connect(WifiLanSocket& socket) ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Called by the server side of a connection before passing ownership of
|
||||
// WifiLanServerSocker to user, to track validity of a pointer to this
|
||||
// server socket.
|
||||
void SetCloseNotifier(std::function<void()> notifier)
|
||||
ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Returns Exception::kIo on error, Exception::kSuccess otherwise.
|
||||
// Calls close_notifier if it was previously set, and marks socket as closed.
|
||||
Exception Close() override ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
private:
|
||||
Exception DoClose() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
mutable absl::Mutex mutex_;
|
||||
std::string ip_address_ ABSL_GUARDED_BY(mutex_);
|
||||
int port_ ABSL_GUARDED_BY(mutex_);
|
||||
absl::CondVar cond_;
|
||||
absl::flat_hash_set<WifiLanSocket*> pending_sockets_ ABSL_GUARDED_BY(mutex_);
|
||||
std::function<void()> close_notifier_ ABSL_GUARDED_BY(mutex_);
|
||||
bool closed_ ABSL_GUARDED_BY(mutex_) = false;
|
||||
};
|
||||
|
||||
// Container of operations that can be performed over the WifiLan medium.
|
||||
class WifiLanMedium : public api::WifiLanMedium {
|
||||
public:
|
||||
WifiLanMedium();
|
||||
~WifiLanMedium() override;
|
||||
|
||||
// Starts WifiLan advertising.
|
||||
//
|
||||
// nsd_service_info - NsdServiceInfo data that's advertised through mDNS
|
||||
// service.
|
||||
// On success if the service is now advertising.
|
||||
// On error if the service cannot start to advertise or the service type in
|
||||
// NsdServiceInfo has been passed previously which StopAdvertising is not
|
||||
// been called.
|
||||
bool StartAdvertising(const NsdServiceInfo& nsd_service_info) override
|
||||
ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Stops WifiLan advertising.
|
||||
//
|
||||
// nsd_service_info - NsdServiceInfo data that's advertised through mDNS
|
||||
// service.
|
||||
// On success if the service stops advertising.
|
||||
// On error if the service cannot stop advertising or the service type in
|
||||
// NsdServiceInfo cannot be found.
|
||||
bool StopAdvertising(const NsdServiceInfo& nsd_service_info) override
|
||||
ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Starts the discovery of nearby WifiLan services.
|
||||
//
|
||||
// Returns true once the WifiLan discovery has been initiated. The
|
||||
// service_type is associated with callback.
|
||||
bool StartDiscovery(const std::string& service_type,
|
||||
DiscoveredServiceCallback callback) override
|
||||
ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Stops the discovery of nearby WifiLan services.
|
||||
//
|
||||
// service_type - The one assigend in StartDiscovery.
|
||||
// On success if service_type is matched to the callback and will be removed
|
||||
// from the list. If list is empty then stops the WifiLan discovery
|
||||
// service.
|
||||
// On error if the service_type is not existed, then return immediately.
|
||||
bool StopDiscovery(const std::string& service_type) override
|
||||
ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Connects to a WifiLan service.
|
||||
// On success, returns a new WifiLanSocket.
|
||||
// On error, returns nullptr.
|
||||
std::unique_ptr<api::WifiLanSocket> ConnectToService(
|
||||
const NsdServiceInfo& remote_service_info,
|
||||
CancellationFlag* cancellation_flag) override ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Connects to a WifiLan service by ip address and port.
|
||||
// On success, returns a new WifiLanSocket.
|
||||
// On error, returns nullptr.
|
||||
std::unique_ptr<api::WifiLanSocket> ConnectToService(
|
||||
const std::string& ip_address, int port,
|
||||
CancellationFlag* cancellation_flag) override ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Listens for incoming connection.
|
||||
//
|
||||
// port - A port number.
|
||||
// 0 : use a random port.
|
||||
// 1~65536 : open a server socket on that exact port.
|
||||
// On success, returns a new WifiLanServerSocket.
|
||||
// On error, returns nullptr.
|
||||
std::unique_ptr<api::WifiLanServerSocket> ListenForService(int port) override
|
||||
ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Returns the port range as a pair of min and max port.
|
||||
absl::optional<std::pair<std::int32_t, std::int32_t>> GetDynamicPortRange()
|
||||
override {
|
||||
return std::make_pair(49152, 65535);
|
||||
}
|
||||
|
||||
private:
|
||||
struct AdvertisingInfo {
|
||||
bool Empty() const { return service_types.empty(); }
|
||||
void Clear() { service_types.clear(); }
|
||||
void Add(const std::string& service_type) {
|
||||
service_types.insert(service_type);
|
||||
}
|
||||
void Remove(const std::string& service_type) {
|
||||
service_types.erase(service_type);
|
||||
}
|
||||
bool Existed(const std::string& service_type) const {
|
||||
return service_types.contains(service_type);
|
||||
}
|
||||
|
||||
absl::flat_hash_set<std::string> service_types;
|
||||
};
|
||||
struct DiscoveringInfo {
|
||||
bool Empty() const { return service_types.empty(); }
|
||||
void Clear() { service_types.clear(); }
|
||||
void Add(const std::string& service_type) {
|
||||
service_types.insert(service_type);
|
||||
}
|
||||
void Remove(const std::string& service_type) {
|
||||
service_types.erase(service_type);
|
||||
}
|
||||
bool Existed(const std::string& service_type) const {
|
||||
return service_types.contains(service_type);
|
||||
}
|
||||
|
||||
absl::flat_hash_set<std::string> service_types;
|
||||
};
|
||||
|
||||
absl::Mutex mutex_;
|
||||
AdvertisingInfo advertising_info_ ABSL_GUARDED_BY(mutex_);
|
||||
DiscoveringInfo discovering_info_ ABSL_GUARDED_BY(mutex_);
|
||||
absl::flat_hash_map<std::string, WifiLanServerSocket*> server_sockets_
|
||||
ABSL_GUARDED_BY(mutex_);
|
||||
};
|
||||
|
||||
} // namespace g3
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // PLATFORM_IMPL_G3_WIFI_LAN_H_
|
||||
@@ -0,0 +1,40 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#ifndef PLATFORM_API_INPUT_FILE_H_
|
||||
#define PLATFORM_API_INPUT_FILE_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "internal/platform/byte_array.h"
|
||||
#include "internal/platform/exception.h"
|
||||
#include "internal/platform/input_stream.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace api {
|
||||
|
||||
// An InputFile represents a readable file on the system.
|
||||
class InputFile : public InputStream {
|
||||
public:
|
||||
~InputFile() override = default;
|
||||
virtual std::string GetFilePath() const = 0;
|
||||
virtual std::int64_t GetTotalSize() const = 0;
|
||||
};
|
||||
|
||||
} // namespace api
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // PLATFORM_API_INPUT_FILE_H_
|
||||
@@ -0,0 +1,72 @@
|
||||
# Copyright 2020 Google LLC
|
||||
#
|
||||
# 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
|
||||
#
|
||||
# https://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.
|
||||
load("//tools/build_defs/apple:ios.bzl", "ios_static_framework")
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
objc_library(
|
||||
name = "Connections",
|
||||
srcs = [
|
||||
"Source/Internal/GNCAdvertiser.mm",
|
||||
"Source/Internal/GNCCore.mm",
|
||||
"Source/Internal/GNCCoreConnection.mm",
|
||||
"Source/Internal/GNCDiscoverer.mm",
|
||||
"Source/Internal/GNCPayload.mm",
|
||||
"Source/Internal/GNCPayloadListener.mm",
|
||||
"Source/Internal/GNCUtils.mm",
|
||||
"Source/Internal/platform.mm",
|
||||
],
|
||||
hdrs = [
|
||||
"Source/GNCAdvertiser.h",
|
||||
"Source/GNCConnection.h",
|
||||
"Source/GNCConnections.h",
|
||||
"Source/GNCDiscoverer.h",
|
||||
"Source/GNCPayload.h",
|
||||
"Source/Internal/GNCCore.h",
|
||||
"Source/Internal/GNCCoreConnection.h",
|
||||
"Source/Internal/GNCPayload+Internal.h",
|
||||
"Source/Internal/GNCPayloadListener.h",
|
||||
"Source/Internal/GNCUtils.h",
|
||||
],
|
||||
deps = [
|
||||
"//connections:core",
|
||||
"//connections:core_types",
|
||||
"//internal/platform/implementation/ios/Source/Mediums",
|
||||
"//internal/platform/implementation/ios/Source/Platform",
|
||||
"//internal/platform/implementation/ios/Source/Shared",
|
||||
"@com_google_absl//absl/functional:bind_front",
|
||||
"//third_party/objective_c/google_toolbox_for_mac:GTM_Logger",
|
||||
],
|
||||
)
|
||||
|
||||
MIN_IOS_VERSION = "12.0"
|
||||
|
||||
HDRS_POD = [
|
||||
"Source/GNCAdvertiser.h",
|
||||
"Source/GNCConnection.h",
|
||||
"Source/GNCDiscoverer.h",
|
||||
"Source/GNCPayload.h",
|
||||
]
|
||||
|
||||
ios_static_framework(
|
||||
name = "NearbyConnections_framework",
|
||||
hdrs = HDRS_POD,
|
||||
bundle_name = "NearbyConnections",
|
||||
minimum_os_version = MIN_IOS_VERSION,
|
||||
deps = [
|
||||
":Connections",
|
||||
],
|
||||
)
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 77 KiB |
+355
@@ -0,0 +1,355 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 50;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
41B7A2C3208F900100EBA53E /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 41B7A2C2208F900100EBA53E /* AppDelegate.m */; };
|
||||
41B7A2C6208F900100EBA53E /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 41B7A2C5208F900100EBA53E /* ViewController.m */; };
|
||||
41B7A2C9208F900100EBA53E /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 41B7A2C7208F900100EBA53E /* Main.storyboard */; };
|
||||
41B7A2CB208F900200EBA53E /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 41B7A2CA208F900200EBA53E /* Assets.xcassets */; };
|
||||
41B7A2CE208F900200EBA53E /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 41B7A2CC208F900200EBA53E /* LaunchScreen.storyboard */; };
|
||||
41B7A2D1208F900200EBA53E /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 41B7A2D0208F900200EBA53E /* main.m */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
41B7A2BE208F900100EBA53E /* NearbyConnectionsExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = NearbyConnectionsExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
41B7A2C1208F900100EBA53E /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
|
||||
41B7A2C2208F900100EBA53E /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
|
||||
41B7A2C4208F900100EBA53E /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = "<group>"; };
|
||||
41B7A2C5208F900100EBA53E /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = "<group>"; };
|
||||
41B7A2C8208F900100EBA53E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||
41B7A2CA208F900200EBA53E /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
41B7A2CD208F900200EBA53E /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
41B7A2CF208F900200EBA53E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
41B7A2D0208F900200EBA53E /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
41B7A2BB208F900100EBA53E /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
41B7A2B5208F900000EBA53E = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
41B7A2C0208F900100EBA53E /* NearbyConnectionsExample */,
|
||||
41B7A2BF208F900100EBA53E /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
41B7A2BF208F900100EBA53E /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
41B7A2BE208F900100EBA53E /* NearbyConnectionsExample.app */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
41B7A2C0208F900100EBA53E /* NearbyConnectionsExample */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
41B7A2C1208F900100EBA53E /* AppDelegate.h */,
|
||||
41B7A2C2208F900100EBA53E /* AppDelegate.m */,
|
||||
41B7A2C4208F900100EBA53E /* ViewController.h */,
|
||||
41B7A2C5208F900100EBA53E /* ViewController.m */,
|
||||
41B7A2C7208F900100EBA53E /* Main.storyboard */,
|
||||
41B7A2CA208F900200EBA53E /* Assets.xcassets */,
|
||||
41B7A2CC208F900200EBA53E /* LaunchScreen.storyboard */,
|
||||
41B7A2CF208F900200EBA53E /* Info.plist */,
|
||||
41B7A2D0208F900200EBA53E /* main.m */,
|
||||
);
|
||||
path = NearbyConnectionsExample;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
41B7A2BD208F900100EBA53E /* NearbyConnectionsExample */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 41B7A2D4208F900200EBA53E /* Build configuration list for PBXNativeTarget "NearbyConnectionsExample" */;
|
||||
buildPhases = (
|
||||
41B7A2BA208F900100EBA53E /* Sources */,
|
||||
41B7A2BB208F900100EBA53E /* Frameworks */,
|
||||
41B7A2BC208F900100EBA53E /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = NearbyConnectionsExample;
|
||||
productName = NearbyConnectionsExample;
|
||||
productReference = 41B7A2BE208F900100EBA53E /* NearbyConnectionsExample.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
41B7A2B6208F900000EBA53E /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 0930;
|
||||
ORGANIZATIONNAME = Google;
|
||||
TargetAttributes = {
|
||||
41B7A2BD208F900100EBA53E = {
|
||||
CreatedOnToolsVersion = 9.3;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 41B7A2B9208F900000EBA53E /* Build configuration list for PBXProject "NearbyConnectionsExample" */;
|
||||
compatibilityVersion = "Xcode 9.3";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 41B7A2B5208F900000EBA53E;
|
||||
productRefGroup = 41B7A2BF208F900100EBA53E /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
41B7A2BD208F900100EBA53E /* NearbyConnectionsExample */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
41B7A2BC208F900100EBA53E /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
41B7A2CE208F900200EBA53E /* LaunchScreen.storyboard in Resources */,
|
||||
41B7A2CB208F900200EBA53E /* Assets.xcassets in Resources */,
|
||||
41B7A2C9208F900100EBA53E /* Main.storyboard in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
41B7A2BA208F900100EBA53E /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
41B7A2C6208F900100EBA53E /* ViewController.m in Sources */,
|
||||
41B7A2D1208F900200EBA53E /* main.m in Sources */,
|
||||
41B7A2C3208F900100EBA53E /* AppDelegate.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
41B7A2C7208F900100EBA53E /* Main.storyboard */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
41B7A2C8208F900100EBA53E /* Base */,
|
||||
);
|
||||
name = Main.storyboard;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
41B7A2CC208F900200EBA53E /* LaunchScreen.storyboard */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
41B7A2CD208F900200EBA53E /* Base */,
|
||||
);
|
||||
name = LaunchScreen.storyboard;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
41B7A2D2208F900200EBA53E /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_BITCODE = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.3;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
41B7A2D3208F900200EBA53E /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_BITCODE = NO;
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.3;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
41B7A2D5208F900200EBA53E /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)",
|
||||
);
|
||||
INFOPLIST_FILE = NearbyConnectionsExample/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.google.NearbyConnectionsExample;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE = "";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
41B7A2D6208F900200EBA53E /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)",
|
||||
);
|
||||
INFOPLIST_FILE = NearbyConnectionsExample/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.google.NearbyConnectionsExample;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
41B7A2B9208F900000EBA53E /* Build configuration list for PBXProject "NearbyConnectionsExample" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
41B7A2D2208F900200EBA53E /* Debug */,
|
||||
41B7A2D3208F900200EBA53E /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
41B7A2D4208F900200EBA53E /* Build configuration list for PBXNativeTarget "NearbyConnectionsExample" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
41B7A2D5208F900200EBA53E /* Debug */,
|
||||
41B7A2D6208F900200EBA53E /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 41B7A2B6208F900000EBA53E /* Project object */;
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
//
|
||||
// Copyright (c) 2021 Google Inc.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// AppDelegate.h
|
||||
// NearbyConnectionsExample
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
/**
|
||||
* A delegate for NSApplication to handle notifications about app launch and
|
||||
* shutdown. Owned by the application object.
|
||||
*/
|
||||
@interface AppDelegate : UIResponder <UIApplicationDelegate>
|
||||
|
||||
/**
|
||||
* Main screen window displayed to the user which contains any active view
|
||||
* hierarchy.
|
||||
*/
|
||||
@property(nonatomic) UIWindow *window;
|
||||
|
||||
@end
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
//
|
||||
// Copyright (c) 2021 Google Inc.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// AppDelegate.m
|
||||
// NearbyConnectionsExample
|
||||
//
|
||||
|
||||
#import "AppDelegate.h"
|
||||
|
||||
@implementation AppDelegate
|
||||
@end
|
||||
+98
@@ -0,0 +1,98 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "20x20",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "20x20",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "29x29",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "29x29",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "40x40",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "40x40",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "60x60",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "60x60",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "20x20",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "20x20",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "29x29",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "29x29",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "40x40",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "40x40",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "76x76",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "76x76",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "83.5x83.5",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ios-marketing",
|
||||
"size" : "1024x1024",
|
||||
"scale" : "1x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" systemVersion="17A277" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="EHf-IW-A2E">
|
||||
<objects>
|
||||
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="53" y="375"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14113" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="fos-Uz-R9B">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="BVh-fg-s4x">
|
||||
<objects>
|
||||
<viewController id="Tuj-3T-cB2" customClass="ViewController" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="9NC-2J-1xS">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<viewLayoutGuide key="safeArea" id="wwJ-gb-kZn"/>
|
||||
</view>
|
||||
<navigationItem key="navigationItem" id="ega-4i-ebx"/>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="xJQ-q8-wWg" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="433" y="-133"/>
|
||||
</scene>
|
||||
<!--Navigation Controller-->
|
||||
<scene sceneID="Jcj-tp-czJ">
|
||||
<objects>
|
||||
<navigationController id="fos-Uz-R9B" sceneMemberID="viewController">
|
||||
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="x6x-US-muI">
|
||||
<rect key="frame" x="0.0" y="20" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</navigationBar>
|
||||
<connections>
|
||||
<segue destination="Tuj-3T-cB2" kind="relationship" relationship="rootViewController" id="Xa2-HD-mUn"/>
|
||||
</connections>
|
||||
</navigationController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="99I-fh-ObP" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-455" y="-132"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>armv7</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>NSLocalNetworkUsageDescription</key>
|
||||
<string>Exchange data with nearby devices running the NearbyConnectionsExmaple app.</string>
|
||||
<key>NSBonjourServices</key>
|
||||
<array>
|
||||
<string>_54167B379724._tcp</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
//
|
||||
// Copyright (c) 2021 Google Inc.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// ViewController.h
|
||||
// NearbyConnectionsExample
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
/** View controller for demo by loading NearbyConnections lib for advertiser and discoverer. */
|
||||
@interface ViewController : UIViewController
|
||||
@end
|
||||
+291
@@ -0,0 +1,291 @@
|
||||
//
|
||||
// Copyright (c) 2021 Google Inc.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// ViewController.m
|
||||
// NearbyConnectionsExample
|
||||
//
|
||||
|
||||
#import "ViewController.h"
|
||||
|
||||
#import <NearbyConnections/NearbyConnections.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
static NSString *kServiceId = @"com.google.NearbyConnectionsExample";
|
||||
static NSString *kCellIdentifier = @"endpointCell";
|
||||
|
||||
// Simplified version of dispatch_after.
|
||||
void delay(NSTimeInterval delay, dispatch_block_t block) {
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)),
|
||||
dispatch_get_main_queue(), block);
|
||||
}
|
||||
|
||||
// This class contains info about a discovered endpoint.
|
||||
@interface EndpointInfo : NSObject
|
||||
@property(nonatomic, readonly) id<GNCDiscoveredEndpointInfo> discInfo;
|
||||
@property(nonatomic, nullable) id<GNCConnection> connection;
|
||||
@end
|
||||
|
||||
@implementation EndpointInfo
|
||||
- (instancetype)initWithDiscoveredInfo:(id<GNCDiscoveredEndpointInfo>)discInfo {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_discInfo = discInfo;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@end
|
||||
|
||||
@interface ViewController () <UITableViewDataSource, UITableViewDelegate>
|
||||
@property(nonatomic) GNCAdvertiser *advertiser;
|
||||
@property(nonatomic) GNCDiscoverer *discoverer;
|
||||
@property(nonatomic) NSMutableDictionary<GNCEndpointId, EndpointInfo *> *endpoints;
|
||||
@property(nonatomic, readonly) NSData *ping;
|
||||
@property(nonatomic, readonly) NSData *pong;
|
||||
@property(nonatomic) UITableView *tableView;
|
||||
@property(nonatomic) UITextView *statusView;
|
||||
@property(nonatomic) NSMutableDictionary<GNCEndpointId, id<GNCConnection> > *incomingConnections;
|
||||
@end
|
||||
|
||||
@implementation ViewController
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
[self makeViews];
|
||||
|
||||
// Enable "info" log messages in the release build.
|
||||
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"GTMVerboseLogging"];
|
||||
|
||||
self.title = [[UIDevice currentDevice] name];
|
||||
NSData *endpointInfo = [self.title dataUsingEncoding:NSUTF8StringEncoding];
|
||||
_endpoints = [NSMutableDictionary dictionary];
|
||||
_ping = [@"ping" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
_pong = [@"pong" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
_incomingConnections = [NSMutableDictionary dictionary];
|
||||
|
||||
// The advertiser.
|
||||
_advertiser = [GNCAdvertiser
|
||||
advertiserWithEndpointInfo:endpointInfo
|
||||
serviceId:kServiceId
|
||||
strategy:GNCStrategyCluster
|
||||
connectionInitiationHandler:^(GNCEndpointId endpointId,
|
||||
id<GNCAdvertiserConnectionInfo> advConnInfo,
|
||||
GNCConnectionResponseHandler responseHandler) {
|
||||
// Show a status that a discoverer has requested a connection.
|
||||
[self logStatus:@"Accepting connection request" final:NO];
|
||||
|
||||
// Accept the connection request.
|
||||
responseHandler(GNCConnectionResponseAccept);
|
||||
return [GNCConnectionResultHandlers
|
||||
successHandler:^(id<GNCConnection> connection) {
|
||||
// Save the connection until the ping-pong sequence is done.
|
||||
self.incomingConnections[endpointId] = connection;
|
||||
__block BOOL receivedPong = NO;
|
||||
|
||||
// Send a ping, expecting the remote endpoint to send a pong.
|
||||
[self logStatus:@"Connection established; sending ping" final:NO];
|
||||
[connection sendBytesPayload:[GNCBytesPayload payloadWithBytes:self.ping]
|
||||
completion:^(GNCPayloadResult result) {
|
||||
if (result == GNCPayloadResultSuccess) {
|
||||
[self logStatus:@"Sent ping; waiting for pong" final:NO];
|
||||
|
||||
// Show an error if the pong isn't received in the expected
|
||||
// timeframe.
|
||||
delay(3.0, ^{
|
||||
if (receivedPong) {
|
||||
[self logStatus:@"Error: Didn't receive pong" final:YES];
|
||||
[self.incomingConnections
|
||||
removeObjectForKey:endpointId]; // close the connection
|
||||
}
|
||||
});
|
||||
} else {
|
||||
[self logStatus:@"Error: Failed to send ping" final:YES];
|
||||
}
|
||||
}];
|
||||
|
||||
// Return handlers for incoming payloads.
|
||||
return [GNCConnectionHandlers handlersWithBuilder:^(GNCConnectionHandlers *handlers) {
|
||||
handlers.bytesPayloadHandler = ^(GNCBytesPayload *payload) {
|
||||
receivedPong = NO;
|
||||
[self.incomingConnections removeObjectForKey:endpointId]; // close the connection
|
||||
|
||||
// Show a status of whether the pong was received.
|
||||
[self logStatus:[payload.bytes isEqual:self.pong] ? @"Received pong"
|
||||
: @"Error: Payload is not pong"
|
||||
final:YES];
|
||||
};
|
||||
}];
|
||||
}
|
||||
failureHandler:^(GNCConnectionFailure result) {
|
||||
[self
|
||||
logStatus:(result == GNCConnectionFailureRejected) ? @"Error: Connection rejected"
|
||||
: @"Error: Connection failed"
|
||||
final:YES];
|
||||
}];
|
||||
}];
|
||||
|
||||
// The discoverer.
|
||||
__weak typeof(self) weakSelf = self;
|
||||
_discoverer = [GNCDiscoverer
|
||||
discovererWithServiceId:kServiceId
|
||||
strategy:GNCStrategyCluster
|
||||
endpointFoundHandler:^(GNCEndpointId endpointId,
|
||||
id<GNCDiscoveredEndpointInfo> endpointInfo) {
|
||||
typeof(self) self = weakSelf;
|
||||
|
||||
// An endpoint was discovered; add it to the endpoint list and UI.
|
||||
self.endpoints[endpointId] = [[EndpointInfo alloc] initWithDiscoveredInfo:endpointInfo];
|
||||
[self.tableView reloadData];
|
||||
|
||||
// Return the lost handler for this endpoint.
|
||||
return ^{
|
||||
typeof(self) self = weakSelf; // shadow
|
||||
|
||||
// Endpoint disappeared; remove it from the endpoint list and UI.
|
||||
[self.endpoints removeObjectForKey:endpointId];
|
||||
[self.tableView reloadData];
|
||||
};
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - UITableViewDelegate
|
||||
|
||||
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
// The user tapped on a cell; request a connection with it.
|
||||
EndpointInfo *info = _endpoints[_endpoints.allKeys[indexPath.row]];
|
||||
if (!info) return;
|
||||
|
||||
void (^deselectRow)(void) = ^{
|
||||
[tableView deselectRowAtIndexPath:indexPath animated:YES];
|
||||
};
|
||||
|
||||
[self logStatus:@"Requesting connection" final:NO];
|
||||
info.discInfo.requestConnection(
|
||||
self.title,
|
||||
^(id<GNCDiscovererConnectionInfo> discConnInfo,
|
||||
GNCConnectionResponseHandler responseHandler) {
|
||||
// Accept the auth token.
|
||||
[self logStatus:@"Accepting auth token" final:NO];
|
||||
responseHandler(GNCConnectionResponseAccept);
|
||||
return ^(id<GNCConnection> connection) {
|
||||
// Save the connection until the ping-pong sequence is done.
|
||||
info.connection = connection;
|
||||
__block BOOL receivedPing = NO;
|
||||
|
||||
[self logStatus:@"Connection established; waiting for ping" final:NO];
|
||||
|
||||
// Show an error if the ping isn't received in the expected timeframe.
|
||||
delay(3.0, ^{
|
||||
if (!receivedPing) {
|
||||
deselectRow();
|
||||
[self logStatus:@"Error: Didn't receive ping" final:YES];
|
||||
info.connection = nil; // close the connection
|
||||
}
|
||||
});
|
||||
|
||||
// Return handlers for incoming payloads.
|
||||
__weak id<GNCConnection> weakConnection = connection; // avoid a retain cycle
|
||||
return [GNCConnectionHandlers handlersWithBuilder:^(GNCConnectionHandlers *handlers) {
|
||||
handlers.bytesPayloadHandler = ^(GNCBytesPayload *payload) {
|
||||
receivedPing = YES;
|
||||
|
||||
// If a ping was received, send a pong back to the advertiser.
|
||||
if ([payload.bytes isEqual:self.ping]) {
|
||||
[self logStatus:@"Received ping; sending pong" final:NO];
|
||||
[weakConnection sendBytesPayload:[GNCBytesPayload payloadWithBytes:self.pong]
|
||||
completion:^(GNCPayloadResult result) {
|
||||
deselectRow();
|
||||
[self logStatus:(result == GNCPayloadResultSuccess)
|
||||
? @"Sent pong"
|
||||
: @"Error: Failed to send pong"
|
||||
final:YES];
|
||||
|
||||
// Pong was sent, so close the connection.
|
||||
info.connection = nil;
|
||||
}];
|
||||
} else {
|
||||
deselectRow();
|
||||
[self logStatus:@"Error: Payload is not ping" final:YES];
|
||||
}
|
||||
};
|
||||
}];
|
||||
};
|
||||
},
|
||||
^(GNCConnectionFailure result) {
|
||||
// Connection failed.
|
||||
deselectRow();
|
||||
[self logStatus:(result == GNCConnectionFailureRejected) ? @"Connection rejected"
|
||||
: @"Connection failed"
|
||||
final:YES];
|
||||
});
|
||||
}
|
||||
|
||||
- (UITableViewCell *)tableView:(UITableView *)tableView
|
||||
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
|
||||
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellIdentifier
|
||||
forIndexPath:indexPath];
|
||||
cell.textLabel.text = _endpoints[_endpoints.allKeys[indexPath.row]].discInfo.name;
|
||||
return cell;
|
||||
}
|
||||
|
||||
#pragma mark - UITableViewDataSource
|
||||
|
||||
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
|
||||
return [_endpoints.allKeys count];
|
||||
}
|
||||
|
||||
#pragma mark - Private
|
||||
|
||||
- (void)makeViews {
|
||||
_tableView = [[UITableView alloc] initWithFrame:self.view.frame style:UITableViewStylePlain];
|
||||
[_tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:kCellIdentifier];
|
||||
_tableView.delegate = self;
|
||||
_tableView.dataSource = self;
|
||||
_tableView.rowHeight = 48;
|
||||
_tableView.scrollEnabled = YES;
|
||||
_tableView.showsVerticalScrollIndicator = YES;
|
||||
_tableView.userInteractionEnabled = YES;
|
||||
_tableView.bounces = YES;
|
||||
_tableView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
[self.view addSubview:_tableView];
|
||||
|
||||
// Make the status view.
|
||||
UITextView * (^newTextView)(CGRect) = ^(CGRect frame) {
|
||||
UITextView *textView = [[UITextView alloc] initWithFrame:frame];
|
||||
textView.autoresizingMask =
|
||||
UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin;
|
||||
textView.layer.borderColor = [UIColor blackColor].CGColor;
|
||||
textView.layer.borderWidth = 1;
|
||||
textView.editable = NO;
|
||||
textView.textContainerInset = UIEdgeInsetsZero;
|
||||
return textView;
|
||||
};
|
||||
CGRect selfFrame = self.view.frame;
|
||||
static const int kStatusHeight = 144;
|
||||
CGRect statusFrame = (CGRect){{selfFrame.origin.x + 4, selfFrame.size.height - kStatusHeight},
|
||||
{selfFrame.size.width - 8, kStatusHeight - 4}};
|
||||
_statusView = newTextView(statusFrame);
|
||||
[self.view addSubview:_statusView];
|
||||
}
|
||||
|
||||
- (void)logStatus:(NSString *)status final:(BOOL)final {
|
||||
_statusView.text = [NSString stringWithFormat:@"%@\n%@%@", _statusView.text, status,
|
||||
final ? @"\n–––––––––––––––––––––––––" : @""];
|
||||
[_statusView scrollRangeToVisible:NSMakeRange(_statusView.text.length - 1, 1)];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// Copyright (c) 2021 Google Inc.
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
// main.m
|
||||
// NearbyConnectionsExample
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "AppDelegate.h"
|
||||
|
||||
int main(int argc, char * argv[]) {
|
||||
@autoreleasepool {
|
||||
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
|
||||
}
|
||||
}
|
||||
+198
@@ -0,0 +1,198 @@
|
||||
# Nearby Connections Sample App for iOS
|
||||
|
||||
This is a sample app for third party developers using the Nearby Connections
|
||||
library. On startup, it advertises and discovers. Discovered advertisers are
|
||||
added to the list in the UI. When the user taps on an advertiser in the list,
|
||||
the discoverer requests a connection with it. When the connection is
|
||||
established, the advertiser sends a "ping" payload to the discoverer, which
|
||||
sends "pong" payload back to the advertiser. The connection is then closed.
|
||||
|
||||
## Setup
|
||||
|
||||
1.Get the NearbyConnections_framework.zip from https://github.com/google/nearby/releases/tag/v0.0.1-ios, unzip it,
|
||||
and put your unzipped folder under your project folder. The directory structure
|
||||
looks like:
|
||||
|
||||
```
|
||||
/NearbyConnectionsExample
|
||||
/NearbyConnectionsExample
|
||||
NearbyConnectionsExample.xcodeproj
|
||||
README.md
|
||||
/NearbyConnections.framework
|
||||
```
|
||||
|
||||
2.Import NearbyConnections.framework
|
||||
|
||||
- In Xcode, click the NearbyConnectionsExample in the left pane. And click the one in TARGETS-NearbyConnectionsExample at the left of right pane and the Build Phases at the right.
|
||||
- See the **Link Binary With Libraries**, and press **+** to import the file - **libc++.tbd**.
|
||||
- In Add **Other…**, import the NearbyConnections.framework folder which was unzipped.
|
||||
|
||||

|
||||
|
||||
3.Update info.plist:
|
||||
|
||||
- add **NSLocalNetworkUsageDescription** key with a description of your usage of Nearby Connections.
|
||||
- add **NSBonjourServices** key.
|
||||
for NSBonjourServices key, add the bonjour type name: `_54167B379724._tcp`
|
||||
|
||||
> **54167B379724** is the 6 byte hash of service id **com.google.NearbyConnectionsExample**
|
||||
|
||||
```
|
||||
// NearbyConnectionsExample/info.plist
|
||||
...
|
||||
<key>NSLocalNetworkUsageDescription</key>
|
||||
<string>Exchange data with nearby devices running the NearbyConnectionsExmaple app.</string>
|
||||
<key>NSBonjourServices</key>
|
||||
<array>
|
||||
<string>_54167B379724._tcp</string>
|
||||
</array>
|
||||
...
|
||||
```
|
||||
|
||||
|
||||
4.Add service id and import `<NearbyConnections/NearbyConnections.h>` in your main view controller.
|
||||
|
||||
```
|
||||
// NearbyConnectionsExample/ViewController.m
|
||||
|
||||
static NSString *kServiceId = @"com.google.NearbyConnectionsExample";
|
||||
|
||||
#import <NearbyConnections/NearbyConnections.h>
|
||||
```
|
||||
|
||||
## Code Snippets
|
||||
Note: All of the callbacks in this library use blocks rather than delegates. Be careful to avoid retain cycles in your block implementations. See the [Apple documentation](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmPractical.html#//apple_ref/doc/uid/TP40004447-SW1) describing how to avoid retain cycles.
|
||||
|
||||
Here is the skeleton code for an advertiser:
|
||||
|
||||
```objc
|
||||
_advertiser = [GNCAdvertiser
|
||||
advertiserWithEndpointInfo:endpointInfo
|
||||
serviceId:myServiceId
|
||||
strategy:GNCStrategyCluster
|
||||
connectionInitiationHandler:^(GNCEndpointId endpointId,
|
||||
id<GNCAdvertiserConnectionInfo> connectionInfo,
|
||||
GNCConnectionResponseHandler responseHandler) {
|
||||
// Decide whether to accept or reject the connection. The following code would normally
|
||||
// exist in the callback for an alert, for instance.
|
||||
if (/* user rejected */) {
|
||||
responseHandler(GNCConnectionResponseReject); // the user rejected the invitation
|
||||
} else {
|
||||
responseHandler(GNCConnectionResponseAccept); // the user accepted the invitation
|
||||
|
||||
// Return connection result handlers, one of which is called depending on
|
||||
// whether a successful connection was made.
|
||||
return [GNCConnectionResultHandlers successHandler:^(id<GNCConnection> connection) {
|
||||
// A successful connection was made. Save the connection somewhere, which can
|
||||
// be used to send payloads to the remote endpoint.
|
||||
|
||||
// Return the incoming payload and disconnect handlers.
|
||||
return [GNCConnectionHandlers handlersWithBuilder:^(GNCConnectionHandlers *handlers) {
|
||||
// Optionally set the Bytes payload handler.
|
||||
handlers.bytesPayloadHandler = ^(GNCBytesPayload *payload) {
|
||||
// Process the payload received from the remote endpoint.
|
||||
};
|
||||
|
||||
// Optionally set the Stream payload handler.
|
||||
handlers.streamPayloadHandler = ^(GNCStreamPayload *payload, NSProgress *progress) {
|
||||
// Receipt of a Stream payload has started. Input can be read from the payload’s
|
||||
// NSInputStream, and progress/cancellation is handled via the NSProgress object.
|
||||
return ^(GNCPayloadResult result) {
|
||||
if (result == GNCPayloadResultSuccess) {
|
||||
// The payload has been successfully received.
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// Optionally set the disconnected handler.
|
||||
handlers.disconnectedHandler = ^(GNCDisconnectedReason reason) {
|
||||
// The connection was severed by either endpoint or lost.
|
||||
};
|
||||
}
|
||||
failureHandler:^(GNCConnectionFailure result) {
|
||||
// Failed to make the connection.
|
||||
}]);
|
||||
}
|
||||
}];
|
||||
```
|
||||
|
||||
Here is the skeleton code for a discoverer:
|
||||
|
||||
```objc
|
||||
_discoverer =
|
||||
[GNCDiscoverer discovererWithServiceId:myServiceId
|
||||
strategy:GNCStrategyCluster
|
||||
endpointFoundHandler:^(GNCEndpointId endpointId,
|
||||
id<GNCDiscoveredEndpointInfo> discEndpointInfo) {
|
||||
// An endpoint was found. Typically you would add it to a list of nearby endpoints
|
||||
// displayed in a UITableView, for instance.
|
||||
|
||||
// The following code shows how to request a connection with the endpoint. This code
|
||||
// would normally exist in the -didSelectRowAtIndexPath: of UITableViewDelegate.
|
||||
if (/* user wants to request a connection */) {
|
||||
requestHandler(myName,
|
||||
// This block is called once an authentication string is generated between the endpoints.
|
||||
^(id<GNCDiscovererConnectionInfo> discConnInfo, GNCConnectionResponseHandler responseHandler) {
|
||||
// Ask the user to confirm the authentication string.
|
||||
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Accept auth?" ...];
|
||||
[alert addAction:[UIAlertAction actionWithTitle:@"OK"
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction *action) {
|
||||
responseHandler(GNCConnectionResponseAccept);
|
||||
}]];
|
||||
[alert addAction:[UIAlertAction actionWithTitle:@"Cancel"
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction *action) {
|
||||
responseHandler(GNCConnectionResponseReject);
|
||||
}]];
|
||||
[self presentViewController:alert animated:YES completion:^{}];
|
||||
|
||||
// Return a block that's called if the connection was successful.
|
||||
return ^(id<GNCConnection> connection) {
|
||||
// A successful connection was made. Save the connection somewhere, which can
|
||||
// be used to send payloads to the remote endpoint.
|
||||
|
||||
// Return incoming data handlers as in the advertiser example above.
|
||||
return [GNCConnectionHandlers handlersWithBuilder:^(GNCConnectionHandlers *handlers) {
|
||||
// Set up payload and disconnect handlers here as in the advertiser example above.
|
||||
}];
|
||||
};
|
||||
},
|
||||
// This block is called if the connection failed for any reason.
|
||||
^(GNCConnectionFailure result) {
|
||||
// Typically an alert would be shown here explaining why the connection failed.
|
||||
});
|
||||
}
|
||||
|
||||
// Return the endpoint-lost handler, which is called when the endpoint goes out of range
|
||||
// or stops advertising.
|
||||
return ^{
|
||||
// The endpoint disappeared.
|
||||
};
|
||||
}];
|
||||
```
|
||||
|
||||
Here is an example of how to send a Bytes payload. The returned NSProgress object can be passed to UIProgressView to display a progress bar.
|
||||
|
||||
```objc
|
||||
NSProgress *progress = [connection
|
||||
sendBytesPayload:[GNCBytesPayload payloadWithBytes:someData]
|
||||
completion:^(GNCPayloadResult result) {
|
||||
// Check status to see if it was successfully sent.
|
||||
}];
|
||||
```
|
||||
|
||||
## Build and run
|
||||
|
||||

|
||||
|
||||
If you meet the following error in the debug panel of Xcode, you likely need to set up the keys listed in step 3 **Update info.plist**, as well as the service type.
|
||||
|
||||
```
|
||||
1970-01-01 00:00:00.000 NearbyConnectionsExample[1383/0x16d87b000] [lvl=1] -[GNCMBonjourService netService:didNotPublish:] Error publishing: service: <NSNetService 0x283a18920> local _307BEAB11028._tcp. IjFQWEUwe-oAAA 51898, errorDic: {
|
||||
NSNetServicesErrorCode = "-72008";
|
||||
NSNetServicesErrorDomain = 10;
|
||||
}
|
||||
```
|
||||
---
|
||||
NOTE: The iOS simulator is unstable when advertising. We recommend using an real iOS device.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 698 KiB |
@@ -0,0 +1,84 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "GNCConnection.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/** This contains info about a discoverer endpoint intitiating a connection with an advertiser. */
|
||||
@protocol GNCAdvertiserConnectionInfo <NSObject>
|
||||
/** This is a human readable name of the discoverer. */
|
||||
@property(nonatomic, readonly, copy) NSString *name;
|
||||
/** This token can be used to verify the identity of the discoverer. */
|
||||
@property(nonatomic, readonly, copy) NSString *authToken;
|
||||
@end
|
||||
|
||||
/** This class contains success and failure handlers for the connection request. */
|
||||
@interface GNCConnectionResultHandlers : NSObject
|
||||
|
||||
/**
|
||||
* This factory method creates a pair of handlers for a successful or failed connection.
|
||||
*
|
||||
* @param successHandler This handler is called if both endpoints accept the connection.
|
||||
* A @c GNCConnection object is passed, meaning that the connection has
|
||||
* been established and you may start sending and receiving payloads.
|
||||
* @param failureHandler This handler is called if either endpoint rejects the connection.
|
||||
*/
|
||||
+ (instancetype)successHandler:(GNCConnectionHandler)successHandler
|
||||
failureHandler:(GNCConnectionFailureHandler)failureHandler;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* This handler is called when a discoverer requests a connection with an advertiser. In
|
||||
* response, the advertiser should accept or reject via @c responseHandler.
|
||||
*
|
||||
* @param endpointId The ID of the endpoint.
|
||||
* @param connectionInfo Information about the discoverer.
|
||||
* @param responseHandler Handler for the connection response, which is either an acceptance or
|
||||
* rejection of the connection request.
|
||||
* @return Handlers for the final connection result. This will be called as soon as the final
|
||||
* connection result is known, when either side rejects or both sides accept.
|
||||
*/
|
||||
typedef GNCConnectionResultHandlers *_Nonnull (^GNCAdvertiserConnectionInitiationHandler)(
|
||||
GNCEndpointId endpointId, id<GNCAdvertiserConnectionInfo> connectionInfo,
|
||||
GNCConnectionResponseHandler responseHandler);
|
||||
|
||||
/**
|
||||
* An advertiser broadcasts a service that can be seen by discoverers, which can then make
|
||||
* requests to connect to it. Release the advertiser object to stop advertising.
|
||||
*/
|
||||
@interface GNCAdvertiser : NSObject
|
||||
|
||||
/**
|
||||
* Factory method that creates an advertiser.
|
||||
*
|
||||
* @param endpointInfo A data for endpoint info which contains readable name of this endpoint,
|
||||
* to be displayed on other endpoints.
|
||||
* @param serviceId A string that uniquely identifies the advertised service.
|
||||
* @param strategy The connection topology to use.
|
||||
* @param connectionInitiationHandler A handler that is called when a discoverer requests a
|
||||
* connection with this endpoint.
|
||||
*/
|
||||
+ (instancetype)advertiserWithEndpointInfo:(NSData *)endpointInfo
|
||||
serviceId:(NSString *)serviceId
|
||||
strategy:(GNCStrategy)strategy
|
||||
connectionInitiationHandler:
|
||||
(GNCAdvertiserConnectionInitiationHandler)connectionInitiationHandler;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,167 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class GNCBytesPayload, GNCStreamPayload, GNCFilePayload;
|
||||
|
||||
/** Response to a connection request. */
|
||||
typedef NS_ENUM(NSInteger, GNCConnectionResponse) {
|
||||
GNCConnectionResponseReject, // reject the connection request
|
||||
GNCConnectionResponseAccept, // accept the connection request
|
||||
};
|
||||
|
||||
/** Reason for a failed connection request. */
|
||||
typedef NS_ENUM(NSInteger, GNCConnectionFailure) {
|
||||
GNCConnectionFailureRejected, // an endpoint rejected the connection request
|
||||
GNCConnectionFailureUnknown, // there was an error while attempting to make the connection
|
||||
};
|
||||
|
||||
/** Handler for a @c GNCConnectionFailure value. */
|
||||
typedef void (^GNCConnectionFailureHandler)(GNCConnectionFailure);
|
||||
|
||||
/** Reasons that a connection can be severed by either endpoint. */
|
||||
typedef NS_ENUM(NSInteger, GNCDisconnectedReason) {
|
||||
GNCDisconnectedReasonUnknown, // the endpoint can no longer be reached
|
||||
};
|
||||
|
||||
/** Handler for a @c GNCDisconnectedReason value. */
|
||||
typedef void (^GNCDisconnectedHandler)(GNCDisconnectedReason);
|
||||
|
||||
/** Result of a payload transfer. */
|
||||
typedef NS_ENUM(NSInteger, GNCPayloadResult) {
|
||||
GNCPayloadResultSuccess, // Payload delivery was successful.
|
||||
GNCPayloadResultFailure, // An error occurred during payload delivery.
|
||||
GNCPayloadResultCanceled, // Payload delivery was canceled.
|
||||
};
|
||||
|
||||
/** Handler for a @c GNCPayloadResult value. */
|
||||
typedef void (^GNCPayloadResultHandler)(GNCPayloadResult);
|
||||
|
||||
/** Connection topology. See https://developers.google.com/nearby/connections/strategies. */
|
||||
typedef NS_ENUM(NSInteger, GNCStrategy) {
|
||||
GNCStrategyCluster, // M-to-N
|
||||
GNCStrategyStar, // 1-to-N
|
||||
GNCStrategyPointToPoint, // 1-to-1
|
||||
};
|
||||
|
||||
/** Every endpoint has a unique identifier. */
|
||||
typedef NSString *GNCEndpointId;
|
||||
|
||||
/** This handler receives a Bytes payload. It is called when the payload data is fully received. */
|
||||
typedef void (^GNCBytesPayloadHandler)(GNCBytesPayload *payload);
|
||||
|
||||
/**
|
||||
* This handler receives a Stream payload, signifying the start of receipt of a stream. The payload
|
||||
* data should be read from the supplied input stream. The progress object can be used to monitor
|
||||
* progress or cancel the operation. This handler must return a completion handler, which is
|
||||
* called when the operation is finished.
|
||||
*/
|
||||
typedef GNCPayloadResultHandler _Nonnull (^GNCStreamPayloadHandler)(GNCStreamPayload *payload,
|
||||
NSProgress *progress);
|
||||
|
||||
/**
|
||||
* This handler receives a File payload, signifying the start of receipt of a file. The
|
||||
* progress object can be used to monitor progress or cancel the operation. This handler must
|
||||
* return a completion handler, which is called when the operation finishes successfully or if
|
||||
* there is an error. The file will be stored in a temporary location. If an error occurs or the
|
||||
* operation is canceled, the file will contain all data that was received. It is the client's
|
||||
* responsibility to delete the file when it is no longer needed.
|
||||
*/
|
||||
typedef GNCPayloadResultHandler _Nonnull (^GNCFilePayloadHandler)(GNCFilePayload *payload,
|
||||
NSProgress *progress);
|
||||
|
||||
/** This class contains optional handlers for a connection. */
|
||||
@interface GNCConnectionHandlers : NSObject
|
||||
|
||||
/**
|
||||
* This handler receives Bytes payloads. It is optional; apps that don't send and receive Bytes
|
||||
* payloads need not supply this handler.
|
||||
*/
|
||||
@property(nonatomic, nullable) GNCBytesPayloadHandler bytesPayloadHandler;
|
||||
|
||||
/**
|
||||
* This handler receives a stream that delivers a payload in chunks. It is optional; apps that
|
||||
* don't send and receive Stream payloads need not supply this handler.
|
||||
*/
|
||||
@property(nonatomic, nullable) GNCStreamPayloadHandler streamPayloadHandler;
|
||||
|
||||
/**
|
||||
* This handler receives a File payload. It is optional; apps that don't send and receive File
|
||||
* payloads need not supply this handler.
|
||||
* Note: File payloads are not yet supported.
|
||||
*/
|
||||
@property(nonatomic, nullable) GNCFilePayloadHandler filePayloadHandler;
|
||||
|
||||
/**
|
||||
* This handler is called when the connection is ended, whether due to the endpoint disconnecting
|
||||
* or moving out of range. It is optional.
|
||||
*/
|
||||
@property(nonatomic, nullable) GNCDisconnectedHandler disconnectedHandler;
|
||||
|
||||
/**
|
||||
* This factory method lets you specify a subset of the connection handlers in a single expression.
|
||||
*
|
||||
* @param builderBlock Set up the handlers in this block.
|
||||
*/
|
||||
+ (instancetype)handlersWithBuilder:(void (^)(GNCConnectionHandlers *))builderBlock;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* This represents a connection with an endpoint. Use it to send payloads to the endpoint, and
|
||||
* release it to disconnect.
|
||||
*/
|
||||
@protocol GNCConnection <NSObject>
|
||||
|
||||
/**
|
||||
* Send a Bytes payload. A progress object is returned, which can be used to monitor
|
||||
* progress or cancel the operation. |completion| will be called when the operation completes
|
||||
* (in all cases, even if failed or was canceled).
|
||||
*/
|
||||
- (NSProgress *)sendBytesPayload:(GNCBytesPayload *)payload
|
||||
completion:(GNCPayloadResultHandler)completion;
|
||||
|
||||
/**
|
||||
* Send a Stream payload. A progress object is returned, which can be used to monitor
|
||||
* progress or cancel the operation. The stream data is read from the supplied NSInputStream.
|
||||
* |completion| will be called when the operation completes.
|
||||
*/
|
||||
- (NSProgress *)sendStreamPayload:(GNCStreamPayload *)payload
|
||||
completion:(GNCPayloadResultHandler)completion;
|
||||
|
||||
/**
|
||||
* Send a File payload. A progress object is returned, which can be used to monitor progress or
|
||||
* cancel the operation. |completion| will be called when the operation completes.
|
||||
* Note: File payloads are not yet supported.
|
||||
*/
|
||||
- (NSProgress *)sendFilePayload:(GNCFilePayload *)payload
|
||||
completion:(GNCPayloadResultHandler)completion;
|
||||
@end
|
||||
|
||||
/**
|
||||
* This handler takes a @c GNCConnection object and returns a @c GNCConnectionHandlers
|
||||
* object containing the desired payload and connection-ended handlers.
|
||||
*/
|
||||
typedef GNCConnectionHandlers *_Nonnull (^GNCConnectionHandler)(id<GNCConnection> connection);
|
||||
|
||||
/**
|
||||
* This handler takes a response to a connection request. Pass @c GNCConnectionResponseAccept to
|
||||
* accept the request and @c GNCConnectionResponseReject to reject it.
|
||||
*/
|
||||
typedef void (^GNCConnectionResponseHandler)(GNCConnectionResponse response);
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,20 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
// Umbrella header file for Nearby Connections library.
|
||||
|
||||
#import "GNCAdvertiser.h"
|
||||
#import "GNCConnection.h"
|
||||
#import "GNCDiscoverer.h"
|
||||
#import "GNCPayload.h"
|
||||
@@ -0,0 +1,95 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "GNCConnection.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/** This is info about an advertiser endpoint with which the discoverer has requested a connection.
|
||||
*/
|
||||
@protocol GNCDiscovererConnectionInfo <NSObject>
|
||||
/** This token can be used to verify the identity of the advertiser. */
|
||||
@property(nonatomic, readonly, copy) NSString *authToken;
|
||||
@end
|
||||
|
||||
/**
|
||||
* This handler is called to establish authorization with the advertiser. In response,
|
||||
* @c responseHandler should be called to accept or reject the connection.
|
||||
*
|
||||
* @param connectionInfo Information about the advertiser.
|
||||
* @param responseHandler Handler for the connection response, which is either an acceptance or
|
||||
* rejection of the connection request.
|
||||
* @return Handler for the connection if it was successful.
|
||||
*/
|
||||
typedef GNCConnectionHandler _Nonnull (^GNCDiscovererConnectionInitializationHandler)(
|
||||
id<GNCDiscovererConnectionInfo> connectionInfo, GNCConnectionResponseHandler responseHandler);
|
||||
|
||||
/**
|
||||
* This handler should be called to request a connection with an advertiser.
|
||||
*
|
||||
* @param endpointInfo A data for endpoint info which contains readable name of this endpoint,
|
||||
* to be displayed on other endpoints.
|
||||
* @param authorizationHandler This handler is called to establish authorization.
|
||||
* @param failureHandler This handler is called if there was an error making the connection.
|
||||
*/
|
||||
typedef void (^GNCConnectionRequester)(
|
||||
NSData *endpointInfo, GNCDiscovererConnectionInitializationHandler connectionAuthorizationHandler,
|
||||
GNCConnectionFailureHandler failureHandler);
|
||||
|
||||
/** Information about an endpoint when it's discovered. */
|
||||
@protocol GNCDiscoveredEndpointInfo <NSObject>
|
||||
/** The human readable name of the remote endpoint. */
|
||||
@property(nonatomic, readonly, copy) NSString *endpointName;
|
||||
/** Information advertised by the remote endpoint. */
|
||||
@property(nonatomic, readonly, copy) NSData *endpointInfo;
|
||||
/** Call this block to request a connection with the advertiser. */
|
||||
@property(nonatomic, readonly) GNCConnectionRequester requestConnection;
|
||||
@end
|
||||
|
||||
/** This handler is called when a previously discovered advertiser endpoint is lost. */
|
||||
typedef void (^GNCEndpointLostHandler)(void);
|
||||
|
||||
/**
|
||||
* This handler is called when an advertiser endpoint is discovered.
|
||||
*
|
||||
* @param endpointId The ID of the endpoint.
|
||||
* @param connectionInfo Information about the endpoint.
|
||||
* @return Block that is called when the endpoint is lost.
|
||||
*/
|
||||
typedef GNCEndpointLostHandler _Nonnull (^GNCEndpointFoundHandler)(
|
||||
GNCEndpointId endpointId, id<GNCDiscoveredEndpointInfo> endpointInfo);
|
||||
|
||||
/**
|
||||
* A discoverer searches for endpoints advertising the specified service, and allows connection
|
||||
* requests to be sent to them. Release the discoverer object to stop discovering.
|
||||
*/
|
||||
@interface GNCDiscoverer : NSObject
|
||||
|
||||
/**
|
||||
* Factory method that creates a discoverer.
|
||||
*
|
||||
* @param serviceId A string that uniquely identifies the advertised service to search for.
|
||||
* @param strategy The connection topology to use.
|
||||
* @param endpointFoundHandler This handler is called when an endpoint advertising the service is
|
||||
* discovered.
|
||||
*/
|
||||
+ (instancetype)discovererWithServiceId:(NSString *)serviceId
|
||||
strategy:(GNCStrategy)strategy
|
||||
endpointFoundHandler:(GNCEndpointFoundHandler)endpointFoundHandler;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,64 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/** This class encapsulates a Bytes payload. */
|
||||
@interface GNCBytesPayload : NSObject
|
||||
|
||||
/** The unique identifier of the payload. */
|
||||
@property(nonatomic, readonly) int64_t identifier;
|
||||
|
||||
/** The content of the payload. */
|
||||
@property(nonatomic, readonly) NSData *bytes;
|
||||
|
||||
/**
|
||||
* Creates a Bytes payload object.
|
||||
* Note: To maximize performance, @c bytes is strongly referenced, not copied.
|
||||
*/
|
||||
+ (instancetype)payloadWithBytes:(NSData *)bytes;
|
||||
|
||||
@end
|
||||
|
||||
/** This class encapsulates a Stream payload. */
|
||||
@interface GNCStreamPayload : NSObject
|
||||
|
||||
/** The unique identifier of the payload. */
|
||||
@property(nonatomic, readonly) int64_t identifier;
|
||||
|
||||
/** The payload data is read from this input stream. */
|
||||
@property(nonatomic, readonly) NSInputStream *stream;
|
||||
|
||||
+ (instancetype)payloadWithStream:(NSInputStream *)stream;
|
||||
|
||||
@end
|
||||
|
||||
/** This class encapsulates a File payload. */
|
||||
@interface GNCFilePayload : NSObject
|
||||
|
||||
/** The unique identifier of the payload. */
|
||||
@property(nonatomic, readonly) int64_t identifier;
|
||||
|
||||
/** A URL that identifies the file. */
|
||||
@property(nonatomic, readonly, copy) NSURL *fileURL;
|
||||
|
||||
+ (instancetype)payloadWithFileURL:(NSURL *)fileURL;
|
||||
|
||||
+ (instancetype)payloadWithFileURL:(NSURL *)fileURL identifier:(int64_t)identifier;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,317 @@
|
||||
// Copyright 2021 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#import "internal/platform/implementation/ios/Source/GNCAdvertiser.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "absl/functional/bind_front.h"
|
||||
#include "connections/advertising_options.h"
|
||||
#include "connections/core.h"
|
||||
#include "connections/listeners.h"
|
||||
#include "connections/params.h"
|
||||
#include "connections/status.h"
|
||||
#include "internal/platform/byte_array.h"
|
||||
#import "internal/platform/implementation/ios/Source/GNCConnection.h"
|
||||
#import "internal/platform/implementation/ios/Source/Internal/GNCCore.h"
|
||||
#import "internal/platform/implementation/ios/Source/Internal/GNCCoreConnection.h"
|
||||
#import "internal/platform/implementation/ios/Source/Internal/GNCPayloadListener.h"
|
||||
#import "internal/platform/implementation/ios/Source/Internal/GNCUtils.h"
|
||||
#import "internal/platform/implementation/ios/Source/Platform/utils.h"
|
||||
#import "GoogleToolboxForMac/GTMLogger.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
using ::location::nearby::ByteArrayFromNSData;
|
||||
using ::location::nearby::CppStringFromObjCString;
|
||||
using ::location::nearby::ObjCStringFromCppString;
|
||||
using ::location::nearby::connections::ConnectionListener;
|
||||
using ::location::nearby::connections::AdvertisingOptions;
|
||||
using ::location::nearby::connections::ConnectionRequestInfo;
|
||||
using ::location::nearby::connections::ConnectionResponseInfo;
|
||||
using ::location::nearby::connections::GNCStrategyToStrategy;
|
||||
using ::location::nearby::connections::Medium;
|
||||
using ResultListener = ::location::nearby::connections::ResultCallback;
|
||||
using ::location::nearby::connections::Status;
|
||||
|
||||
/** This is a GNCAdvertiserConnectionInfo that provides storage for its properties. */
|
||||
@interface GNCAdvertiserConnectionInfo : NSObject
|
||||
|
||||
@property(nonatomic, readonly) NSString *name;
|
||||
@property(nonatomic, readonly) NSString *authToken;
|
||||
|
||||
- (instancetype)initWithName:(NSString *)name authToken:(NSString *)authToken;
|
||||
|
||||
@end
|
||||
|
||||
@implementation GNCAdvertiserConnectionInfo
|
||||
|
||||
- (instancetype)initWithName:(NSString *)name authToken:(NSString *)authToken {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_name = [name copy];
|
||||
_authToken = [authToken copy];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
/** Information retained about an endpoint before and after requesting a connection. */
|
||||
@interface GNCAdvertiserEndpointInfo : NSObject
|
||||
@property(nonatomic) GNCAdvertiserConnectionInfo *connectionInfo;
|
||||
@property(nonatomic) GNCConnectionResponse clientResponse;
|
||||
@property(nonatomic) BOOL clientResponseReceived; // whether the client response has been received
|
||||
@property(nonatomic, nullable) GNCConnectionResultHandlers *connectionResultHandlers;
|
||||
@property(nonatomic, weak) GNCCoreConnection *connection;
|
||||
@property(nonatomic) GNCConnectionHandlers *connectionHandlers;
|
||||
@end
|
||||
|
||||
@implementation GNCAdvertiserEndpointInfo
|
||||
|
||||
+ (instancetype)infoWithEndpointConnectionInfo:(GNCAdvertiserConnectionInfo *)connInfo {
|
||||
GNCAdvertiserEndpointInfo *info = [[GNCAdvertiserEndpointInfo alloc] init];
|
||||
info.connectionInfo = connInfo;
|
||||
return info;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
/** GNCAdvertiser members. */
|
||||
@interface GNCAdvertiser ()
|
||||
@property(nonatomic) GNCCore *core;
|
||||
@property(nonatomic) GNCAdvertiserConnectionInitiationHandler initiationHandler;
|
||||
@property(nonatomic, assign) Status status;
|
||||
@property(nonatomic) NSMutableDictionary<GNCEndpointId, GNCAdvertiserEndpointInfo *> *endpoints;
|
||||
@end
|
||||
|
||||
/** C++ classes passed to the core library by GNCAdvertiser. */
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
|
||||
/** This class contains the callbacks for establishing and severing a connection. */
|
||||
class GNCAdvertiserConnectionListener {
|
||||
public:
|
||||
explicit GNCAdvertiserConnectionListener(GNCAdvertiser *advertiser) : advertiser_(advertiser) {}
|
||||
|
||||
void OnInitiated(const std::string &endpoint_id, const ConnectionResponseInfo &info) {
|
||||
GNCAdvertiser *advertiser = advertiser_; // strongify
|
||||
if (!advertiser) return;
|
||||
|
||||
NSString *endpointId = ObjCStringFromCppString(endpoint_id);
|
||||
GNCAdvertiserEndpointInfo *endpointInfo = advertiser.endpoints[endpointId];
|
||||
if (endpointInfo) {
|
||||
GTMLoggerError(@"Connection already initiated for endpoint: %@", endpointId);
|
||||
} else {
|
||||
// TODO(b/169292092): endpointInfo is an advertisement byte array. Need to implement to
|
||||
// extract the endpoint name not just force to cast string.
|
||||
NSString *name = ObjCStringFromCppString(std::string(info.remote_endpoint_info));
|
||||
NSString *authToken = ObjCStringFromCppString(info.authentication_token);
|
||||
GNCAdvertiserConnectionInfo *connInfo =
|
||||
[[GNCAdvertiserConnectionInfo alloc] initWithName:name authToken:authToken];
|
||||
endpointInfo = [GNCAdvertiserEndpointInfo infoWithEndpointConnectionInfo:connInfo];
|
||||
|
||||
// Call the connection initiation handler. Synchronous because it returns the connection
|
||||
// result handlers.
|
||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
||||
__weak __typeof__(advertiser) weakAdvertiser = advertiser;
|
||||
endpointInfo.connectionResultHandlers = advertiser.initiationHandler(
|
||||
endpointId, (id<GNCAdvertiserConnectionInfo>)connInfo,
|
||||
^(GNCConnectionResponse response) {
|
||||
__strong __typeof__(advertiser) strongAdvertiser = weakAdvertiser;
|
||||
endpointInfo.clientResponse = response;
|
||||
endpointInfo.clientResponseReceived = YES;
|
||||
if (response == GNCConnectionResponseAccept) {
|
||||
// The connection was accepted by the client.
|
||||
if (payload_listener_ == nullptr) {
|
||||
payload_listener_ = std::make_unique<GNCPayloadListener>(
|
||||
advertiser.core,
|
||||
^{
|
||||
return endpointInfo.connectionHandlers;
|
||||
},
|
||||
^{
|
||||
return endpointInfo.connection.payloads;
|
||||
});
|
||||
}
|
||||
advertiser.core->_core->AcceptConnection(
|
||||
CppStringFromObjCString(endpointId),
|
||||
PayloadListener{
|
||||
.payload_cb = absl::bind_front(&GNCPayloadListener::OnPayload,
|
||||
payload_listener_.get()),
|
||||
.payload_progress_cb = absl::bind_front(
|
||||
&GNCPayloadListener::OnPayloadProgress, payload_listener_.get()),
|
||||
},
|
||||
ResultListener{});
|
||||
} else {
|
||||
// The connection was rejected by the client.
|
||||
advertiser.core->_core->RejectConnection(CppStringFromObjCString(endpointId),
|
||||
ResultListener{});
|
||||
}
|
||||
});
|
||||
});
|
||||
advertiser.endpoints[endpointId] = endpointInfo;
|
||||
}
|
||||
}
|
||||
|
||||
void OnAccepted(const std::string &endpoint_id) {
|
||||
GNCAdvertiser *advertiser = advertiser_; // strongify
|
||||
if (!advertiser) return;
|
||||
|
||||
NSString *endpointId = ObjCStringFromCppString(endpoint_id);
|
||||
GNCAdvertiserEndpointInfo *endpointInfo = advertiser.endpoints[endpointId];
|
||||
if (!endpointInfo) {
|
||||
GTMLoggerInfo(@"Connection result for unknown endpoint: %@", endpointId);
|
||||
return;
|
||||
}
|
||||
|
||||
// The connection has been accepted by both endpoints, so create the GNCConnection object
|
||||
// and pass it to |successHandler| for the client to use. It will be removed from |endpoints|
|
||||
// when the client disconnects (on dealloc of GNCConnection).
|
||||
// Note: Use a local strong reference to the connection object; don't just assign to
|
||||
// |endpointInfo.connection|. Without a strong reference, the connection object can be
|
||||
// deallocated before |successHandler| is called in the Release build.
|
||||
__weak __typeof__(advertiser) weakAdvertiser = advertiser;
|
||||
id<GNCConnection> connection = [GNCCoreConnection
|
||||
connectionWithEndpointId:endpointId
|
||||
core:advertiser.core
|
||||
deallocHandler:^{
|
||||
__strong __typeof__(advertiser) strongAdvertiser = weakAdvertiser;
|
||||
if (!strongAdvertiser) return;
|
||||
[strongAdvertiser.endpoints removeObjectForKey:endpointId];
|
||||
}];
|
||||
endpointInfo.connection = connection;
|
||||
|
||||
// Callback is synchronous because it returns the connection handlers.
|
||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
||||
endpointInfo.connectionHandlers =
|
||||
endpointInfo.connectionResultHandlers.successHandler(connection);
|
||||
});
|
||||
}
|
||||
|
||||
void OnRejected(const std::string &endpoint_id, Status status) {
|
||||
GNCAdvertiser *advertiser = advertiser_; // strongify
|
||||
if (!advertiser) return;
|
||||
|
||||
NSString *endpointId = ObjCStringFromCppString(endpoint_id);
|
||||
GNCAdvertiserEndpointInfo *endpointInfo = advertiser.endpoints[endpointId];
|
||||
if (!endpointInfo) {
|
||||
GTMLoggerInfo(@"Connection result for unknown endpoint: %@", endpointId);
|
||||
return;
|
||||
}
|
||||
|
||||
// One side rejected, so call failureHandler with the connection status (we do this in all
|
||||
// cases), and forget the endpoint.
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
endpointInfo.connectionResultHandlers.failureHandler(GNCConnectionFailureRejected);
|
||||
});
|
||||
[advertiser.endpoints removeObjectForKey:endpointId];
|
||||
}
|
||||
|
||||
void OnDisconnected(const std::string &endpoint_id) {
|
||||
GNCAdvertiser *advertiser = advertiser_; // strongify
|
||||
if (!advertiser) return;
|
||||
|
||||
NSString *endpointId = ObjCStringFromCppString(endpoint_id);
|
||||
GNCAdvertiserEndpointInfo *endpointInfo = advertiser.endpoints[endpointId];
|
||||
if (endpointInfo) {
|
||||
if (endpointInfo.connection) {
|
||||
GNCDisconnectedHandler disconnectedHandler =
|
||||
endpointInfo.connectionHandlers.disconnectedHandler;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (disconnectedHandler) disconnectedHandler(GNCDisconnectedReasonUnknown);
|
||||
});
|
||||
} else {
|
||||
GTMLoggerInfo(@"Disconnect for unconnected endpoint: %@", endpointId);
|
||||
}
|
||||
[advertiser.endpoints removeObjectForKey:endpointId];
|
||||
} else {
|
||||
GTMLoggerInfo(@"Disconnect for unknown endpoint: %@", endpointId);
|
||||
}
|
||||
}
|
||||
|
||||
void OnBandwidthChanged(const std::string &endpoint_id, Medium medium) {
|
||||
GNCAdvertiser *advertiser = advertiser_; // strongify
|
||||
if (!advertiser) return;
|
||||
|
||||
// TODO(b/169292092): Implement.
|
||||
}
|
||||
|
||||
private:
|
||||
__weak GNCAdvertiser *advertiser_;
|
||||
std::unique_ptr<GNCPayloadListener> payload_listener_;
|
||||
};
|
||||
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
using ::location::nearby::connections::GNCAdvertiserConnectionListener;
|
||||
|
||||
@interface GNCAdvertiser () {
|
||||
std::unique_ptr<GNCAdvertiserConnectionListener> advertiserListener;
|
||||
};
|
||||
|
||||
@end
|
||||
|
||||
@implementation GNCAdvertiser
|
||||
|
||||
+ (instancetype)advertiserWithEndpointInfo:(NSData *)endpointInfo
|
||||
serviceId:(NSString *)serviceId
|
||||
strategy:(GNCStrategy)strategy
|
||||
connectionInitiationHandler:
|
||||
(GNCAdvertiserConnectionInitiationHandler)initiationHandler {
|
||||
GNCAdvertiser *advertiser = [[GNCAdvertiser alloc] init];
|
||||
advertiser.initiationHandler = initiationHandler;
|
||||
advertiser.endpoints = [[NSMutableDictionary alloc] init];
|
||||
advertiser.core = GNCGetCore();
|
||||
advertiser->advertiserListener = std::make_unique<GNCAdvertiserConnectionListener>(advertiser);
|
||||
|
||||
ConnectionListener listener = {
|
||||
.initiated_cb = absl::bind_front(&GNCAdvertiserConnectionListener::OnInitiated,
|
||||
advertiser->advertiserListener.get()),
|
||||
.accepted_cb = absl::bind_front(&GNCAdvertiserConnectionListener::OnAccepted,
|
||||
advertiser->advertiserListener.get()),
|
||||
.rejected_cb = absl::bind_front(&GNCAdvertiserConnectionListener::OnRejected,
|
||||
advertiser->advertiserListener.get()),
|
||||
.disconnected_cb = absl::bind_front(&GNCAdvertiserConnectionListener::OnDisconnected,
|
||||
advertiser->advertiserListener.get()),
|
||||
};
|
||||
|
||||
advertiser.core->_core->StartAdvertising(
|
||||
CppStringFromObjCString(serviceId),
|
||||
AdvertisingOptions{
|
||||
{
|
||||
GNCStrategyToStrategy(strategy), // .strategy
|
||||
location::nearby::connections::BooleanMediumSelector(), // .allowed
|
||||
},
|
||||
true, // .auto_upgrade_bandwidth
|
||||
true, // .enforce_topology_constraints
|
||||
},
|
||||
ConnectionRequestInfo{
|
||||
.endpoint_info = ByteArrayFromNSData(endpointInfo),
|
||||
.listener = std::move(listener),
|
||||
},
|
||||
ResultListener{});
|
||||
return advertiser;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
GTMLoggerInfo(@"GNCAdvertiser deallocated");
|
||||
_core->_core->StopAdvertising(ResultListener{});
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,55 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "connections/core.h"
|
||||
#include "connections/implementation/service_controller_router.h"
|
||||
#include "internal/platform/payload_id.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/** This class contains the C++ Core object. */
|
||||
@interface GNCCore : NSObject {
|
||||
@public
|
||||
std::unique_ptr<::location::nearby::connections::Core> _core;
|
||||
std::unique_ptr<::location::nearby::connections::ServiceControllerRouter>
|
||||
_service_controller_router;
|
||||
}
|
||||
|
||||
/**
|
||||
* These functions are the utilities to manipulate the InputFile in ImplementationPlatform for
|
||||
* sending File payload.
|
||||
*
|
||||
* Inserts the URL to the map, keyed by payloadID. The element will not be inserted if there
|
||||
* already is an element with the key in the map.
|
||||
*/
|
||||
- (void)insertURLToMapWithPayloadID:(::location::nearby::PayloadId)payloadId urlToSend:(NSURL *)url;
|
||||
|
||||
/**
|
||||
* Returns the URL with the payloadID and removes the entry from the map. Returns nil if
|
||||
* payloadID is not found.
|
||||
*/
|
||||
- (nullable NSURL *)extractURLWithPayloadID:(::location::nearby::PayloadId)payloadId;
|
||||
|
||||
- (void)clearSendingURLMaps;
|
||||
|
||||
@end
|
||||
|
||||
/** This function returns the Core singleton, wrapped in an Obj-C object for lifetime management. */
|
||||
GNCCore *GNCGetCore();
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,94 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#import "internal/platform/implementation/ios/Source/Internal/GNCCore.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "absl/container/flat_hash_map.h"
|
||||
#include "absl/container/internal/common.h"
|
||||
#include "connections/core.h"
|
||||
#include "connections/implementation/service_controller_router.h"
|
||||
#include "internal/platform/payload_id.h"
|
||||
#import "GoogleToolboxForMac/GTMLogger.h"
|
||||
|
||||
using ::location::nearby::connections::Core;
|
||||
using ::location::nearby::PayloadId;
|
||||
using ::location::nearby::connections::ServiceControllerRouter;
|
||||
|
||||
@implementation GNCCore {
|
||||
// A map to store the NSURL object with PayloadId for sendFilePayload in GNCConnection.
|
||||
// This is the place to store the NSURL for InputFile creation in ImplementationPlatform.
|
||||
absl::flat_hash_map<PayloadId, NSURL *> _sending_urls;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
GTMLoggerInfo(@"GNCCore created");
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_service_controller_router = std::make_unique<ServiceControllerRouter>();
|
||||
_core = std::make_unique<Core>(_service_controller_router.get());
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
_core.reset();
|
||||
_service_controller_router.reset();
|
||||
GTMLoggerInfo(@"GNCCore deallocated");
|
||||
}
|
||||
|
||||
- (void)insertURLToMapWithPayloadID:(PayloadId)payloadId urlToSend:(NSURL *)url {
|
||||
_sending_urls.emplace(payloadId, url);
|
||||
}
|
||||
|
||||
- (nullable NSURL *)extractURLWithPayloadID:(PayloadId)payloadId {
|
||||
NSURL *url;
|
||||
auto it = _sending_urls.find(payloadId);
|
||||
if (it != _sending_urls.end()) {
|
||||
auto pair = _sending_urls.extract(it);
|
||||
url = pair.mapped();
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
- (void)clearSendingURLMaps {
|
||||
_sending_urls.clear();
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
GNCCore *GNCGetCore() {
|
||||
static NSObject *syncSingleton;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
syncSingleton = [[NSObject alloc] init];
|
||||
});
|
||||
|
||||
// The purpose of keeping a weak reference to the GNCCore object is to ensure that it will be
|
||||
// released when all external strong references are gone. I.e., when the app is no longer doing
|
||||
// any NC operations, the core will be released.
|
||||
static __weak GNCCore *core;
|
||||
|
||||
// Strongly reference the GNCCore object for the duration of this function to ensure it isn't
|
||||
// prematurely deallocated by ARC after being created (which can happen in optimized builds).
|
||||
GNCCore *strongCore = core;
|
||||
@synchronized(syncSingleton) {
|
||||
if (!strongCore) {
|
||||
strongCore = [[GNCCore alloc] init];
|
||||
core = strongCore;
|
||||
}
|
||||
}
|
||||
return strongCore;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "internal/platform/implementation/ios/Source/GNCConnection.h"
|
||||
#import "internal/platform/implementation/ios/Source/Internal/GNCCore.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/** This holds the progress and completion for a pending payload. */
|
||||
@interface GNCPayloadInfo : NSObject
|
||||
@property(nonatomic, nullable) NSProgress *progress;
|
||||
@property(nonatomic, nullable) GNCPayloadResultHandler completion;
|
||||
|
||||
+ (instancetype)infoWithProgress:(nullable NSProgress *)progress
|
||||
completion:(GNCPayloadResultHandler)completion;
|
||||
- (void)callCompletion:(GNCPayloadResult)result;
|
||||
@end
|
||||
|
||||
/** GNCConnection that interfaces with the Core library. */
|
||||
@interface GNCCoreConnection : NSObject <GNCConnection>
|
||||
@property(nonatomic) GNCCore *core;
|
||||
@property(nonatomic, copy) GNCEndpointId endpointId;
|
||||
@property(nonatomic) dispatch_block_t deallocHandler;
|
||||
@property(nonatomic) NSMutableDictionary<NSNumber *, GNCPayloadInfo *> *payloads;
|
||||
|
||||
+ (instancetype)connectionWithEndpointId:(GNCEndpointId)endpointId
|
||||
core:(GNCCore *)core
|
||||
deallocHandler:(dispatch_block_t)deallocHandler;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,188 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#import "internal/platform/implementation/ios/Source/Internal/GNCCoreConnection.h"
|
||||
|
||||
#include "connections/core.h"
|
||||
#include "connections/payload.h"
|
||||
#include "internal/platform/exception.h"
|
||||
#include "internal/platform/file.h"
|
||||
#include "internal/platform/implementation/input_file.h"
|
||||
#import "internal/platform/implementation/ios/Source/GNCConnection.h"
|
||||
#import "internal/platform/implementation/ios/Source/GNCPayload.h"
|
||||
#import "internal/platform/implementation/ios/Source/Internal/GNCCore.h"
|
||||
#import "internal/platform/implementation/ios/Source/Platform/utils.h"
|
||||
#include "internal/platform/input_stream.h"
|
||||
#include "internal/platform/payload_id.h"
|
||||
|
||||
using ::location::nearby::ByteArrayFromNSData;
|
||||
using ::location::nearby::CppStringFromObjCString;
|
||||
using ::location::nearby::InputFile;
|
||||
using ::location::nearby::InputStream;
|
||||
using ::location::nearby::connections::Payload;
|
||||
using ::location::nearby::PayloadId;
|
||||
using ResultListener = ::location::nearby::connections::ResultCallback;
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
|
||||
/**
|
||||
* This InputStream subclass takes input from an NSInputStream. The update handler is called for
|
||||
* each chunk of data sent, giving the client an opportunity to handle cancelation.
|
||||
*/
|
||||
class GNCInputStreamFromNSStream : public InputStream {
|
||||
public:
|
||||
explicit GNCInputStreamFromNSStream(NSInputStream *nsStream) : nsStream_(nsStream) {
|
||||
[nsStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
|
||||
[nsStream open];
|
||||
}
|
||||
|
||||
~GNCInputStreamFromNSStream() override { Close(); }
|
||||
|
||||
ExceptionOr<ByteArray> Read() { return Read(kMaxChunkSize); }
|
||||
|
||||
ExceptionOr<ByteArray> Read(std::int64_t size) override {
|
||||
uint8_t *bytesRead = new uint8_t[size];
|
||||
NSUInteger numberOfBytesToRead = [[NSNumber numberWithLongLong:size] unsignedIntegerValue];
|
||||
NSInteger numberOfBytesRead = [nsStream_ read:bytesRead maxLength:numberOfBytesToRead];
|
||||
if (numberOfBytesRead == 0) {
|
||||
// Reached end of stream.
|
||||
return ExceptionOr<ByteArray>();
|
||||
} else if (numberOfBytesRead < 0) {
|
||||
// Stream error.
|
||||
return ExceptionOr<ByteArray>{Exception::kIo};
|
||||
}
|
||||
return ExceptionOr<ByteArray>(ByteArrayFromNSData([NSData dataWithBytes:bytesRead
|
||||
length:numberOfBytesRead]));
|
||||
}
|
||||
|
||||
Exception Close() override {
|
||||
[nsStream_ close];
|
||||
return {Exception::kSuccess};
|
||||
}
|
||||
|
||||
private:
|
||||
static const size_t kMaxChunkSize = 32 * 1024;
|
||||
NSInputStream *nsStream_;
|
||||
// dispatch_block_t update_handler_;
|
||||
};
|
||||
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
@implementation GNCPayloadInfo
|
||||
|
||||
+ (instancetype)infoWithProgress:(nullable NSProgress *)progress
|
||||
completion:(GNCPayloadResultHandler)completion {
|
||||
GNCPayloadInfo *info = [[GNCPayloadInfo alloc] init];
|
||||
info.progress = progress;
|
||||
info.completion = completion;
|
||||
return info;
|
||||
}
|
||||
|
||||
- (void)callCompletion:(GNCPayloadResult)result {
|
||||
if (_completion) _completion(result);
|
||||
_completion = nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GNCCoreConnection
|
||||
|
||||
+ (instancetype)connectionWithEndpointId:(GNCEndpointId)endpointId
|
||||
core:(GNCCore *)core
|
||||
deallocHandler:(dispatch_block_t)deallocHandler {
|
||||
GNCCoreConnection *connection = [[GNCCoreConnection alloc] init];
|
||||
connection.endpointId = endpointId;
|
||||
connection.core = core;
|
||||
connection.deallocHandler = deallocHandler;
|
||||
connection.payloads = [[NSMutableDictionary alloc] init];
|
||||
return connection;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
_core->_core->DisconnectFromEndpoint(CppStringFromObjCString(_endpointId), ResultListener{});
|
||||
_deallocHandler();
|
||||
}
|
||||
|
||||
- (NSProgress *)sendBytesPayload:(GNCBytesPayload *)payload
|
||||
completion:(GNCPayloadResultHandler)completion {
|
||||
Payload corePayload(ByteArrayFromNSData(payload.bytes));
|
||||
NSUInteger length = payload.bytes.length;
|
||||
PayloadId payloadId = corePayload.GetId();
|
||||
NSProgress *progress = [NSProgress progressWithTotalUnitCount:length];
|
||||
__weak __typeof__(self) weakSelf = self;
|
||||
progress.cancellationHandler = ^{
|
||||
[weakSelf cancelPayloadWithId:payloadId];
|
||||
};
|
||||
return [self sendPayload:std::move(corePayload)
|
||||
size:length
|
||||
progress:progress
|
||||
completion:completion];
|
||||
}
|
||||
|
||||
- (NSProgress *)sendStreamPayload:(GNCStreamPayload *)payload
|
||||
completion:(GNCPayloadResultHandler)completion {
|
||||
NSProgress *progress = [NSProgress progressWithTotalUnitCount:-1];
|
||||
|
||||
PayloadId payloadId = payload.identifier;
|
||||
Payload corePayload(payloadId, [payload]() -> InputStream & {
|
||||
location::nearby::connections::GNCInputStreamFromNSStream *stream =
|
||||
new location::nearby::connections::GNCInputStreamFromNSStream(payload.stream);
|
||||
return *stream;
|
||||
});
|
||||
return [self sendPayload:std::move(corePayload) size:-1 progress:progress completion:completion];
|
||||
}
|
||||
|
||||
- (NSProgress *)sendFilePayload:(GNCFilePayload *)payload
|
||||
completion:(GNCPayloadResultHandler)completion {
|
||||
NSProgress *progress = [NSProgress progressWithTotalUnitCount:0];
|
||||
|
||||
std::int64_t fileSize = 0;
|
||||
NSURL *fileURL = payload.fileURL;
|
||||
NSNumber *fileSizeValue = nil;
|
||||
BOOL result = [fileURL getResourceValue:&fileSizeValue forKey:NSURLFileSizeKey error:nil];
|
||||
if (result == YES) {
|
||||
fileSize = fileSizeValue.longValue;
|
||||
}
|
||||
PayloadId payloadId = payload.identifier;
|
||||
// Add the pair of payloadId and fileURL to the map in the GNCCore.
|
||||
[_core insertURLToMapWithPayloadID:payloadId urlToSend:fileURL];
|
||||
Payload corePayload(payloadId, InputFile(payloadId, fileSize));
|
||||
progress.totalUnitCount = fileSize;
|
||||
return [self sendPayload:std::move(corePayload)
|
||||
size:fileSize
|
||||
progress:progress
|
||||
completion:completion];
|
||||
}
|
||||
|
||||
#pragma mark Private
|
||||
|
||||
- (NSProgress *)sendPayload:(Payload)payload
|
||||
size:(uint64_t)size
|
||||
progress:(NSProgress *)progress
|
||||
completion:(GNCPayloadResultHandler)completion {
|
||||
_payloads[@(payload.GetId())] = [GNCPayloadInfo infoWithProgress:progress completion:completion];
|
||||
_core->_core->SendPayload(std::vector<std::string>(1, CppStringFromObjCString(_endpointId)),
|
||||
std::move(payload), ResultListener{});
|
||||
return progress;
|
||||
}
|
||||
|
||||
- (void)cancelPayloadWithId:(PayloadId)payloadId {
|
||||
_core->_core->CancelPayload(payloadId, ResultListener{});
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,401 @@
|
||||
// Copyright 2021 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#import "internal/platform/implementation/ios/Source/GNCDiscoverer.h"
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "absl/functional/bind_front.h"
|
||||
#include "connections/connection_options.h"
|
||||
#include "connections/core.h"
|
||||
#include "connections/discovery_options.h"
|
||||
#include "connections/listeners.h"
|
||||
#include "connections/status.h"
|
||||
#include "internal/platform/byte_array.h"
|
||||
#import "internal/platform/implementation/ios/Source/GNCConnection.h"
|
||||
#import "internal/platform/implementation/ios/Source/Internal/GNCCore.h"
|
||||
#import "internal/platform/implementation/ios/Source/Internal/GNCCoreConnection.h"
|
||||
#import "internal/platform/implementation/ios/Source/Internal/GNCPayloadListener.h"
|
||||
#import "internal/platform/implementation/ios/Source/Internal/GNCUtils.h"
|
||||
#import "internal/platform/implementation/ios/Source/Platform/utils.h"
|
||||
#import "GoogleToolboxForMac/GTMLogger.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
using ::location::nearby::ByteArray;
|
||||
using ::location::nearby::CppStringFromObjCString;
|
||||
using ::location::nearby::connections::DiscoveryOptions;
|
||||
using ::location::nearby::connections::ConnectionOptions;
|
||||
using ::location::nearby::connections::DiscoveryListener;
|
||||
using ::location::nearby::connections::DistanceInfo;
|
||||
using ::location::nearby::connections::GNCStrategyToStrategy;
|
||||
using ResultListener = ::location::nearby::connections::ResultCallback;
|
||||
using ::location::nearby::connections::Status;
|
||||
|
||||
/** This is a GNCDiscovererConnectionInfo that provides storage for its properties. */
|
||||
@interface GNCDiscovererConnectionInfo : NSObject <GNCDiscovererConnectionInfo>
|
||||
@property(nonatomic, copy) NSString *authToken;
|
||||
|
||||
/** Creates a GNCDiscovererConnectionInfo object. */
|
||||
+ (instancetype)infoWithAuthToken:(NSString *)authToken;
|
||||
@end
|
||||
|
||||
@implementation GNCDiscovererConnectionInfo
|
||||
|
||||
+ (instancetype)infoWithAuthToken:(NSString *)authToken {
|
||||
GNCDiscovererConnectionInfo *info = [[GNCDiscovererConnectionInfo alloc] init];
|
||||
info.authToken = authToken;
|
||||
return info;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
/** This is a GNCDiscoveredEndpointInfo that provides storage for its properties. */
|
||||
@interface GNCDiscoveredEndpointInfo : NSObject <GNCDiscoveredEndpointInfo>
|
||||
@property(nonatomic, copy) NSString *endpointName;
|
||||
@property(nonatomic, copy) NSData *endpointInfo;
|
||||
@end
|
||||
|
||||
@implementation GNCDiscoveredEndpointInfo
|
||||
|
||||
@synthesize requestConnection = _requestConnection;
|
||||
|
||||
+ (instancetype)infoWithName:(NSString *)endpointName
|
||||
endpointInfo:(NSData *)endpointInfo
|
||||
requestConnection:(GNCConnectionRequester)requestConnection {
|
||||
GNCDiscoveredEndpointInfo *info = [[GNCDiscoveredEndpointInfo alloc] init];
|
||||
info.endpointName = endpointName;
|
||||
info.endpointInfo = endpointInfo;
|
||||
info->_requestConnection = requestConnection;
|
||||
return info;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
/** Information retained by the discoverer about each discovered endpoint. */
|
||||
@interface GNCDiscovererEndpointInfo : NSObject
|
||||
|
||||
/** Handles lostHandler once |onEndpointLost| has been callback. */
|
||||
@property(nonatomic) GNCEndpointLostHandler lostHandler;
|
||||
|
||||
/** The connInitHandler is stored after requestConnection. */
|
||||
@property(nonatomic, nullable) GNCDiscovererConnectionInitializationHandler connInitHandler;
|
||||
|
||||
/** The connFailureHandler is stored after requestConnection. */
|
||||
@property(nonatomic, nullable) GNCConnectionFailureHandler connFailureHandler;
|
||||
|
||||
/** Client responses Accept or Reject. */
|
||||
@property(nonatomic) GNCConnectionResponse clientResponse;
|
||||
|
||||
/** Whether the client response has been received. */
|
||||
@property(nonatomic) BOOL clientResponseReceived;
|
||||
|
||||
/**
|
||||
* The connectionhandler returned by connInitHandler. Stored here if the connection is accepted.
|
||||
*/
|
||||
@property(nonatomic, nullable) GNCConnectionHandler connectionHandler;
|
||||
|
||||
/** @c GNCCoreConnection is created and stored if connection is accepted. */
|
||||
@property(nonatomic, weak) GNCCoreConnection *connection;
|
||||
|
||||
/** @c GNCConnectionHandlers object is returned by connectionHandler and stored here. */
|
||||
@property(nonatomic) GNCConnectionHandlers *connectionHandlers;
|
||||
@end
|
||||
|
||||
@implementation GNCDiscovererEndpointInfo
|
||||
@end
|
||||
|
||||
/** GNCDiscoverer members. */
|
||||
@interface GNCDiscoverer ()
|
||||
@property(nonatomic) GNCCore *core;
|
||||
@property(nonatomic) GNCEndpointFoundHandler endpointFoundHandler;
|
||||
@property(nonatomic, assign) Status status;
|
||||
@property(nonatomic) NSMapTable<GNCEndpointId, GNCDiscovererEndpointInfo *> *endpoints;
|
||||
@end
|
||||
|
||||
/** C++ classes passed to the core library by GNCDiscoverer. */
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
|
||||
/** This class contains the discoverer callbacks related to a connection. */
|
||||
class GNCDiscovererConnectionListener {
|
||||
public:
|
||||
GNCDiscovererConnectionListener(GNCCore *core,
|
||||
NSMapTable<GNCEndpointId, GNCDiscovererEndpointInfo *> *endpoints)
|
||||
: core_(core), endpoints_(endpoints) {}
|
||||
|
||||
void OnInitiated(const std::string &endpoint_id, const ConnectionResponseInfo &info) {
|
||||
NSString *endpointId = ObjCStringFromCppString(endpoint_id);
|
||||
GNCDiscovererEndpointInfo *endpointInfo = [endpoints_ objectForKey:endpointId];
|
||||
if (!endpointInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Call the connection initiation handler. Synchronous because it returns the connection
|
||||
// handler.
|
||||
NSString *authToken = ObjCStringFromCppString(info.authentication_token);
|
||||
GNCCore *core = core_; // don't capture |this|
|
||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
||||
endpointInfo.connectionHandler = endpointInfo.connInitHandler(
|
||||
[GNCDiscovererConnectionInfo infoWithAuthToken:authToken],
|
||||
^(GNCConnectionResponse response) {
|
||||
endpointInfo.clientResponse = response;
|
||||
endpointInfo.clientResponseReceived = YES;
|
||||
if (response == GNCConnectionResponseAccept) {
|
||||
// The connect was accepted by the client.
|
||||
if (payload_listener_ == nullptr) {
|
||||
payload_listener_ = std::make_unique<GNCPayloadListener>(
|
||||
core,
|
||||
^{
|
||||
return endpointInfo.connectionHandlers;
|
||||
},
|
||||
^{
|
||||
return endpointInfo.connection.payloads;
|
||||
});
|
||||
}
|
||||
core->_core->AcceptConnection(
|
||||
CppStringFromObjCString(endpointId),
|
||||
PayloadListener{
|
||||
.payload_cb =
|
||||
absl::bind_front(&GNCPayloadListener::OnPayload, payload_listener_.get()),
|
||||
.payload_progress_cb = absl::bind_front(
|
||||
&GNCPayloadListener::OnPayloadProgress, payload_listener_.get()),
|
||||
},
|
||||
ResultListener{});
|
||||
} else {
|
||||
// The connect was rejected by the client.
|
||||
core->_core->RejectConnection(CppStringFromObjCString(endpointId), ResultListener{});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void OnAccepted(const std::string &endpoint_id) {
|
||||
NSString *endpointId = ObjCStringFromCppString(endpoint_id);
|
||||
GNCDiscovererEndpointInfo *endpointInfo = [endpoints_ objectForKey:endpointId];
|
||||
if (!endpointInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The connection has been accepted by both endpoints, so create the GNCConnection object
|
||||
// and pass it to |successHandler| for the client to use.
|
||||
// Note: Use a local strong reference to the connection object; don't just assign to
|
||||
// |endpointInfo.connection|. Without a strong reference, the connection object can be
|
||||
// deallocated before |successHandler| is called in the Release build.
|
||||
id<GNCConnection> connection = [GNCCoreConnection
|
||||
connectionWithEndpointId:endpointId
|
||||
core:core_
|
||||
deallocHandler:^{
|
||||
// Don't remove the remote endpoint (like GNCAdvertiser does) because that's
|
||||
// done when the endpoint is lost.
|
||||
}];
|
||||
endpointInfo.connection = connection;
|
||||
|
||||
// Callback is synchronous because it returns the connection handlers.
|
||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
||||
endpointInfo.connectionHandlers = endpointInfo.connectionHandler(endpointInfo.connection);
|
||||
});
|
||||
endpointInfo.clientResponseReceived = NO; // support reconnection after disconnection
|
||||
}
|
||||
|
||||
void OnRejected(const std::string &endpoint_id, Status status) {
|
||||
NSString *endpointId = ObjCStringFromCppString(endpoint_id);
|
||||
GNCDiscovererEndpointInfo *endpointInfo = [endpoints_ objectForKey:endpointId];
|
||||
if (!endpointInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If either side rejected, call failureHandler with the connection status.
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
endpointInfo.connFailureHandler(GNCConnectionFailureRejected);
|
||||
});
|
||||
endpointInfo.clientResponseReceived = NO; // support reconnection after disconnection
|
||||
}
|
||||
|
||||
void OnDisconnected(const std::string &endpoint_id) {
|
||||
NSString *endpointId = ObjCStringFromCppString(endpoint_id);
|
||||
GNCDiscovererEndpointInfo *endpointInfo = [endpoints_ objectForKey:endpointId];
|
||||
if (!endpointInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (endpointInfo.connection) {
|
||||
GNCDisconnectedHandler disconnectedHandler =
|
||||
endpointInfo.connectionHandlers.disconnectedHandler;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
if (disconnectedHandler) disconnectedHandler(GNCDisconnectedReasonUnknown);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void OnBandwidthChanged(const std::string &endpoint_id, Medium medium) {
|
||||
// TODO(b/169292092): Implement.
|
||||
}
|
||||
|
||||
private:
|
||||
GNCCore *core_;
|
||||
NSMapTable<GNCEndpointId, GNCDiscovererEndpointInfo *> *endpoints_;
|
||||
std::unique_ptr<GNCPayloadListener> payload_listener_;
|
||||
};
|
||||
|
||||
class GNCDiscoveryListener {
|
||||
public:
|
||||
explicit GNCDiscoveryListener(GNCDiscoverer *discoverer) : discoverer_(discoverer) {}
|
||||
|
||||
void OnEndpointFound(const std::string &endpoint_id, const ByteArray &endpoint_info,
|
||||
const std::string &service_id) {
|
||||
GNCDiscoverer *discoverer = discoverer_; // strongify
|
||||
if (!discoverer) {
|
||||
return;
|
||||
}
|
||||
NSString *endpointId = ObjCStringFromCppString(endpoint_id);
|
||||
|
||||
if ([discoverer.endpoints objectForKey:endpointId] != nil) {
|
||||
GTMLoggerError(@"Endpoint already discovered: %@", endpointId);
|
||||
} else {
|
||||
// The GNCDiscoveredEndpointInfo object created here lives as long as the client has strong
|
||||
// reference to it. Here's the chain of strong references maintained here:
|
||||
// client -> GNCDiscoveredEndpointInfo -> RequestConnection block ->
|
||||
// GNCDiscovererEndpointInfo (stored weakly in the |endpoints| map table)
|
||||
GNCDiscovererEndpointInfo *endpointInfo = [[GNCDiscovererEndpointInfo alloc] init];
|
||||
[discoverer.endpoints setObject:endpointInfo forKey:endpointId];
|
||||
|
||||
NSString *name = ObjCStringFromCppString(std::string(endpoint_info));
|
||||
NSData *info = NSDataFromByteArray(endpoint_info);
|
||||
GNCCore *core = discoverer.core; // don't capture |this| or |discoverer|
|
||||
NSMapTable<GNCEndpointId, GNCDiscovererEndpointInfo *> *endpoints = discoverer.endpoints;
|
||||
GNCDiscoveredEndpointInfo *discEndpointInfo = [GNCDiscoveredEndpointInfo
|
||||
infoWithName:name
|
||||
endpointInfo:info
|
||||
requestConnection:^(NSData *info,
|
||||
GNCDiscovererConnectionInitializationHandler connInitHandler,
|
||||
GNCConnectionFailureHandler connFailureHandler) {
|
||||
endpointInfo.connInitHandler = connInitHandler;
|
||||
endpointInfo.connFailureHandler = connFailureHandler;
|
||||
if (discoverer_connection_listener_ == nullptr) {
|
||||
discoverer_connection_listener_ =
|
||||
std::make_unique<GNCDiscovererConnectionListener>(core, endpoints);
|
||||
}
|
||||
|
||||
ConnectionListener listener = {
|
||||
.initiated_cb = absl::bind_front(&GNCDiscovererConnectionListener::OnInitiated,
|
||||
discoverer_connection_listener_.get()),
|
||||
.accepted_cb = absl::bind_front(&GNCDiscovererConnectionListener::OnAccepted,
|
||||
discoverer_connection_listener_.get()),
|
||||
.rejected_cb = absl::bind_front(&GNCDiscovererConnectionListener::OnRejected,
|
||||
discoverer_connection_listener_.get()),
|
||||
.disconnected_cb =
|
||||
absl::bind_front(&GNCDiscovererConnectionListener::OnDisconnected,
|
||||
discoverer_connection_listener_.get()),
|
||||
};
|
||||
|
||||
core->_core->RequestConnection(
|
||||
CppStringFromObjCString(endpointId),
|
||||
ConnectionRequestInfo{.endpoint_info = ByteArrayFromNSData(info),
|
||||
.listener = std::move(listener)},
|
||||
ConnectionOptions{},
|
||||
ResultListener{.result_cb = [&connFailureHandler](Status status) {
|
||||
if (!status.Ok()) {
|
||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
||||
connFailureHandler(GNCConnectionFailureUnknown);
|
||||
});
|
||||
}
|
||||
}});
|
||||
}];
|
||||
|
||||
// Call the client endpoint-found handler. Tail call for reentrancy.
|
||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
||||
endpointInfo.lostHandler = discoverer.endpointFoundHandler(endpointId, discEndpointInfo);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void OnEndpointLost(const std::string &endpoint_id) {
|
||||
GNCDiscoverer *discoverer = discoverer_; // strongify
|
||||
if (!discoverer) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSString *endpointId = ObjCStringFromCppString(endpoint_id);
|
||||
GNCDiscovererEndpointInfo *info = [discoverer.endpoints objectForKey:endpointId];
|
||||
if (!info) {
|
||||
GTMLoggerError(@"Endpoint already lost: %@", endpointId);
|
||||
} else {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
info.lostHandler();
|
||||
});
|
||||
}
|
||||
[discoverer.endpoints removeObjectForKey:endpointId];
|
||||
}
|
||||
|
||||
void OnEndpointDistanceChanged_cb(const std::string &endpoint_id, DistanceInfo info) {
|
||||
// TODO(b/169292092): Implement.
|
||||
}
|
||||
|
||||
private:
|
||||
__weak GNCDiscoverer *discoverer_;
|
||||
std::unique_ptr<GNCDiscovererConnectionListener> discoverer_connection_listener_;
|
||||
};
|
||||
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
using ::location::nearby::connections::GNCDiscoveryListener;
|
||||
|
||||
@interface GNCDiscoverer () {
|
||||
std::unique_ptr<GNCDiscoveryListener> discoveryListener;
|
||||
};
|
||||
|
||||
@end
|
||||
|
||||
@implementation GNCDiscoverer
|
||||
|
||||
+ (instancetype)discovererWithServiceId:(NSString *)serviceId
|
||||
strategy:(GNCStrategy)strategy
|
||||
endpointFoundHandler:(GNCEndpointFoundHandler)endpointFoundHandler {
|
||||
GNCDiscoverer *discoverer = [[GNCDiscoverer alloc] init];
|
||||
discoverer.endpointFoundHandler = endpointFoundHandler;
|
||||
discoverer.endpoints = [NSMapTable strongToWeakObjectsMapTable];
|
||||
discoverer.core = GNCGetCore();
|
||||
discoverer->discoveryListener = std::make_unique<GNCDiscoveryListener>(discoverer);
|
||||
|
||||
DiscoveryListener listener = {
|
||||
.endpoint_found_cb = absl::bind_front(&GNCDiscoveryListener::OnEndpointFound,
|
||||
discoverer->discoveryListener.get()),
|
||||
.endpoint_lost_cb = absl::bind_front(&GNCDiscoveryListener::OnEndpointLost,
|
||||
discoverer->discoveryListener.get()),
|
||||
};
|
||||
|
||||
discoverer.core->_core->StartDiscovery(CppStringFromObjCString(serviceId),
|
||||
DiscoveryOptions{
|
||||
{
|
||||
GNCStrategyToStrategy(strategy),
|
||||
},
|
||||
},
|
||||
std::move(listener), ResultListener{});
|
||||
|
||||
return discoverer;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
GTMLoggerInfo(@"GNCDiscoverer deallocated");
|
||||
_core->_core->StopDiscovery(ResultListener{});
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#import "internal/platform/implementation/ios/Source/GNCPayload.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/** This category adds the ability to specify a payload ID. */
|
||||
@interface GNCBytesPayload (Internal)
|
||||
+ (instancetype)payloadWithBytes:(NSData *)bytes identifier:(int64_t)identifier;
|
||||
@end
|
||||
|
||||
|
||||
/** This category adds the ability to specify a payload ID. */
|
||||
@interface GNCStreamPayload (Internal)
|
||||
+ (instancetype)payloadWithStream:(NSInputStream *)stream identifier:(int64_t)identifier;
|
||||
@end
|
||||
|
||||
|
||||
/** This category adds the ability to specify a payload ID. */
|
||||
@interface GNCFilePayload (Internal)
|
||||
+ (instancetype)payloadWithFileURL:(NSURL *)fileURL identifier:(int64_t)identifier;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,94 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#import "internal/platform/implementation/ios/Source/GNCPayload.h"
|
||||
|
||||
#include "connections/payload.h"
|
||||
#include "internal/platform/payload_id.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
using ::location::nearby::connections::Payload;
|
||||
using ::location::nearby::PayloadId;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
PayloadId GenerateId() {
|
||||
return Payload::GenerateId();
|
||||
}
|
||||
|
||||
@implementation GNCBytesPayload
|
||||
|
||||
- (instancetype)initWithBytes:(NSData *)bytes identifier:(int64_t)identifier {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_identifier = identifier;
|
||||
_bytes = bytes;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (instancetype)payloadWithBytes:(NSData *)bytes {
|
||||
return [[self alloc] initWithBytes:bytes identifier:GenerateId()];
|
||||
}
|
||||
|
||||
+ (instancetype)payloadWithBytes:(NSData *)bytes identifier:(int64_t)identifier {
|
||||
return [[self alloc] initWithBytes:bytes identifier:identifier];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GNCStreamPayload
|
||||
|
||||
- (instancetype)initWithStream:(NSInputStream *)stream identifier:(int64_t)identifier {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_identifier = identifier;
|
||||
_stream = stream;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (instancetype)payloadWithStream:(NSInputStream *)stream {
|
||||
return [[self alloc] initWithStream:stream identifier:GenerateId()];
|
||||
}
|
||||
|
||||
+ (instancetype)payloadWithStream:(NSInputStream *)stream identifier:(int64_t)identifier {
|
||||
return [[self alloc] initWithStream:stream identifier:identifier];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GNCFilePayload
|
||||
|
||||
- (instancetype)initWithFileURL:(NSURL *)fileURL identifier:(int64_t)identifier {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_identifier = identifier;
|
||||
_fileURL = [fileURL copy];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (instancetype)payloadWithFileURL:(NSURL *)fileURL {
|
||||
return [[self alloc] initWithFileURL:fileURL identifier:GenerateId()];
|
||||
}
|
||||
|
||||
+ (instancetype)payloadWithFileURL:(NSURL *)fileURL identifier:(int64_t)identifier {
|
||||
return [[self alloc] initWithFileURL:fileURL identifier:identifier];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,55 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "internal/platform/implementation/ios/Source/GNCConnection.h"
|
||||
#import "internal/platform/implementation/ios/Source/Internal/GNCCore.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class GNCPayloadInfo;
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
|
||||
/** This fetches a GNCConnectionHandlers object. */
|
||||
typedef GNCConnectionHandlers *_Nonnull (^GNCConnectionHandlersProvider)();
|
||||
|
||||
/** This fetches a payload dictionary. */
|
||||
typedef NSMutableDictionary<NSNumber *, GNCPayloadInfo *> *_Nonnull (^GNCPayloadsProvider)();
|
||||
|
||||
/** This is the payload handler for an advertiser or discoverer. */
|
||||
class GNCPayloadListener : public PayloadListener {
|
||||
public:
|
||||
GNCPayloadListener(GNCCore *core, GNCConnectionHandlersProvider handlersProvider,
|
||||
GNCPayloadsProvider payloadsProvider)
|
||||
: core_(core), handlers_provider_(handlersProvider), payloads_provider_(payloadsProvider) {}
|
||||
|
||||
void OnPayload(const std::string& endpoint_id, Payload payload);
|
||||
void OnPayloadProgress(const std::string& endpoint_id,
|
||||
const PayloadProgressInfo& info);
|
||||
|
||||
private:
|
||||
GNCCore *core_;
|
||||
GNCConnectionHandlersProvider handlers_provider_;
|
||||
GNCPayloadsProvider payloads_provider_;
|
||||
};
|
||||
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,218 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#import "internal/platform/implementation/ios/Source/Internal/GNCPayloadListener.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "connections/core.h"
|
||||
#include "connections/listeners.h"
|
||||
#include "connections/payload.h"
|
||||
#include "internal/platform/byte_array.h"
|
||||
#include "internal/platform/exception.h"
|
||||
#include "internal/platform/file.h"
|
||||
#import "internal/platform/implementation/ios/Source/GNCConnection.h"
|
||||
#import "internal/platform/implementation/ios/Source/GNCPayload.h"
|
||||
#import "internal/platform/implementation/ios/Source/Internal/GNCCore.h"
|
||||
#import "internal/platform/implementation/ios/Source/Internal/GNCCoreConnection.h"
|
||||
#import "internal/platform/implementation/ios/Source/Internal/GNCPayload+Internal.h"
|
||||
#include "internal/platform/implementation/ios/Source/Platform/utils.h"
|
||||
#include "internal/platform/input_stream.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
|
||||
void GNCPayloadListener::OnPayload(const std::string &endpoint_id, Payload payload) {
|
||||
GNCConnectionHandlers *handlers = handlers_provider_();
|
||||
int64_t payloadId = payload.GetId();
|
||||
|
||||
// Note: The payload must be destroyed by each individual payload type handler below, because in
|
||||
// the Stream payload case, it runs an asynchronous read-write loop, which needs the payload
|
||||
// and its stream to live until the stream ends.
|
||||
NSMutableDictionary<NSNumber *, GNCPayloadInfo *> *payloads = payloads_provider_();
|
||||
|
||||
switch (payload.GetType()) {
|
||||
case Payload::Type::kBytes: {
|
||||
NSData *data = NSDataFromByteArray(payload.AsBytes()); // don't capture C++ object
|
||||
|
||||
// Wait for the payload transfer update to arrive before calling the Bytes payload handler.
|
||||
GNCPayloadInfo *info = [GNCPayloadInfo
|
||||
infoWithProgress:nil
|
||||
completion:^(GNCPayloadResult result) {
|
||||
NSCAssert(result == GNCPayloadResultSuccess, @"Expected success");
|
||||
if (handlers.bytesPayloadHandler) {
|
||||
// Call the Bytes payload handler.
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
handlers.bytesPayloadHandler([GNCBytesPayload payloadWithBytes:data
|
||||
identifier:payloadId]);
|
||||
});
|
||||
}
|
||||
}];
|
||||
payloads[@(payloadId)] = info;
|
||||
break;
|
||||
}
|
||||
|
||||
case Payload::Type::kStream:
|
||||
if (handlers.streamPayloadHandler) {
|
||||
// Make a pair of bound streams so data pumped into the output stream becomes
|
||||
// available for reading from the input stream.
|
||||
NSInputStream *clientInputStream;
|
||||
NSOutputStream *clientOutputStream;
|
||||
// TODO(b/169292092): Base on medium's bandwidth?
|
||||
[NSStream getBoundStreamsWithBufferSize:1024
|
||||
inputStream:&clientInputStream
|
||||
outputStream:&clientOutputStream];
|
||||
|
||||
NSProgress *progress = [NSProgress progressWithTotalUnitCount:-1]; // indeterminate
|
||||
progress.cancellable = YES;
|
||||
|
||||
// Pass the payload to the stream payload handler, receiving the completion handler from it.
|
||||
// Since it returns a value, it must be called synchronously.
|
||||
__block GNCPayloadResultHandler completion;
|
||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
||||
completion = handlers.streamPayloadHandler(
|
||||
[GNCStreamPayload payloadWithStream:clientInputStream identifier:payloadId],
|
||||
progress);
|
||||
});
|
||||
GNCPayloadInfo *info = [GNCPayloadInfo infoWithProgress:progress completion:completion];
|
||||
payloads[@(payloadId)] = info;
|
||||
|
||||
// This is a loop that reads data from the C++ input stream and writes it to the output
|
||||
// stream that feeds it to the client input stream.
|
||||
__block InputStream *payloadInputStream = payload.AsStream();
|
||||
dispatch_queue_t queue =
|
||||
dispatch_queue_create("StreamReceiverQueue", DISPATCH_QUEUE_SERIAL);
|
||||
dispatch_async(queue, ^{
|
||||
[clientOutputStream open];
|
||||
while (true) {
|
||||
if (progress.isCancelled) {
|
||||
// Payload was canceled by the client.
|
||||
core_->_core->CancelPayload(payloadId, ResultCallback{.result_cb = [](Status status) {
|
||||
// TODO(b/148640962): Implement.
|
||||
}});
|
||||
break;
|
||||
}
|
||||
|
||||
ExceptionOr<ByteArray> readResult = payloadInputStream->Read(1024);
|
||||
if (!readResult.ok()) {
|
||||
// Error reading from stream.
|
||||
// TODO(b/169292092): Tell core an error has occurred?
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[info callCompletion:GNCPayloadResultFailure];
|
||||
});
|
||||
break;
|
||||
}
|
||||
ByteArray byteArray = readResult.GetResult();
|
||||
if (byteArray.Empty()) {
|
||||
// End of stream.
|
||||
break;
|
||||
}
|
||||
|
||||
// Loop until it's all been consumed by the client output stream.
|
||||
NSData *data = NSDataFromByteArray(byteArray);
|
||||
NSUInteger totalLength = data.length;
|
||||
NSUInteger totalNumberWritten = 0;
|
||||
while (totalNumberWritten < totalLength) {
|
||||
NSInteger numberWritten =
|
||||
[clientOutputStream write:&((const uint8_t *)data.bytes)[totalNumberWritten]
|
||||
maxLength:totalLength - totalNumberWritten];
|
||||
if (numberWritten <= 0) { // stream error or reached end of stream
|
||||
// TODO(b/169292092): Tell core an error has occurred?
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[info callCompletion:GNCPayloadResultFailure];
|
||||
});
|
||||
break;
|
||||
}
|
||||
totalNumberWritten += numberWritten;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
case Payload::Type::kFile:
|
||||
if (handlers.filePayloadHandler) {
|
||||
InputFile *payloadInputFile = payload.AsFile();
|
||||
NSURL *fileURL =
|
||||
[NSURL URLWithString:ObjCStringFromCppString(payloadInputFile->GetFilePath())];
|
||||
int64_t fileSize = payloadInputFile->GetTotalSize();
|
||||
NSProgress *progress = [NSProgress progressWithTotalUnitCount:fileSize];
|
||||
progress.cancellable = YES;
|
||||
progress.cancellationHandler = ^{
|
||||
// Payload was canceled by the client.
|
||||
core_->_core->CancelPayload(payloadId, ResultCallback{.result_cb = [](Status status) {
|
||||
// TODO(b/148640962): Implement.
|
||||
}});
|
||||
};
|
||||
|
||||
// Pass the payload to the file payload handler, receiving the completion handler from it.
|
||||
// Since it returns a value, it must be called synchronously.
|
||||
__block GNCPayloadResultHandler completion;
|
||||
void (^passPayloadBlock)(void) = ^{
|
||||
completion = handlers.filePayloadHandler(
|
||||
[GNCFilePayload payloadWithFileURL:fileURL identifier:payloadId], progress);
|
||||
};
|
||||
if ([NSThread isMainThread]) {
|
||||
passPayloadBlock();
|
||||
} else {
|
||||
dispatch_sync(dispatch_get_main_queue(), passPayloadBlock);
|
||||
}
|
||||
GNCPayloadInfo *info = [GNCPayloadInfo infoWithProgress:progress completion:completion];
|
||||
payloads[@(payloadId)] = info;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
;// fall through
|
||||
}
|
||||
}
|
||||
|
||||
void GNCPayloadListener::OnPayloadProgress(const std::string &endpoint_id,
|
||||
const PayloadProgressInfo &info) {
|
||||
// Note: The logic in this callback for handling progress updates and payload completion is
|
||||
// identical for Bytes, Stream and File payloads.
|
||||
NSMutableDictionary<NSNumber *, GNCPayloadInfo *> *payloads = payloads_provider_();
|
||||
NSNumber *payloadId = @(info.payload_id);
|
||||
GNCPayloadInfo *payloadInfo = payloads[payloadId];
|
||||
if (payloadInfo) {
|
||||
// Update the progress.
|
||||
if (payloadInfo.progress) {
|
||||
payloadInfo.progress.completedUnitCount = info.bytes_transferred;
|
||||
}
|
||||
|
||||
// Call the completion handler for success/failure/canceled, but not in-progress.
|
||||
if (info.status == PayloadProgressInfo::Status::kInProgress) {
|
||||
return;
|
||||
}
|
||||
GNCPayloadResult result =
|
||||
(info.status == PayloadProgressInfo::Status::kSuccess) ? GNCPayloadResultSuccess
|
||||
: (info.status == PayloadProgressInfo::Status::kCanceled) ? GNCPayloadResultCanceled
|
||||
: GNCPayloadResultFailure;
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
payloadInfo.completion(result);
|
||||
});
|
||||
|
||||
// Release the payload info.
|
||||
[payloads removeObjectForKey:payloadId];
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,42 @@
|
||||
// Copyright 2021 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "connections/listeners.h"
|
||||
#import "internal/platform/implementation/ios/Source/GNCAdvertiser.h"
|
||||
#import "internal/platform/implementation/ios/Source/GNCConnection.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
|
||||
/** Converts GNCStrategy to Strategy. */
|
||||
const Strategy& GNCStrategyToStrategy(GNCStrategy strategy);
|
||||
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
/** Internal-only properties of the connection result handlers class. */
|
||||
@interface GNCConnectionResultHandlers ()
|
||||
@property(nonatomic) GNCConnectionHandler successHandler;
|
||||
@property(nonatomic) GNCConnectionFailureHandler failureHandler;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,77 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#import "internal/platform/implementation/ios/Source/Internal/GNCUtils.h"
|
||||
|
||||
#include "connections/strategy.h"
|
||||
#import "internal/platform/implementation/ios/Source/GNCAdvertiser.h"
|
||||
#import "internal/platform/implementation/ios/Source/GNCConnection.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
|
||||
const Strategy& GNCStrategyToStrategy(GNCStrategy strategy) {
|
||||
switch (strategy) {
|
||||
case GNCStrategyCluster:
|
||||
return Strategy::kP2pCluster;
|
||||
case GNCStrategyStar:
|
||||
return Strategy::kP2pStar;
|
||||
case GNCStrategyPointToPoint:
|
||||
return Strategy::kP2pPointToPoint;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
@implementation GNCConnectionHandlers
|
||||
|
||||
- (instancetype)initWithBuilderBlock:(void (^)(GNCConnectionHandlers*))builderBlock {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
builderBlock(self);
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (instancetype)handlersWithBuilder:(void (^)(GNCConnectionHandlers * _Nonnull))builderBlock {
|
||||
return [[self alloc] initWithBuilderBlock:builderBlock];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation GNCConnectionResultHandlers
|
||||
|
||||
- (instancetype)initWithSuccessHandler:(GNCConnectionHandler)successHandler
|
||||
failureHandler:(GNCConnectionFailureHandler)failureHandler {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_successHandler = successHandler;
|
||||
_failureHandler = failureHandler;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (instancetype)successHandler:(GNCConnectionHandler)successHandler
|
||||
failureHandler:(GNCConnectionFailureHandler)failureHandler {
|
||||
return [[self alloc] initWithSuccessHandler:successHandler failureHandler:failureHandler];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,151 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#include "internal/platform/implementation/platform.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#import "internal/platform/implementation/ios/Source/Internal/GNCCore.h"
|
||||
#include "internal/platform/implementation/ios/Source/Platform/atomic_boolean.h"
|
||||
#include "internal/platform/implementation/ios/Source/Platform/atomic_uint32.h"
|
||||
#include "internal/platform/implementation/ios/Source/Platform/condition_variable.h"
|
||||
#include "internal/platform/implementation/ios/Source/Platform/count_down_latch.h"
|
||||
#include "internal/platform/implementation/ios/Source/Platform/input_file.h"
|
||||
#import "internal/platform/implementation/ios/Source/Platform/log_message.h"
|
||||
#import "internal/platform/implementation/ios/Source/Platform/multi_thread_executor.h"
|
||||
#include "internal/platform/implementation/ios/Source/Platform/mutex.h"
|
||||
#import "internal/platform/implementation/ios/Source/Platform/scheduled_executor.h"
|
||||
#import "internal/platform/implementation/ios/Source/Platform/single_thread_executor.h"
|
||||
#import "internal/platform/implementation/ios/Source/Platform/utils.h"
|
||||
#include "internal/platform/implementation/ios/Source/Platform/wifi_lan.h"
|
||||
#include "internal/platform/implementation/mutex.h"
|
||||
#include "internal/platform/implementation/shared/file.h"
|
||||
#include "internal/platform/payload_id.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace api {
|
||||
|
||||
namespace {
|
||||
std::string GetPayloadPath(PayloadId payload_id) {
|
||||
// This is to get a file path, e.g. /tmp/[payload_id], for the storage of payload file.
|
||||
// NOTE: Per
|
||||
// https://developer.apple.com/library/content/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/FileSystemOverview/FileSystemOverview.html
|
||||
// Files saved in the /tmp directory will be deleted by the system. Callers should be responsible
|
||||
// for copying the files to the permanent storage.
|
||||
NSString* payloadIdString = ObjCStringFromCppString(std::to_string(payload_id));
|
||||
return CppStringFromObjCString(
|
||||
[NSTemporaryDirectory() stringByAppendingPathComponent:payloadIdString]);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// Atomics:
|
||||
std::unique_ptr<AtomicBoolean> ImplementationPlatform::CreateAtomicBoolean(bool initial_value) {
|
||||
return std::make_unique<ios::AtomicBoolean>(initial_value);
|
||||
}
|
||||
|
||||
std::unique_ptr<AtomicUint32> ImplementationPlatform::CreateAtomicUint32(
|
||||
std::uint32_t initial_value) {
|
||||
return std::make_unique<ios::AtomicUint32>(initial_value);
|
||||
}
|
||||
|
||||
std::unique_ptr<CountDownLatch> ImplementationPlatform::CreateCountDownLatch(std::int32_t count) {
|
||||
return std::make_unique<ios::CountDownLatch>(count);
|
||||
}
|
||||
|
||||
std::unique_ptr<Mutex> ImplementationPlatform::CreateMutex(Mutex::Mode mode) {
|
||||
// iOS does not support unchecked Mutex in debug mode, therefore
|
||||
// ios::Mutex is used for both kRegular and kRegularNoCheck.
|
||||
if (mode == Mutex::Mode::kRecursive) {
|
||||
return absl::make_unique<ios::RecursiveMutex>();
|
||||
} else {
|
||||
return absl::make_unique<ios::Mutex>();
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<ConditionVariable> ImplementationPlatform::CreateConditionVariable(Mutex* mutex) {
|
||||
return std::make_unique<ios::ConditionVariable>(static_cast<ios::Mutex*>(mutex));
|
||||
}
|
||||
|
||||
std::unique_ptr<InputFile> ImplementationPlatform::CreateInputFile(PayloadId payload_id,
|
||||
std::int64_t total_size) {
|
||||
// Extract the NSURL object with payload_id from |GNCCore| which stores the maps. If the retrieved
|
||||
// NSURL object is not nil, we create InputFile by ios::InputFile. The difference is
|
||||
// that ios::InputFile implements to read bytes from local real file for sending.
|
||||
GNCCore* core = GNCGetCore();
|
||||
NSURL* url = [core extractURLWithPayloadID:payload_id];
|
||||
if (url != nil) {
|
||||
return absl::make_unique<ios::InputFile>(url);
|
||||
} else {
|
||||
return absl::make_unique<shared::InputFile>(GetPayloadPath(payload_id), total_size);
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<OutputFile> ImplementationPlatform::CreateOutputFile(PayloadId payload_id) {
|
||||
return absl::make_unique<shared::OutputFile>(GetPayloadPath(payload_id));
|
||||
}
|
||||
|
||||
std::unique_ptr<LogMessage> ImplementationPlatform::CreateLogMessage(
|
||||
const char* file, int line, LogMessage::Severity severity) {
|
||||
return absl::make_unique<ios::LogMessage>(file, line, severity);
|
||||
}
|
||||
|
||||
// Java-like Executors
|
||||
std::unique_ptr<SubmittableExecutor> ImplementationPlatform::CreateSingleThreadExecutor() {
|
||||
return std::make_unique<ios::SingleThreadExecutor>();
|
||||
}
|
||||
|
||||
std::unique_ptr<SubmittableExecutor> ImplementationPlatform::CreateMultiThreadExecutor(
|
||||
int max_concurrency) {
|
||||
return std::make_unique<ios::MultiThreadExecutor>(max_concurrency);
|
||||
}
|
||||
|
||||
std::unique_ptr<ScheduledExecutor> ImplementationPlatform::CreateScheduledExecutor() {
|
||||
return std::make_unique<ios::ScheduledExecutor>();
|
||||
}
|
||||
|
||||
// Mediums
|
||||
std::unique_ptr<BluetoothAdapter> ImplementationPlatform::CreateBluetoothAdapter() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<BluetoothClassicMedium> ImplementationPlatform::CreateBluetoothClassicMedium(
|
||||
api::BluetoothAdapter& adapter) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<BleMedium> ImplementationPlatform::CreateBleMedium(api::BluetoothAdapter& adapter) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<ble_v2::BleMedium> ImplementationPlatform::CreateBleV2Medium(
|
||||
api::BluetoothAdapter& adapter) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<ServerSyncMedium> ImplementationPlatform::CreateServerSyncMedium() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<WifiMedium> ImplementationPlatform::CreateWifiMedium() { return nullptr; }
|
||||
|
||||
std::unique_ptr<WifiLanMedium> ImplementationPlatform::CreateWifiLanMedium() {
|
||||
return std::make_unique<ios::WifiLanMedium>();
|
||||
}
|
||||
|
||||
std::unique_ptr<WebRtcMedium> ImplementationPlatform::CreateWebRtcMedium() { return nullptr; }
|
||||
|
||||
} // namespace api
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -0,0 +1,56 @@
|
||||
load("//tools/build_defs/apple:objc.bzl", "objc_proto_library")
|
||||
|
||||
# Copyright 2020 Google LLC
|
||||
#
|
||||
# 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
|
||||
#
|
||||
# https://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.
|
||||
licenses(["notice"])
|
||||
|
||||
package(default_visibility = ["//internal/platform/implementation/ios:__subpackages__"])
|
||||
|
||||
objc_library(
|
||||
name = "Mediums",
|
||||
srcs = [
|
||||
"GNCLeaks.m",
|
||||
"GNCMConnection.m",
|
||||
"WifiLan/GNCMBonjourBrowser.m",
|
||||
"WifiLan/GNCMBonjourConnection.m",
|
||||
"WifiLan/GNCMBonjourService.m",
|
||||
"WifiLan/GNCMBonjourUtils.m",
|
||||
],
|
||||
hdrs = [
|
||||
"GNCLeaks.h",
|
||||
"GNCMConnection.h",
|
||||
"WifiLan/GNCMBonjourBrowser.h",
|
||||
"WifiLan/GNCMBonjourConnection.h",
|
||||
"WifiLan/GNCMBonjourService.h",
|
||||
"WifiLan/GNCMBonjourUtils.h",
|
||||
],
|
||||
deps = [
|
||||
":ObjCProtos",
|
||||
"//internal/platform/implementation/ios/Source/Shared",
|
||||
"@com_google_absl//absl/numeric:int128",
|
||||
"//third_party/objective_c/google_toolbox_for_mac:GTM_Logger",
|
||||
],
|
||||
)
|
||||
|
||||
objc_proto_library(
|
||||
name = "ObjCProtos",
|
||||
deps = [":Protos"],
|
||||
)
|
||||
|
||||
proto_library(
|
||||
name = "Protos",
|
||||
deps = [
|
||||
"//connections/implementation/proto:offline_wire_formats_proto",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
// Verifies that an object has been deallocated after the given time period.
|
||||
void GNCVerifyDealloc(id object, NSTimeInterval timeInterval);
|
||||
@@ -0,0 +1,27 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#import "internal/platform/implementation/ios/Source/Mediums/GNCLeaks.h"
|
||||
|
||||
void GNCVerifyDealloc(id object, NSTimeInterval timeInterval) {
|
||||
#if DEBUG
|
||||
__weak id weakObj = object;
|
||||
NSCAssert(weakObj != nil, @"Pointer to %@ is already nil", weakObj);
|
||||
NSLog(@"Verifying deallocation of %@", NSStringFromClass([weakObj class]));
|
||||
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeInterval * NSEC_PER_SEC)),
|
||||
dispatch_get_main_queue(), ^{
|
||||
NSCAssert(weakObj == nil, @"%@ not deallocated.", weakObj);
|
||||
});
|
||||
#endif
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/** Result of a medium payload transfer. */
|
||||
typedef NS_ENUM(NSInteger, GNCMPayloadResult) {
|
||||
GNCMPayloadResultSuccess, // Payload delivery was successful.
|
||||
GNCMPayloadResultFailure, // An error occurred during payload delivery.
|
||||
GNCMPayloadResultCanceled, // Payload delivery was canceled.
|
||||
};
|
||||
|
||||
/** Handler for a @c GNCMPayloadResult value. */
|
||||
typedef void (^GNCMPayloadResultHandler)(GNCMPayloadResult);
|
||||
|
||||
/**
|
||||
* A progress handler is periodically called during payload delivery. It is passed a value
|
||||
* ranging from 0 (when the operation has just started) to the total size (when the operation is
|
||||
* finished).
|
||||
*/
|
||||
typedef void (^GNCMProgressHandler)(size_t count);
|
||||
|
||||
/** This handler is called when data is received from a remote endpoint. */
|
||||
typedef void (^GNCMPayloadHandler)(NSData *data);
|
||||
|
||||
/**
|
||||
* This represents a connection with a remote endpoint at the medium level. Use it to send
|
||||
* payloads to the remote endpoint, and release it to disconnect.
|
||||
*/
|
||||
@protocol GNCMConnection <NSObject>
|
||||
|
||||
/**
|
||||
* Sends data to the remote endpoint. Wait for the completion to be called before sending another
|
||||
* payload.
|
||||
*
|
||||
* @param payload The data to send.
|
||||
* @param progressHandler Called repeatedly for progress feedback while the data is being sent.
|
||||
* @param completion Callback called when the data has been fully sent,
|
||||
* or it has failed to be sent (not connected or disconnected).
|
||||
*/
|
||||
- (void)sendData:(NSData *)payload
|
||||
progressHandler:(GNCMProgressHandler)progressHandler
|
||||
completion:(GNCMPayloadResultHandler)completion;
|
||||
|
||||
@end
|
||||
|
||||
/** This class contains optional handlers for a connection. */
|
||||
@interface GNCMConnectionHandlers : NSObject
|
||||
|
||||
/** This handler is called when data is sent from the remote endpoint. */
|
||||
@property(nonatomic) GNCMPayloadHandler payloadHandler;
|
||||
|
||||
/** This handler is called when the connection is ended. */
|
||||
@property(nonatomic) dispatch_block_t disconnectedHandler;
|
||||
|
||||
/** This method creates a GNCMConnectionHandlers object from payload and disconnect handlers. */
|
||||
+ (instancetype)payloadHandler:(GNCMPayloadHandler)payloadHandler
|
||||
disconnectedHandler:(dispatch_block_t)disconnectedHandler;
|
||||
|
||||
@end
|
||||
|
||||
/**
|
||||
* This handler takes a GNCMConnection object and returns a GNCMConnectionHandlers object. It is
|
||||
* called when a connection is successfully made with a remote endpoint. If |connection| is nil,
|
||||
* the connection couldn't be established; in this case, return nil.
|
||||
*/
|
||||
typedef GNCMConnectionHandlers *_Nullable (^GNCMConnectionHandler)(
|
||||
id<GNCMConnection> __nullable connection);
|
||||
|
||||
/**
|
||||
* This handler is called by a discovering endpoint to request a connection with an an advertising
|
||||
* endpoint.
|
||||
*/
|
||||
typedef void (^GNCMConnectionRequester)(GNCMConnectionHandler connectionHandler);
|
||||
|
||||
/** This handler is called when a previously discovered advertising endpoint is lost. */
|
||||
typedef void (^GNCMEndpointLostHandler)(void);
|
||||
|
||||
/**
|
||||
* This handler is called on a discoverer when a nearby advertising endpoint is
|
||||
* discovered. Calls |requestConnection| to request a connection with the advertiser.
|
||||
*/
|
||||
typedef GNCMEndpointLostHandler _Nonnull (^GNCMEndpointFoundHandler)(
|
||||
NSString *endpointId, NSString *serviceType, NSString *serviceName,
|
||||
NSDictionary<NSString *, NSData *> *_Nullable TXTRecordData,
|
||||
GNCMConnectionRequester requestConnection);
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#import "internal/platform/implementation/ios/Source/Mediums/GNCMConnection.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@implementation GNCMConnectionHandlers
|
||||
|
||||
+ (instancetype)payloadHandler:(GNCMPayloadHandler)payloadHandler
|
||||
disconnectedHandler:(dispatch_block_t)disconnectedHandler {
|
||||
GNCMConnectionHandlers *handlers = [[GNCMConnectionHandlers alloc] init];
|
||||
handlers.payloadHandler = payloadHandler;
|
||||
handlers.disconnectedHandler = disconnectedHandler;
|
||||
return handlers;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,42 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "internal/platform/implementation/ios/Source/Mediums/GNCMConnection.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* GNCMBonjourBrowser browses mDNS services publishing the specified mDNS type and domain. The mDNS
|
||||
* type is a string formatted as "_[serviceIdHash]._tcp." in which [serviceIdHash] is generated as
|
||||
* a SHA-256 hash from the service ID and taken the 6 first bytes of string in upper case.
|
||||
* Calls the specififed endpoint found handler when one is found. The endpoint found handler
|
||||
* supplies a requester block, which can be called to establish a socket to the service found.
|
||||
*
|
||||
* Don't hold the strong reference of caller self in endpointFoundHandler to ensure there is no
|
||||
* retain cycle between them.
|
||||
*
|
||||
* @param serviceType An mDNS type that uniquely identifies the published service to search for.
|
||||
* @param endpointFoundHandler The handler that is called when an endpoint publishing the service
|
||||
* ID is discovered.
|
||||
*/
|
||||
@interface GNCMBonjourBrowser : NSObject
|
||||
|
||||
- (instancetype)initWithServiceType:(NSString *)serviceType
|
||||
endpointFoundHandler:(GNCMEndpointFoundHandler)handler;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,176 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#import "internal/platform/implementation/ios/Source/Mediums/WifiLan/GNCMBonjourBrowser.h"
|
||||
|
||||
#import "internal/platform/implementation/ios/Source/Mediums/GNCMConnection.h"
|
||||
#import "internal/platform/implementation/ios/Source/Mediums/WifiLan/GNCMBonjourConnection.h"
|
||||
#import "internal/platform/implementation/ios/Source/Mediums/WifiLan/GNCMBonjourUtils.h"
|
||||
#import "GoogleToolboxForMac/GTMLogger.h"
|
||||
|
||||
typedef NSString *GNCEndpointId;
|
||||
|
||||
@interface GNCMNetServiceInfo : NSObject
|
||||
@property(nonatomic) NSNetService *service;
|
||||
@property(nonatomic, copy) GNCMEndpointLostHandler endpointLostHandler;
|
||||
@property(nonatomic, copy, nullable) GNCMConnectionHandler connectionHandler;
|
||||
@end
|
||||
|
||||
@implementation GNCMNetServiceInfo
|
||||
|
||||
+ (instancetype)infoWithService:(NSNetService *)service {
|
||||
GNCMNetServiceInfo *info = [[GNCMNetServiceInfo alloc] init];
|
||||
info.service = service;
|
||||
return info;
|
||||
}
|
||||
|
||||
- (BOOL)isEqual:(GNCMNetServiceInfo *)object {
|
||||
return [_service isEqual:object.service];
|
||||
}
|
||||
|
||||
- (NSUInteger)hash {
|
||||
return [_service hash];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface GNCMBonjourBrowser () <NSNetServiceBrowserDelegate, NSNetServiceDelegate>
|
||||
|
||||
@property(nonatomic, copy) GNCMEndpointFoundHandler endpointFoundHandler;
|
||||
|
||||
@property(nonatomic) NSNetServiceBrowser *netBrowser;
|
||||
@property(nonatomic) NSMutableDictionary<GNCEndpointId, GNCMNetServiceInfo *> *endpoints;
|
||||
|
||||
@end
|
||||
|
||||
@implementation GNCMBonjourBrowser
|
||||
|
||||
- (instancetype)initWithServiceType:(NSString *)serviceType
|
||||
endpointFoundHandler:(GNCMEndpointFoundHandler)handler {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_endpointFoundHandler = handler;
|
||||
|
||||
_netBrowser = [[NSNetServiceBrowser alloc] init];
|
||||
_netBrowser.delegate = self;
|
||||
[_netBrowser scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
|
||||
[_netBrowser searchForServicesOfType:serviceType inDomain:GNCMBonjourDomain];
|
||||
_endpoints = [NSMutableDictionary dictionary];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark NSNetServiceBrowserDelegate
|
||||
|
||||
- (void)netServiceBrowserWillSearch:(NSNetServiceBrowser *)browser {
|
||||
GTMLoggerDebug(@"Browsing");
|
||||
}
|
||||
|
||||
- (void)netServiceBrowserDidStopSearch:(NSNetServiceBrowser *)browser {
|
||||
GTMLoggerDebug(@"Stop browsing");
|
||||
}
|
||||
|
||||
- (void)netServiceBrowser:(NSNetServiceBrowser *)browser
|
||||
didNotSearch:(NSDictionary<NSString *, NSNumber *> *)errorDict {
|
||||
GTMLoggerDebug(@"Not browsing with errorDict: %@", errorDict);
|
||||
}
|
||||
|
||||
- (void)netServiceBrowser:(NSNetServiceBrowser *)browser
|
||||
didFindService:(NSNetService *)service
|
||||
moreComing:(BOOL)moreComing {
|
||||
GTMLoggerDebug(@"Found service: %@", service);
|
||||
|
||||
GNCMNetServiceInfo *info = [GNCMNetServiceInfo infoWithService:service];
|
||||
|
||||
// Just to be safe, check if the service is already known and deal with it accordingly.
|
||||
NSArray<GNCEndpointId> *endpointIds = [_endpoints allKeysForObject:info];
|
||||
if (endpointIds.count > 0) return;
|
||||
|
||||
// Add the newly discovered service to the list of services.
|
||||
GNCEndpointId endpointId = [[NSUUID UUID] UUIDString];
|
||||
_endpoints[endpointId] = info;
|
||||
|
||||
service.delegate = self;
|
||||
[service resolveWithTimeout:0];
|
||||
}
|
||||
|
||||
- (void)netServiceBrowser:(NSNetServiceBrowser *)browser
|
||||
didRemoveService:(NSNetService *)service
|
||||
moreComing:(BOOL)moreComing {
|
||||
GTMLoggerDebug(@"Lost service: %@", service);
|
||||
NSArray<GNCEndpointId> *endpointIds =
|
||||
[_endpoints allKeysForObject:[GNCMNetServiceInfo infoWithService:service]];
|
||||
NSAssert(endpointIds.count <= 1, @"Unexpected duplicate service");
|
||||
if (endpointIds.count > 0) {
|
||||
GNCEndpointId endpointId = endpointIds[0];
|
||||
GNCMNetServiceInfo *info = _endpoints[endpointId];
|
||||
[_endpoints removeObjectForKey:endpointId];
|
||||
// Tail call to preserve reentrancy.
|
||||
if (info.endpointLostHandler) {
|
||||
info.endpointLostHandler();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark NSNetServiceDelegate
|
||||
|
||||
- (void)netServiceDidResolveAddress:(NSNetService *)service {
|
||||
GTMLoggerDebug(@"Resolved service: %@ addresses: %@", service, service.addresses);
|
||||
|
||||
GNCMNetServiceInfo *info = [GNCMNetServiceInfo infoWithService:service];
|
||||
|
||||
NSArray<GNCEndpointId> *endpointIds = [_endpoints allKeysForObject:info];
|
||||
if (endpointIds.count > 0) {
|
||||
// Get TXTRecord data.
|
||||
NSData *data = [service TXTRecordData];
|
||||
NSDictionary<NSString *, NSData *> *TXTRecordData =
|
||||
[NSNetService dictionaryFromTXTRecordData:data];
|
||||
|
||||
// The endpointLostHandler is returned from the endpointFoundHandler. The main benefit of this
|
||||
// is that it allows rejection from the remote endpoint to be received by the local endpoint
|
||||
// before the local endpoint has accepted or rejected.
|
||||
info.endpointLostHandler = _endpointFoundHandler(
|
||||
endpointIds[0], service.type, service.name, TXTRecordData,
|
||||
^(GNCMConnectionHandler connectionHandler) {
|
||||
// A connection is requested, so resolve the service to get the I/O streams.
|
||||
info.connectionHandler = connectionHandler;
|
||||
if (info.connectionHandler) {
|
||||
dispatch_sync(dispatch_get_main_queue(), ^{
|
||||
NSInputStream *inputStream;
|
||||
NSOutputStream *outputStream;
|
||||
[info.service scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
|
||||
[info.service getInputStream:&inputStream outputStream:&outputStream];
|
||||
GNCMBonjourConnection *connection =
|
||||
[[GNCMBonjourConnection alloc] initWithInputStream:inputStream
|
||||
outputStream:outputStream
|
||||
queue:nil];
|
||||
connection.connectionHandlers = info.connectionHandler(connection);
|
||||
});
|
||||
}
|
||||
});
|
||||
_endpoints[endpointIds[0]] = info;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)netService:(NSNetService *)service
|
||||
didNotResolve:(NSDictionary<NSString *, NSNumber *> *)errorDict {
|
||||
GTMLoggerDebug(@"Did not resolve service: %@", service);
|
||||
NSArray<GNCEndpointId> *endpointIds =
|
||||
[_endpoints allKeysForObject:[GNCMNetServiceInfo infoWithService:service]];
|
||||
if (endpointIds.count > 0) {
|
||||
_endpoints[endpointIds[0]].connectionHandler = nil;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "internal/platform/implementation/ios/Source/Mediums/GNCMConnection.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* This medium connection sends and receives payloads over the NSInputStream and NSOutputStream
|
||||
* passed to it.
|
||||
*
|
||||
* @param inputStream The input stream to read from.
|
||||
* @param outputStream The output stream to write to.
|
||||
* @param queue The queue on which the GNCMConnection callbacks will be called. If nil, the main
|
||||
* queue is used.
|
||||
*/
|
||||
@interface GNCMBonjourConnection : NSObject <GNCMConnection>
|
||||
@property(nonatomic) GNCMConnectionHandlers *connectionHandlers;
|
||||
|
||||
- (instancetype)initWithInputStream:(NSInputStream *)inputStream
|
||||
outputStream:(NSOutputStream *)outputStream
|
||||
queue:(nullable dispatch_queue_t)queue;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,224 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#import "internal/platform/implementation/ios/Source/Mediums/WifiLan/GNCMBonjourConnection.h"
|
||||
|
||||
#import "internal/platform/implementation/ios/Source/Mediums/GNCMConnection.h"
|
||||
#import "GoogleToolboxForMac/GTMLogger.h"
|
||||
|
||||
enum { kMaxPacketSize = 32 * 1024 };
|
||||
|
||||
@interface GNCMBonjourConnection () <NSStreamDelegate>
|
||||
@property(nonatomic) NSInputStream *inputStream;
|
||||
@property(nonatomic) NSOutputStream *outputStream;
|
||||
@property(nonatomic) dispatch_queue_t callbackQueue;
|
||||
|
||||
@property(nonatomic, copy, nullable) NSData *dataBeingWritten;
|
||||
@property(nonatomic) NSInteger numberOfBytesLeftToWrite;
|
||||
@property(nonatomic, copy, nullable) GNCMProgressHandler progressHandler;
|
||||
@property(nonatomic, copy, nullable) GNCMPayloadResultHandler completion;
|
||||
|
||||
@property(nonatomic) BOOL inputStreamOpen;
|
||||
@property(nonatomic) BOOL outputStreamOpen;
|
||||
@end
|
||||
|
||||
@implementation GNCMBonjourConnection
|
||||
|
||||
- (instancetype)initWithInputStream:(NSInputStream *)inputStream
|
||||
outputStream:(NSOutputStream *)outputStream
|
||||
queue:(nullable dispatch_queue_t)queue {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_inputStreamOpen = NO;
|
||||
_outputStreamOpen = NO;
|
||||
|
||||
_inputStream = inputStream;
|
||||
_inputStream.delegate = self;
|
||||
[_inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
|
||||
[_inputStream open];
|
||||
|
||||
_outputStream = outputStream;
|
||||
_outputStream.delegate = self;
|
||||
[_outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
|
||||
[_outputStream open];
|
||||
|
||||
_callbackQueue = queue;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[self closeStreams];
|
||||
}
|
||||
|
||||
- (void)sendData:(NSData *)payload
|
||||
progressHandler:(GNCMProgressHandler)progressHandler
|
||||
completion:(GNCMPayloadResultHandler)completion {
|
||||
if (_dataBeingWritten) {
|
||||
GTMLoggerInfo(@"Attempting to send payload while one is already in flight");
|
||||
[self dispatchCallback:^{
|
||||
completion(GNCMPayloadResultFailure);
|
||||
}];
|
||||
return;
|
||||
}
|
||||
|
||||
self.dataBeingWritten = payload;
|
||||
self.numberOfBytesLeftToWrite = payload.length;
|
||||
self.progressHandler = progressHandler;
|
||||
self.completion = completion;
|
||||
[self writeChunk];
|
||||
}
|
||||
|
||||
#pragma mark NSStreamDelegate
|
||||
|
||||
- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)event {
|
||||
switch (event) {
|
||||
case NSStreamEventHasBytesAvailable: {
|
||||
// Data has arrived on the input stream.
|
||||
NSAssert(stream == _inputStream, @"Error: Expected input stream");
|
||||
|
||||
if (_connectionHandlers.payloadHandler) {
|
||||
uint8_t bytesRead[kMaxPacketSize];
|
||||
NSInteger numberOfBytesRead;
|
||||
@synchronized(self.inputStream) {
|
||||
numberOfBytesRead = [self.inputStream read:bytesRead maxLength:kMaxPacketSize];
|
||||
}
|
||||
NSData *data = nil;
|
||||
if (numberOfBytesRead > 0) {
|
||||
GTMLoggerInfo(@"Read %lu bytes", (u_long)numberOfBytesRead);
|
||||
data = [NSData dataWithBytes:bytesRead length:numberOfBytesRead];
|
||||
}
|
||||
[self dispatchCallback:^{
|
||||
self.connectionHandlers.payloadHandler(data ?: [NSData data]);
|
||||
}];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case NSStreamEventHasSpaceAvailable:
|
||||
// There is space available on the output stream.
|
||||
NSAssert(stream == _outputStream, @"Error: Expected output stream");
|
||||
|
||||
// Schedule this in a future runloop cycle because -writeChunk, which can cause this event
|
||||
// to be received synchronously, is not reentrant.
|
||||
[self performSelector:@selector(writeChunk) withObject:nil afterDelay:0.0];
|
||||
break;
|
||||
|
||||
case NSStreamEventErrorOccurred:
|
||||
GTMLoggerInfo(@"Stream error: %@", [stream streamError]);
|
||||
// Fall through.
|
||||
case NSStreamEventEndEncountered: {
|
||||
GTMLoggerInfo(@"Stream closing");
|
||||
[self closeStreams];
|
||||
if (_connectionHandlers.disconnectedHandler) {
|
||||
[self dispatchCallback:^{
|
||||
self.connectionHandlers.disconnectedHandler();
|
||||
}];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case NSStreamEventOpenCompleted:
|
||||
if (stream == _inputStream) {
|
||||
_inputStreamOpen = YES;
|
||||
}
|
||||
if (stream == _outputStream) {
|
||||
_outputStreamOpen = YES;
|
||||
}
|
||||
// Schedule this in a future runloop cycle because -writeChunk, which can cause this event
|
||||
// to be received synchronously, is not reentrant.
|
||||
[self performSelector:@selector(writeChunk) withObject:nil afterDelay:0.0];
|
||||
break;
|
||||
|
||||
case NSStreamEventNone:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark Private
|
||||
|
||||
// Calls a block on the callback queue.
|
||||
- (void)dispatchCallback:(dispatch_block_t)block {
|
||||
dispatch_async(_callbackQueue ?: dispatch_get_main_queue(), block);
|
||||
}
|
||||
|
||||
// Writes a chunk of the outgoing data to the output stream, calling the progress and completion
|
||||
// handlers as needed.
|
||||
- (void)writeChunk {
|
||||
void (^reportProgress)(size_t) = ^(size_t count) {
|
||||
// Captures the progress handler because the property is nilled out below.
|
||||
GNCMProgressHandler progressHandler = _progressHandler;
|
||||
if (progressHandler != nil) {
|
||||
[self dispatchCallback:^{
|
||||
progressHandler(count);
|
||||
}];
|
||||
}
|
||||
};
|
||||
|
||||
void (^completed)(GNCMPayloadResult) = ^(GNCMPayloadResult result) {
|
||||
reportProgress(_dataBeingWritten.length);
|
||||
_progressHandler = nil;
|
||||
_dataBeingWritten = nil;
|
||||
|
||||
// Captures the completion because the property is nilled out below.
|
||||
GNCMPayloadResultHandler completion = _completion;
|
||||
if (completion != nil) {
|
||||
[self dispatchCallback:^{
|
||||
completion(result);
|
||||
}];
|
||||
}
|
||||
_completion = nil;
|
||||
};
|
||||
|
||||
@synchronized(_outputStream) {
|
||||
if (_inputStreamOpen && _outputStreamOpen && _numberOfBytesLeftToWrite) {
|
||||
NSUInteger dataLength = (UInt32)_dataBeingWritten.length;
|
||||
if (_numberOfBytesLeftToWrite == dataLength) {
|
||||
GTMLoggerInfo(@"Starting a write operation of length %lu", (u_long)dataLength);
|
||||
}
|
||||
NSInteger numberOfPayloadBytesWritten =
|
||||
[_outputStream write:&_dataBeingWritten.bytes[dataLength - _numberOfBytesLeftToWrite]
|
||||
maxLength:_numberOfBytesLeftToWrite];
|
||||
|
||||
GTMLoggerInfo(@"Wrote %lu bytes", (u_long)numberOfPayloadBytesWritten);
|
||||
if (numberOfPayloadBytesWritten >= 0) {
|
||||
_numberOfBytesLeftToWrite -= numberOfPayloadBytesWritten;
|
||||
reportProgress(_dataBeingWritten.length - _numberOfBytesLeftToWrite);
|
||||
if (_numberOfBytesLeftToWrite < 0) {
|
||||
GTMLoggerInfo(@"Unexpected number of bytes written");
|
||||
_numberOfBytesLeftToWrite = 0;
|
||||
}
|
||||
if (_numberOfBytesLeftToWrite == 0) completed(GNCMPayloadResultSuccess);
|
||||
} else {
|
||||
GTMLoggerInfo(@"Error writing to output stream");
|
||||
completed(GNCMPayloadResultFailure);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)closeStreams {
|
||||
@synchronized(_inputStream) {
|
||||
[_inputStream close];
|
||||
_inputStream = nil;
|
||||
}
|
||||
|
||||
@synchronized(_outputStream) {
|
||||
[_outputStream close];
|
||||
_outputStream = nil;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,48 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "internal/platform/implementation/ios/Source/Mediums/GNCMConnection.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
* GNCMBonjourService publishes mDNS type and domain with the specified service ID via Apple
|
||||
* Bonjour service. The mDNS type is a string formatted as "_[serviceIdHash]._tcp." in which
|
||||
* [serviceIdHash] is generated as a SHA-256 hash from the service ID and taken the 6 first bytes
|
||||
* of string in upper case.
|
||||
* When the service connects, the specified |endpointConnectedHandler| is called, which establishes
|
||||
* a connection to the browser.
|
||||
*
|
||||
* Don't hold the strong reference of caller self in endpointConnectedHandler to ensure there is no
|
||||
* retain cycle between them.
|
||||
*
|
||||
* @param serviceName A service name that embeds the |WifiLanServiceInfo| information.
|
||||
* @param serviceType An mDNS type that uniquely identifies the published service to search for.
|
||||
* @param port The requesting socket port number.
|
||||
* @param txtRecordData The TXTRecord data.
|
||||
* @param endpointConnectedHandler The handler that is called when a browser connects.
|
||||
*/
|
||||
@interface GNCMBonjourService : NSObject
|
||||
|
||||
- (instancetype)initWithServiceName:(NSString *)serviceName
|
||||
serviceType:(NSString *)serviceType
|
||||
port:(NSInteger)port
|
||||
TXTRecordData:(NSDictionary<NSString *, NSData *> *)TXTRecordData
|
||||
endpointConnectedHandler:(GNCMConnectionHandler)handler;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
@@ -0,0 +1,88 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#import "internal/platform/implementation/ios/Source/Mediums/WifiLan/GNCMBonjourService.h"
|
||||
|
||||
#import "internal/platform/implementation/ios/Source/Mediums/GNCMConnection.h"
|
||||
#import "internal/platform/implementation/ios/Source/Mediums/WifiLan/GNCMBonjourConnection.h"
|
||||
#import "internal/platform/implementation/ios/Source/Mediums/WifiLan/GNCMBonjourUtils.h"
|
||||
#import "GoogleToolboxForMac/GTMLogger.h"
|
||||
|
||||
@interface GNCMBonjourService () <NSNetServiceDelegate>
|
||||
|
||||
@property(nonatomic, copy) NSString *serviceName;
|
||||
@property(nonatomic, copy) NSString *serviceType;
|
||||
@property(nonatomic, copy) GNCMConnectionHandler endpointConnectedHandler;
|
||||
|
||||
@property(nonatomic) NSNetService *netService;
|
||||
|
||||
@end
|
||||
|
||||
@implementation GNCMBonjourService
|
||||
|
||||
- (instancetype)initWithServiceName:(NSString *)serviceName
|
||||
serviceType:(NSString *)serviceType
|
||||
port:(NSInteger)port
|
||||
TXTRecordData:(NSDictionary<NSString *, NSData *> *)TXTRecordData
|
||||
endpointConnectedHandler:(GNCMConnectionHandler)handler {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_serviceName = [serviceName copy];
|
||||
_serviceType = [serviceType copy];
|
||||
_endpointConnectedHandler = handler;
|
||||
|
||||
_netService = [[NSNetService alloc] initWithDomain:GNCMBonjourDomain
|
||||
type:_serviceType
|
||||
name:_serviceName
|
||||
port:port];
|
||||
[_netService setTXTRecordData:[NSNetService dataFromTXTRecordDictionary:TXTRecordData]];
|
||||
|
||||
_netService.delegate = self;
|
||||
[_netService scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
|
||||
[_netService publishWithOptions:NSNetServiceListenForConnections];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[_netService stop];
|
||||
}
|
||||
|
||||
#pragma mark NSNetServiceDelegate
|
||||
|
||||
- (void)netServiceDidPublish:(NSNetService *)service {
|
||||
GTMLoggerDebug(@"Did publish service: %@", service);
|
||||
}
|
||||
|
||||
- (void)netService:(NSNetService *)service
|
||||
didNotPublish:(NSDictionary<NSString *, NSNumber *> *)errorDict {
|
||||
GTMLoggerDebug(@"Error publishing: service: %@, errorDic: %@", service, errorDict);
|
||||
}
|
||||
|
||||
- (void)netServiceDidStop:(NSNetService *)service {
|
||||
GTMLoggerDebug(@"Stopped publishing service: %@", service);
|
||||
}
|
||||
|
||||
- (void)netService:(NSNetService *)service
|
||||
didAcceptConnectionWithInputStream:(NSInputStream *)inputStream
|
||||
outputStream:(NSOutputStream *)outputStream {
|
||||
GTMLoggerDebug(@"Accepted connection, service: %@", service);
|
||||
GNCMBonjourConnection *connection =
|
||||
[[GNCMBonjourConnection alloc] initWithInputStream:inputStream
|
||||
outputStream:outputStream
|
||||
queue:nil];
|
||||
connection.connectionHandlers = _endpointConnectedHandler(connection);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,18 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
// mDNS domain.
|
||||
FOUNDATION_EXPORT NSString *_Nonnull const GNCMBonjourDomain;
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#import "internal/platform/implementation/ios/Source/Mediums/WifiLan/GNCMBonjourUtils.h"
|
||||
|
||||
NSString *const GNCMBonjourDomain = @"local";
|
||||
@@ -0,0 +1,93 @@
|
||||
# Copyright 2020 Google LLC
|
||||
#
|
||||
# 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
|
||||
#
|
||||
# https://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.
|
||||
licenses(["notice"])
|
||||
|
||||
package(default_visibility = ["//internal/platform/implementation/ios:__subpackages__"])
|
||||
|
||||
objc_library(
|
||||
name = "Platform",
|
||||
srcs = [
|
||||
"crypto.mm",
|
||||
"input_file.mm",
|
||||
"log_message.mm",
|
||||
"multi_thread_executor.mm",
|
||||
"scheduled_executor.mm",
|
||||
"utils.mm",
|
||||
"wifi_lan.mm",
|
||||
],
|
||||
hdrs = [
|
||||
"input_file.h",
|
||||
"log_message.h",
|
||||
"multi_thread_executor.h",
|
||||
"scheduled_executor.h",
|
||||
"single_thread_executor.h",
|
||||
"utils.h",
|
||||
"wifi_lan.h",
|
||||
],
|
||||
sdk_frameworks = [
|
||||
"CoreBluetooth",
|
||||
"CoreFoundation",
|
||||
],
|
||||
deps = [
|
||||
":Platform_cc",
|
||||
"//internal/platform/implementation:platform",
|
||||
"//internal/platform/implementation:types",
|
||||
"//internal/platform/implementation/ios/Source/Mediums",
|
||||
"//internal/platform/implementation/ios/Source/Shared",
|
||||
"//internal/platform/implementation/shared:file",
|
||||
"//third_party/objective_c/google_toolbox_for_mac:GTM_Logger",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "Platform_cc",
|
||||
srcs = [
|
||||
"condition_variable.cc",
|
||||
"count_down_latch.cc",
|
||||
"system_clock.cc",
|
||||
],
|
||||
hdrs = [
|
||||
"atomic_boolean.h",
|
||||
"atomic_uint32.h",
|
||||
"condition_variable.h",
|
||||
"count_down_latch.h",
|
||||
"mutex.h",
|
||||
],
|
||||
deps = [
|
||||
"//internal/platform/implementation:platform",
|
||||
"//internal/platform/implementation:types",
|
||||
"@com_google_absl//absl/strings:str_format",
|
||||
"@com_google_absl//absl/synchronization",
|
||||
"@com_google_absl//absl/time",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "Platform_cc_test",
|
||||
srcs = [
|
||||
"atomic_boolean_test.cc",
|
||||
"atomic_uint32_test.cc",
|
||||
"condition_variable_test.cc",
|
||||
"count_down_latch_test.cc",
|
||||
"mutex_test.cc",
|
||||
],
|
||||
shard_count = 16,
|
||||
deps = [
|
||||
":Platform_cc",
|
||||
"@com_google_googletest//:gtest_main","@com_github_protobuf_matchers//protobuf-matchers:protobuf-matchers",
|
||||
"@com_google_absl//absl/synchronization",
|
||||
"@com_google_absl//absl/time",
|
||||
"@com_google_nisaba//nisaba/port:thread_pool/fiber",
|
||||
],
|
||||
)
|
||||
@@ -0,0 +1,46 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#ifndef PLATFORM_IMPL_IOS_ATOMIC_BOOLEAN_H_
|
||||
#define PLATFORM_IMPL_IOS_ATOMIC_BOOLEAN_H_
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "internal/platform/implementation/atomic_boolean.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace ios {
|
||||
|
||||
// Concrete AtomicBoolean implementation.
|
||||
class AtomicBoolean : public api::AtomicBoolean {
|
||||
public:
|
||||
explicit AtomicBoolean(bool initial_value) : value_(initial_value) {}
|
||||
~AtomicBoolean() override = default;
|
||||
|
||||
AtomicBoolean(const AtomicBoolean&) = delete;
|
||||
AtomicBoolean& operator=(const AtomicBoolean&) = delete;
|
||||
|
||||
bool Get() const override { return value_.load(); }
|
||||
bool Set(bool value) override { return value_.exchange(value); }
|
||||
|
||||
private:
|
||||
std::atomic_bool value_;
|
||||
};
|
||||
|
||||
} // namespace ios
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // PLATFORM_IMPL_IOS_ATOMIC_BOOLEAN_H_
|
||||
@@ -0,0 +1,78 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#include "internal/platform/implementation/ios/Source/Platform/atomic_boolean.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "thread/fiber/fiber.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace ios {
|
||||
namespace {
|
||||
|
||||
TEST(AtomicBooleanTest, SetOnSameThread) {
|
||||
AtomicBoolean atomic_boolean_{false};
|
||||
|
||||
EXPECT_EQ(false, atomic_boolean_.Get());
|
||||
|
||||
atomic_boolean_.Set(true);
|
||||
EXPECT_EQ(true, atomic_boolean_.Get());
|
||||
}
|
||||
|
||||
TEST(AtomicBooleanTest, MultipleSetGetOnSameThread) {
|
||||
AtomicBoolean atomic_boolean_{false};
|
||||
|
||||
EXPECT_EQ(false, atomic_boolean_.Get());
|
||||
|
||||
atomic_boolean_.Set(true);
|
||||
EXPECT_EQ(true, atomic_boolean_.Get());
|
||||
|
||||
atomic_boolean_.Set(true);
|
||||
EXPECT_EQ(true, atomic_boolean_.Get());
|
||||
|
||||
atomic_boolean_.Set(false);
|
||||
EXPECT_EQ(false, atomic_boolean_.Get());
|
||||
|
||||
atomic_boolean_.Set(true);
|
||||
EXPECT_EQ(true, atomic_boolean_.Get());
|
||||
}
|
||||
|
||||
TEST(AtomicBooleanTest, SetOnNewThread) {
|
||||
AtomicBoolean atomic_boolean_{false};
|
||||
|
||||
EXPECT_EQ(false, atomic_boolean_.Get());
|
||||
|
||||
thread::Fiber f([&] { atomic_boolean_.Set(true); });
|
||||
f.Join();
|
||||
|
||||
EXPECT_EQ(true, atomic_boolean_.Get());
|
||||
}
|
||||
|
||||
TEST(AtomicBooleanTest, GetOnNewThread) {
|
||||
AtomicBoolean atomic_boolean_{false};
|
||||
|
||||
EXPECT_EQ(false, atomic_boolean_.Get());
|
||||
|
||||
atomic_boolean_.Set(true);
|
||||
EXPECT_EQ(true, atomic_boolean_.Get());
|
||||
|
||||
thread::Fiber f([&] { EXPECT_EQ(true, atomic_boolean_.Get()); });
|
||||
f.Join();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace ios
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -0,0 +1,47 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#ifndef PLATFORM_IMPL_IOS_ATOMIC_UINT32_H_
|
||||
#define PLATFORM_IMPL_IOS_ATOMIC_UINT32_H_
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
|
||||
#include "internal/platform/implementation/atomic_reference.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace ios {
|
||||
|
||||
// Concrete AtomicUint32 implementation.
|
||||
class AtomicUint32 : public api::AtomicUint32 {
|
||||
public:
|
||||
explicit AtomicUint32(std::uint32_t initial_value) : value_(initial_value) {}
|
||||
~AtomicUint32() override = default;
|
||||
|
||||
AtomicUint32(const AtomicUint32&) = delete;
|
||||
AtomicUint32& operator=(const AtomicUint32&) = delete;
|
||||
|
||||
std::uint32_t Get() const override { return value_; }
|
||||
void Set(std::uint32_t value) override { value_ = value; }
|
||||
|
||||
private:
|
||||
std::atomic<std::uint32_t> value_;
|
||||
};
|
||||
|
||||
} // namespace ios
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // PLATFORM_IMPL_IOS_ATOMIC_UINT32_H_
|
||||
@@ -0,0 +1,64 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#include "internal/platform/implementation/ios/Source/Platform/atomic_uint32.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "thread/fiber/fiber.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace ios {
|
||||
namespace {
|
||||
|
||||
TEST(AtomicUint32Test, GetOnSameThread) {
|
||||
std::uint32_t initial_value = 1450;
|
||||
AtomicUint32 atomic_reference_{initial_value};
|
||||
|
||||
EXPECT_EQ(initial_value, atomic_reference_.Get());
|
||||
}
|
||||
|
||||
TEST(AtomicUint32Test, SetGetOnSameThread) {
|
||||
std::uint32_t initial_value_ = 1450;
|
||||
AtomicUint32 atomic_reference_{initial_value_};
|
||||
|
||||
std::uint32_t new_value = 28;
|
||||
atomic_reference_.Set(new_value);
|
||||
EXPECT_EQ(new_value, atomic_reference_.Get());
|
||||
}
|
||||
|
||||
TEST(AtomicUint32Test, SetOnNewThread) {
|
||||
std::uint32_t initial_value_ = 1450;
|
||||
AtomicUint32 atomic_reference_{initial_value_};
|
||||
|
||||
std::uint32_t new_thread_value = 28;
|
||||
thread::Fiber f([&] { atomic_reference_.Set(new_thread_value); });
|
||||
f.Join();
|
||||
EXPECT_EQ(new_thread_value, atomic_reference_.Get());
|
||||
}
|
||||
|
||||
TEST(AtomicUint32Test, GetOnNewThread) {
|
||||
std::uint32_t initial_value_ = 1450;
|
||||
AtomicUint32 atomic_reference_{initial_value_};
|
||||
|
||||
std::uint32_t new_value = 28;
|
||||
atomic_reference_.Set(new_value);
|
||||
thread::Fiber f([&] { EXPECT_EQ(new_value, atomic_reference_.Get()); });
|
||||
f.Join();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace ios
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -0,0 +1,37 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#include "internal/platform/implementation/ios/Source/Platform/condition_variable.h"
|
||||
|
||||
#include "internal/platform/implementation/ios/Source/Platform/mutex.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace ios {
|
||||
|
||||
Exception ConditionVariable::Wait() {
|
||||
condition_variable_.Wait(mutex_);
|
||||
return {Exception::kSuccess};
|
||||
}
|
||||
|
||||
Exception ConditionVariable::Wait(absl::Duration timeout) {
|
||||
condition_variable_.WaitWithTimeout(mutex_, timeout);
|
||||
return {Exception::kSuccess};
|
||||
}
|
||||
|
||||
void ConditionVariable::Notify() { condition_variable_.SignalAll(); }
|
||||
|
||||
} // namespace ios
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -0,0 +1,48 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#ifndef PLATFORM_IMPL_IOS_CONDITION_VARIABLE_H_
|
||||
#define PLATFORM_IMPL_IOS_CONDITION_VARIABLE_H_
|
||||
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "internal/platform/implementation/condition_variable.h"
|
||||
#include "internal/platform/implementation/ios/Source/Platform/mutex.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace ios {
|
||||
|
||||
// Concrete ConditionVariable implementation.
|
||||
class ConditionVariable : public api::ConditionVariable {
|
||||
public:
|
||||
explicit ConditionVariable(ios::Mutex* mutex) : mutex_(&mutex->mutex_) {}
|
||||
~ConditionVariable() override = default;
|
||||
|
||||
ConditionVariable(const ConditionVariable&) = delete;
|
||||
ConditionVariable& operator=(const ConditionVariable&) = delete;
|
||||
|
||||
Exception Wait() override;
|
||||
Exception Wait(absl::Duration timeout) override;
|
||||
void Notify() override;
|
||||
|
||||
private:
|
||||
absl::Mutex* mutex_;
|
||||
absl::CondVar condition_variable_;
|
||||
};
|
||||
|
||||
} // namespace ios
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // PLATFORM_IMPL_IOS_CONDITION_VARIABLE_H_
|
||||
@@ -0,0 +1,84 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#include "internal/platform/implementation/ios/Source/Platform/condition_variable.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "absl/time/clock.h"
|
||||
#include "internal/platform/implementation/ios/Source/Platform/mutex.h"
|
||||
#include "thread/fiber/fiber.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace ios {
|
||||
namespace {
|
||||
|
||||
TEST(ConditionVariableTest, CanCreate) {
|
||||
Mutex mutex{};
|
||||
ConditionVariable cond{&mutex};
|
||||
}
|
||||
|
||||
TEST(ConditionVariableTest, CanWakeupWaiter) {
|
||||
Mutex mutex{};
|
||||
ConditionVariable cond{&mutex};
|
||||
bool done = false;
|
||||
bool waiting = false;
|
||||
{
|
||||
thread::Fiber f([&cond, &mutex, &done, &waiting] {
|
||||
mutex.Lock();
|
||||
waiting = true;
|
||||
cond.Wait();
|
||||
waiting = false;
|
||||
done = true;
|
||||
mutex.Unlock();
|
||||
});
|
||||
while (true) {
|
||||
{
|
||||
mutex.Lock();
|
||||
if (waiting) {
|
||||
mutex.Unlock();
|
||||
break;
|
||||
}
|
||||
mutex.Unlock();
|
||||
}
|
||||
absl::SleepFor(absl::Milliseconds(100));
|
||||
}
|
||||
{
|
||||
mutex.Lock();
|
||||
cond.Notify();
|
||||
EXPECT_FALSE(done);
|
||||
mutex.Unlock();
|
||||
}
|
||||
f.Join();
|
||||
}
|
||||
EXPECT_TRUE(done);
|
||||
}
|
||||
|
||||
TEST(ConditionVariableTest, WaitTerminatesOnTimeoutWithoutNotify) {
|
||||
Mutex mutex{};
|
||||
ConditionVariable cond{&mutex};
|
||||
mutex.Lock();
|
||||
|
||||
const absl::Duration kWaitTime = absl::Milliseconds(100);
|
||||
absl::Time start = absl::Now();
|
||||
cond.Wait(kWaitTime);
|
||||
absl::Duration duration = absl::Now() - start;
|
||||
EXPECT_GE(duration, kWaitTime);
|
||||
mutex.Unlock();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace ios
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -0,0 +1,40 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#include "internal/platform/implementation/ios/Source/Platform/count_down_latch.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace ios {
|
||||
|
||||
Exception CountDownLatch::Await() {
|
||||
absl::MutexLock lock(&mutex_, absl::Condition(IsZeroOrNegative, &count_));
|
||||
return {Exception::kSuccess};
|
||||
}
|
||||
|
||||
ExceptionOr<bool> CountDownLatch::Await(absl::Duration timeout) {
|
||||
bool condition = mutex_.LockWhenWithTimeout(
|
||||
absl::Condition(IsZeroOrNegative, &count_), timeout);
|
||||
mutex_.Unlock();
|
||||
return ExceptionOr<bool>(condition);
|
||||
}
|
||||
|
||||
void CountDownLatch::CountDown() {
|
||||
absl::MutexLock lock(&mutex_);
|
||||
count_--;
|
||||
}
|
||||
|
||||
} // namespace ios
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -0,0 +1,49 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#ifndef PLATFORM_IMPL_IOS_COUNT_DOWN_LATCH_H_
|
||||
#define PLATFORM_IMPL_IOS_COUNT_DOWN_LATCH_H_
|
||||
|
||||
#include "absl/synchronization/mutex.h"
|
||||
#include "internal/platform/implementation/count_down_latch.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace ios {
|
||||
|
||||
// Concrete CountDownLatch implementation.
|
||||
class CountDownLatch : public api::CountDownLatch {
|
||||
public:
|
||||
explicit CountDownLatch(int count) : count_(count) {}
|
||||
~CountDownLatch() override = default;
|
||||
|
||||
CountDownLatch(const CountDownLatch&) = delete;
|
||||
CountDownLatch& operator=(const CountDownLatch&) = delete;
|
||||
|
||||
Exception Await() override;
|
||||
ExceptionOr<bool> Await(absl::Duration timeout) override;
|
||||
void CountDown() override;
|
||||
|
||||
private:
|
||||
static bool IsZeroOrNegative(int* count) { return 0 >= *count; }
|
||||
|
||||
absl::Mutex mutex_;
|
||||
int count_ ABSL_GUARDED_BY(mutex_);
|
||||
};
|
||||
|
||||
} // namespace ios
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // PLATFORM_IMPL_IOS_COUNT_DOWN_LATCH_H_
|
||||
@@ -0,0 +1,83 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#include "internal/platform/implementation/ios/Source/Platform/count_down_latch.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "thread/fiber/fiber.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace ios {
|
||||
namespace {
|
||||
|
||||
TEST(CountDownLatchTest, LatchAwaitCanWait) {
|
||||
CountDownLatch latch(1);
|
||||
std::atomic_bool done = false;
|
||||
|
||||
thread::Fiber f([&done, &latch] {
|
||||
done = true;
|
||||
latch.CountDown();
|
||||
});
|
||||
f.Join();
|
||||
|
||||
latch.Await();
|
||||
EXPECT_TRUE(done);
|
||||
}
|
||||
|
||||
TEST(CountDownLatchTest, LatchExtraCountDownIgnored) {
|
||||
CountDownLatch latch(1);
|
||||
std::atomic_bool done = false;
|
||||
|
||||
thread::Fiber f([&done, &latch] {
|
||||
done = true;
|
||||
latch.CountDown();
|
||||
latch.CountDown();
|
||||
latch.CountDown();
|
||||
});
|
||||
f.Join();
|
||||
|
||||
latch.Await();
|
||||
EXPECT_TRUE(done);
|
||||
}
|
||||
|
||||
TEST(CountDownLatchTest, LatchAwaitWithTimeoutCanExpire) {
|
||||
CountDownLatch latch(1);
|
||||
|
||||
auto response = latch.Await(absl::Milliseconds(100));
|
||||
|
||||
EXPECT_TRUE(response.ok());
|
||||
EXPECT_FALSE(response.result());
|
||||
}
|
||||
|
||||
TEST(CountDownLatchTest, InitialCountZero_AwaitDoesNotBlock) {
|
||||
CountDownLatch latch(0);
|
||||
|
||||
auto response = latch.Await();
|
||||
|
||||
EXPECT_TRUE(response.Ok());
|
||||
}
|
||||
|
||||
TEST(CountDownLatchTest, InitialCountNegative_AwaitDoesNotBlock) {
|
||||
CountDownLatch latch(-1);
|
||||
|
||||
auto response = latch.Await();
|
||||
|
||||
EXPECT_TRUE(response.Ok());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace ios
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#include "internal/platform/implementation/crypto.h"
|
||||
|
||||
#import "absl/strings/string_view.h"
|
||||
#import "internal/platform/implementation/ios/Source/Platform/utils.h"
|
||||
#import "internal/platform/implementation/ios/Source/Shared/GNCUtils.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
|
||||
void Crypto::Init() {}
|
||||
|
||||
ByteArray Crypto::Md5(absl::string_view input) {
|
||||
if (input.empty()) return ByteArray();
|
||||
|
||||
return ByteArrayFromNSData(GNCMd5String(ObjCStringFromCppString(input)));
|
||||
}
|
||||
|
||||
ByteArray Crypto::Sha256(absl::string_view input) {
|
||||
if (input.empty()) return ByteArray();
|
||||
|
||||
return ByteArrayFromNSData(GNCSha256String(ObjCStringFromCppString(input)));
|
||||
}
|
||||
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -0,0 +1,48 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#ifndef PLATFORM_IMPL_IOS_INPUT_FILE_H_
|
||||
#define PLATFORM_IMPL_IOS_INPUT_FILE_H_
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#include "internal/platform/implementation/input_file.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace ios {
|
||||
|
||||
/** This InputFile subclass takes input from an NSURL. */
|
||||
class InputFile : public api::InputFile {
|
||||
public:
|
||||
explicit InputFile(NSURL *nsURL);
|
||||
~InputFile() override = default;
|
||||
InputFile(InputFile &&) = default;
|
||||
InputFile &operator=(InputFile &&) = default;
|
||||
|
||||
ExceptionOr<ByteArray> Read(std::int64_t size) override;
|
||||
std::string GetFilePath() const override;
|
||||
std::int64_t GetTotalSize() const override;
|
||||
Exception Close() override;
|
||||
|
||||
private:
|
||||
NSURL *nsURL_;
|
||||
NSInputStream *nsStream_;
|
||||
};
|
||||
|
||||
} // namespace ios
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // PLATFORM_IMPL_IOS_INPUT_FILE_H_
|
||||
@@ -0,0 +1,69 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#import "internal/platform/implementation/ios/Source/Platform/input_file.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#import "internal/platform/exception.h"
|
||||
#import "internal/platform/implementation/ios/Source/Platform/utils.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace ios {
|
||||
|
||||
InputFile::InputFile(NSURL *nsURL) : nsURL_(nsURL) {
|
||||
std::string string = CppStringFromObjCString([nsURL_ absoluteString]);
|
||||
nsStream_ = [NSInputStream inputStreamWithURL:nsURL_];
|
||||
[nsStream_ scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
|
||||
[nsStream_ open];
|
||||
}
|
||||
|
||||
ExceptionOr<ByteArray> InputFile::Read(std::int64_t size) {
|
||||
uint8_t *bytes_read = new uint8_t[size];
|
||||
NSUInteger numberOfBytesToRead = [[NSNumber numberWithLongLong:size] unsignedIntegerValue];
|
||||
NSInteger numberOfBytesRead = [nsStream_ read:bytes_read maxLength:numberOfBytesToRead];
|
||||
if (numberOfBytesRead == 0) {
|
||||
// Reached end of stream.
|
||||
return ExceptionOr<ByteArray>();
|
||||
} else if (numberOfBytesRead < 0) {
|
||||
// Stream error.
|
||||
return ExceptionOr<ByteArray>(Exception::kIo);
|
||||
}
|
||||
return ExceptionOr<ByteArray>(ByteArrayFromNSData([NSData dataWithBytes:bytes_read
|
||||
length:numberOfBytesRead]));
|
||||
}
|
||||
|
||||
std::string InputFile::GetFilePath() const {
|
||||
return CppStringFromObjCString([nsURL_ absoluteString]);
|
||||
}
|
||||
|
||||
std::int64_t InputFile::GetTotalSize() const {
|
||||
NSNumber *fileSizeValue = nil;
|
||||
BOOL result = [nsURL_ getResourceValue:&fileSizeValue forKey:NSURLFileSizeKey error:nil];
|
||||
if (result) {
|
||||
return fileSizeValue.longValue;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
Exception InputFile::Close() {
|
||||
[nsStream_ close];
|
||||
return {Exception::kSuccess};
|
||||
}
|
||||
|
||||
} // namespace ios
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -0,0 +1,47 @@
|
||||
// Copyright 2020 Google LLC
|
||||
//
|
||||
// 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
|
||||
//
|
||||
// https://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.
|
||||
|
||||
#ifndef PLATFORM_IMPL_IOS_LOG_MESSAGE_H_
|
||||
#define PLATFORM_IMPL_IOS_LOG_MESSAGE_H_
|
||||
|
||||
#include "glog/logging.h"
|
||||
#include "internal/platform/implementation/log_message.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace ios {
|
||||
|
||||
// Concrete LogMessage implementation
|
||||
class LogMessage : public api::LogMessage {
|
||||
public:
|
||||
LogMessage(const char* file, int line, Severity severity);
|
||||
~LogMessage() override = default;
|
||||
|
||||
LogMessage(const LogMessage&) = delete;
|
||||
LogMessage& operator=(const LogMessage&) = delete;
|
||||
|
||||
void Print(const char* format, ...) override;
|
||||
|
||||
std::ostream& Stream() override;
|
||||
|
||||
private:
|
||||
google::LogMessage log_streamer_;
|
||||
api::LogMessage::Severity severity_;
|
||||
};
|
||||
|
||||
} // namespace ios
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // IPHONE_SHARED_NEARBY_CONNECTIONS_SOURCE_PLATFORM_LOG_MESSAGE_H_
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user