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.content.Context; 20 import android.content.pm.PackageManager; 21 import android.media.AudioFormat; 22 import android.media.AudioManager; 23 import android.media.AudioRecordingConfiguration; 24 import android.media.AudioSystem; 25 import android.media.IRecordingConfigDispatcher; 26 import android.media.MediaRecorder; 27 import android.media.audiofx.AudioEffect; 28 import android.os.IBinder; 29 import android.os.RemoteException; 30 import android.util.Log; 31 32 import java.io.PrintWriter; 33 import java.text.DateFormat; 34 import java.util.ArrayList; 35 import java.util.Date; 36 import java.util.Iterator; 37 import java.util.List; 38 39 /** 40 * Class to receive and dispatch updates from AudioSystem about recording configurations. 41 */ 42 public final class RecordingActivityMonitor implements AudioSystem.AudioRecordingCallback { 43 44 public final static String TAG = "AudioService.RecordingActivityMonitor"; 45 46 private ArrayList<RecMonitorClient> mClients = new ArrayList<RecMonitorClient>(); 47 // a public client is one that needs an anonymized version of the playback configurations, we 48 // keep track of whether there is at least one to know when we need to create the list of 49 // playback configurations that do not contain uid/package name information. 50 private boolean mHasPublicClients = false; 51 52 static final class RecordingState { 53 private final int mRiid; 54 private final RecorderDeathHandler mDeathHandler; 55 private boolean mIsActive; 56 private AudioRecordingConfiguration mConfig; 57 RecordingState(int riid, RecorderDeathHandler handler)58 RecordingState(int riid, RecorderDeathHandler handler) { 59 mRiid = riid; 60 mDeathHandler = handler; 61 } 62 RecordingState(AudioRecordingConfiguration config)63 RecordingState(AudioRecordingConfiguration config) { 64 mRiid = AudioManager.RECORD_RIID_INVALID; 65 mDeathHandler = null; 66 mConfig = config; 67 } 68 getRiid()69 int getRiid() { 70 return mRiid; 71 } 72 getPortId()73 int getPortId() { 74 return mConfig != null ? mConfig.getClientPortId() : -1; 75 } 76 getConfig()77 AudioRecordingConfiguration getConfig() { 78 return mConfig; 79 } 80 hasDeathHandler()81 boolean hasDeathHandler() { 82 return mDeathHandler != null; 83 } 84 isActiveConfiguration()85 boolean isActiveConfiguration() { 86 return mIsActive && mConfig != null; 87 } 88 89 // returns true if status of an active recording has changed setActive(boolean active)90 boolean setActive(boolean active) { 91 if (mIsActive == active) return false; 92 mIsActive = active; 93 return mConfig != null; 94 } 95 96 // returns true if an active recording has been updated setConfig(AudioRecordingConfiguration config)97 boolean setConfig(AudioRecordingConfiguration config) { 98 if (config.equals(mConfig)) return false; 99 mConfig = config; 100 return mIsActive; 101 } 102 dump(PrintWriter pw)103 void dump(PrintWriter pw) { 104 pw.println("riid " + mRiid + "; active? " + mIsActive); 105 if (mConfig != null) { 106 mConfig.dump(pw); 107 } else { 108 pw.println(" no config"); 109 } 110 } 111 } 112 private List<RecordingState> mRecordStates = new ArrayList<RecordingState>(); 113 114 private final PackageManager mPackMan; 115 RecordingActivityMonitor(Context ctxt)116 RecordingActivityMonitor(Context ctxt) { 117 RecMonitorClient.sMonitor = this; 118 RecorderDeathHandler.sMonitor = this; 119 mPackMan = ctxt.getPackageManager(); 120 } 121 122 /** 123 * Implementation of android.media.AudioSystem.AudioRecordingCallback 124 */ onRecordingConfigurationChanged(int event, int riid, int uid, int session, int source, int portId, boolean silenced, int[] recordingInfo, AudioEffect.Descriptor[] clientEffects, AudioEffect.Descriptor[] effects, int activeSource, String packName)125 public void onRecordingConfigurationChanged(int event, int riid, int uid, int session, 126 int source, int portId, boolean silenced, 127 int[] recordingInfo, 128 AudioEffect.Descriptor[] clientEffects, 129 AudioEffect.Descriptor[] effects, 130 int activeSource, String packName) { 131 final AudioRecordingConfiguration config = createRecordingConfiguration( 132 uid, session, source, recordingInfo, 133 portId, silenced, activeSource, clientEffects, effects); 134 if (MediaRecorder.isSystemOnlyAudioSource(source)) { 135 // still want to log event, it just won't appear in recording configurations; 136 sEventLogger.log(new RecordingEvent(event, riid, config).printLog(TAG)); 137 return; 138 } 139 dispatchCallbacks(updateSnapshot(event, riid, config)); 140 } 141 142 /** 143 * Track a recorder provided by the client 144 */ trackRecorder(IBinder recorder)145 public int trackRecorder(IBinder recorder) { 146 if (recorder == null) { 147 Log.e(TAG, "trackRecorder called with null token"); 148 return AudioManager.RECORD_RIID_INVALID; 149 } 150 final int newRiid = AudioSystem.newAudioRecorderId(); 151 RecorderDeathHandler handler = new RecorderDeathHandler(newRiid, recorder); 152 if (!handler.init()) { 153 // probably means that the AudioRecord has already died 154 return AudioManager.RECORD_RIID_INVALID; 155 } 156 synchronized (mRecordStates) { 157 mRecordStates.add(new RecordingState(newRiid, handler)); 158 } 159 // a newly added record is inactive, no change in active configs is possible. 160 return newRiid; 161 } 162 163 /** 164 * Receive an event from the client about a tracked recorder 165 */ recorderEvent(int riid, int event)166 public void recorderEvent(int riid, int event) { 167 int configEvent = event == AudioManager.RECORDER_STATE_STARTED 168 ? AudioManager.RECORD_CONFIG_EVENT_START : 169 event == AudioManager.RECORDER_STATE_STOPPED 170 ? AudioManager.RECORD_CONFIG_EVENT_STOP : AudioManager.RECORD_CONFIG_EVENT_NONE; 171 if (riid == AudioManager.RECORD_RIID_INVALID 172 || configEvent == AudioManager.RECORD_CONFIG_EVENT_NONE) { 173 sEventLogger.log(new RecordingEvent(event, riid, null).printLog(TAG)); 174 return; 175 } 176 dispatchCallbacks(updateSnapshot(configEvent, riid, null)); 177 } 178 179 /** 180 * Stop tracking the recorder 181 */ releaseRecorder(int riid)182 public void releaseRecorder(int riid) { 183 dispatchCallbacks(updateSnapshot(AudioManager.RECORD_CONFIG_EVENT_RELEASE, riid, null)); 184 } 185 dispatchCallbacks(List<AudioRecordingConfiguration> configs)186 private void dispatchCallbacks(List<AudioRecordingConfiguration> configs) { 187 if (configs == null) { // null means "no changes" 188 return; 189 } 190 synchronized (mClients) { 191 // list of recording configurations for "public consumption". It is only computed if 192 // there are non-system recording activity listeners. 193 final List<AudioRecordingConfiguration> configsPublic = mHasPublicClients 194 ? anonymizeForPublicConsumption(configs) : 195 new ArrayList<AudioRecordingConfiguration>(); 196 for (RecMonitorClient rmc : mClients) { 197 try { 198 if (rmc.mIsPrivileged) { 199 rmc.mDispatcherCb.dispatchRecordingConfigChange(configs); 200 } else { 201 rmc.mDispatcherCb.dispatchRecordingConfigChange(configsPublic); 202 } 203 } catch (RemoteException e) { 204 Log.w(TAG, "Could not call dispatchRecordingConfigChange() on client", e); 205 } 206 } 207 } 208 } 209 dump(PrintWriter pw)210 protected void dump(PrintWriter pw) { 211 // recorders 212 pw.println("\nRecordActivityMonitor dump time: " 213 + DateFormat.getTimeInstance().format(new Date())); 214 synchronized (mRecordStates) { 215 for (RecordingState state : mRecordStates) { 216 state.dump(pw); 217 } 218 } 219 pw.println("\n"); 220 // log 221 sEventLogger.dump(pw); 222 } 223 anonymizeForPublicConsumption( List<AudioRecordingConfiguration> sysConfigs)224 private static ArrayList<AudioRecordingConfiguration> anonymizeForPublicConsumption( 225 List<AudioRecordingConfiguration> sysConfigs) { 226 ArrayList<AudioRecordingConfiguration> publicConfigs = 227 new ArrayList<AudioRecordingConfiguration>(); 228 // only add active anonymized configurations, 229 for (AudioRecordingConfiguration config : sysConfigs) { 230 publicConfigs.add(AudioRecordingConfiguration.anonymizedCopy(config)); 231 } 232 return publicConfigs; 233 } 234 initMonitor()235 void initMonitor() { 236 AudioSystem.setRecordingCallback(this); 237 } 238 onAudioServerDied()239 void onAudioServerDied() { 240 // Remove all RecordingState entries that do not have a death handler (that means 241 // they are tracked by the Audio Server). If there were active entries among removed, 242 // dispatch active configuration changes. 243 List<AudioRecordingConfiguration> configs = null; 244 synchronized (mRecordStates) { 245 boolean configChanged = false; 246 for (Iterator<RecordingState> it = mRecordStates.iterator(); it.hasNext(); ) { 247 RecordingState state = it.next(); 248 if (!state.hasDeathHandler()) { 249 if (state.isActiveConfiguration()) { 250 configChanged = true; 251 sEventLogger.log(new RecordingEvent( 252 AudioManager.RECORD_CONFIG_EVENT_RELEASE, 253 state.getRiid(), state.getConfig())); 254 } 255 it.remove(); 256 } 257 } 258 if (configChanged) { 259 configs = getActiveRecordingConfigurations(true /*isPrivileged*/); 260 } 261 } 262 dispatchCallbacks(configs); 263 } 264 registerRecordingCallback(IRecordingConfigDispatcher rcdb, boolean isPrivileged)265 void registerRecordingCallback(IRecordingConfigDispatcher rcdb, boolean isPrivileged) { 266 if (rcdb == null) { 267 return; 268 } 269 synchronized (mClients) { 270 final RecMonitorClient rmc = new RecMonitorClient(rcdb, isPrivileged); 271 if (rmc.init()) { 272 if (!isPrivileged) { 273 mHasPublicClients = true; 274 } 275 mClients.add(rmc); 276 } 277 } 278 } 279 unregisterRecordingCallback(IRecordingConfigDispatcher rcdb)280 void unregisterRecordingCallback(IRecordingConfigDispatcher rcdb) { 281 if (rcdb == null) { 282 return; 283 } 284 synchronized (mClients) { 285 final Iterator<RecMonitorClient> clientIterator = mClients.iterator(); 286 boolean hasPublicClients = false; 287 while (clientIterator.hasNext()) { 288 RecMonitorClient rmc = clientIterator.next(); 289 if (rcdb.equals(rmc.mDispatcherCb)) { 290 rmc.release(); 291 clientIterator.remove(); 292 } else { 293 if (!rmc.mIsPrivileged) { 294 hasPublicClients = true; 295 } 296 } 297 } 298 mHasPublicClients = hasPublicClients; 299 } 300 } 301 getActiveRecordingConfigurations(boolean isPrivileged)302 List<AudioRecordingConfiguration> getActiveRecordingConfigurations(boolean isPrivileged) { 303 List<AudioRecordingConfiguration> configs = new ArrayList<AudioRecordingConfiguration>(); 304 synchronized (mRecordStates) { 305 for (RecordingState state : mRecordStates) { 306 if (state.isActiveConfiguration()) { 307 configs.add(state.getConfig()); 308 } 309 } 310 } 311 // AudioRecordingConfiguration objects never get updated. If config changes, 312 // the reference to the config is set in RecordingState. 313 if (!isPrivileged) { 314 configs = anonymizeForPublicConsumption(configs); 315 } 316 return configs; 317 } 318 319 /** 320 * Create a recording configuration from the provided parameters 321 * @param uid 322 * @param session 323 * @param source 324 * @param recordingFormat see 325 * {@link AudioSystem.AudioRecordingCallback#onRecordingConfigurationChanged(int, int, int,\ 326 int, int, boolean, int[], AudioEffect.Descriptor[], AudioEffect.Descriptor[], int, String)} 327 * for the definition of the contents of the array 328 * @param portId 329 * @param silenced 330 * @param activeSource 331 * @param clientEffects 332 * @param effects 333 * @return null a configuration object. 334 */ createRecordingConfiguration(int uid, int session, int source, int[] recordingInfo, int portId, boolean silenced, int activeSource, AudioEffect.Descriptor[] clientEffects, AudioEffect.Descriptor[] effects)335 private AudioRecordingConfiguration createRecordingConfiguration(int uid, 336 int session, int source, int[] recordingInfo, int portId, boolean silenced, 337 int activeSource, AudioEffect.Descriptor[] clientEffects, 338 AudioEffect.Descriptor[] effects) { 339 final AudioFormat clientFormat = new AudioFormat.Builder() 340 .setEncoding(recordingInfo[0]) 341 // FIXME this doesn't support index-based masks 342 .setChannelMask(recordingInfo[1]) 343 .setSampleRate(recordingInfo[2]) 344 .build(); 345 final AudioFormat deviceFormat = new AudioFormat.Builder() 346 .setEncoding(recordingInfo[3]) 347 // FIXME this doesn't support index-based masks 348 .setChannelMask(recordingInfo[4]) 349 .setSampleRate(recordingInfo[5]) 350 .build(); 351 final int patchHandle = recordingInfo[6]; 352 final String[] packages = mPackMan.getPackagesForUid(uid); 353 final String packageName; 354 if (packages != null && packages.length > 0) { 355 packageName = packages[0]; 356 } else { 357 packageName = ""; 358 } 359 return new AudioRecordingConfiguration(uid, session, source, 360 clientFormat, deviceFormat, patchHandle, packageName, 361 portId, silenced, activeSource, clientEffects, effects); 362 } 363 364 /** 365 * Update the internal "view" of the active recording sessions 366 * @param event RECORD_CONFIG_EVENT_... 367 * @param riid 368 * @param config 369 * @return null if the list of active recording sessions has not been modified, a list 370 * with the current active configurations otherwise. 371 */ updateSnapshot( int event, int riid, AudioRecordingConfiguration config)372 private List<AudioRecordingConfiguration> updateSnapshot( 373 int event, int riid, AudioRecordingConfiguration config) { 374 List<AudioRecordingConfiguration> configs = null; 375 synchronized (mRecordStates) { 376 int stateIndex = -1; 377 if (riid != AudioManager.RECORD_RIID_INVALID) { 378 stateIndex = findStateByRiid(riid); 379 } else if (config != null) { 380 stateIndex = findStateByPortId(config.getClientPortId()); 381 } 382 if (stateIndex == -1) { 383 if (event == AudioManager.RECORD_CONFIG_EVENT_START && config != null) { 384 // First time registration for a recorder tracked by AudioServer. 385 mRecordStates.add(new RecordingState(config)); 386 stateIndex = mRecordStates.size() - 1; 387 } else { 388 if (config == null) { 389 // Records tracked by clients must be registered first via trackRecorder. 390 Log.e(TAG, String.format( 391 "Unexpected event %d for riid %d", event, riid)); 392 } 393 return configs; 394 } 395 } 396 final RecordingState state = mRecordStates.get(stateIndex); 397 398 boolean configChanged; 399 switch (event) { 400 case AudioManager.RECORD_CONFIG_EVENT_START: 401 configChanged = state.setActive(true); 402 if (config != null) { 403 configChanged = state.setConfig(config) || configChanged; 404 } 405 break; 406 case AudioManager.RECORD_CONFIG_EVENT_UPDATE: 407 // For this event config != null 408 configChanged = state.setConfig(config); 409 break; 410 case AudioManager.RECORD_CONFIG_EVENT_STOP: 411 configChanged = state.setActive(false); 412 if (!state.hasDeathHandler()) { 413 // A recorder tracked by AudioServer has to be removed now so it 414 // does not leak. It will be re-registered if recording starts again. 415 mRecordStates.remove(stateIndex); 416 } 417 break; 418 case AudioManager.RECORD_CONFIG_EVENT_RELEASE: 419 configChanged = state.isActiveConfiguration(); 420 mRecordStates.remove(stateIndex); 421 break; 422 default: 423 Log.e(TAG, String.format("Unknown event %d for riid %d / portid %d", 424 event, riid, state.getPortId())); 425 configChanged = false; 426 } 427 if (configChanged) { 428 sEventLogger.log(new RecordingEvent(event, riid, state.getConfig())); 429 configs = getActiveRecordingConfigurations(true /*isPrivileged*/); 430 } 431 } 432 return configs; 433 } 434 435 // riid is assumed to be valid findStateByRiid(int riid)436 private int findStateByRiid(int riid) { 437 synchronized (mRecordStates) { 438 for (int i = 0; i < mRecordStates.size(); i++) { 439 if (mRecordStates.get(i).getRiid() == riid) { 440 return i; 441 } 442 } 443 } 444 return -1; 445 } 446 findStateByPortId(int portId)447 private int findStateByPortId(int portId) { 448 // Lookup by portId is unambiguous only for recordings managed by the Audio Server. 449 synchronized (mRecordStates) { 450 for (int i = 0; i < mRecordStates.size(); i++) { 451 if (!mRecordStates.get(i).hasDeathHandler() 452 && mRecordStates.get(i).getPortId() == portId) { 453 return i; 454 } 455 } 456 } 457 return -1; 458 } 459 460 /** 461 * Inner class to track clients that want to be notified of recording updates 462 */ 463 private final static class RecMonitorClient implements IBinder.DeathRecipient { 464 465 // can afford to be static because only one RecordingActivityMonitor ever instantiated 466 static RecordingActivityMonitor sMonitor; 467 468 final IRecordingConfigDispatcher mDispatcherCb; 469 final boolean mIsPrivileged; 470 RecMonitorClient(IRecordingConfigDispatcher rcdb, boolean isPrivileged)471 RecMonitorClient(IRecordingConfigDispatcher rcdb, boolean isPrivileged) { 472 mDispatcherCb = rcdb; 473 mIsPrivileged = isPrivileged; 474 } 475 binderDied()476 public void binderDied() { 477 Log.w(TAG, "client died"); 478 sMonitor.unregisterRecordingCallback(mDispatcherCb); 479 } 480 init()481 boolean init() { 482 try { 483 mDispatcherCb.asBinder().linkToDeath(this, 0); 484 return true; 485 } catch (RemoteException e) { 486 Log.w(TAG, "Could not link to client death", e); 487 return false; 488 } 489 } 490 release()491 void release() { 492 mDispatcherCb.asBinder().unlinkToDeath(this, 0); 493 } 494 } 495 496 private static final class RecorderDeathHandler implements IBinder.DeathRecipient { 497 498 // can afford to be static because only one RecordingActivityMonitor ever instantiated 499 static RecordingActivityMonitor sMonitor; 500 501 final int mRiid; 502 private final IBinder mRecorderToken; 503 RecorderDeathHandler(int riid, IBinder recorderToken)504 RecorderDeathHandler(int riid, IBinder recorderToken) { 505 mRiid = riid; 506 mRecorderToken = recorderToken; 507 } 508 binderDied()509 public void binderDied() { 510 sMonitor.releaseRecorder(mRiid); 511 } 512 init()513 boolean init() { 514 try { 515 mRecorderToken.linkToDeath(this, 0); 516 return true; 517 } catch (RemoteException e) { 518 Log.w(TAG, "Could not link to recorder death", e); 519 return false; 520 } 521 } 522 } 523 524 /** 525 * Inner class for recording event logging 526 */ 527 private static final class RecordingEvent extends AudioEventLogger.Event { 528 private final int mRecEvent; 529 private final int mRIId; 530 private final int mClientUid; 531 private final int mSession; 532 private final int mSource; 533 private final String mPackName; 534 RecordingEvent(int event, int riid, AudioRecordingConfiguration config)535 RecordingEvent(int event, int riid, AudioRecordingConfiguration config) { 536 mRecEvent = event; 537 mRIId = riid; 538 if (config != null) { 539 mClientUid = config.getClientUid(); 540 mSession = config.getClientAudioSessionId(); 541 mSource = config.getClientAudioSource(); 542 mPackName = config.getClientPackageName(); 543 } else { 544 mClientUid = -1; 545 mSession = -1; 546 mSource = -1; 547 mPackName = null; 548 } 549 } 550 recordEventToString(int recEvent)551 private static String recordEventToString(int recEvent) { 552 switch (recEvent) { 553 case AudioManager.RECORD_CONFIG_EVENT_START: 554 return "start"; 555 case AudioManager.RECORD_CONFIG_EVENT_UPDATE: 556 return "update"; 557 case AudioManager.RECORD_CONFIG_EVENT_STOP: 558 return "stop"; 559 case AudioManager.RECORD_CONFIG_EVENT_RELEASE: 560 return "release"; 561 default: 562 return "unknown (" + recEvent + ")"; 563 } 564 } 565 566 @Override eventToString()567 public String eventToString() { 568 return new StringBuilder("rec ").append(recordEventToString(mRecEvent)) 569 .append(" riid:").append(mRIId) 570 .append(" uid:").append(mClientUid) 571 .append(" session:").append(mSession) 572 .append(" src:").append(MediaRecorder.toLogFriendlyAudioSource(mSource)) 573 .append(mPackName == null ? "" : " pack:" + mPackName).toString(); 574 } 575 } 576 577 private static final AudioEventLogger sEventLogger = new AudioEventLogger(50, 578 "recording activity received by AudioService"); 579 } 580