- add sources.
[platform/framework/web/crosswalk.git] / src / media / base / android / java / src / org / chromium / media / AudioManagerAndroid.java
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.
4
5 package org.chromium.media;
6
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;
25
26 import org.chromium.base.CalledByNative;
27 import org.chromium.base.JNINamespace;
28
29 @JNINamespace("media")
30 class AudioManagerAndroid {
31     private static final String TAG = "AudioManagerAndroid";
32
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;
40
41     private final AudioManager mAudioManager;
42     private final Context mContext;
43     private final int mNativeAudioManagerAndroid;
44
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();
50
51     private BroadcastReceiver mReceiver;
52     private boolean mOriginalSpeakerStatus;
53
54     @CalledByNative
55     public void setMode(int mode) {
56         try {
57             mAudioManager.setMode(mode);
58         } catch (SecurityException e) {
59             Log.e(TAG, "setMode exception: " + e.getMessage());
60             logDeviceInfo();
61         }
62     }
63
64     @CalledByNative
65     private static AudioManagerAndroid createAudioManagerAndroid(
66             Context context,
67             int nativeAudioManagerAndroid) {
68         return new AudioManagerAndroid(context, nativeAudioManagerAndroid);
69     }
70
71     private AudioManagerAndroid(Context context, int nativeAudioManagerAndroid) {
72         mContext = context;
73         mNativeAudioManagerAndroid = nativeAudioManagerAndroid;
74         mAudioManager = (AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE);
75         mContentResolver = mContext.getContentResolver();
76     }
77
78     @CalledByNative
79     public void registerHeadsetReceiver() {
80         if (mReceiver != null) {
81             return;
82         }
83
84         mOriginalSpeakerStatus = mAudioManager.isSpeakerphoneOn();
85         IntentFilter filter = new IntentFilter(Intent.ACTION_HEADSET_PLUG);
86
87         mReceiver = new BroadcastReceiver() {
88             @Override
89             public void onReceive(Context context, Intent intent) {
90                 if (Intent.ACTION_HEADSET_PLUG.equals(intent.getAction())) {
91                     try {
92                         mAudioManager.setSpeakerphoneOn(
93                                 intent.getIntExtra("state", 0) == 0);
94                     } catch (SecurityException e) {
95                         Log.e(TAG, "setMode exception: " + e.getMessage());
96                         logDeviceInfo();
97                     }
98                 }
99             }
100         };
101         mContext.registerReceiver(mReceiver, filter);
102
103         mSettingsObserverThread = new SettingsObserverThread();
104         mSettingsObserverThread.start();
105         synchronized(mSettingsObserverLock) {
106             try {
107                 mSettingsObserverLock.wait();
108             } catch (InterruptedException e) {
109                 Log.e(TAG, "unregisterHeadsetReceiver exception: " + e.getMessage());
110             }
111         }
112     }
113
114     @CalledByNative
115     public void unregisterHeadsetReceiver() {
116         mContext.unregisterReceiver(mReceiver);
117         mReceiver = null;
118         mAudioManager.setSpeakerphoneOn(mOriginalSpeakerStatus);
119
120         if (mSettingsObserverThread != null ) {
121             mSettingsObserverThread = null;
122         }
123         if (mSettingsObserver != null) {
124             mContentResolver.unregisterContentObserver(mSettingsObserver);
125             mSettingsObserver = null;
126         }
127     }
128
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);
133     }
134
135     @CalledByNative
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));
142         } else {
143             return DEFAULT_SAMPLING_RATE;
144         }
145     }
146
147   /**
148    * Returns the minimum frame size required for audio input.
149    *
150    * @param sampleRate sampling rate
151    * @param channels number of channels
152    */
153     @CalledByNative
154     private static int getMinInputFrameSize(int sampleRate, int channels) {
155         int channelConfig;
156         if (channels == 1) {
157             channelConfig = AudioFormat.CHANNEL_IN_MONO;
158         } else if (channels == 2) {
159             channelConfig = AudioFormat.CHANNEL_IN_STEREO;
160         } else {
161             return -1;
162         }
163         return AudioRecord.getMinBufferSize(
164                 sampleRate, channelConfig, AudioFormat.ENCODING_PCM_16BIT) / 2 / channels;
165     }
166
167   /**
168    * Returns the minimum frame size required for audio output.
169    *
170    * @param sampleRate sampling rate
171    * @param channels number of channels
172    */
173     @CalledByNative
174     private static int getMinOutputFrameSize(int sampleRate, int channels) {
175         int channelConfig;
176         if (channels == 1) {
177             channelConfig = AudioFormat.CHANNEL_OUT_MONO;
178         } else if (channels == 2) {
179             channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
180         } else {
181             return -1;
182         }
183         return AudioTrack.getMinBufferSize(
184                 sampleRate, channelConfig, AudioFormat.ENCODING_PCM_16BIT) / 2 / channels;
185     }
186
187     @CalledByNative
188     private boolean isAudioLowLatencySupported() {
189         return mContext.getPackageManager().hasSystemFeature(
190                 PackageManager.FEATURE_AUDIO_LOW_LATENCY);
191     }
192
193     @CalledByNative
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));
199     }
200
201     private class SettingsObserver extends ContentObserver {
202         SettingsObserver() {
203             super(new Handler());
204             mContentResolver.registerContentObserver(Settings.System.CONTENT_URI, true, this);
205         }
206
207         @Override
208         public void onChange(boolean selfChange) {
209             super.onChange(selfChange);
210             int volume = mAudioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL);
211             nativeSetMute(mNativeAudioManagerAndroid, (volume == 0));
212         }
213     }
214
215     private native void nativeSetMute(int nativeAudioManagerAndroid, boolean muted);
216
217     private class SettingsObserverThread extends Thread {
218         SettingsObserverThread() {
219             super("SettingsObserver");
220         }
221
222         @Override
223         public void run() {
224             // Set this thread up so the handler will work on it.
225             Looper.prepare();
226
227             synchronized(mSettingsObserverLock) {
228                 mSettingsObserver = new SettingsObserver();
229                 mSettingsObserverLock.notify();
230             }
231
232             // Listen for volume change.
233             Looper.loop();
234         }
235     }
236 }