blob: 11df9529e4d34ce6646d507d48605ccdb34935ab [file] [log] [blame]
// 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.
#include "content/browser/geofencing/geofencing_service.h"
#include "base/memory/singleton.h"
#include "base/message_loop/message_loop.h"
#include "content/browser/geofencing/geofencing_provider.h"
#include "content/browser/geofencing/geofencing_registration_delegate.h"
#include "content/public/browser/browser_thread.h"
#include "third_party/WebKit/public/platform/WebCircularGeofencingRegion.h"
namespace content {
namespace {
void RunSoon(const base::Closure& callback) {
if (!callback.is_null())
base::MessageLoop::current()->PostTask(FROM_HERE, callback);
}
} // namespace
struct GeofencingServiceImpl::Registration {
Registration();
Registration(const blink::WebCircularGeofencingRegion& region,
int64 geofencing_registration_id,
GeofencingRegistrationDelegate* delegate);
blink::WebCircularGeofencingRegion region;
int64 geofencing_registration_id;
GeofencingRegistrationDelegate* delegate;
enum RegistrationState {
// In progress of being registered with provider.
STATE_REGISTERING,
// Currently registered with provider.
STATE_REGISTERED,
// In progress of being registered with provider, but should be unregistered
// and deleted.
STATE_SHOULD_UNREGISTER_AND_DELETE,
// Not currently registered with provider, but still an active registration.
STATE_UNREGISTERED
};
RegistrationState state;
};
GeofencingServiceImpl::Registration::Registration()
: geofencing_registration_id(-1),
delegate(nullptr),
state(STATE_UNREGISTERED) {
}
GeofencingServiceImpl::Registration::Registration(
const blink::WebCircularGeofencingRegion& region,
int64 geofencing_registration_id,
GeofencingRegistrationDelegate* delegate)
: region(region),
geofencing_registration_id(geofencing_registration_id),
delegate(delegate),
state(STATE_REGISTERING) {
}
GeofencingServiceImpl::GeofencingServiceImpl() : next_registration_id_(0) {
}
GeofencingServiceImpl::~GeofencingServiceImpl() {
}
GeofencingServiceImpl* GeofencingServiceImpl::GetInstance() {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
return Singleton<GeofencingServiceImpl>::get();
}
bool GeofencingServiceImpl::IsServiceAvailable() {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
return EnsureProvider();
}
int64 GeofencingServiceImpl::RegisterRegion(
const blink::WebCircularGeofencingRegion& region,
GeofencingRegistrationDelegate* delegate) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
int64 geofencing_registration_id = GetNextId();
registrations_[geofencing_registration_id] =
Registration(region, geofencing_registration_id, delegate);
if (!EnsureProvider()) {
RunSoon(
base::Bind(&GeofencingServiceImpl::NotifyRegistrationFinished,
base::Unretained(this),
geofencing_registration_id,
GEOFENCING_STATUS_OPERATION_FAILED_SERVICE_NOT_AVAILABLE));
return geofencing_registration_id;
}
provider_->RegisterRegion(
geofencing_registration_id,
region,
base::Bind(&GeofencingServiceImpl::NotifyRegistrationFinished,
base::Unretained(this),
geofencing_registration_id));
return geofencing_registration_id;
}
void GeofencingServiceImpl::UnregisterRegion(int64 geofencing_registration_id) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
RegistrationsMap::iterator registration_iterator =
registrations_.find(geofencing_registration_id);
DCHECK(registration_iterator != registrations_.end());
if (!EnsureProvider())
return;
switch (registration_iterator->second.state) {
case Registration::STATE_REGISTERED:
provider_->UnregisterRegion(geofencing_registration_id);
// fallthru
case Registration::STATE_UNREGISTERED:
registrations_.erase(registration_iterator);
break;
case Registration::STATE_REGISTERING:
// Update state, NotifyRegistrationFinished will take care of actually
// unregistering.
registration_iterator->second.state =
Registration::STATE_SHOULD_UNREGISTER_AND_DELETE;
break;
case Registration::STATE_SHOULD_UNREGISTER_AND_DELETE:
// Should not happen.
NOTREACHED();
break;
}
}
void GeofencingServiceImpl::SetProviderForTesting(
scoped_ptr<GeofencingProvider> provider) {
DCHECK(!provider_.get());
provider_ = provider.Pass();
}
int GeofencingServiceImpl::RegistrationCountForTesting() {
return registrations_.size();
}
bool GeofencingServiceImpl::EnsureProvider() {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (!provider_) {
// TODO(mek): Create platform specific provider.
return false;
}
return true;
}
int64 GeofencingServiceImpl::GetNextId() {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
return next_registration_id_++;
}
void GeofencingServiceImpl::NotifyRegistrationFinished(
int64 geofencing_registration_id,
GeofencingStatus status) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
RegistrationsMap::iterator registration_iterator =
registrations_.find(geofencing_registration_id);
DCHECK(registration_iterator != registrations_.end());
DCHECK(registration_iterator->second.state ==
Registration::STATE_REGISTERING ||
registration_iterator->second.state ==
Registration::STATE_SHOULD_UNREGISTER_AND_DELETE);
if (registration_iterator->second.state ==
Registration::STATE_SHOULD_UNREGISTER_AND_DELETE) {
// Don't call delegate, but unregister with provider if registration was
// succesfull.
if (status == GEOFENCING_STATUS_OK) {
provider_->UnregisterRegion(geofencing_registration_id);
}
registrations_.erase(registration_iterator);
return;
}
// Normal case, mark as registered and call delegate.
registration_iterator->second.state = Registration::STATE_REGISTERED;
registration_iterator->second.delegate->RegistrationFinished(
geofencing_registration_id, status);
if (status != GEOFENCING_STATUS_OK) {
// Registration failed, remove from our book-keeping.
registrations_.erase(registration_iterator);
}
}
} // namespace content