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