// 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::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 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