OLD | NEW |
---|---|
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 package org.chromium.media; | 5 package org.chromium.media; |
6 | 6 |
7 import android.bluetooth.BluetoothAdapter; | |
8 import android.bluetooth.BluetoothManager; | |
9 import android.content.BroadcastReceiver; | 7 import android.content.BroadcastReceiver; |
10 import android.content.ContentResolver; | 8 import android.content.ContentResolver; |
11 import android.content.Context; | 9 import android.content.Context; |
12 import android.content.Intent; | 10 import android.content.Intent; |
13 import android.content.IntentFilter; | 11 import android.content.IntentFilter; |
14 import android.content.pm.PackageManager; | 12 import android.content.pm.PackageManager; |
15 import android.database.ContentObserver; | 13 import android.database.ContentObserver; |
16 import android.media.AudioFormat; | 14 import android.media.AudioFormat; |
17 import android.media.AudioManager; | 15 import android.media.AudioManager; |
18 import android.media.AudioRecord; | 16 import android.media.AudioRecord; |
19 import android.media.AudioTrack; | 17 import android.media.AudioTrack; |
20 import android.os.Build; | 18 import android.os.Build; |
21 import android.os.Handler; | 19 import android.os.Handler; |
22 import android.os.Looper; | 20 import android.os.Looper; |
23 import android.os.Process; | |
24 import android.provider.Settings; | 21 import android.provider.Settings; |
25 import android.util.Log; | 22 import android.util.Log; |
26 | 23 |
27 import org.chromium.base.CalledByNative; | 24 import org.chromium.base.CalledByNative; |
28 import org.chromium.base.JNINamespace; | 25 import org.chromium.base.JNINamespace; |
29 | 26 |
30 import java.util.ArrayList; | 27 import java.util.ArrayList; |
31 import java.util.Arrays; | 28 import java.util.Arrays; |
32 import java.util.List; | 29 import java.util.List; |
33 | 30 |
34 @JNINamespace("media") | 31 @JNINamespace("media") |
35 class AudioManagerAndroid { | 32 class AudioManagerAndroid { |
36 private static final String TAG = "AudioManagerAndroid"; | 33 private static final String TAG = "AudioManagerAndroid"; |
37 | 34 |
38 // Set to true to enable debug logs. Always check in as false. | 35 // Set to true to enable debug logs. Always check in as false. |
39 private static final boolean DEBUG = false; | 36 private static final boolean DEBUG = true; |
40 | 37 |
41 /** Simple container for device information. */ | 38 /** Simple container for device information. */ |
42 private static class AudioDeviceName { | 39 private static class AudioDeviceName { |
43 private final int mId; | 40 private final int mId; |
44 private final String mName; | 41 private final String mName; |
45 | 42 |
46 private AudioDeviceName(int id, String name) { | 43 private AudioDeviceName(int id, String name) { |
47 mId = id; | 44 mId = id; |
48 mName = name; | 45 mName = name; |
49 } | 46 } |
50 | 47 |
51 @CalledByNative("AudioDeviceName") | 48 @CalledByNative("AudioDeviceName") |
52 private String id() { return String.valueOf(mId); } | 49 private String id() { return String.valueOf(mId); } |
53 | 50 |
54 @CalledByNative("AudioDeviceName") | 51 @CalledByNative("AudioDeviceName") |
55 private String name() { return mName; } | 52 private String name() { return mName; } |
56 } | 53 } |
57 | 54 |
58 // Supported audio device types. | 55 // Supported audio device types. |
56 private static final int DEVICE_DEFAULT = -2; | |
59 private static final int DEVICE_INVALID = -1; | 57 private static final int DEVICE_INVALID = -1; |
60 private static final int DEVICE_SPEAKERPHONE = 0; | 58 private static final int DEVICE_SPEAKERPHONE = 0; |
61 private static final int DEVICE_WIRED_HEADSET = 1; | 59 private static final int DEVICE_WIRED_HEADSET = 1; |
62 private static final int DEVICE_EARPIECE = 2; | 60 private static final int DEVICE_EARPIECE = 2; |
63 private static final int DEVICE_BLUETOOTH_HEADSET = 3; | 61 private static final int DEVICE_BLUETOOTH_HEADSET = 3; |
64 private static final int DEVICE_COUNT = 4; | 62 private static final int DEVICE_COUNT = 4; |
65 | 63 |
66 // Maps audio device types to string values. This map must be in sync | 64 // Maps audio device types to string values. This map must be in sync |
67 // with the device types above. | 65 // with the device types above. |
68 // TODO(henrika): add support for proper detection of device names and | 66 // TODO(henrika): add support for proper detection of device names and |
69 // localize the name strings by using resource strings. | 67 // localize the name strings by using resource strings. |
70 private static final String[] DEVICE_NAMES = new String[] { | 68 private static final String[] DEVICE_NAMES = new String[] { |
71 "Speakerphone", | 69 "Speakerphone", |
72 "Wired headset", // With or without microphone | 70 "Wired headset", // With or without microphone |
73 "Headset earpiece", // Only available on mobile phones | 71 "Headset earpiece", // Only available on mobile phones |
74 "Bluetooth headset", | 72 "Bluetooth headset", |
75 }; | 73 }; |
76 | 74 |
77 // List of valid device types. | 75 // List of valid device types. |
78 private static final Integer[] VALID_DEVICES = new Integer[] { | 76 private static final Integer[] VALID_DEVICES = new Integer[] { |
79 DEVICE_SPEAKERPHONE, | 77 DEVICE_SPEAKERPHONE, |
80 DEVICE_WIRED_HEADSET, | 78 DEVICE_WIRED_HEADSET, |
81 DEVICE_EARPIECE, | 79 DEVICE_EARPIECE, |
82 DEVICE_BLUETOOTH_HEADSET, | 80 DEVICE_BLUETOOTH_HEADSET, |
83 }; | 81 }; |
84 | 82 |
85 // The device does not have any audio device. | |
86 static final int STATE_NO_DEVICE_SELECTED = 0; | |
87 // The speakerphone is on and an associated microphone is used. | |
88 static final int STATE_SPEAKERPHONE_ON = 1; | |
89 // The phone's earpiece is on and an associated microphone is used. | |
90 static final int STATE_EARPIECE_ON = 2; | |
91 // A wired headset (with or without a microphone) is plugged in. | |
92 static final int STATE_WIRED_HEADSET_ON = 3; | |
93 // The audio stream is being directed to a Bluetooth headset. | |
94 static final int STATE_BLUETOOTH_ON = 4; | |
95 // We've requested that the audio stream be directed to Bluetooth, but | |
96 // have not yet received a response from the framework. | |
97 static final int STATE_BLUETOOTH_TURNING_ON = 5; | |
98 // We've requested that the audio stream stop being directed to | |
99 // Bluetooth, but have not yet received a response from the framework. | |
100 static final int STATE_BLUETOOTH_TURNING_OFF = 6; | |
101 // TODO(henrika): document the valid state transitions. | |
102 | |
103 // Use 44.1kHz as the default sampling rate. | 83 // Use 44.1kHz as the default sampling rate. |
104 private static final int DEFAULT_SAMPLING_RATE = 44100; | 84 private static final int DEFAULT_SAMPLING_RATE = 44100; |
105 // Randomly picked up frame size which is close to return value on N4. | 85 // Randomly picked up frame size which is close to return value on N4. |
106 // Return this value when getProperty(PROPERTY_OUTPUT_FRAMES_PER_BUFFER) | 86 // Return this value when getProperty(PROPERTY_OUTPUT_FRAMES_PER_BUFFER) |
107 // fails. | 87 // fails. |
108 private static final int DEFAULT_FRAME_PER_BUFFER = 256; | 88 private static final int DEFAULT_FRAME_PER_BUFFER = 256; |
109 | 89 |
110 private final AudioManager mAudioManager; | 90 private final AudioManager mAudioManager; |
111 private final Context mContext; | 91 private final Context mContext; |
112 private final long mNativeAudioManagerAndroid; | 92 private final long mNativeAudioManagerAndroid; |
113 | 93 |
114 private boolean mHasBluetoothPermission = false; | 94 private int mSavedAudioMode = AudioManager.MODE_INVALID; |
95 | |
115 private boolean mIsInitialized = false; | 96 private boolean mIsInitialized = false; |
116 private boolean mSavedIsSpeakerphoneOn; | 97 private boolean mSavedIsSpeakerphoneOn; |
117 private boolean mSavedIsMicrophoneMute; | 98 private boolean mSavedIsMicrophoneMute; |
118 | 99 |
119 private Integer mAudioDeviceState = STATE_NO_DEVICE_SELECTED; | 100 // Id of the currently active audio device, i.e., audio is routed to |
101 // this device. | |
102 private int mActiveAudioDevice = DEVICE_INVALID; | |
103 // Id of the currently selected audio device. Can only be modified by | |
104 // call to setDevice(). | |
105 private int mSelectedAudioDevice = DEVICE_INVALID; | |
120 | 106 |
121 // Lock to protect |mAudioDevices| which can be accessed from the main | 107 // Lock to protect |mAudioDevices| which can be accessed from the main |
122 // thread and the audio manager thread. | 108 // thread and the audio manager thread. |
123 private final Object mLock = new Object(); | 109 private final Object mLock = new Object(); |
124 | 110 |
125 // Contains a list of currently available audio devices. | 111 // Contains a list of currently available audio devices. |
126 private boolean[] mAudioDevices = new boolean[DEVICE_COUNT]; | 112 private boolean[] mAudioDevices = new boolean[DEVICE_COUNT]; |
127 | 113 |
128 private final ContentResolver mContentResolver; | 114 private final ContentResolver mContentResolver; |
129 private SettingsObserver mSettingsObserver = null; | 115 private SettingsObserver mSettingsObserver = null; |
130 private SettingsObserverThread mSettingsObserverThread = null; | 116 private SettingsObserverThread mSettingsObserverThread = null; |
tommi (sloooow) - chröme
2013/12/12 14:29:45
Change the type to be HandlerThread. You'll also
henrika (OOO until Aug 14)
2013/12/12 15:39:15
Done.
| |
131 private int mCurrentVolume; | 117 private int mCurrentVolume; |
132 private final Object mSettingsObserverLock = new Object(); | 118 private final Object mSettingsObserverLock = new Object(); |
tommi (sloooow) - chröme
2013/12/12 14:29:45
This lock is not needed.
henrika (OOO until Aug 14)
2013/12/12 15:39:15
Done.
| |
133 | 119 |
134 // Broadcast receiver for wired headset intent broadcasts. | 120 // Broadcast receiver for wired headset intent broadcasts. |
135 private BroadcastReceiver mWiredHeadsetReceiver; | 121 private BroadcastReceiver mWiredHeadsetReceiver; |
136 | 122 |
137 /** Construction */ | 123 /** Construction */ |
138 @CalledByNative | 124 @CalledByNative |
139 private static AudioManagerAndroid createAudioManagerAndroid( | 125 private static AudioManagerAndroid createAudioManagerAndroid( |
140 Context context, | 126 Context context, |
141 long nativeAudioManagerAndroid) { | 127 long nativeAudioManagerAndroid) { |
142 return new AudioManagerAndroid(context, nativeAudioManagerAndroid); | 128 return new AudioManagerAndroid(context, nativeAudioManagerAndroid); |
143 } | 129 } |
144 | 130 |
145 private AudioManagerAndroid(Context context, long nativeAudioManagerAndroid) { | 131 private AudioManagerAndroid(Context context, long nativeAudioManagerAndroid) { |
146 mContext = context; | 132 mContext = context; |
147 mNativeAudioManagerAndroid = nativeAudioManagerAndroid; | 133 mNativeAudioManagerAndroid = nativeAudioManagerAndroid; |
148 mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_S ERVICE); | 134 mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_S ERVICE); |
149 mContentResolver = mContext.getContentResolver(); | 135 mContentResolver = mContext.getContentResolver(); |
150 } | 136 } |
151 | 137 |
152 /** | 138 /** |
153 * Saves the initial speakerphone and microphone state. | 139 * Saves the initial speakerphone and microphone state. |
154 * Populates the list of available audio devices and registers receivers | 140 * Populates the list of available audio devices and registers receivers |
155 * for broadcasted intents related to wired headset and bluetooth devices. | 141 * for broadcasted intents related to wired headset and bluetooth devices. |
156 */ | 142 */ |
157 @CalledByNative | 143 @CalledByNative |
158 public void init() { | 144 public void init() { |
145 if (DEBUG) logd("init"); | |
159 if (mIsInitialized) | 146 if (mIsInitialized) |
160 return; | 147 return; |
161 | 148 |
162 synchronized (mLock) { | 149 for (int i = 0; i < DEVICE_COUNT; ++i) { |
163 for (int i = 0; i < DEVICE_COUNT; ++i) { | 150 mAudioDevices[i] = false; |
164 mAudioDevices[i] = false; | |
165 } | |
166 } | 151 } |
167 | 152 |
168 // Store microphone mute state and speakerphone state so it can | |
169 // be restored when closing. | |
170 mSavedIsSpeakerphoneOn = mAudioManager.isSpeakerphoneOn(); | |
171 mSavedIsMicrophoneMute = mAudioManager.isMicrophoneMute(); | |
172 | |
173 // Always enable speaker phone by default. This state might be reset | |
174 // by the wired headset receiver when it gets its initial sticky | |
175 // intent, if any. | |
176 setSpeakerphoneOn(true); | |
177 mAudioDeviceState = STATE_SPEAKERPHONE_ON; | |
178 | |
179 // Initialize audio device list with things we know is always available. | 153 // Initialize audio device list with things we know is always available. |
180 synchronized (mLock) { | 154 if (hasEarpiece()) { |
181 if (hasEarpiece()) { | 155 mAudioDevices[DEVICE_EARPIECE] = true; |
182 mAudioDevices[DEVICE_EARPIECE] = true; | |
183 } | |
184 mAudioDevices[DEVICE_SPEAKERPHONE] = true; | |
185 } | 156 } |
157 mAudioDevices[DEVICE_SPEAKERPHONE] = true; | |
186 | 158 |
187 // Register receiver for broadcasted intents related to adding/ | 159 // Register receiver for broadcasted intents related to adding/ |
188 // removing a wired headset (Intent.ACTION_HEADSET_PLUG). | 160 // removing a wired headset (Intent.ACTION_HEADSET_PLUG). |
189 // Also starts routing to the wired headset/headphone if one is | |
190 // already attached (can be overridden by a Bluetooth headset). | |
191 registerForWiredHeadsetIntentBroadcast(); | 161 registerForWiredHeadsetIntentBroadcast(); |
192 | 162 |
193 // Start routing to Bluetooth if there's a connected device. | |
194 // TODO(henrika): the actual routing part is not implemented yet. | |
195 // All we do currently is to detect if BT headset is attached or not. | |
196 initBluetooth(); | |
197 | |
198 mIsInitialized = true; | |
199 | |
200 mSettingsObserverThread = new SettingsObserverThread(); | 163 mSettingsObserverThread = new SettingsObserverThread(); |
tommi (sloooow) - chröme
2013/12/12 14:29:45
Change lines 163-171 to:
mSettingsObserverThread
| |
201 mSettingsObserverThread.start(); | 164 mSettingsObserverThread.start(); |
202 synchronized (mSettingsObserverLock) { | 165 synchronized (mSettingsObserverLock) { |
203 try { | 166 try { |
204 mSettingsObserverLock.wait(); | 167 mSettingsObserverLock.wait(); |
205 } catch (InterruptedException e) { | 168 } catch (InterruptedException e) { |
206 Log.e(TAG, "unregisterHeadsetReceiver exception: " + e.getMessag e()); | 169 loge("Object.wait exception: " + e.getMessage()); |
207 } | 170 } |
208 } | 171 } |
172 | |
173 mIsInitialized = true; | |
209 } | 174 } |
210 | 175 |
211 /** | 176 /** |
212 * Unregister all previously registered intent receivers and restore | 177 * Unregister all previously registered intent receivers and restore |
213 * the stored state (stored in {@link #init()}). | 178 * the stored state (stored in {@link #init()}). |
214 */ | 179 */ |
215 @CalledByNative | 180 @CalledByNative |
216 public void close() { | 181 public void close() { |
182 if (DEBUG) logd("close"); | |
217 if (!mIsInitialized) | 183 if (!mIsInitialized) |
218 return; | 184 return; |
219 | 185 |
220 if (mSettingsObserverThread != null) { | 186 if (mSettingsObserverThread != null) { |
tommi (sloooow) - chröme
2013/12/12 14:29:45
change lines 186-192 to:
mSettingsObserverThread.q
henrika (OOO until Aug 14)
2013/12/12 15:39:15
Done.
| |
221 mSettingsObserverThread = null; | 187 mSettingsObserverThread = null; |
222 } | 188 } |
223 if (mSettingsObserver != null) { | 189 if (mSettingsObserver != null) { |
224 mContentResolver.unregisterContentObserver(mSettingsObserver); | 190 mContentResolver.unregisterContentObserver(mSettingsObserver); |
225 mSettingsObserver = null; | 191 mSettingsObserver = null; |
226 } | 192 } |
227 | 193 |
228 unregisterForWiredHeadsetIntentBroadcast(); | 194 unregisterForWiredHeadsetIntentBroadcast(); |
229 | 195 |
230 // Restore previously stored audio states. | |
231 setMicrophoneMute(mSavedIsMicrophoneMute); | |
232 setSpeakerphoneOn(mSavedIsSpeakerphoneOn); | |
233 | |
234 mIsInitialized = false; | 196 mIsInitialized = false; |
235 } | 197 } |
236 | 198 |
199 /** | |
200 * Saves current audio mode and sets audio mode to MODE_IN_COMMUNICATION | |
201 * if input parameter is true. Restores saved audio mode if input parameter | |
202 * is false. | |
203 */ | |
237 @CalledByNative | 204 @CalledByNative |
238 public void setMode(int mode) { | 205 public void setCommunicationAudioModeOn(boolean on) { |
239 try { | 206 if (DEBUG) logd("setCommunicationAudioModeOn(" + on + ")"); |
240 mAudioManager.setMode(mode); | 207 |
241 } catch (SecurityException e) { | 208 if (on) { |
242 Log.e(TAG, "setMode exception: " + e.getMessage()); | 209 if (mSavedAudioMode != AudioManager.MODE_INVALID) { |
243 logDeviceInfo(); | 210 logwtf("Audio mode has already been set!"); |
211 return; | |
212 } | |
213 | |
214 // Store the current audio mode the first time we try to | |
215 // switch to communication mode. | |
216 try { | |
217 mSavedAudioMode = mAudioManager.getMode(); | |
218 } catch (SecurityException e) { | |
219 logwtf("getMode exception: " + e.getMessage()); | |
220 logDeviceInfo(); | |
221 } | |
222 | |
223 // Store microphone mute state and speakerphone state so it can | |
224 // be restored when closing. | |
225 mSavedIsSpeakerphoneOn = mAudioManager.isSpeakerphoneOn(); | |
226 mSavedIsMicrophoneMute = mAudioManager.isMicrophoneMute(); | |
227 | |
228 try { | |
229 mAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION); | |
230 } catch (SecurityException e) { | |
231 logwtf("setMode exception: " + e.getMessage()); | |
232 logDeviceInfo(); | |
233 } | |
234 } else { | |
235 if (mSavedAudioMode == AudioManager.MODE_INVALID) { | |
236 logwtf("Audio mode has not yet been set!"); | |
237 return; | |
238 } | |
239 | |
240 // Restore previously stored audio states. | |
241 setMicrophoneMute(mSavedIsMicrophoneMute); | |
242 setSpeakerphoneOn(mSavedIsSpeakerphoneOn); | |
243 | |
244 // Restore the mode that was used before we switched to | |
245 // communication mode. | |
246 try { | |
247 mAudioManager.setMode(mSavedAudioMode); | |
248 } catch (SecurityException e) { | |
249 logwtf("setMode exception: " + e.getMessage()); | |
250 logDeviceInfo(); | |
251 } | |
252 mSavedAudioMode = AudioManager.MODE_INVALID; | |
244 } | 253 } |
245 } | 254 } |
246 | 255 |
247 /** | 256 /** |
248 * Activates, i.e., starts routing audio to, the specified audio device. | 257 * Activates, i.e., starts routing audio to, the specified audio device. |
249 * | 258 * |
250 * @param deviceId Unique device ID (integer converted to string) | 259 * @param deviceId Unique device ID (integer converted to string) |
251 * representing the selected device. This string is empty if the so-called | 260 * representing the selected device. This string is empty if the so-called |
252 * default device is selected. | 261 * default device is selected. |
253 */ | 262 */ |
254 @CalledByNative | 263 @CalledByNative |
255 public void setDevice(String deviceId) { | 264 boolean setDevice(String deviceId) { |
256 boolean devices[] = null; | 265 boolean devices[] = null; |
257 synchronized (mLock) { | 266 synchronized (mLock) { |
258 devices = mAudioDevices.clone(); | 267 devices = mAudioDevices.clone(); |
259 } | 268 } |
260 if (deviceId.isEmpty()) { | 269 if (deviceId.isEmpty()) { |
261 logd("setDevice: default"); | 270 if (DEBUG) logd("setDevice: default"); |
262 // Use a special selection scheme if the default device is selected. | 271 int defaultDevice = selectDefaultDevice(devices); |
263 // The "most unique" device will be selected; Bluetooth first, then | 272 setAudioDevice(defaultDevice); |
264 // wired headset and last the speaker phone. | 273 mSelectedAudioDevice = DEVICE_DEFAULT; |
265 if (devices[DEVICE_BLUETOOTH_HEADSET]) { | 274 return true; |
266 // TODO(henrika): possibly need improvements here if we are | 275 } else if (isNumeric(deviceId)) { |
267 // in a STATE_BLUETOOTH_TURNING_OFF state. | 276 if (DEBUG) logd("setDevice: " + deviceId); |
268 setAudioDevice(DEVICE_BLUETOOTH_HEADSET); | |
269 } else if (devices[DEVICE_WIRED_HEADSET]) { | |
270 setAudioDevice(DEVICE_WIRED_HEADSET); | |
271 } else { | |
272 setAudioDevice(DEVICE_SPEAKERPHONE); | |
273 } | |
274 } else { | |
275 logd("setDevice: " + deviceId); | |
276 // A non-default device is specified. Verify that it is valid | 277 // A non-default device is specified. Verify that it is valid |
277 // device, and if so, start using it. | 278 // device, and if so, start using it. |
278 List<Integer> validIds = Arrays.asList(VALID_DEVICES); | 279 List<Integer> validIds = Arrays.asList(VALID_DEVICES); |
279 Integer id = Integer.valueOf(deviceId); | 280 Integer id = Integer.valueOf(deviceId); |
280 if (validIds.contains(id)) { | 281 if (validIds.contains(id) && mAudioDevices[id.intValue()]) { |
282 mSelectedAudioDevice = id.intValue(); | |
281 setAudioDevice(id.intValue()); | 283 setAudioDevice(id.intValue()); |
282 } else { | 284 return true; |
283 loge("Invalid device ID!"); | |
284 } | 285 } |
285 } | 286 } |
287 loge("Invalid device ID: " + deviceId); | |
288 return false; | |
286 } | 289 } |
287 | 290 |
288 /** | 291 /** |
289 * @return the current list of available audio devices. | 292 * @return the current list of available audio devices. |
290 * Note that this call does not trigger any update of the list of devices, | 293 * Note that this call does not trigger any update of the list of devices, |
291 * it only copies the current state in to the output array. | 294 * it only copies the current state in to the output array. |
292 */ | 295 */ |
293 @CalledByNative | 296 @CalledByNative |
294 public AudioDeviceName[] getAudioInputDeviceNames() { | 297 public AudioDeviceName[] getAudioInputDeviceNames() { |
295 synchronized (mLock) { | 298 synchronized (mLock) { |
296 List<String> devices = new ArrayList<String>(); | 299 List<String> devices = new ArrayList<String>(); |
297 AudioDeviceName[] array = new AudioDeviceName[getNumOfAudioDevicesWi thLock()]; | 300 AudioDeviceName[] array = new AudioDeviceName[getNumOfAudioDevicesWi thLock()]; |
298 int i = 0; | 301 int i = 0; |
299 for (int id = 0; id < DEVICE_COUNT; ++id) { | 302 for (int id = 0; id < DEVICE_COUNT; ++id) { |
300 if (mAudioDevices[id]) { | 303 if (mAudioDevices[id]) { |
301 array[i] = new AudioDeviceName(id, DEVICE_NAMES[id]); | 304 array[i] = new AudioDeviceName(id, DEVICE_NAMES[id]); |
302 devices.add(DEVICE_NAMES[id]); | 305 devices.add(DEVICE_NAMES[id]); |
303 i++; | 306 i++; |
304 } | 307 } |
305 } | 308 } |
306 logd("getAudioInputDeviceNames: " + devices); | 309 if (DEBUG) logd("getAudioInputDeviceNames: " + devices); |
307 return array; | 310 return array; |
308 } | 311 } |
309 } | 312 } |
310 | 313 |
311 @CalledByNative | 314 @CalledByNative |
312 private int getNativeOutputSampleRate() { | 315 private int getNativeOutputSampleRate() { |
313 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.J ELLY_BEAN_MR1) { | 316 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.J ELLY_BEAN_MR1) { |
314 String sampleRateString = mAudioManager.getProperty( | 317 String sampleRateString = mAudioManager.getProperty( |
315 AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE); | 318 AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE); |
316 return (sampleRateString == null ? | 319 return (sampleRateString == null ? |
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
405 | 408 |
406 /** | 409 /** |
407 * Registers receiver for the broadcasted intent when a wired headset is | 410 * Registers receiver for the broadcasted intent when a wired headset is |
408 * plugged in or unplugged. The received intent will have an extra | 411 * plugged in or unplugged. The received intent will have an extra |
409 * 'state' value where 0 means unplugged, and 1 means plugged. | 412 * 'state' value where 0 means unplugged, and 1 means plugged. |
410 */ | 413 */ |
411 private void registerForWiredHeadsetIntentBroadcast() { | 414 private void registerForWiredHeadsetIntentBroadcast() { |
412 IntentFilter filter = new IntentFilter(Intent.ACTION_HEADSET_PLUG); | 415 IntentFilter filter = new IntentFilter(Intent.ACTION_HEADSET_PLUG); |
413 | 416 |
414 /** | 417 /** |
415 * Receiver which handles changes in wired headset availablilty. | 418 * Receiver which handles changes in wired headset availability: |
419 * updates the list of devices; | |
420 * updates the active device if a device selection has been made. | |
416 */ | 421 */ |
417 mWiredHeadsetReceiver = new BroadcastReceiver() { | 422 mWiredHeadsetReceiver = new BroadcastReceiver() { |
418 private static final int STATE_UNPLUGGED = 0; | 423 private static final int STATE_UNPLUGGED = 0; |
419 private static final int STATE_PLUGGED = 1; | 424 private static final int STATE_PLUGGED = 1; |
420 private static final int HAS_NO_MIC = 0; | 425 private static final int HAS_NO_MIC = 0; |
421 private static final int HAS_MIC = 1; | 426 private static final int HAS_MIC = 1; |
422 | 427 |
423 @Override | 428 @Override |
424 public void onReceive(Context context, Intent intent) { | 429 public void onReceive(Context context, Intent intent) { |
425 String action = intent.getAction(); | 430 String action = intent.getAction(); |
426 if (!action.equals(Intent.ACTION_HEADSET_PLUG)) { | 431 if (!action.equals(Intent.ACTION_HEADSET_PLUG)) { |
427 return; | 432 return; |
428 } | 433 } |
429 int state = intent.getIntExtra("state", STATE_UNPLUGGED); | 434 int state = intent.getIntExtra("state", STATE_UNPLUGGED); |
430 int microphone = intent.getIntExtra("microphone", HAS_NO_MIC); | 435 int microphone = intent.getIntExtra("microphone", HAS_NO_MIC); |
431 String name = intent.getStringExtra("name"); | 436 String name = intent.getStringExtra("name"); |
432 logd("==> onReceive: s=" + state | 437 if (DEBUG) { |
438 logd("==> onReceive: s=" + state | |
433 + ", m=" + microphone | 439 + ", m=" + microphone |
434 + ", n=" + name | 440 + ", n=" + name |
435 + ", sb=" + isInitialStickyBroadcast()); | 441 + ", sb=" + isInitialStickyBroadcast()); |
436 | 442 } |
437 switch (state) { | 443 switch (state) { |
438 case STATE_UNPLUGGED: | 444 case STATE_UNPLUGGED: |
439 synchronized (mLock) { | 445 synchronized (mLock) { |
440 // Wired headset and earpiece are mutually exclusive . | 446 // Wired headset and earpiece are mutually exclusive . |
441 mAudioDevices[DEVICE_WIRED_HEADSET] = false; | 447 mAudioDevices[DEVICE_WIRED_HEADSET] = false; |
442 if (hasEarpiece()) { | 448 if (hasEarpiece()) { |
443 mAudioDevices[DEVICE_EARPIECE] = true; | 449 mAudioDevices[DEVICE_EARPIECE] = true; |
444 } | 450 } |
445 } | 451 } |
446 // If wired headset was used before it was unplugged, | |
447 // switch to speaker phone. If it was not in use; just | |
448 // log the change. | |
449 if (mAudioDeviceState == STATE_WIRED_HEADSET_ON) { | |
450 setAudioDevice(DEVICE_SPEAKERPHONE); | |
451 } else { | |
452 reportUpdate(); | |
453 } | |
454 break; | 452 break; |
455 case STATE_PLUGGED: | 453 case STATE_PLUGGED: |
456 synchronized (mLock) { | 454 synchronized (mLock) { |
457 // Wired headset and earpiece are mutually exclusive . | 455 // Wired headset and earpiece are mutually exclusive . |
458 mAudioDevices[DEVICE_WIRED_HEADSET] = true; | 456 mAudioDevices[DEVICE_WIRED_HEADSET] = true; |
459 mAudioDevices[DEVICE_EARPIECE] = false; | 457 mAudioDevices[DEVICE_EARPIECE] = false; |
460 setAudioDevice(DEVICE_WIRED_HEADSET); | |
461 } | 458 } |
462 break; | 459 break; |
463 default: | 460 default: |
464 loge("Invalid state!"); | 461 loge("Invalid state!"); |
465 break; | 462 break; |
466 } | 463 } |
464 | |
465 // Update the existing device selection, but only if a specific | |
466 // device has already been selected explicitly. | |
467 boolean deviceHasBeenSelected = false; | |
468 synchronized (mLock) { | |
469 deviceHasBeenSelected = (mSelectedAudioDevice != DEVICE_INVA LID); | |
470 } | |
471 if (deviceHasBeenSelected) { | |
472 updateDeviceActivation(); | |
473 } | |
467 } | 474 } |
468 }; | 475 }; |
469 | 476 |
470 // Note: the intent we register for here is sticky, so it'll tell us | 477 // Note: the intent we register for here is sticky, so it'll tell us |
471 // immediately what the last action was (plugged or unplugged). | 478 // immediately what the last action was (plugged or unplugged). |
472 // It will enable us to set the speakerphone correctly. | 479 // It will enable us to set the speakerphone correctly. |
473 mContext.registerReceiver(mWiredHeadsetReceiver, filter); | 480 mContext.registerReceiver(mWiredHeadsetReceiver, filter); |
474 } | 481 } |
475 | 482 |
476 /** Unregister receiver for broadcasted ACTION_HEADSET_PLUG intent. */ | 483 /** Unregister receiver for broadcasted ACTION_HEADSET_PLUG intent. */ |
477 private void unregisterForWiredHeadsetIntentBroadcast() { | 484 private void unregisterForWiredHeadsetIntentBroadcast() { |
478 mContext.unregisterReceiver(mWiredHeadsetReceiver); | 485 mContext.unregisterReceiver(mWiredHeadsetReceiver); |
479 mWiredHeadsetReceiver = null; | 486 mWiredHeadsetReceiver = null; |
480 } | 487 } |
481 | 488 |
482 /** | 489 /** |
483 * Check if Bluetooth device is connected, register Bluetooth receiver | |
484 * and start routing to Bluetooth if a device is connected. | |
485 * TODO(henrika): currently only supports the detecion part at startup. | |
486 */ | |
487 private void initBluetooth() { | |
488 // Bail out if we don't have the required permission. | |
489 mHasBluetoothPermission = mContext.checkPermission( | |
490 android.Manifest.permission.BLUETOOTH, | |
491 Process.myPid(), | |
492 Process.myUid()) == PackageManager.PERMISSION_GRANTED; | |
493 if (!mHasBluetoothPermission) { | |
494 loge("BLUETOOTH permission is missing!"); | |
495 return; | |
496 } | |
497 | |
498 // To get a BluetoothAdapter representing the local Bluetooth adapter, | |
499 // when running on JELLY_BEAN_MR1 (4.2) and below, call the static | |
500 // getDefaultAdapter() method; when running on JELLY_BEAN_MR2 (4.3) and | |
501 // higher, retrieve it through getSystemService(String) with | |
502 // BLUETOOTH_SERVICE. | |
503 // Note: Most methods require the BLUETOOTH permission. | |
504 BluetoothAdapter btAdapter = null; | |
505 if (android.os.Build.VERSION.SDK_INT <= | |
506 android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) { | |
507 // Use static method for Android 4.2 and below to get the | |
508 // BluetoothAdapter. | |
509 btAdapter = BluetoothAdapter.getDefaultAdapter(); | |
510 } else { | |
511 // Use BluetoothManager to get the BluetoothAdapter for | |
512 // Android 4.3 and above. | |
513 BluetoothManager btManager = (BluetoothManager) mContext.getSystemSe rvice( | |
514 Context.BLUETOOTH_SERVICE); | |
515 btAdapter = btManager.getAdapter(); | |
516 } | |
517 | |
518 if (btAdapter != null && | |
519 // android.bluetooth.BluetoothAdapter.getProfileConnectionState | |
520 // requires BLUETOOTH permission. | |
521 android.bluetooth.BluetoothProfile.STATE_CONNECTED == | |
522 btAdapter.getProfileConnectionState( | |
523 android.bluetooth.BluetoothProfile.HEADSET)) { | |
524 synchronized (mLock) { | |
525 mAudioDevices[DEVICE_BLUETOOTH_HEADSET] = true; | |
526 } | |
527 // TODO(henrika): ensure that we set the active audio | |
528 // device to Bluetooth (not trivial). | |
529 setAudioDevice(DEVICE_BLUETOOTH_HEADSET); | |
530 } | |
531 } | |
532 | |
533 /** | |
534 * Changes selection of the currently active audio device. | 490 * Changes selection of the currently active audio device. |
535 * | 491 * |
536 * @param device Specifies the selected audio device. | 492 * @param device Specifies the selected audio device. |
537 */ | 493 */ |
538 public void setAudioDevice(int device) { | 494 public void setAudioDevice(int device) { |
495 if (device == mActiveAudioDevice) { | |
496 if (DEBUG) logd("setAudioDevice: " + device + " is already active"); | |
497 return; | |
498 } | |
539 switch (device) { | 499 switch (device) { |
540 case DEVICE_BLUETOOTH_HEADSET: | 500 case DEVICE_BLUETOOTH_HEADSET: |
541 // TODO(henrika): add support for turning on an routing to | 501 // TODO(henrika): add support for turning on an routing to |
542 // BT here. | 502 // BT here. |
543 if (DEBUG) logd("--- TO BE IMPLEMENTED ---"); | 503 if (DEBUG) logd("--- TO BE IMPLEMENTED ---"); |
544 break; | 504 break; |
545 case DEVICE_SPEAKERPHONE: | 505 case DEVICE_SPEAKERPHONE: |
546 // TODO(henrika): turn off BT if required. | 506 // TODO(henrika): turn off BT if required. |
547 mAudioDeviceState = STATE_SPEAKERPHONE_ON; | 507 mActiveAudioDevice = DEVICE_SPEAKERPHONE; |
548 setSpeakerphoneOn(true); | 508 setSpeakerphoneOn(true); |
549 break; | 509 break; |
550 case DEVICE_WIRED_HEADSET: | 510 case DEVICE_WIRED_HEADSET: |
551 // TODO(henrika): turn off BT if required. | 511 // TODO(henrika): turn off BT if required. |
552 mAudioDeviceState = STATE_WIRED_HEADSET_ON; | 512 mActiveAudioDevice = DEVICE_WIRED_HEADSET; |
553 setSpeakerphoneOn(false); | 513 setSpeakerphoneOn(false); |
554 break; | 514 break; |
555 case DEVICE_EARPIECE: | 515 case DEVICE_EARPIECE: |
556 // TODO(henrika): turn off BT if required. | 516 // TODO(henrika): turn off BT if required. |
557 mAudioDeviceState = STATE_EARPIECE_ON; | 517 mActiveAudioDevice = DEVICE_EARPIECE; |
558 setSpeakerphoneOn(false); | 518 setSpeakerphoneOn(false); |
559 break; | 519 break; |
560 default: | 520 default: |
561 loge("Invalid audio device selection!"); | 521 loge("Invalid audio device selection!"); |
562 break; | 522 break; |
563 } | 523 } |
564 reportUpdate(); | 524 reportUpdate(); |
565 } | 525 } |
566 | 526 |
527 private static int selectDefaultDevice(boolean[] devices) { | |
528 // Use a special selection scheme if the default device is selected. | |
529 // The "most unique" device will be selected; Wired headset first, | |
530 // then Bluetooth and last the speaker phone. | |
531 if (devices[DEVICE_WIRED_HEADSET]) { | |
532 return DEVICE_WIRED_HEADSET; | |
533 } else if (devices[DEVICE_BLUETOOTH_HEADSET]) { | |
534 // TODO(henrika): possibly need improvements here if we are | |
535 // in a state where Bluetooth is turning off. | |
536 return DEVICE_BLUETOOTH_HEADSET; | |
537 } | |
538 return DEVICE_SPEAKERPHONE; | |
539 } | |
540 | |
567 private int getNumOfAudioDevicesWithLock() { | 541 private int getNumOfAudioDevicesWithLock() { |
568 int count = 0; | 542 int count = 0; |
569 for (int i = 0; i < DEVICE_COUNT; ++i) { | 543 for (int i = 0; i < DEVICE_COUNT; ++i) { |
570 if (mAudioDevices[i]) | 544 if (mAudioDevices[i]) |
571 count++; | 545 count++; |
572 } | 546 } |
573 return count; | 547 return count; |
574 } | 548 } |
575 | 549 |
576 /** | 550 /** |
551 * Updates the active device given the current list of devices and | |
552 * information about if a specific device has been selected or if | |
553 * the default device is selected. | |
554 */ | |
555 private void updateDeviceActivation() { | |
556 boolean devices[] = null; | |
557 int selected = DEVICE_INVALID; | |
558 synchronized (mLock) { | |
559 selected = mSelectedAudioDevice; | |
560 devices = mAudioDevices.clone(); | |
561 } | |
562 if (selected == DEVICE_INVALID) { | |
563 loge("Unable to activate device since no device is selected!"); | |
564 return; | |
565 } | |
566 | |
567 // Update default device if it has been selected explicitly, or | |
568 // the selected device has been removed from the list. | |
569 if (selected == DEVICE_DEFAULT || !devices[mSelectedAudioDevice]) { | |
570 // Get default device given current list and activate the device. | |
571 int defaultDevice = selectDefaultDevice(devices); | |
572 setAudioDevice(defaultDevice); | |
573 } else { | |
574 // Activate the selected device since we know that it exists in | |
575 // the list. | |
576 setAudioDevice(selected); | |
577 } | |
578 } | |
579 | |
580 private static boolean isNumeric(String str) { | |
581 for (char c : str.toCharArray()) { | |
582 if (!Character.isDigit(c)) { | |
583 return false; | |
584 } | |
585 } | |
586 return true; | |
587 } | |
588 | |
589 /** | |
577 * For now, just log the state change but the idea is that we should | 590 * For now, just log the state change but the idea is that we should |
578 * notify a registered state change listener (if any) that there has | 591 * notify a registered state change listener (if any) that there has |
579 * been a change in the state. | 592 * been a change in the state. |
580 * TODO(henrika): add support for state change listener. | 593 * TODO(henrika): add support for state change listener. |
581 */ | 594 */ |
582 private void reportUpdate() { | 595 private void reportUpdate() { |
583 synchronized (mLock) { | 596 synchronized (mLock) { |
584 List<String> devices = new ArrayList<String>(); | 597 List<String> devices = new ArrayList<String>(); |
585 for (int i = 0; i < DEVICE_COUNT; ++i) { | 598 for (int i = 0; i < DEVICE_COUNT; ++i) { |
586 if (mAudioDevices[i]) | 599 if (mAudioDevices[i]) |
587 devices.add(DEVICE_NAMES[i]); | 600 devices.add(DEVICE_NAMES[i]); |
588 } | 601 } |
589 logd("reportUpdate: state=" + mAudioDeviceState | 602 if (DEBUG) { |
590 + ", devices=" + devices); | 603 logd("reportUpdate: active=" + mActiveAudioDevice |
604 + ", selected=" + mSelectedAudioDevice | |
605 + ", devices=" + devices); | |
606 } | |
591 } | 607 } |
592 } | 608 } |
593 | 609 |
594 private void logDeviceInfo() { | 610 private void logDeviceInfo() { |
595 Log.i(TAG, "Manufacturer:" + Build.MANUFACTURER + | 611 Log.i(TAG, "Manufacturer:" + Build.MANUFACTURER + |
596 " Board: " + Build.BOARD + " Device: " + Build.DEVICE + | 612 " Board: " + Build.BOARD + " Device: " + Build.DEVICE + |
597 " Model: " + Build.MODEL + " PRODUCT: " + Build.PRODUCT); | 613 " Model: " + Build.MODEL + " PRODUCT: " + Build.PRODUCT); |
598 } | 614 } |
599 | 615 |
600 /** Trivial helper method for debug logging */ | 616 /** Trivial helper method for debug logging */ |
601 private void logd(String msg) { | 617 private void logd(String msg) { |
602 Log.d(TAG, msg); | 618 Log.d(TAG, msg); |
603 } | 619 } |
604 | 620 |
605 /** Trivial helper method for error logging */ | 621 /** Trivial helper method for error logging */ |
606 private void loge(String msg) { | 622 private void loge(String msg) { |
607 Log.e(TAG, msg); | 623 Log.e(TAG, msg); |
608 } | 624 } |
609 | 625 |
626 /** What a Terrible Failure: Reports a condition that should never happen */ | |
627 private void logwtf(String msg) { | |
628 Log.wtf(TAG, msg); | |
629 } | |
630 | |
610 private class SettingsObserver extends ContentObserver { | 631 private class SettingsObserver extends ContentObserver { |
611 SettingsObserver() { | 632 SettingsObserver() { |
tommi (sloooow) - chröme
2013/12/12 14:29:45
change the constructor to be:
SettingsObserver(Ha
| |
612 super(new Handler()); | 633 super(new Handler()); |
613 mContentResolver.registerContentObserver(Settings.System.CONTENT_URI , true, this); | 634 mContentResolver.registerContentObserver(Settings.System.CONTENT_URI , true, this); |
614 } | 635 } |
615 | 636 |
616 @Override | 637 @Override |
617 public void onChange(boolean selfChange) { | 638 public void onChange(boolean selfChange) { |
618 super.onChange(selfChange); | 639 super.onChange(selfChange); |
619 int volume = mAudioManager.getStreamVolume(AudioManager.STREAM_VOICE _CALL); | 640 int volume = mAudioManager.getStreamVolume(AudioManager.STREAM_VOICE _CALL); |
620 nativeSetMute(mNativeAudioManagerAndroid, (volume == 0)); | 641 nativeSetMute(mNativeAudioManagerAndroid, (volume == 0)); |
621 } | 642 } |
622 } | 643 } |
623 | 644 |
624 private native void nativeSetMute(long nativeAudioManagerAndroid, boolean mu ted); | 645 private native void nativeSetMute(long nativeAudioManagerAndroid, boolean mu ted); |
625 | 646 |
626 private class SettingsObserverThread extends Thread { | 647 private class SettingsObserverThread extends Thread { |
tommi (sloooow) - chröme
2013/12/12 14:29:45
Remove this class.
henrika (OOO until Aug 14)
2013/12/12 15:39:15
Done.
| |
627 SettingsObserverThread() { | 648 SettingsObserverThread() { |
628 super("SettinsObserver"); | 649 super("SettingsObserver"); |
629 } | 650 } |
630 | 651 |
631 @Override | 652 @Override |
632 public void run() { | 653 public void run() { |
633 // Set this thread up so the handler will work on it. | 654 // Set this thread up so the handler will work on it. |
634 Looper.prepare(); | 655 Looper.prepare(); |
635 | 656 |
636 synchronized (mSettingsObserverLock) { | 657 synchronized (mSettingsObserverLock) { |
637 mSettingsObserver = new SettingsObserver(); | 658 mSettingsObserver = new SettingsObserver(); |
638 mSettingsObserverLock.notify(); | 659 mSettingsObserverLock.notify(); |
639 } | 660 } |
640 | 661 |
641 // Listen for volume change. | 662 // Listen for volume change. |
642 Looper.loop(); | 663 Looper.loop(); |
643 } | 664 } |
644 } | 665 } |
645 } | 666 } |
OLD | NEW |