package me.barrasso.android.volume.media; import android.media.AudioManager; import android.os.IBinder; import android.os.Parcel; import android.os.RemoteException; import android.text.TextUtils; import android.util.SparseArray; import me.barrasso.android.volume.LogUtils; import me.barrasso.android.volume.utils.ReflectionUtils; /** * Proxy to safely access android.media.IAudioFlinger methods. */ public class AudioFlingerProxy { // Values from IAudioFlinger.cpp public static final int CREATE_TRACK = IBinder.FIRST_CALL_TRANSACTION; // 1 public static final int OPEN_RECORD = CREATE_TRACK + 1; // 2 public static final int SAMPLE_RATE = OPEN_RECORD + 1; // 3 public static final int CHANNEL_COUNT = SAMPLE_RATE + 1; // 4 public static final int FORMAT = CHANNEL_COUNT + 1; // 5 public static final int FRAME_COUNT = FORMAT + 1; // 6 public static final int LATENCY = FRAME_COUNT + 1; // 7 public static final int SET_MASTER_VOLUME = LATENCY + 1; // 8 public static final int SET_MASTER_MUTE = SET_MASTER_VOLUME + 1; // 9 public static final int MASTER_VOLUME = SET_MASTER_MUTE + 1; // 10 public static final int MASTER_MUTE = MASTER_VOLUME + 1; // 11 public static final int SET_STREAM_VOLUME = MASTER_MUTE + 1; // 12 public static final int SET_STREAM_MUTE = SET_STREAM_VOLUME + 1; // 13 public static final int STREAM_VOLUME = SET_STREAM_MUTE + 1; // 14 public static final int STREAM_MUTE = STREAM_VOLUME + 1; // 15 public static final int SET_MODE = STREAM_MUTE + 1; // 16 public static final int DEFAULT = -1; public static final int VOICE_CALL = 0; public static final int SYSTEM = 1; public static final int RING = 2; public static final int MUSIC = 3; public static final int ALARM = 4; public static final int NOTIFICATION = 5; public static final int BLUETOOTH_SCO = 6; public static final int ENFORCED_AUDIBLE = 7; public static final int DTMF = 8; public static final int TTS = 9; // Values from errno.h public static final int NO_ERROR = 0; public static final int UNKNOWN_TRANSACTION = -74; // -EBADMSG; public static final int BAD_VALUE = -22; // -EINVAL; public static final int PERMISSION_DENIED = -1; // -EPERM; public static final int CALIBRATION_ERROR = -64; private static final String FLINGER_SERVICE = "media.audio_flinger"; private final String mInterfaceDescriptor; private final IBinder mAudioFlinger; private final SparseArray<SparseArray<Float>> mStreamStepMap = new SparseArray<SparseArray<Float>>(); public AudioFlingerProxy() { mAudioFlinger = ReflectionUtils.getServiceManager(FLINGER_SERVICE); String mID = ""; { try { mID = mAudioFlinger.getInterfaceDescriptor(); } catch (RemoteException e) { mID = ""; LogUtils.LOGE("AudioFlingerProxy", "Error obtained interface descriptor.", e); } } mInterfaceDescriptor = mID; } public boolean isCalibrated(int stream) { SparseArray<Float> map = mStreamStepMap.get(stream); return (map != null && map.size() >= 2); } public void mapStreamIndex(int stream, int index) { SparseArray<Float> map = mStreamStepMap.get(stream); if (null == map) map = new SparseArray<Float>(); try { float value = getStreamVolume(stream); map.put(index, value); } catch (RemoteException re) { LogUtils.LOGE("AudioFlingerProxy", "Error getting stream volume_3.", re); } mStreamStepMap.put(stream, map); LogUtils.LOGI("AudioFlingerProxy", LogUtils.logSparseArray(mStreamStepMap)); } /** * Set the volume_3 of a calibrated stream. * @see {@link #setStreamVolume(int, float)} * @throws RemoteException */ public int adjustStreamVolume(int stream, int direction, int index, int max) throws RemoteException { LogUtils.LOGI("AudioFlingerProxy", "adjustStreamVolume(" + stream + ", " + direction + ")"); if (null == mAudioFlinger || TextUtils.isEmpty(mInterfaceDescriptor) || !isCalibrated(stream)) { return BAD_VALUE; } float value = getStreamVolume(stream); float increment = getStreamIncrement(stream); float newValue = value; switch (direction) { case AudioManager.ADJUST_LOWER: newValue -= increment; case AudioManager.ADJUST_RAISE: newValue += increment; } newValue = Math.max(0, Math.min(newValue, max * increment)); LogUtils.LOGI("AudioFlingerProxy", "adjustStreamVolume() increment = " + increment + ", newVolume = " + newValue); return setStreamVolume(stream, newValue); } // change this value to change volume_3 scaling protected static float dBPerStep = 0.5f; // shouldn't need to touch these protected static float dBConvert = -dBPerStep * 2.302585093f / 20.0f; protected static float dBConvertInverse = 1.0f / dBConvert; protected static float linearToLog(int volume) { if (volume == 0) return 0.0f; return (float) Math.exp(100 - volume * dBConvert); } protected static int logToLinear(float volume) { if (volume == 0.0f) return 0; return (int) (100 - (dBConvertInverse * Math.log(volume) + 0.5)); } protected static float computeVolume(int index, int max) { int mIndexMin = 0; int volInt = (100 * (index - mIndexMin)) / (max - mIndexMin); return linearToLog(volInt); } protected float getStreamIncrement(int stream) throws RemoteException { // Figure out what the increment is. SparseArray<Float> map = mStreamStepMap.get(stream); int indexOne = map.keyAt(0); int indexTwo = map.keyAt(1); float valueOne = map.get(indexOne); float valueTwo = map.get(indexTwo); return Math.abs(valueTwo - valueOne) / Math.abs(indexTwo - indexOne); } public int getStreamIndex(int stream) throws RemoteException { if (null == mAudioFlinger || TextUtils.isEmpty(mInterfaceDescriptor) || !isCalibrated(stream)) { return BAD_VALUE; } float value = getStreamVolume(stream); float increment = getStreamIncrement(stream); return Math.round(value / increment); } public int setStreamVolume(int stream, float value) throws RemoteException { LogUtils.LOGI("AudioFlingerProxy", "setStreamVolume(" + stream + ", " + value + ")"); if (null == mAudioFlinger || TextUtils.isEmpty(mInterfaceDescriptor)) { return BAD_VALUE; } Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(mInterfaceDescriptor); data.writeInt(stream); data.writeFloat(value); mAudioFlinger.transact(SET_STREAM_VOLUME, data, reply, 0); return reply.readInt(); } public float getStreamVolume(int stream) throws RemoteException { LogUtils.LOGI("AudioFlingerProxy", "getStreamVolume(" + stream + ")"); if (null == mAudioFlinger || TextUtils.isEmpty(mInterfaceDescriptor)) { return BAD_VALUE; } Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(mInterfaceDescriptor); data.writeInt(stream); mAudioFlinger.transact(STREAM_VOLUME, data, reply, 0); float ret = Float.intBitsToFloat(reply.readInt()); LogUtils.LOGI("AudioFlingerProxy", "Stream = " + stream + ", volume_3 = " + ret); return ret; } }