| OLD | NEW |
| 1 // Copyright 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 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.device.geolocation; | 5 package org.chromium.device.geolocation; |
| 6 | 6 |
| 7 import android.content.Context; | 7 import android.content.Context; |
| 8 import android.location.Criteria; | 8 import android.location.Criteria; |
| 9 import android.location.Location; | 9 import android.location.Location; |
| 10 import android.location.LocationListener; | 10 import android.location.LocationListener; |
| 11 import android.location.LocationManager; | 11 import android.location.LocationManager; |
| 12 import android.os.Bundle; | 12 import android.os.Bundle; |
| 13 | 13 |
| 14 import org.chromium.base.Log; | 14 import org.chromium.base.Log; |
| 15 import org.chromium.base.ThreadUtils; | 15 import org.chromium.base.ThreadUtils; |
| 16 import org.chromium.base.VisibleForTesting; | |
| 17 | 16 |
| 18 import java.util.List; | 17 import java.util.List; |
| 19 | 18 |
| 20 /** | 19 /** |
| 21 * This is a LocationProvider using Android APIs [1]. It is a separate class for
clarity | 20 * This is a LocationProvider using Android APIs [1]. It is a separate class for
clarity |
| 22 * so that it can manage all processing completely on the UI thread. The contain
er class | 21 * so that it can manage all processing completely on the UI thread. The contain
er class |
| 23 * ensures that the start/stop calls into this class are done on the UI thread. | 22 * ensures that the start/stop calls into this class are done on the UI thread. |
| 24 * | 23 * |
| 25 * [1] https://developer.android.com/reference/android/location/package-summary.
html | 24 * [1] https://developer.android.com/reference/android/location/package-summary.
html |
| 26 */ | 25 */ |
| 27 public class LocationProviderAndroid | 26 public class LocationProviderAndroid |
| 28 implements LocationListener, LocationProviderFactory.LocationProvider { | 27 implements LocationListener, LocationProviderFactory.LocationProvider { |
| 29 private static final String TAG = "cr_LocationProvider"; | 28 private static final String TAG = "cr_LocationProvider"; |
| 30 | 29 |
| 31 private Context mContext; | 30 private Context mContext; |
| 32 private LocationManager mLocationManager; | 31 private LocationManager mLocationManager; |
| 33 private boolean mIsRunning; | 32 private boolean mIsRunning; |
| 34 | 33 |
| 35 LocationProviderAndroid(Context context) { | 34 LocationProviderAndroid(Context context) { |
| 36 mContext = context; | 35 mContext = context; |
| 37 } | 36 } |
| 38 | 37 |
| 39 @Override | 38 @Override |
| 40 public void start(boolean enableHighAccuracy) { | 39 public void start(boolean enableHighAccuracy) { |
| 41 ThreadUtils.assertOnUiThread(); | |
| 42 unregisterFromLocationUpdates(); | 40 unregisterFromLocationUpdates(); |
| 43 registerForLocationUpdates(enableHighAccuracy); | 41 registerForLocationUpdates(enableHighAccuracy); |
| 44 } | 42 } |
| 45 | 43 |
| 46 @Override | 44 @Override |
| 47 public void stop() { | 45 public void stop() { |
| 48 ThreadUtils.assertOnUiThread(); | |
| 49 unregisterFromLocationUpdates(); | 46 unregisterFromLocationUpdates(); |
| 50 } | 47 } |
| 51 | 48 |
| 52 @Override | 49 @Override |
| 53 public boolean isRunning() { | 50 public boolean isRunning() { |
| 54 ThreadUtils.assertOnUiThread(); | |
| 55 return mIsRunning; | 51 return mIsRunning; |
| 56 } | 52 } |
| 57 | 53 |
| 58 @Override | 54 @Override |
| 59 public void onLocationChanged(Location location) { | 55 public void onLocationChanged(Location location) { |
| 60 // Callbacks from the system location service are queued to this thread,
so it's | 56 // Callbacks from the system location service are queued to this thread,
so it's |
| 61 // possible that we receive callbacks after unregistering. At this point
, the | 57 // possible that we receive callbacks after unregistering. At this point
, the |
| 62 // native object will no longer exist. | 58 // native object will no longer exist. |
| 63 if (mIsRunning) { | 59 if (mIsRunning) { |
| 64 LocationProviderAdapter.onNewLocationAvailable(location); | 60 updateNewLocation(location); |
| 65 } | 61 } |
| 66 } | 62 } |
| 67 | 63 |
| 68 @Override | 64 @Override |
| 69 public void onStatusChanged(String provider, int status, Bundle extras) {} | 65 public void onStatusChanged(String provider, int status, Bundle extras) {} |
| 70 | 66 |
| 71 @Override | 67 @Override |
| 72 public void onProviderEnabled(String provider) {} | 68 public void onProviderEnabled(String provider) {} |
| 73 | 69 |
| 74 @Override | 70 @Override |
| 75 public void onProviderDisabled(String provider) {} | 71 public void onProviderDisabled(String provider) {} |
| 76 | 72 |
| 77 @VisibleForTesting | 73 private void ensureLocationManagerCreated() { |
| 78 public void setLocationManagerForTesting(LocationManager manager) { | |
| 79 mLocationManager = manager; | |
| 80 } | |
| 81 | |
| 82 private void createLocationManagerIfNeeded() { | |
| 83 if (mLocationManager != null) return; | 74 if (mLocationManager != null) return; |
| 84 mLocationManager = (LocationManager) mContext.getSystemService(Context.L
OCATION_SERVICE); | 75 mLocationManager = (LocationManager) mContext.getSystemService(Context.L
OCATION_SERVICE); |
| 85 if (mLocationManager == null) { | 76 if (mLocationManager == null) { |
| 86 Log.e(TAG, "Could not get location manager."); | 77 Log.e(TAG, "Could not get location manager."); |
| 87 } | 78 } |
| 88 } | 79 } |
| 89 | 80 |
| 90 /** | 81 /** |
| 91 * Registers this object with the location service. | 82 * Registers this object with the location service. |
| 92 */ | 83 */ |
| 93 private void registerForLocationUpdates(boolean enableHighAccuracy) { | 84 private void registerForLocationUpdates(boolean enableHighAccuracy) { |
| 94 createLocationManagerIfNeeded(); | 85 ensureLocationManagerCreated(); |
| 95 if (usePassiveOneShotLocation()) return; | 86 if (usePassiveOneShotLocation()) return; |
| 96 | 87 |
| 97 assert !mIsRunning; | 88 assert !mIsRunning; |
| 98 mIsRunning = true; | 89 mIsRunning = true; |
| 99 | 90 |
| 100 // We're running on the main thread. The C++ side is responsible to | 91 // We're running on the main thread. The C++ side is responsible to |
| 101 // bounce notifications to the Geolocation thread as they arrive in the
mainLooper. | 92 // bounce notifications to the Geolocation thread as they arrive in the
mainLooper. |
| 102 try { | 93 try { |
| 103 Criteria criteria = new Criteria(); | 94 Criteria criteria = new Criteria(); |
| 104 if (enableHighAccuracy) criteria.setAccuracy(Criteria.ACCURACY_FINE)
; | 95 if (enableHighAccuracy) criteria.setAccuracy(Criteria.ACCURACY_FINE)
; |
| 105 mLocationManager.requestLocationUpdates( | 96 mLocationManager.requestLocationUpdates( |
| 106 0, 0, criteria, this, ThreadUtils.getUiThreadLooper()); | 97 0, 0, criteria, this, ThreadUtils.getUiThreadLooper()); |
| 107 } catch (SecurityException e) { | 98 } catch (SecurityException e) { |
| 108 Log.e(TAG, | 99 Log.e(TAG, |
| 109 "Caught security exception while registering for location up
dates " | 100 "Caught security exception while registering for location up
dates " |
| 110 + "from the system. The application does not have su
fficient " | 101 + "from the system. The application does not have su
fficient " |
| 111 + "geolocation permissions."); | 102 + "geolocation permissions."); |
| 112 unregisterFromLocationUpdates(); | 103 unregisterFromLocationUpdates(); |
| 113 // Propagate an error to JavaScript, this can happen in case of WebV
iew | 104 // Propagate an error to JavaScript, this can happen in case of WebV
iew |
| 114 // when the embedding app does not have sufficient permissions. | 105 // when the embedding app does not have sufficient permissions. |
| 115 LocationProviderAdapter.newErrorAvailable( | 106 LocationProviderAdapter.newErrorAvailable("application does not have
sufficient " |
| 116 "application does not have sufficient geolocation permission
s."); | 107 + "geolocation permissions."); |
| 117 } catch (IllegalArgumentException e) { | 108 } catch (IllegalArgumentException e) { |
| 118 Log.e(TAG, "Caught IllegalArgumentException registering for location
updates."); | 109 Log.e(TAG, "Caught IllegalArgumentException registering for location
updates."); |
| 119 unregisterFromLocationUpdates(); | 110 unregisterFromLocationUpdates(); |
| 120 assert false; | 111 assert false; |
| 121 } | 112 } |
| 122 } | 113 } |
| 123 | 114 |
| 124 /** | 115 /** |
| 125 * Unregisters this object from the location service. | 116 * Unregisters this object from the location service. |
| 126 */ | 117 */ |
| 127 private void unregisterFromLocationUpdates() { | 118 private void unregisterFromLocationUpdates() { |
| 128 if (!mIsRunning) return; | 119 if (mIsRunning) { |
| 129 mIsRunning = false; | 120 mIsRunning = false; |
| 130 mLocationManager.removeUpdates(this); | 121 mLocationManager.removeUpdates(this); |
| 122 } |
| 123 } |
| 124 |
| 125 private void updateNewLocation(Location location) { |
| 126 LocationProviderAdapter.newLocationAvailable(location.getLatitude(), |
| 127 location.getLongitude(), location.getTime() / 1000.0, location.h
asAltitude(), |
| 128 location.getAltitude(), location.hasAccuracy(), location.getAccu
racy(), |
| 129 location.hasBearing(), location.getBearing(), location.hasSpeed(
), |
| 130 location.getSpeed()); |
| 131 } | 131 } |
| 132 | 132 |
| 133 private boolean usePassiveOneShotLocation() { | 133 private boolean usePassiveOneShotLocation() { |
| 134 if (!isOnlyPassiveLocationProviderEnabled()) { | 134 if (!isOnlyPassiveLocationProviderEnabled()) return false; |
| 135 return false; | |
| 136 } | |
| 137 | 135 |
| 138 // Do not request a location update if the only available location provi
der is | 136 // Do not request a location update if the only available location provi
der is |
| 139 // the passive one. Make use of the last known location and call | 137 // the passive one. Make use of the last known location and call |
| 140 // onNewLocationAvailable directly. | 138 // onLocationChanged directly. |
| 141 final Location location = | 139 final Location location = |
| 142 mLocationManager.getLastKnownLocation(LocationManager.PASSIVE_PR
OVIDER); | 140 mLocationManager.getLastKnownLocation(LocationManager.PASSIVE_PR
OVIDER); |
| 143 if (location != null) { | 141 if (location != null) { |
| 144 ThreadUtils.runOnUiThread(new Runnable() { | 142 ThreadUtils.runOnUiThread(new Runnable() { |
| 145 @Override | 143 @Override |
| 146 public void run() { | 144 public void run() { |
| 147 LocationProviderAdapter.onNewLocationAvailable(location); | 145 updateNewLocation(location); |
| 148 } | 146 } |
| 149 }); | 147 }); |
| 150 } | 148 } |
| 151 return true; | 149 return true; |
| 152 } | 150 } |
| 153 | 151 |
| 154 /* | 152 /* |
| 155 * Checks if the passive location provider is the only provider available | 153 * Checks if the passive location provider is the only provider available |
| 156 * in the system. | 154 * in the system. |
| 157 */ | 155 */ |
| 158 private boolean isOnlyPassiveLocationProviderEnabled() { | 156 private boolean isOnlyPassiveLocationProviderEnabled() { |
| 159 final List<String> providers = mLocationManager.getProviders(true); | 157 List<String> providers = mLocationManager.getProviders(true); |
| 160 return providers != null && providers.size() == 1 | 158 return providers != null && providers.size() == 1 |
| 161 && providers.get(0).equals(LocationManager.PASSIVE_PROVIDER); | 159 && providers.get(0).equals(LocationManager.PASSIVE_PROVIDER); |
| 162 } | 160 } |
| 163 } | 161 } |
| OLD | NEW |