Commit 7953e58c authored by noamsml@chromium.org's avatar noamsml@chromium.org

[Privet API] onCloudDeviceStateChagned notifies of state changes for mDNS devices

onCloudDeviceStateChanged will now notify users when the state of an mDNS device
has changed.

This is a modified reland of http://crrev.com/280932 that should fix the test issues.

BUG=383167

Review URL: https://codereview.chromium.org/333243006

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@281838 0039d316-1c4b-4281-b951-d872f2087c98
parent 5c92e972
......@@ -9,6 +9,7 @@
#include "chrome/browser/local_discovery/cloud_device_list.h"
#include "chrome/browser/local_discovery/cloud_print_printer_list.h"
#include "chrome/browser/local_discovery/gcd_constants.h"
#include "chrome/browser/local_discovery/privet_device_lister_impl.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
#include "chrome/browser/signin/signin_manager_factory.h"
......@@ -18,14 +19,25 @@
namespace extensions {
using extensions::api::gcd_private::GCDDevice;
namespace gcd_private = api::gcd_private;
namespace {
scoped_ptr<Event> MakeCloudDeviceStateChangedEvent(
bool available,
const gcd_private::GCDDevice& device) {
scoped_ptr<base::ListValue> params =
gcd_private::OnCloudDeviceStateChanged::Create(available, device);
scoped_ptr<Event> event(new Event(
gcd_private::OnCloudDeviceStateChanged::kEventName, params.Pass()));
return event.Pass();
}
const int kNumRequestsNeeded = 2;
const char kIDPrefixCloudPrinter[] = "cloudprint:";
const char kIDPrefixGcd[] = "gcd:";
const char kIDPrefixMdns[] = "mdns:";
GcdPrivateAPI::GCDApiFlowFactoryForTests* g_gcd_api_flow_factory = NULL;
......@@ -54,10 +66,18 @@ scoped_ptr<local_discovery::GCDApiFlow> MakeGCDApiFlow(Profile* profile) {
} // namespace
GcdPrivateAPI::GcdPrivateAPI(content::BrowserContext* context)
: browser_context_(context) {
: num_device_listeners_(0), browser_context_(context) {
DCHECK(browser_context_);
if (EventRouter::Get(context)) {
EventRouter::Get(context)->RegisterObserver(
this, gcd_private::OnCloudDeviceStateChanged::kEventName);
}
}
GcdPrivateAPI::~GcdPrivateAPI() {
if (EventRouter::Get(browser_context_)) {
EventRouter::Get(browser_context_)->UnregisterObserver(this);
}
}
// static
......@@ -66,6 +86,74 @@ GcdPrivateAPI::GetFactoryInstance() {
return g_factory.Pointer();
}
void GcdPrivateAPI::OnListenerAdded(const EventListenerInfo& details) {
num_device_listeners_++;
if (num_device_listeners_ == 1) {
service_discovery_client_ =
local_discovery::ServiceDiscoverySharedClient::GetInstance();
privet_device_lister_.reset(new local_discovery::PrivetDeviceListerImpl(
service_discovery_client_.get(), this));
privet_device_lister_->Start();
}
for (GCDDeviceMap::iterator i = known_devices_.begin();
i != known_devices_.end();
i++) {
EventRouter::Get(browser_context_)->DispatchEventToExtension(
details.extension_id,
MakeCloudDeviceStateChangedEvent(true, *i->second));
}
}
void GcdPrivateAPI::OnListenerRemoved(const EventListenerInfo& details) {
num_device_listeners_--;
if (num_device_listeners_ == 0) {
privet_device_lister_.reset();
service_discovery_client_ = NULL;
}
}
void GcdPrivateAPI::DeviceChanged(
bool added,
const std::string& name,
const local_discovery::DeviceDescription& description) {
linked_ptr<gcd_private::GCDDevice> device(new gcd_private::GCDDevice);
device->setup_type = gcd_private::SETUP_TYPE_MDNS;
device->id_string = kIDPrefixMdns + name;
device->device_type = description.type;
device->device_name = description.name;
device->device_description = description.description;
if (!description.id.empty())
device->cloud_id.reset(new std::string(description.id));
known_devices_[device->id_string] = device;
EventRouter::Get(browser_context_)
->BroadcastEvent(MakeCloudDeviceStateChangedEvent(true, *device));
}
void GcdPrivateAPI::DeviceRemoved(const std::string& name) {
GCDDeviceMap::iterator found = known_devices_.find(kIDPrefixMdns + name);
linked_ptr<gcd_private::GCDDevice> device = found->second;
known_devices_.erase(found);
EventRouter::Get(browser_context_)
->BroadcastEvent(MakeCloudDeviceStateChangedEvent(false, *device));
}
void GcdPrivateAPI::DeviceCacheFlushed() {
for (GCDDeviceMap::iterator i = known_devices_.begin();
i != known_devices_.end();
i++) {
EventRouter::Get(browser_context_)
->BroadcastEvent(MakeCloudDeviceStateChangedEvent(false, *i->second));
}
known_devices_.clear();
}
// static
void GcdPrivateAPI::SetGCDApiFlowFactoryForTests(
GCDApiFlowFactoryForTests* factory) {
......@@ -122,11 +210,11 @@ void GcdPrivateGetCloudDeviceListFunction::CheckListingDone() {
return;
}
std::vector<linked_ptr<GCDDevice> > devices;
std::vector<linked_ptr<gcd_private::GCDDevice> > devices;
for (DeviceList::iterator i = devices_.begin(); i != devices_.end(); i++) {
linked_ptr<GCDDevice> device(new GCDDevice);
device->setup_type = extensions::api::gcd_private::SETUP_TYPE_CLOUD;
linked_ptr<gcd_private::GCDDevice> device(new gcd_private::GCDDevice);
device->setup_type = gcd_private::SETUP_TYPE_CLOUD;
if (i->type == local_discovery::kGCDTypePrinter) {
device->id_string = kIDPrefixCloudPrinter + i->id;
} else {
......@@ -141,8 +229,7 @@ void GcdPrivateGetCloudDeviceListFunction::CheckListingDone() {
devices.push_back(device);
}
results_ = extensions::api::gcd_private::GetCloudDeviceList::Results::Create(
devices);
results_ = gcd_private::GetCloudDeviceList::Results::Create(devices);
SendResponse(true);
Release();
......
......@@ -5,16 +5,22 @@
#ifndef CHROME_BROWSER_EXTENSIONS_API_GCD_PRIVATE_GCD_PRIVATE_API_H_
#define CHROME_BROWSER_EXTENSIONS_API_GCD_PRIVATE_GCD_PRIVATE_API_H_
#include "base/memory/linked_ptr.h"
#include "base/memory/scoped_ptr.h"
#include "chrome/browser/extensions/chrome_extension_function.h"
#include "chrome/browser/local_discovery/cloud_device_list_delegate.h"
#include "chrome/browser/local_discovery/gcd_api_flow.h"
#include "chrome/browser/local_discovery/privet_device_lister.h"
#include "chrome/browser/local_discovery/service_discovery_shared_client.h"
#include "chrome/common/extensions/api/gcd_private.h"
#include "extensions/browser/browser_context_keyed_api_factory.h"
#include "extensions/browser/event_router.h"
namespace extensions {
class GcdPrivateAPI : public BrowserContextKeyedAPI {
class GcdPrivateAPI : public BrowserContextKeyedAPI,
public EventRouter::Observer,
public local_discovery::PrivetDeviceLister::Delegate {
public:
class GCDApiFlowFactoryForTests {
public:
......@@ -34,9 +40,30 @@ class GcdPrivateAPI : public BrowserContextKeyedAPI {
private:
friend class BrowserContextKeyedAPIFactory<GcdPrivateAPI>;
typedef std::map<std::string /* id_string */,
linked_ptr<api::gcd_private::GCDDevice> > GCDDeviceMap;
// EventRouter::Observer implementation.
virtual void OnListenerAdded(const EventListenerInfo& details) OVERRIDE;
virtual void OnListenerRemoved(const EventListenerInfo& details) OVERRIDE;
// BrowserContextKeyedAPI implementation.
static const char* service_name() { return "GcdPrivateAPI"; }
// local_discovery::PrivetDeviceLister implementation.
virtual void DeviceChanged(
bool added,
const std::string& name,
const local_discovery::DeviceDescription& description) OVERRIDE;
virtual void DeviceRemoved(const std::string& name) OVERRIDE;
virtual void DeviceCacheFlushed() OVERRIDE;
int num_device_listeners_;
scoped_refptr<local_discovery::ServiceDiscoverySharedClient>
service_discovery_client_;
scoped_ptr<local_discovery::PrivetDeviceLister> privet_device_lister_;
GCDDeviceMap known_devices_;
content::BrowserContext* const browser_context_;
};
......
......@@ -2,8 +2,10 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/bind.h"
#include "base/command_line.h"
#include "base/json/json_reader.h"
#include "base/message_loop/message_loop_proxy.h"
#include "chrome/browser/extensions/api/gcd_private/gcd_private_api.h"
#include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/browser/extensions/extension_service.h"
......@@ -11,6 +13,10 @@
#include "extensions/common/switches.h"
#include "testing/gmock/include/gmock/gmock.h"
#if defined(ENABLE_MDNS)
#include "chrome/browser/local_discovery/test_service_discovery_client.h"
#endif // ENABLE_MDNS
namespace api = extensions::api;
namespace {
......@@ -48,6 +54,84 @@ const char kGCDResponse[] =
" \"maxRole\": \"owner\""
" }}]}";
#if defined(ENABLE_MDNS)
const uint8 kAnnouncePacket[] = {
// Header
0x00, 0x00, // ID is zeroed out
0x80, 0x00, // Standard query response, no error
0x00, 0x00, // No questions (for simplicity)
0x00, 0x05, // 5 RR (answers)
0x00, 0x00, // 0 authority RRs
0x00, 0x00, // 0 additional RRs
0x07, '_', 'p', 'r', 'i', 'v', 'e', 't', 0x04, '_',
't', 'c', 'p', 0x05, 'l', 'o', 'c', 'a', 'l', 0x00,
0x00, 0x0c, // TYPE is PTR.
0x00, 0x01, // CLASS is IN.
0x00, 0x00, // TTL (4 bytes) is 32768 second.
0x10, 0x00, 0x00, 0x0c, // RDLENGTH is 12 bytes.
0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
0xc0, 0x0c, 0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i',
'c', 'e', 0xc0, 0x0c, 0x00, 0x10, // TYPE is TXT.
0x00, 0x01, // CLASS is IN.
0x00, 0x00, // TTL (4 bytes) is 32768 seconds.
0x01, 0x00, 0x00, 0x41, // RDLENGTH is 69 bytes.
0x03, 'i', 'd', '=', 0x10, 't', 'y', '=', 'S', 'a',
'm', 'p', 'l', 'e', ' ', 'd', 'e', 'v', 'i', 'c',
'e', 0x1e, 'n', 'o', 't', 'e', '=', 'S', 'a', 'm',
'p', 'l', 'e', ' ', 'd', 'e', 'v', 'i', 'c', 'e',
' ', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i',
'o', 'n', 0x0c, 't', 'y', 'p', 'e', '=', 'p', 'r',
'i', 'n', 't', 'e', 'r', 0x09, 'm', 'y', 'S', 'e',
'r', 'v', 'i', 'c', 'e', 0xc0, 0x0c, 0x00, 0x21, // Type is SRV
0x00, 0x01, // CLASS is IN
0x00, 0x00, // TTL (4 bytes) is 32768 second.
0x10, 0x00, 0x00, 0x17, // RDLENGTH is 23
0x00, 0x00, 0x00, 0x00, 0x22, 0xb8, // port 8888
0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
0x05, 'l', 'o', 'c', 'a', 'l', 0x00, 0x09, 'm', 'y',
'S', 'e', 'r', 'v', 'i', 'c', 'e', 0x05, 'l', 'o',
'c', 'a', 'l', 0x00, 0x00, 0x01, // Type is A
0x00, 0x01, // CLASS is IN
0x00, 0x00, // TTL (4 bytes) is 32768 second.
0x10, 0x00, 0x00, 0x04, // RDLENGTH is 4
0x01, 0x02, 0x03, 0x04, // 1.2.3.4
0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e',
0x05, 'l', 'o', 'c', 'a', 'l', 0x00, 0x00, 0x1C, // Type is AAAA
0x00, 0x01, // CLASS is IN
0x00, 0x00, // TTL (4 bytes) is 32768 second.
0x10, 0x00, 0x00, 0x10, // RDLENGTH is 16
0x01, 0x02, 0x03, 0x04, // 1.2.3.4
0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, 0x01, 0x02,
0x03, 0x04,
};
const uint8 kGoodbyePacket[] = {
// Header
0x00, 0x00, // ID is zeroed out
0x80, 0x00, // Standard query response, RA, no error
0x00, 0x00, // No questions (for simplicity)
0x00, 0x02, // 1 RR (answers)
0x00, 0x00, // 0 authority RRs
0x00, 0x00, // 0 additional RRs
0x07, '_', 'p', 'r', 'i', 'v', 'e', 't', 0x04, '_', 't', 'c',
'p', 0x05, 'l', 'o', 'c', 'a', 'l', 0x00, 0x00, 0x0c, // TYPE is PTR.
0x00, 0x01, // CLASS is IN.
0x00, 0x00, // TTL (4 bytes) is 0 seconds.
0x00, 0x00, 0x00, 0x0c, // RDLENGTH is 12 bytes.
0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e', 0xc0, 0x0c,
0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e', 0xc0, 0x0c,
0x00, 0x21, // Type is SRV
0x00, 0x01, // CLASS is IN
0x00, 0x00, // TTL (4 bytes) is 0 seconds.
0x00, 0x00, 0x00, 0x17, // RDLENGTH is 23
0x00, 0x00, 0x00, 0x00, 0x22, 0xb8, // port 8888
0x09, 'm', 'y', 'S', 'e', 'r', 'v', 'i', 'c', 'e', 0x05, 'l',
'o', 'c', 'a', 'l', 0x00,
};
#endif // ENABLE_MDNS
// Sentinel value to signify the request should fail.
const char kResponseValueFailure[] = "FAILURE";
......@@ -105,10 +189,21 @@ class FakeGCDApiFlowFactory
class GcdPrivateAPITest : public ExtensionApiTest {
public:
GcdPrivateAPITest() {}
GcdPrivateAPITest() {
#if defined(ENABLE_MDNS)
test_service_discovery_client_ =
new local_discovery::TestServiceDiscoveryClient();
test_service_discovery_client_->Start();
#endif // ENABLE_MDNS
}
protected:
FakeGCDApiFlowFactory api_flow_factory_;
#if defined(ENABLE_MDNS)
scoped_refptr<local_discovery::TestServiceDiscoveryClient>
test_service_discovery_client_;
#endif // ENABLE_MDNS
};
IN_PROC_BROWSER_TEST_F(GcdPrivateAPITest, GetCloudList) {
......@@ -121,4 +216,44 @@ IN_PROC_BROWSER_TEST_F(GcdPrivateAPITest, GetCloudList) {
EXPECT_TRUE(RunExtensionSubtest("gcd_private/api", "get_cloud_list.html"));
}
#if defined(ENABLE_MDNS)
IN_PROC_BROWSER_TEST_F(GcdPrivateAPITest, AddBefore) {
test_service_discovery_client_->SimulateReceive(kAnnouncePacket,
sizeof(kAnnouncePacket));
EXPECT_TRUE(
RunExtensionSubtest("gcd_private/api", "receive_new_device.html"));
}
IN_PROC_BROWSER_TEST_F(GcdPrivateAPITest, AddAfter) {
base::MessageLoopProxy::current()->PostDelayedTask(
FROM_HERE,
base::Bind(&local_discovery::TestServiceDiscoveryClient::SimulateReceive,
test_service_discovery_client_,
kAnnouncePacket,
sizeof(kAnnouncePacket)),
base::TimeDelta::FromSeconds(1));
EXPECT_TRUE(
RunExtensionSubtest("gcd_private/api", "receive_new_device.html"));
}
IN_PROC_BROWSER_TEST_F(GcdPrivateAPITest, AddRemove) {
test_service_discovery_client_->SimulateReceive(kAnnouncePacket,
sizeof(kAnnouncePacket));
base::MessageLoopProxy::current()->PostDelayedTask(
FROM_HERE,
base::Bind(&local_discovery::TestServiceDiscoveryClient::SimulateReceive,
test_service_discovery_client_,
kGoodbyePacket,
sizeof(kGoodbyePacket)),
base::TimeDelta::FromSeconds(1));
EXPECT_TRUE(RunExtensionSubtest("gcd_private/api", "remove_device.html"));
}
#endif // ENABLE_MDNS
} // namespace
// 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.
onload = function() {
chrome.test.runTests([
function receiveNewDevice() {
chrome.gcdPrivate.onCloudDeviceStateChanged.addListener(
function(available, device) {
chrome.test.assertTrue(available);
chrome.test.assertEq(device.setupType, "mdns");
chrome.test.assertEq(device.idString,
"mdns:myService._privet._tcp.local");
chrome.test.assertEq(device.deviceType, "printer");
chrome.test.assertEq(device.deviceName,
"Sample device");
chrome.test.assertEq(device.deviceDescription,
"Sample device description");
chrome.test.notifyPass();
})
}]);
};
// 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.
onload = function() {
chrome.test.runTests([
function addRemoveDevice() {
var should_be_available = true;
chrome.gcdPrivate.onCloudDeviceStateChanged.addListener(
function(available, device) {
chrome.test.assertEq(available, should_be_available);
should_be_available = false;
chrome.test.assertEq(device.setupType, "mdns");
chrome.test.assertEq(device.idString,
"mdns:myService._privet._tcp.local");
chrome.test.assertEq(device.deviceType, "printer");
chrome.test.assertEq(device.deviceName,
"Sample device");
chrome.test.assertEq(device.deviceDescription,
"Sample device description");
if (!available) {
// Only pass after device is removed
chrome.test.notifyPass();
}
})
}]);
};
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment