All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.android.media.AudioRecordingConfiguration Maven / Gradle / Ivy

/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.media;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.media.audiofx.AudioEffect;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;

import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

/**
 * The AudioRecordingConfiguration class collects the information describing an audio recording
 * session.
 * 

Direct polling (see {@link AudioManager#getActiveRecordingConfigurations()}) or callback * (see {@link AudioManager#registerAudioRecordingCallback(android.media.AudioManager.AudioRecordingCallback, android.os.Handler)} * methods are ways to receive information about the current recording configuration of the device. *

An audio recording configuration contains information about the recording format as used by * the application ({@link #getClientFormat()}, as well as the recording format actually used by * the device ({@link #getFormat()}). The two recording formats may, for instance, be at different * sampling rates due to hardware limitations (e.g. application recording at 44.1kHz whereas the * device always records at 48kHz, and the Android framework resamples for the application). *

The configuration also contains the use case for which audio is recorded * ({@link #getClientAudioSource()}), enabling the ability to distinguish between different * activities such as ongoing voice recognition or camcorder recording. * */ public final class AudioRecordingConfiguration implements Parcelable { private final static String TAG = new String("AudioRecordingConfiguration"); private final int mClientSessionId; private final int mClientSource; private final AudioFormat mDeviceFormat; private final AudioFormat mClientFormat; @NonNull private final String mClientPackageName; private final int mClientUid; private final int mPatchHandle; private final int mClientPortId; private boolean mClientSilenced; private final int mDeviceSource; private final AudioEffect.Descriptor[] mClientEffects; private final AudioEffect.Descriptor[] mDeviceEffects; /** * @hide */ @TestApi public 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) { mClientUid = uid; mClientSessionId = session; mClientSource = source; mClientFormat = clientFormat; mDeviceFormat = devFormat; mPatchHandle = patchHandle; mClientPackageName = packageName; mClientPortId = clientPortId; mClientSilenced = clientSilenced; mDeviceSource = deviceSource; mClientEffects = clientEffects; mDeviceEffects = deviceEffects; } /** * @hide */ @TestApi public AudioRecordingConfiguration(int uid, int session, int source, AudioFormat clientFormat, AudioFormat devFormat, int patchHandle, String packageName) { this(uid, session, source, clientFormat, devFormat, patchHandle, packageName, 0 /*clientPortId*/, false /*clientSilenced*/, MediaRecorder.AudioSource.DEFAULT /*deviceSource*/, new AudioEffect.Descriptor[0] /*clientEffects*/, new AudioEffect.Descriptor[0] /*deviceEffects*/); } /** * @hide * For AudioService dump * @param pw */ public void dump(PrintWriter pw) { pw.println(" " + toLogFriendlyString(this)); } /** * @hide */ public static String toLogFriendlyString(AudioRecordingConfiguration arc) { String clientEffects = new String(); for (AudioEffect.Descriptor desc : arc.mClientEffects) { clientEffects += "'" + desc.name + "' "; } String deviceEffects = new String(); for (AudioEffect.Descriptor desc : arc.mDeviceEffects) { deviceEffects += "'" + desc.name + "' "; } return new String("session:" + arc.mClientSessionId + " -- source client=" + MediaRecorder.toLogFriendlyAudioSource(arc.mClientSource) + ", dev=" + arc.mDeviceFormat.toLogFriendlyString() + " -- uid:" + arc.mClientUid + " -- patch:" + arc.mPatchHandle + " -- pack:" + arc.mClientPackageName + " -- format client=" + arc.mClientFormat.toLogFriendlyString() + ", dev=" + arc.mDeviceFormat.toLogFriendlyString() + " -- silenced:" + arc.mClientSilenced + " -- effects client=" + clientEffects + ", dev=" + deviceEffects); } // Note that this method is called server side, so no "privileged" information is ever sent // to a client that is not supposed to have access to it. /** * @hide * Creates a copy of the recording configuration that is stripped of any data enabling * identification of which application it is associated with ("anonymized"). * @param in */ public static AudioRecordingConfiguration anonymizedCopy(AudioRecordingConfiguration in) { return new AudioRecordingConfiguration( /*anonymized uid*/ -1, in.mClientSessionId, in.mClientSource, in.mClientFormat, in.mDeviceFormat, in.mPatchHandle, "" /*empty package name*/, in.mClientPortId, in.mClientSilenced, in.mDeviceSource, in.mClientEffects, in.mDeviceEffects); } // matches the sources that return false in MediaRecorder.isSystemOnlyAudioSource(source) /** @hide */ @IntDef({ MediaRecorder.AudioSource.DEFAULT, MediaRecorder.AudioSource.MIC, MediaRecorder.AudioSource.VOICE_UPLINK, MediaRecorder.AudioSource.VOICE_DOWNLINK, MediaRecorder.AudioSource.VOICE_CALL, MediaRecorder.AudioSource.CAMCORDER, MediaRecorder.AudioSource.VOICE_RECOGNITION, MediaRecorder.AudioSource.VOICE_COMMUNICATION, MediaRecorder.AudioSource.UNPROCESSED, MediaRecorder.AudioSource.VOICE_PERFORMANCE }) @Retention(RetentionPolicy.SOURCE) public @interface AudioSource {} // documented return values match the sources that return false // in MediaRecorder.isSystemOnlyAudioSource(source) /** * Returns the audio source selected by the client. * @return the audio source selected by the client. */ public @AudioSource int getClientAudioSource() { return mClientSource; } /** * Returns the session number of the recording, see {@link AudioRecord#getAudioSessionId()}. * @return the session number. */ public int getClientAudioSessionId() { return mClientSessionId; } /** * Returns the audio format at which audio is recorded on this Android device. * Note that it may differ from the client application recording format * (see {@link #getClientFormat()}). * @return the device recording format */ public AudioFormat getFormat() { return mDeviceFormat; } /** * Returns the audio format at which the client application is recording audio. * Note that it may differ from the actual recording format (see {@link #getFormat()}). * @return the recording format */ public AudioFormat getClientFormat() { return mClientFormat; } /** * @pending for SystemApi * Returns the package name of the application performing the recording. * Where there are multiple packages sharing the same user id through the "sharedUserId" * mechanism, only the first one with that id will be returned * (see {@link PackageManager#getPackagesForUid(int)}). *

