blob: aea4cb563d1b3ebe03911d9b7c9e12e2f2dfed3e [file] [log] [blame]
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -08001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.audio;
18
19import android.annotation.NonNull;
20import android.media.AudioAttributes;
21import android.media.AudioFormat;
22import android.media.AudioManager;
23import android.media.AudioPlaybackConfiguration;
24import android.media.AudioSystem;
25import android.media.IPlaybackConfigDispatcher;
26import android.media.MediaRecorder;
27import android.media.PlayerBase;
Jean-Michel Trividce82ab2017-02-07 16:02:33 -080028import android.media.VolumeShaper;
Jean-Michel Trivi44a8f532017-01-02 14:36:43 -080029import android.os.Binder;
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -080030import android.os.IBinder;
31import android.os.RemoteException;
32import android.util.Log;
33
34import java.io.PrintWriter;
35import java.text.DateFormat;
36import java.util.ArrayList;
37import java.util.Date;
38import java.util.HashMap;
39import java.util.Iterator;
40import java.util.List;
Jean-Michel Trivi99489cc2017-01-25 19:08:49 -080041import java.util.Set;
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -080042
43/**
44 * Class to receive and dispatch updates from AudioSystem about recording configurations.
45 */
Jean-Michel Trivi9dc22c22017-01-05 18:06:03 -080046public final class PlaybackActivityMonitor
Jean-Michel Trivi99489cc2017-01-25 19:08:49 -080047 implements AudioPlaybackConfiguration.PlayerDeathMonitor, PlayerFocusEnforcer {
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -080048
Jean-Michel Trivic2769ab2017-04-24 11:59:29 -070049 public static final String TAG = "AudioService.PlaybackActivityMonitor";
Jean-Michel Trivi0f49f822017-02-16 14:36:43 -080050
Jean-Michel Trivic2769ab2017-04-24 11:59:29 -070051 private static final boolean DEBUG = false;
52 private static final int VOLUME_SHAPER_SYSTEM_DUCK_ID = 1;
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -080053
Jean-Michel Trivic2769ab2017-04-24 11:59:29 -070054 private static final VolumeShaper.Configuration DUCK_VSHAPE =
Andy Hung7da0e982017-02-22 12:34:21 -080055 new VolumeShaper.Configuration.Builder()
56 .setId(VOLUME_SHAPER_SYSTEM_DUCK_ID)
57 .setCurve(new float[] { 0.f, 1.f } /* times */,
58 new float[] { 1.f, 0.2f } /* volumes */)
59 .setOptionFlags(VolumeShaper.Configuration.OPTION_FLAG_CLOCK_TIME)
Jean-Michel Trivi0dfbd152017-04-11 18:52:43 -070060 .setDurationMillis(MediaFocusControl.getFocusRampTimeMs(
Andy Hung7da0e982017-02-22 12:34:21 -080061 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK,
62 new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_NOTIFICATION)
63 .build()))
64 .build();
Jean-Michel Trivic2769ab2017-04-24 11:59:29 -070065 private static final VolumeShaper.Configuration DUCK_ID =
Andy Hung7da0e982017-02-22 12:34:21 -080066 new VolumeShaper.Configuration(VOLUME_SHAPER_SYSTEM_DUCK_ID);
Jean-Michel Trivic2769ab2017-04-24 11:59:29 -070067 private static final VolumeShaper.Operation PLAY_CREATE_IF_NEEDED =
Andy Hung7da0e982017-02-22 12:34:21 -080068 new VolumeShaper.Operation.Builder(VolumeShaper.Operation.PLAY)
69 .createIfNeeded()
70 .build();
Andy Hung7da0e982017-02-22 12:34:21 -080071
72 private final ArrayList<PlayMonitorClient> mClients = new ArrayList<PlayMonitorClient>();
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -080073 // a public client is one that needs an anonymized version of the playback configurations, we
74 // keep track of whether there is at least one to know when we need to create the list of
75 // playback configurations that do not contain uid/pid/package name information.
76 private boolean mHasPublicClients = false;
77
78 private final Object mPlayerLock = new Object();
Andy Hung7da0e982017-02-22 12:34:21 -080079 private final HashMap<Integer, AudioPlaybackConfiguration> mPlayers =
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -080080 new HashMap<Integer, AudioPlaybackConfiguration>();
81
82 PlaybackActivityMonitor() {
Jean-Michel Trivi9dc22c22017-01-05 18:06:03 -080083 PlayMonitorClient.sListenerDeathMonitor = this;
84 AudioPlaybackConfiguration.sPlayerDeathMonitor = this;
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -080085 }
86
87 //=================================================================
88 // Track players and their states
Jean-Michel Trivi44a8f532017-01-02 14:36:43 -080089 // methods playerAttributes, playerEvent, releasePlayer are all oneway calls
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -080090 // into AudioService. They trigger synchronous dispatchPlaybackChange() which updates
91 // all listeners as oneway calls.
92
Jean-Michel Trivi44a8f532017-01-02 14:36:43 -080093 public int trackPlayer(PlayerBase.PlayerIdCard pic) {
94 final int newPiid = AudioSystem.newAudioPlayerId();
95 if (DEBUG) { Log.v(TAG, "trackPlayer() new piid=" + newPiid); }
96 final AudioPlaybackConfiguration apc =
97 new AudioPlaybackConfiguration(pic, newPiid,
98 Binder.getCallingUid(), Binder.getCallingPid());
Jean-Michel Trivi9dc22c22017-01-05 18:06:03 -080099 apc.init();
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800100 synchronized(mPlayerLock) {
Jean-Michel Trivi44a8f532017-01-02 14:36:43 -0800101 mPlayers.put(newPiid, apc);
Jean-Michel Trivicafed632017-04-03 18:49:45 -0700102 if (mDuckedUids.contains(new Integer(apc.getClientUid()))) {
103 if (DEBUG) { Log.v(TAG, " > trackPlayer() piid=" + newPiid + " must be ducked"); }
104 mDuckedPlayers.add(new Integer(newPiid));
105 // FIXME here the player needs to be put in a state that is the same as if it
106 // had been ducked as it starts. At the moment, this works already for linked
107 // players, as is the case in gapless playback.
108 }
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800109 }
Jean-Michel Trivi44a8f532017-01-02 14:36:43 -0800110 return newPiid;
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800111 }
112
Jean-Michel Trivi46e310b2017-01-04 15:58:02 -0800113 public void playerAttributes(int piid, @NonNull AudioAttributes attr, int binderUid) {
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800114 final boolean change;
115 synchronized(mPlayerLock) {
116 final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid));
Jean-Michel Trivi46e310b2017-01-04 15:58:02 -0800117 if (checkConfigurationCaller(piid, apc, binderUid)) {
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800118 change = apc.handleAudioAttributesEvent(attr);
Jean-Michel Trivi46e310b2017-01-04 15:58:02 -0800119 } else {
120 Log.e(TAG, "Error updating audio attributes");
121 change = false;
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800122 }
123 }
124 if (change) {
125 dispatchPlaybackChange();
126 }
127 }
128
Jean-Michel Trivi46e310b2017-01-04 15:58:02 -0800129 public void playerEvent(int piid, int event, int binderUid) {
Jean-Michel Trivi44a8f532017-01-02 14:36:43 -0800130 if (DEBUG) { Log.v(TAG, String.format("playerEvent(piid=%d, event=%d)", piid, event)); }
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800131 final boolean change;
132 synchronized(mPlayerLock) {
133 final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid));
Jean-Michel Trivif1d82762017-02-14 14:24:37 -0800134 // FIXME SoundPool not ready for state reporting
Jean-Michel Trivibd39cfa2017-02-17 17:23:36 -0800135 if (apc != null
136 && apc.getPlayerType() == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL)
137 {
Jean-Michel Trivif1d82762017-02-14 14:24:37 -0800138 return;
139 }
Jean-Michel Trivi46e310b2017-01-04 15:58:02 -0800140 if (checkConfigurationCaller(piid, apc, binderUid)) {
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800141 //TODO add generation counter to only update to the latest state
142 change = apc.handleStateEvent(event);
Jean-Michel Trivi46e310b2017-01-04 15:58:02 -0800143 } else {
144 Log.e(TAG, "Error handling event " + event);
145 change = false;
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800146 }
Jean-Michel Trivicafed632017-04-03 18:49:45 -0700147 if (change && event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED
148 && mDuckedUids.contains(new Integer(apc.getClientUid()))) {
149 if (DEBUG) { Log.v(TAG, " > playerEvent() piid=" + piid + " must be ducked"); }
150 if (!mDuckedPlayers.contains(new Integer(piid))) {
151 mDuckedPlayers.add(new Integer(piid));
152 }
153 }
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800154 }
155 if (change) {
156 dispatchPlaybackChange();
157 }
158 }
159
Jean-Michel Trivi46e310b2017-01-04 15:58:02 -0800160 public void releasePlayer(int piid, int binderUid) {
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800161 if (DEBUG) { Log.v(TAG, "releasePlayer() for piid=" + piid); }
162 synchronized(mPlayerLock) {
Jean-Michel Trivi46e310b2017-01-04 15:58:02 -0800163 final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid));
164 if (checkConfigurationCaller(piid, apc, binderUid)) {
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800165 mPlayers.remove(new Integer(piid));
166 }
167 }
168 }
169
Jean-Michel Trivi9dc22c22017-01-05 18:06:03 -0800170 // Implementation of AudioPlaybackConfiguration.PlayerDeathMonitor
171 @Override
172 public void playerDeath(int piid) {
173 releasePlayer(piid, 0);
174 }
175
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800176 protected void dump(PrintWriter pw) {
Jean-Michel Trivi99489cc2017-01-25 19:08:49 -0800177 // players
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800178 pw.println("\nPlaybackActivityMonitor dump time: "
179 + DateFormat.getTimeInstance().format(new Date()));
180 synchronized(mPlayerLock) {
181 for (AudioPlaybackConfiguration conf : mPlayers.values()) {
182 conf.dump(pw);
183 }
Jean-Michel Trivi99489cc2017-01-25 19:08:49 -0800184 // ducked players
185 pw.println("\n ducked player piids:");
186 for (int piid : mDuckedPlayers) {
187 pw.println(" " + piid);
188 }
Jean-Michel Trivi62b86342017-02-04 15:33:47 -0800189 // players muted due to the device ringing or being in a call
190 pw.println("\n muted player piids:");
191 for (int piid : mMutedPlayers) {
192 pw.println(" " + piid);
193 }
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800194 }
195 }
196
Jean-Michel Trivi46e310b2017-01-04 15:58:02 -0800197 /**
Jean-Michel Trivic2769ab2017-04-24 11:59:29 -0700198 * Check that piid and uid are valid for the given valid configuration.
Jean-Michel Trivi46e310b2017-01-04 15:58:02 -0800199 * @param piid the piid of the player.
200 * @param apc the configuration found for this piid.
201 * @param binderUid actual uid of client trying to signal a player state/event/attributes.
Jean-Michel Trivic2769ab2017-04-24 11:59:29 -0700202 * @return true if the call is valid and the change should proceed, false otherwise. Always
203 * returns false when apc is null.
Jean-Michel Trivi46e310b2017-01-04 15:58:02 -0800204 */
205 private static boolean checkConfigurationCaller(int piid,
206 final AudioPlaybackConfiguration apc, int binderUid) {
207 if (apc == null) {
Jean-Michel Trivi46e310b2017-01-04 15:58:02 -0800208 return false;
209 } else if ((binderUid != 0) && (apc.getClientUid() != binderUid)) {
210 Log.e(TAG, "Forbidden operation from uid " + binderUid + " for player " + piid);
211 return false;
212 }
213 return true;
214 }
215
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800216 private void dispatchPlaybackChange() {
217 synchronized (mClients) {
218 // typical use case, nobody is listening, don't do any work
219 if (mClients.isEmpty()) {
220 return;
221 }
222 }
223 if (DEBUG) { Log.v(TAG, "dispatchPlaybackChange to " + mClients.size() + " clients"); }
224 final List<AudioPlaybackConfiguration> configsSystem;
225 // list of playback configurations for "public consumption". It is only computed if there
226 // are non-system playback activity listeners.
227 final List<AudioPlaybackConfiguration> configsPublic;
228 synchronized (mPlayerLock) {
229 if (mPlayers.isEmpty()) {
230 return;
231 }
232 configsSystem = new ArrayList<AudioPlaybackConfiguration>(mPlayers.values());
233 }
234 synchronized (mClients) {
235 // was done at beginning of method, but could have changed
236 if (mClients.isEmpty()) {
237 return;
238 }
239 configsPublic = mHasPublicClients ? anonymizeForPublicConsumption(configsSystem) : null;
240 final Iterator<PlayMonitorClient> clientIterator = mClients.iterator();
241 while (clientIterator.hasNext()) {
242 final PlayMonitorClient pmc = clientIterator.next();
243 try {
244 // do not spam the logs if there are problems communicating with this client
245 if (pmc.mErrorCount < PlayMonitorClient.MAX_ERRORS) {
246 if (pmc.mIsPrivileged) {
247 pmc.mDispatcherCb.dispatchPlaybackConfigChange(configsSystem);
248 } else {
249 pmc.mDispatcherCb.dispatchPlaybackConfigChange(configsPublic);
250 }
251 }
252 } catch (RemoteException e) {
253 pmc.mErrorCount++;
254 Log.e(TAG, "Error (" + pmc.mErrorCount +
255 ") trying to dispatch playback config change to " + pmc, e);
256 }
257 }
258 }
259 }
260
261 private ArrayList<AudioPlaybackConfiguration> anonymizeForPublicConsumption(
262 List<AudioPlaybackConfiguration> sysConfigs) {
263 ArrayList<AudioPlaybackConfiguration> publicConfigs =
264 new ArrayList<AudioPlaybackConfiguration>();
Jean-Michel Trivi99489cc2017-01-25 19:08:49 -0800265 // only add active anonymized configurations,
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800266 for (AudioPlaybackConfiguration config : sysConfigs) {
267 if (config.isActive()) {
268 publicConfigs.add(AudioPlaybackConfiguration.anonymizedCopy(config));
269 }
270 }
271 return publicConfigs;
272 }
273
Jean-Michel Trivi99489cc2017-01-25 19:08:49 -0800274
275 //=================================================================
276 // PlayerFocusEnforcer implementation
277 private final ArrayList<Integer> mDuckedPlayers = new ArrayList<Integer>();
Jean-Michel Trivi62b86342017-02-04 15:33:47 -0800278 private final ArrayList<Integer> mMutedPlayers = new ArrayList<Integer>();
Jean-Michel Trivicafed632017-04-03 18:49:45 -0700279 // size of 2 for typical cases of double-ducking, not expected to grow beyond that, but can
280 private final ArrayList<Integer> mDuckedUids = new ArrayList<Integer>(2);
Jean-Michel Trivi99489cc2017-01-25 19:08:49 -0800281
282 @Override
283 public boolean duckPlayers(FocusRequester winner, FocusRequester loser) {
284 if (DEBUG) {
285 Log.v(TAG, String.format("duckPlayers: uids winner=%d loser=%d",
Jean-Michel Trivicafed632017-04-03 18:49:45 -0700286 winner.getClientUid(), loser.getClientUid()));
287 }
Jean-Michel Trivi99489cc2017-01-25 19:08:49 -0800288 synchronized (mPlayerLock) {
Jean-Michel Trivicafed632017-04-03 18:49:45 -0700289 final Integer loserUid = new Integer(loser.getClientUid());
290 if (!mDuckedUids.contains(loserUid)) {
291 mDuckedUids.add(loserUid);
292 }
Jean-Michel Trivi99489cc2017-01-25 19:08:49 -0800293 if (mPlayers.isEmpty()) {
294 return true;
295 }
296 final Set<Integer> piidSet = mPlayers.keySet();
297 final Iterator<Integer> piidIterator = piidSet.iterator();
298 // find which players to duck
299 while (piidIterator.hasNext()) {
300 final Integer piid = piidIterator.next();
301 final AudioPlaybackConfiguration apc = mPlayers.get(piid);
Jean-Michel Trivibd39cfa2017-02-17 17:23:36 -0800302 if (apc == null) {
303 continue;
304 }
Jean-Michel Trivi99489cc2017-01-25 19:08:49 -0800305 if (!winner.hasSameUid(apc.getClientUid())
306 && loser.hasSameUid(apc.getClientUid())
307 && apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED)
308 {
Jean-Michel Trivicafed632017-04-03 18:49:45 -0700309 if (mDuckedPlayers.contains(new Integer(piid))) {
Jean-Michel Trivi99489cc2017-01-25 19:08:49 -0800310 if (DEBUG) { Log.v(TAG, "player " + piid + " already ducked"); }
311 } else if (apc.getAudioAttributes().getContentType() ==
312 AudioAttributes.CONTENT_TYPE_SPEECH) {
313 // the player is speaking, ducking will make the speech unintelligible
314 // so let the app handle it instead
315 return false;
Jean-Michel Trividce82ab2017-02-07 16:02:33 -0800316 } else if (apc.getPlayerType()
317 == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) {
318 // TODO support ducking of SoundPool players
319 return false;
Jean-Michel Trivi99489cc2017-01-25 19:08:49 -0800320 } else {
321 try {
Jean-Michel Trivi952f2342017-03-06 16:16:56 -0800322 Log.v(TAG, "ducking player " + piid);
Andy Hung7da0e982017-02-22 12:34:21 -0800323 apc.getPlayerProxy().applyVolumeShaper(
324 DUCK_VSHAPE,
325 PLAY_CREATE_IF_NEEDED);
Jean-Michel Trivicafed632017-04-03 18:49:45 -0700326 mDuckedPlayers.add(new Integer(piid));
Jean-Michel Trivi99489cc2017-01-25 19:08:49 -0800327 } catch (Exception e) {
328 Log.e(TAG, "Error ducking player " + piid, e);
329 // something went wrong trying to duck, so let the app handle it
330 // instead, it may know things we don't
331 return false;
332 }
333 }
334 }
335 }
336 }
337 return true;
338 }
339
340 @Override
341 public void unduckPlayers(FocusRequester winner) {
342 if (DEBUG) { Log.v(TAG, "unduckPlayers: uids winner=" + winner.getClientUid()); }
343 synchronized (mPlayerLock) {
344 if (mDuckedPlayers.isEmpty()) {
Jean-Michel Trivicafed632017-04-03 18:49:45 -0700345 mDuckedUids.remove(new Integer(winner.getClientUid()));
Jean-Michel Trivi99489cc2017-01-25 19:08:49 -0800346 return;
347 }
Jean-Michel Trivicafed632017-04-03 18:49:45 -0700348 final ArrayList<Integer> playersToRemove =
349 new ArrayList<Integer>(mDuckedPlayers.size());
Jean-Michel Trivi99489cc2017-01-25 19:08:49 -0800350 for (int piid : mDuckedPlayers) {
351 final AudioPlaybackConfiguration apc = mPlayers.get(piid);
Jean-Michel Trivicafed632017-04-03 18:49:45 -0700352 if (apc != null) {
353 if (winner.hasSameUid(apc.getClientUid())) {
354 try {
355 Log.v(TAG, "unducking player " + piid);
356 apc.getPlayerProxy().applyVolumeShaper(
357 DUCK_ID,
358 VolumeShaper.Operation.REVERSE);
359 } catch (Exception e) {
360 Log.e(TAG, "Error unducking player " + piid, e);
361 } finally {
362 playersToRemove.add(piid);
363 }
Jean-Michel Trivi99489cc2017-01-25 19:08:49 -0800364 }
365 } else {
Jean-Michel Trivicafed632017-04-03 18:49:45 -0700366 // this piid was in the list of ducked players, but wasn't found, discard it
367 Log.v(TAG, "Error unducking player " + piid + ", player not found");
368 playersToRemove.add(piid);
Jean-Michel Trivi99489cc2017-01-25 19:08:49 -0800369 }
370 }
Jean-Michel Trivicafed632017-04-03 18:49:45 -0700371 for (int piid : playersToRemove) {
372 mDuckedPlayers.remove(new Integer(piid));
373 }
374 mDuckedUids.remove(new Integer(winner.getClientUid()));
Jean-Michel Trivi99489cc2017-01-25 19:08:49 -0800375 }
376 }
377
Jean-Michel Trivi62b86342017-02-04 15:33:47 -0800378 @Override
379 public void mutePlayersForCall(int[] usagesToMute) {
380 if (DEBUG) {
381 String log = new String("mutePlayersForCall: usages=");
382 for (int usage : usagesToMute) { log += " " + usage; }
383 Log.v(TAG, log);
384 }
385 synchronized (mPlayerLock) {
386 final Set<Integer> piidSet = mPlayers.keySet();
387 final Iterator<Integer> piidIterator = piidSet.iterator();
388 // find which players to mute
389 while (piidIterator.hasNext()) {
390 final Integer piid = piidIterator.next();
391 final AudioPlaybackConfiguration apc = mPlayers.get(piid);
Jean-Michel Trivibd39cfa2017-02-17 17:23:36 -0800392 if (apc == null) {
393 continue;
394 }
Jean-Michel Trivi62b86342017-02-04 15:33:47 -0800395 final int playerUsage = apc.getAudioAttributes().getUsage();
396 boolean mute = false;
397 for (int usageToMute : usagesToMute) {
398 if (playerUsage == usageToMute) {
399 mute = true;
400 break;
401 }
402 }
403 if (mute) {
404 try {
Jean-Michel Trivi952f2342017-03-06 16:16:56 -0800405 Log.v(TAG, "call: muting player" + piid);
Jean-Michel Trivi62b86342017-02-04 15:33:47 -0800406 apc.getPlayerProxy().setVolume(0.0f);
Jean-Michel Trivicafed632017-04-03 18:49:45 -0700407 mMutedPlayers.add(new Integer(piid));
Jean-Michel Trivi62b86342017-02-04 15:33:47 -0800408 } catch (Exception e) {
Jean-Michel Trivi952f2342017-03-06 16:16:56 -0800409 Log.e(TAG, "call: error muting player " + piid, e);
Jean-Michel Trivi62b86342017-02-04 15:33:47 -0800410 }
411 }
412 }
413 }
414 }
415
416 @Override
417 public void unmutePlayersForCall() {
418 if (DEBUG) {
419 Log.v(TAG, "unmutePlayersForCall()");
420 }
421 synchronized (mPlayerLock) {
422 if (mMutedPlayers.isEmpty()) {
423 return;
424 }
425 for (int piid : mMutedPlayers) {
426 final AudioPlaybackConfiguration apc = mPlayers.get(piid);
427 if (apc != null) {
428 try {
Jean-Michel Trivi952f2342017-03-06 16:16:56 -0800429 Log.v(TAG, "call: unmuting player" + piid);
Jean-Michel Trivi62b86342017-02-04 15:33:47 -0800430 apc.getPlayerProxy().setVolume(1.0f);
Jean-Michel Trivi62b86342017-02-04 15:33:47 -0800431 } catch (Exception e) {
Jean-Michel Trivi952f2342017-03-06 16:16:56 -0800432 Log.e(TAG, "call: error unmuting player " + piid, e);
Jean-Michel Trivi62b86342017-02-04 15:33:47 -0800433 }
434 }
435 }
Jean-Michel Trivi579c5112017-02-10 09:47:30 -0800436 mMutedPlayers.clear();
Jean-Michel Trivi62b86342017-02-04 15:33:47 -0800437 }
438 }
439
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800440 //=================================================================
441 // Track playback activity listeners
442
443 void registerPlaybackCallback(IPlaybackConfigDispatcher pcdb, boolean isPrivileged) {
444 if (pcdb == null) {
445 return;
446 }
447 synchronized(mClients) {
448 final PlayMonitorClient pmc = new PlayMonitorClient(pcdb, isPrivileged);
449 if (pmc.init()) {
450 if (!isPrivileged) {
451 mHasPublicClients = true;
452 }
453 mClients.add(pmc);
454 }
455 }
456 }
457
458 void unregisterPlaybackCallback(IPlaybackConfigDispatcher pcdb) {
459 if (pcdb == null) {
460 return;
461 }
462 synchronized(mClients) {
463 final Iterator<PlayMonitorClient> clientIterator = mClients.iterator();
464 boolean hasPublicClients = false;
465 // iterate over the clients to remove the dispatcher to remove, and reevaluate at
466 // the same time if we still have a public client.
467 while (clientIterator.hasNext()) {
468 PlayMonitorClient pmc = clientIterator.next();
469 if (pcdb.equals(pmc.mDispatcherCb)) {
470 pmc.release();
471 clientIterator.remove();
472 } else {
473 if (!pmc.mIsPrivileged) {
474 hasPublicClients = true;
475 }
476 }
477 }
478 mHasPublicClients = hasPublicClients;
479 }
480 }
481
Jean-Michel Trivi44a8f532017-01-02 14:36:43 -0800482 List<AudioPlaybackConfiguration> getActivePlaybackConfigurations(boolean isPrivileged) {
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800483 synchronized(mPlayers) {
Jean-Michel Trivi44a8f532017-01-02 14:36:43 -0800484 if (isPrivileged) {
485 return new ArrayList<AudioPlaybackConfiguration>(mPlayers.values());
486 } else {
487 final List<AudioPlaybackConfiguration> configsPublic;
488 synchronized (mPlayerLock) {
489 configsPublic = anonymizeForPublicConsumption(
490 new ArrayList<AudioPlaybackConfiguration>(mPlayers.values()));
491 }
492 return configsPublic;
493 }
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800494 }
495 }
496
497
498 /**
499 * Inner class to track clients that want to be notified of playback updates
500 */
Jean-Michel Trivic2769ab2017-04-24 11:59:29 -0700501 private static final class PlayMonitorClient implements IBinder.DeathRecipient {
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800502
503 // can afford to be static because only one PlaybackActivityMonitor ever instantiated
Jean-Michel Trivi9dc22c22017-01-05 18:06:03 -0800504 static PlaybackActivityMonitor sListenerDeathMonitor;
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800505
506 final IPlaybackConfigDispatcher mDispatcherCb;
507 final boolean mIsPrivileged;
508
509 int mErrorCount = 0;
510 // number of errors after which we don't update this client anymore to not spam the logs
511 static final int MAX_ERRORS = 5;
512
513 PlayMonitorClient(IPlaybackConfigDispatcher pcdb, boolean isPrivileged) {
514 mDispatcherCb = pcdb;
515 mIsPrivileged = isPrivileged;
516 }
517
518 public void binderDied() {
519 Log.w(TAG, "client died");
Jean-Michel Trivi9dc22c22017-01-05 18:06:03 -0800520 sListenerDeathMonitor.unregisterPlaybackCallback(mDispatcherCb);
Jean-Michel Trivi292a6a42016-12-01 08:32:15 -0800521 }
522
523 boolean init() {
524 try {
525 mDispatcherCb.asBinder().linkToDeath(this, 0);
526 return true;
527 } catch (RemoteException e) {
528 Log.w(TAG, "Could not link to client death", e);
529 return false;
530 }
531 }
532
533 void release() {
534 mDispatcherCb.asBinder().unlinkToDeath(this, 0);
535 }
536 }
537}