[geolocation] Add a Core Location backend behind a flag
This CL Adds a new Location Provider that uses the Core Location
API to determine Location.
Bug: 1035290
Change-Id: I7cff4285ba10163b7fcc6657c1c8705872526606
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2152105
Commit-Queue: James Hollyer <[email protected]>
Reviewed-by: Ovidio de Jesús Ruiz-Henríquez <[email protected]>
Reviewed-by: Reilly Grant <[email protected]>
Cr-Commit-Position: refs/heads/master@{#765416}
diff --git a/chrome/browser/about_flags.cc b/chrome/browser/about_flags.cc
index e16b34a..3529ea26 100644
--- a/chrome/browser/about_flags.cc
+++ b/chrome/browser/about_flags.cc
@@ -5134,6 +5134,13 @@
FEATURE_VALUE_TYPE(features::kWinrtGeolocationImplementation)},
#endif
+#if defined(OS_MACOSX)
+ {"enable-core-location-implementation",
+ flag_descriptions::kMacCoreLocationImplementationName,
+ flag_descriptions::kMacCoreLocationImplementationDescription, kOsMac,
+ FEATURE_VALUE_TYPE(features::kMacCoreLocationImplementation)},
+#endif
+
#if defined(OS_CHROMEOS)
{"exo-pointer-lock", flag_descriptions::kExoPointerLockName,
flag_descriptions::kExoPointerLockDescription, kOsCrOS,
diff --git a/chrome/browser/flag-metadata.json b/chrome/browser/flag-metadata.json
index 8964b2c..6998c69 100644
--- a/chrome/browser/flag-metadata.json
+++ b/chrome/browser/flag-metadata.json
@@ -1288,6 +1288,11 @@
"expiry_milestone": 85
},
{
+ "name": "enable-core-location-implementation",
+ "owners": [ "[email protected]" ],
+ "expiry_milestone": 86
+ },
+ {
"name": "enable-credit-card-assist",
"owners": [ "ftirelo", "gogerald" ],
"expiry_milestone": 76
diff --git a/chrome/browser/flag_descriptions.cc b/chrome/browser/flag_descriptions.cc
index 12a7413..52369712 100644
--- a/chrome/browser/flag_descriptions.cc
+++ b/chrome/browser/flag_descriptions.cc
@@ -639,6 +639,11 @@
"This option enables a post-quantum (i.e. resistent to quantum computers) "
"key exchange algorithm in TLS (CECPQ2).";
+const char kMacCoreLocationImplementationName[] =
+ "Core Location Implementation";
+const char kMacCoreLocationImplementationDescription[] =
+ "Enables usage of the Core Location APIs on macOS for geolocation";
+
const char kWinrtGeolocationImplementationName[] =
"WinRT Geolocation Implementation";
const char kWinrtGeolocationImplementationDescription[] =
diff --git a/chrome/browser/flag_descriptions.h b/chrome/browser/flag_descriptions.h
index 8673531..f587821 100644
--- a/chrome/browser/flag_descriptions.h
+++ b/chrome/browser/flag_descriptions.h
@@ -387,6 +387,9 @@
extern const char kPostQuantumCECPQ2Name[];
extern const char kPostQuantumCECPQ2Description[];
+extern const char kMacCoreLocationImplementationName[];
+extern const char kMacCoreLocationImplementationDescription[];
+
extern const char kWinrtGeolocationImplementationName[];
extern const char kWinrtGeolocationImplementationDescription[];
diff --git a/services/device/BUILD.gn b/services/device/BUILD.gn
index 8dd1125..d478ddab 100644
--- a/services/device/BUILD.gn
+++ b/services/device/BUILD.gn
@@ -133,6 +133,7 @@
"generic_sensor/platform_sensor_util_unittest.cc",
"generic_sensor/relative_orientation_euler_angles_fusion_algorithm_using_accelerometer_and_gyroscope_unittest.cc",
"generic_sensor/relative_orientation_euler_angles_fusion_algorithm_using_accelerometer_unittest.cc",
+ "geolocation/core_location_provider_unittest.mm",
"geolocation/geolocation_provider_impl_unittest.cc",
"geolocation/geolocation_service_unittest.cc",
"geolocation/location_arbitrator_unittest.cc",
diff --git a/services/device/geolocation/BUILD.gn b/services/device/geolocation/BUILD.gn
index f1a8863..45e6296 100644
--- a/services/device/geolocation/BUILD.gn
+++ b/services/device/geolocation/BUILD.gn
@@ -119,7 +119,14 @@
libs = [
"CoreWLAN.framework",
"Foundation.framework",
+ "CoreLocation.framework",
]
+ sources += [
+ "core_location_provider.h",
+ "core_location_provider.mm",
+ ]
+
+ deps += [ "//services/device/public/cpp:device_features" ]
}
if (is_win) {
diff --git a/services/device/geolocation/DEPS b/services/device/geolocation/DEPS
index 9175131..2109181 100644
--- a/services/device/geolocation/DEPS
+++ b/services/device/geolocation/DEPS
@@ -5,6 +5,7 @@
"+net/base",
"+net/traffic_annotation",
"+services/network/public/cpp",
+ "+services/device/public/cpp/device_features.h",
"+services/network/test",
"+third_party/cros_system_api/dbus",
]
diff --git a/services/device/geolocation/core_location_provider.h b/services/device/geolocation/core_location_provider.h
new file mode 100644
index 0000000..d6fd2bfc
--- /dev/null
+++ b/services/device/geolocation/core_location_provider.h
@@ -0,0 +1,45 @@
+// Copyright 2020 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.
+
+#ifndef SERVICES_DEVICE_GEOLOCATION_CORE_LOCATION_PROVIDER_H_
+#define SERVICES_DEVICE_GEOLOCATION_CORE_LOCATION_PROVIDER_H_
+
+#import <CoreLocation/CoreLocation.h>
+
+#include "base/mac/scoped_nsobject.h"
+#include "services/device/public/cpp/geolocation/location_provider.h"
+#include "services/device/public/mojom/geoposition.mojom.h"
+
+@class LocationDelegate;
+
+namespace device {
+
+// Location provider for macOS using the platform's Core Location API.
+class CoreLocationProvider : public LocationProvider {
+ public:
+ CoreLocationProvider();
+ ~CoreLocationProvider() override;
+
+ // LocationProvider implementation.
+ void SetUpdateCallback(
+ const LocationProviderUpdateCallback& callback) override;
+ void StartProvider(bool high_accuracy) override;
+ void StopProvider() override;
+ const mojom::Geoposition& GetPosition() override;
+ void OnPermissionGranted() override;
+
+ void DidUpdatePosition(CLLocation* location);
+ void SetManagerForTesting(CLLocationManager* location_manager);
+
+ private:
+ base::scoped_nsobject<CLLocationManager> location_manager_;
+ base::scoped_nsobject<LocationDelegate> delegate_;
+ mojom::Geoposition last_position_;
+ LocationProviderUpdateCallback callback_;
+ base::WeakPtrFactory<CoreLocationProvider> weak_ptr_factory_{this};
+};
+
+} // namespace device
+
+#endif // SERVICES_DEVICE_GEOLOCATION_LOCATION_PROVIDER_MAC_H_
diff --git a/services/device/geolocation/core_location_provider.mm b/services/device/geolocation/core_location_provider.mm
new file mode 100644
index 0000000..a2065e2
--- /dev/null
+++ b/services/device/geolocation/core_location_provider.mm
@@ -0,0 +1,120 @@
+// Copyright 2020 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 "services/device/geolocation/core_location_provider.h"
+#include "services/device/public/cpp/device_features.h"
+
+@interface LocationDelegate : NSObject <CLLocationManagerDelegate> {
+ base::WeakPtr<device::CoreLocationProvider> provider_;
+}
+
+- (id)initWithProvider:(base::WeakPtr<device::CoreLocationProvider>)provider;
+
+- (void)locationManager:(CLLocationManager*)manager
+ didUpdateLocations:(NSArray*)locations;
+- (void)locationManager:(CLLocationManager*)manager
+ didChangeAuthorizationStatus:(CLAuthorizationStatus)status;
+
+@end
+
+@implementation LocationDelegate
+
+- (id)initWithProvider:(base::WeakPtr<device::CoreLocationProvider>)provider {
+ self = [super init];
+ provider_ = provider;
+ return self;
+}
+
+- (void)locationManager:(CLLocationManager*)manager
+ didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
+ if (!provider_)
+ return;
+ if (@available(macOS 10.12.0, *)) {
+ if (status == kCLAuthorizationStatusAuthorizedAlways)
+ provider_->OnPermissionGranted();
+ } else {
+ if (status == kCLAuthorizationStatusAuthorized)
+ provider_->OnPermissionGranted();
+ }
+}
+
+- (void)locationManager:(CLLocationManager*)manager
+ didUpdateLocations:(NSArray*)locations {
+ if (provider_)
+ provider_->DidUpdatePosition([locations lastObject]);
+}
+
+@end
+
+namespace device {
+
+CoreLocationProvider::CoreLocationProvider() {
+ location_manager_.reset([[CLLocationManager alloc] init]);
+ delegate_.reset([[LocationDelegate alloc]
+ initWithProvider:weak_ptr_factory_.GetWeakPtr()]);
+ location_manager_.get().delegate = delegate_;
+}
+
+CoreLocationProvider::~CoreLocationProvider() {
+ StopProvider();
+}
+
+void CoreLocationProvider::SetUpdateCallback(
+ const LocationProviderUpdateCallback& callback) {
+ callback_ = callback;
+}
+
+void CoreLocationProvider::StartProvider(bool high_accuracy) {
+ if (high_accuracy) {
+ location_manager_.get().desiredAccuracy = kCLLocationAccuracyBest;
+ } else {
+ // Using kCLLocationAccuracyHundredMeters for consistency with Android.
+ location_manager_.get().desiredAccuracy = kCLLocationAccuracyHundredMeters;
+ }
+
+ [location_manager_ startUpdatingLocation];
+}
+
+void CoreLocationProvider::StopProvider() {
+ [location_manager_ stopUpdatingLocation];
+}
+
+const mojom::Geoposition& CoreLocationProvider::GetPosition() {
+ return last_position_;
+}
+
+void CoreLocationProvider::OnPermissionGranted() {
+ // Nothing to do here.
+}
+
+void CoreLocationProvider::DidUpdatePosition(CLLocation* location) {
+ // The error values in CLLocation correlate exactly to our error values.
+ last_position_.latitude = location.coordinate.latitude;
+ last_position_.longitude = location.coordinate.longitude;
+ last_position_.timestamp =
+ base::Time::FromDoubleT(location.timestamp.timeIntervalSince1970);
+ last_position_.altitude = location.altitude;
+ last_position_.accuracy = location.horizontalAccuracy;
+ last_position_.altitude_accuracy = location.verticalAccuracy;
+ last_position_.speed = location.speed;
+ last_position_.heading = location.course;
+
+ callback_.Run(this, last_position_);
+}
+
+void CoreLocationProvider::SetManagerForTesting(
+ CLLocationManager* location_manager) {
+ location_manager_.reset(location_manager);
+ location_manager_.get().delegate = delegate_;
+}
+
+// static
+std::unique_ptr<LocationProvider> NewSystemLocationProvider() {
+ if (!base::FeatureList::IsEnabled(features::kMacCoreLocationImplementation))
+ return nullptr;
+
+ return std::make_unique<CoreLocationProvider>();
+}
+
+} // namespace device
diff --git a/services/device/geolocation/core_location_provider_unittest.mm b/services/device/geolocation/core_location_provider_unittest.mm
new file mode 100644
index 0000000..4f3d4ce
--- /dev/null
+++ b/services/device/geolocation/core_location_provider_unittest.mm
@@ -0,0 +1,153 @@
+// Copyright 2020 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 "services/device/geolocation/core_location_provider.h"
+
+#include "base/run_loop.h"
+#include "base/test/bind_test_util.h"
+#include "base/test/task_environment.h"
+#include "services/device/public/cpp/geolocation/geoposition.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+@interface FakeCLLocationManager : NSObject {
+ CLLocationAccuracy _desiredAccuracy;
+ id<CLLocationManagerDelegate> _delegate;
+ bool _updating;
+}
+@property(assign, nonatomic) CLLocationAccuracy desiredAccuracy;
+@property(weak, nonatomic) id<CLLocationManagerDelegate> delegate;
+// CLLocationManager implementation.
+- (void)stopUpdatingLocation;
+- (void)startUpdatingLocation;
+- (bool)updating;
+
+// Utility functions.
+- (void)fakeUpdatePosition:(CLLocation*)test_location;
+@end
+
+@implementation FakeCLLocationManager
+@synthesize desiredAccuracy = _desiredAccuracy;
+@synthesize delegate = _delegate;
+- (instancetype)init {
+ self = [super init];
+ _updating = false;
+ return self;
+}
+
+- (void)stopUpdatingLocation {
+ _updating = false;
+}
+
+- (void)startUpdatingLocation {
+ _updating = true;
+}
+
+- (bool)updating {
+ return _updating;
+}
+
+- (void)fakeUpdatePosition:(CLLocation*)testLocation {
+ [_delegate locationManager:(id)self didUpdateLocations:@[ testLocation ]];
+}
+
+@end
+
+namespace device {
+
+class CoreLocationProviderTest : public testing::Test {
+ public:
+ std::unique_ptr<CoreLocationProvider> provider_;
+
+ protected:
+ CoreLocationProviderTest() {}
+
+ void InitializeProvider() {
+ fake_location_manager_ = [[FakeCLLocationManager alloc] init];
+ provider_ = std::make_unique<CoreLocationProvider>();
+ provider_->SetManagerForTesting((id)fake_location_manager_);
+ }
+
+ bool IsUpdating() { return [fake_location_manager_ updating]; }
+
+ // updates the position synchronously
+ void FakeUpdatePosition(CLLocation* location) {
+ [fake_location_manager_ fakeUpdatePosition:location];
+ }
+
+ const mojom::Geoposition& GetLatestPosition() {
+ return provider_->GetPosition();
+ }
+
+ base::test::TaskEnvironment task_environment_;
+ const LocationProvider::LocationProviderUpdateCallback callback_;
+ FakeCLLocationManager* fake_location_manager_;
+};
+
+TEST_F(CoreLocationProviderTest, CreateDestroy) {
+ InitializeProvider();
+ EXPECT_TRUE(provider_);
+ provider_.reset();
+}
+
+TEST_F(CoreLocationProviderTest, StartAndStopUpdating) {
+ InitializeProvider();
+ provider_->StartProvider(/*high_accuracy=*/true);
+ EXPECT_TRUE(IsUpdating());
+ EXPECT_EQ([fake_location_manager_ desiredAccuracy], kCLLocationAccuracyBest);
+ provider_->StopProvider();
+ EXPECT_FALSE(IsUpdating());
+ provider_.reset();
+}
+
+TEST_F(CoreLocationProviderTest, GetPositionUpdates) {
+ InitializeProvider();
+ provider_->StartProvider(/*high_accuracy=*/true);
+ EXPECT_TRUE(IsUpdating());
+ EXPECT_EQ([fake_location_manager_ desiredAccuracy], kCLLocationAccuracyBest);
+
+ // test info
+ double latitude = 147.147;
+ double longitude = 101.101;
+ double altitude = 417.417;
+ double accuracy = 10.5;
+ double altitude_accuracy = 15.5;
+ NSDate* mac_timestamp = [NSDate date];
+
+ CLLocationCoordinate2D coors;
+ coors.latitude = latitude;
+ coors.longitude = longitude;
+ CLLocation* test_mac_location =
+ [[CLLocation alloc] initWithCoordinate:coors
+ altitude:altitude
+ horizontalAccuracy:accuracy
+ verticalAccuracy:altitude_accuracy
+ timestamp:mac_timestamp];
+ mojom::Geoposition test_position;
+ test_position.latitude = latitude;
+ test_position.longitude = longitude;
+ test_position.altitude = altitude;
+ test_position.accuracy = accuracy;
+ test_position.altitude_accuracy = altitude_accuracy;
+ test_position.timestamp =
+ base::Time::FromDoubleT(mac_timestamp.timeIntervalSince1970);
+
+ bool update_callback_called = false;
+ provider_->SetUpdateCallback(
+ base::BindLambdaForTesting([&](const LocationProvider* provider,
+ const mojom::Geoposition& position) {
+ update_callback_called = true;
+ EXPECT_TRUE(test_position.Equals(position));
+ }));
+
+ FakeUpdatePosition(test_mac_location);
+
+ EXPECT_TRUE(update_callback_called);
+ EXPECT_TRUE(GetLatestPosition().Equals(test_position));
+
+ provider_->StopProvider();
+ EXPECT_FALSE(IsUpdating());
+ provider_.reset();
+}
+
+} // namespace device
diff --git a/services/device/geolocation/geolocation_provider_impl.cc b/services/device/geolocation/geolocation_provider_impl.cc
index 829b7caa..26edf4f 100644
--- a/services/device/geolocation/geolocation_provider_impl.cc
+++ b/services/device/geolocation/geolocation_provider_impl.cc
@@ -171,7 +171,11 @@
base::Unretained(this));
} else {
if (!IsRunning()) {
- Start();
+ base::Thread::Options options;
+#if defined(OS_MACOSX)
+ options.message_pump_type = base::MessagePumpType::NS_RUNLOOP;
+#endif
+ StartWithOptions(options);
if (user_did_opt_into_location_services_)
InformProvidersPermissionGranted();
}
diff --git a/services/device/geolocation/location_arbitrator.cc b/services/device/geolocation/location_arbitrator.cc
index d399293..394ec28b 100644
--- a/services/device/geolocation/location_arbitrator.cc
+++ b/services/device/geolocation/location_arbitrator.cc
@@ -113,9 +113,8 @@
return;
}
- if (url_loader_factory_) {
+ if (url_loader_factory_)
RegisterProvider(NewNetworkLocationProvider(url_loader_factory_, api_key_));
- }
}
void LocationArbitrator::OnLocationUpdate(
@@ -157,7 +156,7 @@
std::unique_ptr<LocationProvider>
LocationArbitrator::NewSystemLocationProvider() {
-#if defined(OS_MACOSX) || defined(OS_LINUX) || defined(OS_FUCHSIA)
+#if defined(OS_LINUX) || defined(OS_FUCHSIA)
return nullptr;
#else
return device::NewSystemLocationProvider();
diff --git a/services/device/public/cpp/device_features.cc b/services/device/public/cpp/device_features.cc
index 07072a57..c244cb6ab 100644
--- a/services/device/public/cpp/device_features.cc
+++ b/services/device/public/cpp/device_features.cc
@@ -18,5 +18,9 @@
// LocationProvider instead of the NetworkLocationProvider on Windows.
const base::Feature kWinrtGeolocationImplementation{
"WinrtGeolocationImplementation", base::FEATURE_DISABLED_BY_DEFAULT};
+// Enables usage of the CoreLocation API for LocationProvider instead of
+// NetworkLocationProvider for macOS.
+const base::Feature kMacCoreLocationImplementation{
+ "kMacCoreLocationImplementation", base::FEATURE_DISABLED_BY_DEFAULT};
} // namespace features
diff --git a/services/device/public/cpp/device_features.h b/services/device/public/cpp/device_features.h
index de55d10..dca4398 100644
--- a/services/device/public/cpp/device_features.h
+++ b/services/device/public/cpp/device_features.h
@@ -19,6 +19,8 @@
DEVICE_FEATURES_EXPORT extern const base::Feature kWinrtSensorsImplementation;
DEVICE_FEATURES_EXPORT extern const base::Feature
kWinrtGeolocationImplementation;
+DEVICE_FEATURES_EXPORT extern const base::Feature
+ kMacCoreLocationImplementation;
} // namespace features
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index b843f33..dd87274a 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -38649,6 +38649,7 @@
<int value="-1344375439" label="ServiceWorkerPaymentApps:disabled"/>
<int value="-1343259222" label="RegionalLocalesAsDisplayUI:disabled"/>
<int value="-1342961844" label="InlineUpdateFlow:disabled"/>
+ <int value="-1342829138" label="kMacCoreLocationImplementation:enabled"/>
<int value="-1342039126" label="AshNewSystemMenu:enabled"/>
<int value="-1341685799" label="AutofillAssistantDirectActions:enabled"/>
<int value="-1341092934" label="enable-accelerated-overflow-scroll"/>
@@ -40565,6 +40566,7 @@
<int value="885971656" label="EnablePlayStoreAppSearch:enabled"/>
<int value="886907524" label="autoplay-policy"/>
<int value="887011602" label="enable-spelling-auto-correct"/>
+ <int value="890322192" label="kMacCoreLocationImplementation:disabled"/>
<int value="892899792" label="MaterialDesignIncognitoNTP:disabled"/>
<int value="898311758" label="ReaderMode:disabled"/>
<int value="900614020" label="ContentSuggestionsShowSummary:disabled"/>