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

Commit c18c9138 authored by Eric Laurent's avatar Eric Laurent
Browse files

AudioService: SCO audio backward compatibility

After commit 25fc29b3, AudioManager.startBluetoothSco()
does not use virtual voice call mode anymore when starting the
SCO audio connection to the headset.
To help backward compatibility, this change makes that virtual voice call
is used if the request comes from an application targeting a SDK version
before JB MR2. For applications targeted to JB MR2 and above a raw SCO
audio connection is established.

Bug 8157702

Change-Id: If1ded2fd99b7ed76d2435d95ee03659e78a7882a
parent a0042742
Loading
Loading
Loading
Loading
+7 −1
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -1205,6 +1206,11 @@ public class AudioManager {
     * call {@link #stopBluetoothSco()} to clear the request and turn down the bluetooth connection.
     * <p>Even if a SCO connection is established, the following restrictions apply on audio
     * output streams so that they can be routed to SCO headset:
     * <p>NOTE: up to and including API version
     * {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}, this method initiates a virtual
     * voice call to the bluetooth headset.
     * After API version {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2} only a raw SCO audio
     * connection is established.
     * <ul>
     *   <li> the stream type must be {@link #STREAM_VOICE_CALL} </li>
     *   <li> the format must be mono </li>
@@ -1226,7 +1232,7 @@ public class AudioManager {
    public void startBluetoothSco(){
        IAudioService service = getService();
        try {
            service.startBluetoothSco(mICallBack);
            service.startBluetoothSco(mICallBack, mContext.getApplicationInfo().targetSdkVersion);
        } catch (RemoteException e) {
            Log.e(TAG, "Dead object in startBluetoothSco", e);
        }
+47 −11
Original line number Diff line number Diff line
@@ -48,6 +48,7 @@ import android.database.ContentObserver;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
@@ -369,6 +370,14 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
    // waiting for headset service to connect
    private static final int SCO_STATE_DEACTIVATE_EXT_REQ = 4;

    // Indicates the mode used for SCO audio connection. The mode is virtual call if the request
    // originated from an app targeting an API version before JB MR2 and raw audio after that.
    private int mScoAudioMode;
    // SCO audio mode is virtual voice call (BluetoothHeadset.startScoUsingVirtualVoiceCall())
    private static final int SCO_MODE_VIRTUAL_CALL = 0;
    // SCO audio mode is raw audio (BluetoothHeadset.connectAudio())
    private static final int SCO_MODE_RAW = 1;

    // Current connection state indicated by bluetooth headset
    private int mScoConnectionState;

@@ -1910,7 +1919,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
    }

    /** @see AudioManager#startBluetoothSco() */
    public void startBluetoothSco(IBinder cb){
    public void startBluetoothSco(IBinder cb, int targetSdkVersion){
        if (!checkAudioSettingsPermission("startBluetoothSco()") ||
                !mBootCompleted) {
            return;
@@ -1922,7 +1931,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
        // The caller identity must be cleared after getScoClient() because it is needed if a new
        // client is created.
        final long ident = Binder.clearCallingIdentity();
        client.incCount();
        client.incCount(targetSdkVersion);
        Binder.restoreCallingIdentity(ident);
    }

@@ -1968,9 +1977,9 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
            }
        }

        public void incCount() {
        public void incCount(int targetSdkVersion) {
            synchronized(mScoClients) {
                requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED);
                requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, targetSdkVersion);
                if (mStartcount == 0) {
                    try {
                        mCb.linkToDeath(this, 0);
@@ -1996,7 +2005,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
                            Log.w(TAG, "decCount() going to 0 but not registered to binder");
                        }
                    }
                    requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
                    requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, 0);
                }
            }
        }
@@ -2012,7 +2021,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
                }
                mStartcount = 0;
                if (stopSco) {
                    requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
                    requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, 0);
                }
            }
        }
@@ -2040,7 +2049,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
            }
        }

        private void requestScoState(int state) {
        private void requestScoState(int state, int targetSdkVersion) {
            checkScoAudioState();
            if (totalCount() == 0) {
                if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
@@ -2055,8 +2064,18 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
                                (mScoAudioState == SCO_STATE_INACTIVE ||
                                 mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) {
                            if (mScoAudioState == SCO_STATE_INACTIVE) {
                                mScoAudioMode =
                                        (targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR2) ?
                                                SCO_MODE_VIRTUAL_CALL : SCO_MODE_RAW;
                                if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null) {
                                    if (mBluetoothHeadset.connectAudio()) {
                                    boolean status;
                                    if (mScoAudioMode == SCO_MODE_RAW) {
                                        status = mBluetoothHeadset.connectAudio();
                                    } else {
                                        status = mBluetoothHeadset.startScoUsingVirtualVoiceCall(
                                                                            mBluetoothHeadsetDevice);
                                    }
                                    if (status) {
                                        mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
                                    } else {
                                        broadcastScoConnectionState(
@@ -2078,7 +2097,14 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
                               mScoAudioState == SCO_STATE_ACTIVATE_REQ)) {
                    if (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL) {
                        if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null) {
                            if (!mBluetoothHeadset.disconnectAudio()) {
                            boolean status;
                            if (mScoAudioMode == SCO_MODE_RAW) {
                                status = mBluetoothHeadset.disconnectAudio();
                            } else {
                                status = mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
                                                                        mBluetoothHeadsetDevice);
                            }
                            if (!status) {
                                mScoAudioState = SCO_STATE_INACTIVE;
                                broadcastScoConnectionState(
                                        AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
@@ -2251,10 +2277,20 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
                            switch (mScoAudioState) {
                            case SCO_STATE_ACTIVATE_REQ:
                                mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
                                if (mScoAudioMode == SCO_MODE_RAW) {
                                    status = mBluetoothHeadset.connectAudio();
                                } else {
                                    status = mBluetoothHeadset.startScoUsingVirtualVoiceCall(
                                                                        mBluetoothHeadsetDevice);
                                }
                                break;
                            case SCO_STATE_DEACTIVATE_REQ:
                                if (mScoAudioMode == SCO_MODE_RAW) {
                                    status = mBluetoothHeadset.disconnectAudio();
                                } else {
                                    status = mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
                                                                        mBluetoothHeadsetDevice);
                                }
                                break;
                            case SCO_STATE_DEACTIVATE_EXT_REQ:
                                status = mBluetoothHeadset.stopVoiceRecognition(
+1 −1
Original line number Diff line number Diff line
@@ -179,7 +179,7 @@ interface IAudioService {
           int  getRemoteStreamVolume();
    oneway void registerRemoteVolumeObserverForRcc(int rccId, in IRemoteVolumeObserver rvo);

    void startBluetoothSco(IBinder cb);
    void startBluetoothSco(IBinder cb, int targetSdkVersion);
    void stopBluetoothSco(IBinder cb);

    void forceVolumeControlStream(int streamType, IBinder cb);