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 e5cdadee8ec7d86aebbddf5c0aee2122bbd71404..92cf5214c004de00de1b3144d8e11195dbc46a9c 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 |
@@ -12,12 +12,15 @@ import android.location.LocationManager; |
import android.net.Uri; |
import android.os.Build; |
import android.os.Process; |
+import android.os.SystemClock; |
import android.support.annotation.IntDef; |
import android.util.Base64; |
import org.chromium.base.ApiCompatibilityUtils; |
import org.chromium.base.ContextUtils; |
+import org.chromium.base.Log; |
import org.chromium.base.annotations.CalledByNative; |
+import org.chromium.base.annotations.SuppressFBWarnings; |
import org.chromium.base.metrics.RecordHistogram; |
import org.chromium.chrome.browser.preferences.website.ContentSetting; |
import org.chromium.chrome.browser.preferences.website.GeolocationInfo; |
@@ -28,6 +31,7 @@ import java.lang.annotation.Retention; |
import java.lang.annotation.RetentionPolicy; |
import java.util.List; |
import java.util.Locale; |
+import java.util.concurrent.TimeUnit; |
/** |
* Provides methods for building the X-Geo HTTP header, which provides device location to a server |
@@ -36,6 +40,7 @@ import java.util.Locale; |
* X-Geo header spec: https://goto.google.com/xgeospec. |
*/ |
public class GeolocationHeader { |
+ private static final String TAG = "GeolocationHeader"; |
// Values for the histogram Geolocation.HeaderSentOrNot. Values 1, 5, 6, and 7 are defined in |
// histograms.xml and should not be used in other ways. |
@@ -152,6 +157,12 @@ public class GeolocationHeader { |
@IntDef({PERMISSION_GRANTED, PERMISSION_PROMPT, PERMISSION_BLOCKED}) |
private @interface Permission {} |
+ /** The maximum value for the GeolocationHeader.TimeListening* histograms. */ |
+ public static final int TIME_LISTENING_HISTOGRAM_MAX_MILLIS = 50 * 60 * 1000; // 50 minutes |
+ |
+ /** The maximum value for the GeolocationHeader.LocationAge* histograms. */ |
+ public static final int LOCATION_AGE_HISTOGRAM_MAX_SECONDS = 30 * 24 * 60 * 60; // 30 days |
+ |
/** 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 |
@@ -160,6 +171,9 @@ public class GeolocationHeader { |
private static final String HTTPS_SCHEME = "https"; |
+ /** The time of the first location refresh. Contains Long.MAX_VALUE if not set. */ |
+ private static long sFirstLocationTime = Long.MAX_VALUE; |
+ |
/** |
* Requests a location refresh so that a valid location will be available for constructing |
* an X-Geo header in the near future (i.e. within 5 minutes). |
@@ -169,6 +183,9 @@ public class GeolocationHeader { |
public static void primeLocationForGeoHeader(Context context) { |
if (!hasGeolocationPermission(context)) return; |
+ if (sFirstLocationTime == Long.MAX_VALUE) { |
+ sFirstLocationTime = SystemClock.elapsedRealtime(); |
+ } |
GeolocationTracker.refreshLastKnownLocation(context, REFRESH_LOCATION_AGE); |
} |
@@ -227,6 +244,7 @@ public class GeolocationHeader { |
boolean isIncognito = tab.isIncognito(); |
boolean locationAttached = true; |
Location location = null; |
+ long locationAge = Long.MAX_VALUE; |
if (isGeoHeaderEnabledForUrl(context, url, isIncognito, true)) { |
// Only send X-Geo header if there's a fresh location available. |
location = GeolocationTracker.getLastKnownLocation(context); |
@@ -234,7 +252,8 @@ public class GeolocationHeader { |
recordHistogram(UMA_LOCATION_NOT_AVAILABLE); |
locationAttached = false; |
} else { |
- if (GeolocationTracker.getLocationAge(location) > MAX_LOCATION_AGE) { |
+ locationAge = GeolocationTracker.getLocationAge(location); |
+ if (locationAge > MAX_LOCATION_AGE) { |
recordHistogram(UMA_LOCATION_STALE); |
locationAttached = false; |
} |
@@ -251,6 +270,17 @@ public class GeolocationHeader { |
recordPermissionHistogram( |
locationSource, appPermission, domainPermission, locationAttached); |
+ if (locationSource != LOCATION_SOURCE_MASTER_OFF && appPermission != PERMISSION_BLOCKED |
+ && domainPermission != PERMISSION_BLOCKED) { |
+ // Record the Location Age with a histogram. |
+ recordLocationAgeHistogram(locationSource, locationAge); |
+ long duration = sFirstLocationTime == Long.MAX_VALUE |
+ ? Long.MAX_VALUE |
+ : SystemClock.elapsedRealtime() - sFirstLocationTime; |
+ // Record the Time Listening with a histogram. |
+ recordTimeListeningHistogram(locationSource, locationAttached, duration); |
+ } |
+ |
// Note that strictly speaking "location == null" is not needed here as the |
// logic above prevents location being null when locationAttached is true. |
// It is here to prevent problems if the logic above is changed. |
@@ -530,4 +560,61 @@ public class GeolocationHeader { |
RecordHistogram.recordEnumeratedHistogram( |
"Geolocation.Header.PermissionState", result, UMA_PERM_COUNT); |
} |
+ |
+ /** |
+ * Determines the name for a Time Listening Histogram. Returns empty string if the location |
+ * source is MASTER_OFF as we do not record histograms for that case. |
+ */ |
+ private static String getTimeListeningHistogramEnum( |
+ int locationSource, boolean locationAttached) { |
+ switch (locationSource) { |
+ case LOCATION_SOURCE_HIGH_ACCURACY: |
+ return locationAttached |
+ ? "Geolocation.Header.TimeListening.HighAccuracy.LocationAttached" |
+ : "Geolocation.Header.TimeListening.HighAccuracy.LocationNotAttached"; |
+ case LOCATION_SOURCE_GPS_ONLY: |
+ return locationAttached |
+ ? "Geolocation.Header.TimeListening.GpsOnly.LocationAttached" |
+ : "Geolocation.Header.TimeListening.GpsOnly.LocationNotAttached"; |
+ case LOCATION_SOURCE_BATTERY_SAVING: |
+ return locationAttached |
+ ? "Geolocation.Header.TimeListening.BatterySaving.LocationAttached" |
+ : "Geolocation.Header.TimeListening.BatterySaving.LocationNotAttached"; |
+ default: |
+ Log.e(TAG, "Unexpected locationSource: " + locationSource); |
+ assert false : "Unexpected locationSource: " + locationSource; |
+ return null; |
+ } |
+ } |
+ |
+ /** Records a data point for one of the GeolocationHeader.TimeListening* histograms. */ |
+ @SuppressFBWarnings("RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE") |
+ private static void recordTimeListeningHistogram( |
+ int locationSource, boolean locationAttached, long duration) { |
+ String name = getTimeListeningHistogramEnum(locationSource, locationAttached); |
+ if (name == null) return; |
+ RecordHistogram.recordCustomTimesHistogram( |
+ name, duration, 1, TIME_LISTENING_HISTOGRAM_MAX_MILLIS, TimeUnit.MILLISECONDS, 50); |
+ } |
+ |
+ /** Records a data point for one of the GeolocationHeader.LocationAge* histograms. */ |
+ private static void recordLocationAgeHistogram(int locationSource, long durationMillis) { |
+ String name = ""; |
+ if (locationSource == LOCATION_SOURCE_HIGH_ACCURACY) { |
+ name = "Geolocation.Header.LocationAgeHighAccuracy"; |
+ } else if (locationSource == LOCATION_SOURCE_GPS_ONLY) { |
+ name = "Geolocation.Header.LocationAgeGpsOnly"; |
+ } else if (locationSource == LOCATION_SOURCE_BATTERY_SAVING) { |
+ name = "Geolocation.Header.LocationAgeBatterySaving"; |
+ } else { |
+ Log.e(TAG, "Unexpected locationSource: " + locationSource); |
+ assert false : "Unexpected locationSource: " + locationSource; |
+ return; |
+ } |
+ long durationSeconds = durationMillis / 1000; |
+ int duration = durationSeconds >= (long) Integer.MAX_VALUE ? Integer.MAX_VALUE |
+ : (int) durationSeconds; |
+ RecordHistogram.recordCustomCountHistogram( |
+ name, duration, 1, LOCATION_AGE_HISTOGRAM_MAX_SECONDS, 50); |
+ } |
} |