• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.car.media.CarAudioManager;
22 import android.content.ContentResolver;
23 import android.content.Context;
24 import android.hardware.automotive.audiocontrol.V1_0.ContextNumber;
25 import android.media.AudioDevicePort;
26 import android.provider.Settings;
27 import android.util.SparseArray;
28 import android.util.SparseIntArray;
29 
30 import com.android.internal.util.Preconditions;
31 
32 import java.io.PrintWriter;
33 import java.util.Arrays;
34 
35 /**
36  * A class encapsulates a volume group in car.
37  *
38  * Volume in a car is controlled by group. A group holds one or more car audio contexts.
39  * Call {@link CarAudioManager#getVolumeGroupCount()} to get the count of {@link CarVolumeGroup}
40  * supported in a car.
41  */
42 /* package */ final class CarVolumeGroup {
43 
44     private final ContentResolver mContentResolver;
45     private final int mId;
46     private final int[] mContexts;
47     private final SparseIntArray mContextToBus = new SparseIntArray();
48     private final SparseArray<CarAudioDeviceInfo> mBusToCarAudioDeviceInfos = new SparseArray<>();
49 
50     private int mDefaultGain = Integer.MIN_VALUE;
51     private int mMaxGain = Integer.MIN_VALUE;
52     private int mMinGain = Integer.MAX_VALUE;
53     private int mStepSize = 0;
54     private int mStoredGainIndex;
55     private int mCurrentGainIndex = -1;
56 
CarVolumeGroup(Context context, int id, @NonNull int[] contexts)57     CarVolumeGroup(Context context, int id, @NonNull int[] contexts) {
58         mContentResolver = context.getContentResolver();
59         mId = id;
60         mContexts = contexts;
61 
62         mStoredGainIndex = Settings.Global.getInt(mContentResolver,
63                 CarAudioManager.getVolumeSettingsKeyForGroup(mId), -1);;
64     }
65 
getId()66     int getId() {
67         return mId;
68     }
69 
getContexts()70     int[] getContexts() {
71         return mContexts;
72     }
73 
getBusNumbers()74     int[] getBusNumbers() {
75         final int[] busNumbers = new int[mBusToCarAudioDeviceInfos.size()];
76         for (int i = 0; i < busNumbers.length; i++) {
77             busNumbers[i] = mBusToCarAudioDeviceInfos.keyAt(i);
78         }
79         return busNumbers;
80     }
81 
82     /**
83      * Binds the context number to physical bus number and audio device port information.
84      * Because this may change the groups min/max values, thus invalidating an index computed from
85      * a gain before this call, all calls to this function must happen at startup before any
86      * set/getGainIndex calls.
87      *
88      * @param contextNumber Context number as defined in audio control HAL
89      * @param busNumber Physical bus number for the audio device port
90      * @param info {@link CarAudioDeviceInfo} instance relates to the physical bus
91      */
bind(int contextNumber, int busNumber, CarAudioDeviceInfo info)92     void bind(int contextNumber, int busNumber, CarAudioDeviceInfo info) {
93         if (mBusToCarAudioDeviceInfos.size() == 0) {
94             mStepSize = info.getAudioGain().stepValue();
95         } else {
96             Preconditions.checkArgument(
97                     info.getAudioGain().stepValue() == mStepSize,
98                     "Gain controls within one group must have same step value");
99         }
100 
101         mContextToBus.put(contextNumber, busNumber);
102         mBusToCarAudioDeviceInfos.put(busNumber, info);
103 
104         if (info.getDefaultGain() > mDefaultGain) {
105             // We're arbitrarily selecting the highest bus default gain as the group's default.
106             mDefaultGain = info.getDefaultGain();
107         }
108         if (info.getMaxGain() > mMaxGain) {
109             mMaxGain = info.getMaxGain();
110         }
111         if (info.getMinGain() < mMinGain) {
112             mMinGain = info.getMinGain();
113         }
114         if (mStoredGainIndex < getMinGainIndex() || mStoredGainIndex > getMaxGainIndex()) {
115             // We expected to load a value from last boot, but if we didn't (perhaps this is the
116             // first boot ever?), then use the highest "default" we've seen to initialize
117             // ourselves.
118             mCurrentGainIndex = getIndexForGain(mDefaultGain);
119         } else {
120             // Just use the gain index we stored last time the gain was set (presumably during our
121             // last boot cycle).
122             mCurrentGainIndex = mStoredGainIndex;
123         }
124     }
125 
getDefaultGainIndex()126     int getDefaultGainIndex() {
127         return getIndexForGain(mDefaultGain);
128     }
129 
getMaxGainIndex()130     int getMaxGainIndex() {
131         return getIndexForGain(mMaxGain);
132     }
133 
getMinGainIndex()134     int getMinGainIndex() {
135         return getIndexForGain(mMinGain);
136     }
137 
getCurrentGainIndex()138     int getCurrentGainIndex() {
139         return mCurrentGainIndex;
140     }
141 
setCurrentGainIndex(int gainIndex)142     void setCurrentGainIndex(int gainIndex) {
143         int gainInMillibels = getGainForIndex(gainIndex);
144 
145         Preconditions.checkArgument(
146                 gainInMillibels >= mMinGain && gainInMillibels <= mMaxGain,
147                 "Gain out of range (" +
148                         mMinGain + ":" +
149                         mMaxGain +") " +
150                         gainInMillibels + "index " +
151                         gainIndex);
152 
153         for (int i = 0; i < mBusToCarAudioDeviceInfos.size(); i++) {
154             CarAudioDeviceInfo info = mBusToCarAudioDeviceInfos.valueAt(i);
155             info.setCurrentGain(gainInMillibels);
156         }
157 
158         mCurrentGainIndex = gainIndex;
159         Settings.Global.putInt(mContentResolver,
160                 CarAudioManager.getVolumeSettingsKeyForGroup(mId), gainIndex);
161     }
162 
163     // Given a group level gain index, return the computed gain in millibells
164     // TODO (randolphs) If we ever want to add index to gain curves other than lock-stepped
165     // linear, this would be the place to do it.
getGainForIndex(int gainIndex)166     private int getGainForIndex(int gainIndex) {
167         return mMinGain + gainIndex * mStepSize;
168     }
169 
170     // TODO (randolphs) if we ever went to a non-linear index to gain curve mapping, we'd need to
171     // revisit this as it assumes (at the least) that getGainForIndex is reversible.  Luckily,
172     // this is an internal implementation details we could factor out if/when necessary.
getIndexForGain(int gainInMillibel)173     private int getIndexForGain(int gainInMillibel) {
174         return (gainInMillibel - mMinGain) / mStepSize;
175     }
176 
177     @Nullable
getAudioDevicePortForContext(int contextNumber)178     AudioDevicePort getAudioDevicePortForContext(int contextNumber) {
179         final int busNumber = mContextToBus.get(contextNumber, -1);
180         if (busNumber < 0 || mBusToCarAudioDeviceInfos.get(busNumber) == null) {
181             return null;
182         }
183         return mBusToCarAudioDeviceInfos.get(busNumber).getAudioDevicePort();
184     }
185 
186     @Override
toString()187     public String toString() {
188         return "CarVolumeGroup id: " + mId
189                 + " currentGainIndex: " + mCurrentGainIndex
190                 + " contexts: " + Arrays.toString(mContexts)
191                 + " buses: " + Arrays.toString(getBusNumbers());
192     }
193 
dump(PrintWriter writer)194     void dump(PrintWriter writer) {
195         writer.println("CarVolumeGroup " + mId);
196         writer.printf("\tGain in millibel (min / max / default/ current): %d %d %d %d\n",
197                 mMinGain, mMaxGain, mDefaultGain, getGainForIndex(mCurrentGainIndex));
198         writer.printf("\tGain in index (min / max / default / current): %d %d %d %d\n",
199                 getMinGainIndex(), getMaxGainIndex(), getDefaultGainIndex(), mCurrentGainIndex);
200         for (int i = 0; i < mContextToBus.size(); i++) {
201             writer.printf("\tContext: %s -> Bus: %d\n",
202                     ContextNumber.toString(mContextToBus.keyAt(i)), mContextToBus.valueAt(i));
203         }
204         for (int i = 0; i < mBusToCarAudioDeviceInfos.size(); i++) {
205             mBusToCarAudioDeviceInfos.valueAt(i).dump(writer);
206         }
207         // Empty line for comfortable reading
208         writer.println();
209     }
210 }
211