AudioDeviceBroker: ignore communication route requests by idle apps

Monitor playback and recording activity for apps in the communication clients
stack. If the client is not the audio mode owner or not privileged,
do not take its routing request into account. The inactive client
remains in the stack.
Also track communication route clients by UID instead of PID to
facilitate matching with active players and recorders.

Bug: 286545833
Test: atest AudioCommunicationDeviceTest

Change-Id: I23f06b343efcc10c06a7eb482366c735da54da2d
This commit is contained in:
Eric Laurent
2023-06-02 19:27:05 +02:00
parent a812ef3dae
commit 385ca73d1a
3 changed files with 205 additions and 110 deletions

View File

@@ -30,6 +30,8 @@ import android.media.AudioAttributes;
import android.media.AudioDeviceAttributes; import android.media.AudioDeviceAttributes;
import android.media.AudioDeviceInfo; import android.media.AudioDeviceInfo;
import android.media.AudioManager; import android.media.AudioManager;
import android.media.AudioPlaybackConfiguration;
import android.media.AudioRecordingConfiguration;
import android.media.AudioRoutesInfo; import android.media.AudioRoutesInfo;
import android.media.AudioSystem; import android.media.AudioSystem;
import android.media.BluetoothProfileConnectionInfo; import android.media.BluetoothProfileConnectionInfo;
@@ -289,37 +291,38 @@ import java.util.concurrent.atomic.AtomicBoolean;
* @param on * @param on
* @param eventSource for logging purposes * @param eventSource for logging purposes
*/ */
/*package*/ void setSpeakerphoneOn(IBinder cb, int pid, boolean on, String eventSource) { /*package*/ void setSpeakerphoneOn(
IBinder cb, int uid, boolean on, boolean isPrivileged, String eventSource) {
if (AudioService.DEBUG_COMM_RTE) { if (AudioService.DEBUG_COMM_RTE) {
Log.v(TAG, "setSpeakerphoneOn, on: " + on + " pid: " + pid); Log.v(TAG, "setSpeakerphoneOn, on: " + on + " uid: " + uid);
} }
postSetCommunicationDeviceForClient(new CommunicationDeviceInfo( postSetCommunicationDeviceForClient(new CommunicationDeviceInfo(
cb, pid, new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_SPEAKER, ""), cb, uid, new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_SPEAKER, ""),
on, BtHelper.SCO_MODE_UNDEFINED, eventSource, false)); on, BtHelper.SCO_MODE_UNDEFINED, eventSource, false, isPrivileged));
} }
/** /**
* Select device for use for communication use cases. * Select device for use for communication use cases.
* @param cb Client binder for death detection * @param cb Client binder for death detection
* @param pid Client pid * @param uid Client uid
* @param device Device selected or null to unselect. * @param device Device selected or null to unselect.
* @param eventSource for logging purposes * @param eventSource for logging purposes
*/ */
private static final long SET_COMMUNICATION_DEVICE_TIMEOUT_MS = 3000; private static final long SET_COMMUNICATION_DEVICE_TIMEOUT_MS = 3000;
/*package*/ boolean setCommunicationDevice( /*package*/ boolean setCommunicationDevice(IBinder cb, int uid, AudioDeviceInfo device,
IBinder cb, int pid, AudioDeviceInfo device, String eventSource) { boolean isPrivileged, String eventSource) {
if (AudioService.DEBUG_COMM_RTE) { if (AudioService.DEBUG_COMM_RTE) {
Log.v(TAG, "setCommunicationDevice, device: " + device + ", pid: " + pid); Log.v(TAG, "setCommunicationDevice, device: " + device + ", uid: " + uid);
} }
AudioDeviceAttributes deviceAttr = AudioDeviceAttributes deviceAttr =
(device != null) ? new AudioDeviceAttributes(device) : null; (device != null) ? new AudioDeviceAttributes(device) : null;
CommunicationDeviceInfo deviceInfo = new CommunicationDeviceInfo(cb, pid, deviceAttr, CommunicationDeviceInfo deviceInfo = new CommunicationDeviceInfo(cb, uid, deviceAttr,
device != null, BtHelper.SCO_MODE_UNDEFINED, eventSource, true); device != null, BtHelper.SCO_MODE_UNDEFINED, eventSource, true, isPrivileged);
postSetCommunicationDeviceForClient(deviceInfo); postSetCommunicationDeviceForClient(deviceInfo);
boolean status; boolean status;
synchronized (deviceInfo) { synchronized (deviceInfo) {
@@ -353,7 +356,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
Log.v(TAG, "onSetCommunicationDeviceForClient: " + deviceInfo); Log.v(TAG, "onSetCommunicationDeviceForClient: " + deviceInfo);
} }
if (!deviceInfo.mOn) { if (!deviceInfo.mOn) {
CommunicationRouteClient client = getCommunicationRouteClientForPid(deviceInfo.mPid); CommunicationRouteClient client = getCommunicationRouteClientForUid(deviceInfo.mUid);
if (client == null || (deviceInfo.mDevice != null if (client == null || (deviceInfo.mDevice != null
&& !deviceInfo.mDevice.equals(client.getDevice()))) { && !deviceInfo.mDevice.equals(client.getDevice()))) {
return false; return false;
@@ -361,22 +364,22 @@ import java.util.concurrent.atomic.AtomicBoolean;
} }
AudioDeviceAttributes device = deviceInfo.mOn ? deviceInfo.mDevice : null; AudioDeviceAttributes device = deviceInfo.mOn ? deviceInfo.mDevice : null;
setCommunicationRouteForClient(deviceInfo.mCb, deviceInfo.mPid, device, setCommunicationRouteForClient(deviceInfo.mCb, deviceInfo.mUid, device,
deviceInfo.mScoAudioMode, deviceInfo.mEventSource); deviceInfo.mScoAudioMode, deviceInfo.mIsPrivileged, deviceInfo.mEventSource);
return true; return true;
} }
@GuardedBy("mDeviceStateLock") @GuardedBy("mDeviceStateLock")
/*package*/ void setCommunicationRouteForClient( /*package*/ void setCommunicationRouteForClient(
IBinder cb, int pid, AudioDeviceAttributes device, IBinder cb, int uid, AudioDeviceAttributes device,
int scoAudioMode, String eventSource) { int scoAudioMode, boolean isPrivileged, String eventSource) {
if (AudioService.DEBUG_COMM_RTE) { if (AudioService.DEBUG_COMM_RTE) {
Log.v(TAG, "setCommunicationRouteForClient: device: " + device); Log.v(TAG, "setCommunicationRouteForClient: device: " + device);
} }
AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent( AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent(
"setCommunicationRouteForClient for pid: " + pid "setCommunicationRouteForClient for uid: " + uid
+ " device: " + device + " device: " + device + " isPrivileged: " + isPrivileged
+ " from API: " + eventSource)).printLog(TAG)); + " from API: " + eventSource)).printLog(TAG));
final boolean wasBtScoRequested = isBluetoothScoRequested(); final boolean wasBtScoRequested = isBluetoothScoRequested();
@@ -385,16 +388,18 @@ import java.util.concurrent.atomic.AtomicBoolean;
// Save previous client route in case of failure to start BT SCO audio // Save previous client route in case of failure to start BT SCO audio
AudioDeviceAttributes prevClientDevice = null; AudioDeviceAttributes prevClientDevice = null;
client = getCommunicationRouteClientForPid(pid); boolean prevPrivileged = false;
client = getCommunicationRouteClientForUid(uid);
if (client != null) { if (client != null) {
prevClientDevice = client.getDevice(); prevClientDevice = client.getDevice();
prevPrivileged = client.isPrivileged();
} }
if (device != null) { if (device != null) {
client = addCommunicationRouteClient(cb, pid, device); client = addCommunicationRouteClient(cb, uid, device, isPrivileged);
if (client == null) { if (client == null) {
Log.w(TAG, "setCommunicationRouteForClient: could not add client for pid: " Log.w(TAG, "setCommunicationRouteForClient: could not add client for uid: "
+ pid + " and device: " + device); + uid + " and device: " + device);
} }
} else { } else {
client = removeCommunicationRouteClient(cb, true); client = removeCommunicationRouteClient(cb, true);
@@ -406,11 +411,11 @@ import java.util.concurrent.atomic.AtomicBoolean;
boolean isBtScoRequested = isBluetoothScoRequested(); boolean isBtScoRequested = isBluetoothScoRequested();
if (isBtScoRequested && (!wasBtScoRequested || !isBluetoothScoActive())) { if (isBtScoRequested && (!wasBtScoRequested || !isBluetoothScoActive())) {
if (!mBtHelper.startBluetoothSco(scoAudioMode, eventSource)) { if (!mBtHelper.startBluetoothSco(scoAudioMode, eventSource)) {
Log.w(TAG, "setCommunicationRouteForClient: failure to start BT SCO for pid: " Log.w(TAG, "setCommunicationRouteForClient: failure to start BT SCO for uid: "
+ pid); + uid);
// clean up or restore previous client selection // clean up or restore previous client selection
if (prevClientDevice != null) { if (prevClientDevice != null) {
addCommunicationRouteClient(cb, pid, prevClientDevice); addCommunicationRouteClient(cb, uid, prevClientDevice, prevPrivileged);
} else { } else {
removeCommunicationRouteClient(cb, true); removeCommunicationRouteClient(cb, true);
} }
@@ -447,11 +452,12 @@ import java.util.concurrent.atomic.AtomicBoolean;
@GuardedBy("mDeviceStateLock") @GuardedBy("mDeviceStateLock")
private CommunicationRouteClient topCommunicationRouteClient() { private CommunicationRouteClient topCommunicationRouteClient() {
for (CommunicationRouteClient crc : mCommunicationRouteClients) { for (CommunicationRouteClient crc : mCommunicationRouteClients) {
if (crc.getPid() == mAudioModeOwner.mPid) { if (crc.getUid() == mAudioModeOwner.mUid) {
return crc; return crc;
} }
} }
if (!mCommunicationRouteClients.isEmpty() && mAudioModeOwner.mPid == 0) { if (!mCommunicationRouteClients.isEmpty() && mAudioModeOwner.mPid == 0
&& mCommunicationRouteClients.get(0).isActive()) {
return mCommunicationRouteClients.get(0); return mCommunicationRouteClients.get(0);
} }
return null; return null;
@@ -1107,26 +1113,26 @@ import java.util.concurrent.atomic.AtomicBoolean;
sendLMsgNoDelay(MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE, SENDMSG_QUEUE, info); sendLMsgNoDelay(MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE, SENDMSG_QUEUE, info);
} }
/*package*/ void startBluetoothScoForClient(IBinder cb, int pid, int scoAudioMode, /*package*/ void startBluetoothScoForClient(IBinder cb, int uid, int scoAudioMode,
@NonNull String eventSource) { boolean isPrivileged, @NonNull String eventSource) {
if (AudioService.DEBUG_COMM_RTE) { if (AudioService.DEBUG_COMM_RTE) {
Log.v(TAG, "startBluetoothScoForClient, pid: " + pid); Log.v(TAG, "startBluetoothScoForClient, uid: " + uid);
} }
postSetCommunicationDeviceForClient(new CommunicationDeviceInfo( postSetCommunicationDeviceForClient(new CommunicationDeviceInfo(
cb, pid, new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, ""), cb, uid, new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, ""),
true, scoAudioMode, eventSource, false)); true, scoAudioMode, eventSource, false, isPrivileged));
} }
/*package*/ void stopBluetoothScoForClient( /*package*/ void stopBluetoothScoForClient(
IBinder cb, int pid, @NonNull String eventSource) { IBinder cb, int uid, boolean isPrivileged, @NonNull String eventSource) {
if (AudioService.DEBUG_COMM_RTE) { if (AudioService.DEBUG_COMM_RTE) {
Log.v(TAG, "stopBluetoothScoForClient, pid: " + pid); Log.v(TAG, "stopBluetoothScoForClient, uid: " + uid);
} }
postSetCommunicationDeviceForClient(new CommunicationDeviceInfo( postSetCommunicationDeviceForClient(new CommunicationDeviceInfo(
cb, pid, new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, ""), cb, uid, new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, ""),
false, BtHelper.SCO_MODE_UNDEFINED, eventSource, false)); false, BtHelper.SCO_MODE_UNDEFINED, eventSource, false, isPrivileged));
} }
/*package*/ int setPreferredDevicesForStrategySync(int strategy, /*package*/ int setPreferredDevicesForStrategySync(int strategy,
@@ -1367,22 +1373,24 @@ import java.util.concurrent.atomic.AtomicBoolean;
/*package*/ static final class CommunicationDeviceInfo { /*package*/ static final class CommunicationDeviceInfo {
final @NonNull IBinder mCb; // Identifies the requesting client for death handler final @NonNull IBinder mCb; // Identifies the requesting client for death handler
final int mPid; // Requester process ID final int mUid; // Requester UID
final @Nullable AudioDeviceAttributes mDevice; // Device being set or reset. final @Nullable AudioDeviceAttributes mDevice; // Device being set or reset.
final boolean mOn; // true if setting, false if resetting final boolean mOn; // true if setting, false if resetting
final int mScoAudioMode; // only used for SCO: requested audio mode final int mScoAudioMode; // only used for SCO: requested audio mode
final boolean mIsPrivileged; // true if the client app has MODIFY_PHONE_STATE permission
final @NonNull String mEventSource; // caller identifier for logging final @NonNull String mEventSource; // caller identifier for logging
boolean mWaitForStatus; // true if the caller waits for a completion status (API dependent) boolean mWaitForStatus; // true if the caller waits for a completion status (API dependent)
boolean mStatus = false; // completion status only used if mWaitForStatus is true boolean mStatus = false; // completion status only used if mWaitForStatus is true
CommunicationDeviceInfo(@NonNull IBinder cb, int pid, CommunicationDeviceInfo(@NonNull IBinder cb, int uid,
@Nullable AudioDeviceAttributes device, boolean on, int scoAudioMode, @Nullable AudioDeviceAttributes device, boolean on, int scoAudioMode,
@NonNull String eventSource, boolean waitForStatus) { @NonNull String eventSource, boolean waitForStatus, boolean isPrivileged) {
mCb = cb; mCb = cb;
mPid = pid; mUid = uid;
mDevice = device; mDevice = device;
mOn = on; mOn = on;
mScoAudioMode = scoAudioMode; mScoAudioMode = scoAudioMode;
mIsPrivileged = isPrivileged;
mEventSource = eventSource; mEventSource = eventSource;
mWaitForStatus = waitForStatus; mWaitForStatus = waitForStatus;
} }
@@ -1401,16 +1409,17 @@ import java.util.concurrent.atomic.AtomicBoolean;
} }
return mCb.equals(((CommunicationDeviceInfo) o).mCb) return mCb.equals(((CommunicationDeviceInfo) o).mCb)
&& mPid == ((CommunicationDeviceInfo) o).mPid; && mUid == ((CommunicationDeviceInfo) o).mUid;
} }
@Override @Override
public String toString() { public String toString() {
return "CommunicationDeviceInfo mCb=" + mCb.toString() return "CommunicationDeviceInfo mCb=" + mCb.toString()
+ " mPid=" + mPid + " mUid=" + mUid
+ " mDevice=[" + (mDevice != null ? mDevice.toString() : "null") + "]" + " mDevice=[" + (mDevice != null ? mDevice.toString() : "null") + "]"
+ " mOn=" + mOn + " mOn=" + mOn
+ " mScoAudioMode=" + mScoAudioMode + " mScoAudioMode=" + mScoAudioMode
+ " mIsPrivileged=" + mIsPrivileged
+ " mEventSource=" + mEventSource + " mEventSource=" + mEventSource
+ " mWaitForStatus=" + mWaitForStatus + " mWaitForStatus=" + mWaitForStatus
+ " mStatus=" + mStatus; + " mStatus=" + mStatus;
@@ -1507,8 +1516,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
pw.println("\n" + prefix + "Communication route clients:"); pw.println("\n" + prefix + "Communication route clients:");
mCommunicationRouteClients.forEach((cl) -> { mCommunicationRouteClients.forEach((cl) -> {
pw.println(" " + prefix + "pid: " + cl.getPid() + " device: " pw.println(" " + prefix + cl.toString()); });
+ cl.getDevice() + " cb: " + cl.getBinder()); });
pw.println("\n" + prefix + "Computed Preferred communication device: " pw.println("\n" + prefix + "Computed Preferred communication device: "
+ preferredCommunicationDevice()); + preferredCommunicationDevice());
@@ -2101,13 +2109,20 @@ import java.util.concurrent.atomic.AtomicBoolean;
private class CommunicationRouteClient implements IBinder.DeathRecipient { private class CommunicationRouteClient implements IBinder.DeathRecipient {
private final IBinder mCb; private final IBinder mCb;
private final int mPid; private final int mUid;
private final boolean mIsPrivileged;
private AudioDeviceAttributes mDevice; private AudioDeviceAttributes mDevice;
private boolean mPlaybackActive;
private boolean mRecordingActive;
CommunicationRouteClient(IBinder cb, int pid, AudioDeviceAttributes device) { CommunicationRouteClient(IBinder cb, int uid, AudioDeviceAttributes device,
boolean isPrivileged) {
mCb = cb; mCb = cb;
mPid = pid; mUid = uid;
mDevice = device; mDevice = device;
mIsPrivileged = isPrivileged;
mPlaybackActive = mAudioService.isPlaybackActiveForUid(uid);
mRecordingActive = mAudioService.isRecordingActiveForUid(uid);
} }
public boolean registerDeathRecipient() { public boolean registerDeathRecipient() {
@@ -2138,13 +2153,38 @@ import java.util.concurrent.atomic.AtomicBoolean;
return mCb; return mCb;
} }
int getPid() { int getUid() {
return mPid; return mUid;
}
boolean isPrivileged() {
return mIsPrivileged;
} }
AudioDeviceAttributes getDevice() { AudioDeviceAttributes getDevice() {
return mDevice; return mDevice;
} }
public void setPlaybackActive(boolean active) {
mPlaybackActive = active;
}
public void setRecordingActive(boolean active) {
mRecordingActive = active;
}
public boolean isActive() {
return mIsPrivileged || mRecordingActive || mPlaybackActive;
}
@Override
public String toString() {
return "[CommunicationRouteClient: mUid: " + mUid
+ " mDevice: " + mDevice.toString()
+ " mIsPrivileged: " + mIsPrivileged
+ " mPlaybackActive: " + mPlaybackActive
+ " mRecordingActive: " + mRecordingActive + "]";
}
} }
// @GuardedBy("mSetModeLock") // @GuardedBy("mSetModeLock")
@@ -2154,8 +2194,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
return; return;
} }
Log.w(TAG, "Communication client died"); Log.w(TAG, "Communication client died");
setCommunicationRouteForClient(client.getBinder(), client.getPid(), null, setCommunicationRouteForClient(client.getBinder(), client.getUid(), null,
BtHelper.SCO_MODE_UNDEFINED, "onCommunicationRouteClientDied"); BtHelper.SCO_MODE_UNDEFINED, client.isPrivileged(),
"onCommunicationRouteClientDied");
} }
/** /**
@@ -2242,8 +2283,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
+ crc + " eventSource: " + eventSource); + crc + " eventSource: " + eventSource);
} }
if (crc != null) { if (crc != null) {
setCommunicationRouteForClient(crc.getBinder(), crc.getPid(), crc.getDevice(), setCommunicationRouteForClient(crc.getBinder(), crc.getUid(), crc.getDevice(),
BtHelper.SCO_MODE_UNDEFINED, eventSource); BtHelper.SCO_MODE_UNDEFINED, crc.isPrivileged(), eventSource);
} }
} }
@@ -2282,11 +2323,12 @@ import java.util.concurrent.atomic.AtomicBoolean;
} }
@GuardedBy("mDeviceStateLock") @GuardedBy("mDeviceStateLock")
private CommunicationRouteClient addCommunicationRouteClient( private CommunicationRouteClient addCommunicationRouteClient(IBinder cb, int uid,
IBinder cb, int pid, AudioDeviceAttributes device) { AudioDeviceAttributes device, boolean isPrivileged) {
// always insert new request at first position // always insert new request at first position
removeCommunicationRouteClient(cb, true); removeCommunicationRouteClient(cb, true);
CommunicationRouteClient client = new CommunicationRouteClient(cb, pid, device); CommunicationRouteClient client =
new CommunicationRouteClient(cb, uid, device, isPrivileged);
if (client.registerDeathRecipient()) { if (client.registerDeathRecipient()) {
mCommunicationRouteClients.add(0, client); mCommunicationRouteClients.add(0, client);
return client; return client;
@@ -2295,9 +2337,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
} }
@GuardedBy("mDeviceStateLock") @GuardedBy("mDeviceStateLock")
private CommunicationRouteClient getCommunicationRouteClientForPid(int pid) { private CommunicationRouteClient getCommunicationRouteClientForUid(int uid) {
for (CommunicationRouteClient cl : mCommunicationRouteClients) { for (CommunicationRouteClient cl : mCommunicationRouteClients) {
if (cl.getPid() == pid) { if (cl.getUid() == uid) {
return cl; return cl;
} }
} }
@@ -2330,6 +2372,45 @@ import java.util.concurrent.atomic.AtomicBoolean;
return device; return device;
} }
void updateCommunicationRouteClientsActivity(
List<AudioPlaybackConfiguration> playbackConfigs,
List<AudioRecordingConfiguration> recordConfigs) {
synchronized (mSetModeLock) {
synchronized (mDeviceStateLock) {
boolean updateCommunicationRoute = false;
for (CommunicationRouteClient crc : mCommunicationRouteClients) {
boolean wasActive = crc.isActive();
if (playbackConfigs != null) {
crc.setPlaybackActive(false);
for (AudioPlaybackConfiguration config : playbackConfigs) {
if (config.getClientUid() == crc.getUid()
&& config.isActive()) {
crc.setPlaybackActive(true);
break;
}
}
}
if (recordConfigs != null) {
crc.setRecordingActive(false);
for (AudioRecordingConfiguration config : recordConfigs) {
if (config.getClientUid() == crc.getUid()
&& !config.isClientSilenced()) {
crc.setRecordingActive(true);
break;
}
}
}
if (wasActive != crc.isActive()) {
updateCommunicationRoute = true;
}
}
if (updateCommunicationRoute) {
postUpdateCommunicationRouteClient("updateCommunicationRouteClientsActivity");
}
}
}
}
@Nullable UUID getDeviceSensorUuid(AudioDeviceAttributes device) { @Nullable UUID getDeviceSensorUuid(AudioDeviceAttributes device) {
synchronized (mDeviceStateLock) { synchronized (mDeviceStateLock) {
return mDeviceInventory.getDeviceSensorUuid(device); return mDeviceInventory.getDeviceSensorUuid(device);

View File

@@ -4262,22 +4262,41 @@ public class AudioService extends IAudioService.Stub
// When the audio mode owner becomes active, replace any delayed MSG_UPDATE_AUDIO_MODE // When the audio mode owner becomes active, replace any delayed MSG_UPDATE_AUDIO_MODE
// and request an audio mode update immediately. Upon any other change, queue the message // and request an audio mode update immediately. Upon any other change, queue the message
// and request an audio mode update after a grace period. // and request an audio mode update after a grace period.
updateAudioModeHandlers(
configs /* playbackConfigs */, null /* recordConfigs */);
mDeviceBroker.updateCommunicationRouteClientsActivity(
configs /* playbackConfigs */, null /* recordConfigs */);
}
void updateAudioModeHandlers(List<AudioPlaybackConfiguration> playbackConfigs,
List<AudioRecordingConfiguration> recordConfigs) {
synchronized (mDeviceBroker.mSetModeLock) { synchronized (mDeviceBroker.mSetModeLock) {
boolean updateAudioMode = false; boolean updateAudioMode = false;
int existingMsgPolicy = SENDMSG_QUEUE; int existingMsgPolicy = SENDMSG_QUEUE;
int delay = CHECK_MODE_FOR_UID_PERIOD_MS; int delay = CHECK_MODE_FOR_UID_PERIOD_MS;
for (SetModeDeathHandler h : mSetModeDeathHandlers) { for (SetModeDeathHandler h : mSetModeDeathHandlers) {
boolean wasActive = h.isActive(); boolean wasActive = h.isActive();
h.setPlaybackActive(false); if (playbackConfigs != null) {
for (AudioPlaybackConfiguration config : configs) { h.setPlaybackActive(false);
final int usage = config.getAudioAttributes().getUsage(); for (AudioPlaybackConfiguration config : playbackConfigs) {
if (config.getClientUid() == h.getUid() final int usage = config.getAudioAttributes().getUsage();
&& (usage == AudioAttributes.USAGE_VOICE_COMMUNICATION if (config.getClientUid() == h.getUid()
&& (usage == AudioAttributes.USAGE_VOICE_COMMUNICATION
|| usage == AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING) || usage == AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING)
&& config.getPlayerState() && config.isActive()) {
== AudioPlaybackConfiguration.PLAYER_STATE_STARTED) { h.setPlaybackActive(true);
h.setPlaybackActive(true); break;
break; }
}
}
if (recordConfigs != null) {
h.setRecordingActive(false);
for (AudioRecordingConfiguration config : recordConfigs) {
if (config.getClientUid() == h.getUid() && !config.isClientSilenced()
&& config.getAudioSource() == AudioSource.VOICE_COMMUNICATION) {
h.setRecordingActive(true);
break;
}
} }
} }
if (wasActive != h.isActive()) { if (wasActive != h.isActive()) {
@@ -4315,38 +4334,10 @@ public class AudioService extends IAudioService.Stub
// When the audio mode owner becomes active, replace any delayed MSG_UPDATE_AUDIO_MODE // When the audio mode owner becomes active, replace any delayed MSG_UPDATE_AUDIO_MODE
// and request an audio mode update immediately. Upon any other change, queue the message // and request an audio mode update immediately. Upon any other change, queue the message
// and request an audio mode update after a grace period. // and request an audio mode update after a grace period.
synchronized (mDeviceBroker.mSetModeLock) { updateAudioModeHandlers(
boolean updateAudioMode = false; null /* playbackConfigs */, configs /* recordConfigs */);
int existingMsgPolicy = SENDMSG_QUEUE; mDeviceBroker.updateCommunicationRouteClientsActivity(
int delay = CHECK_MODE_FOR_UID_PERIOD_MS; null /* playbackConfigs */, configs /* recordConfigs */);
for (SetModeDeathHandler h : mSetModeDeathHandlers) {
boolean wasActive = h.isActive();
h.setRecordingActive(false);
for (AudioRecordingConfiguration config : configs) {
if (config.getClientUid() == h.getUid()
&& config.getAudioSource() == AudioSource.VOICE_COMMUNICATION) {
h.setRecordingActive(true);
break;
}
}
if (wasActive != h.isActive()) {
updateAudioMode = true;
if (h.isActive() && h == getAudioModeOwnerHandler()) {
existingMsgPolicy = SENDMSG_REPLACE;
delay = 0;
}
}
}
if (updateAudioMode) {
sendMsg(mAudioHandler,
MSG_UPDATE_AUDIO_MODE,
existingMsgPolicy,
AudioSystem.MODE_CURRENT,
android.os.Process.myPid(),
mContext.getPackageName(),
delay);
}
}
} }
private void dumpAudioMode(PrintWriter pw) { private void dumpAudioMode(PrintWriter pw) {
@@ -6299,10 +6290,12 @@ public class AudioService extends IAudioService.Stub
? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED) ? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED)
.record(); .record();
} }
final boolean isPrivileged = mContext.checkCallingOrSelfPermission(
android.Manifest.permission.MODIFY_PHONE_STATE)
== PackageManager.PERMISSION_GRANTED;
final long ident = Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity();
try { try {
return mDeviceBroker.setCommunicationDevice(cb, pid, device, eventSource); return mDeviceBroker.setCommunicationDevice(cb, uid, device, isPrivileged, eventSource);
} finally { } finally {
Binder.restoreCallingIdentity(ident); Binder.restoreCallingIdentity(ident);
} }
@@ -6348,6 +6341,9 @@ public class AudioService extends IAudioService.Stub
if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) { if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) {
return; return;
} }
final boolean isPrivileged = mContext.checkCallingOrSelfPermission(
android.Manifest.permission.MODIFY_PHONE_STATE)
== PackageManager.PERMISSION_GRANTED;
// for logging only // for logging only
final int uid = Binder.getCallingUid(); final int uid = Binder.getCallingUid();
@@ -6363,9 +6359,10 @@ public class AudioService extends IAudioService.Stub
.set(MediaMetrics.Property.STATE, on .set(MediaMetrics.Property.STATE, on
? MediaMetrics.Value.ON : MediaMetrics.Value.OFF) ? MediaMetrics.Value.ON : MediaMetrics.Value.OFF)
.record(); .record();
final long ident = Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity();
try { try {
mDeviceBroker.setSpeakerphoneOn(cb, pid, on, eventSource); mDeviceBroker.setSpeakerphoneOn(cb, uid, on, isPrivileged, eventSource);
} finally { } finally {
Binder.restoreCallingIdentity(ident); Binder.restoreCallingIdentity(ident);
} }
@@ -6490,7 +6487,7 @@ public class AudioService extends IAudioService.Stub
.set(MediaMetrics.Property.SCO_AUDIO_MODE, .set(MediaMetrics.Property.SCO_AUDIO_MODE,
BtHelper.scoAudioModeToString(scoAudioMode)) BtHelper.scoAudioModeToString(scoAudioMode))
.record(); .record();
startBluetoothScoInt(cb, pid, scoAudioMode, eventSource); startBluetoothScoInt(cb, uid, scoAudioMode, eventSource);
} }
@@ -6513,10 +6510,10 @@ public class AudioService extends IAudioService.Stub
.set(MediaMetrics.Property.SCO_AUDIO_MODE, .set(MediaMetrics.Property.SCO_AUDIO_MODE,
BtHelper.scoAudioModeToString(BtHelper.SCO_MODE_VIRTUAL_CALL)) BtHelper.scoAudioModeToString(BtHelper.SCO_MODE_VIRTUAL_CALL))
.record(); .record();
startBluetoothScoInt(cb, pid, BtHelper.SCO_MODE_VIRTUAL_CALL, eventSource); startBluetoothScoInt(cb, uid, BtHelper.SCO_MODE_VIRTUAL_CALL, eventSource);
} }
void startBluetoothScoInt(IBinder cb, int pid, int scoAudioMode, @NonNull String eventSource) { void startBluetoothScoInt(IBinder cb, int uid, int scoAudioMode, @NonNull String eventSource) {
MediaMetrics.Item mmi = new MediaMetrics.Item(MediaMetrics.Name.AUDIO_BLUETOOTH) MediaMetrics.Item mmi = new MediaMetrics.Item(MediaMetrics.Name.AUDIO_BLUETOOTH)
.set(MediaMetrics.Property.EVENT, "startBluetoothScoInt") .set(MediaMetrics.Property.EVENT, "startBluetoothScoInt")
.set(MediaMetrics.Property.SCO_AUDIO_MODE, .set(MediaMetrics.Property.SCO_AUDIO_MODE,
@@ -6527,9 +6524,13 @@ public class AudioService extends IAudioService.Stub
mmi.set(MediaMetrics.Property.EARLY_RETURN, "permission or systemReady").record(); mmi.set(MediaMetrics.Property.EARLY_RETURN, "permission or systemReady").record();
return; return;
} }
final boolean isPrivileged = mContext.checkCallingOrSelfPermission(
android.Manifest.permission.MODIFY_PHONE_STATE)
== PackageManager.PERMISSION_GRANTED;
final long ident = Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity();
try { try {
mDeviceBroker.startBluetoothScoForClient(cb, pid, scoAudioMode, eventSource); mDeviceBroker.startBluetoothScoForClient(
cb, uid, scoAudioMode, isPrivileged, eventSource);
} finally { } finally {
Binder.restoreCallingIdentity(ident); Binder.restoreCallingIdentity(ident);
} }
@@ -6547,9 +6548,12 @@ public class AudioService extends IAudioService.Stub
final String eventSource = new StringBuilder("stopBluetoothSco()") final String eventSource = new StringBuilder("stopBluetoothSco()")
.append(") from u/pid:").append(uid).append("/") .append(") from u/pid:").append(uid).append("/")
.append(pid).toString(); .append(pid).toString();
final boolean isPrivileged = mContext.checkCallingOrSelfPermission(
android.Manifest.permission.MODIFY_PHONE_STATE)
== PackageManager.PERMISSION_GRANTED;
final long ident = Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity();
try { try {
mDeviceBroker.stopBluetoothScoForClient(cb, pid, eventSource); mDeviceBroker.stopBluetoothScoForClient(cb, uid, isPrivileged, eventSource);
} finally { } finally {
Binder.restoreCallingIdentity(ident); Binder.restoreCallingIdentity(ident);
} }
@@ -9282,8 +9286,8 @@ public class AudioService extends IAudioService.Stub
break; break;
} }
boolean wasActive = h.isActive(); boolean wasActive = h.isActive();
h.setPlaybackActive(mPlaybackMonitor.isPlaybackActiveForUid(h.getUid())); h.setPlaybackActive(isPlaybackActiveForUid(h.getUid()));
h.setRecordingActive(mRecordMonitor.isRecordingActiveForUid(h.getUid())); h.setRecordingActive(isRecordingActiveForUid(h.getUid()));
if (wasActive != h.isActive()) { if (wasActive != h.isActive()) {
onUpdateAudioMode(AudioSystem.MODE_CURRENT, android.os.Process.myPid(), onUpdateAudioMode(AudioSystem.MODE_CURRENT, android.os.Process.myPid(),
mContext.getPackageName(), false /*force*/); mContext.getPackageName(), false /*force*/);
@@ -12381,6 +12385,16 @@ public class AudioService extends IAudioService.Stub
} }
} }
/* package */
boolean isPlaybackActiveForUid(int uid) {
return mPlaybackMonitor.isPlaybackActiveForUid(uid);
}
/* package */
boolean isRecordingActiveForUid(int uid) {
return mRecordMonitor.isRecordingActiveForUid(uid);
}
//====================== //======================
// Audio device management // Audio device management
//====================== //======================

View File

@@ -227,8 +227,8 @@ public final class RecordingActivityMonitor implements AudioSystem.AudioRecordin
synchronized (mRecordStates) { synchronized (mRecordStates) {
for (RecordingState state : mRecordStates) { for (RecordingState state : mRecordStates) {
// Note: isActiveConfiguration() == true => state.getConfig() != null // Note: isActiveConfiguration() == true => state.getConfig() != null
if (state.isActiveConfiguration() if (state.isActiveConfiguration() && state.getConfig().getClientUid() == uid
&& state.getConfig().getClientUid() == uid) { && !state.getConfig().isClientSilenced()) {
return true; return true;
} }
} }