From 92ed7bf41235c95b2c71648a631ce7aaa65f8943 Mon Sep 17 00:00:00 2001 From: Jean-Michel Trivi Date: Mon, 26 Jun 2017 19:32:38 -0700 Subject: [PATCH] AudioService: internal API for disabling audio playback for a UID Internal API, available through AudioManagerInternal, to flag a UID as having lost the ability to play audio. When that happens, all players associated with this UID are paused, and future players are immediately paused when they report they started. Users of this API must "enable" a previously banned UID when it is no longer in use. Test: upcoming manual test with background apps Bug: 34471029 Change-Id: Ic1c103aabe8f3897072b3ce938d84cb949540e23 --- .../android/media/AudioManagerInternal.java | 11 ++++ .../android/server/audio/AudioService.java | 14 +++++ .../server/audio/PlaybackActivityMonitor.java | 56 ++++++++++++++++++- 3 files changed, 79 insertions(+), 2 deletions(-) diff --git a/media/java/android/media/AudioManagerInternal.java b/media/java/android/media/AudioManagerInternal.java index 0a1de33b845b8..2b5ac5e6ec203 100644 --- a/media/java/android/media/AudioManagerInternal.java +++ b/media/java/android/media/AudioManagerInternal.java @@ -59,4 +59,15 @@ public abstract class AudioManagerInternal { int getRingerModeAffectedStreams(int streams); } + + /** + * Disable or restore the ability to play audio for a given UID. + * When a UID isn't meant to be tracked anymore (e.g. client died), re-enable audio for this UID + * to prevent disabling audio for future UIDs that would reuse the same value. + * This operation is asynchronous. + * @param disable when true, prevents playback of audio streams from the given uid. If false, + * restores the ability to play, or no-op if playback hadn't been disabled before. + * @param uid the client UID whose ability to play will be affected. + */ + public abstract void disableAudioForUid(boolean disable, int uid); } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index e5ab784df882c..2199bba9bbacb 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -239,6 +239,7 @@ public class AudioService extends IAudioService.Stub private static final int MSG_SET_A2DP_SRC_CONNECTION_STATE = 101; private static final int MSG_SET_A2DP_SINK_CONNECTION_STATE = 102; private static final int MSG_A2DP_DEVICE_CONFIG_CHANGE = 103; + private static final int MSG_DISABLE_AUDIO_FOR_UID = 104; // end of messages handled under wakelock private static final int BTA2DP_DOCK_TIMEOUT_MILLIS = 8000; @@ -4871,6 +4872,12 @@ public class AudioService extends IAudioService.Stub mAudioEventWakeLock.release(); break; + case MSG_DISABLE_AUDIO_FOR_UID: + mPlaybackMonitor.disableAudioForUid( msg.arg1 == 1 /* disable */, + msg.arg2 /* uid */); + mAudioEventWakeLock.release(); + break; + case MSG_REPORT_NEW_ROUTES: { int N = mRoutesObservers.beginBroadcast(); if (N > 0) { @@ -6591,6 +6598,13 @@ public class AudioService extends IAudioService.Stub } } } + + @Override + public void disableAudioForUid(boolean disable, int uid) { + queueMsgUnderWakeLock(mAudioHandler, MSG_DISABLE_AUDIO_FOR_UID, + disable ? 1 : 0 /* arg1 */, uid /* arg2 */, + null /* obj */, 0 /* delay */); + } } //========================================================================================== diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java index 702cbbed17dea..cdd52f5fffde8 100644 --- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java +++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java @@ -86,6 +86,43 @@ public final class PlaybackActivityMonitor AudioPlaybackConfiguration.sPlayerDeathMonitor = this; } + //================================================================= + private final ArrayList mBannedUids = new ArrayList(); + + // see AudioManagerInternal.disableAudioForUid(boolean disable, int uid) + public void disableAudioForUid(boolean disable, int uid) { + synchronized(mPlayerLock) { + final int index = mBannedUids.indexOf(new Integer(uid)); + if (index >= 0) { + if (!disable) { + mBannedUids.remove(index); + // nothing else to do, future playback requests from this uid are ok + } // no else to handle, uid already present, so disabling again is no-op + } else { + if (disable) { + for (AudioPlaybackConfiguration apc : mPlayers.values()) { + checkBanPlayer(apc, uid); + } + mBannedUids.add(new Integer(uid)); + } // no else to handle, uid already not in list, so enabling again is no-op + } + } + } + + private boolean checkBanPlayer(@NonNull AudioPlaybackConfiguration apc, int uid) { + final boolean toBan = (apc.getClientUid() == uid); + if (toBan) { + final int piid = apc.getPlayerInterfaceId(); + try { + Log.v(TAG, "banning player " + piid + " uid:" + uid); + apc.getPlayerProxy().pause(); + } catch (Exception e) { + Log.e(TAG, "error banning player " + piid + " uid:" + uid, e); + } + } + return toBan; + } + //================================================================= // Track players and their states // methods playerAttributes, playerEvent, releasePlayer are all oneway calls @@ -129,6 +166,14 @@ public final class PlaybackActivityMonitor if (apc == null) { return; } + if (event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) { + for (Integer uidInteger: mBannedUids) { + if (checkBanPlayer(apc, uidInteger.intValue())) { + // player was banned, do not update its state + return; + } + } + } if (apc.getPlayerType() == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) { // FIXME SoundPool not ready for state reporting return; @@ -178,10 +223,17 @@ public final class PlaybackActivityMonitor pw.println("\n ducked players:"); mDuckingManager.dump(pw); // players muted due to the device ringing or being in a call - pw.println("\n muted player piids:"); + pw.print("\n muted player piids:"); for (int piid : mMutedPlayers) { - pw.println(" " + piid); + pw.print(" " + piid); } + pw.println(); + // banned players: + pw.print("\n banned uids:"); + for (int uid : mBannedUids) { + pw.print(" " + uid); + } + pw.println(); } }