1 /* 2 * Copyright (C) 2021 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 static android.media.AudioDeviceInfo.TYPE_AUX_LINE; 20 import static android.media.AudioDeviceInfo.TYPE_BLE_BROADCAST; 21 import static android.media.AudioDeviceInfo.TYPE_BLE_HEADSET; 22 import static android.media.AudioDeviceInfo.TYPE_BLE_SPEAKER; 23 import static android.media.AudioDeviceInfo.TYPE_BLUETOOTH_A2DP; 24 import static android.media.AudioDeviceInfo.TYPE_BUILTIN_MIC; 25 import static android.media.AudioDeviceInfo.TYPE_BUILTIN_SPEAKER; 26 import static android.media.AudioDeviceInfo.TYPE_BUS; 27 import static android.media.AudioDeviceInfo.TYPE_HDMI; 28 import static android.media.AudioDeviceInfo.TYPE_USB_ACCESSORY; 29 import static android.media.AudioDeviceInfo.TYPE_USB_DEVICE; 30 import static android.media.AudioDeviceInfo.TYPE_USB_HEADSET; 31 import static android.media.AudioDeviceInfo.TYPE_WIRED_HEADPHONES; 32 import static android.media.AudioDeviceInfo.TYPE_WIRED_HEADSET; 33 import static android.media.AudioManager.GET_DEVICES_OUTPUTS; 34 35 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.PRIVATE_CONSTRUCTOR; 36 37 import static java.util.Collections.EMPTY_LIST; 38 39 import android.annotation.Nullable; 40 import android.car.feature.Flags; 41 import android.car.media.CarAudioZoneConfigInfo; 42 import android.car.media.CarVolumeGroupEvent; 43 import android.car.media.CarVolumeGroupInfo; 44 import android.media.AudioAttributes; 45 import android.media.AudioDeviceAttributes; 46 import android.media.AudioDeviceInfo; 47 48 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; 49 50 import java.util.ArrayList; 51 import java.util.List; 52 53 final class CarAudioUtils { 54 55 @ExcludeFromCodeCoverageGeneratedReport(reason = PRIVATE_CONSTRUCTOR) CarAudioUtils()56 private CarAudioUtils() { 57 throw new UnsupportedOperationException(); 58 } 59 hasExpired(long startTimeMs, long currentTimeMs, int timeoutMs)60 static boolean hasExpired(long startTimeMs, long currentTimeMs, int timeoutMs) { 61 return (currentTimeMs - startTimeMs) > timeoutMs; 62 } 63 isMicrophoneInputDevice(AudioDeviceInfo device)64 static boolean isMicrophoneInputDevice(AudioDeviceInfo device) { 65 return device.getType() == TYPE_BUILTIN_MIC; 66 } 67 convertVolumeChangeToEvent(CarVolumeGroupInfo info, int flags, int eventTypes)68 static CarVolumeGroupEvent convertVolumeChangeToEvent(CarVolumeGroupInfo info, int flags, 69 int eventTypes) { 70 List<Integer> extraInfos = CarVolumeGroupEvent.convertFlagsToExtraInfo(flags, 71 eventTypes); 72 return convertVolumeChangesToEvents(List.of(info), eventTypes, extraInfos); 73 } 74 convertVolumeChangesToEvents(List<CarVolumeGroupInfo> infoList, int eventTypes, List<Integer> extraInfos)75 static CarVolumeGroupEvent convertVolumeChangesToEvents(List<CarVolumeGroupInfo> infoList, 76 int eventTypes, 77 List<Integer> extraInfos) { 78 return new CarVolumeGroupEvent.Builder(infoList, eventTypes, extraInfos).build(); 79 } 80 81 @Nullable getAudioDeviceInfo(AudioDeviceAttributes audioDeviceAttributes, AudioManagerWrapper audioManager)82 static AudioDeviceInfo getAudioDeviceInfo(AudioDeviceAttributes audioDeviceAttributes, 83 AudioManagerWrapper audioManager) { 84 AudioDeviceInfo[] infos = audioManager.getDevices(GET_DEVICES_OUTPUTS); 85 for (int c = 0; c < infos.length; c++) { 86 if (!infos[c].getAddress().equals(audioDeviceAttributes.getAddress())) { 87 continue; 88 } 89 return infos[c]; 90 } 91 return null; 92 } 93 isDynamicDeviceType(int type)94 static boolean isDynamicDeviceType(int type) { 95 switch (type) { 96 case TYPE_WIRED_HEADSET: // fallthrough 97 case TYPE_WIRED_HEADPHONES: // fallthrough 98 case TYPE_BLUETOOTH_A2DP: // fallthrough 99 case TYPE_HDMI: // fallthrough 100 case TYPE_USB_ACCESSORY: // fallthrough 101 case TYPE_USB_DEVICE: // fallthrough 102 case TYPE_USB_HEADSET: // fallthrough 103 case TYPE_AUX_LINE: // fallthrough 104 case TYPE_BLE_HEADSET: // fallthrough 105 case TYPE_BLE_SPEAKER: // fallthrough 106 case TYPE_BLE_BROADCAST: 107 return true; 108 case TYPE_BUILTIN_SPEAKER: // fallthrough 109 case TYPE_BUS: // fallthrough 110 default: 111 return false; 112 } 113 } 114 getDynamicDevicesInConfig(CarAudioZoneConfigInfo zoneConfig, AudioManagerWrapper manager)115 static List<AudioDeviceInfo> getDynamicDevicesInConfig(CarAudioZoneConfigInfo zoneConfig, 116 AudioManagerWrapper manager) { 117 return Flags.carAudioDynamicDevices() 118 ? getDynamicAudioDevices(zoneConfig.getConfigVolumeGroups(), manager) : EMPTY_LIST; 119 } 120 excludesDynamicDevices(CarAudioZoneConfigInfo zoneConfig)121 static boolean excludesDynamicDevices(CarAudioZoneConfigInfo zoneConfig) { 122 if (!Flags.carAudioDynamicDevices()) { 123 return true; 124 } 125 List<CarVolumeGroupInfo> carVolumeInfos = zoneConfig.getConfigVolumeGroups(); 126 for (int c = 0; c < carVolumeInfos.size(); c++) { 127 if (excludesDynamicDevices(carVolumeInfos.get(c).getAudioDeviceAttributes())) { 128 continue; 129 } 130 return false; 131 } 132 return true; 133 } 134 excludesDynamicDevices(List<AudioDeviceAttributes> devices)135 private static boolean excludesDynamicDevices(List<AudioDeviceAttributes> devices) { 136 for (int c = 0; c < devices.size(); c++) { 137 if (!isDynamicDeviceType(devices.get(c).getType())) { 138 continue; 139 } 140 return false; 141 } 142 return true; 143 } 144 getAudioAttributesForDynamicDevices(CarAudioZoneConfigInfo info)145 static List<AudioAttributes> getAudioAttributesForDynamicDevices(CarAudioZoneConfigInfo info) { 146 List<AudioAttributes> audioAttributes = new ArrayList<>(); 147 if (!Flags.carAudioDynamicDevices()) { 148 return audioAttributes; 149 } 150 List<CarVolumeGroupInfo> groups = info.getConfigVolumeGroups(); 151 for (int c = 0; c < groups.size(); c++) { 152 CarVolumeGroupInfo groupInfo = groups.get(c); 153 if (excludesDynamicDevices(groupInfo.getAudioDeviceAttributes())) { 154 continue; 155 } 156 audioAttributes.addAll(groupInfo.getAudioAttributes()); 157 } 158 return audioAttributes; 159 } 160 getDynamicAudioDevices( List<CarVolumeGroupInfo> volumeGroups, AudioManagerWrapper manager)161 private static List<AudioDeviceInfo> getDynamicAudioDevices( 162 List<CarVolumeGroupInfo> volumeGroups, AudioManagerWrapper manager) { 163 List<AudioDeviceInfo> dynamicDevices = new ArrayList<>(); 164 for (int c = 0; c < volumeGroups.size(); c++) { 165 dynamicDevices.addAll(getDynamicDevices(volumeGroups.get(c), manager)); 166 } 167 return dynamicDevices; 168 } 169 getDynamicDevices(CarVolumeGroupInfo carVolumeGroupInfo, AudioManagerWrapper manager)170 private static List<AudioDeviceInfo> getDynamicDevices(CarVolumeGroupInfo carVolumeGroupInfo, 171 AudioManagerWrapper manager) { 172 List<AudioDeviceInfo> dynamicDevices = new ArrayList<>(); 173 List<AudioDeviceAttributes> devices = carVolumeGroupInfo.getAudioDeviceAttributes(); 174 for (int c = 0; c < devices.size(); c++) { 175 if (!isDynamicDeviceType(devices.get(c).getType())) { 176 continue; 177 } 178 AudioDeviceInfo info = getAudioDeviceInfo(devices.get(c), manager); 179 if (info == null) { 180 continue; 181 } 182 dynamicDevices.add(info); 183 } 184 return dynamicDevices; 185 } 186 } 187