Loading media/java/android/media/AudioManager.java +1 −1 Original line number Original line Diff line number Diff line Loading @@ -1505,7 +1505,7 @@ public class AudioManager { public void setSpeakerphoneOn(boolean on){ public void setSpeakerphoneOn(boolean on){ final IAudioService service = getService(); final IAudioService service = getService(); try { try { service.setSpeakerphoneOn(on); service.setSpeakerphoneOn(mICallBack, on); } catch (RemoteException e) { } catch (RemoteException e) { throw e.rethrowFromSystemServer(); throw e.rethrowFromSystemServer(); } } Loading media/java/android/media/IAudioService.aidl +1 −1 Original line number Original line Diff line number Diff line Loading @@ -150,7 +150,7 @@ interface IAudioService { oneway void avrcpSupportsAbsoluteVolume(String address, boolean support); oneway void avrcpSupportsAbsoluteVolume(String address, boolean support); void setSpeakerphoneOn(boolean on); void setSpeakerphoneOn(IBinder cb, boolean on); boolean isSpeakerphoneOn(); boolean isSpeakerphoneOn(); Loading services/core/java/com/android/server/audio/AudioDeviceBroker.java +180 −25 Original line number Original line Diff line number Diff line Loading @@ -36,6 +36,7 @@ import android.os.IBinder; import android.os.Looper; import android.os.Looper; import android.os.Message; import android.os.Message; import android.os.PowerManager; import android.os.PowerManager; import android.os.RemoteException; import android.os.SystemClock; import android.os.SystemClock; import android.util.Log; import android.util.Log; import android.util.PrintWriterPrinter; import android.util.PrintWriterPrinter; Loading @@ -43,6 +44,9 @@ import android.util.PrintWriterPrinter; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.GuardedBy; import java.io.PrintWriter; import java.io.PrintWriter; import java.util.ArrayList; import java.util.NoSuchElementException; /** @hide */ /** @hide */ /*package*/ final class AudioDeviceBroker { /*package*/ final class AudioDeviceBroker { Loading Loading @@ -91,6 +95,9 @@ import java.io.PrintWriter; // TODO do not "share" the lock between AudioService and BtHelpr, see b/123769055 // TODO do not "share" the lock between AudioService and BtHelpr, see b/123769055 /*package*/ final Object mSetModeLock = new Object(); /*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) { /*package*/ AudioDeviceBroker(@NonNull Context context, @NonNull AudioService service) { mContext = context; mContext = context; Loading Loading @@ -136,6 +143,7 @@ import java.io.PrintWriter; /*package*/ void onSystemReady() { /*package*/ void onSystemReady() { synchronized (mSetModeLock) { synchronized (mSetModeLock) { synchronized (mDeviceStateLock) { synchronized (mDeviceStateLock) { mModeOwnerPid = mAudioService.getModeOwnerPid(); mBtHelper.onSystemReady(); mBtHelper.onSystemReady(); } } } } Loading Loading @@ -202,10 +210,20 @@ import java.io.PrintWriter; * @param eventSource for logging purposes * @param eventSource for logging purposes * @return true if speakerphone state changed * @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) { synchronized (mDeviceStateLock) { if (!addSpeakerphoneClient(cb, pid, on)) { return false; } final boolean wasOn = isSpeakerphoneOn(); final boolean wasOn = isSpeakerphoneOn(); if (on) { updateSpeakerphoneOn(eventSource); return (wasOn != isSpeakerphoneOn()); } } @GuardedBy("mDeviceStateLock") private void updateSpeakerphoneOn(String eventSource) { if (isSpeakerphoneOnRequested()) { if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) { if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) { setForceUse_Async(AudioSystem.FOR_RECORD, AudioSystem.FORCE_NONE, eventSource); setForceUse_Async(AudioSystem.FOR_RECORD, AudioSystem.FORCE_NONE, eventSource); } } Loading @@ -219,11 +237,28 @@ import java.io.PrintWriter; mForcedUseForComm = AudioSystem.FORCE_NONE; mForcedUseForComm = AudioSystem.FORCE_NONE; } } } } mForcedUseForCommExt = mForcedUseForComm; mForcedUseForCommExt = mForcedUseForComm; setForceUse_Async(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, eventSource); setForceUse_Async(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, eventSource); return (wasOn != isSpeakerphoneOn()); } } /** * 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() { /*package*/ boolean isSpeakerphoneOn() { Loading Loading @@ -384,7 +419,8 @@ import java.io.PrintWriter; } } mForcedUseForComm = AudioSystem.FORCE_BT_SCO; mForcedUseForComm = AudioSystem.FORCE_BT_SCO; } else if (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; mForcedUseForCommExt = mForcedUseForComm; AudioSystem.setParameters("BT_SCO=" + (on ? "on" : "off")); AudioSystem.setParameters("BT_SCO=" + (on ? "on" : "off")); Loading Loading @@ -429,8 +465,8 @@ import java.io.PrintWriter; sendIIMsgNoDelay(MSG_II_SET_HEARING_AID_VOLUME, SENDMSG_REPLACE, index, streamType); sendIIMsgNoDelay(MSG_II_SET_HEARING_AID_VOLUME, SENDMSG_REPLACE, index, streamType); } } /*package*/ void postDisconnectBluetoothSco(int exceptPid) { /*package*/ void postSetModeOwnerPid(int pid) { sendIMsgNoDelay(MSG_I_DISCONNECT_BT_SCO, SENDMSG_REPLACE, exceptPid); sendIMsgNoDelay(MSG_I_SET_MODE_OWNER_PID, SENDMSG_REPLACE, pid); } } /*package*/ void postBluetoothA2dpDeviceConfigChange(@NonNull BluetoothDevice device) { /*package*/ void postBluetoothA2dpDeviceConfigChange(@NonNull BluetoothDevice device) { Loading Loading @@ -483,7 +519,7 @@ import java.io.PrintWriter; } } /*package*/ int getModeOwnerPid() { /*package*/ int getModeOwnerPid() { return mAudioService.getModeOwnerPid(); return mModeOwnerPid; } } /*package*/ int getDeviceForStream(int streamType) { /*package*/ int getDeviceForStream(int streamType) { Loading Loading @@ -605,6 +641,10 @@ import java.io.PrintWriter; sendLMsgNoDelay(MSG_L_SCOCLIENT_DIED, SENDMSG_QUEUE, obj); 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, /*package*/ void postSaveSetPreferredDeviceForStrategy(int strategy, AudioDeviceAttributes device) AudioDeviceAttributes device) { { Loading Loading @@ -707,7 +747,20 @@ import java.io.PrintWriter; } else { } else { pw.println("Message handler is null"); pw.println("Message handler is null"); } } mDeviceInventory.dump(pw, prefix); 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); } } //--------------------------------------------------------------------- //--------------------------------------------------------------------- Loading Loading @@ -877,10 +930,16 @@ import java.io.PrintWriter; mBtHelper.setAvrcpAbsoluteVolumeIndex(msg.arg1); mBtHelper.setAvrcpAbsoluteVolumeIndex(msg.arg1); } } break; break; case MSG_I_DISCONNECT_BT_SCO: case MSG_I_SET_MODE_OWNER_PID: synchronized (mSetModeLock) { synchronized (mSetModeLock) { synchronized (mDeviceStateLock) { synchronized (mDeviceStateLock) { mBtHelper.disconnectBluetoothSco(msg.arg1); if (mModeOwnerPid != msg.arg1) { mModeOwnerPid = msg.arg1; updateSpeakerphoneOn("setNewModeOwner"); if (mModeOwnerPid != 0) { mBtHelper.disconnectBluetoothSco(mModeOwnerPid); } } } } } } break; break; Loading @@ -891,6 +950,11 @@ import java.io.PrintWriter; } } } } break; break; case MSG_L_SPEAKERPHONE_CLIENT_DIED: synchronized (mDeviceStateLock) { speakerphoneClientDied(msg.obj); } break; case MSG_TOGGLE_HDMI: case MSG_TOGGLE_HDMI: synchronized (mDeviceStateLock) { synchronized (mDeviceStateLock) { mDeviceInventory.onToggleHdmi(); mDeviceInventory.onToggleHdmi(); Loading Loading @@ -1021,7 +1085,7 @@ import java.io.PrintWriter; private static final int MSG_REPORT_NEW_ROUTES = 13; 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_II_SET_HEARING_AID_VOLUME = 14; private static final int MSG_I_SET_AVRCP_ABSOLUTE_VOLUME = 15; 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 // process active A2DP device change, obj is BtHelper.BluetoothA2dpDeviceInfo private static final int MSG_L_A2DP_ACTIVE_DEVICE_CHANGE = 18; private static final int MSG_L_A2DP_ACTIVE_DEVICE_CHANGE = 18; Loading Loading @@ -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_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_I_SAVE_REMOVE_PREF_DEVICE_FOR_STRATEGY = 34; private static final int MSG_L_SPEAKERPHONE_CLIENT_DIED = 35; private static boolean isMessageHandledUnderWakelock(int msgId) { private static boolean isMessageHandledUnderWakelock(int msgId) { switch(msgId) { switch(msgId) { Loading Loading @@ -1166,4 +1232,93 @@ import java.io.PrintWriter; time); 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>(); } } services/core/java/com/android/server/audio/AudioService.java +12 −29 Original line number Original line Diff line number Diff line Loading @@ -3567,11 +3567,9 @@ public class AudioService extends IAudioService.Stub } } public void binderDied() { public void binderDied() { int oldModeOwnerPid; int newModeOwnerPid = 0; int newModeOwnerPid = 0; synchronized (mDeviceBroker.mSetModeLock) { synchronized (mDeviceBroker.mSetModeLock) { Log.w(TAG, "setMode() client died"); Log.w(TAG, "setMode() client died"); oldModeOwnerPid = getModeOwnerPid(); int index = mSetModeDeathHandlers.indexOf(this); int index = mSetModeDeathHandlers.indexOf(this); if (index < 0) { if (index < 0) { Log.w(TAG, "unregistered setMode() client died"); Log.w(TAG, "unregistered setMode() client died"); Loading @@ -3581,9 +3579,7 @@ public class AudioService extends IAudioService.Stub } } // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all // SCO connections not started by the application changing the mode when pid changes // SCO connections not started by the application changing the mode when pid changes if ((newModeOwnerPid != oldModeOwnerPid) && (newModeOwnerPid != 0)) { mDeviceBroker.postSetModeOwnerPid(newModeOwnerPid); mDeviceBroker.postDisconnectBluetoothSco(newModeOwnerPid); } } } public int getPid() { public int getPid() { Loading Loading @@ -3635,16 +3631,19 @@ public class AudioService extends IAudioService.Stub return; return; } } int oldModeOwnerPid; int newModeOwnerPid; int newModeOwnerPid; synchronized (mDeviceBroker.mSetModeLock) { synchronized (mDeviceBroker.mSetModeLock) { if (mode == AudioSystem.MODE_CURRENT) { if (mode == AudioSystem.MODE_CURRENT) { mode = mMode; mode = mMode; } } oldModeOwnerPid = getModeOwnerPid(); int oldModeOwnerPid = getModeOwnerPid(); // Do not allow changing mode if a call is active and the requester // 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. // does not have permission to modify phone state or is not the mode owner, if (((mMode == AudioSystem.MODE_IN_CALL) // 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)) || (mMode == AudioSystem.MODE_IN_COMMUNICATION)) && !(hasModifyPhoneStatePermission || (oldModeOwnerPid == callingPid))) { && !(hasModifyPhoneStatePermission || (oldModeOwnerPid == callingPid))) { Log.w(TAG, "setMode(" + mode + ") from pid=" + callingPid Log.w(TAG, "setMode(" + mode + ") from pid=" + callingPid Loading @@ -3658,9 +3657,7 @@ public class AudioService extends IAudioService.Stub } } // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all // SCO connections not started by the application changing the mode when pid changes // SCO connections not started by the application changing the mode when pid changes if ((newModeOwnerPid != oldModeOwnerPid) && (newModeOwnerPid != 0)) { mDeviceBroker.postSetModeOwnerPid(newModeOwnerPid); mDeviceBroker.postDisconnectBluetoothSco(newModeOwnerPid); } } } // setModeInt() returns a valid PID if the audio mode was successfully set to // setModeInt() returns a valid PID if the audio mode was successfully set to Loading Loading @@ -3937,32 +3934,18 @@ public class AudioService extends IAudioService.Stub } } /** @see AudioManager#setSpeakerphoneOn(boolean) */ /** @see AudioManager#setSpeakerphoneOn(boolean) */ public void setSpeakerphoneOn(boolean on){ public void setSpeakerphoneOn(IBinder cb, boolean on) { if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) { if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) { return; 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 // for logging only final int uid = Binder.getCallingUid(); final int uid = Binder.getCallingUid(); final int pid = Binder.getCallingPid(); final int pid = Binder.getCallingPid(); final String eventSource = new StringBuilder("setSpeakerphoneOn(").append(on) final String eventSource = new StringBuilder("setSpeakerphoneOn(").append(on) .append(") from u/pid:").append(uid).append("/") .append(") from u/pid:").append(uid).append("/") .append(pid).toString(); .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 new MediaMetrics.Item(MediaMetrics.Name.AUDIO_DEVICE + MediaMetrics.SEPARATOR + "setSpeakerphoneOn") + MediaMetrics.SEPARATOR + "setSpeakerphoneOn") .setUid(uid) .setUid(uid) Loading services/core/java/com/android/server/audio/BtHelper.java +37 −0 Original line number Original line Diff line number Diff line Loading @@ -38,6 +38,7 @@ import android.util.Log; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.GuardedBy; import java.io.PrintWriter; import java.util.ArrayList; import java.util.ArrayList; import java.util.List; import java.util.List; import java.util.NoSuchElementException; import java.util.NoSuchElementException; Loading Loading @@ -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 { /*package*/ static class BluetoothA2dpDeviceInfo { private final @NonNull BluetoothDevice mBtDevice; private final @NonNull BluetoothDevice mBtDevice; Loading Loading @@ -1007,4 +1028,20 @@ public class BtHelper { return "ENCODING_BT_CODEC_TYPE(" + btCodecType + ")"; 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); } } } Loading
media/java/android/media/AudioManager.java +1 −1 Original line number Original line Diff line number Diff line Loading @@ -1505,7 +1505,7 @@ public class AudioManager { public void setSpeakerphoneOn(boolean on){ public void setSpeakerphoneOn(boolean on){ final IAudioService service = getService(); final IAudioService service = getService(); try { try { service.setSpeakerphoneOn(on); service.setSpeakerphoneOn(mICallBack, on); } catch (RemoteException e) { } catch (RemoteException e) { throw e.rethrowFromSystemServer(); throw e.rethrowFromSystemServer(); } } Loading
media/java/android/media/IAudioService.aidl +1 −1 Original line number Original line Diff line number Diff line Loading @@ -150,7 +150,7 @@ interface IAudioService { oneway void avrcpSupportsAbsoluteVolume(String address, boolean support); oneway void avrcpSupportsAbsoluteVolume(String address, boolean support); void setSpeakerphoneOn(boolean on); void setSpeakerphoneOn(IBinder cb, boolean on); boolean isSpeakerphoneOn(); boolean isSpeakerphoneOn(); Loading
services/core/java/com/android/server/audio/AudioDeviceBroker.java +180 −25 Original line number Original line Diff line number Diff line Loading @@ -36,6 +36,7 @@ import android.os.IBinder; import android.os.Looper; import android.os.Looper; import android.os.Message; import android.os.Message; import android.os.PowerManager; import android.os.PowerManager; import android.os.RemoteException; import android.os.SystemClock; import android.os.SystemClock; import android.util.Log; import android.util.Log; import android.util.PrintWriterPrinter; import android.util.PrintWriterPrinter; Loading @@ -43,6 +44,9 @@ import android.util.PrintWriterPrinter; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.GuardedBy; import java.io.PrintWriter; import java.io.PrintWriter; import java.util.ArrayList; import java.util.NoSuchElementException; /** @hide */ /** @hide */ /*package*/ final class AudioDeviceBroker { /*package*/ final class AudioDeviceBroker { Loading Loading @@ -91,6 +95,9 @@ import java.io.PrintWriter; // TODO do not "share" the lock between AudioService and BtHelpr, see b/123769055 // TODO do not "share" the lock between AudioService and BtHelpr, see b/123769055 /*package*/ final Object mSetModeLock = new Object(); /*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) { /*package*/ AudioDeviceBroker(@NonNull Context context, @NonNull AudioService service) { mContext = context; mContext = context; Loading Loading @@ -136,6 +143,7 @@ import java.io.PrintWriter; /*package*/ void onSystemReady() { /*package*/ void onSystemReady() { synchronized (mSetModeLock) { synchronized (mSetModeLock) { synchronized (mDeviceStateLock) { synchronized (mDeviceStateLock) { mModeOwnerPid = mAudioService.getModeOwnerPid(); mBtHelper.onSystemReady(); mBtHelper.onSystemReady(); } } } } Loading Loading @@ -202,10 +210,20 @@ import java.io.PrintWriter; * @param eventSource for logging purposes * @param eventSource for logging purposes * @return true if speakerphone state changed * @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) { synchronized (mDeviceStateLock) { if (!addSpeakerphoneClient(cb, pid, on)) { return false; } final boolean wasOn = isSpeakerphoneOn(); final boolean wasOn = isSpeakerphoneOn(); if (on) { updateSpeakerphoneOn(eventSource); return (wasOn != isSpeakerphoneOn()); } } @GuardedBy("mDeviceStateLock") private void updateSpeakerphoneOn(String eventSource) { if (isSpeakerphoneOnRequested()) { if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) { if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) { setForceUse_Async(AudioSystem.FOR_RECORD, AudioSystem.FORCE_NONE, eventSource); setForceUse_Async(AudioSystem.FOR_RECORD, AudioSystem.FORCE_NONE, eventSource); } } Loading @@ -219,11 +237,28 @@ import java.io.PrintWriter; mForcedUseForComm = AudioSystem.FORCE_NONE; mForcedUseForComm = AudioSystem.FORCE_NONE; } } } } mForcedUseForCommExt = mForcedUseForComm; mForcedUseForCommExt = mForcedUseForComm; setForceUse_Async(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, eventSource); setForceUse_Async(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, eventSource); return (wasOn != isSpeakerphoneOn()); } } /** * 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() { /*package*/ boolean isSpeakerphoneOn() { Loading Loading @@ -384,7 +419,8 @@ import java.io.PrintWriter; } } mForcedUseForComm = AudioSystem.FORCE_BT_SCO; mForcedUseForComm = AudioSystem.FORCE_BT_SCO; } else if (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; mForcedUseForCommExt = mForcedUseForComm; AudioSystem.setParameters("BT_SCO=" + (on ? "on" : "off")); AudioSystem.setParameters("BT_SCO=" + (on ? "on" : "off")); Loading Loading @@ -429,8 +465,8 @@ import java.io.PrintWriter; sendIIMsgNoDelay(MSG_II_SET_HEARING_AID_VOLUME, SENDMSG_REPLACE, index, streamType); sendIIMsgNoDelay(MSG_II_SET_HEARING_AID_VOLUME, SENDMSG_REPLACE, index, streamType); } } /*package*/ void postDisconnectBluetoothSco(int exceptPid) { /*package*/ void postSetModeOwnerPid(int pid) { sendIMsgNoDelay(MSG_I_DISCONNECT_BT_SCO, SENDMSG_REPLACE, exceptPid); sendIMsgNoDelay(MSG_I_SET_MODE_OWNER_PID, SENDMSG_REPLACE, pid); } } /*package*/ void postBluetoothA2dpDeviceConfigChange(@NonNull BluetoothDevice device) { /*package*/ void postBluetoothA2dpDeviceConfigChange(@NonNull BluetoothDevice device) { Loading Loading @@ -483,7 +519,7 @@ import java.io.PrintWriter; } } /*package*/ int getModeOwnerPid() { /*package*/ int getModeOwnerPid() { return mAudioService.getModeOwnerPid(); return mModeOwnerPid; } } /*package*/ int getDeviceForStream(int streamType) { /*package*/ int getDeviceForStream(int streamType) { Loading Loading @@ -605,6 +641,10 @@ import java.io.PrintWriter; sendLMsgNoDelay(MSG_L_SCOCLIENT_DIED, SENDMSG_QUEUE, obj); 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, /*package*/ void postSaveSetPreferredDeviceForStrategy(int strategy, AudioDeviceAttributes device) AudioDeviceAttributes device) { { Loading Loading @@ -707,7 +747,20 @@ import java.io.PrintWriter; } else { } else { pw.println("Message handler is null"); pw.println("Message handler is null"); } } mDeviceInventory.dump(pw, prefix); 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); } } //--------------------------------------------------------------------- //--------------------------------------------------------------------- Loading Loading @@ -877,10 +930,16 @@ import java.io.PrintWriter; mBtHelper.setAvrcpAbsoluteVolumeIndex(msg.arg1); mBtHelper.setAvrcpAbsoluteVolumeIndex(msg.arg1); } } break; break; case MSG_I_DISCONNECT_BT_SCO: case MSG_I_SET_MODE_OWNER_PID: synchronized (mSetModeLock) { synchronized (mSetModeLock) { synchronized (mDeviceStateLock) { synchronized (mDeviceStateLock) { mBtHelper.disconnectBluetoothSco(msg.arg1); if (mModeOwnerPid != msg.arg1) { mModeOwnerPid = msg.arg1; updateSpeakerphoneOn("setNewModeOwner"); if (mModeOwnerPid != 0) { mBtHelper.disconnectBluetoothSco(mModeOwnerPid); } } } } } } break; break; Loading @@ -891,6 +950,11 @@ import java.io.PrintWriter; } } } } break; break; case MSG_L_SPEAKERPHONE_CLIENT_DIED: synchronized (mDeviceStateLock) { speakerphoneClientDied(msg.obj); } break; case MSG_TOGGLE_HDMI: case MSG_TOGGLE_HDMI: synchronized (mDeviceStateLock) { synchronized (mDeviceStateLock) { mDeviceInventory.onToggleHdmi(); mDeviceInventory.onToggleHdmi(); Loading Loading @@ -1021,7 +1085,7 @@ import java.io.PrintWriter; private static final int MSG_REPORT_NEW_ROUTES = 13; 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_II_SET_HEARING_AID_VOLUME = 14; private static final int MSG_I_SET_AVRCP_ABSOLUTE_VOLUME = 15; 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 // process active A2DP device change, obj is BtHelper.BluetoothA2dpDeviceInfo private static final int MSG_L_A2DP_ACTIVE_DEVICE_CHANGE = 18; private static final int MSG_L_A2DP_ACTIVE_DEVICE_CHANGE = 18; Loading Loading @@ -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_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_I_SAVE_REMOVE_PREF_DEVICE_FOR_STRATEGY = 34; private static final int MSG_L_SPEAKERPHONE_CLIENT_DIED = 35; private static boolean isMessageHandledUnderWakelock(int msgId) { private static boolean isMessageHandledUnderWakelock(int msgId) { switch(msgId) { switch(msgId) { Loading Loading @@ -1166,4 +1232,93 @@ import java.io.PrintWriter; time); 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>(); } }
services/core/java/com/android/server/audio/AudioService.java +12 −29 Original line number Original line Diff line number Diff line Loading @@ -3567,11 +3567,9 @@ public class AudioService extends IAudioService.Stub } } public void binderDied() { public void binderDied() { int oldModeOwnerPid; int newModeOwnerPid = 0; int newModeOwnerPid = 0; synchronized (mDeviceBroker.mSetModeLock) { synchronized (mDeviceBroker.mSetModeLock) { Log.w(TAG, "setMode() client died"); Log.w(TAG, "setMode() client died"); oldModeOwnerPid = getModeOwnerPid(); int index = mSetModeDeathHandlers.indexOf(this); int index = mSetModeDeathHandlers.indexOf(this); if (index < 0) { if (index < 0) { Log.w(TAG, "unregistered setMode() client died"); Log.w(TAG, "unregistered setMode() client died"); Loading @@ -3581,9 +3579,7 @@ public class AudioService extends IAudioService.Stub } } // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all // SCO connections not started by the application changing the mode when pid changes // SCO connections not started by the application changing the mode when pid changes if ((newModeOwnerPid != oldModeOwnerPid) && (newModeOwnerPid != 0)) { mDeviceBroker.postSetModeOwnerPid(newModeOwnerPid); mDeviceBroker.postDisconnectBluetoothSco(newModeOwnerPid); } } } public int getPid() { public int getPid() { Loading Loading @@ -3635,16 +3631,19 @@ public class AudioService extends IAudioService.Stub return; return; } } int oldModeOwnerPid; int newModeOwnerPid; int newModeOwnerPid; synchronized (mDeviceBroker.mSetModeLock) { synchronized (mDeviceBroker.mSetModeLock) { if (mode == AudioSystem.MODE_CURRENT) { if (mode == AudioSystem.MODE_CURRENT) { mode = mMode; mode = mMode; } } oldModeOwnerPid = getModeOwnerPid(); int oldModeOwnerPid = getModeOwnerPid(); // Do not allow changing mode if a call is active and the requester // 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. // does not have permission to modify phone state or is not the mode owner, if (((mMode == AudioSystem.MODE_IN_CALL) // 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)) || (mMode == AudioSystem.MODE_IN_COMMUNICATION)) && !(hasModifyPhoneStatePermission || (oldModeOwnerPid == callingPid))) { && !(hasModifyPhoneStatePermission || (oldModeOwnerPid == callingPid))) { Log.w(TAG, "setMode(" + mode + ") from pid=" + callingPid Log.w(TAG, "setMode(" + mode + ") from pid=" + callingPid Loading @@ -3658,9 +3657,7 @@ public class AudioService extends IAudioService.Stub } } // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all // SCO connections not started by the application changing the mode when pid changes // SCO connections not started by the application changing the mode when pid changes if ((newModeOwnerPid != oldModeOwnerPid) && (newModeOwnerPid != 0)) { mDeviceBroker.postSetModeOwnerPid(newModeOwnerPid); mDeviceBroker.postDisconnectBluetoothSco(newModeOwnerPid); } } } // setModeInt() returns a valid PID if the audio mode was successfully set to // setModeInt() returns a valid PID if the audio mode was successfully set to Loading Loading @@ -3937,32 +3934,18 @@ public class AudioService extends IAudioService.Stub } } /** @see AudioManager#setSpeakerphoneOn(boolean) */ /** @see AudioManager#setSpeakerphoneOn(boolean) */ public void setSpeakerphoneOn(boolean on){ public void setSpeakerphoneOn(IBinder cb, boolean on) { if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) { if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) { return; 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 // for logging only final int uid = Binder.getCallingUid(); final int uid = Binder.getCallingUid(); final int pid = Binder.getCallingPid(); final int pid = Binder.getCallingPid(); final String eventSource = new StringBuilder("setSpeakerphoneOn(").append(on) final String eventSource = new StringBuilder("setSpeakerphoneOn(").append(on) .append(") from u/pid:").append(uid).append("/") .append(") from u/pid:").append(uid).append("/") .append(pid).toString(); .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 new MediaMetrics.Item(MediaMetrics.Name.AUDIO_DEVICE + MediaMetrics.SEPARATOR + "setSpeakerphoneOn") + MediaMetrics.SEPARATOR + "setSpeakerphoneOn") .setUid(uid) .setUid(uid) Loading
services/core/java/com/android/server/audio/BtHelper.java +37 −0 Original line number Original line Diff line number Diff line Loading @@ -38,6 +38,7 @@ import android.util.Log; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.GuardedBy; import java.io.PrintWriter; import java.util.ArrayList; import java.util.ArrayList; import java.util.List; import java.util.List; import java.util.NoSuchElementException; import java.util.NoSuchElementException; Loading Loading @@ -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 { /*package*/ static class BluetoothA2dpDeviceInfo { private final @NonNull BluetoothDevice mBtDevice; private final @NonNull BluetoothDevice mBtDevice; Loading Loading @@ -1007,4 +1028,20 @@ public class BtHelper { return "ENCODING_BT_CODEC_TYPE(" + btCodecType + ")"; 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); } } }