普通文本  |  1636行  |  61.18 KB

/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * 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 <algorithm>
#include <cinttypes>

#include "chre/platform/platform_sensor.h"

extern "C" {

#include "fixed_point.h"
#include "sns_smgr_api_v01.h"
#include "sns_smgr_internal_api_v02.h"
#include "sns_usmr.h"
#include "timetick.h"

}  // extern "C"

#include "chre_api/chre/sensor.h"
#include "chre/core/event_loop_manager.h"
#include "chre/core/sensor.h"
#include "chre/core/timer_pool.h"
#include "chre/platform/assert.h"
#include "chre/platform/fatal_error.h"
#include "chre/platform/log.h"
#include "chre/platform/shared/platform_sensor_util.h"
#include "chre/platform/slpi/uimg_util.h"
#include "chre/platform/slpi/smgr/platform_sensor_util.h"
#include "chre/platform/slpi/smgr/smgr_client.h"
#include "chre/platform/slpi/smgr/smr_helper.h"
#include "chre/platform/system_time.h"
#include "chre/util/macros.h"

#ifdef CHREX_SENSOR_SUPPORT
#include "chre/extensions/platform/slpi/smgr/platform_sensor_util.h"
#include "chrex_variant_smgr_sensor_id.h"
#endif  // CHREX_SENSOR_SUPPORT

// As SMGR doesn't support passive sensor request, it's now implemented on the
// client (CHRE) side using a combination of the SNS_SMGR_INTERNAL_API_V02 and a
// modified SNS_SMGR_API_V01.
//
// Here's a summary of its design:
// 1. A sensor status monitor is added in addSensorMonitor() to receive the
//    SNS_SMGR_SENSOR_STATUS_MONITOR_IND_V02 message the first time a sensor is
//    requested.
// 2. When a request is made in PlatformSensor::applyRequest(), it checkes
//    whether it's allowed at that point and makes a corresponding QMI request.
//    1) The request is allowed if
//       - it's an active or an off request, or
//       - it's a passive request and the merged mode (to be explained
//         shortly) is active or there exist other SMGR clients.
//    2) If the request is allowed, a QMI request to add the sensor request is
//       made. Otherwise, a QMI request to remove the sensor request is made to
//       handle the potential active-and-allowed to passive-and-disallowed
//       transition.
//    3) The merged mode of a sensor is the strongest mode of all sensor
//       requests of the same sensor ID, with active > passive > off.
// 3. When SNS_SMGR_SENSOR_STATUS_MONITOR_IND_V02 from SMGR is received, a new
//    timer is set for kStatusDelayIntervalNanos in the future for each
//    sensorId. Any future updates that occur before the timer fires are
//    ignored.
// 4. Once the timer fires, an asynchronous SNS_SMGR_CLIENT_REQUEST_INFO_REQ_V01
//    message is sent to query SMGR on the existence of other clients.
//    - If a transition from absence-to-presence of other clients is detected,
//      all pending passive requests are made.
//    - If a transition from presence-to-absence of other clients is deteted,
//      all passive requests are removed if the merged mode is passive.
//
// Note that currently the sensor status monitor indication only supports
// primary sensor status change. So for a secondary sensor that can be requested
// without an accompanying primary sensor (Light), this design doesn't work.
// In PlatformSensor::applyRequest(), a passive Light sensor request is
// overridden to be an active one.

