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#include "content/browser/geolocation/location_api_adapter_android.h"
6
7#include "base/android/jni_android.h"
8#include "base/android/jni_string.h"
9#include "base/bind.h"
10#include "base/location.h"
11#include "content/browser/geolocation/location_provider_android.h"
12#include "jni/LocationProviderAdapter_jni.h"
13
14using base::android::AttachCurrentThread;
15using base::android::CheckException;
16using base::android::ClearException;
17using content::AndroidLocationApiAdapter;
18
19static void NewLocationAvailable(JNIEnv* env, jclass,
20                                 jdouble latitude,
21                                 jdouble longitude,
22                                 jdouble time_stamp,
23                                 jboolean has_altitude, jdouble altitude,
24                                 jboolean has_accuracy, jdouble accuracy,
25                                 jboolean has_heading, jdouble heading,
26                                 jboolean has_speed, jdouble speed) {
27  AndroidLocationApiAdapter::OnNewLocationAvailable(latitude, longitude,
28      time_stamp, has_altitude, altitude, has_accuracy, accuracy,
29      has_heading, heading, has_speed, speed);
30}
31
32static void NewErrorAvailable(JNIEnv* env, jclass, jstring message) {
33  AndroidLocationApiAdapter::OnNewErrorAvailable(env, message);
34}
35
36namespace content {
37
38AndroidLocationApiAdapter::AndroidLocationApiAdapter()
39    : location_provider_(NULL) {
40}
41
42AndroidLocationApiAdapter::~AndroidLocationApiAdapter() {
43  CHECK(!location_provider_);
44  CHECK(!message_loop_.get());
45  CHECK(java_location_provider_android_object_.is_null());
46}
47
48bool AndroidLocationApiAdapter::Start(
49    LocationProviderAndroid* location_provider, bool high_accuracy) {
50  JNIEnv* env = AttachCurrentThread();
51  if (!location_provider_) {
52    location_provider_ = location_provider;
53    CHECK(java_location_provider_android_object_.is_null());
54    CreateJavaObject(env);
55    {
56      base::AutoLock lock(lock_);
57      CHECK(!message_loop_.get());
58      message_loop_ = base::MessageLoopProxy::current();
59    }
60  }
61  // At this point we should have all our pre-conditions ready, and they'd only
62  // change in Stop() which must be called on the same thread as here.
63  CHECK(location_provider_);
64  CHECK(message_loop_.get());
65  CHECK(!java_location_provider_android_object_.is_null());
66  // We'll start receiving notifications from java in the main thread looper
67  // until Stop() is called.
68  return Java_LocationProviderAdapter_start(env,
69      java_location_provider_android_object_.obj(), high_accuracy);
70}
71
72void AndroidLocationApiAdapter::Stop() {
73  if (!location_provider_) {
74    CHECK(!message_loop_.get());
75    CHECK(java_location_provider_android_object_.is_null());
76    return;
77  }
78
79  {
80    base::AutoLock lock(lock_);
81    message_loop_ = NULL;
82  }
83
84  location_provider_ = NULL;
85
86  JNIEnv* env = AttachCurrentThread();
87  Java_LocationProviderAdapter_stop(
88      env, java_location_provider_android_object_.obj());
89  java_location_provider_android_object_.Reset();
90}
91
92// static
93void AndroidLocationApiAdapter::NotifyProviderNewGeoposition(
94    const Geoposition& geoposition) {
95  // Called on the geolocation thread, safe to access location_provider_ here.
96  if (GetInstance()->location_provider_) {
97    CHECK(GetInstance()->message_loop_->BelongsToCurrentThread());
98    GetInstance()->location_provider_->NotifyNewGeoposition(geoposition);
99  }
100}
101
102// static
103void AndroidLocationApiAdapter::OnNewLocationAvailable(
104    double latitude, double longitude, double time_stamp,
105    bool has_altitude, double altitude,
106    bool has_accuracy, double accuracy,
107    bool has_heading, double heading,
108    bool has_speed, double speed) {
109  Geoposition position;
110  position.latitude = latitude;
111  position.longitude = longitude;
112  position.timestamp = base::Time::FromDoubleT(time_stamp);
113  if (has_altitude)
114    position.altitude = altitude;
115  if (has_accuracy)
116    position.accuracy = accuracy;
117  if (has_heading)
118    position.heading = heading;
119  if (has_speed)
120    position.speed = speed;
121  GetInstance()->OnNewGeopositionInternal(position);
122}
123
124// static
125void AndroidLocationApiAdapter::OnNewErrorAvailable(JNIEnv* env,
126                                                    jstring message) {
127  Geoposition position_error;
128  position_error.error_code = Geoposition::ERROR_CODE_POSITION_UNAVAILABLE;
129  position_error.error_message =
130      base::android::ConvertJavaStringToUTF8(env, message);
131  GetInstance()->OnNewGeopositionInternal(position_error);
132}
133
134// static
135AndroidLocationApiAdapter* AndroidLocationApiAdapter::GetInstance() {
136  return Singleton<AndroidLocationApiAdapter>::get();
137}
138
139// static
140bool AndroidLocationApiAdapter::RegisterGeolocationService(JNIEnv* env) {
141  return RegisterNativesImpl(env);
142}
143
144void AndroidLocationApiAdapter::CreateJavaObject(JNIEnv* env) {
145  // Create the Java AndroidLocationProvider object.
146  java_location_provider_android_object_.Reset(
147      Java_LocationProviderAdapter_create(env,
148          base::android::GetApplicationContext()));
149  CHECK(!java_location_provider_android_object_.is_null());
150}
151
152void AndroidLocationApiAdapter::OnNewGeopositionInternal(
153    const Geoposition& geoposition) {
154  base::AutoLock lock(lock_);
155  if (!message_loop_.get())
156    return;
157  message_loop_->PostTask(
158      FROM_HERE,
159      base::Bind(
160          &AndroidLocationApiAdapter::NotifyProviderNewGeoposition,
161          geoposition));
162}
163
164}  // namespace content
165