1 /*
2  * Copyright (C) 2019 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.settings.development.bluetooth;
18 
19 import android.bluetooth.BluetoothA2dp;
20 import android.bluetooth.BluetoothCodecConfig;
21 import android.bluetooth.BluetoothDevice;
22 import android.content.Context;
23 import android.util.Log;
24 
25 import androidx.annotation.Nullable;
26 import androidx.annotation.VisibleForTesting;
27 import androidx.preference.PreferenceScreen;
28 
29 import com.android.settings.development.BluetoothA2dpConfigStore;
30 import com.android.settings.development.Flags;
31 import com.android.settingslib.core.lifecycle.Lifecycle;
32 
33 import java.util.ArrayList;
34 import java.util.List;
35 
36 /**
37  * Dialog preference controller to set the Bluetooth A2DP config of codec
38  */
39 public class BluetoothCodecDialogPreferenceController extends
40         AbstractBluetoothDialogPreferenceController {
41 
42     private static final String KEY = "bluetooth_audio_codec_settings";
43     private static final String TAG = "BtCodecCtr";
44 
45     @Nullable private final Callback mCallback;
46 
BluetoothCodecDialogPreferenceController(Context context, Lifecycle lifecycle, BluetoothA2dpConfigStore store, @Nullable Callback callback)47     public BluetoothCodecDialogPreferenceController(Context context, Lifecycle lifecycle,
48                                                     BluetoothA2dpConfigStore store,
49                                                     @Nullable Callback callback) {
50         super(context, lifecycle, store);
51         mCallback = callback;
52     }
53 
54     @Override
isAvailable()55     public boolean isAvailable() {
56         return !Flags.a2dpOffloadCodecExtensibilitySettings();
57     }
58 
59     @Override
getPreferenceKey()60     public String getPreferenceKey() {
61         return KEY;
62     }
63 
64     @Override
displayPreference(PreferenceScreen screen)65     public void displayPreference(PreferenceScreen screen) {
66         super.displayPreference(screen);
67         ((BaseBluetoothDialogPreference) mPreference).setCallback(this);
68     }
69 
70     @Override
getSelectableIndex()71     public List<Integer> getSelectableIndex() {
72         List<Integer> index = new ArrayList<>();
73         final BluetoothA2dp bluetoothA2dp = mBluetoothA2dp;
74 
75         index.add(getDefaultIndex());
76         if (bluetoothA2dp == null) {
77             return index;
78         }
79         final BluetoothDevice activeDevice = getA2dpActiveDevice();
80         if (activeDevice == null) {
81             Log.d(TAG, "Unable to get selectable index. No Active Bluetooth device");
82             return index;
83         }
84         // Check HD audio is enabled, display the available list.
85         if (bluetoothA2dp.isOptionalCodecsEnabled(activeDevice)
86                 == BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED) {
87             List<BluetoothCodecConfig> configs = getSelectableConfigs(activeDevice);
88             if (configs != null) {
89                 return getIndexFromConfig(configs);
90             }
91         }
92         // If HD audio is disabled, SBC is the only one available codec.
93         index.add(convertCfgToBtnIndex(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC));
94         return index;
95     }
96 
97     @Override
writeConfigurationValues(final int index)98     protected void writeConfigurationValues(final int index) {
99         int codecTypeValue = BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC; // default
100         int codecPriorityValue = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT;
101         switch (index) {
102             case 0:
103                 final BluetoothDevice activeDevice = getA2dpActiveDevice();
104                 codecTypeValue = getHighestCodec(mBluetoothA2dp, activeDevice,
105                         getSelectableConfigs(activeDevice));
106                 codecPriorityValue = BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST;
107                 break;
108             case 1:
109                 codecTypeValue = BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC;
110                 codecPriorityValue = BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST;
111                 break;
112             case 2:
113                 codecTypeValue = BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC;
114                 codecPriorityValue = BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST;
115                 break;
116             case 3:
117                 codecTypeValue = BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX;
118                 codecPriorityValue = BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST;
119                 break;
120             case 4:
121                 codecTypeValue = BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD;
122                 codecPriorityValue = BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST;
123                 break;
124             case 5:
125                 codecTypeValue = BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC;
126                 codecPriorityValue = BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST;
127                 break;
128             case 6:
129                 codecTypeValue = BluetoothCodecConfig.SOURCE_CODEC_TYPE_LC3;
130                 codecPriorityValue = BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST;
131                 break;
132             case 7:
133                 codecTypeValue = BluetoothCodecConfig.SOURCE_CODEC_TYPE_OPUS;
134                 codecPriorityValue = BluetoothCodecConfig.CODEC_PRIORITY_HIGHEST;
135                 break;
136             default:
137                 break;
138         }
139         mBluetoothA2dpConfigStore.setCodecType(codecTypeValue);
140         mBluetoothA2dpConfigStore.setCodecPriority(codecPriorityValue);
141 
142         // Once user changes codec, to reset configs with highest quality.
143         final BluetoothCodecConfig config = getSelectableByCodecType(codecTypeValue);
144         if (config == null) {
145             Log.d(TAG, "Selectable config is null. Unable to reset");
146         }
147         mBluetoothA2dpConfigStore.setSampleRate(getHighestSampleRate(config));
148         mBluetoothA2dpConfigStore.setBitsPerSample(getHighestBitsPerSample(config));
149         mBluetoothA2dpConfigStore.setChannelMode(getHighestChannelMode(config));
150     }
151 
152     @Override
getCurrentIndexByConfig(BluetoothCodecConfig config)153     protected int getCurrentIndexByConfig(BluetoothCodecConfig config) {
154         if (config == null) {
155             Log.e(TAG, "Unable to get current config index. Config is null.");
156         }
157         return convertCfgToBtnIndex(config.getCodecType());
158     }
159 
160     @Override
onIndexUpdated(int index)161     public void onIndexUpdated(int index) {
162         super.onIndexUpdated(index);
163         mCallback.onBluetoothCodecChanged();
164     }
165 
166     @Override
onHDAudioEnabled(boolean enabled)167     public void onHDAudioEnabled(boolean enabled) {
168         writeConfigurationValues(/* index= */ 0);
169     }
170 
getIndexFromConfig(List<BluetoothCodecConfig> configs)171     private List<Integer> getIndexFromConfig(List<BluetoothCodecConfig> configs) {
172         List<Integer> indexArray = new ArrayList<>();
173         for (BluetoothCodecConfig config : configs) {
174             indexArray.add(convertCfgToBtnIndex(config.getCodecType()));
175         }
176         return indexArray;
177     }
178 
179     @VisibleForTesting
convertCfgToBtnIndex(int config)180     int convertCfgToBtnIndex(int config) {
181         int index = getDefaultIndex();
182         switch (config) {
183             case BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC:
184                 index = 1;
185                 break;
186             case BluetoothCodecConfig.SOURCE_CODEC_TYPE_AAC:
187                 index = 2;
188                 break;
189             case BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX:
190                 index = 3;
191                 break;
192             case BluetoothCodecConfig.SOURCE_CODEC_TYPE_APTX_HD:
193                 index = 4;
194                 break;
195             case BluetoothCodecConfig.SOURCE_CODEC_TYPE_LDAC:
196                 index = 5;
197                 break;
198             case BluetoothCodecConfig.SOURCE_CODEC_TYPE_OPUS:
199                 index = 7;
200                 break;
201             default:
202                 Log.e(TAG, "Unsupported config:" + config);
203                 break;
204         }
205         return index;
206     }
207 }
208