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.WebViewCore;
28
29
30/**
31 * Implements the Java side of GeolocationServiceAndroid.
32 */
33final class GeolocationService implements LocationListener {
34
35    // Log tag
36    private static final String TAG = "geolocationService";
37
38    private long mNativeObject;
39    private LocationManager mLocationManager;
40    private boolean mIsGpsEnabled;
41    private boolean mIsRunning;
42    private boolean mIsNetworkProviderAvailable;
43    private boolean mIsGpsProviderAvailable;
44
45    /**
46     * Constructor
47     * @param context The context from which we obtain the system service.
48     * @param nativeObject The native object to which this object will report position updates and
49     *     errors.
50     */
51    public GeolocationService(Context context, long nativeObject) {
52        mNativeObject = nativeObject;
53        // Register newLocationAvailable with platform service.
54        mLocationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
55        if (mLocationManager == null) {
56            Log.e(TAG, "Could not get location manager.");
57        }
58     }
59
60    /**
61     * Start listening for location updates.
62     */
63    public boolean start() {
64        registerForLocationUpdates();
65        mIsRunning = true;
66        return mIsNetworkProviderAvailable || mIsGpsProviderAvailable;
67    }
68
69    /**
70     * Stop listening for location updates.
71     */
72    public void stop() {
73        unregisterFromLocationUpdates();
74        mIsRunning = false;
75    }
76
77    /**
78     * Sets whether to use the GPS.
79     * @param enable Whether to use the GPS.
80     */
81    public void setEnableGps(boolean enable) {
82        if (mIsGpsEnabled != enable) {
83            mIsGpsEnabled = enable;
84            if (mIsRunning) {
85                // There's no way to unregister from a single provider, so we can
86                // only unregister from all, then reregister with all but the GPS.
87                unregisterFromLocationUpdates();
88                registerForLocationUpdates();
89                // Check that the providers are still available after we re-register.
90                maybeReportError("The last location provider is no longer available");
91            }
92        }
93    }
94
95    /**
96     * LocationListener implementation.
97     * Called when the location has changed.
98     * @param location The new location, as a Location object.
99     */
100    public void onLocationChanged(Location location) {
101        // Callbacks from the system location sevice are queued to this thread, so it's possible
102        // that we receive callbacks after unregistering. At this point, the native object will no
103        // longer exist.
104        if (mIsRunning) {
105            nativeNewLocationAvailable(mNativeObject, location);
106        }
107    }
108
109    /**
110     * LocationListener implementation.
111     * Called when the provider status changes.
112     * @param provider The name of the provider.
113     * @param status The new status of the provider.
114     * @param extras an optional Bundle with provider specific data.
115     */
116    public void onStatusChanged(String providerName, int status, Bundle extras) {
117        boolean isAvailable = (status == LocationProvider.AVAILABLE);
118        if (LocationManager.NETWORK_PROVIDER.equals(providerName)) {
119            mIsNetworkProviderAvailable = isAvailable;
120        } else if (LocationManager.GPS_PROVIDER.equals(providerName)) {
121            mIsGpsProviderAvailable = isAvailable;
122        }
123        maybeReportError("The last location provider is no longer available");
124    }
125
126    /**
127     * LocationListener implementation.
128     * Called when the provider is enabled.
129     * @param provider The name of the location provider that is now enabled.
130     */
131    public void onProviderEnabled(String providerName) {
132        // No need to notify the native side. It's enough to start sending
133        // valid position fixes again.
134        if (LocationManager.NETWORK_PROVIDER.equals(providerName)) {
135            mIsNetworkProviderAvailable = true;
136        } else if (LocationManager.GPS_PROVIDER.equals(providerName)) {
137            mIsGpsProviderAvailable = true;
138        }
139    }
140
141    /**
142     * LocationListener implementation.
143     * Called when the provider is disabled.
144     * @param provider The name of the location provider that is now disabled.
145     */
146    public void onProviderDisabled(String providerName) {
147        if (LocationManager.NETWORK_PROVIDER.equals(providerName)) {
148            mIsNetworkProviderAvailable = false;
149        } else if (LocationManager.GPS_PROVIDER.equals(providerName)) {
150            mIsGpsProviderAvailable = false;
151        }
152        maybeReportError("The last location provider was disabled");
153    }
154
155    /**
156     * Registers this object with the location service.
157     */
158    private void registerForLocationUpdates() {
159        try {
160            // Registration may fail if providers are not present on the device.
161            try {
162                mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, this);
163                mIsNetworkProviderAvailable = true;
164            } catch(IllegalArgumentException e) { }
165            if (mIsGpsEnabled) {
166                try {
167                    mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);
168                    mIsGpsProviderAvailable = true;
169                } catch(IllegalArgumentException e) { }
170            }
171        } catch(SecurityException e) {
172            Log.e(TAG, "Caught security exception registering for location updates from system. " +
173                "This should only happen in DumpRenderTree.");
174        }
175    }
176
177    /**
178     * Unregisters this object from the location service.
179     */
180    private void unregisterFromLocationUpdates() {
181        mLocationManager.removeUpdates(this);
182        mIsNetworkProviderAvailable = false;
183        mIsGpsProviderAvailable = false;
184    }
185
186    /**
187     * Reports an error if neither the network nor the GPS provider is available.
188     */
189    private void maybeReportError(String message) {
190        // Callbacks from the system location sevice are queued to this thread, so it's possible
191        // that we receive callbacks after unregistering. At this point, the native object will no
192        // longer exist.
193        if (mIsRunning && !mIsNetworkProviderAvailable && !mIsGpsProviderAvailable) {
194            nativeNewErrorAvailable(mNativeObject, message);
195        }
196    }
197
198    // Native functions
199    private static native void nativeNewLocationAvailable(long nativeObject, Location location);
200    private static native void nativeNewErrorAvailable(long nativeObject, String message);
201}
202