This source file includes following definitions.
- JNINamespace
- calledOnValidThread
- runningOnJellyBeanOrHigher
- runningOnJellyBeanMR1OrHigher
- runningOnJellyBeanMR2OrHigher
- CalledByNative
- id
- CalledByNative
- name
- createAudioManagerAndroid
- init
- close
- setCommunicationAudioModeOn
- setDevice
- getAudioInputDeviceNames
- getNativeOutputSampleRate
- getMinInputFrameSize
- getMinOutputFrameSize
- isAudioLowLatencySupported
- getAudioLowLatencyOutputFrameSize
- shouldUseAcousticEchoCanceler
- checkIfCalledOnValidThread
- registerBluetoothIntentsIfNeeded
- unregisterBluetoothIntentsIfNeeded
- setSpeakerphoneOn
- setMicrophoneMute
- isMicrophoneMute
- hasEarpiece
- hasWiredHeadset
- hasBluetoothPermission
- hasBluetoothHeadset
- registerForWiredHeadsetIntentBroadcast
- unregisterForWiredHeadsetIntentBroadcast
- registerForBluetoothHeadsetIntentBroadcast
- unregisterForBluetoothHeadsetIntentBroadcast
- registerForBluetoothScoIntentBroadcast
- unregisterForBluetoothScoIntentBroadcast
- startBluetoothSco
- stopBluetoothSco
- setAudioDevice
- selectDefaultDevice
- deviceHasBeenRequested
- updateDeviceActivation
- getNumOfAudioDevices
- reportUpdate
- logDeviceInfo
- logd
- loge
- startObservingVolumeChanges
- stopObservingVolumeChanges
- nativeSetMute
package org.chromium.media;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothManager;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.database.ContentObserver;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.AudioTrack;
import android.media.audiofx.AcousticEchoCanceler;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Process;
import android.provider.Settings;
import android.util.Log;
import org.chromium.base.CalledByNative;
import org.chromium.base.JNINamespace;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@JNINamespace("media")
class AudioManagerAndroid {
private static final String TAG = "AudioManagerAndroid";
private static final boolean DEBUG = false;
private static class NonThreadSafe {
private final Long mThreadId;
public NonThreadSafe() {
if (DEBUG) {
mThreadId = Thread.currentThread().getId();
} else {
mThreadId = 0L;
}
}
public boolean calledOnValidThread() {
if (DEBUG) {
return mThreadId.equals(Thread.currentThread().getId());
}
return true;
}
}
private static boolean runningOnJellyBeanOrHigher() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
}
private static boolean runningOnJellyBeanMR1OrHigher() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1;
}
private static boolean runningOnJellyBeanMR2OrHigher() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2;
}
private static class AudioDeviceName {
private final int mId;
private final String mName;
private AudioDeviceName(int id, String name) {
mId = id;
mName = name;
}
@CalledByNative("AudioDeviceName")
private String id() { return String.valueOf(mId); }
@CalledByNative("AudioDeviceName")
private String name() { return mName; }
}
private static final String[] SUPPORTED_AEC_MODELS = new String[] {
"GT-I9300",
"GT-I9500",
"GT-N7105",
"Nexus 4",
"Nexus 5",
"Nexus 7",
"SM-N9005",
"SM-T310",
};
private static final int DEVICE_DEFAULT = -2;
private static final int DEVICE_INVALID = -1;
private static final int DEVICE_SPEAKERPHONE = 0;
private static final int DEVICE_WIRED_HEADSET = 1;
private static final int DEVICE_EARPIECE = 2;
private static final int DEVICE_BLUETOOTH_HEADSET = 3;
private static final int DEVICE_COUNT = 4;
private static final String[] DEVICE_NAMES = new String[] {
"Speakerphone",
"Wired headset",
"Headset earpiece",
"Bluetooth headset",
};
private static final Integer[] VALID_DEVICES = new Integer[] {
DEVICE_SPEAKERPHONE,
DEVICE_WIRED_HEADSET,
DEVICE_EARPIECE,
DEVICE_BLUETOOTH_HEADSET,
};
private static final int STATE_BLUETOOTH_SCO_INVALID = -1;
private static final int STATE_BLUETOOTH_SCO_OFF = 0;
private static final int STATE_BLUETOOTH_SCO_ON = 1;
private static final int STATE_BLUETOOTH_SCO_TURNING_ON = 2;
private static final int STATE_BLUETOOTH_SCO_TURNING_OFF = 3;
private static final int DEFAULT_SAMPLING_RATE = 44100;
private static final int DEFAULT_FRAME_PER_BUFFER = 256;
private final AudioManager mAudioManager;
private final Context mContext;
private final long mNativeAudioManagerAndroid;
private boolean mHasBluetoothPermission = false;
private int mSavedAudioMode = AudioManager.MODE_INVALID;
private int mBluetoothScoState = STATE_BLUETOOTH_SCO_INVALID;
private boolean mIsInitialized = false;
private boolean mSavedIsSpeakerphoneOn;
private boolean mSavedIsMicrophoneMute;
private int mRequestedAudioDevice = DEVICE_INVALID;
private final NonThreadSafe mNonThreadSafe = new NonThreadSafe();
private final Object mLock = new Object();
private boolean[] mAudioDevices = new boolean[DEVICE_COUNT];
private final ContentResolver mContentResolver;
private ContentObserver mSettingsObserver = null;
private HandlerThread mSettingsObserverThread = null;
private int mCurrentVolume;
private BroadcastReceiver mWiredHeadsetReceiver;
private BroadcastReceiver mBluetoothHeadsetReceiver;
private BroadcastReceiver mBluetoothScoReceiver;
@CalledByNative
private static AudioManagerAndroid createAudioManagerAndroid(
Context context,
long nativeAudioManagerAndroid) {
return new AudioManagerAndroid(context, nativeAudioManagerAndroid);
}
private AudioManagerAndroid(Context context, long nativeAudioManagerAndroid) {
mContext = context;
mNativeAudioManagerAndroid = nativeAudioManagerAndroid;
mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
mContentResolver = mContext.getContentResolver();
}
@CalledByNative
private void init() {
checkIfCalledOnValidThread();
if (DEBUG) logd("init");
if (DEBUG) logDeviceInfo();
if (mIsInitialized)
return;
mAudioDevices[DEVICE_EARPIECE] = hasEarpiece();
mAudioDevices[DEVICE_WIRED_HEADSET] = hasWiredHeadset();
mAudioDevices[DEVICE_SPEAKERPHONE] = true;
registerBluetoothIntentsIfNeeded();
registerForWiredHeadsetIntentBroadcast();
mIsInitialized = true;
if (DEBUG) reportUpdate();
}
@CalledByNative
private void close() {
checkIfCalledOnValidThread();
if (DEBUG) logd("close");
if (!mIsInitialized)
return;
stopObservingVolumeChanges();
unregisterForWiredHeadsetIntentBroadcast();
unregisterBluetoothIntentsIfNeeded();
mIsInitialized = false;
}
@CalledByNative
private void setCommunicationAudioModeOn(boolean on) {
if (DEBUG) logd("setCommunicationAudioModeOn(" + on + ")");
if (on) {
if (mSavedAudioMode != AudioManager.MODE_INVALID) {
Log.wtf(TAG, "Audio mode has already been set!");
return;
}
try {
mSavedAudioMode = mAudioManager.getMode();
} catch (SecurityException e) {
Log.wtf(TAG, "getMode exception: ", e);
logDeviceInfo();
}
mSavedIsSpeakerphoneOn = mAudioManager.isSpeakerphoneOn();
mSavedIsMicrophoneMute = mAudioManager.isMicrophoneMute();
try {
mAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
} catch (SecurityException e) {
Log.wtf(TAG, "setMode exception: ", e);
logDeviceInfo();
}
startObservingVolumeChanges();
} else {
if (mSavedAudioMode == AudioManager.MODE_INVALID) {
Log.wtf(TAG, "Audio mode has not yet been set!");
return;
}
stopObservingVolumeChanges();
setMicrophoneMute(mSavedIsMicrophoneMute);
setSpeakerphoneOn(mSavedIsSpeakerphoneOn);
try {
mAudioManager.setMode(mSavedAudioMode);
} catch (SecurityException e) {
Log.wtf(TAG, "setMode exception: ", e);
logDeviceInfo();
}
mSavedAudioMode = AudioManager.MODE_INVALID;
}
}
@CalledByNative
private boolean setDevice(String deviceId) {
if (DEBUG) logd("setDevice: " + deviceId);
if (!mIsInitialized)
return false;
int intDeviceId = deviceId.isEmpty() ?
DEVICE_DEFAULT : Integer.parseInt(deviceId);
if (intDeviceId == DEVICE_DEFAULT) {
boolean devices[] = null;
synchronized (mLock) {
devices = mAudioDevices.clone();
mRequestedAudioDevice = DEVICE_DEFAULT;
}
int defaultDevice = selectDefaultDevice(devices);
setAudioDevice(defaultDevice);
return true;
}
List<Integer> validIds = Arrays.asList(VALID_DEVICES);
if (!validIds.contains(intDeviceId) || !mAudioDevices[intDeviceId]) {
return false;
}
synchronized (mLock) {
mRequestedAudioDevice = intDeviceId;
}
setAudioDevice(intDeviceId);
return true;
}
@CalledByNative
private AudioDeviceName[] getAudioInputDeviceNames() {
if (DEBUG) logd("getAudioInputDeviceNames");
if (!mIsInitialized)
return null;
boolean devices[] = null;
synchronized (mLock) {
devices = mAudioDevices.clone();
}
List<String> list = new ArrayList<String>();
AudioDeviceName[] array =
new AudioDeviceName[getNumOfAudioDevices(devices)];
int i = 0;
for (int id = 0; id < DEVICE_COUNT; ++id) {
if (devices[id]) {
array[i] = new AudioDeviceName(id, DEVICE_NAMES[id]);
list.add(DEVICE_NAMES[id]);
i++;
}
}
if (DEBUG) logd("getAudioInputDeviceNames: " + list);
return array;
}
@CalledByNative
private int getNativeOutputSampleRate() {
if (runningOnJellyBeanMR1OrHigher()) {
String sampleRateString = mAudioManager.getProperty(
AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE);
return (sampleRateString == null ?
DEFAULT_SAMPLING_RATE : Integer.parseInt(sampleRateString));
} else {
return DEFAULT_SAMPLING_RATE;
}
}
@CalledByNative
private static int getMinInputFrameSize(int sampleRate, int channels) {
int channelConfig;
if (channels == 1) {
channelConfig = AudioFormat.CHANNEL_IN_MONO;
} else if (channels == 2) {
channelConfig = AudioFormat.CHANNEL_IN_STEREO;
} else {
return -1;
}
return AudioRecord.getMinBufferSize(
sampleRate, channelConfig, AudioFormat.ENCODING_PCM_16BIT) / 2 / channels;
}
@CalledByNative
private static int getMinOutputFrameSize(int sampleRate, int channels) {
int channelConfig;
if (channels == 1) {
channelConfig = AudioFormat.CHANNEL_OUT_MONO;
} else if (channels == 2) {
channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
} else {
return -1;
}
return AudioTrack.getMinBufferSize(
sampleRate, channelConfig, AudioFormat.ENCODING_PCM_16BIT) / 2 / channels;
}
@CalledByNative
private boolean isAudioLowLatencySupported() {
return mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_AUDIO_LOW_LATENCY);
}
@CalledByNative
private int getAudioLowLatencyOutputFrameSize() {
String framesPerBuffer =
mAudioManager.getProperty(AudioManager.PROPERTY_OUTPUT_FRAMES_PER_BUFFER);
return (framesPerBuffer == null ?
DEFAULT_FRAME_PER_BUFFER : Integer.parseInt(framesPerBuffer));
}
@CalledByNative
private static boolean shouldUseAcousticEchoCanceler() {
if (!runningOnJellyBeanOrHigher()) {
return false;
}
List<String> supportedModels = Arrays.asList(SUPPORTED_AEC_MODELS);
if (!supportedModels.contains(Build.MODEL)) {
return false;
}
if (DEBUG && AcousticEchoCanceler.isAvailable()) {
logd("Approved for use of hardware acoustic echo canceler.");
}
return AcousticEchoCanceler.isAvailable();
}
private void checkIfCalledOnValidThread() {
if (DEBUG && !mNonThreadSafe.calledOnValidThread()) {
Log.wtf(TAG, "Method is not called on valid thread!");
}
}
private void registerBluetoothIntentsIfNeeded() {
mHasBluetoothPermission = hasBluetoothPermission();
if (!mHasBluetoothPermission) {
return;
}
mAudioDevices[DEVICE_BLUETOOTH_HEADSET] = hasBluetoothHeadset();
registerForBluetoothHeadsetIntentBroadcast();
registerForBluetoothScoIntentBroadcast();
}
private void unregisterBluetoothIntentsIfNeeded() {
if (mHasBluetoothPermission) {
mAudioManager.stopBluetoothSco();
unregisterForBluetoothHeadsetIntentBroadcast();
unregisterForBluetoothScoIntentBroadcast();
}
}
private void setSpeakerphoneOn(boolean on) {
boolean wasOn = mAudioManager.isSpeakerphoneOn();
if (wasOn == on) {
return;
}
mAudioManager.setSpeakerphoneOn(on);
}
private void setMicrophoneMute(boolean on) {
boolean wasMuted = mAudioManager.isMicrophoneMute();
if (wasMuted == on) {
return;
}
mAudioManager.setMicrophoneMute(on);
}
private boolean isMicrophoneMute() {
return mAudioManager.isMicrophoneMute();
}
private boolean hasEarpiece() {
return mContext.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_TELEPHONY);
}
@Deprecated
private boolean hasWiredHeadset() {
return mAudioManager.isWiredHeadsetOn();
}
private boolean hasBluetoothPermission() {
boolean hasBluetooth = mContext.checkPermission(
android.Manifest.permission.BLUETOOTH,
Process.myPid(),
Process.myUid()) == PackageManager.PERMISSION_GRANTED;
if (DEBUG && !hasBluetooth) {
logd("BLUETOOTH permission is missing!");
}
return hasBluetooth;
}
private boolean hasBluetoothHeadset() {
if (!mHasBluetoothPermission) {
Log.wtf(TAG, "hasBluetoothHeadset() requires BLUETOOTH permission!");
return false;
}
BluetoothAdapter btAdapter = null;
if (runningOnJellyBeanMR2OrHigher()) {
try {
BluetoothManager btManager =
(BluetoothManager)mContext.getSystemService(
Context.BLUETOOTH_SERVICE);
btAdapter = btManager.getAdapter();
} catch (Exception e) {
Log.wtf(TAG, "BluetoothManager.getAdapter exception", e);
return false;
}
} else {
try {
btAdapter = BluetoothAdapter.getDefaultAdapter();
} catch (Exception e) {
Log.wtf(TAG, "BluetoothAdapter.getDefaultAdapter exception", e);
return false;
}
}
int profileConnectionState;
try {
profileConnectionState = btAdapter.getProfileConnectionState(
android.bluetooth.BluetoothProfile.HEADSET);
} catch (Exception e) {
Log.wtf(TAG, "BluetoothAdapter.getProfileConnectionState exception", e);
profileConnectionState =
android.bluetooth.BluetoothProfile.STATE_DISCONNECTED;
}
return btAdapter.isEnabled() && profileConnectionState ==
android.bluetooth.BluetoothProfile.STATE_CONNECTED;
}
private void registerForWiredHeadsetIntentBroadcast() {
IntentFilter filter = new IntentFilter(Intent.ACTION_HEADSET_PLUG);
mWiredHeadsetReceiver = new BroadcastReceiver() {
private static final int STATE_UNPLUGGED = 0;
private static final int STATE_PLUGGED = 1;
private static final int HAS_NO_MIC = 0;
private static final int HAS_MIC = 1;
@Override
public void onReceive(Context context, Intent intent) {
int state = intent.getIntExtra("state", STATE_UNPLUGGED);
if (DEBUG) {
int microphone = intent.getIntExtra("microphone", HAS_NO_MIC);
String name = intent.getStringExtra("name");
logd("BroadcastReceiver.onReceive: a=" + intent.getAction() +
", s=" + state +
", m=" + microphone +
", n=" + name +
", sb=" + isInitialStickyBroadcast());
}
switch (state) {
case STATE_UNPLUGGED:
synchronized (mLock) {
mAudioDevices[DEVICE_WIRED_HEADSET] = false;
if (hasEarpiece()) {
mAudioDevices[DEVICE_EARPIECE] = true;
}
}
break;
case STATE_PLUGGED:
synchronized (mLock) {
mAudioDevices[DEVICE_WIRED_HEADSET] = true;
mAudioDevices[DEVICE_EARPIECE] = false;
}
break;
default:
loge("Invalid state!");
break;
}
if (deviceHasBeenRequested()) {
updateDeviceActivation();
} else if (DEBUG) {
reportUpdate();
}
}
};
mContext.registerReceiver(mWiredHeadsetReceiver, filter);
}
private void unregisterForWiredHeadsetIntentBroadcast() {
mContext.unregisterReceiver(mWiredHeadsetReceiver);
mWiredHeadsetReceiver = null;
}
private void registerForBluetoothHeadsetIntentBroadcast() {
IntentFilter filter = new IntentFilter(
android.bluetooth.BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
mBluetoothHeadsetReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
int profileState = intent.getIntExtra(
android.bluetooth.BluetoothHeadset.EXTRA_STATE,
android.bluetooth.BluetoothHeadset.STATE_DISCONNECTED);
if (DEBUG) {
logd("BroadcastReceiver.onReceive: a=" + intent.getAction() +
", s=" + profileState +
", sb=" + isInitialStickyBroadcast());
}
switch (profileState) {
case android.bluetooth.BluetoothProfile.STATE_DISCONNECTED:
synchronized (mLock) {
mAudioDevices[DEVICE_BLUETOOTH_HEADSET] = false;
}
break;
case android.bluetooth.BluetoothProfile.STATE_CONNECTED:
synchronized (mLock) {
mAudioDevices[DEVICE_BLUETOOTH_HEADSET] = true;
}
break;
case android.bluetooth.BluetoothProfile.STATE_CONNECTING:
break;
case android.bluetooth.BluetoothProfile.STATE_DISCONNECTING:
break;
default:
loge("Invalid state!");
break;
}
if (deviceHasBeenRequested()) {
updateDeviceActivation();
} else if (DEBUG) {
reportUpdate();
}
}
};
mContext.registerReceiver(mBluetoothHeadsetReceiver, filter);
}
private void unregisterForBluetoothHeadsetIntentBroadcast() {
mContext.unregisterReceiver(mBluetoothHeadsetReceiver);
mBluetoothHeadsetReceiver = null;
}
private void registerForBluetoothScoIntentBroadcast() {
IntentFilter filter = new IntentFilter(
AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED);
mBluetoothScoReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
int state = intent.getIntExtra(
AudioManager.EXTRA_SCO_AUDIO_STATE,
AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
if (DEBUG) {
logd("BroadcastReceiver.onReceive: a=" + intent.getAction() +
", s=" + state +
", sb=" + isInitialStickyBroadcast());
}
switch (state) {
case AudioManager.SCO_AUDIO_STATE_CONNECTED:
mBluetoothScoState = STATE_BLUETOOTH_SCO_ON;
break;
case AudioManager.SCO_AUDIO_STATE_DISCONNECTED:
mBluetoothScoState = STATE_BLUETOOTH_SCO_OFF;
break;
case AudioManager.SCO_AUDIO_STATE_CONNECTING:
break;
default:
loge("Invalid state!");
}
if (DEBUG) {
reportUpdate();
}
}
};
mContext.registerReceiver(mBluetoothScoReceiver, filter);
}
private void unregisterForBluetoothScoIntentBroadcast() {
mContext.unregisterReceiver(mBluetoothScoReceiver);
mBluetoothScoReceiver = null;
}
private void startBluetoothSco() {
if (!mHasBluetoothPermission) {
return;
}
if (mBluetoothScoState == STATE_BLUETOOTH_SCO_ON ||
mBluetoothScoState == STATE_BLUETOOTH_SCO_TURNING_ON) {
return;
}
if (mAudioManager.isBluetoothScoOn()) {
mBluetoothScoState = STATE_BLUETOOTH_SCO_ON;
return;
}
if (DEBUG) logd("startBluetoothSco: turning BT SCO on...");
mBluetoothScoState = STATE_BLUETOOTH_SCO_TURNING_ON;
mAudioManager.startBluetoothSco();
}
private void stopBluetoothSco() {
if (!mHasBluetoothPermission) {
return;
}
if (mBluetoothScoState != STATE_BLUETOOTH_SCO_ON &&
mBluetoothScoState != STATE_BLUETOOTH_SCO_TURNING_ON) {
return;
}
if (!mAudioManager.isBluetoothScoOn()) {
loge("Unable to stop BT SCO since it is already disabled!");
return;
}
if (DEBUG) logd("stopBluetoothSco: turning BT SCO off...");
mBluetoothScoState = STATE_BLUETOOTH_SCO_TURNING_OFF;
mAudioManager.stopBluetoothSco();
}
private void setAudioDevice(int device) {
if (DEBUG) logd("setAudioDevice(device=" + device + ")");
if (device == DEVICE_BLUETOOTH_HEADSET) {
startBluetoothSco();
} else {
stopBluetoothSco();
}
switch (device) {
case DEVICE_BLUETOOTH_HEADSET:
break;
case DEVICE_SPEAKERPHONE:
setSpeakerphoneOn(true);
break;
case DEVICE_WIRED_HEADSET:
setSpeakerphoneOn(false);
break;
case DEVICE_EARPIECE:
setSpeakerphoneOn(false);
break;
default:
loge("Invalid audio device selection!");
break;
}
reportUpdate();
}
private static int selectDefaultDevice(boolean[] devices) {
if (devices[DEVICE_WIRED_HEADSET]) {
return DEVICE_WIRED_HEADSET;
} else if (devices[DEVICE_BLUETOOTH_HEADSET]) {
return DEVICE_BLUETOOTH_HEADSET;
}
return DEVICE_SPEAKERPHONE;
}
private boolean deviceHasBeenRequested() {
synchronized (mLock) {
return (mRequestedAudioDevice != DEVICE_INVALID);
}
}
private void updateDeviceActivation() {
boolean devices[] = null;
int requested = DEVICE_INVALID;
synchronized (mLock) {
requested = mRequestedAudioDevice;
devices = mAudioDevices.clone();
}
if (requested == DEVICE_INVALID) {
loge("Unable to activate device since no device is selected!");
return;
}
if (requested == DEVICE_DEFAULT || !devices[requested]) {
int defaultDevice = selectDefaultDevice(devices);
setAudioDevice(defaultDevice);
} else {
setAudioDevice(requested);
}
}
private static int getNumOfAudioDevices(boolean[] devices) {
int count = 0;
for (int i = 0; i < DEVICE_COUNT; ++i) {
if (devices[i])
++count;
}
return count;
}
private void reportUpdate() {
synchronized (mLock) {
List<String> devices = new ArrayList<String>();
for (int i = 0; i < DEVICE_COUNT; ++i) {
if (mAudioDevices[i])
devices.add(DEVICE_NAMES[i]);
}
if (DEBUG) {
logd("reportUpdate: requested=" + mRequestedAudioDevice +
", btSco=" + mBluetoothScoState +
", devices=" + devices);
}
}
}
private void logDeviceInfo() {
logd("Android SDK: " + Build.VERSION.SDK_INT + ", " +
"Release: " + Build.VERSION.RELEASE + ", " +
"Brand: " + Build.BRAND + ", " +
"CPU_ABI: " + Build.CPU_ABI + ", " +
"Device: " + Build.DEVICE + ", " +
"Id: " + Build.ID + ", " +
"Hardware: " + Build.HARDWARE + ", " +
"Manufacturer: " + Build.MANUFACTURER + ", " +
"Model: " + Build.MODEL + ", " +
"Product: " + Build.PRODUCT);
}
private static void logd(String msg) {
Log.d(TAG, msg);
}
private static void loge(String msg) {
Log.e(TAG, msg);
}
private void startObservingVolumeChanges() {
if (DEBUG) logd("startObservingVolumeChanges");
if (mSettingsObserverThread != null)
return;
mSettingsObserverThread = new HandlerThread("SettingsObserver");
mSettingsObserverThread.start();
mSettingsObserver = new ContentObserver(
new Handler(mSettingsObserverThread.getLooper())) {
@Override
public void onChange(boolean selfChange) {
if (DEBUG) logd("SettingsObserver.onChange: " + selfChange);
super.onChange(selfChange);
if (mAudioManager.getMode() != AudioManager.MODE_IN_COMMUNICATION) {
Log.wtf(TAG, "Only enable SettingsObserver in COMM mode!");
return;
}
int volume = mAudioManager.getStreamVolume(
AudioManager.STREAM_VOICE_CALL);
if (DEBUG) logd("nativeSetMute: " + (volume == 0));
nativeSetMute(mNativeAudioManagerAndroid, (volume == 0));
}
};
mContentResolver.registerContentObserver(
Settings.System.CONTENT_URI, true, mSettingsObserver);
}
private void stopObservingVolumeChanges() {
if (DEBUG) logd("stopObservingVolumeChanges");
if (mSettingsObserverThread == null)
return;
mContentResolver.unregisterContentObserver(mSettingsObserver);
mSettingsObserver = null;
mSettingsObserverThread.quit();
try {
mSettingsObserverThread.join();
} catch (InterruptedException e) {
Log.wtf(TAG, "Thread.join() exception: ", e);
}
mSettingsObserverThread = null;
}
private native void nativeSetMute(long nativeAudioManagerAndroid, boolean muted);
}