13

Same as this question and many others from a few years ago: how to turn speaker on/off programmatically in android 4.0

It seems that Android has changed the way it handles this.

here are the things I tried to make an Outgoing call to have a speakerphone programmatically. None of these solutions worked for me on Android Pie. while they seem to work well on Android Nougat and Oreo

Solution 1.

final static int FOR_MEDIA = 1;
final static int FORCE_NONE = 0;
final static int FORCE_SPEAKER = 1;

Class audioSystemClass = Class.forName("android.media.AudioSystem");
Method setForceUse = audioSystemClass.getMethod("setForceUse", int.class, int.class);
setForceUse.invoke(null, FOR_MEDIA, FORCE_SPEAKER);

2.

AudioManager audioManager = (AudioManager)getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
if (audioManager != null) {
   audioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
   audioManager.setSpeakerphoneOn(true);

3.

AudioManager audioManager = (AudioManager)getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
if (audioManager != null) {
   audioManager.setMode(AudioManager.MODE_IN_CALL);
   audioManager.setSpeakerphoneOn(true);

4.

Thread thread = new Thread() {
    @Override
    public void run() {
        try {
            while(true) {
                sleep(1000);
                audioManager.setMode(AudioManager.MODE_IN_CALL);
                if (!audioManager.isSpeakerphoneOn())
                    audioManager.setSpeakerphoneOn(true);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
};
thread.start();

the app has the following permissions granted among many others:

<uses-permission android:name="android.permission.CALL_PHONE"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />

I have also tried the following solution only for outgoing calls. and it also didn't work.

Intent callIntent = new Intent(Intent.ACTION_CALL);
callIntent.putExtra("speaker", true);
callIntent.setData(Uri.parse("tel:" + number));
context.startActivity(callIntent);
eglease
  • 2,445
  • 11
  • 18
  • 28
Vasili Fedotov
  • 1,091
  • 14
  • 31
  • How did u solve this problem? – Shivam Sharma Oct 19 '19 at 04:40
  • M facing the same problem on oreo nd up – Shivam Sharma Oct 19 '19 at 04:41
  • The issue is not yet resolved. – Vasili Fedotov Oct 20 '19 at 06:45
  • I have some more information. In Android Pie, the app requires an additional permission, MODIFY_AUDIO_ROUTING, this can only be granted to system apps as far as I know. Solution 1 should work, if you have this permission. ndroidaudiotes: Accessing hidden method Landroid/media/AudioSystem;->setForceUse(II)I (light greylist, reflection) Permission failure: android.permission.MODIFY_AUDIO_ROUTING from uid=10296 pid=26584 – Michael van der Horst Oct 30 '19 at 08:00
  • Try adding `CALL_LOG` permission group and see if that changes anything, I can't see any changes to AudioManager in Android Pie so this must be it – Zohaib Amir Nov 04 '19 at 07:56
  • @MichaelvanderHorst I think you are confusing `CALL_LOG` with some other permission, According to docs, [CALL_LOG was introduced in Android 9.](https://developer.android.com/about/versions/pie/android-9.0-changes-all#restrict-access-call-logs) – Zohaib Amir Nov 05 '19 at 18:02
  • Did you come up with a solution? Is it possible to be done on Android 10+? – MaaAn13 May 26 '21 at 14:00

4 Answers4

5

In Android Pie, I had the same problem. I resolved it using an InCallService with

setAudioRoute(ROUTE_SPEAKER)

Your app needs to be Default phone app.

odgatelmand
  • 393
  • 1
  • 5
  • 16
5

I am surprised nobody mentioned TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE.

As an intent:

val uri = Uri.fromParts("tel", PHONE_NUMBER, null)
val intentCall = Intent(Intent.ACTION_CALL, uri).apply {
    putExtra(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, true)
    flags = Intent.FLAG_ACTIVITY_NEW_TASK
}
startActivity(intentCall)

Using TelecomManager.placeCall:

val uri = Uri.fromParts("tel", PHONE_NUMBER, null)
val extras = Bundle().apply { putBoolean(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, true) }
val telecomManager = getSystemService(TELECOM_SERVICE) as TelecomManager
telecomManager.placeCall(uri, extras)

The default phone app should handle that extra data and enable the speakers.

Remember to ask for android.permission.CALL_PHONE permission.

Necrontyr
  • 171
  • 3
  • 2
0

in my self managed voice calling app I managed by changing connection service audio route like below beside using audioManager,

 public void setSpeakerphoneOn(final boolean enabled) {
            if (enabled != audioManager.isSpeakerphoneOn()) {
                Log.d(TAG, "setSpeakerphoneOn(): " + enabled);
                audioManager.setSpeakerphoneOn(enabled);
            }
    }

and in my connectionservice class,

   public void speakerOnOff(String uuid,boolean on) {
        Log.d(TAG,"SPEAKER  is" + String.valueOf(on));

        Connection connection = VoipConnectionService.getConnection(uuid);

        if (on) {
            connection.setAudioRoute(CallAudioState.ROUTE_SPEAKER);

        } else {
            connection.setAudioRoute(CallAudioState.ROUTE_EARPIECE);
        }


    }

or you can change route according to your audio device for output

public void speakerOnOff(String uuid,String device) {
     Log.d(TAG,"Device to  route  is" + device);

        Connection connection = VoipConnectionService.getConnection(uuid);

            int state =  CallAudioState.ROUTE_EARPIECE ;
            switch (device){
                case   "SPEAKER_PHONE":
                  state  = CallAudioState.ROUTE_SPEAKER ;
                  break;
                case "WIRED_HEADSET":
                    state  = CallAudioState.ROUTE_WIRED_HEADSET ;
                    break;
                case "EARPIECE" :
                case "NONE" :
                    state  = CallAudioState.ROUTE_EARPIECE ;
                    break;
                case "BLUETOOTH" :
                    state  = CallAudioState.ROUTE_BLUETOOTH ;
                    break;
            }
            connection.setAudioRoute(state);
        }
Bilal Şimşek
  • 5,453
  • 2
  • 19
  • 33
-1

For this problem please try the following code it is working for me.

AudioManager myAudioManager;
myAudioManager.setMode(AudioManager.MODE_NORMAL);
myAudioManager.setSpeakerphoneOn(true);