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