1
// Copyright 2008, Google Inc.
3
// Redistribution and use in source and binary forms, with or without
4
// modification, are permitted provided that the following conditions are met:
6
// 1. Redistributions of source code must retain the above copyright notice,
7
// this list of conditions and the following disclaimer.
8
// 2. Redistributions in binary form must reproduce the above copyright notice,
9
// this list of conditions and the following disclaimer in the documentation
10
// and/or other materials provided with the distribution.
11
// 3. Neither the name of Google Inc. nor the names of its contributors may be
12
// used to endorse or promote products derived from this software without
13
// specific prior written permission.
15
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
16
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
17
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
18
// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21
// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22
// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
24
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
// TODO(cprince): remove platform-specific #ifdef guards when OS-specific
27
// sources (e.g. WIN32_CPPSRCS) are implemented.
30
#include "gears/geolocation/gps_device_android.h"
32
#include "gears/base/android/java_local_frame.h"
33
#include "gears/base/android/java_exception_scope.h"
34
#include "gears/base/android/webview_manager.h"
36
static const char *kGpsProviderClass =
37
GEARS_JAVA_PACKAGE "/AndroidGpsLocationProvider";
39
static const char *kLocationClass = "android/location/Location";
41
static JavaClass::Method provider_methods[] = {
42
{ JavaClass::kNonStatic, "<init>", "(Landroid/webkit/WebView;J)V"},
43
{ JavaClass::kNonStatic, "shutdown", "()V"},
46
enum ProviderMethodID {
51
static const char16 *kGpsDisabledMessage =
52
STRING16(L"GPS is disabled.");
53
static const char16 *kGpsErrorMessage =
54
STRING16(L"GPS failed to get a position fix.");
56
JNINativeMethod AndroidGpsDevice::native_methods_[] = {
57
{"nativeLocationChanged",
58
"(Landroid/location/Location;J)V",
59
reinterpret_cast<void*>(AndroidGpsDevice::OnLocationChanged)
61
{"nativeProviderError",
63
reinterpret_cast<void*>(AndroidGpsDevice::OnProviderError)
68
// This assumes that position_2 is a good fix.
69
static bool PositionsDiffer(const Position &position_1,
70
const Position &position_2);
71
static bool GetPositionFromJavaLocation(JNIEnv *env,
72
const jobject &location,
75
// GpsLocationProvider factory method for platform-dependent GPS devices.
77
GpsDeviceBase *GpsLocationProvider::NewGpsDevice(
78
GpsDeviceBase::ListenerInterface *listener) {
79
return new AndroidGpsDevice(listener);
82
AndroidGpsDevice::AndroidGpsDevice(GpsDeviceBase::ListenerInterface *listener)
83
: GpsDeviceBase(listener) {
86
if (!provider_java_class_.FindClass(kGpsProviderClass)) {
87
LOG(("Could not find the AndroidGpsDevice class.\n"));
92
// Get the provider method IDs.
93
if (!provider_java_class_.GetMultipleMethodIDs(provider_methods,
94
NELEM(provider_methods))) {
95
LOG(("Could not find the AndroidGpsDevice methods.\n"));
100
// Get the WebView pointer. Note that this MUST execute on either
101
// the browser main thread or a worker thread. See WebView::RegisterWebView()
102
// method comments for more details aboud this constraint.
104
if (!WebViewManager::GetWebView(&webview)) {
105
LOG(("Could not find the window object.\n"));
109
webview_.Reset(webview);
110
// We're now ready to go. Start the thread.
114
void AndroidGpsDevice::BeforeLoop() {
115
JavaExceptionScope scope;
116
JavaLocalFrame frame;
117
// Construct the Java Gps provider.
118
jlong native_object_ptr = reinterpret_cast<jlong>(this);
119
jobject object = JniGetEnv()->NewObject(provider_java_class_.Get(),
120
provider_methods[METHOD_ID_INIT].id,
123
if (scope.Occurred()) {
124
// Something went wrong when instantiating the Java object.
125
LOG(("Could not instantiate the Java object.\n"));
126
position_.error_code = Position::ERROR_CODE_POSITION_UNAVAILABLE;
127
position_.error_message = kGpsErrorMessage;
128
// No need to call 'scope.Clear();', as the exception
129
// will be cleared by the destructor of scope.
131
provider_java_object_.MoveLocal(object);
132
// Register the native callback.
133
jniRegisterNativeMethods(JniGetEnv(),
136
NELEM(native_methods_));
140
AndroidGpsDevice::~AndroidGpsDevice() {
144
void AndroidGpsDevice::AfterLoop() {
145
// Call the shutdown method if the Java object was created
147
if (provider_java_object_.Get()) {
148
JniGetEnv()->CallVoidMethod(provider_java_object_.Get(),
149
provider_methods[METHOD_ID_SHUTDOWN].id);
154
void AndroidGpsDevice::OnLocationChanged(JNIEnv *env,
160
AndroidGpsDevice *self_ptr =
161
reinterpret_cast<AndroidGpsDevice*>(self);
163
if (GetPositionFromJavaLocation(env, location, &position) &&
164
position.IsGoodFix()) {
165
self_ptr->PositionChanged(position);
170
void AndroidGpsDevice::OnProviderError(JNIEnv *env,
172
jboolean is_disabled,
175
AndroidGpsDevice *self_ptr =
176
reinterpret_cast<AndroidGpsDevice*>(self);
177
self_ptr->ProviderError(is_disabled);
180
void AndroidGpsDevice::PositionChanged(const Position &new_position) {
181
if (PositionsDiffer(position_, new_position)) {
182
position_ = new_position;
183
LOG(("Position changed, updating listener.\n"));
184
listener_->GpsPositionUpdateAvailable(position_);
188
void AndroidGpsDevice::ProviderError(bool is_disabled) {
190
LOG(("GPS disabled, updating listener.\n"));
191
listener_->GpsFatalError(Position::ERROR_CODE_POSITION_UNAVAILABLE,
192
kGpsDisabledMessage);
194
LOG(("GPS error, updating listener.\n"));
195
listener_->GpsFatalError(Position::ERROR_CODE_POSITION_UNAVAILABLE,
200
static bool GetPositionFromJavaLocation(JNIEnv *env,
201
const jobject &location,
202
Position *position) {
204
JavaLocalFrame frame;
205
JavaExceptionScope scope;
206
JavaClass clazz(kLocationClass, env->GetObjectClass(location));
208
JavaClass::Method methods[] = {
209
{ JavaClass::kNonStatic, "getLatitude", "()D"},
210
{ JavaClass::kNonStatic, "getLongitude", "()D"},
211
{ JavaClass::kNonStatic, "hasAltitude", "()Z"},
212
{ JavaClass::kNonStatic, "getAltitude", "()D"},
213
{ JavaClass::kNonStatic, "hasAccuracy", "()Z"},
214
{ JavaClass::kNonStatic, "getAccuracy", "()F"},
215
{ JavaClass::kNonStatic, "getTime", "()J"},
218
enum LocationMethods {
219
LOCATION_METHODS_GET_LATITUDE,
220
LOCATION_METHODS_GET_LONGITUDE,
221
LOCATION_METHODS_HAS_ALTITUDE,
222
LOCATION_METHODS_GET_ALTITUDE,
223
LOCATION_METHODS_HAS_ACCURACY,
224
LOCATION_METHODS_GET_ACCURACY,
225
LOCATION_METHODS_GET_TIME,
228
if (!clazz.GetMultipleMethodIDs(methods, NELEM(methods))) {
229
LOG(("Could not find the Location methods.\n"));
234
position->latitude = env->CallDoubleMethod(
236
methods[LOCATION_METHODS_GET_LATITUDE].id);
238
position->longitude = env->CallDoubleMethod(
240
methods[LOCATION_METHODS_GET_LONGITUDE].id);
242
if (env->CallBooleanMethod(location,
243
methods[LOCATION_METHODS_HAS_ALTITUDE].id)) {
244
position->altitude = env->CallDoubleMethod(
246
methods[LOCATION_METHODS_GET_ALTITUDE].id);
249
if (env->CallBooleanMethod(location,
250
methods[LOCATION_METHODS_HAS_ACCURACY].id)) {
251
position->accuracy = env->CallFloatMethod(
253
methods[LOCATION_METHODS_GET_ACCURACY].id);
255
// On the Android emulator valid position fixes do not have
256
// the accuracy set. For debugging purposes, we set it to 0 here.
257
position->accuracy = 0;
260
position->timestamp = env->CallLongMethod(
262
methods[LOCATION_METHODS_GET_TIME].id);
268
// TODO(andreip/steveblock): move this to GpsDeviceBase.
270
bool PositionsDiffer(const Position &position_1, const Position &position_2) {
271
assert(position_2.IsGoodFix());
273
if (!position_1.IsGoodFix()) {
276
return position_1.latitude != position_2.latitude ||
277
position_1.longitude != position_2.longitude ||
278
position_1.accuracy != position_2.accuracy ||
279
position_1.altitude != position_2.altitude;