1/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.webkit;
18
19import android.app.ActivityThread;
20import android.content.Context;
21import android.location.Location;
22import android.location.LocationListener;
23import android.location.LocationManager;
24import android.location.LocationProvider;
25import android.os.Bundle;
26import android.util.Log;
27import android.webkit.WebView;
28import android.webkit.WebViewCore;
29
30
31/**
32 * Implements the Java side of GeolocationServiceAndroid.
33 */
34final class GeolocationService implements LocationListener {
35
36    // Log tag
37    private static final String TAG = "geolocationService";
38
39    private long mNativeObject;
40    private LocationManager mLocationManager;
41    private boolean mIsGpsEnabled;
42    private boolean mIsRunning;
43    private boolean mIsNetworkProviderAvailable;
44    private boolean mIsGpsProviderAvailable;
45
46    /**
47     * Constructor
48     * @param nativeObject The native object to which this object will report position updates and
49     *     errors.
50     */
51    public GeolocationService(long nativeObject) {
52        mNativeObject = nativeObject;
53        // Register newLocationAvailable with platform service.
54        ActivityThread thread = ActivityThread.systemMain();
55        Context context = thread.getApplication();
56        mLocationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
57        if (mLocationManager == null) {
58            Log.e(TAG, "Could not get location manager.");
59        }
60     }
61
62    /**
63     * Start listening for location updates.
64     */
65    public void start() {
66        registerForLocationUpdates();
67        mIsRunning = true;
68    }
69
70    /**
71     * Stop listening for location updates.
72     */
73    public void stop() {
74        unregisterFromLocationUpdates();
75        mIsRunning = false;
76    }
77
78    /**
79     * Sets whether to use the GPS.
80     * @param enable Whether to use the GPS.
81     */
82    public void setEnableGps(boolean enable) {
83        if (mIsGpsEnabled != enable) {
84            mIsGpsEnabled = enable;
85            if (mIsRunning) {
86                // There's no way to unregister from a single provider, so we can
87                // only unregister from all, then reregister with all but the GPS.
88                unregisterFromLocationUpdates();
89                registerForLocationUpdates();
90            }
91        }
92    }
93
94    /**
95     * LocationListener implementation.
96     * Called when the location has changed.
97     * @param location The new location, as a Location object.
98     */
99    public void onLocationChanged(Location location) {
100        // Callbacks from the system location sevice are queued to this thread, so it's possible
101        // that we receive callbacks after unregistering. At this point, the native object will no
102        // longer exist.
103        if (mIsRunning) {
104            nativeNewLocationAvailable(mNativeObject, location);
105        }
106    }
107
108    /**
109     * LocationListener implementation.
110     * Called when the provider status changes.
111     * @param provider The name of the provider.
112     * @param status The new status of the provider.
113     * @param extras an optional Bundle with provider specific data.
114     */
115    public void onStatusChanged(String providerName, int status, Bundle extras) {
116        boolean isAvailable = (status == LocationProvider.AVAILABLE);
117        if (LocationManager.NETWORK_PROVIDER.equals(providerName)) {
118            mIsNetworkProviderAvailable = isAvailable;
119        } else if (LocationManager.GPS_PROVIDER.equals(providerName)) {
120            mIsGpsProviderAvailable = isAvailable;
121        }
122        maybeReportError("The last location provider is no longer available");
123    }
124
125    /**
126     * LocationListener implementation.
127     * Called when the provider is enabled.
128     * @param provider The name of the location provider that is now enabled.
129     */
130    public void onProviderEnabled(String providerName) {
131        // No need to notify the native side. It's enough to start sending
132        // valid position fixes again.
133        if (LocationManager.NETWORK_PROVIDER.equals(providerName)) {
134            mIsNetworkProviderAvailable = true;
135        } else if (LocationManager.GPS_PROVIDER.equals(providerName)) {
136            mIsGpsProviderAvailable = true;
137        }
138    }
139
140    /**
141     * LocationListener implementation.
142     * Called when the provider is disabled.
143     * @param provider The name of the location provider that is now disabled.
144     */
145    public void onProviderDisabled(String providerName) {
146        if (LocationManager.NETWORK_PROVIDER.equals(providerName)) {
147            mIsNetworkProviderAvailable = false;
148        } else if (LocationManager.GPS_PROVIDER.equals(providerName)) {
149            mIsGpsProviderAvailable = false;
150        }
151        maybeReportError("The last location provider was disabled");
152    }
153
154    /**
155     * Registers this object with the location service.
156     */
157    private void registerForLocationUpdates() {
158        try {
159            mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, this);
160            mIsNetworkProviderAvailable = true;
161            if (mIsGpsEnabled) {
162                mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);
163                mIsGpsProviderAvailable = true;
164            }
165        } catch(SecurityException e) {
166            Log.e(TAG, "Caught security exception registering for location updates from system. " +
167                "This should only happen in DumpRenderTree.");
168        }
169    }
170
171    /**
172     * Unregisters this object from the location service.
173     */
174    private void unregisterFromLocationUpdates() {
175        mLocationManager.removeUpdates(this);
176    }
177
178    /**
179     * Reports an error if neither the network nor the GPS provider is available.
180     */
181    private void maybeReportError(String message) {
182        // Callbacks from the system location sevice are queued to this thread, so it's possible
183        // that we receive callbacks after unregistering. At this point, the native object will no
184        // longer exist.
185        if (mIsRunning && !mIsNetworkProviderAvailable && !mIsGpsProviderAvailable) {
186            nativeNewErrorAvailable(mNativeObject, message);
187        }
188    }
189
190    // Native functions
191    private static native void nativeNewLocationAvailable(long nativeObject, Location location);
192    private static native void nativeNewErrorAvailable(long nativeObject, String message);
193}
194