Android 10 – Audio Settings Permission Denial: setSpeakerphoneOn()

I’m running on Android 10 and cannot turn on speaker phone using Kotlin. Below is my code and it will always show False during incall.

MainActivity.kt

val audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
audioManager.mode = AudioManager.MODE_IN_COMMUNICATION
audioManager.isSpeakerphoneOn = true
if (audioManager.isSpeakerphoneOn)
    Toast.makeText(this, "True", Toast.LENGTH_SHORT).show()
else
    Toast.makeText(this, "False", Toast.LENGTH_SHORT).show()

AndroidManifest.xml

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

Logcat

04-18 10:43:58.064  1313  3118 W AS.AudioService: Audio Settings Permission Denial: setSpeakerphoneOn() from pid=8073, uid=10213

Answer

I was able to resolve the issue using InCallService which I am using to develop an app as Default Dialer. All you have to do is get the instance of the CallService class which extends InCallService. Since on Android 10 you cannot directly use setSpeakerphoneOn(), which works great on Android 9. InCallService have a method setAudioRoute() which you can use to route your audio. So far I have tested the below code on Android 10 and 9. Also tested Wouter Vanhauwaert issue of alternating between speaker and earpiece during a call which also works fine. My Code is as below.

CallService.java

public class CallService extends InCallService {

  private static CallService sInstance;
  ……

  @Override
  public void onCreate() {
    super.onCreate();
    sInstance = this;
  }
  public static CallService getInstance(){
    return sInstance;
  }
}

AnotherActivity.java

public void toggleSpeaker() {
 AudioManager am = (AudioManager)this.getSystemService(Context.AUDIO_SERVICE);
 boolean isSpeakerOn = audioManager.isSpeakerphoneOn();
 int earpiece = CallAudioState.ROUTE_WIRED_OR_EARPIECE;
 int speaker = CallAudioState.ROUTE_SPEAKER;

 if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.P){
 CallService.getInstance().setAudioRoute(isSpeakerOn ? earpiece : speaker);
 } else {
   am.setSpeakerphoneOn(!isSpeakerOn);
 }
}