namespace chre {
namespace {

//! The constant used to convert from SMGR to Android unit for magnetometer.
constexpr float kMicroTeslaPerGauss = 100.0f;

//! The maximum number of CHRE sensors that share the same SMGR sensor ID.
constexpr size_t kMaxNumSensorsPerSensorId = 3;

//! The value to override a default interval request.
constexpr uint64_t kDefaultInterval = Seconds(1).toRawNanoseconds();

//! The offset in nanoseconds each 32-bit tick rollover introduces in timestamp
constexpr uint64_t kTickRolloverOffset =
    ((1ULL << 32) * Seconds(1).toRawNanoseconds()) / TIMETICK_NOMINAL_FREQ_HZ;

//! The delay in nanoseconds between receiving a sensor status change
//! and updating the sensor status.
constexpr Nanoseconds kStatusDelayIntervalNanos = Milliseconds(20);

smr_client_hndl gPlatformSensorServiceSmrClientHandle;
smr_client_hndl gPlatformSensorInternalServiceSmrClientHandle;

//! A struct to store the number of SMGR clients of a sensor ID.
struct SensorMonitor {
  uint8_t sensorId;
  bool otherClientPresent;
};

//! A vector that tracks the SensorMonitor of each supported sensor ID.
DynamicVector<SensorMonitor> gSensorMonitors;

//! Forward declarations
bool makeAllPendingRequests(uint8_t sensorId);
bool removeAllPassiveRequests(uint8_t sensorId);

/**
 * Obtains the element index of gSensorMonitors that corresponds to the
 * specified sensor ID. If it's not present, gSensorMonitors.size() is returned.
 *
 * @return The index of the element that belongs to sensorId.
 */
size_t getSensorMonitorIndex(uint8_t sensorId) {
  size_t i;
  for (i = 0; i < gSensorMonitors.size(); i++) {
    if (gSensorMonitors[i].sensorId == sensorId) {
      break;
    }
  }
  return i;
}

/**
 * Converts a sensorId, dataType and calType as provided by SMGR to a
 * SensorType as used by platform-independent CHRE code. This is useful in
 * sensor discovery.
 *
 * @param sensorId The sensorID as provided by the SMGR request for sensor info.
 * @param dataType The dataType for the sesnor as provided by the SMGR request
 *                 for sensor info.
 * @param calType The calibration type (CAL_SEL) as defined in the SMGR API.
 * @return Returns the platform-independent sensor type or Unknown if no
 *         match is found.
 */
SensorType getSensorTypeFromSensorId(uint8_t sensorId, uint8_t dataType,
                                     uint8_t calType) {
  // Here be dragons. These constants below are defined in
  // sns_smgr_common_v01.h. Refer to the section labelled "Define sensor
  // identifier" for more details. This function relies on the ordering of
  // constants provided by their API. Do not change these values without care.
  // You have been warned!
  if (dataType == SNS_SMGR_DATA_TYPE_PRIMARY_V01) {
    if (sensorId >= SNS_SMGR_ID_ACCEL_V01
        && sensorId < SNS_SMGR_ID_GYRO_V01) {
      if (calType == SNS_SMGR_CAL_SEL_FULL_CAL_V01) {
        return SensorType::Accelerometer;
      } else if (calType == SNS_SMGR_CAL_SEL_FACTORY_CAL_V01) {
        return SensorType::UncalibratedAccelerometer;
      }
    } else if (sensorId >= SNS_SMGR_ID_GYRO_V01
        && sensorId < SNS_SMGR_ID_MAG_V01) {
      if (calType == SNS_SMGR_CAL_SEL_FULL_CAL_V01) {
        return SensorType::Gyroscope;
      } else if (calType == SNS_SMGR_CAL_SEL_FACTORY_CAL_V01) {
        return SensorType::UncalibratedGyroscope;
      }
    } else if (sensorId >= SNS_SMGR_ID_MAG_V01
        && sensorId < SNS_SMGR_ID_PRESSURE_V01) {
      if (calType == SNS_SMGR_CAL_SEL_FULL_CAL_V01) {
        return SensorType::GeomagneticField;
      } else if (calType == SNS_SMGR_CAL_SEL_FACTORY_CAL_V01) {
        return SensorType::UncalibratedGeomagneticField;
      }
    } else if (sensorId >= SNS_SMGR_ID_PRESSURE_V01
        && sensorId < SNS_SMGR_ID_PROX_LIGHT_V01) {
      return SensorType::Pressure;
    } else if (sensorId >= SNS_SMGR_ID_PROX_LIGHT_V01
        && sensorId < SNS_SMGR_ID_HUMIDITY_V01) {
      return SensorType::Proximity;
    } else if (sensorId == SNS_SMGR_ID_OEM_SENSOR_09_V01) {
      return SensorType::StationaryDetect;
    } else if (sensorId == SNS_SMGR_ID_OEM_SENSOR_10_V01) {
      return SensorType::InstantMotion;
#ifdef CHREX_SENSOR_SUPPORT
    } else if (sensorId == CHREX_VENDOR_TYPE0_SENSOR_ID) {
      return SensorType::VendorType0;
#endif  // CHREX_SENSOR_SUPPORT
    }
  } else if (dataType == SNS_SMGR_DATA_TYPE_SECONDARY_V01) {
    if (sensorId >= SNS_SMGR_ID_ACCEL_V01
        && sensorId < SNS_SMGR_ID_GYRO_V01) {
      return SensorType::AccelerometerTemperature;
    } else if (sensorId >= SNS_SMGR_ID_GYRO_V01
        && sensorId < SNS_SMGR_ID_MAG_V01) {
      return SensorType::GyroscopeTemperature;
    } else if ((sensorId >= SNS_SMGR_ID_PROX_LIGHT_V01
        && sensorId < SNS_SMGR_ID_HUMIDITY_V01)
        || (sensorId >= SNS_SMGR_ID_ULTRA_VIOLET_V01
            && sensorId < SNS_SMGR_ID_OBJECT_TEMP_V01)) {
      return SensorType::Light;
    }
  }

  return SensorType::Unknown;
}

/**
 * Converts a reportId as provided by SMGR to a SensorType.
 *
 * @param reportId The reportID as provided by the SMGR buffering index.
 * @return Returns the sensorType that corresponds to the reportId.
 */
SensorType getSensorTypeFromReportId(uint8_t reportId) {
  SensorType sensorType;
  if (reportId < static_cast<uint8_t>(SensorType::SENSOR_TYPE_COUNT)) {
    sensorType = static_cast<SensorType>(reportId);
  } else {
    sensorType = SensorType::Unknown;
  }
  return sensorType;
}

/**
 * Converts a PlatformSensor to a unique report ID through SensorType. This is
 * useful in making sensor request.
 *
 * @param sensorId The sensorID as provided by the SMGR request for sensor info.
 * @param dataType The dataType for the sesnor as provided by the SMGR request
 *                 for sensor info.
 * @param calType The calibration type (CAL_SEL) as defined in the SMGR API.
 * @return Returns a unique report ID that is based on SensorType.
 */
uint8_t getReportId(uint8_t sensorId, uint8_t dataType, uint8_t calType) {
  SensorType sensorType = getSensorTypeFromSensorId(
      sensorId, dataType, calType);

  CHRE_ASSERT_LOG(sensorType != SensorType::Unknown,
                  "sensorId %" PRIu8 ", dataType %" PRIu8 ", calType %" PRIu8,
                  sensorId, dataType, calType);
  return static_cast<uint8_t>(sensorType);
}

/**
 * Checks whether the corresponding sensor is a sencondary temperature sensor.
 *
 * @param reportId The reportID as provided by the SMGR buffering index.
 * @return true if the sensor is a secondary temperature sensor.
 */
bool isSecondaryTemperature(uint8_t reportId) {
  SensorType sensorType = getSensorTypeFromReportId(reportId);
  return (sensorType == SensorType::AccelerometerTemperature
          || sensorType == SensorType::GyroscopeTemperature);
}

/**
 * Verifies whether the buffering index's report ID matches the expected
 * indices length.
 *
 * @return true if it's a valid pair of indices length and report ID.
 */
bool isValidIndicesLength(
    const sns_smgr_buffering_ind_msg_v01& bufferingIndMsg) {
  return ((bufferingIndMsg.Indices_len == 1
           && !isSecondaryTemperature(bufferingIndMsg.ReportId))
          || (bufferingIndMsg.Indices_len == 2
              && isSecondaryTemperature(bufferingIndMsg.ReportId)));
}

/**
 * Allocates memory and specifies the memory size for an on-change sensor to
 * store its last data event.
 *
 * @param sensorType The sensorType of this sensor.
 * @param eventSize A non-null pointer to indicate the memory size allocated.
 * @return Pointer to the memory allocated.
 */
ChreSensorData *allocateLastEvent(SensorType sensorType, size_t *eventSize) {
  CHRE_ASSERT(eventSize);

  *eventSize = 0;
  ChreSensorData *event = nullptr;
  if (sensorTypeIsOnChange(sensorType)) {
    SensorSampleType sampleType = getSensorSampleTypeFromSensorType(sensorType);
    switch (sampleType) {
      case SensorSampleType::ThreeAxis:
        *eventSize = sizeof(chreSensorThreeAxisData);
        break;
      case SensorSampleType::Float:
        *eventSize = sizeof(chreSensorFloatData);
        break;
      case SensorSampleType::Byte:
        *eventSize = sizeof(chreSensorByteData);
        break;
      case SensorSampleType::Occurrence:
        *eventSize = sizeof(chreSensorOccurrenceData);
        break;
      default:
        CHRE_ASSERT_LOG(false, "Unhandled sample type");
        break;
    }

    event = static_cast<ChreSensorData *>(memoryAlloc(*eventSize));
    if (event == nullptr) {
      *eventSize = 0;
      FATAL_ERROR("Failed to allocate last event memory for SensorType %d",
                  static_cast<int>(sensorType));
    }
  }
  return event;
}

/**
 * Constructs and initializes a sensor, and adds it to the sensor list.
 *
 * @param sensorInfo The sensorInfo as provided by the SMGR.
 * @param calType The calibration type (CAL_SEL) as defined in the SMGR API.
 * @param sensor The sensor list.
 */
void addSensor(const sns_smgr_sensor_datatype_info_s_v01& sensorInfo,
               uint8_t calType, DynamicVector<Sensor> *sensors) {
  Sensor sensor;
  sensor.sensorId = sensorInfo.SensorID;
  sensor.dataType = sensorInfo.DataType;
  sensor.calType = calType;
  size_t bytesToCopy = std::min(sizeof(sensor.sensorName) - 1,
                                static_cast<size_t>(sensorInfo.SensorName_len));
  memcpy(sensor.sensorName, sensorInfo.SensorName, bytesToCopy);
  sensor.sensorName[bytesToCopy] = '\0';

  // Override one-shot sensor's minInterval to default
  SensorType sensorType = getSensorTypeFromSensorId(
      sensorInfo.SensorID, sensorInfo.DataType, calType);
  sensor.minInterval = sensorTypeIsOneShot(sensorType) ?
      CHRE_SENSOR_INTERVAL_DEFAULT : static_cast<uint64_t>(
          Seconds(1).toRawNanoseconds() / sensorInfo.MaxSampleRate);

  // Allocates memory for on-change sensor's last event.
  sensor.lastEvent = allocateLastEvent(sensorType, &sensor.lastEventSize);

  sensor.isSensorOff = true;
  sensor.samplingStatus.enabled = false;
  sensor.samplingStatus.interval = CHRE_SENSOR_INTERVAL_DEFAULT;
  sensor.samplingStatus.latency = CHRE_SENSOR_LATENCY_DEFAULT;

  if (!sensors->push_back(std::move(sensor))) {
    FATAL_ERROR("Failed to allocate new sensor: out of memory");
  }
}

/**
 * Converts SMGR ticks to nanoseconds as a uint64_t.
 *
 * @param ticks The number of ticks.
 * @return The number of nanoseconds represented by the ticks value.
 */
uint64_t getNanosecondsFromSmgrTicks(uint32_t ticks) {
  return (ticks * Seconds(1).toRawNanoseconds()) / TIMETICK_NOMINAL_FREQ_HZ;
}

void populateSensorDataHeader(
    SensorType sensorType, chreSensorDataHeader *header,
    const sns_smgr_buffering_sample_index_s_v01& sensorIndex) {
  // Compensate for header timestamp's 32-bit rollovers
  uint64_t slpiTime = SystemTime::getMonotonicTime().toRawNanoseconds();
  uint64_t baseTime = getNanosecondsFromSmgrTicks(
      sensorIndex.FirstSampleTimestamp);
  while (slpiTime > baseTime + kTickRolloverOffset / 2) {
    baseTime += kTickRolloverOffset;
  }
  header->reserved = 0;
  header->baseTimestamp = baseTime;
  header->sensorHandle = getSensorHandleFromSensorType(sensorType);
  header->readingCount = sensorIndex.SampleCount;
  header->accuracy = CHRE_SENSOR_ACCURACY_UNKNOWN;
}

void populateThreeAxisEvent(
    const sns_smgr_buffering_ind_msg_v01& bufferingIndMsg,
    SensorType sensorType, chreSensorThreeAxisData *data,
    const sns_smgr_buffering_sample_index_s_v01& sensorIndex) {
  populateSensorDataHeader(sensorType, &data->header, sensorIndex);

  for (size_t i = 0; i < sensorIndex.SampleCount; i++) {
    const sns_smgr_buffering_sample_s_v01& sensorData =
        bufferingIndMsg.Samples[i + sensorIndex.FirstSampleIdx];

    // TimeStampOffset has max value of < 2 sec so it will not overflow here.
    data->readings[i].timestampDelta =
        getNanosecondsFromSmgrTicks(sensorData.TimeStampOffset);

    // Convert from SMGR's NED coordinate to Android coordinate.
    data->readings[i].x = FX_FIXTOFLT_Q16_SP(sensorData.Data[1]);
    data->readings[i].y = FX_FIXTOFLT_Q16_SP(sensorData.Data[0]);
    data->readings[i].z = -FX_FIXTOFLT_Q16_SP(sensorData.Data[2]);

    // Convert from Gauss to micro Tesla
    if (sensorType == SensorType::GeomagneticField
        || sensorType == SensorType::UncalibratedGeomagneticField) {
      data->readings[i].x *= kMicroTeslaPerGauss;
      data->readings[i].y *= kMicroTeslaPerGauss;
      data->readings[i].z *= kMicroTeslaPerGauss;
    }
  }
}

void populateFloatEvent(
    const sns_smgr_buffering_ind_msg_v01& bufferingIndMsg,
    SensorType sensorType, chreSensorFloatData *data,
    const sns_smgr_buffering_sample_index_s_v01& sensorIndex) {
  populateSensorDataHeader(sensorType, &data->header, sensorIndex);

  for (size_t i = 0; i < sensorIndex.SampleCount; i++) {
    const sns_smgr_buffering_sample_s_v01& sensorData =
        bufferingIndMsg.Samples[i + sensorIndex.FirstSampleIdx];

    // TimeStampOffset has max value of < 2 sec so it will not overflow.
    data->readings[i].timestampDelta =
        getNanosecondsFromSmgrTicks(sensorData.TimeStampOffset);
    data->readings[i].value = FX_FIXTOFLT_Q16_SP(sensorData.Data[0]);
  }
}

void populateByteEvent(
    const sns_smgr_buffering_ind_msg_v01& bufferingIndMsg,
    SensorType sensorType, chreSensorByteData *data,
    const sns_smgr_buffering_sample_index_s_v01& sensorIndex) {
  populateSensorDataHeader(sensorType, &data->header, sensorIndex);

  for (size_t i = 0; i < sensorIndex.SampleCount; i++) {
    const sns_smgr_buffering_sample_s_v01& sensorData =
        bufferingIndMsg.Samples[i + sensorIndex.FirstSampleIdx];

    // TimeStampOffset has max value of < 2 sec so it will not overflow.
    data->readings[i].timestampDelta =
        getNanosecondsFromSmgrTicks(sensorData.TimeStampOffset);
    // Zero out fields invalid and padding0.
    data->readings[i].value = 0;
    // SMGR reports 1 in Q16 for near, and 0 for far.
    data->readings[i].isNear = sensorData.Data[0] ? 1 : 0;
  }
}

void populateOccurrenceEvent(
    const sns_smgr_buffering_ind_msg_v01& bufferingIndMsg,
    SensorType sensorType, chreSensorOccurrenceData *data,
    const sns_smgr_buffering_sample_index_s_v01& sensorIndex) {
  populateSensorDataHeader(sensorType, &data->header, sensorIndex);

  for (size_t i = 0; i < sensorIndex.SampleCount; i++) {
    const sns_smgr_buffering_sample_s_v01& sensorData =
        bufferingIndMsg.Samples[i + sensorIndex.FirstSampleIdx];

    // TimeStampOffset has max value of < 2 sec so it will not overflow.
    data->readings[i].timestampDelta =
        getNanosecondsFromSmgrTicks(sensorData.TimeStampOffset);
  }
}

/**
 * Allocate event memory according to SensorType and populate event readings.
 */
void *allocateAndPopulateEvent(
    const sns_smgr_buffering_ind_msg_v01& bufferingIndMsg,
    SensorType sensorType,
    const sns_smgr_buffering_sample_index_s_v01& sensorIndex) {
  SensorSampleType sampleType = getSensorSampleTypeFromSensorType(sensorType);
  size_t memorySize = sizeof(chreSensorDataHeader);
  switch (sampleType) {
    case SensorSampleType::ThreeAxis: {
      memorySize += sensorIndex.SampleCount *
          sizeof(chreSensorThreeAxisData::chreSensorThreeAxisSampleData);
      auto *event =
          static_cast<chreSensorThreeAxisData *>(memoryAlloc(memorySize));
      if (event != nullptr) {
        populateThreeAxisEvent(bufferingIndMsg, sensorType, event, sensorIndex);
      }
      return event;
    }

    case SensorSampleType::Float: {
      memorySize += sensorIndex.SampleCount *
          sizeof(chreSensorFloatData::chreSensorFloatSampleData);
      auto *event =
          static_cast<chreSensorFloatData *>(memoryAlloc(memorySize));
      if (event != nullptr) {
        populateFloatEvent(bufferingIndMsg, sensorType, event, sensorIndex);
      }
      return event;
    }

    case SensorSampleType::Byte: {
      memorySize += sensorIndex.SampleCount *
          sizeof(chreSensorByteData::chreSensorByteSampleData);
      auto *event =
          static_cast<chreSensorByteData *>(memoryAlloc(memorySize));
      if (event != nullptr) {
        populateByteEvent(bufferingIndMsg, sensorType, event, sensorIndex);
      }
      return event;
    }

    case SensorSampleType::Occurrence: {
      memorySize += sensorIndex.SampleCount *
          sizeof(chreSensorOccurrenceData::chreSensorOccurrenceSampleData);
      auto *event =
          static_cast<chreSensorOccurrenceData *>(memoryAlloc(memorySize));
      if (event != nullptr) {
        populateOccurrenceEvent(
            bufferingIndMsg, sensorType, event, sensorIndex);
      }
      return event;
    }

#ifdef CHREX_SENSOR_SUPPORT
    case SensorSampleType::Vendor0:
      return allocateAndPopulateVendor0Event(
          bufferingIndMsg, sensorType, sensorIndex,
          populateSensorDataHeader, getNanosecondsFromSmgrTicks);
#endif  // CHREX_SENSOR_SUPPORT

    default:
      LOGW("Unhandled sensor data %" PRIu8, static_cast<uint8_t>(sensorType));
      return nullptr;
  }
}

void smgrSensorDataEventFree(uint16_t eventType, void *eventData) {
  // Events are allocated using the simple memoryAlloc/memoryFree platform
  // functions.
  // TODO: Consider using a MemoryPool.
  memoryFree(eventData);

  // Remove all requests if it's a one-shot sensor and only after data has been
  // delivered to all clients.
  SensorType sensorType = getSensorTypeForSampleEventType(eventType);
  if (sensorTypeIsOneShot(sensorType)) {
    EventLoopManagerSingleton::get()->getSensorRequestManager()
        .removeAllRequests(sensorType);
  }
}

/**
 * Handles sensor data provided by the SMGR framework.
 *
 * @param bufferingIndMsg Decoded buffering indication message
 */
void handleSensorDataIndication(
    const sns_smgr_buffering_ind_msg_v01& bufferingIndMsg) {
  // We only requested one sensor per request except for a secondary
  // temperature sensor.
  bool validReport = isValidIndicesLength(bufferingIndMsg);
  CHRE_ASSERT_LOG(validReport,
                  "Got buffering indication from %" PRIu32
                  " sensors with report ID %" PRIu8,
                  bufferingIndMsg.Indices_len,
                  bufferingIndMsg.ReportId);
  if (validReport) {
    // Identify the index for the desired sensor. It is always 0 except
    // possibly for a secondary temperature sensor.
    uint32_t index = 0;
    if (isSecondaryTemperature(bufferingIndMsg.ReportId)) {
      index = (bufferingIndMsg.Indices[0].DataType
               == SNS_SMGR_DATA_TYPE_SECONDARY_V01) ? 0 : 1;
    }
    const sns_smgr_buffering_sample_index_s_v01& sensorIndex =
        bufferingIndMsg.Indices[index];

    // Use ReportID to identify sensors as
    // bufferingIndMsg.Samples[i].Flags are not populated.
    SensorType sensorType = getSensorTypeFromReportId(
        bufferingIndMsg.ReportId);
    if (sensorType == SensorType::Unknown) {
      LOGW("Received sensor sample for unknown sensor %" PRIu8 " %" PRIu8,
           sensorIndex.SensorId, sensorIndex.DataType);
    } else if (sensorIndex.SampleCount == 0) {
      LOGW("Received sensorType %d event with 0 sample",
           static_cast<int>(sensorType));
    } else {
      void *eventData = allocateAndPopulateEvent(
          bufferingIndMsg, sensorType, sensorIndex);
      auto *header = static_cast< chreSensorDataHeader *>(eventData);
      if (eventData == nullptr) {
        LOGW("Dropping event due to allocation failure");
      } else if (header->readingCount == 0) {
        LOGW("Dropping zero readingCount event");
        memoryFree(eventData);
      } else {
        // Schedule a deferred callback to update on-change sensor's last
        // event in the main thread.
        if (sensorTypeIsOnChange(sensorType)) {
          updateLastEvent(sensorType, eventData);
        }

        EventLoopManagerSingleton::get()->getEventLoop().postEventOrFree(
            getSampleEventTypeForSensorType(sensorType), eventData,
            smgrSensorDataEventFree);
      }
    }
  }  // if (validReport)
}

/**
 * This callback is invoked by the SMR framework when an asynchronous message is
 * delivered. Unhandled messages are logged.
 *
 * @param handle Handle for the SMR client this indication was received on.
 * @param messageId The message ID number.
 * @param buffer Buffer containing decoded (C struct) message data.
 * @param bufferLength Size of the decoded buffer in bytes.
 * @param callbackData Data that is provided as a context to this callback. This
 *                     is not used in this context.
 *
 * @see smr_client_ind_cb
 */
void platformSensorServiceIndicationCallback(
    smr_client_hndl handle, unsigned int messageId, void *decodedInd,
    unsigned int decodedIndLen, void *callbackData) {
  switch (messageId) {
    case SNS_SMGR_BUFFERING_IND_V01: {
      CHRE_ASSERT(decodedIndLen >= sizeof(sns_smgr_buffering_ind_msg_v01));
      auto *bufferingInd =
          static_cast<sns_smgr_buffering_ind_msg_v01 *>(decodedInd);
      handleSensorDataIndication(*bufferingInd);
      break;
    }
    default:
      LOGW("Received unhandled sensor service message: 0x%x", messageId);
      break;
  };
}

/**
 * Populates the supplied SensorTypes array with SensorTypes of the specified
 * sensor ID.
 *
 * @param sensorId The sensor ID as provided by the SMGR.
 * @param sensorTypes A non-null pointer to a SensorType array of size at least
 *        kMaxNumSensorsPerSensorId.
 */
size_t populateSensorTypeArrayFromSensorId(uint8_t sensorId,
                                           SensorType *sensorTypes) {
  static_assert(kMaxNumSensorsPerSensorId >= 3,
                "This function assumes kMaxNumSensorsPerSensorId >= 3");
  CHRE_ASSERT(sensorTypes);

  size_t numSensorTypes = 0;
  if (sensorTypes != nullptr) {
    if (sensorId >= SNS_SMGR_ID_ACCEL_V01
          && sensorId < SNS_SMGR_ID_GYRO_V01) {
      sensorTypes[0] = SensorType::Accelerometer;
      sensorTypes[1] = SensorType::UncalibratedAccelerometer;
      sensorTypes[2] = SensorType::AccelerometerTemperature;
      numSensorTypes = 3;
    } else if (sensorId >= SNS_SMGR_ID_GYRO_V01
          && sensorId < SNS_SMGR_ID_MAG_V01) {
      sensorTypes[0] = SensorType::Gyroscope;
      sensorTypes[1] = SensorType::UncalibratedGyroscope;
      sensorTypes[2] = SensorType::GyroscopeTemperature;
      numSensorTypes = 3;
    } else if (sensorId >= SNS_SMGR_ID_MAG_V01
          && sensorId < SNS_SMGR_ID_PRESSURE_V01) {
      sensorTypes[0] = SensorType::GeomagneticField;
      sensorTypes[1] = SensorType::UncalibratedGeomagneticField;
      numSensorTypes = 2;
    } else {
      SensorType sensorType = getSensorTypeFromSensorId(sensorId,
          SNS_SMGR_DATA_TYPE_PRIMARY_V01, SNS_SMGR_CAL_SEL_FULL_CAL_V01);
      if (sensorType != SensorType::Unknown) {
        sensorTypes[0] = sensorType;
        numSensorTypes = 1;
      }
    }
  }
  return numSensorTypes;
}

/**
 * Obtains the merged SensorMode of the specified sensor ID, with sensorType's
 * sensor request replaced by the supplied request.
 *
 * @param sensorId The sensor ID as provided by the SMGR.
 * @param sensorType The SensorType whose sensor request is to be replaced by
 *        the supplied request.
 * @param request The sensor request to replace the existing one.
 * @return The merged SensorMode.
 */
SensorMode getMergedMode(uint8_t sensorId, SensorType sensorType,
                         const SensorRequest& request) {
  // Identify sensor requests to merge
  SensorType sensorTypes[kMaxNumSensorsPerSensorId];
  size_t numSensorTypes = populateSensorTypeArrayFromSensorId(
      sensorId, sensorTypes);

  // merge requests
  SensorRequest mergedRequest;
  for (size_t i = 0; i < numSensorTypes; i++) {
    const Sensor *sensor = EventLoopManagerSingleton::get()
      ->getSensorRequestManager().getSensor(sensorTypes[i]);
    if (sensor != nullptr) {
      mergedRequest.mergeWith(
          (sensorTypes[i] == sensorType) ? request : sensor->getRequest());
    }
  }
  return mergedRequest.getMode();
}

/**
 * Makes or removes passive sensor requests when the presence of other SMGR
 * clients changes.
 *
 * @param sensorID The sensor ID being monitored.
 * @param otherClientPresent The presence of other SMGR clients.
 */
void onOtherClientPresenceChange(uint8_t sensorId, bool otherClientPresent) {
  bool makeAllRequests = otherClientPresent;

  SensorRequest dummyRequest;
  SensorMode mode = getMergedMode(sensorId, SensorType::Unknown, dummyRequest);
  bool removeAllRequests = (sensorModeIsPassive(mode) && !otherClientPresent);

  bool requestMade = false;
  if (makeAllRequests) {
    requestMade = makeAllPendingRequests(sensorId);
  } else if (removeAllRequests) {
    requestMade = removeAllPassiveRequests(sensorId);
  }

  if (requestMade) {
    LOGD("%s: id %" PRIu8 ", otherClientPresent %d, mode %d",
         makeAllRequests ? "+" : "-", sensorId, otherClientPresent,
         static_cast<size_t>(mode));
  }
}

/**
 * Retrieves first valid sensor that has the given sensor ID. Can be
 * invoked from any thread.
 *
 * @param sensorID The sensor handle that should be used to search
 *     the current list of sensors.
 * @return The first non-null Sensor that matches the given sensor handle or
 *     nullptr if no match is found.
 */
Sensor *getFirstValidSensor(uint8_t sensorId) {
  SensorType sensorTypes[kMaxNumSensorsPerSensorId];
  size_t numSensorTypes = populateSensorTypeArrayFromSensorId(
      sensorId, sensorTypes);

  Sensor *sensor = nullptr;
  for (size_t i = 0; i < numSensorTypes; i++) {
    sensor = EventLoopManagerSingleton::get()
        ->getSensorRequestManager().getSensor(sensorTypes[i]);
    if (sensor != nullptr) {
      break;
    }
  }
  return sensor;
}

/**
 * Processes the latest client request info response for the given sensor ID.
 * Must be invoked from the CHRE thread.
 *
 * @param resp The SMGR client request info response.
 * @param sensorId The sensor ID the response is for.
 * @param transpErr The error related to the request.
 */
void onClientRequestInfoResponse(
    const sns_smgr_client_request_info_resp_msg_v01& resp,
    uint8_t sensorId,
    smr_err transpErr) {
  size_t index = getSensorMonitorIndex(sensorId);
  if (transpErr != SMR_NO_ERR) {
    LOGE("Error receiving client request info: %" PRIu8, transpErr);
  } else if (resp.resp.sns_result_t != SNS_RESULT_SUCCESS_V01) {
    LOGE("Client request info failed with error: %" PRIu8 ", id %" PRIu8,
         resp.resp.sns_err_t, sensorId);
  } else if (index == gSensorMonitors.size()) {
    LOGE("Sensor status monitor update of invalid sensor ID %" PRIu8, sensorId);
  } else {
    bool otherClientPresent = resp.other_client_present;
    if (gSensorMonitors[index].otherClientPresent != otherClientPresent) {
      onOtherClientPresenceChange(sensorId, otherClientPresent);
      gSensorMonitors[index].otherClientPresent = otherClientPresent;
    }
  }
}

/**
 * Makes an asynchronous request to SMGR to receive the latest client
 * request info.
 *
 * @param sensorId The handle to the sensor whose status has changed.
 */
void onStatusChange(uint8_t sensorId) {
  // Sensor already verified to be valid before onStatusChange is called.
  Sensor *sensor = getFirstValidSensor(sensorId);
  // Invalidate timer first so a status update isn't potentially
  // missed.
  sensor->timerHandle = CHRE_TIMER_INVALID;

  size_t index = getSensorMonitorIndex(sensorId);
  if (index == gSensorMonitors.size()) {
    LOGE("Sensor status monitor update of invalid sensor ID %" PRIu8, sensorId);
  } else {
    // Use the asynchronous sensor status monitor indication message as a cue
    // to query and obtain the latest client request info. Since the status
    // changes are processed on a delay, the current client status is out of
    // date so query the latest status asynchronously to avoid holding up the
    // CHRE thread.
    auto infoRequest =
        MakeUniqueZeroFill<sns_smgr_client_request_info_req_msg_v01>();
    auto infoResponse = MakeUnique<sns_smgr_client_request_info_resp_msg_v01>();

    if (infoRequest.isNull() || infoResponse.isNull()) {
      LOG_OOM();
    } else {
      // Enables passing the sensor ID through the event data pointer to avoid
      // allocating memory
      union NestedSensorId {
        void *eventData;
        uint8_t sensorId;
      };
      NestedSensorId nestedId = {};
      nestedId.sensorId = sensorId;

      SmrReqCallback<sns_smgr_client_request_info_resp_msg_v01> callback =
          [](UniquePtr<sns_smgr_client_request_info_resp_msg_v01> resp,
             void *data,
             smr_err transpErr) {
        NestedSensorId nestedIdCb;
        nestedIdCb.eventData = data;
        onClientRequestInfoResponse(*resp.get(),
                                    nestedIdCb.sensorId, transpErr);
      };

      infoRequest->sensor_id = sensorId;
      smr_err smrStatus = getSmrHelper()->sendReqAsync(
          gPlatformSensorServiceSmrClientHandle,
          SNS_SMGR_CLIENT_REQUEST_INFO_REQ_V01,
          &infoRequest, &infoResponse, callback, nestedId.eventData);
      if (smrStatus != SMR_NO_ERR) {
        LOGE("Error requesting client request info: %d", smrStatus);
      }
    }
  }
}

/**
 * Posts a CHRE_EVENT_SENSOR_SAMPLING_CHANGE event to the specified Nanoapp.
 *
 * @param instaceId The instance ID of the nanoapp with an open request
 * @param eventRef A reference of the sampling status event to be posted.
 */
void postSamplingStatusEvent(uint32_t instanceId, uint32_t sensorHandle,
                             const struct chreSensorSamplingStatus& status) {
  // TODO: add a generic reference counted pointer class and use it for Event
  // to share across interested nanoapps.
  auto *event = memoryAlloc<struct chreSensorSamplingStatusEvent>();
  if (event == nullptr) {
    LOGE("Failed to allocate memory for sampling status change event");
  } else {
    event->sensorHandle = sensorHandle;
    memcpy(&event->status, &status, sizeof(event->status));

    EventLoopManagerSingleton::get()->getEventLoop().postEventOrFree(
        CHRE_EVENT_SENSOR_SAMPLING_CHANGE, event, freeEventDataCallback,
        kSystemInstanceId, instanceId);
  }
}

/**
 * Updates the sampling status after the sensor request is accepted by SMGR.
 */
void updateSamplingStatus(Sensor *sensor, const SensorRequest& request) {
  // With SMGR's implementation, sampling interval will be filtered to be the
  // same as requested. Latency can be shorter if there were other SMGR clients
  // with proc_type also set to SNS_PROC_SSC_V01.
  // If the request is passive, 'enabled' may change over time and needs to be
  // updated.
  if (sensor != nullptr) {
    bool postUpdate = false;
    struct chreSensorSamplingStatus& status = sensor->samplingStatus;
    bool enabled = (request.getMode() != SensorMode::Off);
    if (status.enabled != enabled) {
      postUpdate = true;
      status.enabled = enabled;
    }
    if (!sensorTypeIsOneShot(sensor->getSensorType())) {
      if (status.interval != request.getInterval().toRawNanoseconds()) {
        postUpdate = true;
        status.interval = request.getInterval().toRawNanoseconds();
      }
      if (status.latency != request.getLatency().toRawNanoseconds()) {
        postUpdate = true;
        status.latency = request.getLatency().toRawNanoseconds();
      }
    }

    if (postUpdate) {
      uint32_t sensorHandle = getSensorHandleFromSensorType(
          sensor->getSensorType());

      // Only post to Nanoapps with an open request.
      auto& requests = EventLoopManagerSingleton::get()->
          getSensorRequestManager().getRequests(sensor->getSensorType());
      for (const auto& req : requests) {
        postSamplingStatusEvent(req.getInstanceId(), sensorHandle, status);
      }
    }
  }
}

/**
 * Handles sensor status provided by the SMGR framework.
 *
 * @param smgrMonitorIndMsg Indication message received from SMGR
 */
void handleSensorStatusMonitorIndication(
    const sns_smgr_sensor_status_monitor_ind_msg_v02& smgrMonitorIndMsg) {
  uint8_t sensorId = smgrMonitorIndMsg.sensor_id;

  // Only use one Sensor to avoid multiple timers per sensorId.
  Sensor *sensor = getFirstValidSensor(sensorId);
  if (sensor == nullptr) {
    LOGE("Sensor ID: %" PRIu8 " in status update doesn't correspond to "
         "valid sensor.", sensorId);
  // SMGR should send all callbacks back on the same thread which 
  // means the following code won't result in any timers overriding one
  // another.
  } else if (sensor->timerHandle.load() == CHRE_TIMER_INVALID) {
    // Enables passing the sensor ID through the event data pointer to avoid
    // allocating memory
    union NestedSensorId {
      void *eventData;
      uint8_t sensorId;
    };
    NestedSensorId nestedId = {};
    nestedId.sensorId = sensorId;

    auto callback = [](uint16_t /* type */, void *data) {
      NestedSensorId nestedIdCb;
      nestedIdCb.eventData = data;
      onStatusChange(nestedIdCb.sensorId);
    };

    // Schedule a delayed callback to handle sensor status change on the main
    // thread.
    TimerHandle timer = EventLoopManagerSingleton::get()->setDelayedCallback(
        SystemCallbackType::SensorStatusUpdate,
        nestedId.eventData,
        callback,
        kStatusDelayIntervalNanos);
    sensor->timerHandle = timer;
  }
}

/**
 * This callback is invoked by the SMR framework when an asynchronous message is
 * delivered. Unhandled messages are logged.
 *
 * @param handle Handle for the SMR client this indication was received on.
 * @param messageId The message ID number.
 * @param decodedInd Buffer containing decoded (C struct) message data.
 * @param decodedIndLen Size of the decoded buffer in bytes.
 * @param callbackData Data that is provided as a context to this callback. This
 *                     is not used in this context.
 *
 * @see smr_client_ind_cb
 */
void platformSensorInternalServiceIndicationCallback(
    smr_client_hndl handle, unsigned int messageId, void *decodedInd,
    unsigned int decodedIndLen, void *callbackData) {
  switch (messageId) {
    case SNS_SMGR_SENSOR_STATUS_MONITOR_IND_V02: {
      CHRE_ASSERT(decodedIndLen >=
                  sizeof(sns_smgr_sensor_status_monitor_ind_msg_v02));
      auto *monitorInd =
          static_cast<sns_smgr_sensor_status_monitor_ind_msg_v02 *>(decodedInd);
      handleSensorStatusMonitorIndication(*monitorInd);
      break;
    }
    default:
      LOGW("Received unhandled sensor internal service message: 0x%x",
           messageId);
      break;
  };
}

/**
 * Adds or removes an SMGR sensor monitor for the specified sensor ID.
 *
 * @param sensorId The sensor ID to add/remove sensor status monitor for.
 * @param enable true to add and false to remove the status monitor.
 */
void setSensorMonitorRequest(uint8_t sensorId, bool enable) {
  auto monitorRequest =
      MakeUniqueZeroFill<sns_smgr_sensor_status_monitor_req_msg_v02>();
  auto monitorResponse =
      MakeUnique<sns_smgr_sensor_status_monitor_resp_msg_v02>();

  if (monitorRequest.isNull() || monitorResponse.isNull()) {
    LOGE("Failed to allocate monitor request/response");
  } else {
    monitorRequest->sensor_id = sensorId;
    monitorRequest->registering = enable;

    smr_err status = getSmrHelper()->sendReqSync(
        gPlatformSensorInternalServiceSmrClientHandle,
        SNS_SMGR_SENSOR_STATUS_MONITOR_REQ_V02,
        &monitorRequest, &monitorResponse);
    if (status != SMR_NO_ERR) {
      LOGE("Error setting sensor status monitor: %d", status);
    } else if (monitorResponse->resp.sns_result_t != SNS_RESULT_SUCCESS_V01) {
      LOGE("Sensor status monitor request failed with error: %" PRIu8
           " sensor ID %" PRIu8 " enable %d",
           monitorResponse->resp.sns_err_t, sensorId, enable);
    }
  }
}

/**
 * Adds and initializes a sensor monitor for the specified sensor ID if it
 * doesn't exist yet.
 *
 * @param sensorId The sensor ID to request monitor for.
 */
void addSensorMonitor(uint8_t sensorId) {
  size_t index = getSensorMonitorIndex(sensorId);
  if (index == gSensorMonitors.size()) {
    LOGD("Adding sensor status monitor for sensor ID %" PRIu8, sensorId);

    // Initialize sensor monitor status before making the request.
    SensorMonitor monitor;
    monitor.sensorId = sensorId;
    monitor.otherClientPresent = false;
    gSensorMonitors.push_back(monitor);

    // Make a request to add the status monitor
    setSensorMonitorRequest(sensorId, true);
  }
}

/**
 * Requests the sensors for a given sensor ID and appends them to the provided
 * list of sensors. If an error occurs, false is returned.
 *
 * @param sensorId The sensor ID to request sensor info for.
 * @param sensors The list of sensors to append newly found sensors to.
 * @return Returns false if an error occurs.
 */
bool getSensorsForSensorId(uint8_t sensorId,
                           DynamicVector<Sensor> *sensors) {
  bool success = false;
  auto sensorInfoRequest =
      MakeUniqueZeroFill<sns_smgr_single_sensor_info_req_msg_v01>();
  auto sensorInfoResponse =
      MakeUnique<sns_smgr_single_sensor_info_resp_msg_v01>();

  if (sensorInfoRequest.isNull() || sensorInfoResponse.isNull()) {
    LOGE("Failed to allocate sensor info msg");
  } else {
    sensorInfoRequest->SensorID = sensorId;

    smr_err status = getSmrHelper()->sendReqSync(
        gPlatformSensorServiceSmrClientHandle,
        SNS_SMGR_SINGLE_SENSOR_INFO_REQ_V01,
        &sensorInfoRequest, &sensorInfoResponse);

    if (status != SMR_NO_ERR) {
      LOGE("Error requesting single sensor info: %d", status);
    } else if (sensorInfoResponse->Resp.sns_result_t !=
                   SNS_RESULT_SUCCESS_V01) {
      LOGE("Single sensor info request failed with error: %d",
           sensorInfoResponse->Resp.sns_err_t);
    } else {
      const sns_smgr_sensor_info_s_v01& sensorInfoList =
          sensorInfoResponse->SensorInfo;
      for (uint32_t i = 0; i < sensorInfoList.data_type_info_len; i++) {
        const sns_smgr_sensor_datatype_info_s_v01& sensorInfo =
            sensorInfoList.data_type_info[i];
        LOGD("SensorID %" PRIu8 ", DataType %" PRIu8 ", MaxRate %" PRIu16
             "Hz, SensorName %s",
             sensorInfo.SensorID, sensorInfo.DataType,
             sensorInfo.MaxSampleRate, sensorInfo.SensorName);

        SensorType sensorType = getSensorTypeFromSensorId(
            sensorInfo.SensorID, sensorInfo.DataType,
            SNS_SMGR_CAL_SEL_FULL_CAL_V01);
        if (sensorType != SensorType::Unknown) {
          addSensor(sensorInfo, SNS_SMGR_CAL_SEL_FULL_CAL_V01, sensors);

          // Add an uncalibrated version if defined.
          SensorType uncalibratedType = getSensorTypeFromSensorId(
              sensorInfo.SensorID, sensorInfo.DataType,
              SNS_SMGR_CAL_SEL_FACTORY_CAL_V01);
          if (sensorType != uncalibratedType) {
            addSensor(sensorInfo, SNS_SMGR_CAL_SEL_FACTORY_CAL_V01, sensors);
          }
        }
      }
      success = true;
    }
  }

  return success;
}

/**
 * Converts a SensorMode into an SMGR request action. When the net request for
 * a sensor is considered to be active an add operation is required for the
 * SMGR request. When the sensor becomes inactive the request is deleted.
 *
 * @param mode The sensor mode.
 * @return Returns the SMGR request action given the sensor mode.
 */
uint8_t getSmgrRequestActionForMode(SensorMode mode) {
  if (mode != SensorMode::Off) {
    return SNS_SMGR_BUFFERING_ACTION_ADD_V01;
  } else {
    return SNS_SMGR_BUFFERING_ACTION_DELETE_V01;
  }
}

/**
 * Specify the sensor decimation type.
 *
 * @param sensorId The sensorID as provided by the SMGR.
 * @param dataType The dataType for the sesnor as provided by the SMGR.
 * return The decimation type as defined by the SMGR.
 */
uint8_t getDecimationType(uint8_t sensorId, uint8_t dataType) {
  // Request filtered data for accel and gyro to reduce noise aliasing in case
  // SMGR has other higher ODR clients.
  if ((sensorId == SNS_SMGR_ID_ACCEL_V01 || sensorId == SNS_SMGR_ID_GYRO_V01)
      && dataType == SNS_SMGR_DATA_TYPE_PRIMARY_V01) {
    return SNS_SMGR_DECIMATION_FILTER_V01;
  } else {
    return SNS_SMGR_DECIMATION_RECENT_SAMPLE_V01;
  }
}

/**
 * Populates a sns_smgr_buffering_req_msg_v01 struct to request sensor data.
 *
 * @param request The new request to set this sensor to.
 * @param sensorId The sensorID as provided by the SMGR request for sensor info.
 * @param dataType The dataType for the sesnor as provided by the SMGR request
 *                 for sensor info.
 * @param calType The calibration type (CAL_SEL) as defined in the SMGR API.
 * @param minInterval The minimum interval allowed by this sensor.
 * @param sensorDataRequest The pointer to the data request to be populated.
 */
void populateSensorRequest(
    const SensorRequest& chreRequest, uint8_t sensorId, uint8_t dataType,
    uint8_t calType, uint64_t minInterval,
    sns_smgr_buffering_req_msg_v01 *sensorRequest) {
  // Zero the fields in the request. All mandatory and unused fields are
  // specified to be set to false or zero so this is safe.
  memset(sensorRequest, 0, sizeof(*sensorRequest));

  // Reconstructs a request to deliver one-shot sensors' data ASAP and set
  // default interval to some meaningful number.
  bool isOneShot = sensorTypeIsOneShot(getSensorTypeFromSensorId(
      sensorId, dataType, calType));
  uint64_t cappedInterval = chreRequest.getInterval().toRawNanoseconds();
  if (cappedInterval == CHRE_SENSOR_INTERVAL_DEFAULT) {
    // For one-shot sensors, we've overridden minInterval to default in init.
    // However, for InstantMotion/StationaryDetect, making a request with
    // default interval will not trigger.
    cappedInterval =
        isOneShot ? kDefaultInterval : std::max(minInterval, kDefaultInterval);
  }
  SensorRequest request(chreRequest.getMode(), Nanoseconds(cappedInterval),
                        isOneShot ? Nanoseconds(0) : chreRequest.getLatency());

  // Build the request for one sensor at the requested rate. An add action for a
  // ReportID that is already in use causes a replacement of the last request.
  sensorRequest->ReportId = getReportId(sensorId, dataType, calType);
  sensorRequest->Action = getSmgrRequestActionForMode(request.getMode());

  // SMGR report interval should be (interval + latency). However, to handle
  // fractional-interval latency setting and to guarantee meeting chre request,
  // report interval is set to latency only. Also, lower-bound batchInterval as
  // request to SMGR would fail if batchInterval < interval.
  Nanoseconds batchInterval =
      std::max(request.getLatency(), request.getInterval());
  sensorRequest->ReportRate = intervalToSmgrQ16ReportRate(batchInterval);
  sensorRequest->Item_len = 1;  // One sensor per request if possible.
  sensorRequest->Item[0].SensorId = sensorId;
  sensorRequest->Item[0].DataType = dataType;
  sensorRequest->Item[0].Decimation = getDecimationType(sensorId, dataType);
  sensorRequest->Item[0].Calibration = calType;
  sensorRequest->Item[0].SamplingRate =
      intervalToSmgrSamplingRate(request.getInterval());

  // Add a dummy primary sensor to accompany a secondary temperature sensor.
  // This is requred by the SMGR. The primary sensor is requested with the same
  // (low) rate and the same latency, whose response data will be ignored.
  if (isSecondaryTemperature(sensorRequest->ReportId)) {
    sensorRequest->Item_len = 2;
    sensorRequest->Item[1].SensorId = sensorId;
    sensorRequest->Item[1].DataType = SNS_SMGR_DATA_TYPE_PRIMARY_V01;
    sensorRequest->Item[1].Decimation = getDecimationType(
        sensorId, SNS_SMGR_DATA_TYPE_PRIMARY_V01);
    sensorRequest->Item[1].Calibration = SNS_SMGR_CAL_SEL_FULL_CAL_V01;
    sensorRequest->Item[1].SamplingRate = sensorRequest->Item[0].SamplingRate;
  }

  // Synchronize fifo flushes with other clients that have SSC proc_type.
  // send_indications_during_suspend has no effect on data sent to SLPI.
  // Default is to synchronize with AP clients, which may shorten flush
  // intervals for data sent to the AP.
  sensorRequest->notify_suspend_valid = true;
  sensorRequest->notify_suspend.proc_type = SNS_PROC_SSC_V01;
  sensorRequest->notify_suspend.send_indications_during_suspend = true;
}

/**
 * Determines whether a request is allowed. A passive request is not always
 * allowed.
 *
 * @param sensorType The SensorType of this request
 * @param request The intended sensor request
 * @return true if the request is allowed.
 */
bool isRequestAllowed(SensorType sensorType, const SensorRequest& request) {
  bool allowed = false;

  const Sensor *sensor = EventLoopManagerSingleton::get()
      ->getSensorRequestManager().getSensor(sensorType);
  if (sensor != nullptr) {
    if (sensorModeIsPassive(request.getMode())) {
      size_t index = getSensorMonitorIndex(sensor->sensorId);
      if (index == gSensorMonitors.size()) {
        LOGE("SensorId %" PRIu8 " doesn't have a monitor", sensor->sensorId);
      } else {
        SensorMode mergedMode = getMergedMode(
            sensor->sensorId, sensorType, request);
        bool otherClientPresent = gSensorMonitors[index].otherClientPresent;
        allowed = (sensorModeIsActive(mergedMode) || otherClientPresent);
        LOGD("sensorType %d allowed %d: mergedMode %d, otherClientPresent %d",
             static_cast<size_t>(sensorType), allowed,
             static_cast<int>(mergedMode), otherClientPresent);
      }
    } else {
      // If it's an ACTIVE or an OFF request, it's always allowed.
      allowed = true;
    }
  }
  return allowed;
}

/**
 * Makes a SNS_SMGR_BUFFERING_REQ request based on the arguments provided.
 *
 * @param sensorId The sensorID as provided by the SMGR.
 * @param dataType The dataType for the sesnor as provided by the MSGR.
 * @param calType The calibration type (CAL_SEL) as defined in the SMGR API.
 * @param minInterval The minimum interval of this sensor.
 * @param request The sensor request
 * @return true if the request has been made successfully.
 */
bool makeBufferingReq(uint8_t sensorId, uint8_t dataType, uint8_t calType,
                      uint64_t minInterval, const SensorRequest& request) {
  bool success = false;
  auto sensorRequest = MakeUniqueZeroFill<sns_smgr_buffering_req_msg_v01>();
  auto sensorResponse = MakeUnique<sns_smgr_buffering_resp_msg_v01>();

  if (sensorRequest.isNull() || sensorResponse.isNull()) {
    LOGE("Failed to allocate buffering msg");
  } else {
    populateSensorRequest(request, sensorId, dataType, calType,
                          minInterval, sensorRequest.get());

    smr_err status = getSmrHelper()->sendReqSync(
        gPlatformSensorServiceSmrClientHandle, SNS_SMGR_BUFFERING_REQ_V01,
        &sensorRequest, &sensorResponse);

    if (status != SMR_NO_ERR) {
      LOGE("Error requesting sensor data: %d", status);
    } else if (sensorResponse->Resp.sns_result_t != SNS_RESULT_SUCCESS_V01
        || (sensorResponse->AckNak != SNS_SMGR_RESPONSE_ACK_SUCCESS_V01
            && sensorResponse->AckNak != SNS_SMGR_RESPONSE_ACK_MODIFIED_V01)) {
      LOGE("Sensor data request failed with error: %d, AckNak: %d",
           sensorResponse->Resp.sns_err_t, sensorResponse->AckNak);
    } else {
      success = true;
    }
  }

  return success;
}

/**
 * Makes a SNS_SMGR_BUFFERING_REQ request if necessary.
 *
 * @param sensorType The sensor type of the request.
 * @param request The sensor request to be made.
 * @return true if the request has been accepted.
 */
bool makeRequest(SensorType sensorType, const SensorRequest& request) {
  bool success = false;

  Sensor *sensor = EventLoopManagerSingleton::get()->getSensorRequestManager()
      .getSensor(sensorType);
  if (sensor != nullptr) {
    // Do not make an off request if the sensor is already off. Otherwise, SMGR
    // returns an error.
    if (request.getMode() == SensorMode::Off) {
      success = sensor->isSensorOff;
    }

    // Make a SMGR buffering request if necessary.
    if (!success) {
      success = makeBufferingReq(sensor->sensorId, sensor->dataType,
                                 sensor->calType, sensor->minInterval, request);
    }
  }

  // TODO: handle makeBufferingReq failures
  if (success) {
    // Update internal states if request was accepted by SMGR.
    sensor->isSensorOff = (request.getMode() == SensorMode::Off);

    if (request.getMode() == SensorMode::Off) {
      sensor->lastEventValid = false;
    }

    updateSamplingStatus(sensor, request);
  }
  return success;
}

/**
 * Makes all pending requests of the specified sensor ID to SMGR.
 *
 * @param sensorId The sensor ID whose pending requests are to be made.
 * @return true if an ADD request has been accepted.
 */
bool makeAllPendingRequests(uint8_t sensorId) {
  // Identify sensor types to check for pending requests
  SensorType sensorTypes[kMaxNumSensorsPerSensorId];
  size_t numSensorTypes = populateSensorTypeArrayFromSensorId(
      sensorId, sensorTypes);
  bool accepted = false;
  for (size_t i = 0; i < numSensorTypes; i++) {
    const Sensor *sensor = EventLoopManagerSingleton::get()
        ->getSensorRequestManager().getSensor(sensorTypes[i]);

    // If sensor is off and the request is not off, it's a pending request.
    if (sensor != nullptr && sensor->isSensorOff
        && sensor->getRequest().getMode() != SensorMode::Off) {
      accepted |= makeRequest(sensorTypes[i], sensor->getRequest());
    }
  }
  return accepted;
}

/**
 * Identifies and removes passive requests that have been made to the SMGR, and
 * adds them to the sensor monitor.
 *
 * @param sensorId The sensor ID whose passive requests are to be removed.
 * @return true if a DELETE request has been accepted.
 */
bool removeAllPassiveRequests(uint8_t sensorId) {
  // Specify sensor types to check for passive requests
  SensorType sensorTypes[kMaxNumSensorsPerSensorId];
  size_t numSensorTypes = populateSensorTypeArrayFromSensorId(
      sensorId, sensorTypes);
  bool accepted = false;
  for (size_t i = 0; i < numSensorTypes; i++) {
    const Sensor *sensor = EventLoopManagerSingleton::get()
        ->getSensorRequestManager().getSensor(sensorTypes[i]);

    // Turn off sensors that have a passive request
    if (sensor != nullptr
        && sensorModeIsPassive(sensor->getRequest().getMode())) {
      SensorRequest offRequest;
      accepted |= makeRequest(sensorTypes[i], offRequest);
    }
  }
  return accepted;
}

}  // anonymous namespace

PlatformSensor::~PlatformSensor() {
  if (lastEvent != nullptr) {
    LOGD("Releasing lastEvent: id %" PRIu8 ", type %" PRIu8 ", cal %" PRIu8
         ", size %zu", sensorId, dataType, calType, lastEventSize);
    memoryFree(lastEvent);
  }
}

void PlatformSensor::init() {
  // Timeout for SMR client initialization, in milliseconds.
  constexpr uint32_t kSmrInitTimeoutMs = 10;

  SmrHelperSingleton::init();

  // sns_smgr_api_v01
  qmi_idl_service_object_type smgrSvcObj =
      SNS_SMGR_SVC_get_service_object_v01();
  if (smgrSvcObj == nullptr) {
    FATAL_ERROR("Failed to obtain the SNS SMGR service instance");
  }

  smr_err result = getSmrHelper()->waitForService(smgrSvcObj);
  if (result != SMR_NO_ERR) {
    FATAL_ERROR("Failed while waiting for SNS SMGR service");
  }

  // Note: giving nullptr for err_cb prevents this from degrading to a regular
  // QMI client if the service is not found.
  smr_err status = smr_client_init(
      smgrSvcObj, SMR_CLIENT_INSTANCE_ANY,
      platformSensorServiceIndicationCallback, nullptr /* ind_cb_data */,
      kSmrInitTimeoutMs, nullptr /* err_cb */, nullptr /* err_cb_data */,
      &gPlatformSensorServiceSmrClientHandle, isSlpiUimgSupported());
  if (status != SMR_NO_ERR) {
    FATAL_ERROR("Failed to initialize SMGR client: %d", status);
  }

  // sns_smgr_interal_api_v02
  qmi_idl_service_object_type smgrInternalSvcObj =
      SNS_SMGR_INTERNAL_SVC_get_service_object_v02();
  if (smgrInternalSvcObj == nullptr) {
    FATAL_ERROR("Failed to obtain the SNS SMGR internal service instance");
  }

  result = getSmrHelper()->waitForService(smgrInternalSvcObj);
  if (result != SMR_NO_ERR) {
    FATAL_ERROR("Failed while waiting for SNS SMGR internal service");
  }

  status = smr_client_init(
      smgrInternalSvcObj, SMR_CLIENT_INSTANCE_ANY,
      platformSensorInternalServiceIndicationCallback,
      nullptr /* ind_cb_data */, kSmrInitTimeoutMs, nullptr /* err_cb */,
      nullptr /* err_cb_data */, &gPlatformSensorInternalServiceSmrClientHandle,
      isSlpiUimgSupported());
  if (status != SMR_NO_ERR) {
    FATAL_ERROR("Failed to initialize SMGR internal client: %d", status);
  }
}

void PlatformSensor::deinit() {
  smr_err err = getSmrHelper()->releaseSync(
      gPlatformSensorServiceSmrClientHandle);
  if (err != SMR_NO_ERR) {
    LOGE("Failed to release SMGR client: %d", err);
  }
  gPlatformSensorServiceSmrClientHandle = nullptr;

  err = getSmrHelper()->releaseSync(
      gPlatformSensorInternalServiceSmrClientHandle);
  if (err != SMR_NO_ERR) {
    LOGE("Failed to release SMGR internal client: %d", err);
  }
  gPlatformSensorInternalServiceSmrClientHandle = nullptr;

  // Clearing all sensor status monitors. Releasing an SMR client also releases
  // all sensor status monitor requests.
  gSensorMonitors.clear();
  SmrHelperSingleton::deinit();
}

bool PlatformSensor::getSensors(DynamicVector<Sensor> *sensors) {
  CHRE_ASSERT(sensors);

  auto sensorListRequest =
      MakeUniqueZeroFill<sns_smgr_all_sensor_info_req_msg_v01>();
  auto sensorListResponse = MakeUnique<sns_smgr_all_sensor_info_resp_msg_v01>();

  smr_err status = getSmrHelper()->sendReqSync(
      gPlatformSensorServiceSmrClientHandle, SNS_SMGR_ALL_SENSOR_INFO_REQ_V01,
      &sensorListRequest, &sensorListResponse);

  bool success = false;
  if (status != SMR_NO_ERR) {
    LOGE("Error requesting sensor list: %d", status);
  } else if (sensorListResponse->Resp.sns_result_t != SNS_RESULT_SUCCESS_V01) {
    LOGE("Sensor list lequest failed with error: %d",
         sensorListResponse->Resp.sns_err_t);
  } else {
    success = true;
    for (uint32_t i = 0; i < sensorListResponse->SensorInfo_len; i++) {
      uint8_t sensorId = sensorListResponse->SensorInfo[i].SensorID;
      if (!getSensorsForSensorId(sensorId, sensors)) {
        success = false;
        break;
      }
    }
  }

  return success;
}

bool PlatformSensor::applyRequest(const SensorRequest& request) {
  bool success;

  if (!SmrHelperSingleton::isInitialized()) {
    // Off requests made as part of shutdown come after PlatformSensor::deinit()
    // which releases our SMGR clients, removing all requests. Report success in
    // this case.
    success = (request.getMode() == SensorMode::Off) ? true : false;
    CHRE_ASSERT_LOG(success, "Sensor request made before init/after deinit");
  } else {
    // Adds a sensor monitor the first time this sensor is requested.
    addSensorMonitor(this->sensorId);

    // As sensor status monior indication doesn't support secondary sensor
    // status change, Light sensor (a secondary one) is always overridden to be
    // requested with an active mode.
    bool passiveLight = (getSensorType() == SensorType::Light
                         && sensorModeIsPassive(request.getMode()));
    if (passiveLight) {
      LOGE("Passive request for Light sensor is not supported. "
           "Overriding request to active");
    }
    SensorRequest localRequest(
        passiveLight ? SensorMode::ActiveContinuous : request.getMode(),
        request.getInterval(), request.getLatency());

    // Determines whether a (passive) request is allowed at this point.
    bool requestAllowed = isRequestAllowed(getSensorType(), localRequest);

    // If request is not allowed, turn off the sensor. Otherwise, make request.
    SensorRequest offRequest;
    success = makeRequest(getSensorType(),
                          requestAllowed ? localRequest : offRequest);
  }
  return success;
}

bool PlatformSensor::flushAsync() {
  // NOTE: SMGR framework flushes all pending data when a new request comes in
  //       (ref sns_rh_sol_schedule_existing_report() in sns_rh_sol.c).
  //       In this implementation of flushAsync, we make a request identical to
  //       the existing sensor request, blocking on an asynchronous response,
  //       and assume that the flush request has completed when this identical
  //       sensor request is successfully handled and executed. This
  //       implementation mirrors the sensors HAL implementation of flush.
  bool success = false;
  Sensor *sensor = EventLoopManagerSingleton::get()->getSensorRequestManager()
      .getSensor(getSensorType());
  if (sensor != nullptr) {
    success = applyRequest(sensor->getRequest());
    if (success) {
      EventLoopManagerSingleton::get()->getSensorRequestManager()
          .handleFlushCompleteEvent(CHRE_ERROR_NONE, getSensorType());
    }
  }
  return success;
}

SensorType PlatformSensor::getSensorType() const {
  return getSensorTypeFromSensorId(this->sensorId, this->dataType,
                                   this->calType);
}

uint64_t PlatformSensor::getMinInterval() const {
  return minInterval;
}

const char *PlatformSensor::getSensorName() const {
  return sensorName;
}

PlatformSensor::PlatformSensor(PlatformSensor&& other) {
  // Our move assignment operator doesn't assume that "this" is initialized, so
  // we can just use that here
  *this = std::move(other);
}

PlatformSensor& PlatformSensor::operator=(PlatformSensor&& other) {
  // Note: if this implementation is ever changed to depend on "this" containing
  // initialized values, the move constructor implemenation must be updated
  sensorId = other.sensorId;
  dataType = other.dataType;
  calType = other.calType;
  memcpy(sensorName, other.sensorName, SNS_SMGR_MAX_SENSOR_NAME_SIZE_V01);
  minInterval = other.minInterval;

  lastEvent = other.lastEvent;
  other.lastEvent = nullptr;

  lastEventSize = other.lastEventSize;
  other.lastEventSize = 0;

  lastEventValid = other.lastEventValid;
  isSensorOff = other.isSensorOff;
  samplingStatus = other.samplingStatus;

  return *this;
}

ChreSensorData *PlatformSensor::getLastEvent() const {
  return (this->lastEventValid) ? this->lastEvent : nullptr;
}

bool PlatformSensor::getSamplingStatus(
    struct chreSensorSamplingStatus *status) const {
  CHRE_ASSERT(status);

  memcpy(status, &samplingStatus, sizeof(*status));
  return true;
}

bool PlatformSensor::getThreeAxisBias(
    struct chreSensorThreeAxisData *bias) const {
  // TODO: Implement this.
  return false;
}

void PlatformSensorBase::setLastEvent(const ChreSensorData *event) {
  memcpy(this->lastEvent, event, this->lastEventSize);
  this->lastEventValid = true;
}

smr_client_hndl getSensorServiceSmrClientHandle() {
  return gPlatformSensorServiceSmrClientHandle;
}

}  // namespace chre