summaryrefslogtreecommitdiffstats
path: root/components/copresence/handlers/gcm_handler_impl.cc
blob: 362b53550e3c0b1c7a31aea30e136d85ed6b92f8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "components/copresence/handlers/gcm_handler_impl.h"

#include "base/base64.h"
#include "base/bind.h"
#include "base/logging.h"
#include "base/strings/string_util.h"
#include "components/copresence/handlers/directive_handler.h"
#include "components/copresence/proto/push_message.pb.h"
#include "components/gcm_driver/gcm_driver.h"

using gcm::GCMClient;

namespace {

// TODO(ckehoe): Move this to a common library.
bool Base64Decode(std::string data, std::string* out) {
  // Convert from URL-safe.
  base::ReplaceChars(data, "-", "+", &data);
  base::ReplaceChars(data, "_", "/", &data);

  // Add padding if needed.
  while (data.size() % 4)
    data.push_back('=');

  // Decode.
  return base::Base64Decode(data, out);
}

}  // namespace

namespace copresence {

const char GCMHandlerImpl::kCopresenceAppId[] =
    "com.google.android.gms.location.copresence";
const char GCMHandlerImpl::kCopresenceSenderId[] = "745476177629";
const char GCMHandlerImpl::kGcmMessageKey[] = "PUSH_MESSAGE";


// Public functions.

GCMHandlerImpl::GCMHandlerImpl(gcm::GCMDriver* gcm_driver,
                               DirectiveHandler* directive_handler,
                               const MessagesCallback& new_messages_callback)
    : driver_(gcm_driver),
      directive_handler_(directive_handler),
      new_messages_callback_(new_messages_callback),
      registration_callback_(base::Bind(&GCMHandlerImpl::RegistrationComplete,
                                        base::Unretained(this))) {
  DCHECK(driver_);
  DCHECK(directive_handler_);

  driver_->AddAppHandler(kCopresenceAppId, this);
  driver_->Register(kCopresenceAppId,
                    std::vector<std::string>(1, kCopresenceSenderId),
                    registration_callback_.callback());
}

GCMHandlerImpl::~GCMHandlerImpl() {
  if (driver_)
    driver_->RemoveAppHandler(kCopresenceAppId);
}

void GCMHandlerImpl::GetGcmId(const RegistrationCallback& callback) {
  if (gcm_id_.empty()) {
    pending_id_requests_.push_back(callback);
  } else {
    callback.Run(gcm_id_);
  }
}

void GCMHandlerImpl::ShutdownHandler() {
  // The GCMDriver is going away. Make sure we don't try to contact it.
  driver_ = nullptr;
}

void GCMHandlerImpl::OnMessage(const std::string& app_id,
                               const gcm::IncomingMessage& message) {
  DCHECK_EQ(kCopresenceAppId, app_id);
  DVLOG(2) << "Incoming GCM message";

  const auto& content = message.data.find(kGcmMessageKey);
  if (content == message.data.end()) {
    LOG(ERROR) << "GCM message missing data key";
    return;
  }

  std::string serialized_message;
  if (!Base64Decode(content->second, &serialized_message)) {
    LOG(ERROR) << "Couldn't decode GCM message";
    return;
  }

  PushMessage push_message;
  if (!push_message.ParseFromString(serialized_message)) {
    LOG(ERROR) << "GCM message contained invalid proto";
    return;
  }

  if (push_message.type() != PushMessage::REPORT) {
    DVLOG(2) << "Discarding non-report GCM message";
    return;
  }

  DVLOG(3) << "Processing " << push_message.report().directive_size()
           << " directive(s) from GCM message";
  for (const Directive& directive : push_message.report().directive()) {
    // TODO(ckehoe): Use a callback here so GCMHandler
    // is decoupled from DirectiveHandler.
    directive_handler_->AddDirective(directive);
  }

  new_messages_callback_.Run(push_message.report().subscribed_message());
}

void GCMHandlerImpl::OnMessagesDeleted(const std::string& app_id) {
  DCHECK_EQ(kCopresenceAppId, app_id);
  DVLOG(2) << "GCM message overflow reported";
}

void GCMHandlerImpl::OnSendError(
    const std::string& /* app_id */,
    const GCMClient::SendErrorDetails& /* send_error_details */) {
  NOTREACHED() << "Copresence clients should not be sending GCM messages";
}

void GCMHandlerImpl::OnSendAcknowledged(const std::string& /* app_id */,
                                        const std::string& /* message_id */) {
  NOTREACHED() << "Copresence clients should not be sending GCM messages";
}

bool GCMHandlerImpl::CanHandle(const std::string& app_id) const {
  return app_id == kCopresenceAppId;
}


// Private functions.

void GCMHandlerImpl::RegistrationComplete(const std::string& registration_id,
                                          GCMClient::Result result) {
  if (result == GCMClient::SUCCESS) {
    DVLOG(2) << "GCM registration successful. ID: " << registration_id;
    gcm_id_ = registration_id;
  } else {
    LOG(ERROR) << "GCM registration failed with error " << result;
  }

  for (const RegistrationCallback& callback : pending_id_requests_) {
    callback.Run(result == GCMClient::SUCCESS ?
        registration_id : std::string());
  }
  pending_id_requests_.clear();
}

}  // namespace copresence