blob: a55cfd4977e48035ce6fe77661a8d5d06c11b072 [file] [log] [blame]
// Copyright 2020 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 "chrome/browser/installable/installed_webapp_geolocation_bridge.h"
#include <utility>
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/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"
namespace {
const char kLocationUpdateHistogramName[] =
"TrustedWebActivity.LocationUpdateErrorCode";
// Do not modify or reuse existing entries; they are used in a UMA histogram.
// Please edit TrustedWebActivityLocationErrorCode in the enums.xml if a value
// is added.
// GENERATED_JAVA_ENUM_PACKAGE: (
// org.chromium.chrome.browser.browserservices.constants)
enum class LocationUpdateError {
// There was no error.
kNone = 0,
// Geoposition could not be determined, i.e. error from the TWA client app.
kLocationError = 1,
// Invalid position.
kInvalidPosition = 2,
// Trusted web activity service not found or does not handle the request.
kNoTwa = 3,
// NOTE: Add entries only immediately above this line.
kMaxValue = kNoTwa
};
} // namespace
InstalledWebappGeolocationBridge::InstalledWebappGeolocationBridge(
mojo::PendingReceiver<Geolocation> receiver,
const GURL& origin,
InstalledWebappGeolocationContext* context)
: context_(context),
origin_(origin),
high_accuracy_(false),
has_position_to_report_(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()) {
base::android::ScopedJavaLocalRef<jstring> j_origin =
base::android::ConvertUTF8ToJavaString(
env, origin_.DeprecatedGetOriginAsURL().spec());
java_ref_.Reset(Java_InstalledWebappGeolocationBridge_create(
env, reinterpret_cast<intptr_t>(this), j_origin));
}
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 (device::ValidateGeoposition(position_override_)) {
OnLocationUpdate(position_override_);
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 (has_position_to_report_)
ReportCurrentPosition();
}
void InstalledWebappGeolocationBridge::SetOverride(
const device::mojom::Geoposition& position) {
if (!position_callback_.is_null())
ReportCurrentPosition();
position_override_ = position;
StopUpdates();
OnLocationUpdate(position_override_);
}
void InstalledWebappGeolocationBridge::ClearOverride() {
position_override_ = device::mojom::Geoposition();
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(
const device::mojom::Geoposition& position) {
DCHECK(context_);
current_position_ = position;
current_position_.valid = device::ValidateGeoposition(position);
has_position_to_report_ = true;
if (!position_callback_.is_null())
ReportCurrentPosition();
}
void InstalledWebappGeolocationBridge::ReportCurrentPosition() {
DCHECK(position_callback_);
std::move(position_callback_).Run(current_position_.Clone());
has_position_to_report_ = false;
}
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) {
device::mojom::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;
// If position is invalid, mark it as unavailable.
if (!device::ValidateGeoposition(position)) {
position.error_code =
device::mojom::Geoposition::ErrorCode::POSITION_UNAVAILABLE;
base::UmaHistogramEnumeration(kLocationUpdateHistogramName,
LocationUpdateError::kInvalidPosition);
} else {
base::UmaHistogramEnumeration(kLocationUpdateHistogramName,
LocationUpdateError::kNone);
}
OnLocationUpdate(position);
}
void InstalledWebappGeolocationBridge::OnNewErrorAvailable(JNIEnv* env,
jstring message) {
base::UmaHistogramEnumeration(kLocationUpdateHistogramName,
LocationUpdateError::kLocationError);
device::mojom::Geoposition position_error;
position_error.error_code =
device::mojom::Geoposition::ErrorCode::POSITION_UNAVAILABLE;
position_error.error_message =
base::android::ConvertJavaStringToUTF8(env, message);
OnLocationUpdate(position_error);
}