• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 android.media;
18 
19 import android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.annotation.TestApi;
22 import android.annotation.UnsupportedAppUsage;
23 import android.media.audiofx.AudioEffect;
24 import android.os.Parcel;
25 import android.os.Parcelable;
26 import android.util.Log;
27 
28 import java.io.PrintWriter;
29 import java.lang.annotation.Retention;
30 import java.lang.annotation.RetentionPolicy;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.List;
34 import java.util.Objects;
35 
36 /**
37  * The AudioRecordingConfiguration class collects the information describing an audio recording
38  * session.
39  * <p>Direct polling (see {@link AudioManager#getActiveRecordingConfigurations()}) or callback
40  * (see {@link AudioManager#registerAudioRecordingCallback(android.media.AudioManager.AudioRecordingCallback, android.os.Handler)}
41  * methods are ways to receive information about the current recording configuration of the device.
42  * <p>An audio recording configuration contains information about the recording format as used by
43  * the application ({@link #getClientFormat()}, as well as the recording format actually used by
44  * the device ({@link #getFormat()}). The two recording formats may, for instance, be at different
45  * sampling rates due to hardware limitations (e.g. application recording at 44.1kHz whereas the
46  * device always records at 48kHz, and the Android framework resamples for the application).
47  * <p>The configuration also contains the use case for which audio is recorded
48  * ({@link #getClientAudioSource()}), enabling the ability to distinguish between different
49  * activities such as ongoing voice recognition or camcorder recording.
50  *
51  */
52 public final class AudioRecordingConfiguration implements Parcelable {
53     private final static String TAG = new String("AudioRecordingConfiguration");
54 
55     private final int mClientSessionId;
56 
57     private final int mClientSource;
58 
59     private final AudioFormat mDeviceFormat;
60     private final AudioFormat mClientFormat;
61 
62     @NonNull private final String mClientPackageName;
63     private final int mClientUid;
64 
65     private final int mPatchHandle;
66 
67     private final int mClientPortId;
68 
69     private boolean mClientSilenced;
70 
71     private final int mDeviceSource;
72 
73     private final AudioEffect.Descriptor[] mClientEffects;
74 
75     private final AudioEffect.Descriptor[] mDeviceEffects;
76 
77     /**
78      * @hide
79      */
80     @TestApi
AudioRecordingConfiguration(int uid, int session, int source, AudioFormat clientFormat, AudioFormat devFormat, int patchHandle, String packageName, int clientPortId, boolean clientSilenced, int deviceSource, AudioEffect.Descriptor[] clientEffects, AudioEffect.Descriptor[] deviceEffects)81     public AudioRecordingConfiguration(int uid, int session, int source, AudioFormat clientFormat,
82             AudioFormat devFormat, int patchHandle, String packageName, int clientPortId,
83             boolean clientSilenced, int deviceSource,
84             AudioEffect.Descriptor[] clientEffects, AudioEffect.Descriptor[] deviceEffects) {
85         mClientUid = uid;
86         mClientSessionId = session;
87         mClientSource = source;
88         mClientFormat = clientFormat;
89         mDeviceFormat = devFormat;
90         mPatchHandle = patchHandle;
91         mClientPackageName = packageName;
92         mClientPortId = clientPortId;
93         mClientSilenced = clientSilenced;
94         mDeviceSource = deviceSource;
95         mClientEffects = clientEffects;
96         mDeviceEffects = deviceEffects;
97     }
98 
99     /**
100      * @hide
101      */
102     @TestApi
AudioRecordingConfiguration(int uid, int session, int source, AudioFormat clientFormat, AudioFormat devFormat, int patchHandle, String packageName)103     public AudioRecordingConfiguration(int uid, int session, int source,
104                                        AudioFormat clientFormat, AudioFormat devFormat,
105                                        int patchHandle, String packageName) {
106         this(uid, session, source, clientFormat,
107                    devFormat, patchHandle, packageName, 0 /*clientPortId*/,
108                    false /*clientSilenced*/, MediaRecorder.AudioSource.DEFAULT /*deviceSource*/,
109                    new AudioEffect.Descriptor[0] /*clientEffects*/,
110                    new AudioEffect.Descriptor[0] /*deviceEffects*/);
111     }
112 
113     /**
114      * @hide
115      * For AudioService dump
116      * @param pw
117      */
dump(PrintWriter pw)118     public void dump(PrintWriter pw) {
119         pw.println("  " + toLogFriendlyString(this));
120     }
121 
122     /**
123      * @hide
124      */
toLogFriendlyString(AudioRecordingConfiguration arc)125     public static String toLogFriendlyString(AudioRecordingConfiguration arc) {
126         String clientEffects = new String();
127         for (AudioEffect.Descriptor desc : arc.mClientEffects) {
128             clientEffects += "'" + desc.name + "' ";
129         }
130         String deviceEffects = new String();
131         for (AudioEffect.Descriptor desc : arc.mDeviceEffects) {
132             deviceEffects += "'" + desc.name + "' ";
133         }
134 
135         return new String("session:" + arc.mClientSessionId
136                 + " -- source client=" + MediaRecorder.toLogFriendlyAudioSource(arc.mClientSource)
137                 + ", dev=" + arc.mDeviceFormat.toLogFriendlyString()
138                 + " -- uid:" + arc.mClientUid
139                 + " -- patch:" + arc.mPatchHandle
140                 + " -- pack:" + arc.mClientPackageName
141                 + " -- format client=" + arc.mClientFormat.toLogFriendlyString()
142                 + ", dev=" + arc.mDeviceFormat.toLogFriendlyString()
143                 + " -- silenced:" + arc.mClientSilenced
144                 + " -- effects client=" + clientEffects
145                 + ", dev=" + deviceEffects);
146     }
147 
148     // Note that this method is called server side, so no "privileged" information is ever sent
149     // to a client that is not supposed to have access to it.
150     /**
151      * @hide
152      * Creates a copy of the recording configuration that is stripped of any data enabling
153      * identification of which application it is associated with ("anonymized").
154      * @param in
155      */
anonymizedCopy(AudioRecordingConfiguration in)156     public static AudioRecordingConfiguration anonymizedCopy(AudioRecordingConfiguration in) {
157         return new AudioRecordingConfiguration( /*anonymized uid*/ -1,
158                 in.mClientSessionId, in.mClientSource, in.mClientFormat,
159                 in.mDeviceFormat, in.mPatchHandle, "" /*empty package name*/,
160                 in.mClientPortId, in.mClientSilenced, in.mDeviceSource, in.mClientEffects,
161                 in.mDeviceEffects);
162     }
163 
164     // matches the sources that return false in MediaRecorder.isSystemOnlyAudioSource(source)
165     /** @hide */
166     @IntDef({
167         MediaRecorder.AudioSource.DEFAULT,
168         MediaRecorder.AudioSource.MIC,
169         MediaRecorder.AudioSource.VOICE_UPLINK,
170         MediaRecorder.AudioSource.VOICE_DOWNLINK,
171         MediaRecorder.AudioSource.VOICE_CALL,
172         MediaRecorder.AudioSource.CAMCORDER,
173         MediaRecorder.AudioSource.VOICE_RECOGNITION,
174         MediaRecorder.AudioSource.VOICE_COMMUNICATION,
175         MediaRecorder.AudioSource.UNPROCESSED,
176         MediaRecorder.AudioSource.VOICE_PERFORMANCE
177     })
178     @Retention(RetentionPolicy.SOURCE)
179     public @interface AudioSource {}
180 
181     // documented return values match the sources that return false
182     //   in MediaRecorder.isSystemOnlyAudioSource(source)
183     /**
184      * Returns the audio source selected by the client.
185      * @return the audio source selected by the client.
186      */
getClientAudioSource()187     public @AudioSource int getClientAudioSource() { return mClientSource; }
188 
189     /**
190      * Returns the session number of the recording, see {@link AudioRecord#getAudioSessionId()}.
191      * @return the session number.
192      */
getClientAudioSessionId()193     public int getClientAudioSessionId() {
194         return mClientSessionId;
195     }
196 
197     /**
198      * Returns the audio format at which audio is recorded on this Android device.
199      * Note that it may differ from the client application recording format
200      * (see {@link #getClientFormat()}).
201      * @return the device recording format
202      */
getFormat()203     public AudioFormat getFormat() { return mDeviceFormat; }
204 
205     /**
206      * Returns the audio format at which the client application is recording audio.
207      * Note that it may differ from the actual recording format (see {@link #getFormat()}).
208      * @return the recording format
209      */
getClientFormat()210     public AudioFormat getClientFormat() { return mClientFormat; }
211 
212     /**
213      * @pending for SystemApi
214      * Returns the package name of the application performing the recording.
215      * Where there are multiple packages sharing the same user id through the "sharedUserId"
216      * mechanism, only the first one with that id will be returned
217      * (see {@link PackageManager#getPackagesForUid(int)}).
218      * <p>This information is only available if the caller has the
219      * {@link android.Manifest.permission.MODIFY_AUDIO_ROUTING} permission.
220      * <br>When called without the permission, the result is an empty string.
221      * @return the package name
222      */
223     @UnsupportedAppUsage
getClientPackageName()224     public String getClientPackageName() { return mClientPackageName; }
225 
226     /**
227      * @pending for SystemApi
228      * Returns the user id of the application performing the recording.
229      * <p>This information is only available if the caller has the
230      * {@link android.Manifest.permission.MODIFY_AUDIO_ROUTING}
231      * permission.
232      * <br>The result is -1 without the permission.
233      * @return the user id
234      */
235     @UnsupportedAppUsage
getClientUid()236     public int getClientUid() { return mClientUid; }
237 
238     /**
239      * Returns information about the audio input device used for this recording.
240      * @return the audio recording device or null if this information cannot be retrieved
241      */
getAudioDevice()242     public AudioDeviceInfo getAudioDevice() {
243         // build the AudioDeviceInfo from the patch handle
244         ArrayList<AudioPatch> patches = new ArrayList<AudioPatch>();
245         if (AudioManager.listAudioPatches(patches) != AudioManager.SUCCESS) {
246             Log.e(TAG, "Error retrieving list of audio patches");
247             return null;
248         }
249         for (int i = 0 ; i < patches.size() ; i++) {
250             final AudioPatch patch = patches.get(i);
251             if (patch.id() == mPatchHandle) {
252                 final AudioPortConfig[] sources = patch.sources();
253                 if ((sources != null) && (sources.length > 0)) {
254                     // not supporting multiple sources, so just look at the first source
255                     final int devId = sources[0].port().id();
256                     final AudioDeviceInfo[] devices =
257                             AudioManager.getDevicesStatic(AudioManager.GET_DEVICES_INPUTS);
258                     for (int j = 0; j < devices.length; j++) {
259                         if (devices[j].getId() == devId) {
260                             return devices[j];
261                         }
262                     }
263                 }
264                 // patch handle is unique, there won't be another with the same handle
265                 break;
266             }
267         }
268         Log.e(TAG, "Couldn't find device for recording, did recording end already?");
269         return null;
270     }
271 
272     /**
273      * @hide
274      * Returns the system unique ID assigned for the AudioRecord object corresponding to this
275      * AudioRecordingConfiguration client.
276      * @return the port ID.
277      */
getClientPortId()278     public int getClientPortId() {
279         return mClientPortId;
280     }
281 
282     /**
283      * Returns true if the audio returned to the client is currently being silenced by the
284      * audio framework due to concurrent capture policy (e.g the capturing application does not have
285      * an active foreground process or service anymore).
286      * @return true if captured audio is silenced, false otherwise .
287      */
isClientSilenced()288     public boolean isClientSilenced() {
289         return mClientSilenced;
290     }
291 
292     /**
293      * Returns the audio source currently used to configure the capture path. It can be different
294      * from the source returned by {@link #getClientAudioSource()} if another capture is active.
295      * @return the audio source active on the capture path.
296      */
getAudioSource()297     public @AudioSource int getAudioSource() {
298         return mDeviceSource;
299     }
300 
301     /**
302      * Returns the list of {@link AudioEffect.Descriptor} for all effects currently enabled on
303      * the audio capture client (e.g. {@link AudioRecord} or {@link MediaRecorder}).
304      * @return List of {@link AudioEffect.Descriptor} containing all effects enabled for the client.
305      */
getClientEffects()306     public @NonNull List<AudioEffect.Descriptor> getClientEffects() {
307         return new ArrayList<AudioEffect.Descriptor>(Arrays.asList(mClientEffects));
308     }
309 
310     /**
311      * Returns the list of {@link AudioEffect.Descriptor} for all effects currently enabled on
312      * the capture stream.
313      * @return List of {@link AudioEffect.Descriptor} containing all effects enabled on the
314      * capture stream. This can be different from the list returned by {@link #getClientEffects()}
315      * if another capture is active.
316      */
getEffects()317     public @NonNull List<AudioEffect.Descriptor> getEffects() {
318         return new ArrayList<AudioEffect.Descriptor>(Arrays.asList(mDeviceEffects));
319     }
320 
321     public static final @android.annotation.NonNull Parcelable.Creator<AudioRecordingConfiguration> CREATOR
322             = new Parcelable.Creator<AudioRecordingConfiguration>() {
323         /**
324          * Rebuilds an AudioRecordingConfiguration previously stored with writeToParcel().
325          * @param p Parcel object to read the AudioRecordingConfiguration from
326          * @return a new AudioRecordingConfiguration created from the data in the parcel
327          */
328         public AudioRecordingConfiguration createFromParcel(Parcel p) {
329             return new AudioRecordingConfiguration(p);
330         }
331         public AudioRecordingConfiguration[] newArray(int size) {
332             return new AudioRecordingConfiguration[size];
333         }
334     };
335 
336     @Override
hashCode()337     public int hashCode() {
338         return Objects.hash(mClientSessionId, mClientSource);
339     }
340 
341     @Override
describeContents()342     public int describeContents() {
343         return 0;
344     }
345 
346     @Override
writeToParcel(Parcel dest, int flags)347     public void writeToParcel(Parcel dest, int flags) {
348         dest.writeInt(mClientSessionId);
349         dest.writeInt(mClientSource);
350         mClientFormat.writeToParcel(dest, 0);
351         mDeviceFormat.writeToParcel(dest, 0);
352         dest.writeInt(mPatchHandle);
353         dest.writeString(mClientPackageName);
354         dest.writeInt(mClientUid);
355         dest.writeInt(mClientPortId);
356         dest.writeBoolean(mClientSilenced);
357         dest.writeInt(mDeviceSource);
358         dest.writeInt(mClientEffects.length);
359         for (int i = 0; i < mClientEffects.length; i++) {
360             mClientEffects[i].writeToParcel(dest);
361         }
362         dest.writeInt(mDeviceEffects.length);
363         for (int i = 0; i < mDeviceEffects.length; i++) {
364             mDeviceEffects[i].writeToParcel(dest);
365         }
366     }
367 
AudioRecordingConfiguration(Parcel in)368     private AudioRecordingConfiguration(Parcel in) {
369         mClientSessionId = in.readInt();
370         mClientSource = in.readInt();
371         mClientFormat = AudioFormat.CREATOR.createFromParcel(in);
372         mDeviceFormat = AudioFormat.CREATOR.createFromParcel(in);
373         mPatchHandle = in.readInt();
374         mClientPackageName = in.readString();
375         mClientUid = in.readInt();
376         mClientPortId = in.readInt();
377         mClientSilenced = in.readBoolean();
378         mDeviceSource = in.readInt();
379         mClientEffects = new AudioEffect.Descriptor[in.readInt()];
380         for (int i = 0; i < mClientEffects.length; i++) {
381             mClientEffects[i] = new AudioEffect.Descriptor(in);
382         }
383         mDeviceEffects = new AudioEffect.Descriptor[in.readInt()];
384         for (int i = 0; i < mDeviceEffects.length; i++) {
385             mDeviceEffects[i] = new AudioEffect.Descriptor(in);
386         }
387     }
388 
389     @Override
equals(Object o)390     public boolean equals(Object o) {
391         if (this == o) return true;
392         if (o == null || !(o instanceof AudioRecordingConfiguration)) return false;
393 
394         AudioRecordingConfiguration that = (AudioRecordingConfiguration) o;
395 
396         return ((mClientUid == that.mClientUid)
397                 && (mClientSessionId == that.mClientSessionId)
398                 && (mClientSource == that.mClientSource)
399                 && (mPatchHandle == that.mPatchHandle)
400                 && (mClientFormat.equals(that.mClientFormat))
401                 && (mDeviceFormat.equals(that.mDeviceFormat))
402                 && (mClientPackageName.equals(that.mClientPackageName))
403                 && (mClientPortId == that.mClientPortId)
404                 && (mClientSilenced == that.mClientSilenced)
405                 && (mDeviceSource == that.mDeviceSource)
406                 && (Arrays.equals(mClientEffects, that.mClientEffects))
407                 && (Arrays.equals(mDeviceEffects, that.mDeviceEffects)));
408     }
409 }
410