Bug 1121265 - Fallback to MLS Provider. r=dougt

On CoreLocation error, fallback to MLS network provider, which
will report GeoIP, or error.
This commit is contained in:
Garvan Keeley 2015-01-26 10:37:00 +01:00
Родитель 8e5e324b89
Коммит ecf60f1a1f
2 изменённых файлов: 140 добавлений и 21 удалений

Просмотреть файл

@ -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<nsIGeolocationUpdate> mCallback;
nsCOMPtr<nsIGeolocationProvider> mMLSFallbackProvider;
class MLSUpdate : public nsIGeolocationUpdate
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIGEOLOCATIONUPDATE
MLSUpdate(CoreLocationLocationProvider& parentProvider);
private:
CoreLocationLocationProvider& mParentLocationProvider;
virtual ~MLSUpdate() {}
};
};

Просмотреть файл

@ -31,19 +31,14 @@ static const CLLocationAccuracy kDEFAULT_ACCURACY = kCLLocationAccuracyNearestTe
@interface LocationDelegate : NSObject <CLLocationManagerDelegate>
{
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<nsIDOMGeoPosition> 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<nsIDOMGeoPositionCoords> 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;
}