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); } |
| 270 | synchronized(mPlayerLock) { |
Jean-Michel Trivi | 46e310b | 2017-01-04 15:58:02 -0800 | [diff] [blame] | 271 | final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid)); |
| 272 | if (checkConfigurationCaller(piid, apc, binderUid)) { |
Jean-Michel Trivi | 74a5596 | 2017-10-02 17:46:18 -0700 | [diff] [blame] | 273 | sEventLogger.log(new AudioEventLogger.StringEvent( |
Jean-Michel Trivi | 011f39e | 2017-08-19 18:08:06 -0700 | [diff] [blame] | 274 | "releasing player piid:" + piid)); |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 275 | mPlayers.remove(new Integer(piid)); |
Jean-Michel Trivi | cb84fc0 | 2017-05-03 12:16:17 -0700 | [diff] [blame] | 276 | mDuckingManager.removeReleased(apc); |
Eric Laurent | e5a351c | 2017-09-27 20:11:51 -0700 | [diff] [blame] | 277 | checkVolumeForPrivilegedAlarm(apc, AudioPlaybackConfiguration.PLAYER_STATE_RELEASED); |
Jean-Michel Trivi | c2e7ceb | 2017-09-05 12:13:05 -0700 | [diff] [blame] | 278 | apc.handleStateEvent(AudioPlaybackConfiguration.PLAYER_STATE_RELEASED); |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 279 | } |
| 280 | } |
| 281 | } |
| 282 | |
Jean-Michel Trivi | 9dc22c2 | 2017-01-05 18:06:03 -0800 | [diff] [blame] | 283 | // Implementation of AudioPlaybackConfiguration.PlayerDeathMonitor |
| 284 | @Override |
| 285 | public void playerDeath(int piid) { |
| 286 | releasePlayer(piid, 0); |
| 287 | } |
| 288 | |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 289 | protected void dump(PrintWriter pw) { |
Jean-Michel Trivi | 99489cc | 2017-01-25 19:08:49 -0800 | [diff] [blame] | 290 | // players |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 291 | pw.println("\nPlaybackActivityMonitor dump time: " |
| 292 | + DateFormat.getTimeInstance().format(new Date())); |
| 293 | synchronized(mPlayerLock) { |
Jean-Michel Trivi | 776a399 | 2017-09-12 16:45:34 -0700 | [diff] [blame] | 294 | pw.println("\n playback listeners:"); |
| 295 | synchronized(mClients) { |
| 296 | for (PlayMonitorClient pmc : mClients) { |
| 297 | pw.print(" " + (pmc.mIsPrivileged ? "(S)" : "(P)") |
| 298 | + pmc.toString()); |
| 299 | } |
| 300 | } |
| 301 | pw.println("\n"); |
Jean-Michel Trivi | 5626619 | 2017-07-18 14:53:40 -0700 | [diff] [blame] | 302 | // all players |
| 303 | pw.println("\n players:"); |
| 304 | final List<Integer> piidIntList = new ArrayList<Integer>(mPlayers.keySet()); |
| 305 | Collections.sort(piidIntList); |
| 306 | for (Integer piidInt : piidIntList) { |
| 307 | final AudioPlaybackConfiguration apc = mPlayers.get(piidInt); |
| 308 | if (apc != null) { |
| 309 | apc.dump(pw); |
| 310 | } |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 311 | } |
Jean-Michel Trivi | 99489cc | 2017-01-25 19:08:49 -0800 | [diff] [blame] | 312 | // ducked players |
Jean-Michel Trivi | 5626619 | 2017-07-18 14:53:40 -0700 | [diff] [blame] | 313 | pw.println("\n ducked players piids:"); |
Jean-Michel Trivi | cb84fc0 | 2017-05-03 12:16:17 -0700 | [diff] [blame] | 314 | mDuckingManager.dump(pw); |
Jean-Michel Trivi | 62b8634 | 2017-02-04 15:33:47 -0800 | [diff] [blame] | 315 | // 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] | 316 | pw.print("\n muted player piids:"); |
Jean-Michel Trivi | 62b8634 | 2017-02-04 15:33:47 -0800 | [diff] [blame] | 317 | for (int piid : mMutedPlayers) { |
Jean-Michel Trivi | 92ed7bf | 2017-06-26 19:32:38 -0700 | [diff] [blame] | 318 | pw.print(" " + piid); |
Jean-Michel Trivi | 62b8634 | 2017-02-04 15:33:47 -0800 | [diff] [blame] | 319 | } |
Jean-Michel Trivi | 92ed7bf | 2017-06-26 19:32:38 -0700 | [diff] [blame] | 320 | pw.println(); |
| 321 | // banned players: |
| 322 | pw.print("\n banned uids:"); |
| 323 | for (int uid : mBannedUids) { |
| 324 | pw.print(" " + uid); |
| 325 | } |
Jean-Michel Trivi | 776a399 | 2017-09-12 16:45:34 -0700 | [diff] [blame] | 326 | pw.println("\n"); |
Jean-Michel Trivi | 011f39e | 2017-08-19 18:08:06 -0700 | [diff] [blame] | 327 | // log |
Jean-Michel Trivi | 74a5596 | 2017-10-02 17:46:18 -0700 | [diff] [blame] | 328 | sEventLogger.dump(pw); |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 329 | } |
| 330 | } |
| 331 | |
Jean-Michel Trivi | 46e310b | 2017-01-04 15:58:02 -0800 | [diff] [blame] | 332 | /** |
Jean-Michel Trivi | c2769ab | 2017-04-24 11:59:29 -0700 | [diff] [blame] | 333 | * 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] | 334 | * @param piid the piid of the player. |
| 335 | * @param apc the configuration found for this piid. |
| 336 | * @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] | 337 | * @return true if the call is valid and the change should proceed, false otherwise. Always |
| 338 | * returns false when apc is null. |
Jean-Michel Trivi | 46e310b | 2017-01-04 15:58:02 -0800 | [diff] [blame] | 339 | */ |
| 340 | private static boolean checkConfigurationCaller(int piid, |
| 341 | final AudioPlaybackConfiguration apc, int binderUid) { |
| 342 | if (apc == null) { |
Jean-Michel Trivi | 46e310b | 2017-01-04 15:58:02 -0800 | [diff] [blame] | 343 | return false; |
| 344 | } else if ((binderUid != 0) && (apc.getClientUid() != binderUid)) { |
| 345 | Log.e(TAG, "Forbidden operation from uid " + binderUid + " for player " + piid); |
| 346 | return false; |
| 347 | } |
| 348 | return true; |
| 349 | } |
| 350 | |
Jean-Michel Trivi | 776a399 | 2017-09-12 16:45:34 -0700 | [diff] [blame] | 351 | /** |
| 352 | * Sends new list after update of playback configurations |
| 353 | * @param iplayerReleased indicates if the change was due to a player being released |
| 354 | */ |
| 355 | private void dispatchPlaybackChange(boolean iplayerReleased) { |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 356 | synchronized (mClients) { |
| 357 | // typical use case, nobody is listening, don't do any work |
| 358 | if (mClients.isEmpty()) { |
| 359 | return; |
| 360 | } |
| 361 | } |
| 362 | if (DEBUG) { Log.v(TAG, "dispatchPlaybackChange to " + mClients.size() + " clients"); } |
| 363 | final List<AudioPlaybackConfiguration> configsSystem; |
| 364 | // list of playback configurations for "public consumption". It is only computed if there |
| 365 | // are non-system playback activity listeners. |
| 366 | final List<AudioPlaybackConfiguration> configsPublic; |
| 367 | synchronized (mPlayerLock) { |
| 368 | if (mPlayers.isEmpty()) { |
| 369 | return; |
| 370 | } |
| 371 | configsSystem = new ArrayList<AudioPlaybackConfiguration>(mPlayers.values()); |
| 372 | } |
| 373 | synchronized (mClients) { |
| 374 | // was done at beginning of method, but could have changed |
| 375 | if (mClients.isEmpty()) { |
| 376 | return; |
| 377 | } |
| 378 | configsPublic = mHasPublicClients ? anonymizeForPublicConsumption(configsSystem) : null; |
| 379 | final Iterator<PlayMonitorClient> clientIterator = mClients.iterator(); |
| 380 | while (clientIterator.hasNext()) { |
| 381 | final PlayMonitorClient pmc = clientIterator.next(); |
| 382 | try { |
| 383 | // do not spam the logs if there are problems communicating with this client |
| 384 | if (pmc.mErrorCount < PlayMonitorClient.MAX_ERRORS) { |
| 385 | if (pmc.mIsPrivileged) { |
Jean-Michel Trivi | 776a399 | 2017-09-12 16:45:34 -0700 | [diff] [blame] | 386 | pmc.mDispatcherCb.dispatchPlaybackConfigChange(configsSystem, |
| 387 | iplayerReleased); |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 388 | } else { |
Jean-Michel Trivi | 776a399 | 2017-09-12 16:45:34 -0700 | [diff] [blame] | 389 | // non-system clients don't have the control interface IPlayer, so |
| 390 | // they don't need to flush commands when a player was released |
| 391 | pmc.mDispatcherCb.dispatchPlaybackConfigChange(configsPublic, false); |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 392 | } |
| 393 | } |
| 394 | } catch (RemoteException e) { |
| 395 | pmc.mErrorCount++; |
| 396 | Log.e(TAG, "Error (" + pmc.mErrorCount + |
| 397 | ") trying to dispatch playback config change to " + pmc, e); |
| 398 | } |
| 399 | } |
| 400 | } |
| 401 | } |
| 402 | |
| 403 | private ArrayList<AudioPlaybackConfiguration> anonymizeForPublicConsumption( |
| 404 | List<AudioPlaybackConfiguration> sysConfigs) { |
| 405 | ArrayList<AudioPlaybackConfiguration> publicConfigs = |
| 406 | new ArrayList<AudioPlaybackConfiguration>(); |
Jean-Michel Trivi | 99489cc | 2017-01-25 19:08:49 -0800 | [diff] [blame] | 407 | // only add active anonymized configurations, |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 408 | for (AudioPlaybackConfiguration config : sysConfigs) { |
| 409 | if (config.isActive()) { |
| 410 | publicConfigs.add(AudioPlaybackConfiguration.anonymizedCopy(config)); |
| 411 | } |
| 412 | } |
| 413 | return publicConfigs; |
| 414 | } |
| 415 | |
Jean-Michel Trivi | 99489cc | 2017-01-25 19:08:49 -0800 | [diff] [blame] | 416 | |
| 417 | //================================================================= |
| 418 | // PlayerFocusEnforcer implementation |
Jean-Michel Trivi | 62b8634 | 2017-02-04 15:33:47 -0800 | [diff] [blame] | 419 | private final ArrayList<Integer> mMutedPlayers = new ArrayList<Integer>(); |
Jean-Michel Trivi | cb84fc0 | 2017-05-03 12:16:17 -0700 | [diff] [blame] | 420 | |
| 421 | private final DuckingManager mDuckingManager = new DuckingManager(); |
Jean-Michel Trivi | 99489cc | 2017-01-25 19:08:49 -0800 | [diff] [blame] | 422 | |
| 423 | @Override |
Jean-Michel Trivi | 9228af6 | 2018-01-05 17:06:17 -0800 | [diff] [blame] | 424 | public boolean duckPlayers(FocusRequester winner, FocusRequester loser, boolean forceDuck) { |
Jean-Michel Trivi | 99489cc | 2017-01-25 19:08:49 -0800 | [diff] [blame] | 425 | if (DEBUG) { |
| 426 | Log.v(TAG, String.format("duckPlayers: uids winner=%d loser=%d", |
Jean-Michel Trivi | cafed63 | 2017-04-03 18:49:45 -0700 | [diff] [blame] | 427 | winner.getClientUid(), loser.getClientUid())); |
| 428 | } |
Jean-Michel Trivi | 99489cc | 2017-01-25 19:08:49 -0800 | [diff] [blame] | 429 | synchronized (mPlayerLock) { |
| 430 | if (mPlayers.isEmpty()) { |
| 431 | return true; |
| 432 | } |
Jean-Michel Trivi | cb84fc0 | 2017-05-03 12:16:17 -0700 | [diff] [blame] | 433 | // check if this UID needs to be ducked (return false if not), and gather list of |
| 434 | // eligible players to duck |
| 435 | final Iterator<AudioPlaybackConfiguration> apcIterator = mPlayers.values().iterator(); |
| 436 | final ArrayList<AudioPlaybackConfiguration> apcsToDuck = |
| 437 | new ArrayList<AudioPlaybackConfiguration>(); |
| 438 | while (apcIterator.hasNext()) { |
| 439 | final AudioPlaybackConfiguration apc = apcIterator.next(); |
Jean-Michel Trivi | 99489cc | 2017-01-25 19:08:49 -0800 | [diff] [blame] | 440 | if (!winner.hasSameUid(apc.getClientUid()) |
| 441 | && loser.hasSameUid(apc.getClientUid()) |
| 442 | && apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) |
| 443 | { |
Jean-Michel Trivi | 9228af6 | 2018-01-05 17:06:17 -0800 | [diff] [blame] | 444 | if (!forceDuck && (apc.getAudioAttributes().getContentType() == |
| 445 | AudioAttributes.CONTENT_TYPE_SPEECH)) { |
Jean-Michel Trivi | 99489cc | 2017-01-25 19:08:49 -0800 | [diff] [blame] | 446 | // the player is speaking, ducking will make the speech unintelligible |
| 447 | // so let the app handle it instead |
Jean-Michel Trivi | 087b672 | 2017-05-10 13:05:06 -0700 | [diff] [blame] | 448 | Log.v(TAG, "not ducking player " + apc.getPlayerInterfaceId() |
| 449 | + " uid:" + apc.getClientUid() + " pid:" + apc.getClientPid() |
| 450 | + " - SPEECH"); |
Jean-Michel Trivi | 99489cc | 2017-01-25 19:08:49 -0800 | [diff] [blame] | 451 | return false; |
Jean-Michel Trivi | 83271bd | 2017-06-23 18:28:41 -0700 | [diff] [blame] | 452 | } else if (ArrayUtils.contains(UNDUCKABLE_PLAYER_TYPES, apc.getPlayerType())) { |
Jean-Michel Trivi | 087b672 | 2017-05-10 13:05:06 -0700 | [diff] [blame] | 453 | Log.v(TAG, "not ducking player " + apc.getPlayerInterfaceId() |
| 454 | + " uid:" + apc.getClientUid() + " pid:" + apc.getClientPid() |
Jean-Michel Trivi | 83271bd | 2017-06-23 18:28:41 -0700 | [diff] [blame] | 455 | + " due to type:" |
| 456 | + AudioPlaybackConfiguration.toLogFriendlyPlayerType( |
| 457 | apc.getPlayerType())); |
Jean-Michel Trivi | dce82ab | 2017-02-07 16:02:33 -0800 | [diff] [blame] | 458 | return false; |
Jean-Michel Trivi | 99489cc | 2017-01-25 19:08:49 -0800 | [diff] [blame] | 459 | } |
Jean-Michel Trivi | cb84fc0 | 2017-05-03 12:16:17 -0700 | [diff] [blame] | 460 | apcsToDuck.add(apc); |
Jean-Michel Trivi | 99489cc | 2017-01-25 19:08:49 -0800 | [diff] [blame] | 461 | } |
| 462 | } |
Jean-Michel Trivi | cb84fc0 | 2017-05-03 12:16:17 -0700 | [diff] [blame] | 463 | // add the players eligible for ducking to the list, and duck them |
| 464 | // (if apcsToDuck is empty, this will at least mark this uid as ducked, so when |
| 465 | // players of the same uid start, they will be ducked by DuckingManager.checkDuck()) |
| 466 | mDuckingManager.duckUid(loser.getClientUid(), apcsToDuck); |
Jean-Michel Trivi | 99489cc | 2017-01-25 19:08:49 -0800 | [diff] [blame] | 467 | } |
| 468 | return true; |
| 469 | } |
| 470 | |
| 471 | @Override |
| 472 | public void unduckPlayers(FocusRequester winner) { |
| 473 | if (DEBUG) { Log.v(TAG, "unduckPlayers: uids winner=" + winner.getClientUid()); } |
| 474 | synchronized (mPlayerLock) { |
Jean-Michel Trivi | cb84fc0 | 2017-05-03 12:16:17 -0700 | [diff] [blame] | 475 | mDuckingManager.unduckUid(winner.getClientUid(), mPlayers); |
Jean-Michel Trivi | 99489cc | 2017-01-25 19:08:49 -0800 | [diff] [blame] | 476 | } |
| 477 | } |
| 478 | |
Jean-Michel Trivi | 62b8634 | 2017-02-04 15:33:47 -0800 | [diff] [blame] | 479 | @Override |
| 480 | public void mutePlayersForCall(int[] usagesToMute) { |
| 481 | if (DEBUG) { |
| 482 | String log = new String("mutePlayersForCall: usages="); |
| 483 | for (int usage : usagesToMute) { log += " " + usage; } |
| 484 | Log.v(TAG, log); |
| 485 | } |
| 486 | synchronized (mPlayerLock) { |
| 487 | final Set<Integer> piidSet = mPlayers.keySet(); |
| 488 | final Iterator<Integer> piidIterator = piidSet.iterator(); |
| 489 | // find which players to mute |
| 490 | while (piidIterator.hasNext()) { |
| 491 | final Integer piid = piidIterator.next(); |
| 492 | final AudioPlaybackConfiguration apc = mPlayers.get(piid); |
Jean-Michel Trivi | bd39cfa | 2017-02-17 17:23:36 -0800 | [diff] [blame] | 493 | if (apc == null) { |
| 494 | continue; |
| 495 | } |
Jean-Michel Trivi | 62b8634 | 2017-02-04 15:33:47 -0800 | [diff] [blame] | 496 | final int playerUsage = apc.getAudioAttributes().getUsage(); |
| 497 | boolean mute = false; |
| 498 | for (int usageToMute : usagesToMute) { |
| 499 | if (playerUsage == usageToMute) { |
| 500 | mute = true; |
| 501 | break; |
| 502 | } |
| 503 | } |
| 504 | if (mute) { |
| 505 | try { |
Jean-Michel Trivi | 74a5596 | 2017-10-02 17:46:18 -0700 | [diff] [blame] | 506 | sEventLogger.log((new AudioEventLogger.StringEvent("call: muting piid:" |
| 507 | + piid + " uid:" + apc.getClientUid())).printLog(TAG)); |
Jean-Michel Trivi | 62b8634 | 2017-02-04 15:33:47 -0800 | [diff] [blame] | 508 | apc.getPlayerProxy().setVolume(0.0f); |
Jean-Michel Trivi | cafed63 | 2017-04-03 18:49:45 -0700 | [diff] [blame] | 509 | mMutedPlayers.add(new Integer(piid)); |
Jean-Michel Trivi | 62b8634 | 2017-02-04 15:33:47 -0800 | [diff] [blame] | 510 | } catch (Exception e) { |
Jean-Michel Trivi | 952f234 | 2017-03-06 16:16:56 -0800 | [diff] [blame] | 511 | Log.e(TAG, "call: error muting player " + piid, e); |
Jean-Michel Trivi | 62b8634 | 2017-02-04 15:33:47 -0800 | [diff] [blame] | 512 | } |
| 513 | } |
| 514 | } |
| 515 | } |
| 516 | } |
| 517 | |
| 518 | @Override |
| 519 | public void unmutePlayersForCall() { |
| 520 | if (DEBUG) { |
| 521 | Log.v(TAG, "unmutePlayersForCall()"); |
| 522 | } |
| 523 | synchronized (mPlayerLock) { |
| 524 | if (mMutedPlayers.isEmpty()) { |
| 525 | return; |
| 526 | } |
| 527 | for (int piid : mMutedPlayers) { |
| 528 | final AudioPlaybackConfiguration apc = mPlayers.get(piid); |
| 529 | if (apc != null) { |
| 530 | try { |
Jean-Michel Trivi | 74a5596 | 2017-10-02 17:46:18 -0700 | [diff] [blame] | 531 | sEventLogger.log(new AudioEventLogger.StringEvent("call: unmuting piid:" |
| 532 | + piid).printLog(TAG)); |
Jean-Michel Trivi | 62b8634 | 2017-02-04 15:33:47 -0800 | [diff] [blame] | 533 | apc.getPlayerProxy().setVolume(1.0f); |
Jean-Michel Trivi | 62b8634 | 2017-02-04 15:33:47 -0800 | [diff] [blame] | 534 | } catch (Exception e) { |
Jean-Michel Trivi | 087b672 | 2017-05-10 13:05:06 -0700 | [diff] [blame] | 535 | Log.e(TAG, "call: error unmuting player " + piid + " uid:" |
| 536 | + apc.getClientUid(), e); |
Jean-Michel Trivi | 62b8634 | 2017-02-04 15:33:47 -0800 | [diff] [blame] | 537 | } |
| 538 | } |
| 539 | } |
Jean-Michel Trivi | 579c511 | 2017-02-10 09:47:30 -0800 | [diff] [blame] | 540 | mMutedPlayers.clear(); |
Jean-Michel Trivi | 62b8634 | 2017-02-04 15:33:47 -0800 | [diff] [blame] | 541 | } |
| 542 | } |
| 543 | |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 544 | //================================================================= |
| 545 | // Track playback activity listeners |
| 546 | |
| 547 | void registerPlaybackCallback(IPlaybackConfigDispatcher pcdb, boolean isPrivileged) { |
| 548 | if (pcdb == null) { |
| 549 | return; |
| 550 | } |
| 551 | synchronized(mClients) { |
| 552 | final PlayMonitorClient pmc = new PlayMonitorClient(pcdb, isPrivileged); |
| 553 | if (pmc.init()) { |
| 554 | if (!isPrivileged) { |
| 555 | mHasPublicClients = true; |
| 556 | } |
| 557 | mClients.add(pmc); |
| 558 | } |
| 559 | } |
| 560 | } |
| 561 | |
| 562 | void unregisterPlaybackCallback(IPlaybackConfigDispatcher pcdb) { |
| 563 | if (pcdb == null) { |
| 564 | return; |
| 565 | } |
| 566 | synchronized(mClients) { |
| 567 | final Iterator<PlayMonitorClient> clientIterator = mClients.iterator(); |
| 568 | boolean hasPublicClients = false; |
| 569 | // iterate over the clients to remove the dispatcher to remove, and reevaluate at |
| 570 | // the same time if we still have a public client. |
| 571 | while (clientIterator.hasNext()) { |
| 572 | PlayMonitorClient pmc = clientIterator.next(); |
| 573 | if (pcdb.equals(pmc.mDispatcherCb)) { |
| 574 | pmc.release(); |
| 575 | clientIterator.remove(); |
| 576 | } else { |
| 577 | if (!pmc.mIsPrivileged) { |
| 578 | hasPublicClients = true; |
| 579 | } |
| 580 | } |
| 581 | } |
| 582 | mHasPublicClients = hasPublicClients; |
| 583 | } |
| 584 | } |
| 585 | |
Jean-Michel Trivi | 44a8f53 | 2017-01-02 14:36:43 -0800 | [diff] [blame] | 586 | List<AudioPlaybackConfiguration> getActivePlaybackConfigurations(boolean isPrivileged) { |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 587 | synchronized(mPlayers) { |
Jean-Michel Trivi | 44a8f53 | 2017-01-02 14:36:43 -0800 | [diff] [blame] | 588 | if (isPrivileged) { |
| 589 | return new ArrayList<AudioPlaybackConfiguration>(mPlayers.values()); |
| 590 | } else { |
| 591 | final List<AudioPlaybackConfiguration> configsPublic; |
| 592 | synchronized (mPlayerLock) { |
| 593 | configsPublic = anonymizeForPublicConsumption( |
| 594 | new ArrayList<AudioPlaybackConfiguration>(mPlayers.values())); |
| 595 | } |
| 596 | return configsPublic; |
| 597 | } |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 598 | } |
| 599 | } |
| 600 | |
| 601 | |
| 602 | /** |
| 603 | * Inner class to track clients that want to be notified of playback updates |
| 604 | */ |
Jean-Michel Trivi | c2769ab | 2017-04-24 11:59:29 -0700 | [diff] [blame] | 605 | private static final class PlayMonitorClient implements IBinder.DeathRecipient { |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 606 | |
| 607 | // can afford to be static because only one PlaybackActivityMonitor ever instantiated |
Jean-Michel Trivi | 9dc22c2 | 2017-01-05 18:06:03 -0800 | [diff] [blame] | 608 | static PlaybackActivityMonitor sListenerDeathMonitor; |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 609 | |
| 610 | final IPlaybackConfigDispatcher mDispatcherCb; |
| 611 | final boolean mIsPrivileged; |
| 612 | |
| 613 | int mErrorCount = 0; |
| 614 | // number of errors after which we don't update this client anymore to not spam the logs |
| 615 | static final int MAX_ERRORS = 5; |
| 616 | |
| 617 | PlayMonitorClient(IPlaybackConfigDispatcher pcdb, boolean isPrivileged) { |
| 618 | mDispatcherCb = pcdb; |
| 619 | mIsPrivileged = isPrivileged; |
| 620 | } |
| 621 | |
| 622 | public void binderDied() { |
| 623 | Log.w(TAG, "client died"); |
Jean-Michel Trivi | 9dc22c2 | 2017-01-05 18:06:03 -0800 | [diff] [blame] | 624 | sListenerDeathMonitor.unregisterPlaybackCallback(mDispatcherCb); |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 625 | } |
| 626 | |
| 627 | boolean init() { |
| 628 | try { |
| 629 | mDispatcherCb.asBinder().linkToDeath(this, 0); |
| 630 | return true; |
| 631 | } catch (RemoteException e) { |
| 632 | Log.w(TAG, "Could not link to client death", e); |
| 633 | return false; |
| 634 | } |
| 635 | } |
| 636 | |
| 637 | void release() { |
| 638 | mDispatcherCb.asBinder().unlinkToDeath(this, 0); |
| 639 | } |
| 640 | } |
Jean-Michel Trivi | cb84fc0 | 2017-05-03 12:16:17 -0700 | [diff] [blame] | 641 | |
| 642 | //================================================================= |
| 643 | // Class to handle ducking related operations for a given UID |
| 644 | private static final class DuckingManager { |
| 645 | private final HashMap<Integer, DuckedApp> mDuckers = new HashMap<Integer, DuckedApp>(); |
| 646 | |
Jean-Michel Trivi | 2e48fb5 | 2017-05-16 17:59:54 -0700 | [diff] [blame] | 647 | synchronized void duckUid(int uid, ArrayList<AudioPlaybackConfiguration> apcsToDuck) { |
| 648 | if (DEBUG) { Log.v(TAG, "DuckingManager: duckUid() uid:"+ uid); } |
Jean-Michel Trivi | cb84fc0 | 2017-05-03 12:16:17 -0700 | [diff] [blame] | 649 | if (!mDuckers.containsKey(uid)) { |
| 650 | mDuckers.put(uid, new DuckedApp(uid)); |
| 651 | } |
| 652 | final DuckedApp da = mDuckers.get(uid); |
| 653 | for (AudioPlaybackConfiguration apc : apcsToDuck) { |
Jean-Michel Trivi | 2e48fb5 | 2017-05-16 17:59:54 -0700 | [diff] [blame] | 654 | da.addDuck(apc, false /*skipRamp*/); |
Jean-Michel Trivi | cb84fc0 | 2017-05-03 12:16:17 -0700 | [diff] [blame] | 655 | } |
| 656 | } |
| 657 | |
Jean-Michel Trivi | 2e48fb5 | 2017-05-16 17:59:54 -0700 | [diff] [blame] | 658 | synchronized void unduckUid(int uid, HashMap<Integer, AudioPlaybackConfiguration> players) { |
| 659 | if (DEBUG) { Log.v(TAG, "DuckingManager: unduckUid() uid:"+ uid); } |
Jean-Michel Trivi | cb84fc0 | 2017-05-03 12:16:17 -0700 | [diff] [blame] | 660 | final DuckedApp da = mDuckers.remove(uid); |
| 661 | if (da == null) { |
| 662 | return; |
| 663 | } |
| 664 | da.removeUnduckAll(players); |
| 665 | } |
| 666 | |
| 667 | // pre-condition: apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED |
Jean-Michel Trivi | 2e48fb5 | 2017-05-16 17:59:54 -0700 | [diff] [blame] | 668 | synchronized void checkDuck(@NonNull AudioPlaybackConfiguration apc) { |
| 669 | if (DEBUG) { Log.v(TAG, "DuckingManager: checkDuck() player piid:" |
| 670 | + apc.getPlayerInterfaceId()+ " uid:"+ apc.getClientUid()); } |
Jean-Michel Trivi | cb84fc0 | 2017-05-03 12:16:17 -0700 | [diff] [blame] | 671 | final DuckedApp da = mDuckers.get(apc.getClientUid()); |
| 672 | if (da == null) { |
| 673 | return; |
| 674 | } |
Jean-Michel Trivi | 2e48fb5 | 2017-05-16 17:59:54 -0700 | [diff] [blame] | 675 | da.addDuck(apc, true /*skipRamp*/); |
Jean-Michel Trivi | cb84fc0 | 2017-05-03 12:16:17 -0700 | [diff] [blame] | 676 | } |
| 677 | |
Jean-Michel Trivi | 2e48fb5 | 2017-05-16 17:59:54 -0700 | [diff] [blame] | 678 | synchronized void dump(PrintWriter pw) { |
Jean-Michel Trivi | cb84fc0 | 2017-05-03 12:16:17 -0700 | [diff] [blame] | 679 | for (DuckedApp da : mDuckers.values()) { |
| 680 | da.dump(pw); |
| 681 | } |
| 682 | } |
| 683 | |
Jean-Michel Trivi | 2e48fb5 | 2017-05-16 17:59:54 -0700 | [diff] [blame] | 684 | synchronized void removeReleased(@NonNull AudioPlaybackConfiguration apc) { |
| 685 | final int uid = apc.getClientUid(); |
| 686 | if (DEBUG) { Log.v(TAG, "DuckingManager: removedReleased() player piid: " |
| 687 | + apc.getPlayerInterfaceId() + " uid:" + uid); } |
| 688 | final DuckedApp da = mDuckers.get(uid); |
Jean-Michel Trivi | cb84fc0 | 2017-05-03 12:16:17 -0700 | [diff] [blame] | 689 | if (da == null) { |
| 690 | return; |
| 691 | } |
| 692 | da.removeReleased(apc); |
| 693 | } |
| 694 | |
| 695 | private static final class DuckedApp { |
| 696 | private final int mUid; |
| 697 | private final ArrayList<Integer> mDuckedPlayers = new ArrayList<Integer>(); |
| 698 | |
| 699 | DuckedApp(int uid) { |
| 700 | mUid = uid; |
| 701 | } |
| 702 | |
| 703 | void dump(PrintWriter pw) { |
| 704 | pw.print("\t uid:" + mUid + " piids:"); |
| 705 | for (int piid : mDuckedPlayers) { |
| 706 | pw.print(" " + piid); |
| 707 | } |
| 708 | pw.println(""); |
| 709 | } |
| 710 | |
| 711 | // pre-conditions: |
| 712 | // * apc != null |
| 713 | // * apc.getPlayerState() == AudioPlaybackConfiguration.PLAYER_STATE_STARTED |
Jean-Michel Trivi | 2e48fb5 | 2017-05-16 17:59:54 -0700 | [diff] [blame] | 714 | void addDuck(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp) { |
Jean-Michel Trivi | cb84fc0 | 2017-05-03 12:16:17 -0700 | [diff] [blame] | 715 | final int piid = new Integer(apc.getPlayerInterfaceId()); |
| 716 | if (mDuckedPlayers.contains(piid)) { |
Jean-Michel Trivi | 2e48fb5 | 2017-05-16 17:59:54 -0700 | [diff] [blame] | 717 | if (DEBUG) { Log.v(TAG, "player piid:" + piid + " already ducked"); } |
Jean-Michel Trivi | cb84fc0 | 2017-05-03 12:16:17 -0700 | [diff] [blame] | 718 | return; |
| 719 | } |
| 720 | try { |
Jean-Michel Trivi | 74a5596 | 2017-10-02 17:46:18 -0700 | [diff] [blame] | 721 | sEventLogger.log((new DuckEvent(apc, skipRamp)).printLog(TAG)); |
Jean-Michel Trivi | cb84fc0 | 2017-05-03 12:16:17 -0700 | [diff] [blame] | 722 | apc.getPlayerProxy().applyVolumeShaper( |
| 723 | DUCK_VSHAPE, |
Jean-Michel Trivi | 2e48fb5 | 2017-05-16 17:59:54 -0700 | [diff] [blame] | 724 | skipRamp ? PLAY_SKIP_RAMP : PLAY_CREATE_IF_NEEDED); |
Jean-Michel Trivi | cb84fc0 | 2017-05-03 12:16:17 -0700 | [diff] [blame] | 725 | mDuckedPlayers.add(piid); |
| 726 | } catch (Exception e) { |
Jean-Michel Trivi | 2e48fb5 | 2017-05-16 17:59:54 -0700 | [diff] [blame] | 727 | Log.e(TAG, "Error ducking player piid:" + piid + " uid:" + mUid, e); |
Jean-Michel Trivi | cb84fc0 | 2017-05-03 12:16:17 -0700 | [diff] [blame] | 728 | } |
| 729 | } |
| 730 | |
| 731 | void removeUnduckAll(HashMap<Integer, AudioPlaybackConfiguration> players) { |
| 732 | for (int piid : mDuckedPlayers) { |
| 733 | final AudioPlaybackConfiguration apc = players.get(piid); |
| 734 | if (apc != null) { |
| 735 | try { |
Jean-Michel Trivi | 74a5596 | 2017-10-02 17:46:18 -0700 | [diff] [blame] | 736 | sEventLogger.log((new AudioEventLogger.StringEvent("unducking piid:" |
| 737 | + piid)).printLog(TAG)); |
Jean-Michel Trivi | cb84fc0 | 2017-05-03 12:16:17 -0700 | [diff] [blame] | 738 | apc.getPlayerProxy().applyVolumeShaper( |
| 739 | DUCK_ID, |
| 740 | VolumeShaper.Operation.REVERSE); |
| 741 | } catch (Exception e) { |
Jean-Michel Trivi | 2e48fb5 | 2017-05-16 17:59:54 -0700 | [diff] [blame] | 742 | Log.e(TAG, "Error unducking player piid:" + piid + " uid:" + mUid, e); |
Jean-Michel Trivi | cb84fc0 | 2017-05-03 12:16:17 -0700 | [diff] [blame] | 743 | } |
| 744 | } else { |
| 745 | // this piid was in the list of ducked players, but wasn't found |
| 746 | if (DEBUG) { |
Jean-Michel Trivi | 2e48fb5 | 2017-05-16 17:59:54 -0700 | [diff] [blame] | 747 | Log.v(TAG, "Error unducking player piid:" + piid |
| 748 | + ", player not found for uid " + mUid); |
Jean-Michel Trivi | cb84fc0 | 2017-05-03 12:16:17 -0700 | [diff] [blame] | 749 | } |
| 750 | } |
| 751 | } |
| 752 | mDuckedPlayers.clear(); |
| 753 | } |
| 754 | |
| 755 | void removeReleased(@NonNull AudioPlaybackConfiguration apc) { |
| 756 | mDuckedPlayers.remove(new Integer(apc.getPlayerInterfaceId())); |
| 757 | } |
| 758 | } |
| 759 | } |
Jean-Michel Trivi | 011f39e | 2017-08-19 18:08:06 -0700 | [diff] [blame] | 760 | |
| 761 | //================================================================= |
| 762 | // For logging |
| 763 | private final static class PlayerEvent extends AudioEventLogger.Event { |
| 764 | // only keeping the player interface ID as it uniquely identifies the player in the event |
| 765 | final int mPlayerIId; |
| 766 | final int mState; |
| 767 | |
| 768 | PlayerEvent(int piid, int state) { |
| 769 | mPlayerIId = piid; |
| 770 | mState = state; |
| 771 | } |
| 772 | |
| 773 | @Override |
| 774 | public String eventToString() { |
Jean-Michel Trivi | 3120059d | 2017-08-28 12:40:55 -0700 | [diff] [blame] | 775 | return new StringBuilder("player piid:").append(mPlayerIId).append(" state:") |
| 776 | .append(AudioPlaybackConfiguration.toLogFriendlyPlayerState(mState)).toString(); |
| 777 | } |
| 778 | } |
| 779 | |
| 780 | private final static class PlayerOpPlayAudioEvent extends AudioEventLogger.Event { |
| 781 | // only keeping the player interface ID as it uniquely identifies the player in the event |
| 782 | final int mPlayerIId; |
| 783 | final boolean mHasOp; |
| 784 | final int mUid; |
| 785 | |
| 786 | PlayerOpPlayAudioEvent(int piid, boolean hasOp, int uid) { |
| 787 | mPlayerIId = piid; |
| 788 | mHasOp = hasOp; |
| 789 | mUid = uid; |
| 790 | } |
| 791 | |
| 792 | @Override |
| 793 | public String eventToString() { |
| 794 | return new StringBuilder("player piid:").append(mPlayerIId) |
| 795 | .append(" has OP_PLAY_AUDIO:").append(mHasOp) |
| 796 | .append(" in uid:").append(mUid).toString(); |
Jean-Michel Trivi | 011f39e | 2017-08-19 18:08:06 -0700 | [diff] [blame] | 797 | } |
| 798 | } |
| 799 | |
| 800 | private final static class NewPlayerEvent extends AudioEventLogger.Event { |
| 801 | private final int mPlayerIId; |
| 802 | private final int mPlayerType; |
| 803 | private final int mClientUid; |
| 804 | private final int mClientPid; |
| 805 | private final AudioAttributes mPlayerAttr; |
| 806 | |
| 807 | NewPlayerEvent(AudioPlaybackConfiguration apc) { |
| 808 | mPlayerIId = apc.getPlayerInterfaceId(); |
| 809 | mPlayerType = apc.getPlayerType(); |
| 810 | mClientUid = apc.getClientUid(); |
| 811 | mClientPid = apc.getClientPid(); |
| 812 | mPlayerAttr = apc.getAudioAttributes(); |
| 813 | } |
| 814 | |
| 815 | @Override |
| 816 | public String eventToString() { |
| 817 | return new String("new player piid:" + mPlayerIId + " uid/pid:" + mClientUid + "/" |
| 818 | + mClientPid + " type:" |
| 819 | + AudioPlaybackConfiguration.toLogFriendlyPlayerType(mPlayerType) |
| 820 | + " attr:" + mPlayerAttr); |
| 821 | } |
| 822 | } |
| 823 | |
Jean-Michel Trivi | 74a5596 | 2017-10-02 17:46:18 -0700 | [diff] [blame] | 824 | private static final class DuckEvent extends AudioEventLogger.Event { |
| 825 | private final int mPlayerIId; |
| 826 | private final boolean mSkipRamp; |
| 827 | private final int mClientUid; |
| 828 | private final int mClientPid; |
| 829 | |
| 830 | DuckEvent(@NonNull AudioPlaybackConfiguration apc, boolean skipRamp) { |
| 831 | mPlayerIId = apc.getPlayerInterfaceId(); |
| 832 | mSkipRamp = skipRamp; |
| 833 | mClientUid = apc.getClientUid(); |
| 834 | mClientPid = apc.getClientPid(); |
| 835 | } |
| 836 | |
| 837 | @Override |
| 838 | public String eventToString() { |
| 839 | return new StringBuilder("ducking player piid:").append(mPlayerIId) |
| 840 | .append(" uid/pid:").append(mClientUid).append("/").append(mClientPid) |
| 841 | .append(" skip ramp:").append(mSkipRamp).toString(); |
| 842 | } |
| 843 | } |
| 844 | |
| 845 | private static final class AudioAttrEvent extends AudioEventLogger.Event { |
Jean-Michel Trivi | 011f39e | 2017-08-19 18:08:06 -0700 | [diff] [blame] | 846 | private final int mPlayerIId; |
| 847 | private final AudioAttributes mPlayerAttr; |
| 848 | |
| 849 | AudioAttrEvent(int piid, AudioAttributes attr) { |
| 850 | mPlayerIId = piid; |
| 851 | mPlayerAttr = attr; |
| 852 | } |
| 853 | |
| 854 | @Override |
| 855 | public String eventToString() { |
| 856 | return new String("player piid:" + mPlayerIId + " new AudioAttributes:" + mPlayerAttr); |
| 857 | } |
| 858 | } |
| 859 | |
Jean-Michel Trivi | 74a5596 | 2017-10-02 17:46:18 -0700 | [diff] [blame] | 860 | private static final AudioEventLogger sEventLogger = new AudioEventLogger(100, |
Jean-Michel Trivi | 011f39e | 2017-08-19 18:08:06 -0700 | [diff] [blame] | 861 | "playback activity as reported through PlayerBase"); |
Jean-Michel Trivi | 292a6a4 | 2016-12-01 08:32:15 -0800 | [diff] [blame] | 862 | } |