Merge branch 'google3'

Change-Id: Ibebf84b98939ee8e5006beedc34d225e8c2dd413
This commit is contained in:
Alexey Polyudov
2020-05-28 01:36:50 -07:00
337 changed files with 20487 additions and 1586 deletions
+43
View File
@@ -0,0 +1,43 @@
cc_library(
name = "api",
hdrs = [
"atomic_boolean.h",
"atomic_reference.h",
"ble.h",
"ble_v2.h",
"bluetooth_adapter.h",
"bluetooth_classic.h",
"cancelable.h",
"condition_variable.h",
"count_down_latch.h",
"crypto.h",
"executor.h",
"future.h",
"input_file.h",
"listenable_future.h",
"mutex.h",
"output_file.h",
"platform.h",
"scheduled_executor.h",
"server_sync.h",
"settable_future.h",
"submittable_executor.h",
"system_clock.h",
"webrtc.h",
"wifi.h",
"wifi_lan.h",
],
visibility = [
"//platform_v2/base:__pkg__",
"//platform_v2/impl:__subpackages__",
"//platform_v2/public:__subpackages__",
],
deps = [
"//platform_v2/base",
"//absl/base:core_headers",
"//absl/strings",
"//absl/time",
"//absl/types:any",
"//webrtc/api:libjingle_peerconnection_api",
],
)
+41
View File
@@ -0,0 +1,41 @@
add_library(platform_api2 STATIC)
target_sources(platform_api2
PUBLIC
atomic_boolean.h
atomic_reference.h
ble.h
ble_v2.h
bluetooth_adapter.h
bluetooth_classic.h
condition_variable.h
count_down_latch.h
executor.h
future.h
hash_utils.h
input_file.h
input_stream.h
listenable_future.h
multi_thread_executor.h
mutex.h
output_file.h
output_stream.h
scheduled_executor.h
server_sync.h
settable_future.h
single_thread_executor.h
socket.h
submittable_executor.h
system_clock.h
thread_utils.h
webrtc.h
wifi.h
)
target_link_libraries(platform_api2
PUBLIC
absl::strings
absl::time
platform_types
webrtc_api_libjingle_peerconnection_api
)
+24
View File
@@ -0,0 +1,24 @@
#ifndef PLATFORM_V2_API_ATOMIC_BOOLEAN_H_
#define PLATFORM_V2_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_V2_API_ATOMIC_BOOLEAN_H_
+26
View File
@@ -0,0 +1,26 @@
#ifndef PLATFORM_V2_API_ATOMIC_REFERENCE_H_
#define PLATFORM_V2_API_ATOMIC_REFERENCE_H_
namespace location {
namespace nearby {
namespace api {
// An object reference that may be updated atomically.
//
// https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/AtomicReference.html
template <typename T>
class AtomicReference {
public:
virtual ~AtomicReference() = default;
virtual T Get() const & = 0;
virtual T Get() && = 0;
virtual void Set(const T& value) = 0;
virtual void Set(T&& value) = 0;
};
} // namespace api
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_API_ATOMIC_REFERENCE_H_
+112
View File
@@ -0,0 +1,112 @@
#ifndef PLATFORM_V2_API_BLE_H_
#define PLATFORM_V2_API_BLE_H_
#include "platform_v2/api/bluetooth_classic.h"
#include "platform_v2/base/byte_array.h"
#include "platform_v2/base/input_stream.h"
#include "platform_v2/base/output_stream.h"
#include "absl/strings/string_view.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() {}
// The returned reference lifetime matches BlePeripheral object.
virtual BluetoothDevice& GetBluetoothDevice() = 0;
};
class BleSocket {
public:
virtual ~BleSocket() {}
// 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;
// The returned object is not owned by the caller, and can be invalidated once
// the BleSocket object is destroyed.
virtual BlePeripheral& GetRemotePeripheral() = 0;
};
// Container of operations that can be performed over the BLE medium.
class BleMedium {
public:
virtual ~BleMedium() {}
virtual bool StartAdvertising(absl::string_view service_id,
const ByteArray& advertisement) = 0;
virtual void StopAdvertising(absl::string_view service_id) = 0;
class DiscoveredPeripheralCallback {
public:
virtual ~DiscoveredPeripheralCallback() {}
// The BlePeripheral* is not owned by callbacks.
// It is passed to give access to its non-const methods.
// It is guaranteed to be valid for the duration of call.
virtual void OnPeripheralDiscovered(BlePeripheral* ble_peripheral,
absl::string_view service_id,
const ByteArray& advertisement) = 0;
virtual void OnPeripheralLost(BlePeripheral* ble_peripheral,
absl::string_view service_id) = 0;
};
// Returns true once the BLE scan has been initiated.
virtual bool StartScanning(
absl::string_view service_id,
const DiscoveredPeripheralCallback& discovered_peripheral_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 void StopScanning(absl::string_view service_id) = 0;
// Callback that is invoked when a new connection is accepted.
class AcceptedConnectionCallback {
public:
virtual ~AcceptedConnectionCallback() {}
virtual void OnConnectionAccepted(std::unique_ptr<BleSocket> socket,
absl::string_view service_id) = 0;
};
// Returns true once BLE socket connection requests to service_id can be
// accepted.
virtual bool StartAcceptingConnections(
absl::string_view service_id,
const AcceptedConnectionCallback& accepted_connection_callback) = 0;
virtual void StopAcceptingConnections(const std::string& service_id) = 0;
// BlePeripheral* is not owned by this call;
// it must remain valid for the duration of a call.
virtual std::unique_ptr<BleSocket> Connect(BlePeripheral* ble_peripheral,
absl::string_view service_id) = 0;
};
} // namespace api
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_API_BLE_H_
+392
View File
@@ -0,0 +1,392 @@
#ifndef PLATFORM_V2_API_BLE_V2_H_
#define PLATFORM_V2_API_BLE_V2_H_
#include <cstdint>
#include <limits>
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <string>
#include "platform_v2/base/byte_array.h"
#include "platform_v2/base/exception.h"
#include "absl/strings/string_view.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 std::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 std::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 std::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_V2_API_BLE_V2_H_
+58
View File
@@ -0,0 +1,58 @@
#ifndef PLATFORM_V2_API_BLUETOOTH_ADAPTER_H_
#define PLATFORM_V2_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;
};
} // namespace api
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_API_BLUETOOTH_ADAPTER_H_
+126
View File
@@ -0,0 +1,126 @@
#ifndef PLATFORM_V2_API_BLUETOOTH_CLASSIC_H_
#define PLATFORM_V2_API_BLUETOOTH_CLASSIC_H_
#include <memory>
#include <string>
#include "platform_v2/base/byte_array.h"
#include "platform_v2/base/exception.h"
#include "platform_v2/base/input_stream.h"
#include "platform_v2/base/output_stream.h"
#include "absl/strings/string_view.h"
namespace location {
namespace nearby {
namespace api {
// https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html.
class BluetoothDevice {
public:
virtual ~BluetoothDevice() {}
// https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#getName()
virtual std::string GetName() const = 0;
};
// https://developer.android.com/reference/android/bluetooth/BluetoothSocket.html.
class BluetoothSocket {
public:
virtual ~BluetoothSocket() {}
// Returns the InputStream of the BluetoothSocket.
virtual InputStream& GetInputStream() = 0;
// Returns the OutputStream of the BluetoothSocket.
virtual OutputStream& GetOutputStream() = 0;
// https://developer.android.com/reference/android/bluetooth/BluetoothSocket.html#close()
//
// Returns Exception::kIo on error, Exception::kSuccess otherwise.
virtual Exception Close() = 0;
// https://developer.android.com/reference/android/bluetooth/BluetoothSocket.html#getRemoteDevice()
virtual BluetoothDevice& GetRemoteDevice() = 0;
};
// https://developer.android.com/reference/android/bluetooth/BluetoothServerSocket.html.
class BluetoothServerSocket {
public:
virtual ~BluetoothServerSocket() {}
// https://developer.android.com/reference/android/bluetooth/BluetoothServerSocket.html#accept()
//
// returns Exception::kIo on error.
virtual ExceptionOr<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() {}
class DiscoveryCallback {
public:
virtual ~DiscoveryCallback() {}
// BluetoothDevice* is not owned by callbacks.
// Pointer is guaranteed to remain valid for the duration of a call.
virtual void OnDeviceDiscovered(BluetoothDevice* device) = 0;
virtual void OnDeviceNameChanged(BluetoothDevice* device) = 0;
virtual void OnDeviceLost(BluetoothDevice* device) = 0;
};
// https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#startDiscovery()
//
// Returns true once the process of discovery has been initiated.
//
// Does not take ownership of the passed-in discovery_callback -- destroying
// that is up to the caller.
virtual bool StartDiscovery(const 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, wrapped in a ExceptionOr object.
// On error, returns Exception object.
virtual ExceptionOr<std::unique_ptr<BluetoothSocket>> ConnectToService(
BluetoothDevice* remote_device, absl::string_view service_uuid) = 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 Exception::kIo on error.
virtual ExceptionOr<std::unique_ptr<BluetoothServerSocket>> ListenForService(
absl::string_view service_name, absl::string_view service_uuid) = 0;
};
} // namespace api
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_API_BLUETOOTH_CLASSIC_H_
+21
View File
@@ -0,0 +1,21 @@
#ifndef PLATFORM_V2_API_CANCELABLE_H_
#define PLATFORM_V2_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_V2_API_CANCELABLE_H_
+28
View File
@@ -0,0 +1,28 @@
#ifndef PLATFORM_V2_API_CONDITION_VARIABLE_H_
#define PLATFORM_V2_API_CONDITION_VARIABLE_H_
#include "platform_v2/base/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() {}
// https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#notify--
virtual void Notify() = 0;
// https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#wait--
virtual Exception Wait() = 0; // throws Exception::kInterrupted
};
} // namespace api
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_API_CONDITION_VARIABLE_H_
+31
View File
@@ -0,0 +1,31 @@
#ifndef PLATFORM_V2_API_COUNT_DOWN_LATCH_H_
#define PLATFORM_V2_API_COUNT_DOWN_LATCH_H_
#include <cstdint>
#include "platform_v2/base/exception.h"
#include "absl/time/time.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_V2_API_COUNT_DOWN_LATCH_H_
+24
View File
@@ -0,0 +1,24 @@
#ifndef PLATFORM_V2_API_CRYPTO_H_
#define PLATFORM_V2_API_CRYPTO_H_
#include "platform_v2/base/byte_array.h"
#include "absl/strings/string_view.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_V2_API_CRYPTO_H_
+28
View File
@@ -0,0 +1,28 @@
#ifndef PLATFORM_V2_API_EXECUTOR_H_
#define PLATFORM_V2_API_EXECUTOR_H_
#include "platform_v2/base/runnable.h"
namespace location {
namespace nearby {
namespace api {
// 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_V2_API_EXECUTOR_H_
+32
View File
@@ -0,0 +1,32 @@
#ifndef PLATFORM_V2_API_FUTURE_H_
#define PLATFORM_V2_API_FUTURE_H_
#include "platform_v2/base/exception.h"
#include "absl/time/clock.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_V2_API_FUTURE_H_
+26
View File
@@ -0,0 +1,26 @@
#ifndef PLATFORM_V2_API_INPUT_FILE_H_
#define PLATFORM_V2_API_INPUT_FILE_H_
#include <cstdint>
#include "platform_v2/base/byte_array.h"
#include "platform_v2/base/exception.h"
#include "platform_v2/base/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_V2_API_INPUT_FILE_H_
+32
View File
@@ -0,0 +1,32 @@
#ifndef PLATFORM_V2_API_LISTENABLE_FUTURE_H_
#define PLATFORM_V2_API_LISTENABLE_FUTURE_H_
#include <functional>
#include <memory>
#include "platform_v2/api/executor.h"
#include "platform_v2/api/future.h"
#include "platform_v2/base/exception.h"
#include "platform_v2/base/runnable.h"
namespace location {
namespace nearby {
namespace api {
// A Future that accepts completion listeners.
//
// https://guava.dev/releases/20.0/api/docs/com/google/common/util/concurrent/ListenableFuture.html
template <typename T>
class ListenableFuture : public Future<T> {
public:
~ListenableFuture() override = default;
virtual void AddListener(Runnable runnable,
Executor* executor) = 0;
};
} // namespace api
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_API_LISTENABLE_FUTURE_H_
+41
View File
@@ -0,0 +1,41 @@
#ifndef PLATFORM_V2_API_MUTEX_H_
#define PLATFORM_V2_API_MUTEX_H_
#include "absl/base/thread_annotations.h"
namespace location {
namespace nearby {
namespace api {
// A lock is a tool for controlling access to a shared resource by multiple
// threads.
//
// https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/Lock.html
class ABSL_LOCKABLE Mutex {
public:
// Mode to pass to implementation constructor.
// kRegular - produces a regular mutex: disallows multiple locks from
// the same thread; optionally, detects double locks in
// debug mode.
// This is the default option.
// kRecursive - produces recursive mutex: allows multiple locks from the
// same thread.
// kRegularNoCheck - produces a regular mutex: disallows double locks,
// but does not check for deadlocks.
enum class Mode {
kRegular = 0,
kRecursive = 1,
kRegularNoCheck = 2,
};
virtual ~Mutex() {}
virtual void Lock() ABSL_EXCLUSIVE_LOCK_FUNCTION() = 0;
virtual void Unlock() ABSL_UNLOCK_FUNCTION() = 0;
};
} // namespace api
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_API_MUTEX_H_
+22
View File
@@ -0,0 +1,22 @@
#ifndef PLATFORM_V2_API_OUTPUT_FILE_H_
#define PLATFORM_V2_API_OUTPUT_FILE_H_
#include "platform_v2/base/byte_array.h"
#include "platform_v2/base/exception.h"
#include "platform_v2/base/output_stream.h"
namespace location {
namespace nearby {
namespace api {
// An OutputFile represents a writable file on the system.
class OutputFile : public OutputStream {
public:
~OutputFile() override = default;
};
} // namespace api
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_API_OUTPUT_FILE_H_
+78
View File
@@ -0,0 +1,78 @@
#ifndef PLATFORM_V2_API_PLATFORM_H_
#define PLATFORM_V2_API_PLATFORM_H_
#include <cstdint>
#include <memory>
#include <string>
#include "platform_v2/api/atomic_boolean.h"
#include "platform_v2/api/atomic_reference.h"
#include "platform_v2/api/ble.h"
#include "platform_v2/api/ble_v2.h"
#include "platform_v2/api/bluetooth_adapter.h"
#include "platform_v2/api/bluetooth_classic.h"
#include "platform_v2/api/condition_variable.h"
#include "platform_v2/api/count_down_latch.h"
#include "platform_v2/api/crypto.h"
#include "platform_v2/api/mutex.h"
#include "platform_v2/api/scheduled_executor.h"
#include "platform_v2/api/server_sync.h"
#include "platform_v2/api/settable_future.h"
#include "platform_v2/api/submittable_executor.h"
#include "platform_v2/api/system_clock.h"
#include "platform_v2/api/webrtc.h"
#include "platform_v2/api/wifi.h"
#include "platform_v2/api/wifi_lan.h"
#include "absl/strings/string_view.h"
#include "absl/types/any.h"
namespace location {
namespace nearby {
namespace api {
// API rework notes:
// https://docs.google.com/spreadsheets/d/1erZNkX7pX8s5jWTHdxgjntxTMor3BGiY2H_fC_ldtoQ/edit#gid=381357998
class ImplementationPlatform {
public:
// General platform support:
// - atomic variables (boolean, and any other copyable type)
// - synchronization primitives:
// - mutex (regular, and recursive)
// - condition variable (must work with regular mutex only)
// - Future<T> : to synchronize on Callable<T> schduled to execute.
// - CountDownLatch : to ensure at least N threads are waiting.
static std::unique_ptr<AtomicReference<absl::any>> CreateAtomicReferenceAny(
absl::any initial_value);
static std::unique_ptr<SettableFuture<absl::any>> CreateSettableFutureAny();
static std::unique_ptr<AtomicBoolean> CreateAtomicBoolean(bool initial_value);
static std::unique_ptr<CountDownLatch> CreateCountDownLatch(
std::int32_t count);
static std::unique_ptr<Mutex> CreateMutex(Mutex::Mode mode);
static std::unique_ptr<ConditionVariable> CreateConditionVariable(
Mutex* mutex);
// Java-like Executors
static std::unique_ptr<SubmittableExecutor> CreateSingleThreadExecutor();
static std::unique_ptr<SubmittableExecutor> CreateMultiThreadExecutor(
std::int32_t max_concurrency);
static std::unique_ptr<ScheduledExecutor> CreateScheduledExecutor();
// Protocol implementations, domain-specific support
static std::unique_ptr<BluetoothAdapter> CreateBluetoothAdapter();
static std::unique_ptr<BluetoothClassicMedium> CreateBluetoothClassicMedium();
static std::unique_ptr<BleMedium> CreateBleMedium();
static std::unique_ptr<ble_v2::BleMedium> CreateBleV2Medium();
static std::unique_ptr<ServerSyncMedium> CreateServerSyncMedium();
static std::unique_ptr<WifiMedium> CreateWifiMedium();
static std::unique_ptr<WifiLanMedium> CreateWifiLanMedium();
static std::unique_ptr<WebRtcSignalingMessenger>
CreateWebRtcSignalingMessenger(absl::string_view self_id);
static std::string GetDeviceId();
static std::string GetPayloadPath(std::int64_t payload_id);
};
} // namespace api
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_API_PLATFORM_H_
+36
View File
@@ -0,0 +1,36 @@
#ifndef PLATFORM_V2_API_SCHEDULED_EXECUTOR_H_
#define PLATFORM_V2_API_SCHEDULED_EXECUTOR_H_
#include <cstdint>
#include <functional>
#include <memory>
#include "platform_v2/api/cancelable.h"
#include "platform_v2/api/executor.h"
#include "platform_v2/base/runnable.h"
#include "absl/time/time.h"
namespace location {
namespace nearby {
namespace api {
// An Executor that can schedule commands to run after a given delay, or to
// execute periodically.
//
// https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ScheduledExecutorService.html
class ScheduledExecutor : public Executor {
public:
~ScheduledExecutor() override = default;
// Cancelable is kept both in the executor context, and in the caller context.
// We want Cancelable to live until both caller and executor are done with it.
// Exclusive ownership model does not work for this case;
// using std:shared_ptr<> instead if std::unique_ptr<>.
virtual std::shared_ptr<Cancelable> Schedule(Runnable&& runnable,
absl::Duration duration) = 0;
};
} // namespace api
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_API_SCHEDULED_EXECUTOR_H_
+62
View File
@@ -0,0 +1,62 @@
#ifndef PLATFORM_V2_API_SERVER_SYNC_H_
#define PLATFORM_V2_API_SERVER_SYNC_H_
#include <string>
#include "platform_v2/base/byte_array.h"
#include "absl/strings/string_view.h"
namespace location {
namespace nearby {
namespace api {
// Abstraction that represents a Nearby endpoint exchanging data through
// ServerSync Medium.
class ServerSyncDevice {
public:
virtual ~ServerSyncDevice() = default;
virtual std::string GetName() const = 0;
virtual std::string GetGuid() const = 0;
virtual std::string GetOwnGuid() const = 0;
};
// Container of operations that can be performed over the Chrome Sync medium.
class ServerSyncMedium {
public:
virtual ~ServerSyncMedium() = default;
virtual bool StartAdvertising(absl::string_view service_id,
absl::string_view endpoint_id,
const ByteArray& endpoint_info) = 0;
virtual void StopAdvertising(absl::string_view service_id) = 0;
class DiscoveredDeviceCallback {
public:
virtual ~DiscoveredDeviceCallback() = default;
// Called on a new ServerSyncDevice discovery.
virtual void OnDeviceDiscovered(ServerSyncDevice* device,
absl::string_view service_id,
absl::string_view endpoint_id,
const ByteArray& endpoint_info) = 0;
// Called when ServerSyncDevice is no longer reachable.
virtual void OnDeviceLost(ServerSyncDevice* device,
absl::string_view service_id) = 0;
};
// Returns true once the Chrome Sync scan has been initiated.
virtual bool StartDiscovery(
absl::string_view service_id,
const DiscoveredDeviceCallback& discovered_device_callback) = 0;
// Returns true once Chrome Sync scan for service_id is well and truly
// stopped; after this returns, there must be no more invocations of the
// DiscoveredDeviceCallback passed in to startScanning() for service_id.
virtual void StopDiscovery(absl::string_view service_id) = 0;
};
} // namespace api
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_API_SERVER_SYNC_H_
+28
View File
@@ -0,0 +1,28 @@
#ifndef PLATFORM_V2_API_SETTABLE_FUTURE_H_
#define PLATFORM_V2_API_SETTABLE_FUTURE_H_
#include "platform_v2/api/listenable_future.h"
#include "platform_v2/base/exception.h"
namespace location {
namespace nearby {
namespace api {
// A SettableFuture is a type of Future whose result can be set.
//
// https://google.github.io/guava/releases/20.0/api/docs/com/google/common/util/concurrent/SettableFuture.html
template <typename T>
class SettableFuture : public ListenableFuture<T> {
public:
~SettableFuture() override = default;
virtual bool Set(const T& value) = 0;
virtual bool Set(T&& value) = 0;
virtual bool SetException(Exception exception) = 0;
};
} // namespace api
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_API_SETTABLE_FUTURE_H_
@@ -0,0 +1,33 @@
#ifndef PLATFORM_V2_API_SUBMITTABLE_EXECUTOR_H_
#define PLATFORM_V2_API_SUBMITTABLE_EXECUTOR_H_
#include <functional>
#include <memory>
#include "platform_v2/api/executor.h"
#include "platform_v2/api/future.h"
#include "platform_v2/base/runnable.h"
namespace location {
namespace nearby {
namespace api {
// Main interface to be used by platform as a base class for
// - MultiThreadExecutorWrapper
// - SingleThreadExecutorWrapper
// Platform must override bool submit(std::function<void()>) method.
class SubmittableExecutor : public Executor {
public:
~SubmittableExecutor() override = default;
// Submit a callable (with no delay).
// Returns true, if callable was submitted, false otherwise.
// Callable is not submitted if shutdown is in progress.
virtual bool DoSubmit(Runnable&& wrapped_callable) = 0;
};
} // namespace api
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_API_SUBMITTABLE_EXECUTOR_H_
+23
View File
@@ -0,0 +1,23 @@
#ifndef PLATFORM_V2_API_SYSTEM_CLOCK_H_
#define PLATFORM_V2_API_SYSTEM_CLOCK_H_
#include "platform_v2/base/exception.h"
#include "absl/time/clock.h"
namespace location {
namespace nearby {
class SystemClock final {
public:
// Initialize global system state.
static void Init();
// Returns current absolute time. It is guaranteed to be monotonic.
static absl::Time ElapsedRealtime();
// Pauses current thread for the specified duration.
static Exception Sleep(absl::Duration duration);
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_API_SYSTEM_CLOCK_H_
+48
View File
@@ -0,0 +1,48 @@
#ifndef PLATFORM_V2_API_WEBRTC_H_
#define PLATFORM_V2_API_WEBRTC_H_
#include <vector>
#include "platform_v2/base/byte_array.h"
#include "webrtc/api/peer_connection_interface.h"
namespace location {
namespace nearby {
namespace api {
class WebRtcSignalingMessenger {
public:
virtual ~WebRtcSignalingMessenger() = default;
/** Called whenever we receive an inbox message from tachyon. */
class SignalingMessageListener {
public:
virtual ~SignalingMessageListener() = default;
virtual void OnSignalingMessage(const ByteArray& message) = 0;
};
class IceServersListener {
public:
virtual ~IceServersListener() = default;
virtual void OnIceServersFetched(
std::vector<webrtc::PeerConnectionInterface::IceServer>
ice_servers) = 0;
};
virtual bool RegisterSignaling() = 0;
virtual bool UnregisterSignaling() = 0;
virtual bool SendMessage(std::string_view peer_id,
const ByteArray& message) = 0;
virtual bool StartReceivingMessages(
const SignalingMessageListener& listener) = 0;
virtual void GetIceServers(
const IceServersListener& ice_servers_listener) = 0;
};
} // namespace api
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_API_WEBRTC_H_
+90
View File
@@ -0,0 +1,90 @@
#ifndef PLATFORM_V2_API_WIFI_H_
#define PLATFORM_V2_API_WIFI_H_
#include <cstdint>
#include <string>
#include <vector>
#include "absl/strings/string_view.h"
namespace location {
namespace nearby {
namespace api {
// Possible authentication types for a WiFi network.
enum class WifiAuthType {
// WiFi Authentication type; either none (non-secured a.k.a. open) link, or
// WPA PSK (WiFi Protected Access PreShared Key), or
// see https://en.wikipedia.org/wiki/Wi-Fi_Protected_Access
// WEP (Wired Equivalent Privacy);
// see https://en.wikipedia.org/wiki/Wired_Equivalent_Privacy
kUnknown = 0,
kOpen = 1,
kWpaPsk = 2,
kWep = 3,
};
// Possible statuses of a device's connection to a WiFi network.
enum class WifiConnectionStatus {
kUnknown = 0,
kConnected = 1,
kConnectionFailure = 2,
kAuthFailure = 3,
};
// Represents a WiFi network found during a call to WifiMedium#scan().
class WifiScanResult {
public:
virtual ~WifiScanResult() = default;
// Gets the SSID of this WiFi network.
virtual std::string GetSsid() const = 0;
// Gets the signal strength of this WiFi network in dBm.
virtual std::int32_t GetSignalStrengthDbm() const = 0;
// Gets the frequency band of this WiFi network in MHz.
virtual std::int32_t GetFrequencyMhz() const = 0;
// Gets the authentication type of this WiFi network.
virtual WifiAuthType GetAuthType() const = 0;
};
// Container of operations that can be performed over the WiFi medium.
class WifiMedium {
public:
virtual ~WifiMedium() {}
class ScanResultCallback {
public:
virtual ~ScanResultCallback() = default;
virtual void OnScanResults(
const std::vector<WifiScanResult>& scan_results) = 0;
};
// Does not take ownership of the passed-in scan_result_callback -- destroying
// that is up to the caller.
virtual bool Scan(const ScanResultCallback& scan_result_callback) = 0;
// If 'password' is an empty string, none has been provided. Returns
// WifiConnectionStatus::CONNECTED on success, or the appropriate failure code
// otherwise.
virtual WifiConnectionStatus ConnectToNetwork(absl::string_view ssid,
absl::string_view password,
WifiAuthType auth_type) = 0;
// Blocks until it's certain of there being a connection to the internet, or
// returns false if it fails to do so.
//
// How this method wants to verify said connection is totally up to it (so it
// can feel free to ping whatever server, download whatever resource, etc.
// that it needs to gain confidence that the internet is reachable hereon in).
virtual bool VerifyInternetConnectivity() = 0;
// Returns the local device's IP address in the IPv4 dotted-quad format.
virtual std::string GetIpAddress() = 0;
};
} // namespace api
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_API_WIFI_H_
+87
View File
@@ -0,0 +1,87 @@
#ifndef PLATFORM_V2_API_WIFI_LAN_H_
#define PLATFORM_V2_API_WIFI_LAN_H_
#include <string>
#include "platform_v2/base/byte_array.h"
#include "platform_v2/base/exception.h"
#include "platform_v2/base/input_stream.h"
#include "platform_v2/base/output_stream.h"
#include "absl/strings/string_view.h"
namespace location {
namespace nearby {
namespace api {
// Opaque wrapper over a WifiLan service which contains encoded service name.
class WifiLanService {
public:
virtual ~WifiLanService() = default;
virtual std::string GetName() = 0;
};
class WifiLanSocket {
public:
virtual ~WifiLanSocket() = default;
// Returns the InputStream of the WifiLanSocket, empty std::unique_ptr<>
// on error.
virtual std::unique_ptr<InputStream> GetInputStream() = 0;
// Returns the OutputStream of the WifiLanSocket, empty std::unique_ptr<>
// on error.
virtual std::unique_ptr<OutputStream> GetOutputStream() = 0;
// Returns Exception::kIo on error, Exception::kSuccess otherwise.
virtual Exception::Value Close() = 0;
virtual WifiLanService& GetRemoteWifiLanService() = 0;
};
// Container of operations that can be performed over the WifiLan medium.
class WifiLanMedium {
public:
virtual ~WifiLanMedium() = default;
virtual bool StartAdvertising(
absl::string_view service_id,
absl::string_view wifi_lan_service_info_name) = 0;
virtual void StopAdvertising(absl::string_view service_id) = 0;
// Callback for WifiLan discover results.
class DiscoveredServiceCallback {
public:
virtual ~DiscoveredServiceCallback() = default;
virtual void OnServiceDiscovered(WifiLanService* wifi_lan_service) = 0;
virtual void OnServiceLost(WifiLanService* wifi_lan_service) = 0;
};
virtual bool StartDiscovery(
absl::string_view service_id,
DiscoveredServiceCallback* discovered_service_callback) = 0;
virtual void StopDiscovery(absl::string_view service_id) = 0;
class AcceptedConnectionCallback {
public:
virtual ~AcceptedConnectionCallback() = default;
virtual void OnConnectionAccepted(WifiLanSocket* socket,
absl::string_view service_id) = 0;
};
virtual bool StartAcceptingConnections(
absl::string_view service_id,
AcceptedConnectionCallback* accepted_connection_callback) = 0;
virtual void StopAcceptingConnections(absl::string_view service_id) = 0;
virtual WifiLanSocket* Connect(WifiLanService* wifi_lan_service,
absl::string_view service_id) = 0;
};
} // namespace api
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_API_WIFI_LAN_H_
+73
View File
@@ -0,0 +1,73 @@
load("//ads/util/non_compile:non_compile.bzl", "cc_with_non_compile_test")
cc_library(
name = "base",
srcs = [
"base64_utils.cc",
"prng.cc",
],
hdrs = [
"base64_utils.h",
"byte_array.h",
"callable.h",
"exception.h",
"input_stream.h",
"listeners.h",
"output_stream.h",
"prng.h",
"runnable.h",
"socket.h",
],
visibility = [
"//core_v2:__subpackages__",
"//platform_v2:__subpackages__",
"//platform_v2/api:__subpackages__",
],
deps = [
"//absl/strings",
"//absl/time",
],
)
cc_library(
name = "util",
srcs = [
"base_pipe.cc",
],
hdrs = [
"base_mutex_lock.h",
"base_pipe.h",
],
visibility = [
"//platform_v2/impl:__subpackages__",
"//platform_v2/public:__pkg__",
],
deps = [
":base",
"//platform_v2/api",
"//absl/base:core_headers",
],
)
cc_test(
name = "platform_base_test",
srcs = [
"byte_array_test.cc",
"prng_test.cc",
],
deps = [
":base",
"//testing/base/public:gunit_main",
],
)
cc_with_non_compile_test(
name = "exception_test",
srcs = [
"exception_test.cc",
],
deps = [
":base",
"//testing/base/public:gunit_main",
],
)
+27
View File
@@ -0,0 +1,27 @@
#include "platform_v2/base/base64_utils.h"
#include "platform_v2/base/byte_array.h"
#include "absl/strings/escaping.h"
namespace location {
namespace nearby {
std::string Base64Utils::Encode(const ByteArray& bytes) {
std::string base64_string;
absl::WebSafeBase64Escape(std::string(bytes), &base64_string);
return base64_string;
}
ByteArray Base64Utils::Decode(absl::string_view base64_string) {
std::string decoded_string;
if (!absl::WebSafeBase64Unescape(base64_string, &decoded_string)) {
return ByteArray();
}
return ByteArray(decoded_string.data(), decoded_string.size());
}
} // namespace nearby
} // namespace location
+19
View File
@@ -0,0 +1,19 @@
#ifndef PLATFORM_V2_BASE_BASE64_UTILS_H_
#define PLATFORM_V2_BASE_BASE64_UTILS_H_
#include "platform_v2/base/byte_array.h"
#include "absl/strings/string_view.h"
namespace location {
namespace nearby {
class Base64Utils {
public:
static std::string Encode(const ByteArray& bytes);
static ByteArray Decode(absl::string_view base64_string);
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_BASE_BASE64_UTILS_H_
+26
View File
@@ -0,0 +1,26 @@
#ifndef PLATFORM_V2_BASE_BASE_MUTEX_LOCK_H_
#define PLATFORM_V2_BASE_BASE_MUTEX_LOCK_H_
#include "platform_v2/api/mutex.h"
#include "absl/base/thread_annotations.h"
namespace location {
namespace nearby {
// An RAII mechanism to acquire a Lock over a block of code.
class ABSL_SCOPED_LOCKABLE BaseMutexLock final {
public:
explicit BaseMutexLock(api::Mutex* mutex) ABSL_EXCLUSIVE_LOCK_FUNCTION(mutex)
: mutex_(mutex) {
mutex_->Lock();
}
~BaseMutexLock() ABSL_UNLOCK_FUNCTION() { mutex_->Unlock(); }
private:
api::Mutex* mutex_;
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_BASE_BASE_MUTEX_LOCK_H_
+96
View File
@@ -0,0 +1,96 @@
#include "platform_v2/base/base_pipe.h"
#include "platform_v2/api/platform.h"
#include "platform_v2/base/base_mutex_lock.h"
#include "platform_v2/base/input_stream.h"
#include "platform_v2/base/output_stream.h"
namespace location {
namespace nearby {
ExceptionOr<ByteArray> BasePipe::Read(size_t size) {
BaseMutexLock lock(mutex_.get());
// We're done reading all the chunks that were written before the OutputStream
// was closed, so there's nothing to do here other than return an empty chunk
// to serve as an EOF indication to callers.
if (read_all_chunks_) {
return ExceptionOr<ByteArray>{ByteArray{}};
}
while (buffer_.empty() && !input_stream_closed_) {
Exception wait_exception = cond_->Wait();
if (wait_exception.Raised()) {
return ExceptionOr<ByteArray>{wait_exception};
}
}
if (input_stream_closed_) {
return ExceptionOr<ByteArray>{Exception::kIo};
}
ByteArray first_chunk{buffer_.front()};
buffer_.pop_front();
// If we received our sentinel chunk, mark the fact that there cannot
// possibly be any more chunks to read here on in, and return an empty chunk
// to serve as an EOF indication to callers.
if (first_chunk.Empty()) {
read_all_chunks_ = true;
return ExceptionOr<ByteArray>{ByteArray{}};
}
// If first_chunk is small enough to not overshoot the requested 'size', just
// return that.
if (first_chunk.size() <= size) {
return ExceptionOr<ByteArray>{first_chunk};
} else {
// Break first_chunk into 2 parts -- the first one of which (next_chunk)
// will be 'size' bytes long, and will be returned, and the second one of
// which (overflow_chunk) will be re-inserted into buffer_, at the head of
// the queue, to be served up in the next call to read().
ByteArray next_chunk(first_chunk.data(), size);
buffer_.push_front(
ByteArray(first_chunk.data() + size, first_chunk.size() - size));
return ExceptionOr<ByteArray>{next_chunk};
}
}
Exception BasePipe::Write(const ByteArray& data) {
BaseMutexLock lock(mutex_.get());
return WriteLocked(data);
}
void BasePipe::MarkInputStreamClosed() {
BaseMutexLock lock(mutex_.get());
input_stream_closed_ = true;
// Trigger cond_ to unblock a potentially-blocked call to read(), and to let
// it know to return Exception::IO.
cond_->Notify();
}
void BasePipe::MarkOutputStreamClosed() {
BaseMutexLock lock(mutex_.get());
// Write a sentinel null chunk before marking output_stream_closed as true.
WriteLocked(ByteArray{});
output_stream_closed_ = true;
}
Exception BasePipe::WriteLocked(const ByteArray& data) {
if (input_stream_closed_ || output_stream_closed_) {
return {Exception::kIo};
}
buffer_.push_back(data);
// Trigger cond_ to unblock a potentially-blocked call to read(), now that
// there's more data for it to consume.
cond_->Notify();
return {Exception::kSuccess};
}
} // namespace nearby
} // namespace location
+128
View File
@@ -0,0 +1,128 @@
#ifndef PLATFORM_V2_BASE_BASE_PIPE_H_
#define PLATFORM_V2_BASE_BASE_PIPE_H_
#include <cstdint>
#include <deque>
#include <memory>
#include "platform_v2/api/condition_variable.h"
#include "platform_v2/api/mutex.h"
#include "platform_v2/base/byte_array.h"
#include "platform_v2/base/exception.h"
#include "platform_v2/base/input_stream.h"
#include "platform_v2/base/output_stream.h"
#include "absl/base/thread_annotations.h"
namespace location {
namespace nearby {
// Common Pipe implenentation.
// It does not depend on platform implementation, and this allows it to
// be used in the platform implementation itself.
// Concrete class must be derived from it, as follows:
//
// class DerivedPipe : public BasePipe {
// public:
// DerivedPipe() {
// auto mutex = /* construct platform-dependent mutex */;
// auto cond = /* construct platform-dependent condition variable */;
// Setup(std::move(mutex), std::move(cond));
// }
// ~DerivedPipe() override = default;
// DerivedPipe(DerivedPipe&&) = default;
// DerivedPipe& operator=(DerivedPipe&&) = default;
// };
class BasePipe {
public:
static constexpr const size_t kChunkSize = 64 * 1024;
virtual ~BasePipe() = default;
// Pipe is not copyable or movable, because copy/move will invalidate
// references to input and output streams.
// If move is required, Pipe could be wrapped with std::unique_ptr<>.
BasePipe(BasePipe&&) = delete;
BasePipe& operator=(BasePipe&&) = delete;
// Get...() methods return references to input and output steam facades.
// It is safe to call Get...() methods multiple times.
InputStream& GetInputStream() { return input_stream_; }
OutputStream& GetOutputStream() { return output_stream_; }
protected:
BasePipe() = default;
void Setup(std::unique_ptr<api::Mutex> mutex,
std::unique_ptr<api::ConditionVariable> cond) {
mutex_ = std::move(mutex);
cond_ = std::move(cond);
}
private:
class BasePipeInputStream : public InputStream {
public:
explicit BasePipeInputStream(BasePipe* pipe) : pipe_(pipe) {}
~BasePipeInputStream() override { DoClose(); }
ExceptionOr<ByteArray> Read(std::int64_t size) override {
return pipe_->Read(size);
}
Exception Close() override {
return DoClose();
}
private:
Exception DoClose() {
pipe_->MarkInputStreamClosed();
return {Exception::kSuccess};
}
BasePipe* pipe_;
};
class BasePipeOutputStream : public OutputStream {
public:
explicit BasePipeOutputStream(BasePipe* pipe) : pipe_(pipe) {}
~BasePipeOutputStream() override { DoClose(); }
Exception Write(const ByteArray& data) override {
return pipe_->Write(data);
}
Exception Flush() override { return {Exception::kSuccess}; }
Exception Close() override {
return DoClose();
}
private:
Exception DoClose() {
pipe_->MarkOutputStreamClosed();
return {Exception::kSuccess};
}
BasePipe* pipe_;
};
ExceptionOr<ByteArray> Read(size_t size) ABSL_LOCKS_EXCLUDED(mutex_);
Exception Write(const ByteArray& data) ABSL_LOCKS_EXCLUDED(mutex_);
void MarkInputStreamClosed() ABSL_LOCKS_EXCLUDED(mutex_);
void MarkOutputStreamClosed() ABSL_LOCKS_EXCLUDED(mutex_);
Exception WriteLocked(const ByteArray& data)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Order of declaration matters:
// - mutex must be defined before condvar;
// - input & output streams must be after both mutex and condvar.
bool input_stream_closed_ ABSL_GUARDED_BY(mutex_) = false;
bool output_stream_closed_ ABSL_GUARDED_BY(mutex_) = false;
bool read_all_chunks_ ABSL_GUARDED_BY(mutex_) = false;
std::deque<ByteArray> ABSL_GUARDED_BY(mutex_) buffer_;
std::unique_ptr<api::Mutex> mutex_;
std::unique_ptr<api::ConditionVariable> cond_;
BasePipeInputStream input_stream_{this};
BasePipeOutputStream output_stream_{this};
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_BASE_BASE_PIPE_H_
+81
View File
@@ -0,0 +1,81 @@
#ifndef PLATFORM_V2_BASE_BYTE_ARRAY_H_
#define PLATFORM_V2_BASE_BYTE_ARRAY_H_
#include <cstdint>
#include <string>
#include "absl/strings/string_view.h"
namespace location {
namespace nearby {
class ByteArray {
public:
// Create an empty ByteArray
ByteArray() = default;
ByteArray(const ByteArray&) = default;
ByteArray& operator=(const ByteArray&) = default;
ByteArray(ByteArray&&) = default;
ByteArray& operator=(ByteArray&&) = default;
// Create ByteArray from string.
explicit ByteArray(absl::string_view source) { data_ = source; }
// Create default-initialized ByteArray of a given size.
explicit ByteArray(size_t size) { SetData(size); }
// Create value-initialized ByteArray of a given size.
ByteArray(const char* data, size_t size) { SetData(data, size); }
// Assign a new value to this ByteArray, as a copy of data, with a given size.
void SetData(const char* data, size_t size) {
if (data == nullptr) {
size = 0;
}
data_.assign(data, size);
}
// Assign a new value of a given size to this ByteArray
// (as a repeated char value).
void SetData(size_t size, char value = 0) { data_.assign(size, value); }
// Returns true, if changes were performed to container, false otherwise.
bool CopyAt(size_t offset, const ByteArray& from, size_t source_offset = 0) {
if (offset >= size()) return false;
if (source_offset >= from.size()) return false;
memcpy(data() + offset, from.data() + source_offset,
std::min(size() - offset, from.size() - source_offset));
return true;
}
char* data() { return &data_[0]; }
const char* data() const { return data_.data(); }
size_t size() const { return data_.size(); }
bool Empty() const { return data_.empty(); }
friend bool operator==(const ByteArray& lhs, const ByteArray& rhs);
friend bool operator!=(const ByteArray& lhs, const ByteArray& rhs);
friend bool operator<(const ByteArray& lhs, const ByteArray& rhs);
explicit operator std::string() const { return data_; }
private:
std::string data_;
};
inline bool operator==(const ByteArray& lhs, const ByteArray& rhs) {
return lhs.data_ == rhs.data_;
}
inline bool operator!=(const ByteArray& lhs, const ByteArray& rhs) {
return !(lhs == rhs);
}
inline bool operator<(const ByteArray& lhs, const ByteArray& rhs) {
return lhs.data_ < rhs.data_;
}
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_BASE_BYTE_ARRAY_H_
+68
View File
@@ -0,0 +1,68 @@
#include "platform_v2/base/byte_array.h"
#include <cstring>
#include "gtest/gtest.h"
namespace {
using location::nearby::ByteArray;
TEST(ByteArrayTest, DefaultSizeIsZero) {
ByteArray bytes;
EXPECT_EQ(0, bytes.size());
}
TEST(ByteArrayTest, DefaultIsEmpty) {
ByteArray bytes;
EXPECT_TRUE(bytes.Empty());
}
TEST(ByteArrayTest, NullArrayIsEmpty) {
ByteArray bytes{nullptr, 5};
EXPECT_TRUE(bytes.Empty());
}
TEST(ByteArrayTest, CopyAtDoesNotExtendArray) {
ByteArray v1("12345");
ByteArray v2("ABCDEFGH");
EXPECT_TRUE(v2.CopyAt(/*offset=*/5, v1));
EXPECT_TRUE(v2.CopyAt(/*offset=*/1, v1, /*source_offset=*/3));
EXPECT_EQ(v2, ByteArray("A45DE123"));
}
TEST(ByteArrayTest, CopyAtOutOfBoundsIsIgnored) {
ByteArray v1("12345");
ByteArray v2("ABCDEFGH");
// Try to do an out-of-bounds read.
EXPECT_FALSE(v2.CopyAt(/* offset=*/5, v1, /*source_offset=*/10));
// Try to do an out-of-bounds write.
EXPECT_FALSE(v2.CopyAt(/* offset=*/9, v1));
EXPECT_EQ(v2, ByteArray("ABCDEFGH"));
}
TEST(ByteArrayTest, SetFromString) {
std::string setup("setup_test");
ByteArray bytes{setup}; // array initialized with a copy of string.
EXPECT_EQ(setup.size(), bytes.size());
EXPECT_EQ(std::string(bytes), setup);
}
TEST(ByteArrayTest, SetExplicitSize) {
constexpr size_t kArraySize = 10;
char reference[kArraySize]{};
ByteArray bytes{kArraySize}; // array of size 10, zero-initialized.
EXPECT_EQ(kArraySize, bytes.size());
EXPECT_EQ(0, memcmp(bytes.data(), reference, kArraySize));
}
TEST(ByteArrayTest, SetExplicitData) {
constexpr static const char message[]{"test_message"};
constexpr size_t kMessageSize = sizeof(message);
ByteArray bytes{message, kMessageSize};
EXPECT_EQ(kMessageSize, bytes.size());
EXPECT_NE(message, bytes.data());
EXPECT_EQ(0, memcmp(message, bytes.data(), kMessageSize));
}
} // namespace
+23
View File
@@ -0,0 +1,23 @@
#ifndef PLATFORM_V2_BASE_CALLABLE_H_
#define PLATFORM_V2_BASE_CALLABLE_H_
#include <functional>
#include "platform_v2/base/exception.h"
namespace location {
namespace nearby {
// The Callable is and object intended to be executed by a thread, that is able
// to return a value of specified type T.
// It must be invokable without arguments. It must return a value implicitly
// convertible to ExceptionOr<T>.
//
// https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Callable.html
template <typename T>
using Callable = std::function<ExceptionOr<T>()>;
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_BASE_CALLABLE_H_
+97
View File
@@ -0,0 +1,97 @@
#ifndef PLATFORM_V2_BASE_EXCEPTION_H_
#define PLATFORM_V2_BASE_EXCEPTION_H_
#include <type_traits>
#include <utility>
namespace location {
namespace nearby {
struct Exception {
enum Value : int {
kFailed = -1, // Initial value of Exception; any unknown error.
kSuccess = 0, // No exception.
kIo = 1, // IO Error happened.
kInterrupted = 2, // Operation was interrupted.
kInvalidProtocolBuffer = 3, // Couldn't parse.
kExecution = 4, // Couldn't execute.
kTimeout = 5, // Operarion did not finish within specified time.
};
bool Ok() const { return value == kSuccess; }
bool Raised() const { return !Ok(); }
bool Raised(Value value) const { return this->value == value; }
Value value{kFailed};
};
constexpr inline bool operator==(const Exception& a, const Exception& b) {
return a.value == b.value;
}
constexpr inline bool operator!=(const Exception& a, const Exception& b) {
return !(a == b);
}
// ExceptionOr provides experience similar to StatusOr<T> used in
// Google Cloud API, see:
// https://googleapis.github.io/google-cloud-cpp/0.7.0/common/status__or_8h_source.html
//
// If ok() returns true, result() is a usable return value. Otherwise,
// exception() explains why such a value is not present.
//
// A typical pattern of usage is as follows:
//
// if (!e.ok()) {
// if (Exception::EXCEPTION_TYPE_1 == e.exception()) {
// // Handle Exception::EXCEPTION_TYPE_1.
// } else if (Exception::EXCEPTION_TYPE_2 == e.exception()) {
// // Handle Exception::EXCEPTION_TYPE_2.
// }
//
// return;
// }
//
// // Use e.result().
template <typename T>
class ExceptionOr {
public:
ExceptionOr() = default;
explicit ExceptionOr(T&& result)
: result_{std::move(result)},
exception_{Exception::kSuccess} {} // NOLINT
explicit ExceptionOr(const T& result)
: result_{result}, exception_{Exception::kSuccess} {} // NOLINT
ExceptionOr(Exception::Value exception) : exception_{exception} {} // NOLINT
ExceptionOr(Exception exception) : exception_{exception} {} // NOLINT
// If there exists explicit conversion from from U to T,
// then allow explicit conversion from ExceptionOr<U> to ExceptionOr<T>.
template <typename U, typename = std::void_t<decltype(T{std::declval<U>()})>>
explicit ExceptionOr<T>(ExceptionOr<U> value) {
if (!value.ok()) {
exception_ = value.GetException();
} else {
result_ = T{std::move(value.result())};
exception_ = Exception{Exception::kSuccess};
}
}
bool ok() const { return exception_.value == Exception::kSuccess; }
T& result() & { return result_; }
const T& result() const& { return result_; }
T&& result() && { return std::move(result_); }
const T&& result() const&& { return std::move(result_); }
Exception::Value exception() const { return exception_.value; }
T GetResult() const { return result_; }
Exception GetException() const { return exception_; }
private:
T result_{};
Exception exception_{Exception::kFailed};
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_BASE_EXCEPTION_H_
+106
View File
@@ -0,0 +1,106 @@
#include "platform_v2/base/exception.h"
#include <vector>
#include "platform_v2/base/exception_test.nc.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
namespace location::nearby {
TEST(ExceptionOr, Result_Copy_NonConst) {
ExceptionOr<std::vector<int>> exception_or_vector({1, 2, 3});
EXPECT_FALSE(exception_or_vector.result().empty());
// Expect a copy when not explicitly moving the result.
std::vector<int> copy = exception_or_vector.result();
EXPECT_FALSE(copy.empty());
EXPECT_FALSE(exception_or_vector.result().empty());
// Modifying |exception_or_vector| should not affect the copy.
exception_or_vector.result().clear();
EXPECT_FALSE(copy.empty());
}
TEST(ExceptionOr, Result_Copy_Const) {
const ExceptionOr<std::vector<int>> exception_or_vector({1, 2, 3});
EXPECT_FALSE(exception_or_vector.result().empty());
// Expect a copy when not explicitly moving the result.
std::vector<int> copy = exception_or_vector.result();
EXPECT_FALSE(copy.empty());
EXPECT_FALSE(exception_or_vector.result().empty());
}
TEST(ExceptionOr, Result_Reference_NonConst) {
ExceptionOr<std::vector<int>> exception_or_vector({1, 2, 3});
EXPECT_FALSE(exception_or_vector.result().empty());
// Getting a reference should not modify the source.
std::vector<int>& reference = exception_or_vector.result();
EXPECT_FALSE(reference.empty());
EXPECT_FALSE(exception_or_vector.result().empty());
// Modifying |exception_or_vector| should reflect in the reference.
exception_or_vector.result().clear();
EXPECT_TRUE(reference.empty());
}
TEST(ExceptionOr, Result_Reference_Const) {
const ExceptionOr<std::vector<int>> exception_or_vector({1, 2, 3});
EXPECT_FALSE(exception_or_vector.result().empty());
// Getting a reference should not modify the source.
const std::vector<int>& reference = exception_or_vector.result();
EXPECT_FALSE(reference.empty());
EXPECT_FALSE(exception_or_vector.result().empty());
}
TEST(ExceptionOr, Result_Move_NonConst) {
ExceptionOr<std::vector<int>> exception_or_vector({1, 2, 3});
EXPECT_FALSE(exception_or_vector.result().empty());
// Moving the result should clear the source.
std::vector<int> moved = std::move(exception_or_vector).result();
EXPECT_FALSE(moved.empty());
}
TEST(ExceptionOr, Result_Move_Const) {
const ExceptionOr<std::vector<int>> exception_or_vector({1, 2, 3});
EXPECT_FALSE(exception_or_vector.result().empty());
// Moving const rvalue reference will result in a copy.
std::vector<int> moved = std::move(exception_or_vector).result();
EXPECT_FALSE(moved.empty());
}
TEST(ExceptionOr, ExplicitConversionWorks) {
class A {
public:
A() = default;
};
class B {
public:
B() = default;
explicit B(A) {}
};
ExceptionOr<A> a(A{});
ExceptionOr<B> b(a);
EXPECT_TRUE(a.ok());
EXPECT_TRUE(b.ok());
}
TEST(ExceptionOr, ExplicitConversionFailsToCompile) {
class A {
public:
A() = default;
};
class B {
public:
B() = default;
};
ExceptionOr<A> a(A{});
EXPECT_NON_COMPILE("no matching constructor", { ExceptionOr<B> b(a); });
}
} // namespace location::nearby
+28
View File
@@ -0,0 +1,28 @@
#ifndef PLATFORM_V2_BASE_INPUT_STREAM_H_
#define PLATFORM_V2_BASE_INPUT_STREAM_H_
#include <cstdint>
#include "platform_v2/base/byte_array.h"
#include "platform_v2/base/exception.h"
namespace location {
namespace nearby {
// An InputStream represents an input stream of bytes.
//
// https://docs.oracle.com/javase/8/docs/api/java/io/InputStream.html
class InputStream {
public:
virtual ~InputStream() = default;
// throws Exception::kIo
virtual ExceptionOr<ByteArray> Read(std::int64_t size) = 0;
// throws Exception::kIo
virtual Exception Close() = 0;
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_BASE_INPUT_STREAM_H_
+20
View File
@@ -0,0 +1,20 @@
#ifndef PLATFORM_V2_BASE_LISTENERS_H_
#define PLATFORM_V2_BASE_LISTENERS_H_
#include <functional>
namespace location {
namespace nearby {
// Provides default-initialization with a valid empty method,
// instead of nullptr. This allows partial initialization
// of a set of listeners.
template <typename... Args>
constexpr std::function<void(Args...)> DefaultCallback() {
return std::function<void(Args...)>{[](Args...) {}};
}
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_BASE_LISTENERS_H_
+25
View File
@@ -0,0 +1,25 @@
#ifndef PLATFORM_V2_BASE_OUTPUT_STREAM_H_
#define PLATFORM_V2_BASE_OUTPUT_STREAM_H_
#include "platform_v2/base/byte_array.h"
#include "platform_v2/base/exception.h"
namespace location {
namespace nearby {
// An OutputStream represents an output stream of bytes.
//
// https://docs.oracle.com/javase/8/docs/api/java/io/OutputStream.html
class OutputStream {
public:
virtual ~OutputStream() = default;
virtual Exception Write(const ByteArray& data) = 0; // throws Exception::kIo
virtual Exception Flush() = 0; // throws Exception::kIo
virtual Exception Close() = 0; // throws Exception::kIo
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_BASE_OUTPUT_STREAM_H_
+45
View File
@@ -0,0 +1,45 @@
#include "platform_v2/base/prng.h"
#include <limits>
#include "absl/time/clock.h"
namespace location {
namespace nearby {
#define UNSIGNED_INT_BITMASK (std::numeric_limits<unsigned int>::max())
Prng::Prng() {
// absl::GetCurrentTimeNanos() returns 64 bits, but srand() wants an unsigned
// int, so we may have to lose some of those 64 bits.
//
// The lower bits of the current-time-in-nanos are likely to have more entropy
// than the upper bits, so choose the former.
srand(static_cast<unsigned int>(absl::GetCurrentTimeNanos() &
UNSIGNED_INT_BITMASK));
}
Prng::~Prng() {
// Nothing to do.
}
#define RANDOM_BYTE (rand() & 0x0FF) // NOLINT
std::int32_t Prng::NextInt32() {
return (static_cast<std::int32_t>(RANDOM_BYTE) << 24) |
(static_cast<std::int32_t>(RANDOM_BYTE) << 16) |
(static_cast<std::int32_t>(RANDOM_BYTE) << 8) |
(static_cast<std::int32_t>(RANDOM_BYTE));
}
std::uint32_t Prng::NextUint32() {
return static_cast<std::uint32_t>(NextInt32());
}
std::int64_t Prng::NextInt64() {
return (static_cast<std::int64_t>(NextInt32()) << 32) |
(static_cast<std::int64_t>(NextInt32()));
}
} // namespace nearby
} // namespace location
+23
View File
@@ -0,0 +1,23 @@
#ifndef PLATFORM_V2_BASE_PRNG_H_
#define PLATFORM_V2_BASE_PRNG_H_
#include <cstdint>
namespace location {
namespace nearby {
// A (non-cryptographic) pseudo-random number generator.
class Prng {
public:
Prng();
~Prng();
std::int32_t NextInt32();
std::uint32_t NextUint32();
std::int64_t NextInt64();
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_BASE_PRNG_H_
+27
View File
@@ -0,0 +1,27 @@
#include "platform_v2/base/prng.h"
#include "gtest/gtest.h"
namespace location {
namespace nearby {
TEST(PrngTest, NextInt32) {
std::int32_t i = Prng().NextInt32();
EXPECT_LE(i, std::numeric_limits<std::int32_t>::max());
EXPECT_GE(i, std::numeric_limits<std::int32_t>::min());
}
TEST(PrngTest, NextUInt32) {
std::uint32_t i = Prng().NextUint32();
EXPECT_LE(i, std::numeric_limits<std::uint32_t>::max());
EXPECT_GE(i, std::numeric_limits<std::uint32_t>::min());
}
TEST(PrngTest, NextInt64) {
std::int64_t i = Prng().NextInt64();
EXPECT_LE(i, std::numeric_limits<std::int64_t>::max());
EXPECT_GE(i, std::numeric_limits<std::int64_t>::min());
}
} // namespace nearby
} // namespace location
+19
View File
@@ -0,0 +1,19 @@
#ifndef PLATFORM_V2_BASE_RUNNABLE_H_
#define PLATFORM_V2_BASE_RUNNABLE_H_
#include <functional>
namespace location {
namespace nearby {
// The Runnable is an object intended to be executed by a thread.
// It must be invokable without arguments. It must return void.
//
// https://docs.oracle.com/javase/8/docs/api/java/lang/Runnable.html
using Runnable = std::function<void()>;
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_BASE_RUNNABLE_H_
+25
View File
@@ -0,0 +1,25 @@
#ifndef PLATFORM_V2_BASE_SOCKET_H_
#define PLATFORM_V2_BASE_SOCKET_H_
#include "platform_v2/base/input_stream.h"
#include "platform_v2/base/output_stream.h"
namespace location {
namespace nearby {
// A socket is an endpoint for communication between two machines.
//
// https://docs.oracle.com/javase/8/docs/api/java/net/Socket.html
class Socket {
public:
virtual ~Socket() = default;
virtual InputStream& GetInputStream() = 0;
virtual OutputStream& GetOutputStream() = 0;
virtual void Close() = 0;
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_BASE_SOCKET_H_
+21
View File
@@ -0,0 +1,21 @@
cc_library(
name = "config",
hdrs = [
"config.h",
],
visibility = [
"//visibility:private",
],
)
cc_library(
name = "string",
hdrs = [
"string.h",
],
visibility = [
],
deps = [
":config",
],
)
+22
View File
@@ -0,0 +1,22 @@
#ifndef PLATFORM_V2_CONFIG_CONFIG_H_
#define PLATFORM_V2_CONFIG_CONFIG_H_
// Clients can modify this file to customize the Nearby C++ codebase as per
// their particular constraints and environments.
// Note: Every entry in this file should conform to the following format, to
// give precedence to command-line options (-D) that set these symbols:
//
// #ifndef XXX
// #define XXX 0/1
// #endif
#ifndef NEARBY_USE_STD_STRING
#define NEARBY_USE_STD_STRING 0
#endif
#ifndef NEARBY_USE_RTTI
#define NEARBY_USE_RTTI 1
#endif
#endif // PLATFORM_V2_CONFIG_CONFIG_H_
+12
View File
@@ -0,0 +1,12 @@
#ifndef PLATFORM_V2_CONFIG_STRING_H_
#define PLATFORM_V2_CONFIG_STRING_H_
#include <string>
#include "platform_v2/config/config.h"
#if NEARBY_USE_STD_STRING
using std::string;
#endif
#endif // PLATFORM_V2_CONFIG_STRING_H_
+57
View File
@@ -0,0 +1,57 @@
cc_library(
name = "g3",
srcs = [
"atomic_boolean.h",
"atomic_reference_any.h",
"bluetooth_adapter.cc",
"bluetooth_adapter.h",
"condition_variable.h",
"count_down_latch.h",
"medium_environment.cc",
"medium_environment.h",
"multi_thread_executor.h",
"mutex.h",
"platform.cc",
"scheduled_executor.cc",
"scheduled_executor.h",
"settable_future_any.h",
"single_thread_executor.h",
"system_clock.cc",
],
visibility = [
"//googlemac/iPhone/Shared/Nearby/Connections:__subpackages__",
"//core_v2:__subpackages__",
"//platform_v2:__subpackages__",
],
deps = [
":crypto", # build_cleaner: keep
"//platform_v2/api",
"//platform_v2/base",
"//platform_v2/impl/shared:posix_mutex",
"//absl/base:core_headers",
"//absl/container:flat_hash_map",
"//absl/container:flat_hash_set",
"//absl/memory",
"//absl/strings",
"//absl/synchronization",
"//absl/time",
"//absl/types:any",
"//thread",
],
)
cc_library(
name = "crypto",
srcs = [
"crypto.cc",
],
visibility = [
"//platform_v2/g3:__pkg__",
],
deps = [
"//platform_v2/api",
"//platform_v2/base",
"//absl/strings",
"//openssl:crypto",
],
)
+30
View File
@@ -0,0 +1,30 @@
#ifndef PLATFORM_V2_IMPL_G3_ATOMIC_BOOLEAN_H_
#define PLATFORM_V2_IMPL_G3_ATOMIC_BOOLEAN_H_
#include <atomic>
#include "platform_v2/api/atomic_boolean.h"
namespace location {
namespace nearby {
namespace g3 {
// See documentation in
// https://source.corp.google.com/piper///depot/google3/platform_v2/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_V2_IMPL_G3_ATOMIC_BOOLEAN_H_
@@ -0,0 +1,46 @@
#ifndef PLATFORM_V2_IMPL_G3_ATOMIC_REFERENCE_ANY_H_
#define PLATFORM_V2_IMPL_G3_ATOMIC_REFERENCE_ANY_H_
#include "platform_v2/api/atomic_reference.h"
#include "absl/base/integral_types.h"
#include "absl/synchronization/mutex.h"
#include "absl/types/any.h"
namespace location {
namespace nearby {
namespace g3 {
// Provide implementation for absl::any.
class AtomicReferenceAny : public api::AtomicReference<absl::any> {
public:
explicit AtomicReferenceAny(absl::any initial_value)
: value_(std::move(initial_value)) {}
~AtomicReferenceAny() override = default;
absl::any Get() const & override {
absl::MutexLock lock(&mutex_);
return value_;
}
absl::any Get() && override {
absl::MutexLock lock(&mutex_);
return std::move(value_);
}
void Set(const absl::any& value) override {
absl::MutexLock lock(&mutex_);
value_ = value;
}
void Set(absl::any&& value) override {
absl::MutexLock lock(&mutex_);
value_ = std::move(value);
}
private:
mutable absl::Mutex mutex_;
absl::any value_;
};
} // namespace g3
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_IMPL_G3_ATOMIC_REFERENCE_ANY_H_
@@ -0,0 +1,65 @@
#include "platform_v2/impl/g3/bluetooth_adapter.h"
#include <string>
#include "platform_v2/impl/g3/medium_environment.h"
namespace location {
namespace nearby {
namespace g3 {
BluetoothDevice::BluetoothDevice(BluetoothAdapter* adapter)
: adapter_(*adapter) {}
std::string BluetoothDevice::GetName() const { return adapter_.GetName(); }
bool BluetoothAdapter::SetStatus(Status status) ABSL_LOCKS_EXCLUDED(mutex_) {
absl::MutexLock lock(&mutex_);
enabled_ = (status == Status::kEnabled);
RunOnCallbackThread([this]() {
auto& env = MediumEnvironment::Instance();
env.OnBluetoothAdapterChangedState(*this);
});
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) {
absl::MutexLock lock(&mutex_);
if (enabled_) return false;
mode_ = mode;
RunOnCallbackThread([this]() {
auto& env = MediumEnvironment::Instance();
env.OnBluetoothAdapterChangedState(*this);
});
return true;
}
std::string BluetoothAdapter::GetName() const {
absl::MutexLock lock(&mutex_);
return name_;
}
bool BluetoothAdapter::SetName(absl::string_view name) {
absl::MutexLock lock(&mutex_);
if (enabled_) return false;
name_ = name;
RunOnCallbackThread([this]() {
auto& env = MediumEnvironment::Instance();
env.OnBluetoothAdapterChangedState(*this);
});
return true;
}
} // namespace g3
} // namespace nearby
} // namespace location
@@ -0,0 +1,90 @@
#ifndef PLATFORM_V2_IMPL_G3_BLUETOOTH_ADAPTER_H_
#define PLATFORM_V2_IMPL_G3_BLUETOOTH_ADAPTER_H_
#include <string>
#include "platform_v2/api/bluetooth_adapter.h"
#include "platform_v2/api/bluetooth_classic.h"
#include "platform_v2/impl/g3/single_thread_executor.h"
#include "absl/base/thread_annotations.h"
#include "absl/strings/string_view.h"
#include "absl/synchronization/mutex.h"
namespace location {
namespace nearby {
namespace g3 {
// BluetoothDevice and BluetoothAdapter have a mutual dependency.
class BluetoothAdapter;
// 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;
BluetoothAdapter& GetAdapter();
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() = default;
~BluetoothAdapter() override = default;
// 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_);
BluetoothDevice& GetDevice() { return device_; }
private:
void RunOnCallbackThread(std::function<void()> runnable) {
serial_executor_.Execute(std::move(runnable));
}
mutable absl::Mutex mutex_;
BluetoothDevice device_{this};
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;
SingleThreadExecutor serial_executor_;
};
} // namespace g3
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_IMPL_G3_BLUETOOTH_ADAPTER_H_
@@ -0,0 +1,33 @@
#ifndef PLATFORM_V2_IMPL_G3_CONDITION_VARIABLE_H_
#define PLATFORM_V2_IMPL_G3_CONDITION_VARIABLE_H_
#include "platform_v2/api/condition_variable.h"
#include "platform_v2/base/exception.h"
#include "platform_v2/impl/g3/mutex.h"
#include "absl/synchronization/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};
}
void Notify() override { cond_var_.SignalAll(); }
private:
absl::Mutex* mutex_;
absl::CondVar cond_var_;
};
} // namespace g3
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_IMPL_G3_CONDITION_VARIABLE_H_
@@ -0,0 +1,59 @@
#ifndef PLATFORM_V2_IMPL_G3_COUNT_DOWN_LATCH_H_
#define PLATFORM_V2_IMPL_G3_COUNT_DOWN_LATCH_H_
#include "platform_v2/api/count_down_latch.h"
#include "absl/base/thread_annotations.h"
#include "absl/synchronization/mutex.h"
#include "absl/time/clock.h"
namespace location {
namespace nearby {
namespace g3 {
// 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 final : public api::CountDownLatch {
public:
explicit CountDownLatch(int count) : count_(count) {}
CountDownLatch(const CountDownLatch&) = delete;
CountDownLatch& operator=(const CountDownLatch&) = delete;
CountDownLatch(CountDownLatch&&) = delete;
CountDownLatch& operator=(CountDownLatch&&) = delete;
ExceptionOr<bool> Await(absl::Duration timeout) override {
absl::MutexLock lock(&mutex_);
absl::Time deadline = absl::Now() + timeout;
while (count_ > 0) {
if (cond_.WaitWithDeadline(&mutex_, deadline)) {
return ExceptionOr<bool>(false);
}
}
return ExceptionOr<bool>(true);
}
Exception Await() override {
absl::MutexLock lock(&mutex_);
while (count_ > 0) {
cond_.Wait(&mutex_);
}
return {Exception::kSuccess};
}
void CountDown() override {
absl::MutexLock lock(&mutex_);
if (count_ > 0 && --count_ == 0) {
cond_.SignalAll();
}
}
private:
absl::Mutex mutex_; // Mutex to be used with cond_.Wait...() method family.
absl::CondVar cond_; // Condition to synchronize up to N waiting threads.
int count_
ABSL_GUARDED_BY(mutex_); // When zero, latch should release all waiters.
};
} // namespace g3
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_IMPL_G3_COUNT_DOWN_LATCH_H_
+39
View File
@@ -0,0 +1,39 @@
#include "platform_v2/api/crypto.h"
#include <cstdint>
#include <string>
#include "platform_v2/base/byte_array.h"
#include "absl/strings/string_view.h"
#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,32 @@
#include "platform_v2/impl/g3/medium_environment.h"
namespace location {
namespace nearby {
namespace g3 {
MediumEnvironment& MediumEnvironment::Instance() {
static std::aligned_storage_t<sizeof(MediumEnvironment),
alignof(MediumEnvironment)>
storage;
static MediumEnvironment* env = new (&storage) MediumEnvironment();
return *env;
}
void MediumEnvironment::Reset() {
absl::MutexLock lock(&mutex_);
bluetooth_adapters_.clear();
}
void MediumEnvironment::OnBluetoothAdapterChangedState(
BluetoothAdapter& adapter) {
absl::MutexLock lock(&mutex_);
// We don't care if there is an adapter already since all we store is a
// pointer.
bluetooth_adapters_.emplace(&adapter);
// TODO(apolyudov): Add event propagation code when Medium registration is
// implemented.
}
} // namespace g3
} // namespace nearby
} // namespace location
@@ -0,0 +1,47 @@
#ifndef PLATFORM_V2_IMPL_G3_MEDIUM_ENVIRONMENT_H_
#define PLATFORM_V2_IMPL_G3_MEDIUM_ENVIRONMENT_H_
#include <new>
#include <string>
#include <type_traits>
#include "platform_v2/api/bluetooth_classic.h"
#include "platform_v2/impl/g3/bluetooth_adapter.h"
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/synchronization/mutex.h"
namespace location {
namespace nearby {
namespace g3 {
// MediumEnvironment is a simulated environment which allowes multiple instances
// of simulated HW devices to "work" together as if they are physical.
// For each medium type it provides necessary methods to implement
// advertising, discovery and establishment of a data link.
class MediumEnvironment {
public:
~MediumEnvironment() = default;
// Singleton constructor/accessor.
static MediumEnvironment& Instance();
// Clear state. No notifications are sent.
void Reset() ABSL_LOCKS_EXCLUDED(mutex_);
// Add an adapter to internal container.
// Notify BluetoothClassicMediums if any that adapter state has changed.
void OnBluetoothAdapterChangedState(BluetoothAdapter& adapter)
ABSL_LOCKS_EXCLUDED(mutex_);
private:
MediumEnvironment() = default;
absl::Mutex mutex_;
absl::flat_hash_set<BluetoothAdapter*> bluetooth_adapters_
ABSL_GUARDED_BY(mutex_);
};
} // namespace g3
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_IMPL_G3_MEDIUM_ENVIRONMENT_H_
@@ -0,0 +1,54 @@
#ifndef PLATFORM_V2_IMPL_G3_MULTI_THREAD_EXECUTOR_H_
#define PLATFORM_V2_IMPL_G3_MULTI_THREAD_EXECUTOR_H_
#include <atomic>
#include "platform_v2/api/submittable_executor.h"
#include "platform_v2/impl/g3/count_down_latch.h"
#include "absl/time/clock.h"
#include "thread/threadpool.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_V2_IMPL_G3_MULTI_THREAD_EXECUTOR_H_
+47
View File
@@ -0,0 +1,47 @@
#ifndef PLATFORM_V2_IMPL_G3_MUTEX_H_
#define PLATFORM_V2_IMPL_G3_MUTEX_H_
#include "platform_v2/api/mutex.h"
#include "platform_v2/impl/shared/posix_mutex.h"
#include "absl/synchronization/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_V2_IMPL_G3_MUTEX_H_
+30
View File
@@ -0,0 +1,30 @@
#ifndef PLATFORM_V2_IMPL_G3_PIPE_H_
#define PLATFORM_V2_IMPL_G3_PIPE_H_
#include <memory>
#include "platform_v2/base/base_pipe.h"
#include "platform_v2/impl/g3/condition_variable.h"
#include "platform_v2/impl/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_V2_IMPL_G3_PIPE_H_
+136
View File
@@ -0,0 +1,136 @@
#include "platform_v2/api/platform.h"
#include <atomic>
#include <memory>
#include "platform_v2/api/atomic_boolean.h"
#include "platform_v2/api/atomic_reference.h"
#include "platform_v2/api/ble.h"
#include "platform_v2/api/ble_v2.h"
#include "platform_v2/api/bluetooth_adapter.h"
#include "platform_v2/api/bluetooth_classic.h"
#include "platform_v2/api/condition_variable.h"
#include "platform_v2/api/count_down_latch.h"
#include "platform_v2/api/mutex.h"
#include "platform_v2/api/scheduled_executor.h"
#include "platform_v2/api/server_sync.h"
#include "platform_v2/api/settable_future.h"
#include "platform_v2/api/submittable_executor.h"
#include "platform_v2/api/webrtc.h"
#include "platform_v2/api/wifi.h"
#include "platform_v2/impl/g3/atomic_boolean.h"
#include "platform_v2/impl/g3/atomic_reference_any.h"
#include "platform_v2/impl/g3/bluetooth_adapter.h"
#include "platform_v2/impl/g3/condition_variable.h"
#include "platform_v2/impl/g3/count_down_latch.h"
#include "platform_v2/impl/g3/multi_thread_executor.h"
#include "platform_v2/impl/g3/mutex.h"
#include "platform_v2/impl/g3/scheduled_executor.h"
#include "platform_v2/impl/g3/settable_future_any.h"
#include "platform_v2/impl/g3/single_thread_executor.h"
#include "absl/base/integral_types.h"
#include "absl/memory/memory.h"
#include "absl/time/time.h"
namespace location {
namespace nearby {
namespace api {
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<AtomicReference<absl::any>>
ImplementationPlatform::CreateAtomicReferenceAny(absl::any initial_value) {
return absl::make_unique<g3::AtomicReferenceAny>(initial_value);
}
std::unique_ptr<SettableFuture<absl::any>>
ImplementationPlatform::CreateSettableFutureAny() {
return absl::make_unique<g3::SettableFutureAny>();
}
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<g3::CountDownLatch>(count);
}
std::unique_ptr<AtomicBoolean> ImplementationPlatform::CreateAtomicBoolean(
bool initial_value) {
return absl::make_unique<g3::AtomicBoolean>(initial_value);
}
std::unique_ptr<BluetoothClassicMedium>
ImplementationPlatform::CreateBluetoothClassicMedium() {
return std::unique_ptr<BluetoothClassicMedium>();
}
std::unique_ptr<BleMedium> ImplementationPlatform::CreateBleMedium() {
return std::unique_ptr<BleMedium>();
}
std::unique_ptr<ble_v2::BleMedium> ImplementationPlatform::CreateBleV2Medium() {
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 std::unique_ptr<WifiLanMedium>();
}
std::unique_ptr<WebRtcSignalingMessenger>
ImplementationPlatform::CreateWebRtcSignalingMessenger(
absl::string_view self_id) {
return std::unique_ptr<WebRtcSignalingMessenger>(
/*new FCMSignalingMessenger()*/);
}
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)));
}
std::string ImplementationPlatform::GetDeviceId() {
// TODO(alexchau): Get deviceId from base
return "google3";
}
std::string ImplementationPlatform::GetPayloadPath(int64_t payload_id) {
return "/tmp/" + std::to_string(payload_id);
}
} // namespace api
} // namespace nearby
} // namespace location
@@ -0,0 +1,65 @@
#include "platform_v2/impl/g3/scheduled_executor.h"
#include <atomic>
#include <memory>
#include "platform_v2/api/cancelable.h"
#include "platform_v2/base/runnable.h"
#include "absl/time/clock.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,42 @@
#ifndef PLATFORM_V2_IMPL_G3_SCHEDULED_EXECUTOR_H_
#define PLATFORM_V2_IMPL_G3_SCHEDULED_EXECUTOR_H_
#include <atomic>
#include <memory>
#include "platform_v2/api/cancelable.h"
#include "platform_v2/api/scheduled_executor.h"
#include "platform_v2/base/runnable.h"
#include "platform_v2/impl/g3/single_thread_executor.h"
#include "absl/time/clock.h"
#include "thread/threadpool.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_V2_IMPL_G3_SCHEDULED_EXECUTOR_H_
@@ -0,0 +1,104 @@
#ifndef PLATFORM_V2_IMPL_G3_SETTABLE_FUTURE_ANY_H_
#define PLATFORM_V2_IMPL_G3_SETTABLE_FUTURE_ANY_H_
#include <utility>
#include "platform_v2/api/platform.h"
#include "platform_v2/api/settable_future.h"
#include "absl/synchronization/mutex.h"
#include "absl/time/clock.h"
#include "absl/types/any.h"
namespace location {
namespace nearby {
namespace g3 {
class SettableFutureAny : public api::SettableFuture<absl::any> {
public:
SettableFutureAny() = default;
~SettableFutureAny() override = default;
bool Set(const absl::any& value) override {
absl::MutexLock lock(&mutex_);
if (!done_) {
value_ = value;
done_ = true;
exception_ = {Exception::kSuccess};
completed_.SignalAll();
}
return true;
}
bool Set(absl::any&& value) override {
absl::MutexLock lock(&mutex_);
if (!done_) {
value_ = std::move(value);
done_ = true;
exception_ = {Exception::kSuccess};
completed_.SignalAll();
}
return true;
}
bool SetException(Exception exception) override {
absl::MutexLock lock(&mutex_);
return SetExceptionLocked(exception);
}
void AddListener(Runnable runnable, api::Executor* executor) override {}
ExceptionOr<std::any> Get() override {
absl::MutexLock lock(&mutex_);
while (!done_) {
completed_.Wait(&mutex_);
}
return exception_.value != Exception::kSuccess
? ExceptionOr<std::any>{exception_.value}
: ExceptionOr<std::any>{value_};
}
ExceptionOr<std::any> Get(absl::Duration timeout) override {
absl::MutexLock lock(&mutex_);
while (!done_) {
absl::Time start_time = absl::Now();
if (completed_.WaitWithTimeout(&mutex_, timeout)) {
SetExceptionLocked({Exception::kTimeout});
break;
}
absl::Duration spent = absl::Now() - start_time;
if (spent < timeout) {
timeout -= spent;
} else if (!done_) {
SetExceptionLocked({Exception::kTimeout});
break;
}
}
return exception_.value != Exception::kSuccess
? ExceptionOr<std::any>{exception_.value}
: ExceptionOr<std::any>{value_};
}
private:
bool SetExceptionLocked(Exception exception) {
if (!done_) {
exception_ = exception.value != Exception::kSuccess
? exception
: Exception{Exception::kFailed};
done_ = true;
completed_.SignalAll();
}
return true;
}
absl::Mutex mutex_;
absl::CondVar completed_;
bool done_{false};
absl::any value_;
Exception exception_{Exception::kFailed};
};
} // namespace g3
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_IMPL_G3_SETTABLE_FUTURE_ANY_H_
@@ -0,0 +1,22 @@
#ifndef PLATFORM_V2_IMPL_G3_SINGLE_THREAD_EXECUTOR_H_
#define PLATFORM_V2_IMPL_G3_SINGLE_THREAD_EXECUTOR_H_
#include "platform_v2/impl/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_V2_IMPL_G3_SINGLE_THREAD_EXECUTOR_H_
+16
View File
@@ -0,0 +1,16 @@
#include "platform_v2/api/system_clock.h"
#include "platform_v2/base/exception.h"
#include "absl/time/clock.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
+34
View File
@@ -0,0 +1,34 @@
cc_library(
name = "posix_mutex",
srcs = [
"posix_mutex.cc",
],
hdrs = [
"posix_mutex.h",
],
visibility = [
"//platform_v2/impl:__subpackages__",
],
deps = [
"//platform_v2/api",
"//platform_v2/base",
],
)
cc_library(
name = "posix_condition_variable",
srcs = [
"posix_condition_variable.cc",
],
hdrs = [
"posix_condition_variable.h",
],
visibility = [
"//platform_v2/impl:__subpackages__",
],
deps = [
":posix_mutex",
"//platform_v2/api",
"//platform_v2/base",
],
)
@@ -0,0 +1,30 @@
#include "platform_v2/impl/shared/posix_condition_variable.h"
namespace location {
namespace nearby {
namespace posix {
ConditionVariable::ConditionVariable(Mutex* mutex)
: mutex_(mutex), attr_(), cond_() {
pthread_condattr_init(&attr_);
pthread_cond_init(&cond_, &attr_);
}
ConditionVariable::~ConditionVariable() {
pthread_cond_destroy(&cond_);
pthread_condattr_destroy(&attr_);
}
void ConditionVariable::Notify() { pthread_cond_broadcast(&cond_); }
Exception ConditionVariable::Wait() {
pthread_cond_wait(&cond_, &(mutex_->mutex_));
return {Exception::kSuccess};
}
} // namespace posix
} // namespace nearby
} // namespace location
@@ -0,0 +1,31 @@
#ifndef PLATFORM_V2_IMPL_SHARED_POSIX_CONDITION_VARIABLE_H_
#define PLATFORM_V2_IMPL_SHARED_POSIX_CONDITION_VARIABLE_H_
#include <pthread.h>
#include "platform_v2/api/condition_variable.h"
#include "platform_v2/impl/shared/posix_mutex.h"
namespace location {
namespace nearby {
namespace posix {
class ConditionVariable : public api::ConditionVariable {
public:
explicit ConditionVariable(Mutex* mutex);
~ConditionVariable() override;
void Notify() override;
Exception Wait() override;
private:
Mutex* mutex_;
pthread_condattr_t attr_;
pthread_cond_t cond_;
};
} // namespace posix
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_IMPL_SHARED_POSIX_CONDITION_VARIABLE_H_
@@ -0,0 +1,26 @@
#include "platform_v2/impl/shared/posix_mutex.h"
namespace location {
namespace nearby {
namespace posix {
Mutex::Mutex() : attr_(), mutex_() {
pthread_mutexattr_init(&attr_);
pthread_mutexattr_settype(&attr_, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&mutex_, &attr_);
}
Mutex::~Mutex() {
pthread_mutex_destroy(&mutex_);
pthread_mutexattr_destroy(&attr_);
}
void Mutex::Lock() { pthread_mutex_lock(&mutex_); }
void Mutex::Unlock() { pthread_mutex_unlock(&mutex_); }
} // namespace posix
} // namespace nearby
} // namespace location
+31
View File
@@ -0,0 +1,31 @@
#ifndef PLATFORM_V2_IMPL_SHARED_POSIX_MUTEX_H_
#define PLATFORM_V2_IMPL_SHARED_POSIX_MUTEX_H_
#include <pthread.h>
#include "platform_v2/api/mutex.h"
namespace location {
namespace nearby {
namespace posix {
class ABSL_LOCKABLE Mutex : public api::Mutex {
public:
Mutex();
~Mutex() override;
void Lock() ABSL_EXCLUSIVE_LOCK_FUNCTION() override;
void Unlock() ABSL_UNLOCK_FUNCTION() override;
private:
friend class ConditionVariable;
pthread_mutexattr_t attr_;
pthread_mutex_t mutex_;
};
} // namespace posix
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_IMPL_SHARED_POSIX_MUTEX_H_
+86
View File
@@ -0,0 +1,86 @@
cc_library(
name = "public",
srcs = [
"file.cc",
"pipe.cc",
],
hdrs = [
"atomic_boolean.h",
"atomic_reference.h",
"bluetooth_adapter.h",
"cancelable.h",
"cancelable_alarm.h",
"condition_variable.h",
"count_down_latch.h",
"crypto.h",
"file.h",
"future.h",
"multi_thread_executor.h",
"mutex.h",
"mutex_lock.h",
"pipe.h",
"scheduled_executor.h",
"single_thread_executor.h",
"submittable_executor.h",
"system_clock.h",
],
visibility = [
"//core_v2:__subpackages__",
"//platform_v2/impl:__subpackages__",
],
deps = [
"//platform_v2/api",
"//platform_v2/base",
"//platform_v2/base:util",
"//absl/base:core_headers",
"//absl/strings",
"//absl/time",
"//absl/types:any",
],
)
cc_library(
name = "logging",
hdrs = [
"logging.h",
],
visibility = [
"//googlemac/iPhone/Shared/Nearby/Connections:__subpackages__",
"//core_v2:__subpackages__",
"//platform_v2:__subpackages__",
],
deps = [
"//platform:logging",
],
)
cc_test(
name = "public_test",
srcs = [
"atomic_boolean_test.cc",
"atomic_reference_test.cc",
"bluetooth_adapter_test.cc",
"count_down_latch_test.cc",
"crypto_test.cc",
"file_test.cc",
"future_test.cc",
"logging_test.cc",
"multi_thread_executor_test.cc",
"mutex_test.cc",
"pipe_test.cc",
"scheduled_executor_test.cc",
"single_thread_executor_test.cc",
],
shard_count = 16,
deps = [
":logging",
":public",
"//file/util:temp_path",
"//platform_v2/base",
"//platform_v2/impl/g3",
"//testing/base/public:gunit_main",
"//absl/strings",
"//absl/synchronization",
"//absl/time",
],
)
+34
View File
@@ -0,0 +1,34 @@
#ifndef PLATFORM_V2_PUBLIC_ATOMIC_BOOLEAN_H_
#define PLATFORM_V2_PUBLIC_ATOMIC_BOOLEAN_H_
#include <memory>
#include "platform_v2/api/atomic_boolean.h"
#include "platform_v2/api/platform.h"
namespace location {
namespace nearby {
// A boolean value that may be updated atomically.
// See documentation in
// https://source.corp.google.com/piper///depot/google3/platform_v2/api/atomic_boolean.h
class AtomicBoolean final : public api::AtomicBoolean {
public:
using Platform = api::ImplementationPlatform;
explicit AtomicBoolean(bool value = false)
: impl_(Platform::CreateAtomicBoolean(value)) {}
~AtomicBoolean() override = default;
AtomicBoolean(AtomicBoolean&&) = default;
AtomicBoolean& operator=(AtomicBoolean&&) = default;
bool Get() const override { return impl_->Get(); }
bool Set(bool value) override { return impl_->Set(value); }
private:
std::unique_ptr<api::AtomicBoolean> impl_;
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_PUBLIC_ATOMIC_BOOLEAN_H_
@@ -0,0 +1,24 @@
#include "platform_v2/public/atomic_boolean.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
namespace location {
namespace nearby {
namespace {
TEST(AtomicBooleanTest, SetReturnsPrevoiusValue) {
AtomicBoolean value(false);
EXPECT_FALSE(value.Set(true));
EXPECT_TRUE(value.Set(true));
}
TEST(AtomicBooleanTest, GetReturnsWhatWasSet) {
AtomicBoolean value(false);
EXPECT_FALSE(value.Set(true));
EXPECT_TRUE(value.Get());
}
} // namespace
} // namespace nearby
} // namespace location
+40
View File
@@ -0,0 +1,40 @@
#ifndef PLATFORM_V2_PUBLIC_ATOMIC_REFERENCE_H_
#define PLATFORM_V2_PUBLIC_ATOMIC_REFERENCE_H_
#include <memory>
#include "platform_v2/api/atomic_reference.h"
#include "platform_v2/api/platform.h"
#include "absl/types/any.h"
namespace location {
namespace nearby {
// An object reference that may be updated atomically.
//
// https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/AtomicReference.html
template <typename T>
class AtomicReference final : public api::AtomicReference<T> {
public:
using Platform = api::ImplementationPlatform;
explicit AtomicReference(const T& value)
: impl_(Platform::CreateAtomicReferenceAny(value)) {}
explicit AtomicReference(T&& value)
: impl_(Platform::CreateAtomicReferenceAny(std::move(value))) {}
~AtomicReference() override = default;
AtomicReference(AtomicReference&&) = default;
AtomicReference& operator=(AtomicReference&&) = default;
T Get() const& override { return absl::any_cast<T>(impl_->Get()); }
T Get() && override { return absl::any_cast<T>(std::move(impl_->Get())); }
void Set(const T& value) override { impl_->Set(absl::any(value)); }
void Set(T&& value) override { impl_->Set(absl::any(value)); }
private:
std::unique_ptr<api::AtomicReference<absl::any>> impl_;
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_PUBLIC_ATOMIC_REFERENCE_H_
@@ -0,0 +1,75 @@
#include "platform_v2/public/atomic_reference.h"
#include "gtest/gtest.h"
namespace location {
namespace nearby {
namespace {
struct BigSizedStruct {
int data[100]{};
};
enum TestEnum {
kValue1 = 1,
kValue2 = 2,
};
enum class ScopedTestEnum {
kValue1 = 1,
kValue2 = 2,
};
bool operator==(const BigSizedStruct& a, const BigSizedStruct& b) {
return memcmp(a.data, b.data, sizeof(BigSizedStruct::data)) == 0;
}
bool operator!=(const BigSizedStruct& a, const BigSizedStruct& b) {
return !(a == b);
}
} // namespace
TEST(AtomicReferenceTest, SupportIntegralTypes) {
AtomicReference<int> atomic_ref({});
atomic_ref.Set(5);
EXPECT_EQ(atomic_ref.Get(), 5);
}
TEST(AtomicReferenceTest, SupportEnum) {
AtomicReference<TestEnum> atomic_ref({});
atomic_ref.Set(TestEnum::kValue1);
EXPECT_EQ(atomic_ref.Get(), TestEnum::kValue1);
}
TEST(AtomicReferenceTest, SupportScopedEnum) {
AtomicReference<ScopedTestEnum> atomic_ref({});
atomic_ref.Set(ScopedTestEnum::kValue1);
EXPECT_EQ(atomic_ref.Get(), ScopedTestEnum::kValue1);
}
TEST(AtomicReferenceTest, SetTakesCopyOfValue) {
// Default constructor is zero-initalizing all data in BigSizedStruct.
BigSizedStruct v1;
AtomicReference<BigSizedStruct> atomic_ref({});
v1.data[0] = 5; // Changing value before calling set() will affect stored
v1.data[7] = 3; // value.
atomic_ref.Set(v1);
v1.data[1] = 6; // Changing value after calling set() will not affect stored
v1.data[5] = 4; // value.
BigSizedStruct v2 = atomic_ref.Get();
EXPECT_NE(v1, v2);
v1.data[1] = 0;
v1.data[5] = 0;
EXPECT_EQ(v2, v1);
}
TEST(AtomicReferenceTest, SupportObjects) {
std::string s{"test"};
AtomicReference<std::string> atomic_ref({});
atomic_ref.Set(s);
EXPECT_EQ(s, atomic_ref.Get());
}
} // namespace nearby
} // namespace location
@@ -0,0 +1,63 @@
#ifndef PLATFORM_V2_PUBLIC_BLUETOOTH_ADAPTER_H_
#define PLATFORM_V2_PUBLIC_BLUETOOTH_ADAPTER_H_
#include <string>
#include "platform_v2/api/bluetooth_adapter.h"
#include "platform_v2/api/platform.h"
#include "absl/strings/string_view.h"
namespace location {
namespace nearby {
// 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()
: impl_(api::ImplementationPlatform::CreateBluetoothAdapter()) {}
~BluetoothAdapter() override = default;
BluetoothAdapter(BluetoothAdapter&&) = default;
BluetoothAdapter& operator=(BluetoothAdapter&&) = default;
// Synchronously sets the status of the BluetoothAdapter to 'status', and
// returns true if the operation was a success.
bool SetStatus(Status status) override { return impl_->SetStatus(status); }
Status GetStatus() const {
return IsEnabled() ? Status::kEnabled : Status::kDisabled;
}
// Returns true if the BluetoothAdapter's current status is
// Status::Value::kEnabled.
bool IsEnabled() const override { return impl_->IsEnabled(); }
// https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#getScanMode()
//
// Returns ScanMode::kUnknown on error.
ScanMode GetScanMode() const override { return impl_->GetScanMode(); }
// Synchronously sets the scan mode of the adapter, and returns true if the
// operation was a success.
bool SetScanMode(ScanMode scan_mode) override {
return impl_->SetScanMode(scan_mode);
}
// https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#getName()
// Returns an empty string on error
std::string GetName() const override { return impl_->GetName(); }
// https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#setName(java.lang.String)
bool SetName(absl::string_view name) override { return impl_->SetName(name); }
bool IsValid() const { return impl_ != nullptr; }
private:
std::unique_ptr<api::BluetoothAdapter> impl_;
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_PUBLIC_BLUETOOTH_ADAPTER_H_
@@ -0,0 +1,44 @@
#include "platform_v2/public/bluetooth_adapter.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
namespace location {
namespace nearby {
namespace {
TEST(BluetoothAdapterTest, ConstructorDestructorWorks) {
BluetoothAdapter adapter;
EXPECT_TRUE(adapter.IsValid());
}
TEST(BluetoothAdapterTest, CanSetName) {
constexpr char kAdapterName[] = "MyBtAdapter";
BluetoothAdapter adapter;
EXPECT_EQ(adapter.GetStatus(), BluetoothAdapter::Status::kDisabled);
EXPECT_TRUE(adapter.SetName(kAdapterName));
EXPECT_EQ(adapter.GetName(), std::string(kAdapterName));
}
TEST(BluetoothAdapterTest, CanSetStatus) {
BluetoothAdapter adapter;
EXPECT_EQ(adapter.GetStatus(), BluetoothAdapter::Status::kDisabled);
EXPECT_TRUE(adapter.SetStatus(BluetoothAdapter::Status::kEnabled));
EXPECT_EQ(adapter.GetStatus(), BluetoothAdapter::Status::kEnabled);
}
TEST(BluetoothAdapterTest, CanSetMode) {
BluetoothAdapter adapter;
EXPECT_TRUE(adapter.SetScanMode(BluetoothAdapter::ScanMode::kConnectable));
EXPECT_EQ(adapter.GetScanMode(), BluetoothAdapter::ScanMode::kConnectable);
EXPECT_TRUE(adapter.SetScanMode(
BluetoothAdapter::ScanMode::kConnectableDiscoverable));
EXPECT_EQ(adapter.GetScanMode(),
BluetoothAdapter::ScanMode::kConnectableDiscoverable);
EXPECT_TRUE(adapter.SetScanMode(BluetoothAdapter::ScanMode::kNone));
EXPECT_EQ(adapter.GetScanMode(), BluetoothAdapter::ScanMode::kNone);
}
} // namespace
} // namespace nearby
} // namespace location
+36
View File
@@ -0,0 +1,36 @@
#ifndef PLATFORM_V2_PUBLIC_CANCELABLE_H_
#define PLATFORM_V2_PUBLIC_CANCELABLE_H_
#include <memory>
#include <utility>
#include "platform_v2/api/cancelable.h"
namespace location {
namespace nearby {
// An interface to provide a cancellation mechanism for objects that represent
// long-running operations.
class Cancelable final {
public:
Cancelable() = default;
Cancelable(const Cancelable&) = default;
Cancelable& operator=(const Cancelable& other) = default;
~Cancelable() = default;
// This constructor is used internally only,
// by other classes in "//platform_v2/public/".
explicit Cancelable(std::shared_ptr<api::Cancelable> impl)
: impl_(std::move(impl)) {}
bool Cancel() { return impl_->Cancel(); }
private:
std::shared_ptr<api::Cancelable> impl_;
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_PUBLIC_CANCELABLE_H_
+56
View File
@@ -0,0 +1,56 @@
#ifndef PLATFORM_V2_PUBLIC_CANCELABLE_ALARM_H_
#define PLATFORM_V2_PUBLIC_CANCELABLE_ALARM_H_
#include <cstdint>
#include <functional>
#include <memory>
#include <string>
#include "platform_v2/public/cancelable.h"
#include "platform_v2/public/mutex.h"
#include "platform_v2/public/mutex_lock.h"
#include "platform_v2/public/scheduled_executor.h"
namespace location {
namespace nearby {
/**
* A cancelable alarm with a name. This is a simple wrapper around the logic
* for posting a Runnable on a ScheduledExecutor and (possibly) later
* canceling it.
*/
class CancelableAlarm {
public:
CancelableAlarm(absl::string_view name, std::function<void()>&& runnable,
absl::Duration delay, ScheduledExecutor* scheduled_executor)
: name_(name),
cancelable_(scheduled_executor->Schedule(std::move(runnable), delay)) {}
~CancelableAlarm() = default;
CancelableAlarm(CancelableAlarm&& other) {
*this = std::move(other);
}
CancelableAlarm& operator=(CancelableAlarm&& other) {
MutexLock lock(&mutex_);
{
MutexLock other_lock(&other.mutex_);
name_ = std::move(other.name_);
cancelable_ = std::move(other.cancelable_);
}
return *this;
}
bool Cancel() {
MutexLock lock(&mutex_);
return cancelable_.Cancel();
}
private:
Mutex mutex_;
std::string name_;
Cancelable cancelable_;
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_PUBLIC_CANCELABLE_ALARM_H_
@@ -0,0 +1,36 @@
#ifndef PLATFORM_V2_PUBLIC_CONDITION_VARIABLE_H_
#define PLATFORM_V2_PUBLIC_CONDITION_VARIABLE_H_
#include "platform_v2/api/condition_variable.h"
#include "platform_v2/api/platform.h"
#include "platform_v2/base/exception.h"
#include "platform_v2/public/mutex.h"
namespace location {
namespace nearby {
// 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 final {
public:
using Platform = api::ImplementationPlatform;
explicit ConditionVariable(Mutex* mutex)
: impl_(Platform::CreateConditionVariable(mutex->impl_.get())) {}
ConditionVariable(ConditionVariable&&) = default;
ConditionVariable& operator=(ConditionVariable&&) = default;
// https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#notify--
void Notify() { impl_->Notify(); }
// https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#wait--
Exception Wait() { return impl_->Wait(); }
private:
std::unique_ptr<api::ConditionVariable> impl_;
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_PUBLIC_CONDITION_VARIABLE_H_
+40
View File
@@ -0,0 +1,40 @@
#ifndef PLATFORM_V2_PUBLIC_COUNT_DOWN_LATCH_H_
#define PLATFORM_V2_PUBLIC_COUNT_DOWN_LATCH_H_
#include <cstdint>
#include "platform_v2/api/count_down_latch.h"
#include "platform_v2/api/platform.h"
#include "platform_v2/base/exception.h"
#include "absl/time/time.h"
namespace location {
namespace nearby {
// 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 final {
public:
using Platform = api::ImplementationPlatform;
explicit CountDownLatch(int count)
: impl_(Platform::CreateCountDownLatch(count)) {}
CountDownLatch(CountDownLatch&&) = default;
CountDownLatch& operator=(CountDownLatch&&) = default;
~CountDownLatch() = default;
Exception Await() { return impl_->Await(); }
ExceptionOr<bool> Await(absl::Duration timeout) {
return impl_->Await(timeout);
}
void CountDown() { impl_->CountDown(); }
private:
std::unique_ptr<api::CountDownLatch> impl_;
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_PUBLIC_COUNT_DOWN_LATCH_H_
@@ -0,0 +1,48 @@
#include "platform_v2/public/count_down_latch.h"
#include "platform_v2/public/single_thread_executor.h"
#include "gtest/gtest.h"
namespace location {
namespace nearby {
namespace {
TEST(CountDownLatch, ConstructorDestructorWorks) { CountDownLatch latch(1); }
TEST(CountDownLatch, LatchAwaitCanWait) {
CountDownLatch latch(1);
SingleThreadExecutor executor;
std::atomic_bool done = false;
executor.Execute([&done, &latch]() {
done = true;
latch.CountDown();
});
latch.Await();
EXPECT_TRUE(done);
}
TEST(CountDownLatch, LatchExtraCountDownIgnored) {
CountDownLatch latch(1);
SingleThreadExecutor executor;
std::atomic_bool done = false;
executor.Execute([&done, &latch]() {
done = true;
latch.CountDown();
latch.CountDown();
latch.CountDown();
});
latch.Await();
EXPECT_TRUE(done);
}
TEST(CountDownLatch, LatchAwaitWithTimeoutCanExpire) {
CountDownLatch latch(1);
SingleThreadExecutor executor;
auto response = latch.Await(absl::Milliseconds(100));
EXPECT_TRUE(response.ok());
EXPECT_FALSE(response.result());
}
} // namespace
} // namespace nearby
} // namespace location
+6
View File
@@ -0,0 +1,6 @@
#ifndef PLATFORM_V2_PUBLIC_CRYPTO_H_
#define PLATFORM_V2_PUBLIC_CRYPTO_H_
#include "platform_v2/api/crypto.h"
#endif // PLATFORM_V2_PUBLIC_CRYPTO_H_
+34
View File
@@ -0,0 +1,34 @@
#include "platform_v2/public/crypto.h"
#include "platform_v2/base/byte_array.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
namespace location {
namespace nearby {
TEST(CryptoTest, Md5GeneratesHash) {
const ByteArray expected_md5(
"\xb4\x5c\xff\xe0\x84\xdd\x3d\x20\xd9\x28\xbe\xe8\x5e\x7b\x0f\x21");
ByteArray md5_hash = Crypto::Md5("string");
EXPECT_EQ(md5_hash, expected_md5);
}
TEST(CryptoTest, Md5ReturnsEmptyOnError) {
EXPECT_EQ(Crypto::Md5(""), ByteArray{});
}
TEST(CryptoTest, Sha256GeneratesHash) {
const ByteArray expected_sha256(
"\x47\x32\x87\xf8\x29\x8d\xba\x71\x63\xa8\x97\x90\x89\x58\xf7\xc0"
"\xea\xe7\x33\xe2\x5d\x2e\x02\x79\x92\xea\x2e\xdc\x9b\xed\x2f\xa8");
ByteArray sha256_hash = Crypto::Sha256("string");
EXPECT_EQ(sha256_hash, expected_sha256);
}
TEST(CryptoTest, Sha256ReturnsEmptyOnError) {
EXPECT_EQ(Crypto::Sha256(""), ByteArray{});
}
} // namespace nearby
} // namespace location
+79
View File
@@ -0,0 +1,79 @@
#include "platform_v2/public/file.h"
#include <cstddef>
#include <memory>
#include "platform_v2/base/exception.h"
#include "absl/strings/string_view.h"
namespace location {
namespace nearby {
// InputFile
InputFile::InputFile(const std::string& path, std::int64_t size)
: file_(path), path_(path), total_size_(size) {}
ExceptionOr<ByteArray> InputFile::Read(std::int64_t size) {
if (!file_.is_open()) {
return ExceptionOr<ByteArray>{Exception::kIo};
}
if (file_.peek() == EOF) {
return ExceptionOr<ByteArray>{ByteArray{}};
}
if (!file_.good()) {
return ExceptionOr<ByteArray>{Exception::kIo};
}
ByteArray bytes(size);
std::unique_ptr<char[]> read_bytes{new char[size]};
file_.read(read_bytes.get(), static_cast<ptrdiff_t>(size));
auto num_bytes_read = file_.gcount();
if (num_bytes_read == 0) {
return ExceptionOr<ByteArray>{Exception::kIo};
}
return ExceptionOr<ByteArray>(ByteArray(read_bytes.get(), num_bytes_read));
}
Exception InputFile::Close() {
if (file_.is_open()) {
file_.close();
}
return {Exception::kSuccess};
}
// OutputFile
OutputFile::OutputFile(absl::string_view path) : file_(path) {}
Exception OutputFile::Write(const ByteArray& data) {
if (!file_.is_open()) {
return {Exception::kIo};
}
if (!file_.good()) {
return {Exception::kIo};
}
file_.write(data.data(), data.size());
file_.flush();
return {file_.good() ? Exception::kSuccess : Exception::kIo};
}
Exception OutputFile::Flush() {
file_.flush();
return {file_.good() ? Exception::kSuccess : Exception::kIo};
}
Exception OutputFile::Close() {
if (file_.is_open()) {
file_.close();
}
return {Exception::kSuccess};
}
} // namespace nearby
} // namespace location
+51
View File
@@ -0,0 +1,51 @@
#ifndef PLATFORM_V2_PUBLIC_FILE_H_
#define PLATFORM_V2_PUBLIC_FILE_H_
#include <cstdint>
#include <fstream>
#include "platform_v2/api/input_file.h"
#include "platform_v2/api/output_file.h"
#include "platform_v2/base/exception.h"
#include "absl/strings/string_view.h"
namespace location {
namespace nearby {
class InputFile final : public api::InputFile {
public:
explicit InputFile(const std::string& path, std::int64_t size);
~InputFile() override = default;
InputFile(InputFile&&) = default;
InputFile& operator=(InputFile&&) = default;
ExceptionOr<ByteArray> Read(std::int64_t size) override;
std::string GetFilePath() const override { return path_; }
std::int64_t GetTotalSize() const override { return total_size_; }
Exception Close() override;
private:
std::ifstream file_;
std::string path_;
std::int64_t total_size_;
};
class OutputFile final : public api::OutputFile {
public:
explicit OutputFile(absl::string_view path);
~OutputFile() override = default;
OutputFile(OutputFile&&) = default;
OutputFile& operator=(OutputFile&&) = default;
Exception Write(const ByteArray& data) override;
Exception Flush() override;
Exception Close() override;
private:
std::ofstream file_;
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_PUBLIC_FILE_H_
+131
View File
@@ -0,0 +1,131 @@
#include "platform_v2/public/file.h"
#include <cstring>
#include <fstream>
#include <memory>
#include <ostream>
#include "file/util/temp_path.h"
#include "platform_v2/base/byte_array.h"
#include "gtest/gtest.h"
namespace location {
namespace nearby {
class FileTest : public ::testing::Test {
protected:
void SetUp() override {
temp_path_ = std::make_unique<TempPath>(TempPath::Local);
path_ = temp_path_->path() + "/file.txt";
std::ofstream output_file(path_);
file_ = std::fstream(path_, std::fstream::in | std::fstream::out);
}
void WriteToFile(const std::string& text) {
file_ << text;
file_.flush();
size_ += text.size();
}
size_t GetSize() const { return size_; }
void AssertEquals(const ExceptionOr<ByteArray>& bytes,
const std::string& expected) {
EXPECT_TRUE(bytes.ok());
EXPECT_EQ(std::string(bytes.result()), expected);
}
void AssertEmpty(const ExceptionOr<ByteArray>& bytes) {
EXPECT_TRUE(bytes.ok());
EXPECT_TRUE(bytes.result().Empty());
}
static constexpr int64_t kMaxSize = 3;
std::unique_ptr<TempPath> temp_path_;
std::string path_;
std::fstream file_;
size_t size_ = 0;
};
TEST_F(FileTest, InputFile_NonExistentPath) {
InputFile input_file("/not/a/valid/path.txt", GetSize());
ExceptionOr<ByteArray> read_result = input_file.Read(kMaxSize);
EXPECT_FALSE(read_result.ok());
EXPECT_TRUE(read_result.GetException().Raised(Exception::kIo));
}
TEST_F(FileTest, InputFile_GetFilePath) {
InputFile input_file(path_, GetSize());
EXPECT_EQ(input_file.GetFilePath(), path_);
}
TEST_F(FileTest, InputFile_EmptyFileEOF) {
InputFile input_file(path_, GetSize());
AssertEmpty(input_file.Read(kMaxSize));
}
TEST_F(FileTest, InputFile_ReadWorks) {
WriteToFile("abc");
InputFile input_file(path_, GetSize());
input_file.Read(kMaxSize);
SUCCEED();
}
TEST_F(FileTest, InputFile_ReadUntilEOF) {
WriteToFile("abc");
InputFile input_file(path_, GetSize());
AssertEquals(input_file.Read(kMaxSize), "abc");
AssertEmpty(input_file.Read(kMaxSize));
}
TEST_F(FileTest, InputFile_ReadWithSize) {
WriteToFile("abc");
InputFile input_file(path_, GetSize());
AssertEquals(input_file.Read(2), "ab");
AssertEquals(input_file.Read(1), "c");
AssertEmpty(input_file.Read(kMaxSize));
}
TEST_F(FileTest, InputFile_GetTotalSize) {
WriteToFile("abc");
InputFile input_file(path_, GetSize());
EXPECT_EQ(input_file.GetTotalSize(), 3);
AssertEquals(input_file.Read(1), "a");
EXPECT_EQ(input_file.GetTotalSize(), 3);
}
TEST_F(FileTest, InputFile_Close) {
WriteToFile("abc");
InputFile input_file(path_, GetSize());
input_file.Close();
ExceptionOr<ByteArray> read_result = input_file.Read(kMaxSize);
EXPECT_FALSE(read_result.ok());
EXPECT_TRUE(read_result.GetException().Raised(Exception::kIo));
}
TEST_F(FileTest, OutputFile_NonExistentPath) {
OutputFile output_file("/not/a/valid/path.txt");
ByteArray bytes("a", 1);
EXPECT_TRUE(output_file.Write(bytes).Raised(Exception::kIo));
}
TEST_F(FileTest, OutputFile_Write) {
OutputFile output_file(path_);
ByteArray bytes1("a");
ByteArray bytes2("bc");
EXPECT_EQ(output_file.Write(bytes1), Exception{Exception::kSuccess});
EXPECT_EQ(output_file.Write(bytes2), Exception{Exception::kSuccess});
InputFile input_file(path_, GetSize());
AssertEquals(input_file.Read(kMaxSize), "abc");
}
TEST_F(FileTest, OutputFile_Close) {
OutputFile output_file(path_);
output_file.Close();
ByteArray bytes("a");
EXPECT_EQ(output_file.Write(bytes), Exception{Exception::kIo});
}
} // namespace nearby
} // namespace location
+63
View File
@@ -0,0 +1,63 @@
#ifndef PLATFORM_V2_PUBLIC_FUTURE_H_
#define PLATFORM_V2_PUBLIC_FUTURE_H_
#include "platform_v2/api/executor.h"
#include "platform_v2/api/platform.h"
#include "platform_v2/api/settable_future.h"
#include "platform_v2/base/exception.h"
#include "platform_v2/base/runnable.h"
#include "absl/time/time.h"
#include "absl/types/any.h"
namespace location {
namespace nearby {
template <typename T>
class Future final : public api::SettableFuture<T> {
public:
using Platform = api::ImplementationPlatform;
~Future() override = default;
Future() : impl_(Platform::CreateSettableFutureAny().release()) {}
Future(Future&& other) = default;
Future& operator=(Future&& other) = default;
void AddListener(Runnable runnable, api::Executor* executor) override {
impl_->AddListener(runnable, executor);
}
bool Set(const T& value) override { return impl_->Set(absl::any(value)); }
bool Set(T&& value) override { return impl_->Set(absl::any(value)); }
bool SetException(Exception exception) override {
return impl_->SetException(exception);
}
// throws Exception::kInterrupted, Exception::kExecution
ExceptionOr<T> Get() override {
auto ret_val = impl_->Get();
if (ret_val.ok()) {
T result = std::any_cast<T>(ret_val.result());
return ExceptionOr<T>{result};
} else {
return ExceptionOr<T>{ret_val.exception()};
}
}
// throws Exception::kInterrupted, Exception::kExecution
// throws Exception::kTimeout if timeout is exceeded while waiting for
// result.
ExceptionOr<T> Get(absl::Duration timeout) override {
auto ret_val = impl_->Get(timeout);
if (ret_val.ok()) {
T result = std::any_cast<T>(ret_val.result());
return ExceptionOr<T>{result};
} else {
return ExceptionOr<T>{ret_val.exception()};
}
}
private:
std::unique_ptr<api::SettableFuture<absl::any>> impl_;
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_PUBLIC_FUTURE_H_
+102
View File
@@ -0,0 +1,102 @@
#include "platform_v2/public/future.h"
#include "platform_v2/public/single_thread_executor.h"
#include "gtest/gtest.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
namespace location {
namespace nearby {
namespace {
enum TestEnum {
kValue1 = 1,
kValue2 = 2,
};
enum class ScopedTestEnum {
kValue1 = 1,
kValue2 = 2,
};
struct BigSizedStruct {
int data[100]{};
};
bool operator==(const BigSizedStruct& a, const BigSizedStruct& b) {
return memcmp(a.data, b.data, sizeof(BigSizedStruct::data)) == 0;
}
bool operator!=(const BigSizedStruct& a, const BigSizedStruct& b) {
return !(a == b);
}
} // namespace
TEST(FutureTest, SupportIntegralTypes) {
Future<int> future;
future.Set(5);
EXPECT_EQ(future.Get().exception(), Exception::kSuccess);
EXPECT_EQ(future.Get().result(), 5);
}
TEST(FutureTest, SetExceptionIsPropagated) {
Future<int> future;
future.SetException({Exception::kIo});
EXPECT_EQ(future.Get().exception(), Exception::kIo);
}
TEST(FutureTest, SupportEnum) {
Future<TestEnum> future;
future.Set(TestEnum::kValue1);
EXPECT_EQ(future.Get().exception(), Exception::kSuccess);
EXPECT_EQ(future.Get().result(), TestEnum::kValue1);
}
TEST(FutureTest, SupportScopedEnum) {
Future<ScopedTestEnum> future;
future.Set(ScopedTestEnum::kValue1);
EXPECT_EQ(future.Get().exception(), Exception::kSuccess);
EXPECT_EQ(future.Get().result(), ScopedTestEnum::kValue1);
}
TEST(FutureTest, SetTakesCopyOfValue) {
// Default constructor is zero-initalizing all data in BigSizedStruct.
BigSizedStruct v1;
Future<BigSizedStruct> future;
v1.data[0] = 5; // Changing value before calling Set() will affect stored
v1.data[7] = 3; // value.
future.Set(v1);
v1.data[1] = 6; // Changing value after calling Set() will not affect stored
v1.data[5] = 4; // value.
EXPECT_EQ(future.Get().exception(), Exception::kSuccess);
BigSizedStruct v2 = future.Get().result();
EXPECT_NE(v1, v2);
v1.data[1] = 0;
v1.data[5] = 0;
EXPECT_EQ(v2, v1);
}
TEST(FutureTest, SetsExceptionOnTimeout) {
Future<int> future;
EXPECT_EQ(future.Get(absl::Milliseconds(100)).exception(),
Exception::kTimeout);
}
TEST(FutureTest, GetBlocksWhenNotReady) {
Future<int> future;
SingleThreadExecutor executor;
absl::Time start = absl::Now();
executor.Execute([&future](){
absl::SleepFor(absl::Milliseconds(500));
future.Set(10);
});
auto response = future.Get();
absl::Duration blocked_duration = absl::Now() - start;
EXPECT_EQ(response.result(), 10);
EXPECT_GE(blocked_duration, absl::Milliseconds(500));
}
} // namespace nearby
} // namespace location
+6
View File
@@ -0,0 +1,6 @@
#ifndef PLATFORM_V2_PUBLIC_LOGGING_H_
#define PLATFORM_V2_PUBLIC_LOGGING_H_
#include "platform/logging.h"
#endif // PLATFORM_V2_PUBLIC_LOGGING_H_
+12
View File
@@ -0,0 +1,12 @@
#include "platform_v2/public/logging.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
namespace {
TEST(LoggingTest, CanLog) {
NEARBY_LOG(INFO, "message");
}
}
@@ -0,0 +1,28 @@
#ifndef PLATFORM_V2_PUBLIC_MULTI_THREAD_EXECUTOR_H_
#define PLATFORM_V2_PUBLIC_MULTI_THREAD_EXECUTOR_H_
#include "platform_v2/api/platform.h"
#include "platform_v2/public/submittable_executor.h"
namespace location {
namespace nearby {
// An Executor that reuses a fixed number of threads operating off a shared
// unbounded queue.
//
// https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executors.html#newFixedThreadPool-int-
class MultiThreadExecutor final : public SubmittableExecutor {
public:
using Platform = api::ImplementationPlatform;
explicit MultiThreadExecutor(int max_parallelism)
: SubmittableExecutor(
Platform::CreateMultiThreadExecutor(max_parallelism)) {}
MultiThreadExecutor(MultiThreadExecutor&&) = default;
MultiThreadExecutor& operator=(MultiThreadExecutor&&) = default;
~MultiThreadExecutor() override = default;
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_PUBLIC_MULTI_THREAD_EXECUTOR_H_
@@ -0,0 +1,94 @@
#include "platform_v2/public/multi_thread_executor.h"
#include <atomic>
#include <functional>
#include "platform_v2/base/exception.h"
#include "gtest/gtest.h"
#include "absl/synchronization/mutex.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
namespace location {
namespace nearby {
namespace {
const int kMaxThreads = 5;
}
TEST(MultiThreadExecutorTest, ConsructorDestructorWorks) {
MultiThreadExecutor executor(kMaxThreads);
}
TEST(MultiThreadExecutorTest, CanExecute) {
absl::CondVar cond;
std::atomic_bool done = false;
MultiThreadExecutor executor(kMaxThreads);
executor.Execute([&done, &cond]() {
done = true;
cond.SignalAll();
});
absl::Mutex mutex;
{
absl::MutexLock lock(&mutex);
if (!done) {
cond.WaitWithTimeout(&mutex, absl::Seconds(1));
}
}
EXPECT_TRUE(done);
}
TEST(MultiThreadExecutorTest, JobsExecuteInParallel) {
absl::Mutex mutex;
absl::CondVar thread_cond;
absl::CondVar test_cond;
MultiThreadExecutor executor(kMaxThreads);
int count = 0;
for (int i = 0; i < kMaxThreads; ++i) {
executor.Execute([&count, &mutex, &test_cond, &thread_cond]() {
absl::MutexLock lock(&mutex);
count++;
test_cond.Signal();
thread_cond.Wait(&mutex);
count--;
test_cond.Signal();
});
}
{
absl::Duration duration = absl::Milliseconds(kMaxThreads * 100);
absl::MutexLock lock(&mutex);
while (count < kMaxThreads) {
absl::Time start = absl::Now();
if (test_cond.WaitWithTimeout(&mutex, duration)) break;
duration -= absl::Now() - start;
}
}
EXPECT_EQ(count, kMaxThreads);
thread_cond.SignalAll();
{
absl::Duration duration = absl::Milliseconds(kMaxThreads * 100);
absl::MutexLock lock(&mutex);
while (count > 0) {
absl::Time start = absl::Now();
if (test_cond.WaitWithTimeout(&mutex, duration)) break;
duration -= absl::Now() - start;
}
}
EXPECT_EQ(count, 0);
}
TEST(MultiThreadExecutorTest, CanSubmit) {
MultiThreadExecutor executor(kMaxThreads);
Future<bool> future;
bool submitted =
executor.Submit<bool>([]() { return ExceptionOr<bool>{true}; }, &future);
EXPECT_TRUE(submitted);
EXPECT_TRUE(future.Get().result());
}
} // namespace nearby
} // namespace location
+64
View File
@@ -0,0 +1,64 @@
#ifndef PLATFORM_V2_PUBLIC_MUTEX_H_
#define PLATFORM_V2_PUBLIC_MUTEX_H_
#include <memory>
#include "platform_v2/api/mutex.h"
#include "platform_v2/api/platform.h"
#include "absl/base/thread_annotations.h"
namespace location {
namespace nearby {
// This is a classic mutex can be acquired at most once.
// Atttempt to acuire mutex from the same thread that is holding it will likely
// cause a deadlock.
class ABSL_LOCKABLE Mutex final {
public:
using Platform = api::ImplementationPlatform;
using Mode = api::Mutex::Mode;
explicit Mutex(bool check = true)
: impl_(Platform::CreateMutex(check ? Mode::kRegular
: Mode::kRegularNoCheck)) {}
Mutex(Mutex&&) = default;
Mutex& operator=(Mutex&&) = default;
~Mutex() = default;
void Lock() ABSL_EXCLUSIVE_LOCK_FUNCTION() { impl_->Lock(); }
void Unlock() ABSL_UNLOCK_FUNCTION() { impl_->Unlock(); }
private:
friend class ConditionVariable;
friend class MutexLock;
std::unique_ptr<api::Mutex> impl_;
};
// This mutex is compatible with Java definition:
// https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/locks/Lock.html
// This mutex may be acuired multiple times by a thread that is already holding
// it without blocking.
// It needs to be released equal number of times before any other thread could
// successfully acquire it.
class ABSL_LOCKABLE RecursiveMutex final {
public:
using Platform = api::ImplementationPlatform;
using Mode = api::Mutex::Mode;
RecursiveMutex() : impl_(Platform::CreateMutex(Mode::kRecursive)) {}
RecursiveMutex(RecursiveMutex&&) = default;
RecursiveMutex& operator=(RecursiveMutex&&) = default;
~RecursiveMutex() = default;
void Lock() ABSL_EXCLUSIVE_LOCK_FUNCTION() { impl_->Lock(); }
void Unlock() ABSL_UNLOCK_FUNCTION() { impl_->Unlock(); }
private:
friend class MutexLock;
std::unique_ptr<api::Mutex> impl_;
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_PUBLIC_MUTEX_H_
+31
View File
@@ -0,0 +1,31 @@
#ifndef PLATFORM_V2_PUBLIC_MUTEX_LOCK_H_
#define PLATFORM_V2_PUBLIC_MUTEX_LOCK_H_
#include "platform_v2/api/mutex.h"
#include "platform_v2/public/mutex.h"
#include "absl/base/thread_annotations.h"
namespace location {
namespace nearby {
// An RAII mechanism to acquire a Lock over a block of code.
class ABSL_SCOPED_LOCKABLE MutexLock final {
public:
explicit MutexLock(Mutex* mutex) ABSL_EXCLUSIVE_LOCK_FUNCTION(mutex)
: mutex_(mutex->impl_.get()) {
mutex_->Lock();
}
explicit MutexLock(RecursiveMutex* mutex) ABSL_EXCLUSIVE_LOCK_FUNCTION(mutex)
: mutex_(mutex->impl_.get()) {
mutex_->Lock();
}
~MutexLock() ABSL_UNLOCK_FUNCTION() { mutex_->Unlock(); }
private:
api::Mutex* mutex_;
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_PUBLIC_MUTEX_LOCK_H_
+103
View File
@@ -0,0 +1,103 @@
#include "platform_v2/public/mutex.h"
#include "platform_v2/public/condition_variable.h"
#include "platform_v2/public/single_thread_executor.h"
#include "gtest/gtest.h"
#include "absl/synchronization/mutex.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
namespace location {
namespace nearby {
namespace {
class MutexTest : public testing::Test {
public:
void VerifyStepReached(int expected) {
absl::MutexLock lock(&step_mutex_);
absl::Time deadline = absl::Now() + kTimeToWait;
while (step_ != expected) {
if (step_cond_.WaitWithDeadline(&step_mutex_, deadline)) break;
}
EXPECT_EQ(step_, expected);
// Make sure we are not progressing further.
absl::SleepFor(kTimeToWait);
EXPECT_EQ(step_, expected);
}
protected:
SingleThreadExecutor executor_;
const absl::Duration kTimeToWait = absl::Milliseconds(200);
std::atomic_int step_ = 0;
absl::Mutex step_mutex_;
absl::CondVar step_cond_;
};
TEST_F(MutexTest, ConstructorDestructorWorks) {
Mutex test_mutex;
SUCCEED();
}
TEST_F(MutexTest, BasicLockingWorks) {
Mutex test_mutex;
test_mutex.Lock();
executor_.Execute([this, &test_mutex]() {
step_ = 1;
step_cond_.Signal();
test_mutex.Lock();
test_mutex.Unlock();
step_ = 2;
step_cond_.Signal();
});
VerifyStepReached(1);
test_mutex.Unlock();
VerifyStepReached(2);
}
#ifdef THREAD_SANITIZER
TEST_F(MutexTest, DISABLED_DoubleLockIsDeadlock)
ABSL_NO_THREAD_SAFETY_ANALYSIS {
#else
TEST_F(MutexTest, DoubleLockIsDeadlock) ABSL_NO_THREAD_SAFETY_ANALYSIS {
#endif
Mutex test_mutex{/*check=*/false}; // Disable run-time deadlock detection.
test_mutex.Lock();
executor_.Execute([this, &test_mutex]() ABSL_NO_THREAD_SAFETY_ANALYSIS {
step_ = 1;
step_cond_.Signal(); // We entered executor.
test_mutex.Lock();
step_ = 2;
step_cond_.Signal(); // We acquired the test lock.
test_mutex.Lock(); // Deadlock. (Main thread should save us).
step_ = 3;
step_cond_.Signal(); // We are done.
});
VerifyStepReached(1);
test_mutex.Unlock(); // Let executor proceed to step 2.
VerifyStepReached(2);
test_mutex.Unlock(); // Bring executor out of deadlock.
VerifyStepReached(3);
test_mutex.Unlock(); // Unlock before shutdown.
}
TEST_F(MutexTest, DoubleLockIsNotDeadlock) {
RecursiveMutex test_mutex;
test_mutex.Lock();
executor_.Execute([this, &test_mutex]() ABSL_NO_THREAD_SAFETY_ANALYSIS {
step_ = 1;
step_cond_.Signal(); // We entered executor.
test_mutex.Lock();
test_mutex.Lock();
test_mutex.Unlock();
test_mutex.Unlock();
step_ = 2;
step_cond_.Signal(); // We are done.
});
VerifyStepReached(1);
test_mutex.Unlock(); // Let executor continue.
VerifyStepReached(2);
}
} // namespace
} // namespace nearby
} // namespace location
+21
View File
@@ -0,0 +1,21 @@
#include "platform_v2/public/pipe.h"
#include "platform_v2/api/condition_variable.h"
#include "platform_v2/api/mutex.h"
#include "platform_v2/api/platform.h"
namespace location {
namespace nearby {
namespace {
using Platform = api::ImplementationPlatform;
}
Pipe::Pipe() {
auto mutex = Platform::CreateMutex(api::Mutex::Mode::kRegular);
auto cond = Platform::CreateConditionVariable(mutex.get());
Setup(std::move(mutex), std::move(cond));
}
} // namespace nearby
} // namespace location

Some files were not shown because too many files have changed in this diff Show More