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 org.chromium.base.ThreadUtils;
16 import org.chromium.base.VisibleForTesting;
18 import java.util.List;
21 * Factory to create a LocationProvider to allow us to inject
24 public class LocationProviderFactory {
25 private static LocationProviderFactory.LocationProvider sProviderImpl;
28 * LocationProviderFactory.get() returns an instance of this interface.
30 public interface LocationProvider {
31 public void start(boolean gpsEnabled);
33 public boolean isRunning();
36 private LocationProviderFactory() {
40 public static void setLocationProviderImpl(LocationProviderFactory.LocationProvider provider) {
41 assert sProviderImpl == null;
42 sProviderImpl = provider;
45 public static LocationProvider get(Context context) {
46 if (sProviderImpl == null) {
47 sProviderImpl = new LocationProviderImpl(context);
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 container class
55 * ensures that the start/stop calls into this class are done in the UI thread.
57 private static class LocationProviderImpl
58 implements LocationListener, LocationProviderFactory.LocationProvider {
61 private static final String TAG = "LocationProvider";
63 private Context mContext;
64 private LocationManager mLocationManager;
65 private boolean mIsRunning;
67 LocationProviderImpl(Context context) {
72 * Start listening for location updates.
73 * @param gpsEnabled Whether or not we're interested in high accuracy GPS.
76 public void start(boolean gpsEnabled) {
77 unregisterFromLocationUpdates();
78 registerForLocationUpdates(gpsEnabled);
82 * Stop listening for location updates.
86 unregisterFromLocationUpdates();
90 * Returns true if we are currently listening for location updates, false if not.
93 public boolean isRunning() {
98 public void onLocationChanged(Location location) {
99 // Callbacks from the system location service are queued to this thread, so it's
100 // possible that we receive callbacks after unregistering. At this point, the
101 // native object will no longer exist.
103 updateNewLocation(location);
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());
118 public void onStatusChanged(String provider, int status, Bundle extras) {
122 public void onProviderEnabled(String provider) {
126 public void onProviderDisabled(String provider) {
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.");
139 * Registers this object with the location service.
141 private void registerForLocationUpdates(boolean isGpsEnabled) {
142 ensureLocationManagerCreated();
143 if (usePassiveOneShotLocation()) return;
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.
151 Criteria criteria = new Criteria();
152 if (isGpsEnabled) criteria.setAccuracy(Criteria.ACCURACY_FINE);
153 mLocationManager.requestLocationUpdates(0, 0, criteria, this,
154 ThreadUtils.getUiThreadLooper());
155 } catch (SecurityException e) {
156 Log.e(TAG, "Caught security exception registering for location updates from " +
157 "system. This should only happen in DumpRenderTree.");
158 } catch (IllegalArgumentException e) {
159 Log.e(TAG, "Caught IllegalArgumentException registering for location updates.");
164 * Unregisters this object from the location service.
166 private void unregisterFromLocationUpdates() {
169 mLocationManager.removeUpdates(this);
173 private boolean usePassiveOneShotLocation() {
174 if (!isOnlyPassiveLocationProviderEnabled()) return false;
176 // Do not request a location update if the only available location provider is
177 // the passive one. Make use of the last known location and call
178 // onLocationChanged directly.
179 final Location location = mLocationManager.getLastKnownLocation(
180 LocationManager.PASSIVE_PROVIDER);
181 if (location != null) {
182 ThreadUtils.runOnUiThread(new Runnable() {
185 updateNewLocation(location);
193 * Checks if the passive location provider is the only provider available
196 private boolean isOnlyPassiveLocationProviderEnabled() {
197 List<String> providers = mLocationManager.getProviders(true);
198 return providers != null && providers.size() == 1
199 && providers.get(0).equals(LocationManager.PASSIVE_PROVIDER);