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