chromium/third_party/cardboard/src_overrides/sdk/sensors/android/device_accelerometer_sensor.cc

/*
 * Copyright 2019 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "third_party/cardboard/src/sdk/sensors/device_accelerometer_sensor.h"

#include <android/looper.h>
#include <android/sensor.h>
#include <stddef.h>

#include <memory>
#include <mutex>  // NOLINT

#include "third_party/cardboard/src/sdk/util/constants.h"
#include "third_party/cardboard/src/sdk/util/logging.h"

// Workaround to avoid the inclusion of "android_native_app_glue.h.
#ifndef LOOPER_ID_USER
#define LOOPER_ID_USER 3
#endif

namespace cardboard {

namespace {

// Creates an Android sensor event queue for the current thread.
static ASensorEventQueue* CreateSensorQueue(ASensorManager* sensor_manager) {
  ALooper* event_looper = ALooper_forThread();

  if (event_looper == nullptr) {
    event_looper = ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
    CARDBOARD_LOGI(
        "AccelerometerSensor: Created new event looper for accelerometer "
        "sensor capture thread.");
  }

  return ASensorManager_createEventQueue(sensor_manager, event_looper,
                                         LOOPER_ID_USER, nullptr, nullptr);
}

const ASensor* InitSensor(ASensorManager* sensor_manager) {
  return ASensorManager_getDefaultSensor(sensor_manager,
                                         ASENSOR_TYPE_ACCELEROMETER);
}

bool PollLooper(int timeout_ms, int* num_events) {
  void* source = nullptr;
  const int looper_id = ALooper_pollOnce(timeout_ms, NULL, num_events,
                                        reinterpret_cast<void**>(&source));
  if (looper_id != LOOPER_ID_USER) {
    return false;
  }
  if (*num_events <= 0) {
    return false;
  }
  return true;
}

class SensorEventQueueReader {
 public:
  SensorEventQueueReader(ASensorManager* manager, const ASensor* sensor)
      : manager_(manager),
        sensor_(sensor),
        queue_(CreateSensorQueue(manager_)) {}

  ~SensorEventQueueReader() {
    ASensorManager_destroyEventQueue(manager_, queue_);
  }

  bool Start() {
    ASensorEventQueue_enableSensor(queue_, sensor_);
    const int min_delay = ASensor_getMinDelay(sensor_);
    // Set sensor capture rate to the highest possible sampling rate.
    ASensorEventQueue_setEventRate(queue_, sensor_, min_delay);
    return true;
  }

  void Stop() { ASensorEventQueue_disableSensor(queue_, sensor_); }

  bool WaitForEvent(int timeout_ms, ASensorEvent* event) {
    int num_events;
    if (!PollLooper(timeout_ms, &num_events)) {
      return false;
    }
    return (ASensorEventQueue_getEvents(queue_, event, 1) > 0);
  }

  bool ReadEvent(ASensorEvent* event) {
    return (ASensorEventQueue_getEvents(queue_, event, 1) > 0);
  }

 private:
  ASensorManager* manager_;   // Owned by android library.
  const ASensor* sensor_;     // Owned by android library.
  ASensorEventQueue* queue_;  // Owned by this.
};

void ParseAccelerometerEvent(const ASensorEvent& event,
                             AccelerometerData* sample) {
  sample->sensor_timestamp_ns = event.timestamp;
  sample->system_timestamp = event.timestamp;
  // The event values in ASensorEvent (event, acceleration and
  // magnetic) are all in the same union type so they can be
  // accessed by event.
  sample->data = {event.vector.x, event.vector.y, event.vector.z};
}

}  // namespace

// This struct holds android specific sensor information.
struct DeviceAccelerometerSensor::SensorInfo {
  SensorInfo() : sensor_manager(nullptr), sensor(nullptr) {}

  ASensorManager* sensor_manager;
  const ASensor* sensor;
  std::unique_ptr<SensorEventQueueReader> reader;
};

DeviceAccelerometerSensor::DeviceAccelerometerSensor()
    : sensor_info_(new SensorInfo()) {
#if __ANDROID_MIN_SDK_VERSION__ >= 26
  sensor_info_->sensor_manager =
      ASensorManager_getInstanceForPackage(Constants::kCardboardSdkPackageName);
#else
  // TODO: b/314792983 - Remove deprecated NDK methods.
  sensor_info_->sensor_manager = ASensorManager_getInstance();
#endif
  sensor_info_->sensor = InitSensor(sensor_info_->sensor_manager);
  if (!sensor_info_->sensor) {
    return;
  }

  sensor_info_->reader =
      std::unique_ptr<SensorEventQueueReader>(new SensorEventQueueReader(
          sensor_info_->sensor_manager, sensor_info_->sensor));
}

DeviceAccelerometerSensor::~DeviceAccelerometerSensor() {}

void DeviceAccelerometerSensor::PollForSensorData(
    int timeout_ms, std::vector<AccelerometerData>* results) const {
  results->clear();
  ASensorEvent event;
  if (!sensor_info_->reader->WaitForEvent(timeout_ms, &event)) {
    return;
  }
  do {
    AccelerometerData sample;
    ParseAccelerometerEvent(event, &sample);
    results->push_back(sample);
  } while (sensor_info_->reader->ReadEvent(&event));
}

bool DeviceAccelerometerSensor::Start() {
  if (!sensor_info_->reader) {
    CARDBOARD_LOGE("Could not start accelerometer sensor");
    return false;
  }
  return sensor_info_->reader->Start();
}

void DeviceAccelerometerSensor::Stop() {
  if (!sensor_info_->reader) {
    return;
  }
  sensor_info_->reader->Stop();
}

}  // namespace cardboard