Merge "AudioService: internal API for disabling audio playback for a UID" into oc-dr1-dev am: 8f81919093
am: 051285b648

Change-Id: Ib78281b6bc1b4ae37e0aba5e6a0b20cd81a29db2
diff --git a/media/java/android/media/AudioManagerInternal.java b/media/java/android/media/AudioManagerInternal.java
index 0a1de33..2b5ac5e 100644
--- a/media/java/android/media/AudioManagerInternal.java
+++ b/media/java/android/media/AudioManagerInternal.java
@@ -59,4 +59,15 @@
 
         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 c6307a7..7eee3e4 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 @@
     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;
@@ -4873,6 +4874,12 @@
                     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) {
@@ -6595,6 +6602,13 @@
                 }
             }
         }
+
+        @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 f9e4d94..663559f 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -95,6 +95,43 @@
     }
 
     //=================================================================
+    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
     //  into AudioService. They trigger synchronous dispatchPlaybackChange() which updates
@@ -137,6 +174,14 @@
             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;
@@ -186,10 +231,17 @@
             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();
         }
     }