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.
5 package org.chromium.content.browser;
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 import android.util.Log;
15 import com.google.common.annotations.VisibleForTesting;
17 import org.chromium.base.ThreadUtils;
19 import java.util.List;
22 * Factory to create a LocationProvider to allow us to inject
25 public class LocationProviderFactory {
26 private static LocationProviderFactory.LocationProvider sProviderImpl;
29 * LocationProviderFactory.get() returns an instance of this interface.
31 public interface LocationProvider {
32 public void start(boolean gpsEnabled);
34 public boolean isRunning();
37 private LocationProviderFactory() {
41 public static void setLocationProviderImpl(LocationProviderFactory.LocationProvider provider) {
42 assert sProviderImpl == null;
43 sProviderImpl = provider;
46 public static LocationProvider get(Context context) {
47 if (sProviderImpl == null) {
48 sProviderImpl = new LocationProviderImpl(context);
54 * This is the core of android location provider. It is a separate class for clarity
55 * so that it can manage all processing completely in the UI thread. The container class
56 * ensures that the start/stop calls into this class are done in the UI thread.
58 private static class LocationProviderImpl
59 implements LocationListener, LocationProviderFactory.LocationProvider {
62 private static final String TAG = "LocationProvider";
64 private Context mContext;
65 private LocationManager mLocationManager;
66 private boolean mIsRunning;
68 LocationProviderImpl(Context context) {
73 * Start listening for location updates.
74 * @param gpsEnabled Whether or not we're interested in high accuracy GPS.
77 public void start(boolean gpsEnabled) {
78 unregisterFromLocationUpdates();
79 registerForLocationUpdates(gpsEnabled);
83 * Stop listening for location updates.
87 unregisterFromLocationUpdates();
91 * Returns true if we are currently listening for location updates, false if not.
94 public boolean isRunning() {
99 public void onLocationChanged(Location location) {
100 // Callbacks from the system location sevice are queued to this thread, so it's
101 // possible that we receive callbacks after unregistering. At this point, the
102 // native object will no longer exist.
104 updateNewLocation(location);
108 private void updateNewLocation(Location location) {
109 LocationProviderAdapter.newLocationAvailable(
110 location.getLatitude(), location.getLongitude(),
111 location.getTime() / 1000.0,
112 location.hasAltitude(), location.getAltitude(),
113 location.hasAccuracy(), location.getAccuracy(),
114 location.hasBearing(), location.getBearing(),
115 location.hasSpeed(), location.getSpeed());
119 public void onStatusChanged(String provider, int status, Bundle extras) {
123 public void onProviderEnabled(String provider) {
127 public void onProviderDisabled(String provider) {
130 private void ensureLocationManagerCreated() {
131 if (mLocationManager != null) return;
132 mLocationManager = (LocationManager) mContext.getSystemService(
133 Context.LOCATION_SERVICE);
134 if (mLocationManager == null) {
135 Log.e(TAG, "Could not get location manager.");
140 * Registers this object with the location service.
142 private void registerForLocationUpdates(boolean isGpsEnabled) {
143 ensureLocationManagerCreated();
144 if (usePassiveOneShotLocation()) return;
149 // We're running on the main thread. The C++ side is responsible to
150 // bounce notifications to the Geolocation thread as they arrive in the mainLooper.
152 Criteria criteria = new Criteria();
153 mLocationManager.requestLocationUpdates(0, 0, criteria, this,
154 ThreadUtils.getUiThreadLooper());
156 criteria.setAccuracy(Criteria.ACCURACY_FINE);
157 mLocationManager.requestLocationUpdates(0, 0, criteria, this,
158 ThreadUtils.getUiThreadLooper());
160 } catch (SecurityException e) {
161 Log.e(TAG, "Caught security exception registering for location updates from " +
162 "system. This should only happen in DumpRenderTree.");
163 } catch (IllegalArgumentException e) {
164 Log.e(TAG, "Caught IllegalArgumentException registering for location updates.");
169 * Unregisters this object from the location service.
171 private void unregisterFromLocationUpdates() {
174 mLocationManager.removeUpdates(this);
178 private boolean usePassiveOneShotLocation() {
179 if (!isOnlyPassiveLocationProviderEnabled()) return false;
181 // Do not request a location update if the only available location provider is
182 // the passive one. Make use of the last known location and call
183 // onLocationChanged directly.
184 final Location location = mLocationManager.getLastKnownLocation(
185 LocationManager.PASSIVE_PROVIDER);
186 if (location != null) {
187 ThreadUtils.runOnUiThread(new Runnable() {
190 updateNewLocation(location);
198 * Checks if the passive location provider is the only provider available
201 private boolean isOnlyPassiveLocationProviderEnabled() {
202 List<String> providers = mLocationManager.getProviders(true);
203 return providers != null && providers.size() == 1
204 && providers.get(0).equals(LocationManager.PASSIVE_PROVIDER);