• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 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.server.audio;
18 
19 import static android.media.AudioManager.GET_DEVICES_INPUTS;
20 
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.content.ContentResolver;
24 import android.media.AudioDeviceAttributes;
25 import android.media.AudioDeviceInfo;
26 import android.media.AudioSystem;
27 import android.os.UserHandle;
28 import android.util.IntArray;
29 import android.util.Log;
30 import android.util.SparseIntArray;
31 
32 import java.util.HashSet;
33 import java.util.Set;
34 
35 /** Maintains the current state of input gains. */
36 /*package*/ class InputDeviceVolumeHelper {
37     private static final String TAG = "InputDeviceVolumeHelper";
38 
39     // TODO(b/364923030): retrieve these constants from AudioPolicyManager.
40     private final int INDEX_MIN = 0;
41     private final int INDEX_MAX = 100;
42     private final int INDEX_DEFAULT = 50;
43 
44     private final SettingsAdapter mSettings;
45     private final ContentResolver mContentResolver;
46     private final String mInputGainIndexSettingsName;
47 
48     // A map between device internal type (e.g. AudioSystem.DEVICE_IN_BUILTIN_MIC) to its input gain
49     // index.
50     private final SparseIntArray mInputGainIndexMap;
51     private final Set<Integer> mSupportedDeviceTypes = new HashSet<>();
52 
InputDeviceVolumeHelper( SettingsAdapter settings, ContentResolver contentResolver, String settingsName)53     InputDeviceVolumeHelper(
54             SettingsAdapter settings,
55             ContentResolver contentResolver,
56             String settingsName) {
57         mSettings = settings;
58         mContentResolver = contentResolver;
59         mInputGainIndexSettingsName = settingsName;
60 
61         IntArray internalDeviceTypes = new IntArray();
62         int status = AudioSystem.getSupportedDeviceTypes(GET_DEVICES_INPUTS, internalDeviceTypes);
63         if (status != AudioSystem.SUCCESS) {
64             Log.e(TAG, "AudioSystem.getSupportedDeviceTypes(GET_DEVICES_INPUTS) failed. status:"
65                     + status);
66         }
67 
68         // Note that in a rare case, if AudioSystem.getSupportedDeviceTypes call fails, both
69         // mInputGainIndexMap and mSupportedDeviceTypes will be empty.
70         mInputGainIndexMap = new SparseIntArray(internalDeviceTypes.size());
71         for (int i = 0; i < internalDeviceTypes.size(); i++) {
72             mSupportedDeviceTypes.add(internalDeviceTypes.get(i));
73         }
74 
75         readSettings();
76     }
77 
readSettings()78     private void readSettings() {
79         synchronized (InputDeviceVolumeHelper.class) {
80             for (int inputDeviceType : mSupportedDeviceTypes) {
81                 // Retrieve current input gain for device. If no input gain stored for current
82                 // device, use default input gain.
83                 String name = getSettingNameForDevice(inputDeviceType);
84                 int index = name == null
85                         ? INDEX_DEFAULT
86                         : mSettings.getSystemIntForUser(
87                                 mContentResolver, name, INDEX_DEFAULT, UserHandle.USER_CURRENT);
88 
89                 mInputGainIndexMap.put(inputDeviceType, getValidIndex(index));
90             }
91         }
92     }
93 
getSettingNameForDevice(int inputDeviceType)94     private @Nullable String getSettingNameForDevice(int inputDeviceType) {
95         if (mInputGainIndexSettingsName == null || mInputGainIndexSettingsName.isEmpty()) {
96             return null;
97         }
98 
99         final String suffix = AudioSystem.getInputDeviceName(inputDeviceType);
100         if (suffix.isEmpty()) {
101             return mInputGainIndexSettingsName;
102         }
103         return mInputGainIndexSettingsName + "_" + suffix;
104     }
105 
getValidIndex(int index)106     private int getValidIndex(int index) {
107         if (index < INDEX_MIN) {
108             return INDEX_MIN;
109         }
110         if (index > INDEX_MAX) {
111             return INDEX_MAX;
112         }
113         return index;
114     }
115 
getInputGainIndex(@onNull AudioDeviceAttributes ada)116     public int getInputGainIndex(@NonNull AudioDeviceAttributes ada) {
117         int inputDeviceType = AudioDeviceInfo.convertDeviceTypeToInternalInputDevice(ada.getType());
118         ensureValidInputDeviceType(inputDeviceType);
119 
120         synchronized (InputDeviceVolumeHelper.class) {
121             return mInputGainIndexMap.get(inputDeviceType, INDEX_DEFAULT);
122         }
123     }
124 
getMaxInputGainIndex()125     public int getMaxInputGainIndex() {
126         return INDEX_MAX;
127     }
128 
getMinInputGainIndex()129     public int getMinInputGainIndex() {
130         return INDEX_MIN;
131     }
132 
isInputGainFixed(@onNull AudioDeviceAttributes ada)133     public boolean isInputGainFixed(@NonNull AudioDeviceAttributes ada) {
134         int inputDeviceType = AudioDeviceInfo.convertDeviceTypeToInternalInputDevice(ada.getType());
135         ensureValidInputDeviceType(inputDeviceType);
136 
137         // For simplicity, all devices have non fixed input gain. This might change
138         // when more input devices are supported and some do not support input gain control.
139         return false;
140     }
141 
setInputGainIndex(@onNull AudioDeviceAttributes ada, int index)142     public boolean setInputGainIndex(@NonNull AudioDeviceAttributes ada, int index) {
143         int inputDeviceType = AudioDeviceInfo.convertDeviceTypeToInternalInputDevice(ada.getType());
144         ensureValidInputDeviceType(inputDeviceType);
145 
146         int oldIndex;
147         synchronized (InputDeviceVolumeHelper.class) {
148             oldIndex = getInputGainIndex(ada);
149             index = getValidIndex(index);
150 
151             if (oldIndex == index) {
152                 return false;
153             }
154 
155             mInputGainIndexMap.put(inputDeviceType, index);
156             return true;
157         }
158     }
159 
persistInputGainIndex(@onNull AudioDeviceAttributes ada)160     public void persistInputGainIndex(@NonNull AudioDeviceAttributes ada) {
161         int inputDeviceType = AudioDeviceInfo.convertDeviceTypeToInternalInputDevice(ada.getType());
162         String name = getSettingNameForDevice(inputDeviceType);
163         if (name != null) {
164             int index = getInputGainIndex(ada);
165             mSettings.putSystemIntForUser(
166                     mContentResolver,
167                     name,
168                     index,
169                     UserHandle.USER_CURRENT);
170         }
171     }
172 
isValidInputDeviceType(int inputDeviceType)173     public boolean isValidInputDeviceType(int inputDeviceType) {
174         return mSupportedDeviceTypes.contains(inputDeviceType);
175     }
176 
ensureValidInputDeviceType(int inputDeviceType)177     private void ensureValidInputDeviceType(int inputDeviceType) {
178         if (!isValidInputDeviceType(inputDeviceType)) {
179             throw new IllegalArgumentException("Bad input device type " + inputDeviceType);
180         }
181     }
182 }
183