|  | // Copyright 2013 The Chromium Authors | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #import "ios/chrome/browser/geolocation/model/geolocation_manager.h" | 
|  |  | 
|  | #import <CoreLocation/CoreLocation.h> | 
|  |  | 
|  | #import <optional> | 
|  |  | 
|  | #import "base/metrics/histogram_macros.h" | 
|  | #import "ios/chrome/browser/geolocation/model/authorization_status_cache_util.h" | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // These values are persisted to logs. Entries should not be renumbered and | 
|  | // numeric values should never be reused. | 
|  | // | 
|  | // LINT.IfChange(AuthorizationStatus) | 
|  | enum class AuthorizationStatus { | 
|  | // The user has not chosen whether to allow location access. | 
|  | kNotDetermined = 0, | 
|  | // The user cannot allow location access. | 
|  | kRestricted = 1, | 
|  | // The user denied location access either for the app or globally. | 
|  | kDenied = 2, | 
|  | // The user granted location access at all times. | 
|  | kAuthorizedAlways = 3, | 
|  | // The user granted location access only while the app is in use. | 
|  | kAuthorizedWhenInUse = 4, | 
|  | kMaxValue = kAuthorizedWhenInUse | 
|  | }; | 
|  | // LINT.ThenChange(/tools/metrics/histograms/metadata/geolocation/enums.xml) | 
|  |  | 
|  | // Name of the histogram recording initial geolocation authorization state. | 
|  | constexpr char kGeolocationInitialAuthorizationStateHistogram[] = | 
|  | "Geolocation.IOS.InitialAuthorizationState"; | 
|  |  | 
|  | // Name of the histogram recording a change in geolocation authorization state. | 
|  | constexpr char kGeolocationAuthorizationStateChangedHistogram[] = | 
|  | "Geolocation.IOS.ChangedAuthorizationState"; | 
|  |  | 
|  | AuthorizationStatus ToAuthorizationStatus( | 
|  | CLAuthorizationStatus authorization_status) { | 
|  | switch (authorization_status) { | 
|  | case kCLAuthorizationStatusNotDetermined: | 
|  | return AuthorizationStatus::kNotDetermined; | 
|  | case kCLAuthorizationStatusRestricted: | 
|  | return AuthorizationStatus::kRestricted; | 
|  | case kCLAuthorizationStatusDenied: | 
|  | return AuthorizationStatus::kDenied; | 
|  | case kCLAuthorizationStatusAuthorizedAlways: | 
|  | return AuthorizationStatus::kAuthorizedAlways; | 
|  | case kCLAuthorizationStatusAuthorizedWhenInUse: | 
|  | return AuthorizationStatus::kAuthorizedWhenInUse; | 
|  | default: | 
|  | // Since CLAuthorizationStatus is an iOS-provided enum, safely handle new | 
|  | // values by falling back to `kNotDetermined`. | 
|  | return AuthorizationStatus::kNotDetermined; | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // anonymous namespace | 
|  |  | 
|  | @interface GeolocationManager () <CLLocationManagerDelegate> | 
|  |  | 
|  | @property(nonatomic, strong) CLLocationManager* locationManager; | 
|  |  | 
|  | // The status received during this application run | 
|  | @property(nonatomic) std::optional<CLAuthorizationStatus> status; | 
|  |  | 
|  | @end | 
|  |  | 
|  | @implementation GeolocationManager | 
|  |  | 
|  | + (GeolocationManager*)sharedInstance { | 
|  | static GeolocationManager* instance = [[GeolocationManager alloc] init]; | 
|  | return instance; | 
|  | } | 
|  |  | 
|  | + (GeolocationManager*)createForTesting { | 
|  | return [[GeolocationManager alloc] init]; | 
|  | } | 
|  |  | 
|  | - (instancetype)init { | 
|  | self = [super init]; | 
|  | if (self) { | 
|  | _locationManager = [[CLLocationManager alloc] init]; | 
|  | [_locationManager setDelegate:self]; | 
|  | } | 
|  | return self; | 
|  | } | 
|  |  | 
|  | - (CLAuthorizationStatus)authorizationStatus { | 
|  | if (self.status) { | 
|  | return static_cast<CLAuthorizationStatus>(self.status.value()); | 
|  | } | 
|  |  | 
|  | std::optional<CLAuthorizationStatus> cached_status = | 
|  | authorization_status_cache_util::GetAuthorizationStatus(); | 
|  | if (cached_status) { | 
|  | return cached_status.value(); | 
|  | } | 
|  |  | 
|  | return kCLAuthorizationStatusNotDetermined; | 
|  | } | 
|  |  | 
|  | #pragma mark - CLLocationManagerDelegate | 
|  |  | 
|  | - (void)locationManagerDidChangeAuthorization: | 
|  | (CLLocationManager*)locationManager { | 
|  | self.status = self.locationManager.authorizationStatus; | 
|  |  | 
|  | authorization_status_cache_util::SetAuthorizationStatus(self.status.value()); | 
|  |  | 
|  | // The initial call to this method represents the initial value of geolocation | 
|  | // authorization status rather than a change. | 
|  | static BOOL initialCall = YES; | 
|  | if (initialCall) { | 
|  | initialCall = NO; | 
|  | UMA_HISTOGRAM_ENUMERATION(kGeolocationInitialAuthorizationStateHistogram, | 
|  | ToAuthorizationStatus(self.status.value())); | 
|  | return; | 
|  | } | 
|  |  | 
|  | UMA_HISTOGRAM_ENUMERATION(kGeolocationAuthorizationStateChangedHistogram, | 
|  | ToAuthorizationStatus(self.status.value())); | 
|  | } | 
|  |  | 
|  | @end |