Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 1 | /* |
| 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 | |
| 17 | package com.android.server.audio; |
| 18 | |
| 19 | import android.annotation.NonNull; |
Eric Laurent | e5a351c | 2017-09-27 20:11:51 -0700 | [diff] [blame] | 20 | import android.content.Context; |
| 21 | import android.content.pm.PackageManager; |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 22 | import android.media.AudioAttributes; |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 23 | import android.media.AudioManager; |
| 24 | import android.media.AudioPlaybackConfiguration; |
| 25 | import android.media.AudioSystem; |
| 26 | import android.media.IPlaybackConfigDispatcher; |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 27 | import android.media.PlayerBase; |
Jean-Michel Trivi | dce82ab | 2017-02-07 16:02:33 -0800 | [diff] [blame] | 28 | import android.media.VolumeShaper; |
Jean-Michel Trivi | 44a8f53 | 2017-01-02 14:36:43 -0800 | [diff] [blame] | 29 | import android.os.Binder; |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 30 | import android.os.IBinder; |
| 31 | import android.os.RemoteException; |
| 32 | import android.util.Log; |
| 33 | |
Jean-Michel Trivi | 83271bd | 2017-06-23 18:28:41 -0700 | [diff] [blame] | 34 | import com.android.internal.util.ArrayUtils; |
| 35 | |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 36 | import java.io.PrintWriter; |
| 37 | import java.text.DateFormat; |
| 38 | import java.util.ArrayList; |
Jean-Michel Trivi | 5626619 | 2017-07-18 14:53:40 -0700 | [diff] [blame] | 39 | import java.util.Collections; |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 40 | import java.util.Date; |
| 41 | import java.util.HashMap; |
| 42 | import java.util.Iterator; |
| 43 | import java.util.List; |
Jean-Michel Trivi | 99489cc | 2017-01-25 19:08:49 -0800 | [diff] [blame] | 44 | import java.util.Set; |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 45 | |
| 46 | /** |
| 47 | * Class to receive and dispatch updates from AudioSystem about recording configurations. |
| 48 | */ |
Jean-Michel Trivi | 9dc22c2 | 2017-01-05 18:06:03 -0800 | [diff] [blame] | 49 | public final class PlaybackActivityMonitor |
Jean-Michel Trivi | 99489cc | 2017-01-25 19:08:49 -0800 | [diff] [blame] | 50 | implements AudioPlaybackConfiguration.PlayerDeathMonitor, PlayerFocusEnforcer { |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 51 | |
Jean-Michel Trivi | c2769ab | 2017-04-24 11:59:29 -0700 | [diff] [blame] | 52 | public static final String TAG = "AudioService.PlaybackActivityMonitor"; |
Jean-Michel Trivi | 0f49f82 | 2017-02-16 14:36:43 -0800 | [diff] [blame] | 53 | |
Jean-Michel Trivi | c2769ab | 2017-04-24 11:59:29 -0700 | [diff] [blame] | 54 | private static final boolean DEBUG = false; |
| 55 | private static final int VOLUME_SHAPER_SYSTEM_DUCK_ID = 1; |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 56 | |
Jean-Michel Trivi | c2769ab | 2017-04-24 11:59:29 -0700 | [diff] [blame] | 57 | private static final VolumeShaper.Configuration DUCK_VSHAPE = |
Andy Hung | 7da0e98 | 2017-02-22 12:34:21 -0800 | [diff] [blame] | 58 | new VolumeShaper.Configuration.Builder() |
| 59 | .setId(VOLUME_SHAPER_SYSTEM_DUCK_ID) |
| 60 | .setCurve(new float[] { 0.f, 1.f } /* times */, |
| 61 | new float[] { 1.f, 0.2f } /* volumes */) |
| 62 | .setOptionFlags(VolumeShaper.Configuration.OPTION_FLAG_CLOCK_TIME) |
Jean-Michel Trivi | 4c86efa1 | 2017-04-20 18:13:34 -0700 | [diff] [blame] | 63 | .setDuration(MediaFocusControl.getFocusRampTimeMs( |
Andy Hung | 7da0e98 | 2017-02-22 12:34:21 -0800 | [diff] [blame] | 64 | AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, |
| 65 | new AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_NOTIFICATION) |
| 66 | .build())) |
| 67 | .build(); |
Jean-Michel Trivi | c2769ab | 2017-04-24 11:59:29 -0700 | [diff] [blame] | 68 | private static final VolumeShaper.Configuration DUCK_ID = |
Andy Hung | 7da0e98 | 2017-02-22 12:34:21 -0800 | [diff] [blame] | 69 | new VolumeShaper.Configuration(VOLUME_SHAPER_SYSTEM_DUCK_ID); |
Jean-Michel Trivi | c2769ab | 2017-04-24 11:59:29 -0700 | [diff] [blame] | 70 | private static final VolumeShaper.Operation PLAY_CREATE_IF_NEEDED = |
Andy Hung | 7da0e98 | 2017-02-22 12:34:21 -0800 | [diff] [blame] | 71 | new VolumeShaper.Operation.Builder(VolumeShaper.Operation.PLAY) |
| 72 | .createIfNeeded() |
| 73 | .build(); |
Andy Hung | 7da0e98 | 2017-02-22 12:34:21 -0800 | [diff] [blame] | 74 | |
Jean-Michel Trivi | 83271bd | 2017-06-23 18:28:41 -0700 | [diff] [blame] | 75 | // TODO support VolumeShaper on those players |
| 76 | private static final int[] UNDUCKABLE_PLAYER_TYPES = { |
| 77 | AudioPlaybackConfiguration.PLAYER_TYPE_AAUDIO, |
| 78 | AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL, |
| 79 | }; |
| 80 | |
Jean-Michel Trivi | 2e48fb5 | 2017-05-16 17:59:54 -0700 | [diff] [blame] | 81 | // like a PLAY_CREATE_IF_NEEDED operation but with a skip to the end of the ramp |
| 82 | private static final VolumeShaper.Operation PLAY_SKIP_RAMP = |
| 83 | new VolumeShaper.Operation.Builder(PLAY_CREATE_IF_NEEDED).setXOffset(1.0f).build(); |
| 84 | |
Andy Hung | 7da0e98 | 2017-02-22 12:34:21 -0800 | [diff] [blame] | 85 | private final ArrayList<PlayMonitorClient> mClients = new ArrayList<PlayMonitorClient>(); |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 86 | // a public client is one that needs an anonymized version of the playback configurations, we |
| 87 | // keep track of whether there is at least one to know when we need to create the list of |
| 88 | // playback configurations that do not contain uid/pid/package name information. |
| 89 | private boolean mHasPublicClients = false; |
| 90 | |
| 91 | private final Object mPlayerLock = new Object(); |
Andy Hung | 7da0e98 | 2017-02-22 12:34:21 -0800 | [diff] [blame] | 92 | private final HashMap<Integer, AudioPlaybackConfiguration> mPlayers = |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 93 | new HashMap<Integer, AudioPlaybackConfiguration>(); |
| 94 | |
Eric Laurent | e5a351c | 2017-09-27 20:11:51 -0700 | [diff] [blame] | 95 | private final Context mContext; |
| 96 | private int mSavedAlarmVolume = -1; |
| 97 | private final int mMaxAlarmVolume; |
| 98 | private int mPrivilegedAlarmActiveCount = 0; |
| 99 | |
| 100 | PlaybackActivityMonitor(Context context, int maxAlarmVolume) { |
| 101 | mContext = context; |
| 102 | mMaxAlarmVolume = maxAlarmVolume; |
Jean-Michel Trivi | 9dc22c2 | 2017-01-05 18:06:03 -0800 | [diff] [blame] | 103 | PlayMonitorClient.sListenerDeathMonitor = this; |
| 104 | AudioPlaybackConfiguration.sPlayerDeathMonitor = this; |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 105 | } |
| 106 | |
| 107 | //================================================================= |
Jean-Michel Trivi | 92ed7bf | 2017-06-26 19:32:38 -0700 | [diff] [blame] | 108 | private final ArrayList<Integer> mBannedUids = new ArrayList<Integer>(); |
| 109 | |
| 110 | // see AudioManagerInternal.disableAudioForUid(boolean disable, int uid) |
| 111 | public void disableAudioForUid(boolean disable, int uid) { |
| 112 | synchronized(mPlayerLock) { |
| 113 | final int index = mBannedUids.indexOf(new Integer(uid)); |
| 114 | if (index >= 0) { |
| 115 | if (!disable) { |
Jean-Michel Trivi | 011f39e | 2017-08-19 18:08:06 -0700 | [diff] [blame] | 116 | if (DEBUG) { // hidden behind DEBUG, too noisy otherwise |
Jean-Michel Trivi | 74a5596 | 2017-10-02 17:46:18 -0700 | [diff] [blame] | 117 | sEventLogger.log(new AudioEventLogger.StringEvent("unbanning uid:" + uid)); |
Jean-Michel Trivi | 011f39e | 2017-08-19 18:08:06 -0700 | [diff] [blame] | 118 | } |
Jean-Michel Trivi | 92ed7bf | 2017-06-26 19:32:38 -0700 | [diff] [blame] | 119 | mBannedUids.remove(index); |
| 120 | // nothing else to do, future playback requests from this uid are ok |
| 121 | } // no else to handle, uid already present, so disabling again is no-op |
| 122 | } else { |
| 123 | if (disable) { |
| 124 | for (AudioPlaybackConfiguration apc : mPlayers.values()) { |
| 125 | checkBanPlayer(apc, uid); |
| 126 | } |
Jean-Michel Trivi | 011f39e | 2017-08-19 18:08:06 -0700 | [diff] [blame] | 127 | if (DEBUG) { // hidden behind DEBUG, too noisy otherwise |
Jean-Michel Trivi | 74a5596 | 2017-10-02 17:46:18 -0700 | [diff] [blame] | 128 | sEventLogger.log(new AudioEventLogger.StringEvent("banning uid:" + uid)); |
Jean-Michel Trivi | 011f39e | 2017-08-19 18:08:06 -0700 | [diff] [blame] | 129 | } |
Jean-Michel Trivi | 92ed7bf | 2017-06-26 19:32:38 -0700 | [diff] [blame] | 130 | mBannedUids.add(new Integer(uid)); |
| 131 | } // no else to handle, uid already not in list, so enabling again is no-op |
| 132 | } |
| 133 | } |
| 134 | } |
| 135 | |
| 136 | private boolean checkBanPlayer(@NonNull AudioPlaybackConfiguration apc, int uid) { |
| 137 | final boolean toBan = (apc.getClientUid() == uid); |
| 138 | if (toBan) { |
| 139 | final int piid = apc.getPlayerInterfaceId(); |
| 140 | try { |
| 141 | Log.v(TAG, "banning player " + piid + " uid:" + uid); |
| 142 | apc.getPlayerProxy().pause(); |
| 143 | } catch (Exception e) { |
| 144 | Log.e(TAG, "error banning player " + piid + " uid:" + uid, e); |
| 145 | } |
| 146 | } |
| 147 | return toBan; |
| 148 | } |
| 149 | |
| 150 | //================================================================= |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 151 | // Track players and their states |
Jean-Michel Trivi | 44a8f53 | 2017-01-02 14:36:43 -0800 | [diff] [blame] | 152 | // methods playerAttributes, playerEvent, releasePlayer are all oneway calls |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 153 | // into AudioService. They trigger synchronous dispatchPlaybackChange() which updates |
| 154 | // all listeners as oneway calls. |
| 155 | |
Jean-Michel Trivi | 44a8f53 | 2017-01-02 14:36:43 -0800 | [diff] [blame] | 156 | public int trackPlayer(PlayerBase.PlayerIdCard pic) { |
| 157 | final int newPiid = AudioSystem.newAudioPlayerId(); |
| 158 | if (DEBUG) { Log.v(TAG, "trackPlayer() new piid=" + newPiid); } |
| 159 | final AudioPlaybackConfiguration apc = |
| 160 | new AudioPlaybackConfiguration(pic, newPiid, |
| 161 | Binder.getCallingUid(), Binder.getCallingPid()); |
Jean-Michel Trivi | 9dc22c2 | 2017-01-05 18:06:03 -0800 | [diff] [blame] | 162 | apc.init(); |
Jean-Michel Trivi | 74a5596 | 2017-10-02 17:46:18 -0700 | [diff] [blame] | 163 | sEventLogger.log(new NewPlayerEvent(apc)); |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 164 | synchronized(mPlayerLock) { |
Jean-Michel Trivi | 44a8f53 | 2017-01-02 14:36:43 -0800 | [diff] [blame] | 165 | mPlayers.put(newPiid, apc); |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 166 | } |
Jean-Michel Trivi | 44a8f53 | 2017-01-02 14:36:43 -0800 | [diff] [blame] | 167 | return newPiid; |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 168 | } |
| 169 | |
Jean-Michel Trivi | 46e310b | 2017-01-04 15:58:02 -0800 | [diff] [blame] | 170 | public void playerAttributes(int piid, @NonNull AudioAttributes attr, int binderUid) { |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 171 | final boolean change; |
| 172 | synchronized(mPlayerLock) { |
| 173 | final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid)); |
Jean-Michel Trivi | 46e310b | 2017-01-04 15:58:02 -0800 | [diff] [blame] | 174 | if (checkConfigurationCaller(piid, apc, binderUid)) { |
Jean-Michel Trivi | 74a5596 | 2017-10-02 17:46:18 -0700 | [diff] [blame] | 175 | sEventLogger.log(new AudioAttrEvent(piid, attr)); |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 176 | change = apc.handleAudioAttributesEvent(attr); |
Jean-Michel Trivi | 46e310b | 2017-01-04 15:58:02 -0800 | [diff] [blame] | 177 | } else { |
| 178 | Log.e(TAG, "Error updating audio attributes"); |
| 179 | change = false; |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 180 | } |
| 181 | } |
| 182 | if (change) { |
Jean-Michel Trivi | 776a399 | 2017-09-12 16:45:34 -0700 | [diff] [blame] | 183 | dispatchPlaybackChange(false); |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 184 | } |
| 185 | } |
| 186 | |
Jean-Michel Trivi | d51d398 | 2017-10-10 16:32:38 -0700 | [diff] [blame] | 187 | private static final int FLAGS_FOR_SILENCE_OVERRIDE = |
| 188 | AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY | |
| 189 | AudioAttributes.FLAG_BYPASS_MUTE; |
| 190 | |
Eric Laurent | e5a351c | 2017-09-27 20:11:51 -0700 | [diff] [blame] | 191 | private void checkVolumeForPrivilegedAlarm(AudioPlaybackConfiguration apc, int event) { |
| 192 | if (event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED || |
| 193 | apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) { |
Jean-Michel Trivi | d51d398 | 2017-10-10 16:32:38 -0700 | [diff] [blame] | 194 | if ((apc.getAudioAttributes().getAllFlags() & FLAGS_FOR_SILENCE_OVERRIDE) |
| 195 | == FLAGS_FOR_SILENCE_OVERRIDE && |
Eric Laurent | e5a351c | 2017-09-27 20:11:51 -0700 | [diff] [blame] | 196 | apc.getAudioAttributes().getUsage() == AudioAttributes.USAGE_ALARM && |
| 197 | mContext.checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE, |
| 198 | apc.getClientPid(), apc.getClientUid()) == |
| 199 | PackageManager.PERMISSION_GRANTED) { |
| 200 | if (event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED && |
| 201 | apc.getPlayerState() != AudioPlaybackConfiguration.PLAYER_STATE_STARTED) { |
| 202 | if (mPrivilegedAlarmActiveCount++ == 0) { |
| 203 | mSavedAlarmVolume = AudioSystem.getStreamVolumeIndex( |
| 204 | AudioSystem.STREAM_ALARM, AudioSystem.DEVICE_OUT_SPEAKER); |
| 205 | AudioSystem.setStreamVolumeIndex(AudioSystem.STREAM_ALARM, |
| 206 | mMaxAlarmVolume, AudioSystem.DEVICE_OUT_SPEAKER); |
| 207 | } |
| 208 | } else if (event != AudioPlaybackConfiguration.PLAYER_STATE_STARTED && |
| 209 | apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) { |
| 210 | if (--mPrivilegedAlarmActiveCount == 0) { |
| 211 | if (AudioSystem.getStreamVolumeIndex( |
| 212 | AudioSystem.STREAM_ALARM, AudioSystem.DEVICE_OUT_SPEAKER) == |
| 213 | mMaxAlarmVolume) { |
| 214 | AudioSystem.setStreamVolumeIndex(AudioSystem.STREAM_ALARM, |
| 215 | mSavedAlarmVolume, AudioSystem.DEVICE_OUT_SPEAKER); |
| 216 | } |
| 217 | } |
| 218 | } |
| 219 | } |
| 220 | } |
| 221 | } |
| 222 | |
Jean-Michel Trivi | 46e310b | 2017-01-04 15:58:02 -0800 | [diff] [blame] | 223 | public void playerEvent(int piid, int event, int binderUid) { |
Jean-Michel Trivi | 44a8f53 | 2017-01-02 14:36:43 -0800 | [diff] [blame] | 224 | if (DEBUG) { Log.v(TAG, String.format("playerEvent(piid=%d, event=%d)", piid, event)); } |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 225 | final boolean change; |
| 226 | synchronized(mPlayerLock) { |
| 227 | final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid)); |
Jean-Michel Trivi | cb84fc0 | 2017-05-03 12:16:17 -0700 | [diff] [blame] | 228 | if (apc == null) { |
| 229 | return; |
| 230 | } |
Jean-Michel Trivi | 74a5596 | 2017-10-02 17:46:18 -0700 | [diff] [blame] | 231 | sEventLogger.log(new PlayerEvent(piid, event)); |
Jean-Michel Trivi | 92ed7bf | 2017-06-26 19:32:38 -0700 | [diff] [blame] | 232 | if (event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) { |
| 233 | for (Integer uidInteger: mBannedUids) { |
| 234 | if (checkBanPlayer(apc, uidInteger.intValue())) { |
| 235 | // player was banned, do not update its state |
Jean-Michel Trivi | 74a5596 | 2017-10-02 17:46:18 -0700 | [diff] [blame] | 236 | sEventLogger.log(new AudioEventLogger.StringEvent( |
Jean-Michel Trivi | 011f39e | 2017-08-19 18:08:06 -0700 | [diff] [blame] | 237 | "not starting piid:" + piid + " ,is banned")); |
Jean-Michel Trivi | 92ed7bf | 2017-06-26 19:32:38 -0700 | [diff] [blame] | 238 | return; |
| 239 | } |
| 240 | } |
| 241 | } |
Jean-Michel Trivi | cb84fc0 | 2017-05-03 12:16:17 -0700 | [diff] [blame] | 242 | if (apc.getPlayerType() == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) { |
| 243 | // FIXME SoundPool not ready for state reporting |
Jean-Michel Trivi | f1d8276 | 2017-02-14 14:24:37 -0800 | [diff] [blame] | 244 | return; |
| 245 | } |
Jean-Michel Trivi | 46e310b | 2017-01-04 15:58:02 -0800 | [diff] [blame] | 246 | if (checkConfigurationCaller(piid, apc, binderUid)) { |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 247 | //TODO add generation counter to only update to the latest state |
Eric Laurent | e5a351c | 2017-09-27 20:11:51 -0700 | [diff] [blame] | 248 | checkVolumeForPrivilegedAlarm(apc, event); |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 249 | change = apc.handleStateEvent(event); |
Jean-Michel Trivi | 46e310b | 2017-01-04 15:58:02 -0800 | [diff] [blame] | 250 | } else { |
| 251 | Log.e(TAG, "Error handling event " + event); |
| 252 | change = false; |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 253 | } |
Jean-Michel Trivi | cb84fc0 | 2017-05-03 12:16:17 -0700 | [diff] [blame] | 254 | if (change && event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) { |
| 255 | mDuckingManager.checkDuck(apc); |
Jean-Michel Trivi | cafed63 | 2017-04-03 18:49:45 -0700 | [diff] [blame] | 256 | } |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 257 | } |
| 258 | if (change) { |
Jean-Michel Trivi | 776a399 | 2017-09-12 16:45:34 -0700 | [diff] [blame] | 259 | dispatchPlaybackChange(event == AudioPlaybackConfiguration.PLAYER_STATE_RELEASED); |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 260 | } |
| 261 | } |
| 262 | |
Jean-Michel Trivi | 3120059d | 2017-08-28 12:40:55 -0700 | [diff] [blame] | 263 | public void playerHasOpPlayAudio(int piid, boolean hasOpPlayAudio, int binderUid) { |
| 264 | // no check on UID yet because this is only for logging at the moment |
Jean-Michel Trivi | 74a5596 | 2017-10-02 17:46:18 -0700 | [diff] [blame] | 265 | sEventLogger.log(new PlayerOpPlayAudioEvent(piid, hasOpPlayAudio, binderUid)); |
Jean-Michel Trivi | 3120059d | 2017-08-28 12:40:55 -0700 | [diff] [blame] | 266 | } |
| 267 | |
Jean-Michel Trivi | 46e310b | 2017-01-04 15:58:02 -0800 | [diff] [blame] | 268 | public void releasePlayer(int piid, int binderUid) { |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 269 | if (DEBUG) { Log.v(TAG, "releasePlayer() for piid=" + piid); } |
Jean-Michel Trivi | 2a28126 | 2018-04-23 17:22:55 -0700 | [diff] [blame] | 270 | boolean change = false; |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 271 | synchronized(mPlayerLock) { |
Jean-Michel Trivi | 46e310b | 2017-01-04 15:58:02 -0800 | [diff] [blame] | 272 | final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid)); |
| 273 | if (checkConfigurationCaller(piid, apc, binderUid)) { |
Jean-Michel Trivi | 74a5596 | 2017-10-02 17:46:18 -0700 | [diff] [blame] | 274 | sEventLogger.log(new AudioEventLogger.StringEvent( |
Jean-Michel Trivi | 011f39e | 2017-08-19 18:08:06 -0700 | [diff] [blame] | 275 | "releasing player piid:" + piid)); |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 276 | mPlayers.remove(new Integer(piid)); |
Jean-Michel Trivi | cb84fc0 | 2017-05-03 12:16:17 -0700 | [diff] [blame] | 277 | mDuckingManager.removeReleased(apc); |
Eric Laurent | e5a351c | 2017-09-27 20:11:51 -0700 | [diff] [blame] | 278 | checkVolumeForPrivilegedAlarm(apc, AudioPlaybackConfiguration.PLAYER_STATE_RELEASED); |
Jean-Michel Trivi | 2a28126 | 2018-04-23 17:22:55 -0700 | [diff] [blame] | 279 | change = apc.handleStateEvent(AudioPlaybackConfiguration.PLAYER_STATE_RELEASED); |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 280 | } |
| 281 | } |
Jean-Michel Trivi | 2a28126 | 2018-04-23 17:22:55 -0700 | [diff] [blame] | 282 | if (change) { |
| 283 | dispatchPlaybackChange(true /*iplayerreleased*/); |
| 284 | } |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 285 | } |
| 286 | |
Jean-Michel Trivi | 9dc22c2 | 2017-01-05 18:06:03 -0800 | [diff] [blame] | 287 | // Implementation of AudioPlaybackConfiguration.PlayerDeathMonitor |
| 288 | @Override |
| 289 | public void playerDeath(int piid) { |
| 290 | releasePlayer(piid, 0); |
| 291 | } |
| 292 | |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 293 | protected void dump(PrintWriter pw) { |
Jean-Michel Trivi | 99489cc | 2017-01-25 19:08:49 -0800 | [diff] [blame] | 294 | // players |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 295 | pw.println("\nPlaybackActivityMonitor dump time: " |
| 296 | + DateFormat.getTimeInstance().format(new Date())); |
| 297 | synchronized(mPlayerLock) { |
Jean-Michel Trivi | 776a399 | 2017-09-12 16:45:34 -0700 | [diff] [blame] | 298 | pw.println("\n playback listeners:"); |
| 299 | synchronized(mClients) { |
| 300 | for (PlayMonitorClient pmc : mClients) { |
| 301 | pw.print(" " + (pmc.mIsPrivileged ? "(S)" : "(P)") |
| 302 | + pmc.toString()); |
| 303 | } |
| 304 | } |
| 305 | pw.println("\n"); |
Jean-Michel Trivi | 5626619 | 2017-07-18 14:53:40 -0700 | [diff] [blame] | 306 | // all players |
| 307 | pw.println("\n players:"); |
| 308 | final List<Integer> piidIntList = new ArrayList<Integer>(mPlayers.keySet()); |
| 309 | Collections.sort(piidIntList); |
| 310 | for (Integer piidInt : piidIntList) { |
| 311 | final AudioPlaybackConfiguration apc = mPlayers.get(piidInt); |
| 312 | if (apc != null) { |
| 313 | apc.dump(pw); |
| 314 | } |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 315 | } |
Jean-Michel Trivi | 99489cc | 2017-01-25 19:08:49 -0800 | [diff] [blame] | 316 | // ducked players |
Jean-Michel Trivi | 5626619 | 2017-07-18 14:53:40 -0700 | [diff] [blame] | 317 | pw.println("\n ducked players piids:"); |
Jean-Michel Trivi | cb84fc0 | 2017-05-03 12:16:17 -0700 | [diff] [blame] | 318 | mDuckingManager.dump(pw); |
Jean-Michel Trivi | 62b8634 | 2017-02-04 15:33:47 -0800 | [diff] [blame] | 319 | // players muted due to the device ringing or being in a call |
Jean-Michel Trivi | 92ed7bf | 2017-06-26 19:32:38 -0700 | [diff] [blame] | 320 | pw.print("\n muted player piids:"); |
Jean-Michel Trivi | 62b8634 | 2017-02-04 15:33:47 -0800 | [diff] [blame] | 321 | for (int piid : mMutedPlayers) { |
Jean-Michel Trivi | 92ed7bf | 2017-06-26 19:32:38 -0700 | [diff] [blame] | 322 | pw.print(" " + piid); |
Jean-Michel Trivi | 62b8634 | 2017-02-04 15:33:47 -0800 | [diff] [blame] | 323 | } |
Jean-Michel Trivi | 92ed7bf | 2017-06-26 19:32:38 -0700 | [diff] [blame] | 324 | pw.println(); |
| 325 | // banned players: |
| 326 | pw.print("\n banned uids:"); |
| 327 | for (int uid : mBannedUids) { |
| 328 | pw.print(" " + uid); |
| 329 | } |
Jean-Michel Trivi | 776a399 | 2017-09-12 16:45:34 -0700 | [diff] [blame] | 330 | pw.println("\n"); |
Jean-Michel Trivi | 011f39e | 2017-08-19 18:08:06 -0700 | [diff] [blame] | 331 | // log |
Jean-Michel Trivi | 74a5596 | 2017-10-02 17:46:18 -0700 | [diff] [blame] | 332 | sEventLogger.dump(pw); |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 333 | } |
| 334 | } |
| 335 | |
Jean-Michel Trivi | 46e310b | 2017-01-04 15:58:02 -0800 | [diff] [blame] | 336 | /** |
Jean-Michel Trivi | c2769ab | 2017-04-24 11:59:29 -0700 | [diff] [blame] | 337 | * Check that piid and uid are valid for the given valid configuration. |
Jean-Michel Trivi | 46e310b | 2017-01-04 15:58:02 -0800 | [diff] [blame] | 338 | * @param piid the piid of the player. |
| 339 | * @param apc the configuration found for this piid. |
| 340 | * @param binderUid actual uid of client trying to signal a player state/event/attributes. |
Jean-Michel Trivi | c2769ab | 2017-04-24 11:59:29 -0700 | [diff] [blame] | 341 | * @return true if the call is valid and the change should proceed, false otherwise. Always |
| 342 | * returns false when apc is null. |
Jean-Michel Trivi | 46e310b | 2017-01-04 15:58:02 -0800 | [diff] [blame] | 343 | */ |
| 344 | private static boolean checkConfigurationCaller(int piid, |
| 345 | final AudioPlaybackConfiguration apc, int binderUid) { |
| 346 | if (apc == null) { |
Jean-Michel Trivi | 46e310b | 2017-01-04 15:58:02 -0800 | [diff] [blame] | 347 | return false; |
| 348 | } else if ((binderUid != 0) && (apc.getClientUid() != binderUid)) { |
| 349 | Log.e(TAG, "Forbidden operation from uid " + binderUid + " for player " + piid); |
| 350 | return false; |
| 351 | } |
| 352 | return true; |
| 353 | } |
| 354 | |
Jean-Michel Trivi | 776a399 | 2017-09-12 16:45:34 -0700 | [diff] [blame] | 355 | /** |
| 356 | * Sends new list after update of playback configurations |
| 357 | * @param iplayerReleased indicates if the change was due to a player being released |
| 358 | */ |
| 359 | private void dispatchPlaybackChange(boolean iplayerReleased) { |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 360 | synchronized (mClients) { |
| 361 | // typical use case, nobody is listening, don't do any work |
| 362 | if (mClients.isEmpty()) { |
| 363 | return; |
| 364 | } |
| 365 | } |
| 366 | if (DEBUG) { Log.v(TAG, "dispatchPlaybackChange to " + mClients.size() + " clients"); } |
| 367 | final List<AudioPlaybackConfiguration> configsSystem; |
| 368 | // list of playback configurations for "public consumption". It is only computed if there |
| 369 | // are non-system playback activity listeners. |
| 370 | final List<AudioPlaybackConfiguration> configsPublic; |
| 371 | synchronized (mPlayerLock) { |
| 372 | if (mPlayers.isEmpty()) { |
| 373 | return; |
| 374 | } |
| 375 | configsSystem = new ArrayList<AudioPlaybackConfiguration>(mPlayers.values()); |
| 376 | } |
| 377 | synchronized (mClients) { |
| 378 | // was done at beginning of method, but could have changed |
| 379 | if (mClients.isEmpty()) { |
| 380 | return; |
| 381 | } |
| 382 | configsPublic = mHasPublicClients ? anonymizeForPublicConsumption(configsSystem) : null; |
| 383 | final Iterator<PlayMonitorClient> clientIterator = mClients.iterator(); |
| 384 | while (clientIterator.hasNext()) { |
| 385 | final PlayMonitorClient pmc = clientIterator.next(); |
| 386 | try { |
| 387 | // do not spam the logs if there are problems communicating with this client |
| 388 | if (pmc.mErrorCount < PlayMonitorClient.MAX_ERRORS) { |
| 389 | if (pmc.mIsPrivileged) { |
Jean-Michel Trivi | 776a399 | 2017-09-12 16:45:34 -0700 | [diff] [blame] | 390 | pmc.mDispatcherCb.dispatchPlaybackConfigChange(configsSystem, |
| 391 | iplayerReleased); |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 392 | } else { |
Jean-Michel Trivi | 776a399 | 2017-09-12 16:45:34 -0700 | [diff] [blame] | 393 | // non-system clients don't have the control interface IPlayer, so |
| 394 | // they don't need to flush commands when a player was released |
| 395 | pmc.mDispatcherCb.dispatchPlaybackConfigChange(configsPublic, false); |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 396 | } |
| 397 | } |
| 398 | } catch (RemoteException e) { |
| 399 | pmc.mErrorCount++; |
| 400 | Log.e(TAG, "Error (" + pmc.mErrorCount + |
| 401 | ") trying to dispatch playback config change to " + pmc, e); |
| 402 | } |
| 403 | } |
| 404 | } |
| 405 | } |
| 406 | |
| 407 | private ArrayList<AudioPlaybackConfiguration> anonymizeForPublicConsumption( |
| 408 | List<AudioPlaybackConfiguration> sysConfigs) { |
| 409 | ArrayList<AudioPlaybackConfiguration> publicConfigs = |
| 410 | new ArrayList<AudioPlaybackConfiguration>(); |
Jean-Michel Trivi | 99489cc | 2017-01-25 19:08:49 -0800 | [diff] [blame] | 411 | // only add active anonymized configurations, |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 412 | for (AudioPlaybackConfiguration config : sysConfigs) { |
| 413 | if (config.isActive()) { |
| 414 | publicConfigs.add(AudioPlaybackConfiguration.anonymizedCopy(config)); |
| 415 | } |
| 416 | } |
| 417 | return publicConfigs; |
| 418 | } |
| 419 | |
Jean-Michel Trivi | 99489cc | 2017-01-25 19:08:49 -0800 | [diff] [blame] | 420 | |
| 421 | //================================================================= |
| 422 | // PlayerFocusEnforcer implementation |
Jean-Michel Trivi | 62b8634 | 2017-02-04 15:33:47 -0800 | [diff] [blame] | 423 | private final ArrayList<Integer> mMutedPlayers = new ArrayList<Integer>(); |
Jean-Michel Trivi | cb84fc0 | 2017-05-03 12:16:17 -0700 | [diff] [blame] | 424 | |
| 425 | private final DuckingManager mDuckingManager = new DuckingManager(); |
Jean-Michel Trivi | 99489cc | 2017-01-25 19:08:49 -0800 | [diff] [blame] | 426 | |
| 427 | @Override |
Jean-Michel Trivi | 9228af6 | 2018-01-05 17:06:17 -0800 | [diff] [blame] | 428 | public boolean duckPlayers(FocusRequester winner, FocusRequester loser, boolean forceDuck) { |
Jean-Michel Trivi | 99489cc | 2017-01-25 19:08:49 -0800 | [diff] [blame] | 429 | if (DEBUG) { |
| 430 | Log.v(TAG, String.format("duckPlayers: uids winner=%d loser=%d", |
Jean-Michel Trivi | cafed63 | 2017-04-03 18:49:45 -0700 | [diff] [blame] | 431 | winner.getClientUid(), loser.getClientUid())); |
| 432 | } |
Jean-Michel Trivi | 99489cc | 2017-01-25 19:08:49 -0800 | [diff] [blame] | 433 | synchronized (mPlayerLock) { |
| 434 | if (mPlayers.isEmpty()) { |
| 435 | return true; |
| 436 | } |
Jean-Michel Trivi | cb84fc0 | 2017-05-03 12:16:17 -0700 | [diff] [blame] | 437 | // check if this UID needs to be ducked (return false if not), and gather list of |
| 438 | // eligible players to duck |
| 439 | final Iterator<AudioPlaybackConfiguration> apcIterator = mPlayers.values().iterator(); |
| 440 | final ArrayList<AudioPlaybackConfiguration> apcsToDuck = |
| 441 | new ArrayList<AudioPlaybackConfiguration>(); |
| 442 | while (apcIterator.hasNext()) { |
| 443 | final AudioPlaybackConfiguration apc = apcIterator.next(); |
Jean-Michel Trivi | 99489cc | 2017-01-25 19:08:49 -0800 | [diff] [blame] | 444 | if (!winner.hasSameUid(apc.getClientUid()) |
| 445 | && loser.hasSameUid(apc.getClientUid()) |
| 446 | && apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) |
| 447 | { |
Jean-Michel Trivi | 9228af6 | 2018-01-05 17:06:17 -0800 | [diff] [blame] | 448 | if (!forceDuck && (apc.getAudioAttributes().getContentType() == |
| 449 | AudioAttributes.CONTENT_TYPE_SPEECH)) { |
Jean-Michel Trivi | 99489cc | 2017-01-25 19:08:49 -0800 | [diff] [blame] | 450 | // the player is speaking, ducking will make the speech unintelligible |
| 451 | // so let the app handle it instead |
Jean-Michel Trivi | 087b672 | 2017-05-10 13:05:06 -0700 | [diff] [blame] | 452 | Log.v(TAG, "not ducking player " + apc.getPlayerInterfaceId() |
| 453 | + " uid:" + apc.getClientUid() + " pid:" + apc.getClientPid() |
| 454 | + " - SPEECH"); |
Jean-Michel Trivi | 99489cc | 2017-01-25 19:08:49 -0800 | [diff] [blame] | 455 | return false; |
Jean-Michel Trivi | 83271bd | 2017-06-23 18:28:41 -0700 | [diff] [blame] | 456 | } else if (ArrayUtils.contains(UNDUCKABLE_PLAYER_TYPES, apc.getPlayerType())) { |
Jean-Michel Trivi | 087b672 | 2017-05-10 13:05:06 -0700 | [diff] [blame] | 457 | Log.v(TAG, "not ducking player " + apc.getPlayerInterfaceId() |
| 458 | + " uid:" + apc.getClientUid() + " pid:" + apc.getClientPid() |
Jean-Michel Trivi | 83271bd | 2017-06-23 18:28:41 -0700 | [diff] [blame] | 459 | + " due to type:" |
| 460 | + AudioPlaybackConfiguration.toLogFriendlyPlayerType( |
| 461 | apc.getPlayerType())); |
Jean-Michel Trivi | dce82ab | 2017-02-07 16:02:33 -0800 | [diff] [blame] | 462 | return false; |
Jean-Michel Trivi | 99489cc | 2017-01-25 19:08:49 -0800 | [diff] [blame] | 463 | } |
Jean-Michel Trivi | cb84fc0 | 2017-05-03 12:16:17 -0700 | [diff] [blame] | 464 | apcsToDuck.add(apc); |
Jean-Michel Trivi | 99489cc | 2017-01-25 19:08:49 -0800 | [diff] [blame] | 465 | } |
| 466 | } |
Jean-Michel Trivi | cb84fc0 | 2017-05-03 12:16:17 -0700 | [diff] [blame] | 467 | // add the players eligible for ducking to the list, and duck them |
| 468 | // (if apcsToDuck is empty, this will at least mark this uid as ducked, so when |
| 469 | // players of the same uid start, they will be ducked by DuckingManager.checkDuck()) |
| 470 | mDuckingManager.duckUid(loser.getClientUid(), apcsToDuck); |
Jean-Michel Trivi | 99489cc | 2017-01-25 19:08:49 -0800 | [diff] [blame] | 471 | } |
| 472 | return true; |
| 473 | } |
| 474 | |
| 475 | @Override |
| 476 | public void unduckPlayers(FocusRequester winner) { |
| 477 | if (DEBUG) { Log.v(TAG, "unduckPlayers: uids winner=" + winner.getClientUid()); } |
| 478 | synchronized (mPlayerLock) { |
Jean-Michel Trivi | cb84fc0 | 2017-05-03 12:16:17 -0700 | [diff] [blame] | 479 | mDuckingManager.unduckUid(winner.getClientUid(), mPlayers); |
Jean-Michel Trivi | 99489cc | 2017-01-25 19:08:49 -0800 | [diff] [blame] | 480 | } |
| 481 | } |
| 482 | |
Jean-Michel Trivi | 62b8634 | 2017-02-04 15:33:47 -0800 | [diff] [blame] | 483 | @Override |
| 484 | public void mutePlayersForCall(int[] usagesToMute) { |
| 485 | if (DEBUG) { |
| 486 | String log = new String("mutePlayersForCall: usages="); |
| 487 | for (int usage : usagesToMute) { log += " " + usage; } |
| 488 | Log.v(TAG, log); |
| 489 | } |
| 490 | synchronized (mPlayerLock) { |
| 491 | final Set<Integer> piidSet = mPlayers.keySet(); |
| 492 | final Iterator<Integer> piidIterator = piidSet.iterator(); |
| 493 | // find which players to mute |
| 494 | while (piidIterator.hasNext()) { |
| 495 | final Integer piid = piidIterator.next(); |
| 496 | final AudioPlaybackConfiguration apc = mPlayers.get(piid); |
Jean-Michel Trivi | bd39cfa | 2017-02-17 17:23:36 -0800 | [diff] [blame] | 497 | if (apc == null) { |
| 498 | continue; |
| 499 | } |
Jean-Michel Trivi | 62b8634 | 2017-02-04 15:33:47 -0800 | [diff] [blame] | 500 | final int playerUsage = apc.getAudioAttributes().getUsage(); |
| 501 | boolean mute = false; |
| 502 | for (int usageToMute : usagesToMute) { |
| 503 | if (playerUsage == usageToMute) { |
| 504 | mute = true; |
| 505 | break; |
| 506 | } |
| 507 | } |
| 508 | if (mute) { |
| 509 | try { |
Jean-Michel Trivi | 74a5596 | 2017-10-02 17:46:18 -0700 | [diff] [blame] | 510 | sEventLogger.log((new AudioEventLogger.StringEvent("call: muting piid:" |
| 511 | + piid + " uid:" + apc.getClientUid())).printLog(TAG)); |
Jean-Michel Trivi | 62b8634 | 2017-02-04 15:33:47 -0800 | [diff] [blame] | 512 | apc.getPlayerProxy().setVolume(0.0f); |
Jean-Michel Trivi | cafed63 | 2017-04-03 18:49:45 -0700 | [diff] [blame] | 513 | mMutedPlayers.add(new Integer(piid)); |
Jean-Michel Trivi | 62b8634 | 2017-02-04 15:33:47 -0800 | [diff] [blame] | 514 | } catch (Exception e) { |
Jean-Michel Trivi | 952f234 | 2017-03-06 16:16:56 -0800 | [diff] [blame] | 515 | Log.e(TAG, "call: error muting player " + piid, e); |
Jean-Michel Trivi | 62b8634 | 2017-02-04 15:33:47 -0800 | [diff] [blame] | 516 | } |
| 517 | } |
| 518 | } |
| 519 | } |
| 520 | } |
| 521 | |
| 522 | @Override |
| 523 | public void unmutePlayersForCall() { |
| 524 | if (DEBUG) { |
| 525 | Log.v(TAG, "unmutePlayersForCall()"); |
| 526 | } |
| 527 | synchronized (mPlayerLock) { |
| 528 | if (mMutedPlayers.isEmpty()) { |
| 529 | return; |
| 530 | } |
| 531 | for (int piid : mMutedPlayers) { |
| 532 | final AudioPlaybackConfiguration apc = mPlayers.get(piid); |
| 533 | if (apc != null) { |
| 534 | try { |
Jean-Michel Trivi | 74a5596 | 2017-10-02 17:46:18 -0700 | [diff] [blame] | 535 | sEventLogger.log(new AudioEventLogger.StringEvent("call: unmuting piid:" |
| 536 | + piid).printLog(TAG)); |
Jean-Michel Trivi | 62b8634 | 2017-02-04 15:33:47 -0800 | [diff] [blame] | 537 | apc.getPlayerProxy().setVolume(1.0f); |
Jean-Michel Trivi | 62b8634 | 2017-02-04 15:33:47 -0800 | [diff] [blame] | 538 | } catch (Exception e) { |
Jean-Michel Trivi | 087b672 | 2017-05-10 13:05:06 -0700 | [diff] [blame] | 539 | Log.e(TAG, "call: error unmuting player " + piid + " uid:" |
| 540 | + apc.getClientUid(), e); |
Jean-Michel Trivi | 62b8634 | 2017-02-04 15:33:47 -0800 | [diff] [blame] | 541 | } |
| 542 | } |
| 543 | } |
Jean-Michel Trivi | 579c511 | 2017-02-10 09:47:30 -0800 | [diff] [blame] | 544 | mMutedPlayers.clear(); |
Jean-Michel Trivi | 62b8634 | 2017-02-04 15:33:47 -0800 | [diff] [blame] | 545 | } |
| 546 | } |
| 547 | |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 548 | //================================================================= |
| 549 | // Track playback activity listeners |
| 550 | |
| 551 | void registerPlaybackCallback(IPlaybackConfigDispatcher pcdb, boolean isPrivileged) { |
| 552 | if (pcdb == null) { |
| 553 | return; |
| 554 | } |
| 555 | synchronized(mClients) { |
| 556 | final PlayMonitorClient pmc = new PlayMonitorClient(pcdb, isPrivileged); |
| 557 | if (pmc.init()) { |
| 558 | if (!isPrivileged) { |
| 559 | mHasPublicClients = true; |
| 560 | } |
| 561 | mClients.add(pmc); |
| 562 | } |
| 563 | } |
| 564 | } |
| 565 | |
| 566 | void unregisterPlaybackCallback(IPlaybackConfigDispatcher pcdb) { |
| 567 | if (pcdb == null) { |
| 568 | return; |
| 569 | } |
| 570 | synchronized(mClients) { |
| 571 | final Iterator<PlayMonitorClient> clientIterator = mClients.iterator(); |
| 572 | boolean hasPublicClients = false; |
| 573 | // iterate over the clients to remove the dispatcher to remove, and reevaluate at |
| 574 | // the same time if we still have a public client. |
| 575 | while (clientIterator.hasNext()) { |
| 576 | PlayMonitorClient pmc = clientIterator.next(); |
| 577 | if (pcdb.equals(pmc.mDispatcherCb)) { |
| 578 | pmc.release(); |
| 579 | clientIterator.remove(); |
| 580 | } else { |
| 581 | if (!pmc.mIsPrivileged) { |
| 582 | hasPublicClients = true; |
| 583 | } |
| 584 | } |
| 585 | } |
| 586 | mHasPublicClients = hasPublicClients; |
| 587 | } |
| 588 | } |
| 589 | |
Jean-Michel Trivi | 44a8f53 | 2017-01-02 14:36:43 -0800 | [diff] [blame] | 590 | List<AudioPlaybackConfiguration> getActivePlaybackConfigurations(boolean isPrivileged) { |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 591 | synchronized(mPlayers) { |
Jean-Michel Trivi | 44a8f53 | 2017-01-02 14:36:43 -0800 | [diff] [blame] | 592 | if (isPrivileged) { |
| 593 | return new ArrayList<AudioPlaybackConfiguration>(mPlayers.values()); |
| 594 | } else { |
| 595 | final List<AudioPlaybackConfiguration> configsPublic; |
| 596 | synchronized (mPlayerLock) { |
| 597 | configsPublic = anonymizeForPublicConsumption( |
| 598 | new ArrayList<AudioPlaybackConfiguration>(mPlayers.values())); |
| 599 | } |
| 600 | return configsPublic; |
| 601 | } |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 602 | } |
| 603 | } |
| 604 | |
| 605 | |
| 606 | /** |
| 607 | * Inner class to track clients that want to be notified of playback updates |
| 608 | */ |
Jean-Michel Trivi | c2769ab | 2017-04-24 11:59:29 -0700 | [diff] [blame] | 609 | private static final class PlayMonitorClient implements IBinder.DeathRecipient { |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 610 | |
| 611 | // can afford to be static because only one PlaybackActivityMonitor ever instantiated |
Jean-Michel Trivi | 9dc22c2 | 2017-01-05 18:06:03 -0800 | [diff] [blame] | 612 | static PlaybackActivityMonitor sListenerDeathMonitor; |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 613 | |
| 614 | final IPlaybackConfigDispatcher mDispatcherCb; |
| 615 | final boolean mIsPrivileged; |
| 616 | |
| 617 | int mErrorCount = 0; |
| 618 | // number of errors after which we don't update this client anymore to not spam the logs |
| 619 | static final int MAX_ERRORS = 5; |
| 620 | |
| 621 | PlayMonitorClient(IPlaybackConfigDispatcher pcdb, boolean isPrivileged) { |
| 622 | mDispatcherCb = pcdb; |
| 623 | mIsPrivileged = isPrivileged; |
| 624 | } |
| 625 | |
| 626 | public void binderDied() { |
| 627 | Log.w(TAG, "client died"); |
Jean-Michel Trivi | 9dc22c2 | 2017-01-05 18:06:03 -0800 | [diff] [blame] | 628 | sListenerDeathMonitor.unregisterPlaybackCallback(mDispatcherCb); |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 629 | } |
| 630 | |
| 631 | boolean init() { |
| 632 | try { |
| 633 | mDispatcherCb.asBinder().linkToDeath(this, 0); |
| 634 | return true; |
| 635 | } catch (RemoteException e) { |
| 636 | Log.w(TAG, "Could not link to client death", e); |
| 637 | return false; |
| 638 | } |
| 639 | } |
| 640 | |
| 641 | void release() { |
| 642 | mDispatcherCb.asBinder().unlinkToDeath(this, 0); |
| 643 | } |
| 644 | } |
Jean-Michel Trivi | cb84fc0 | 2017-05-03 12:16:17 -0700 | [diff] [blame] | 645 | |
| 646 | //================================================================= |
| 647 | // Class to handle ducking related operations for a given UID |
| 648 | private static final class DuckingManager { |
| 649 | private final HashMap<Integer, DuckedApp> mDuckers = new HashMap<Integer, DuckedApp>(); |
| 650 | |
Jean-Michel Trivi | 2e48fb5 | 2017-05-16 17:59:54 -0700 | [diff] [blame] | 651 | synchronized void duckUid(int uid, ArrayList<AudioPlaybackConfiguration> apcsToDuck) { |
| 652 | if (DEBUG) { Log.v(TAG, "DuckingManager: duckUid() uid:"+ uid); } |
Jean-Michel Trivi | cb84fc0 | 2017-05-03 12:16:17 -0700 | [diff] [blame] | 653 | if (!mDuckers.containsKey(uid)) { |
| 654 | mDuckers.put(uid, new DuckedApp(uid)); |
| 655 | } |
| 656 | final DuckedApp da = mDuckers.get(uid); |
| 657 | for (AudioPlaybackConfiguration apc : apcsToDuck) { |
Jean-Michel Trivi | 2e48fb5 | 2017-05-16 17:59:54 -0700 | [diff] [blame] | 658 | da.addDuck(apc, false /*skipRamp*/); |
Jean-Michel Trivi | cb84fc0 | 2017-05-03 12:16:17 -0700 | [diff] [blame] | 659 | } |
| 660 | } |
| 661 | |
Jean-Michel Trivi | 2e48fb5 | 2017-05-16 17:59:54 -0700 | [diff] [blame] | 662 | synchronized void unduckUid(int uid, HashMap<Integer, AudioPlaybackConfiguration> players) { |
| 663 | if (DEBUG) { Log.v(TAG, "DuckingManager: unduckUid() uid:"+ uid); } |
Jean-Michel Trivi | cb84fc0 | 2017-05-03 12:16:17 -0700 | [diff] [blame] | 664 | final DuckedApp da = mDuckers.remove(uid); |
| 665 | if (da == null) { |
| 666 | return; |
| 667 | } |
| 668 | da.removeUnduckAll(players); |
| 669 | } |
| 670 | |
| 671 | // pre-condition: apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED |
Jean-Michel Trivi | 2e48fb5 | 2017-05-16 17:59:54 -0700 | [diff] [blame] | 672 | synchronized void checkDuck(@NonNull AudioPlaybackConfiguration apc) { |
| 673 | if (DEBUG) { Log.v(TAG, "DuckingManager: checkDuck() player piid:" |
| 674 | + apc.getPlayerInterfaceId()+ " uid:"+ apc.getClientUid()); } |
Jean-Michel Trivi | cb84fc0 | 2017-05-03 12:16:17 -0700 | [diff] [blame] | 675 | final DuckedApp da = mDuckers.get(apc.getClientUid()); |
| 676 | if (da == null) { |
| 677 | return; |
| 678 | } |
Jean-Michel Trivi | 2e48fb5 | 2017-05-16 17:59:54 -0700 | [diff] [blame] | 679 | da.addDuck(apc, true /*skipRamp*/); |
Jean-Michel Trivi | cb84fc0 | 2017-05-03 12:16:17 -0700 | [diff] [blame] | 680 | } |
| 681 | |
Jean-Michel Trivi | 2e48fb5 | 2017-05-16 17:59:54 -0700 | [diff] [blame] | 682 | synchronized void dump(PrintWriter pw) { |
Jean-Michel Trivi | cb84fc0 | 2017-05-03 12:16:17 -0700 | [diff] [blame] | 683 | for (DuckedApp da : mDuckers.values()) { |
| 684 | da.dump(pw); |
| 685 | } |
| 686 | } |
| 687 | |
Jean-Michel Trivi | 2e48fb5 | 2017-05-16 17:59:54 -0700 | [diff] [blame] | 688 | synchronized void removeReleased(@NonNull AudioPlaybackConfiguration apc) { |
| 689 | final int uid = apc.getClientUid(); |
| 690 | if (DEBUG) { Log.v(TAG, "DuckingManager: removedReleased() player piid: " |
| 691 | + apc.getPlayerInterfaceId() + " uid:" + uid); } |
| 692 | final DuckedApp da = mDuckers.get(uid); |
Jean-Michel Trivi | cb84fc0 | 2017-05-03 12:16:17 -0700 | [diff] [blame] | 693 | if (da == null) { |
| 694 | return; |
| 695 | } |
| 696 | da.removeReleased(apc); |
| 697 | } |
| 698 | |
| 699 | private static final class DuckedApp { |
| 700 | private final int mUid; |
| 701 | private final ArrayList<Integer> mDuckedPlayers = new ArrayList<Integer>(); |
| 702 | |
| 703 | DuckedApp(int uid) { |
| 704 | mUid = uid; |
| 705 | } |
| 706 | |
| 707 | void dump(PrintWriter pw) { |
| 708 | pw.print("\t uid:" + mUid + " piids:"); |
| 709 | for (int piid : mDuckedPlayers) { |
| 710 | pw.print(" " + piid); |
| 711 | } |
| 712 | pw.println(""); |
| 713 | } |
| 714 | |
| 715 | // pre-conditions: |
| 716 | // * apc != null |
| 717 | // * apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED |
Jean-Michel Trivi | 2e48fb5 | 2017-05-16 17:59:54 -0700 | [diff] [blame] | 718 | void addDuck(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp) { |
Jean-Michel Trivi | cb84fc0 | 2017-05-03 12:16:17 -0700 | [diff] [blame] | 719 | final int piid = new Integer(apc.getPlayerInterfaceId()); |
| 720 | if (mDuckedPlayers.contains(piid)) { |
Jean-Michel Trivi | 2e48fb5 | 2017-05-16 17:59:54 -0700 | [diff] [blame] | 721 | if (DEBUG) { Log.v(TAG, "player piid:" + piid + " already ducked"); } |
Jean-Michel Trivi | cb84fc0 | 2017-05-03 12:16:17 -0700 | [diff] [blame] | 722 | return; |
| 723 | } |
| 724 | try { |
Jean-Michel Trivi | 74a5596 | 2017-10-02 17:46:18 -0700 | [diff] [blame] | 725 | sEventLogger.log((new DuckEvent(apc, skipRamp)).printLog(TAG)); |
Jean-Michel Trivi | cb84fc0 | 2017-05-03 12:16:17 -0700 | [diff] [blame] | 726 | apc.getPlayerProxy().applyVolumeShaper( |
| 727 | DUCK_VSHAPE, |
Jean-Michel Trivi | 2e48fb5 | 2017-05-16 17:59:54 -0700 | [diff] [blame] | 728 | skipRamp ? PLAY_SKIP_RAMP : PLAY_CREATE_IF_NEEDED); |
Jean-Michel Trivi | cb84fc0 | 2017-05-03 12:16:17 -0700 | [diff] [blame] | 729 | mDuckedPlayers.add(piid); |
| 730 | } catch (Exception e) { |
Jean-Michel Trivi | 2e48fb5 | 2017-05-16 17:59:54 -0700 | [diff] [blame] | 731 | Log.e(TAG, "Error ducking player piid:" + piid + " uid:" + mUid, e); |
Jean-Michel Trivi | cb84fc0 | 2017-05-03 12:16:17 -0700 | [diff] [blame] | 732 | } |
| 733 | } |
| 734 | |
| 735 | void removeUnduckAll(HashMap<Integer, AudioPlaybackConfiguration> players) { |
| 736 | for (int piid : mDuckedPlayers) { |
| 737 | final AudioPlaybackConfiguration apc = players.get(piid); |
| 738 | if (apc != null) { |
| 739 | try { |
Jean-Michel Trivi | 74a5596 | 2017-10-02 17:46:18 -0700 | [diff] [blame] | 740 | sEventLogger.log((new AudioEventLogger.StringEvent("unducking piid:" |
| 741 | + piid)).printLog(TAG)); |
Jean-Michel Trivi | cb84fc0 | 2017-05-03 12:16:17 -0700 | [diff] [blame] | 742 | apc.getPlayerProxy().applyVolumeShaper( |
| 743 | DUCK_ID, |
| 744 | VolumeShaper.Operation.REVERSE); |
| 745 | } catch (Exception e) { |
Jean-Michel Trivi | 2e48fb5 | 2017-05-16 17:59:54 -0700 | [diff] [blame] | 746 | Log.e(TAG, "Error unducking player piid:" + piid + " uid:" + mUid, e); |
Jean-Michel Trivi | cb84fc0 | 2017-05-03 12:16:17 -0700 | [diff] [blame] | 747 | } |
| 748 | } else { |
| 749 | // this piid was in the list of ducked players, but wasn't found |
| 750 | if (DEBUG) { |
Jean-Michel Trivi | 2e48fb5 | 2017-05-16 17:59:54 -0700 | [diff] [blame] | 751 | Log.v(TAG, "Error unducking player piid:" + piid |
| 752 | + ", player not found for uid " + mUid); |
Jean-Michel Trivi | cb84fc0 | 2017-05-03 12:16:17 -0700 | [diff] [blame] | 753 | } |
| 754 | } |
| 755 | } |
| 756 | mDuckedPlayers.clear(); |
| 757 | } |
| 758 | |
| 759 | void removeReleased(@NonNull AudioPlaybackConfiguration apc) { |
| 760 | mDuckedPlayers.remove(new Integer(apc.getPlayerInterfaceId())); |
| 761 | } |
| 762 | } |
| 763 | } |
Jean-Michel Trivi | 011f39e | 2017-08-19 18:08:06 -0700 | [diff] [blame] | 764 | |
| 765 | //================================================================= |
| 766 | // For logging |
| 767 | private final static class PlayerEvent extends AudioEventLogger.Event { |
| 768 | // only keeping the player interface ID as it uniquely identifies the player in the event |
| 769 | final int mPlayerIId; |
| 770 | final int mState; |
| 771 | |
| 772 | PlayerEvent(int piid, int state) { |
| 773 | mPlayerIId = piid; |
| 774 | mState = state; |
| 775 | } |
| 776 | |
| 777 | @Override |
| 778 | public String eventToString() { |
Jean-Michel Trivi | 3120059d | 2017-08-28 12:40:55 -0700 | [diff] [blame] | 779 | return new StringBuilder("player piid:").append(mPlayerIId).append(" state:") |
| 780 | .append(AudioPlaybackConfiguration.toLogFriendlyPlayerState(mState)).toString(); |
| 781 | } |
| 782 | } |
| 783 | |
| 784 | private final static class PlayerOpPlayAudioEvent extends AudioEventLogger.Event { |
| 785 | // only keeping the player interface ID as it uniquely identifies the player in the event |
| 786 | final int mPlayerIId; |
| 787 | final boolean mHasOp; |
| 788 | final int mUid; |
| 789 | |
| 790 | PlayerOpPlayAudioEvent(int piid, boolean hasOp, int uid) { |
| 791 | mPlayerIId = piid; |
| 792 | mHasOp = hasOp; |
| 793 | mUid = uid; |
| 794 | } |
| 795 | |
| 796 | @Override |
| 797 | public String eventToString() { |
| 798 | return new StringBuilder("player piid:").append(mPlayerIId) |
| 799 | .append(" has OP_PLAY_AUDIO:").append(mHasOp) |
| 800 | .append(" in uid:").append(mUid).toString(); |
Jean-Michel Trivi | 011f39e | 2017-08-19 18:08:06 -0700 | [diff] [blame] | 801 | } |
| 802 | } |
| 803 | |
| 804 | private final static class NewPlayerEvent extends AudioEventLogger.Event { |
| 805 | private final int mPlayerIId; |
| 806 | private final int mPlayerType; |
| 807 | private final int mClientUid; |
| 808 | private final int mClientPid; |
| 809 | private final AudioAttributes mPlayerAttr; |
| 810 | |
| 811 | NewPlayerEvent(AudioPlaybackConfiguration apc) { |
| 812 | mPlayerIId = apc.getPlayerInterfaceId(); |
| 813 | mPlayerType = apc.getPlayerType(); |
| 814 | mClientUid = apc.getClientUid(); |
| 815 | mClientPid = apc.getClientPid(); |
| 816 | mPlayerAttr = apc.getAudioAttributes(); |
| 817 | } |
| 818 | |
| 819 | @Override |
| 820 | public String eventToString() { |
| 821 | return new String("new player piid:" + mPlayerIId + " uid/pid:" + mClientUid + "/" |
| 822 | + mClientPid + " type:" |
| 823 | + AudioPlaybackConfiguration.toLogFriendlyPlayerType(mPlayerType) |
| 824 | + " attr:" + mPlayerAttr); |
| 825 | } |
| 826 | } |
| 827 | |
Jean-Michel Trivi | 74a5596 | 2017-10-02 17:46:18 -0700 | [diff] [blame] | 828 | private static final class DuckEvent extends AudioEventLogger.Event { |
| 829 | private final int mPlayerIId; |
| 830 | private final boolean mSkipRamp; |
| 831 | private final int mClientUid; |
| 832 | private final int mClientPid; |
| 833 | |
| 834 | DuckEvent(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp) { |
| 835 | mPlayerIId = apc.getPlayerInterfaceId(); |
| 836 | mSkipRamp = skipRamp; |
| 837 | mClientUid = apc.getClientUid(); |
| 838 | mClientPid = apc.getClientPid(); |
| 839 | } |
| 840 | |
| 841 | @Override |
| 842 | public String eventToString() { |
| 843 | return new StringBuilder("ducking player piid:").append(mPlayerIId) |
| 844 | .append(" uid/pid:").append(mClientUid).append("/").append(mClientPid) |
| 845 | .append(" skip ramp:").append(mSkipRamp).toString(); |
| 846 | } |
| 847 | } |
| 848 | |
| 849 | private static final class AudioAttrEvent extends AudioEventLogger.Event { |
Jean-Michel Trivi | 011f39e | 2017-08-19 18:08:06 -0700 | [diff] [blame] | 850 | private final int mPlayerIId; |
| 851 | private final AudioAttributes mPlayerAttr; |
| 852 | |
| 853 | AudioAttrEvent(int piid, AudioAttributes attr) { |
| 854 | mPlayerIId = piid; |
| 855 | mPlayerAttr = attr; |
| 856 | } |
| 857 | |
| 858 | @Override |
| 859 | public String eventToString() { |
| 860 | return new String("player piid:" + mPlayerIId + " new AudioAttributes:" + mPlayerAttr); |
| 861 | } |
| 862 | } |
| 863 | |
Jean-Michel Trivi | 74a5596 | 2017-10-02 17:46:18 -0700 | [diff] [blame] | 864 | private static final AudioEventLogger sEventLogger = new AudioEventLogger(100, |
Jean-Michel Trivi | 011f39e | 2017-08-19 18:08:06 -0700 | [diff] [blame] | 865 | "playback activity as reported through PlayerBase"); |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 866 | } |