OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include <cmath> | 5 #include "content/browser/device_orientation/provider_impl.h" |
6 | |
6 #include <set> | 7 #include <set> |
7 #include <vector> | 8 #include <vector> |
8 | 9 |
9 #include "base/bind.h" | 10 #include "base/bind.h" |
10 #include "base/logging.h" | 11 #include "base/logging.h" |
11 #include "base/message_loop.h" | 12 #include "base/message_loop.h" |
12 #include "base/threading/thread.h" | 13 #include "base/threading/thread.h" |
13 #include "base/threading/worker_pool.h" | 14 #include "base/threading/worker_pool.h" |
14 #include "content/browser/device_orientation/orientation.h" | |
15 #include "content/browser/device_orientation/provider_impl.h" | |
16 | 15 |
17 namespace { | 16 namespace { |
18 | 17 |
19 bool IsElementSignificantlyDifferent(bool can_provide_element1, | |
20 bool can_provide_element2, | |
21 double element1, | |
22 double element2) { | |
23 const double kThreshold = 0.1; | |
24 | |
25 if (can_provide_element1 != can_provide_element2) | |
26 return true; | |
27 if (can_provide_element1 && | |
28 std::fabs(element1 - element2) >= kThreshold) | |
29 return true; | |
30 return false; | |
31 } | |
32 | |
33 void DeleteThread(base::Thread* thread) { | 18 void DeleteThread(base::Thread* thread) { |
34 thread->Stop(); | 19 thread->Stop(); |
35 delete thread; | 20 delete thread; |
36 } | 21 } |
37 | 22 |
38 } | 23 } |
39 | 24 |
40 namespace device_orientation { | 25 namespace device_orientation { |
41 | 26 |
42 class ProviderImpl::PollingThread : public base::Thread { | 27 class ProviderImpl::PollingThread : public base::Thread { |
43 public: | 28 public: |
44 PollingThread(const char* name, | 29 PollingThread(const char* name, |
45 base::WeakPtr<ProviderImpl> provider, | 30 base::WeakPtr<ProviderImpl> provider, |
46 MessageLoop* creator_loop); | 31 MessageLoop* creator_loop); |
47 virtual ~PollingThread(); | 32 virtual ~PollingThread(); |
48 | 33 |
49 // Method for finding a suitable DataFetcher and starting the polling. | 34 // Method for creating a DataFetcher and starting the polling, if the fetcher |
50 void Initialize(DataFetcherFactory factory); | 35 // can provide this type of data. |
36 void Initialize(DataFetcherFactory factory, DeviceData::Type type); | |
37 | |
38 // Method for adding a type of data to poll for. | |
39 void DoAddPollingDataType(DeviceData::Type type); | |
51 | 40 |
52 private: | 41 private: |
53 // Method for polling a DataFetcher. | 42 // Method for polling a DataFetcher. |
54 void DoPoll(); | 43 void DoPoll(); |
55 void ScheduleDoPoll(); | 44 void ScheduleDoPoll(); |
56 | 45 |
57 // Schedule a notification to the |provider_| which lives on a different | 46 // Schedule a notification to the |provider_| which lives on a different |
58 // thread (|creator_loop_| is its message loop). | 47 // thread (|creator_loop_| is its message loop). |
59 void ScheduleDoNotify(const Orientation& orientation); | 48 void ScheduleDoNotify(const DeviceData* device_data, |
60 | 49 DeviceData::Type device_data_type); |
61 static bool SignificantlyDifferent(const Orientation& orientation1, | |
62 const Orientation& orientation2); | |
63 | 50 |
64 enum { kDesiredSamplingIntervalMs = 100 }; | 51 enum { kDesiredSamplingIntervalMs = 100 }; |
65 base::TimeDelta SamplingInterval() const; | 52 base::TimeDelta SamplingInterval() const; |
66 | 53 |
67 // The Message Loop on which this object was created. | 54 // The Message Loop on which this object was created. |
68 // Typically the I/O loop, but may be something else during testing. | 55 // Typically the I/O loop, but may be something else during testing. |
69 MessageLoop* creator_loop_; | 56 MessageLoop* creator_loop_; |
70 | 57 |
71 scoped_ptr<DataFetcher> data_fetcher_; | 58 scoped_ptr<DataFetcher> data_fetcher_; |
72 Orientation last_orientation_; | 59 std::map<DeviceData::Type, scoped_refptr<const DeviceData> > |
60 last_device_data_map_; | |
61 std::set<DeviceData::Type> polling_data_types_; | |
73 | 62 |
74 base::WeakPtr<ProviderImpl> provider_; | 63 base::WeakPtr<ProviderImpl> provider_; |
75 }; | 64 }; |
76 | 65 |
77 ProviderImpl::PollingThread::PollingThread( | 66 ProviderImpl::PollingThread::PollingThread( |
78 const char* name, | 67 const char* name, |
79 base::WeakPtr<ProviderImpl> provider, | 68 base::WeakPtr<ProviderImpl> provider, |
80 MessageLoop* creator_loop) | 69 MessageLoop* creator_loop) |
81 : base::Thread(name), | 70 : base::Thread(name), |
82 creator_loop_(creator_loop), | 71 creator_loop_(creator_loop), |
83 provider_(provider) { | 72 provider_(provider) { |
84 } | 73 } |
85 | 74 |
86 ProviderImpl::PollingThread::~PollingThread() { | 75 ProviderImpl::PollingThread::~PollingThread() { |
87 } | 76 } |
88 | 77 |
89 void ProviderImpl::PollingThread::Initialize(DataFetcherFactory factory) { | 78 void ProviderImpl::PollingThread::DoAddPollingDataType(DeviceData::Type type) { |
79 DCHECK(MessageLoop::current() == message_loop()); | |
80 | |
81 polling_data_types_.insert(type); | |
82 } | |
83 | |
84 void ProviderImpl::PollingThread::Initialize(DataFetcherFactory factory, | |
85 DeviceData::Type type) { | |
90 DCHECK(MessageLoop::current() == message_loop()); | 86 DCHECK(MessageLoop::current() == message_loop()); |
91 | 87 |
92 if (factory != NULL) { | 88 if (factory != NULL) { |
93 // Try to use factory to create a fetcher that can provide orientation data. | 89 // Try to use factory to create a fetcher that can provide this type of |
90 // data. If factory creates a fetcher that provides this type of data, | |
91 // start polling. | |
94 scoped_ptr<DataFetcher> fetcher(factory()); | 92 scoped_ptr<DataFetcher> fetcher(factory()); |
95 Orientation orientation; | |
96 | 93 |
97 if (fetcher.get() && fetcher->GetOrientation(&orientation)) { | 94 if (fetcher.get()) { |
98 // Pass ownership of fetcher to provider_. | 95 scoped_refptr<const DeviceData> device_data(fetcher->GetDeviceData(type)); |
99 data_fetcher_.swap(fetcher); | 96 if (device_data != NULL) { |
100 last_orientation_ = orientation; | 97 // Pass ownership of fetcher to provider_. |
98 data_fetcher_.swap(fetcher); | |
99 last_device_data_map_[type] = device_data; | |
101 | 100 |
102 // Notify observers. | 101 // Notify observers. |
103 if (!orientation.is_empty()) | 102 ScheduleDoNotify(device_data, type); |
104 ScheduleDoNotify(orientation); | |
105 | 103 |
106 // Start polling. | 104 // Start polling. |
107 ScheduleDoPoll(); | 105 ScheduleDoPoll(); |
108 return; | 106 return; |
107 } | |
109 } | 108 } |
110 } | 109 } |
111 | 110 |
112 // When no orientation data can be provided. | 111 // When no device data can be provided. |
113 ScheduleDoNotify(Orientation::Empty()); | 112 ScheduleDoNotify(NULL, type); |
114 } | 113 } |
115 | 114 |
116 void ProviderImpl::PollingThread::ScheduleDoNotify( | 115 void ProviderImpl::PollingThread::ScheduleDoNotify( |
117 const Orientation& orientation) { | 116 const DeviceData* device_data, DeviceData::Type device_data_type) { |
118 DCHECK(MessageLoop::current() == message_loop()); | 117 DCHECK(MessageLoop::current() == message_loop()); |
119 | 118 |
120 creator_loop_->PostTask( | 119 creator_loop_->PostTask(FROM_HERE, |
121 FROM_HERE, base::Bind(&ProviderImpl::DoNotify, provider_, orientation)); | 120 base::Bind(&ProviderImpl::DoNotify, provider_, |
121 device_data, device_data_type)); | |
122 } | 122 } |
123 | 123 |
124 void ProviderImpl::PollingThread::DoPoll() { | 124 void ProviderImpl::PollingThread::DoPoll() { |
125 DCHECK(MessageLoop::current() == message_loop()); | 125 DCHECK(MessageLoop::current() == message_loop()); |
126 | 126 |
127 Orientation orientation; | 127 // Poll the fetcher for each type of data. |
128 if (!data_fetcher_->GetOrientation(&orientation)) { | 128 typedef std::set<DeviceData::Type>::const_iterator SetIterator; |
129 LOG(ERROR) << "Failed to poll device orientation data fetcher."; | 129 for (SetIterator i = polling_data_types_.begin(); |
130 i != polling_data_types_.end(); ++i) { | |
131 DeviceData::Type device_data_type = *i; | |
132 scoped_refptr<const DeviceData> device_data(data_fetcher_->GetDeviceData( | |
133 device_data_type)); | |
130 | 134 |
131 ScheduleDoNotify(Orientation::Empty()); | 135 if (device_data == NULL) { |
132 return; | 136 LOG(ERROR) << "Failed to poll device data fetcher."; |
133 } | 137 ScheduleDoNotify(NULL, device_data_type); |
138 continue; | |
139 } | |
134 | 140 |
135 if (!orientation.is_empty() && | 141 const DeviceData* old_data = last_device_data_map_[device_data_type]; |
136 SignificantlyDifferent(orientation, last_orientation_)) { | 142 if (old_data != NULL && !device_data->ShouldFireEvent(old_data)) |
137 last_orientation_ = orientation; | 143 continue; |
138 ScheduleDoNotify(orientation); | 144 |
145 // Update the last device data of this type and notify observers. | |
146 last_device_data_map_[device_data_type] = device_data; | |
147 ScheduleDoNotify(device_data, device_data_type); | |
139 } | 148 } |
140 | 149 |
141 ScheduleDoPoll(); | 150 ScheduleDoPoll(); |
142 } | 151 } |
143 | 152 |
144 void ProviderImpl::PollingThread::ScheduleDoPoll() { | 153 void ProviderImpl::PollingThread::ScheduleDoPoll() { |
145 DCHECK(MessageLoop::current() == message_loop()); | 154 DCHECK(MessageLoop::current() == message_loop()); |
146 | 155 |
147 message_loop()->PostDelayedTask( | 156 message_loop()->PostDelayedTask( |
148 FROM_HERE, | 157 FROM_HERE, |
149 base::Bind(&PollingThread::DoPoll, base::Unretained(this)), | 158 base::Bind(&PollingThread::DoPoll, base::Unretained(this)), |
150 SamplingInterval()); | 159 SamplingInterval()); |
151 } | 160 } |
152 | 161 |
153 // Returns true if two orientations are considered different enough that | |
154 // observers should be notified of the new orientation. | |
155 bool ProviderImpl::PollingThread::SignificantlyDifferent( | |
156 const Orientation& o1, | |
157 const Orientation& o2) { | |
158 return IsElementSignificantlyDifferent(o1.can_provide_alpha(), | |
159 o2.can_provide_alpha(), | |
160 o1.alpha(), | |
161 o2.alpha()) || | |
162 IsElementSignificantlyDifferent(o1.can_provide_beta(), | |
163 o2.can_provide_beta(), | |
164 o1.beta(), | |
165 o2.beta()) || | |
166 IsElementSignificantlyDifferent(o1.can_provide_gamma(), | |
167 o2.can_provide_gamma(), | |
168 o1.gamma(), | |
169 o2.gamma()) || | |
170 (o1.can_provide_absolute() != o2.can_provide_absolute() || | |
171 o1.absolute() != o2.absolute()); | |
172 } | |
173 | |
174 base::TimeDelta ProviderImpl::PollingThread::SamplingInterval() const { | 162 base::TimeDelta ProviderImpl::PollingThread::SamplingInterval() const { |
175 DCHECK(MessageLoop::current() == message_loop()); | 163 DCHECK(MessageLoop::current() == message_loop()); |
176 DCHECK(data_fetcher_.get()); | 164 DCHECK(data_fetcher_.get()); |
177 | 165 |
178 // TODO(erg): There used to be unused code here, that called a default | 166 // TODO(erg): There used to be unused code here, that called a default |
179 // implementation on the DataFetcherInterface that was never defined. I'm | 167 // implementation on the DataFetcherInterface that was never defined. I'm |
180 // removing unused methods from headers. | 168 // removing unused methods from headers. |
181 return base::TimeDelta::FromMilliseconds(kDesiredSamplingIntervalMs); | 169 return base::TimeDelta::FromMilliseconds(kDesiredSamplingIntervalMs); |
182 } | 170 } |
183 | 171 |
184 ProviderImpl::ProviderImpl(DataFetcherFactory factory) | 172 ProviderImpl::ProviderImpl(DataFetcherFactory factory) |
185 : creator_loop_(MessageLoop::current()), | 173 : creator_loop_(MessageLoop::current()), |
186 factory_(factory), | 174 factory_(factory), |
187 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)), | 175 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)), |
188 polling_thread_(NULL) { | 176 polling_thread_(NULL) { |
189 } | 177 } |
190 | 178 |
191 ProviderImpl::~ProviderImpl() { | 179 ProviderImpl::~ProviderImpl() { |
192 Stop(); | 180 Stop(); |
193 } | 181 } |
194 | 182 |
183 void ProviderImpl::ScheduleDoAddPollingDataType(DeviceData::Type type) { | |
184 DCHECK(MessageLoop::current() == creator_loop_); | |
185 | |
186 MessageLoop* polling_loop = polling_thread_->message_loop(); | |
187 polling_loop->PostTask(FROM_HERE, | |
188 base::Bind(&PollingThread::DoAddPollingDataType, | |
189 base::Unretained(polling_thread_), | |
190 type)); | |
191 } | |
192 | |
195 void ProviderImpl::AddObserver(Observer* observer) { | 193 void ProviderImpl::AddObserver(Observer* observer) { |
196 DCHECK(MessageLoop::current() == creator_loop_); | 194 DCHECK(MessageLoop::current() == creator_loop_); |
197 | 195 |
196 DeviceData::Type type = observer->device_data_type(); | |
197 | |
198 observers_.insert(observer); | 198 observers_.insert(observer); |
199 if (observers_.size() == 1) | 199 if (observers_.size() == 1) |
200 Start(); | 200 Start(type); |
201 else | 201 else { |
202 observer->OnOrientationUpdate(last_notification_); | 202 // Notify observer of most recent notification if one exists. |
203 const DeviceData *last_notification = last_notifications_map_[type]; | |
204 if (last_notification != NULL) | |
205 observer->OnDeviceDataUpdate(last_notification, type); | |
206 } | |
207 | |
208 ScheduleDoAddPollingDataType(type); | |
203 } | 209 } |
204 | 210 |
205 void ProviderImpl::RemoveObserver(Observer* observer) { | 211 void ProviderImpl::RemoveObserver(Observer* observer) { |
206 DCHECK(MessageLoop::current() == creator_loop_); | 212 DCHECK(MessageLoop::current() == creator_loop_); |
207 | 213 |
208 observers_.erase(observer); | 214 observers_.erase(observer); |
209 if (observers_.empty()) | 215 if (observers_.empty()) |
210 Stop(); | 216 Stop(); |
211 } | 217 } |
212 | 218 |
213 void ProviderImpl::Start() { | 219 void ProviderImpl::Start(DeviceData::Type type) { |
214 DCHECK(MessageLoop::current() == creator_loop_); | 220 DCHECK(MessageLoop::current() == creator_loop_); |
215 DCHECK(!polling_thread_); | 221 DCHECK(!polling_thread_); |
216 | 222 |
217 polling_thread_ = new PollingThread("Device orientation polling thread", | 223 polling_thread_ = new PollingThread("Device data polling thread", |
218 weak_factory_.GetWeakPtr(), | 224 weak_factory_.GetWeakPtr(), |
219 creator_loop_); | 225 creator_loop_); |
220 if (!polling_thread_->Start()) { | 226 if (!polling_thread_->Start()) { |
221 LOG(ERROR) << "Failed to start device orientation polling thread"; | 227 LOG(ERROR) << "Failed to start device data polling thread"; |
222 delete polling_thread_; | 228 delete polling_thread_; |
223 polling_thread_ = NULL; | 229 polling_thread_ = NULL; |
224 return; | 230 return; |
225 } | 231 } |
226 ScheduleInitializePollingThread(); | 232 ScheduleInitializePollingThread(type); |
227 } | 233 } |
228 | 234 |
229 void ProviderImpl::Stop() { | 235 void ProviderImpl::Stop() { |
230 DCHECK(MessageLoop::current() == creator_loop_); | 236 DCHECK(MessageLoop::current() == creator_loop_); |
231 | 237 |
232 weak_factory_.InvalidateWeakPtrs(); | 238 weak_factory_.InvalidateWeakPtrs(); |
233 if (polling_thread_) { | 239 if (polling_thread_) { |
234 polling_thread_->StopSoon(); | 240 polling_thread_->StopSoon(); |
235 bool posted = base::WorkerPool::PostTask( | 241 bool posted = base::WorkerPool::PostTask( |
236 FROM_HERE, | 242 FROM_HERE, |
237 base::Bind(&DeleteThread, base::Unretained(polling_thread_)), | 243 base::Bind(&DeleteThread, base::Unretained(polling_thread_)), |
238 true /* task is slow */); | 244 true /* task is slow */); |
239 DCHECK(posted); | 245 DCHECK(posted); |
240 polling_thread_ = NULL; | 246 polling_thread_ = NULL; |
241 } | 247 } |
242 } | 248 } |
243 | 249 |
244 void ProviderImpl::ScheduleInitializePollingThread() { | 250 void ProviderImpl::ScheduleInitializePollingThread( |
251 DeviceData::Type device_data_type) { | |
245 DCHECK(MessageLoop::current() == creator_loop_); | 252 DCHECK(MessageLoop::current() == creator_loop_); |
246 | 253 |
247 MessageLoop* polling_loop = polling_thread_->message_loop(); | 254 MessageLoop* polling_loop = polling_thread_->message_loop(); |
248 polling_loop->PostTask(FROM_HERE, | 255 polling_loop->PostTask(FROM_HERE, |
249 base::Bind(&PollingThread::Initialize, | 256 base::Bind(&PollingThread::Initialize, |
250 base::Unretained(polling_thread_), | 257 base::Unretained(polling_thread_), |
251 factory_)); | 258 factory_, |
259 device_data_type)); | |
252 } | 260 } |
253 | 261 |
254 void ProviderImpl::DoNotify(const Orientation& orientation) { | 262 void ProviderImpl::DoNotify(const DeviceData* device_data, |
263 DeviceData::Type device_data_type) { | |
255 DCHECK(MessageLoop::current() == creator_loop_); | 264 DCHECK(MessageLoop::current() == creator_loop_); |
256 | 265 |
257 last_notification_ = orientation; | 266 scoped_refptr<const DeviceData> data(device_data); |
258 | 267 |
259 typedef std::set<Observer*>::const_iterator Iterator; | 268 // Update last notification of this type. |
260 for (Iterator i = observers_.begin(); i != observers_.end(); ++i) | 269 last_notifications_map_[device_data_type] = data; |
261 (*i)->OnOrientationUpdate(orientation); | |
262 | 270 |
263 if (orientation.is_empty()) { | 271 // Notify observers of this type of the new data. |
264 // Notify observers about failure to provide data exactly once. | 272 typedef std::set<Observer*>::const_iterator ConstIterator; |
265 observers_.clear(); | 273 for (ConstIterator i = observers_.begin(); i != observers_.end(); ++i) { |
266 Stop(); | 274 if ((*i)->device_data_type() == device_data_type) |
275 (*i)->OnDeviceDataUpdate(data.get(), device_data_type); | |
276 } | |
277 | |
278 if (data == NULL) { | |
279 // Notify observers exactly once about failure to provide data. | |
280 typedef std::set<Observer*>::iterator Iterator; | |
281 Iterator i = observers_.begin(); | |
282 while (i != observers_.end()) { | |
283 Iterator current = i++; | |
284 if ((*current)->device_data_type() == device_data_type) | |
285 observers_.erase(current); | |
286 } | |
287 | |
288 if (observers_.empty()) | |
289 Stop(); | |
267 } | 290 } |
268 } | 291 } |
269 | 292 |
293 | |
270 } // namespace device_orientation | 294 } // namespace device_orientation |
295 | |
hans
2012/08/03 13:05:41
ultra nit: a blank line too much here
aousterh
2012/08/03 13:42:18
Done.
| |
OLD | NEW |