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 */ AudioRecordingConfiguration(int uid, int session, int source, AudioFormat clientFormat, AudioFormat devFormat, int patchHandle, String packageName)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 */ dump(PrintWriter pw)81 public void dump(PrintWriter pw) { 82 pw.println(" " + toLogFriendlyString(this)); 83 } 84 85 /** 86 * @hide 87 */ toLogFriendlyString(AudioRecordingConfiguration arc)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 */ anonymizedCopy(AudioRecordingConfiguration in)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 */ getClientAudioSource()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 */ getClientAudioSessionId()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 */ getFormat()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 */ getClientFormat()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 */ getClientPackageName()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 */ getClientUid()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 */ getAudioDevice()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 hashCode()239 public int hashCode() { 240 return Objects.hash(mSessionId, mClientSource); 241 } 242 243 @Override describeContents()244 public int describeContents() { 245 return 0; 246 } 247 248 @Override writeToParcel(Parcel dest, int flags)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 AudioRecordingConfiguration(Parcel in)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 equals(Object o)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