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