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

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
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.ContextUtils; 22 import org.chromium.base.ContextUtils;
23 import org.chromium.base.Log; 23 import org.chromium.base.Log;
24 import org.chromium.base.VisibleForTesting;
24 import org.chromium.base.annotations.CalledByNative; 25 import org.chromium.base.annotations.CalledByNative;
25 import org.chromium.base.annotations.SuppressFBWarnings; 26 import org.chromium.base.annotations.SuppressFBWarnings;
26 import org.chromium.base.metrics.RecordHistogram; 27 import org.chromium.base.metrics.RecordHistogram;
27 import org.chromium.chrome.browser.ChromeFeatureList; 28 import org.chromium.chrome.browser.ChromeFeatureList;
28 import org.chromium.chrome.browser.UrlConstants; 29 import org.chromium.chrome.browser.UrlConstants;
30 import org.chromium.chrome.browser.omnibox.geo.VisibleNetworks.VisibleCell;
31 import org.chromium.chrome.browser.omnibox.geo.VisibleNetworks.VisibleWifi;
29 import org.chromium.chrome.browser.preferences.website.ContentSetting; 32 import org.chromium.chrome.browser.preferences.website.ContentSetting;
30 import org.chromium.chrome.browser.preferences.website.GeolocationInfo; 33 import org.chromium.chrome.browser.preferences.website.GeolocationInfo;
31 import org.chromium.chrome.browser.preferences.website.WebsitePreferenceBridge; 34 import org.chromium.chrome.browser.preferences.website.WebsitePreferenceBridge;
32 import org.chromium.chrome.browser.tab.Tab; 35 import org.chromium.chrome.browser.tab.Tab;
33 import org.chromium.chrome.browser.util.UrlUtilities; 36 import org.chromium.chrome.browser.util.UrlUtilities;
34 37
35 import java.lang.annotation.Retention; 38 import java.lang.annotation.Retention;
36 import java.lang.annotation.RetentionPolicy; 39 import java.lang.annotation.RetentionPolicy;
40 import java.util.Arrays;
41 import java.util.HashSet;
37 import java.util.Locale; 42 import java.util.Locale;
43 import java.util.Set;
38 import java.util.concurrent.TimeUnit; 44 import java.util.concurrent.TimeUnit;
39 45
46 import javax.annotation.Nullable;
47
40 /** 48 /**
41 * Provides methods for building the X-Geo HTTP header, which provides device lo cation to a server 49 * Provides methods for building the X-Geo HTTP header, which provides device lo cation to a server
42 * when making an HTTP request. 50 * when making an HTTP request.
43 * 51 *
44 * X-Geo header spec: https://goto.google.com/xgeospec. 52 * X-Geo header spec: https://goto.google.com/xgeospec.
45 */ 53 */
46 public class GeolocationHeader { 54 public class GeolocationHeader {
47 private static final String TAG = "GeolocationHeader"; 55 private static final String TAG = "GeolocationHeader";
48 56
49 // Values for the histogram Geolocation.HeaderSentOrNot. Values 1, 5, 6, and 7 are defined in 57 // 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, 149 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, 150 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, 151 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, 152 UMA_PERM_MASTER_OFF_APP_PROMPT_DOMAIN_BLOCKED,
145 UMA_PERM_MASTER_OFF_APP_BLOCKED_DOMAIN_YES, 153 UMA_PERM_MASTER_OFF_APP_BLOCKED_DOMAIN_YES,
146 UMA_PERM_MASTER_OFF_APP_BLOCKED_DOMAIN_PROMPT, 154 UMA_PERM_MASTER_OFF_APP_BLOCKED_DOMAIN_PROMPT,
147 UMA_PERM_MASTER_OFF_APP_BLOCKED_DOMAIN_BLOCKED, UMA_PERM_UNSUITABLE_ URL, 155 UMA_PERM_MASTER_OFF_APP_BLOCKED_DOMAIN_BLOCKED, UMA_PERM_UNSUITABLE_ URL,
148 UMA_PERM_NOT_HTTPS}) 156 UMA_PERM_NOT_HTTPS})
149 public @interface UmaPermission {} 157 public @interface UmaPermission {}
150 158
151 private static final int LOCATION_SOURCE_HIGH_ACCURACY = 0; 159 @VisibleForTesting
152 private static final int LOCATION_SOURCE_BATTERY_SAVING = 1; 160 static final int LOCATION_SOURCE_HIGH_ACCURACY = 0;
153 private static final int LOCATION_SOURCE_GPS_ONLY = 2; 161 @VisibleForTesting
154 private static final int LOCATION_SOURCE_MASTER_OFF = 3; 162 static final int LOCATION_SOURCE_BATTERY_SAVING = 1;
163 @VisibleForTesting
164 static final int LOCATION_SOURCE_GPS_ONLY = 2;
165 @VisibleForTesting
166 static final int LOCATION_SOURCE_MASTER_OFF = 3;
155 @Retention(RetentionPolicy.SOURCE) 167 @Retention(RetentionPolicy.SOURCE)
156 @IntDef({LOCATION_SOURCE_HIGH_ACCURACY, LOCATION_SOURCE_BATTERY_SAVING, 168 @IntDef({LOCATION_SOURCE_HIGH_ACCURACY, LOCATION_SOURCE_BATTERY_SAVING,
157 LOCATION_SOURCE_GPS_ONLY, LOCATION_SOURCE_MASTER_OFF}) 169 LOCATION_SOURCE_GPS_ONLY, LOCATION_SOURCE_MASTER_OFF})
158 private @interface LocationSource {} 170 private @interface LocationSource {}
159 171
160 private static final int PERMISSION_GRANTED = 0; 172 private static final int PERMISSION_GRANTED = 0;
161 private static final int PERMISSION_PROMPT = 1; 173 private static final int PERMISSION_PROMPT = 1;
162 private static final int PERMISSION_BLOCKED = 2; 174 private static final int PERMISSION_BLOCKED = 2;
163 @Retention(RetentionPolicy.SOURCE) 175 @Retention(RetentionPolicy.SOURCE)
164 @IntDef({PERMISSION_GRANTED, PERMISSION_PROMPT, PERMISSION_BLOCKED}) 176 @IntDef({PERMISSION_GRANTED, PERMISSION_PROMPT, PERMISSION_BLOCKED})
(...skipping 19 matching lines...) Expand all
184 196
185 /** The maximum age in milliseconds of a location before we'll request a ref resh. */ 197 /** 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 198 private static final int REFRESH_LOCATION_AGE = 5 * 60 * 1000; // 5 minutes
187 199
188 /** The X-Geo header prefix, preceding any location descriptors */ 200 /** The X-Geo header prefix, preceding any location descriptors */
189 private static final String XGEO_HEADER_PREFIX = "X-Geo: "; 201 private static final String XGEO_HEADER_PREFIX = "X-Geo: ";
190 202
191 /** The location descriptor prefix used in the X-Geo header to specify a pro to wire encoding */ 203 /** 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 "; 204 private static final String LOCATION_PROTO_PREFIX = "w ";
193 205
206 /** The location descriptor separator used in the X-Geo header to upload mul tiple descriptors */
207 private static final String LOCATION_PROTO_SEPARATOR = " ";
208
194 /** The location descriptor prefix used in the X-Geo header to specify an AS CII encoding */ 209 /** 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 "; 210 private static final String LOCATION_ASCII_PREFIX = "a ";
196 211
197 /** The time of the first location refresh. Contains Long.MAX_VALUE if not s et. */ 212 /** The time of the first location refresh. Contains Long.MAX_VALUE if not s et. */
198 private static long sFirstLocationTime = Long.MAX_VALUE; 213 private static long sFirstLocationTime = Long.MAX_VALUE;
199 214
215 /** Present in WiFi SSID that should not be mapped */
216 private static final String SSID_NOMAP = "_nomap";
217
218 /** Present in WiFi SSID that opted out */
219 private static final String SSID_OPTOUT = "_optout";
220
221 private static int sLocationSourceForTesting;
222 private static boolean sUseLocationSourceForTesting;
223
224 private static boolean sAppPermissionGrantedForTesting;
225 private static boolean sUseAppPermissionGrantedForTesting;
226
200 /** 227 /**
201 * Requests a location refresh so that a valid location will be available fo r constructing 228 * 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). 229 * an X-Geo header in the near future (i.e. within 5 minutes).
203 */ 230 */
204 public static void primeLocationForGeoHeader() { 231 public static void primeLocationForGeoHeader() {
205 if (!hasGeolocationPermission()) return; 232 if (!hasGeolocationPermission()) return;
206 233
207 if (sFirstLocationTime == Long.MAX_VALUE) { 234 if (sFirstLocationTime == Long.MAX_VALUE) {
208 sFirstLocationTime = SystemClock.elapsedRealtime(); 235 sFirstLocationTime = SystemClock.elapsedRealtime();
209 } 236 }
210 GeolocationTracker.refreshLastKnownLocation( 237 GeolocationTracker.refreshLastKnownLocation(
211 ContextUtils.getApplicationContext(), REFRESH_LOCATION_AGE); 238 ContextUtils.getApplicationContext(), REFRESH_LOCATION_AGE);
239
240 // Only refresh visible networks if enabled.
241 if (ChromeFeatureList.isEnabled(ChromeFeatureList.XGEO_VISIBLE_NETWORKS) ) {
242 VisibleNetworksTracker.refreshLastKnownVisibleNetworks(
243 ContextUtils.getApplicationContext(), REFRESH_LOCATION_AGE);
244 }
212 } 245 }
213 246
214 /** 247 /**
215 * Returns whether the X-Geo header is allowed to be sent for the current UR L. 248 * Returns whether the X-Geo header is allowed to be sent for the current UR L.
216 * 249 *
217 * @param url The URL of the request with which this header will be sent. 250 * @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. 251 * @param isIncognito Whether the request will happen in an incognito tab.
219 */ 252 */
220 public static boolean isGeoHeaderEnabledForUrl(String url, boolean isIncogni to) { 253 public static boolean isGeoHeaderEnabledForUrl(String url, boolean isIncogni to) {
221 return geoHeaderStateForUrl(url, isIncognito, false) == HEADER_ENABLED; 254 return geoHeaderStateForUrl(url, isIncognito, false) == HEADER_ENABLED;
(...skipping 30 matching lines...) Expand all
252 * 2. The url is a google search URL (e.g. www.google.co.uk/search?q=cars), and 285 * 2. The url is a google search URL (e.g. www.google.co.uk/search?q=cars), and
253 * 3. The user has not disabled sharing location with this url, and 286 * 3. The user has not disabled sharing location with this url, and
254 * 4. There is a valid and recent location available. 287 * 4. There is a valid and recent location available.
255 * 288 *
256 * Returns null otherwise. 289 * Returns null otherwise.
257 * 290 *
258 * @param url The URL of the request with which this header will be sent. 291 * @param url The URL of the request with which this header will be sent.
259 * @param tab The Tab currently being accessed. 292 * @param tab The Tab currently being accessed.
260 * @return The X-Geo header string or null. 293 * @return The X-Geo header string or null.
261 */ 294 */
262 public static String getGeoHeader(String url, Tab tab) { 295 public static String getGeoHeader(String url, Tab tab) {
dougt 2017/05/18 02:33:15 This method is getting a bit unwieldy. We should
lbargu 2017/05/18 15:14:09 Agree. Already added all new functionality to priv
263 boolean isIncognito = tab.isIncognito(); 296 boolean isIncognito = tab.isIncognito();
264 boolean locationAttached = true; 297 Location locationToAttach = null;
265 Location location = null; 298 VisibleNetworks visibleNetworksToAttach = null;
266 long locationAge = Long.MAX_VALUE; 299 long locationAge = Long.MAX_VALUE;
267 @HeaderState int headerState = geoHeaderStateForUrl(url, isIncognito, tr ue); 300 @HeaderState int headerState = geoHeaderStateForUrl(url, isIncognito, tr ue);
268 boolean isXGeoVisibleNetworksEnabled = 301 boolean isXGeoVisibleNetworksEnabled =
269 ChromeFeatureList.isEnabled(ChromeFeatureList.XGEO_VISIBLE_NETWO RKS); 302 ChromeFeatureList.isEnabled(ChromeFeatureList.XGEO_VISIBLE_NETWO RKS);
dougt 2017/05/18 02:33:15 I should have caught this in the last review, but
lbargu 2017/05/18 15:14:09 Done.
270 if (headerState == HEADER_ENABLED) { 303 if (headerState == HEADER_ENABLED) {
271 // Only send X-Geo header if there's a fresh location available. 304 // 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 305 // Use flag controlling visible network changes to decide whether GP S location should be
273 // included as a fallback. 306 // included as a fallback.
274 location = GeolocationTracker.getLastKnownLocation( 307 locationToAttach = GeolocationTracker.getLastKnownLocation(
dougt 2017/05/18 02:33:15 Since this is on the hot path, I think we should c
lbargu 2017/05/18 15:14:08 Added TODO. Will add metrics in follow up. For now
275 ContextUtils.getApplicationContext(), isXGeoVisibleNetworksE nabled); 308 ContextUtils.getApplicationContext(), isXGeoVisibleNetworksE nabled);
276 if (location == null) { 309 if (locationToAttach == null) {
277 recordHistogram(UMA_LOCATION_NOT_AVAILABLE); 310 recordHistogram(UMA_LOCATION_NOT_AVAILABLE);
278 locationAttached = false;
279 } else { 311 } else {
280 locationAge = GeolocationTracker.getLocationAge(location); 312 locationAge = GeolocationTracker.getLocationAge(locationToAttach );
281 if (locationAge > MAX_LOCATION_AGE) { 313 if (locationAge > MAX_LOCATION_AGE) {
314 // Do not attach the location
282 recordHistogram(UMA_LOCATION_STALE); 315 recordHistogram(UMA_LOCATION_STALE);
283 locationAttached = false; 316 locationToAttach = null;
317 } else {
318 recordHistogram(UMA_HEADER_SENT);
284 } 319 }
285 } 320 }
286 } else { 321
287 locationAttached = false; 322 // The header state is enabled, so this means we have app permission s, and the url is
323 // allowed to receive location. Before attempting to attach visible networks, check if
324 // the device location settigs are in High Accuracy or Battery Savin g, meaning the
325 // device is allowed to use network-based location.
326 if (isXGeoVisibleNetworksEnabled
327 && (getLocationSource() == LOCATION_SOURCE_HIGH_ACCURACY
328 || getLocationSource() == LOCATION_SOURCE_BATTERY _SAVING)) {
329 if (locationToAttach == null
330 || GeolocationTracker.getLocationAge(locationToAttach)
331 > REFRESH_LOCATION_AGE) {
dougt 2017/05/18 02:33:15 I would have turned this logic around a bit and ju
lbargu 2017/05/18 15:14:09 Moved checks to private methods. Should be easier
332 // We should attach visible networks in this case, since per missions are good
333 // but we didn't attach location or it's older than the refr esh time.
dougt 2017/05/18 02:33:15 no need for this comment.
lbargu 2017/05/18 15:14:08 Done.
334 visibleNetworksToAttach = VisibleNetworksTracker.getLastKnow nVisibleNetworks(
335 ContextUtils.getApplicationContext());
336 }
337 }
288 } 338 }
289 339
290 @LocationSource int locationSource = getLocationSource(); 340 @LocationSource int locationSource = getLocationSource();
291 @Permission int appPermission = getGeolocationPermission(tab); 341 @Permission int appPermission = getGeolocationPermission(tab);
292 @Permission int domainPermission = getDomainPermission(url, isIncognito) ; 342 @Permission int domainPermission = getDomainPermission(url, isIncognito) ;
293 343
294 // Record the permission state with a histogram. 344 // Record the permission state with a histogram.
295 recordPermissionHistogram( 345 recordPermissionHistogram(locationSource, appPermission, domainPermissio n,
296 locationSource, appPermission, domainPermission, locationAttache d, headerState); 346 locationToAttach != null, headerState);
297 347
298 if (locationSource != LOCATION_SOURCE_MASTER_OFF && appPermission != PER MISSION_BLOCKED 348 if (locationSource != LOCATION_SOURCE_MASTER_OFF && appPermission != PER MISSION_BLOCKED
299 && domainPermission != PERMISSION_BLOCKED && !isIncognito) { 349 && domainPermission != PERMISSION_BLOCKED && !isIncognito) {
300 // Record the Location Age with a histogram. 350 // Record the Location Age with a histogram.
301 recordLocationAgeHistogram(locationSource, locationAge); 351 recordLocationAgeHistogram(locationSource, locationAge);
302 long duration = sFirstLocationTime == Long.MAX_VALUE 352 long duration = sFirstLocationTime == Long.MAX_VALUE
303 ? 0 353 ? 0
304 : SystemClock.elapsedRealtime() - sFirstLocationTime; 354 : SystemClock.elapsedRealtime() - sFirstLocationTime;
305 // Record the Time Listening with a histogram. 355 // Record the Time Listening with a histogram.
306 recordTimeListeningHistogram(locationSource, locationAttached, durat ion); 356 recordTimeListeningHistogram(locationSource, locationToAttach != nul l, duration);
307 } 357 }
308 358
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 359
333 if (!isXGeoVisibleNetworksEnabled) { 360 if (!isXGeoVisibleNetworksEnabled) {
334 return XGEO_HEADER_PREFIX + LOCATION_ASCII_PREFIX + locationAsciiEnc oding; 361 if (locationToAttach != null) {
dougt 2017/05/18 02:33:15 nit: avoid the block, and just return early: if (
lbargu 2017/05/18 15:14:09 Done.
362 // Timestamp in microseconds since the UNIX epoch.
363 long timestamp = locationToAttach.getTime() * 1000;
364 // Latitude times 1e7.
365 int latitudeE7 = (int) (locationToAttach.getLatitude() * 1000000 0);
366 // Longitude times 1e7.
367 int longitudeE7 = (int) (locationToAttach.getLongitude() * 10000 000);
368 // Radius of 68% accuracy in mm.
369 int radius = (int) (locationToAttach.getAccuracy() * 1000);
370
371 // Encode location using ascii protobuf format followed by base6 4 encoding.
372 // https://goto.google.com/partner_location_proto
373 String locationAscii = String.format(Locale.US,
374 "role:1 producer:12 timestamp:%d latlng{latitude_e7:%d l ongitude_e7:%d}"
375 + " radius:%d",
376 timestamp, latitudeE7, longitudeE7, radius);
377 String locationAsciiEncoding =
378 new String(Base64.encode(locationAscii.getBytes(), Base6 4.NO_WRAP));
379 return XGEO_HEADER_PREFIX + LOCATION_ASCII_PREFIX + locationAsci iEncoding;
380 }
381 return null;
335 } 382 }
336 383
337 // Create a LatLng for the coordinates. 384 // Proto encoding
338 PartnerLocationDescriptor.LatLng latlng = new PartnerLocationDescriptor. LatLng(); 385 String locationProtoEncoding = encodeProtoLocation(locationToAttach);
339 latlng.latitudeE7 = latitudeE7; 386 String visibleNetworksProtoEncoding = encodeProtoVisibleNetworks(visible NetworksToAttach);
340 latlng.longitudeE7 = longitudeE7;
341 387
342 // Populate a LocationDescriptor with the LatLng. 388 if (locationProtoEncoding != null) {
343 PartnerLocationDescriptor.LocationDescriptor locationDescriptor = 389 if (visibleNetworksProtoEncoding != null) {
344 new PartnerLocationDescriptor.LocationDescriptor(); 390 return XGEO_HEADER_PREFIX + LOCATION_PROTO_PREFIX + locationProt oEncoding
345 locationDescriptor.latlng = latlng; 391 + LOCATION_PROTO_SEPARATOR + visibleNetworksProtoEncodin g;
346 // Include role, producer, timestamp and radius. 392 }
347 locationDescriptor.role = PartnerLocationDescriptor.CURRENT_LOCATION; 393 return XGEO_HEADER_PREFIX + LOCATION_PROTO_PREFIX + locationProtoEnc oding;
348 locationDescriptor.producer = PartnerLocationDescriptor.DEVICE_LOCATION; 394 }
dougt 2017/05/18 02:33:15 Would something like the following work? if (loca
Ted C 2017/05/18 14:43:03 We should use a StringBuilder if we go with this a
lbargu 2017/05/18 15:14:08 Done.
lbargu 2017/05/18 15:14:09 Done with builder.
349 locationDescriptor.timestamp = timestamp; 395 if (visibleNetworksProtoEncoding != null) {
350 locationDescriptor.radius = (float) radius; 396 return XGEO_HEADER_PREFIX + LOCATION_PROTO_PREFIX + visibleNetworksP rotoEncoding;
351 397 }
352 String locationProtoEncoding = Base64.encodeToString( 398 return null;
353 MessageNano.toByteArray(locationDescriptor), Base64.NO_WRAP | Ba se64.URL_SAFE);
354
355 return XGEO_HEADER_PREFIX + LOCATION_PROTO_PREFIX + locationProtoEncodin g;
356 } 399 }
357 400
358 @CalledByNative 401 @CalledByNative
359 static boolean hasGeolocationPermission() { 402 static boolean hasGeolocationPermission() {
403 if (sUseAppPermissionGrantedForTesting) return sAppPermissionGrantedForT esting;
Ted C 2017/05/18 14:43:03 can we use http://robolectric.org/javadoc/latest/o
lbargu 2017/05/18 15:14:08 It's not available in chromium source code?
Ted C 2017/05/18 15:25:29 Indeed it is not...sadness. Looking at what is in
lbargu 2017/05/18 15:56:28 Tried, but a time drainer, no success :(
360 int pid = Process.myPid(); 404 int pid = Process.myPid();
361 int uid = Process.myUid(); 405 int uid = Process.myUid();
362 if (ApiCompatibilityUtils.checkPermission(ContextUtils.getApplicationCon text(), 406 if (ApiCompatibilityUtils.checkPermission(ContextUtils.getApplicationCon text(),
363 Manifest.permission.ACCESS_COARSE_LOCATION, pid, uid) 407 Manifest.permission.ACCESS_COARSE_LOCATION, pid, uid)
364 != PackageManager.PERMISSION_GRANTED) { 408 != PackageManager.PERMISSION_GRANTED) {
365 return false; 409 return false;
366 } 410 }
367 411
368 // Work around a bug in OnePlus2 devices running Lollipop, where the NET WORK_PROVIDER 412 // 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 413 // incorrectly requires FINE_LOCATION permission (it should only require COARSE_LOCATION
370 // permission). http://crbug.com/580733 414 // permission). http://crbug.com/580733
371 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M 415 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M
372 && ApiCompatibilityUtils.checkPermission(ContextUtils.getApplica tionContext(), 416 && ApiCompatibilityUtils.checkPermission(ContextUtils.getApplica tionContext(),
373 Manifest.permission.ACCESS_FINE_LOCATION, pid, uid) 417 Manifest.permission.ACCESS_FINE_LOCATION, pid, uid)
374 != PackageManager.PERMISSION_GRANTED) { 418 != PackageManager.PERMISSION_GRANTED) {
375 return false; 419 return false;
376 } 420 }
377 421
378 return true; 422 return true;
379 } 423 }
380 424
381 /** 425 /**
382 * Returns the app level geolocation permission. 426 * Returns the app level geolocation permission.
383 * This permission can be either granted, blocked or prompt. 427 * This permission can be either granted, blocked or prompt.
384 */ 428 */
385 @Permission 429 @Permission
386 static int getGeolocationPermission(Tab tab) { 430 static int getGeolocationPermission(Tab tab) {
431 if (sUseAppPermissionGrantedForTesting) {
432 return sAppPermissionGrantedForTesting ? PERMISSION_GRANTED : PERMIS SION_BLOCKED;
433 }
387 if (hasGeolocationPermission()) return PERMISSION_GRANTED; 434 if (hasGeolocationPermission()) return PERMISSION_GRANTED;
388 return tab.getWindowAndroid().canRequestPermission( 435 return tab.getWindowAndroid().canRequestPermission(
389 Manifest.permission.ACCESS_COARSE_LOCATION) 436 Manifest.permission.ACCESS_COARSE_LOCATION)
390 ? PERMISSION_PROMPT 437 ? PERMISSION_PROMPT
391 : PERMISSION_BLOCKED; 438 : PERMISSION_BLOCKED;
392 } 439 }
393 440
394 /** 441 /**
395 * Returns true if the user has disabled sharing their location with url (e. g. via the 442 * Returns true if the user has disabled sharing their location with url (e. g. via the
396 * geolocation infobar). 443 * geolocation infobar).
(...skipping 12 matching lines...) Expand all
409 /** 456 /**
410 * Returns the location permission for sharing their location with url (e.g. via the 457 * Returns the location permission for sharing their location with url (e.g. via the
411 * geolocation infobar). 458 * geolocation infobar).
412 */ 459 */
413 static ContentSetting locationContentSettingForUrl(Uri uri, boolean isIncogn ito) { 460 static ContentSetting locationContentSettingForUrl(Uri uri, boolean isIncogn ito) {
414 GeolocationInfo locationSettings = new GeolocationInfo(uri.toString(), n ull, isIncognito); 461 GeolocationInfo locationSettings = new GeolocationInfo(uri.toString(), n ull, isIncognito);
415 ContentSetting locationPermission = locationSettings.getContentSetting() ; 462 ContentSetting locationPermission = locationSettings.getContentSetting() ;
416 return locationPermission; 463 return locationPermission;
417 } 464 }
418 465
466 @VisibleForTesting
467 static void setLocationSourceForTesting(int locationSourceForTesting) {
468 sLocationSourceForTesting = locationSourceForTesting;
469 sUseLocationSourceForTesting = true;
470 }
471
472 @VisibleForTesting
473 static void setAppPermissionGrantedForTesting(boolean appPermissionGrantedFo rTesting) {
474 sAppPermissionGrantedForTesting = appPermissionGrantedForTesting;
475 sUseAppPermissionGrantedForTesting = true;
476 }
477
419 /** Records a data point for the Geolocation.HeaderSentOrNot histogram. */ 478 /** Records a data point for the Geolocation.HeaderSentOrNot histogram. */
420 private static void recordHistogram(int result) { 479 private static void recordHistogram(int result) {
421 RecordHistogram.recordEnumeratedHistogram("Geolocation.HeaderSentOrNot", result, UMA_MAX); 480 RecordHistogram.recordEnumeratedHistogram("Geolocation.HeaderSentOrNot", result, UMA_MAX);
422 } 481 }
423 482
424 /** Returns the location source. */ 483 /** Returns the location source. */
425 @LocationSource 484 @LocationSource
426 // We should replace our usage of LOCATION_PROVIDERS_ALLOWED when the min AP I is 19. 485 // We should replace our usage of LOCATION_PROVIDERS_ALLOWED when the min AP I is 19.
427 @SuppressWarnings("deprecation") 486 @SuppressWarnings("deprecation")
428 private static int getLocationSource() { 487 private static int getLocationSource() {
488 if (sUseLocationSourceForTesting) return sLocationSourceForTesting;
489
429 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { 490 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
430 int locationMode; 491 int locationMode;
431 try { 492 try {
432 locationMode = Settings.Secure.getInt( 493 locationMode = Settings.Secure.getInt(
433 ContextUtils.getApplicationContext().getContentResolver( ), 494 ContextUtils.getApplicationContext().getContentResolver( ),
434 Settings.Secure.LOCATION_MODE); 495 Settings.Secure.LOCATION_MODE);
435 } catch (Settings.SettingNotFoundException e) { 496 } catch (Settings.SettingNotFoundException e) {
436 Log.e(TAG, "Error getting the LOCATION_MODE"); 497 Log.e(TAG, "Error getting the LOCATION_MODE");
437 return LOCATION_SOURCE_MASTER_OFF; 498 return LOCATION_SOURCE_MASTER_OFF;
438 } 499 }
(...skipping 229 matching lines...) Expand 10 before | Expand all | Expand 10 after
668 Log.e(TAG, "Unexpected locationSource: " + locationSource); 729 Log.e(TAG, "Unexpected locationSource: " + locationSource);
669 assert false : "Unexpected locationSource: " + locationSource; 730 assert false : "Unexpected locationSource: " + locationSource;
670 return; 731 return;
671 } 732 }
672 long durationSeconds = durationMillis / 1000; 733 long durationSeconds = durationMillis / 1000;
673 int duration = durationSeconds >= (long) Integer.MAX_VALUE ? Integer.MAX _VALUE 734 int duration = durationSeconds >= (long) Integer.MAX_VALUE ? Integer.MAX _VALUE
674 : (int) durat ionSeconds; 735 : (int) durat ionSeconds;
675 RecordHistogram.recordCustomCountHistogram( 736 RecordHistogram.recordCustomCountHistogram(
676 name, duration, 1, LOCATION_AGE_HISTOGRAM_MAX_SECONDS, 50); 737 name, duration, 1, LOCATION_AGE_HISTOGRAM_MAX_SECONDS, 50);
677 } 738 }
739
740 /**
741 * Encodes location into proto encoding.
742 */
743 @Nullable
744 @VisibleForTesting
745 static String encodeProtoLocation(@Nullable Location location) {
746 if (location == null) {
747 // No data to encode.
748 return null;
749 }
750 // Timestamp in microseconds since the UNIX epoch.
751 long timestamp = location.getTime() * 1000;
752 // Latitude times 1e7.
753 int latitudeE7 = (int) (location.getLatitude() * 10000000);
754 // Longitude times 1e7.
755 int longitudeE7 = (int) (location.getLongitude() * 10000000);
756 // Radius of 68% accuracy in mm.
757 int radius = (int) (location.getAccuracy() * 1000);
758
759 // Create a LatLng for the coordinates.
760 PartnerLocationDescriptor.LatLng latlng = new PartnerLocationDescriptor. LatLng();
761 latlng.latitudeE7 = latitudeE7;
762 latlng.longitudeE7 = longitudeE7;
763
764 // Populate a LocationDescriptor with the LatLng.
765 PartnerLocationDescriptor.LocationDescriptor locationDescriptor =
766 new PartnerLocationDescriptor.LocationDescriptor();
767 locationDescriptor.latlng = latlng;
768 // Include role, producer, timestamp and radius.
769 locationDescriptor.role = PartnerLocationDescriptor.CURRENT_LOCATION;
770 locationDescriptor.producer = PartnerLocationDescriptor.DEVICE_LOCATION;
771 locationDescriptor.timestamp = timestamp;
772 locationDescriptor.radius = (float) radius;
773 return encodeLocationDescriptor(locationDescriptor);
774 }
775
776 /**
777 * Encodes the given proto location descriptor into a BASE64 URL_SAFE encodi ng.
778 */
779 private static String encodeLocationDescriptor(
780 PartnerLocationDescriptor.LocationDescriptor locationDescriptor) {
781 return Base64.encodeToString(
782 MessageNano.toByteArray(locationDescriptor), Base64.NO_WRAP | Ba se64.URL_SAFE);
783 }
784
785 /**
786 * Encodes visible networks in proto encoding.
787 */
788 @Nullable
789 @VisibleForTesting
790 static String encodeProtoVisibleNetworks(@Nullable VisibleNetworks visibleNe tworks) {
791 VisibleNetworks visibleNetworksToEncode = trimVisibleNetworks(visibleNet works);
792 if (visibleNetworksToEncode == null) {
793 // No data to encode.
794 return null;
795 }
796 VisibleWifi connectedWifi = visibleNetworksToEncode.connectedWifi();
797 VisibleCell connectedCell = visibleNetworksToEncode.connectedCell();
798 Set<VisibleWifi> visibleWifis = visibleNetworksToEncode.allVisibleWifis( );
799 Set<VisibleCell> visibleCells = visibleNetworksToEncode.allVisibleCells( );
800
801 int numVisibleNetworks = (connectedWifi != null ? 1 : 0)
802 + (visibleWifis != null ? visibleWifis.size() : 0) + (connectedC ell != null ? 1 : 0)
803 + (visibleCells != null ? visibleCells.size() : 0);
804 if (numVisibleNetworks == 0) {
805 // No data to encode.
806 return null;
807 }
808
809 int i = 0;
810 PartnerLocationDescriptor.VisibleNetwork[] protoNetworks =
811 new PartnerLocationDescriptor.VisibleNetwork[numVisibleNetworks] ;
812 if (connectedWifi != null) {
813 protoNetworks[i++] = connectedWifi.toProto(true);
814 }
815 if (visibleWifis != null) {
816 for (VisibleWifi visibleWifi : visibleWifis) {
817 protoNetworks[i++] = visibleWifi.toProto(false);
818 }
819 }
820 if (connectedCell != null) {
821 protoNetworks[i++] = connectedCell.toProto(true);
822 }
823 if (visibleCells != null) {
824 for (VisibleCell visibleCell : visibleCells) {
825 protoNetworks[i++] = visibleCell.toProto(false);
826 }
827 }
828
829 PartnerLocationDescriptor.LocationDescriptor locationDescriptor =
830 new PartnerLocationDescriptor.LocationDescriptor();
831 locationDescriptor.role = PartnerLocationDescriptor.CURRENT_LOCATION;
832 locationDescriptor.producer = PartnerLocationDescriptor.DEVICE_LOCATION;
833 locationDescriptor.visibleNetwork = protoNetworks;
834
835 return encodeLocationDescriptor(locationDescriptor);
836 }
837
838 @Nullable
839 @VisibleForTesting
840 static VisibleNetworks trimVisibleNetworks(@Nullable VisibleNetworks visible Networks) {
841 if (visibleNetworks == null || visibleNetworks.isEmpty()) {
842 return null;
843 }
844 // Trim visible networks to only include a limited number of visible not -conntected networks
845 // based on flag.
846 VisibleCell connectedCell = visibleNetworks.connectedCell();
847 VisibleWifi connectedWifi = visibleNetworks.connectedWifi();
848 Set<VisibleCell> visibleCells = visibleNetworks.allVisibleCells();
849 Set<VisibleWifi> visibleWifis = visibleNetworks.allVisibleWifis();
850 VisibleCell extraVisibleCell = null;
851 VisibleWifi extraVisibleWifi = null;
852 if (shouldExcludeVisibleWifi(connectedWifi)) {
853 // Trim the connected wifi.
854 connectedWifi = null;
855 }
856 // Select the extra visible cell.
857 if (visibleCells != null) {
858 for (VisibleCell candidateCell : visibleCells) {
859 if (ApiCompatibilityUtils.objectEquals(connectedCell, candidateC ell)) {
860 // Do not include this candidate cell, since its already the connected one.
861 continue;
862 }
863 // Add it and since we only want one, stop iterating over other cells.
864 extraVisibleCell = candidateCell;
865 break;
866 }
867 }
868 // Select the extra visible wifi.
869 if (visibleWifis != null) {
870 for (VisibleWifi candidateWifi : visibleWifis) {
871 if (shouldExcludeVisibleWifi(candidateWifi)) {
872 // Do not include this candidate wifi.
873 continue;
874 }
875 if (ApiCompatibilityUtils.objectEquals(connectedWifi, candidateW ifi)) {
876 // Replace the connected, since the candidate will have leve l. This is because
877 // the android APIs exposing connected WIFI do not expose le vel, while the ones
878 // exposing visible wifis expose level.
879 connectedWifi = candidateWifi;
880 // Do not include this candidate wifi, since its already the connected one.
881 continue;
882 }
883 // Keep the one with stronger level (since it's negative, this i s the smaller value)
884 if (extraVisibleWifi == null || extraVisibleWifi.level() > candi dateWifi.level()) {
885 extraVisibleWifi = candidateWifi;
886 }
887 }
888 }
889
890 if (connectedCell == null && connectedWifi == null && extraVisibleCell = = null
891 && extraVisibleWifi == null) {
892 return null;
893 }
894
895 return VisibleNetworks.create(connectedWifi, connectedCell,
896 extraVisibleWifi != null ? new HashSet<>(Arrays.asList(extraVisi bleWifi)) : null,
Ted C 2017/05/18 14:43:03 I would use newHashSet here https://cs.chromium.o
lbargu 2017/05/18 15:14:09 Done.
897 extraVisibleCell != null ? new HashSet<>(Arrays.asList(extraVisi bleCell)) : null);
898 }
899
900 /**
901 * Returns whether the provided {@link VisibleWifi} should be excluded. This can happen if the
902 * network is opted out (ssid contains "_nomap" or "_optout").
903 */
904 private static boolean shouldExcludeVisibleWifi(@Nullable VisibleWifi visibl eWifi) {
905 if (visibleWifi == null) {
906 return true;
907 }
908 String ssid = visibleWifi.ssid();
909 String bssid = visibleWifi.bssid();
910 if (ssid == null) {
911 // No ssid, so the networks is not opted out and should not be exclu ded.
912 return false;
913 }
914 // Optimization to avoid costly toLowerCase() in most cases.
915 if (ssid.indexOf('_') < 0) {
916 // No "_nomap
917 return false;
918 }
919 String ssidLowerCase = ssid.toLowerCase(Locale.ENGLISH);
920 return ssidLowerCase.contains(SSID_NOMAP) || ssidLowerCase.contains(SSID _OPTOUT);
921 }
678 } 922 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698