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