This information is only available if the caller has the * {@link android.Manifest.permission.MODIFY_AUDIO_ROUTING} permission. *
When called without the permission, the result is an empty string. * @return the package name */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public String getClientPackageName() { return mClientPackageName; } /** * Returns the user id of the application performing the recording. *

This information is only available if the caller has the * {@link android.Manifest.permission.MODIFY_AUDIO_ROUTING} * permission. * @return the user id * @throws SecurityException Thrown if the caller is missing the MODIFY_AUDIO_ROUTING permission * * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public int getClientUid() { if (mClientUid == -1) { throw new SecurityException("MODIFY_AUDIO_ROUTING permission is missing"); } return mClientUid; } /** * Returns information about the audio input device used for this recording. * @return the audio recording device or null if this information cannot be retrieved */ public AudioDeviceInfo getAudioDevice() { // build the AudioDeviceInfo from the patch handle ArrayList patches = new ArrayList(); if (AudioManager.listAudioPatches(patches) != AudioManager.SUCCESS) { Log.e(TAG, "Error retrieving list of audio patches"); return null; } for (int i = 0 ; i < patches.size() ; i++) { final AudioPatch patch = patches.get(i); if (patch.id() == mPatchHandle) { final AudioPortConfig[] sources = patch.sources(); if ((sources != null) && (sources.length > 0)) { // not supporting multiple sources, so just look at the first source int devId = sources[0].port().id(); return AudioManager.getDeviceForPortId(devId, AudioManager.GET_DEVICES_INPUTS); } // patch handle is unique, there won't be another with the same handle break; } } Log.e(TAG, "Couldn't find device for recording, did recording end already?"); return null; } /** * @hide * Returns the system unique ID assigned for the AudioRecord object corresponding to this * AudioRecordingConfiguration client. * @return the port ID. */ public int getClientPortId() { return mClientPortId; } /** * Returns true if the audio returned to the client is currently being silenced by the * audio framework due to concurrent capture policy (e.g the capturing application does not have * an active foreground process or service anymore). * @return true if captured audio is silenced, false otherwise . */ public boolean isClientSilenced() { return mClientSilenced; } /** * Returns the audio source currently used to configure the capture path. It can be different * from the source returned by {@link #getClientAudioSource()} if another capture is active. * @return the audio source active on the capture path. */ public @AudioSource int getAudioSource() { return mDeviceSource; } /** * Returns the list of {@link AudioEffect.Descriptor} for all effects currently enabled on * the audio capture client (e.g. {@link AudioRecord} or {@link MediaRecorder}). * @return List of {@link AudioEffect.Descriptor} containing all effects enabled for the client. */ public @NonNull List getClientEffects() { return new ArrayList(Arrays.asList(mClientEffects)); } /** * Returns the list of {@link AudioEffect.Descriptor} for all effects currently enabled on * the capture stream. * @return List of {@link AudioEffect.Descriptor} containing all effects enabled on the * capture stream. This can be different from the list returned by {@link #getClientEffects()} * if another capture is active. */ public @NonNull List getEffects() { return new ArrayList(Arrays.asList(mDeviceEffects)); } public static final @android.annotation.NonNull Parcelable.Creator CREATOR = new Parcelable.Creator() { /** * Rebuilds an AudioRecordingConfiguration previously stored with writeToParcel(). * @param p Parcel object to read the AudioRecordingConfiguration from * @return a new AudioRecordingConfiguration created from the data in the parcel */ public AudioRecordingConfiguration createFromParcel(Parcel p) { return new AudioRecordingConfiguration(p); } public AudioRecordingConfiguration[] newArray(int size) { return new AudioRecordingConfiguration[size]; } }; @Override public int hashCode() { return Objects.hash(mClientSessionId, mClientSource); } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mClientSessionId); dest.writeInt(mClientSource); mClientFormat.writeToParcel(dest, 0); mDeviceFormat.writeToParcel(dest, 0); dest.writeInt(mPatchHandle); dest.writeString(mClientPackageName); dest.writeInt(mClientUid); dest.writeInt(mClientPortId); dest.writeBoolean(mClientSilenced); dest.writeInt(mDeviceSource); dest.writeInt(mClientEffects.length); for (int i = 0; i < mClientEffects.length; i++) { mClientEffects[i].writeToParcel(dest); } dest.writeInt(mDeviceEffects.length); for (int i = 0; i < mDeviceEffects.length; i++) { mDeviceEffects[i].writeToParcel(dest); } } private AudioRecordingConfiguration(Parcel in) { mClientSessionId = in.readInt(); mClientSource = in.readInt(); mClientFormat = AudioFormat.CREATOR.createFromParcel(in); mDeviceFormat = AudioFormat.CREATOR.createFromParcel(in); mPatchHandle = in.readInt(); mClientPackageName = in.readString(); mClientUid = in.readInt(); mClientPortId = in.readInt(); mClientSilenced = in.readBoolean(); mDeviceSource = in.readInt(); mClientEffects = new AudioEffect.Descriptor[in.readInt()]; for (int i = 0; i < mClientEffects.length; i++) { mClientEffects[i] = new AudioEffect.Descriptor(in); } mDeviceEffects = new AudioEffect.Descriptor[in.readInt()]; for (int i = 0; i < mDeviceEffects.length; i++) { mDeviceEffects[i] = new AudioEffect.Descriptor(in); } } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || !(o instanceof AudioRecordingConfiguration)) return false; AudioRecordingConfiguration that = (AudioRecordingConfiguration) o; return ((mClientUid == that.mClientUid) && (mClientSessionId == that.mClientSessionId) && (mClientSource == that.mClientSource) && (mPatchHandle == that.mPatchHandle) && (mClientFormat.equals(that.mClientFormat)) && (mDeviceFormat.equals(that.mDeviceFormat)) && (mClientPackageName.equals(that.mClientPackageName)) && (mClientPortId == that.mClientPortId) && (mClientSilenced == that.mClientSilenced) && (mDeviceSource == that.mDeviceSource) && (Arrays.equals(mClientEffects, that.mClientEffects)) && (Arrays.equals(mDeviceEffects, that.mDeviceEffects))); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy