1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef GOOGLE_APIS_GCM_ENGINE_MCS_CLIENT_H_
6#define GOOGLE_APIS_GCM_ENGINE_MCS_CLIENT_H_
7
8#include <deque>
9#include <map>
10#include <string>
11#include <vector>
12
13#include "base/files/file_path.h"
14#include "base/memory/linked_ptr.h"
15#include "base/memory/weak_ptr.h"
16#include "google_apis/gcm/base/gcm_export.h"
17#include "google_apis/gcm/base/mcs_message.h"
18#include "google_apis/gcm/engine/connection_handler.h"
19#include "google_apis/gcm/engine/gcm_store.h"
20#include "google_apis/gcm/engine/heartbeat_manager.h"
21
22namespace base {
23class Clock;
24}  // namespace base
25
26namespace google {
27namespace protobuf {
28class MessageLite;
29}  // namespace protobuf
30}  // namespace google
31
32namespace mcs_proto {
33class LoginRequest;
34}
35
36namespace gcm {
37
38class CollapseKey;
39class ConnectionFactory;
40class GCMStatsRecorder;
41struct ReliablePacketInfo;
42
43// An MCS client. This client is in charge of all communications with an
44// MCS endpoint, and is capable of reliably sending/receiving GCM messages.
45// NOTE: Not thread safe. This class should live on the same thread as that
46// network requests are performed on.
47class GCM_EXPORT MCSClient {
48 public:
49  // Any change made to this enum should have corresponding change in the
50  // GetStateString(...) function.
51  enum State {
52    UNINITIALIZED,  // Uninitialized.
53    LOADED,         // GCM Load finished, waiting to connect.
54    CONNECTING,     // Connection in progress.
55    CONNECTED,      // Connected and running.
56  };
57
58  // Any change made to this enum should have corresponding change in the
59  // GetMessageSendStatusString(...) function in mcs_client.cc.
60  enum MessageSendStatus {
61    // Message was queued succcessfully.
62    QUEUED,
63    // Message was sent to the server and the ACK was received.
64    SENT,
65    // Message not saved, because total queue size limit reached.
66    QUEUE_SIZE_LIMIT_REACHED,
67    // Message not saved, because app queue size limit reached.
68    APP_QUEUE_SIZE_LIMIT_REACHED,
69    // Message too large to send.
70    MESSAGE_TOO_LARGE,
71    // Message not send becuase of TTL = 0 and no working connection.
72    NO_CONNECTION_ON_ZERO_TTL,
73    // Message exceeded TTL.
74    TTL_EXCEEDED,
75
76    // NOTE: always keep this entry at the end. Add new status types only
77    // immediately above this line. Make sure to update the corresponding
78    // histogram enum accordingly.
79    SEND_STATUS_COUNT
80  };
81
82  // Callback for MCSClient's error conditions.
83  // TODO(fgorski): Keeping it as a callback with intention to add meaningful
84  // error information.
85  typedef base::Callback<void()> ErrorCallback;
86  // Callback when a message is received.
87  typedef base::Callback<void(const MCSMessage& message)>
88      OnMessageReceivedCallback;
89  // Callback when a message is sent (and receipt has been acknowledged by
90  // the MCS endpoint).
91  typedef base::Callback<
92      void(int64 user_serial_number,
93           const std::string& app_id,
94           const std::string& message_id,
95           MessageSendStatus status)> OnMessageSentCallback;
96
97  MCSClient(const std::string& version_string,
98            base::Clock* clock,
99            ConnectionFactory* connection_factory,
100            GCMStore* gcm_store,
101            GCMStatsRecorder* recorder);
102  virtual ~MCSClient();
103
104  // Initialize the client. Will load any previous id/token information as well
105  // as unacknowledged message information from the GCM storage, if it exists,
106  // passing the id/token information back via |initialization_callback| along
107  // with a |success == true| result. If no GCM information is present (and
108  // this is therefore a fresh client), a clean GCM store will be created and
109  // values of 0 will be returned via |initialization_callback| with
110  // |success == true|.
111  /// If an error loading the GCM store is encountered,
112  // |initialization_callback| will be invoked with |success == false|.
113  void Initialize(const ErrorCallback& initialization_callback,
114                  const OnMessageReceivedCallback& message_received_callback,
115                  const OnMessageSentCallback& message_sent_callback,
116                  scoped_ptr<GCMStore::LoadResult> load_result);
117
118  // Logs the client into the server. Client must be initialized.
119  // |android_id| and |security_token| are optional if this is not a new
120  // client, else they must be non-zero.
121  // Successful login will result in |message_received_callback| being invoked
122  // with a valid LoginResponse.
123  // Login failure (typically invalid id/token) will shut down the client, and
124  // |initialization_callback| to be invoked with |success = false|.
125  virtual void Login(uint64 android_id, uint64 security_token);
126
127  // Sends a message, with or without reliable message queueing (RMQ) support.
128  // Will asynchronously invoke the OnMessageSent callback regardless.
129  // Whether to use RMQ depends on whether the protobuf has |ttl| set or not.
130  // |ttl == 0| denotes the message should only be sent if the connection is
131  // open. |ttl > 0| will keep the message saved for |ttl| seconds, after which
132  // it will be dropped if it was unable to be sent. When a message is dropped,
133  // |message_sent_callback_| is invoked with a TTL expiration error.
134  virtual void SendMessage(const MCSMessage& message);
135
136  // Returns the current state of the client.
137  State state() const { return state_; }
138
139  // Returns the size of the send message queue.
140  int GetSendQueueSize() const;
141
142  // Returns the size of the resend messaage queue.
143  int GetResendQueueSize() const;
144
145  // Returns text representation of the state enum.
146  std::string GetStateString() const;
147
148 private:
149  typedef uint32 StreamId;
150  typedef std::string PersistentId;
151  typedef std::vector<StreamId> StreamIdList;
152  typedef std::vector<PersistentId> PersistentIdList;
153  typedef std::map<StreamId, PersistentId> StreamIdToPersistentIdMap;
154  typedef linked_ptr<ReliablePacketInfo> MCSPacketInternal;
155
156  // Resets the internal state and builds a new login request, acknowledging
157  // any pending server-to-device messages and rebuilding the send queue
158  // from all unacknowledged device-to-server messages.
159  // Should only be called when the connection has been reset.
160  void ResetStateAndBuildLoginRequest(mcs_proto::LoginRequest* request);
161
162  // Send a heartbeat to the MCS server.
163  void SendHeartbeat();
164
165  // GCM Store callback.
166  void OnGCMUpdateFinished(bool success);
167
168  // Attempt to send a message.
169  void MaybeSendMessage();
170
171  // Helper for sending a protobuf along with any unacknowledged ids to the
172  // wire.
173  void SendPacketToWire(ReliablePacketInfo* packet_info);
174
175  // Handle a data message sent to the MCS client system from the MCS server.
176  void HandleMCSDataMesssage(
177      scoped_ptr<google::protobuf::MessageLite> protobuf);
178
179  // Handle a packet received over the wire.
180  void HandlePacketFromWire(scoped_ptr<google::protobuf::MessageLite> protobuf);
181
182  // ReliableMessageQueue acknowledgment helpers.
183  // Handle a StreamAck sent by the server confirming receipt of all
184  // messages up to the message with stream id |last_stream_id_received|.
185  void HandleStreamAck(StreamId last_stream_id_received_);
186  // Handle a SelectiveAck sent by the server confirming all messages
187  // in |id_list|.
188  void HandleSelectiveAck(const PersistentIdList& id_list);
189  // Handle server confirmation of a device message, including device's
190  // acknowledgment of receipt of messages.
191  void HandleServerConfirmedReceipt(StreamId device_stream_id);
192
193  // Generates a new persistent id for messages.
194  // Virtual for testing.
195  virtual PersistentId GetNextPersistentId();
196
197  // Helper for the heartbeat manager to signal a connection reset.
198  void OnConnectionResetByHeartbeat();
199
200  // Runs the message_sent_callback_ with send |status| of the |protobuf|.
201  void NotifyMessageSendStatus(const google::protobuf::MessageLite& protobuf,
202                               MessageSendStatus status);
203
204  // Pops the next message from the front of the send queue (cleaning up
205  // any associated state).
206  MCSPacketInternal PopMessageForSend();
207
208  // Local version string. Sent on login.
209  const std::string version_string_;
210
211  // Clock for enforcing TTL. Passed in for testing.
212  base::Clock* const clock_;
213
214  // Client state.
215  State state_;
216
217  // Callbacks for owner.
218  ErrorCallback mcs_error_callback_;
219  OnMessageReceivedCallback message_received_callback_;
220  OnMessageSentCallback message_sent_callback_;
221
222  // The android id and security token in use by this device.
223  uint64 android_id_;
224  uint64 security_token_;
225
226  // Factory for creating new connections and connection handlers.
227  ConnectionFactory* connection_factory_;
228
229  // Connection handler to handle all over-the-wire protocol communication
230  // with the mobile connection server.
231  ConnectionHandler* connection_handler_;
232
233  // -----  Reliablie Message Queue section -----
234  // Note: all queues/maps are ordered from oldest (front/begin) message to
235  // most recent (back/end).
236
237  // Send/acknowledge queues.
238  std::deque<MCSPacketInternal> to_send_;
239  std::deque<MCSPacketInternal> to_resend_;
240
241  // Map of collapse keys to their pending messages.
242  std::map<CollapseKey, ReliablePacketInfo*> collapse_key_map_;
243
244  // Last device_to_server stream id acknowledged by the server.
245  StreamId last_device_to_server_stream_id_received_;
246  // Last server_to_device stream id acknowledged by this device.
247  StreamId last_server_to_device_stream_id_received_;
248  // The stream id for the last sent message. A new message should consume
249  // stream_id_out_ + 1.
250  StreamId stream_id_out_;
251  // The stream id of the last received message. The LoginResponse will always
252  // have a stream id of 1, and stream ids increment by 1 for each received
253  // message.
254  StreamId stream_id_in_;
255
256  // The server messages that have not been acked by the device yet. Keyed by
257  // server stream id.
258  StreamIdToPersistentIdMap unacked_server_ids_;
259
260  // Those server messages that have been acked. They must remain tracked
261  // until the ack message is itself confirmed. The list of all message ids
262  // acknowledged are keyed off the device stream id of the message that
263  // acknowledged them.
264  std::map<StreamId, PersistentIdList> acked_server_ids_;
265
266  // Those server messages from a previous connection that were not fully
267  // acknowledged. They do not have associated stream ids, and will be
268  // acknowledged on the next login attempt.
269  PersistentIdList restored_unackeds_server_ids_;
270
271  // The GCM persistent store. Not owned.
272  GCMStore* gcm_store_;
273
274  // Manager to handle triggering/detecting heartbeats.
275  HeartbeatManager heartbeat_manager_;
276
277  // Recorder that records GCM activities for debugging purpose. Not owned.
278  GCMStatsRecorder* recorder_;
279
280  base::WeakPtrFactory<MCSClient> weak_ptr_factory_;
281
282  DISALLOW_COPY_AND_ASSIGN(MCSClient);
283};
284
285} // namespace gcm
286
287#endif  // GOOGLE_APIS_GCM_ENGINE_MCS_CLIENT_H_
288