Home | History | Annotate | Download | only in media
      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      */
     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      */
     81     public void dump(PrintWriter pw) {
     82         pw.println("  " + toLogFriendlyString(this));
     83     }
     84 
     85     /**
     86      * @hide
     87      */
     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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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
    239     public int hashCode() {
    240         return Objects.hash(mSessionId, mClientSource);
    241     }
    242 
    243     @Override
    244     public int describeContents() {
    245         return 0;
    246     }
    247 
    248     @Override
    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 
    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
    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