1 // Copyright (c) 2012 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 CHROME_BROWSER_EXTENSIONS_API_DIAL_DIAL_REGISTRY_H_ 6 #define CHROME_BROWSER_EXTENSIONS_API_DIAL_DIAL_REGISTRY_H_ 7 8 #include <map> 9 #include <string> 10 #include <vector> 11 12 #include "base/basictypes.h" 13 #include "base/containers/hash_tables.h" 14 #include "base/gtest_prod_util.h" 15 #include "base/memory/linked_ptr.h" 16 #include "base/memory/ref_counted.h" 17 #include "base/threading/thread_checker.h" 18 #include "base/time/time.h" 19 #include "base/timer/timer.h" 20 #include "chrome/browser/extensions/api/dial/dial_service.h" 21 #include "chrome/browser/profiles/profile.h" 22 #include "net/base/network_change_notifier.h" 23 24 namespace extensions { 25 26 // Keeps track of devices that have responded to discovery requests and notifies 27 // the observer with an updated, complete set of active devices. The registry's 28 // observer (i.e., the Dial API) owns the registry instance. 29 class DialRegistry : public DialService::Observer, 30 public net::NetworkChangeNotifier::NetworkChangeObserver { 31 public: 32 typedef std::vector<DialDeviceData> DeviceList; 33 34 enum DialErrorCode { 35 DIAL_NO_LISTENERS = 0, 36 DIAL_NO_INTERFACES, 37 DIAL_NETWORK_DISCONNECTED, 38 DIAL_CELLULAR_NETWORK, 39 DIAL_SOCKET_ERROR, 40 DIAL_UNKNOWN 41 }; 42 43 class Observer { 44 public: 45 // Methods invoked on the IO thread when a new device is discovered, an 46 // update is triggered by dial.discoverNow or an error occured. 47 virtual void OnDialDeviceEvent(const DeviceList& devices) = 0; 48 virtual void OnDialError(DialErrorCode type) = 0; 49 50 protected: 51 virtual ~Observer() {} 52 }; 53 54 // Create the DIAL registry and pass a reference to allow it to notify on 55 // DIAL device events. 56 DialRegistry(Observer* dial_api, 57 const base::TimeDelta& refresh_interval, 58 const base::TimeDelta& expiration, 59 const size_t max_devices); 60 61 virtual ~DialRegistry(); 62 63 // Called by the DIAL API when event listeners are added or removed. The dial 64 // service is started after the first listener is added and stopped after the 65 // last listener is removed. 66 void OnListenerAdded(); 67 void OnListenerRemoved(); 68 69 // Called by the DIAL API to try to kickoff a discovery if there is not one 70 // already active. 71 bool DiscoverNow(); 72 73 protected: 74 // Returns a new instance of the DIAL service. Overridden by tests. 75 virtual DialService* CreateDialService(); 76 virtual void ClearDialService(); 77 78 // Returns the current time. Overridden by tests. 79 virtual base::Time Now() const; 80 81 protected: 82 // The DIAL service. Periodic discovery is active when this is not NULL. 83 scoped_ptr<DialService> dial_; 84 85 private: 86 typedef base::hash_map<std::string, linked_ptr<DialDeviceData> > 87 DeviceByIdMap; 88 typedef std::map<std::string, linked_ptr<DialDeviceData> > DeviceByLabelMap; 89 90 // DialService::Observer: 91 virtual void OnDiscoveryRequest(DialService* service) OVERRIDE; 92 virtual void OnDeviceDiscovered(DialService* service, 93 const DialDeviceData& device) OVERRIDE; 94 virtual void OnDiscoveryFinished(DialService* service) OVERRIDE; 95 virtual void OnError(DialService* service, 96 const DialService::DialServiceErrorCode& code) OVERRIDE; 97 98 // net::NetworkChangeObserver: 99 virtual void OnNetworkChanged( 100 net::NetworkChangeNotifier::ConnectionType type) OVERRIDE; 101 102 // Starts and stops periodic discovery. Periodic discovery is done when there 103 // are registered event listeners. 104 void StartPeriodicDiscovery(); 105 void StopPeriodicDiscovery(); 106 107 // Check whether we are in a state ready to discover and dispatch error 108 // notifications if not. 109 bool ReadyToDiscover(); 110 111 // Purge our whole registry. We may need to do this occasionally, e.g. when 112 // the network status changes. Increments the registry generation. 113 void Clear(); 114 115 // The repeating timer schedules discoveries with this method. 116 void DoDiscovery(); 117 118 // Attempts to add a newly discovered device to the registry. Returns true if 119 // successful. 120 bool MaybeAddDevice(const linked_ptr<DialDeviceData>& device_data); 121 122 // Remove devices from the registry that have expired, i.e. not responded 123 // after some time. Returns true if the registry was modified. 124 bool PruneExpiredDevices(); 125 126 // Returns true if the device has expired and should be removed from the 127 // active set. 128 bool IsDeviceExpired(const DialDeviceData& device) const; 129 130 // Notify clients with the current device list if necessary. 131 void MaybeSendEvent(); 132 133 // Returns the next label to use for a newly-seen device. 134 std::string NextLabel(); 135 136 // The current number of event listeners attached to this registry. 137 int num_listeners_; 138 139 // Incremented each time we DoDiscovery(). 140 int discovery_generation_; 141 142 // Incremented each time we modify the registry of active devices. 143 int registry_generation_; 144 145 // The discovery generation associated with the last time we sent an event. 146 // Used to ensure that we generate at least one event per round of discovery. 147 int last_event_discovery_generation_; 148 149 // The registry generation associated with the last time we sent an event. 150 // Used to suppress events with duplicate device lists. 151 int last_event_registry_generation_; 152 153 // Counter to generate device labels. 154 int label_count_; 155 156 // Registry parameters 157 base::TimeDelta refresh_interval_delta_; 158 base::TimeDelta expiration_delta_; 159 size_t max_devices_; 160 161 // A map used to track known devices by their device_id. 162 DeviceByIdMap device_by_id_map_; 163 164 // A map used to track known devices sorted by label. We iterate over this to 165 // construct the device list sent to API clients. 166 DeviceByLabelMap device_by_label_map_; 167 168 // Timer used to manage periodic discovery requests. 169 base::RepeatingTimer<DialRegistry> repeating_timer_; 170 171 // Interface from which the DIAL API is notified of DIAL device events. the 172 // DIAL API owns this DIAL registry. 173 Observer* const dial_api_; 174 175 // Thread checker. 176 base::ThreadChecker thread_checker_; 177 178 FRIEND_TEST_ALL_PREFIXES(DialRegistryTest, TestAddRemoveListeners); 179 FRIEND_TEST_ALL_PREFIXES(DialRegistryTest, TestNoDevicesDiscovered); 180 FRIEND_TEST_ALL_PREFIXES(DialRegistryTest, TestDevicesDiscovered); 181 FRIEND_TEST_ALL_PREFIXES(DialRegistryTest, TestDeviceExpires); 182 FRIEND_TEST_ALL_PREFIXES(DialRegistryTest, TestExpiredDeviceIsRediscovered); 183 FRIEND_TEST_ALL_PREFIXES(DialRegistryTest, 184 TestRemovingListenerDoesNotClearList); 185 FRIEND_TEST_ALL_PREFIXES(DialRegistryTest, TestNetworkEventConnectionLost); 186 FRIEND_TEST_ALL_PREFIXES(DialRegistryTest, 187 TestNetworkEventConnectionRestored); 188 DISALLOW_COPY_AND_ASSIGN(DialRegistry); 189 }; 190 191 } // namespace extensions 192 193 #endif // CHROME_BROWSER_EXTENSIONS_API_DIAL_DIAL_REGISTRY_H_ 194