chromium/services/device/generic_sensor/platform_sensor_provider_android.cc

// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "services/device/generic_sensor/platform_sensor_provider_android.h"

#include <utility>
#include <vector>

#include "base/android/scoped_java_ref.h"
#include "base/memory/ref_counted.h"
#include "services/device/generic_sensor/absolute_orientation_euler_angles_fusion_algorithm_using_accelerometer_and_magnetometer.h"
#include "services/device/generic_sensor/gravity_fusion_algorithm_using_accelerometer.h"
#include "services/device/generic_sensor/linear_acceleration_fusion_algorithm_using_accelerometer.h"
#include "services/device/generic_sensor/orientation_euler_angles_fusion_algorithm_using_quaternion.h"
#include "services/device/generic_sensor/orientation_quaternion_fusion_algorithm_using_euler_angles.h"
#include "services/device/generic_sensor/platform_sensor_android.h"
#include "services/device/generic_sensor/platform_sensor_fusion.h"
#include "services/device/generic_sensor/relative_orientation_euler_angles_fusion_algorithm_using_accelerometer.h"

// Must come after all headers that specialize FromJniType() / ToJniType().
#include "services/device/generic_sensor/jni_headers/PlatformSensorProvider_jni.h"

using base::android::ScopedJavaLocalRef;
using jni_zero::AttachCurrentThread;

