Index: device/geolocation/android/java/src/org/chromium/device/geolocation/LocationProviderAndroid.java |
diff --git a/device/geolocation/android/java/src/org/chromium/device/geolocation/LocationProviderAndroid.java b/device/geolocation/android/java/src/org/chromium/device/geolocation/LocationProviderAndroid.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..261ce9dfd3c147140266a3e244010aec30152027 |
--- /dev/null |
+++ b/device/geolocation/android/java/src/org/chromium/device/geolocation/LocationProviderAndroid.java |
@@ -0,0 +1,161 @@ |
+// Copyright 2017 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+package org.chromium.device.geolocation; |
+ |
+import android.content.Context; |
+import android.location.Criteria; |
+import android.location.Location; |
+import android.location.LocationListener; |
+import android.location.LocationManager; |
+import android.os.Bundle; |
+ |
+import org.chromium.base.Log; |
+import org.chromium.base.ThreadUtils; |
+ |
+import java.util.List; |
+ |
+/** |
+ * This is a LocationProvider using Android APIs [1]. It is a separate class for clarity |
+ * so that it can manage all processing completely on the UI thread. The container class |
+ * ensures that the start/stop calls into this class are done on the UI thread. |
+ * |
+ * [1] https://developer.android.com/reference/android/location/package-summary.html |
+ */ |
+public class LocationProviderAndroid |
+ implements LocationListener, LocationProviderFactory.LocationProvider { |
+ private static final String TAG = "cr_LocationProvider"; |
+ |
+ private Context mContext; |
+ private LocationManager mLocationManager; |
+ private boolean mIsRunning; |
+ |
+ LocationProviderAndroid(Context context) { |
+ mContext = context; |
+ } |
+ |
+ @Override |
+ public void start(boolean enableHighAccuracy) { |
+ unregisterFromLocationUpdates(); |
+ registerForLocationUpdates(enableHighAccuracy); |
+ } |
+ |
+ @Override |
+ public void stop() { |
+ unregisterFromLocationUpdates(); |
+ } |
+ |
+ @Override |
+ public boolean isRunning() { |
+ return mIsRunning; |
+ } |
+ |
+ @Override |
+ public void onLocationChanged(Location location) { |
+ // Callbacks from the system location service are queued to this thread, so it's |
+ // possible that we receive callbacks after unregistering. At this point, the |
+ // native object will no longer exist. |
+ if (mIsRunning) { |
+ updateNewLocation(location); |
+ } |
+ } |
+ |
+ @Override |
+ public void onStatusChanged(String provider, int status, Bundle extras) {} |
+ |
+ @Override |
+ public void onProviderEnabled(String provider) {} |
+ |
+ @Override |
+ public void onProviderDisabled(String provider) {} |
+ |
+ private void ensureLocationManagerCreated() { |
+ if (mLocationManager != null) return; |
+ mLocationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE); |
+ if (mLocationManager == null) { |
+ Log.e(TAG, "Could not get location manager."); |
+ } |
+ } |
+ |
+ /** |
+ * Registers this object with the location service. |
+ */ |
+ private void registerForLocationUpdates(boolean enableHighAccuracy) { |
+ ensureLocationManagerCreated(); |
+ if (usePassiveOneShotLocation()) return; |
+ |
+ assert !mIsRunning; |
+ mIsRunning = true; |
+ |
+ // We're running on the main thread. The C++ side is responsible to |
+ // bounce notifications to the Geolocation thread as they arrive in the mainLooper. |
+ try { |
+ Criteria criteria = new Criteria(); |
+ if (enableHighAccuracy) criteria.setAccuracy(Criteria.ACCURACY_FINE); |
+ mLocationManager.requestLocationUpdates( |
+ 0, 0, criteria, this, ThreadUtils.getUiThreadLooper()); |
+ } catch (SecurityException e) { |
+ Log.e(TAG, |
+ "Caught security exception while registering for location updates " |
+ + "from the system. The application does not have sufficient " |
+ + "geolocation permissions."); |
+ unregisterFromLocationUpdates(); |
+ // Propagate an error to JavaScript, this can happen in case of WebView |
+ // when the embedding app does not have sufficient permissions. |
+ LocationProviderAdapter.newErrorAvailable("application does not have sufficient " |
+ + "geolocation permissions."); |
+ } catch (IllegalArgumentException e) { |
+ Log.e(TAG, "Caught IllegalArgumentException registering for location updates."); |
+ unregisterFromLocationUpdates(); |
+ assert false; |
+ } |
+ } |
+ |
+ /** |
+ * Unregisters this object from the location service. |
+ */ |
+ private void unregisterFromLocationUpdates() { |
+ if (mIsRunning) { |
+ mIsRunning = false; |
+ mLocationManager.removeUpdates(this); |
+ } |
+ } |
+ |
+ private void updateNewLocation(Location location) { |
+ LocationProviderAdapter.newLocationAvailable(location.getLatitude(), |
+ location.getLongitude(), location.getTime() / 1000.0, location.hasAltitude(), |
+ location.getAltitude(), location.hasAccuracy(), location.getAccuracy(), |
+ location.hasBearing(), location.getBearing(), location.hasSpeed(), |
+ location.getSpeed()); |
+ } |
+ |
+ private boolean usePassiveOneShotLocation() { |
+ if (!isOnlyPassiveLocationProviderEnabled()) return false; |
+ |
+ // Do not request a location update if the only available location provider is |
+ // the passive one. Make use of the last known location and call |
+ // onLocationChanged directly. |
+ final Location location = |
+ mLocationManager.getLastKnownLocation(LocationManager.PASSIVE_PROVIDER); |
+ if (location != null) { |
+ ThreadUtils.runOnUiThread(new Runnable() { |
+ @Override |
+ public void run() { |
+ updateNewLocation(location); |
+ } |
+ }); |
+ } |
+ return true; |
+ } |
+ |
+ /* |
+ * Checks if the passive location provider is the only provider available |
+ * in the system. |
+ */ |
+ private boolean isOnlyPassiveLocationProviderEnabled() { |
+ List<String> providers = mLocationManager.getProviders(true); |
+ return providers != null && providers.size() == 1 |
+ && providers.get(0).equals(LocationManager.PASSIVE_PROVIDER); |
+ } |
+} |