From f7de6cc231c1f88372091868f717637dd3628444 Mon Sep 17 00:00:00 2001 From: Garvan Keeley Date: Tue, 27 Jan 2015 08:26:00 +0100 Subject: [PATCH] Bug 1121265 - Fallback to MLS Provider, r=dougt On CoreLocation error, fallback to MLS network provider, which will report GeoIP, or error. --- dom/system/mac/CoreLocationLocationProvider.h | 17 +++ .../mac/CoreLocationLocationProvider.mm | 144 +++++++++++++++--- 2 files changed, 140 insertions(+), 21 deletions(-) diff --git a/dom/system/mac/CoreLocationLocationProvider.h b/dom/system/mac/CoreLocationLocationProvider.h index 65f8aa16b8c2..33c6780c057e 100644 --- a/dom/system/mac/CoreLocationLocationProvider.h +++ b/dom/system/mac/CoreLocationLocationProvider.h @@ -32,9 +32,26 @@ public: CoreLocationLocationProvider(); void NotifyError(uint16_t aErrorCode); void Update(nsIDOMGeoPosition* aSomewhere); + void CreateMLSFallbackProvider(); + void CancelMLSFallbackProvider(); + private: virtual ~CoreLocationLocationProvider() {}; CoreLocationObjects* mCLObjects; nsCOMPtr mCallback; + nsCOMPtr mMLSFallbackProvider; + + class MLSUpdate : public nsIGeolocationUpdate + { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIGEOLOCATIONUPDATE + + explicit MLSUpdate(CoreLocationLocationProvider& parentProvider); + + private: + CoreLocationLocationProvider& mParentLocationProvider; + virtual ~MLSUpdate() {} + }; }; diff --git a/dom/system/mac/CoreLocationLocationProvider.mm b/dom/system/mac/CoreLocationLocationProvider.mm index cff2571648c1..9032e3bdb584 100644 --- a/dom/system/mac/CoreLocationLocationProvider.mm +++ b/dom/system/mac/CoreLocationLocationProvider.mm @@ -31,19 +31,14 @@ static const CLLocationAccuracy kDEFAULT_ACCURACY = kCLLocationAccuracyNearestTe @interface LocationDelegate : NSObject { CoreLocationLocationProvider* mProvider; + NSTimer* mHandoffTimer; } - (id)init:(CoreLocationLocationProvider*)aProvider; - (void)locationManager:(CLLocationManager*)aManager didFailWithError:(NSError *)aError; +- (void)locationManager:(CLLocationManager*)aManager didUpdateLocations:(NSArray*)locations; -/* XXX (ggp) didUpdateToLocation is supposedly deprecated in favor of - * locationManager:didUpdateLocations, which is undocumented and didn't seem to - * work for me. This should be changed in the future, though. - */ -- (void)locationManager:(CLLocationManager*)aManager - didUpdateToLocation:(CLLocation *)aNewLocation - fromLocation:(CLLocation *)aOldLocation; @end @implementation LocationDelegate @@ -56,6 +51,24 @@ static const CLLocationAccuracy kDEFAULT_ACCURACY = kCLLocationAccuracyNearestTe return self; } +- (void)shutdownHandoffTimer +{ + if (!mHandoffTimer) { + return; + } + + [mHandoffTimer invalidate]; + mHandoffTimer = nil; +} + +- (void)handoffToGeoIPProvider +{ + // Single-shot timers are invalid once executed and are released by the run loop + mHandoffTimer = nil; + + mProvider->CreateMLSFallbackProvider(); +} + - (void)locationManager:(CLLocationManager*)aManager didFailWithError:(NSError *)aError { @@ -69,32 +82,87 @@ static const CLLocationAccuracy kDEFAULT_ACCURACY = kCLLocationAccuracyNearestTe console->LogStringMessage(NS_ConvertUTF8toUTF16([message UTF8String]).get()); - uint16_t err = nsIDOMGeoPositionError::POSITION_UNAVAILABLE; if ([aError code] == kCLErrorDenied) { - err = nsIDOMGeoPositionError::PERMISSION_DENIED; + mProvider->NotifyError(nsIDOMGeoPositionError::PERMISSION_DENIED); + return; } - mProvider->NotifyError(err); + if (!mHandoffTimer) { + // The CL provider does not fallback to GeoIP, so use NetworkGeolocationProvider for this. + // The concept here is: on error, hand off geolocation to MLS, which will then report + // back a location or error. We can't call this with no delay however, as this method + // is called with an error code of 0 in both failed geolocation cases, and also when + // geolocation is not immediately available. + // The 2 sec delay is arbitrarily large enough that CL has a reasonable head start and + // if it is likely to succeed, it should complete before the MLS provider. + // Take note that in locationManager:didUpdateLocations: the handoff to MLS is stopped. + mHandoffTimer = [NSTimer scheduledTimerWithTimeInterval:2.0 + target:self + selector:@selector(handoffToGeoIPProvider) + userInfo:nil + repeats:NO]; + } } -- (void)locationManager:(CLLocationManager*)aManager - didUpdateToLocation:(CLLocation *)aNewLocation - fromLocation:(CLLocation *)aOldLocation +- (void)locationManager:(CLLocationManager*)aManager didUpdateLocations:(NSArray*)aLocations { + if (aLocations.count < 1) { + return; + } + + [self shutdownHandoffTimer]; + mProvider->CancelMLSFallbackProvider(); + + CLLocation* location = [aLocations objectAtIndex:0]; + nsCOMPtr geoPosition = - new nsGeoPosition(aNewLocation.coordinate.latitude, - aNewLocation.coordinate.longitude, - aNewLocation.altitude, - aNewLocation.horizontalAccuracy, - aNewLocation.verticalAccuracy, - aNewLocation.course, - aNewLocation.speed, + new nsGeoPosition(location.coordinate.latitude, + location.coordinate.longitude, + location.altitude, + location.horizontalAccuracy, + location.verticalAccuracy, + location.course, + location.speed, PR_Now()); mProvider->Update(geoPosition); } @end +NS_IMPL_ISUPPORTS(CoreLocationLocationProvider::MLSUpdate, nsIGeolocationUpdate); + +CoreLocationLocationProvider::MLSUpdate::MLSUpdate(CoreLocationLocationProvider& parentProvider) + : mParentLocationProvider(parentProvider) +{ +} + +NS_IMETHODIMP +CoreLocationLocationProvider::MLSUpdate::Update(nsIDOMGeoPosition *position) +{ + nsCOMPtr coords; + position->GetCoords(getter_AddRefs(coords)); + if (!coords) { + return NS_ERROR_FAILURE; + } + + mParentLocationProvider.Update(position); + return NS_OK; +} + +NS_IMETHODIMP +CoreLocationLocationProvider::MLSUpdate::LocationUpdatePending() +{ + return NS_OK; +} + +NS_IMETHODIMP +CoreLocationLocationProvider::MLSUpdate::NotifyError(uint16_t error) +{ + mParentLocationProvider.NotifyError(error); + return NS_OK; +} + + class CoreLocationObjects { public: NS_METHOD Init(CoreLocationLocationProvider* aProvider) { @@ -127,7 +195,7 @@ public: NS_IMPL_ISUPPORTS(CoreLocationLocationProvider, nsIGeolocationProvider) CoreLocationLocationProvider::CoreLocationLocationProvider() - : mCLObjects(nullptr) + : mCLObjects(nullptr), mMLSFallbackProvider(nullptr) { } @@ -163,10 +231,17 @@ CoreLocationLocationProvider::Shutdown() { NS_ENSURE_STATE(mCLObjects); + [mCLObjects->mLocationDelegate shutdownHandoffTimer]; [mCLObjects->mLocationManager stopUpdatingLocation]; delete mCLObjects; mCLObjects = nullptr; + + if (mMLSFallbackProvider) { + mMLSFallbackProvider->Shutdown(); + mMLSFallbackProvider = nullptr; + } + return NS_OK; } @@ -194,3 +269,30 @@ CoreLocationLocationProvider::NotifyError(uint16_t aErrorCode) { mCallback->NotifyError(aErrorCode); } + +void +CoreLocationLocationProvider::CreateMLSFallbackProvider() +{ + if (mMLSFallbackProvider) { + return; + } + + mMLSFallbackProvider = do_CreateInstance("@mozilla.org/geolocation/mls-provider;1"); + if (mMLSFallbackProvider) { + nsresult rv = mMLSFallbackProvider->Startup(); + if (NS_SUCCEEDED(rv)) { + mMLSFallbackProvider->Watch(new CoreLocationLocationProvider::MLSUpdate(*this)); + } + } +} + +void +CoreLocationLocationProvider::CancelMLSFallbackProvider() +{ + if (!mMLSFallbackProvider) { + return; + } + + mMLSFallbackProvider->Shutdown(); + mMLSFallbackProvider = nullptr; +}