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

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

Issue 2880663004: Adding visible networks to the Geolocation Header. (Closed)
Patch Set: Adding visible networks to the Geolocation Header. Created 3 years, 7 months 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 unified diff | Download patch
« no previous file with comments | « no previous file | chrome/android/java/src/org/chromium/chrome/browser/omnibox/geo/GeolocationTracker.java » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package org.chromium.chrome.browser.omnibox.geo; 5 package org.chromium.chrome.browser.omnibox.geo;
6 6
7 import android.Manifest; 7 import android.Manifest;
8 import android.content.pm.PackageManager; 8 import android.content.pm.PackageManager;
9 import android.location.Location; 9 import android.location.Location;
10 import android.location.LocationManager; 10 import android.location.LocationManager;
11 import android.net.Uri; 11 import android.net.Uri;
12 import android.os.Build; 12 import android.os.Build;
13 import android.os.Process; 13 import android.os.Process;
14 import android.os.SystemClock; 14 import android.os.SystemClock;
15 import android.provider.Settings; 15 import android.provider.Settings;
16 import android.support.annotation.IntDef; 16 import android.support.annotation.IntDef;
17 import android.util.Base64; 17 import android.util.Base64;
18 18
19 import com.google.protobuf.nano.MessageNano; 19 import com.google.protobuf.nano.MessageNano;
20 20
21 import org.chromium.base.ApiCompatibilityUtils; 21 import org.chromium.base.ApiCompatibilityUtils;
22 import org.chromium.base.CollectionUtil;
22 import org.chromium.base.ContextUtils; 23 import org.chromium.base.ContextUtils;
23 import org.chromium.base.Log; 24 import org.chromium.base.Log;
25 import org.chromium.base.VisibleForTesting;
24 import org.chromium.base.annotations.CalledByNative; 26 import org.chromium.base.annotations.CalledByNative;
25 import org.chromium.base.annotations.SuppressFBWarnings; 27 import org.chromium.base.annotations.SuppressFBWarnings;
26 import org.chromium.base.metrics.RecordHistogram; 28 import org.chromium.base.metrics.RecordHistogram;
27 import org.chromium.chrome.browser.ChromeFeatureList; 29 import org.chromium.chrome.browser.ChromeFeatureList;
28 import org.chromium.chrome.browser.UrlConstants; 30 import org.chromium.chrome.browser.UrlConstants;
31 import org.chromium.chrome.browser.omnibox.geo.VisibleNetworks.VisibleCell;
32 import org.chromium.chrome.browser.omnibox.geo.VisibleNetworks.VisibleWifi;
29 import org.chromium.chrome.browser.preferences.website.ContentSetting; 33 import org.chromium.chrome.browser.preferences.website.ContentSetting;
30 import org.chromium.chrome.browser.preferences.website.GeolocationInfo; 34 import org.chromium.chrome.browser.preferences.website.GeolocationInfo;
31 import org.chromium.chrome.browser.preferences.website.WebsitePreferenceBridge; 35 import org.chromium.chrome.browser.preferences.website.WebsitePreferenceBridge;
32 import org.chromium.chrome.browser.tab.Tab; 36 import org.chromium.chrome.browser.tab.Tab;
33 import org.chromium.chrome.browser.util.UrlUtilities; 37 import org.chromium.chrome.browser.util.UrlUtilities;
34 38
35 import java.lang.annotation.Retention; 39 import java.lang.annotation.Retention;
36 import java.lang.annotation.RetentionPolicy; 40 import java.lang.annotation.RetentionPolicy;
37 import java.util.Locale; 41 import java.util.Locale;
42 import java.util.Set;
38 import java.util.concurrent.TimeUnit; 43 import java.util.concurrent.TimeUnit;
39 44
45 import javax.annotation.Nullable;
46
40 /** 47 /**
41 * Provides methods for building the X-Geo HTTP header, which provides device lo cation to a server 48 * Provides methods for building the X-Geo HTTP header, which provides device lo cation to a server
42 * when making an HTTP request. 49 * when making an HTTP request.
43 * 50 *
44 * X-Geo header spec: https://goto.google.com/xgeospec. 51 * X-Geo header spec: https://goto.google.com/xgeospec.
45 */ 52 */
46 public class GeolocationHeader { 53 public class GeolocationHeader {
47 private static final String TAG = "GeolocationHeader"; 54 private static final String TAG = "GeolocationHeader";
48 55
49 // Values for the histogram Geolocation.HeaderSentOrNot. Values 1, 5, 6, and 7 are defined in 56 // Values for the histogram Geolocation.HeaderSentOrNot. Values 1, 5, 6, and 7 are defined in
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after
141 UMA_PERM_GPS_ONLY_APP_BLOCKED_DOMAIN_BLOCKED, UMA_PERM_MASTER_OFF_AP P_YES_DOMAIN_YES, 148 UMA_PERM_GPS_ONLY_APP_BLOCKED_DOMAIN_BLOCKED, UMA_PERM_MASTER_OFF_AP P_YES_DOMAIN_YES,
142 UMA_PERM_MASTER_OFF_APP_YES_DOMAIN_PROMPT, UMA_PERM_MASTER_OFF_APP_Y ES_DOMAIN_BLOCKED, 149 UMA_PERM_MASTER_OFF_APP_YES_DOMAIN_PROMPT, UMA_PERM_MASTER_OFF_APP_Y ES_DOMAIN_BLOCKED,
143 UMA_PERM_MASTER_OFF_APP_PROMPT_DOMAIN_YES, UMA_PERM_MASTER_OFF_APP_P ROMPT_DOMAIN_PROMPT, 150 UMA_PERM_MASTER_OFF_APP_PROMPT_DOMAIN_YES, UMA_PERM_MASTER_OFF_APP_P ROMPT_DOMAIN_PROMPT,
144 UMA_PERM_MASTER_OFF_APP_PROMPT_DOMAIN_BLOCKED, 151 UMA_PERM_MASTER_OFF_APP_PROMPT_DOMAIN_BLOCKED,
145 UMA_PERM_MASTER_OFF_APP_BLOCKED_DOMAIN_YES, 152 UMA_PERM_MASTER_OFF_APP_BLOCKED_DOMAIN_YES,
146 UMA_PERM_MASTER_OFF_APP_BLOCKED_DOMAIN_PROMPT, 153 UMA_PERM_MASTER_OFF_APP_BLOCKED_DOMAIN_PROMPT,
147 UMA_PERM_MASTER_OFF_APP_BLOCKED_DOMAIN_BLOCKED, UMA_PERM_UNSUITABLE_ URL, 154 UMA_PERM_MASTER_OFF_APP_BLOCKED_DOMAIN_BLOCKED, UMA_PERM_UNSUITABLE_ URL,
148 UMA_PERM_NOT_HTTPS}) 155 UMA_PERM_NOT_HTTPS})
149 public @interface UmaPermission {} 156 public @interface UmaPermission {}
150 157
151 private static final int LOCATION_SOURCE_HIGH_ACCURACY = 0; 158 @VisibleForTesting
152 private static final int LOCATION_SOURCE_BATTERY_SAVING = 1; 159 static final int LOCATION_SOURCE_HIGH_ACCURACY = 0;
153 private static final int LOCATION_SOURCE_GPS_ONLY = 2; 160 @VisibleForTesting
154 private static final int LOCATION_SOURCE_MASTER_OFF = 3; 161 static final int LOCATION_SOURCE_BATTERY_SAVING = 1;
162 @VisibleForTesting
163 static final int LOCATION_SOURCE_GPS_ONLY = 2;
164 @VisibleForTesting
165 static final int LOCATION_SOURCE_MASTER_OFF = 3;
155 @Retention(RetentionPolicy.SOURCE) 166 @Retention(RetentionPolicy.SOURCE)
156 @IntDef({LOCATION_SOURCE_HIGH_ACCURACY, LOCATION_SOURCE_BATTERY_SAVING, 167 @IntDef({LOCATION_SOURCE_HIGH_ACCURACY, LOCATION_SOURCE_BATTERY_SAVING,
157 LOCATION_SOURCE_GPS_ONLY, LOCATION_SOURCE_MASTER_OFF}) 168 LOCATION_SOURCE_GPS_ONLY, LOCATION_SOURCE_MASTER_OFF})
158 private @interface LocationSource {} 169 private @interface LocationSource {}
159 170
160 private static final int PERMISSION_GRANTED = 0; 171 private static final int PERMISSION_GRANTED = 0;
161 private static final int PERMISSION_PROMPT = 1; 172 private static final int PERMISSION_PROMPT = 1;
162 private static final int PERMISSION_BLOCKED = 2; 173 private static final int PERMISSION_BLOCKED = 2;
163 @Retention(RetentionPolicy.SOURCE) 174 @Retention(RetentionPolicy.SOURCE)
164 @IntDef({PERMISSION_GRANTED, PERMISSION_PROMPT, PERMISSION_BLOCKED}) 175 @IntDef({PERMISSION_GRANTED, PERMISSION_PROMPT, PERMISSION_BLOCKED})
(...skipping 14 matching lines...) Expand all
179 @IntDef({HEADER_ENABLED, INCOGNITO, UNSUITABLE_URL, NOT_HTTPS, LOCATION_PERM ISSION_BLOCKED}) 190 @IntDef({HEADER_ENABLED, INCOGNITO, UNSUITABLE_URL, NOT_HTTPS, LOCATION_PERM ISSION_BLOCKED})
180 private @interface HeaderState {} 191 private @interface HeaderState {}
181 192
182 /** The maximum age in milliseconds of a location that we'll send in an X-Ge o header. */ 193 /** The maximum age in milliseconds of a location that we'll send in an X-Ge o header. */
183 private static final int MAX_LOCATION_AGE = 24 * 60 * 60 * 1000; // 24 hour s 194 private static final int MAX_LOCATION_AGE = 24 * 60 * 60 * 1000; // 24 hour s
184 195
185 /** The maximum age in milliseconds of a location before we'll request a ref resh. */ 196 /** The maximum age in milliseconds of a location before we'll request a ref resh. */
186 private static final int REFRESH_LOCATION_AGE = 5 * 60 * 1000; // 5 minutes 197 private static final int REFRESH_LOCATION_AGE = 5 * 60 * 1000; // 5 minutes
187 198
188 /** The X-Geo header prefix, preceding any location descriptors */ 199 /** The X-Geo header prefix, preceding any location descriptors */
189 private static final String XGEO_HEADER_PREFIX = "X-Geo: "; 200 private static final String XGEO_HEADER_PREFIX = "X-Geo:";
201
202 /**
203 * The location descriptor separator used in the X-Geo header to separate en coding prefix, and
204 * encoded descriptors
205 */
206 private static final String LOCATION_SEPARATOR = " ";
190 207
191 /** The location descriptor prefix used in the X-Geo header to specify a pro to wire encoding */ 208 /** The location descriptor prefix used in the X-Geo header to specify a pro to wire encoding */
192 private static final String LOCATION_PROTO_PREFIX = "w "; 209 private static final String LOCATION_PROTO_PREFIX = "w";
193 210
194 /** The location descriptor prefix used in the X-Geo header to specify an AS CII encoding */ 211 /** The location descriptor prefix used in the X-Geo header to specify an AS CII encoding */
195 private static final String LOCATION_ASCII_PREFIX = "a "; 212 private static final String LOCATION_ASCII_PREFIX = "a";
196 213
197 /** The time of the first location refresh. Contains Long.MAX_VALUE if not s et. */ 214 /** The time of the first location refresh. Contains Long.MAX_VALUE if not s et. */
198 private static long sFirstLocationTime = Long.MAX_VALUE; 215 private static long sFirstLocationTime = Long.MAX_VALUE;
199 216
217 /** Present in WiFi SSID that should not be mapped */
218 private static final String SSID_NOMAP = "_nomap";
219
220 /** Present in WiFi SSID that opted out */
221 private static final String SSID_OPTOUT = "_optout";
222
223 private static int sLocationSourceForTesting;
224 private static boolean sUseLocationSourceForTesting;
225
226 private static boolean sAppPermissionGrantedForTesting;
227 private static boolean sUseAppPermissionGrantedForTesting;
228
200 /** 229 /**
201 * Requests a location refresh so that a valid location will be available fo r constructing 230 * Requests a location refresh so that a valid location will be available fo r constructing
202 * an X-Geo header in the near future (i.e. within 5 minutes). 231 * an X-Geo header in the near future (i.e. within 5 minutes).
203 */ 232 */
204 public static void primeLocationForGeoHeader() { 233 public static void primeLocationForGeoHeader() {
205 if (!hasGeolocationPermission()) return; 234 if (!hasGeolocationPermission()) return;
206 235
207 if (sFirstLocationTime == Long.MAX_VALUE) { 236 if (sFirstLocationTime == Long.MAX_VALUE) {
208 sFirstLocationTime = SystemClock.elapsedRealtime(); 237 sFirstLocationTime = SystemClock.elapsedRealtime();
209 } 238 }
210 GeolocationTracker.refreshLastKnownLocation( 239 GeolocationTracker.refreshLastKnownLocation(
211 ContextUtils.getApplicationContext(), REFRESH_LOCATION_AGE); 240 ContextUtils.getApplicationContext(), REFRESH_LOCATION_AGE);
241
242 // Only refresh visible networks if enabled.
243 if (ChromeFeatureList.isEnabled(ChromeFeatureList.XGEO_VISIBLE_NETWORKS) ) {
244 VisibleNetworksTracker.refreshVisibleNetworks(ContextUtils.getApplic ationContext());
245 }
212 } 246 }
213 247
214 /** 248 /**
215 * Returns whether the X-Geo header is allowed to be sent for the current UR L. 249 * Returns whether the X-Geo header is allowed to be sent for the current UR L.
216 * 250 *
217 * @param url The URL of the request with which this header will be sent. 251 * @param url The URL of the request with which this header will be sent.
218 * @param isIncognito Whether the request will happen in an incognito tab. 252 * @param isIncognito Whether the request will happen in an incognito tab.
219 */ 253 */
220 public static boolean isGeoHeaderEnabledForUrl(String url, boolean isIncogni to) { 254 public static boolean isGeoHeaderEnabledForUrl(String url, boolean isIncogni to) {
221 return geoHeaderStateForUrl(url, isIncognito, false) == HEADER_ENABLED; 255 return geoHeaderStateForUrl(url, isIncognito, false) == HEADER_ENABLED;
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
253 * 3. The user has not disabled sharing location with this url, and 287 * 3. The user has not disabled sharing location with this url, and
254 * 4. There is a valid and recent location available. 288 * 4. There is a valid and recent location available.
255 * 289 *
256 * Returns null otherwise. 290 * Returns null otherwise.
257 * 291 *
258 * @param url The URL of the request with which this header will be sent. 292 * @param url The URL of the request with which this header will be sent.
259 * @param tab The Tab currently being accessed. 293 * @param tab The Tab currently being accessed.
260 * @return The X-Geo header string or null. 294 * @return The X-Geo header string or null.
261 */ 295 */
262 public static String getGeoHeader(String url, Tab tab) { 296 public static String getGeoHeader(String url, Tab tab) {
297 // TODO(lbargu): Refactor and simplify flow.
263 boolean isIncognito = tab.isIncognito(); 298 boolean isIncognito = tab.isIncognito();
264 boolean locationAttached = true; 299 Location locationToAttach = null;
265 Location location = null; 300 VisibleNetworks visibleNetworksToAttach = null;
266 long locationAge = Long.MAX_VALUE; 301 long locationAge = Long.MAX_VALUE;
267 @HeaderState int headerState = geoHeaderStateForUrl(url, isIncognito, tr ue); 302 @HeaderState int headerState = geoHeaderStateForUrl(url, isIncognito, tr ue);
303 // XGEO_VISIBLE_NETWORKS
304 // When this feature is enabled, we will send visible WiFi and Cell Acce ss Points as part of
305 // the X-GEO HTTP Header so that we can better position the client serve r side in the case
306 // where there is no lat/long or it's too old.
268 boolean isXGeoVisibleNetworksEnabled = 307 boolean isXGeoVisibleNetworksEnabled =
269 ChromeFeatureList.isEnabled(ChromeFeatureList.XGEO_VISIBLE_NETWO RKS); 308 ChromeFeatureList.isEnabled(ChromeFeatureList.XGEO_VISIBLE_NETWO RKS);
270 if (headerState == HEADER_ENABLED) { 309 if (headerState == HEADER_ENABLED) {
271 // Only send X-Geo header if there's a fresh location available. 310 // Only send X-Geo header if there's a fresh location available.
272 // Use flag controlling visible network changes to decide whether GP S location should be 311 // Use flag controlling visible network changes to decide whether GP S location should be
273 // included as a fallback. 312 // included as a fallback.
274 location = GeolocationTracker.getLastKnownLocation( 313 // TODO(lbargu): Measure timing here and to get visible networks.
314 locationToAttach = GeolocationTracker.getLastKnownLocation(
275 ContextUtils.getApplicationContext(), isXGeoVisibleNetworksE nabled); 315 ContextUtils.getApplicationContext(), isXGeoVisibleNetworksE nabled);
276 if (location == null) { 316 if (locationToAttach == null) {
277 recordHistogram(UMA_LOCATION_NOT_AVAILABLE); 317 recordHistogram(UMA_LOCATION_NOT_AVAILABLE);
278 locationAttached = false;
279 } else { 318 } else {
280 locationAge = GeolocationTracker.getLocationAge(location); 319 locationAge = GeolocationTracker.getLocationAge(locationToAttach );
281 if (locationAge > MAX_LOCATION_AGE) { 320 if (locationAge > MAX_LOCATION_AGE) {
321 // Do not attach the location
282 recordHistogram(UMA_LOCATION_STALE); 322 recordHistogram(UMA_LOCATION_STALE);
283 locationAttached = false; 323 locationToAttach = null;
324 } else {
325 recordHistogram(UMA_HEADER_SENT);
284 } 326 }
285 } 327 }
286 } else { 328
287 locationAttached = false; 329 // The header state is enabled, so this means we have app permission s, and the url is
330 // allowed to receive location. Before attempting to attach visible networks, check if
331 // network-based location is enabled.
332 if (isXGeoVisibleNetworksEnabled && isNetworkLocationEnabled()
333 && !isLocationFresh(locationToAttach)) {
334 visibleNetworksToAttach = VisibleNetworksTracker.getLastKnownVis ibleNetworks(
335 ContextUtils.getApplicationContext());
336 }
288 } 337 }
289 338
290 @LocationSource int locationSource = getLocationSource(); 339 @LocationSource int locationSource = getLocationSource();
291 @Permission int appPermission = getGeolocationPermission(tab); 340 @Permission int appPermission = getGeolocationPermission(tab);
292 @Permission int domainPermission = getDomainPermission(url, isIncognito) ; 341 @Permission int domainPermission = getDomainPermission(url, isIncognito) ;
293 342
294 // Record the permission state with a histogram. 343 // Record the permission state with a histogram.
295 recordPermissionHistogram( 344 recordPermissionHistogram(locationSource, appPermission, domainPermissio n,
296 locationSource, appPermission, domainPermission, locationAttache d, headerState); 345 locationToAttach != null, headerState);
297 346
298 if (locationSource != LOCATION_SOURCE_MASTER_OFF && appPermission != PER MISSION_BLOCKED 347 if (locationSource != LOCATION_SOURCE_MASTER_OFF && appPermission != PER MISSION_BLOCKED
299 && domainPermission != PERMISSION_BLOCKED && !isIncognito) { 348 && domainPermission != PERMISSION_BLOCKED && !isIncognito) {
300 // Record the Location Age with a histogram. 349 // Record the Location Age with a histogram.
301 recordLocationAgeHistogram(locationSource, locationAge); 350 recordLocationAgeHistogram(locationSource, locationAge);
302 long duration = sFirstLocationTime == Long.MAX_VALUE 351 long duration = sFirstLocationTime == Long.MAX_VALUE
303 ? 0 352 ? 0
304 : SystemClock.elapsedRealtime() - sFirstLocationTime; 353 : SystemClock.elapsedRealtime() - sFirstLocationTime;
305 // Record the Time Listening with a histogram. 354 // Record the Time Listening with a histogram.
306 recordTimeListeningHistogram(locationSource, locationAttached, durat ion); 355 recordTimeListeningHistogram(locationSource, locationToAttach != nul l, duration);
307 } 356 }
308 357
309 // Note that strictly speaking "location == null" is not needed here as the
310 // logic above prevents location being null when locationAttached is tru e.
311 // It is here to prevent problems if the logic above is changed.
312 if (!locationAttached || location == null) return null;
313
314 recordHistogram(UMA_HEADER_SENT);
315
316 // Timestamp in microseconds since the UNIX epoch.
317 long timestamp = location.getTime() * 1000;
318 // Latitude times 1e7.
319 int latitudeE7 = (int) (location.getLatitude() * 10000000);
320 // Longitude times 1e7.
321 int longitudeE7 = (int) (location.getLongitude() * 10000000);
322 // Radius of 68% accuracy in mm.
323 int radius = (int) (location.getAccuracy() * 1000);
324
325 // Encode location using ascii protobuf format followed by base64 encodi ng.
326 // https://goto.google.com/partner_location_proto
327 String locationAscii = String.format(Locale.US,
328 "role:1 producer:12 timestamp:%d latlng{latitude_e7:%d longitude _e7:%d} radius:%d",
329 timestamp, latitudeE7, longitudeE7, radius);
330 String locationAsciiEncoding =
331 new String(Base64.encode(locationAscii.getBytes(), Base64.NO_WRA P));
332 358
333 if (!isXGeoVisibleNetworksEnabled) { 359 if (!isXGeoVisibleNetworksEnabled) {
334 return XGEO_HEADER_PREFIX + LOCATION_ASCII_PREFIX + locationAsciiEnc oding; 360 String locationAsciiEncoding = encodeAsciiLocation(locationToAttach) ;
361 if (locationAsciiEncoding == null) return null;
362 return XGEO_HEADER_PREFIX + LOCATION_SEPARATOR + LOCATION_ASCII_PREF IX
363 + LOCATION_SEPARATOR + locationAsciiEncoding;
335 } 364 }
336 365
337 // Create a LatLng for the coordinates. 366 // Proto encoding
338 PartnerLocationDescriptor.LatLng latlng = new PartnerLocationDescriptor. LatLng(); 367 String locationProtoEncoding = encodeProtoLocation(locationToAttach);
339 latlng.latitudeE7 = latitudeE7; 368 String visibleNetworksProtoEncoding = encodeProtoVisibleNetworks(visible NetworksToAttach);
340 latlng.longitudeE7 = longitudeE7;
341 369
342 // Populate a LocationDescriptor with the LatLng. 370 if (locationProtoEncoding == null && visibleNetworksProtoEncoding == nul l) return null;
343 PartnerLocationDescriptor.LocationDescriptor locationDescriptor =
344 new PartnerLocationDescriptor.LocationDescriptor();
345 locationDescriptor.latlng = latlng;
346 // Include role, producer, timestamp and radius.
347 locationDescriptor.role = PartnerLocationDescriptor.CURRENT_LOCATION;
348 locationDescriptor.producer = PartnerLocationDescriptor.DEVICE_LOCATION;
349 locationDescriptor.timestamp = timestamp;
350 locationDescriptor.radius = (float) radius;
351 371
352 String locationProtoEncoding = Base64.encodeToString( 372 StringBuilder header = new StringBuilder(XGEO_HEADER_PREFIX);
353 MessageNano.toByteArray(locationDescriptor), Base64.NO_WRAP | Ba se64.URL_SAFE); 373 if (locationProtoEncoding != null) {
354 374 header.append(LOCATION_SEPARATOR).append(LOCATION_PROTO_PREFIX)
355 return XGEO_HEADER_PREFIX + LOCATION_PROTO_PREFIX + locationProtoEncodin g; 375 .append(LOCATION_SEPARATOR).append(locationProtoEncoding);
376 }
377 if (visibleNetworksProtoEncoding != null) {
378 header.append(LOCATION_SEPARATOR).append(LOCATION_PROTO_PREFIX)
379 .append(LOCATION_SEPARATOR).append(visibleNetworksProtoEncod ing);
380 }
381 return header.toString();
356 } 382 }
357 383
358 @CalledByNative 384 @CalledByNative
359 static boolean hasGeolocationPermission() { 385 static boolean hasGeolocationPermission() {
386 if (sUseAppPermissionGrantedForTesting) return sAppPermissionGrantedForT esting;
360 int pid = Process.myPid(); 387 int pid = Process.myPid();
361 int uid = Process.myUid(); 388 int uid = Process.myUid();
362 if (ApiCompatibilityUtils.checkPermission(ContextUtils.getApplicationCon text(), 389 if (ApiCompatibilityUtils.checkPermission(ContextUtils.getApplicationCon text(),
363 Manifest.permission.ACCESS_COARSE_LOCATION, pid, uid) 390 Manifest.permission.ACCESS_COARSE_LOCATION, pid, uid)
364 != PackageManager.PERMISSION_GRANTED) { 391 != PackageManager.PERMISSION_GRANTED) {
365 return false; 392 return false;
366 } 393 }
367 394
368 // Work around a bug in OnePlus2 devices running Lollipop, where the NET WORK_PROVIDER 395 // Work around a bug in OnePlus2 devices running Lollipop, where the NET WORK_PROVIDER
369 // incorrectly requires FINE_LOCATION permission (it should only require COARSE_LOCATION 396 // incorrectly requires FINE_LOCATION permission (it should only require COARSE_LOCATION
370 // permission). http://crbug.com/580733 397 // permission). http://crbug.com/580733
371 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M 398 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M
372 && ApiCompatibilityUtils.checkPermission(ContextUtils.getApplica tionContext(), 399 && ApiCompatibilityUtils.checkPermission(ContextUtils.getApplica tionContext(),
373 Manifest.permission.ACCESS_FINE_LOCATION, pid, uid) 400 Manifest.permission.ACCESS_FINE_LOCATION, pid, uid)
374 != PackageManager.PERMISSION_GRANTED) { 401 != PackageManager.PERMISSION_GRANTED) {
375 return false; 402 return false;
376 } 403 }
377 404
378 return true; 405 return true;
379 } 406 }
380 407
381 /** 408 /**
382 * Returns the app level geolocation permission. 409 * Returns the app level geolocation permission.
383 * This permission can be either granted, blocked or prompt. 410 * This permission can be either granted, blocked or prompt.
384 */ 411 */
385 @Permission 412 @Permission
386 static int getGeolocationPermission(Tab tab) { 413 static int getGeolocationPermission(Tab tab) {
414 if (sUseAppPermissionGrantedForTesting) {
415 return sAppPermissionGrantedForTesting ? PERMISSION_GRANTED : PERMIS SION_BLOCKED;
416 }
387 if (hasGeolocationPermission()) return PERMISSION_GRANTED; 417 if (hasGeolocationPermission()) return PERMISSION_GRANTED;
388 return tab.getWindowAndroid().canRequestPermission( 418 return tab.getWindowAndroid().canRequestPermission(
389 Manifest.permission.ACCESS_COARSE_LOCATION) 419 Manifest.permission.ACCESS_COARSE_LOCATION)
390 ? PERMISSION_PROMPT 420 ? PERMISSION_PROMPT
391 : PERMISSION_BLOCKED; 421 : PERMISSION_BLOCKED;
392 } 422 }
393 423
394 /** 424 /**
395 * Returns true if the user has disabled sharing their location with url (e. g. via the 425 * Returns true if the user has disabled sharing their location with url (e. g. via the
396 * geolocation infobar). 426 * geolocation infobar).
(...skipping 12 matching lines...) Expand all
409 /** 439 /**
410 * Returns the location permission for sharing their location with url (e.g. via the 440 * Returns the location permission for sharing their location with url (e.g. via the
411 * geolocation infobar). 441 * geolocation infobar).
412 */ 442 */
413 static ContentSetting locationContentSettingForUrl(Uri uri, boolean isIncogn ito) { 443 static ContentSetting locationContentSettingForUrl(Uri uri, boolean isIncogn ito) {
414 GeolocationInfo locationSettings = new GeolocationInfo(uri.toString(), n ull, isIncognito); 444 GeolocationInfo locationSettings = new GeolocationInfo(uri.toString(), n ull, isIncognito);
415 ContentSetting locationPermission = locationSettings.getContentSetting() ; 445 ContentSetting locationPermission = locationSettings.getContentSetting() ;
416 return locationPermission; 446 return locationPermission;
417 } 447 }
418 448
449 @VisibleForTesting
450 static void setLocationSourceForTesting(int locationSourceForTesting) {
451 sLocationSourceForTesting = locationSourceForTesting;
452 sUseLocationSourceForTesting = true;
453 }
454
455 @VisibleForTesting
456 static void setAppPermissionGrantedForTesting(boolean appPermissionGrantedFo rTesting) {
457 sAppPermissionGrantedForTesting = appPermissionGrantedForTesting;
458 sUseAppPermissionGrantedForTesting = true;
459 }
460
419 /** Records a data point for the Geolocation.HeaderSentOrNot histogram. */ 461 /** Records a data point for the Geolocation.HeaderSentOrNot histogram. */
420 private static void recordHistogram(int result) { 462 private static void recordHistogram(int result) {
421 RecordHistogram.recordEnumeratedHistogram("Geolocation.HeaderSentOrNot", result, UMA_MAX); 463 RecordHistogram.recordEnumeratedHistogram("Geolocation.HeaderSentOrNot", result, UMA_MAX);
422 } 464 }
423 465
424 /** Returns the location source. */ 466 /** Returns the location source. */
425 @LocationSource 467 @LocationSource
426 // We should replace our usage of LOCATION_PROVIDERS_ALLOWED when the min AP I is 19. 468 // We should replace our usage of LOCATION_PROVIDERS_ALLOWED when the min AP I is 19.
427 @SuppressWarnings("deprecation") 469 @SuppressWarnings("deprecation")
428 private static int getLocationSource() { 470 private static int getLocationSource() {
471 if (sUseLocationSourceForTesting) return sLocationSourceForTesting;
472
429 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { 473 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
430 int locationMode; 474 int locationMode;
431 try { 475 try {
432 locationMode = Settings.Secure.getInt( 476 locationMode = Settings.Secure.getInt(
433 ContextUtils.getApplicationContext().getContentResolver( ), 477 ContextUtils.getApplicationContext().getContentResolver( ),
434 Settings.Secure.LOCATION_MODE); 478 Settings.Secure.LOCATION_MODE);
435 } catch (Settings.SettingNotFoundException e) { 479 } catch (Settings.SettingNotFoundException e) {
436 Log.e(TAG, "Error getting the LOCATION_MODE"); 480 Log.e(TAG, "Error getting the LOCATION_MODE");
437 return LOCATION_SOURCE_MASTER_OFF; 481 return LOCATION_SOURCE_MASTER_OFF;
438 } 482 }
(...skipping 16 matching lines...) Expand all
455 } else if (locationProviders.contains(LocationManager.GPS_PROVIDER)) { 499 } else if (locationProviders.contains(LocationManager.GPS_PROVIDER)) {
456 return LOCATION_SOURCE_GPS_ONLY; 500 return LOCATION_SOURCE_GPS_ONLY;
457 } else if (locationProviders.contains(LocationManager.NETWORK_PROVID ER)) { 501 } else if (locationProviders.contains(LocationManager.NETWORK_PROVID ER)) {
458 return LOCATION_SOURCE_BATTERY_SAVING; 502 return LOCATION_SOURCE_BATTERY_SAVING;
459 } else { 503 } else {
460 return LOCATION_SOURCE_MASTER_OFF; 504 return LOCATION_SOURCE_MASTER_OFF;
461 } 505 }
462 } 506 }
463 } 507 }
464 508
509 private static boolean isNetworkLocationEnabled() {
510 int locationSource = getLocationSource();
511 return locationSource == LOCATION_SOURCE_HIGH_ACCURACY
512 || locationSource == LOCATION_SOURCE_BATTERY_SAVING;
513 }
514
515 private static boolean isLocationFresh(@Nullable Location location) {
516 return location != null
517 && GeolocationTracker.getLocationAge(location) <= REFRESH_LOCATI ON_AGE;
518 }
519
465 /** 520 /**
466 * Returns the domain permission as either granted, blocked or prompt. 521 * Returns the domain permission as either granted, blocked or prompt.
467 * This is based upon the location permission for sharing their location wit h url (e.g. via the 522 * This is based upon the location permission for sharing their location wit h url (e.g. via the
468 * geolocation infobar). 523 * geolocation infobar).
469 */ 524 */
470 @Permission 525 @Permission
471 private static int getDomainPermission(String url, boolean isIncognito) { 526 private static int getDomainPermission(String url, boolean isIncognito) {
472 ContentSetting domainPermission = locationContentSettingForUrl(Uri.parse (url), isIncognito); 527 ContentSetting domainPermission = locationContentSettingForUrl(Uri.parse (url), isIncognito);
473 switch (domainPermission) { 528 switch (domainPermission) {
474 case ALLOW: 529 case ALLOW:
(...skipping 193 matching lines...) Expand 10 before | Expand all | Expand 10 after
668 Log.e(TAG, "Unexpected locationSource: " + locationSource); 723 Log.e(TAG, "Unexpected locationSource: " + locationSource);
669 assert false : "Unexpected locationSource: " + locationSource; 724 assert false : "Unexpected locationSource: " + locationSource;
670 return; 725 return;
671 } 726 }
672 long durationSeconds = durationMillis / 1000; 727 long durationSeconds = durationMillis / 1000;
673 int duration = durationSeconds >= (long) Integer.MAX_VALUE ? Integer.MAX _VALUE 728 int duration = durationSeconds >= (long) Integer.MAX_VALUE ? Integer.MAX _VALUE
674 : (int) durat ionSeconds; 729 : (int) durat ionSeconds;
675 RecordHistogram.recordCustomCountHistogram( 730 RecordHistogram.recordCustomCountHistogram(
676 name, duration, 1, LOCATION_AGE_HISTOGRAM_MAX_SECONDS, 50); 731 name, duration, 1, LOCATION_AGE_HISTOGRAM_MAX_SECONDS, 50);
677 } 732 }
733
734 /**
735 * Encodes location into ascii encoding.
736 */
737 @Nullable
738 @VisibleForTesting
739 static String encodeAsciiLocation(@Nullable Location location) {
740 if (location == null) return null;
741
742 // Timestamp in microseconds since the UNIX epoch.
743 long timestamp = location.getTime() * 1000;
744 // Latitude times 1e7.
745 int latitudeE7 = (int) (location.getLatitude() * 10000000);
746 // Longitude times 1e7.
747 int longitudeE7 = (int) (location.getLongitude() * 10000000);
748 // Radius of 68% accuracy in mm.
749 int radius = (int) (location.getAccuracy() * 1000);
750
751 // Encode location using ascii protobuf format followed by base64 encodi ng.
752 // https://goto.google.com/partner_location_proto
753 String locationAscii = String.format(Locale.US,
754 "role:1 producer:12 timestamp:%d latlng{latitude_e7:%d longitude _e7:%d}"
755 + " radius:%d",
756 timestamp, latitudeE7, longitudeE7, radius);
757 return new String(Base64.encode(locationAscii.getBytes(), Base64.NO_WRAP ));
758 }
759
760 /**
761 * Encodes location into proto encoding.
762 */
763 @Nullable
764 @VisibleForTesting
765 static String encodeProtoLocation(@Nullable Location location) {
766 if (location == null) return null;
767
768 // Timestamp in microseconds since the UNIX epoch.
769 long timestamp = location.getTime() * 1000;
770 // Latitude times 1e7.
771 int latitudeE7 = (int) (location.getLatitude() * 10000000);
772 // Longitude times 1e7.
773 int longitudeE7 = (int) (location.getLongitude() * 10000000);
774 // Radius of 68% accuracy in mm.
775 int radius = (int) (location.getAccuracy() * 1000);
776
777 // Create a LatLng for the coordinates.
778 PartnerLocationDescriptor.LatLng latlng = new PartnerLocationDescriptor. LatLng();
779 latlng.latitudeE7 = latitudeE7;
780 latlng.longitudeE7 = longitudeE7;
781
782 // Populate a LocationDescriptor with the LatLng.
783 PartnerLocationDescriptor.LocationDescriptor locationDescriptor =
784 new PartnerLocationDescriptor.LocationDescriptor();
785 locationDescriptor.latlng = latlng;
786 // Include role, producer, timestamp and radius.
787 locationDescriptor.role = PartnerLocationDescriptor.CURRENT_LOCATION;
788 locationDescriptor.producer = PartnerLocationDescriptor.DEVICE_LOCATION;
789 locationDescriptor.timestamp = timestamp;
790 locationDescriptor.radius = (float) radius;
791 return encodeLocationDescriptor(locationDescriptor);
792 }
793
794 /**
795 * Encodes the given proto location descriptor into a BASE64 URL_SAFE encodi ng.
796 */
797 private static String encodeLocationDescriptor(
798 PartnerLocationDescriptor.LocationDescriptor locationDescriptor) {
799 return Base64.encodeToString(
800 MessageNano.toByteArray(locationDescriptor), Base64.NO_WRAP | Ba se64.URL_SAFE);
801 }
802
803 /**
804 * Encodes visible networks in proto encoding.
805 */
806 @Nullable
807 @VisibleForTesting
808 static String encodeProtoVisibleNetworks(@Nullable VisibleNetworks visibleNe tworks) {
809 VisibleNetworks visibleNetworksToEncode = trimVisibleNetworks(visibleNet works);
810 if (visibleNetworksToEncode == null) {
811 // No data to encode.
812 return null;
813 }
814 VisibleWifi connectedWifi = visibleNetworksToEncode.connectedWifi();
815 VisibleCell connectedCell = visibleNetworksToEncode.connectedCell();
816 Set<VisibleWifi> visibleWifis = visibleNetworksToEncode.allVisibleWifis( );
817 Set<VisibleCell> visibleCells = visibleNetworksToEncode.allVisibleCells( );
818
819 int numVisibleNetworks = (connectedWifi != null ? 1 : 0)
820 + (visibleWifis != null ? visibleWifis.size() : 0) + (connectedC ell != null ? 1 : 0)
821 + (visibleCells != null ? visibleCells.size() : 0);
822 if (numVisibleNetworks == 0) {
823 // No data to encode.
824 return null;
825 }
826
827 int i = 0;
828 PartnerLocationDescriptor.VisibleNetwork[] protoNetworks =
829 new PartnerLocationDescriptor.VisibleNetwork[numVisibleNetworks] ;
830 if (connectedWifi != null) {
831 protoNetworks[i++] = connectedWifi.toProto(true);
832 }
833 if (visibleWifis != null) {
834 for (VisibleWifi visibleWifi : visibleWifis) {
835 protoNetworks[i++] = visibleWifi.toProto(false);
836 }
837 }
838 if (connectedCell != null) {
839 protoNetworks[i++] = connectedCell.toProto(true);
840 }
841 if (visibleCells != null) {
842 for (VisibleCell visibleCell : visibleCells) {
843 protoNetworks[i++] = visibleCell.toProto(false);
844 }
845 }
846
847 PartnerLocationDescriptor.LocationDescriptor locationDescriptor =
848 new PartnerLocationDescriptor.LocationDescriptor();
849 locationDescriptor.role = PartnerLocationDescriptor.CURRENT_LOCATION;
850 locationDescriptor.producer = PartnerLocationDescriptor.DEVICE_LOCATION;
851 locationDescriptor.visibleNetwork = protoNetworks;
852
853 return encodeLocationDescriptor(locationDescriptor);
854 }
855
856 @Nullable
857 @VisibleForTesting
858 static VisibleNetworks trimVisibleNetworks(@Nullable VisibleNetworks visible Networks) {
859 if (visibleNetworks == null || visibleNetworks.isEmpty()) {
860 return null;
861 }
862 // Trim visible networks to only include a limited number of visible not -conntected networks
863 // based on flag.
864 VisibleCell connectedCell = visibleNetworks.connectedCell();
865 VisibleWifi connectedWifi = visibleNetworks.connectedWifi();
866 Set<VisibleCell> visibleCells = visibleNetworks.allVisibleCells();
867 Set<VisibleWifi> visibleWifis = visibleNetworks.allVisibleWifis();
868 VisibleCell extraVisibleCell = null;
869 VisibleWifi extraVisibleWifi = null;
870 if (shouldExcludeVisibleWifi(connectedWifi)) {
871 // Trim the connected wifi.
872 connectedWifi = null;
873 }
874 // Select the extra visible cell.
875 if (visibleCells != null) {
876 for (VisibleCell candidateCell : visibleCells) {
877 if (ApiCompatibilityUtils.objectEquals(connectedCell, candidateC ell)) {
878 // Do not include this candidate cell, since its already the connected one.
879 continue;
880 }
881 // Add it and since we only want one, stop iterating over other cells.
882 extraVisibleCell = candidateCell;
883 break;
884 }
885 }
886 // Select the extra visible wifi.
887 if (visibleWifis != null) {
888 for (VisibleWifi candidateWifi : visibleWifis) {
889 if (shouldExcludeVisibleWifi(candidateWifi)) {
890 // Do not include this candidate wifi.
891 continue;
892 }
893 if (ApiCompatibilityUtils.objectEquals(connectedWifi, candidateW ifi)) {
894 // Replace the connected, since the candidate will have leve l. This is because
895 // the android APIs exposing connected WIFI do not expose le vel, while the ones
896 // exposing visible wifis expose level.
897 connectedWifi = candidateWifi;
898 // Do not include this candidate wifi, since its already the connected one.
899 continue;
900 }
901 // Keep the one with stronger level (since it's negative, this i s the smaller value)
902 if (extraVisibleWifi == null || extraVisibleWifi.level() > candi dateWifi.level()) {
903 extraVisibleWifi = candidateWifi;
904 }
905 }
906 }
907
908 if (connectedCell == null && connectedWifi == null && extraVisibleCell = = null
909 && extraVisibleWifi == null) {
910 return null;
911 }
912
913 return VisibleNetworks.create(connectedWifi, connectedCell,
914 extraVisibleWifi != null ? CollectionUtil.newHashSet(extraVisibl eWifi) : null,
915 extraVisibleCell != null ? CollectionUtil.newHashSet(extraVisibl eCell) : null);
916 }
917
918 /**
919 * Returns whether the provided {@link VisibleWifi} should be excluded. This can happen if the
920 * network is opted out (ssid contains "_nomap" or "_optout").
921 */
922 private static boolean shouldExcludeVisibleWifi(@Nullable VisibleWifi visibl eWifi) {
923 if (visibleWifi == null || visibleWifi.bssid() == null) {
924 return true;
925 }
926 String ssid = visibleWifi.ssid();
927 if (ssid == null) {
928 // No ssid, so the networks is not opted out and should not be exclu ded.
929 return false;
930 }
931 // Optimization to avoid costly toLowerCase() in most cases.
932 if (ssid.indexOf('_') < 0) {
933 // No "_nomap" or "_optout".
934 return false;
935 }
936 String ssidLowerCase = ssid.toLowerCase(Locale.ENGLISH);
937 return ssidLowerCase.contains(SSID_NOMAP) || ssidLowerCase.contains(SSID _OPTOUT);
938 }
678 } 939 }
OLDNEW
« no previous file with comments | « no previous file | chrome/android/java/src/org/chromium/chrome/browser/omnibox/geo/GeolocationTracker.java » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698