/* * Copyright (C) 2015 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. */ #define LOG_TAG "VehicleNetworkAudioHelper" #include <VehicleNetwork.h> #include <vehicle-internal.h> #include <utils/SystemClock.h> #include "VehicleNetworkAudioHelper.h" //#define DBG #ifdef DBG #define LOGD(x...) ALOGD(x) #else #define LOGD(x...) #endif namespace android { // ---------------------------------------------------------------------------- VehicleNetworkAudioHelper::VehicleNetworkAudioHelper(int64_t timeoutNs) : mTimeoutNs(timeoutNs), mListener(NULL), mHasFocusProperty(false) { } VehicleNetworkAudioHelper::VehicleNetworkAudioHelper(int64_t timeoutNs, sp<VehicleNetworkAudioFocusListener> listener) : mTimeoutNs(timeoutNs), mListener(listener), mHasFocusProperty(false) { } VehicleNetworkAudioHelper::~VehicleNetworkAudioHelper() { // nothing to do } status_t VehicleNetworkAudioHelper::init() { Mutex::Autolock autoLock(mLock); sp<VehicleNetworkListener> listener(this); mService = VehicleNetwork::createVehicleNetwork(listener); mScratchValueStreamState.prop = VEHICLE_PROPERTY_INTERNAL_AUDIO_STREAM_STATE; mScratchValueStreamState.value_type = VEHICLE_VALUE_TYPE_INT32_VEC2; mScratchValueStreamState.timestamp = 0; mScratchValueFocus.prop = VEHICLE_PROPERTY_AUDIO_FOCUS; mScratchValueFocus.value_type = VEHICLE_VALUE_TYPE_INT32_VEC4; mScratchValueFocus.timestamp = 0; updatePropertiesLocked(); return NO_ERROR; } void VehicleNetworkAudioHelper::updatePropertiesLocked() { sp<VehiclePropertiesHolder> holder = mService->listProperties(VEHICLE_PROPERTY_AUDIO_FOCUS); if (holder.get() != NULL && holder->getList().size() == 1) { mHasFocusProperty = true; mService->subscribe(VEHICLE_PROPERTY_AUDIO_FOCUS, 0); mService->getProperty(&mScratchValueFocus); mAllowedStreams = mScratchValueFocus.value.int32_array[VEHICLE_AUDIO_FOCUS_INDEX_STREAMS]; ALOGI("initial focus state 0x%x", mAllowedStreams); } else { ALOGW("No focus property, assume focus always granted"); mHasFocusProperty = false; mAllowedStreams = 0xffffffff; } for (size_t i = 0; i < mStreamStates.size(); i++) { mStreamStates.editItemAt(i).timeoutStartNs = 0; } } void VehicleNetworkAudioHelper::release() { Mutex::Autolock autoLock(mLock); if (mService.get() == NULL) { return; } mService = NULL; } static int32_t streamFlagToStreamNumber(int32_t streamFlag) { int32_t flag = 0x1; for (int32_t i = 0; i < 32; i++) { if ((flag & streamFlag) != 0) { return i; } flag = flag << 1; } return -1; } void VehicleNetworkAudioHelper::notifyStreamStarted(int32_t stream) { Mutex::Autolock autoLock(mLock); if (!mHasFocusProperty) { return; } int32_t streamNumber = streamFlagToStreamNumber(stream); if (streamNumber < 0) { ALOGE("notifyStreamStarted, wrong stream:0x%x", stream); return; } StreamState& state = getStreamStateLocked(streamNumber); if (state.started) { return; } state.started = true; state.timeoutStartNs = elapsedRealtimeNano(); mScratchValueStreamState.value.int32_array[VEHICLE_AUDIO_STREAM_STATE_INDEX_STATE] = VEHICLE_AUDIO_STREAM_STATE_STARTED; mScratchValueStreamState.value.int32_array[VEHICLE_AUDIO_STREAM_STATE_INDEX_STREAM] = streamNumber; mScratchValueStreamState.timestamp = android::elapsedRealtimeNano(); mService->setProperty(mScratchValueStreamState); } void VehicleNetworkAudioHelper::notifyStreamStopped(int32_t stream) { Mutex::Autolock autoLock(mLock); if (!mHasFocusProperty) { return; } int32_t streamNumber = streamFlagToStreamNumber(stream); if (streamNumber < 0) { ALOGE("notifyStreamStopped, wrong stream:0x%x", stream); return; } StreamState& state = getStreamStateLocked(streamNumber); if (!state.started) { return; } state.started = false; state.timeoutStartNs = 0; mScratchValueStreamState.value.int32_array[VEHICLE_AUDIO_STREAM_STATE_INDEX_STATE] = VEHICLE_AUDIO_STREAM_STATE_STOPPED; mScratchValueStreamState.value.int32_array[VEHICLE_AUDIO_STREAM_STATE_INDEX_STREAM] = streamNumber; mScratchValueStreamState.timestamp = android::elapsedRealtimeNano(); mService->setProperty(mScratchValueStreamState); } VehicleNetworkAudioHelper::StreamState& VehicleNetworkAudioHelper::getStreamStateLocked( int32_t streamNumber) { if (streamNumber >= (int32_t) mStreamStates.size()) { mStreamStates.insertAt(mStreamStates.size(), streamNumber - mStreamStates.size() + 1); } return mStreamStates.editItemAt(streamNumber); } vehicle_network_audio_helper_focus_state VehicleNetworkAudioHelper::getStreamFocusState( int32_t stream) { Mutex::Autolock autoLock(mLock); if ((mAllowedStreams & stream) == stream) { return VEHICLE_NETWORK_AUDIO_HELPER_FOCUS_STATE_FOCUS; } int32_t streamNumber = streamFlagToStreamNumber(stream); if (streamNumber < 0) { ALOGE("getStreamFocusState, wrong stream:0x%x", stream); return VEHICLE_NETWORK_AUDIO_HELPER_FOCUS_STATE_TIMEOUT; } StreamState& state = getStreamStateLocked(streamNumber); if (state.timeoutStartNs == 0) { if (state.started) { state.timeoutStartNs = elapsedRealtimeNano(); } } else { int64_t now = elapsedRealtimeNano(); if ((state.timeoutStartNs + mTimeoutNs) < now) { return VEHICLE_NETWORK_AUDIO_HELPER_FOCUS_STATE_TIMEOUT; } } return VEHICLE_NETWORK_AUDIO_HELPER_FOCUS_STATE_NO_FOCUS; } bool VehicleNetworkAudioHelper::waitForStreamFocus(int32_t stream, nsecs_t waitTimeNs) { LOGD("waitForStreamFocus"); Mutex::Autolock autoLock(mLock); int64_t currentTime = android::elapsedRealtimeNano(); int64_t finishTime = currentTime + waitTimeNs; while (true) { if ((stream & mAllowedStreams) == stream) { LOGD("waitForStreamFocus, has focus"); return true; } currentTime = android::elapsedRealtimeNano(); if (currentTime >= finishTime) { break; } nsecs_t waitTime = finishTime - currentTime; mFocusWait.waitRelative(mLock, waitTime); } LOGD("waitForStreamFocus, no focus"); return false; } void VehicleNetworkAudioHelper::onEvents(sp<VehiclePropValueListHolder>& events) { sp<VehicleNetworkAudioFocusListener> listener; int32_t allowedStreams; bool changed = false; do { Mutex::Autolock autoLock(mLock); if (mService.get() == NULL) { // already released return; } for (vehicle_prop_value_t* value : events->getList()) { if (value->prop == VEHICLE_PROPERTY_AUDIO_FOCUS) { mAllowedStreams = value->value.int32_array[VEHICLE_AUDIO_FOCUS_INDEX_STREAMS]; ALOGI("audio focus change 0x%x", mAllowedStreams); changed = true; } } listener = mListener; allowedStreams = mAllowedStreams; if (changed) { mFocusWait.signal(); } } while (false); if (listener.get() != NULL && changed) { listener->onFocusChange(allowedStreams); } } void VehicleNetworkAudioHelper::onHalError(int32_t /*errorCode*/, int32_t /*property*/, int32_t /*operation*/) { // not used } void VehicleNetworkAudioHelper::onHalRestart(bool /*inMocking*/) { LOGD("onHalRestart"); Mutex::Autolock autoLock(mLock); if (mService.get() == NULL) { // already released return; } updatePropertiesLocked(); mFocusWait.signal(); } }; // namespace android