Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / content / public / android / java / src / org / chromium / content / browser / LocationProviderFactory.java
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.
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.ThreadUtils;
16 import org.chromium.base.VisibleForTesting;
17
18 import java.util.List;
19
20 /**
21  * Factory to create a LocationProvider to allow us to inject
22  * a mock for tests.
23  */
24 public class LocationProviderFactory {
25     private static LocationProviderFactory.LocationProvider sProviderImpl;
26
27     /**
28      * LocationProviderFactory.get() returns an instance of this interface.
29      */
30     public interface LocationProvider {
31         public void start(boolean gpsEnabled);
32         public void stop();
33         public boolean isRunning();
34     }
35
36     private LocationProviderFactory() {
37     }
38
39     @VisibleForTesting
40     public static void setLocationProviderImpl(LocationProviderFactory.LocationProvider provider) {
41         assert sProviderImpl == null;
42         sProviderImpl = provider;
43     }
44
45     public static LocationProvider get(Context context) {
46         if (sProviderImpl == null) {
47             sProviderImpl = new LocationProviderImpl(context);
48         }
49         return sProviderImpl;
50     }
51
52     /**
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.
56      */
57     private static class LocationProviderImpl
58             implements LocationListener, LocationProviderFactory.LocationProvider {
59
60         // Log tag
61         private static final String TAG = "LocationProvider";
62
63         private Context mContext;
64         private LocationManager mLocationManager;
65         private boolean mIsRunning;
66
67         LocationProviderImpl(Context context) {
68             mContext = context;
69         }
70
71         /**
72          * Start listening for location updates.
73          * @param gpsEnabled Whether or not we're interested in high accuracy GPS.
74          */
75         @Override
76         public void start(boolean gpsEnabled) {
77             unregisterFromLocationUpdates();
78             registerForLocationUpdates(gpsEnabled);
79         }
80
81         /**
82          * Stop listening for location updates.
83          */
84         @Override
85         public void stop() {
86             unregisterFromLocationUpdates();
87         }
88
89         /**
90          * Returns true if we are currently listening for location updates, false if not.
91          */
92         @Override
93         public boolean isRunning() {
94             return mIsRunning;
95         }
96
97         @Override
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.
102             if (mIsRunning) {
103                 updateNewLocation(location);
104             }
105         }
106
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());
115         }
116
117         @Override
118         public void onStatusChanged(String provider, int status, Bundle extras) {
119         }
120
121         @Override
122         public void onProviderEnabled(String provider) {
123         }
124
125         @Override
126         public void onProviderDisabled(String provider) {
127         }
128
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.");
135             }
136         }
137
138         /**
139          * Registers this object with the location service.
140          */
141         private void registerForLocationUpdates(boolean isGpsEnabled) {
142             ensureLocationManagerCreated();
143             if (usePassiveOneShotLocation()) return;
144
145             assert !mIsRunning;
146             mIsRunning = true;
147
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.
150             try {
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.");
160             }
161         }
162
163         /**
164          * Unregisters this object from the location service.
165          */
166         private void unregisterFromLocationUpdates() {
167             if (mIsRunning) {
168                 mIsRunning = false;
169                 mLocationManager.removeUpdates(this);
170             }
171         }
172
173         private boolean usePassiveOneShotLocation() {
174             if (!isOnlyPassiveLocationProviderEnabled()) return false;
175
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() {
183                     @Override
184                     public void run() {
185                         updateNewLocation(location);
186                     }
187                 });
188             }
189             return true;
190         }
191
192         /*
193          * Checks if the passive location provider is the only provider available
194          * in the system.
195          */
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);
200         }
201     }
202 }
203
204