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
3 // found in the LICENSE file.
5 package org.chromium.media;
7 import android.content.BroadcastReceiver;
8 import android.content.ContentResolver;
9 import android.content.Context;
10 import android.content.Intent;
11 import android.content.IntentFilter;
12 import android.content.pm.PackageManager;
13 import android.database.ContentObserver;
14 import android.media.AudioFormat;
15 import android.media.AudioManager;
16 import android.media.AudioRecord;
17 import android.media.AudioTrack;
18 import android.net.Uri;
19 import android.os.Build;
20 import android.os.Handler;
21 import android.os.Looper;
22 import android.provider.Settings;
23 import android.provider.Settings.System;
24 import android.util.Log;
26 import org.chromium.base.CalledByNative;
27 import org.chromium.base.JNINamespace;
29 @JNINamespace("media")
30 class AudioManagerAndroid {
31 private static final String TAG = "AudioManagerAndroid";
33 // Most of Google lead devices use 44.1K as the default sampling rate, 44.1K
34 // is also widely used on other android devices.
35 private static final int DEFAULT_SAMPLING_RATE = 44100;
36 // Randomly picked up frame size which is close to return value on N4.
37 // Return this default value when
38 // getProperty(PROPERTY_OUTPUT_FRAMES_PER_BUFFER) fails.
39 private static final int DEFAULT_FRAME_PER_BUFFER = 256;
41 private final AudioManager mAudioManager;
42 private final Context mContext;
43 private final int mNativeAudioManagerAndroid;
45 private final ContentResolver mContentResolver;
46 private SettingsObserver mSettingsObserver = null;
47 private SettingsObserverThread mSettingsObserverThread = null;
48 private int mCurrentVolume;
49 private final Object mSettingsObserverLock = new Object();
51 private BroadcastReceiver mReceiver;
52 private boolean mOriginalSpeakerStatus;
55 public void setMode(int mode) {
57 mAudioManager.setMode(mode);
58 } catch (SecurityException e) {
59 Log.e(TAG, "setMode exception: " + e.getMessage());
65 private static AudioManagerAndroid createAudioManagerAndroid(
67 int nativeAudioManagerAndroid) {
68 return new AudioManagerAndroid(context, nativeAudioManagerAndroid);
71 private AudioManagerAndroid(Context context, int nativeAudioManagerAndroid) {
73 mNativeAudioManagerAndroid = nativeAudioManagerAndroid;
74 mAudioManager = (AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE);
75 mContentResolver = mContext.getContentResolver();
79 public void registerHeadsetReceiver() {
80 if (mReceiver != null) {
84 mOriginalSpeakerStatus = mAudioManager.isSpeakerphoneOn();
85 IntentFilter filter = new IntentFilter(Intent.ACTION_HEADSET_PLUG);
87 mReceiver = new BroadcastReceiver() {
89 public void onReceive(Context context, Intent intent) {
90 if (Intent.ACTION_HEADSET_PLUG.equals(intent.getAction())) {
92 mAudioManager.setSpeakerphoneOn(
93 intent.getIntExtra("state", 0) == 0);
94 } catch (SecurityException e) {
95 Log.e(TAG, "setMode exception: " + e.getMessage());
101 mContext.registerReceiver(mReceiver, filter);
103 mSettingsObserverThread = new SettingsObserverThread();
104 mSettingsObserverThread.start();
105 synchronized(mSettingsObserverLock) {
107 mSettingsObserverLock.wait();
108 } catch (InterruptedException e) {
109 Log.e(TAG, "unregisterHeadsetReceiver exception: " + e.getMessage());
115 public void unregisterHeadsetReceiver() {
116 mContext.unregisterReceiver(mReceiver);
118 mAudioManager.setSpeakerphoneOn(mOriginalSpeakerStatus);
120 if (mSettingsObserverThread != null ) {
121 mSettingsObserverThread = null;
123 if (mSettingsObserver != null) {
124 mContentResolver.unregisterContentObserver(mSettingsObserver);
125 mSettingsObserver = null;
129 private void logDeviceInfo() {
130 Log.i(TAG, "Manufacturer:" + Build.MANUFACTURER +
131 " Board: " + Build.BOARD + " Device: " + Build.DEVICE +
132 " Model: " + Build.MODEL + " PRODUCT: " + Build.PRODUCT);
136 private int getNativeOutputSampleRate() {
137 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
138 String sampleRateString = mAudioManager.getProperty(
139 AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE);
140 return (sampleRateString == null ?
141 DEFAULT_SAMPLING_RATE : Integer.parseInt(sampleRateString));
143 return DEFAULT_SAMPLING_RATE;
148 * Returns the minimum frame size required for audio input.
150 * @param sampleRate sampling rate
151 * @param channels number of channels
154 private static int getMinInputFrameSize(int sampleRate, int channels) {
157 channelConfig = AudioFormat.CHANNEL_IN_MONO;
158 } else if (channels == 2) {
159 channelConfig = AudioFormat.CHANNEL_IN_STEREO;
163 return AudioRecord.getMinBufferSize(
164 sampleRate, channelConfig, AudioFormat.ENCODING_PCM_16BIT) / 2 / channels;
168 * Returns the minimum frame size required for audio output.
170 * @param sampleRate sampling rate
171 * @param channels number of channels
174 private static int getMinOutputFrameSize(int sampleRate, int channels) {
177 channelConfig = AudioFormat.CHANNEL_OUT_MONO;
178 } else if (channels == 2) {
179 channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
183 return AudioTrack.getMinBufferSize(
184 sampleRate, channelConfig, AudioFormat.ENCODING_PCM_16BIT) / 2 / channels;
188 private boolean isAudioLowLatencySupported() {
189 return mContext.getPackageManager().hasSystemFeature(
190 PackageManager.FEATURE_AUDIO_LOW_LATENCY);
194 private int getAudioLowLatencyOutputFrameSize() {
195 String framesPerBuffer =
196 mAudioManager.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER);
197 return (framesPerBuffer == null ?
198 DEFAULT_FRAME_PER_BUFFER : Integer.parseInt(framesPerBuffer));
201 private class SettingsObserver extends ContentObserver {
203 super(new Handler());
204 mContentResolver.registerContentObserver(Settings.System.CONTENT_URI, true, this);
208 public void onChange(boolean selfChange) {
209 super.onChange(selfChange);
210 int volume = mAudioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL);
211 nativeSetMute(mNativeAudioManagerAndroid, (volume == 0));
215 private native void nativeSetMute(int nativeAudioManagerAndroid, boolean muted);
217 private class SettingsObserverThread extends Thread {
218 SettingsObserverThread() {
219 super("SettingsObserver");
224 // Set this thread up so the handler will work on it.
227 synchronized(mSettingsObserverLock) {
228 mSettingsObserver = new SettingsObserver();
229 mSettingsObserverLock.notify();
232 // Listen for volume change.