OLD | NEW |
(Empty) | |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 package org.chromium.content.browser; |
| 6 |
| 7 import android.content.Context; |
| 8 import android.location.Criteria; |
| 9 import android.location.Location; |
| 10 import android.location.LocationListener; |
| 11 import android.location.LocationManager; |
| 12 import android.os.Bundle; |
| 13 |
| 14 import org.chromium.base.Log; |
| 15 import org.chromium.base.ThreadUtils; |
| 16 import org.chromium.base.VisibleForTesting; |
| 17 |
| 18 import java.util.List; |
| 19 |
| 20 /** |
| 21 * Factory to create a LocationProvider to allow us to inject |
| 22 * a mock for tests. |
| 23 */ |
| 24 public class LocationProviderFactory { |
| 25 private static LocationProviderFactory.LocationProvider sProviderImpl; |
| 26 |
| 27 /** |
| 28 * LocationProviderFactory.get() returns an instance of this interface. |
| 29 */ |
| 30 public interface LocationProvider { |
| 31 public void start(boolean enableHighAccuracy); |
| 32 public void stop(); |
| 33 public boolean isRunning(); |
| 34 } |
| 35 |
| 36 private LocationProviderFactory() { |
| 37 } |
| 38 |
| 39 @VisibleForTesting |
| 40 public static void setLocationProviderImpl(LocationProviderFactory.LocationP
rovider provider) { |
| 41 assert sProviderImpl == null; |
| 42 sProviderImpl = provider; |
| 43 } |
| 44 |
| 45 public static LocationProvider get(Context context) { |
| 46 if (sProviderImpl == null) { |
| 47 sProviderImpl = new LocationProviderImpl(context); |
| 48 } |
| 49 return sProviderImpl; |
| 50 } |
| 51 |
| 52 /** |
| 53 * This is the core of android location provider. It is a separate class for
clarity |
| 54 * so that it can manage all processing completely in the UI thread. The con
tainer class |
| 55 * ensures that the start/stop calls into this class are done in the UI thre
ad. |
| 56 */ |
| 57 private static class LocationProviderImpl |
| 58 implements LocationListener, LocationProviderFactory.LocationProvide
r { |
| 59 |
| 60 // Log tag |
| 61 private static final String TAG = "cr.LocationProvider"; |
| 62 |
| 63 private Context mContext; |
| 64 private LocationManager mLocationManager; |
| 65 private boolean mIsRunning; |
| 66 |
| 67 LocationProviderImpl(Context context) { |
| 68 mContext = context; |
| 69 } |
| 70 |
| 71 /** |
| 72 * Start listening for location updates. |
| 73 * @param enableHighAccuracy Whether or not to enable high accuracy loca
tion providers. |
| 74 */ |
| 75 @Override |
| 76 public void start(boolean enableHighAccuracy) { |
| 77 unregisterFromLocationUpdates(); |
| 78 registerForLocationUpdates(enableHighAccuracy); |
| 79 } |
| 80 |
| 81 /** |
| 82 * Stop listening for location updates. |
| 83 */ |
| 84 @Override |
| 85 public void stop() { |
| 86 unregisterFromLocationUpdates(); |
| 87 } |
| 88 |
| 89 /** |
| 90 * Returns true if we are currently listening for location updates, fals
e if not. |
| 91 */ |
| 92 @Override |
| 93 public boolean isRunning() { |
| 94 return mIsRunning; |
| 95 } |
| 96 |
| 97 @Override |
| 98 public void onLocationChanged(Location location) { |
| 99 // Callbacks from the system location service are queued to this thr
ead, so it's |
| 100 // possible that we receive callbacks after unregistering. At this p
oint, the |
| 101 // native object will no longer exist. |
| 102 if (mIsRunning) { |
| 103 updateNewLocation(location); |
| 104 } |
| 105 } |
| 106 |
| 107 private void updateNewLocation(Location location) { |
| 108 LocationProviderAdapter.newLocationAvailable( |
| 109 location.getLatitude(), location.getLongitude(), |
| 110 location.getTime() / 1000.0, |
| 111 location.hasAltitude(), location.getAltitude(), |
| 112 location.hasAccuracy(), location.getAccuracy(), |
| 113 location.hasBearing(), location.getBearing(), |
| 114 location.hasSpeed(), location.getSpeed()); |
| 115 } |
| 116 |
| 117 @Override |
| 118 public void onStatusChanged(String provider, int status, Bundle extras)
{ |
| 119 } |
| 120 |
| 121 @Override |
| 122 public void onProviderEnabled(String provider) { |
| 123 } |
| 124 |
| 125 @Override |
| 126 public void onProviderDisabled(String provider) { |
| 127 } |
| 128 |
| 129 private void ensureLocationManagerCreated() { |
| 130 if (mLocationManager != null) return; |
| 131 mLocationManager = (LocationManager) mContext.getSystemService( |
| 132 Context.LOCATION_SERVICE); |
| 133 if (mLocationManager == null) { |
| 134 Log.e(TAG, "Could not get location manager."); |
| 135 } |
| 136 } |
| 137 |
| 138 /** |
| 139 * Registers this object with the location service. |
| 140 */ |
| 141 private void registerForLocationUpdates(boolean enableHighAccuracy) { |
| 142 ensureLocationManagerCreated(); |
| 143 if (usePassiveOneShotLocation()) return; |
| 144 |
| 145 assert !mIsRunning; |
| 146 mIsRunning = true; |
| 147 |
| 148 // We're running on the main thread. The C++ side is responsible to |
| 149 // bounce notifications to the Geolocation thread as they arrive in
the mainLooper. |
| 150 try { |
| 151 Criteria criteria = new Criteria(); |
| 152 if (enableHighAccuracy) criteria.setAccuracy(Criteria.ACCURACY_F
INE); |
| 153 mLocationManager.requestLocationUpdates(0, 0, criteria, this, |
| 154 ThreadUtils.getUiThreadLooper()); |
| 155 } catch (SecurityException e) { |
| 156 Log.e(TAG, "Caught security exception while registering for loca
tion updates " |
| 157 + "from the system. The application does not have suffic
ient geolocation " |
| 158 + "permissions."); |
| 159 unregisterFromLocationUpdates(); |
| 160 // Propagate an error to JavaScript, this can happen in case of
WebView |
| 161 // when the embedding app does not have sufficient permissions. |
| 162 LocationProviderAdapter.newErrorAvailable("application does not
have sufficient " |
| 163 + "geolocation permissions."); |
| 164 } catch (IllegalArgumentException e) { |
| 165 Log.e(TAG, "Caught IllegalArgumentException registering for loca
tion updates."); |
| 166 unregisterFromLocationUpdates(); |
| 167 assert false; |
| 168 } |
| 169 } |
| 170 |
| 171 /** |
| 172 * Unregisters this object from the location service. |
| 173 */ |
| 174 private void unregisterFromLocationUpdates() { |
| 175 if (mIsRunning) { |
| 176 mIsRunning = false; |
| 177 mLocationManager.removeUpdates(this); |
| 178 } |
| 179 } |
| 180 |
| 181 private boolean usePassiveOneShotLocation() { |
| 182 if (!isOnlyPassiveLocationProviderEnabled()) return false; |
| 183 |
| 184 // Do not request a location update if the only available location p
rovider is |
| 185 // the passive one. Make use of the last known location and call |
| 186 // onLocationChanged directly. |
| 187 final Location location = mLocationManager.getLastKnownLocation( |
| 188 LocationManager.PASSIVE_PROVIDER); |
| 189 if (location != null) { |
| 190 ThreadUtils.runOnUiThread(new Runnable() { |
| 191 @Override |
| 192 public void run() { |
| 193 updateNewLocation(location); |
| 194 } |
| 195 }); |
| 196 } |
| 197 return true; |
| 198 } |
| 199 |
| 200 /* |
| 201 * Checks if the passive location provider is the only provider availabl
e |
| 202 * in the system. |
| 203 */ |
| 204 private boolean isOnlyPassiveLocationProviderEnabled() { |
| 205 List<String> providers = mLocationManager.getProviders(true); |
| 206 return providers != null && providers.size() == 1 |
| 207 && providers.get(0).equals(LocationManager.PASSIVE_PROVIDER)
; |
| 208 } |
| 209 } |
| 210 } |
| 211 |
| 212 |
OLD | NEW |