普通文本  |  305行  |  11.01 KB

/*
 * Copyright (C) 2017 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 "chre/core/audio_request_manager.h"

#include "chre/core/event_loop_manager.h"
#include "chre/platform/fatal_error.h"
#include "chre/platform/system_time.h"

/*
 * TODO(P1-62e045): Evict pending audio events from the event queue as needed.
 *
 * Under the following conditions, an audio data event may be posted to the
 * CHRE event queue when it should not be.
 *
 * 1. Nanoapp changes request
 * 2. Nanoapp removes request
 *
 * A previously scheduled audio data event may be residing in the event queue
 * and will be dispatched to the nanoapp after it has cancelled the request.
 *
 * The solution is to evict any audio events for a given audio handle that are
 * directed to a nanoapp before scheduling the next request to the platform.
 */

namespace chre {

AudioRequestManager::AudioRequestManager() {
  size_t sourceCount = mPlatformAudio.getSourceCount();
  if (!mAudioRequestLists.reserve(sourceCount)) {
    FATAL_ERROR_OOM();
  }

  for (size_t i = 0; i < sourceCount; i++) {
    mAudioRequestLists.emplace_back();
  }
}

void AudioRequestManager::init() {
  mPlatformAudio.init();
}

bool AudioRequestManager::configureSource(const Nanoapp *nanoapp,
                                          uint32_t handle,
                                          bool enable,
                                          uint64_t bufferDuration,
                                          uint64_t deliveryInterval) {
  uint32_t numSamples;
  bool success = validateConfigureSourceArguments(
      handle, enable, bufferDuration, deliveryInterval, &numSamples);
  if (success) {
    size_t requestIndex;
    auto *audioRequest = findAudioRequest(handle, nanoapp->getInstanceId(),
                                          &requestIndex);
    Nanoseconds nextEventTimestamp = SystemTime::getMonotonicTime()
        + Nanoseconds(deliveryInterval);
    size_t lastNumRequests = mAudioRequestLists[handle].requests.size();
    if (audioRequest == nullptr) {
      // The nanoapp is making a new request for audio data.
      if (enable) {
        mAudioRequestLists[handle].requests.emplace_back(
            nanoapp->getInstanceId(), numSamples,
            Nanoseconds(deliveryInterval), nextEventTimestamp);
        postAudioSamplingChangeEvent(nanoapp->getInstanceId(), handle,
                                     mAudioRequestLists[handle].available);
        scheduleNextAudioDataEvent(handle);
      } else {
        LOGW("Nanoapp disabling nonexistent audio request");
      }
    } else {
      // The nanoapp is modifying an existing request for audio.
      if (enable) {
        audioRequest->numSamples = numSamples;
        audioRequest->deliveryInterval = Nanoseconds(deliveryInterval);
        audioRequest->nextEventTimestamp = nextEventTimestamp;
      } else {
        mAudioRequestLists[handle].requests.erase(requestIndex);
      }

      // Note that if the next request did not change, this call is not strictly
      // necessary. The expectation is that the platform will gracefully handle
      // rescheduling the same request.
      scheduleNextAudioDataEvent(handle);
    }

    size_t numRequests = mAudioRequestLists[handle].requests.size();
    if (lastNumRequests == 0 && numRequests > 0) {
      mPlatformAudio.setHandleEnabled(handle, true);
    } else if (lastNumRequests > 0 && numRequests == 0) {
      mPlatformAudio.setHandleEnabled(handle, false);
    }
  }

  return success;
}

void AudioRequestManager::handleAudioDataEvent(
    const struct chreAudioDataEvent *audioDataEvent) {
  auto callback = [](uint16_t /* eventType */, void *eventData) {
    auto *event = static_cast<struct chreAudioDataEvent *>(eventData);
    EventLoopManagerSingleton::get()->getAudioRequestManager()
        .handleAudioDataEventSync(event);
  };

  // Cast off the event const so that it can be provided to the free callback as
  // non-const. The event is provided to nanoapps as const and the runtime
  // itself will not modify this memory so this is safe.
  EventLoopManagerSingleton::get()->deferCallback(
      SystemCallbackType::AudioHandleDataEvent,
      const_cast<struct chreAudioDataEvent *>(audioDataEvent), callback);
}

void AudioRequestManager::handleAudioAvailability(uint32_t handle, bool available) {
  struct CallbackState {
    uint32_t handle;
    bool available;
  };

  auto *cbState = memoryAlloc<CallbackState>();
  if (cbState == nullptr) {
    LOG_OOM();
  } else {
    cbState->handle = handle;
    cbState->available = available;

    auto callback = [](uint16_t /* eventType */, void *eventData) {
      auto *state = static_cast<CallbackState *>(eventData);
      EventLoopManagerSingleton::get()->getAudioRequestManager()
          .handleAudioAvailabilitySync(state->handle, state->available);
      memoryFree(state);
    };

    EventLoopManagerSingleton::get()->deferCallback(
        SystemCallbackType::AudioAvailabilityChange, cbState, callback);
  }
}

bool AudioRequestManager::validateConfigureSourceArguments(
    uint32_t handle, bool enable, uint64_t bufferDuration,
    uint64_t deliveryInterval, uint32_t *numSamples) {
  bool success = false;
  if (handle >= mAudioRequestLists.size()) {
    LOGE("Provided audio handle out of range");
  } else if (enable) {
    chreAudioSource audioSource;
    if (!mPlatformAudio.getAudioSource(handle, &audioSource)) {
      LOGE("Failed to query for audio source");
    } else if (bufferDuration > deliveryInterval) {
      LOGE("Buffer duration must be less than or equal to delivery interval");
    } else if (bufferDuration < audioSource.minBufferDuration
               || bufferDuration > audioSource.maxBufferDuration) {
      LOGE("Invalid buffer duration %" PRIu64 " not in range [%" PRIu64
           ",%" PRIu64 "]", bufferDuration, audioSource.minBufferDuration,
           audioSource.maxBufferDuration);
    } else {
      *numSamples = getSampleCountFromRateAndDuration(
          audioSource.sampleRate, Nanoseconds(bufferDuration));
      success = true;
    }
  } else {
    // Disabling the request, no need to validate bufferDuration or
    // deliveryInterval.
    success = true;
  }
  return success;
}

AudioRequestManager::AudioRequest *AudioRequestManager::findAudioRequest(
    uint32_t handle, uint32_t instanceId, size_t *index) {
  AudioRequest *foundAudioRequest = nullptr;
  auto& requests = mAudioRequestLists[handle].requests;
  for (size_t i = 0; i < requests.size(); i++) {
    auto& audioRequest = requests[i];
    if (audioRequest.instanceId == instanceId) {
      foundAudioRequest = &audioRequest;
      *index = i;
      break;
    }
  }

  return foundAudioRequest;
}

AudioRequestManager::AudioRequest *AudioRequestManager::findNextAudioRequest(
    uint32_t handle) {
  Nanoseconds earliestNextEventTimestamp = Nanoseconds(UINT64_MAX);
  AudioRequest *nextRequest = nullptr;

  auto& reqList = mAudioRequestLists[handle];
  for (auto& req : reqList.requests) {
    if (req.nextEventTimestamp < earliestNextEventTimestamp) {
      earliestNextEventTimestamp = req.nextEventTimestamp;
      nextRequest = &req;
    }
  }

  return nextRequest;
}

void AudioRequestManager::handleAudioDataEventSync(
    struct chreAudioDataEvent *event) {
  uint32_t handle = event->handle;
  if (handle < mAudioRequestLists.size()) {
    auto& reqList = mAudioRequestLists[handle];
    AudioRequest *nextAudioRequest = reqList.nextAudioRequest;

    if (reqList.nextAudioRequest != nullptr) {
      postAudioDataEventFatal(event, nextAudioRequest->instanceId);
      nextAudioRequest->nextEventTimestamp = SystemTime::getMonotonicTime()
          + nextAudioRequest->deliveryInterval;
      reqList.nextAudioRequest = nullptr;
    } else {
      LOGW("Received audio data event with no pending audio request");
    }

    scheduleNextAudioDataEvent(handle);
  } else {
    LOGE("Audio data event handle out of range: %" PRIu32, handle);
  }
}

void AudioRequestManager::handleAudioAvailabilitySync(uint32_t handle,
                                                      bool available) {
  if (handle < mAudioRequestLists.size()) {
    mAudioRequestLists[handle].available = available;
    postAudioSamplingChangeEvents(handle, available);
    scheduleNextAudioDataEvent(handle);
  } else {
    LOGE("Audio availability handle out of range: %" PRIu32, handle);
  }
}

void AudioRequestManager::scheduleNextAudioDataEvent(uint32_t handle) {
  auto& reqList = mAudioRequestLists[handle];
  AudioRequest *nextRequest = findNextAudioRequest(handle);

  if (reqList.available && nextRequest != nullptr) {
    Nanoseconds curTime = SystemTime::getMonotonicTime();
    Nanoseconds eventDelay = Nanoseconds(0);
    if (nextRequest->nextEventTimestamp > curTime) {
      eventDelay = nextRequest->nextEventTimestamp - curTime;
    }
    reqList.nextAudioRequest = nextRequest;
    mPlatformAudio.requestAudioDataEvent(
        handle, nextRequest->numSamples, eventDelay);
  } else {
    mPlatformAudio.cancelAudioDataEventRequest(handle);
  }
}

void AudioRequestManager::postAudioSamplingChangeEvents(uint32_t handle,
                                                        bool available) {
  for (const auto& request : mAudioRequestLists[handle].requests) {
    postAudioSamplingChangeEvent(request.instanceId, handle, available);
  }
}

void AudioRequestManager::postAudioSamplingChangeEvent(uint32_t instanceId,
                                                       uint32_t handle,
                                                       bool available) {
  auto *event = memoryAlloc<struct chreAudioSourceStatusEvent>();
  event->handle = handle;
  event->status.enabled = true;
  event->status.suspended = !available;

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

void AudioRequestManager::postAudioDataEventFatal(
    struct chreAudioDataEvent *event, uint32_t instanceId) {
  EventLoopManagerSingleton::get()->getEventLoop()
      .postEvent(CHRE_EVENT_AUDIO_DATA, event,
                 freeAudioDataEventCallback,
                 kSystemInstanceId, instanceId);
}

void AudioRequestManager::handleFreeAudioDataEvent(
    struct chreAudioDataEvent *audioDataEvent) {
  mPlatformAudio.releaseAudioDataEvent(audioDataEvent);
}

void AudioRequestManager::freeAudioDataEventCallback(uint16_t eventType,
                                                     void *eventData) {
  auto *event = static_cast<struct chreAudioDataEvent *>(eventData);
  EventLoopManagerSingleton::get()->getAudioRequestManager()
      .handleFreeAudioDataEvent(event);
}

}  // namespace chre