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