Merge "AudioService AudioDeviceBroker: fix speakerphone control" into rvc-dev

This commit is contained in:
Eric Laurent
2020-05-16 03:58:21 +00:00
committed by Android (Google) Code Review
5 changed files with 232 additions and 57 deletions

View File

@@ -1505,7 +1505,7 @@ public class AudioManager {
public void setSpeakerphoneOn(boolean on){
final IAudioService service = getService();
try {
service.setSpeakerphoneOn(on);
service.setSpeakerphoneOn(mICallBack, on);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}

View File

@@ -150,7 +150,7 @@ interface IAudioService {
oneway void avrcpSupportsAbsoluteVolume(String address, boolean support);
void setSpeakerphoneOn(boolean on);
void setSpeakerphoneOn(IBinder cb, boolean on);
boolean isSpeakerphoneOn();

View File

@@ -36,6 +36,7 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Log;
import android.util.PrintWriterPrinter;
@@ -43,6 +44,9 @@ import android.util.PrintWriterPrinter;
import com.android.internal.annotations.GuardedBy;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.NoSuchElementException;
/** @hide */
/*package*/ final class AudioDeviceBroker {
@@ -91,6 +95,9 @@ import java.io.PrintWriter;
// TODO do not "share" the lock between AudioService and BtHelpr, see b/123769055
/*package*/ final Object mSetModeLock = new Object();
/** PID of current audio mode owner communicated by AudioService */
private int mModeOwnerPid = 0;
//-------------------------------------------------------------------
/*package*/ AudioDeviceBroker(@NonNull Context context, @NonNull AudioService service) {
mContext = context;
@@ -136,6 +143,7 @@ import java.io.PrintWriter;
/*package*/ void onSystemReady() {
synchronized (mSetModeLock) {
synchronized (mDeviceStateLock) {
mModeOwnerPid = mAudioService.getModeOwnerPid();
mBtHelper.onSystemReady();
}
}
@@ -202,30 +210,57 @@ import java.io.PrintWriter;
* @param eventSource for logging purposes
* @return true if speakerphone state changed
*/
/*package*/ boolean setSpeakerphoneOn(boolean on, String eventSource) {
/*package*/ boolean setSpeakerphoneOn(IBinder cb, int pid, boolean on, String eventSource) {
synchronized (mDeviceStateLock) {
final boolean wasOn = isSpeakerphoneOn();
if (on) {
if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) {
setForceUse_Async(AudioSystem.FOR_RECORD, AudioSystem.FORCE_NONE, eventSource);
}
mForcedUseForComm = AudioSystem.FORCE_SPEAKER;
} else if (mForcedUseForComm == AudioSystem.FORCE_SPEAKER) {
if (mBtHelper.isBluetoothScoOn()) {
mForcedUseForComm = AudioSystem.FORCE_BT_SCO;
setForceUse_Async(
AudioSystem.FOR_RECORD, AudioSystem.FORCE_BT_SCO, eventSource);
} else {
mForcedUseForComm = AudioSystem.FORCE_NONE;
}
if (!addSpeakerphoneClient(cb, pid, on)) {
return false;
}
mForcedUseForCommExt = mForcedUseForComm;
setForceUse_Async(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, eventSource);
final boolean wasOn = isSpeakerphoneOn();
updateSpeakerphoneOn(eventSource);
return (wasOn != isSpeakerphoneOn());
}
}
@GuardedBy("mDeviceStateLock")
private void updateSpeakerphoneOn(String eventSource) {
if (isSpeakerphoneOnRequested()) {
if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) {
setForceUse_Async(AudioSystem.FOR_RECORD, AudioSystem.FORCE_NONE, eventSource);
}
mForcedUseForComm = AudioSystem.FORCE_SPEAKER;
} else if (mForcedUseForComm == AudioSystem.FORCE_SPEAKER) {
if (mBtHelper.isBluetoothScoOn()) {
mForcedUseForComm = AudioSystem.FORCE_BT_SCO;
setForceUse_Async(
AudioSystem.FOR_RECORD, AudioSystem.FORCE_BT_SCO, eventSource);
} else {
mForcedUseForComm = AudioSystem.FORCE_NONE;
}
}
mForcedUseForCommExt = mForcedUseForComm;
setForceUse_Async(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, eventSource);
}
/**
* Returns if speakerphone is requested ON or OFF.
* If the current audio mode owner is in the speakerphone client list, use this preference.
* Otherwise use first client's preference (first client corresponds to latest request).
* Speakerphone is requested OFF if no client is in the list.
* @return true if speakerphone is requested ON, false otherwise
*/
@GuardedBy("mDeviceStateLock")
private boolean isSpeakerphoneOnRequested() {
if (mSpeakerphoneClients.isEmpty()) {
return false;
}
for (SpeakerphoneClient cl : mSpeakerphoneClients) {
if (cl.getPid() == mModeOwnerPid) {
return cl.isOn();
}
}
return mSpeakerphoneClients.get(0).isOn();
}
/*package*/ boolean isSpeakerphoneOn() {
synchronized (mDeviceStateLock) {
return (mForcedUseForCommExt == AudioSystem.FORCE_SPEAKER);
@@ -384,7 +419,8 @@ import java.io.PrintWriter;
}
mForcedUseForComm = AudioSystem.FORCE_BT_SCO;
} else if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) {
mForcedUseForComm = AudioSystem.FORCE_NONE;
mForcedUseForComm = isSpeakerphoneOnRequested()
? AudioSystem.FORCE_SPEAKER : AudioSystem.FORCE_NONE;
}
mForcedUseForCommExt = mForcedUseForComm;
AudioSystem.setParameters("BT_SCO=" + (on ? "on" : "off"));
@@ -429,8 +465,8 @@ import java.io.PrintWriter;
sendIIMsgNoDelay(MSG_II_SET_HEARING_AID_VOLUME, SENDMSG_REPLACE, index, streamType);
}
/*package*/ void postDisconnectBluetoothSco(int exceptPid) {
sendIMsgNoDelay(MSG_I_DISCONNECT_BT_SCO, SENDMSG_REPLACE, exceptPid);
/*package*/ void postSetModeOwnerPid(int pid) {
sendIMsgNoDelay(MSG_I_SET_MODE_OWNER_PID, SENDMSG_REPLACE, pid);
}
/*package*/ void postBluetoothA2dpDeviceConfigChange(@NonNull BluetoothDevice device) {
@@ -483,7 +519,7 @@ import java.io.PrintWriter;
}
/*package*/ int getModeOwnerPid() {
return mAudioService.getModeOwnerPid();
return mModeOwnerPid;
}
/*package*/ int getDeviceForStream(int streamType) {
@@ -605,6 +641,10 @@ import java.io.PrintWriter;
sendLMsgNoDelay(MSG_L_SCOCLIENT_DIED, SENDMSG_QUEUE, obj);
}
/*package*/ void postSpeakerphoneClientDied(Object obj) {
sendLMsgNoDelay(MSG_L_SPEAKERPHONE_CLIENT_DIED, SENDMSG_QUEUE, obj);
}
/*package*/ void postSaveSetPreferredDeviceForStrategy(int strategy,
AudioDeviceAttributes device)
{
@@ -674,7 +714,7 @@ import java.io.PrintWriter;
new BtHelper.BluetoothA2dpDeviceInfo(btDevice);
return (mBrokerHandler.hasEqualMessages(
MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_CONNECTED, devInfoToCheck)
|| mBrokerHandler.hasEqualMessages(
|| mBrokerHandler.hasEqualMessages(
MSG_IL_SET_A2DP_SINK_CONNECTION_STATE_DISCONNECTED, devInfoToCheck));
}
@@ -707,7 +747,20 @@ import java.io.PrintWriter;
} else {
pw.println("Message handler is null");
}
mDeviceInventory.dump(pw, prefix);
pw.println("\n" + prefix + "mForcedUseForComm: "
+ AudioSystem.forceUseConfigToString(mForcedUseForComm));
pw.println(prefix + "mForcedUseForCommExt: "
+ AudioSystem.forceUseConfigToString(mForcedUseForCommExt));
pw.println(prefix + "mModeOwnerPid: " + mModeOwnerPid);
pw.println(prefix + "Speakerphone clients:");
mSpeakerphoneClients.forEach((cl) -> {
pw.println(" " + prefix + "pid: " + cl.getPid() + " on: "
+ cl.isOn() + " cb: " + cl.getBinder()); });
mBtHelper.dump(pw, prefix);
}
//---------------------------------------------------------------------
@@ -877,10 +930,16 @@ import java.io.PrintWriter;
mBtHelper.setAvrcpAbsoluteVolumeIndex(msg.arg1);
}
break;
case MSG_I_DISCONNECT_BT_SCO:
case MSG_I_SET_MODE_OWNER_PID:
synchronized (mSetModeLock) {
synchronized (mDeviceStateLock) {
mBtHelper.disconnectBluetoothSco(msg.arg1);
if (mModeOwnerPid != msg.arg1) {
mModeOwnerPid = msg.arg1;
updateSpeakerphoneOn("setNewModeOwner");
if (mModeOwnerPid != 0) {
mBtHelper.disconnectBluetoothSco(mModeOwnerPid);
}
}
}
}
break;
@@ -891,6 +950,11 @@ import java.io.PrintWriter;
}
}
break;
case MSG_L_SPEAKERPHONE_CLIENT_DIED:
synchronized (mDeviceStateLock) {
speakerphoneClientDied(msg.obj);
}
break;
case MSG_TOGGLE_HDMI:
synchronized (mDeviceStateLock) {
mDeviceInventory.onToggleHdmi();
@@ -1021,7 +1085,7 @@ import java.io.PrintWriter;
private static final int MSG_REPORT_NEW_ROUTES = 13;
private static final int MSG_II_SET_HEARING_AID_VOLUME = 14;
private static final int MSG_I_SET_AVRCP_ABSOLUTE_VOLUME = 15;
private static final int MSG_I_DISCONNECT_BT_SCO = 16;
private static final int MSG_I_SET_MODE_OWNER_PID = 16;
// process active A2DP device change, obj is BtHelper.BluetoothA2dpDeviceInfo
private static final int MSG_L_A2DP_ACTIVE_DEVICE_CHANGE = 18;
@@ -1051,6 +1115,8 @@ import java.io.PrintWriter;
private static final int MSG_IL_SAVE_PREF_DEVICE_FOR_STRATEGY = 33;
private static final int MSG_I_SAVE_REMOVE_PREF_DEVICE_FOR_STRATEGY = 34;
private static final int MSG_L_SPEAKERPHONE_CLIENT_DIED = 35;
private static boolean isMessageHandledUnderWakelock(int msgId) {
switch(msgId) {
@@ -1166,4 +1232,93 @@ import java.io.PrintWriter;
time);
}
}
private class SpeakerphoneClient implements IBinder.DeathRecipient {
private final IBinder mCb;
private final int mPid;
private final boolean mOn;
SpeakerphoneClient(IBinder cb, int pid, boolean on) {
mCb = cb;
mPid = pid;
mOn = on;
}
public boolean registerDeathRecipient() {
boolean status = false;
try {
mCb.linkToDeath(this, 0);
status = true;
} catch (RemoteException e) {
Log.w(TAG, "SpeakerphoneClient could not link to " + mCb + " binder death");
}
return status;
}
public void unregisterDeathRecipient() {
try {
mCb.unlinkToDeath(this, 0);
} catch (NoSuchElementException e) {
Log.w(TAG, "SpeakerphoneClient could not not unregistered to binder");
}
}
@Override
public void binderDied() {
postSpeakerphoneClientDied(this);
}
IBinder getBinder() {
return mCb;
}
int getPid() {
return mPid;
}
boolean isOn() {
return mOn;
}
}
@GuardedBy("mDeviceStateLock")
private void speakerphoneClientDied(Object obj) {
if (obj == null) {
return;
}
Log.w(TAG, "Speaker client died");
if (removeSpeakerphoneClient(((SpeakerphoneClient) obj).getBinder(), false) != null) {
updateSpeakerphoneOn("speakerphoneClientDied");
}
}
private SpeakerphoneClient removeSpeakerphoneClient(IBinder cb, boolean unregister) {
for (SpeakerphoneClient cl : mSpeakerphoneClients) {
if (cl.getBinder() == cb) {
if (unregister) {
cl.unregisterDeathRecipient();
}
mSpeakerphoneClients.remove(cl);
return cl;
}
}
return null;
}
@GuardedBy("mDeviceStateLock")
private boolean addSpeakerphoneClient(IBinder cb, int pid, boolean on) {
// always insert new request at first position
removeSpeakerphoneClient(cb, true);
SpeakerphoneClient client = new SpeakerphoneClient(cb, pid, on);
if (client.registerDeathRecipient()) {
mSpeakerphoneClients.add(0, client);
return true;
}
return false;
}
// List of clients requesting speakerPhone ON
@GuardedBy("mDeviceStateLock")
private final @NonNull ArrayList<SpeakerphoneClient> mSpeakerphoneClients =
new ArrayList<SpeakerphoneClient>();
}

