blob: f9e3b400b12490daa59e7ece02ae148dd409ca19 [file] [log] [blame]
// 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/geolocation_logger.h"
#import <CoreLocation/CoreLocation.h>
#import "base/metrics/histogram_macros.h"
#import "components/google/core/common/google_util.h"
#import "ios/chrome/app/tests_hook.h"
#import "ios/chrome/browser/shared/model/browser_state/chrome_browser_state.h"
#import "ios/web/public/navigation/navigation_item.h"
#import "ios/web/public/navigation/navigation_manager.h"
#import "ios/web/public/web_state.h"
#if !defined(__has_feature) || !__has_feature(objc_arc)
#error "This file requires ARC support."
#endif
namespace {
// Values for the histograms that record the user's action when prompted to
// authorize the use of location by Chrome. These match the definition of
// GeolocationAuthorizationAction in Chromium
// src-internal/tools/histograms/histograms.xml.
typedef enum {
// The user authorized use of location.
kAuthorizationActionAuthorized = 0,
// The user permanently denied use of location (Don't Allow).
kAuthorizationActionPermanentlyDenied,
// The user denied use of location at this prompt (Not Now).
kAuthorizationActionDenied,
// The number of possible AuthorizationAction values to report.
kAuthorizationActionCount,
} AuthorizationAction;
// Name of the histogram recording AuthorizationAction for an existing user.
const char* const kGeolocationAuthorizationActionExistingUser =
"Geolocation.AuthorizationActionExistingUser";
enum class PermissionStatus {
// Status unknown, usually because the system is slow to respond.
kPermissionUnknown = 0,
// User has not made a permission choice.
kPermissionNotDetermined = 1,
// User has made a permission choice, be it allowed, denied, etc.
kPermissionDetermined = 2,
kMaxValue = kPermissionDetermined,
};
} // anonymous namespace
@interface GeolocationLogger () <CLLocationManagerDelegate>
@property(nonatomic, strong) CLLocationManager* locationManager;
// Whether the permission was undefined or not. Used to choose whether to log
// the permission or not.
@property(atomic, assign) PermissionStatus permissionStatus;
@end
@implementation GeolocationLogger
+ (GeolocationLogger*)sharedInstance {
static GeolocationLogger* instance = [[GeolocationLogger alloc] init];
return instance;
}
- (instancetype)init {
self = [super init];
if (self) {
_locationManager = [[CLLocationManager alloc] init];
[_locationManager setDelegate:self];
dispatch_queue_t priorityQueue =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
_permissionStatus = PermissionStatus::kPermissionUnknown;
dispatch_async(priorityQueue, ^{
self.permissionStatus = self.locationManager.authorizationStatus ==
kCLAuthorizationStatusNotDetermined
? PermissionStatus::kPermissionNotDetermined
: PermissionStatus::kPermissionDetermined;
});
}
return self;
}
#pragma mark - Private
- (void)recordAuthorizationAction:(AuthorizationAction)authorizationAction {
self.permissionStatus = PermissionStatus::kPermissionDetermined;
UMA_HISTOGRAM_ENUMERATION(kGeolocationAuthorizationActionExistingUser,
authorizationAction, kAuthorizationActionCount);
}
// Boolean value indicating whether location services are enabled on the
// device.
- (BOOL)locationServicesEnabled {
return !tests_hook::DisableGeolocation() &&
[CLLocationManager locationServicesEnabled];
}
#pragma mark - CLLocationManagerDelegate
- (void)locationManagerDidChangeAuthorization:
(CLLocationManager*)locationManager {
if (self.permissionStatus == PermissionStatus::kPermissionUnknown)
return;
if (self.permissionStatus == PermissionStatus::kPermissionNotDetermined) {
switch (locationManager.authorizationStatus) {
case kCLAuthorizationStatusNotDetermined:
// We may get a spurious notification about a transition to
// `kCLAuthorizationStatusNotDetermined` when we first start location
// services. Ignore it and don't reset `systemPrompt_` until we get a
// real change.
break;
case kCLAuthorizationStatusRestricted:
case kCLAuthorizationStatusDenied:
[self recordAuthorizationAction:kAuthorizationActionPermanentlyDenied];
break;
case kCLAuthorizationStatusAuthorizedAlways:
case kCLAuthorizationStatusAuthorizedWhenInUse:
[self recordAuthorizationAction:kAuthorizationActionAuthorized];
break;
}
}
}
@end