133707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang/*
233707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang * Copyright (C) 2018 The Android Open Source Project
333707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang *
433707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang * Licensed under the Apache License, Version 2.0 (the "License");
533707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang * you may not use this file except in compliance with the License.
633707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang * You may obtain a copy of the License at
733707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang *
833707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang *      http://www.apache.org/licenses/LICENSE-2.0
933707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang *
1033707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang * Unless required by applicable law or agreed to in writing, software
1133707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang * distributed under the License is distributed on an "AS IS" BASIS,
1233707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1333707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang * See the License for the specific language governing permissions and
1433707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang * limitations under the License.
1533707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang */
1633707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wangpackage com.android.car;
1733707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang
1833707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wangimport android.media.AudioDeviceInfo;
1933707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wangimport android.media.AudioDevicePort;
201e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wangimport android.media.AudioFormat;
2133707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wangimport android.media.AudioGain;
2233707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wangimport android.media.AudioGainConfig;
2333707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wangimport android.media.AudioManager;
2433707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wangimport android.media.AudioPort;
2533707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wangimport android.util.Log;
2633707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang
2733707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wangimport com.android.internal.util.Preconditions;
2833707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang
293302a6b200fdacd5a23dae114b38581937af231fHongwei Wangimport java.io.PrintWriter;
303302a6b200fdacd5a23dae114b38581937af231fHongwei Wang
3133707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang/**
32dec25bcbd97a57879e9dd6fbda9d53a122dd1023Scott Randolph * A helper class wraps {@link AudioDeviceInfo}, and helps get/set the gain on a specific port
33dec25bcbd97a57879e9dd6fbda9d53a122dd1023Scott Randolph * in terms of millibels.
341e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang * Note to the reader. For whatever reason, it seems that AudioGain contains only configuration
351e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang * information (min/max/step, etc) while the AudioGainConfig class contains the
36dec25bcbd97a57879e9dd6fbda9d53a122dd1023Scott Randolph * actual currently active gain value(s).
3733707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang */
3833707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang/* package */ class CarAudioDeviceInfo {
3933707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang
4033707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang    private final AudioDeviceInfo mAudioDeviceInfo;
4133707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang    private final int mBusNumber;
4233707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang    private final int mSampleRate;
431e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang    private final int mEncodingFormat;
4433707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang    private final int mChannelCount;
45dec25bcbd97a57879e9dd6fbda9d53a122dd1023Scott Randolph    private final int mDefaultGain;
46dec25bcbd97a57879e9dd6fbda9d53a122dd1023Scott Randolph    private final int mMaxGain;
47dec25bcbd97a57879e9dd6fbda9d53a122dd1023Scott Randolph    private final int mMinGain;
4833707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang
491e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang    /**
501e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang     * We need to store the current gain because it is not accessible from the current
511e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang     * audio engine implementation. It would be nice if AudioPort#activeConfig() would return it,
521e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang     * but in the current implementation, that function actually works only for mixer ports.
531e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang     */
541e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang    private int mCurrentGain;
551e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang
56bd51582556b558a8d371f054743c10cd1fd370cfHongwei Wang    CarAudioDeviceInfo(AudioDeviceInfo audioDeviceInfo) {
5733707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang        mAudioDeviceInfo = audioDeviceInfo;
58dec25bcbd97a57879e9dd6fbda9d53a122dd1023Scott Randolph        mBusNumber = parseDeviceAddress(audioDeviceInfo.getAddress());
5933707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang        mSampleRate = getMaxSampleRate(audioDeviceInfo);
601e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang        mEncodingFormat = getEncodingFormat(audioDeviceInfo);
6133707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang        mChannelCount = getMaxChannels(audioDeviceInfo);
6233707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang        final AudioGain audioGain = Preconditions.checkNotNull(
6333707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang                getAudioGain(), "No audio gain on device port " + audioDeviceInfo);
64dec25bcbd97a57879e9dd6fbda9d53a122dd1023Scott Randolph        mDefaultGain = audioGain.defaultValue();
65dec25bcbd97a57879e9dd6fbda9d53a122dd1023Scott Randolph        mMaxGain = audioGain.maxValue();
66dec25bcbd97a57879e9dd6fbda9d53a122dd1023Scott Randolph        mMinGain = audioGain.minValue();
671e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang
681e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang        mCurrentGain = -1; // Not initialized till explicitly set
6933707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang    }
7033707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang
7133707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang    AudioDeviceInfo getAudioDeviceInfo() {
7233707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang        return mAudioDeviceInfo;
7333707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang    }
7433707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang
7533707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang    AudioDevicePort getAudioDevicePort() {
7633707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang        return mAudioDeviceInfo.getPort();
7733707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang    }
7833707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang
7933707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang    int getBusNumber() {
8033707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang        return mBusNumber;
8133707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang    }
8233707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang
83dec25bcbd97a57879e9dd6fbda9d53a122dd1023Scott Randolph    int getDefaultGain() {
84dec25bcbd97a57879e9dd6fbda9d53a122dd1023Scott Randolph        return mDefaultGain;
85dec25bcbd97a57879e9dd6fbda9d53a122dd1023Scott Randolph    }
86dec25bcbd97a57879e9dd6fbda9d53a122dd1023Scott Randolph
87dec25bcbd97a57879e9dd6fbda9d53a122dd1023Scott Randolph    int getMaxGain() {
88dec25bcbd97a57879e9dd6fbda9d53a122dd1023Scott Randolph        return mMaxGain;
8933707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang    }
9033707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang
91dec25bcbd97a57879e9dd6fbda9d53a122dd1023Scott Randolph    int getMinGain() {
92dec25bcbd97a57879e9dd6fbda9d53a122dd1023Scott Randolph        return mMinGain;
9333707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang    }
9433707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang
9533707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang    int getSampleRate() {
9633707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang        return mSampleRate;
9733707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang    }
9833707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang
991e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang    int getEncodingFormat() {
1001e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang        return mEncodingFormat;
1011e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang    }
1021e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang
10333707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang    int getChannelCount() {
10433707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang        return mChannelCount;
10533707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang    }
10633707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang
107dec25bcbd97a57879e9dd6fbda9d53a122dd1023Scott Randolph    // Input is in millibels
108dec25bcbd97a57879e9dd6fbda9d53a122dd1023Scott Randolph    void setCurrentGain(int gainInMillibels) {
109dec25bcbd97a57879e9dd6fbda9d53a122dd1023Scott Randolph        // Clamp the incoming value to our valid range.  Out of range values ARE legal input
110dec25bcbd97a57879e9dd6fbda9d53a122dd1023Scott Randolph        if (gainInMillibels < mMinGain) {
111dec25bcbd97a57879e9dd6fbda9d53a122dd1023Scott Randolph            gainInMillibels = mMinGain;
112dec25bcbd97a57879e9dd6fbda9d53a122dd1023Scott Randolph        } else if (gainInMillibels > mMaxGain) {
113dec25bcbd97a57879e9dd6fbda9d53a122dd1023Scott Randolph            gainInMillibels = mMaxGain;
114dec25bcbd97a57879e9dd6fbda9d53a122dd1023Scott Randolph        }
11533707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang
116dec25bcbd97a57879e9dd6fbda9d53a122dd1023Scott Randolph        // Push the new gain value down to our underlying port which will cause it to show up
117dec25bcbd97a57879e9dd6fbda9d53a122dd1023Scott Randolph        // at the HAL.
11833707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang        AudioGain audioGain = getAudioGain();
119dec25bcbd97a57879e9dd6fbda9d53a122dd1023Scott Randolph        if (audioGain == null) {
120dec25bcbd97a57879e9dd6fbda9d53a122dd1023Scott Randolph            Log.e(CarLog.TAG_AUDIO, "getAudioGain() returned null.");
1211e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang            return;
1221e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang        }
1231e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang
1241e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang        // size of gain values is 1 in MODE_JOINT
1251e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang        AudioGainConfig audioGainConfig = audioGain.buildConfig(
1261e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang                AudioGain.MODE_JOINT,
1271e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang                audioGain.channelMask(),
1281e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang                new int[] { gainInMillibels },
1291e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang                0);
1301e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang        if (audioGainConfig == null) {
1311e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang            Log.e(CarLog.TAG_AUDIO, "Failed to construct AudioGainConfig");
1321e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang            return;
1331e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang        }
1341e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang
1351e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang        int r = AudioManager.setAudioPortGain(getAudioDevicePort(), audioGainConfig);
1361e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang        if (r == AudioManager.SUCCESS) {
1371e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang            // Since we can't query for the gain on a device port later,
1381e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang            // we have to remember what we asked for
1391e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang            mCurrentGain = gainInMillibels;
140dec25bcbd97a57879e9dd6fbda9d53a122dd1023Scott Randolph        } else {
1411e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang            Log.e(CarLog.TAG_AUDIO, "Failed to setAudioPortGain: " + r);
14233707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang        }
14333707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang    }
14433707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang
14533707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang    /**
14633707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang     * Parse device address. Expected format is BUS%d_%s, address, usage hint
14733707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang     * @return valid address (from 0 to positive) or -1 for invalid address.
14833707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang     */
14933707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang    private int parseDeviceAddress(String address) {
15033707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang        String[] words = address.split("_");
15133707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang        int addressParsed = -1;
15233707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang        if (words[0].toLowerCase().startsWith("bus")) {
15333707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang            try {
15433707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang                addressParsed = Integer.parseInt(words[0].substring(3));
15533707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang            } catch (NumberFormatException e) {
15633707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang                //ignore
15733707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang            }
15833707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang        }
15933707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang        if (addressParsed < 0) {
16033707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang            return -1;
16133707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang        }
16233707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang        return addressParsed;
16333707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang    }
16433707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang
16533707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang    private int getMaxSampleRate(AudioDeviceInfo info) {
16633707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang        int[] sampleRates = info.getSampleRates();
16733707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang        if (sampleRates == null || sampleRates.length == 0) {
16833707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang            return 48000;
16933707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang        }
17033707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang        int sampleRate = sampleRates[0];
17133707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang        for (int i = 1; i < sampleRates.length; i++) {
17233707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang            if (sampleRates[i] > sampleRate) {
17333707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang                sampleRate = sampleRates[i];
17433707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang            }
17533707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang        }
17633707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang        return sampleRate;
17733707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang    }
17833707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang
1791e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang    /** Always returns {@link AudioFormat#ENCODING_PCM_16BIT} as for now */
1801e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang    private int getEncodingFormat(AudioDeviceInfo info) {
1811e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang        return AudioFormat.ENCODING_PCM_16BIT;
1821e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang    }
1831e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang
184ff9b36b176de502db4c8ccc5b90423f0a095e3cbHongwei Wang    /**
185ff9b36b176de502db4c8ccc5b90423f0a095e3cbHongwei Wang     * Gets the maximum channel count for a given {@link AudioDeviceInfo}
186ff9b36b176de502db4c8ccc5b90423f0a095e3cbHongwei Wang     *
187ff9b36b176de502db4c8ccc5b90423f0a095e3cbHongwei Wang     * @param info {@link AudioDeviceInfo} instance to get maximum channel count for
188ff9b36b176de502db4c8ccc5b90423f0a095e3cbHongwei Wang     * @return Maximum channel count for a given {@link AudioDeviceInfo},
189ff9b36b176de502db4c8ccc5b90423f0a095e3cbHongwei Wang     * 1 (mono) if there is no channel masks configured
190ff9b36b176de502db4c8ccc5b90423f0a095e3cbHongwei Wang     */
19133707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang    private int getMaxChannels(AudioDeviceInfo info) {
192ff9b36b176de502db4c8ccc5b90423f0a095e3cbHongwei Wang        int numChannels = 1;
19333707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang        int[] channelMasks = info.getChannelMasks();
19433707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang        if (channelMasks == null) {
195ff9b36b176de502db4c8ccc5b90423f0a095e3cbHongwei Wang            return numChannels;
19633707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang        }
19733707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang        for (int channelMask : channelMasks) {
19833707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang            int currentNumChannels = Integer.bitCount(channelMask);
19933707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang            if (currentNumChannels > numChannels) {
20033707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang                numChannels = currentNumChannels;
20133707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang            }
20233707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang        }
203ff9b36b176de502db4c8ccc5b90423f0a095e3cbHongwei Wang        return numChannels;
20433707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang    }
20533707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang
20633707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang    /**
207dec25bcbd97a57879e9dd6fbda9d53a122dd1023Scott Randolph     * @return {@link AudioGain} with {@link AudioGain#MODE_JOINT} on a given {@link AudioPort}.
208dec25bcbd97a57879e9dd6fbda9d53a122dd1023Scott Randolph     * This is useful for inspecting the configuration data associated with this gain controller
209dec25bcbd97a57879e9dd6fbda9d53a122dd1023Scott Randolph     * (min/max/step/default).
21033707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang     */
21133707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang    AudioGain getAudioGain() {
21233707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang        final AudioDevicePort audioPort = getAudioDevicePort();
21333707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang        if (audioPort != null && audioPort.gains().length > 0) {
21433707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang            for (AudioGain audioGain : audioPort.gains()) {
21533707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang                if ((audioGain.mode() & AudioGain.MODE_JOINT) != 0) {
21633707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang                    return checkAudioGainConfiguration(audioGain);
21733707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang                }
21833707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang            }
21933707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang        }
22033707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang        return null;
22133707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang    }
22233707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang
22333707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang    /**
22433707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang     * Constraints applied to gain configuration, see also audio_policy_configuration.xml
22533707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang     */
22633707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang    private AudioGain checkAudioGainConfiguration(AudioGain audioGain) {
22733707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang        Preconditions.checkArgument(audioGain.maxValue() >= audioGain.minValue());
22833707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang        Preconditions.checkArgument((audioGain.defaultValue() >= audioGain.minValue())
22933707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang                && (audioGain.defaultValue() <= audioGain.maxValue()));
23033707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang        Preconditions.checkArgument(
23133707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang                ((audioGain.maxValue() - audioGain.minValue()) % audioGain.stepValue()) == 0);
23233707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang        Preconditions.checkArgument(
23333707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang                ((audioGain.defaultValue() - audioGain.minValue()) % audioGain.stepValue()) == 0);
23433707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang        return audioGain;
23533707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang    }
23633707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang
23733707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang    @Override
23833707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang    public String toString() {
2391e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang        return "bus number: " + mBusNumber
2401e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang                + " address: " + mAudioDeviceInfo.getAddress()
2413302a6b200fdacd5a23dae114b38581937af231fHongwei Wang                + " sampleRate: " + getSampleRate()
2421e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang                + " encodingFormat: " + getEncodingFormat()
2433302a6b200fdacd5a23dae114b38581937af231fHongwei Wang                + " channelCount: " + getChannelCount()
2441e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang                + " currentGain: " + mCurrentGain
245dec25bcbd97a57879e9dd6fbda9d53a122dd1023Scott Randolph                + " maxGain: " + mMaxGain
246dec25bcbd97a57879e9dd6fbda9d53a122dd1023Scott Randolph                + " minGain: " + mMinGain;
24733707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang    }
2483302a6b200fdacd5a23dae114b38581937af231fHongwei Wang
2493302a6b200fdacd5a23dae114b38581937af231fHongwei Wang    void dump(PrintWriter writer) {
2501e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang        writer.printf("Bus Number (%d) / address (%s)\n ",
2511e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang                mBusNumber, mAudioDeviceInfo.getAddress());
2521e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang        writer.printf("\tsample rate / encoding format / channel count: %d %d %d\n",
2531e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang                getSampleRate(), getEncodingFormat(), getChannelCount());
2543302a6b200fdacd5a23dae114b38581937af231fHongwei Wang        writer.printf("\tGain in millibel (min / max / default/ current): %d %d %d %d\n",
2551e7f55cff6d393e66965e17b59804d23ff9ab23bHongwei Wang                mMinGain, mMaxGain, mDefaultGain, mCurrentGain);
2563302a6b200fdacd5a23dae114b38581937af231fHongwei Wang    }
25733707a9d6e5beb03c5ae010e661035f820cc4bb8Hongwei Wang}
258