summaryrefslogtreecommitdiffstats
path: root/content/browser/device_orientation/provider_impl.cc
blob: 4b8876430d8d1bd1bd30a0853f70c231e88dad8c (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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
// Copyright (c) 2012 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 "content/browser/device_orientation/provider_impl.h"

#include <set>
#include <vector>

#include "base/bind.h"
#include "base/logging.h"
#include "base/message_loop.h"
#include "base/threading/thread.h"
#include "base/threading/worker_pool.h"

namespace {

void DeleteThread(base::Thread* thread) {
  delete thread;
}

}

namespace content {

class ProviderImpl::PollingThread : public base::Thread {
 public:
  PollingThread(const char* name,
                base::WeakPtr<ProviderImpl> provider,
                MessageLoop* creator_loop);
  virtual ~PollingThread();

  // Method for creating a DataFetcher and starting the polling, if the fetcher
  // can provide this type of data.
  void Initialize(DataFetcherFactory factory, DeviceData::Type type);

  // Method for adding a type of data to poll for.
  void DoAddPollingDataType(DeviceData::Type type);

 private:
  // Method for polling a DataFetcher.
  void DoPoll();
  void ScheduleDoPoll();

  // Schedule a notification to the |provider_| which lives on a different
  // thread (|creator_loop_| is its message loop).
  void ScheduleDoNotify(const scoped_refptr<const DeviceData>& device_data,
                        DeviceData::Type device_data_type);

  enum { kDesiredSamplingIntervalMs = 100 };
  base::TimeDelta SamplingInterval() const;

  // The Message Loop on which this object was created.
  // Typically the I/O loop, but may be something else during testing.
  MessageLoop* creator_loop_;

  scoped_ptr<DataFetcher> data_fetcher_;
  std::map<DeviceData::Type, scoped_refptr<const DeviceData> >
    last_device_data_map_;
  std::set<DeviceData::Type> polling_data_types_;

  base::WeakPtr<ProviderImpl> provider_;
};

ProviderImpl::PollingThread::PollingThread(
    const char* name,
    base::WeakPtr<ProviderImpl> provider,
    MessageLoop* creator_loop)
    : base::Thread(name),
      creator_loop_(creator_loop),
      provider_(provider) {
}

ProviderImpl::PollingThread::~PollingThread() {
  Stop();
}

void ProviderImpl::PollingThread::DoAddPollingDataType(DeviceData::Type type) {
  DCHECK(MessageLoop::current() == message_loop());

  polling_data_types_.insert(type);
}

void ProviderImpl::PollingThread::Initialize(DataFetcherFactory factory,
                                             DeviceData::Type type) {
  DCHECK(MessageLoop::current() == message_loop());

  if (factory != NULL) {
    // Try to use factory to create a fetcher that can provide this type of
    // data. If factory creates a fetcher that provides this type of data,
    // start polling.
    scoped_ptr<DataFetcher> fetcher(factory());

    if (fetcher) {
      scoped_refptr<const DeviceData> device_data(fetcher->GetDeviceData(type));
      if (device_data != NULL) {
        // Pass ownership of fetcher to provider_.
        data_fetcher_.swap(fetcher);
        last_device_data_map_[type] = device_data;

        // Notify observers.
        ScheduleDoNotify(device_data, type);

        // Start polling.
        ScheduleDoPoll();
        return;
      }
    }
  }

  // When no device data can be provided.
  ScheduleDoNotify(NULL, type);
}

void ProviderImpl::PollingThread::ScheduleDoNotify(
    const scoped_refptr<const DeviceData>& device_data,
    DeviceData::Type device_data_type) {
  DCHECK(MessageLoop::current() == message_loop());

  creator_loop_->PostTask(FROM_HERE,
                          base::Bind(&ProviderImpl::DoNotify, provider_,
                                     device_data, device_data_type));
}

void ProviderImpl::PollingThread::DoPoll() {
  DCHECK(MessageLoop::current() == message_loop());

  // Poll the fetcher for each type of data.
  typedef std::set<DeviceData::Type>::const_iterator SetIterator;
  for (SetIterator i = polling_data_types_.begin();
       i != polling_data_types_.end(); ++i) {
    DeviceData::Type device_data_type = *i;
    scoped_refptr<const DeviceData> device_data(data_fetcher_->GetDeviceData(
        device_data_type));

    if (device_data == NULL) {
      LOG(ERROR) << "Failed to poll device data fetcher.";
      ScheduleDoNotify(NULL, device_data_type);
      continue;
    }

    const DeviceData* old_data = last_device_data_map_[device_data_type];
    if (old_data != NULL && !device_data->ShouldFireEvent(old_data))
      continue;

    // Update the last device data of this type and notify observers.
    last_device_data_map_[device_data_type] = device_data;
    ScheduleDoNotify(device_data, device_data_type);
  }

  ScheduleDoPoll();
}

void ProviderImpl::PollingThread::ScheduleDoPoll() {
  DCHECK(MessageLoop::current() == message_loop());

  message_loop()->PostDelayedTask(
      FROM_HERE,
      base::Bind(&PollingThread::DoPoll, base::Unretained(this)),
      SamplingInterval());
}

base::TimeDelta ProviderImpl::PollingThread::SamplingInterval() const {
  DCHECK(MessageLoop::current() == message_loop());
  DCHECK(data_fetcher_.get());

  // TODO(erg): There used to be unused code here, that called a default
  // implementation on the DataFetcherInterface that was never defined. I'm
  // removing unused methods from headers.
  return base::TimeDelta::FromMilliseconds(kDesiredSamplingIntervalMs);
}

ProviderImpl::ProviderImpl(DataFetcherFactory factory)
    : creator_loop_(MessageLoop::current()),
      factory_(factory),
      weak_factory_(this),
      polling_thread_(NULL) {
}

ProviderImpl::~ProviderImpl() {
  Stop();
}

void ProviderImpl::ScheduleDoAddPollingDataType(DeviceData::Type type) {
  DCHECK(MessageLoop::current() == creator_loop_);

  MessageLoop* polling_loop = polling_thread_->message_loop();
  polling_loop->PostTask(FROM_HERE,
                         base::Bind(&PollingThread::DoAddPollingDataType,
                                    base::Unretained(polling_thread_),
                                    type));
}

void ProviderImpl::AddObserver(Observer* observer) {
  DCHECK(MessageLoop::current() == creator_loop_);

  DeviceData::Type type = observer->device_data_type();

  observers_.insert(observer);
  if (observers_.size() == 1)
    Start(type);
  else {
    // Notify observer of most recent notification if one exists.
    const DeviceData *last_notification = last_notifications_map_[type];
    if (last_notification != NULL)
      observer->OnDeviceDataUpdate(last_notification, type);
  }

  ScheduleDoAddPollingDataType(type);
}

void ProviderImpl::RemoveObserver(Observer* observer) {
  DCHECK(MessageLoop::current() == creator_loop_);

  observers_.erase(observer);
  if (observers_.empty())
    Stop();
}

void ProviderImpl::Start(DeviceData::Type type) {
  DCHECK(MessageLoop::current() == creator_loop_);
  DCHECK(!polling_thread_);

  polling_thread_ = new PollingThread("Device data polling thread",
                                      weak_factory_.GetWeakPtr(),
                                      creator_loop_);
#if defined(OS_WIN)
  polling_thread_->init_com_with_mta(true);
#endif
  if (!polling_thread_->Start()) {
    LOG(ERROR) << "Failed to start device data polling thread";
    delete polling_thread_;
    polling_thread_ = NULL;
    return;
  }
  ScheduleInitializePollingThread(type);
}

void ProviderImpl::Stop() {
  DCHECK(MessageLoop::current() == creator_loop_);

  weak_factory_.InvalidateWeakPtrs();
  if (polling_thread_) {
    polling_thread_->StopSoon();
    bool posted = base::WorkerPool::PostTask(
        FROM_HERE,
        base::Bind(&DeleteThread, base::Unretained(polling_thread_)),
        true /* task is slow */);
    DCHECK(posted);
    polling_thread_ = NULL;
  }
}

void ProviderImpl::ScheduleInitializePollingThread(
    DeviceData::Type device_data_type) {
  DCHECK(MessageLoop::current() == creator_loop_);

  MessageLoop* polling_loop = polling_thread_->message_loop();
  polling_loop->PostTask(FROM_HERE,
                         base::Bind(&PollingThread::Initialize,
                                    base::Unretained(polling_thread_),
                                    factory_,
                                    device_data_type));
}

void ProviderImpl::DoNotify(const scoped_refptr<const DeviceData>& data,
    DeviceData::Type device_data_type) {
  DCHECK(MessageLoop::current() == creator_loop_);

  // Update last notification of this type.
  last_notifications_map_[device_data_type] = data;

  // Notify observers of this type of the new data.
  typedef std::set<Observer*>::const_iterator ConstIterator;
  for (ConstIterator i = observers_.begin(); i != observers_.end(); ++i) {
    if ((*i)->device_data_type() == device_data_type)
      (*i)->OnDeviceDataUpdate(data.get(), device_data_type);
  }

  if (data == NULL) {
    // Notify observers exactly once about failure to provide data.
    typedef std::set<Observer*>::iterator Iterator;
    Iterator i = observers_.begin();
    while (i != observers_.end()) {
      Iterator current = i++;
      if ((*current)->device_data_type() == device_data_type)
        observers_.erase(current);
    }

    if (observers_.empty())
      Stop();
  }
}


}  // namespace content