Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(256)

Unified Diff: chrome/android/java/src/org/chromium/chrome/browser/omnibox/geo/GeolocationHeader.java

Issue 2533523002: Add Geolocation.PermissionState histogram. (Closed)
Patch Set: Change enums to use formula. Update histrograms.xml. Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/android/java/src/org/chromium/chrome/browser/omnibox/geo/GeolocationHeader.java
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/geo/GeolocationHeader.java b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/geo/GeolocationHeader.java
index ab4f5fae4fcd7b55113ee16a98dd6a737527df97..10d11625bdee957debb0b0eae2cbe7d5de70fc11 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/omnibox/geo/GeolocationHeader.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/omnibox/geo/GeolocationHeader.java
@@ -5,9 +5,11 @@
package org.chromium.chrome.browser.omnibox.geo;
import android.Manifest;
+import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageManager;
import android.location.Location;
+import android.location.LocationManager;
import android.net.Uri;
import android.os.Build;
import android.os.Process;
@@ -20,7 +22,9 @@ import org.chromium.base.metrics.RecordHistogram;
import org.chromium.chrome.browser.preferences.website.ContentSetting;
import org.chromium.chrome.browser.preferences.website.GeolocationInfo;
import org.chromium.chrome.browser.util.UrlUtilities;
+import org.chromium.ui.base.WindowAndroid;
+import java.util.List;
import java.util.Locale;
/**
@@ -40,6 +44,72 @@ public class GeolocationHeader {
public static final int UMA_LOCATION_DISABLED_FOR_CHROME_APP = 5;
public static final int UMA_MAX = 8;
+ // Values for the histogram GeolocationHeader.PermissionState.
+ // These are used to back an UMA histogram and so should be treated as append-only.
+ //
+ // These enum values are assigned using the equation:
+ // 36 * !locationAttached + 9 * locationSource + 3 * appPermission + domainPermission
+ // so if new enum values are added, care must be taken to ensure that the equation still holds.
+ // Enum values 38-44 and 47-53 are skipped to comply with the conversion formula.
+ //
+ // In order to keep the names of these constants from being too long, the following were used:
+ // UMA_PERM to indicate UMA location permission related metrics,
+ // APP_YES (instead of APP_GRANTED) to indicate App permission granted,
+ // DOMAIN_YES (instead of DOMAIN_GRANTED) to indicate Domain permission granted.
+ public static final int UMA_PERM_HIGH_ACCURACY_APP_YES_DOMAIN_YES_LOCATION = 0;
dominickn 2016/11/30 00:08:45 You might want to use @IntDef here to help with th
pdyson 2016/11/30 08:00:43 Done. I've added this here and also to LOCATION_SO
+ public static final int UMA_PERM_HIGH_ACCURACY_APP_YES_DOMAIN_PROMPT_LOCATION = 1;
+ public static final int UMA_PERM_HIGH_ACCURACY_APP_YES_DOMAIN_BLOCKED = 2;
+ public static final int UMA_PERM_HIGH_ACCURACY_APP_PROMPT_DOMAIN_YES = 3;
+ public static final int UMA_PERM_HIGH_ACCURACY_APP_PROMPT_DOMAIN_PROMPT = 4;
+ public static final int UMA_PERM_HIGH_ACCURACY_APP_PROMPT_DOMAIN_BLOCKED = 5;
+ public static final int UMA_PERM_HIGH_ACCURACY_APP_BLOCKED_DOMAIN_YES = 6;
+ public static final int UMA_PERM_HIGH_ACCURACY_APP_BLOCKED_DOMAIN_PROMPT = 7;
+ public static final int UMA_PERM_HIGH_ACCURACY_APP_BLOCKED_DOMAIN_BLOCKED = 8;
+ public static final int UMA_PERM_BATTERY_SAVING_APP_YES_DOMAIN_YES_LOCATION = 9;
+ public static final int UMA_PERM_BATTERY_SAVING_APP_YES_DOMAIN_PROMPT_LOCATION = 10;
+ public static final int UMA_PERM_BATTERY_SAVING_APP_YES_DOMAIN_BLOCKED = 11;
+ public static final int UMA_PERM_BATTERY_SAVING_APP_PROMPT_DOMAIN_YES = 12;
+ public static final int UMA_PERM_BATTERY_SAVING_APP_PROMPT_DOMAIN_PROMPT = 13;
+ public static final int UMA_PERM_BATTERY_SAVING_APP_PROMPT_DOMAIN_BLOCKED = 14;
+ public static final int UMA_PERM_BATTERY_SAVING_APP_BLOCKED_DOMAIN_YES = 15;
+ public static final int UMA_PERM_BATTERY_SAVING_APP_BLOCKED_DOMAIN_PROMPT = 16;
+ public static final int UMA_PERM_BATTERY_SAVING_APP_BLOCKED_DOMAIN_BLOCKED = 17;
+ public static final int UMA_PERM_GPS_ONLY_APP_YES_DOMAIN_YES_LOCATION = 18;
+ public static final int UMA_PERM_GPS_ONLY_APP_YES_DOMAIN_PROMPT_LOCATION = 19;
+ public static final int UMA_PERM_GPS_ONLY_APP_YES_DOMAIN_BLOCKED = 20;
+ public static final int UMA_PERM_GPS_ONLY_APP_PROMPT_DOMAIN_YES = 21;
+ public static final int UMA_PERM_GPS_ONLY_APP_PROMPT_DOMAIN_PROMPT = 22;
+ public static final int UMA_PERM_GPS_ONLY_APP_PROMPT_DOMAIN_BLOCKED = 23;
+ public static final int UMA_PERM_GPS_ONLY_APP_BLOCKED_DOMAIN_YES = 24;
+ public static final int UMA_PERM_GPS_ONLY_APP_BLOCKED_DOMAIN_PROMPT = 25;
+ public static final int UMA_PERM_GPS_ONLY_APP_BLOCKED_DOMAIN_BLOCKED = 26;
+ public static final int UMA_PERM_MASTER_OFF_APP_YES_DOMAIN_YES = 27;
+ public static final int UMA_PERM_MASTER_OFF_APP_YES_DOMAIN_PROMPT = 28;
+ public static final int UMA_PERM_MASTER_OFF_APP_YES_DOMAIN_BLOCKED = 29;
+ public static final int UMA_PERM_MASTER_OFF_APP_PROMPT_DOMAIN_YES = 30;
+ public static final int UMA_PERM_MASTER_OFF_APP_PROMPT_DOMAIN_PROMPT = 31;
+ public static final int UMA_PERM_MASTER_OFF_APP_PROMPT_DOMAIN_BLOCKED = 32;
+ public static final int UMA_PERM_MASTER_OFF_APP_BLOCKED_DOMAIN_YES = 33;
+ public static final int UMA_PERM_MASTER_OFF_APP_BLOCKED_DOMAIN_PROMPT = 34;
+ public static final int UMA_PERM_MASTER_OFF_APP_BLOCKED_DOMAIN_BLOCKED = 35;
+ public static final int UMA_PERM_HIGH_ACCURACY_APP_YES_DOMAIN_YES_NO_LOCATION = 36;
+ public static final int UMA_PERM_HIGH_ACCURACY_APP_YES_DOMAIN_PROMPT_NO_LOCATION = 37;
+ public static final int UMA_PERM_BATTERY_SAVING_APP_YES_DOMAIN_YES_NO_LOCATION = 45;
+ public static final int UMA_PERM_BATTERY_SAVING_APP_YES_DOMAIN_PROMPT_NO_LOCATION = 46;
+ public static final int UMA_PERM_GPS_ONLY_APP_YES_DOMAIN_YES_NO_LOCATION = 54;
+ public static final int UMA_PERM_GPS_ONLY_APP_YES_DOMAIN_PROMPT_NO_LOCATION = 55;
+ public static final int UMA_PERM_UNKNOWN = 56;
+ public static final int UMA_PERM_MAX = 56;
+
+ public static final int LOCATION_SOURCE_HIGH_ACCURACY = 0;
+ public static final int LOCATION_SOURCE_BATTERY_SAVING = 1;
+ public static final int LOCATION_SOURCE_GPS_ONLY = 2;
+ public static final int LOCATION_SOURCE_MASTER_OFF = 3;
+
+ public static final int PERMISSION_GRANTED = 0;
+ public static final int PERMISSION_PROMPT = 1;
+ public static final int PERMISSION_BLOCKED = 2;
+
/** The maximum age in milliseconds of a location that we'll send in an X-Geo header. */
private static final int MAX_LOCATION_AGE = 24 * 60 * 60 * 1000; // 24 hours
@@ -112,21 +182,34 @@ public class GeolocationHeader {
* @return The X-Geo header string or null.
*/
public static String getGeoHeader(Context context, String url, boolean isIncognito) {
+ boolean locationAttached = true;
if (!isGeoHeaderEnabledForUrl(context, url, isIncognito, true)) {
- return null;
+ locationAttached = false;
}
// Only send X-Geo header if there's a fresh location available.
Location location = GeolocationTracker.getLastKnownLocation(context);
if (location == null) {
recordHistogram(UMA_LOCATION_NOT_AVAILABLE);
- return null;
- }
- if (GeolocationTracker.getLocationAge(location) > MAX_LOCATION_AGE) {
- recordHistogram(UMA_LOCATION_STALE);
- return null;
+ locationAttached = false;
+ } else {
+ long locationAge = GeolocationTracker.getLocationAge(location);
+ if (locationAge > MAX_LOCATION_AGE) {
+ recordHistogram(UMA_LOCATION_STALE);
+ locationAttached = false;
+ }
}
+ int locationSource = getLocationSource(context);
+ int appPermission = getGeolocationPermission(context);
+ int domainPermission = getDomainPermission(url, isIncognito);
+
+ // Record the permission state with a histogram.
+ recordPermissionHistogram(
+ locationSource, appPermission, domainPermission, locationAttached);
+
+ if (!locationAttached) return null;
+
recordHistogram(UMA_HEADER_SENT);
// Timestamp in microseconds since the UNIX epoch.
@@ -155,12 +238,31 @@ public class GeolocationHeader {
}
static boolean hasGeolocationPermission(Context context) {
+ return getGeolocationPermission(context) == PERMISSION_GRANTED;
+ }
+
+ /**
+ * Returns the geolocation permission.
+ * This permission can be either granted, blocked or prompt.
+ */
+ static int getGeolocationPermission(Context context) {
int pid = Process.myPid();
int uid = Process.myUid();
if (ApiCompatibilityUtils.checkPermission(
context, Manifest.permission.ACCESS_COARSE_LOCATION, pid, uid)
!= PackageManager.PERMISSION_GRANTED) {
- return false;
+ WindowAndroid window = new WindowAndroid(context);
dominickn 2016/11/30 00:08:46 I'm almost certain that creating a new WindowAndro
pdyson 2016/11/30 08:00:43 I have made this change, but now I need to mock a
dominickn 2016/12/01 00:30:26 You'll need to change the test case to extend Chro
+ Activity activity = window.activityFromContext(context);
+ if (activity == null || Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
+ // Can't check for prompt when activity is null, so default to blocked.
+ // No prompt before version M.
+ return PERMISSION_BLOCKED;
+ } else {
+ return activity.shouldShowRequestPermissionRationale(
+ Manifest.permission.ACCESS_COARSE_LOCATION)
+ ? PERMISSION_PROMPT
+ : PERMISSION_BLOCKED;
+ }
}
// Work around a bug in OnePlus2 devices running Lollipop, where the NETWORK_PROVIDER
@@ -170,10 +272,10 @@ public class GeolocationHeader {
&& ApiCompatibilityUtils.checkPermission(
context, Manifest.permission.ACCESS_FINE_LOCATION, pid, uid)
!= PackageManager.PERMISSION_GRANTED) {
- return false;
+ return PERMISSION_BLOCKED;
}
- return true;
+ return PERMISSION_GRANTED;
}
/**
@@ -182,6 +284,16 @@ public class GeolocationHeader {
* scheme, this considers the user's preference for url with the http scheme instead.
*/
static boolean isLocationDisabledForUrl(Uri uri, boolean isIncognito) {
+ ContentSetting locationPermission = locationPermissionForUrl(uri, isIncognito);
+ return locationPermission == ContentSetting.BLOCK;
dominickn 2016/11/30 00:08:45 Nit: remove the local variable and directly compar
pdyson 2016/11/30 08:00:43 Done. Still getting the hang of how much to put on
+ }
+
+ /**
+ * Returns the location permission for sharing their location with url (e.g. via the
+ * geolocation infobar). If the user has not chosen a preference for url and url uses the https
+ * scheme, this returns the user's preference for url with the http scheme instead.
+ */
+ static ContentSetting locationPermissionForUrl(Uri uri, boolean isIncognito) {
dominickn 2016/11/30 00:08:45 Nit: call this locationContentSettingForUrl
pdyson 2016/11/30 08:00:42 Done.
GeolocationInfo locationSettings = new GeolocationInfo(uri.toString(), null, isIncognito);
ContentSetting locationPermission = locationSettings.getContentSetting();
@@ -197,11 +309,81 @@ public class GeolocationHeader {
}
}
- return locationPermission == ContentSetting.BLOCK;
+ return locationPermission;
}
/** Records a data point for the Geolocation.HeaderSentOrNot histogram. */
private static void recordHistogram(int result) {
RecordHistogram.recordEnumeratedHistogram("Geolocation.HeaderSentOrNot", result, UMA_MAX);
}
+
+ /** Returns the location source. */
+ private static int getLocationSource(Context context) {
+ LocationManager locationManager =
+ (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
+ List<String> providers = locationManager.getProviders(true);
+ boolean hasNetworkProvider = false;
+ boolean hasGpsProvider = false;
+ for (String provider : providers) {
+ if (!LocationManager.PASSIVE_PROVIDER.equals(provider)) {
+ if (LocationManager.NETWORK_PROVIDER.equals(provider)) {
+ hasNetworkProvider = true;
+ } else if (LocationManager.GPS_PROVIDER.equals(provider)) {
+ hasGpsProvider = true;
+ }
+ }
+ }
+ int locationSource;
dominickn 2016/11/30 00:08:45 Nit: this variable is unused.
pdyson 2016/11/30 08:00:43 Done.
+ return hasNetworkProvider
+ ? (hasGpsProvider ? LOCATION_SOURCE_HIGH_ACCURACY : LOCATION_SOURCE_BATTERY_SAVING)
+ : (hasGpsProvider ? LOCATION_SOURCE_GPS_ONLY : LOCATION_SOURCE_MASTER_OFF);
+ }
+
+ /**
+ * Returns the domain permission.
+ * This permission can be either granted, blocked or prompt.
+ */
+ private static int getDomainPermission(String url, boolean isIncognito) {
+ Uri uri = Uri.parse(url);
dominickn 2016/11/30 00:08:45 Nit: remove the local variable and inline Uri.pars
pdyson 2016/11/30 08:00:43 Done.
+ final ContentSetting domainPermission = locationPermissionForUrl(uri, isIncognito);
dominickn 2016/11/30 00:08:45 Nit: I don't think this needs to be final?
pdyson 2016/11/30 08:00:42 Done.
+ switch (domainPermission) {
+ case BLOCK:
dominickn 2016/11/30 00:08:45 I would make BLOCK the default case, in case new C
pdyson 2016/11/30 08:00:43 Done.
+ return PERMISSION_BLOCKED;
+ case ASK:
+ return PERMISSION_PROMPT;
+ default:
+ return PERMISSION_GRANTED;
+ }
+ }
+
+ /**
+ * Returns the enum to use in the GeolocationHeader.PermissionState histogram.
+ * Unexpected input values return UMA_PERM_UNKNOWN.
+ * Note that this relies on the enum values being obtainable using a mathematical formula.
+ */
+ private static int getPermissionHistogramEnum(
+ int locationSource, int appPermission, int domainPermission, boolean locationAttached) {
+ // Calculate the ENUM value using an equation.
+ int value = 9 * locationSource + 3 * appPermission + domainPermission;
dominickn 2016/11/30 00:08:46 This calculation is quite magic and makes me very
Ilya Sherman 2016/11/30 00:33:35 FWIW, I agree that it would be best to clarify how
pdyson 2016/11/30 08:00:43 I've put this back to using the switch statements.
+ if (value < 0 || value > 35) return UMA_PERM_UNKNOWN;
+ // Handle the NO_LOCATION cases.
+ if ((locationSource == LOCATION_SOURCE_HIGH_ACCURACY
+ || locationSource == LOCATION_SOURCE_BATTERY_SAVING
+ || locationSource == LOCATION_SOURCE_GPS_ONLY)
+ && appPermission == PERMISSION_GRANTED
+ && (domainPermission == PERMISSION_GRANTED || domainPermission == PERMISSION_PROMPT)
+ && !locationAttached) {
+ value += 36;
+ }
+ return value;
+ }
+
+ /** Records a data point for the GeolocationHeader.PermissionState histogram. */
+ private static void recordPermissionHistogram(
+ int locationSource, int appPermission, int domainPermission, boolean locationAttached) {
+ int result = getPermissionHistogramEnum(
+ locationSource, appPermission, domainPermission, locationAttached);
+ RecordHistogram.recordEnumeratedHistogram(
+ "GeolocationHeader.PermissionState", result, UMA_PERM_MAX);
+ }
}

Powered by Google App Engine
This is Rietveld 408576698