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