Chromium Code Reviews| 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; | |
| 7 import android.content.BroadcastReceiver; | 9 import android.content.BroadcastReceiver; |
| 8 import android.content.Context; | 10 import android.content.Context; |
| 9 import android.content.Intent; | 11 import android.content.Intent; |
| 10 import android.content.IntentFilter; | 12 import android.content.IntentFilter; |
| 11 import android.content.pm.PackageManager; | 13 import android.content.pm.PackageManager; |
| 12 import android.media.AudioFormat; | 14 import android.media.AudioFormat; |
| 13 import android.media.AudioManager; | 15 import android.media.AudioManager; |
| 14 import android.media.AudioRecord; | 16 import android.media.AudioRecord; |
| 15 import android.media.AudioTrack; | 17 import android.media.AudioTrack; |
| 16 import android.os.Build; | 18 import android.os.Build; |
| 19 import android.os.Process; | |
| 17 import android.util.Log; | 20 import android.util.Log; |
| 18 | 21 |
| 22 import java.util.Arrays; | |
| 23 import java.util.ArrayList; | |
| 24 import java.util.HashSet; | |
| 25 import java.util.List; | |
| 26 import java.util.Set; | |
| 27 | |
| 19 import org.chromium.base.CalledByNative; | 28 import org.chromium.base.CalledByNative; |
| 20 import org.chromium.base.JNINamespace; | 29 import org.chromium.base.JNINamespace; |
| 21 | 30 |
| 22 @JNINamespace("media") | 31 @JNINamespace("media") |
| 23 class AudioManagerAndroid { | 32 class AudioManagerAndroid { |
| 24 private static final String TAG = "AudioManagerAndroid"; | 33 private static final String TAG = "AudioManagerAndroid"; |
| 25 | 34 |
| 26 // Most of Google lead devices use 44.1K as the default sampling rate, 44.1K | 35 /** Simple container for device information. */ |
| 27 // is also widely used on other android devices. | 36 private static class AudioDeviceName { |
| 37 private final int mId; | |
| 38 private final String mName; | |
| 39 | |
| 40 private AudioDeviceName(int id, String name) { | |
| 41 mId = id; | |
| 42 mName = name; | |
| 43 } | |
| 44 | |
| 45 @CalledByNative("AudioDeviceName") | |
| 46 private String id() { return String.valueOf(mId); } | |
| 47 | |
| 48 @CalledByNative("AudioDeviceName") | |
| 49 private String name() { return mName; } | |
| 50 } | |
| 51 | |
| 52 // Supported audio device types. | |
| 53 private static final int DEVICE_INVALID = 0; | |
| 54 private static final int DEVICE_SPEAKERPHONE = 1; | |
| 55 private static final int DEVICE_WIRED_HEADSET = 2; | |
| 56 private static final int DEVICE_EARPIECE = 3; | |
| 57 private static final int DEVICE_BLUETOOTH_HEADSET = 4; | |
| 58 | |
| 59 // List of valid device types. | |
| 60 private static Integer[] VALID_DEVICES = new Integer[] { | |
| 61 DEVICE_SPEAKERPHONE, | |
| 62 DEVICE_WIRED_HEADSET, | |
| 63 DEVICE_EARPIECE, | |
| 64 DEVICE_BLUETOOTH_HEADSET, | |
| 65 }; | |
| 66 | |
| 67 // Maps audio device types to string values. | |
|
Jói
2013/11/29 14:40:46
Maybe it would be better to have this right after
henrika (OOO until Aug 14)
2013/11/29 15:31:49
Done.
| |
| 68 // TODO(henrika): add support for proper detection of device names and | |
| 69 // localize the name strings. | |
| 70 private static final String[] DEVICE_NAMES = new String[] { | |
| 71 "Invalid device", | |
| 72 "Speakerphone", | |
| 73 "Wired headset", // With or without microphone | |
| 74 "Headset earpiece", // Only available on mobile phones | |
| 75 "Bluetooth headset", | |
| 76 }; | |
| 77 | |
| 78 // The device does not have any audio device. | |
|
Jói
2013/11/29 14:40:46
It would be cool to document the valid state trans
henrika (OOO until Aug 14)
2013/11/29 15:31:49
Added TODO.
| |
| 79 static final int STATE_NO_DEVICE_SELECTED = 0; | |
| 80 // The speakerphone is on and an associated microphone is used. | |
| 81 static final int STATE_SPEAKERPHONE_ON = 1; | |
| 82 // The phone's earpiece is on and an associated microphone is used. | |
| 83 static final int STATE_EARPIECE_ON = 2; | |
| 84 // A wired headset (with or without a microphone) is plugged in. | |
| 85 static final int STATE_WIRED_HEADSET_ON = 3; | |
| 86 // The audio stream is being directed to a Bluetooth headset. | |
| 87 static final int STATE_BLUETOOTH_ON = 4; | |
| 88 // We've requested that the audio stream be directed to Bluetooth, but | |
| 89 // have not yet received a response from the framework. | |
| 90 static final int STATE_BLUETOOTH_TURNING_ON = 5; | |
| 91 // We've requested that the audio stream stop being directed to | |
| 92 // Bluetooth, but have not yet received a response from the framework. | |
| 93 static final int STATE_BLUETOOTH_TURNING_OFF = 6; | |
| 94 | |
| 95 // Use 44.1kHz as the default sampling rate. | |
| 28 private static final int DEFAULT_SAMPLING_RATE = 44100; | 96 private static final int DEFAULT_SAMPLING_RATE = 44100; |
| 29 // Randomly picked up frame size which is close to return value on N4. | 97 // Randomly picked up frame size which is close to return value on N4. |
| 30 // Return this default value when | 98 // Return this value when getProperty(PROPERTY_OUTPUT_FRAMES_PER_BUFFER) |
| 31 // getProperty(PROPERTY_OUTPUT_FRAMES_PER_BUFFER) fails. | 99 // fails. |
| 32 private static final int DEFAULT_FRAME_PER_BUFFER = 256; | 100 private static final int DEFAULT_FRAME_PER_BUFFER = 256; |
| 33 | 101 |
| 34 private final AudioManager mAudioManager; | 102 private final AudioManager mAudioManager; |
| 35 private final Context mContext; | 103 private final Context mContext; |
| 36 | 104 |
| 37 private BroadcastReceiver mReceiver; | 105 private boolean mHasBluetoothPermission = false; |
| 38 private boolean mOriginalSpeakerStatus; | 106 private boolean mIsInitialized = false; |
| 107 private boolean mSavedSpeakerPhoneState; | |
| 108 private boolean mSavedMicrophoneMuteState; | |
| 39 | 109 |
| 40 @CalledByNative | 110 private Integer mAudioDeviceState = STATE_NO_DEVICE_SELECTED; |
| 41 public void setMode(int mode) { | |
| 42 try { | |
| 43 mAudioManager.setMode(mode); | |
| 44 } catch (SecurityException e) { | |
| 45 Log.e(TAG, "setMode exception: " + e.getMessage()); | |
| 46 logDeviceInfo(); | |
| 47 } | |
| 48 } | |
| 49 | 111 |
| 112 // Contains a list of currently available audio devices. | |
| 113 private Set<Integer> mAudioDevices = new HashSet<Integer>(); | |
| 114 | |
| 115 // Broadcast receiver for wired headset intent broadcasts. | |
| 116 private BroadcastReceiver mWiredHeadsetReceiver; | |
| 117 | |
| 118 /** Construction */ | |
| 50 @CalledByNative | 119 @CalledByNative |
| 51 private static AudioManagerAndroid createAudioManagerAndroid(Context context ) { | 120 private static AudioManagerAndroid createAudioManagerAndroid(Context context ) { |
| 52 return new AudioManagerAndroid(context); | 121 return new AudioManagerAndroid(context); |
| 53 } | 122 } |
| 54 | 123 |
| 55 private AudioManagerAndroid(Context context) { | 124 private AudioManagerAndroid(Context context) { |
| 56 mContext = context; | 125 mContext = context; |
| 57 mAudioManager = (AudioManager)mContext.getSystemService(Context.AUDIO_SE RVICE); | 126 mAudioManager = (AudioManager)mContext.getSystemService(Context.AUDIO_SE RVICE); |
| 58 } | 127 } |
| 59 | 128 |
| 129 /** | |
| 130 * Saves the initial speakerphone and microphone state. | |
| 131 * Populates the list of available audio devices and registers receivers | |
| 132 * for broadcasted intents related to wired headset and bluetooth devices. | |
| 133 */ | |
| 60 @CalledByNative | 134 @CalledByNative |
| 61 public void registerHeadsetReceiver() { | 135 public void init() { |
| 62 if (mReceiver != null) { | 136 if (mIsInitialized) |
| 63 return; | 137 return; |
| 138 | |
| 139 // Store microphone mute state and speakerphone state so it can | |
| 140 // be restored when closing. | |
| 141 mSavedSpeakerPhoneState = mAudioManager.isSpeakerphoneOn(); | |
|
Jói
2013/11/29 14:40:46
Naming suggestion: mSavedIsSpeakerphoneOn and mSav
henrika (OOO until Aug 14)
2013/11/29 15:31:49
thx
| |
| 142 mSavedMicrophoneMuteState = mAudioManager.isMicrophoneMute(); | |
| 143 | |
| 144 // Always enable speaker phone by default. This state might be reset | |
| 145 // by the wired headset receiver when it gets its initial sticky | |
| 146 // intent, if any. | |
| 147 setSpeakerphoneOn(true); | |
| 148 mAudioDeviceState = STATE_SPEAKERPHONE_ON; | |
| 149 | |
| 150 // Initialize audio device list with things we know is always available. | |
| 151 if (hasEarpiece()) { | |
| 152 mAudioDevices.add(DEVICE_EARPIECE); | |
| 64 } | 153 } |
| 154 mAudioDevices.add(DEVICE_SPEAKERPHONE); | |
| 65 | 155 |
| 66 mOriginalSpeakerStatus = mAudioManager.isSpeakerphoneOn(); | 156 // Register receiver for broadcasted intents related to adding/ |
| 67 if (!mOriginalSpeakerStatus) { | 157 // removing a wired headset (Intent.ACTION_HEADSET_PLUG). |
| 68 mAudioManager.setSpeakerphoneOn(true); | 158 // Also starts routing to the wired headset/headphone if one is |
| 69 } | 159 // already attached (can be overridden by a Bluetooth headset). |
| 70 IntentFilter filter = new IntentFilter(Intent.ACTION_HEADSET_PLUG); | 160 registerForWiredHeadsetIntentBroadcast(); |
| 71 | 161 |
| 72 mReceiver = new BroadcastReceiver() { | 162 // Start routing to Bluetooth if there's a connected device. |
| 73 @Override | 163 // TODO(henrika): the actual routing part is not implemented yet. |
| 74 public void onReceive(Context context, Intent intent) { | 164 // All we do currently is to detect if BT headset is attached or not. |
| 75 if (Intent.ACTION_HEADSET_PLUG.equals(intent.getAction())) { | 165 initBluetooth(); |
| 76 try { | 166 |
| 77 mAudioManager.setSpeakerphoneOn( | 167 mIsInitialized = true; |
| 78 intent.getIntExtra("state", 0) == 0); | 168 } |
| 79 } catch (SecurityException e) { | 169 |
| 80 Log.e(TAG, "setMode exception: " + e.getMessage()); | 170 /** |
| 81 logDeviceInfo(); | 171 * Unregister all previously registered intent receivers and restore |
| 82 } | 172 * the stored state (stored in {@link #init()}). |
| 83 } | 173 */ |
| 84 } | 174 @CalledByNative |
| 85 }; | 175 public void close() { |
| 86 mContext.registerReceiver(mReceiver, filter); | 176 if (!mIsInitialized) |
| 177 return; | |
| 178 | |
| 179 unregisterForWiredHeadsetIntentBroadcast(); | |
| 180 | |
| 181 // Restore previously stored audio states. | |
| 182 setMicrophoneMute(mSavedMicrophoneMuteState); | |
| 183 setSpeakerphoneOn(mSavedSpeakerPhoneState); | |
| 184 | |
| 185 mIsInitialized = false; | |
| 87 } | 186 } |
| 88 | 187 |
| 89 @CalledByNative | 188 @CalledByNative |
| 90 public void unregisterHeadsetReceiver() { | 189 public void setMode(int mode) { |
| 91 mContext.unregisterReceiver(mReceiver); | 190 try { |
| 92 mReceiver = null; | 191 mAudioManager.setMode(mode); |
| 93 mAudioManager.setSpeakerphoneOn(mOriginalSpeakerStatus); | 192 } catch (SecurityException e) { |
| 193 Log.e(TAG, "setMode exception: " + e.getMessage()); | |
| 194 logDeviceInfo(); | |
| 195 } | |
| 94 } | 196 } |
| 95 | 197 |
| 96 private void logDeviceInfo() { | 198 /** |
| 97 Log.i(TAG, "Manufacturer:" + Build.MANUFACTURER + | 199 * Activates, i.e., starts routing audio to, the specified audio device. |
| 98 " Board: " + Build.BOARD + " Device: " + Build.DEVICE + | 200 * |
| 99 " Model: " + Build.MODEL + " PRODUCT: " + Build.PRODUCT); | 201 * @param deviceId Unique device ID (integer converted to string) |
| 202 * representing the selected device. This string is empty if the so-called | |
| 203 * default device is selected. | |
| 204 */ | |
| 205 @CalledByNative | |
| 206 public void setDevice(String deviceId) { | |
| 207 if (deviceId.isEmpty()) { | |
| 208 logd("setDevice: default"); | |
| 209 // Use a special selection scheme if the default device is selected. | |
| 210 // The "most unique" device will be selected; Bluetooth first, then | |
| 211 // wired headset and last the speaker phone. | |
| 212 if (mAudioDevices.contains(DEVICE_BLUETOOTH_HEADSET)) { | |
| 213 // TODO(henrika): possibly need improvements here if we are | |
| 214 // in a STATE_BLUETOOTH_TURNING_OFF state. | |
| 215 setAudioDevice(DEVICE_BLUETOOTH_HEADSET); | |
| 216 } else if (mAudioDevices.contains(DEVICE_WIRED_HEADSET)) { | |
| 217 setAudioDevice(DEVICE_WIRED_HEADSET); | |
| 218 } else { | |
| 219 setAudioDevice(DEVICE_SPEAKERPHONE); | |
| 220 } | |
| 221 } else { | |
| 222 logd("setDevice: " + deviceId); | |
| 223 // A non-default device is specified. Verify that it is valid | |
| 224 // device, and if so, start using it. | |
| 225 List<Integer> validIds = Arrays.asList(VALID_DEVICES); | |
| 226 Integer id = Integer.valueOf(deviceId); | |
| 227 if (validIds.contains(id)) { | |
| 228 setAudioDevice(id.intValue()); | |
| 229 } else { | |
| 230 loge("Invalid device ID!"); | |
| 231 } | |
| 232 } | |
| 233 } | |
| 234 | |
| 235 /** | |
| 236 * @return the current list of available audio devices. | |
| 237 * Note that this call does not trigger any update of the list of devices, | |
| 238 * it only copies the current state in to the output array. | |
| 239 */ | |
| 240 @CalledByNative | |
| 241 public AudioDeviceName[] getAudioInputDeviceNames() { | |
| 242 List<String> devices = new ArrayList<String>(); | |
| 243 AudioDeviceName[] array = new AudioDeviceName[mAudioDevices.size()]; | |
| 244 int i = 0; | |
| 245 for (Integer dev : mAudioDevices) { | |
| 246 array[i] = new AudioDeviceName(dev, DEVICE_NAMES[dev]); | |
| 247 devices.add(DEVICE_NAMES[dev]); | |
| 248 i++; | |
| 249 } | |
| 250 logd("getAudioInputDeviceNames: " + devices); | |
| 251 return array; | |
| 100 } | 252 } |
| 101 | 253 |
| 102 @CalledByNative | 254 @CalledByNative |
| 103 private int getNativeOutputSampleRate() { | 255 private int getNativeOutputSampleRate() { |
| 104 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.J ELLY_BEAN_MR1) { | 256 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.J ELLY_BEAN_MR1) { |
| 105 String sampleRateString = mAudioManager.getProperty( | 257 String sampleRateString = mAudioManager.getProperty( |
| 106 AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE); | 258 AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE); |
| 107 return (sampleRateString == null ? | 259 return (sampleRateString == null ? |
| 108 DEFAULT_SAMPLING_RATE : Integer.parseInt(sampleRateString)); | 260 DEFAULT_SAMPLING_RATE : Integer.parseInt(sampleRateString)); |
| 109 } else { | 261 } else { |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 158 } | 310 } |
| 159 | 311 |
| 160 @CalledByNative | 312 @CalledByNative |
| 161 private int getAudioLowLatencyOutputFrameSize() { | 313 private int getAudioLowLatencyOutputFrameSize() { |
| 162 String framesPerBuffer = | 314 String framesPerBuffer = |
| 163 mAudioManager.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PE R_BUFFER); | 315 mAudioManager.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PE R_BUFFER); |
| 164 return (framesPerBuffer == null ? | 316 return (framesPerBuffer == null ? |
| 165 DEFAULT_FRAME_PER_BUFFER : Integer.parseInt(framesPerBuffer)); | 317 DEFAULT_FRAME_PER_BUFFER : Integer.parseInt(framesPerBuffer)); |
| 166 } | 318 } |
| 167 | 319 |
| 320 /** Sets the speaker phone mode. */ | |
| 321 public void setSpeakerphoneOn(boolean on) { | |
| 322 boolean wasOn = mAudioManager.isSpeakerphoneOn(); | |
| 323 if (wasOn == on) { | |
| 324 return; | |
| 325 } | |
| 326 mAudioManager.setSpeakerphoneOn(on); | |
| 327 } | |
| 328 | |
| 329 /** Sets the microphone mute state. */ | |
| 330 public void setMicrophoneMute(boolean on) { | |
| 331 boolean wasOn = mAudioManager.isMicrophoneMute(); | |
|
Jói
2013/11/29 14:40:46
nit: wasMuted maybe instead of wasOn?
henrika (OOO until Aug 14)
2013/11/29 15:31:49
Done.
| |
| 332 if (wasOn == on) { | |
| 333 return; | |
| 334 } | |
| 335 mAudioManager.setMicrophoneMute(on); | |
| 336 } | |
| 337 | |
| 338 /** Gets the current microphone mute state. */ | |
| 339 public boolean isMicrophoneMute() { | |
| 340 return mAudioManager.isMicrophoneMute(); | |
| 341 } | |
| 342 | |
| 343 /** Gets the current earpice state. */ | |
| 344 private boolean hasEarpiece() { | |
| 345 boolean hasFeature = mContext.getPackageManager().hasSystemFeature( | |
|
Jói
2013/11/29 14:40:46
could just return directly
henrika (OOO until Aug 14)
2013/11/29 15:31:49
Done.
| |
| 346 PackageManager.FEATURE_TELEPHONY); | |
| 347 return hasFeature; | |
| 348 } | |
| 349 | |
| 350 /** | |
| 351 * Registers receiver for the broadcasted intent when a wired headset is | |
| 352 * plugged in or unplugged. The received intent will have an extra | |
| 353 * 'state' value where 0 means unplugged, and 1 means plugged. | |
| 354 */ | |
| 355 private void registerForWiredHeadsetIntentBroadcast() { | |
| 356 IntentFilter filter = new IntentFilter(); | |
| 357 filter.addAction(Intent.ACTION_HEADSET_PLUG); | |
| 358 | |
| 359 /** | |
| 360 * Receiver which handles changes in wired headset availablilty. | |
| 361 */ | |
| 362 mWiredHeadsetReceiver = new BroadcastReceiver() { | |
| 363 private static final int STATE_UNPLUGGED = 0; | |
| 364 private static final int STATE_PLUGGED = 1; | |
| 365 private static final int HAS_NO_MIC = 0; | |
| 366 private static final int HAS_MIC = 1; | |
| 367 | |
| 368 @Override | |
| 369 public void onReceive(Context context, Intent intent) { | |
| 370 String action = intent.getAction(); | |
| 371 if (action.equals(Intent.ACTION_HEADSET_PLUG)) { | |
|
Jói
2013/11/29 14:40:46
Will it not always equal this, since you have a fi
tommi (sloooow) - chröme
2013/11/29 15:04:33
+1 - and if it isn't then it might be better to sa
henrika (OOO until Aug 14)
2013/11/29 15:31:49
You guys ;-)
| |
| 372 int state = intent.getIntExtra("state", STATE_UNPLUGGED); | |
| 373 int microphone = intent.getIntExtra("microphone", HAS_NO_MIC ); | |
| 374 String name = intent.getStringExtra("name"); | |
| 375 logd("==> onReceive: s=" + state | |
| 376 + ", m=" + microphone | |
| 377 + ", n=" + name | |
| 378 + ", s=" + isInitialStickyBroadcast()); | |
| 379 | |
| 380 switch (state) { | |
| 381 case STATE_UNPLUGGED: | |
| 382 // Wired headset and earpiece are mutually exclusive . | |
| 383 mAudioDevices.remove(DEVICE_WIRED_HEADSET); | |
| 384 if (hasEarpiece()) { | |
| 385 mAudioDevices.add(DEVICE_EARPIECE); | |
| 386 } | |
| 387 // If wired headset was used before it was unplugged , | |
| 388 // switch to speaker phone. If it was not in use; ju st | |
| 389 // log the change. | |
| 390 if (mAudioDeviceState == STATE_WIRED_HEADSET_ON) { | |
| 391 setAudioDevice(DEVICE_SPEAKERPHONE); | |
| 392 } else { | |
| 393 reportUpdate(); | |
| 394 } | |
| 395 break; | |
| 396 case STATE_PLUGGED: | |
| 397 // Wired headset and earpiece are mutually exclusive . | |
| 398 mAudioDevices.add(DEVICE_WIRED_HEADSET); | |
| 399 mAudioDevices.remove(DEVICE_EARPIECE); | |
| 400 setAudioDevice(DEVICE_WIRED_HEADSET); | |
| 401 break; | |
| 402 } | |
| 403 } | |
| 404 } | |
| 405 }; | |
| 406 | |
| 407 // Note: the intent we register for here is sticky, so it'll tell us | |
| 408 // immediately what the last action was (plugged or unplugged). | |
| 409 // It will enable us to set the speakerphone correctly. | |
| 410 mContext.registerReceiver(mWiredHeadsetReceiver, filter); | |
| 411 } | |
| 412 | |
| 413 /** Unregister receiver for broadcasted ACTION_HEADSET_PLUG intent. */ | |
| 414 private void unregisterForWiredHeadsetIntentBroadcast() { | |
| 415 mContext.unregisterReceiver(mWiredHeadsetReceiver); | |
| 416 mWiredHeadsetReceiver = null; | |
| 417 } | |
| 418 | |
| 419 | |
|
Jói
2013/11/29 14:40:46
nit: just one blank line?
henrika (OOO until Aug 14)
2013/11/29 15:31:49
Done.
| |
| 420 /** | |
| 421 * Check if Bluetooth device is connected, register Bluetooth receiver | |
| 422 * and start routing to Bluetooth if a device is connected. | |
| 423 * TODO(henrika): currently only supports the detecion part at startup. | |
| 424 */ | |
| 425 private void initBluetooth() { | |
| 426 // Bail out if we don't have the required permission. | |
| 427 mHasBluetoothPermission = mContext.checkPermission( | |
| 428 android.Manifest.permission.BLUETOOTH, | |
| 429 Process.myPid(), | |
| 430 Process.myUid()) == PackageManager.PERMISSION_GRANTED; | |
| 431 if (!mHasBluetoothPermission) { | |
| 432 loge("BLUETOOTH permission is missing!"); | |
| 433 return; | |
| 434 } | |
| 435 | |
| 436 // To get a BluetoothAdapter representing the local Bluetooth adapter, | |
| 437 // when running on JELLY_BEAN_MR1 (4.2) and below, call the static | |
| 438 // getDefaultAdapter() method; when running on JELLY_BEAN_MR2 (4.3) and | |
| 439 // higher, retrieve it through getSystemService(String) with | |
| 440 // BLUETOOTH_SERVICE. | |
| 441 // Note: Most methods require the BLUETOOTH permission. | |
| 442 BluetoothAdapter btAdapter = null; | |
| 443 if (android.os.Build.VERSION.SDK_INT <= | |
| 444 android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) { | |
| 445 // Use static method for Android 4.2 and below to get the | |
| 446 // BluetoothAdapter. | |
| 447 btAdapter = BluetoothAdapter.getDefaultAdapter(); | |
| 448 } else { | |
| 449 // Use BluetoothManager to get the BluetoothAdapter for | |
| 450 // Android 4.3 and above. | |
| 451 BluetoothManager btManager = | |
| 452 (BluetoothManager)mContext.getSystemService( | |
| 453 Context.BLUETOOTH_SERVICE); | |
| 454 btAdapter = btManager.getAdapter(); | |
| 455 } | |
| 456 | |
| 457 if (btAdapter != null && | |
| 458 // android.bluetooth.BluetoothAdapter.getProfileConnectionState | |
| 459 // requires BLUETOOTH permission. | |
| 460 android.bluetooth.BluetoothProfile.STATE_CONNECTED == | |
| 461 btAdapter.getProfileConnectionState( | |
| 462 android.bluetooth.BluetoothProfile.HEADSET)) { | |
| 463 mAudioDevices.add(DEVICE_BLUETOOTH_HEADSET); | |
| 464 // TODO(henrika): ensure that we set the active audio | |
| 465 // device to Bluetooth (not trivial). | |
| 466 setAudioDevice(DEVICE_BLUETOOTH_HEADSET); | |
| 467 } | |
| 468 } | |
| 469 | |
| 470 /** | |
| 471 * Changes selection of the currently active audio device. | |
| 472 * | |
| 473 * @param device Specifies the selected audio device. | |
| 474 */ | |
| 475 public void setAudioDevice(int device) { | |
| 476 switch (device) { | |
| 477 case DEVICE_BLUETOOTH_HEADSET: | |
| 478 // TODO(henrika): add support for turning on an routing to | |
| 479 // BT here. | |
| 480 logd("--- TO BE IMPLEMENTED ---"); | |
|
tommi (sloooow) - chröme
2013/11/29 15:04:33
use |if (DEBUG)| for debug logging?
henrika (OOO until Aug 14)
2013/11/29 15:31:49
Forgot, will do.
| |
| 481 break; | |
| 482 case DEVICE_SPEAKERPHONE: | |
| 483 // TODO(henrika): turn off BT if required. | |
| 484 mAudioDeviceState = STATE_SPEAKERPHONE_ON; | |
| 485 setSpeakerphoneOn(true); | |
| 486 break; | |
| 487 case DEVICE_WIRED_HEADSET: | |
| 488 // TODO(henrika): turn off BT if required. | |
| 489 mAudioDeviceState = STATE_WIRED_HEADSET_ON; | |
| 490 setSpeakerphoneOn(false); | |
| 491 break; | |
| 492 case DEVICE_EARPIECE: | |
| 493 // TODO(henrika): turn off BT if required. | |
| 494 mAudioDeviceState = STATE_EARPIECE_ON; | |
| 495 setSpeakerphoneOn(false); | |
| 496 break; | |
| 497 default: | |
| 498 loge("Invalid audio device selection!"); | |
| 499 break; | |
| 500 } | |
| 501 reportUpdate(); | |
| 502 } | |
| 503 | |
| 504 /** | |
| 505 * For now, just log the state change but the idea is that we should | |
| 506 * notifies a registered state change listener (if any) that there has | |
| 507 * been a change in the state. | |
| 508 * TODO(henrika): add support for state change listener. | |
| 509 */ | |
| 510 private void reportUpdate() { | |
| 511 List<String> devices = new ArrayList<String>(); | |
| 512 for (Integer dev : mAudioDevices) { | |
| 513 devices.add(DEVICE_NAMES[dev]); | |
| 514 } | |
| 515 logd("reportUpdate: state=" + mAudioDeviceState | |
| 516 + ", devices=" + devices); | |
| 517 } | |
| 518 | |
| 519 private void logDeviceInfo() { | |
| 520 Log.i(TAG, "Manufacturer:" + Build.MANUFACTURER + | |
| 521 " Board: " + Build.BOARD + " Device: " + Build.DEVICE + | |
| 522 " Model: " + Build.MODEL + " PRODUCT: " + Build.PRODUCT); | |
| 523 } | |
| 524 | |
| 525 /** Trivial helper method for debug logging */ | |
| 526 private void logd(String msg) { | |
| 527 Log.d(TAG, msg); | |
| 528 } | |
| 529 | |
| 530 /** Trivial helper method for error logging */ | |
| 531 private void loge(String msg) { | |
| 532 Log.e(TAG, msg); | |
| 533 } | |
| 168 } | 534 } |
| OLD | NEW |