summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authornoamsml@chromium.org <noamsml@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-07-02 01:25:53 +0000
committernoamsml@chromium.org <noamsml@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-07-02 01:25:53 +0000
commitca387296b9249ab106632996d9f37b2342ccf6bb (patch)
tree0897bf420ce5573707804dcf0d6ffb361c4b8241
parent3f016cabc869fd57e80dea68f399ccdbc3e9eddb (diff)
downloadchromium_src-ca387296b9249ab106632996d9f37b2342ccf6bb.zip
chromium_src-ca387296b9249ab106632996d9f37b2342ccf6bb.tar.gz
chromium_src-ca387296b9249ab106632996d9f37b2342ccf6bb.tar.bz2
[Privet API] onCloudDeviceStateChagned notifies of state changes for mDNS devices
onCloudDeviceStateChanged will now notify users when the state of an mDNS device has changed. BUG=383167 NOTRY=true Review URL: https://codereview.chromium.org/333243006 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@280932 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/browser/extensions/api/gcd_private/gcd_private_api.cc96
-rw-r--r--chrome/browser/extensions/api/gcd_private/gcd_private_api.h29
-rw-r--r--chrome/browser/extensions/api/gcd_private/gcd_private_apitest.cc137
-rw-r--r--chrome/test/data/extensions/api_test/gcd_private/api/receive_new_device.html1
-rw-r--r--chrome/test/data/extensions/api_test/gcd_private/api/receive_new_device.js24
-rw-r--r--chrome/test/data/extensions/api_test/gcd_private/api/remove_device.html1
-rw-r--r--chrome/test/data/extensions/api_test/gcd_private/api/remove_device.js29
7 files changed, 308 insertions, 9 deletions
diff --git a/chrome/browser/extensions/api/gcd_private/gcd_private_api.cc b/chrome/browser/extensions/api/gcd_private/gcd_private_api.cc
index b17b984..5d3dba1 100644
--- a/chrome/browser/extensions/api/gcd_private/gcd_private_api.cc
+++ b/chrome/browser/extensions/api/gcd_private/gcd_private_api.cc
@@ -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,7 +66,10 @@ 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_);
+ EventRouter::Get(context)->RegisterObserver(
+ this, gcd_private::OnCloudDeviceStateChanged::kEventName);
}
GcdPrivateAPI::~GcdPrivateAPI() {
@@ -66,6 +81,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 +205,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 +224,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();
diff --git a/chrome/browser/extensions/api/gcd_private/gcd_private_api.h b/chrome/browser/extensions/api/gcd_private/gcd_private_api.h
index d8c6f46..61f0db6 100644
--- a/chrome/browser/extensions/api/gcd_private/gcd_private_api.h
+++ b/chrome/browser/extensions/api/gcd_private/gcd_private_api.h
@@ -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_;
};
diff --git a/chrome/browser/extensions/api/gcd_private/gcd_private_apitest.cc b/chrome/browser/extensions/api/gcd_private/gcd_private_apitest.cc
index 7e6ae8b..193a498 100644
--- a/chrome/browser/extensions/api/gcd_private/gcd_private_apitest.cc
+++ b/chrome/browser/extensions/api/gcd_private/gcd_private_apitest.cc
@@ -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
diff --git a/chrome/test/data/extensions/api_test/gcd_private/api/receive_new_device.html b/chrome/test/data/extensions/api_test/gcd_private/api/receive_new_device.html
new file mode 100644
index 0000000..5570a76
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/gcd_private/api/receive_new_device.html
@@ -0,0 +1 @@
+<script src="receive_new_device.js"></script>
diff --git a/chrome/test/data/extensions/api_test/gcd_private/api/receive_new_device.js b/chrome/test/data/extensions/api_test/gcd_private/api/receive_new_device.js
new file mode 100644
index 0000000..2ccd688
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/gcd_private/api/receive_new_device.js
@@ -0,0 +1,24 @@
+// 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();
+ })
+ }]);
+};
diff --git a/chrome/test/data/extensions/api_test/gcd_private/api/remove_device.html b/chrome/test/data/extensions/api_test/gcd_private/api/remove_device.html
new file mode 100644
index 0000000..ecbcf39a
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/gcd_private/api/remove_device.html
@@ -0,0 +1 @@
+<script src="remove_device.js"></script>
diff --git a/chrome/test/data/extensions/api_test/gcd_private/api/remove_device.js b/chrome/test/data/extensions/api_test/gcd_private/api/remove_device.js
new file mode 100644
index 0000000..072b79b
--- /dev/null
+++ b/chrome/test/data/extensions/api_test/gcd_private/api/remove_device.js
@@ -0,0 +1,29 @@
+// 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();
+ }
+ })
+ }]);
+};