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.
5 // This file contains the class definitions for the CoreLocation data provider
6 // class and the accompanying Objective C wrapper class. This data provider
7 // is used to allow the CoreLocation wrapper to run on the UI thread, since
8 // CLLocationManager's start and stop updating methods must be called from a
9 // thread with an active NSRunLoop. Currently only the UI thread appears to
10 // fill that requirement.
12 #include "content/browser/geolocation/core_location_data_provider_mac.h"
14 #include "base/bind.h"
15 #include "base/logging.h"
16 #include "base/time.h"
17 #include "content/browser/geolocation/core_location_provider_mac.h"
18 #include "content/browser/geolocation/geolocation_provider.h"
20 using content::CoreLocationDataProviderMac;
21 using content::Geoposition;
23 // A few required declarations since the CoreLocation headers are not available
24 // with the Mac OS X 10.5 SDK.
25 // TODO(jorgevillatoro): Remove these declarations when we build against 10.6
27 // This idea was borrowed from wifi_data_provider_corewlan_mac.mm
28 typedef double CLLocationDegrees;
29 typedef double CLLocationAccuracy;
30 typedef double CLLocationSpeed;
31 typedef double CLLocationDirection;
32 typedef double CLLocationDistance;
34 CLLocationDegrees latitude;
35 CLLocationDegrees longitude;
36 } CLLocationCoordinate2D;
39 kCLErrorLocationUnknown = 0,
43 @interface CLLocationManager : NSObject
44 + (BOOL)locationServicesEnabled;
45 @property(assign) id delegate;
46 - (void)startUpdatingLocation;
47 - (void)stopUpdatingLocation;
50 @interface CLLocation : NSObject<NSCopying, NSCoding>
51 @property(readonly) CLLocationCoordinate2D coordinate;
52 @property(readonly) CLLocationDistance altitude;
53 @property(readonly) CLLocationAccuracy horizontalAccuracy;
54 @property(readonly) CLLocationAccuracy verticalAccuracy;
55 @property(readonly) CLLocationDirection course;
56 @property(readonly) CLLocationSpeed speed;
59 @protocol CLLocationManagerDelegate
60 - (void)locationManager:(CLLocationManager*)manager
61 didUpdateToLocation:(CLLocation*)newLocation
62 fromLocation:(CLLocation*)oldLocation;
63 - (void)locationManager:(CLLocationManager*)manager
64 didFailWithError:(NSError*)error;
67 // This wrapper class receives CLLocation objects from CoreLocation, converts
68 // them to Geoposition objects, and passes them on to the data provider class
69 // Note: This class has some specific threading requirements, inherited from
70 // CLLocationManager. The location manaager's start and stop updating
71 // methods must be called from a thread that has an active run loop (which
72 // seems to only be the UI thread)
73 @interface CoreLocationWrapperMac : NSObject<CLLocationManagerDelegate>
77 Class locationManagerClass_;
79 CoreLocationDataProviderMac* dataProvider_;
82 - (id)initWithDataProvider:(CoreLocationDataProviderMac*)dataProvider;
85 // Can be called from any thread since it does not require an NSRunLoop. However
86 // it is not threadsafe to receive concurrent calls until after a first
87 // successful call (to avoid |bundle_| being double initialized)
88 - (BOOL)locationDataAvailable;
90 // These should always be called from BrowserThread::UI
91 - (void)startLocation;
94 // These should only be called by CLLocationManager
95 - (void)locationManager:(CLLocationManager*)manager
96 didUpdateToLocation:(CLLocation*)newLocation
97 fromLocation:(CLLocation*)oldLocation;
98 - (void)locationManager:(CLLocationManager*)manager
99 didFailWithError:(NSError*)error;
100 - (BOOL)loadCoreLocationBundle;
104 @implementation CoreLocationWrapperMac
106 - (id)initWithDataProvider:(CoreLocationDataProviderMac*)dataProvider {
107 DCHECK(dataProvider);
108 dataProvider_ = dataProvider;
114 [locationManager_ release];
115 [locationManagerClass_ release];
120 // Load the bundle and check to see if location services are enabled
121 // but don't do anything else
122 - (BOOL)locationDataAvailable {
123 return ([self loadCoreLocationBundle] &&
124 [locationManagerClass_ locationServicesEnabled]);
127 - (void)startLocation {
128 if ([self locationDataAvailable]) {
129 if (!locationManager_) {
130 locationManager_ = [[locationManagerClass_ alloc] init];
131 [locationManager_ setDelegate:self];
133 [locationManager_ startUpdatingLocation];
137 - (void)stopLocation {
138 [locationManager_ stopUpdatingLocation];
141 - (void)locationManager:(CLLocationManager*)manager
142 didUpdateToLocation:(CLLocation*)newLocation
143 fromLocation:(CLLocation*)oldLocation {
144 Geoposition position;
145 position.latitude = [newLocation coordinate].latitude;
146 position.longitude = [newLocation coordinate].longitude;
147 position.altitude = [newLocation altitude];
148 position.accuracy = [newLocation horizontalAccuracy];
149 position.altitude_accuracy = [newLocation verticalAccuracy];
150 position.speed = [newLocation speed];
151 position.heading = [newLocation course];
152 position.timestamp = base::Time::Now();
153 position.error_code = Geoposition::ERROR_CODE_NONE;
154 dataProvider_->UpdatePosition(&position);
157 - (void)locationManager:(CLLocationManager*)manager
158 didFailWithError:(NSError*)error {
159 Geoposition position;
160 switch ([error code]) {
161 case kCLErrorLocationUnknown:
162 position.error_code = Geoposition::ERROR_CODE_POSITION_UNAVAILABLE;
165 position.error_code = Geoposition::ERROR_CODE_PERMISSION_DENIED;
168 NOTREACHED() << "Unknown CoreLocation error: " << [error code];
171 dataProvider_->UpdatePosition(&position);
174 - (BOOL)loadCoreLocationBundle {
176 bundle_ = [[NSBundle alloc]
177 initWithPath:@"/System/Library/Frameworks/CoreLocation.framework"];
179 DLOG(WARNING) << "Couldn't load CoreLocation Framework";
183 locationManagerClass_ = [bundle_ classNamed:@"CLLocationManager"];
193 CoreLocationDataProviderMac::CoreLocationDataProviderMac() {
194 if (MessageLoop::current() !=
195 GeolocationProvider::GetInstance()->message_loop()) {
196 NOTREACHED() << "CoreLocation data provider must be created on "
197 "the Geolocation thread.";
200 wrapper_.reset([[CoreLocationWrapperMac alloc] initWithDataProvider:this]);
203 CoreLocationDataProviderMac::~CoreLocationDataProviderMac() {
206 // Returns true if the CoreLocation wrapper can load the framework and
207 // location services are enabled. The pointer argument will only be accessed
208 // in the origin thread.
209 bool CoreLocationDataProviderMac::
210 StartUpdating(CoreLocationProviderMac* provider) {
212 DCHECK(!provider_) << "StartUpdating called twice";
213 if (![wrapper_ locationDataAvailable]) return false;
214 provider_ = provider;
215 BrowserThread::PostTask(
216 BrowserThread::UI, FROM_HERE,
217 base::Bind(&CoreLocationDataProviderMac::StartUpdatingTask, this));
221 // Clears provider_ so that any leftover messages from CoreLocation get ignored
222 void CoreLocationDataProviderMac::StopUpdating() {
224 BrowserThread::PostTask(
225 BrowserThread::UI, FROM_HERE,
226 base::Bind(&CoreLocationDataProviderMac::StopUpdatingTask, this));
229 void CoreLocationDataProviderMac::UpdatePosition(Geoposition *position) {
230 GeolocationProvider::GetInstance()->message_loop()->PostTask(
232 base::Bind(&CoreLocationDataProviderMac::PositionUpdated, this,
236 // Runs in BrowserThread::UI
237 void CoreLocationDataProviderMac::StartUpdatingTask() {
238 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
239 [wrapper_ startLocation];
242 // Runs in BrowserThread::UI
243 void CoreLocationDataProviderMac::StopUpdatingTask() {
244 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
245 [wrapper_ stopLocation];
248 void CoreLocationDataProviderMac::PositionUpdated(Geoposition position) {
249 DCHECK(MessageLoop::current() ==
250 GeolocationProvider::GetInstance()->message_loop());
252 provider_->SetPosition(&position);
255 } // namespace content