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

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

Issue 78033003: Adding device enumeration to Android device manager (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: tommi@, 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;
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;
17 import android.util.Log; 19 import android.util.Log;
18 20
21 import java.util.Arrays;
22 import java.util.ArrayList;
23 import java.util.HashSet;
24 import java.util.List;
25 import java.util.Set;
26
19 import org.chromium.base.CalledByNative; 27 import org.chromium.base.CalledByNative;
20 import org.chromium.base.JNINamespace; 28 import org.chromium.base.JNINamespace;
21 29
22 @JNINamespace("media") 30 @JNINamespace("media")
23 class AudioManagerAndroid { 31 class AudioManagerAndroid {
24 private static final String TAG = "AudioManagerAndroid"; 32 private static final String TAG = "AudioManagerAndroid";
25 33
34 /**
35 * Lists the possible types (and names) of audio device that we support
36 * on Android.
37 */
38 private enum AudioDevice {
39 SPEAKERPHONE("Speaker"),
40 WIRED_HEADSET("Wired headphones"),
41 EARPIECE("Handset earpice"),
42 BLUETOOTH_HEADSET("Bluetooth headset");
43
44 private final String name;
45
46 private AudioDevice(String s) {
47 name = s;
48 }
49
50 @Override
51 public String toString() {
52 return name;
53 }
54
55 public AudioDevice fromString(String name) {
56 if (name == null)
57 return null;
58 for (AudioDevice a : AudioDevice.values()) {
59 if (name.equalsIgnoreCase(a.name)) {
60 return a;
61 }
62 }
63 return null;
64 }
65 }
66
26 // Most of Google lead devices use 44.1K as the default sampling rate, 44.1K 67 // Most of Google lead devices use 44.1K as the default sampling rate, 44.1K
27 // is also widely used on other android devices. 68 // is also widely used on other android devices.
28 private static final int DEFAULT_SAMPLING_RATE = 44100; 69 private static final int DEFAULT_SAMPLING_RATE = 44100;
29 // Randomly picked up frame size which is close to return value on N4. 70 // Randomly picked up frame size which is close to return value on N4.
30 // Return this default value when 71 // Return this default value when
31 // getProperty(PROPERTY_OUTPUT_FRAMES_PER_BUFFER) fails. 72 // getProperty(PROPERTY_OUTPUT_FRAMES_PER_BUFFER) fails.
32 private static final int DEFAULT_FRAME_PER_BUFFER = 256; 73 private static final int DEFAULT_FRAME_PER_BUFFER = 256;
33 74
75 private boolean mIsInitialized = false;
76
34 private final AudioManager mAudioManager; 77 private final AudioManager mAudioManager;
35 private final Context mContext; 78 private final Context mContext;
36 79
37 private BroadcastReceiver mReceiver; 80 private BroadcastReceiver mReceiver;
38 private boolean mOriginalSpeakerStatus; 81 private boolean mOriginalSpeakerStatus;
39 82
83 // Use Set to ensure that we don't store duplicates. Ignores order.
84 private Set<AudioDevice> mAudioDevices = new HashSet<AudioDevice>();
85
86 /**
87 * Class to handle changes in wired headset availablilty.
88 *
89 * Note that isInitialStickyBroadcast() returns true if the receiver is
90 * currently processing the initial value of a sticky broadcast. It can
91 * happen during startup when the cached state us pushed out to us an
92 * initial "sticky broadcast".
93 */
94 private BroadcastReceiver mWiredHeadsetReceiver = new BroadcastReceiver() {
95 private static final int STATE_UNPLUGGED = 0;
96 private static final int STATE_PLUGGED = 1;
97
98 @Override
99 public void onReceive(Context context, Intent intent) {
100 String action = intent.getAction();
101 if (action.equals(Intent.ACTION_HEADSET_PLUG)) {
102 int state = intent.getIntExtra("state", STATE_UNPLUGGED);
103
104 logd("==> WiredHeadsetReceiver.onReceive: state=" + state
105 + ", isInitialStickyBroadcast="
106 + isInitialStickyBroadcast());
107
108 switch (state) {
109 case STATE_UNPLUGGED:
110 // Wired headset and earpiece are mutually-exclusive.
111 mAudioDevices.remove(AudioDevice.WIRED_HEADSET);
112 if (hasEarpiece()) {
113 mAudioDevices.add(AudioDevice.EARPIECE);
114 }
115 // TODO(henrika): check if wired headset was used before
116 // it was unlpugged. If so, switch to speaker phone.
Jói 2013/11/25 13:09:35 unlpugged -> unplugged
henrika (OOO until Aug 14) 2013/11/25 13:28:14 Done.
117 // If'it was not in use, don't do anything.
Jói 2013/11/25 13:09:35 If'it -> If it
henrika (OOO until Aug 14) 2013/11/25 13:28:14 Done.
118 // Also, turn off BT if it was active at this stage.
119 break;
120 case STATE_PLUGGED:
121 // Wired headset and earpiece are mutually-exclusive.
122 mAudioDevices.add(AudioDevice.WIRED_HEADSET);
123 mAudioDevices.remove(AudioDevice.EARPIECE);
124 // TODO(henrika): ensure that the wired headset is active.
125 // Possibly turn BT off and also disable the speaker phone.
126 break;
127 }
128 }
129 }
130 };
131
40 @CalledByNative 132 @CalledByNative
41 public void setMode(int mode) { 133 public void setMode(int mode) {
42 try { 134 try {
43 mAudioManager.setMode(mode); 135 mAudioManager.setMode(mode);
44 } catch (SecurityException e) { 136 } catch (SecurityException e) {
45 Log.e(TAG, "setMode exception: " + e.getMessage()); 137 Log.e(TAG, "setMode exception: " + e.getMessage());
46 logDeviceInfo(); 138 logDeviceInfo();
47 } 139 }
48 } 140 }
49 141
50 @CalledByNative 142 @CalledByNative
51 private static AudioManagerAndroid createAudioManagerAndroid(Context context ) { 143 private static AudioManagerAndroid createAudioManagerAndroid(Context context ) {
52 return new AudioManagerAndroid(context); 144 return new AudioManagerAndroid(context);
53 } 145 }
54 146
55 private AudioManagerAndroid(Context context) { 147 private AudioManagerAndroid(Context context) {
56 mContext = context; 148 mContext = context;
57 mAudioManager = (AudioManager)mContext.getSystemService(Context.AUDIO_SE RVICE); 149 mAudioManager = (AudioManager)mContext.getSystemService(Context.AUDIO_SE RVICE);
58 } 150 }
59 151
152 /**
153 * Pupulate the list of available audio devices and register receivers
Jói 2013/11/25 13:09:35 Pupulate -> Populate
henrika (OOO until Aug 14) 2013/11/25 13:28:14 Done.
154 * for broadcasted intents related to wired headset and bluetooth devices.
155 * TODO(henrika): we should probably store an initial entry state here
156 * as well.
157 */
158 @CalledByNative
159 public void init() {
160 if (mIsInitialized)
161 return;
162
163 // Initialize audio device list with things we know is always available.
164 if (hasEarpiece()) {
165 mAudioDevices.add(AudioDevice.EARPIECE);
166 }
167 mAudioDevices.add(AudioDevice.SPEAKERPHONE);
168
169 registerForWiredHeadsetIntentBroadcast();
170 initBluetooth();
171
172 logd("init: devices=" + mAudioDevices);
173 mIsInitialized = true;
174 }
175
176 /**
177 * Unregister all previously registered intent receivers.
178 * TODO(henrika): we should probably restore the initial entry state here
179 * as well.
180 */
181 @CalledByNative
182 public void close() {
183 if (!mIsInitialized)
184 return;
185
186 logd("close");
187 unregisterForWiredHeadsetIntentBroadcast();
188 mIsInitialized = false;
189 }
190
191 @CalledByNative
192 public String[] getAudioOutputDeviceNames() {
193 logd("getAudioOutputDeviceNames");
194 return toStringArray(mAudioDevices);
195 }
196
60 @CalledByNative 197 @CalledByNative
61 public void registerHeadsetReceiver() { 198 public void registerHeadsetReceiver() {
62 if (mReceiver != null) { 199 if (mReceiver != null) {
63 return; 200 return;
64 } 201 }
65 202
66 mOriginalSpeakerStatus = mAudioManager.isSpeakerphoneOn(); 203 mOriginalSpeakerStatus = mAudioManager.isSpeakerphoneOn();
67 if (!mOriginalSpeakerStatus) { 204 if (!mOriginalSpeakerStatus) {
68 mAudioManager.setSpeakerphoneOn(true); 205 mAudioManager.setSpeakerphoneOn(true);
69 } 206 }
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after
158 } 295 }
159 296
160 @CalledByNative 297 @CalledByNative
161 private int getAudioLowLatencyOutputFrameSize() { 298 private int getAudioLowLatencyOutputFrameSize() {
162 String framesPerBuffer = 299 String framesPerBuffer =
163 mAudioManager.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PE R_BUFFER); 300 mAudioManager.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PE R_BUFFER);
164 return (framesPerBuffer == null ? 301 return (framesPerBuffer == null ?
165 DEFAULT_FRAME_PER_BUFFER : Integer.parseInt(framesPerBuffer)); 302 DEFAULT_FRAME_PER_BUFFER : Integer.parseInt(framesPerBuffer));
166 } 303 }
167 304
305 /**
306 * Returns true if the device has a headset earpice and false if not.
Jói 2013/11/25 13:09:35 I think the convention for JavaDoc comments is for
henrika (OOO until Aug 14) 2013/11/25 13:28:14 Done.
307 */
308 private boolean hasEarpiece() {
309 boolean hasFeature = mContext.getPackageManager().hasSystemFeature(
310 PackageManager.FEATURE_TELEPHONY);
311 return hasFeature;
312 }
313
314 /**
315 * Converts a set of audio devices into array of strings.
316 */
317 private static String[] toStringArray(Set<AudioDevice> devices) {
318 if (devices == null) {
319 return null;
320 }
321 String[] retVal = new String[devices.size()];
322 int i = 0;
323 for (AudioDevice device : devices) {
324 retVal[i] = device.toString();
325 i++;
326 }
327 return retVal;
328 }
329
330 /**
331 * Registers receiver for the broadcasted intent when a Wired Headset is
332 * plugged in or unplugged. The received intent will have an extra
333 * 'state' value where 0 means unplugged, and 1 means plugged.
334 */
335 private void registerForWiredHeadsetIntentBroadcast() {
336 logd("registerForWiredHeadsetIntentBroadcast");
337 IntentFilter filter = new IntentFilter();
338 filter.addAction(Intent.ACTION_HEADSET_PLUG);
339 mContext.registerReceiver(mWiredHeadsetReceiver, filter);
340 // Note: the intent we just registered for is sticky, so it'll tell us
341 // immediately what the last action was (plugged or unplugged).
342 // It will enable us to set the speakerphone correctly.
343 }
344
345 /**
346 * Unregister receiver for broadcasted ACTION_HEADSET_PLUG intent.
347 */
348 private void unregisterForWiredHeadsetIntentBroadcast() {
349 logd("unregisterForWiredHeadsetIntentBroadcast");
350 mContext.unregisterReceiver(mWiredHeadsetReceiver);
351 mWiredHeadsetReceiver = null;
352 }
353
354
355 /**
356 * Check if Bluetooth device is connected, register Bluetooth receiver
357 * and start routing to Bluetooth if a device is connected.
358 * TODO(henrika): currently only supports the detecion part.
359 */
360 private void initBluetooth() {
361 logd("initBluetooth");
362
363 // To get a BluetoothAdapter representing the local Bluetooth adapter,
364 // when running on JELLY_BEAN_MR1 (4.2) and below, call the static
365 // getDefaultAdapter() method; when running on JELLY_BEAN_MR2 (4.3) and
366 // higher, retrieve it through getSystemService(String) with
367 // BLUETOOTH_SERVICE.
368 // Note: Most methods require the BLUETOOTH permission.
369 BluetoothAdapter btAdapter = null;
370 if (android.os.Build.VERSION.SDK_INT <=
371 android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
372 // Use static method for Android 4.2 and below to get the
373 // BluetoothAdapter.
374 btAdapter = BluetoothAdapter.getDefaultAdapter();
375 } else {
376 // Use BluetoothManager to get the BluetoothAdapter for
377 // Android 4.3 and above.
378 BluetoothManager btManager =
379 (BluetoothManager)mContext.getSystemService(
380 Context.BLUETOOTH_SERVICE);
381 btAdapter = btManager.getAdapter();
382 }
383
384 if (btAdapter != null &&
385 android.bluetooth.BluetoothProfile.STATE_CONNECTED ==
386 btAdapter.getProfileConnectionState(
387 android.bluetooth.BluetoothProfile.HEADSET)) {
388 logd("BT device was connected at start of call");
Jói 2013/11/25 13:09:35 The indent here seems off, shouldn't it be 4 in fr
henrika (OOO until Aug 14) 2013/11/25 13:28:14 Done.
389 mAudioDevices.add(AudioDevice.BLUETOOTH_HEADSET);
390 // TODO(henrika): ensure that we set the active audio
391 // device to Bluetooth (not trivial).
392 // setAudioDevice(AudioDevice.BLUETOOTH_HEADSET);
393 }
394 }
395
396 /**
397 * Trivial helper method for debug logging.
398 */
399 private void logd(String msg) {
400 Log.d(TAG, msg);
401 }
168 } 402 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698