blob: 57d55de8d00200c7703ee2c6e619a7cc3b45c848 [file] [log] [blame]
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -08001/*
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
17package com.android.server.audio;
18
Jean-Michel Trivi33fd8162016-02-22 10:52:41 -080019import android.media.AudioFormat;
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -080020import android.media.AudioManager;
Jean-Michel Trivi598c0c92016-03-07 14:13:03 -080021import android.media.AudioRecordingConfiguration;
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -080022import android.media.AudioSystem;
23import android.media.IRecordingConfigDispatcher;
Jean-Michel Trividd2772a2016-02-17 12:30:52 -080024import android.media.MediaRecorder;
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -080025import android.os.IBinder;
26import android.os.RemoteException;
27import android.util.Log;
28
29import java.util.ArrayList;
30import java.util.HashMap;
31import java.util.Iterator;
Jean-Michel Trivif04fab12016-05-19 10:42:35 -070032import java.util.List;
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -080033
34/**
35 * Class to receive and dispatch updates from AudioSystem about recording configurations.
36 */
37public final class RecordingActivityMonitor implements AudioSystem.AudioRecordingCallback {
38
39 public final static String TAG = "AudioService.RecordingActivityMonitor";
40
41 private ArrayList<RecMonitorClient> mClients = new ArrayList<RecMonitorClient>();
42
Jean-Michel Trivi598c0c92016-03-07 14:13:03 -080043 private HashMap<Integer, AudioRecordingConfiguration> mRecordConfigs =
44 new HashMap<Integer, AudioRecordingConfiguration>();
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -080045
46 RecordingActivityMonitor() {
47 RecMonitorClient.sMonitor = this;
48 }
49
50 /**
51 * Implementation of android.media.AudioSystem.AudioRecordingCallback
52 */
Jean-Michel Trivi33fd8162016-02-22 10:52:41 -080053 public void onRecordingConfigurationChanged(int event, int session, int source,
Jean-Michel Trivi8ab72802016-02-25 16:31:45 -080054 int[] recordingInfo) {
Jean-Michel Trividd2772a2016-02-17 12:30:52 -080055 if (MediaRecorder.isSystemOnlyAudioSource(source)) {
56 return;
57 }
Jean-Michel Trivif04fab12016-05-19 10:42:35 -070058 final List<AudioRecordingConfiguration> configs =
Jean-Michel Trivi28ff76b2016-03-02 09:36:30 -080059 updateSnapshot(event, session, source, recordingInfo);
60 if (configs != null){
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -080061 synchronized(mClients) {
Jean-Michel Trivi28ff76b2016-03-02 09:36:30 -080062 final Iterator<RecMonitorClient> clientIterator = mClients.iterator();
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -080063 while (clientIterator.hasNext()) {
64 try {
Jean-Michel Trivi28ff76b2016-03-02 09:36:30 -080065 clientIterator.next().mDispatcherCb.dispatchRecordingConfigChange(
66 configs);
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -080067 } catch (RemoteException e) {
68 Log.w(TAG, "Could not call dispatchRecordingConfigChange() on client", e);
69 }
70 }
71 }
72 }
73 }
74
75 void initMonitor() {
76 AudioSystem.setRecordingCallback(this);
77 }
78
79 void registerRecordingCallback(IRecordingConfigDispatcher rcdb) {
80 if (rcdb == null) {
81 return;
82 }
83 synchronized(mClients) {
84 final RecMonitorClient rmc = new RecMonitorClient(rcdb);
85 if (rmc.init()) {
86 mClients.add(rmc);
87 }
88 }
89 }
90
91 void unregisterRecordingCallback(IRecordingConfigDispatcher rcdb) {
92 if (rcdb == null) {
93 return;
94 }
95 synchronized(mClients) {
96 final Iterator<RecMonitorClient> clientIterator = mClients.iterator();
97 while (clientIterator.hasNext()) {
98 RecMonitorClient rmc = clientIterator.next();
99 if (rcdb.equals(rmc.mDispatcherCb)) {
100 rmc.release();
101 clientIterator.remove();
102 break;
103 }
104 }
105 }
106 }
107
Jean-Michel Trivif04fab12016-05-19 10:42:35 -0700108 List<AudioRecordingConfiguration> getActiveRecordingConfigurations() {
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -0800109 synchronized(mRecordConfigs) {
Jean-Michel Trivif04fab12016-05-19 10:42:35 -0700110 return new ArrayList<AudioRecordingConfiguration>(mRecordConfigs.values());
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -0800111 }
112 }
113
114 /**
115 * Update the internal "view" of the active recording sessions
116 * @param event
117 * @param session
118 * @param source
Jean-Michel Trivi33fd8162016-02-22 10:52:41 -0800119 * @param recordingFormat see
120 * {@link AudioSystem.AudioRecordingCallback#onRecordingConfigurationChanged(int, int, int, int[])}
121 * for the definition of the contents of the array
Jean-Michel Trivif04fab12016-05-19 10:42:35 -0700122 * @return null if the list of active recording sessions has not been modified, a list
Jean-Michel Trivi28ff76b2016-03-02 09:36:30 -0800123 * with the current active configurations otherwise.
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -0800124 */
Jean-Michel Trivif04fab12016-05-19 10:42:35 -0700125 private List<AudioRecordingConfiguration> updateSnapshot(int event, int session, int source,
Jean-Michel Trivi28ff76b2016-03-02 09:36:30 -0800126 int[] recordingInfo) {
127 final boolean configChanged;
Jean-Michel Trivif04fab12016-05-19 10:42:35 -0700128 final ArrayList<AudioRecordingConfiguration> configs;
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -0800129 synchronized(mRecordConfigs) {
130 switch (event) {
131 case AudioManager.RECORD_CONFIG_EVENT_STOP:
132 // return failure if an unknown recording session stopped
Jean-Michel Trivi28ff76b2016-03-02 09:36:30 -0800133 configChanged = (mRecordConfigs.remove(new Integer(session)) != null);
134 break;
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -0800135 case AudioManager.RECORD_CONFIG_EVENT_START:
Jean-Michel Trivi33fd8162016-02-22 10:52:41 -0800136 final AudioFormat clientFormat = new AudioFormat.Builder()
Jean-Michel Trivi8ab72802016-02-25 16:31:45 -0800137 .setEncoding(recordingInfo[0])
Jean-Michel Trivi33fd8162016-02-22 10:52:41 -0800138 // FIXME this doesn't support index-based masks
Jean-Michel Trivi8ab72802016-02-25 16:31:45 -0800139 .setChannelMask(recordingInfo[1])
140 .setSampleRate(recordingInfo[2])
Jean-Michel Trivi33fd8162016-02-22 10:52:41 -0800141 .build();
142 final AudioFormat deviceFormat = new AudioFormat.Builder()
Jean-Michel Trivi8ab72802016-02-25 16:31:45 -0800143 .setEncoding(recordingInfo[3])
Jean-Michel Trivi33fd8162016-02-22 10:52:41 -0800144 // FIXME this doesn't support index-based masks
Jean-Michel Trivi8ab72802016-02-25 16:31:45 -0800145 .setChannelMask(recordingInfo[4])
146 .setSampleRate(recordingInfo[5])
Jean-Michel Trivi33fd8162016-02-22 10:52:41 -0800147 .build();
Jean-Michel Trivi8ab72802016-02-25 16:31:45 -0800148 final int patchHandle = recordingInfo[6];
149 final Integer sessionKey = new Integer(session);
150 if (mRecordConfigs.containsKey(sessionKey)) {
Jean-Michel Trivi598c0c92016-03-07 14:13:03 -0800151 final AudioRecordingConfiguration updatedConfig =
152 new AudioRecordingConfiguration(session, source,
Jean-Michel Trivi8ab72802016-02-25 16:31:45 -0800153 clientFormat, deviceFormat, patchHandle);
154 if (updatedConfig.equals(mRecordConfigs.get(sessionKey))) {
Jean-Michel Trivi28ff76b2016-03-02 09:36:30 -0800155 configChanged = false;
Jean-Michel Trivi8ab72802016-02-25 16:31:45 -0800156 } else {
157 // config exists but has been modified
158 mRecordConfigs.remove(sessionKey);
159 mRecordConfigs.put(sessionKey, updatedConfig);
Jean-Michel Trivi28ff76b2016-03-02 09:36:30 -0800160 configChanged = true;
Jean-Michel Trivi8ab72802016-02-25 16:31:45 -0800161 }
162 } else {
163 mRecordConfigs.put(sessionKey,
Jean-Michel Trivi598c0c92016-03-07 14:13:03 -0800164 new AudioRecordingConfiguration(session, source,
Jean-Michel Trivi8ab72802016-02-25 16:31:45 -0800165 clientFormat, deviceFormat, patchHandle));
Jean-Michel Trivi28ff76b2016-03-02 09:36:30 -0800166 configChanged = true;
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -0800167 }
Jean-Michel Trivi28ff76b2016-03-02 09:36:30 -0800168 break;
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -0800169 default:
170 Log.e(TAG, String.format("Unknown event %d for session %d, source %d",
171 event, session, source));
Jean-Michel Trivi28ff76b2016-03-02 09:36:30 -0800172 configChanged = false;
173 }
174 if (configChanged) {
Jean-Michel Trivif04fab12016-05-19 10:42:35 -0700175 configs = new ArrayList<AudioRecordingConfiguration>(mRecordConfigs.values());
Jean-Michel Trivi28ff76b2016-03-02 09:36:30 -0800176 } else {
177 configs = null;
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -0800178 }
179 }
Jean-Michel Trivi28ff76b2016-03-02 09:36:30 -0800180 return configs;
Jean-Michel Trivid3c71f02015-12-07 11:59:31 -0800181 }
182
183 /**
184 * Inner class to track clients that want to be notified of recording updates
185 */
186 private final static class RecMonitorClient implements IBinder.DeathRecipient {
187
188 // can afford to be static because only one RecordingActivityMonitor ever instantiated
189 static RecordingActivityMonitor sMonitor;
190
191 final IRecordingConfigDispatcher mDispatcherCb;
192
193 RecMonitorClient(IRecordingConfigDispatcher rcdb) {
194 mDispatcherCb = rcdb;
195 }
196
197 public void binderDied() {
198 Log.w(TAG, "client died");
199 sMonitor.unregisterRecordingCallback(mDispatcherCb);
200 }
201
202 boolean init() {
203 try {
204 mDispatcherCb.asBinder().linkToDeath(this, 0);
205 return true;
206 } catch (RemoteException e) {
207 Log.w(TAG, "Could not link to client death", e);
208 return false;
209 }
210 }
211
212 void release() {
213 mDispatcherCb.asBinder().unlinkToDeath(this, 0);
214 }
215 }
216}