/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef CHRE_HOST_SOCKET_CLIENT_H_
#define CHRE_HOST_SOCKET_CLIENT_H_
#include <atomic>
#include <condition_variable>
#include <mutex>
#include <thread>
#include <cutils/sockets.h>
#include <utils/RefBase.h>
#include <utils/StrongPointer.h>
namespace android {
namespace chre {
class SocketClient {
public:
SocketClient();
~SocketClient();
/**
* Represents the callback interface used for handling events that occur on
* the receive thread. Note that it is *not* safe to call connect(),
* connectInBackground(), or disconnect() from the context of these callbacks.
*/
class ICallbacks : public VirtualLightRefBase {
public:
/**
* Invoked from within the context of the read thread when a message is
* received on the socket.
*
* @param data Buffer containing received message data
* @param length Size of the message in bytes
*/
virtual void onMessageReceived(const void *data, size_t length) = 0;
/**
* Called when the socket is successfully (re-)connected.
*/
virtual void onConnected() {};
/**
* Called when we have failed to (re-)connect the socket after many attempts
* and are giving up.
*/
virtual void onConnectionAborted() {};
/**
* Invoked when the socket is disconnected, and this connection loss was not
* the result of an explicit call to disconnect(), i.e. the connection was
* terminated on the remote end.
*/
virtual void onDisconnected() {};
};
/**
* Synchronously attempts to connect to the Android reserved namespace socket
* with the given name. If this connection attempt is successful, starts a
* receive thread to handle messages received on the socket, and uses this
* thread to automatically reconnect if disconnected by the remote end.
*
* @param socketName Name of the Android domain socket to connect to
* @param callbacks
*
* @return true if the connection was successful
*/
bool connect(const char *socketName,
const ::android::sp<ICallbacks>& callbacks);
/**
* Starts up the receive thread and attempts to connect to the socket in the
* background. The onConnected() callback will be invoked when the socket is
* connected successfully, or onConnectionAborted() will be invoked if the
* connection could not be made after many retries and the client is giving
* up.
*
* @param socketName Name of the Android domain socket to connect to
* @param callbacks
*
* @return true if the receive thread was started and will attempt to connect
* the socket asynchronously
*/
bool connectInBackground(const char *socketName,
const ::android::sp<ICallbacks>& callbacks);
/**
* Performs graceful teardown of the socket. After this function returns, this
* object will no longer invoke any callbacks or hold a reference to the
* callbacks object provided to connect().
*/
void disconnect();
/**
* @return true if the socket is currently connected
*/
bool isConnected() const;
/**
* Send a message on the connected socket. Safe to call from any thread.
*
* @param data Buffer containing message data
* @param length Size of the message to send in bytes
*
* @return true if the message was successfully sent
*/
bool sendMessage(const void *data, size_t length);
private:
static constexpr size_t kMaxSocketNameLen = 64;
char mSocketName[kMaxSocketNameLen];
sp<ICallbacks> mCallbacks;
std::atomic<int> mSockFd;
std::thread mRxThread;
//! Set to true when we initiate the graceful socket shutdown procedure, so we
//! know not to invoke onSocketDisconnectedByRemote()
std::atomic<bool> mGracefulShutdown;
//! Condition variable used as the method to wake the RX thread when we want
//! to disconnect, but it's trying to reconnect automatically
std::condition_variable mShutdownCond;
std::mutex mShutdownMutex;
bool doConnect(const char *socketName,
const ::android::sp<ICallbacks>& callbacks,
bool connectInBackground);
bool inReceiveThread() const;
void receiveThread();
bool receiveThreadRunning() const;
bool reconnect();
void startReceiveThread();
bool tryConnect(bool suppressErrorLogs = false);
};
} // namespace chre
} // namespace android
#endif // CHRE_HOST_SOCKET_CLIENT_H_