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.os.Parcel;
22 import android.os.Parcelable;
23 import android.util.Log;
24 
25 import java.io.PrintWriter;
26 import java.lang.annotation.Retention;
27 import java.lang.annotation.RetentionPolicy;
28 import java.util.ArrayList;
29 import java.util.Objects;
30 
31 /**
32  * The AudioRecordingConfiguration class collects the information describing an audio recording
33  * session.
34  * <p>Direct polling (see {@link AudioManager#getActiveRecordingConfigurations()}) or callback
35  * (see {@link AudioManager#registerAudioRecordingCallback(android.media.AudioManager.AudioRecordingCallback, android.os.Handler)}
36  * methods are ways to receive information about the current recording configuration of the device.
37  * <p>An audio recording configuration contains information about the recording format as used by
38  * the application ({@link #getClientFormat()}, as well as the recording format actually used by
39  * the device ({@link #getFormat()}). The two recording formats may, for instance, be at different
40  * sampling rates due to hardware limitations (e.g. application recording at 44.1kHz whereas the
41  * device always records at 48kHz, and the Android framework resamples for the application).
42  * <p>The configuration also contains the use case for which audio is recorded
43  * ({@link #getClientAudioSource()}), enabling the ability to distinguish between different
44  * activities such as ongoing voice recognition or camcorder recording.
45  *
46  */
47 public final class AudioRecordingConfiguration implements Parcelable {
48     private final static String TAG = new String("AudioRecordingConfiguration");
49 
50     private final int mSessionId;
51 
52     private final int mClientSource;
53 
54     private final AudioFormat mDeviceFormat;
55     private final AudioFormat mClientFormat;
56 
57     @NonNull private final String mClientPackageName;
58     private final int mClientUid;
59 
60     private final int mPatchHandle;
61 
62     /**
63      * @hide
64      */
AudioRecordingConfiguration(int uid, int session, int source, AudioFormat clientFormat, AudioFormat devFormat, int patchHandle, String packageName)65     public AudioRecordingConfiguration(int uid, int session, int source, AudioFormat clientFormat,
66             AudioFormat devFormat, int patchHandle, String packageName) {
67         mClientUid = uid;
68         mSessionId = session;
69         mClientSource = source;
70         mClientFormat = clientFormat;
71         mDeviceFormat = devFormat;
72         mPatchHandle = patchHandle;
73         mClientPackageName = packageName;
74     }
75 
76     /**
77      * @hide
78      * For AudioService dump
79      * @param pw
80      */
dump(PrintWriter pw)81     public void dump(PrintWriter pw) {
82         pw.println("  " + toLogFriendlyString(this));
83     }
84 
85     /**
86      * @hide
87      */
toLogFriendlyString(AudioRecordingConfiguration arc)88     public static String toLogFriendlyString(AudioRecordingConfiguration arc) {
89         return new String("session:" + arc.mSessionId
90                 + " -- source:" + MediaRecorder.toLogFriendlyAudioSource(arc.mClientSource)
91                 + " -- uid:" + arc.mClientUid
92                 + " -- patch:" + arc.mPatchHandle
93                 + " -- pack:" + arc.mClientPackageName
94                 + " -- format client=" + arc.mClientFormat.toLogFriendlyString()
95                     + ", dev=" + arc.mDeviceFormat.toLogFriendlyString());
96     }
97 
98     // Note that this method is called server side, so no "privileged" information is ever sent
99     // to a client that is not supposed to have access to it.
100     /**
101      * @hide
102      * Creates a copy of the recording configuration that is stripped of any data enabling
103      * identification of which application it is associated with ("anonymized").
104      * @param in
105      */
anonymizedCopy(AudioRecordingConfiguration in)106     public static AudioRecordingConfiguration anonymizedCopy(AudioRecordingConfiguration in) {
107         return new AudioRecordingConfiguration( /*anonymized uid*/ -1,
108                 in.mSessionId, in.mClientSource, in.mClientFormat,
109                 in.mDeviceFormat, in.mPatchHandle, "" /*empty package name*/);
110     }
111 
112     // matches the sources that return false in MediaRecorder.isSystemOnlyAudioSource(source)
113     /** @hide */
114     @IntDef({
115         MediaRecorder.AudioSource.DEFAULT,
116         MediaRecorder.AudioSource.MIC,
117         MediaRecorder.AudioSource.VOICE_UPLINK,
118         MediaRecorder.AudioSource.VOICE_DOWNLINK,
119         MediaRecorder.AudioSource.VOICE_CALL,
120         MediaRecorder.AudioSource.CAMCORDER,
121         MediaRecorder.AudioSource.VOICE_RECOGNITION,
122         MediaRecorder.AudioSource.VOICE_COMMUNICATION,
123         MediaRecorder.AudioSource.UNPROCESSED
124     })
125     @Retention(RetentionPolicy.SOURCE)
126     public @interface AudioSource {}
127 
128     // documented return values match the sources that return false
129     //   in MediaRecorder.isSystemOnlyAudioSource(source)
130     /**
131      * Returns the audio source being used for the recording.
132      * @return one of {@link MediaRecorder.AudioSource#DEFAULT},
133      *       {@link MediaRecorder.AudioSource#MIC},
134      *       {@link MediaRecorder.AudioSource#VOICE_UPLINK},
135      *       {@link MediaRecorder.AudioSource#VOICE_DOWNLINK},
136      *       {@link MediaRecorder.AudioSource#VOICE_CALL},
137      *       {@link MediaRecorder.AudioSource#CAMCORDER},
138      *       {@link MediaRecorder.AudioSource#VOICE_RECOGNITION},
139      *       {@link MediaRecorder.AudioSource#VOICE_COMMUNICATION},
140      *       {@link MediaRecorder.AudioSource#UNPROCESSED}.
141      */
getClientAudioSource()142     public @AudioSource int getClientAudioSource() { return mClientSource; }
143 
144     /**
145      * Returns the session number of the recording, see {@link AudioRecord#getAudioSessionId()}.
146      * @return the session number.
147      */
getClientAudioSessionId()148     public int getClientAudioSessionId() { return mSessionId; }
149 
150     /**
151      * Returns the audio format at which audio is recorded on this Android device.
152      * Note that it may differ from the client application recording format
153      * (see {@link #getClientFormat()}).
154      * @return the device recording format
155      */
getFormat()156     public AudioFormat getFormat() { return mDeviceFormat; }
157 
158     /**
159      * Returns the audio format at which the client application is recording audio.
160      * Note that it may differ from the actual recording format (see {@link #getFormat()}).
161      * @return the recording format
162      */
getClientFormat()163     public AudioFormat getClientFormat() { return mClientFormat; }
164 
165     /**
166      * @pending for SystemApi
167      * Returns the package name of the application performing the recording.
168      * Where there are multiple packages sharing the same user id through the "sharedUserId"
169      * mechanism, only the first one with that id will be returned
170      * (see {@link PackageManager#getPackagesForUid(int)}).
171      * <p>This information is only available if the caller has the
172      * {@link android.Manifest.permission.MODIFY_AUDIO_ROUTING} permission.
173      * <br>When called without the permission, the result is an empty string.
174      * @return the package name
175      */
getClientPackageName()176     public String getClientPackageName() { return mClientPackageName; }
177 
178     /**
179      * @pending for SystemApi
180      * Returns the user id of the application performing the recording.
181      * <p>This information is only available if the caller has the
182      * {@link android.Manifest.permission.MODIFY_AUDIO_ROUTING}
183      * permission.
184      * <br>The result is -1 without the permission.
185      * @return the user id
186      */
getClientUid()187     public int getClientUid() { return mClientUid; }
188 
189     /**
190      * Returns information about the audio input device used for this recording.
191      * @return the audio recording device or null if this information cannot be retrieved
192      */
getAudioDevice()193     public AudioDeviceInfo getAudioDevice() {
194         // build the AudioDeviceInfo from the patch handle
195         ArrayList<AudioPatch> patches = new ArrayList<AudioPatch>();
196         if (AudioManager.listAudioPatches(patches) != AudioManager.SUCCESS) {
197             Log.e(TAG, "Error retrieving list of audio patches");
198             return null;
199         }
200         for (int i = 0 ; i < patches.size() ; i++) {
201             final AudioPatch patch = patches.get(i);
202             if (patch.id() == mPatchHandle) {
203                 final AudioPortConfig[] sources = patch.sources();
204                 if ((sources != null) && (sources.length > 0)) {
205                     // not supporting multiple sources, so just look at the first source
206                     final int devId = sources[0].port().id();
207                     final AudioDeviceInfo[] devices =
208                             AudioManager.getDevicesStatic(AudioManager.GET_DEVICES_INPUTS);
209                     for (int j = 0; j < devices.length; j++) {
210                         if (devices[j].getId() == devId) {
211                             return devices[j];
212                         }
213                     }
214                 }
215                 // patch handle is unique, there won't be another with the same handle
216                 break;
217             }
218         }
219         Log.e(TAG, "Couldn't find device for recording, did recording end already?");
220         return null;
221     }
222 
223     public static final Parcelable.Creator<AudioRecordingConfiguration> CREATOR
224             = new Parcelable.Creator<AudioRecordingConfiguration>() {
225         /**
226          * Rebuilds an AudioRecordingConfiguration previously stored with writeToParcel().
227          * @param p Parcel object to read the AudioRecordingConfiguration from
228          * @return a new AudioRecordingConfiguration created from the data in the parcel
229          */
230         public AudioRecordingConfiguration createFromParcel(Parcel p) {
231             return new AudioRecordingConfiguration(p);
232         }
233         public AudioRecordingConfiguration[] newArray(int size) {
234             return new AudioRecordingConfiguration[size];
235         }
236     };
237 
238     @Override
hashCode()239     public int hashCode() {
240         return Objects.hash(mSessionId, mClientSource);
241     }
242 
243     @Override
describeContents()244     public int describeContents() {
245         return 0;
246     }
247 
248     @Override
writeToParcel(Parcel dest, int flags)249     public void writeToParcel(Parcel dest, int flags) {
250         dest.writeInt(mSessionId);
251         dest.writeInt(mClientSource);
252         mClientFormat.writeToParcel(dest, 0);
253         mDeviceFormat.writeToParcel(dest, 0);
254         dest.writeInt(mPatchHandle);
255         dest.writeString(mClientPackageName);
256         dest.writeInt(mClientUid);
257     }
258 
AudioRecordingConfiguration(Parcel in)259     private AudioRecordingConfiguration(Parcel in) {
260         mSessionId = in.readInt();
261         mClientSource = in.readInt();
262         mClientFormat = AudioFormat.CREATOR.createFromParcel(in);
263         mDeviceFormat = AudioFormat.CREATOR.createFromParcel(in);
264         mPatchHandle = in.readInt();
265         mClientPackageName = in.readString();
266         mClientUid = in.readInt();
267     }
268 
269     @Override
equals(Object o)270     public boolean equals(Object o) {
271         if (this == o) return true;
272         if (o == null || !(o instanceof AudioRecordingConfiguration)) return false;
273 
274         AudioRecordingConfiguration that = (AudioRecordingConfiguration) o;
275 
276         return ((mClientUid == that.mClientUid)
277                 && (mSessionId == that.mSessionId)
278                 && (mClientSource == that.mClientSource)
279                 && (mPatchHandle == that.mPatchHandle)
280                 && (mClientFormat.equals(that.mClientFormat))
281                 && (mDeviceFormat.equals(that.mDeviceFormat))
282                 && (mClientPackageName.equals(that.mClientPackageName)));
283     }
284 }
285