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 ThreadUtils.assertOnUiThread(); |
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 |