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 package com.android.car.audio; 18 19 import android.car.builtin.util.Slogf; 20 import android.car.media.CarVolumeGroupEvent; 21 import android.util.SparseArray; 22 23 import com.android.car.CarLog; 24 import com.android.car.audio.hal.AudioControlWrapper; 25 import com.android.car.audio.hal.HalAudioDeviceInfo; 26 import com.android.car.audio.hal.HalAudioModuleChangeCallback; 27 28 import java.util.ArrayList; 29 import java.util.List; 30 import java.util.Objects; 31 32 /** 33 * Helper class to set, clear and handle audio hardware module callbacks 34 */ 35 final class CarAudioModuleChangeMonitor { 36 37 private final AudioControlWrapper mAudioControlWrapper; 38 private final CarVolumeInfoWrapper mCarVolumeInfoWrapper; 39 // [key, value] -> [zone id, CarAudioZone] 40 private final SparseArray<CarAudioZone> mCarAudioZones; 41 CarAudioModuleChangeMonitor(AudioControlWrapper audioControlWrapper, CarVolumeInfoWrapper carVolumeInfoWrapper, SparseArray<CarAudioZone> carAudioZones)42 CarAudioModuleChangeMonitor(AudioControlWrapper audioControlWrapper, 43 CarVolumeInfoWrapper carVolumeInfoWrapper, SparseArray<CarAudioZone> carAudioZones) { 44 mAudioControlWrapper = 45 Objects.requireNonNull( 46 audioControlWrapper, "Audio control wrapper can not be null"); 47 mCarVolumeInfoWrapper = Objects.requireNonNull(carVolumeInfoWrapper, 48 "Car volume info wrapper can not be null"); 49 mCarAudioZones = Objects.requireNonNull(carAudioZones, "Car audio zones can not be null"); 50 } 51 52 /** 53 * Sets {@code HalAudioModuleChangeCallback} on {@code AudioControlWrapper} to receive 54 * callbacks for audio hardware changes. 55 */ setModuleChangeCallback(HalAudioModuleChangeCallback callback)56 void setModuleChangeCallback(HalAudioModuleChangeCallback callback) { 57 Objects.requireNonNull(callback, "Hal audio module change callback can not be null"); 58 mAudioControlWrapper.setModuleChangeCallback(callback); 59 } 60 61 /** 62 * Clears (any) {@code HalAudioModuleChangeCallback} from {@code AudioControlWrapper} 63 */ clearModuleChangeCallback()64 void clearModuleChangeCallback() { 65 mAudioControlWrapper.clearModuleChangeCallback(); 66 } 67 68 /** 69 * Handles incoming list of updated {@code HalAudioDeviceInfo}. 70 * If the changes result in volume group info updates (min/max/current), 71 * trigger volume group event(s) callback on the listeners. 72 */ handleAudioPortsChanged(List<HalAudioDeviceInfo> deviceInfos)73 void handleAudioPortsChanged(List<HalAudioDeviceInfo> deviceInfos) { 74 List<CarVolumeGroupEvent> events = new ArrayList<>(); 75 for (int i = 0; i < mCarAudioZones.size(); i++) { 76 CarAudioZone zone = mCarAudioZones.valueAt(i); 77 events.addAll(zone.onAudioPortsChanged(deviceInfos)); 78 } 79 80 // its possible we received redundant callbacks from hal. In such cases, 81 // do not call listeners with empty events. 82 if (events.isEmpty()) { 83 Slogf.w(CarLog.TAG_AUDIO, "Audio ports changed callback resulted in no events!"); 84 return; 85 } 86 mCarVolumeInfoWrapper.onVolumeGroupEvent(events); 87 } 88 } 89