View File

@@ -3567,11 +3567,9 @@ public class AudioService extends IAudioService.Stub
}
public void binderDied() {
int oldModeOwnerPid;
int newModeOwnerPid = 0;
synchronized (mDeviceBroker.mSetModeLock) {
Log.w(TAG, "setMode() client died");
oldModeOwnerPid = getModeOwnerPid();
int index = mSetModeDeathHandlers.indexOf(this);
if (index < 0) {
Log.w(TAG, "unregistered setMode() client died");
@@ -3581,9 +3579,7 @@ public class AudioService extends IAudioService.Stub
}
// when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
// SCO connections not started by the application changing the mode when pid changes
if ((newModeOwnerPid != oldModeOwnerPid) && (newModeOwnerPid != 0)) {
mDeviceBroker.postDisconnectBluetoothSco(newModeOwnerPid);
}
mDeviceBroker.postSetModeOwnerPid(newModeOwnerPid);
}
public int getPid() {
@@ -3635,17 +3631,20 @@ public class AudioService extends IAudioService.Stub
return;
}
int oldModeOwnerPid;
int newModeOwnerPid;
synchronized (mDeviceBroker.mSetModeLock) {
if (mode == AudioSystem.MODE_CURRENT) {
mode = mMode;
}
oldModeOwnerPid = getModeOwnerPid();
int oldModeOwnerPid = getModeOwnerPid();
// Do not allow changing mode if a call is active and the requester
// does not have permission to modify phone state or is not the mode owner.
if (((mMode == AudioSystem.MODE_IN_CALL)
|| (mMode == AudioSystem.MODE_IN_COMMUNICATION))
// does not have permission to modify phone state or is not the mode owner,
// unless returning to NORMAL mode (will not change current mode owner) or
// not changing mode in which case the mode owner will reflect the last
// requester of current mode
if (!((mode == mMode) || (mode == AudioSystem.MODE_NORMAL))
&& ((mMode == AudioSystem.MODE_IN_CALL)
|| (mMode == AudioSystem.MODE_IN_COMMUNICATION))
&& !(hasModifyPhoneStatePermission || (oldModeOwnerPid == callingPid))) {
Log.w(TAG, "setMode(" + mode + ") from pid=" + callingPid
+ ", uid=" + Binder.getCallingUid()
@@ -3658,9 +3657,7 @@ public class AudioService extends IAudioService.Stub
}
// when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
// SCO connections not started by the application changing the mode when pid changes
if ((newModeOwnerPid != oldModeOwnerPid) && (newModeOwnerPid != 0)) {
mDeviceBroker.postDisconnectBluetoothSco(newModeOwnerPid);
}
mDeviceBroker.postSetModeOwnerPid(newModeOwnerPid);
}
// setModeInt() returns a valid PID if the audio mode was successfully set to
@@ -3937,32 +3934,18 @@ public class AudioService extends IAudioService.Stub
}
/** @see AudioManager#setSpeakerphoneOn(boolean) */
public void setSpeakerphoneOn(boolean on){
public void setSpeakerphoneOn(IBinder cb, boolean on) {
if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) {
return;
}
if (mContext.checkCallingOrSelfPermission(
android.Manifest.permission.MODIFY_PHONE_STATE)
!= PackageManager.PERMISSION_GRANTED) {
synchronized (mSetModeDeathHandlers) {
for (SetModeDeathHandler h : mSetModeDeathHandlers) {
if (h.getMode() == AudioSystem.MODE_IN_CALL) {
Log.w(TAG, "getMode is call, Permission Denial: setSpeakerphoneOn from pid="
+ Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
return;
}
}
}
}
// for logging only
final int uid = Binder.getCallingUid();
final int pid = Binder.getCallingPid();
final String eventSource = new StringBuilder("setSpeakerphoneOn(").append(on)
.append(") from u/pid:").append(uid).append("/")
.append(pid).toString();
final boolean stateChanged = mDeviceBroker.setSpeakerphoneOn(on, eventSource);
final boolean stateChanged = mDeviceBroker.setSpeakerphoneOn(cb, pid, on, eventSource);
new MediaMetrics.Item(MediaMetrics.Name.AUDIO_DEVICE
+ MediaMetrics.SEPARATOR + "setSpeakerphoneOn")
.setUid(uid)

View File

@@ -38,6 +38,7 @@ import android.util.Log;
import com.android.internal.annotations.GuardedBy;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
@@ -131,6 +132,26 @@ public class BtHelper {
}
}
/**
* Returns a string representation of the scoAudioState.
*/
public static String scoAudioStateToString(int scoAudioState) {
switch (scoAudioState) {
case SCO_STATE_INACTIVE:
return "SCO_STATE_INACTIVE";
case SCO_STATE_ACTIVATE_REQ:
return "SCO_STATE_ACTIVATE_REQ";
case SCO_STATE_ACTIVE_EXTERNAL:
return "SCO_STATE_ACTIVE_EXTERNAL";
case SCO_STATE_ACTIVE_INTERNAL:
return "SCO_STATE_ACTIVE_INTERNAL";
case SCO_STATE_DEACTIVATING:
return "SCO_STATE_DEACTIVATING";
default:
return "SCO_STATE_(" + scoAudioState + ")";
}
}
//----------------------------------------------------------------------
/*package*/ static class BluetoothA2dpDeviceInfo {
private final @NonNull BluetoothDevice mBtDevice;
@@ -1007,4 +1028,20 @@ public class BtHelper {
return "ENCODING_BT_CODEC_TYPE(" + btCodecType + ")";
}
}
//------------------------------------------------------------
/*package*/ void dump(PrintWriter pw, String prefix) {
pw.println("\n" + prefix + "mBluetoothHeadset: " + mBluetoothHeadset);
pw.println(prefix + "mBluetoothHeadsetDevice: " + mBluetoothHeadsetDevice);
pw.println(prefix + "mScoAudioState: " + scoAudioStateToString(mScoAudioState));
pw.println(prefix + "mScoAudioMode: " + scoAudioModeToString(mScoAudioMode));
pw.println(prefix + "Sco clients:");
mScoClients.forEach((cl) -> {
pw.println(" " + prefix + "pid: " + cl.getPid() + " cb: " + cl.getBinder()); });
pw.println("\n" + prefix + "mHearingAid: " + mHearingAid);
pw.println(prefix + "mA2dp: " + mA2dp);
pw.println(prefix + "mAvrcpAbsVolSupported: " + mAvrcpAbsVolSupported);
}
}