Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(33)

Side by Side Diff: media/base/android/java/src/org/chromium/media/AudioManagerAndroid.java

Issue 110173003: Refactor audio manager for Android to avoid heavy tasks at startup (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: nits Created 7 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698