Jean-Michel Trivi | d3c71f0 | 2015-12-07 11:59:31 -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 | |
Jean-Michel Trivi | 66ffacf | 2017-02-04 17:25:31 -0800 | [diff] [blame] | 19 | import android.content.Context; |
| 20 | import android.content.pm.PackageManager; |
Jean-Michel Trivi | 33fd816 | 2016-02-22 10:52:41 -0800 | [diff] [blame] | 21 | import android.media.AudioFormat; |
Jean-Michel Trivi | d3c71f0 | 2015-12-07 11:59:31 -0800 | [diff] [blame] | 22 | import android.media.AudioManager; |
Jean-Michel Trivi | 598c0c9 | 2016-03-07 14:13:03 -0800 | [diff] [blame] | 23 | import android.media.AudioRecordingConfiguration; |
Jean-Michel Trivi | d3c71f0 | 2015-12-07 11:59:31 -0800 | [diff] [blame] | 24 | import android.media.AudioSystem; |
| 25 | import android.media.IRecordingConfigDispatcher; |
Jean-Michel Trivi | dd2772a | 2016-02-17 12:30:52 -0800 | [diff] [blame] | 26 | import android.media.MediaRecorder; |
Eric Laurent | c7a0cdf | 2018-11-30 12:16:52 -0800 | [diff] [blame^] | 27 | import android.media.audiofx.AudioEffect; |
Jean-Michel Trivi | d3c71f0 | 2015-12-07 11:59:31 -0800 | [diff] [blame] | 28 | import android.os.IBinder; |
| 29 | import android.os.RemoteException; |
| 30 | import android.util.Log; |
| 31 | |
Jean-Michel Trivi | 66ffacf | 2017-02-04 17:25:31 -0800 | [diff] [blame] | 32 | import java.io.PrintWriter; |
| 33 | import java.text.DateFormat; |
Jean-Michel Trivi | d3c71f0 | 2015-12-07 11:59:31 -0800 | [diff] [blame] | 34 | import java.util.ArrayList; |
Jean-Michel Trivi | 66ffacf | 2017-02-04 17:25:31 -0800 | [diff] [blame] | 35 | import java.util.Date; |
Jean-Michel Trivi | d3c71f0 | 2015-12-07 11:59:31 -0800 | [diff] [blame] | 36 | import java.util.HashMap; |
| 37 | import java.util.Iterator; |
Jean-Michel Trivi | f04fab1 | 2016-05-19 10:42:35 -0700 | [diff] [blame] | 38 | import java.util.List; |
Jean-Michel Trivi | d3c71f0 | 2015-12-07 11:59:31 -0800 | [diff] [blame] | 39 | |
| 40 | /** |
| 41 | * Class to receive and dispatch updates from AudioSystem about recording configurations. |
| 42 | */ |
| 43 | public final class RecordingActivityMonitor implements AudioSystem.AudioRecordingCallback { |
| 44 | |
| 45 | public final static String TAG = "AudioService.RecordingActivityMonitor"; |
| 46 | |
| 47 | private ArrayList<RecMonitorClient> mClients = new ArrayList<RecMonitorClient>(); |
Jean-Michel Trivi | 66ffacf | 2017-02-04 17:25:31 -0800 | [diff] [blame] | 48 | // a public client is one that needs an anonymized version of the playback configurations, we |
| 49 | // keep track of whether there is at least one to know when we need to create the list of |
| 50 | // playback configurations that do not contain uid/package name information. |
| 51 | private boolean mHasPublicClients = false; |
Jean-Michel Trivi | d3c71f0 | 2015-12-07 11:59:31 -0800 | [diff] [blame] | 52 | |
Jean-Michel Trivi | 598c0c9 | 2016-03-07 14:13:03 -0800 | [diff] [blame] | 53 | private HashMap<Integer, AudioRecordingConfiguration> mRecordConfigs = |
| 54 | new HashMap<Integer, AudioRecordingConfiguration>(); |
Jean-Michel Trivi | d3c71f0 | 2015-12-07 11:59:31 -0800 | [diff] [blame] | 55 | |
Jean-Michel Trivi | 66ffacf | 2017-02-04 17:25:31 -0800 | [diff] [blame] | 56 | private final PackageManager mPackMan; |
| 57 | |
| 58 | RecordingActivityMonitor(Context ctxt) { |
Jean-Michel Trivi | d3c71f0 | 2015-12-07 11:59:31 -0800 | [diff] [blame] | 59 | RecMonitorClient.sMonitor = this; |
Jean-Michel Trivi | 66ffacf | 2017-02-04 17:25:31 -0800 | [diff] [blame] | 60 | mPackMan = ctxt.getPackageManager(); |
Jean-Michel Trivi | d3c71f0 | 2015-12-07 11:59:31 -0800 | [diff] [blame] | 61 | } |
| 62 | |
| 63 | /** |
| 64 | * Implementation of android.media.AudioSystem.AudioRecordingCallback |
| 65 | */ |
Jean-Michel Trivi | 66ffacf | 2017-02-04 17:25:31 -0800 | [diff] [blame] | 66 | public void onRecordingConfigurationChanged(int event, int uid, int session, int source, |
Eric Laurent | c7a0cdf | 2018-11-30 12:16:52 -0800 | [diff] [blame^] | 67 | int portId, boolean silenced, int[] recordingInfo, |
| 68 | AudioEffect.Descriptor[] clientEffects, |
| 69 | AudioEffect.Descriptor[] effects, |
| 70 | int activeSource, String packName) { |
Jean-Michel Trivi | dd2772a | 2016-02-17 12:30:52 -0800 | [diff] [blame] | 71 | if (MediaRecorder.isSystemOnlyAudioSource(source)) { |
| 72 | return; |
| 73 | } |
Eric Laurent | c7a0cdf | 2018-11-30 12:16:52 -0800 | [diff] [blame^] | 74 | String clientEffectName = clientEffects.length == 0 ? "None" : clientEffects[0].name; |
| 75 | String effectName = effects.length == 0 ? "None" : effects[0].name; |
| 76 | |
Jean-Michel Trivi | 66ffacf | 2017-02-04 17:25:31 -0800 | [diff] [blame] | 77 | final List<AudioRecordingConfiguration> configsSystem = |
Eric Laurent | c7a0cdf | 2018-11-30 12:16:52 -0800 | [diff] [blame^] | 78 | updateSnapshot(event, uid, session, source, recordingInfo, |
| 79 | portId, silenced, activeSource, clientEffects, effects); |
Jean-Michel Trivi | 66ffacf | 2017-02-04 17:25:31 -0800 | [diff] [blame] | 80 | if (configsSystem != null){ |
| 81 | synchronized (mClients) { |
| 82 | // list of recording configurations for "public consumption". It is only computed if |
| 83 | // there are non-system recording activity listeners. |
| 84 | final List<AudioRecordingConfiguration> configsPublic = mHasPublicClients ? |
| 85 | anonymizeForPublicConsumption(configsSystem) : |
| 86 | new ArrayList<AudioRecordingConfiguration>(); |
Jean-Michel Trivi | 28ff76b | 2016-03-02 09:36:30 -0800 | [diff] [blame] | 87 | final Iterator<RecMonitorClient> clientIterator = mClients.iterator(); |
Jean-Michel Trivi | d3c71f0 | 2015-12-07 11:59:31 -0800 | [diff] [blame] | 88 | while (clientIterator.hasNext()) { |
Jean-Michel Trivi | 66ffacf | 2017-02-04 17:25:31 -0800 | [diff] [blame] | 89 | final RecMonitorClient rmc = clientIterator.next(); |
Jean-Michel Trivi | d3c71f0 | 2015-12-07 11:59:31 -0800 | [diff] [blame] | 90 | try { |
Jean-Michel Trivi | 66ffacf | 2017-02-04 17:25:31 -0800 | [diff] [blame] | 91 | if (rmc.mIsPrivileged) { |
| 92 | rmc.mDispatcherCb.dispatchRecordingConfigChange(configsSystem); |
| 93 | } else { |
| 94 | rmc.mDispatcherCb.dispatchRecordingConfigChange(configsPublic); |
| 95 | } |
Jean-Michel Trivi | d3c71f0 | 2015-12-07 11:59:31 -0800 | [diff] [blame] | 96 | } catch (RemoteException e) { |
| 97 | Log.w(TAG, "Could not call dispatchRecordingConfigChange() on client", e); |
| 98 | } |
| 99 | } |
| 100 | } |
| 101 | } |
| 102 | } |
| 103 | |
Jean-Michel Trivi | 66ffacf | 2017-02-04 17:25:31 -0800 | [diff] [blame] | 104 | protected void dump(PrintWriter pw) { |
| 105 | // players |
| 106 | pw.println("\nRecordActivityMonitor dump time: " |
| 107 | + DateFormat.getTimeInstance().format(new Date())); |
| 108 | synchronized(mRecordConfigs) { |
| 109 | for (AudioRecordingConfiguration conf : mRecordConfigs.values()) { |
| 110 | conf.dump(pw); |
| 111 | } |
| 112 | } |
Jean-Michel Trivi | 12a8676 | 2018-04-24 16:57:49 -0700 | [diff] [blame] | 113 | pw.println("\n"); |
| 114 | // log |
| 115 | sEventLogger.dump(pw); |
Jean-Michel Trivi | 66ffacf | 2017-02-04 17:25:31 -0800 | [diff] [blame] | 116 | } |
| 117 | |
| 118 | private ArrayList<AudioRecordingConfiguration> anonymizeForPublicConsumption( |
| 119 | List<AudioRecordingConfiguration> sysConfigs) { |
| 120 | ArrayList<AudioRecordingConfiguration> publicConfigs = |
| 121 | new ArrayList<AudioRecordingConfiguration>(); |
| 122 | // only add active anonymized configurations, |
| 123 | for (AudioRecordingConfiguration config : sysConfigs) { |
| 124 | publicConfigs.add(AudioRecordingConfiguration.anonymizedCopy(config)); |
| 125 | } |
| 126 | return publicConfigs; |
| 127 | } |
| 128 | |
Jean-Michel Trivi | d3c71f0 | 2015-12-07 11:59:31 -0800 | [diff] [blame] | 129 | void initMonitor() { |
| 130 | AudioSystem.setRecordingCallback(this); |
| 131 | } |
| 132 | |
Jean-Michel Trivi | 66ffacf | 2017-02-04 17:25:31 -0800 | [diff] [blame] | 133 | void registerRecordingCallback(IRecordingConfigDispatcher rcdb, boolean isPrivileged) { |
Jean-Michel Trivi | d3c71f0 | 2015-12-07 11:59:31 -0800 | [diff] [blame] | 134 | if (rcdb == null) { |
| 135 | return; |
| 136 | } |
Jean-Michel Trivi | 66ffacf | 2017-02-04 17:25:31 -0800 | [diff] [blame] | 137 | synchronized (mClients) { |
| 138 | final RecMonitorClient rmc = new RecMonitorClient(rcdb, isPrivileged); |
Jean-Michel Trivi | d3c71f0 | 2015-12-07 11:59:31 -0800 | [diff] [blame] | 139 | if (rmc.init()) { |
Jean-Michel Trivi | 66ffacf | 2017-02-04 17:25:31 -0800 | [diff] [blame] | 140 | if (!isPrivileged) { |
| 141 | mHasPublicClients = true; |
| 142 | } |
Jean-Michel Trivi | d3c71f0 | 2015-12-07 11:59:31 -0800 | [diff] [blame] | 143 | mClients.add(rmc); |
| 144 | } |
| 145 | } |
| 146 | } |
| 147 | |
| 148 | void unregisterRecordingCallback(IRecordingConfigDispatcher rcdb) { |
| 149 | if (rcdb == null) { |
| 150 | return; |
| 151 | } |
Jean-Michel Trivi | 66ffacf | 2017-02-04 17:25:31 -0800 | [diff] [blame] | 152 | synchronized (mClients) { |
Jean-Michel Trivi | d3c71f0 | 2015-12-07 11:59:31 -0800 | [diff] [blame] | 153 | final Iterator<RecMonitorClient> clientIterator = mClients.iterator(); |
Jean-Michel Trivi | 66ffacf | 2017-02-04 17:25:31 -0800 | [diff] [blame] | 154 | boolean hasPublicClients = false; |
Jean-Michel Trivi | d3c71f0 | 2015-12-07 11:59:31 -0800 | [diff] [blame] | 155 | while (clientIterator.hasNext()) { |
| 156 | RecMonitorClient rmc = clientIterator.next(); |
| 157 | if (rcdb.equals(rmc.mDispatcherCb)) { |
| 158 | rmc.release(); |
| 159 | clientIterator.remove(); |
Jean-Michel Trivi | 66ffacf | 2017-02-04 17:25:31 -0800 | [diff] [blame] | 160 | } else { |
| 161 | if (!rmc.mIsPrivileged) { |
| 162 | hasPublicClients = true; |
| 163 | } |
Jean-Michel Trivi | d3c71f0 | 2015-12-07 11:59:31 -0800 | [diff] [blame] | 164 | } |
| 165 | } |
Jean-Michel Trivi | 66ffacf | 2017-02-04 17:25:31 -0800 | [diff] [blame] | 166 | mHasPublicClients = hasPublicClients; |
Jean-Michel Trivi | d3c71f0 | 2015-12-07 11:59:31 -0800 | [diff] [blame] | 167 | } |
| 168 | } |
| 169 | |
Jean-Michel Trivi | 66ffacf | 2017-02-04 17:25:31 -0800 | [diff] [blame] | 170 | List<AudioRecordingConfiguration> getActiveRecordingConfigurations(boolean isPrivileged) { |
Jean-Michel Trivi | d3c71f0 | 2015-12-07 11:59:31 -0800 | [diff] [blame] | 171 | synchronized(mRecordConfigs) { |
Jean-Michel Trivi | 66ffacf | 2017-02-04 17:25:31 -0800 | [diff] [blame] | 172 | if (isPrivileged) { |
| 173 | return new ArrayList<AudioRecordingConfiguration>(mRecordConfigs.values()); |
| 174 | } else { |
| 175 | final List<AudioRecordingConfiguration> configsPublic = |
| 176 | anonymizeForPublicConsumption( |
| 177 | new ArrayList<AudioRecordingConfiguration>(mRecordConfigs.values())); |
| 178 | return configsPublic; |
| 179 | } |
Jean-Michel Trivi | d3c71f0 | 2015-12-07 11:59:31 -0800 | [diff] [blame] | 180 | } |
| 181 | } |
| 182 | |
| 183 | /** |
| 184 | * Update the internal "view" of the active recording sessions |
| 185 | * @param event |
| 186 | * @param session |
| 187 | * @param source |
Jean-Michel Trivi | 33fd816 | 2016-02-22 10:52:41 -0800 | [diff] [blame] | 188 | * @param recordingFormat see |
Eric Laurent | c7a0cdf | 2018-11-30 12:16:52 -0800 | [diff] [blame^] | 189 | * {@link AudioSystem.AudioRecordingCallback#onRecordingConfigurationChanged(int, int, int,\ |
| 190 | int, int, boolean, int[], AudioEffect.Descriptor[], AudioEffect.Descriptor[], int, String)} |
Jean-Michel Trivi | 33fd816 | 2016-02-22 10:52:41 -0800 | [diff] [blame] | 191 | * for the definition of the contents of the array |
Eric Laurent | c7a0cdf | 2018-11-30 12:16:52 -0800 | [diff] [blame^] | 192 | * @param portId |
| 193 | * @param silenced |
| 194 | * @param activeSource |
| 195 | * @param clientEffects |
| 196 | * @param effects |
Jean-Michel Trivi | f04fab1 | 2016-05-19 10:42:35 -0700 | [diff] [blame] | 197 | * @return null if the list of active recording sessions has not been modified, a list |
Jean-Michel Trivi | 28ff76b | 2016-03-02 09:36:30 -0800 | [diff] [blame] | 198 | * with the current active configurations otherwise. |
Jean-Michel Trivi | d3c71f0 | 2015-12-07 11:59:31 -0800 | [diff] [blame] | 199 | */ |
Jean-Michel Trivi | 66ffacf | 2017-02-04 17:25:31 -0800 | [diff] [blame] | 200 | private List<AudioRecordingConfiguration> updateSnapshot(int event, int uid, int session, |
Eric Laurent | c7a0cdf | 2018-11-30 12:16:52 -0800 | [diff] [blame^] | 201 | int source, int[] recordingInfo, int portId, boolean silenced, int activeSource, |
| 202 | AudioEffect.Descriptor[] clientEffects, AudioEffect.Descriptor[] effects) { |
Jean-Michel Trivi | 28ff76b | 2016-03-02 09:36:30 -0800 | [diff] [blame] | 203 | final boolean configChanged; |
Jean-Michel Trivi | f04fab1 | 2016-05-19 10:42:35 -0700 | [diff] [blame] | 204 | final ArrayList<AudioRecordingConfiguration> configs; |
Jean-Michel Trivi | d3c71f0 | 2015-12-07 11:59:31 -0800 | [diff] [blame] | 205 | synchronized(mRecordConfigs) { |
| 206 | switch (event) { |
| 207 | case AudioManager.RECORD_CONFIG_EVENT_STOP: |
| 208 | // return failure if an unknown recording session stopped |
Jean-Michel Trivi | 28ff76b | 2016-03-02 09:36:30 -0800 | [diff] [blame] | 209 | configChanged = (mRecordConfigs.remove(new Integer(session)) != null); |
Jean-Michel Trivi | 12a8676 | 2018-04-24 16:57:49 -0700 | [diff] [blame] | 210 | if (configChanged) { |
| 211 | sEventLogger.log(new RecordingEvent(event, uid, session, source, null)); |
| 212 | } |
Jean-Michel Trivi | 28ff76b | 2016-03-02 09:36:30 -0800 | [diff] [blame] | 213 | break; |
Jean-Michel Trivi | d3c71f0 | 2015-12-07 11:59:31 -0800 | [diff] [blame] | 214 | case AudioManager.RECORD_CONFIG_EVENT_START: |
Jean-Michel Trivi | 33fd816 | 2016-02-22 10:52:41 -0800 | [diff] [blame] | 215 | final AudioFormat clientFormat = new AudioFormat.Builder() |
Jean-Michel Trivi | 8ab7280 | 2016-02-25 16:31:45 -0800 | [diff] [blame] | 216 | .setEncoding(recordingInfo[0]) |
Jean-Michel Trivi | 33fd816 | 2016-02-22 10:52:41 -0800 | [diff] [blame] | 217 | // FIXME this doesn't support index-based masks |
Jean-Michel Trivi | 8ab7280 | 2016-02-25 16:31:45 -0800 | [diff] [blame] | 218 | .setChannelMask(recordingInfo[1]) |
| 219 | .setSampleRate(recordingInfo[2]) |
Jean-Michel Trivi | 33fd816 | 2016-02-22 10:52:41 -0800 | [diff] [blame] | 220 | .build(); |
| 221 | final AudioFormat deviceFormat = new AudioFormat.Builder() |
Jean-Michel Trivi | 8ab7280 | 2016-02-25 16:31:45 -0800 | [diff] [blame] | 222 | .setEncoding(recordingInfo[3]) |
Jean-Michel Trivi | 33fd816 | 2016-02-22 10:52:41 -0800 | [diff] [blame] | 223 | // FIXME this doesn't support index-based masks |
Jean-Michel Trivi | 8ab7280 | 2016-02-25 16:31:45 -0800 | [diff] [blame] | 224 | .setChannelMask(recordingInfo[4]) |
| 225 | .setSampleRate(recordingInfo[5]) |
Jean-Michel Trivi | 33fd816 | 2016-02-22 10:52:41 -0800 | [diff] [blame] | 226 | .build(); |
Jean-Michel Trivi | 8ab7280 | 2016-02-25 16:31:45 -0800 | [diff] [blame] | 227 | final int patchHandle = recordingInfo[6]; |
Eric Laurent | c7a0cdf | 2018-11-30 12:16:52 -0800 | [diff] [blame^] | 228 | final Integer portIdKey = new Integer(portId); |
Jean-Michel Trivi | 66ffacf | 2017-02-04 17:25:31 -0800 | [diff] [blame] | 229 | |
| 230 | final String[] packages = mPackMan.getPackagesForUid(uid); |
| 231 | final String packageName; |
| 232 | if (packages != null && packages.length > 0) { |
| 233 | packageName = packages[0]; |
| 234 | } else { |
| 235 | packageName = ""; |
| 236 | } |
| 237 | final AudioRecordingConfiguration updatedConfig = |
| 238 | new AudioRecordingConfiguration(uid, session, source, |
Eric Laurent | c7a0cdf | 2018-11-30 12:16:52 -0800 | [diff] [blame^] | 239 | clientFormat, deviceFormat, patchHandle, packageName, |
| 240 | portId, silenced, activeSource, clientEffects, effects); |
Jean-Michel Trivi | 66ffacf | 2017-02-04 17:25:31 -0800 | [diff] [blame] | 241 | |
Eric Laurent | c7a0cdf | 2018-11-30 12:16:52 -0800 | [diff] [blame^] | 242 | if (mRecordConfigs.containsKey(portIdKey)) { |
| 243 | if (updatedConfig.equals(mRecordConfigs.get(portIdKey))) { |
Jean-Michel Trivi | 28ff76b | 2016-03-02 09:36:30 -0800 | [diff] [blame] | 244 | configChanged = false; |
Jean-Michel Trivi | 8ab7280 | 2016-02-25 16:31:45 -0800 | [diff] [blame] | 245 | } else { |
| 246 | // config exists but has been modified |
Eric Laurent | c7a0cdf | 2018-11-30 12:16:52 -0800 | [diff] [blame^] | 247 | mRecordConfigs.remove(portIdKey); |
| 248 | mRecordConfigs.put(portIdKey, updatedConfig); |
Jean-Michel Trivi | 28ff76b | 2016-03-02 09:36:30 -0800 | [diff] [blame] | 249 | configChanged = true; |
Jean-Michel Trivi | 8ab7280 | 2016-02-25 16:31:45 -0800 | [diff] [blame] | 250 | } |
| 251 | } else { |
Eric Laurent | c7a0cdf | 2018-11-30 12:16:52 -0800 | [diff] [blame^] | 252 | mRecordConfigs.put(portIdKey, updatedConfig); |
Jean-Michel Trivi | 28ff76b | 2016-03-02 09:36:30 -0800 | [diff] [blame] | 253 | configChanged = true; |
Jean-Michel Trivi | d3c71f0 | 2015-12-07 11:59:31 -0800 | [diff] [blame] | 254 | } |
Jean-Michel Trivi | 12a8676 | 2018-04-24 16:57:49 -0700 | [diff] [blame] | 255 | if (configChanged) { |
| 256 | sEventLogger.log(new RecordingEvent(event, uid, session, source, packageName)); |
| 257 | } |
Jean-Michel Trivi | 28ff76b | 2016-03-02 09:36:30 -0800 | [diff] [blame] | 258 | break; |
Jean-Michel Trivi | d3c71f0 | 2015-12-07 11:59:31 -0800 | [diff] [blame] | 259 | default: |
| 260 | Log.e(TAG, String.format("Unknown event %d for session %d, source %d", |
| 261 | event, session, source)); |
Jean-Michel Trivi | 28ff76b | 2016-03-02 09:36:30 -0800 | [diff] [blame] | 262 | configChanged = false; |
| 263 | } |
| 264 | if (configChanged) { |
Jean-Michel Trivi | f04fab1 | 2016-05-19 10:42:35 -0700 | [diff] [blame] | 265 | configs = new ArrayList<AudioRecordingConfiguration>(mRecordConfigs.values()); |
Jean-Michel Trivi | 28ff76b | 2016-03-02 09:36:30 -0800 | [diff] [blame] | 266 | } else { |
| 267 | configs = null; |
Jean-Michel Trivi | d3c71f0 | 2015-12-07 11:59:31 -0800 | [diff] [blame] | 268 | } |
| 269 | } |
Jean-Michel Trivi | 28ff76b | 2016-03-02 09:36:30 -0800 | [diff] [blame] | 270 | return configs; |
Jean-Michel Trivi | d3c71f0 | 2015-12-07 11:59:31 -0800 | [diff] [blame] | 271 | } |
| 272 | |
| 273 | /** |
| 274 | * Inner class to track clients that want to be notified of recording updates |
| 275 | */ |
| 276 | private final static class RecMonitorClient implements IBinder.DeathRecipient { |
| 277 | |
| 278 | // can afford to be static because only one RecordingActivityMonitor ever instantiated |
| 279 | static RecordingActivityMonitor sMonitor; |
| 280 | |
| 281 | final IRecordingConfigDispatcher mDispatcherCb; |
Jean-Michel Trivi | 66ffacf | 2017-02-04 17:25:31 -0800 | [diff] [blame] | 282 | final boolean mIsPrivileged; |
Jean-Michel Trivi | d3c71f0 | 2015-12-07 11:59:31 -0800 | [diff] [blame] | 283 | |
Jean-Michel Trivi | 66ffacf | 2017-02-04 17:25:31 -0800 | [diff] [blame] | 284 | RecMonitorClient(IRecordingConfigDispatcher rcdb, boolean isPrivileged) { |
Jean-Michel Trivi | d3c71f0 | 2015-12-07 11:59:31 -0800 | [diff] [blame] | 285 | mDispatcherCb = rcdb; |
Jean-Michel Trivi | 66ffacf | 2017-02-04 17:25:31 -0800 | [diff] [blame] | 286 | mIsPrivileged = isPrivileged; |
Jean-Michel Trivi | d3c71f0 | 2015-12-07 11:59:31 -0800 | [diff] [blame] | 287 | } |
| 288 | |
| 289 | public void binderDied() { |
| 290 | Log.w(TAG, "client died"); |
| 291 | sMonitor.unregisterRecordingCallback(mDispatcherCb); |
| 292 | } |
| 293 | |
| 294 | boolean init() { |
| 295 | try { |
| 296 | mDispatcherCb.asBinder().linkToDeath(this, 0); |
| 297 | return true; |
| 298 | } catch (RemoteException e) { |
| 299 | Log.w(TAG, "Could not link to client death", e); |
| 300 | return false; |
| 301 | } |
| 302 | } |
| 303 | |
| 304 | void release() { |
| 305 | mDispatcherCb.asBinder().unlinkToDeath(this, 0); |
| 306 | } |
| 307 | } |
Jean-Michel Trivi | 12a8676 | 2018-04-24 16:57:49 -0700 | [diff] [blame] | 308 | |
| 309 | /** |
| 310 | * Inner class for recording event logging |
| 311 | */ |
| 312 | private static final class RecordingEvent extends AudioEventLogger.Event { |
| 313 | private final int mRecEvent; |
| 314 | private final int mClientUid; |
| 315 | private final int mSession; |
| 316 | private final int mSource; |
| 317 | private final String mPackName; |
| 318 | |
| 319 | RecordingEvent(int event, int uid, int session, int source, String packName) { |
| 320 | mRecEvent = event; |
| 321 | mClientUid = uid; |
| 322 | mSession = session; |
| 323 | mSource = source; |
| 324 | mPackName = packName; |
| 325 | } |
| 326 | |
| 327 | @Override |
| 328 | public String eventToString() { |
| 329 | return new StringBuilder("rec ").append( |
| 330 | mRecEvent == AudioManager.RECORD_CONFIG_EVENT_START ? "start" : "stop ") |
| 331 | .append(" uid:").append(mClientUid) |
| 332 | .append(" session:").append(mSession) |
| 333 | .append(" src:").append(MediaRecorder.toLogFriendlyAudioSource(mSource)) |
| 334 | .append(mPackName == null ? "" : " pack:" + mPackName).toString(); |
| 335 | } |
| 336 | } |
| 337 | |
| 338 | private static final AudioEventLogger sEventLogger = new AudioEventLogger(50, |
| 339 | "recording activity as reported through AudioSystem.AudioRecordingCallback"); |
Jean-Michel Trivi | d3c71f0 | 2015-12-07 11:59:31 -0800 | [diff] [blame] | 340 | } |