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