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