blob: b5fcc54ccfa8c77a15c4cff9f05ca43c6db89a54 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "device/geolocation/location_api_adapter_android.h"
#include "base/android/context_utils.h"
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/bind.h"
#include "base/location.h"
#include "base/threading/thread_task_runner_handle.h"
#include "device/geolocation/location_provider_android.h"
#include "jni/LocationProviderAdapter_jni.h"
using base::android::AttachCurrentThread;
using base::android::CheckException;
using base::android::ClearException;
using base::android::JavaParamRef;
using device::AndroidLocationApiAdapter;
static void NewLocationAvailable(JNIEnv* env,
const JavaParamRef<jclass>&,
jdouble latitude,
jdouble longitude,
jdouble time_stamp,
jboolean has_altitude,
jdouble altitude,
jboolean has_accuracy,
jdouble accuracy,
jboolean has_heading,
jdouble heading,
jboolean has_speed,
jdouble speed) {
AndroidLocationApiAdapter::OnNewLocationAvailable(
latitude, longitude, time_stamp, has_altitude, altitude, has_accuracy,
accuracy, has_heading, heading, has_speed, speed);
}
static void NewErrorAvailable(JNIEnv* env,
const JavaParamRef<jclass>&,
const JavaParamRef<jstring>& message) {
AndroidLocationApiAdapter::OnNewErrorAvailable(env, message);
}
namespace device {
AndroidLocationApiAdapter::AndroidLocationApiAdapter()
: location_provider_(NULL) {}
AndroidLocationApiAdapter::~AndroidLocationApiAdapter() {
CHECK(!location_provider_);
CHECK(!task_runner_.get());
CHECK(java_location_provider_android_object_.is_null());
}
bool AndroidLocationApiAdapter::Start(
LocationProviderAndroid* location_provider,
bool high_accuracy) {
JNIEnv* env = AttachCurrentThread();
if (!location_provider_) {
location_provider_ = location_provider;
CHECK(java_location_provider_android_object_.is_null());
CreateJavaObject(env);
{
base::AutoLock lock(lock_);
CHECK(!task_runner_.get());
task_runner_ = base::ThreadTaskRunnerHandle::Get();
}
}
// At this point we should have all our pre-conditions ready, and they'd only
// change in Stop() which must be called on the same thread as here.
CHECK(location_provider_);
CHECK(task_runner_.get());
CHECK(!java_location_provider_android_object_.is_null());
// We'll start receiving notifications from java in the main thread looper
// until Stop() is called.
return Java_LocationProviderAdapter_start(
env, java_location_provider_android_object_, high_accuracy);
}
void AndroidLocationApiAdapter::Stop() {
if (!location_provider_) {
CHECK(!task_runner_.get());
CHECK(java_location_provider_android_object_.is_null());
return;
}
{
base::AutoLock lock(lock_);
task_runner_ = NULL;
}
location_provider_ = NULL;
JNIEnv* env = AttachCurrentThread();
Java_LocationProviderAdapter_stop(env,
java_location_provider_android_object_);
java_location_provider_android_object_.Reset();
}
// static
void AndroidLocationApiAdapter::NotifyProviderNewGeoposition(
const Geoposition& geoposition) {
// Called on the geolocation thread, safe to access location_provider_ here.
if (GetInstance()->location_provider_) {
CHECK(GetInstance()->task_runner_->BelongsToCurrentThread());
GetInstance()->location_provider_->NotifyNewGeoposition(geoposition);
}
}
// static
void AndroidLocationApiAdapter::OnNewLocationAvailable(double latitude,
double longitude,
double time_stamp,
bool has_altitude,
double altitude,
bool has_accuracy,
double accuracy,
bool has_heading,
double heading,
bool has_speed,
double speed) {
Geoposition position;
position.latitude = latitude;
position.longitude = longitude;
position.timestamp = base::Time::FromDoubleT(time_stamp);
if (has_altitude)
position.altitude = altitude;
if (has_accuracy)
position.accuracy = accuracy;
if (has_heading)
position.heading = heading;
if (has_speed)
position.speed = speed;
GetInstance()->OnNewGeopositionInternal(position);
}
// static
void AndroidLocationApiAdapter::OnNewErrorAvailable(JNIEnv* env,
jstring message) {
Geoposition position_error;
position_error.error_code = Geoposition::ERROR_CODE_POSITION_UNAVAILABLE;
position_error.error_message =
base::android::ConvertJavaStringToUTF8(env, message);
GetInstance()->OnNewGeopositionInternal(position_error);
}
// static
AndroidLocationApiAdapter* AndroidLocationApiAdapter::GetInstance() {
return base::Singleton<AndroidLocationApiAdapter>::get();
}
// static
bool AndroidLocationApiAdapter::RegisterGeolocationService(JNIEnv* env) {
return RegisterNativesImpl(env);
}
void AndroidLocationApiAdapter::CreateJavaObject(JNIEnv* env) {
// Create the Java LocationProviderAdapter object.
java_location_provider_android_object_.Reset(
Java_LocationProviderAdapter_create(
env, base::android::GetApplicationContext()));
CHECK(!java_location_provider_android_object_.is_null());
}
void AndroidLocationApiAdapter::OnNewGeopositionInternal(
const Geoposition& geoposition) {
base::AutoLock lock(lock_);
if (!task_runner_.get())
return;
task_runner_->PostTask(
FROM_HERE,
base::Bind(&AndroidLocationApiAdapter::NotifyProviderNewGeoposition,
geoposition));
}
} // namespace device