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
This commit is contained in:
Jean-Michel Trivi
2017-06-26 19:32:38 -07:00
parent 214baa034a
commit 92ed7bf412
3 changed files with 79 additions and 2 deletions

View File

@@ -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);
}

View File

@@ -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 */);
}
}
//==========================================================================================

View File

@@ -86,6 +86,43 @@ public final class PlaybackActivityMonitor
AudioPlaybackConfiguration.sPlayerDeathMonitor = this;
}
//=================================================================
private final ArrayList<Integer> mBannedUids = new ArrayList<Integer>();
// 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();
}
}