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