- add sources.
[platform/framework/web/crosswalk.git] / src / content / public / android / java / src / org / chromium / content / browser / LocationProvider.java
1 // Copyright (c) 2012 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 import android.util.Log;
14
15 import org.chromium.base.ActivityStatus;
16 import org.chromium.base.CalledByNative;
17 import org.chromium.base.ThreadUtils;
18
19 import java.util.List;
20 import java.util.concurrent.FutureTask;
21
22 /**
23  * Implements the Java side of LocationProviderAndroid.
24  * Delegates all real functionality to the inner class.
25  * See detailed documentation on
26  * content/browser/geolocation/android_location_api_adapter.h.
27  * Based on android.webkit.GeolocationService.java
28  */
29 class LocationProvider {
30
31     // Log tag
32     private static final String TAG = "LocationProvider";
33
34     /**
35      * This is the core of android location provider. It is a separate class for clarity
36      * so that it can manage all processing completely in the UI thread. The container class
37      * ensures that the start/stop calls into this class are done in the UI thread.
38      */
39     private static class LocationProviderImpl
40             implements LocationListener, ActivityStatus.StateListener {
41
42         private Context mContext;
43         private LocationManager mLocationManager;
44         private boolean mIsRunning;
45         private boolean mShouldRunAfterActivityResume;
46         private boolean mIsGpsEnabled;
47
48         LocationProviderImpl(Context context) {
49             mContext = context;
50         }
51
52         @Override
53         public void onActivityStateChange(int state) {
54             if (state == ActivityStatus.PAUSED) {
55                 mShouldRunAfterActivityResume |= mIsRunning;
56                 unregisterFromLocationUpdates();
57             } else if (state == ActivityStatus.RESUMED) {
58                 assert !mIsRunning;
59                 if (mShouldRunAfterActivityResume) {
60                     registerForLocationUpdates();
61                 }
62             }
63         }
64
65         /**
66          * Start listening for location updates.
67          * @param gpsEnabled Whether or not we're interested in high accuracy GPS.
68          */
69         private void start(boolean gpsEnabled) {
70             if (!mIsRunning && !mShouldRunAfterActivityResume) {
71                 // Currently idle so start listening to activity status changes.
72                 ActivityStatus.registerStateListener(this);
73             }
74             mIsGpsEnabled = gpsEnabled;
75
76             if (ActivityStatus.getState() != ActivityStatus.RESUMED) {
77                 mShouldRunAfterActivityResume = true;
78             } else {
79                 unregisterFromLocationUpdates();
80                 registerForLocationUpdates();
81             }
82         }
83
84         /**
85          * Stop listening for location updates.
86          */
87         private void stop() {
88             unregisterFromLocationUpdates();
89             ActivityStatus.unregisterStateListener(this);
90             mShouldRunAfterActivityResume = false;
91         }
92
93         /**
94          * Returns true if we are currently listening for location updates, false if not.
95          */
96         private boolean isRunning() {
97             return mIsRunning;
98         }
99
100         @Override
101         public void onLocationChanged(Location location) {
102             // Callbacks from the system location sevice are queued to this thread, so it's
103             // possible that we receive callbacks after unregistering. At this point, the
104             // native object will no longer exist.
105             if (mIsRunning) {
106                 updateNewLocation(location);
107             }
108         }
109
110         private void updateNewLocation(Location location) {
111             nativeNewLocationAvailable(location.getLatitude(), location.getLongitude(),
112                     location.getTime() / 1000.0,
113                     location.hasAltitude(), location.getAltitude(),
114                     location.hasAccuracy(), location.getAccuracy(),
115                     location.hasBearing(), location.getBearing(),
116                     location.hasSpeed(), location.getSpeed());
117         }
118
119         @Override
120         public void onStatusChanged(String provider, int status, Bundle extras) {
121         }
122
123         @Override
124         public void onProviderEnabled(String provider) {
125         }
126
127         @Override
128         public void onProviderDisabled(String provider) {
129         }
130
131         private void ensureLocationManagerCreated() {
132             if (mLocationManager != null) return;
133             mLocationManager = (LocationManager) mContext.getSystemService(
134                     Context.LOCATION_SERVICE);
135             if (mLocationManager == null) {
136                 Log.e(TAG, "Could not get location manager.");
137             }
138         }
139
140         /**
141          * Registers this object with the location service.
142          */
143         private void registerForLocationUpdates() {
144             ensureLocationManagerCreated();
145             if (usePassiveOneShotLocation()) return;
146
147             assert !mIsRunning;
148             mIsRunning = true;
149
150             // We're running on the main thread. The C++ side is responsible to
151             // bounce notifications to the Geolocation thread as they arrive in the mainLooper.
152             try {
153                 Criteria criteria = new Criteria();
154                 mLocationManager.requestLocationUpdates(0, 0, criteria, this,
155                         ThreadUtils.getUiThreadLooper());
156                 if (mIsGpsEnabled) {
157                     criteria.setAccuracy(Criteria.ACCURACY_FINE);
158                     mLocationManager.requestLocationUpdates(0, 0, criteria, this,
159                             ThreadUtils.getUiThreadLooper());
160                 }
161             } catch(SecurityException e) {
162                 Log.e(TAG, "Caught security exception registering for location updates from " +
163                     "system. This should only happen in DumpRenderTree.");
164             } catch(IllegalArgumentException e) {
165                 Log.e(TAG, "Caught IllegalArgumentException registering for location updates.");
166             }
167         }
168
169         /**
170          * Unregisters this object from the location service.
171          */
172         private void unregisterFromLocationUpdates() {
173             if (mIsRunning) {
174                 mIsRunning = false;
175                 mLocationManager.removeUpdates(this);
176             }
177         }
178
179         private boolean usePassiveOneShotLocation() {
180             if (!isOnlyPassiveLocationProviderEnabled()) return false;
181
182             // Do not request a location update if the only available location provider is
183             // the passive one. Make use of the last known location and call
184             // onLocationChanged directly.
185             final Location location = mLocationManager.getLastKnownLocation(
186                     LocationManager.PASSIVE_PROVIDER);
187             if (location != null) {
188                 ThreadUtils.runOnUiThread(new Runnable() {
189                     @Override
190                     public void run() {
191                         updateNewLocation(location);
192                     }
193                 });
194             }
195             return true;
196         }
197
198         /*
199          * Checks if the passive location provider is the only provider available
200          * in the system.
201          */
202         private boolean isOnlyPassiveLocationProviderEnabled() {
203             List<String> providers = mLocationManager.getProviders(true);
204             return providers != null && providers.size() == 1
205                     && providers.get(0).equals(LocationManager.PASSIVE_PROVIDER);
206         }
207     }
208
209     // Delegate handling the real work in the main thread.
210     private LocationProviderImpl mImpl;
211
212     private LocationProvider(Context context) {
213         mImpl = new LocationProviderImpl(context);
214     }
215
216     @CalledByNative
217     static LocationProvider create(Context context) {
218         return new LocationProvider(context);
219     }
220
221     /**
222      * Start listening for location updates until we're told to quit. May be
223      * called in any thread.
224      * @param gpsEnabled Whether or not we're interested in high accuracy GPS.
225      */
226     @CalledByNative
227     public boolean start(final boolean gpsEnabled) {
228         FutureTask<Void> task = new FutureTask<Void>(new Runnable() {
229             @Override
230             public void run() {
231                 mImpl.start(gpsEnabled);
232             }
233         }, null);
234         ThreadUtils.runOnUiThread(task);
235         return true;
236     }
237
238     /**
239      * Stop listening for location updates. May be called in any thread.
240      */
241     @CalledByNative
242     public void stop() {
243         FutureTask<Void> task = new FutureTask<Void>(new Runnable() {
244             @Override
245             public void run() {
246                 mImpl.stop();
247             }
248         }, null);
249         ThreadUtils.runOnUiThread(task);
250     }
251
252     /**
253      * Returns true if we are currently listening for location updates, false if not.
254      * Must be called only in the UI thread.
255      */
256     public boolean isRunning() {
257         assert ThreadUtils.runningOnUiThread();
258         return mImpl.isRunning();
259     }
260
261     // Native functions
262     public static native void nativeNewLocationAvailable(
263             double latitude, double longitude, double timeStamp,
264             boolean hasAltitude, double altitude,
265             boolean hasAccuracy, double accuracy,
266             boolean hasHeading, double heading,
267             boolean hasSpeed, double speed);
268     public static native void nativeNewErrorAvailable(String message);
269 }