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