text: /*
   * Copyright 2019 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.
   */
  
  #ifndef OBOE_AAUDIO_EXTENSIONS_H
  #define OBOE_AAUDIO_EXTENSIONS_H
  
  #include <dlfcn.h>
  #include <stdint.h>
  
  #include <sys/system_properties.h>
  
  #include "oboe_common_OboeDebug_android.h"
  #include "oboe_oboe_Oboe_android.h"
  #include "oboe_aaudio_AAudioLoader_android.h"
  
  namespace oboe {
  
  #define LIB_AAUDIO_NAME          "libaaudio.so"
  #define FUNCTION_IS_MMAP         "AAudioStream_isMMapUsed"
  #define FUNCTION_SET_MMAP_POLICY "AAudio_setMMapPolicy"
  #define FUNCTION_GET_MMAP_POLICY "AAudio_getMMapPolicy"
  
  #define AAUDIO_ERROR_UNAVAILABLE  static_cast<aaudio_result_t>(Result::ErrorUnavailable)
  
  typedef struct AAudioStreamStruct         AAudioStream;
  
  /**
   * Call some AAudio test routines that are not part of the normal API.
   */
  class AAudioExtensions {
  public:
      AAudioExtensions() {
          int32_t policy = getIntegerProperty("aaudio.mmap_policy", 0);
          mMMapSupported = isPolicyEnabled(policy);
  
          policy = getIntegerProperty("aaudio.mmap_exclusive_policy", 0);
          mMMapExclusiveSupported = isPolicyEnabled(policy);
      }
  
      static bool isPolicyEnabled(int32_t policy) {
          return (policy == AAUDIO_POLICY_AUTO || policy == AAUDIO_POLICY_ALWAYS);
      }
  
      static AAudioExtensions &getInstance() {
          static AAudioExtensions instance;
          return instance;
      }
  
      bool isMMapUsed(oboe::AudioStream *oboeStream) {
          AAudioStream *aaudioStream = (AAudioStream *) oboeStream->getUnderlyingStream();
          return isMMapUsed(aaudioStream);
      }
  
      bool isMMapUsed(AAudioStream *aaudioStream) {
          if (loadSymbols()) return false;
          if (mAAudioStream_isMMap == nullptr) return false;
          return mAAudioStream_isMMap(aaudioStream);
      }
  
      /**
       * Controls whether the MMAP data path can be selected when opening a stream.
       * It has no effect after the stream has been opened.
       * It only affects the application that calls it. Other apps are not affected.
       *
       * @param enabled
       * @return 0 or a negative error code
       */
      int32_t setMMapEnabled(bool enabled) {
          if (loadSymbols()) return AAUDIO_ERROR_UNAVAILABLE;
          if (mAAudio_setMMapPolicy == nullptr) return false;
          return mAAudio_setMMapPolicy(enabled ? AAUDIO_POLICY_AUTO : AAUDIO_POLICY_NEVER);
      }
  
      bool isMMapEnabled() {
          if (loadSymbols()) return false;
          if (mAAudio_getMMapPolicy == nullptr) return false;
          int32_t policy = mAAudio_getMMapPolicy();
          return isPolicyEnabled(policy);
      }
  
      bool isMMapSupported() {
          return mMMapSupported;
      }
  
      bool isMMapExclusiveSupported() {
          return mMMapExclusiveSupported;
      }
  
  private:
  
      enum {
          AAUDIO_POLICY_NEVER = 1,
          AAUDIO_POLICY_AUTO,
          AAUDIO_POLICY_ALWAYS
      };
      typedef int32_t aaudio_policy_t;
  
      int getIntegerProperty(const char *name, int defaultValue) {
          int result = defaultValue;
          char valueText[PROP_VALUE_MAX] = {0};
          if (__system_property_get(name, valueText) != 0) {
              result = atoi(valueText);
          }
          return result;
      }
  
      /**
       * Load the function pointers.
       * This can be called multiple times.
       * It should only be called from one thread.
       *
       * @return 0 if successful or negative error.
       */
      aaudio_result_t loadSymbols() {
          if (mAAudio_getMMapPolicy != nullptr) {
              return 0;
          }
  
          void *libHandle = AAudioLoader::getInstance()->getLibHandle();
          if (libHandle == nullptr) {
              LOGI("%s() could not find " LIB_AAUDIO_NAME, __func__);
              return AAUDIO_ERROR_UNAVAILABLE;
          }
  
          mAAudioStream_isMMap = (bool (*)(AAudioStream *stream))
                  dlsym(libHandle, FUNCTION_IS_MMAP);
          if (mAAudioStream_isMMap == nullptr) {
              LOGI("%s() could not find " FUNCTION_IS_MMAP, __func__);
              return AAUDIO_ERROR_UNAVAILABLE;
          }
  
          mAAudio_setMMapPolicy = (int32_t (*)(aaudio_policy_t policy))
                  dlsym(libHandle, FUNCTION_SET_MMAP_POLICY);
          if (mAAudio_setMMapPolicy == nullptr) {
              LOGI("%s() could not find " FUNCTION_SET_MMAP_POLICY, __func__);
              return AAUDIO_ERROR_UNAVAILABLE;
          }
  
          mAAudio_getMMapPolicy = (aaudio_policy_t (*)())
                  dlsym(libHandle, FUNCTION_GET_MMAP_POLICY);
          if (mAAudio_getMMapPolicy == nullptr) {
              LOGI("%s() could not find " FUNCTION_GET_MMAP_POLICY, __func__);
              return AAUDIO_ERROR_UNAVAILABLE;
          }
  
          return 0;
      }
  
      bool      mMMapSupported = false;
      bool      mMMapExclusiveSupported = false;
  
      bool    (*mAAudioStream_isMMap)(AAudioStream *stream) = nullptr;
      int32_t (*mAAudio_setMMapPolicy)(aaudio_policy_t policy) = nullptr;
      aaudio_policy_t (*mAAudio_getMMapPolicy)() = nullptr;
  };
  
  } // namespace oboe
  
  #endif //OBOE_AAUDIO_EXTENSIONS_H
  