Merge changes from topic "comm_device_hardening_udc_qpr" into udc-qpr-dev

* changes:
  AudioDeviceBroker: clean communication route clients upon device disconnection
  AudioDeviceBroker: ignore communication route requests by idle apps
This commit is contained in:
Eric Laurent
2023-07-03 08:07:26 +00:00
committed by Android (Google) Code Review
4 changed files with 277 additions and 123 deletions

View File

@@ -30,6 +30,8 @@ import android.media.AudioAttributes;
import android.media.AudioDeviceAttributes;
import android.media.AudioDeviceInfo;
import android.media.AudioManager;
import android.media.AudioPlaybackConfiguration;
import android.media.AudioRecordingConfiguration;
import android.media.AudioRoutesInfo;
import android.media.AudioSystem;
import android.media.BluetoothProfileConnectionInfo;
@@ -289,37 +291,38 @@ import java.util.concurrent.atomic.AtomicBoolean;
* @param on
* @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) {
Log.v(TAG, "setSpeakerphoneOn, on: " + on + " pid: " + pid);
Log.v(TAG, "setSpeakerphoneOn, on: " + on + " uid: " + uid);
}
postSetCommunicationDeviceForClient(new CommunicationDeviceInfo(
cb, pid, new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_SPEAKER, ""),
on, BtHelper.SCO_MODE_UNDEFINED, eventSource, false));
cb, uid, new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_SPEAKER, ""),
on, BtHelper.SCO_MODE_UNDEFINED, eventSource, false, isPrivileged));
}
/**
* Select device for use for communication use cases.
* @param cb Client binder for death detection
* @param pid Client pid
* @param uid Client uid
* @param device Device selected or null to unselect.
* @param eventSource for logging purposes
*/
private static final long SET_COMMUNICATION_DEVICE_TIMEOUT_MS = 3000;
/*package*/ boolean setCommunicationDevice(
IBinder cb, int pid, AudioDeviceInfo device, String eventSource) {
/*package*/ boolean setCommunicationDevice(IBinder cb, int uid, AudioDeviceInfo device,
boolean isPrivileged, String eventSource) {
if (AudioService.DEBUG_COMM_RTE) {
Log.v(TAG, "setCommunicationDevice, device: " + device + ", pid: " + pid);
Log.v(TAG, "setCommunicationDevice, device: " + device + ", uid: " + uid);
}
AudioDeviceAttributes deviceAttr =
(device != null) ? new AudioDeviceAttributes(device) : null;
CommunicationDeviceInfo deviceInfo = new CommunicationDeviceInfo(cb, pid, deviceAttr,
device != null, BtHelper.SCO_MODE_UNDEFINED, eventSource, true);
CommunicationDeviceInfo deviceInfo = new CommunicationDeviceInfo(cb, uid, deviceAttr,
device != null, BtHelper.SCO_MODE_UNDEFINED, eventSource, true, isPrivileged);
postSetCommunicationDeviceForClient(deviceInfo);
boolean status;
synchronized (deviceInfo) {
@@ -353,7 +356,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
Log.v(TAG, "onSetCommunicationDeviceForClient: " + deviceInfo);
}
if (!deviceInfo.mOn) {
CommunicationRouteClient client = getCommunicationRouteClientForPid(deviceInfo.mPid);
CommunicationRouteClient client = getCommunicationRouteClientForUid(deviceInfo.mUid);
if (client == null || (deviceInfo.mDevice != null
&& !deviceInfo.mDevice.equals(client.getDevice()))) {
return false;
@@ -361,22 +364,23 @@ import java.util.concurrent.atomic.AtomicBoolean;
}
AudioDeviceAttributes device = deviceInfo.mOn ? deviceInfo.mDevice : null;
setCommunicationRouteForClient(deviceInfo.mCb, deviceInfo.mPid, device,
deviceInfo.mScoAudioMode, deviceInfo.mEventSource);
setCommunicationRouteForClient(deviceInfo.mCb, deviceInfo.mUid, device,
deviceInfo.mScoAudioMode, deviceInfo.mIsPrivileged, deviceInfo.mEventSource);
return true;
}
@GuardedBy("mDeviceStateLock")
/*package*/ void setCommunicationRouteForClient(
IBinder cb, int pid, AudioDeviceAttributes device,
int scoAudioMode, String eventSource) {
IBinder cb, int uid, AudioDeviceAttributes device,
int scoAudioMode, boolean isPrivileged, String eventSource) {
if (AudioService.DEBUG_COMM_RTE) {
Log.v(TAG, "setCommunicationRouteForClient: device: " + device);
Log.v(TAG, "setCommunicationRouteForClient: device: " + device
+ ", eventSource: " + eventSource);
}
AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent(
"setCommunicationRouteForClient for pid: " + pid
+ " device: " + device
"setCommunicationRouteForClient for uid: " + uid
+ " device: " + device + " isPrivileged: " + isPrivileged
+ " from API: " + eventSource)).printLog(TAG));
final boolean wasBtScoRequested = isBluetoothScoRequested();
@@ -385,16 +389,18 @@ import java.util.concurrent.atomic.AtomicBoolean;
// Save previous client route in case of failure to start BT SCO audio
AudioDeviceAttributes prevClientDevice = null;
client = getCommunicationRouteClientForPid(pid);
boolean prevPrivileged = false;
client = getCommunicationRouteClientForUid(uid);
if (client != null) {
prevClientDevice = client.getDevice();
prevPrivileged = client.isPrivileged();
}
if (device != null) {
client = addCommunicationRouteClient(cb, pid, device);
client = addCommunicationRouteClient(cb, uid, device, isPrivileged);
if (client == null) {
Log.w(TAG, "setCommunicationRouteForClient: could not add client for pid: "
+ pid + " and device: " + device);
Log.w(TAG, "setCommunicationRouteForClient: could not add client for uid: "
+ uid + " and device: " + device);
}
} else {
client = removeCommunicationRouteClient(cb, true);
@@ -406,11 +412,11 @@ import java.util.concurrent.atomic.AtomicBoolean;
boolean isBtScoRequested = isBluetoothScoRequested();
if (isBtScoRequested && (!wasBtScoRequested || !isBluetoothScoActive())) {
if (!mBtHelper.startBluetoothSco(scoAudioMode, eventSource)) {
Log.w(TAG, "setCommunicationRouteForClient: failure to start BT SCO for pid: "
+ pid);
Log.w(TAG, "setCommunicationRouteForClient: failure to start BT SCO for uid: "
+ uid);
// clean up or restore previous client selection
if (prevClientDevice != null) {
addCommunicationRouteClient(cb, pid, prevClientDevice);
addCommunicationRouteClient(cb, uid, prevClientDevice, prevPrivileged);
} else {
removeCommunicationRouteClient(cb, true);
}
@@ -447,11 +453,12 @@ import java.util.concurrent.atomic.AtomicBoolean;
@GuardedBy("mDeviceStateLock")
private CommunicationRouteClient topCommunicationRouteClient() {
for (CommunicationRouteClient crc : mCommunicationRouteClients) {
if (crc.getPid() == mAudioModeOwner.mPid) {
if (crc.getUid() == mAudioModeOwner.mUid) {
return crc;
}
}
if (!mCommunicationRouteClients.isEmpty() && mAudioModeOwner.mPid == 0) {
if (!mCommunicationRouteClients.isEmpty() && mAudioModeOwner.mPid == 0
&& mCommunicationRouteClients.get(0).isActive()) {
return mCommunicationRouteClients.get(0);
}
return null;
@@ -491,14 +498,48 @@ import java.util.concurrent.atomic.AtomicBoolean;
};
/*package */ static boolean isValidCommunicationDevice(AudioDeviceInfo device) {
return isValidCommunicationDeviceType(device.getType());
}
private static boolean isValidCommunicationDeviceType(int deviceType) {
for (int type : VALID_COMMUNICATION_DEVICE_TYPES) {
if (device.getType() == type) {
if (deviceType == type) {
return true;
}
}
return false;
}
/*package */
void postCheckCommunicationDeviceRemoval(@NonNull AudioDeviceAttributes device) {
if (!isValidCommunicationDeviceType(
AudioDeviceInfo.convertInternalDeviceToDeviceType(device.getInternalType()))) {
return;
}
sendLMsgNoDelay(MSG_L_CHECK_COMMUNICATION_DEVICE_REMOVAL, SENDMSG_QUEUE, device);
}
@GuardedBy("mDeviceStateLock")
void onCheckCommunicationDeviceRemoval(@NonNull AudioDeviceAttributes device) {
if (AudioService.DEBUG_COMM_RTE) {
Log.v(TAG, "onCheckCommunicationDeviceRemoval device: " + device.toString());
}
for (CommunicationRouteClient crc : mCommunicationRouteClients) {
if (device.equals(crc.getDevice())) {
if (AudioService.DEBUG_COMM_RTE) {
Log.v(TAG, "onCheckCommunicationDeviceRemoval removing client: "
+ crc.toString());
}
// Cancelling the route for this client will remove it from the stack and update
// the communication route.
CommunicationDeviceInfo deviceInfo = new CommunicationDeviceInfo(
crc.getBinder(), crc.getUid(), device, false,
BtHelper.SCO_MODE_UNDEFINED, "onCheckCommunicationDeviceRemoval",
false, crc.isPrivileged());
postSetCommunicationDeviceForClient(deviceInfo);
}
}
}
/* package */ static List<AudioDeviceInfo> getAvailableCommunicationDevices() {
ArrayList<AudioDeviceInfo> commDevices = new ArrayList<>();
AudioDeviceInfo[] allDevices =
@@ -1107,26 +1148,26 @@ import java.util.concurrent.atomic.AtomicBoolean;
sendLMsgNoDelay(MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE, SENDMSG_QUEUE, info);
}
/*package*/ void startBluetoothScoForClient(IBinder cb, int pid, int scoAudioMode,
@NonNull String eventSource) {
/*package*/ void startBluetoothScoForClient(IBinder cb, int uid, int scoAudioMode,
boolean isPrivileged, @NonNull String eventSource) {
if (AudioService.DEBUG_COMM_RTE) {
Log.v(TAG, "startBluetoothScoForClient, pid: " + pid);
Log.v(TAG, "startBluetoothScoForClient, uid: " + uid);
}
postSetCommunicationDeviceForClient(new CommunicationDeviceInfo(
cb, pid, new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, ""),
true, scoAudioMode, eventSource, false));
cb, uid, new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, ""),
true, scoAudioMode, eventSource, false, isPrivileged));
}
/*package*/ void stopBluetoothScoForClient(
IBinder cb, int pid, @NonNull String eventSource) {
IBinder cb, int uid, boolean isPrivileged, @NonNull String eventSource) {
if (AudioService.DEBUG_COMM_RTE) {
Log.v(TAG, "stopBluetoothScoForClient, pid: " + pid);
Log.v(TAG, "stopBluetoothScoForClient, uid: " + uid);
}
postSetCommunicationDeviceForClient(new CommunicationDeviceInfo(
cb, pid, new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, ""),
false, BtHelper.SCO_MODE_UNDEFINED, eventSource, false));
cb, uid, new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, ""),
false, BtHelper.SCO_MODE_UNDEFINED, eventSource, false, isPrivileged));
}
/*package*/ int setPreferredDevicesForStrategySync(int strategy,
@@ -1367,22 +1408,24 @@ import java.util.concurrent.atomic.AtomicBoolean;
/*package*/ static final class CommunicationDeviceInfo {
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 boolean mOn; // true if setting, false if resetting
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
boolean mWaitForStatus; // true if the caller waits for a completion status (API dependent)
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,
@NonNull String eventSource, boolean waitForStatus) {
@NonNull String eventSource, boolean waitForStatus, boolean isPrivileged) {
mCb = cb;
mPid = pid;
mUid = uid;
mDevice = device;
mOn = on;
mScoAudioMode = scoAudioMode;
mIsPrivileged = isPrivileged;
mEventSource = eventSource;
mWaitForStatus = waitForStatus;
}
@@ -1401,16 +1444,17 @@ import java.util.concurrent.atomic.AtomicBoolean;
}
return mCb.equals(((CommunicationDeviceInfo) o).mCb)
&& mPid == ((CommunicationDeviceInfo) o).mPid;
&& mUid == ((CommunicationDeviceInfo) o).mUid;
}
@Override
public String toString() {
return "CommunicationDeviceInfo mCb=" + mCb.toString()
+ " mPid=" + mPid
+ " mUid=" + mUid
+ " mDevice=[" + (mDevice != null ? mDevice.toString() : "null") + "]"
+ " mOn=" + mOn
+ " mScoAudioMode=" + mScoAudioMode
+ " mIsPrivileged=" + mIsPrivileged
+ " mEventSource=" + mEventSource
+ " mWaitForStatus=" + mWaitForStatus
+ " mStatus=" + mStatus;
@@ -1440,7 +1484,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
}
}
/*package*/ boolean handleDeviceConnection(AudioDeviceAttributes attributes,
/*package*/ boolean handleDeviceConnection(@NonNull AudioDeviceAttributes attributes,
boolean connect, @Nullable BluetoothDevice btDevice) {
synchronized (mDeviceStateLock) {
return mDeviceInventory.handleDeviceConnection(
@@ -1507,8 +1551,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
pw.println("\n" + prefix + "Communication route clients:");
mCommunicationRouteClients.forEach((cl) -> {
pw.println(" " + prefix + "pid: " + cl.getPid() + " device: "
+ cl.getDevice() + " cb: " + cl.getBinder()); });
pw.println(" " + prefix + cl.toString()); });
pw.println("\n" + prefix + "Computed Preferred communication device: "
+ preferredCommunicationDevice());
@@ -1850,6 +1893,15 @@ import java.util.concurrent.atomic.AtomicBoolean;
final BluetoothDevice btDevice = (BluetoothDevice) msg.obj;
BtHelper.onNotifyPreferredAudioProfileApplied(btDevice);
} break;
case MSG_L_CHECK_COMMUNICATION_DEVICE_REMOVAL: {
synchronized (mSetModeLock) {
synchronized (mDeviceStateLock) {
onCheckCommunicationDeviceRemoval((AudioDeviceAttributes) msg.obj);
}
}
} break;
default:
Log.wtf(TAG, "Invalid message " + msg.what);
}
@@ -1926,6 +1978,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
private static final int MSG_IL_BTLEAUDIO_TIMEOUT = 49;
private static final int MSG_L_NOTIFY_PREFERRED_AUDIOPROFILE_APPLIED = 52;
private static final int MSG_L_CHECK_COMMUNICATION_DEVICE_REMOVAL = 53;
private static boolean isMessageHandledUnderWakelock(int msgId) {
switch(msgId) {
@@ -2101,13 +2154,20 @@ import java.util.concurrent.atomic.AtomicBoolean;
private class CommunicationRouteClient implements IBinder.DeathRecipient {
private final IBinder mCb;
private final int mPid;
private final int mUid;
private final boolean mIsPrivileged;
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;
mPid = pid;
mUid = uid;
mDevice = device;
mIsPrivileged = isPrivileged;
mPlaybackActive = mAudioService.isPlaybackActiveForUid(uid);
mRecordingActive = mAudioService.isRecordingActiveForUid(uid);
}
public boolean registerDeathRecipient() {
@@ -2138,13 +2198,38 @@ import java.util.concurrent.atomic.AtomicBoolean;
return mCb;
}
int getPid() {
return mPid;
int getUid() {
return mUid;
}
boolean isPrivileged() {
return mIsPrivileged;
}
AudioDeviceAttributes getDevice() {
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")
@@ -2154,8 +2239,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
return;
}
Log.w(TAG, "Communication client died");
setCommunicationRouteForClient(client.getBinder(), client.getPid(), null,
BtHelper.SCO_MODE_UNDEFINED, "onCommunicationRouteClientDied");
setCommunicationRouteForClient(client.getBinder(), client.getUid(), null,
BtHelper.SCO_MODE_UNDEFINED, client.isPrivileged(),
"onCommunicationRouteClientDied");
}
/**
@@ -2242,8 +2328,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
+ crc + " eventSource: " + eventSource);
}
if (crc != null) {
setCommunicationRouteForClient(crc.getBinder(), crc.getPid(), crc.getDevice(),
BtHelper.SCO_MODE_UNDEFINED, eventSource);
setCommunicationRouteForClient(crc.getBinder(), crc.getUid(), crc.getDevice(),
BtHelper.SCO_MODE_UNDEFINED, crc.isPrivileged(), eventSource);
}
}
@@ -2267,6 +2353,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
dispatchCommunicationDevice();
}
@GuardedBy("mDeviceStateLock")
private CommunicationRouteClient removeCommunicationRouteClient(
IBinder cb, boolean unregister) {
for (CommunicationRouteClient cl : mCommunicationRouteClients) {
@@ -2282,11 +2369,12 @@ import java.util.concurrent.atomic.AtomicBoolean;
}
@GuardedBy("mDeviceStateLock")
private CommunicationRouteClient addCommunicationRouteClient(
IBinder cb, int pid, AudioDeviceAttributes device) {
private CommunicationRouteClient addCommunicationRouteClient(IBinder cb, int uid,
AudioDeviceAttributes device, boolean isPrivileged) {
// always insert new request at first position
removeCommunicationRouteClient(cb, true);
CommunicationRouteClient client = new CommunicationRouteClient(cb, pid, device);
CommunicationRouteClient client =
new CommunicationRouteClient(cb, uid, device, isPrivileged);
if (client.registerDeathRecipient()) {
mCommunicationRouteClients.add(0, client);
return client;
@@ -2295,9 +2383,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
}
@GuardedBy("mDeviceStateLock")
private CommunicationRouteClient getCommunicationRouteClientForPid(int pid) {
private CommunicationRouteClient getCommunicationRouteClientForUid(int uid) {
for (CommunicationRouteClient cl : mCommunicationRouteClients) {
if (cl.getPid() == pid) {
if (cl.getUid() == uid) {
return cl;
}
}
@@ -2330,6 +2418,45 @@ import java.util.concurrent.atomic.AtomicBoolean;
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) {
synchronized (mDeviceStateLock) {
return mDeviceInventory.getDeviceSensorUuid(device);

View File

@@ -1245,8 +1245,9 @@ public class AudioDeviceInventory {
* @param btDevice the corresponding Bluetooth device when relevant.
* @return false if an error was reported by AudioSystem
*/
/*package*/ boolean handleDeviceConnection(AudioDeviceAttributes attributes, boolean connect,
boolean isForTesting, @Nullable BluetoothDevice btDevice) {
/*package*/ boolean handleDeviceConnection(@NonNull AudioDeviceAttributes attributes,
boolean connect, boolean isForTesting,
@Nullable BluetoothDevice btDevice) {
int device = attributes.getInternalType();
String address = attributes.getAddress();
String deviceName = attributes.getName();
@@ -1297,6 +1298,7 @@ public class AudioDeviceInventory {
AudioSystem.DEVICE_STATE_UNAVAILABLE, AudioSystem.AUDIO_FORMAT_DEFAULT);
// always remove even if disconnection failed
mConnectedDevices.remove(deviceKey);
mDeviceBroker.postCheckCommunicationDeviceRemoval(attributes);
status = true;
}
if (status) {
@@ -1801,8 +1803,9 @@ public class AudioDeviceInventory {
// device to remove was visible by APM, update APM
mDeviceBroker.clearAvrcpAbsoluteVolumeSupported();
final int res = mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(
AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address),
AudioDeviceAttributes ada = new AudioDeviceAttributes(
AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address);
final int res = mAudioSystem.setDeviceConnectionState(ada,
AudioSystem.DEVICE_STATE_UNAVAILABLE, a2dpCodec);
if (res != AudioSystem.AUDIO_STATUS_OK) {
@@ -1816,11 +1819,13 @@ public class AudioDeviceInventory {
"A2DP device addr=" + address + " made unavailable")).printLog(TAG));
}
mApmConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
// Remove A2DP routes as well
setCurrentAudioRouteNameIfPossible(null, true /*fromA2dp*/);
mmi.record();
updateBluetoothPreferredModes_l(null /*connectedDevice*/);
purgeDevicesRoles_l();
mDeviceBroker.postCheckCommunicationDeviceRemoval(ada);
}
@GuardedBy("mDevicesLock")
@@ -1855,12 +1860,14 @@ public class AudioDeviceInventory {
@GuardedBy("mDevicesLock")
private void makeA2dpSrcUnavailable(String address) {
mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(
AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address),
AudioDeviceAttributes ada = new AudioDeviceAttributes(
AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address);
mAudioSystem.setDeviceConnectionState(ada,
AudioSystem.DEVICE_STATE_UNAVAILABLE,
AudioSystem.AUDIO_FORMAT_DEFAULT);
mConnectedDevices.remove(
DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address));
mDeviceBroker.postCheckCommunicationDeviceRemoval(ada);
}
@GuardedBy("mDevicesLock")
@@ -1893,8 +1900,9 @@ public class AudioDeviceInventory {
@GuardedBy("mDevicesLock")
private void makeHearingAidDeviceUnavailable(String address) {
mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(
AudioSystem.DEVICE_OUT_HEARING_AID, address),
AudioDeviceAttributes ada = new AudioDeviceAttributes(
AudioSystem.DEVICE_OUT_HEARING_AID, address);
mAudioSystem.setDeviceConnectionState(ada,
AudioSystem.DEVICE_STATE_UNAVAILABLE,
AudioSystem.AUDIO_FORMAT_DEFAULT);
mConnectedDevices.remove(
@@ -1906,6 +1914,7 @@ public class AudioDeviceInventory {
.set(MediaMetrics.Property.DEVICE,
AudioSystem.getDeviceName(AudioSystem.DEVICE_OUT_HEARING_AID))
.record();
mDeviceBroker.postCheckCommunicationDeviceRemoval(ada);
}
/**
@@ -2002,9 +2011,10 @@ public class AudioDeviceInventory {
@GuardedBy("mDevicesLock")
private void makeLeAudioDeviceUnavailableNow(String address, int device) {
AudioDeviceAttributes ada = null;
if (device != AudioSystem.DEVICE_NONE) {
final int res = AudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(
device, address),
ada = new AudioDeviceAttributes(device, address);
final int res = AudioSystem.setDeviceConnectionState(ada,
AudioSystem.DEVICE_STATE_UNAVAILABLE,
AudioSystem.AUDIO_FORMAT_DEFAULT);
@@ -2024,6 +2034,9 @@ public class AudioDeviceInventory {
setCurrentAudioRouteNameIfPossible(null, false /*fromA2dp*/);
updateBluetoothPreferredModes_l(null /*connectedDevice*/);
purgeDevicesRoles_l();
if (ada != null) {
mDeviceBroker.postCheckCommunicationDeviceRemoval(ada);
}
}
@GuardedBy("mDevicesLock")

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
// and request an audio mode update immediately. Upon any other change, queue the message
// 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) {
boolean updateAudioMode = false;
int existingMsgPolicy = SENDMSG_QUEUE;
int delay = CHECK_MODE_FOR_UID_PERIOD_MS;
for (SetModeDeathHandler h : mSetModeDeathHandlers) {
boolean wasActive = h.isActive();
h.setPlaybackActive(false);
for (AudioPlaybackConfiguration config : configs) {
final int usage = config.getAudioAttributes().getUsage();
if (config.getClientUid() == h.getUid()
&& (usage == AudioAttributes.USAGE_VOICE_COMMUNICATION
if (playbackConfigs != null) {
h.setPlaybackActive(false);
for (AudioPlaybackConfiguration config : playbackConfigs) {
final int usage = config.getAudioAttributes().getUsage();
if (config.getClientUid() == h.getUid()
&& (usage == AudioAttributes.USAGE_VOICE_COMMUNICATION
|| usage == AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING)
&& config.getPlayerState()
== AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
h.setPlaybackActive(true);
break;
&& config.isActive()) {
h.setPlaybackActive(true);
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()) {
@@ -4315,38 +4334,10 @@ public class AudioService extends IAudioService.Stub
// 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 after a grace period.
synchronized (mDeviceBroker.mSetModeLock) {
boolean updateAudioMode = false;
int existingMsgPolicy = SENDMSG_QUEUE;
int delay = CHECK_MODE_FOR_UID_PERIOD_MS;
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);
}
}
updateAudioModeHandlers(
null /* playbackConfigs */, configs /* recordConfigs */);
mDeviceBroker.updateCommunicationRouteClientsActivity(
null /* playbackConfigs */, configs /* recordConfigs */);
}
private void dumpAudioMode(PrintWriter pw) {
@@ -6299,10 +6290,12 @@ public class AudioService extends IAudioService.Stub
? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED)
.record();
}
final boolean isPrivileged = mContext.checkCallingOrSelfPermission(
android.Manifest.permission.MODIFY_PHONE_STATE)
== PackageManager.PERMISSION_GRANTED;
final long ident = Binder.clearCallingIdentity();
try {
return mDeviceBroker.setCommunicationDevice(cb, pid, device, eventSource);
return mDeviceBroker.setCommunicationDevice(cb, uid, device, isPrivileged, eventSource);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -6348,6 +6341,9 @@ public class AudioService extends IAudioService.Stub
if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) {
return;
}
final boolean isPrivileged = mContext.checkCallingOrSelfPermission(
android.Manifest.permission.MODIFY_PHONE_STATE)
== PackageManager.PERMISSION_GRANTED;
// for logging only
final int uid = Binder.getCallingUid();
@@ -6363,9 +6359,10 @@ public class AudioService extends IAudioService.Stub
.set(MediaMetrics.Property.STATE, on
? MediaMetrics.Value.ON : MediaMetrics.Value.OFF)
.record();
final long ident = Binder.clearCallingIdentity();
try {
mDeviceBroker.setSpeakerphoneOn(cb, pid, on, eventSource);
mDeviceBroker.setSpeakerphoneOn(cb, uid, on, isPrivileged, eventSource);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -6490,7 +6487,7 @@ public class AudioService extends IAudioService.Stub
.set(MediaMetrics.Property.SCO_AUDIO_MODE,
BtHelper.scoAudioModeToString(scoAudioMode))
.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,
BtHelper.scoAudioModeToString(BtHelper.SCO_MODE_VIRTUAL_CALL))
.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)
.set(MediaMetrics.Property.EVENT, "startBluetoothScoInt")
.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();
return;
}
final boolean isPrivileged = mContext.checkCallingOrSelfPermission(
android.Manifest.permission.MODIFY_PHONE_STATE)
== PackageManager.PERMISSION_GRANTED;
final long ident = Binder.clearCallingIdentity();
try {
mDeviceBroker.startBluetoothScoForClient(cb, pid, scoAudioMode, eventSource);
mDeviceBroker.startBluetoothScoForClient(
cb, uid, scoAudioMode, isPrivileged, eventSource);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -6547,9 +6548,12 @@ public class AudioService extends IAudioService.Stub
final String eventSource = new StringBuilder("stopBluetoothSco()")
.append(") from u/pid:").append(uid).append("/")
.append(pid).toString();
final boolean isPrivileged = mContext.checkCallingOrSelfPermission(
android.Manifest.permission.MODIFY_PHONE_STATE)
== PackageManager.PERMISSION_GRANTED;
final long ident = Binder.clearCallingIdentity();
try {
mDeviceBroker.stopBluetoothScoForClient(cb, pid, eventSource);
mDeviceBroker.stopBluetoothScoForClient(cb, uid, isPrivileged, eventSource);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -9284,8 +9288,8 @@ public class AudioService extends IAudioService.Stub
break;
}
boolean wasActive = h.isActive();
h.setPlaybackActive(mPlaybackMonitor.isPlaybackActiveForUid(h.getUid()));
h.setRecordingActive(mRecordMonitor.isRecordingActiveForUid(h.getUid()));
h.setPlaybackActive(isPlaybackActiveForUid(h.getUid()));
h.setRecordingActive(isRecordingActiveForUid(h.getUid()));
if (wasActive != h.isActive()) {
onUpdateAudioMode(AudioSystem.MODE_CURRENT, android.os.Process.myPid(),
mContext.getPackageName(), false /*force*/);
@@ -12378,6 +12382,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
//======================

View File

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