nearby: snapshot as of cl/296436629

Signed-off-by: Alexey Polyudov <apolyudov@google.com>
Change-Id: I2cf5bf225b76f4c1541954651f3a7544a14e0cec
This commit is contained in:
Alexey Polyudov
2020-04-04 12:52:31 -07:00
parent 598516303b
commit 204f76077d
195 changed files with 27318 additions and 0 deletions
+144
View File
@@ -0,0 +1,144 @@
cc_library(
name = "utils",
srcs = [
"base64_utils.cc",
"file_impl.cc",
"prng.cc",
"reliability_utils.cc",
],
hdrs = [
"base64_utils.h",
"cancelable_alarm.cc",
"cancelable_alarm.h",
"file_impl.h",
"pipe.cc",
"pipe.h",
"prng.h",
"reliability_utils.h",
"synchronized.h",
],
visibility = [
"//core:__subpackages__",
"//platform/impl:__subpackages__",
"//location/nearby/setup/core/internal:__subpackages__",
],
deps = [
":types",
"//platform/api",
"//platform/port:string",
"//strings",
"//absl/strings",
"//absl/time",
],
)
cc_library(
name = "types",
srcs = [
"ptr.cc",
],
hdrs = [
"byte_array.h",
"callable.h",
"cancelable.h",
"container_of.h",
"exception.cc",
"exception.h",
"ptr.h",
"runnable.h",
],
visibility = [
"//googlemac/iPhone/Shared/Nearby/Connections:__subpackages__",
"//core:__subpackages__",
"//platform:__subpackages__",
"//location/nearby/setup/core:__subpackages__",
],
deps = [
":logging",
"//platform/impl/default:lock",
"//platform/port:down_cast",
"//platform/port:string",
],
)
cc_library(
name = "logging",
hdrs = [
"logging.h",
],
visibility = [
"//googlemac/iPhone/Shared/Nearby/Connections:__subpackages__",
"//core:__subpackages__",
],
deps = [
"//absl/base",
"//absl/base:raw_logging_internal",
],
)
cc_test(
name = "container_of_test",
srcs = ["container_of_test.cc"],
deps = [
":types",
"//testing/base/public:gunit_main",
],
)
cc_test(
name = "ptr_test",
srcs = ["ptr_test.cc"],
deps = [
":types",
"//testing/base/public:gunit_main",
],
)
cc_test(
name = "prng_test",
srcs = ["prng_test.cc"],
deps = [
":utils",
"//testing/base/public:gunit_main",
],
)
cc_test(
name = "file_test",
srcs = ["file_impl_test.cc"],
deps = [
":utils",
"//file/util:temp_path",
"//testing/base/public:gunit_main",
],
)
cc_test(
name = "pipe_test",
timeout = "short",
srcs = ["pipe_test.cc"],
deps = [
":utils",
"//platform:types",
"//platform/impl/default:condition_variable",
"//platform/impl/default:lock",
"//platform/port:string",
"//testing/base/public:gunit_main",
"//absl/time",
],
)
cc_test(
name = "byte_array_test",
timeout = "short",
srcs = ["byte_array_test.cc"],
deps = [
":utils",
"//platform:types",
"//platform/impl/default:condition_variable",
"//platform/impl/default:lock",
"//platform/port:string",
"//testing/base/public:gunit_main",
"//absl/time",
],
)
+58
View File
@@ -0,0 +1,58 @@
package(default_visibility = [
"//core:__subpackages__",
"//platform:__subpackages__",
"//location/nearby/setup/core:__subpackages__",
])
cc_library(
name = "api",
hdrs = [
"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",
"lock.h",
"multi_thread_executor.h",
"output_file.h",
"output_stream.h",
"scheduled_executor.h",
"settable_future.h",
"single_thread_executor.h",
"socket.h",
"submittable_executor.h",
"system_clock.h",
"thread_utils.h",
"wifi.h",
],
deps = [
"//platform:types",
"//platform/port:down_cast",
"//platform/port:string",
],
)
cc_library(
name = "lock",
hdrs = ["lock.h"],
visibility = [
"//platform:__subpackages__",
],
)
cc_library(
name = "condition_variable",
hdrs = ["condition_variable.h"],
visibility = [
"//platform:__subpackages__",
],
deps = ["//platform:types"],
)
+21
View File
@@ -0,0 +1,21 @@
#ifndef PLATFORM_API_ATOMIC_BOOLEAN_H_
#define PLATFORM_API_ATOMIC_BOOLEAN_H_
namespace location {
namespace nearby {
// A boolean value that may be updated atomically.
//
// https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/AtomicBoolean.html
class AtomicBoolean {
public:
virtual ~AtomicBoolean() {}
virtual bool get() = 0;
virtual void set(bool value) = 0;
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_API_ATOMIC_BOOLEAN_H_
+22
View File
@@ -0,0 +1,22 @@
#ifndef PLATFORM_API_ATOMIC_REFERENCE_H_
#define PLATFORM_API_ATOMIC_REFERENCE_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 {
public:
virtual ~AtomicReference() {}
virtual T get() = 0;
virtual void set(T value) = 0;
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_API_ATOMIC_REFERENCE_H_
+124
View File
@@ -0,0 +1,124 @@
#ifndef PLATFORM_API_BLE_H_
#define PLATFORM_API_BLE_H_
#include "platform/api/bluetooth_classic.h"
#include "platform/api/input_stream.h"
#include "platform/api/output_stream.h"
#include "platform/byte_array.h"
#include "platform/ptr.h"
namespace location {
namespace nearby {
// 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 Ptr is not owned by the caller, and can be invalidated once
// the corresponding BLEPeripheral object is destroyed.
virtual Ptr<BluetoothDevice> getBluetoothDevice() = 0;
};
class BLESocket {
public:
virtual ~BLESocket() {}
// Returns the InputStream of the BLESocket, or a null Ptr<InputStream>
// on error.
//
// The returned Ptr is not owned by the caller, and can be invalidated once
// the BLESocket object is destroyed.
virtual Ptr<InputStream> getInputStream() = 0;
// Returns the OutputStream of the BLESocket, or a null
// Ptr<OutputStream> on error.
//
// The returned Ptr is not owned by the caller, and can be invalidated once
// the BLESocket object is destroyed.
virtual Ptr<OutputStream> getOutputStream() = 0;
// Conforms to the same contract as
// https://developer.android.com/reference/android/bluetooth/BluetoothSocket.html#close().
//
// Returns Exception::IO on error, Exception::NONE otherwise.
virtual Exception::Value close() = 0;
// The returned Ptr is not owned by the caller, and can be invalidated once
// the BLESocket object is destroyed.
virtual Ptr<BLEPeripheral> getRemotePeripheral() = 0;
};
// Container of operations that can be performed over the BLE medium.
class BLEMedium {
public:
virtual ~BLEMedium() {}
// Takes ownership of (and is responsible for destroying) the passed-in
// 'advertisement'.
virtual bool startAdvertising(const std::string& service_id,
ConstPtr<ByteArray> advertisement) = 0;
virtual void stopAdvertising(const std::string& service_id) = 0;
class DiscoveredPeripheralCallback {
public:
virtual ~DiscoveredPeripheralCallback() {}
// The Ptrs provided in these callback methods will be owned (and
// destroyed) by the recipient of the callback methods (i.e. the creator of
// the concrete DiscoveredPeripheralCallback object).
virtual void onPeripheralDiscovered(Ptr<BLEPeripheral> ble_peripheral,
const std::string& service_id,
ConstPtr<ByteArray> advertisement) = 0;
virtual void onPeripheralLost(Ptr<BLEPeripheral> ble_peripheral,
const std::string& service_id) = 0;
};
// Returns true once the BLE scan has been initiated.
//
// Does not take ownership of the passed-in discovered_peripheral_callback --
// destroying that is up to the caller.
virtual bool startScanning(
const std::string& service_id,
Ptr<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.
//
// Does not need to bother with destroying the DiscoveredPeripheralCallback
// passed in to startScanning() -- that's the job of the caller.
virtual void stopScanning(const std::string& service_id) = 0;
// Callback that is invoked when a new connection is accepted.
class AcceptedConnectionCallback {
public:
virtual ~AcceptedConnectionCallback() {}
// The Ptr provided in this callback method will be owned (and
// destroyed) by the recipient of the callback methods (i.e. the creator of
// the concrete AcceptedConnectionCallback object).
virtual void onConnectionAccepted(Ptr<BLESocket> socket,
const std::string& service_id) = 0;
};
// Returns true once BLE socket connection requests to service_id can be
// accepted.
//
// Does not take ownership of the passed-in accepted_connection_callback --
// destroying that is up to the caller.
virtual bool startAcceptingConnections(
const std::string& service_id,
Ptr<AcceptedConnectionCallback> accepted_connection_callback) = 0;
virtual void stopAcceptingConnections(const std::string& service_id) = 0;
// The returned Ptr will be owned (and destroyed) by the caller. Returns
// a null Ptr<BleSocket> on error.
virtual Ptr<BLESocket> connect(Ptr<BLEPeripheral> ble_peripheral,
const std::string& service_id) = 0;
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_API_BLE_H_
+401
View File
@@ -0,0 +1,401 @@
#ifndef PLATFORM_API_BLE_V2_H_
#define PLATFORM_API_BLE_V2_H_
#include <cstdint>
#include <limits>
#include <map>
#include <set>
#include "platform/byte_array.h"
#include "platform/exception.h"
#include "platform/port/string.h"
#include "platform/ptr.h"
namespace location {
namespace nearby {
// 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 {
typedef std::int8_t TXPowerLevel;
static const TXPowerLevel UNSPECIFIED_TX_POWER_LEVEL =
std::numeric_limits<TXPowerLevel>::min();
bool is_connectable;
// When set to UNSPECIFIED_TX_POWER_LEVEL, 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.
// Ownership of the map values is tied to ownership of BLEAdvertisementData.
std::map<std::string, ConstPtr<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.
//
// BLEPeripheralV2 should always be created as a RefCountedPtr because ownership
// is shared between the per-platform implementation and the internals of Nearby
// Connections.
class BLEPeripheralV2 {
public:
virtual ~BLEPeripheralV2() {}
// 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() = 0;
};
// https://developer.android.com/reference/android/bluetooth/BluetoothGattCharacteristic
//
// Representation of a GATT characteristic.
//
// GATTCharacteristics are RefCounted so that ownership can be shared between
// the per-platform implementation and C++ internals. All GATTCharacteristics
// should be created with MakeRefCountedPtr().
class GATTCharacteristic {
public:
virtual ~GATTCharacteristic() {}
// Possible permissions of a GATT characteristic.
struct Permission {
enum Value {
UNKNOWN = 0,
READ = 1,
WRITE = 2,
};
};
// Possible properties of a GATT characteristic.
struct Property {
enum Value {
UNKNOWN = 0,
READ = 1,
WRITE = 2,
INDICATE = 3,
};
};
// 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 Ptr<BLEPeripheralV2> 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. A null Ptr is returned upon error.
//
// 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 Ptr<GATTCharacteristic> getCharacteristic(
const std::string& service_uuid,
const std::string& 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. A null ConstPtr is returned upon error.
virtual ConstPtr<ByteArray> readCharacteristic(
Ptr<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(Ptr<GATTCharacteristic> characteristic,
ConstPtr<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(Ptr<GATTCharacteristic> characteristic,
ConstPtr<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(Ptr<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(
Ptr<ServerGATTConnection> connection,
Ptr<GATTCharacteristic> characteristic) = 0;
// Called when a remote peripheral unsubscribed from one of our
// characteristics.
virtual void onCharacteristicUnsubscription(
Ptr<ServerGATTConnection> connection,
Ptr<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 a null Ptr 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 Ptr<GATTCharacteristic> createCharacteristic(
const std::string& service_uuid, const std::string& characteristic_uuid,
const std::set<GATTCharacteristic::Permission::Value>& permissions,
const std::set<GATTCharacteristic::Property::Value>& 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(Ptr<GATTCharacteristic> characteristic,
ConstPtr<ByteArray> value) = 0;
// Stops a GATT server.
virtual void stop() = 0;
};
// A BLE socket representation.
class BLESocketV0 {
public:
virtual ~BLESocketV0() {}
// Returns the remote BLE peripheral tied to this socket.
virtual Ptr<BLEPeripheralV2> getRemotePeripheral() = 0;
// Writes a message on the socket and blocks until finished. Returns
// Exception::IO upon error, and Exception::NONE otherwise.
virtual Exception::Value write(ConstPtr<ByteArray> message) = 0;
// Closes the socket and blocks until finished. Returns Exception::IO upon
// error, and Exception::NONE otherwise.
virtual Exception::Value close() = 0;
};
// Callback for asynchronous events on a BLESocketV0 object.
class BLESocketLifecycleCallback {
public:
virtual ~BLESocketLifecycleCallback() {}
// Called when a message arrives on a socket.
virtual void onMessageReceived(Ptr<BLESocketV0> socket,
ConstPtr<ByteArray> message) = 0;
// Called when a socket gets disconnected.
virtual void onDisconnected(Ptr<BLESocketV0> socket) = 0;
};
// Callback for asynchronous events on the server side of a BLESocketV0 object.
class ServerBLESocketLifecycleCallback : public BLESocketLifecycleCallback {
public:
~ServerBLESocketLifecycleCallback() override {}
// Called when a new incoming socket has been established.
virtual void onSocketEstablished(Ptr<BLESocketV0> socket) = 0;
};
// The main BLE medium used inside of Nearby. This serves as the entry point for
// all BLE and GATT related operations.
class BLEMediumV2 {
public:
virtual ~BLEMediumV2() {}
typedef std::uint32_t MTU;
// Coarse representation of power settings throughout all BLE operations.
struct PowerMode {
enum Value {
UNKNOWN = 0,
LOW = 1,
HIGH = 2,
};
};
// 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(
ConstPtr<BLEAdvertisementData> advertisement_data,
ConstPtr<BLEAdvertisementData> scan_response,
PowerMode::Value 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(
Ptr<BLEPeripheralV2> peripheral,
ConstPtr<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::Value power_mode,
Ptr<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 null Ptr upon error.
virtual Ptr<GATTServer> startGATTServer(
Ptr<ServerGATTConnectionLifecycleCallback>
connection_lifecycle_callback) = 0;
// Starts listening for incoming BLE sockets and returns false upon error.
virtual bool startListeningForIncomingBLESockets(
Ptr<ServerBLESocketLifecycleCallback> socket_lifecycle_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 a null Ptr 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 Ptr<ClientGATTConnection> connectToGATTServer(
Ptr<BLEPeripheralV2> peripheral, MTU mtu, PowerMode::Value power_mode,
Ptr<ClientGATTConnectionLifecycleCallback>
connection_lifecycle_callback) = 0;
// Establishes a BLE socket to the specified remote peripheral. Returns a null
// Ptr on error.
virtual Ptr<BLESocketV0> establishBLESocket(
Ptr<BLEPeripheralV2> ble_peripheral,
Ptr<BLESocketLifecycleCallback> socket_lifecycle_callback) = 0;
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_API_BLE_V2_H_
+58
View File
@@ -0,0 +1,58 @@
#ifndef PLATFORM_API_BLUETOOTH_ADAPTER_H_
#define PLATFORM_API_BLUETOOTH_ADAPTER_H_
#include "platform/port/string.h"
#include "platform/ptr.h"
namespace location {
namespace nearby {
// https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html
class BluetoothAdapter {
public:
virtual ~BluetoothAdapter() {}
// Eligible statuses of the BluetoothAdapter.
struct Status {
enum Value {
DISABLED,
ENABLED,
};
};
// Synchronously sets the status of the BluetoothAdapter to 'status', and
// returns true if the operation was a success.
virtual bool setStatus(Status::Value status) = 0;
// Returns true if the BluetoothAdapter's current status is
// Status::Value::ENABLED.
virtual bool isEnabled() = 0;
// Scan modes of a BluetoothAdapter, as described at
// https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#getScanMode().
struct ScanMode {
enum Value {
UNKNOWN,
CONNECTABLE_DISCOVERABLE,
};
};
// https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#getScanMode()
//
// Returns ScanMode::UNKNOWN on error.
virtual ScanMode::Value getScanMode() = 0;
// Synchronously sets the scan mode of the adapter, and returns true if the
// operation was a success.
virtual bool setScanMode(ScanMode::Value scan_mode) = 0;
// https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#getName()
//
// Returns a null Ptr<string> on error.
virtual Ptr<std::string> getName() = 0;
// https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#setName(java.lang.String)
virtual bool setName(const std::string& name) = 0;
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_API_BLUETOOTH_ADAPTER_H_
+139
View File
@@ -0,0 +1,139 @@
#ifndef PLATFORM_API_BLUETOOTH_CLASSIC_H_
#define PLATFORM_API_BLUETOOTH_CLASSIC_H_
#include "platform/api/input_stream.h"
#include "platform/api/output_stream.h"
#include "platform/byte_array.h"
#include "platform/exception.h"
#include "platform/port/string.h"
#include "platform/ptr.h"
namespace location {
namespace nearby {
// 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() = 0;
};
// https://developer.android.com/reference/android/bluetooth/BluetoothSocket.html.
class BluetoothSocket {
public:
virtual ~BluetoothSocket() {}
// Returns the InputStream of the BluetoothSocket, or a null Ptr<InputStream>
// on error.
//
// The returned Ptr is not owned by the caller, and can be invalidated once
// the BluetoothSocket object is destroyed.
virtual Ptr<InputStream> getInputStream() = 0;
// Returns the OutputStream of the BluetoothSocket, or a null
// Ptr<OutputStream> on error.
//
// The returned Ptr is not owned by the caller, and can be invalidated once
// the BluetoothSocket object is destroyed.
virtual Ptr<OutputStream> getOutputStream() = 0;
// https://developer.android.com/reference/android/bluetooth/BluetoothSocket.html#close()
//
// Returns Exception::IO on error, Exception::NONE otherwise.
virtual Exception::Value close() = 0;
// https://developer.android.com/reference/android/bluetooth/BluetoothSocket.html#getRemoteDevice()
//
// The returned Ptr is not owned by the caller, and can be invalidated once
// the BluetoothSocket object is destroyed.
virtual Ptr<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()
//
// The returned Ptr will be owned (and destroyed) by the caller. Returns
// Exception::IO on error.
virtual ExceptionOr<Ptr<BluetoothSocket> > accept() = 0;
// https://developer.android.com/reference/android/bluetooth/BluetoothServerSocket.html#close()
//
// Returns Exception::IO on error, Exception::NONE otherwise.
virtual Exception::Value close() = 0;
};
// Container of operations that can be performed over the Bluetooth Classic
// medium.
class BluetoothClassicMedium {
public:
virtual ~BluetoothClassicMedium() {}
class DiscoveryCallback {
public:
virtual ~DiscoveryCallback() {}
// The Ptrs provided in these callback methods will be owned (and
// destroyed) by the recipient of the callback methods (i.e. the creator of
// the concrete DiscoveryCallback object).
virtual void onDeviceDiscovered(Ptr<BluetoothDevice> device) = 0;
virtual void onDeviceNameChanged(Ptr<BluetoothDevice> device) = 0;
virtual void onDeviceLost(Ptr<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(Ptr<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().
//
// Does not need to bother with destroying the DiscoveryCallback passed in to
// startDiscovery() -- that's the job of the caller.
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.
//
// The returned Ptr will be owned (and destroyed) by the caller. Returns
// Exception::IO on error.
virtual ExceptionOr<Ptr<BluetoothSocket> > connectToService(
Ptr<BluetoothDevice> remote_device, const std::string& 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.
//
// The returned Ptr will be owned (and destroyed) by the caller. Returns
// Exception::IO on error.
virtual ExceptionOr<Ptr<BluetoothServerSocket> > listenForService(
const std::string& service_name, const std::string& service_uuid) = 0;
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_API_BLUETOOTH_CLASSIC_H_
+26
View File
@@ -0,0 +1,26 @@
#ifndef PLATFORM_API_CONDITION_VARIABLE_H_
#define PLATFORM_API_CONDITION_VARIABLE_H_
#include "platform/exception.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 {
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::Value wait() = 0; // throws Exception::INTERRUPTED
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_API_CONDITION_VARIABLE_H_
+28
View File
@@ -0,0 +1,28 @@
#ifndef PLATFORM_API_COUNT_DOWN_LATCH_H_
#define PLATFORM_API_COUNT_DOWN_LATCH_H_
#include <cstdint>
#include "platform/exception.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 {
public:
virtual ~CountDownLatch() {}
virtual Exception::Value await() = 0; // throws Exception::INTERRUPTED
virtual ExceptionOr<bool> await(
std::int32_t timeout_millis) = 0; // throws Exception::INTERRUPTED
virtual void countDown() = 0;
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_API_COUNT_DOWN_LATCH_H_
+20
View File
@@ -0,0 +1,20 @@
#ifndef PLATFORM_API_EXECUTOR_H_
#define PLATFORM_API_EXECUTOR_H_
namespace location {
namespace nearby {
// This abstract class is the superclass of all classes representing an
// Executor.
class Executor {
public:
virtual ~Executor() {}
// https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html#shutdown--
virtual void shutdown() = 0;
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_API_EXECUTOR_H_
+24
View File
@@ -0,0 +1,24 @@
#ifndef PLATFORM_API_FUTURE_H_
#define PLATFORM_API_FUTURE_H_
#include "platform/exception.h"
namespace location {
namespace nearby {
// 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() {}
virtual ExceptionOr<T>
get() = 0; // throws Exception::INTERRUPTED, Exception::EXECUTION
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_API_FUTURE_H_
+23
View File
@@ -0,0 +1,23 @@
#ifndef PLATFORM_API_HASH_UTILS_H_
#define PLATFORM_API_HASH_UTILS_H_
#include "platform/byte_array.h"
#include "platform/port/string.h"
#include "platform/ptr.h"
namespace location {
namespace nearby {
// A provider of standard hashing algorithms.
class HashUtils {
public:
virtual ~HashUtils() {}
virtual ConstPtr<ByteArray> md5(const std::string& input) = 0;
virtual ConstPtr<ByteArray> sha256(const std::string& input) = 0;
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_API_HASH_UTILS_H_
+30
View File
@@ -0,0 +1,30 @@
#ifndef PLATFORM_API_INPUT_FILE_H_
#define PLATFORM_API_INPUT_FILE_H_
#include "platform/byte_array.h"
#include "platform/exception.h"
#include "platform/ptr.h"
namespace location {
namespace nearby {
// An InputFile represents a readable file on the system.
class InputFile {
public:
virtual ~InputFile() {}
// The returned ConstPtr will be owned (and destroyed) by the caller.
// When we have exhausted reading the file and no bytes remain, read will
// always return an empty ConstPtr for which isNull() is true.
virtual ExceptionOr<ConstPtr<ByteArray> > read(
std::int64_t size) = 0; // throws Exception::IO when the file cannot be
// opened or read.
virtual std::string getFilePath() const = 0;
virtual std::int64_t getTotalSize() const = 0;
virtual void close() = 0;
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_API_INPUT_FILE_H_
+31
View File
@@ -0,0 +1,31 @@
#ifndef PLATFORM_API_INPUT_STREAM_H_
#define PLATFORM_API_INPUT_STREAM_H_
#include <cstdint>
#include "platform/byte_array.h"
#include "platform/exception.h"
#include "platform/ptr.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() {}
// The returned ConstPtr will be owned (and destroyed) by the caller.
virtual ExceptionOr<ConstPtr<ByteArray> > read() = 0; // throws Exception::IO
// The returned ConstPtr will be owned (and destroyed) by the caller.
virtual ExceptionOr<ConstPtr<ByteArray> > read(
std::int64_t size) = 0; // throws Exception::IO
virtual Exception::Value close() = 0; // throws Exception::IO
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_API_INPUT_STREAM_H_
+22
View File
@@ -0,0 +1,22 @@
#ifndef PLATFORM_API_LOCK_H_
#define PLATFORM_API_LOCK_H_
namespace location {
namespace nearby {
// 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 Lock {
public:
virtual ~Lock() {}
virtual void lock() = 0;
virtual void unlock() = 0;
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_API_LOCK_H_
+23
View File
@@ -0,0 +1,23 @@
#ifndef PLATFORM_API_MULTI_THREAD_EXECUTOR_H_
#define PLATFORM_API_MULTI_THREAD_EXECUTOR_H_
#include "platform/api/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-
template <typename ConcreteSubmittableExecutor>
class MultiThreadExecutor :
public SubmittableExecutor<ConcreteSubmittableExecutor> {
public:
~MultiThreadExecutor() override {}
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_API_MULTI_THREAD_EXECUTOR_H_
+26
View File
@@ -0,0 +1,26 @@
#ifndef PLATFORM_API_OUTPUT_FILE_H_
#define PLATFORM_API_OUTPUT_FILE_H_
#include "platform/byte_array.h"
#include "platform/exception.h"
#include "platform/ptr.h"
namespace location {
namespace nearby {
// An OutputFile represents a writable file on the system.
class OutputFile {
public:
virtual ~OutputFile() {}
// Takes ownership of the passed-in ConstPtr, and ensures that it is destroyed
// even upon error (i.e. when the return value is not Exception::NONE).
virtual Exception::Value write(
ConstPtr<ByteArray> data) = 0; // throws Exception::IO
virtual void close() = 0;
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_API_OUTPUT_FILE_H_
+29
View File
@@ -0,0 +1,29 @@
#ifndef PLATFORM_API_OUTPUT_STREAM_H_
#define PLATFORM_API_OUTPUT_STREAM_H_
#include "platform/byte_array.h"
#include "platform/exception.h"
#include "platform/ptr.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() {}
// Takes ownership of the passed-in ConstPtr, and ensures that it is destroyed
// even upon error (i.e. when the return value is not Exception::NONE).
virtual Exception::Value write(
ConstPtr<ByteArray> data) = 0; // throws Exception::IO
virtual Exception::Value flush() = 0; // throws Exception::IO
virtual Exception::Value close() = 0; // throws Exception::IO
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_API_OUTPUT_STREAM_H_
+29
View File
@@ -0,0 +1,29 @@
#ifndef PLATFORM_API_SCHEDULED_EXECUTOR_H_
#define PLATFORM_API_SCHEDULED_EXECUTOR_H_
#include <cstdint>
#include "platform/api/executor.h"
#include "platform/cancelable.h"
#include "platform/ptr.h"
#include "platform/runnable.h"
namespace location {
namespace nearby {
// 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:
virtual ~ScheduledExecutor() {}
virtual Ptr<Cancelable> schedule(Ptr<Runnable> runnable,
std::int64_t delay_millis) = 0;
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_API_SCHEDULED_EXECUTOR_H_
+23
View File
@@ -0,0 +1,23 @@
#ifndef PLATFORM_API_SETTABLE_FUTURE_H_
#define PLATFORM_API_SETTABLE_FUTURE_H_
#include "platform/api/future.h"
namespace location {
namespace nearby {
// 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 Future<T> {
public:
~SettableFuture() override {}
virtual bool set(T value) = 0;
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_API_SETTABLE_FUTURE_H_
+23
View File
@@ -0,0 +1,23 @@
#ifndef PLATFORM_API_SINGLE_THREAD_EXECUTOR_H_
#define PLATFORM_API_SINGLE_THREAD_EXECUTOR_H_
#include "platform/api/submittable_executor.h"
namespace location {
namespace nearby {
// An Executor that uses a single worker thread operating off an unbounded
// queue.
//
// https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executors.html#newSingleThreadExecutor--
template <typename ConcreteSubmittableExecutor>
class SingleThreadExecutor :
public SubmittableExecutor<ConcreteSubmittableExecutor> {
public:
~SingleThreadExecutor() override {}
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_API_SINGLE_THREAD_EXECUTOR_H_
+26
View File
@@ -0,0 +1,26 @@
#ifndef PLATFORM_API_SOCKET_H_
#define PLATFORM_API_SOCKET_H_
#include "platform/api/input_stream.h"
#include "platform/api/output_stream.h"
#include "platform/ptr.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() {}
virtual Ptr<InputStream> getInputStream() = 0;
virtual Ptr<OutputStream> getOutputStream() = 0;
virtual void close() = 0;
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_API_SOCKET_H_
+43
View File
@@ -0,0 +1,43 @@
#ifndef PLATFORM_API_SUBMITTABLE_EXECUTOR_H_
#define PLATFORM_API_SUBMITTABLE_EXECUTOR_H_
#include "platform/api/executor.h"
#include "platform/api/future.h"
#include "platform/callable.h"
#include "platform/port/down_cast.h"
#include "platform/ptr.h"
#include "platform/runnable.h"
namespace location {
namespace nearby {
// Each per-platform concrete implementation is expected to extend from
// SubmittableExecutor and provide an override of its submit() method.
//
// e.g.
// class IOSSubmittableExecutor
// : public SubmittableExecutor<IOSSubmittableExecutor> {
// public:
// template <typename T>
// Ptr<Future<T> > submit(Ptr<Callable<T> > callable) {
// ...
// }
// }
template <typename ConcreteSubmittableExecutor>
class SubmittableExecutor : public Executor {
public:
~SubmittableExecutor() override {}
template <typename T>
Ptr<Future<T> > submit(Ptr<Callable<T> > callable) {
return DOWN_CAST<ConcreteSubmittableExecutor*>(this)->submit(callable);
}
// https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executor.html#execute-java.lang.Runnable-
virtual void execute(Ptr<Runnable> runnable) = 0;
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_API_SUBMITTABLE_EXECUTOR_H_
+22
View File
@@ -0,0 +1,22 @@
#ifndef PLATFORM_API_SYSTEM_CLOCK_H_
#define PLATFORM_API_SYSTEM_CLOCK_H_
#include <cstdint>
namespace location {
namespace nearby {
class SystemClock {
public:
virtual ~SystemClock() {}
// Returns the time (in milliseconds) since the system was booted, and
// includes deep sleep. This clock should be guaranteed to be monotonic, and
// should continue to tick even when the CPU is in power saving modes.
virtual std::int64_t elapsedRealtime() = 0;
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_API_SYSTEM_CLOCK_H_
+23
View File
@@ -0,0 +1,23 @@
#ifndef PLATFORM_API_THREAD_UTILS_H_
#define PLATFORM_API_THREAD_UTILS_H_
#include <cstdint>
#include "platform/exception.h"
namespace location {
namespace nearby {
class ThreadUtils {
public:
virtual ~ThreadUtils() {}
// https://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html#sleep(long)
virtual Exception::Value sleep(
std::int64_t millis) = 0; // throws Exception::INTERRUPTED
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_API_THREAD_UTILS_H_
+90
View File
@@ -0,0 +1,90 @@
#ifndef PLATFORM_API_WIFI_H_
#define PLATFORM_API_WIFI_H_
#include <cstdint>
#include <vector>
#include "platform/port/string.h"
#include "platform/ptr.h"
namespace location {
namespace nearby {
// Possible authentication types for a WiFi network.
struct WifiAuthType {
enum Value {
UNKNOWN = 0,
OPEN = 1,
WPA_PSK = 2,
WEP = 3,
};
};
// Possible statuses of a device's connection to a WiFi network.
struct WifiConnectionStatus {
enum Value {
UNKNOWN = 0,
CONNECTED = 1,
CONNECTION_FAILURE = 2,
AUTH_FAILURE = 3,
};
};
// Represents a WiFi network found during a call to WifiMedium#scan().
class WifiScanResult {
public:
virtual ~WifiScanResult() {}
// 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::Value getAuthType() const = 0;
};
// Container of operations that can be performed over the WiFi medium.
class WifiMedium {
public:
virtual ~WifiMedium() {}
class ScanResultCallback {
public:
virtual ~ScanResultCallback() {}
// The ConstPtr<WifiScanResult> objects contained in scan_results will be
// owned (and destroyed) by the recipient of the callback methods (i.e. the
// creator of the concrete ScanResultCallback object).
virtual void onScanResults(
const std::vector<ConstPtr<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(Ptr<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::Value connectToNetwork(
const std::string& ssid, const std::string& password,
WifiAuthType::Value 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 nearby
} // namespace location
#endif // PLATFORM_API_WIFI_H_
+56
View File
@@ -0,0 +1,56 @@
#include "platform/base64_utils.h"
#include "strings/escaping.h"
#include "absl/strings/escaping.h"
namespace location {
namespace nearby {
std::string Base64Utils::encode(ConstPtr<ByteArray> bytes) {
std::string base64_string;
if (!bytes.isNull()) {
absl::WebSafeBase64Escape(std::string(bytes->getData(), bytes->size()),
&base64_string);
}
return base64_string;
}
std::string Base64Utils::encode(const ByteArray& bytes) {
std::string base64_string;
absl::WebSafeBase64Escape(std::string(bytes.getData(), bytes.size()),
&base64_string);
return base64_string;
}
std::string Base64Utils::encode(const std::string& input) {
std::string base64_string;
absl::WebSafeBase64Escape(input, &base64_string);
return base64_string;
}
template<>
Ptr<ByteArray> Base64Utils::decode(const std::string& base64_string) {
std::string decoded_string;
if (!absl::WebSafeBase64Unescape(base64_string, &decoded_string)) {
return Ptr<ByteArray>();
}
return MakePtr(new ByteArray(decoded_string.data(), decoded_string.size()));
}
template<>
ByteArray Base64Utils::decode(const std::string& 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
+31
View File
@@ -0,0 +1,31 @@
#ifndef PLATFORM_BASE64_UTILS_H_
#define PLATFORM_BASE64_UTILS_H_
#include "platform/byte_array.h"
#include "platform/port/string.h"
#include "platform/ptr.h"
namespace location {
namespace nearby {
class Base64Utils {
public:
static std::string encode(const std::string& input);
static std::string encode(const ByteArray& bytes);
static std::string encode(ConstPtr<ByteArray> bytes);
template <typename T>
static T decode(const std::string& base64_string);
template <>
Ptr<ByteArray> decode(const std::string& base64_string);
template <>
ByteArray decode(const std::string& base64_string);
static Ptr<ByteArray> decode(const std::string& base64_string) {
return decode<Ptr<ByteArray>>(base64_string);
}
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_BASE64_UTILS_H_
+66
View File
@@ -0,0 +1,66 @@
#ifndef PLATFORM_BYTE_ARRAY_H_
#define PLATFORM_BYTE_ARRAY_H_
#include "platform/port/string.h"
namespace location {
namespace nearby {
class ByteArray {
public:
// Create an empty ByteArray
ByteArray() {}
// Create ByteArray from string.
explicit ByteArray(const std::string& 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) {
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);
}
char* getData() { return data_.data(); }
const char* getData() const { return data_.data(); }
size_t size() const { return data_.size(); }
// Operator overloads when comparing ConstPtr<ByteArray>.
bool operator==(const ByteArray& rhs) const {
return this->size() == rhs.size() &&
memcmp(this->getData(), rhs.getData(), this->size()) == 0;
}
bool operator!=(const ByteArray& rhs) const { return !(*this == rhs); }
bool operator<(const ByteArray& rhs) const {
if (this->size() != rhs.size()) {
return this->size() < rhs.size();
}
return memcmp(this->getData(), rhs.getData(), this->size()) < 0;
}
// TODO(b/149869249) : rename according to go/c-style
std::string asString() const { return data_; }
private:
std::string data_;
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_BYTE_ARRAY_H_
+39
View File
@@ -0,0 +1,39 @@
#include "platform/byte_array.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
namespace {
using location::nearby::ByteArray;
TEST(ByteArrayTest, DefaultSizeIsZero) {
ByteArray bytes;
ASSERT_EQ(0, bytes.size());
}
TEST(ByteArrayTest, SetFromString) {
std::string setup("setup_test");
ByteArray bytes{setup}; // array initialized with a copy of string.
ASSERT_EQ(setup.size(), bytes.size());
ASSERT_EQ(bytes.asString(), setup);
}
TEST(ByteArrayTest, SetExplicitSize) {
constexpr size_t kArraySize = 10;
char reference[kArraySize]{};
ByteArray bytes{kArraySize}; // array of size 10, zero-initialized.
ASSERT_EQ(kArraySize, bytes.size());
ASSERT_EQ(0, memcmp(bytes.getData(), reference, kArraySize));
}
TEST(ByteArrayTest, SetExplicitData) {
constexpr static const char message[] {"test_message"};
constexpr size_t kMessageSize = sizeof(message);
ByteArray bytes{message, kMessageSize};
ASSERT_EQ(kMessageSize, bytes.size());
ASSERT_NE(message, bytes.getData());
ASSERT_EQ(0, memcmp(message, bytes.getData(), kMessageSize));
}
} // namespace
+26
View File
@@ -0,0 +1,26 @@
#ifndef PLATFORM_CALLABLE_H_
#define PLATFORM_CALLABLE_H_
#include "platform/exception.h"
namespace location {
namespace nearby {
// The Callable interface should be implemented by any class whose instances are
// intended to be executed by a thread, and need to return a result. The class
// must define a method named call() with no arguments and a specific return
// type.
//
// https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Callable.html
template <typename T>
class Callable {
public:
virtual ~Callable() {}
virtual ExceptionOr<T> call() = 0;
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_CALLABLE_H_
+19
View File
@@ -0,0 +1,19 @@
#ifndef PLATFORM_CANCELABLE_H_
#define PLATFORM_CANCELABLE_H_
namespace location {
namespace nearby {
// An interface to provide a cancellation mechanism for objects that represent
// long-running operations.
class Cancelable {
public:
virtual ~Cancelable() {}
virtual bool cancel() = 0;
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_CANCELABLE_H_
+37
View File
@@ -0,0 +1,37 @@
#include "platform/cancelable_alarm.h"
#include "platform/synchronized.h"
namespace location {
namespace nearby {
template <typename Platform>
CancelableAlarm<Platform>::CancelableAlarm(
const string &name, Ptr<Runnable> runnable, std::int64_t delay_millis,
Ptr<typename Platform::ScheduledExecutorType> scheduled_executor)
: name_(name),
lock_(Platform::createLock()),
cancelable_(scheduled_executor->schedule(runnable, delay_millis)) {}
template <typename Platform>
CancelableAlarm<Platform>::~CancelableAlarm() {
cancelable_.destroy();
}
template <typename Platform>
bool CancelableAlarm<Platform>::cancel() {
Synchronized s(lock_.get());
if (cancelable_.isNull()) {
// TODO(tracyzhou): Add logging
return false;
}
bool canceled = cancelable_->cancel();
// TODO(tracyzhou): Add logging
cancelable_.destroy();
return canceled;
}
} // namespace nearby
} // namespace location
+41
View File
@@ -0,0 +1,41 @@
#ifndef PLATFORM_CANCELABLE_ALARM_H_
#define PLATFORM_CANCELABLE_ALARM_H_
#include <cstdint>
#include "platform/api/lock.h"
#include "platform/cancelable.h"
#include "platform/port/string.h"
#include "platform/ptr.h"
#include "platform/runnable.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.
*/
template <typename Platform>
class CancelableAlarm {
public:
CancelableAlarm(
const string& name, Ptr<Runnable> runnable, std::int64_t delay_millis,
Ptr<typename Platform::ScheduledExecutorType> scheduled_executor);
~CancelableAlarm();
bool cancel();
private:
string name_;
ScopedPtr<Ptr<Lock> > lock_;
Ptr<Cancelable> cancelable_;
};
} // namespace nearby
} // namespace location
#include "platform/cancelable_alarm.cc"
#endif // PLATFORM_CANCELABLE_ALARM_H_
+71
View File
@@ -0,0 +1,71 @@
#ifndef PLATFORM_CONTAINER_OF_H_
#define PLATFORM_CONTAINER_OF_H_
#include <cstddef>
#include <type_traits>
namespace location::nearby {
// Similar to offsetof() macro, but implemented in a type-safe way,
// OffsetOf(<pointer-to-member>) returns the byte offset of a given data
// member in the ClassType.
// Behavior is undefined if member is not a direct, non-static data member of
// type ClassType.
// usage example:
// struct S { int x; double y; };
// size_t y_offset = OffsetOf(&S::y);
// CHECK(y_offset >= sizeof(int));
//
// the following is not guaranteed to work:
// struct S1 { int x; };
// struct S2 { double y; };
// struct S : public S1, S2 { char t; };
// size_t y_offset_bad = OffsetOf(&S::y);
// because S::y is not a direct member of S; it is a member by inheritance.
// To make sure OffsetOf works with inherited members, it must be called
// with explicitly defined template parameters, as follows:
// size_t y_offset_ok = OffsetOf<double,S>(&S::y);
//
// However, the following is guaranteed to work:
// struct S1 { int x; };
// struct S2 { double y; };
// struct S3 { double z; };
// struct S : public S1, S2 { S3 s3; char t; };
// size_t s3_offset = OffsetOf(&S::s3);
template <typename ValueType, typename ClassType>
constexpr size_t OffsetOf(const ValueType ClassType::*member) {
std::aligned_storage_t<sizeof(ClassType), alignof(ClassType)> obj_memory;
ClassType* obj = reinterpret_cast<ClassType*>(&obj_memory);
return reinterpret_cast<size_t>(&(obj->*member)) -
reinterpret_cast<size_t>(obj);
}
// Similar to Linux containerof() macro, this function returns pointer to
// the type instance that contains the specified member;
// ContainerOf(<pointer to variable instance of type ValueType, which is member
// of ClassType>, <pointer-to-ClassType-member>);
// usage example:
// struct S { int x; double y; } a;
// S *b = ContainerOf(&a.y, &S::y);
// CHECK(b == &a);
template <typename ValueType, typename ClassType>
ClassType* ContainerOf(ValueType* ptr, ValueType ClassType::*member) {
using BaseValueType = std::remove_volatile_t<ValueType>;
return reinterpret_cast<ClassType*>(
reinterpret_cast<char*>(const_cast<BaseValueType*>(ptr)) -
OffsetOf(member));
}
template <typename ValueType, typename ClassType>
const ClassType* ContainerOf(const ValueType* ptr,
ValueType ClassType::*member) {
using BaseValueType = std::remove_volatile_t<ValueType>;
return reinterpret_cast<const ClassType*>(
reinterpret_cast<const char*>(const_cast<BaseValueType*>(ptr)) -
OffsetOf(member));
}
} // namespace location::nearby
#endif // PLATFORM_CONTAINER_OF_H_
+47
View File
@@ -0,0 +1,47 @@
#include "platform/container_of.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
namespace location::nearby {
TEST(OffsetOf, OffsetOfTest) {
struct [[gnu::packed]] S {
char x;
double y;
};
EXPECT_EQ(OffsetOf(&S::x), 0U);
EXPECT_EQ(OffsetOf(&S::y), sizeof(S::x));
}
TEST(OffsetOf, ExplicitOffsetOfTest) {
struct [[gnu::packed]] S1 { int x; };
struct [[gnu::packed]] S2 { double y; };
struct [[gnu::packed]] S : public S1, S2 { char t; };
EXPECT_EQ((OffsetOf<decltype(std::declval<S>().x), S>(&S::x)), 0U);
EXPECT_EQ((OffsetOf<decltype(std::declval<S>().y), S>(&S::y)), sizeof(S::x));
}
TEST(ContainerOf, ContainerOfTest) {
struct [[gnu::packed]] S {
char x;
double y;
} s;
char* p = &s.x;
double* q = &s.y;
EXPECT_EQ(ContainerOf(p, &S::x), &s);
EXPECT_EQ(ContainerOf(q, &S::y), &s);
}
TEST(ContainerOf, ContainerOfTestConst) {
struct [[gnu::packed]] S {
char x;
double y;
} s;
const char* p = &s.x;
const double* q = &s.y;
EXPECT_EQ(ContainerOf(p, &S::x), &s);
EXPECT_EQ(ContainerOf(q, &S::y), &s);
}
} // namespace location::nearby
+30
View File
@@ -0,0 +1,30 @@
#include "platform/exception.h"
namespace location {
namespace nearby {
template <typename T>
ExceptionOr<T>::ExceptionOr(T result)
: result_(result), exception_(Exception::NONE) {}
template <typename T>
ExceptionOr<T>::ExceptionOr(Exception::Value exception)
: result_(), exception_(exception) {}
template <typename T>
bool ExceptionOr<T>::ok() const {
return Exception::NONE == exception_;
}
template <typename T>
T ExceptionOr<T>::result() const {
return result_;
}
template <typename T>
Exception::Value ExceptionOr<T>::exception() const {
return exception_;
}
} // namespace nearby
} // namespace location
+57
View File
@@ -0,0 +1,57 @@
#ifndef PLATFORM_EXCEPTION_H_
#define PLATFORM_EXCEPTION_H_
namespace location {
namespace nearby {
struct Exception {
enum Value {
NONE,
IO,
INTERRUPTED,
INVALID_PROTOCOL_BUFFER,
EXECUTION,
};
};
// ExceptionOr models the concept of the return value of a function that might
// throw an exception.
//
// 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:
explicit ExceptionOr(T result);
explicit ExceptionOr(Exception::Value exception);
bool ok() const;
T result() const;
Exception::Value exception() const;
private:
T result_;
Exception::Value exception_;
};
} // namespace nearby
} // namespace location
#include "platform/exception.cc"
#endif // PLATFORM_EXCEPTION_H_
+75
View File
@@ -0,0 +1,75 @@
#include "platform/file_impl.h"
#include <cstddef>
#include <memory>
namespace location {
namespace nearby {
// InputFile
InputFileImpl::InputFileImpl(const std::string& path, std::int64_t size)
: file_(path), path_(path), total_size_(size) {}
ExceptionOr<ConstPtr<ByteArray>> InputFileImpl::read(int64_t size) {
if (!file_.is_open()) {
return ExceptionOr<ConstPtr<ByteArray>>(Exception::IO);
}
if (file_.peek() == EOF) {
return ExceptionOr<ConstPtr<ByteArray>>(ConstPtr<ByteArray>());
}
if (!file_.good()) {
return ExceptionOr<ConstPtr<ByteArray>>(Exception::IO);
}
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<ConstPtr<ByteArray>>(Exception::IO);
}
return ExceptionOr<ConstPtr<ByteArray>>(
MakeConstPtr(new ByteArray(read_bytes.get(), num_bytes_read)));
}
std::string InputFileImpl::getFilePath() const { return path_; }
std::int64_t InputFileImpl::getTotalSize() const { return total_size_; }
void InputFileImpl::close() {
if (file_.is_open()) {
file_.close();
}
}
// OutputFile
OutputFileImpl::OutputFileImpl(const std::string& path) : file_(path) {}
Exception::Value OutputFileImpl::write(ConstPtr<ByteArray> data) {
ScopedPtr<ConstPtr<ByteArray>> scoped_data(data);
if (!file_.is_open()) {
return Exception::IO;
}
if (!file_.good()) {
return Exception::IO;
}
file_.write(data->getData(), data->size());
file_.flush();
return file_.good() ? Exception::NONE : Exception::IO;
}
void OutputFileImpl::close() {
if (file_.is_open()) {
file_.close();
}
}
} // namespace nearby
} // namespace location
+46
View File
@@ -0,0 +1,46 @@
#ifndef PLATFORM_FILE_IMPL_H_
#define PLATFORM_FILE_IMPL_H_
#include <cstdint>
#include <fstream>
#include "platform/api/input_file.h"
#include "platform/api/output_file.h"
#include "platform/exception.h"
#include "platform/ptr.h"
namespace location {
namespace nearby {
class InputFileImpl final : public InputFile {
public:
explicit InputFileImpl(const std::string& path, std::int64_t size);
~InputFileImpl() override {}
ExceptionOr<ConstPtr<ByteArray>> read(std::int64_t size) override;
std::string getFilePath() const override;
std::int64_t getTotalSize() const override;
void close() override;
private:
std::ifstream file_;
const std::string path_;
const std::int64_t total_size_;
};
class OutputFileImpl final : public OutputFile {
public:
explicit OutputFileImpl(const std::string& path);
~OutputFileImpl() override {}
Exception::Value write(ConstPtr<ByteArray> data) override;
void close() override;
private:
std::ofstream file_;
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_FILE_IMPL_H_
+133
View File
@@ -0,0 +1,133 @@
#include "platform/file_impl.h"
#include <cstring>
#include <fstream>
#include <memory>
#include <ostream>
#include "file/util/temp_path.h"
#include "gtest/gtest.h"
namespace location {
namespace nearby {
class FileImplTest : 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<ConstPtr<ByteArray>>& bytes,
const std::string& expected) {
ASSERT_TRUE(bytes.ok());
ScopedPtr<ConstPtr<ByteArray>> byte_array(bytes.result());
ASSERT_STREQ(byte_array->getData(), expected.c_str());
ASSERT_EQ(byte_array->size(), expected.length());
}
void AssertNull(const ExceptionOr<ConstPtr<ByteArray>>& bytes) {
ASSERT_TRUE(bytes.ok());
ASSERT_TRUE(bytes.result().isNull());
}
static const int64_t kMaxSize = 3;
std::unique_ptr<TempPath> temp_path_;
std::string path_;
std::fstream file_;
size_t size_ = 0;
};
TEST_F(FileImplTest, InputFile_NonExistentPath) {
InputFileImpl input_file("/not/a/valid/path.txt", GetSize());
ExceptionOr<ConstPtr<ByteArray>> read_result = input_file.read(kMaxSize);
ASSERT_FALSE(read_result.ok());
ASSERT_EQ(read_result.exception(), Exception::IO);
}
TEST_F(FileImplTest, InputFile_GetFilePath) {
InputFileImpl input_file(path_, GetSize());
ASSERT_EQ(input_file.getFilePath(), path_);
}
TEST_F(FileImplTest, InputFile_EmptyFileEOF) {
InputFileImpl input_file(path_, GetSize());
AssertNull(input_file.read(kMaxSize));
}
TEST_F(FileImplTest, InputFile_ReadWorks) {
WriteToFile("abc");
InputFileImpl input_file(path_, GetSize());
auto read_data = input_file.read(kMaxSize);
read_data.result().destroy();
SUCCEED();
}
TEST_F(FileImplTest, InputFile_ReadUntilEOF) {
WriteToFile("abc");
InputFileImpl input_file(path_, GetSize());
AssertEquals(input_file.read(kMaxSize), "abc");
AssertNull(input_file.read(kMaxSize));
}
TEST_F(FileImplTest, InputFile_ReadWithSize) {
WriteToFile("abc");
InputFileImpl input_file(path_, GetSize());
AssertEquals(input_file.read(2), "ab");
AssertEquals(input_file.read(1), "c");
AssertNull(input_file.read(kMaxSize));
}
TEST_F(FileImplTest, InputFile_GetTotalSize) {
WriteToFile("abc");
InputFileImpl input_file(path_, GetSize());
EXPECT_EQ(input_file.getTotalSize(), 3);
AssertEquals(input_file.read(1), "a");
EXPECT_EQ(input_file.getTotalSize(), 3);
}
TEST_F(FileImplTest, InputFile_Close) {
WriteToFile("abc");
InputFileImpl input_file(path_, GetSize());
input_file.close();
ExceptionOr<ConstPtr<ByteArray>> read_result = input_file.read(kMaxSize);
ASSERT_FALSE(read_result.ok());
ASSERT_EQ(read_result.exception(), Exception::IO);
}
TEST_F(FileImplTest, OutputFile_NonExistentPath) {
OutputFileImpl output_file("/not/a/valid/path.txt");
ConstPtr<ByteArray> bytes = MakeConstPtr(new ByteArray("a", 1));
Exception::Value write_result = output_file.write(bytes);
ASSERT_EQ(write_result, Exception::IO);
}
TEST_F(FileImplTest, OutputFile_Write) {
OutputFileImpl output_file(path_);
ConstPtr<ByteArray> bytes1 = MakeConstPtr(new ByteArray("a", 1));
ConstPtr<ByteArray> bytes2 = MakeConstPtr(new ByteArray("bc", 2));
ASSERT_EQ(output_file.write(bytes1), Exception::NONE);
ASSERT_EQ(output_file.write(bytes2), Exception::NONE);
InputFileImpl input_file(path_, GetSize());
AssertEquals(input_file.read(kMaxSize), "abc");
}
TEST_F(FileImplTest, OutputFile_Close) {
OutputFileImpl output_file(path_);
output_file.close();
ConstPtr<ByteArray> bytes = MakeConstPtr(new ByteArray("a", 1));
ASSERT_EQ(output_file.write(bytes), Exception::IO);
}
} // namespace nearby
} // namespace location
+45
View File
@@ -0,0 +1,45 @@
cc_library(
name = "default",
srcs = [
"default_condition_variable.cc",
"default_lock.cc",
"default_platform.cc",
],
hdrs = [
"default_condition_variable.h",
"default_lock.h",
"default_platform.h",
],
visibility = [
"//googlemac/iPhone/Shared/Nearby/Connections:__subpackages__",
"//core:__subpackages__",
],
deps = [
"//platform:types",
"//platform/api",
],
)
cc_library(
name = "lock",
srcs = ["default_lock.cc"],
hdrs = ["default_lock.h"],
visibility = [
"//platform:__subpackages__",
],
deps = ["//platform/api:lock"],
)
cc_library(
name = "condition_variable",
srcs = ["default_condition_variable.cc"],
hdrs = ["default_condition_variable.h"],
visibility = [
"//platform:__subpackages__",
],
deps = [
":default",
"//platform:types",
"//platform/api:condition_variable",
],
)
@@ -0,0 +1,28 @@
#include "platform/impl/default/default_condition_variable.h"
namespace location {
namespace nearby {
DefaultConditionVariable::DefaultConditionVariable(Ptr<DefaultLock> lock)
: lock_(lock), attr_(), cond_() {
pthread_condattr_init(&attr_);
pthread_cond_init(&cond_, &attr_);
}
DefaultConditionVariable::~DefaultConditionVariable() {
pthread_cond_destroy(&cond_);
pthread_condattr_destroy(&attr_);
}
void DefaultConditionVariable::notify() { pthread_cond_broadcast(&cond_); }
Exception::Value DefaultConditionVariable::wait() {
pthread_cond_wait(&cond_, &(lock_->mutex_));
return Exception::NONE;
}
} // namespace nearby
} // namespace location
@@ -0,0 +1,30 @@
#ifndef PLATFORM_IMPL_DEFAULT_DEFAULT_CONDITION_VARIABLE_H_
#define PLATFORM_IMPL_DEFAULT_DEFAULT_CONDITION_VARIABLE_H_
#include <pthread.h>
#include "platform/api/condition_variable.h"
#include "platform/impl/default/default_lock.h"
#include "platform/ptr.h"
namespace location {
namespace nearby {
class DefaultConditionVariable : public ConditionVariable {
public:
explicit DefaultConditionVariable(Ptr<DefaultLock> lock);
~DefaultConditionVariable() override;
void notify() override;
Exception::Value wait() override;
private:
Ptr<DefaultLock> lock_;
pthread_condattr_t attr_;
pthread_cond_t cond_;
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_IMPL_DEFAULT_DEFAULT_CONDITION_VARIABLE_H_
+24
View File
@@ -0,0 +1,24 @@
#include "platform/impl/default/default_lock.h"
namespace location {
namespace nearby {
DefaultLock::DefaultLock() : attr_(), mutex_() {
pthread_mutexattr_init(&attr_);
pthread_mutexattr_settype(&attr_, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&mutex_, &attr_);
}
DefaultLock::~DefaultLock() {
pthread_mutex_destroy(&mutex_);
pthread_mutexattr_destroy(&attr_);
}
void DefaultLock::lock() { pthread_mutex_lock(&mutex_); }
void DefaultLock::unlock() { pthread_mutex_unlock(&mutex_); }
} // namespace nearby
} // namespace location
+29
View File
@@ -0,0 +1,29 @@
#ifndef PLATFORM_IMPL_DEFAULT_DEFAULT_LOCK_H_
#define PLATFORM_IMPL_DEFAULT_DEFAULT_LOCK_H_
#include <pthread.h>
#include "platform/api/lock.h"
namespace location {
namespace nearby {
class DefaultLock : public Lock {
public:
DefaultLock();
~DefaultLock() override;
void lock() override;
void unlock() override;
private:
friend class DefaultConditionVariable;
pthread_mutexattr_t attr_;
pthread_mutex_t mutex_;
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_IMPL_DEFAULT_DEFAULT_LOCK_H_
@@ -0,0 +1,17 @@
#include "platform/impl/default/default_platform.h"
#include "platform/impl/default/default_condition_variable.h"
#include "platform/impl/default/default_lock.h"
namespace location {
namespace nearby {
Ptr<Lock> DefaultPlatform::createLock() { return MakePtr(new DefaultLock()); }
Ptr<ConditionVariable> DefaultPlatform::createConditionVariable(
Ptr<Lock> lock) {
return MakePtr(new DefaultConditionVariable(DowncastPtr<DefaultLock>(lock)));
}
} // namespace nearby
} // namespace location
@@ -0,0 +1,26 @@
#ifndef PLATFORM_IMPL_DEFAULT_DEFAULT_PLATFORM_H_
#define PLATFORM_IMPL_DEFAULT_DEFAULT_PLATFORM_H_
#include "platform/api/condition_variable.h"
#include "platform/api/lock.h"
#include "platform/ptr.h"
namespace location {
namespace nearby {
// Provides obvious portable implementations of a subset of the hooks specified
// within //platform/api/.
//
// It's highly recommended that custom Platform implementations delegate to
// these methods unless there's a very good reason not to.
class DefaultPlatform {
public:
static Ptr<Lock> createLock();
static Ptr<ConditionVariable> createConditionVariable(Ptr<Lock> lock);
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_IMPL_DEFAULT_DEFAULT_PLATFORM_H_
View File
+9
View File
@@ -0,0 +1,9 @@
objc_library(
name = "ios",
visibility = [
"//googlemac/iPhone/Nearby/HelloSetup:__subpackages__",
],
deps = [
"//googlemac/iPhone/Shared/Nearby/Connections:Platform",
],
)
+20
View File
@@ -0,0 +1,20 @@
cc_library(
name = "sample",
srcs = [
"sample_wifi_medium.cc",
"sample_wifi_medium.h",
],
hdrs = ["sample_platform.h"],
visibility = [
"//googlemac/iPhone/Shared/Nearby/Connections:__subpackages__",
"//core:__subpackages__",
"//location/nearby/setup/core:__subpackages__",
],
deps = [
"//platform:types",
"//platform:utils",
"//platform/api",
"//platform/port:string",
"//absl/time",
],
)
+141
View File
@@ -0,0 +1,141 @@
#ifndef PLATFORM_IMPL_SAMPLE_SAMPLE_PLATFORM_H_
#define PLATFORM_IMPL_SAMPLE_SAMPLE_PLATFORM_H_
#include <cstdint>
#include "platform/api/atomic_boolean.h"
#include "platform/api/atomic_reference.h"
#include "platform/api/ble.h"
#include "platform/api/ble_v2.h"
#include "platform/api/bluetooth_adapter.h"
#include "platform/api/bluetooth_classic.h"
#include "platform/api/condition_variable.h"
#include "platform/api/count_down_latch.h"
#include "platform/api/hash_utils.h"
#include "platform/api/lock.h"
#include "platform/api/multi_thread_executor.h"
#include "platform/api/settable_future.h"
#include "platform/api/single_thread_executor.h"
#include "platform/api/system_clock.h"
#include "platform/api/thread_utils.h"
#include "platform/api/wifi.h"
#include "platform/cancelable.h"
#include "platform/impl/sample/sample_wifi_medium.h"
#include "platform/port/string.h"
#include "platform/ptr.h"
#include "platform/runnable.h"
namespace location {
namespace nearby {
namespace sample {
// The SamplePlatform class below shows an example of the factory functions
// and typedefs.
class SamplePlatform {
public:
class SampleSubmittableExecutor
: public SubmittableExecutor<SampleSubmittableExecutor> {
public:
template <typename T>
Ptr<Future<T> > submit(Ptr<Callable<T> > callable) {
return Ptr<Future<T> >();
}
};
class SampleSingleThreadExecutor
: public SingleThreadExecutor<SampleSubmittableExecutor> {
public:
void execute(Ptr<Runnable> runnable) override {}
void shutdown() override {}
};
class SampleMultiThreadExecutor
: public MultiThreadExecutor<SampleSubmittableExecutor> {
public:
void execute(Ptr<Runnable> runnable) override {}
void shutdown() override {}
};
class SampleScheduledExecutor {
public:
Ptr<Cancelable> schedule(Ptr<Runnable> runnable,
std::int64_t delay_millis) {
return Ptr<Cancelable>();
}
void shutdown() {}
};
typedef SampleSingleThreadExecutor SingleThreadExecutorType;
static Ptr<SingleThreadExecutorType> createSingleThreadExecutor() {
return MakePtr(new SingleThreadExecutorType());
}
typedef SampleMultiThreadExecutor MultiThreadExecutorType;
static Ptr<MultiThreadExecutorType> createMultiThreadExecutor(
std::int32_t max_concurrency) {
return MakePtr(new MultiThreadExecutorType());
}
typedef SampleScheduledExecutor ScheduledExecutorType;
static Ptr<ScheduledExecutorType> createScheduledExecutor() {
return MakePtr(new ScheduledExecutorType());
}
static Ptr<BluetoothAdapter> createBluetoothAdapter() {
return Ptr<BluetoothAdapter>();
}
static Ptr<WifiMedium> createWifiMedium() {
return MakePtr(new SampleWifiMedium());
}
static Ptr<CountDownLatch> createCountDownLatch(std::int32_t count) {
return Ptr<CountDownLatch>();
}
template <typename T>
static Ptr<SettableFuture<T> > createSettableFuture() {
return Ptr<SettableFuture<T> >();
}
static Ptr<ThreadUtils> createThreadUtils() { return Ptr<ThreadUtils>(); }
static Ptr<SystemClock> createSystemClock() { return Ptr<SystemClock>(); }
static Ptr<AtomicBoolean> createAtomicBoolean(bool initial_value) {
return Ptr<AtomicBoolean>();
}
template <typename T>
static Ptr<AtomicReference<T> > createAtomicReference(T initial_value) {
return Ptr<AtomicReference<T> >();
}
static Ptr<BluetoothClassicMedium> createBluetoothClassicMedium() {
return Ptr<BluetoothClassicMedium>();
}
static Ptr<BLEMedium> createBLEMedium() { return Ptr<BLEMedium>(); }
static Ptr<BLEMediumV2> createBLEMediumV2() { return Ptr<BLEMediumV2>(); }
static Ptr<Lock> createLock() { return Ptr<Lock>(); }
static Ptr<ConditionVariable> createConditionVariable(Ptr<Lock> lock) {
return Ptr<ConditionVariable>();
}
static Ptr<HashUtils> createHashUtils() { return Ptr<HashUtils>(); }
static std::string getDeviceId() { return ""; }
static std::string getPayloadPath(int64_t payload_id) {
return "/tmp/" + std::to_string(payload_id);
}
};
} // namespace sample
} // namespace nearby
} // namespace location
#endif // PLATFORM_IMPL_SAMPLE_SAMPLE_PLATFORM_H_
@@ -0,0 +1,110 @@
#include "platform/impl/sample/sample_wifi_medium.h"
#include <cstdint>
#include "platform/prng.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
namespace location {
namespace nearby {
namespace sample {
namespace {
const char* kOpenSSID = "__OPEN__";
const char* kWpaPskSSID = "__WPA_PSK__";
const char* kWepSSID = "__WEP__";
const char* kNoInternetConnectivitySSID = "__NO_INTERNET_CONNECTIVITY__";
const char* kConnectionFailureSSID = "__CONNECTION_FAILURE__";
const char* kAuthFailureSSID = "__AUTH_FAILURE__";
std::uint32_t boundedUInt32(std::uint32_t upper_limit) {
return Prng().nextUInt32() % (upper_limit + 1);
}
void randomSleep(std::uint32_t upper_limit_millis) {
absl::SleepFor(absl::Milliseconds(boundedUInt32(upper_limit_millis)));
}
} // namespace
std::vector<SampleWifiScanResult> SampleWifiMedium::canned_scan_results_;
SampleWifiMedium::SampleWifiMedium() : current_ssid_() {
// One-time initialization of our static canned_scan_results_.
if (canned_scan_results_.empty()) {
canned_scan_results_.push_back(
SampleWifiScanResult(kOpenSSID, 1, 2401, WifiAuthType::OPEN));
canned_scan_results_.push_back(
SampleWifiScanResult(kWpaPskSSID, 2, 5002, WifiAuthType::WPA_PSK));
canned_scan_results_.push_back(
SampleWifiScanResult(kWepSSID, 3, 2403, WifiAuthType::WEP));
canned_scan_results_.push_back(SampleWifiScanResult(
kNoInternetConnectivitySSID, 4, 5004, WifiAuthType::OPEN));
canned_scan_results_.push_back(SampleWifiScanResult(
kConnectionFailureSSID, 5, 2405, WifiAuthType::OPEN));
canned_scan_results_.push_back(
SampleWifiScanResult(kAuthFailureSSID, 6, 5006, WifiAuthType::OPEN));
}
}
SampleWifiMedium::~SampleWifiMedium() {}
bool SampleWifiMedium::scan(
Ptr<WifiMedium::ScanResultCallback> scan_result_callback) {
// Sleep for up to 10 seconds, to simulate performing an actual Wifi scan.
randomSleep(10 * 1000);
// Construct the response.
std::vector<ConstPtr<WifiScanResult> > scan_results;
for (std::vector<SampleWifiScanResult>::const_iterator it =
canned_scan_results_.begin();
it != canned_scan_results_.end(); it++) {
scan_results.push_back(ConstPtr<WifiScanResult>(
new SampleWifiScanResult(it->getSSID(), it->getSignalStrengthDbm(),
it->getFrequencyMhz(), it->getAuthType())));
}
// And report it back.
scan_result_callback->onScanResults(scan_results);
return false;
}
WifiConnectionStatus::Value SampleWifiMedium::connectToNetwork(
const std::string& ssid, const std::string& password,
WifiAuthType::Value auth_type) {
// Sleep for up to 10 seconds, to simulate actually connecting to the SSID.
randomSleep(10 * 1000);
if (kConnectionFailureSSID == ssid) {
return WifiConnectionStatus::CONNECTION_FAILURE;
}
if (kAuthFailureSSID == ssid) {
return WifiConnectionStatus::AUTH_FAILURE;
}
return WifiConnectionStatus::CONNECTED;
}
bool SampleWifiMedium::verifyInternetConnectivity() {
if (current_ssid_.empty()) {
return false;
}
// Sleep for up to 5 seconds, to simulate actually verifying internet
// connectivity.
randomSleep(5 * 1000);
return current_ssid_ != kNoInternetConnectivitySSID;
}
std::string SampleWifiMedium::getIPAddress() {
return current_ssid_.empty() ? "" : "1.2.3.4";
}
} // namespace sample
} // namespace nearby
} // namespace location
@@ -0,0 +1,59 @@
#ifndef PLATFORM_IMPL_SAMPLE_SAMPLE_WIFI_MEDIUM_H_
#define PLATFORM_IMPL_SAMPLE_SAMPLE_WIFI_MEDIUM_H_
#include "platform/api/wifi.h"
namespace location {
namespace nearby {
namespace sample {
class SampleWifiScanResult : public WifiScanResult {
public:
SampleWifiScanResult(const std::string& ssid,
std::int32_t signal_strength_dbm,
std::int32_t frequency_mhz,
WifiAuthType::Value auth_type)
: ssid_(ssid),
signal_strength_dbm_(signal_strength_dbm),
frequency_mhz_(frequency_mhz),
auth_type_(auth_type) {}
~SampleWifiScanResult() override {}
std::string getSSID() const override { return ssid_; }
std::int32_t getSignalStrengthDbm() const override {
return signal_strength_dbm_;
}
std::int32_t getFrequencyMhz() const override { return frequency_mhz_; }
WifiAuthType::Value getAuthType() const override { return auth_type_; }
private:
const std::string ssid_;
const std::int32_t signal_strength_dbm_;
const std::int32_t frequency_mhz_;
const WifiAuthType::Value auth_type_;
};
class SampleWifiMedium : public WifiMedium {
public:
SampleWifiMedium();
~SampleWifiMedium() override;
bool scan(Ptr<ScanResultCallback> scan_result_callback) override;
WifiConnectionStatus::Value connectToNetwork(
const std::string& ssid, const std::string& password,
WifiAuthType::Value auth_type) override;
bool verifyInternetConnectivity() override;
std::string getIPAddress() override;
private:
static std::vector<SampleWifiScanResult> canned_scan_results_;
// The SSID this Wifi stack is currently connected to; empty string if none.
std::string current_ssid_;
};
} // namespace sample
} // namespace nearby
} // namespace location
#endif // PLATFORM_IMPL_SAMPLE_SAMPLE_WIFI_MEDIUM_H_
+29
View File
@@ -0,0 +1,29 @@
#ifndef PLATFORM_LOGGING_H_
#define PLATFORM_LOGGING_H_
#include "absl/base/internal/raw_logging.h"
namespace location {
namespace nearby {
// This uses an explicit printf-format and arguments list, and supports the
// following severities:
//
// - INFO
// - WARNING
// - ERROR
// - FATAL
//
// To make it easy to filer while debugging, it prepends "[NEARBY] " to all its
// logged messages.
//
// Sample usage:
//
// NEARBY_LOG(INFO, "%d is an int and %s is a std::string", i, s.c_str());
#define NEARBY_LOG(severity, ...) \
ABSL_RAW_LOG(severity, "[NEARBY] " __VA_ARGS__)
} // namespace nearby
} // namespace location
#endif // PLATFORM_LOGGING_H_
+199
View File
@@ -0,0 +1,199 @@
#include "platform/pipe.h"
#include "platform/synchronized.h"
namespace location {
namespace nearby {
namespace pipe {
template <typename Platform>
class PipeInputStream : public InputStream {
public:
explicit PipeInputStream(Ptr<Pipe<Platform>> pipe) : pipe_(pipe) {}
~PipeInputStream() override {
close();
}
ExceptionOr<ConstPtr<ByteArray>> read() override { return read(kChunkSize); }
ExceptionOr<ConstPtr<ByteArray>> read(std::int64_t size) override {
return pipe_->read(size);
}
Exception::Value close() override {
pipe_->markInputStreamClosed();
return Exception::NONE;
}
private:
static const std::int64_t kChunkSize = 64 * 1024;
Ptr<Pipe<Platform>> pipe_;
};
template <typename Platform>
class PipeOutputStream : public OutputStream {
public:
explicit PipeOutputStream(Ptr<Pipe<Platform>> pipe) : pipe_(pipe) {}
~PipeOutputStream() override {
close();
}
Exception::Value write(ConstPtr<ByteArray> data) override {
// Avoid leaks.
ScopedPtr<ConstPtr<ByteArray>> scoped_data(data);
return pipe_->write(scoped_data.release());
}
Exception::Value flush() override {
// No-op.
return Exception::NONE;
}
Exception::Value close() override {
pipe_->markOutputStreamClosed();
return Exception::NONE;
}
private:
Ptr<Pipe<Platform>> pipe_;
};
} // namespace pipe
template <typename Platform>
Pipe<Platform>::Pipe()
: lock_(Platform::createLock()),
cond_(Platform::createConditionVariable(lock_.get())),
buffer_(),
input_stream_closed_(false),
output_stream_closed_(false),
read_all_chunks_(false) {}
template <typename Platform>
Pipe<Platform>::~Pipe() {
// Deallocate all the chunks still left in buffer_.
for (BufferType::iterator chunk_iter = buffer_.begin();
chunk_iter != buffer_.end(); ++chunk_iter) {
(*chunk_iter).destroy();
}
}
template <typename Platform>
Ptr<InputStream> Pipe<Platform>::createInputStream(Ptr<Pipe> self) {
assert(self.isRefCounted());
return MakeRefCountedPtr(new pipe::PipeInputStream<Platform>(self));
}
template <typename Platform>
Ptr<OutputStream> Pipe<Platform>::createOutputStream(Ptr<Pipe> self) {
assert(self.isRefCounted());
return MakeRefCountedPtr(new pipe::PipeOutputStream<Platform>(self));
}
template <typename Platform>
ExceptionOr<ConstPtr<ByteArray>> Pipe<Platform>::read(std::int64_t size) {
Synchronized s(lock_.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_) {
ExceptionOr<ConstPtr<ByteArray>>(ConstPtr<ByteArray>());
}
while (buffer_.empty() && !input_stream_closed_) {
Exception::Value wait_exception = cond_->wait();
if (Exception::NONE != wait_exception) {
if (Exception::INTERRUPTED == wait_exception) {
return ExceptionOr<ConstPtr<ByteArray>>(Exception::IO);
}
}
}
if (input_stream_closed_) {
return ExceptionOr<ConstPtr<ByteArray>>(Exception::IO);
}
ScopedPtr<ConstPtr<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.isNull()) {
read_all_chunks_ = true;
return ExceptionOr<ConstPtr<ByteArray>>(ConstPtr<ByteArray>());
}
// If first_chunk is small enough to not overshoot the requested 'size', just
// return that.
if (first_chunk->size() <= size) {
return ExceptionOr<ConstPtr<ByteArray>>(first_chunk.release());
} 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().
ScopedPtr<ConstPtr<ByteArray>> next_chunk(
MakeConstPtr(new ByteArray(first_chunk->getData(), size)));
ScopedPtr<ConstPtr<ByteArray>> overflow_chunk(MakeConstPtr(new ByteArray(
first_chunk->getData() + size, first_chunk->size() - size)));
buffer_.push_front(overflow_chunk.release());
return ExceptionOr<ConstPtr<ByteArray>>(next_chunk.release());
}
}
template <typename Platform>
Exception::Value Pipe<Platform>::write(ConstPtr<ByteArray> data) {
Synchronized s(lock_.get());
return writeLocked(data);
}
template <typename Platform>
void Pipe<Platform>::markInputStreamClosed() {
Synchronized s(lock_.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();
}
template <typename Platform>
void Pipe<Platform>::markOutputStreamClosed() {
Synchronized s(lock_.get());
// Write a sentinel null chunk before marking output_stream_closed as true.
writeLocked(ConstPtr<ByteArray>());
output_stream_closed_ = true;
}
template <typename Platform>
Exception::Value Pipe<Platform>::writeLocked(ConstPtr<ByteArray> data) {
// Avoid leaks.
ScopedPtr<ConstPtr<ByteArray>> scoped_data(data);
if (eitherStreamClosed()) {
return Exception::IO;
}
buffer_.push_back(scoped_data.release());
// Trigger cond_ to unblock a potentially-blocked call to read(), now that
// there's more data for it to consume.
cond_->notify();
return Exception::NONE;
}
template <typename Platform>
bool Pipe<Platform>::eitherStreamClosed() const {
return input_stream_closed_ || output_stream_closed_;
}
} // namespace nearby
} // namespace location
+75
View File
@@ -0,0 +1,75 @@
#ifndef PLATFORM_PIPE_H_
#define PLATFORM_PIPE_H_
#include <cstdint>
#include <deque>
#include "platform/api/condition_variable.h"
#include "platform/api/input_stream.h"
#include "platform/api/lock.h"
#include "platform/api/output_stream.h"
#include "platform/byte_array.h"
#include "platform/exception.h"
#include "platform/ptr.h"
namespace location {
namespace nearby {
namespace pipe {
template <typename>
class PipeInputStream;
template <typename>
class PipeOutputStream;
} // namespace pipe
template <typename Platform>
class Pipe {
public:
Pipe();
~Pipe();
// The returned InputStream is auto-destroyed when no longer referenced.
static Ptr<InputStream> createInputStream(Ptr<Pipe>);
// The returned OutputStream is auto-destroyed when no longer referenced.
static Ptr<OutputStream> createOutputStream(Ptr<Pipe>);
private:
//////////////////////////////////////////////////////////////////////////////
// Everything in this first private: section is only used by PipeInputStream
// and PipeOutputStream, thus forming the interface presented to those 2
// classes.
//////////////////////////////////////////////////////////////////////////////
template <typename>
friend class pipe::PipeInputStream;
template <typename>
friend class pipe::PipeOutputStream;
ExceptionOr<ConstPtr<ByteArray> > read(std::int64_t size);
Exception::Value write(ConstPtr<ByteArray> data);
void markInputStreamClosed();
void markOutputStreamClosed();
private:
Exception::Value writeLocked(ConstPtr<ByteArray> data);
bool eitherStreamClosed() const;
ScopedPtr<Ptr<Lock> > lock_;
ScopedPtr<Ptr<ConditionVariable> > cond_;
typedef std::deque<ConstPtr<ByteArray> > BufferType;
BufferType buffer_;
bool input_stream_closed_;
bool output_stream_closed_;
bool read_all_chunks_;
};
} // namespace nearby
} // namespace location
#include "platform/pipe.cc"
#endif // PLATFORM_PIPE_H_
+407
View File
@@ -0,0 +1,407 @@
#include "platform/pipe.h"
#include <pthread.h>
#include <cstring>
#include "platform/impl/default/default_condition_variable.h"
#include "platform/impl/default/default_lock.h"
#include "platform/port/string.h"
#include "platform/prng.h"
#include "platform/ptr.h"
#include "platform/runnable.h"
#include "gtest/gtest.h"
#include "absl/time/time.h"
namespace location {
namespace nearby {
namespace {
class SamplePlatform {
public:
static Ptr<Lock> createLock() { return MakePtr(new DefaultLock()); }
static Ptr<ConditionVariable> createConditionVariable(Ptr<Lock> lock) {
return MakePtr(
new DefaultConditionVariable(DowncastPtr<DefaultLock>(lock)));
}
};
using SamplePipe = Pipe<SamplePlatform>;
TEST(PipeTest, SimpleWriteRead) {
auto pipe = MakeRefCountedPtr(new SamplePipe());
ScopedPtr<Ptr<InputStream>> input_stream(SamplePipe::createInputStream(pipe));
ScopedPtr<Ptr<OutputStream>> output_stream(
SamplePipe::createOutputStream(pipe));
std::string data("ABCD");
ASSERT_EQ(Exception::NONE, output_stream->write(MakeConstPtr(
new ByteArray(data.data(), data.size()))));
ExceptionOr<ConstPtr<ByteArray>> read_data = input_stream->read();
ASSERT_TRUE(read_data.ok());
ScopedPtr<ConstPtr<ByteArray>> scoped_read_data(read_data.result());
ASSERT_EQ(data.size(), scoped_read_data->size());
ASSERT_EQ(0, memcmp(data.data(), scoped_read_data->getData(),
scoped_read_data->size()));
}
TEST(PipeTest, WriteEndClosedBeforeRead) {
auto pipe = MakeRefCountedPtr(new SamplePipe());
ScopedPtr<Ptr<InputStream>> input_stream(SamplePipe::createInputStream(pipe));
ScopedPtr<Ptr<OutputStream>> output_stream(
SamplePipe::createOutputStream(pipe));
std::string data("ABCD");
ASSERT_EQ(Exception::NONE, output_stream->write(MakeConstPtr(
new ByteArray(data.data(), data.size()))));
// Close the write end before the read end has even begun reading.
ASSERT_EQ(Exception::NONE, output_stream->close());
// We should still be able to read what was written.
ExceptionOr<ConstPtr<ByteArray>> read_data = input_stream->read();
ASSERT_TRUE(read_data.ok());
ScopedPtr<ConstPtr<ByteArray>> scoped_read_data(read_data.result());
ASSERT_EQ(data.size(), scoped_read_data->size());
ASSERT_EQ(0, memcmp(data.data(), scoped_read_data->getData(),
scoped_read_data->size()));
// And after that, we should get our indication that all the data that could
// ever be read, has already been read.
read_data = input_stream->read();
ASSERT_TRUE(read_data.ok());
ASSERT_TRUE(read_data.result().isNull());
}
TEST(PipeTest, ReadEndClosedBeforeWrite) {
auto pipe = MakeRefCountedPtr(new SamplePipe());
ScopedPtr<Ptr<InputStream>> input_stream(SamplePipe::createInputStream(pipe));
ScopedPtr<Ptr<OutputStream>> output_stream(
SamplePipe::createOutputStream(pipe));
// Close the read end before the write end has even begun writing.
ASSERT_EQ(Exception::NONE, input_stream->close());
std::string data("ABCD");
ASSERT_EQ(Exception::IO, output_stream->write(MakeConstPtr(
new ByteArray(data.data(), data.size()))));
}
TEST(PipeTest, SizedReadMoreThanFirstChunkSize) {
auto pipe = MakeRefCountedPtr(new SamplePipe());
ScopedPtr<Ptr<InputStream>> input_stream(SamplePipe::createInputStream(pipe));
ScopedPtr<Ptr<OutputStream>> output_stream(
SamplePipe::createOutputStream(pipe));
std::string data("ABCD");
ASSERT_EQ(Exception::NONE, output_stream->write(MakeConstPtr(
new ByteArray(data.data(), data.size()))));
// Even though we ask for double of what's there in the first chunk, we should
// get back only what's there in that first chunk, and that's alright.
ExceptionOr<ConstPtr<ByteArray>> read_data =
input_stream->read(data.size() * 2);
ASSERT_TRUE(read_data.ok());
ScopedPtr<ConstPtr<ByteArray>> scoped_read_data(read_data.result());
ASSERT_EQ(data.size(), scoped_read_data->size());
ASSERT_EQ(0, memcmp(data.data(), scoped_read_data->getData(),
scoped_read_data->size()));
}
TEST(PipeTest, SizedReadLessThanFirstChunkSize) {
auto pipe = MakeRefCountedPtr(new SamplePipe());
ScopedPtr<Ptr<InputStream>> input_stream(SamplePipe::createInputStream(pipe));
ScopedPtr<Ptr<OutputStream>> output_stream(
SamplePipe::createOutputStream(pipe));
// Compose 'data' of 2 parts, to make it easier to validate our expectations.
std::string data_first_part("ABCD");
std::string data_second_part("EFGHIJ");
std::string data = data_first_part + data_second_part;
ASSERT_EQ(Exception::NONE, output_stream->write(MakeConstPtr(
new ByteArray(data.data(), data.size()))));
// When we ask for less than what's there in the first chunk, we should get
// back exactly what we asked for, with the remainder still being available
// for the next read.
std::int64_t desired_size = data_first_part.size();
ExceptionOr<ConstPtr<ByteArray>> first_read_data =
input_stream->read(desired_size);
ASSERT_TRUE(first_read_data.ok());
ScopedPtr<ConstPtr<ByteArray>> scoped_first_read_data(
first_read_data.result());
ASSERT_EQ(desired_size, scoped_first_read_data->size());
ASSERT_EQ(0, memcmp(data_first_part.data(), scoped_first_read_data->getData(),
scoped_first_read_data->size()));
// Now read the remainder, and get everything that ought to have been left.
std::int64_t remaining_size = data_second_part.size();
ExceptionOr<ConstPtr<ByteArray>> second_read_data = input_stream->read();
ASSERT_TRUE(second_read_data.ok());
ScopedPtr<ConstPtr<ByteArray>> scoped_second_read_data(
second_read_data.result());
ASSERT_EQ(remaining_size, scoped_second_read_data->size());
ASSERT_EQ(0,
memcmp(data_second_part.data(), scoped_second_read_data->getData(),
scoped_second_read_data->size()));
}
TEST(PipeTest, ReadAfterInputStreamClosed) {
auto pipe = MakeRefCountedPtr(new SamplePipe());
ScopedPtr<Ptr<InputStream>> input_stream(SamplePipe::createInputStream(pipe));
ScopedPtr<Ptr<OutputStream>> output_stream(
SamplePipe::createOutputStream(pipe));
input_stream->close();
ExceptionOr<ConstPtr<ByteArray>> read_data = input_stream->read();
ASSERT_TRUE(!read_data.ok());
ASSERT_EQ(Exception::IO, read_data.exception());
}
TEST(PipeTest, WriteAfterOutputStreamClosed) {
auto pipe = MakeRefCountedPtr(new SamplePipe());
ScopedPtr<Ptr<InputStream>> input_stream(SamplePipe::createInputStream(pipe));
ScopedPtr<Ptr<OutputStream>> output_stream(
SamplePipe::createOutputStream(pipe));
output_stream->close();
std::string data("ABCD");
ASSERT_EQ(Exception::IO, output_stream->write(MakeConstPtr(
new ByteArray(data.data(), data.size()))));
}
TEST(PipeTest, RepeatedClose) {
auto pipe = MakeRefCountedPtr(new SamplePipe());
ScopedPtr<Ptr<InputStream>> input_stream(SamplePipe::createInputStream(pipe));
ScopedPtr<Ptr<OutputStream>> output_stream(
SamplePipe::createOutputStream(pipe));
ASSERT_EQ(Exception::NONE, output_stream->close());
ASSERT_EQ(Exception::NONE, output_stream->close());
ASSERT_EQ(Exception::NONE, output_stream->close());
ASSERT_EQ(Exception::NONE, input_stream->close());
ASSERT_EQ(Exception::NONE, input_stream->close());
ASSERT_EQ(Exception::NONE, input_stream->close());
}
class Thread {
public:
Thread() : thread_(), attr_(), runnable_() {
pthread_attr_init(&attr_);
pthread_attr_setdetachstate(&attr_, PTHREAD_CREATE_JOINABLE);
}
~Thread() { pthread_attr_destroy(&attr_); }
void start(Ptr<Runnable> runnable) {
runnable_ = runnable;
pthread_create(&thread_, &attr_, Thread::body, this);
}
void join() {
pthread_join(thread_, nullptr);
runnable_.destroy();
}
private:
static void* body(void* args) {
reinterpret_cast<Thread*>(args)->runnable_->run();
return nullptr;
}
pthread_t thread_;
pthread_attr_t attr_;
Ptr<Runnable> runnable_;
};
TEST(PipeTest, ReadBlockedUntilWrite) {
typedef volatile bool CrossThreadBool;
class ReaderRunnable : public Runnable {
public:
ReaderRunnable(Ptr<InputStream> input_stream,
const std::string& expected_read_data,
CrossThreadBool* ok_for_read_to_unblock)
: input_stream_(input_stream),
expected_read_data_(expected_read_data),
ok_for_read_to_unblock_(ok_for_read_to_unblock) {}
~ReaderRunnable() override {}
void run() override {
ExceptionOr<ConstPtr<ByteArray>> read_data = input_stream_->read();
// Make sure read() doesn't return before it's appropriate.
if (!*ok_for_read_to_unblock_) {
FAIL() << "read() unblocked before it was supposed to.";
}
// And then run our normal set of checks to make sure the read() was
// successful.
ASSERT_TRUE(read_data.ok());
ScopedPtr<ConstPtr<ByteArray>> scoped_read_data(read_data.result());
ASSERT_EQ(expected_read_data_.size(), scoped_read_data->size());
ASSERT_EQ(0,
memcmp(expected_read_data_.data(), scoped_read_data->getData(),
scoped_read_data->size()));
}
private:
ScopedPtr<Ptr<InputStream>> input_stream_;
const std::string& expected_read_data_;
CrossThreadBool* ok_for_read_to_unblock_;
};
auto pipe = MakeRefCountedPtr(new SamplePipe());
ScopedPtr<Ptr<OutputStream>> output_stream(
SamplePipe::createOutputStream(pipe));
// State shared between this thread (the writer) and reader_thread.
CrossThreadBool ok_for_read_to_unblock = false;
std::string data("ABCD");
// Kick off reader_thread.
Thread reader_thread;
reader_thread.start(MakePtr(new ReaderRunnable(
SamplePipe::createInputStream(pipe), data, &ok_for_read_to_unblock)));
// Introduce a delay before we actually write anything.
absl::SleepFor(absl::Seconds(5));
// Mark that we're done with the delay, and that the write is about to occur
// (this is slightly earlier than it ought to be, but there's no way to
// atomically set this from within the implementation of write(), and doing it
// after is too late for the purposes of this test).
ok_for_read_to_unblock = true;
// Perform the actual write.
ASSERT_EQ(Exception::NONE, output_stream->write(MakeConstPtr(
new ByteArray(data.data(), data.size()))));
// And wait for reader_thread to finish.
reader_thread.join();
}
TEST(PipeTest, ConcurrentWriteAndRead) {
class BaseRunnable : public Runnable {
protected:
explicit BaseRunnable(const std::vector<std::string>& chunks)
: chunks_(chunks), prng_() {}
~BaseRunnable() override {}
void randomSleep() {
// Generate a random sleep between 100 and 1000 milliseconds.
absl::SleepFor(absl::Milliseconds(boundedUInt32(100, 1000)));
}
const std::vector<std::string>& chunks_;
private:
// Both ends of the bounds are inclusive.
std::uint32_t boundedUInt32(std::uint32_t lower_bound,
std::uint32_t upper_bound) {
return (prng_.nextUInt32() % (upper_bound - lower_bound + 1)) +
lower_bound;
}
Prng prng_;
};
class WriterRunnable : public BaseRunnable {
public:
WriterRunnable(Ptr<OutputStream> output_stream,
const std::vector<std::string>& chunks)
: BaseRunnable(chunks), output_stream_(output_stream) {}
~WriterRunnable() override {}
void run() override {
for (std::vector<std::string>::const_iterator it = chunks_.begin();
it != chunks_.end(); ++it) {
const std::string& chunk = *it;
randomSleep(); // Random pauses before each write.
ASSERT_EQ(Exception::NONE,
output_stream_->write(
MakeConstPtr(new ByteArray(chunk.data(), chunk.size()))));
}
randomSleep(); // A random pause before closing the writer end.
ASSERT_EQ(Exception::NONE, output_stream_->close());
}
private:
ScopedPtr<Ptr<OutputStream>> output_stream_;
};
class ReaderRunnable : public BaseRunnable {
public:
ReaderRunnable(Ptr<InputStream> input_stream,
const std::vector<std::string>& chunks)
: BaseRunnable(chunks), input_stream_(input_stream) {}
~ReaderRunnable() override {}
void run() override {
// First, calculate what we expect to receive, in total.
std::string expected_data;
for (std::vector<std::string>::const_iterator it = chunks_.begin();
it != chunks_.end(); ++it) {
expected_data += *it;
}
// Then, start actually receiving.
std::string actual_data;
while (true) {
randomSleep(); // Random pauses before each read.
ExceptionOr<ConstPtr<ByteArray>> read_data = input_stream_->read();
if (read_data.ok()) {
ScopedPtr<ConstPtr<ByteArray>> scoped_read_data(read_data.result());
if (scoped_read_data.isNull()) {
break; // Normal exit from the read loop.
}
actual_data += std::string(scoped_read_data->getData(),
scoped_read_data->size());
} else {
break; // Erroneous exit from the read loop.
}
}
// And once we're done, check that we got everything we expected.
ASSERT_EQ(expected_data, actual_data);
}
private:
ScopedPtr<Ptr<InputStream>> input_stream_;
};
auto pipe = MakeRefCountedPtr(new SamplePipe());
std::vector<std::string> chunks;
chunks.push_back("ABCD");
chunks.push_back("EFGH");
chunks.push_back("IJKL");
Thread writer_thread;
Thread reader_thread;
writer_thread.start(MakePtr(
new WriterRunnable(SamplePipe::createOutputStream(pipe), chunks)));
reader_thread.start(
MakePtr(new ReaderRunnable(SamplePipe::createInputStream(pipe), chunks)));
writer_thread.join();
reader_thread.join();
}
} // namespace
} // namespace nearby
} // namespace location
+38
View File
@@ -0,0 +1,38 @@
cc_library(
name = "config",
hdrs = [
"config.h",
],
visibility = [
"//visibility:private",
],
)
cc_library(
name = "string",
hdrs = [
"string.h",
],
visibility = [
"//core:__subpackages__",
"//platform:__subpackages__",
"//location/nearby/setup/core:__subpackages__",
],
deps = [
":config",
],
)
cc_library(
name = "down_cast",
hdrs = [
"down_cast.h",
],
visibility = [
"//core:__subpackages__",
"//platform:__subpackages__",
],
deps = [
":config",
],
)
+22
View File
@@ -0,0 +1,22 @@
#ifndef PLATFORM_PORT_CONFIG_H_
#define PLATFORM_PORT_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_PORT_CONFIG_H_
+12
View File
@@ -0,0 +1,12 @@
#ifndef PLATFORM_PORT_DOWN_CAST_H_
#define PLATFORM_PORT_DOWN_CAST_H_
#include "platform/port/config.h"
#if NEARBY_USE_RTTI
#define DOWN_CAST dynamic_cast
#else
#define DOWN_CAST static_cast
#endif
#endif // PLATFORM_PORT_DOWN_CAST_H_
+12
View File
@@ -0,0 +1,12 @@
#ifndef PLATFORM_PORT_STRING_H_
#define PLATFORM_PORT_STRING_H_
#include <string>
#include "platform/port/config.h"
#if NEARBY_USE_STD_STRING
using std::string;
#endif
#endif // PLATFORM_PORT_STRING_H_
+45
View File
@@ -0,0 +1,45 @@
#include "platform/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_PRNG_H_
#define PLATFORM_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_PRNG_H_
+27
View File
@@ -0,0 +1,27 @@
#include "platform/prng.h"
#include "gtest/gtest.h"
namespace location {
namespace nearby {
TEST(PrngTest, NextInt32) {
std::int32_t i = Prng().nextInt32();
ASSERT_LE(i, std::numeric_limits<std::int32_t>::max());
ASSERT_GE(i, std::numeric_limits<std::int32_t>::min());
}
TEST(PrngTest, NextUInt32) {
std::uint32_t i = Prng().nextUInt32();
ASSERT_LE(i, std::numeric_limits<std::uint32_t>::max());
ASSERT_GE(i, std::numeric_limits<std::uint32_t>::min());
}
TEST(PrngTest, NextInt64) {
std::int64_t i = Prng().nextInt64();
ASSERT_LE(i, std::numeric_limits<std::int64_t>::max());
ASSERT_GE(i, std::numeric_limits<std::int64_t>::min());
}
} // namespace nearby
} // namespace location
+13
View File
@@ -0,0 +1,13 @@
#include "platform/ptr.h"
namespace location {
namespace nearby {
namespace ptr_impl {
const std::int32_t RefCount::kInitialCount = 0;
} // namespace ptr_impl
} // namespace nearby
} // namespace location
+400
View File
@@ -0,0 +1,400 @@
#ifndef PLATFORM_PTR_H_
#define PLATFORM_PTR_H_
#include <cassert>
#include <cstddef>
#include <cstdint>
#include "platform/impl/default/default_lock.h"
#include "platform/logging.h"
#include "platform/port/down_cast.h"
namespace location {
namespace nearby {
namespace ptr_impl {
class RefCount {
public:
RefCount() : lock_(), count_(kInitialCount) {}
// Returns false if this operation doesn't make conceptual sense any more
// (for example, if it leads to bringing count_ back from the dead).
bool increment() {
bool result;
lock_.lock();
{
// Avoid coming back from the dead.
if (count_ < kInitialCount) {
result = false;
} else {
count_++;
result = true;
}
}
lock_.unlock();
return result;
}
// Returns true if after this operation, count_ is 0.
bool decrement() {
bool result;
lock_.lock();
{
// It's alright for count_ to go negative because it will only be exactly
// 0 once (since increment() makes sure that once you go negative, you
// can't come back from the dead).
count_--;
result = (count_ == 0);
}
lock_.unlock();
return result;
}
private:
static const std::int32_t kInitialCount;
DefaultLock lock_;
std::int32_t count_;
};
} // namespace ptr_impl
template <typename T>
class ObjectDestroyer {
public:
static void destroy(T* t) { delete t; }
};
template <typename T>
class ArrayDestroyer {
public:
static void destroy(T* t) { delete[] t; }
};
// Forward declarations to make it possible for Ptr (a class template) to
// declare ConstifyPtr, DowncastPtr, and DowncastConstPtr (function templates)
// as friends.
//
// Note that the default template parameters to Ptr need to be defined here (at
// the first point of declaration), as opposed to at the actual definition of
// Ptr (which is what one might reasonably expect).
//
// See https://isocpp.org/wiki/faq/templates#template-friends for more.
template <typename T, template <typename> class Destroyer = ObjectDestroyer>
class Ptr;
template <typename T>
class ConstPtr;
template <typename T>
ConstPtr<T> ConstifyPtr(Ptr<T> ptr);
template <typename ChildT, typename BaseT>
Ptr<ChildT> DowncastPtr(Ptr<BaseT> base_ptr);
template <typename ChildT, typename BaseT>
ConstPtr<ChildT> DowncastConstPtr(ConstPtr<BaseT> base_ptr);
// A layer of indirection over a raw pointer, to buy flexibility in the
// future to use, for instance:
//
// a) the in-built shared_ptr in modern implementations of C++,
// b) a custom reference-counting mechanism, etc.
//
// , all without having to touch every line of our codebase that uses
// pointers.
//
// Destroyer defines how the owned pointee should be destroyed, and is
// expected to be a class template that provides at least a destroy()
// method, like so:
//
// template <typename T>
// class MyDestroyer {
// public:
// static void destroy(T* t);
// };
//
// It defaults to ObjectDestroyer<T>.
template <typename T, template <typename> class Destroyer>
class Ptr {
public:
// Provide an alias for use as a dependent name.
typedef T PointeeType;
Ptr() : pointee_(nullptr), ref_count_(nullptr) {}
explicit Ptr(T* pointee, bool is_ref_counted = false,
ptr_impl::RefCount* ref_count = nullptr)
: pointee_(pointee),
ref_count_(
is_ref_counted
? (ref_count != nullptr ? ref_count : new ptr_impl::RefCount())
: nullptr) {
init();
}
Ptr(const Ptr& that) : pointee_(that.pointee_), ref_count_(that.ref_count_) {
init();
}
Ptr& operator=(const Ptr& other) {
if (pointee_ != other.pointee_) {
// If we're not currently ref-counted, then an assignment shouldn't lead
// to any destruction of our past state -- that's the responsibility of
// whichever instance of Ptr believes it owns pointee_.
destroy(false);
pointee_ = other.pointee_;
ref_count_ = other.ref_count_;
init();
}
return *this;
}
// Conversion to Ptr<T2>, where T is trivially convertible to T2. E.g.
// conversion from derived to base class.
template <typename T2>
operator Ptr<T2>() {
return Ptr<T2>(pointee_, isRefCounted(), ref_count_);
}
~Ptr() {
if (isRefCounted()) {
destroy();
} else {
// Left empty on purpose.
}
}
bool operator==(const Ptr& other) const {
assert(!(this->isNull()));
assert(!(other.isNull()));
return ((*(this->pointee_) == *(other.pointee_)) &&
(this->isRefCounted() == other.isRefCounted()));
}
bool operator!=(const Ptr& other) const { return !(*this == other); }
bool operator<(const Ptr& other) const {
assert(!(this->isNull()));
assert(!(other.isNull()));
return *(this->pointee_) < *(other.pointee_);
}
// Calls Destroyer::destroy() to perform deallocation of pointee_.
void destroy(bool should_destroy_if_not_ref_counted = true) {
bool need_to_destroy = isRefCounted() ? ref_count_->decrement()
: should_destroy_if_not_ref_counted;
if (need_to_destroy) {
delete ref_count_;
Destroyer<T>::destroy(pointee_);
}
ref_count_ = NULL; // NOLINT
pointee_ = NULL; // NOLINT
}
// Use this function only when the ownership is held by someone else, and this
// Ptr object has no responsibility to destroy it.
void clear() {
if (isRefCounted()) {
NEARBY_LOG(FATAL, "Attempting to invoke clear() on a RefCounted Ptr.");
}
pointee_ = NULL; // NOLINT
}
T& operator*() const {
assert(pointee_ != NULL); // NOLINT
return *pointee_;
}
T* operator->() const {
assert(pointee_ != NULL); // NOLINT
return pointee_;
}
bool isNull() const { return pointee_ == nullptr; }
bool isRefCounted() const { return ref_count_ != nullptr; }
private:
template <typename PointeeT>
friend ConstPtr<PointeeT> ConstifyPtr(Ptr<PointeeT> ptr);
template <typename ChildT, typename BaseT>
friend Ptr<ChildT> DowncastPtr(Ptr<BaseT> base_ptr);
template <typename ChildT, typename BaseT>
friend ConstPtr<ChildT> DowncastConstPtr(ConstPtr<BaseT> base_ptr);
void init() {
if (isRefCounted()) {
if (!ref_count_->increment()) {
NEARBY_LOG(FATAL, "Failed to increment RefCount.");
}
}
}
T* pointee_;
ptr_impl::RefCount* ref_count_;
};
// Convenience wrapper for a read-only version of Ptr (in which the pointee
// cannot be modified).
//
// The C++11 equivalent would be:
//
// using ConstPtr = Ptr<T const>;
//
// Thus,
//
// Ptr<X> x1(new X(...));
//
// allows the underlying X instance to be modified, whereas
//
// ConstPtr<X> x2(new X(...));
//
// disallows that.
template <typename T>
class ConstPtr : public Ptr<T const> {
public:
ConstPtr() {}
explicit ConstPtr(T* pointee, bool is_ref_counted = false,
ptr_impl::RefCount* ref_count = nullptr)
: Ptr<T const>(pointee, is_ref_counted, ref_count) {}
};
// RAII wrapper over Ptr and ConstPtr (hereon referred to by the PtrType
// placeholder), to allow for guarantees that the wrapped PtrType will be
// automatically destroyed when this wrapper object goes out of scope.
//
// Any class that has a PtrType member that it owns (and thus needs to invoke
// destroy() on) should wrap that PtrType in a ScopedPtr object.
//
// Similarly, any method that manipulates a (likely local) PtrType variable
// that needs to be destroy()ed at the end of that method should wrap that
// PtrType variable in a ScopedPtr object.
//
// Sample usage:
//
// Ptr<X> x1(new X(...));
// ScopedPtr<Ptr<X> > sx1(x1);
//
// ConstPtr<X> x2(new X(...));
// ScopedPtr<ConstPtr<X> > sx2(x2);
//
// ScopedPtr<Ptr<X> > sx3(new X(...));
//
// ScopedPtr<ConstPtr<X> > sx4(new X(...));
template <typename PtrType>
class ScopedPtr {
public:
explicit ScopedPtr(typename PtrType::PointeeType* pointee) : ptr_(pointee) {}
explicit ScopedPtr(PtrType ptr) : ptr_(ptr) {}
~ScopedPtr() { ptr_.destroy(); }
// Shadow methods for the underlying Ptr.
typename PtrType::PointeeType& operator*() const { return ptr_.operator*(); }
typename PtrType::PointeeType* operator->() const {
return ptr_.operator->();
}
bool isNull() const { return ptr_.isNull(); }
// Accessor for the underlying Ptr.
PtrType get() const { return ptr_; }
// Releases the underlying Ptr from the clutches of this ScopedPtr,
// effectively resetting this ScopedPtr (and making its destructor be a no-op)
// -- useful for transfer of ownership from one ScopedPtr to another across
// scopes.
PtrType release() {
PtrType released = ptr_;
ptr_ = PtrType();
return released;
}
private:
// Disallow copy and assignment.
ScopedPtr(const ScopedPtr&);
ScopedPtr& operator=(const ScopedPtr&);
PtrType ptr_;
};
// Utility function to create Ptr objects with less template-y noise by
// leveraging template argument deduction, in the same vein as std::make_pair().
//
// Helps convert
//
// Ptr<MyRichType<MyTemplateParam> >(new MyRichType<MyTemplateParam>());
//
// to
//
// MakePtr(new MyRichType<MyTemplateParam>());
template <typename T>
Ptr<T> MakePtr(T* raw_ptr) {
return Ptr<T>(raw_ptr);
}
// Like MakePtr(), utility function to create ConstPtr objects with less
// template-y noise.
template <typename T>
ConstPtr<T> MakeConstPtr(T* raw_ptr) {
return ConstPtr<T>(raw_ptr);
}
// Used to create Ptr instances that are reference-counted (for when the
// lifetime and/or ownership of the pointee is not deterministic, like when a
// cache gives out handles to its cached objects to multiple threads to manage
// independently).
//
// Needless to say, the reference-counted-ness of these Ptr instances propagates
// across all copies and assignments, and as one might expect, the underlying
// pointee is deallocated when the reference count goes to 0.
//
// That implies that it's not strictly necessary to wrap these in ScopedPtrs
// (but it's perfectly fine to do so, and is even recommended, so readers of
// your code get a better understanding of the ownership story for each
// reference).
template <typename T>
Ptr<T> MakeRefCountedPtr(T* raw_ptr) {
return Ptr<T>(raw_ptr, true);
}
// ConstPtr counterpart to MakeRefCountedPtr().
template <typename T>
ConstPtr<T> MakeRefCountedConstPtr(T* raw_ptr) {
return ConstPtr<T>(raw_ptr, true);
}
// Use this function to convert a Ptr object to a ConstPtr object.
template <typename T>
ConstPtr<T> ConstifyPtr(Ptr<T> ptr) {
return ConstPtr<T>(ptr.pointee_, ptr.isRefCounted(), ptr.ref_count_);
}
// Use this function to downcast from a Ptr<BaseT> to a Ptr<ChildT>.
//
// Because BaseT can be automatically deduced based on the base_ptr that's
// passed in, invocations of this method only need to explicitly specify ChildT,
// like so:
//
// Ptr<MyChild> my_child_ptr = DowncastPtr<MyChild>(my_base_ptr);
template <typename ChildT, typename BaseT>
Ptr<ChildT> DowncastPtr(Ptr<BaseT> base_ptr) {
return Ptr<ChildT>(DOWN_CAST<ChildT*>(base_ptr.pointee_),
base_ptr.isRefCounted(), base_ptr.ref_count_);
}
// ConstPtr counterpart to DowncastPtr().
template <typename ChildT, typename BaseT>
ConstPtr<ChildT> DowncastConstPtr(ConstPtr<BaseT> base_ptr) {
return ConstPtr<ChildT>(
const_cast<ChildT*>(DOWN_CAST<const ChildT*>(base_ptr.pointee_)),
base_ptr.isRefCounted(), base_ptr.ref_count_);
}
} // namespace nearby
} // namespace location
#endif // PLATFORM_PTR_H_
+272
View File
@@ -0,0 +1,272 @@
#include "platform/ptr.h"
#include "gtest/gtest.h"
namespace location {
namespace nearby {
TEST(PtrTest, RefCountedPtr_SingleReference) {
Ptr<int> ref_counted = MakeRefCountedPtr(new int(1234));
// We just want to make sure that this test doesn't lead to a leak.
SUCCEED();
}
TEST(PtrTest, RefCountedPtr_MultipleReferences) {
Ptr<int> ref_counted_1 = MakeRefCountedPtr(new int(1234));
Ptr<int> ref_counted_2 = ref_counted_1;
Ptr<int> ref_counted_3(ref_counted_2);
// We just want to make sure that this test doesn't lead to a leak, nor to
// double-deletion.
SUCCEED();
}
TEST(PtrTest, RefCountedPtr_IsRefCounted_Works) {
Ptr<int> ref_counted = MakeRefCountedPtr(new int(1234));
Ptr<int> manually_counted = MakePtr(new int(1234));
ScopedPtr<Ptr<int> > scoped_manually_counted(manually_counted);
ASSERT_TRUE(ref_counted.isRefCounted());
ASSERT_FALSE(manually_counted.isRefCounted());
}
TEST(PtrTest, RefCountedPtr_MultipleReferencesWithScoped) {
Ptr<int> ref_counted = MakeRefCountedPtr(new int(1234));
ScopedPtr<Ptr<int> > scoped_ref_counted_1(ref_counted);
ScopedPtr<Ptr<int> > scoped_ref_counted_2(ref_counted);
// We just want to make sure that this test doesn't lead to a leak, nor to
// double-deletion.
SUCCEED();
}
TEST(PtrTest, AssignmentOperator_RefCountedToRefCounted) {
Ptr<int> ref_counted_1 = MakeRefCountedPtr(new int(1234));
Ptr<int> ref_counted_2 = MakeRefCountedPtr(new int(5678));
ref_counted_2 = ref_counted_1;
ASSERT_EQ(1234, *ref_counted_1);
ASSERT_EQ(1234, *ref_counted_2);
}
TEST(PtrTest, AssignmentOperator_ManuallyCountedToManuallyCounted) {
Ptr<int> manually_counted_1 = MakePtr(new int(1234));
Ptr<int> manually_counted_2 = MakePtr(new int(5678));
// Avoid leaks.
ScopedPtr<Ptr<int> > scoped_manually_counted_1(manually_counted_1);
ScopedPtr<Ptr<int> > scoped_manually_counted_2(manually_counted_2);
manually_counted_2 = manually_counted_1;
ASSERT_EQ(1234, *manually_counted_1);
ASSERT_EQ(1234, *manually_counted_2);
ASSERT_EQ(1234, *scoped_manually_counted_1);
ASSERT_EQ(5678, *scoped_manually_counted_2);
}
TEST(PtrTest, AssignmentOperator_RefCountedToManuallyCounted) {
Ptr<int> ref_counted = MakeRefCountedPtr(new int(1234));
Ptr<int> manually_counted = MakePtr(new int(5678));
// Avoid leaks.
ScopedPtr<Ptr<int> > scoped_manually_counted(manually_counted);
manually_counted = ref_counted;
ASSERT_EQ(1234, *ref_counted);
ASSERT_EQ(1234, *manually_counted);
ASSERT_EQ(5678, *scoped_manually_counted);
}
TEST(PtrTest, AssignmentOperator_ManuallyCountedToRefCounted) {
Ptr<int> manually_counted = MakePtr(new int(1234));
Ptr<int> ref_counted = MakeRefCountedPtr(new int(5678));
// Avoid leaks.
ScopedPtr<Ptr<int> > scoped_manually_counted(manually_counted);
ref_counted = manually_counted;
ASSERT_EQ(1234, *ref_counted);
ASSERT_EQ(1234, *manually_counted);
ASSERT_EQ(1234, *scoped_manually_counted);
}
TEST(PtrTest, AssignmentOperator_SelfAssignment_ManuallyCounted) {
Ptr<int> manually_counted_1 = MakePtr(new int(1234));
Ptr<int> manually_counted_2(manually_counted_1);
// Avoid leaks.
ScopedPtr<Ptr<int> > scoped_manually_counted_1(manually_counted_1);
manually_counted_1 = manually_counted_2;
ASSERT_EQ(1234, *manually_counted_1);
ASSERT_EQ(1234, *manually_counted_2);
ASSERT_EQ(1234, *scoped_manually_counted_1);
}
TEST(PtrTest, AssignmentOperator_SelfAssignment_RefCounted) {
Ptr<int> ref_counted_1 = MakeRefCountedPtr(new int(1234));
Ptr<int> ref_counted_2(ref_counted_1);
ref_counted_1 = ref_counted_2;
ASSERT_EQ(1234, *ref_counted_1);
ASSERT_EQ(1234, *ref_counted_2);
}
TEST(PtrTest, EqualityOperator_ManuallyCounted) {
Ptr<int> manually_counted_1 = MakePtr(new int(1234));
Ptr<int> manually_counted_2(manually_counted_1);
// Avoid leaks.
ScopedPtr<Ptr<int> > scoped_manually_counted_1(manually_counted_1);
ASSERT_TRUE(manually_counted_1 == manually_counted_2);
manually_counted_1 = manually_counted_2;
ASSERT_TRUE(manually_counted_1 == manually_counted_2);
}
TEST(PtrTest, EqualityOperator_RefCounted) {
Ptr<int> ref_counted_1 = MakeRefCountedPtr(new int(1234));
Ptr<int> ref_counted_2(ref_counted_1);
ASSERT_TRUE(ref_counted_1 == ref_counted_2);
ref_counted_1 = ref_counted_2;
ASSERT_TRUE(ref_counted_1 == ref_counted_2);
}
TEST(PtrTest, EqualityOperator_ManuallyAndRefCounted) {
int* raw = new int(1234);
Ptr<int> manually_counted = MakePtr(raw);
Ptr<int> ref_counted = MakeRefCountedPtr(raw);
// No need for a ScopedPtr for manually_counted here because we know that
// ref_counted will take care of deallocating 'raw'.
ASSERT_FALSE(manually_counted == ref_counted);
}
namespace {
class Base {
public:
virtual ~Base() {}
virtual int getInt() const = 0;
};
class Derived : public Base {
public:
explicit Derived(int i) : i_(i) {}
~Derived() override {}
int getInt() const override { return i_; }
private:
const int i_;
};
} // namespace
TEST(PtrTest, DerivedToBaseConversion_ManuallyCounted) {
Ptr<Derived> derived = MakePtr(new Derived(1234));
Ptr<Base> base = derived;
// Avoid leaks.
ScopedPtr<Ptr<Derived> > scoped_derived(derived);
ASSERT_EQ(1234, base->getInt());
ASSERT_EQ(1234, derived->getInt());
ASSERT_EQ(1234, scoped_derived->getInt());
}
TEST(PtrTest, DerivedToBaseConversion_RefCounted) {
Ptr<Derived> derived = MakeRefCountedPtr(new Derived(1234));
Ptr<Base> base = derived;
ASSERT_EQ(1234, derived->getInt());
derived.destroy();
// Additionally, make sure that 'base' is valid even after 'derived' has been
// destroyed.
ASSERT_EQ(1234, base->getInt());
}
TEST(PtrTest, ScopedPtr_Release_ManuallyCounted) {
Ptr<int> manually_counted_1 = MakePtr(new int(1234));
ScopedPtr<Ptr<int> > scoped_manually_counted_1(manually_counted_1);
Ptr<int> manually_counted_2 = scoped_manually_counted_1.release();
// Avoid leaks.
ScopedPtr<Ptr<int> > scoped_manually_counted_2(manually_counted_2);
ASSERT_TRUE(scoped_manually_counted_1.isNull());
ASSERT_EQ(1234, *manually_counted_2);
ASSERT_EQ(1234, *scoped_manually_counted_2);
}
TEST(PtrTest, ScopedPtr_Release_RefCounted) {
Ptr<int> ref_counted_1 = MakeRefCountedPtr(new int(1234));
ScopedPtr<Ptr<int> > scoped_ref_counted_1(ref_counted_1);
Ptr<int> ref_counted_2 = scoped_ref_counted_1.release();
ASSERT_TRUE(scoped_ref_counted_1.isNull());
ASSERT_EQ(1234, *ref_counted_2);
}
TEST(PtrTest, ConstifyPtr_ManuallyCounted) {
Ptr<int> manually_counted = MakePtr(new int(1234));
// Avoid leaks.
ScopedPtr<Ptr<int> > scoped_manually_counted(manually_counted);
ConstPtr<int> const_manually_counted = ConstifyPtr(manually_counted);
ASSERT_EQ(1234, *const_manually_counted);
ASSERT_EQ(1234, *manually_counted);
ASSERT_EQ(1234, *scoped_manually_counted);
}
TEST(PtrTest, ConstifyPtr_RefCounted) {
Ptr<int> ref_counted = MakeRefCountedPtr(new int(1234));
ConstPtr<int> const_ref_counted = ConstifyPtr(ref_counted);
ASSERT_EQ(1234, *ref_counted);
ref_counted.destroy();
// Additionally, make sure that const_ref_counted is valid even after
// ref_counted has been destroyed.
ASSERT_EQ(1234, *const_ref_counted);
}
TEST(PtrTest, DowncastPtr_ManuallyCounted) {
Ptr<Derived> derived = MakePtr(new Derived(1234));
Ptr<Base> base = derived;
// Avoid leaks.
ScopedPtr<Ptr<Derived> > scoped_derived(derived);
Ptr<Derived> derived_from_downcast = DowncastPtr<Derived>(base);
ASSERT_EQ(1234, base->getInt());
ASSERT_EQ(1234, derived->getInt());
ASSERT_EQ(1234, derived_from_downcast->getInt());
}
TEST(PtrTest, DowncastPtr_RefCounted) {
Ptr<Derived> derived = MakeRefCountedPtr(new Derived(1234));
Ptr<Base> base = derived;
Ptr<Derived> derived_from_downcast = DowncastPtr<Derived>(base);
ASSERT_EQ(1234, base->getInt());
base.destroy();
ASSERT_EQ(1234, derived->getInt());
derived.destroy();
// Additionally, make sure that derived_from_downcast is valid even after
// derived has been destroyed.
ASSERT_EQ(1234, derived_from_downcast->getInt());
}
} // namespace nearby
} // namespace location
+42
View File
@@ -0,0 +1,42 @@
#include "platform/reliability_utils.h"
namespace location {
namespace nearby {
bool ReliabilityUtils::attemptRepeatedly(Ptr<Runnable> runnable,
const std::string &runnable_name,
Ptr<Runnable> recovery_runnable) {
return false;
}
bool ReliabilityUtils::attemptRepeatedly(Ptr<Runnable> runnable,
const std::string &runnable_name,
Ptr<Runnable> recovery_runnable,
const AtomicBoolean &isCancelled) {
return false;
}
bool ReliabilityUtils::attemptRepeatedly(Ptr<Runnable> runnable,
const std::string &runnable_name,
std::int64_t recovery_pause_millis) {
return false;
}
bool ReliabilityUtils::attemptRepeatedly(Ptr<Runnable> runnable,
const std::string &runnable_name,
std::int64_t recovery_pause_millis,
const AtomicBoolean &isCancelled) {
return false;
}
bool ReliabilityUtils::attemptRepeatedly(Ptr<Runnable> runnable,
const std::string &runnable_name,
int num_attempts,
std::int64_t recovery_pause_millis,
Ptr<Runnable> recovery_runnable,
const AtomicBoolean &isCancelled) {
return false;
}
} // namespace nearby
} // namespace location
+43
View File
@@ -0,0 +1,43 @@
#ifndef PLATFORM_RELIABILITY_UTILS_H_
#define PLATFORM_RELIABILITY_UTILS_H_
#include <cstdint>
#include "platform/api/atomic_boolean.h"
#include "platform/port/string.h"
#include "platform/ptr.h"
#include "platform/runnable.h"
namespace location {
namespace nearby {
class ReliabilityUtils {
public:
static bool attemptRepeatedly(Ptr<Runnable> runnable,
const std::string& runnable_name,
Ptr<Runnable> recovery_runnable);
static bool attemptRepeatedly(Ptr<Runnable> runnable,
const std::string& runnable_name,
Ptr<Runnable> recovery_runnable,
const AtomicBoolean& isCancelled);
static bool attemptRepeatedly(Ptr<Runnable> runnable,
const std::string& runnable_name,
std::int64_t recovery_pause_millis);
static bool attemptRepeatedly(Ptr<Runnable> runnable,
const std::string& runnable_name,
std::int64_t recovery_pause_millis,
const AtomicBoolean& isCancelled);
private:
static bool attemptRepeatedly(Ptr<Runnable> runnable,
const std::string& runnable_name,
int num_attempts,
std::int64_t recovery_pause_millis,
Ptr<Runnable> recovery_runnable,
const AtomicBoolean& isCancelled);
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_RELIABILITY_UTILS_H_
+22
View File
@@ -0,0 +1,22 @@
#ifndef PLATFORM_RUNNABLE_H_
#define PLATFORM_RUNNABLE_H_
namespace location {
namespace nearby {
// The Runnable interface should be implemented by any class whose instances are
// intended to be executed by a thread. The class must define a method named
// run() with no arguments.
//
// https://docs.oracle.com/javase/8/docs/api/java/lang/Runnable.html
class Runnable {
public:
virtual ~Runnable() {}
virtual void run() = 0;
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_RUNNABLE_H_
+26
View File
@@ -0,0 +1,26 @@
#ifndef PLATFORM_SYNCHRONIZED_H_
#define PLATFORM_SYNCHRONIZED_H_
#include "platform/api/lock.h"
#include "platform/ptr.h"
namespace location {
namespace nearby {
// An RAII mechanism to acquire a Lock over a block of code.
//
// https://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html
// https://docs.oracle.com/javase/tutorial/essential/concurrency/locksync.html
class Synchronized {
public:
explicit Synchronized(Ptr<Lock> lock) : lock_(lock) { lock_->lock(); }
~Synchronized() { lock_->unlock(); }
private:
Ptr<Lock> lock_;
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_SYNCHRONIZED_H_