1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 /** 18 ** This class is deprecated because its use causes threading issues 19 ** with the FMQ due to change of threads reading and writing into FMQ. 20 ** 21 ** DO NOT USE. These files will be removed. 22 **/ 23 24 #pragma once 25 26 #include "core-impl/Stream.h" 27 28 namespace aidl::android::hardware::audio::core::deprecated { 29 30 // 'StreamSwitcher' is an implementation of 'StreamCommonInterface' which allows 31 // dynamically switching the underlying stream implementation based on currently 32 // connected devices. This is achieved by replacing inheritance from 33 // 'StreamCommonImpl' with owning an instance of it. StreamSwitcher must be 34 // extended in order to supply the logic for choosing the stream 35 // implementation. When there are no connected devices, for instance, upon the 36 // creation, the StreamSwitcher engages an instance of a stub stream in order to 37 // keep serving requests coming via 'StreamDescriptor'. 38 // 39 // StreamSwitcher implements the 'IStreamCommon' interface directly, with 40 // necessary delegation to the current stream implementation. While the stub 41 // stream is engaged, any requests made via 'IStreamCommon' (parameters, effects 42 // setting, etc) are postponed and only delivered on device connection change 43 // to the "real" stream implementation provided by the extending class. This is why 44 // the behavior of StreamSwitcher in the "stub" state is not identical to behavior 45 // of 'StreamStub'. It can become a full substitute for 'StreamStub' once 46 // device connection change event occurs and the extending class returns 47 // 'LEAVE_CURRENT_STREAM' from 'switchCurrentStream' method. 48 // 49 // There is a natural limitation that the current stream implementation may only 50 // be switched when the stream is in the 'STANDBY' state. Thus, when the event 51 // to switch the stream occurs, the current stream is stopped and joined, and 52 // its last state is validated. Since the change of the set of connected devices 53 // normally occurs on patch updates, if the stream was not in standby, this is 54 // reported to the caller of 'IModule.setAudioPatch' as the 'EX_ILLEGAL_STATE' 55 // error. 56 // 57 // The simplest use case, when the implementor just needs to emulate the legacy HAL API 58 // behavior of receiving the connected devices upon stream creation, the implementation 59 // of the extending class can look as follows. We assume that 'StreamLegacy' implementation 60 // is the one requiring to know connected devices on creation: 61 // 62 // class StreamLegacy : public StreamCommonImpl { 63 // public: 64 // StreamLegacy(StreamContext* context, const Metadata& metadata, 65 // const std::vector<AudioDevice>& devices); 66 // }; 67 // 68 // class StreamOutLegacy final : public StreamOut, public StreamSwitcher { 69 // public: 70 // StreamOutLegacy(StreamContext&& context, metatadata etc.) 71 // private: 72 // DeviceSwitchBehavior switchCurrentStream(const std::vector<AudioDevice>&) override { 73 // // This implementation effectively postpones stream creation until 74 // // receiving the first call to 'setConnectedDevices' with a non-empty list. 75 // return isStubStream() ? DeviceSwitchBehavior::CREATE_NEW_STREAM : 76 // DeviceSwitchBehavior::USE_CURRENT_STREAM; 77 // } 78 // std::unique_ptr<StreamCommonInterfaceEx> createNewStream( 79 // const std::vector<AudioDevice>& devices, 80 // StreamContext* context, const Metadata& metadata) override { 81 // return std::unique_ptr<StreamCommonInterfaceEx>(new InnerStreamWrapper<StreamLegacy>( 82 // context, metadata, devices)); 83 // } 84 // void onClose(StreamDescriptor::State) override { defaultOnClose(); } 85 // } 86 // 87 88 class StreamCommonInterfaceEx : virtual public StreamCommonInterface { 89 public: 90 virtual StreamDescriptor::State getStatePriorToClosing() const = 0; 91 }; 92 93 template <typename T> 94 class InnerStreamWrapper : public T, public StreamCommonInterfaceEx { 95 public: 96 template <typename... Args> InnerStreamWrapper(Args &&...args)97 InnerStreamWrapper(Args&&... args) : T(std::forward<Args>(args)...) {} getStatePriorToClosing()98 StreamDescriptor::State getStatePriorToClosing() const override { return mStatePriorToClosing; } 99 100 private: 101 // Do not need to do anything on close notification from the inner stream 102 // because StreamSwitcher handles IStreamCommon::close by itself. onClose(StreamDescriptor::State statePriorToClosing)103 void onClose(StreamDescriptor::State statePriorToClosing) override { 104 mStatePriorToClosing = statePriorToClosing; 105 } 106 107 StreamDescriptor::State mStatePriorToClosing = StreamDescriptor::State::STANDBY; 108 }; 109 110 class StreamSwitcher : virtual public StreamCommonInterface { 111 public: 112 StreamSwitcher(StreamContext* context, const Metadata& metadata); 113 114 ndk::ScopedAStatus close() override; 115 ndk::ScopedAStatus prepareToClose() override; 116 ndk::ScopedAStatus updateHwAvSyncId(int32_t in_hwAvSyncId) override; 117 ndk::ScopedAStatus getVendorParameters(const std::vector<std::string>& in_ids, 118 std::vector<VendorParameter>* _aidl_return) override; 119 ndk::ScopedAStatus setVendorParameters(const std::vector<VendorParameter>& in_parameters, 120 bool in_async) override; 121 ndk::ScopedAStatus addEffect( 122 const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& in_effect) 123 override; 124 ndk::ScopedAStatus removeEffect( 125 const std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>& in_effect) 126 override; 127 128 ndk::ScopedAStatus getStreamCommonCommon(std::shared_ptr<IStreamCommon>* _aidl_return) override; 129 ndk::ScopedAStatus updateMetadataCommon(const Metadata& metadata) override; 130 131 ndk::ScopedAStatus initInstance( 132 const std::shared_ptr<StreamCommonInterface>& delegate) override; 133 const StreamContext& getContext() const override; 134 bool isClosed() const override; 135 const ConnectedDevices& getConnectedDevices() const override; 136 ndk::ScopedAStatus setConnectedDevices( 137 const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) 138 override; 139 ndk::ScopedAStatus bluetoothParametersUpdated() override; 140 ndk::ScopedAStatus setGain(float gain) override; 141 142 protected: 143 // Since switching a stream requires closing down the current stream, StreamSwitcher 144 // asks the extending class its intent on the connected devices change. 145 enum DeviceSwitchBehavior { 146 // Continue using the current stream implementation. If it's the stub implementation, 147 // StreamSwitcher starts treating the stub stream as a "real" implementation, 148 // without effectively closing it and starting again. 149 USE_CURRENT_STREAM, 150 // This is the normal case when the extending class provides a "real" implementation 151 // which is not a stub implementation. 152 CREATE_NEW_STREAM, 153 // This is the case when the extending class wants to revert back to the initial 154 // condition of using a stub stream provided by the StreamSwitcher. This behavior 155 // is only allowed when the list of connected devices is empty. 156 SWITCH_TO_STUB_STREAM, 157 // Use when the set of devices is not supported by the extending class. This returns 158 // 'EX_UNSUPPORTED_OPERATION' from 'setConnectedDevices'. 159 UNSUPPORTED_DEVICES, 160 }; 161 // StreamSwitcher will call these methods from 'setConnectedDevices'. If the switch behavior 162 // is 'CREATE_NEW_STREAM', the 'createwNewStream' function will be called (with the same 163 // device vector) for obtaining a new stream implementation, assuming that closing 164 // the current stream was a success. 165 virtual DeviceSwitchBehavior switchCurrentStream( 166 const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices) = 0; 167 virtual std::unique_ptr<StreamCommonInterfaceEx> createNewStream( 168 const std::vector<::aidl::android::media::audio::common::AudioDevice>& devices, 169 StreamContext* context, const Metadata& metadata) = 0; 170 virtual void onClose(StreamDescriptor::State streamPriorToClosing) = 0; 171 isStubStream()172 bool isStubStream() const { return mIsStubStream; } getCurrentStream()173 StreamCommonInterfaceEx* getCurrentStream() const { return mStream.get(); } 174 175 private: 176 using VndParam = std::pair<std::vector<VendorParameter>, bool /*isAsync*/>; 177 isValidClosingStreamState(StreamDescriptor::State state)178 static constexpr bool isValidClosingStreamState(StreamDescriptor::State state) { 179 return state == StreamDescriptor::State::STANDBY || state == StreamDescriptor::State::ERROR; 180 } 181 182 ndk::ScopedAStatus closeCurrentStream(bool validateStreamState); 183 184 // StreamSwitcher does not own the context. 185 StreamContext* mContext; 186 Metadata mMetadata; 187 ChildInterface<StreamCommonDelegator> mCommon; 188 // The current stream. 189 std::unique_ptr<StreamCommonInterfaceEx> mStream; 190 // Indicates whether 'mCurrentStream' is a stub stream implementation 191 // maintained by StreamSwitcher until the extending class provides a "real" 192 // implementation. The invariant of this state is that there are no connected 193 // devices. 194 bool mIsStubStream = true; 195 // Storage for the data from commands received via 'IStreamCommon'. 196 std::optional<int32_t> mHwAvSyncId; 197 std::vector<VndParam> mMissedParameters; 198 std::vector<std::shared_ptr<::aidl::android::hardware::audio::effect::IEffect>> mEffects; 199 bool mBluetoothParametersUpdated = false; 200 }; 201 202 } // namespace aidl::android::hardware::audio::core::deprecated 203