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 |