namespace device {

PlatformSensorProviderAndroid::PlatformSensorProviderAndroid() {
  JNIEnv* env = AttachCurrentThread();
  j_object_.Reset(Java_PlatformSensorProvider_create(env));
}

PlatformSensorProviderAndroid::~PlatformSensorProviderAndroid() = default;

base::WeakPtr<PlatformSensorProvider>
PlatformSensorProviderAndroid::AsWeakPtr() {
  return weak_factory_.GetWeakPtr();
}

void PlatformSensorProviderAndroid::SetSensorManagerToNullForTesting() {
  JNIEnv* env = AttachCurrentThread();
  Java_PlatformSensorProvider_setSensorManagerToNullForTesting(env, j_object_);
}

void PlatformSensorProviderAndroid::CreateSensorInternal(
    mojom::SensorType type,
    CreateSensorCallback callback) {
  JNIEnv* env = AttachCurrentThread();

  // Some of the sensors may not be available depending on the device and
  // Android version, so the fallback ensures selection of the best possible
  // option.
  switch (type) {
    case mojom::SensorType::GRAVITY:
      CreateGravitySensor(env, std::move(callback));
      break;
    case mojom::SensorType::LINEAR_ACCELERATION:
      CreateLinearAccelerationSensor(env, std::move(callback));
      break;
    case mojom::SensorType::ABSOLUTE_ORIENTATION_EULER_ANGLES:
      CreateAbsoluteOrientationEulerAnglesSensor(env, std::move(callback));
      break;
    case mojom::SensorType::ABSOLUTE_ORIENTATION_QUATERNION:
      CreateAbsoluteOrientationQuaternionSensor(env, std::move(callback));
      break;
    case mojom::SensorType::RELATIVE_ORIENTATION_EULER_ANGLES:
      CreateRelativeOrientationEulerAnglesSensor(env, std::move(callback));
      break;
    default: {
      std::move(callback).Run(PlatformSensorAndroid::Create(
          type, GetSensorReadingSharedBufferForType(type), AsWeakPtr(),
          j_object_));
      break;
    }
  }
}

// For GRAVITY we see if the platform supports it directly through
// TYPE_GRAVITY. If not we use a fusion algorithm to remove the
// contribution of linear acceleration from the raw ACCELEROMETER.
void PlatformSensorProviderAndroid::CreateGravitySensor(
    JNIEnv* env,
    CreateSensorCallback callback) {
  auto sensor = PlatformSensorAndroid::Create(
      mojom::SensorType::GRAVITY,
      GetSensorReadingSharedBufferForType(mojom::SensorType::GRAVITY),
      AsWeakPtr(), j_object_);

  if (sensor) {
    std::move(callback).Run(std::move(sensor));
  } else {
    auto sensor_fusion_algorithm =
        std::make_unique<GravityFusionAlgorithmUsingAccelerometer>();

    // If this PlatformSensorFusion object is successfully initialized,
    // |callback| will be run with a reference to this object.
    PlatformSensorFusion::Create(
        AsWeakPtr(), std::move(sensor_fusion_algorithm), std::move(callback));
  }
}

// For LINEAR_ACCELERATION we see if the platform supports it directly through
// TYPE_LINEAR_ACCELERATION. If not we use a fusion algorithm to remove the
// contribution of gravity from the raw ACCELEROMETER.
void PlatformSensorProviderAndroid::CreateLinearAccelerationSensor(
    JNIEnv* env,
    CreateSensorCallback callback) {
  auto sensor =
      PlatformSensorAndroid::Create(mojom::SensorType::LINEAR_ACCELERATION,
                                    GetSensorReadingSharedBufferForType(
                                        mojom::SensorType::LINEAR_ACCELERATION),
                                    AsWeakPtr(), j_object_);

  if (sensor) {
    std::move(callback).Run(std::move(sensor));
  } else {
    auto sensor_fusion_algorithm =
        std::make_unique<LinearAccelerationFusionAlgorithmUsingAccelerometer>();

    // If this PlatformSensorFusion object is successfully initialized,
    // |callback| will be run with a reference to this object.
    PlatformSensorFusion::Create(
        AsWeakPtr(), std::move(sensor_fusion_algorithm), std::move(callback));
  }
}

// For ABSOLUTE_ORIENTATION_EULER_ANGLES we use a 3-way fallback approach
// where up to 3 different sets of sensors are attempted if necessary. The
// sensors to be used are determined in the following order:
//   A: ABSOLUTE_ORIENTATION_QUATERNION (if it uses TYPE_ROTATION_VECTOR
//      directly)
//   B: TODO(juncai): Combination of ACCELEROMETER, MAGNETOMETER and
//      GYROSCOPE
//   C: Combination of ACCELEROMETER and MAGNETOMETER
void PlatformSensorProviderAndroid::CreateAbsoluteOrientationEulerAnglesSensor(
    JNIEnv* env,
    CreateSensorCallback callback) {
  if (static_cast<bool>(Java_PlatformSensorProvider_hasSensorType(
          env, j_object_,
          static_cast<jint>(
              mojom::SensorType::ABSOLUTE_ORIENTATION_QUATERNION)))) {
    auto sensor_fusion_algorithm =
        std::make_unique<OrientationEulerAnglesFusionAlgorithmUsingQuaternion>(
            true /* absolute */);

    // If this PlatformSensorFusion object is successfully initialized,
    // |callback| will be run with a reference to this object.
    PlatformSensorFusion::Create(
        AsWeakPtr(), std::move(sensor_fusion_algorithm), std::move(callback));
  } else {
    auto sensor_fusion_algorithm = std::make_unique<
        AbsoluteOrientationEulerAnglesFusionAlgorithmUsingAccelerometerAndMagnetometer>();

    // If this PlatformSensorFusion object is successfully initialized,
    // |callback| will be run with a reference to this object.
    PlatformSensorFusion::Create(
        AsWeakPtr(), std::move(sensor_fusion_algorithm), std::move(callback));
  }
}

// For ABSOLUTE_ORIENTATION_QUATERNION we use a 2-way fallback approach
// where up to 2 different sets of sensors are attempted if necessary. The
// sensors to be used are determined in the following order:
//   A: Use TYPE_ROTATION_VECTOR directly
//   B: ABSOLUTE_ORIENTATION_EULER_ANGLES
void PlatformSensorProviderAndroid::CreateAbsoluteOrientationQuaternionSensor(
    JNIEnv* env,
    CreateSensorCallback callback) {
  auto sensor = PlatformSensorAndroid::Create(
      mojom::SensorType::ABSOLUTE_ORIENTATION_QUATERNION,
      GetSensorReadingSharedBufferForType(
          mojom::SensorType::ABSOLUTE_ORIENTATION_QUATERNION),
      AsWeakPtr(), j_object_);

  if (sensor) {
    std::move(callback).Run(std::move(sensor));
  } else {
    auto sensor_fusion_algorithm =
        std::make_unique<OrientationQuaternionFusionAlgorithmUsingEulerAngles>(
            true /* absolute */);

    // If this PlatformSensorFusion object is successfully initialized,
    // |callback| will be run with a reference to this object.
    PlatformSensorFusion::Create(
        AsWeakPtr(), std::move(sensor_fusion_algorithm), std::move(callback));
  }
}

// For RELATIVE_ORIENTATION_EULER_ANGLES we use RELATIVE_ORIENTATION_QUATERNION
// (if it uses TYPE_GAME_ROTATION_VECTOR directly).
void PlatformSensorProviderAndroid::CreateRelativeOrientationEulerAnglesSensor(
    JNIEnv* env,
    CreateSensorCallback callback) {
  if (static_cast<bool>(Java_PlatformSensorProvider_hasSensorType(
          env, j_object_,
          static_cast<jint>(
              mojom::SensorType::RELATIVE_ORIENTATION_QUATERNION)))) {
    auto sensor_fusion_algorithm =
        std::make_unique<OrientationEulerAnglesFusionAlgorithmUsingQuaternion>(
            false /* absolute */);

    // If this PlatformSensorFusion object is successfully initialized,
    // |callback| will be run with a reference to this object.
    PlatformSensorFusion::Create(
        AsWeakPtr(), std::move(sensor_fusion_algorithm), std::move(callback));
  } else {
    std::move(callback).Run(nullptr);
  }
}

}  // namespace device