Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 77bea654 authored by Jean-Michel Trivi's avatar Jean-Michel Trivi Committed by Android Git Automerger
Browse files

am dce7a427: Merge "Full volume on remote submix for apps that need it" into lmp-dev

* commit 'dce7a427':
  Full volume on remote submix for apps that need it
parents 2b5fe1cd dce7a427
Loading
Loading
Loading
Loading
+39 −1
Original line number Diff line number Diff line
@@ -18,10 +18,15 @@ package android.media;

import java.lang.ref.WeakReference;
import java.nio.ByteBuffer;
import java.util.Iterator;

import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;

/**
@@ -99,6 +104,8 @@ public class AudioRecord

    private final static String TAG = "android.media.AudioRecord";

    /** @hide */
    public final static String SUBMIX_FIXED_VOLUME = "fixedVolume";

    //---------------------------------------------------------
    // Used exclusively by native code
@@ -184,6 +191,7 @@ public class AudioRecord
     * AudioAttributes
     */
    private AudioAttributes mAudioAttributes;
    private boolean mIsSubmixFullVolume = false;

    //---------------------------------------------------------
    // Constructor, Finalize
@@ -267,6 +275,18 @@ public class AudioRecord

        mAudioAttributes = attributes;

        // is this AudioRecord using REMOTE_SUBMIX at full volume?
        if (mAudioAttributes.getCapturePreset() == MediaRecorder.AudioSource.REMOTE_SUBMIX) {
            final Iterator<String> tagsIter = mAudioAttributes.getTags().iterator();
            while (tagsIter.hasNext()) {
                if (tagsIter.next().equalsIgnoreCase(SUBMIX_FIXED_VOLUME)) {
                    mIsSubmixFullVolume = true;
                    Log.v(TAG, "Will record from REMOTE_SUBMIX at full fixed volume");
                    break;
                }
            }
        }

        int rate = 0;
        if ((format.getPropertySetMask()
                & AudioFormat.AUDIO_FORMAT_HAS_PROPERTY_SAMPLE_RATE) != 0)
@@ -419,7 +439,8 @@ public class AudioRecord

    @Override
    protected void finalize() {
        native_finalize();
        // will cause stop() to be called, and if appropriate, will handle fixed volume recording
        release();
    }


@@ -586,6 +607,7 @@ public class AudioRecord
        // start recording
        synchronized(mRecordingStateLock) {
            if (native_start(MediaSyncEvent.SYNC_EVENT_NONE, 0) == SUCCESS) {
                handleFullVolumeRec(true);
                mRecordingState = RECORDSTATE_RECORDING;
            }
        }
@@ -608,6 +630,7 @@ public class AudioRecord
        // start recording
        synchronized(mRecordingStateLock) {
            if (native_start(syncEvent.getType(), syncEvent.getAudioSessionId()) == SUCCESS) {
                handleFullVolumeRec(true);
                mRecordingState = RECORDSTATE_RECORDING;
            }
        }
@@ -625,11 +648,25 @@ public class AudioRecord

        // stop recording
        synchronized(mRecordingStateLock) {
            handleFullVolumeRec(false);
            native_stop();
            mRecordingState = RECORDSTATE_STOPPED;
        }
    }

    private final IBinder mICallBack = new Binder();
    private void handleFullVolumeRec(boolean starting) {
        if (!mIsSubmixFullVolume) {
            return;
        }
        final IBinder b = ServiceManager.getService(android.content.Context.AUDIO_SERVICE);
        final IAudioService ias = IAudioService.Stub.asInterface(b);
        try {
            ias.forceRemoteSubmixFullVolume(starting, mICallBack);
        } catch (RemoteException e) {
            Log.e(TAG, "Error talking to AudioService when handling full submix volume", e);
        }
    }

    //---------------------------------------------------------
    // Audio data supply
