| // Copyright 2020 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/installable/installed_webapp_geolocation_bridge.h" |
| |
| #include <utility> |
| |
| #include "base/android/jni_android.h" |
| #include "base/android/jni_string.h" |
| #include "base/functional/bind.h" |
| #include "base/metrics/histogram_functions.h" |
| #include "chrome/android/chrome_jni_headers/InstalledWebappGeolocationBridge_jni.h" |
| #include "chrome/browser/installable/installed_webapp_geolocation_context.h" |
| #include "services/device/public/cpp/geolocation/geoposition.h" |
| #include "url/android/gurl_android.h" |
| |
| InstalledWebappGeolocationBridge::InstalledWebappGeolocationBridge( |
| mojo::PendingReceiver<Geolocation> receiver, |
| const GURL& url, |
| InstalledWebappGeolocationContext* context) |
| : context_(context), |
| url_(url), |
| high_accuracy_(false), |
| receiver_(this, std::move(receiver)) { |
| DCHECK(context_); |
| receiver_.set_disconnect_handler( |
| base::BindOnce(&InstalledWebappGeolocationBridge::OnConnectionError, |
| base::Unretained(this))); |
| } |
| |
| InstalledWebappGeolocationBridge::~InstalledWebappGeolocationBridge() { |
| StopUpdates(); |
| } |
| |
| void InstalledWebappGeolocationBridge::StartListeningForUpdates() { |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| if (java_ref_.is_null()) { |
| java_ref_.Reset(Java_InstalledWebappGeolocationBridge_create( |
| env, reinterpret_cast<intptr_t>(this), |
| url::GURLAndroid::FromNativeGURL(env, url_))); |
| } |
| Java_InstalledWebappGeolocationBridge_start(env, java_ref_, high_accuracy_); |
| } |
| |
| void InstalledWebappGeolocationBridge::StopUpdates() { |
| if (!java_ref_.is_null()) { |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| Java_InstalledWebappGeolocationBridge_stopAndDestroy(env, java_ref_); |
| java_ref_.Reset(); |
| } |
| } |
| |
| void InstalledWebappGeolocationBridge::SetHighAccuracy(bool high_accuracy) { |
| high_accuracy_ = high_accuracy; |
| |
| if (position_override_ && position_override_->is_position() && |
| device::ValidateGeoposition(*position_override_->get_position())) { |
| OnLocationUpdate(position_override_.Clone()); |
| return; |
| } |
| |
| StartListeningForUpdates(); |
| } |
| |
| void InstalledWebappGeolocationBridge::QueryNextPosition( |
| QueryNextPositionCallback callback) { |
| if (!position_callback_.is_null()) { |
| DVLOG(1) << "Overlapped call to QueryNextPosition!"; |
| OnConnectionError(); // Simulate a connection error. |
| return; |
| } |
| |
| position_callback_ = std::move(callback); |
| |
| if (current_position_) { |
| ReportCurrentPosition(); |
| } |
| } |
| |
| void InstalledWebappGeolocationBridge::SetOverride( |
| device::mojom::GeopositionResultPtr result) { |
| CHECK(result); |
| if (current_position_ && !position_callback_.is_null()) { |
| ReportCurrentPosition(); |
| } |
| |
| position_override_ = std::move(result); |
| StopUpdates(); |
| |
| OnLocationUpdate(position_override_.Clone()); |
| } |
| |
| void InstalledWebappGeolocationBridge::ClearOverride() { |
| position_override_.reset(); |
| StartListeningForUpdates(); |
| } |
| |
| void InstalledWebappGeolocationBridge::OnConnectionError() { |
| context_->OnConnectionError(this); |
| |
| // The above call deleted this instance, so the only safe thing to do is |
| // return. |
| } |
| |
| void InstalledWebappGeolocationBridge::OnLocationUpdate( |
| device::mojom::GeopositionResultPtr result) { |
| DCHECK(context_); |
| CHECK(result); |
| |
| current_position_ = std::move(result); |
| |
| if (!position_callback_.is_null()) |
| ReportCurrentPosition(); |
| } |
| |
| void InstalledWebappGeolocationBridge::ReportCurrentPosition() { |
| DCHECK(position_callback_); |
| CHECK(current_position_); |
| std::move(position_callback_).Run(std::move(current_position_)); |
| } |
| |
| void InstalledWebappGeolocationBridge::OnNewLocationAvailable( |
| JNIEnv* env, |
| 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) { |
| auto position = device::mojom::Geoposition::New(); |
| 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; |
| |
| // If position is invalid, mark it as unavailable. |
| device::mojom::GeopositionResultPtr result; |
| if (device::ValidateGeoposition(*position)) { |
| result = device::mojom::GeopositionResult::NewPosition(std::move(position)); |
| } else { |
| result = device::mojom::GeopositionResult::NewError( |
| device::mojom::GeopositionError::New( |
| device::mojom::GeopositionErrorCode::kPositionUnavailable, |
| /*error_message=*/"", /*error_technical=*/"")); |
| } |
| |
| OnLocationUpdate(std::move(result)); |
| } |
| |
| void InstalledWebappGeolocationBridge::OnNewErrorAvailable(JNIEnv* env, |
| jstring message) { |
| OnLocationUpdate(device::mojom::GeopositionResult::NewError( |
| device::mojom::GeopositionError::New( |
| device::mojom::GeopositionErrorCode::kPositionUnavailable, |
| base::android::ConvertJavaStringToUTF8(env, message), |
| /*error_technical=*/""))); |
| } |