Loading media/java/android/media/MediaMetrics.java +5 −0 Original line number Diff line number Diff line Loading @@ -53,6 +53,7 @@ public class MediaMetrics { public static final String AUDIO_SERVICE = AUDIO + SEPARATOR + "service"; public static final String AUDIO_VOLUME = AUDIO + SEPARATOR + "volume"; public static final String AUDIO_VOLUME_EVENT = AUDIO_VOLUME + SEPARATOR + "event"; public static final String AUDIO_MODE = AUDIO + SEPARATOR + "mode"; } /** Loading Loading @@ -140,6 +141,10 @@ public class MediaMetrics { public static final Key<String> REQUEST = createKey("request", String.class); // For audio mode public static final Key<String> REQUESTED_MODE = createKey("requestedMode", String.class); // audio_mode // For Bluetooth public static final Key<String> SCO_AUDIO_MODE = createKey("scoAudioMode", String.class); Loading services/core/java/com/android/server/audio/AudioService.java +54 −2 Original line number Diff line number Diff line Loading @@ -283,6 +283,7 @@ public class AudioService extends IAudioService.Stub private static final int MSG_HDMI_VOLUME_CHECK = 28; private static final int MSG_PLAYBACK_CONFIG_CHANGE = 29; private static final int MSG_BROADCAST_MICROPHONE_MUTE = 30; private static final int MSG_CHECK_MODE_FOR_UID = 31; // start of messages handled under wakelock // these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(), // and not with sendMsg(..., ..., SENDMSG_QUEUE, ...) Loading Loading @@ -3679,12 +3680,14 @@ public class AudioService extends IAudioService.Stub private final IBinder mCb; // To be notified of client's death private final int mPid; private final int mUid; private String mPackage; private int mMode = AudioSystem.MODE_NORMAL; // Current mode set by this client SetModeDeathHandler(IBinder cb, int pid, int uid) { SetModeDeathHandler(IBinder cb, int pid, int uid, String caller) { mCb = cb; mPid = pid; mUid = uid; mPackage = caller; } public void binderDied() { Loading Loading @@ -3722,6 +3725,10 @@ public class AudioService extends IAudioService.Stub public int getUid() { return mUid; } public String getPackage() { return mPackage; } } /** @see AudioManager#setMode(int) */ Loading Loading @@ -3803,6 +3810,9 @@ public class AudioService extends IAudioService.Stub hdlr = h; // Remove from client list so that it is re-inserted at top of list iter.remove(); if (hdlr.getMode() == AudioSystem.MODE_IN_COMMUNICATION) { mAudioHandler.removeEqualMessages(MSG_CHECK_MODE_FOR_UID, hdlr); } try { hdlr.getBinder().unlinkToDeath(hdlr, 0); if (cb != hdlr.getBinder()) { Loading Loading @@ -3833,7 +3843,7 @@ public class AudioService extends IAudioService.Stub } } else { if (hdlr == null) { hdlr = new SetModeDeathHandler(cb, pid, uid); hdlr = new SetModeDeathHandler(cb, pid, uid, caller); } // Register for client death notification try { Loading Loading @@ -3880,6 +3890,7 @@ public class AudioService extends IAudioService.Stub // Note: newModeOwnerPid is always 0 when actualMode is MODE_NORMAL mModeLogger.log( new PhoneStateEvent(caller, pid, mode, newModeOwnerPid, actualMode)); int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE); int device = getDeviceForStream(streamType); int index = mStreamStates[mStreamVolumeAlias[streamType]].getIndex(device); Loading @@ -3890,6 +3901,16 @@ public class AudioService extends IAudioService.Stub // change of mode may require volume to be re-applied on some devices updateAbsVolumeMultiModeDevices(oldMode, actualMode); if (actualMode == AudioSystem.MODE_IN_COMMUNICATION) { sendMsg(mAudioHandler, MSG_CHECK_MODE_FOR_UID, SENDMSG_QUEUE, 0, 0, hdlr, CHECK_MODE_FOR_UID_PERIOD_MS); } } return newModeOwnerPid; } Loading Loading @@ -6374,6 +6395,35 @@ public class AudioService extends IAudioService.Stub case MSG_BROADCAST_MICROPHONE_MUTE: mSystemServer.sendMicrophoneMuteChangedIntent(); break; case MSG_CHECK_MODE_FOR_UID: synchronized (mDeviceBroker.mSetModeLock) { if (msg.obj == null) { break; } // If the app corresponding to this mode death handler object is not // capturing or playing audio anymore after 3 seconds, remove it // from the stack. Otherwise, check again in 3 seconds. SetModeDeathHandler h = (SetModeDeathHandler) msg.obj; if (mSetModeDeathHandlers.indexOf(h) < 0) { break; } if (mRecordMonitor.isRecordingActiveForUid(h.getUid()) || mPlaybackMonitor.isPlaybackActiveForUid(h.getUid())) { sendMsg(mAudioHandler, MSG_CHECK_MODE_FOR_UID, SENDMSG_QUEUE, 0, 0, h, CHECK_MODE_FOR_UID_PERIOD_MS); break; } // For now just log the fact that an app is hogging the audio mode. // TODO(b/160260850): remove abusive app from audio mode stack. mModeLogger.log(new PhoneStateEvent(h.getPackage(), h.getPid())); } break; } } } Loading Loading @@ -7017,6 +7067,8 @@ public class AudioService extends IAudioService.Stub private static final int UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX = (20 * 3600 * 1000); // 20 hours private static final int MUSIC_ACTIVE_POLL_PERIOD_MS = 60000; // 1 minute polling interval private static final int SAFE_VOLUME_CONFIGURE_TIMEOUT_MS = 30000; // 30s after boot completed // check playback or record activity every 3 seconds for UIDs owning mode IN_COMMUNICATION private static final int CHECK_MODE_FOR_UID_PERIOD_MS = 3000; private int safeMediaVolumeIndex(int device) { if (!mSafeMediaVolumeDevices.contains(device)) { Loading services/core/java/com/android/server/audio/AudioServiceEvents.java +59 −5 Original line number Diff line number Diff line Loading @@ -27,28 +27,82 @@ import com.android.server.audio.AudioDeviceInventory.WiredDeviceConnectionState; public class AudioServiceEvents { final static class PhoneStateEvent extends AudioEventLogger.Event { static final int MODE_SET = 0; static final int MODE_IN_COMMUNICATION_TIMEOUT = 1; final int mOp; final String mPackage; final int mOwnerPid; final int mRequesterPid; final int mRequestedMode; final int mActualMode; /** used for MODE_SET */ PhoneStateEvent(String callingPackage, int requesterPid, int requestedMode, int ownerPid, int actualMode) { mOp = MODE_SET; mPackage = callingPackage; mRequesterPid = requesterPid; mRequestedMode = requestedMode; mOwnerPid = ownerPid; mActualMode = actualMode; logMetricEvent(); } /** used for MODE_IN_COMMUNICATION_TIMEOUT */ PhoneStateEvent(String callingPackage, int ownerPid) { mOp = MODE_IN_COMMUNICATION_TIMEOUT; mPackage = callingPackage; mOwnerPid = ownerPid; mRequesterPid = 0; mRequestedMode = 0; mActualMode = 0; logMetricEvent(); } @Override public String eventToString() { return new StringBuilder("setMode(").append(AudioSystem.modeToString(mRequestedMode)) switch (mOp) { case MODE_SET: return new StringBuilder("setMode(") .append(AudioSystem.modeToString(mRequestedMode)) .append(") from package=").append(mPackage) .append(" pid=").append(mRequesterPid) .append(" selected mode=").append(AudioSystem.modeToString(mActualMode)) .append(" selected mode=") .append(AudioSystem.modeToString(mActualMode)) .append(" by pid=").append(mOwnerPid).toString(); case MODE_IN_COMMUNICATION_TIMEOUT: return new StringBuilder("mode IN COMMUNICATION timeout") .append(" for package=").append(mPackage) .append(" pid=").append(mOwnerPid).toString(); default: return new StringBuilder("FIXME invalid op:").append(mOp).toString(); } } /** * Audio Analytics unique Id. */ private static final String mMetricsId = MediaMetrics.Name.AUDIO_MODE; private void logMetricEvent() { switch (mOp) { case MODE_SET: new MediaMetrics.Item(mMetricsId) .set(MediaMetrics.Property.EVENT, "set") .set(MediaMetrics.Property.REQUESTED_MODE, AudioSystem.modeToString(mRequestedMode)) .set(MediaMetrics.Property.MODE, AudioSystem.modeToString(mActualMode)) .set(MediaMetrics.Property.CALLING_PACKAGE, mPackage) .record(); return; case MODE_IN_COMMUNICATION_TIMEOUT: new MediaMetrics.Item(mMetricsId) .set(MediaMetrics.Property.EVENT, "inCommunicationTimeout") .set(MediaMetrics.Property.CALLING_PACKAGE, mPackage) .record(); return; default: return; } } } Loading services/core/java/com/android/server/audio/PlaybackActivityMonitor.java +17 −0 Original line number Diff line number Diff line Loading @@ -366,6 +366,23 @@ public final class PlaybackActivityMonitor releasePlayer(piid, 0); } /** * Returns true if a player belonging to the app with given uid is active. * * @param uid the app uid * @return true if a player is active, false otherwise */ public boolean isPlaybackActiveForUid(int uid) { synchronized (mPlayerLock) { for (AudioPlaybackConfiguration apc : mPlayers.values()) { if (apc.isActive() && apc.getClientUid() == uid) { return true; } } } return false; } protected void dump(PrintWriter pw) { // players pw.println("\nPlaybackActivityMonitor dump time: " Loading services/core/java/com/android/server/audio/RecordingActivityMonitor.java +19 −0 Original line number Diff line number Diff line Loading @@ -215,6 +215,25 @@ public final class RecordingActivityMonitor implements AudioSystem.AudioRecordin dispatchCallbacks(updateSnapshot(AudioManager.RECORD_CONFIG_EVENT_RELEASE, riid, null)); } /** * Returns true if a recorder belonging to the app with given uid is active. * * @param uid the app uid * @return true if a recorder is active, false otherwise */ public boolean isRecordingActiveForUid(int uid) { synchronized (mRecordStates) { for (RecordingState state : mRecordStates) { // Note: isActiveConfiguration() == true => state.getConfig() != null if (state.isActiveConfiguration() && state.getConfig().getClientUid() == uid) { return true; } } } return false; } private void dispatchCallbacks(List<AudioRecordingConfiguration> configs) { if (configs == null) { // null means "no changes" return; Loading Loading
media/java/android/media/MediaMetrics.java +5 −0 Original line number Diff line number Diff line Loading @@ -53,6 +53,7 @@ public class MediaMetrics { public static final String AUDIO_SERVICE = AUDIO + SEPARATOR + "service"; public static final String AUDIO_VOLUME = AUDIO + SEPARATOR + "volume"; public static final String AUDIO_VOLUME_EVENT = AUDIO_VOLUME + SEPARATOR + "event"; public static final String AUDIO_MODE = AUDIO + SEPARATOR + "mode"; } /** Loading Loading @@ -140,6 +141,10 @@ public class MediaMetrics { public static final Key<String> REQUEST = createKey("request", String.class); // For audio mode public static final Key<String> REQUESTED_MODE = createKey("requestedMode", String.class); // audio_mode // For Bluetooth public static final Key<String> SCO_AUDIO_MODE = createKey("scoAudioMode", String.class); Loading
services/core/java/com/android/server/audio/AudioService.java +54 −2 Original line number Diff line number Diff line Loading @@ -283,6 +283,7 @@ public class AudioService extends IAudioService.Stub private static final int MSG_HDMI_VOLUME_CHECK = 28; private static final int MSG_PLAYBACK_CONFIG_CHANGE = 29; private static final int MSG_BROADCAST_MICROPHONE_MUTE = 30; private static final int MSG_CHECK_MODE_FOR_UID = 31; // start of messages handled under wakelock // these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(), // and not with sendMsg(..., ..., SENDMSG_QUEUE, ...) Loading Loading @@ -3679,12 +3680,14 @@ public class AudioService extends IAudioService.Stub private final IBinder mCb; // To be notified of client's death private final int mPid; private final int mUid; private String mPackage; private int mMode = AudioSystem.MODE_NORMAL; // Current mode set by this client SetModeDeathHandler(IBinder cb, int pid, int uid) { SetModeDeathHandler(IBinder cb, int pid, int uid, String caller) { mCb = cb; mPid = pid; mUid = uid; mPackage = caller; } public void binderDied() { Loading Loading @@ -3722,6 +3725,10 @@ public class AudioService extends IAudioService.Stub public int getUid() { return mUid; } public String getPackage() { return mPackage; } } /** @see AudioManager#setMode(int) */ Loading Loading @@ -3803,6 +3810,9 @@ public class AudioService extends IAudioService.Stub hdlr = h; // Remove from client list so that it is re-inserted at top of list iter.remove(); if (hdlr.getMode() == AudioSystem.MODE_IN_COMMUNICATION) { mAudioHandler.removeEqualMessages(MSG_CHECK_MODE_FOR_UID, hdlr); } try { hdlr.getBinder().unlinkToDeath(hdlr, 0); if (cb != hdlr.getBinder()) { Loading Loading @@ -3833,7 +3843,7 @@ public class AudioService extends IAudioService.Stub } } else { if (hdlr == null) { hdlr = new SetModeDeathHandler(cb, pid, uid); hdlr = new SetModeDeathHandler(cb, pid, uid, caller); } // Register for client death notification try { Loading Loading @@ -3880,6 +3890,7 @@ public class AudioService extends IAudioService.Stub // Note: newModeOwnerPid is always 0 when actualMode is MODE_NORMAL mModeLogger.log( new PhoneStateEvent(caller, pid, mode, newModeOwnerPid, actualMode)); int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE); int device = getDeviceForStream(streamType); int index = mStreamStates[mStreamVolumeAlias[streamType]].getIndex(device); Loading @@ -3890,6 +3901,16 @@ public class AudioService extends IAudioService.Stub // change of mode may require volume to be re-applied on some devices updateAbsVolumeMultiModeDevices(oldMode, actualMode); if (actualMode == AudioSystem.MODE_IN_COMMUNICATION) { sendMsg(mAudioHandler, MSG_CHECK_MODE_FOR_UID, SENDMSG_QUEUE, 0, 0, hdlr, CHECK_MODE_FOR_UID_PERIOD_MS); } } return newModeOwnerPid; } Loading Loading @@ -6374,6 +6395,35 @@ public class AudioService extends IAudioService.Stub case MSG_BROADCAST_MICROPHONE_MUTE: mSystemServer.sendMicrophoneMuteChangedIntent(); break; case MSG_CHECK_MODE_FOR_UID: synchronized (mDeviceBroker.mSetModeLock) { if (msg.obj == null) { break; } // If the app corresponding to this mode death handler object is not // capturing or playing audio anymore after 3 seconds, remove it // from the stack. Otherwise, check again in 3 seconds. SetModeDeathHandler h = (SetModeDeathHandler) msg.obj; if (mSetModeDeathHandlers.indexOf(h) < 0) { break; } if (mRecordMonitor.isRecordingActiveForUid(h.getUid()) || mPlaybackMonitor.isPlaybackActiveForUid(h.getUid())) { sendMsg(mAudioHandler, MSG_CHECK_MODE_FOR_UID, SENDMSG_QUEUE, 0, 0, h, CHECK_MODE_FOR_UID_PERIOD_MS); break; } // For now just log the fact that an app is hogging the audio mode. // TODO(b/160260850): remove abusive app from audio mode stack. mModeLogger.log(new PhoneStateEvent(h.getPackage(), h.getPid())); } break; } } } Loading Loading @@ -7017,6 +7067,8 @@ public class AudioService extends IAudioService.Stub private static final int UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX = (20 * 3600 * 1000); // 20 hours private static final int MUSIC_ACTIVE_POLL_PERIOD_MS = 60000; // 1 minute polling interval private static final int SAFE_VOLUME_CONFIGURE_TIMEOUT_MS = 30000; // 30s after boot completed // check playback or record activity every 3 seconds for UIDs owning mode IN_COMMUNICATION private static final int CHECK_MODE_FOR_UID_PERIOD_MS = 3000; private int safeMediaVolumeIndex(int device) { if (!mSafeMediaVolumeDevices.contains(device)) { Loading
services/core/java/com/android/server/audio/AudioServiceEvents.java +59 −5 Original line number Diff line number Diff line Loading @@ -27,28 +27,82 @@ import com.android.server.audio.AudioDeviceInventory.WiredDeviceConnectionState; public class AudioServiceEvents { final static class PhoneStateEvent extends AudioEventLogger.Event { static final int MODE_SET = 0; static final int MODE_IN_COMMUNICATION_TIMEOUT = 1; final int mOp; final String mPackage; final int mOwnerPid; final int mRequesterPid; final int mRequestedMode; final int mActualMode; /** used for MODE_SET */ PhoneStateEvent(String callingPackage, int requesterPid, int requestedMode, int ownerPid, int actualMode) { mOp = MODE_SET; mPackage = callingPackage; mRequesterPid = requesterPid; mRequestedMode = requestedMode; mOwnerPid = ownerPid; mActualMode = actualMode; logMetricEvent(); } /** used for MODE_IN_COMMUNICATION_TIMEOUT */ PhoneStateEvent(String callingPackage, int ownerPid) { mOp = MODE_IN_COMMUNICATION_TIMEOUT; mPackage = callingPackage; mOwnerPid = ownerPid; mRequesterPid = 0; mRequestedMode = 0; mActualMode = 0; logMetricEvent(); } @Override public String eventToString() { return new StringBuilder("setMode(").append(AudioSystem.modeToString(mRequestedMode)) switch (mOp) { case MODE_SET: return new StringBuilder("setMode(") .append(AudioSystem.modeToString(mRequestedMode)) .append(") from package=").append(mPackage) .append(" pid=").append(mRequesterPid) .append(" selected mode=").append(AudioSystem.modeToString(mActualMode)) .append(" selected mode=") .append(AudioSystem.modeToString(mActualMode)) .append(" by pid=").append(mOwnerPid).toString(); case MODE_IN_COMMUNICATION_TIMEOUT: return new StringBuilder("mode IN COMMUNICATION timeout") .append(" for package=").append(mPackage) .append(" pid=").append(mOwnerPid).toString(); default: return new StringBuilder("FIXME invalid op:").append(mOp).toString(); } } /** * Audio Analytics unique Id. */ private static final String mMetricsId = MediaMetrics.Name.AUDIO_MODE; private void logMetricEvent() { switch (mOp) { case MODE_SET: new MediaMetrics.Item(mMetricsId) .set(MediaMetrics.Property.EVENT, "set") .set(MediaMetrics.Property.REQUESTED_MODE, AudioSystem.modeToString(mRequestedMode)) .set(MediaMetrics.Property.MODE, AudioSystem.modeToString(mActualMode)) .set(MediaMetrics.Property.CALLING_PACKAGE, mPackage) .record(); return; case MODE_IN_COMMUNICATION_TIMEOUT: new MediaMetrics.Item(mMetricsId) .set(MediaMetrics.Property.EVENT, "inCommunicationTimeout") .set(MediaMetrics.Property.CALLING_PACKAGE, mPackage) .record(); return; default: return; } } } Loading
services/core/java/com/android/server/audio/PlaybackActivityMonitor.java +17 −0 Original line number Diff line number Diff line Loading @@ -366,6 +366,23 @@ public final class PlaybackActivityMonitor releasePlayer(piid, 0); } /** * Returns true if a player belonging to the app with given uid is active. * * @param uid the app uid * @return true if a player is active, false otherwise */ public boolean isPlaybackActiveForUid(int uid) { synchronized (mPlayerLock) { for (AudioPlaybackConfiguration apc : mPlayers.values()) { if (apc.isActive() && apc.getClientUid() == uid) { return true; } } } return false; } protected void dump(PrintWriter pw) { // players pw.println("\nPlaybackActivityMonitor dump time: " Loading
services/core/java/com/android/server/audio/RecordingActivityMonitor.java +19 −0 Original line number Diff line number Diff line Loading @@ -215,6 +215,25 @@ public final class RecordingActivityMonitor implements AudioSystem.AudioRecordin dispatchCallbacks(updateSnapshot(AudioManager.RECORD_CONFIG_EVENT_RELEASE, riid, null)); } /** * Returns true if a recorder belonging to the app with given uid is active. * * @param uid the app uid * @return true if a recorder is active, false otherwise */ public boolean isRecordingActiveForUid(int uid) { synchronized (mRecordStates) { for (RecordingState state : mRecordStates) { // Note: isActiveConfiguration() == true => state.getConfig() != null if (state.isActiveConfiguration() && state.getConfig().getClientUid() == uid) { return true; } } } return false; } private void dispatchCallbacks(List<AudioRecordingConfiguration> configs) { if (configs == null) { // null means "no changes" return; Loading