@@ -880,6 +917,7 @@ public class AudioRecord
            int sampleRate, int channelMask, int audioFormat,
            int buffSizeInBytes, int[] sessionId);

    // TODO remove: implementation calls directly into implementation of native_release()
    private native final void native_finalize();

    private native final void native_release();
+113 −5
Original line number Diff line number Diff line
@@ -486,6 +486,7 @@ public class AudioService extends IAudioService.Stub {
            AudioSystem.DEVICE_OUT_HDMI_ARC |
            AudioSystem.DEVICE_OUT_SPDIF |
            AudioSystem.DEVICE_OUT_AUX_LINE;
    int mFullVolumeDevices = 0;

    // TODO merge orientation and rotation
    private final boolean mMonitorOrientation;
@@ -734,6 +735,10 @@ public class AudioService extends IAudioService.Stub {
        }
    }

    private void checkAllFixedVolumeDevices(int streamType) {
        mStreamStates[streamType].checkFixedVolumeDevices();
    }

    private void createStreamStates() {
        int numStreamTypes = AudioSystem.getNumStreamTypes();
        VolumeStreamState[] streams = mStreamStates = new VolumeStreamState[numStreamTypes];
@@ -1492,6 +1497,106 @@ public class AudioService extends IAudioService.Stub {
        return mStreamStates[streamType].isMuted();
    }

    private class RmtSbmxFullVolDeathHandler implements IBinder.DeathRecipient {
        private IBinder mICallback; // To be notified of client's death

        RmtSbmxFullVolDeathHandler(IBinder cb) {
            mICallback = cb;
            try {
                cb.linkToDeath(this, 0/*flags*/);
            } catch (RemoteException e) {
                Log.e(TAG, "can't link to death", e);
            }
        }

        boolean isHandlerFor(IBinder cb) {
            return mICallback.equals(cb);
        }

        void forget() {
            try {
                mICallback.unlinkToDeath(this, 0/*flags*/);
            } catch (NoSuchElementException e) {
                Log.e(TAG, "error unlinking to death", e);
            }
        }

        public void binderDied() {
            Log.w(TAG, "Recorder with remote submix at full volume died " + mICallback);
            forceRemoteSubmixFullVolume(false, mICallback);
        }
    }

    /**
     * call must be synchronized on mRmtSbmxFullVolDeathHandlers
     * @return true if there is a registered death handler, false otherwise */
    private boolean discardRmtSbmxFullVolDeathHandlerFor(IBinder cb) {
        Iterator<RmtSbmxFullVolDeathHandler> it = mRmtSbmxFullVolDeathHandlers.iterator();
        while (it.hasNext()) {
            final RmtSbmxFullVolDeathHandler handler = it.next();
            if (handler.isHandlerFor(cb)) {
                handler.forget();
                mRmtSbmxFullVolDeathHandlers.remove(handler);
                return true;
            }
        }
        return false;
    }

    /** call synchronized on mRmtSbmxFullVolDeathHandlers */
    private boolean hasRmtSbmxFullVolDeathHandlerFor(IBinder cb) {
        Iterator<RmtSbmxFullVolDeathHandler> it = mRmtSbmxFullVolDeathHandlers.iterator();
        while (it.hasNext()) {
            if (it.next().isHandlerFor(cb)) {
                return true;
            }
        }
        return false;
    }

    private int mRmtSbmxFullVolRefCount = 0;
    private ArrayList<RmtSbmxFullVolDeathHandler> mRmtSbmxFullVolDeathHandlers =
            new ArrayList<RmtSbmxFullVolDeathHandler>();

    public void forceRemoteSubmixFullVolume(boolean startForcing, IBinder cb) {
        if (cb == null) {
            return;
        }
        if ((PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission(
                        android.Manifest.permission.CAPTURE_AUDIO_OUTPUT))) {
            Log.w(TAG, "Trying to call forceRemoteSubmixFullVolume() without CAPTURE_AUDIO_OUTPUT");
            return;
        }
        synchronized(mRmtSbmxFullVolDeathHandlers) {
            boolean applyRequired = false;
            if (startForcing) {
                if (!hasRmtSbmxFullVolDeathHandlerFor(cb)) {
                    mRmtSbmxFullVolDeathHandlers.add(new RmtSbmxFullVolDeathHandler(cb));
                    if (mRmtSbmxFullVolRefCount == 0) {
                        mFullVolumeDevices |= AudioSystem.DEVICE_OUT_REMOTE_SUBMIX;
                        mFixedVolumeDevices |= AudioSystem.DEVICE_OUT_REMOTE_SUBMIX;
                        applyRequired = true;
                    }
                    mRmtSbmxFullVolRefCount++;
                }
            } else {
                if (discardRmtSbmxFullVolDeathHandlerFor(cb) && (mRmtSbmxFullVolRefCount > 0)) {
                    mRmtSbmxFullVolRefCount--;
                    if (mRmtSbmxFullVolRefCount == 0) {
                        mFullVolumeDevices &= ~AudioSystem.DEVICE_OUT_REMOTE_SUBMIX;
                        mFixedVolumeDevices &= ~AudioSystem.DEVICE_OUT_REMOTE_SUBMIX;
                        applyRequired = true;
                    }
                }
            }
            if (applyRequired) {
                // Assumes only STREAM_MUSIC going through DEVICE_OUT_REMOTE_SUBMIX
                checkAllFixedVolumeDevices(AudioSystem.STREAM_MUSIC);
                mStreamStates[AudioSystem.STREAM_MUSIC].applyAllVolumes();
            }
        }
    }

    /** @see AudioManager#setMasterMute(boolean, int) */
    public void setMasterMute(boolean state, int flags, String callingPackage, IBinder cb) {
        if (mUseFixedVolume) {
@@ -3269,8 +3374,8 @@ public class AudioService extends IAudioService.Stub {
            int index;
            if (isMuted()) {
                index = 0;
            } else if ((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 &&
                       mAvrcpAbsVolSupported) {
            } else if (((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 && mAvrcpAbsVolSupported)
                    || ((device & mFullVolumeDevices) != 0)) {
                index = (mIndexMax + 5)/10;
            } else {
                index = (getIndex(device) + 5)/10;
@@ -3298,8 +3403,10 @@ public class AudioService extends IAudioService.Stub {
                    if (device != AudioSystem.DEVICE_OUT_DEFAULT) {
                        if (isMuted()) {
                            index = 0;
                        } else if ((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 &&
                                mAvrcpAbsVolSupported) {
                        } else if (((device & AudioSystem.DEVICE_OUT_ALL_A2DP) != 0 &&
                                mAvrcpAbsVolSupported)
                                    || ((device & mFullVolumeDevices) != 0))
                        {
                            index = (mIndexMax + 5)/10;
                        } else {
                            index = ((Integer)entry.getValue() + 5)/10;
@@ -3429,7 +3536,8 @@ public class AudioService extends IAudioService.Stub {
                        Map.Entry entry = (Map.Entry)i.next();
                        int device = ((Integer)entry.getKey()).intValue();
                        int index = ((Integer)entry.getValue()).intValue();
                        if (((device & mFixedVolumeDevices) != 0) && index != 0) {
                        if (((device & mFullVolumeDevices) != 0)
                                || (((device & mFixedVolumeDevices) != 0) && index != 0)) {
                            entry.setValue(mIndexMax);
                        }
                        applyDeviceVolume(device);
+2 −0
Original line number Diff line number Diff line
@@ -56,6 +56,8 @@ interface IAudioService {

    boolean isStreamMute(int streamType);

    void forceRemoteSubmixFullVolume(boolean startForcing, IBinder cb);

    void setMasterMute(boolean state, int flags, String callingPackage, IBinder cb);

    boolean isMasterMute();