1 /*
2  * Copyright (C) 2008 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 static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAULT;
20 import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_AUDIO;
21 import static android.content.Context.DEVICE_ID_DEFAULT;
22 import static android.media.AudioManager.AUDIO_SESSION_ID_GENERATE;
23 
24 import android.annotation.CallbackExecutor;
25 import android.annotation.FloatRange;
26 import android.annotation.IntDef;
27 import android.annotation.IntRange;
28 import android.annotation.NonNull;
29 import android.annotation.Nullable;
30 import android.annotation.RequiresPermission;
31 import android.annotation.SystemApi;
32 import android.annotation.TestApi;
33 import android.app.ActivityThread;
34 import android.companion.virtual.VirtualDeviceManager;
35 import android.compat.annotation.UnsupportedAppUsage;
36 import android.content.AttributionSource;
37 import android.content.AttributionSource.ScopedParcelState;
38 import android.content.Context;
39 import android.media.MediaRecorder.Source;
40 import android.media.audio.common.AudioInputFlags;
41 import android.media.audiopolicy.AudioMix;
42 import android.media.audiopolicy.AudioMixingRule;
43 import android.media.audiopolicy.AudioPolicy;
44 import android.media.metrics.LogSessionId;
45 import android.media.projection.MediaProjection;
46 import android.os.Binder;
47 import android.os.Build;
48 import android.os.Handler;
49 import android.os.IBinder;
50 import android.os.Looper;
51 import android.os.Message;
52 import android.os.Parcel;
53 import android.os.PersistableBundle;
54 import android.os.RemoteException;
55 import android.os.ServiceManager;
56 import android.util.ArrayMap;
57 import android.util.Log;
58 import android.util.Pair;
59 
60 import com.android.internal.annotations.GuardedBy;
61 import com.android.internal.util.Preconditions;
62 
63 import java.io.IOException;
64 import java.lang.annotation.Retention;
65 import java.lang.annotation.RetentionPolicy;
66 import java.lang.ref.WeakReference;
67 import java.nio.ByteBuffer;
68 import java.util.ArrayList;
69 import java.util.HashSet;
70 import java.util.Iterator;
71 import java.util.List;
72 import java.util.Objects;
73 import java.util.concurrent.Executor;
74 
75 /**
76  * The AudioRecord class manages the audio resources for Java applications
77  * to record audio from the audio input hardware of the platform. This is
78  * achieved by "pulling" (reading) the data from the AudioRecord object. The
79  * application is responsible for polling the AudioRecord object in time using one of
80  * the following three methods:  {@link #read(byte[],int, int)}, {@link #read(short[], int, int)}
81  * or {@link #read(ByteBuffer, int)}. The choice of which method to use will be based
82  * on the audio data storage format that is the most convenient for the user of AudioRecord.
83  * <p>Upon creation, an AudioRecord object initializes its associated audio buffer that it will
84  * fill with the new audio data. The size of this buffer, specified during the construction,
85  * determines how long an AudioRecord can record before "over-running" data that has not
86  * been read yet. Data should be read from the audio hardware in chunks of sizes inferior to
87  * the total recording buffer size.</p>
88  * <p>
89  * Applications creating an AudioRecord instance need
90  * {@link android.Manifest.permission#RECORD_AUDIO} or the Builder will throw
91  * {@link java.lang.UnsupportedOperationException} on
92  * {@link android.media.AudioRecord.Builder#build build()},
93  * and the constructor will return an instance in state
94  * {@link #STATE_UNINITIALIZED}.</p>
95  */
96 public class AudioRecord implements AudioRouting, MicrophoneDirection,
97         AudioRecordingMonitor, AudioRecordingMonitorClient
98 {
99     //---------------------------------------------------------
100     // Constants
101     //--------------------
102 
103 
104     /**
105      *  indicates AudioRecord state is not successfully initialized.
106      */
107     public static final int STATE_UNINITIALIZED = 0;
108     /**
109      *  indicates AudioRecord state is ready to be used
110      */
111     public static final int STATE_INITIALIZED   = 1;
112 
113     /**
114      * indicates AudioRecord recording state is not recording
115      */
116     public static final int RECORDSTATE_STOPPED = 1;  // matches SL_RECORDSTATE_STOPPED
117     /**
118      * indicates AudioRecord recording state is recording
119      */
120     public static final int RECORDSTATE_RECORDING = 3;// matches SL_RECORDSTATE_RECORDING
121 
122     /**
123      * Denotes a successful operation.
124      */
125     public  static final int SUCCESS                               = AudioSystem.SUCCESS;
126     /**
127      * Denotes a generic operation failure.
128      */
129     public  static final int ERROR                                 = AudioSystem.ERROR;
130     /**
131      * Denotes a failure due to the use of an invalid value.
132      */
133     public  static final int ERROR_BAD_VALUE                       = AudioSystem.BAD_VALUE;
134     /**
135      * Denotes a failure due to the improper use of a method.
136      */
137     public  static final int ERROR_INVALID_OPERATION               = AudioSystem.INVALID_OPERATION;
138     /**
139      * An error code indicating that the object reporting it is no longer valid and needs to
140      * be recreated.
141      */
142     public  static final int ERROR_DEAD_OBJECT                     = AudioSystem.DEAD_OBJECT;
143 
144     // Error codes:
145     // to keep in sync with frameworks/base/core/jni/android_media_AudioRecord.cpp
146     private static final int AUDIORECORD_ERROR_SETUP_ZEROFRAMECOUNT      = -16;
147     private static final int AUDIORECORD_ERROR_SETUP_INVALIDCHANNELMASK  = -17;
148     private static final int AUDIORECORD_ERROR_SETUP_INVALIDFORMAT       = -18;
149     private static final int AUDIORECORD_ERROR_SETUP_INVALIDSOURCE       = -19;
150     private static final int AUDIORECORD_ERROR_SETUP_NATIVEINITFAILED    = -20;
151 
152     // Events:
153     // to keep in sync with frameworks/av/include/media/AudioRecord.h
154     /**
155      * Event id denotes when record head has reached a previously set marker.
156      */
157     private static final int NATIVE_EVENT_MARKER  = 2;
158     /**
159      * Event id denotes when previously set update period has elapsed during recording.
160      */
161     private static final int NATIVE_EVENT_NEW_POS = 3;
162 
163     private final static String TAG = "android.media.AudioRecord";
164 
165     /** @hide */
166     public final static String SUBMIX_FIXED_VOLUME = "fixedVolume";
167 
168     /** @hide */
169     @IntDef({
170         READ_BLOCKING,
171         READ_NON_BLOCKING
172     })
173     @Retention(RetentionPolicy.SOURCE)
174     public @interface ReadMode {}
175 
176     /**
177      * The read mode indicating the read operation will block until all data
178      * requested has been read.
179      */
180     public final static int READ_BLOCKING = 0;
181 
182     /**
183      * The read mode indicating the read operation will return immediately after
184      * reading as much audio data as possible without blocking.
185      */
186     public final static int READ_NON_BLOCKING = 1;
187 
188     //---------------------------------------------------------
189     // Used exclusively by native code
190     //--------------------
191     /**
192      * Accessed by native methods: provides access to C++ AudioRecord object
193      * Is 0 after release()
194      */
195     @SuppressWarnings("unused")
196     @UnsupportedAppUsage
197     private long mNativeAudioRecordHandle;
198 
199     /**
200      * Accessed by native methods: provides access to the callback data.
201      */
202     @SuppressWarnings("unused")
203     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
204     private long mNativeJNIDataHandle;
205 
206     //---------------------------------------------------------
207     // Member variables
208     //--------------------
209     private AudioPolicy mAudioCapturePolicy;
210 
211     /**
212      * The audio data sampling rate in Hz.
213      * Never {@link AudioFormat#SAMPLE_RATE_UNSPECIFIED}.
214      */
215     private int mSampleRate; // initialized by all constructors via audioParamCheck()
216     /**
217      * The number of input audio channels (1 is mono, 2 is stereo)
218      */
219     private int mChannelCount;
220     /**
221      * The audio channel position mask
222      */
223     private int mChannelMask;
224     /**
225      * The audio channel index mask
226      */
227     private int mChannelIndexMask;
228     /**
229      * The encoding of the audio samples.
230      * @see AudioFormat#ENCODING_PCM_8BIT
231      * @see AudioFormat#ENCODING_PCM_16BIT
232      * @see AudioFormat#ENCODING_PCM_FLOAT
233      */
234     private int mAudioFormat;
235     /**
236      * Where the audio data is recorded from.
237      */
238     private int mRecordSource;
239     /**
240      * Indicates the state of the AudioRecord instance.
241      */
242     private int mState = STATE_UNINITIALIZED;
243     /**
244      * Indicates the recording state of the AudioRecord instance.
245      */
246     private int mRecordingState = RECORDSTATE_STOPPED;
247     /**
248      * Lock to make sure mRecordingState updates are reflecting the actual state of the object.
249      */
250     private final Object mRecordingStateLock = new Object();
251     /**
252      * The listener the AudioRecord notifies when the record position reaches a marker
253      * or for periodic updates during the progression of the record head.
254      *  @see #setRecordPositionUpdateListener(OnRecordPositionUpdateListener)
255      *  @see #setRecordPositionUpdateListener(OnRecordPositionUpdateListener, Handler)
256      */
257     private OnRecordPositionUpdateListener mPositionListener = null;
258     /**
259      * Lock to protect position listener updates against event notifications
260      */
261     private final Object mPositionListenerLock = new Object();
262     /**
263      * Handler for marker events coming from the native code
264      */
265     private NativeEventHandler mEventHandler = null;
266     /**
267      * Looper associated with the thread that creates the AudioRecord instance
268      */
269     @UnsupportedAppUsage
270     private Looper mInitializationLooper = null;
271     /**
272      * Size of the native audio buffer.
273      */
274     private int mNativeBufferSizeInBytes = 0;
275     /**
276      * Audio session ID
277      */
278     private int mSessionId = AudioManager.AUDIO_SESSION_ID_GENERATE;
279     /**
280      * Audio HAL Input Flags as bitfield.
281      */
282     private int mHalInputFlags = 0;
283 
284     /**
285      * AudioAttributes
286      */
287     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
288     private AudioAttributes mAudioAttributes;
289     private boolean mIsSubmixFullVolume = false;
290 
291     /**
292      * The log session id used for metrics.
293      * {@link LogSessionId#LOG_SESSION_ID_NONE} here means it is not set.
294      */
295     @NonNull private LogSessionId mLogSessionId = LogSessionId.LOG_SESSION_ID_NONE;
296 
297     //---------------------------------------------------------
298     // Constructor, Finalize
299     //--------------------
300     /**
301      * Class constructor.
302      * Though some invalid parameters will result in an {@link IllegalArgumentException} exception,
303      * other errors do not.  Thus you should call {@link #getState()} immediately after construction
304      * to confirm that the object is usable.
305      * @param audioSource the recording source.
306      *   See {@link MediaRecorder.AudioSource} for the recording source definitions.
307      * @param sampleRateInHz the sample rate expressed in Hertz. 44100Hz is currently the only
308      *   rate that is guaranteed to work on all devices, but other rates such as 22050,
309      *   16000, and 11025 may work on some devices.
310      *   {@link AudioFormat#SAMPLE_RATE_UNSPECIFIED} means to use a route-dependent value
311      *   which is usually the sample rate of the source.
312      *   {@link #getSampleRate()} can be used to retrieve the actual sample rate chosen.
313      * @param channelConfig describes the configuration of the audio channels.
314      *   See {@link AudioFormat#CHANNEL_IN_MONO} and
315      *   {@link AudioFormat#CHANNEL_IN_STEREO}.  {@link AudioFormat#CHANNEL_IN_MONO} is guaranteed
316      *   to work on all devices.
317      * @param audioFormat the format in which the audio data is to be returned.
318      *   See {@link AudioFormat#ENCODING_PCM_8BIT}, {@link AudioFormat#ENCODING_PCM_16BIT},
319      *   and {@link AudioFormat#ENCODING_PCM_FLOAT}.
320      * @param bufferSizeInBytes the total size (in bytes) of the buffer where audio data is written
321      *   to during the recording. New audio data can be read from this buffer in smaller chunks
322      *   than this size. See {@link #getMinBufferSize(int, int, int)} to determine the minimum
323      *   required buffer size for the successful creation of an AudioRecord instance. Using values
324      *   smaller than getMinBufferSize() will result in an initialization failure.
325      * @throws java.lang.IllegalArgumentException
326      */
327     @RequiresPermission(android.Manifest.permission.RECORD_AUDIO)
AudioRecord(int audioSource, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes)328     public AudioRecord(int audioSource, int sampleRateInHz, int channelConfig, int audioFormat,
329             int bufferSizeInBytes)
330     throws IllegalArgumentException {
331         this((new AudioAttributes.Builder())
332                     .setInternalCapturePreset(audioSource)
333                     .build(),
334                 (new AudioFormat.Builder())
335                     .setChannelMask(getChannelMaskFromLegacyConfig(channelConfig,
336                                         true/*allow legacy configurations*/))
337                     .setEncoding(audioFormat)
338                     .setSampleRate(sampleRateInHz)
339                     .build(),
340                 bufferSizeInBytes,
341                 AudioManager.AUDIO_SESSION_ID_GENERATE);
342     }
343 
344     /**
345      * @hide
346      * Class constructor with {@link AudioAttributes} and {@link AudioFormat}.
347      * @param attributes a non-null {@link AudioAttributes} instance. Use
348      *     {@link AudioAttributes.Builder#setCapturePreset(int)} for configuring the audio
349      *     source for this instance.
350      * @param format a non-null {@link AudioFormat} instance describing the format of the data
351      *     that will be recorded through this AudioRecord. See {@link AudioFormat.Builder} for
352      *     configuring the audio format parameters such as encoding, channel mask and sample rate.
353      * @param bufferSizeInBytes the total size (in bytes) of the buffer where audio data is written
354      *   to during the recording. New audio data can be read from this buffer in smaller chunks
355      *   than this size. See {@link #getMinBufferSize(int, int, int)} to determine the minimum
356      *   required buffer size for the successful creation of an AudioRecord instance. Using values
357      *   smaller than getMinBufferSize() will result in an initialization failure.
358      * @param sessionId ID of audio session the AudioRecord must be attached to, or
359      *   {@link AudioManager#AUDIO_SESSION_ID_GENERATE} if the session isn't known at construction
360      *   time. See also {@link AudioManager#generateAudioSessionId()} to obtain a session ID before
361      *   construction.
362      * @throws IllegalArgumentException
363      */
364     @SystemApi
365     @RequiresPermission(android.Manifest.permission.RECORD_AUDIO)
AudioRecord(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes, int sessionId)366     public AudioRecord(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,
367             int sessionId) throws IllegalArgumentException {
368         this(attributes, format, bufferSizeInBytes, sessionId,
369                 ActivityThread.currentApplication(),
370                 0 /*maxSharedAudioHistoryMs*/, 0 /* halInputFlags */);
371     }
372 
373     /**
374      * @hide
375      * Class constructor with {@link AudioAttributes} and {@link AudioFormat}.
376      * @param attributes a non-null {@link AudioAttributes} instance. Use
377      *     {@link AudioAttributes.Builder#setCapturePreset(int)} for configuring the audio
378      *     source for this instance.
379      * @param format a non-null {@link AudioFormat} instance describing the format of the data
380      *     that will be recorded through this AudioRecord. See {@link AudioFormat.Builder} for
381      *     configuring the audio format parameters such as encoding, channel mask and sample rate.
382      * @param bufferSizeInBytes the total size (in bytes) of the buffer where audio data is written
383      *   to during the recording. New audio data can be read from this buffer in smaller chunks
384      *   than this size. See {@link #getMinBufferSize(int, int, int)} to determine the minimum
385      *   required buffer size for the successful creation of an AudioRecord instance. Using values
386      *   smaller than getMinBufferSize() will result in an initialization failure.
387      * @param sessionId ID of audio session the AudioRecord must be attached to, or
388      *   {@link AudioManager#AUDIO_SESSION_ID_GENERATE} if the session isn't known at construction
389      *   time. See also {@link AudioManager#generateAudioSessionId()} to obtain a session ID before
390      *   construction.
391      * @param context An optional context on whose behalf the recoding is performed.
392      * @param maxSharedAudioHistoryMs
393      * @param halInputFlags Bitfield indexed by {@link AudioInputFlags} which is passed to the HAL.
394      * @throws IllegalArgumentException
395      */
AudioRecord(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes, int sessionId, @Nullable Context context, int maxSharedAudioHistoryMs, int halInputFlags)396     private AudioRecord(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,
397             int sessionId, @Nullable Context context,
398             int maxSharedAudioHistoryMs, int halInputFlags) throws IllegalArgumentException {
399         mRecordingState = RECORDSTATE_STOPPED;
400         mHalInputFlags = halInputFlags;
401         if (attributes == null) {
402             throw new IllegalArgumentException("Illegal null AudioAttributes");
403         }
404         if (format == null) {
405             throw new IllegalArgumentException("Illegal null AudioFormat");
406         }
407 
408         // remember which looper is associated with the AudioRecord instanciation
409         if ((mInitializationLooper = Looper.myLooper()) == null) {
410             mInitializationLooper = Looper.getMainLooper();
411         }
412 
413         // is this AudioRecord using REMOTE_SUBMIX at full volume?
414         if (attributes.getCapturePreset() == MediaRecorder.AudioSource.REMOTE_SUBMIX) {
415             final AudioAttributes.Builder ab =
416                     new AudioAttributes.Builder(attributes);
417             HashSet<String> filteredTags = new HashSet<String>();
418             final Iterator<String> tagsIter = attributes.getTags().iterator();
419             while (tagsIter.hasNext()) {
420                 final String tag = tagsIter.next();
421                 if (tag.equalsIgnoreCase(SUBMIX_FIXED_VOLUME)) {
422                     mIsSubmixFullVolume = true;
423                     Log.v(TAG, "Will record from REMOTE_SUBMIX at full fixed volume");
424                 } else { // SUBMIX_FIXED_VOLUME: is not to be propagated to the native layers
425                     filteredTags.add(tag);
426                 }
427             }
428             ab.replaceTags(filteredTags);
429             attributes = ab.build();
430         }
431 
432         mAudioAttributes = attributes;
433 
434         int rate = format.getSampleRate();
435         if (rate == AudioFormat.SAMPLE_RATE_UNSPECIFIED) {
436             rate = 0;
437         }
438 
439         int encoding = AudioFormat.ENCODING_DEFAULT;
440         if ((format.getPropertySetMask() & AudioFormat.AUDIO_FORMAT_HAS_PROPERTY_ENCODING) != 0)
441         {
442             encoding = format.getEncoding();
443         }
444 
445         audioParamCheck(mAudioAttributes.getCapturePreset(), rate, encoding);
446 
447         if ((format.getPropertySetMask()
448                 & AudioFormat.AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_INDEX_MASK) != 0) {
449             mChannelIndexMask = format.getChannelIndexMask();
450             mChannelCount = format.getChannelCount();
451         }
452         if ((format.getPropertySetMask()
453                 & AudioFormat.AUDIO_FORMAT_HAS_PROPERTY_CHANNEL_MASK) != 0) {
454             mChannelMask = getChannelMaskFromLegacyConfig(format.getChannelMask(), false);
455             mChannelCount = format.getChannelCount();
456         } else if (mChannelIndexMask == 0) {
457             mChannelMask = getChannelMaskFromLegacyConfig(AudioFormat.CHANNEL_IN_DEFAULT, false);
458             mChannelCount =  AudioFormat.channelCountFromInChannelMask(mChannelMask);
459         }
460 
461         audioBuffSizeCheck(bufferSizeInBytes);
462 
463         AttributionSource attributionSource = (context != null)
464                 ? context.getAttributionSource() : AttributionSource.myAttributionSource();
465         if (attributionSource.getPackageName() == null) {
466             // Command line utility
467             attributionSource = attributionSource.withPackageName("uid:" + Binder.getCallingUid());
468         }
469 
470         int[] sampleRate = new int[] {mSampleRate};
471         int[] session = new int[1];
472         session[0] = resolveSessionId(context, sessionId);
473 
474         //TODO: update native initialization when information about hardware init failure
475         //      due to capture device already open is available.
476         try (ScopedParcelState attributionSourceState = attributionSource.asScopedParcelState()) {
477             int initResult = native_setup(new WeakReference<AudioRecord>(this), mAudioAttributes,
478                     sampleRate, mChannelMask, mChannelIndexMask, mAudioFormat,
479                     mNativeBufferSizeInBytes, session, attributionSourceState.getParcel(),
480                     0 /*nativeRecordInJavaObj*/, maxSharedAudioHistoryMs, mHalInputFlags);
481             if (initResult != SUCCESS) {
482                 loge("Error code " + initResult + " when initializing native AudioRecord object.");
483                 return; // with mState == STATE_UNINITIALIZED
484             }
485         }
486 
487         mSampleRate = sampleRate[0];
488         mSessionId = session[0];
489 
490         mState = STATE_INITIALIZED;
491     }
492 
493     /**
494      * A constructor which explicitly connects a Native (C++) AudioRecord. For use by
495      * the AudioRecordRoutingProxy subclass.
496      * @param nativeRecordInJavaObj A C/C++ pointer to a native AudioRecord
497      * (associated with an OpenSL ES recorder). Note: the caller must ensure a correct
498      * value here as no error checking is or can be done.
499      */
AudioRecord(long nativeRecordInJavaObj)500     /*package*/ AudioRecord(long nativeRecordInJavaObj) {
501         mNativeAudioRecordHandle = 0;
502         mNativeJNIDataHandle = 0;
503 
504         // other initialization...
505         if (nativeRecordInJavaObj != 0) {
506             deferred_connect(nativeRecordInJavaObj);
507         } else {
508             mState = STATE_UNINITIALIZED;
509         }
510     }
511 
512     /**
513      * Sets an {@link AudioPolicy} to automatically unregister when the record is released.
514      *
515      * <p>This is to prevent users of the audio capture API from having to manually unregister the
516      * policy that was used to create the record.
517      */
unregisterAudioPolicyOnRelease(AudioPolicy audioPolicy)518     private void unregisterAudioPolicyOnRelease(AudioPolicy audioPolicy) {
519         mAudioCapturePolicy = audioPolicy;
520     }
521 
522     /**
523      * @hide
524      */
deferred_connect(long nativeRecordInJavaObj)525     /* package */ void deferred_connect(long  nativeRecordInJavaObj) {
526         if (mState != STATE_INITIALIZED) {
527             int[] session = {0};
528             int[] rates = {0};
529             //TODO: update native initialization when information about hardware init failure
530             //      due to capture device already open is available.
531             // Note that for this native_setup, we are providing an already created/initialized
532             // *Native* AudioRecord, so the attributes parameters to native_setup() are ignored.
533             final int initResult;
534             try (ScopedParcelState attributionSourceState = AttributionSource.myAttributionSource()
535                     .asScopedParcelState()) {
536                 initResult = native_setup(new WeakReference<>(this),
537                         null /*mAudioAttributes*/,
538                         rates /*mSampleRates*/,
539                         0 /*mChannelMask*/,
540                         0 /*mChannelIndexMask*/,
541                         0 /*mAudioFormat*/,
542                         0 /*mNativeBufferSizeInBytes*/,
543                         session,
544                         attributionSourceState.getParcel(),
545                         nativeRecordInJavaObj,
546                         0 /*maxSharedAudioHistoryMs*/,
547                         0 /*halInputFlags*/);
548             }
549             if (initResult != SUCCESS) {
550                 loge("Error code "+initResult+" when initializing native AudioRecord object.");
551                 return; // with mState == STATE_UNINITIALIZED
552             }
553 
554             mSessionId = session[0];
555 
556             mState = STATE_INITIALIZED;
557         }
558     }
559 
560     /** @hide */
getAudioAttributes()561     public AudioAttributes getAudioAttributes() {
562         return mAudioAttributes;
563     }
564 
565     /**
566      * Builder class for {@link AudioRecord} objects.
567      * Use this class to configure and create an <code>AudioRecord</code> instance. By setting the
568      * recording source and audio format parameters, you indicate which of
569      * those vary from the default behavior on the device.
570      * <p> Here is an example where <code>Builder</code> is used to specify all {@link AudioFormat}
571      * parameters, to be used by a new <code>AudioRecord</code> instance:
572      *
573      * <pre class="prettyprint">
574      * AudioRecord recorder = new AudioRecord.Builder()
575      *         .setAudioSource(MediaRecorder.AudioSource.VOICE_COMMUNICATION)
576      *         .setAudioFormat(new AudioFormat.Builder()
577      *                 .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
578      *                 .setSampleRate(32000)
579      *                 .setChannelMask(AudioFormat.CHANNEL_IN_MONO)
580      *                 .build())
581      *         .setBufferSizeInBytes(2*minBuffSize)
582      *         .build();
583      * </pre>
584      * <p>
585      * If the audio source is not set with {@link #setAudioSource(int)},
586      * {@link MediaRecorder.AudioSource#DEFAULT} is used.
587      * <br>If the audio format is not specified or is incomplete, its channel configuration will be
588      * {@link AudioFormat#CHANNEL_IN_MONO}, and the encoding will be
589      * {@link AudioFormat#ENCODING_PCM_16BIT}.
590      * The sample rate will depend on the device actually selected for capture and can be queried
591      * with {@link #getSampleRate()} method.
592      * <br>If the buffer size is not specified with {@link #setBufferSizeInBytes(int)},
593      * the minimum buffer size for the source is used.
594      */
595     public static class Builder {
596 
597         private static final String ERROR_MESSAGE_SOURCE_MISMATCH =
598                 "Cannot both set audio source and set playback capture config";
599 
600         private AudioPlaybackCaptureConfiguration mAudioPlaybackCaptureConfiguration;
601         private AudioAttributes mAttributes;
602         private AudioFormat mFormat;
603         private Context mContext;
604         private int mBufferSizeInBytes;
605         private int mSessionId = AudioManager.AUDIO_SESSION_ID_GENERATE;
606         private int mPrivacySensitive = PRIVACY_SENSITIVE_DEFAULT;
607         private int mMaxSharedAudioHistoryMs = 0;
608         private int mCallRedirectionMode = AudioManager.CALL_REDIRECT_NONE;
609         private boolean mIsHotwordStream = false;
610         private boolean mIsHotwordLookback = false;
611 
612         private static final int PRIVACY_SENSITIVE_DEFAULT = -1;
613         private static final int PRIVACY_SENSITIVE_DISABLED = 0;
614         private static final int PRIVACY_SENSITIVE_ENABLED = 1;
615 
616         /**
617          * Constructs a new Builder with the default values as described above.
618          */
Builder()619         public Builder() {
620         }
621 
622         /**
623          * @param source the audio source.
624          * See {@link MediaRecorder.AudioSource} for the supported audio source definitions.
625          * @return the same Builder instance.
626          * @throws IllegalArgumentException
627          */
setAudioSource(@ource int source)628         public Builder setAudioSource(@Source int source) throws IllegalArgumentException {
629             Preconditions.checkState(
630                     mAudioPlaybackCaptureConfiguration == null,
631                     ERROR_MESSAGE_SOURCE_MISMATCH);
632             if ( (source < MediaRecorder.AudioSource.DEFAULT) ||
633                     (source > MediaRecorder.getAudioSourceMax()) ) {
634                 throw new IllegalArgumentException("Invalid audio source " + source);
635             }
636             mAttributes = new AudioAttributes.Builder()
637                     .setInternalCapturePreset(source)
638                     .build();
639             return this;
640         }
641 
642         /**
643          * Sets the context the record belongs to. This context will be used to pull information,
644          * such as {@link android.content.AttributionSource} and device specific session ids,
645          * which will be associated with the {@link AudioRecord} the AudioRecord.
646          * However, the context itself will not be retained by the AudioRecord.
647          * @param context a non-null {@link Context} instance
648          * @return the same Builder instance.
649          */
setContext(@onNull Context context)650         public @NonNull Builder setContext(@NonNull Context context) {
651             // keep reference, we only copy the data when building
652             mContext = Objects.requireNonNull(context);
653             return this;
654         }
655 
656         /**
657          * @hide
658          * To be only used by system components. Allows specifying non-public capture presets
659          * @param attributes a non-null {@link AudioAttributes} instance that contains the capture
660          *     preset to be used.
661          * @return the same Builder instance.
662          * @throws IllegalArgumentException
663          */
664         @SystemApi
setAudioAttributes(@onNull AudioAttributes attributes)665         public Builder setAudioAttributes(@NonNull AudioAttributes attributes)
666                 throws IllegalArgumentException {
667             if (attributes == null) {
668                 throw new IllegalArgumentException("Illegal null AudioAttributes argument");
669             }
670             if (attributes.getCapturePreset() == MediaRecorder.AudioSource.AUDIO_SOURCE_INVALID) {
671                 throw new IllegalArgumentException(
672                         "No valid capture preset in AudioAttributes argument");
673             }
674             // keep reference, we only copy the data when building
675             mAttributes = attributes;
676             return this;
677         }
678 
679         /**
680          * Sets the format of the audio data to be captured.
681          * @param format a non-null {@link AudioFormat} instance
682          * @return the same Builder instance.
683          * @throws IllegalArgumentException
684          */
setAudioFormat(@onNull AudioFormat format)685         public Builder setAudioFormat(@NonNull AudioFormat format) throws IllegalArgumentException {
686             if (format == null) {
687                 throw new IllegalArgumentException("Illegal null AudioFormat argument");
688             }
689             // keep reference, we only copy the data when building
690             mFormat = format;
691             return this;
692         }
693 
694         /**
695          * Sets the total size (in bytes) of the buffer where audio data is written
696          * during the recording. New audio data can be read from this buffer in smaller chunks
697          * than this size. See {@link #getMinBufferSize(int, int, int)} to determine the minimum
698          * required buffer size for the successful creation of an AudioRecord instance.
699          * Since bufferSizeInBytes may be internally increased to accommodate the source
700          * requirements, use {@link #getBufferSizeInFrames()} to determine the actual buffer size
701          * in frames.
702          * @param bufferSizeInBytes a value strictly greater than 0
703          * @return the same Builder instance.
704          * @throws IllegalArgumentException
705          */
setBufferSizeInBytes(int bufferSizeInBytes)706         public Builder setBufferSizeInBytes(int bufferSizeInBytes) throws IllegalArgumentException {
707             if (bufferSizeInBytes <= 0) {
708                 throw new IllegalArgumentException("Invalid buffer size " + bufferSizeInBytes);
709             }
710             mBufferSizeInBytes = bufferSizeInBytes;
711             return this;
712         }
713 
714         /**
715          * Sets the {@link AudioRecord} to record audio played by other apps.
716          *
717          * @param config Defines what apps to record audio from (i.e., via either their uid or
718          *               the type of audio).
719          * @throws IllegalStateException if called in conjunction with {@link #setAudioSource(int)}.
720          * @throws NullPointerException if {@code config} is null.
721          */
setAudioPlaybackCaptureConfig( @onNull AudioPlaybackCaptureConfiguration config)722         public @NonNull Builder setAudioPlaybackCaptureConfig(
723                 @NonNull AudioPlaybackCaptureConfiguration config) {
724             Preconditions.checkNotNull(
725                     config, "Illegal null AudioPlaybackCaptureConfiguration argument");
726             Preconditions.checkState(
727                     mAttributes == null,
728                     ERROR_MESSAGE_SOURCE_MISMATCH);
729             mAudioPlaybackCaptureConfiguration = config;
730             return this;
731         }
732 
733         /**
734          * Indicates that this capture request is privacy sensitive and that
735          * any concurrent capture is not permitted.
736          * <p>
737          * The default is not privacy sensitive except when the audio source set with
738          * {@link #setAudioSource(int)} is {@link MediaRecorder.AudioSource#VOICE_COMMUNICATION} or
739          * {@link MediaRecorder.AudioSource#CAMCORDER}.
740          * <p>
741          * Always takes precedence over default from audio source when set explicitly.
742          * <p>
743          * Using this API is only permitted when the audio source is one of:
744          * <ul>
745          * <li>{@link MediaRecorder.AudioSource#MIC}</li>
746          * <li>{@link MediaRecorder.AudioSource#CAMCORDER}</li>
747          * <li>{@link MediaRecorder.AudioSource#VOICE_RECOGNITION}</li>
748          * <li>{@link MediaRecorder.AudioSource#VOICE_COMMUNICATION}</li>
749          * <li>{@link MediaRecorder.AudioSource#UNPROCESSED}</li>
750          * <li>{@link MediaRecorder.AudioSource#VOICE_PERFORMANCE}</li>
751          * </ul>
752          * Invoking {@link #build()} will throw an UnsupportedOperationException if this
753          * condition is not met.
754          * @param privacySensitive True if capture from this AudioRecord must be marked as privacy
755          * sensitive, false otherwise.
756          */
setPrivacySensitive(boolean privacySensitive)757         public @NonNull Builder setPrivacySensitive(boolean privacySensitive) {
758             mPrivacySensitive =
759                 privacySensitive ? PRIVACY_SENSITIVE_ENABLED : PRIVACY_SENSITIVE_DISABLED;
760             return this;
761         }
762 
763         /**
764          * @hide
765          * To be only used by system components.
766          *
767          * Note, that if there's a device specific session id asociated with the context, explicitly
768          * setting a session id using this method will override it.
769          * @param sessionId ID of audio session the AudioRecord must be attached to, or
770          *     {@link AudioManager#AUDIO_SESSION_ID_GENERATE} if the session isn't known at
771          *     construction time.
772          * @return the same Builder instance.
773          * @throws IllegalArgumentException
774          */
775         @SystemApi
setSessionId(int sessionId)776         public Builder setSessionId(int sessionId) throws IllegalArgumentException {
777             if (sessionId < 0) {
778                 throw new IllegalArgumentException("Invalid session ID " + sessionId);
779             }
780             // Do not override a session ID previously set with setSharedAudioEvent()
781             if (mSessionId == AudioManager.AUDIO_SESSION_ID_GENERATE) {
782                 mSessionId = sessionId;
783             } else {
784                 Log.e(TAG, "setSessionId() called twice or after setSharedAudioEvent()");
785             }
786             return this;
787         }
788 
buildAudioPlaybackCaptureRecord()789         private @NonNull AudioRecord buildAudioPlaybackCaptureRecord() {
790             AudioMix audioMix = mAudioPlaybackCaptureConfiguration.createAudioMix(mFormat);
791             MediaProjection projection = mAudioPlaybackCaptureConfiguration.getMediaProjection();
792             AudioPolicy audioPolicy = new AudioPolicy.Builder(/*context=*/ null)
793                     .setMediaProjection(projection)
794                     .addMix(audioMix).build();
795 
796             int error = AudioManager.registerAudioPolicyStatic(audioPolicy);
797             if (error != 0) {
798                 throw new UnsupportedOperationException("Error: could not register audio policy");
799             }
800 
801             AudioRecord record = audioPolicy.createAudioRecordSink(audioMix);
802             if (record == null) {
803                 throw new UnsupportedOperationException("Cannot create AudioRecord");
804             }
805             record.unregisterAudioPolicyOnRelease(audioPolicy);
806             return record;
807         }
808 
809         /**
810          * @hide
811          * Sets the {@link AudioRecord} call redirection mode.
812          * Used when creating an AudioRecord to extract audio from call downlink path. The mode
813          * indicates if the call is a PSTN call or a VoIP call in which case a dynamic audio
814          * policy is created to forward all playback with voice communication usage this record.
815          *
816          * @param callRedirectionMode one of
817          * {@link AudioManager#CALL_REDIRECT_NONE},
818          * {@link AudioManager#CALL_REDIRECT_PSTN},
819          * or {@link AAudioManager#CALL_REDIRECT_VOIP}.
820          * @return the same Builder instance.
821          * @throws IllegalArgumentException if {@code callRedirectionMode} is not valid.
822          */
setCallRedirectionMode( @udioManager.CallRedirectionMode int callRedirectionMode)823         public @NonNull Builder setCallRedirectionMode(
824                 @AudioManager.CallRedirectionMode int callRedirectionMode) {
825             switch (callRedirectionMode) {
826                 case AudioManager.CALL_REDIRECT_NONE:
827                 case AudioManager.CALL_REDIRECT_PSTN:
828                 case AudioManager.CALL_REDIRECT_VOIP:
829                     mCallRedirectionMode = callRedirectionMode;
830                     break;
831                 default:
832                     throw new IllegalArgumentException(
833                             "Invalid call redirection mode " + callRedirectionMode);
834             }
835             return this;
836         }
837 
buildCallExtractionRecord()838         private @NonNull AudioRecord buildCallExtractionRecord() {
839             AudioMixingRule audioMixingRule = new AudioMixingRule.Builder()
840                     .addMixRule(AudioMixingRule.RULE_MATCH_ATTRIBUTE_USAGE,
841                             new AudioAttributes.Builder()
842                                 .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
843                                 .setForCallRedirection()
844                                 .build())
845                     .addMixRule(AudioMixingRule.RULE_MATCH_ATTRIBUTE_USAGE,
846                             new AudioAttributes.Builder()
847                                 .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING)
848                                 .setForCallRedirection()
849                                 .build())
850                     .setTargetMixRole(AudioMixingRule.MIX_ROLE_PLAYERS)
851                     .build();
852             AudioMix audioMix = new AudioMix.Builder(audioMixingRule)
853                     .setFormat(mFormat)
854                     .setRouteFlags(AudioMix.ROUTE_FLAG_LOOP_BACK)
855                     .build();
856             AudioPolicy audioPolicy = new AudioPolicy.Builder(null).addMix(audioMix).build();
857             if (AudioManager.registerAudioPolicyStatic(audioPolicy) != 0) {
858                 throw new UnsupportedOperationException("Error: could not register audio policy");
859             }
860             AudioRecord record = audioPolicy.createAudioRecordSink(audioMix);
861             if (record == null) {
862                 throw new UnsupportedOperationException("Cannot create extraction AudioRecord");
863             }
864             record.unregisterAudioPolicyOnRelease(audioPolicy);
865             return record;
866         }
867 
868         /**
869          * @hide
870          * Specifies the maximum duration in the past of the this AudioRecord's capture buffer
871          * that can be shared with another app by calling
872          * {@link AudioRecord#shareAudioHistory(String, long)}.
873          * @param maxSharedAudioHistoryMillis the maximum duration that will be available
874          *                                    in milliseconds.
875          * @return the same Builder instance.
876          * @throws IllegalArgumentException
877          *
878          */
879         @SystemApi
880         @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_HOTWORD)
setMaxSharedAudioHistoryMillis(long maxSharedAudioHistoryMillis)881         public @NonNull Builder setMaxSharedAudioHistoryMillis(long maxSharedAudioHistoryMillis)
882                 throws IllegalArgumentException {
883             if (maxSharedAudioHistoryMillis <= 0
884                     || maxSharedAudioHistoryMillis > MAX_SHARED_AUDIO_HISTORY_MS) {
885                 throw new IllegalArgumentException("Illegal maxSharedAudioHistoryMillis argument");
886             }
887             mMaxSharedAudioHistoryMs = (int) maxSharedAudioHistoryMillis;
888             return this;
889         }
890 
891         /**
892          * @hide
893          * Indicates that this AudioRecord will use the audio history shared by another app's
894          * AudioRecord. See {@link AudioRecord#shareAudioHistory(String, long)}.
895          * The audio session ID set with {@link AudioRecord.Builder#setSessionId(int)} will be
896          * ignored if this method is used.
897          * @param event The {@link MediaSyncEvent} provided by the app sharing its audio history
898          *              with this AudioRecord.
899          * @return the same Builder instance.
900          * @throws IllegalArgumentException
901          */
902         @SystemApi
setSharedAudioEvent(@onNull MediaSyncEvent event)903         public @NonNull Builder setSharedAudioEvent(@NonNull MediaSyncEvent event)
904                 throws IllegalArgumentException {
905             Objects.requireNonNull(event);
906             if (event.getType() != MediaSyncEvent.SYNC_EVENT_SHARE_AUDIO_HISTORY) {
907                 throw new IllegalArgumentException(
908                         "Invalid event type " + event.getType());
909             }
910             if (event.getAudioSessionId() == AudioSystem.AUDIO_SESSION_ALLOCATE) {
911                 throw new IllegalArgumentException(
912                         "Invalid session ID " + event.getAudioSessionId());
913             }
914             // This prevails over a session ID set with setSessionId()
915             mSessionId = event.getAudioSessionId();
916             return this;
917         }
918 
919         /**
920          * @hide
921          * Set to indicate that the requested AudioRecord object should produce the same type
922          * of audio content that the hotword recognition model consumes. SoundTrigger hotword
923          * recognition will not be disrupted. The source in the set AudioAttributes and the set
924          * audio source will be overridden if this API is used.
925          * <br> Use {@link AudioManager#isHotwordStreamSupported(boolean)} to query support.
926          * @param hotwordContent true if AudioRecord should produce content captured from the
927          *        hotword pipeline. false if AudioRecord should produce content captured outside
928          *        the hotword pipeline.
929          * @return the same Builder instance.
930          **/
931         @SystemApi
932         @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_HOTWORD)
setRequestHotwordStream(boolean hotwordContent)933         public @NonNull Builder setRequestHotwordStream(boolean hotwordContent) {
934             mIsHotwordStream = hotwordContent;
935             return this;
936         }
937 
938         /**
939          * @hide
940          * Set to indicate that the requested AudioRecord object should produce the same type
941          * of audio content that the hotword recognition model consumes and that the stream will
942          * be able to provide buffered audio content from an unspecified duration prior to stream
943          * open. The source in the set AudioAttributes and the set audio source will be overridden
944          * if this API is used.
945          * <br> Use {@link AudioManager#isHotwordStreamSupported(boolean)} to query support.
946          * <br> If this is set, {@link AudioRecord.Builder#setRequestHotwordStream(boolean)}
947          * must not be set, or {@link AudioRecord.Builder#build()} will throw.
948          * @param hotwordLookbackContent true if AudioRecord should produce content captured from
949          *        the hotword pipeline with capture content from prior to open. false if AudioRecord
950          *        should not capture such content.
951          * to stream open is requested.
952          * @return the same Builder instance.
953          **/
954         @SystemApi
955         @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_HOTWORD)
setRequestHotwordLookbackStream(boolean hotwordLookbackContent)956         public @NonNull Builder setRequestHotwordLookbackStream(boolean hotwordLookbackContent) {
957             mIsHotwordLookback = hotwordLookbackContent;
958             return this;
959         }
960 
961 
962         /**
963          * @return a new {@link AudioRecord} instance successfully initialized with all
964          *     the parameters set on this <code>Builder</code>.
965          * @throws UnsupportedOperationException if the parameters set on the <code>Builder</code>
966          *     were incompatible, if the parameters are not supported by the device, if the caller
967          *     does not hold the appropriate permissions, or if the device was not available.
968          */
969         @RequiresPermission(android.Manifest.permission.RECORD_AUDIO)
build()970         public AudioRecord build() throws UnsupportedOperationException {
971             if (mAudioPlaybackCaptureConfiguration != null) {
972                 return buildAudioPlaybackCaptureRecord();
973             }
974             int halInputFlags = 0;
975             if (mIsHotwordStream) {
976                 if (mIsHotwordLookback) {
977                     throw new UnsupportedOperationException(
978                             "setRequestHotwordLookbackStream and " +
979                             "setRequestHotwordStream used concurrently");
980                 } else {
981                     halInputFlags = (1 << AudioInputFlags.HOTWORD_TAP);
982                 }
983             } else if (mIsHotwordLookback) {
984                     halInputFlags = (1 << AudioInputFlags.HOTWORD_TAP) |
985                         (1 << AudioInputFlags.HW_LOOKBACK);
986             }
987 
988             if (mFormat == null) {
989                 mFormat = new AudioFormat.Builder()
990                         .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
991                         .setChannelMask(AudioFormat.CHANNEL_IN_MONO)
992                         .build();
993             } else {
994                 if (mFormat.getEncoding() == AudioFormat.ENCODING_INVALID) {
995                     mFormat = new AudioFormat.Builder(mFormat)
996                             .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
997                             .build();
998                 }
999                 if (mFormat.getChannelMask() == AudioFormat.CHANNEL_INVALID
1000                         && mFormat.getChannelIndexMask() == AudioFormat.CHANNEL_INVALID) {
1001                     mFormat = new AudioFormat.Builder(mFormat)
1002                             .setChannelMask(AudioFormat.CHANNEL_IN_MONO)
1003                             .build();
1004                 }
1005             }
1006             if (mAttributes == null) {
1007                 mAttributes = new AudioAttributes.Builder()
1008                         .setInternalCapturePreset(MediaRecorder.AudioSource.DEFAULT)
1009                         .build();
1010             }
1011 
1012             if (mIsHotwordStream || mIsHotwordLookback) {
1013                 mAttributes = new AudioAttributes.Builder(mAttributes)
1014                         .setInternalCapturePreset(MediaRecorder.AudioSource.VOICE_RECOGNITION)
1015                         .build();
1016             }
1017 
1018             // If mPrivacySensitive is default, the privacy flag is already set
1019             // according to audio source in audio attributes.
1020             if (mPrivacySensitive != PRIVACY_SENSITIVE_DEFAULT) {
1021                 int source = mAttributes.getCapturePreset();
1022                 if (source == MediaRecorder.AudioSource.REMOTE_SUBMIX
1023                         || source == MediaRecorder.AudioSource.RADIO_TUNER
1024                         || source == MediaRecorder.AudioSource.VOICE_DOWNLINK
1025                         || source == MediaRecorder.AudioSource.VOICE_UPLINK
1026                         || source == MediaRecorder.AudioSource.VOICE_CALL
1027                         || source == MediaRecorder.AudioSource.ECHO_REFERENCE) {
1028                     throw new UnsupportedOperationException(
1029                             "Cannot request private capture with source: " + source);
1030                 }
1031 
1032                 mAttributes = new AudioAttributes.Builder(mAttributes)
1033                         .setInternalCapturePreset(source)
1034                         .setPrivacySensitive(mPrivacySensitive == PRIVACY_SENSITIVE_ENABLED)
1035                         .build();
1036             }
1037 
1038             if (mCallRedirectionMode == AudioManager.CALL_REDIRECT_VOIP) {
1039                 return buildCallExtractionRecord();
1040             } else if (mCallRedirectionMode == AudioManager.CALL_REDIRECT_PSTN) {
1041                 mAttributes = new AudioAttributes.Builder(mAttributes)
1042                         .setForCallRedirection()
1043                         .build();
1044             }
1045 
1046             try {
1047                 // If the buffer size is not specified,
1048                 // use a single frame for the buffer size and let the
1049                 // native code figure out the minimum buffer size.
1050                 if (mBufferSizeInBytes == 0) {
1051                     mBufferSizeInBytes = mFormat.getChannelCount()
1052                             * mFormat.getBytesPerSample(mFormat.getEncoding());
1053                 }
1054                 final AudioRecord record = new AudioRecord(
1055                         mAttributes, mFormat, mBufferSizeInBytes, mSessionId, mContext,
1056                                     mMaxSharedAudioHistoryMs, halInputFlags);
1057                 if (record.getState() == STATE_UNINITIALIZED) {
1058                     // release is not necessary
1059                     throw new UnsupportedOperationException("Cannot create AudioRecord");
1060                 }
1061                 return record;
1062             } catch (IllegalArgumentException e) {
1063                 throw new UnsupportedOperationException(e.getMessage());
1064             }
1065         }
1066     }
1067 
1068     /**
1069      * Helper method to resolve session id to be used for AudioRecord initialization.
1070      *
1071      * This method will assign session id in following way:
1072      * 1. Explicitly requested session id has the highest priority, if there is one,
1073      *    it will be used.
1074      * 2. If there's device-specific session id asociated with the provided context,
1075      *    it will be used.
1076      * 3. Otherwise {@link AUDIO_SESSION_ID_GENERATE} is returned.
1077      *
1078      * @param context {@link Context} to use for extraction of device specific session id.
1079      * @param requestedSessionId explicitly requested session id or AUDIO_SESSION_ID_GENERATE.
1080      * @return session id to be passed to AudioService for the {@link AudioRecord} instance given
1081      *   provided {@link Context} instance and explicitly requested session id.
1082      */
resolveSessionId(@ullable Context context, int requestedSessionId)1083     private static int resolveSessionId(@Nullable Context context, int requestedSessionId) {
1084         if (requestedSessionId != AUDIO_SESSION_ID_GENERATE) {
1085             // Use explicitly requested session id.
1086             return requestedSessionId;
1087         }
1088 
1089         if (context == null) {
1090             return AUDIO_SESSION_ID_GENERATE;
1091         }
1092 
1093         int deviceId = context.getDeviceId();
1094         if (deviceId == DEVICE_ID_DEFAULT) {
1095             return AUDIO_SESSION_ID_GENERATE;
1096         }
1097 
1098         VirtualDeviceManager vdm = context.getSystemService(VirtualDeviceManager.class);
1099         if (vdm == null || vdm.getDevicePolicy(deviceId, POLICY_TYPE_AUDIO)
1100                 == DEVICE_POLICY_DEFAULT) {
1101             return AUDIO_SESSION_ID_GENERATE;
1102         }
1103 
1104         return vdm.getAudioRecordingSessionId(deviceId);
1105     }
1106 
1107     // Convenience method for the constructor's parameter checks.
1108     // This, getChannelMaskFromLegacyConfig and audioBuffSizeCheck are where constructor
1109     // IllegalArgumentException-s are thrown
getChannelMaskFromLegacyConfig(int inChannelConfig, boolean allowLegacyConfig)1110     private static int getChannelMaskFromLegacyConfig(int inChannelConfig,
1111             boolean allowLegacyConfig) {
1112         int mask;
1113         switch (inChannelConfig) {
1114         case AudioFormat.CHANNEL_IN_DEFAULT: // AudioFormat.CHANNEL_CONFIGURATION_DEFAULT
1115         case AudioFormat.CHANNEL_IN_MONO:
1116         case AudioFormat.CHANNEL_CONFIGURATION_MONO:
1117         mask = AudioFormat.CHANNEL_IN_MONO;
1118         break;
1119         case AudioFormat.CHANNEL_IN_STEREO:
1120         case AudioFormat.CHANNEL_CONFIGURATION_STEREO:
1121             mask = AudioFormat.CHANNEL_IN_STEREO;
1122             break;
1123         case (AudioFormat.CHANNEL_IN_FRONT | AudioFormat.CHANNEL_IN_BACK):
1124             mask = inChannelConfig;
1125             break;
1126         default:
1127             throw new IllegalArgumentException("Unsupported channel configuration.");
1128         }
1129 
1130         if (!allowLegacyConfig && ((inChannelConfig == AudioFormat.CHANNEL_CONFIGURATION_MONO)
1131                 || (inChannelConfig == AudioFormat.CHANNEL_CONFIGURATION_STEREO))) {
1132             // only happens with the constructor that uses AudioAttributes and AudioFormat
1133             throw new IllegalArgumentException("Unsupported deprecated configuration.");
1134         }
1135 
1136         return mask;
1137     }
1138 
1139     // postconditions:
1140     //    mRecordSource is valid
1141     //    mAudioFormat is valid
1142     //    mSampleRate is valid
audioParamCheck(int audioSource, int sampleRateInHz, int audioFormat)1143     private void audioParamCheck(int audioSource, int sampleRateInHz, int audioFormat)
1144             throws IllegalArgumentException {
1145 
1146         //--------------
1147         // audio source
1148         if ((audioSource < MediaRecorder.AudioSource.DEFAULT)
1149                 || ((audioSource > MediaRecorder.getAudioSourceMax())
1150                     && (audioSource != MediaRecorder.AudioSource.RADIO_TUNER)
1151                     && (audioSource != MediaRecorder.AudioSource.ECHO_REFERENCE)
1152                     && (audioSource != MediaRecorder.AudioSource.HOTWORD)
1153                     && (audioSource != MediaRecorder.AudioSource.ULTRASOUND))) {
1154             throw new IllegalArgumentException("Invalid audio source " + audioSource);
1155         }
1156         mRecordSource = audioSource;
1157 
1158         //--------------
1159         // sample rate
1160         if ((sampleRateInHz < AudioFormat.SAMPLE_RATE_HZ_MIN ||
1161                 sampleRateInHz > AudioFormat.SAMPLE_RATE_HZ_MAX) &&
1162                 sampleRateInHz != AudioFormat.SAMPLE_RATE_UNSPECIFIED) {
1163             throw new IllegalArgumentException(sampleRateInHz
1164                     + "Hz is not a supported sample rate.");
1165         }
1166         mSampleRate = sampleRateInHz;
1167 
1168         //--------------
1169         // audio format
1170         switch (audioFormat) {
1171             case AudioFormat.ENCODING_DEFAULT:
1172                 mAudioFormat = AudioFormat.ENCODING_PCM_16BIT;
1173                 break;
1174             case AudioFormat.ENCODING_PCM_24BIT_PACKED:
1175             case AudioFormat.ENCODING_PCM_32BIT:
1176             case AudioFormat.ENCODING_PCM_FLOAT:
1177             case AudioFormat.ENCODING_PCM_16BIT:
1178             case AudioFormat.ENCODING_PCM_8BIT:
1179                 mAudioFormat = audioFormat;
1180                 break;
1181             default:
1182                 throw new IllegalArgumentException("Unsupported sample encoding " + audioFormat
1183                         + ". Should be ENCODING_PCM_8BIT, ENCODING_PCM_16BIT,"
1184                         + " ENCODING_PCM_24BIT_PACKED, ENCODING_PCM_32BIT,"
1185                         + " or ENCODING_PCM_FLOAT.");
1186         }
1187     }
1188 
1189 
1190     // Convenience method for the contructor's audio buffer size check.
1191     // preconditions:
1192     //    mChannelCount is valid
1193     //    mAudioFormat is AudioFormat.ENCODING_PCM_8BIT, AudioFormat.ENCODING_PCM_16BIT,
1194     //                 or AudioFormat.ENCODING_PCM_FLOAT
1195     // postcondition:
1196     //    mNativeBufferSizeInBytes is valid (multiple of frame size, positive)
audioBuffSizeCheck(int audioBufferSize)1197     private void audioBuffSizeCheck(int audioBufferSize) throws IllegalArgumentException {
1198         // NB: this section is only valid with PCM data.
1199         // To update when supporting compressed formats
1200         int frameSizeInBytes = mChannelCount
1201             * (AudioFormat.getBytesPerSample(mAudioFormat));
1202         if ((audioBufferSize % frameSizeInBytes != 0) || (audioBufferSize < 1)) {
1203             throw new IllegalArgumentException("Invalid audio buffer size " + audioBufferSize
1204                     + " (frame size " + frameSizeInBytes + ")");
1205         }
1206 
1207         mNativeBufferSizeInBytes = audioBufferSize;
1208     }
1209 
1210 
1211 
1212     /**
1213      * Releases the native AudioRecord resources.
1214      * The object can no longer be used and the reference should be set to null
1215      * after a call to release()
1216      */
release()1217     public void release() {
1218         try {
1219             stop();
1220         } catch(IllegalStateException ise) {
1221             // don't raise an exception, we're releasing the resources.
1222         }
1223         if (mAudioCapturePolicy != null) {
1224             AudioManager.unregisterAudioPolicyAsyncStatic(mAudioCapturePolicy);
1225             mAudioCapturePolicy = null;
1226         }
1227         native_release();
1228         mState = STATE_UNINITIALIZED;
1229     }
1230 
1231 
1232     @Override
finalize()1233     protected void finalize() {
1234         // will cause stop() to be called, and if appropriate, will handle fixed volume recording
1235         release();
1236     }
1237 
1238 
1239     //--------------------------------------------------------------------------
1240     // Getters
1241     //--------------------
1242     /**
1243      * Returns the configured audio sink sample rate in Hz.
1244      * The sink sample rate never changes after construction.
1245      * If the constructor had a specific sample rate, then the sink sample rate is that value.
1246      * If the constructor had {@link AudioFormat#SAMPLE_RATE_UNSPECIFIED},
1247      * then the sink sample rate is a route-dependent default value based on the source [sic].
1248      */
getSampleRate()1249     public int getSampleRate() {
1250         return mSampleRate;
1251     }
1252 
1253     /**
1254      * Returns the audio recording source.
1255      * @see MediaRecorder.AudioSource
1256      */
getAudioSource()1257     public int getAudioSource() {
1258         return mRecordSource;
1259     }
1260 
1261     /**
1262      * Returns the configured audio data encoding. See {@link AudioFormat#ENCODING_PCM_8BIT},
1263      * {@link AudioFormat#ENCODING_PCM_16BIT}, and {@link AudioFormat#ENCODING_PCM_FLOAT}.
1264      */
getAudioFormat()1265     public int getAudioFormat() {
1266         return mAudioFormat;
1267     }
1268 
1269     /**
1270      * Returns the configured channel position mask.
1271      * <p> See {@link AudioFormat#CHANNEL_IN_MONO}
1272      * and {@link AudioFormat#CHANNEL_IN_STEREO}.
1273      * This method may return {@link AudioFormat#CHANNEL_INVALID} if
1274      * a channel index mask is used.
1275      * Consider {@link #getFormat()} instead, to obtain an {@link AudioFormat},
1276      * which contains both the channel position mask and the channel index mask.
1277      */
getChannelConfiguration()1278     public int getChannelConfiguration() {
1279         return mChannelMask;
1280     }
1281 
1282     /**
1283      * Returns the configured <code>AudioRecord</code> format.
1284      * @return an {@link AudioFormat} containing the
1285      * <code>AudioRecord</code> parameters at the time of configuration.
1286      */
getFormat()1287     public @NonNull AudioFormat getFormat() {
1288         AudioFormat.Builder builder = new AudioFormat.Builder()
1289             .setSampleRate(mSampleRate)
1290             .setEncoding(mAudioFormat);
1291         if (mChannelMask != AudioFormat.CHANNEL_INVALID) {
1292             builder.setChannelMask(mChannelMask);
1293         }
1294         if (mChannelIndexMask != AudioFormat.CHANNEL_INVALID  /* 0 */) {
1295             builder.setChannelIndexMask(mChannelIndexMask);
1296         }
1297         return builder.build();
1298     }
1299 
1300     /**
1301      * Returns the configured number of channels.
1302      */
getChannelCount()1303     public int getChannelCount() {
1304         return mChannelCount;
1305     }
1306 
1307     /**
1308      * Returns the state of the AudioRecord instance. This is useful after the
1309      * AudioRecord instance has been created to check if it was initialized
1310      * properly. This ensures that the appropriate hardware resources have been
1311      * acquired.
1312      * @see AudioRecord#STATE_INITIALIZED
1313      * @see AudioRecord#STATE_UNINITIALIZED
1314      */
getState()1315     public int getState() {
1316         return mState;
1317     }
1318 
1319     /**
1320      * Returns the recording state of the AudioRecord instance.
1321      * @see AudioRecord#RECORDSTATE_STOPPED
1322      * @see AudioRecord#RECORDSTATE_RECORDING
1323      */
getRecordingState()1324     public int getRecordingState() {
1325         synchronized (mRecordingStateLock) {
1326             return mRecordingState;
1327         }
1328     }
1329 
1330     /**
1331      *  Returns the frame count of the native <code>AudioRecord</code> buffer.
1332      *  This is greater than or equal to the bufferSizeInBytes converted to frame units
1333      *  specified in the <code>AudioRecord</code> constructor or Builder.
1334      *  The native frame count may be enlarged to accommodate the requirements of the
1335      *  source on creation or if the <code>AudioRecord</code>
1336      *  is subsequently rerouted.
1337      *  @return current size in frames of the <code>AudioRecord</code> buffer.
1338      *  @throws IllegalStateException
1339      */
getBufferSizeInFrames()1340     public int getBufferSizeInFrames() {
1341         return native_get_buffer_size_in_frames();
1342     }
1343 
1344     /**
1345      * Returns the notification marker position expressed in frames.
1346      */
getNotificationMarkerPosition()1347     public int getNotificationMarkerPosition() {
1348         return native_get_marker_pos();
1349     }
1350 
1351     /**
1352      * Returns the notification update period expressed in frames.
1353      */
getPositionNotificationPeriod()1354     public int getPositionNotificationPeriod() {
1355         return native_get_pos_update_period();
1356     }
1357 
1358     /**
1359      * Poll for an {@link AudioTimestamp} on demand.
1360      * <p>
1361      * The AudioTimestamp reflects the frame delivery information at
1362      * the earliest point available in the capture pipeline.
1363      * <p>
1364      * Calling {@link #startRecording()} following a {@link #stop()} will reset
1365      * the frame count to 0.
1366      *
1367      * @param outTimestamp a caller provided non-null AudioTimestamp instance,
1368      *        which is updated with the AudioRecord frame delivery information upon success.
1369      * @param timebase one of
1370      *        {@link AudioTimestamp#TIMEBASE_BOOTTIME AudioTimestamp.TIMEBASE_BOOTTIME} or
1371      *        {@link AudioTimestamp#TIMEBASE_MONOTONIC AudioTimestamp.TIMEBASE_MONOTONIC},
1372      *        used to select the clock for the AudioTimestamp time.
1373      * @return {@link #SUCCESS} if a timestamp is available,
1374      *         or {@link #ERROR_INVALID_OPERATION} if a timestamp not available.
1375      */
getTimestamp(@onNull AudioTimestamp outTimestamp, @AudioTimestamp.Timebase int timebase)1376      public int getTimestamp(@NonNull AudioTimestamp outTimestamp,
1377              @AudioTimestamp.Timebase int timebase)
1378      {
1379          if (outTimestamp == null ||
1380                  (timebase != AudioTimestamp.TIMEBASE_BOOTTIME
1381                  && timebase != AudioTimestamp.TIMEBASE_MONOTONIC)) {
1382              throw new IllegalArgumentException();
1383          }
1384          return native_get_timestamp(outTimestamp, timebase);
1385      }
1386 
1387     /**
1388      * Returns the minimum buffer size required for the successful creation of an AudioRecord
1389      * object, in byte units.
1390      * Note that this size doesn't guarantee a smooth recording under load, and higher values
1391      * should be chosen according to the expected frequency at which the AudioRecord instance
1392      * will be polled for new data.
1393      * See {@link #AudioRecord(int, int, int, int, int)} for more information on valid
1394      * configuration values.
1395      * @param sampleRateInHz the sample rate expressed in Hertz.
1396      *   {@link AudioFormat#SAMPLE_RATE_UNSPECIFIED} is not permitted.
1397      * @param channelConfig describes the configuration of the audio channels.
1398      *   See {@link AudioFormat#CHANNEL_IN_MONO} and
1399      *   {@link AudioFormat#CHANNEL_IN_STEREO}
1400      * @param audioFormat the format in which the audio data is represented.
1401      *   See {@link AudioFormat#ENCODING_PCM_16BIT}.
1402      * @return {@link #ERROR_BAD_VALUE} if the recording parameters are not supported by the
1403      *  hardware, or an invalid parameter was passed,
1404      *  or {@link #ERROR} if the implementation was unable to query the hardware for its
1405      *  input properties,
1406      *   or the minimum buffer size expressed in bytes.
1407      * @see #AudioRecord(int, int, int, int, int)
1408      */
getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat)1409     static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat) {
1410         int channelCount = 0;
1411         switch (channelConfig) {
1412         case AudioFormat.CHANNEL_IN_DEFAULT: // AudioFormat.CHANNEL_CONFIGURATION_DEFAULT
1413         case AudioFormat.CHANNEL_IN_MONO:
1414         case AudioFormat.CHANNEL_CONFIGURATION_MONO:
1415             channelCount = 1;
1416             break;
1417         case AudioFormat.CHANNEL_IN_STEREO:
1418         case AudioFormat.CHANNEL_CONFIGURATION_STEREO:
1419         case (AudioFormat.CHANNEL_IN_FRONT | AudioFormat.CHANNEL_IN_BACK):
1420             channelCount = 2;
1421             break;
1422         case AudioFormat.CHANNEL_INVALID:
1423         default:
1424             loge("getMinBufferSize(): Invalid channel configuration.");
1425             return ERROR_BAD_VALUE;
1426         }
1427 
1428         int size = native_get_min_buff_size(sampleRateInHz, channelCount, audioFormat);
1429         if (size == 0) {
1430             return ERROR_BAD_VALUE;
1431         }
1432         else if (size == -1) {
1433             return ERROR;
1434         }
1435         else {
1436             return size;
1437         }
1438     }
1439 
1440     /**
1441      * Returns the audio session ID.
1442      *
1443      * @return the ID of the audio session this AudioRecord belongs to.
1444      */
getAudioSessionId()1445     public int getAudioSessionId() {
1446         return mSessionId;
1447     }
1448 
1449     /**
1450      * Returns whether this AudioRecord is marked as privacy sensitive or not.
1451      * <p>
1452      * See {@link Builder#setPrivacySensitive(boolean)}
1453      * <p>
1454      * @return true if privacy sensitive, false otherwise
1455      */
isPrivacySensitive()1456     public boolean isPrivacySensitive() {
1457         return (mAudioAttributes.getAllFlags() & AudioAttributes.FLAG_CAPTURE_PRIVATE) != 0;
1458     }
1459 
1460     /**
1461      * @hide
1462      * Returns whether the AudioRecord object produces the same type of audio content that
1463      * the hotword recognition model consumes.
1464      * <br> If {@link isHotwordLookbackStream(boolean)} is true, this will return false
1465      * <br> See {@link Builder#setRequestHotwordStream(boolean)}
1466      * @return true if AudioRecord produces hotword content, false otherwise
1467      **/
1468     @SystemApi
isHotwordStream()1469     public boolean isHotwordStream() {
1470         return ((mHalInputFlags & (1 << AudioInputFlags.HOTWORD_TAP)) != 0 &&
1471                  (mHalInputFlags & (1 << AudioInputFlags.HW_LOOKBACK)) == 0);
1472     }
1473 
1474     /**
1475      * @hide
1476      * Returns whether the AudioRecord object produces the same type of audio content that
1477      * the hotword recognition model consumes, and includes capture content from prior to
1478      * stream open.
1479      * <br> See {@link Builder#setRequestHotwordLookbackStream(boolean)}
1480      * @return true if AudioRecord produces hotword capture content from
1481      * prior to stream open, false otherwise
1482      **/
1483     @SystemApi
isHotwordLookbackStream()1484     public boolean isHotwordLookbackStream() {
1485         return ((mHalInputFlags & (1 << AudioInputFlags.HW_LOOKBACK)) != 0);
1486     }
1487 
1488 
1489     //---------------------------------------------------------
1490     // Transport control methods
1491     //--------------------
1492     /**
1493      * Starts recording from the AudioRecord instance.
1494      * @throws IllegalStateException
1495      */
startRecording()1496     public void startRecording()
1497     throws IllegalStateException {
1498         if (mState != STATE_INITIALIZED) {
1499             throw new IllegalStateException("startRecording() called on an "
1500                     + "uninitialized AudioRecord.");
1501         }
1502 
1503         // start recording
1504         synchronized(mRecordingStateLock) {
1505             if (native_start(MediaSyncEvent.SYNC_EVENT_NONE, 0) == SUCCESS) {
1506                 handleFullVolumeRec(true);
1507                 mRecordingState = RECORDSTATE_RECORDING;
1508             }
1509         }
1510     }
1511 
1512     /**
1513      * Starts recording from the AudioRecord instance when the specified synchronization event
1514      * occurs on the specified audio session.
1515      * @throws IllegalStateException
1516      * @param syncEvent event that triggers the capture.
1517      * @see MediaSyncEvent
1518      */
startRecording(MediaSyncEvent syncEvent)1519     public void startRecording(MediaSyncEvent syncEvent)
1520     throws IllegalStateException {
1521         if (mState != STATE_INITIALIZED) {
1522             throw new IllegalStateException("startRecording() called on an "
1523                     + "uninitialized AudioRecord.");
1524         }
1525 
1526         // start recording
1527         synchronized(mRecordingStateLock) {
1528             if (native_start(syncEvent.getType(), syncEvent.getAudioSessionId()) == SUCCESS) {
1529                 handleFullVolumeRec(true);
1530                 mRecordingState = RECORDSTATE_RECORDING;
1531             }
1532         }
1533     }
1534 
1535     /**
1536      * Stops recording.
1537      * @throws IllegalStateException
1538      */
stop()1539     public void stop()
1540     throws IllegalStateException {
1541         if (mState != STATE_INITIALIZED) {
1542             throw new IllegalStateException("stop() called on an uninitialized AudioRecord.");
1543         }
1544 
1545         // stop recording
1546         synchronized(mRecordingStateLock) {
1547             handleFullVolumeRec(false);
1548             native_stop();
1549             mRecordingState = RECORDSTATE_STOPPED;
1550         }
1551     }
1552 
1553     private final IBinder mICallBack = new Binder();
handleFullVolumeRec(boolean starting)1554     private void handleFullVolumeRec(boolean starting) {
1555         if (!mIsSubmixFullVolume) {
1556             return;
1557         }
1558         final IBinder b = ServiceManager.getService(android.content.Context.AUDIO_SERVICE);
1559         final IAudioService ias = IAudioService.Stub.asInterface(b);
1560         try {
1561             ias.forceRemoteSubmixFullVolume(starting, mICallBack);
1562         } catch (RemoteException e) {
1563             Log.e(TAG, "Error talking to AudioService when handling full submix volume", e);
1564         }
1565     }
1566 
1567     //---------------------------------------------------------
1568     // Audio data supply
1569     //--------------------
1570     /**
1571      * Reads audio data from the audio hardware for recording into a byte array.
1572      * The format specified in the AudioRecord constructor should be
1573      * {@link AudioFormat#ENCODING_PCM_8BIT} to correspond to the data in the array.
1574      * @param audioData the array to which the recorded audio data is written.
1575      * @param offsetInBytes index in audioData from which the data is written expressed in bytes.
1576      * @param sizeInBytes the number of requested bytes.
1577      * @return zero or the positive number of bytes that were read, or one of the following
1578      *    error codes. The number of bytes will not exceed sizeInBytes.
1579      * <ul>
1580      * <li>{@link #ERROR_INVALID_OPERATION} if the object isn't properly initialized</li>
1581      * <li>{@link #ERROR_BAD_VALUE} if the parameters don't resolve to valid data and indexes</li>
1582      * <li>{@link #ERROR_DEAD_OBJECT} if the object is not valid anymore and
1583      *    needs to be recreated. The dead object error code is not returned if some data was
1584      *    successfully transferred. In this case, the error is returned at the next read()</li>
1585      * <li>{@link #ERROR} in case of other error</li>
1586      * </ul>
1587      */
read(@onNull byte[] audioData, int offsetInBytes, int sizeInBytes)1588     public int read(@NonNull byte[] audioData, int offsetInBytes, int sizeInBytes) {
1589         return read(audioData, offsetInBytes, sizeInBytes, READ_BLOCKING);
1590     }
1591 
1592     /**
1593      * Reads audio data from the audio hardware for recording into a byte array.
1594      * The format specified in the AudioRecord constructor should be
1595      * {@link AudioFormat#ENCODING_PCM_8BIT} to correspond to the data in the array.
1596      * The format can be {@link AudioFormat#ENCODING_PCM_16BIT}, but this is deprecated.
1597      * @param audioData the array to which the recorded audio data is written.
1598      * @param offsetInBytes index in audioData to which the data is written expressed in bytes.
1599      *        Must not be negative, or cause the data access to go out of bounds of the array.
1600      * @param sizeInBytes the number of requested bytes.
1601      *        Must not be negative, or cause the data access to go out of bounds of the array.
1602      * @param readMode one of {@link #READ_BLOCKING}, {@link #READ_NON_BLOCKING}.
1603      *     <br>With {@link #READ_BLOCKING}, the read will block until all the requested data
1604      *     is read.
1605      *     <br>With {@link #READ_NON_BLOCKING}, the read will return immediately after
1606      *     reading as much audio data as possible without blocking.
1607      * @return zero or the positive number of bytes that were read, or one of the following
1608      *    error codes. The number of bytes will be a multiple of the frame size in bytes
1609      *    not to exceed sizeInBytes.
1610      * <ul>
1611      * <li>{@link #ERROR_INVALID_OPERATION} if the object isn't properly initialized</li>
1612      * <li>{@link #ERROR_BAD_VALUE} if the parameters don't resolve to valid data and indexes</li>
1613      * <li>{@link #ERROR_DEAD_OBJECT} if the object is not valid anymore and
1614      *    needs to be recreated. The dead object error code is not returned if some data was
1615      *    successfully transferred. In this case, the error is returned at the next read()</li>
1616      * <li>{@link #ERROR} in case of other error</li>
1617      * </ul>
1618      */
read(@onNull byte[] audioData, int offsetInBytes, int sizeInBytes, @ReadMode int readMode)1619     public int read(@NonNull byte[] audioData, int offsetInBytes, int sizeInBytes,
1620             @ReadMode int readMode) {
1621         // Note: we allow reads of extended integers into a byte array.
1622         if (mState != STATE_INITIALIZED  || mAudioFormat == AudioFormat.ENCODING_PCM_FLOAT) {
1623             return ERROR_INVALID_OPERATION;
1624         }
1625 
1626         if ((readMode != READ_BLOCKING) && (readMode != READ_NON_BLOCKING)) {
1627             Log.e(TAG, "AudioRecord.read() called with invalid blocking mode");
1628             return ERROR_BAD_VALUE;
1629         }
1630 
1631         if ( (audioData == null) || (offsetInBytes < 0 ) || (sizeInBytes < 0)
1632                 || (offsetInBytes + sizeInBytes < 0)  // detect integer overflow
1633                 || (offsetInBytes + sizeInBytes > audioData.length)) {
1634             return ERROR_BAD_VALUE;
1635         }
1636 
1637         return native_read_in_byte_array(audioData, offsetInBytes, sizeInBytes,
1638                 readMode == READ_BLOCKING);
1639     }
1640 
1641     /**
1642      * Reads audio data from the audio hardware for recording into a short array.
1643      * The format specified in the AudioRecord constructor should be
1644      * {@link AudioFormat#ENCODING_PCM_16BIT} to correspond to the data in the array.
1645      * @param audioData the array to which the recorded audio data is written.
1646      * @param offsetInShorts index in audioData to which the data is written expressed in shorts.
1647      *        Must not be negative, or cause the data access to go out of bounds of the array.
1648      * @param sizeInShorts the number of requested shorts.
1649      *        Must not be negative, or cause the data access to go out of bounds of the array.
1650      * @return zero or the positive number of shorts that were read, or one of the following
1651      *    error codes. The number of shorts will be a multiple of the channel count not to exceed
1652      *    sizeInShorts.
1653      * <ul>
1654      * <li>{@link #ERROR_INVALID_OPERATION} if the object isn't properly initialized</li>
1655      * <li>{@link #ERROR_BAD_VALUE} if the parameters don't resolve to valid data and indexes</li>
1656      * <li>{@link #ERROR_DEAD_OBJECT} if the object is not valid anymore and
1657      *    needs to be recreated. The dead object error code is not returned if some data was
1658      *    successfully transferred. In this case, the error is returned at the next read()</li>
1659      * <li>{@link #ERROR} in case of other error</li>
1660      * </ul>
1661      */
read(@onNull short[] audioData, int offsetInShorts, int sizeInShorts)1662     public int read(@NonNull short[] audioData, int offsetInShorts, int sizeInShorts) {
1663         return read(audioData, offsetInShorts, sizeInShorts, READ_BLOCKING);
1664     }
1665 
1666     /**
1667      * Reads audio data from the audio hardware for recording into a short array.
1668      * The format specified in the AudioRecord constructor should be
1669      * {@link AudioFormat#ENCODING_PCM_16BIT} to correspond to the data in the array.
1670      * @param audioData the array to which the recorded audio data is written.
1671      * @param offsetInShorts index in audioData from which the data is written expressed in shorts.
1672      *        Must not be negative, or cause the data access to go out of bounds of the array.
1673      * @param sizeInShorts the number of requested shorts.
1674      *        Must not be negative, or cause the data access to go out of bounds of the array.
1675      * @param readMode one of {@link #READ_BLOCKING}, {@link #READ_NON_BLOCKING}.
1676      *     <br>With {@link #READ_BLOCKING}, the read will block until all the requested data
1677      *     is read.
1678      *     <br>With {@link #READ_NON_BLOCKING}, the read will return immediately after
1679      *     reading as much audio data as possible without blocking.
1680      * @return zero or the positive number of shorts that were read, or one of the following
1681      *    error codes. The number of shorts will be a multiple of the channel count not to exceed
1682      *    sizeInShorts.
1683      * <ul>
1684      * <li>{@link #ERROR_INVALID_OPERATION} if the object isn't properly initialized</li>
1685      * <li>{@link #ERROR_BAD_VALUE} if the parameters don't resolve to valid data and indexes</li>
1686      * <li>{@link #ERROR_DEAD_OBJECT} if the object is not valid anymore and
1687      *    needs to be recreated. The dead object error code is not returned if some data was
1688      *    successfully transferred. In this case, the error is returned at the next read()</li>
1689      * <li>{@link #ERROR} in case of other error</li>
1690      * </ul>
1691      */
read(@onNull short[] audioData, int offsetInShorts, int sizeInShorts, @ReadMode int readMode)1692     public int read(@NonNull short[] audioData, int offsetInShorts, int sizeInShorts,
1693             @ReadMode int readMode) {
1694         if (mState != STATE_INITIALIZED
1695                 || mAudioFormat == AudioFormat.ENCODING_PCM_FLOAT
1696                 // use ByteBuffer instead for later encodings
1697                 || mAudioFormat > AudioFormat.ENCODING_LEGACY_SHORT_ARRAY_THRESHOLD) {
1698             return ERROR_INVALID_OPERATION;
1699         }
1700 
1701         if ((readMode != READ_BLOCKING) && (readMode != READ_NON_BLOCKING)) {
1702             Log.e(TAG, "AudioRecord.read() called with invalid blocking mode");
1703             return ERROR_BAD_VALUE;
1704         }
1705 
1706         if ( (audioData == null) || (offsetInShorts < 0 ) || (sizeInShorts < 0)
1707                 || (offsetInShorts + sizeInShorts < 0)  // detect integer overflow
1708                 || (offsetInShorts + sizeInShorts > audioData.length)) {
1709             return ERROR_BAD_VALUE;
1710         }
1711         return native_read_in_short_array(audioData, offsetInShorts, sizeInShorts,
1712                 readMode == READ_BLOCKING);
1713     }
1714 
1715     /**
1716      * Reads audio data from the audio hardware for recording into a float array.
1717      * The format specified in the AudioRecord constructor should be
1718      * {@link AudioFormat#ENCODING_PCM_FLOAT} to correspond to the data in the array.
1719      * @param audioData the array to which the recorded audio data is written.
1720      * @param offsetInFloats index in audioData from which the data is written.
1721      *        Must not be negative, or cause the data access to go out of bounds of the array.
1722      * @param sizeInFloats the number of requested floats.
1723      *        Must not be negative, or cause the data access to go out of bounds of the array.
1724      * @param readMode one of {@link #READ_BLOCKING}, {@link #READ_NON_BLOCKING}.
1725      *     <br>With {@link #READ_BLOCKING}, the read will block until all the requested data
1726      *     is read.
1727      *     <br>With {@link #READ_NON_BLOCKING}, the read will return immediately after
1728      *     reading as much audio data as possible without blocking.
1729      * @return zero or the positive number of floats that were read, or one of the following
1730      *    error codes. The number of floats will be a multiple of the channel count not to exceed
1731      *    sizeInFloats.
1732      * <ul>
1733      * <li>{@link #ERROR_INVALID_OPERATION} if the object isn't properly initialized</li>
1734      * <li>{@link #ERROR_BAD_VALUE} if the parameters don't resolve to valid data and indexes</li>
1735      * <li>{@link #ERROR_DEAD_OBJECT} if the object is not valid anymore and
1736      *    needs to be recreated. The dead object error code is not returned if some data was
1737      *    successfully transferred. In this case, the error is returned at the next read()</li>
1738      * <li>{@link #ERROR} in case of other error</li>
1739      * </ul>
1740      */
read(@onNull float[] audioData, int offsetInFloats, int sizeInFloats, @ReadMode int readMode)1741     public int read(@NonNull float[] audioData, int offsetInFloats, int sizeInFloats,
1742             @ReadMode int readMode) {
1743         if (mState == STATE_UNINITIALIZED) {
1744             Log.e(TAG, "AudioRecord.read() called in invalid state STATE_UNINITIALIZED");
1745             return ERROR_INVALID_OPERATION;
1746         }
1747 
1748         if (mAudioFormat != AudioFormat.ENCODING_PCM_FLOAT) {
1749             Log.e(TAG, "AudioRecord.read(float[] ...) requires format ENCODING_PCM_FLOAT");
1750             return ERROR_INVALID_OPERATION;
1751         }
1752 
1753         if ((readMode != READ_BLOCKING) && (readMode != READ_NON_BLOCKING)) {
1754             Log.e(TAG, "AudioRecord.read() called with invalid blocking mode");
1755             return ERROR_BAD_VALUE;
1756         }
1757 
1758         if ((audioData == null) || (offsetInFloats < 0) || (sizeInFloats < 0)
1759                 || (offsetInFloats + sizeInFloats < 0)  // detect integer overflow
1760                 || (offsetInFloats + sizeInFloats > audioData.length)) {
1761             return ERROR_BAD_VALUE;
1762         }
1763 
1764         return native_read_in_float_array(audioData, offsetInFloats, sizeInFloats,
1765                 readMode == READ_BLOCKING);
1766     }
1767 
1768     /**
1769      * Reads audio data from the audio hardware for recording into a direct buffer. If this buffer
1770      * is not a direct buffer, this method will always return 0.
1771      * Note that the value returned by {@link java.nio.Buffer#position()} on this buffer is
1772      * unchanged after a call to this method.
1773      * The representation of the data in the buffer will depend on the format specified in
1774      * the AudioRecord constructor, and will be native endian.
1775      * @param audioBuffer the direct buffer to which the recorded audio data is written.
1776      * Data is written to audioBuffer.position().
1777      * @param sizeInBytes the number of requested bytes. It is recommended but not enforced
1778      *    that the number of bytes requested be a multiple of the frame size (sample size in
1779      *    bytes multiplied by the channel count).
1780      * @return zero or the positive number of bytes that were read, or one of the following
1781      *    error codes. The number of bytes will not exceed sizeInBytes and will be truncated to be
1782      *    a multiple of the frame size.
1783      * <ul>
1784      * <li>{@link #ERROR_INVALID_OPERATION} if the object isn't properly initialized</li>
1785      * <li>{@link #ERROR_BAD_VALUE} if the parameters don't resolve to valid data and indexes</li>
1786      * <li>{@link #ERROR_DEAD_OBJECT} if the object is not valid anymore and
1787      *    needs to be recreated. The dead object error code is not returned if some data was
1788      *    successfully transferred. In this case, the error is returned at the next read()</li>
1789      * <li>{@link #ERROR} in case of other error</li>
1790      * </ul>
1791      */
read(@onNull ByteBuffer audioBuffer, int sizeInBytes)1792     public int read(@NonNull ByteBuffer audioBuffer, int sizeInBytes) {
1793         return read(audioBuffer, sizeInBytes, READ_BLOCKING);
1794     }
1795 
1796     /**
1797      * Reads audio data from the audio hardware for recording into a direct buffer. If this buffer
1798      * is not a direct buffer, this method will always return 0.
1799      * Note that the value returned by {@link java.nio.Buffer#position()} on this buffer is
1800      * unchanged after a call to this method.
1801      * The representation of the data in the buffer will depend on the format specified in
1802      * the AudioRecord constructor, and will be native endian.
1803      * @param audioBuffer the direct buffer to which the recorded audio data is written.
1804      * Data is written to audioBuffer.position().
1805      * @param sizeInBytes the number of requested bytes. It is recommended but not enforced
1806      *    that the number of bytes requested be a multiple of the frame size (sample size in
1807      *    bytes multiplied by the channel count).
1808      * @param readMode one of {@link #READ_BLOCKING}, {@link #READ_NON_BLOCKING}.
1809      *     <br>With {@link #READ_BLOCKING}, the read will block until all the requested data
1810      *     is read.
1811      *     <br>With {@link #READ_NON_BLOCKING}, the read will return immediately after
1812      *     reading as much audio data as possible without blocking.
1813      * @return zero or the positive number of bytes that were read, or one of the following
1814      *    error codes. The number of bytes will not exceed sizeInBytes and will be truncated to be
1815      *    a multiple of the frame size.
1816      * <ul>
1817      * <li>{@link #ERROR_INVALID_OPERATION} if the object isn't properly initialized</li>
1818      * <li>{@link #ERROR_BAD_VALUE} if the parameters don't resolve to valid data and indexes</li>
1819      * <li>{@link #ERROR_DEAD_OBJECT} if the object is not valid anymore and
1820      *    needs to be recreated. The dead object error code is not returned if some data was
1821      *    successfully transferred. In this case, the error is returned at the next read()</li>
1822      * <li>{@link #ERROR} in case of other error</li>
1823      * </ul>
1824      */
read(@onNull ByteBuffer audioBuffer, int sizeInBytes, @ReadMode int readMode)1825     public int read(@NonNull ByteBuffer audioBuffer, int sizeInBytes, @ReadMode int readMode) {
1826         if (mState != STATE_INITIALIZED) {
1827             return ERROR_INVALID_OPERATION;
1828         }
1829 
1830         if ((readMode != READ_BLOCKING) && (readMode != READ_NON_BLOCKING)) {
1831             Log.e(TAG, "AudioRecord.read() called with invalid blocking mode");
1832             return ERROR_BAD_VALUE;
1833         }
1834 
1835         if ( (audioBuffer == null) || (sizeInBytes < 0) ) {
1836             return ERROR_BAD_VALUE;
1837         }
1838 
1839         return native_read_in_direct_buffer(audioBuffer, sizeInBytes, readMode == READ_BLOCKING);
1840     }
1841 
1842     /**
1843      *  Return Metrics data about the current AudioTrack instance.
1844      *
1845      * @return a {@link PersistableBundle} containing the set of attributes and values
1846      * available for the media being handled by this instance of AudioRecord
1847      * The attributes are descibed in {@link MetricsConstants}.
1848      *
1849      * Additional vendor-specific fields may also be present in
1850      * the return value.
1851      */
getMetrics()1852     public PersistableBundle getMetrics() {
1853         PersistableBundle bundle = native_getMetrics();
1854         return bundle;
1855     }
1856 
native_getMetrics()1857     private native PersistableBundle native_getMetrics();
1858 
1859     //--------------------------------------------------------------------------
1860     // Initialization / configuration
1861     //--------------------
1862     /**
1863      * Sets the listener the AudioRecord notifies when a previously set marker is reached or
1864      * for each periodic record head position update.
1865      * @param listener
1866      */
setRecordPositionUpdateListener(OnRecordPositionUpdateListener listener)1867     public void setRecordPositionUpdateListener(OnRecordPositionUpdateListener listener) {
1868         setRecordPositionUpdateListener(listener, null);
1869     }
1870 
1871     /**
1872      * Sets the listener the AudioRecord notifies when a previously set marker is reached or
1873      * for each periodic record head position update.
1874      * Use this method to receive AudioRecord events in the Handler associated with another
1875      * thread than the one in which you created the AudioRecord instance.
1876      * @param listener
1877      * @param handler the Handler that will receive the event notification messages.
1878      */
setRecordPositionUpdateListener(OnRecordPositionUpdateListener listener, Handler handler)1879     public void setRecordPositionUpdateListener(OnRecordPositionUpdateListener listener,
1880                                                     Handler handler) {
1881         synchronized (mPositionListenerLock) {
1882 
1883             mPositionListener = listener;
1884 
1885             if (listener != null) {
1886                 if (handler != null) {
1887                     mEventHandler = new NativeEventHandler(this, handler.getLooper());
1888                 } else {
1889                     // no given handler, use the looper the AudioRecord was created in
1890                     mEventHandler = new NativeEventHandler(this, mInitializationLooper);
1891                 }
1892             } else {
1893                 mEventHandler = null;
1894             }
1895         }
1896 
1897     }
1898 
1899 
1900     /**
1901      * Sets the marker position at which the listener is called, if set with
1902      * {@link #setRecordPositionUpdateListener(OnRecordPositionUpdateListener)} or
1903      * {@link #setRecordPositionUpdateListener(OnRecordPositionUpdateListener, Handler)}.
1904      * @param markerInFrames marker position expressed in frames
1905      * @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE},
1906      *  {@link #ERROR_INVALID_OPERATION}
1907      */
setNotificationMarkerPosition(int markerInFrames)1908     public int setNotificationMarkerPosition(int markerInFrames) {
1909         if (mState == STATE_UNINITIALIZED) {
1910             return ERROR_INVALID_OPERATION;
1911         }
1912         return native_set_marker_pos(markerInFrames);
1913     }
1914 
1915     /**
1916      * Returns an {@link AudioDeviceInfo} identifying the current routing of this AudioRecord.
1917      * Note: The query is only valid if the AudioRecord is currently recording. If it is not,
1918      * <code>getRoutedDevice()</code> will return null.
1919      */
1920     @Override
getRoutedDevice()1921     public AudioDeviceInfo getRoutedDevice() {
1922         int deviceId = native_getRoutedDeviceId();
1923         if (deviceId == 0) {
1924             return null;
1925         }
1926         return AudioManager.getDeviceForPortId(deviceId, AudioManager.GET_DEVICES_INPUTS);
1927     }
1928 
1929     /**
1930      * Must match the native definition in frameworks/av/service/audioflinger/Audioflinger.h.
1931      */
1932     private static final long MAX_SHARED_AUDIO_HISTORY_MS = 5000;
1933 
1934     /**
1935      * @hide
1936      * returns the maximum duration in milliseconds of the audio history that can be requested
1937      * to be made available to other clients using the same session with
1938      * {@Link Builder#setMaxSharedAudioHistory(long)}.
1939      */
1940     @SystemApi
getMaxSharedAudioHistoryMillis()1941     public static long getMaxSharedAudioHistoryMillis() {
1942         return MAX_SHARED_AUDIO_HISTORY_MS;
1943     }
1944 
1945     /**
1946      * @hide
1947      *
1948      * A privileged app with permission CAPTURE_AUDIO_HOTWORD can share part of its recent
1949      * capture history on a given AudioRecord with the following steps:
1950      * 1) Specify the maximum time in the past that will be available for other apps by calling
1951      * {@link Builder#setMaxSharedAudioHistoryMillis(long)} when creating the AudioRecord.
1952      * 2) Start recording and determine where the other app should start capturing in the past.
1953      * 3) Call this method with the package name of the app the history will be shared with and
1954      * the intended start time for this app's capture relative to this AudioRecord's start time.
1955      * 4) Communicate the {@link MediaSyncEvent} returned by this method to the other app.
1956      * 5) The other app will use the MediaSyncEvent when creating its AudioRecord with
1957      * {@link Builder#setSharedAudioEvent(MediaSyncEvent).
1958      * 6) Only after the other app has started capturing can this app stop capturing and
1959      * release its AudioRecord.
1960      * This method is intended to be called only once: if called multiple times, only the last
1961      * request will be honored.
1962      * The implementation is "best effort": if the specified start time if too far in the past
1963      * compared to the max available history specified, the start time will be adjusted to the
1964      * start of the available history.
1965      * @param sharedPackage the package the history will be shared with
1966      * @param startFromMillis the start time, relative to the initial start time of this
1967      *        AudioRecord, at which the other AudioRecord will start.
1968      * @return a {@link MediaSyncEvent} to be communicated to the app this AudioRecord's audio
1969      *         history will be shared with.
1970      * @throws IllegalArgumentException
1971      * @throws SecurityException
1972      */
1973     @SystemApi
1974     @RequiresPermission(android.Manifest.permission.CAPTURE_AUDIO_HOTWORD)
shareAudioHistory(@onNull String sharedPackage, @IntRange(from = 0) long startFromMillis)1975     @NonNull public MediaSyncEvent shareAudioHistory(@NonNull String sharedPackage,
1976                                   @IntRange(from = 0) long startFromMillis) {
1977         Objects.requireNonNull(sharedPackage);
1978         if (startFromMillis < 0) {
1979             throw new IllegalArgumentException("Illegal negative sharedAudioHistoryMs argument");
1980         }
1981         int status = native_shareAudioHistory(sharedPackage, startFromMillis);
1982         if (status == AudioSystem.BAD_VALUE) {
1983             throw new IllegalArgumentException("Illegal sharedAudioHistoryMs argument");
1984         } else if (status == AudioSystem.PERMISSION_DENIED) {
1985             throw new SecurityException("permission CAPTURE_AUDIO_HOTWORD required");
1986         }
1987         MediaSyncEvent event =
1988                 MediaSyncEvent.createEvent(MediaSyncEvent.SYNC_EVENT_SHARE_AUDIO_HISTORY);
1989         event.setAudioSessionId(mSessionId);
1990         return event;
1991     }
1992 
1993     /*
1994      * Call BEFORE adding a routing callback handler.
1995      */
1996     @GuardedBy("mRoutingChangeListeners")
testEnableNativeRoutingCallbacksLocked()1997     private void testEnableNativeRoutingCallbacksLocked() {
1998         if (mRoutingChangeListeners.size() == 0) {
1999             native_enableDeviceCallback();
2000         }
2001     }
2002 
2003     /*
2004      * Call AFTER removing a routing callback handler.
2005      */
2006     @GuardedBy("mRoutingChangeListeners")
testDisableNativeRoutingCallbacksLocked()2007     private void testDisableNativeRoutingCallbacksLocked() {
2008         if (mRoutingChangeListeners.size() == 0) {
2009             native_disableDeviceCallback();
2010         }
2011     }
2012 
2013     //--------------------------------------------------------------------------
2014     // (Re)Routing Info
2015     //--------------------
2016     /**
2017      * The list of AudioRouting.OnRoutingChangedListener interfaces added (with
2018      * {@link AudioRecord#addOnRoutingChangedListener} by an app to receive
2019      * (re)routing notifications.
2020      */
2021     @GuardedBy("mRoutingChangeListeners")
2022     private ArrayMap<AudioRouting.OnRoutingChangedListener,
2023             NativeRoutingEventHandlerDelegate> mRoutingChangeListeners = new ArrayMap<>();
2024 
2025     /**
2026      * Adds an {@link AudioRouting.OnRoutingChangedListener} to receive notifications of
2027      * routing changes on this AudioRecord.
2028      * @param listener The {@link AudioRouting.OnRoutingChangedListener} interface to receive
2029      * notifications of rerouting events.
2030      * @param handler  Specifies the {@link Handler} object for the thread on which to execute
2031      * the callback. If <code>null</code>, the {@link Handler} associated with the main
2032      * {@link Looper} will be used.
2033      */
2034     @Override
addOnRoutingChangedListener(AudioRouting.OnRoutingChangedListener listener, android.os.Handler handler)2035     public void addOnRoutingChangedListener(AudioRouting.OnRoutingChangedListener listener,
2036             android.os.Handler handler) {
2037         synchronized (mRoutingChangeListeners) {
2038             if (listener != null && !mRoutingChangeListeners.containsKey(listener)) {
2039                 testEnableNativeRoutingCallbacksLocked();
2040                 mRoutingChangeListeners.put(
2041                         listener, new NativeRoutingEventHandlerDelegate(this, listener,
2042                                 handler != null ? handler : new Handler(mInitializationLooper)));
2043             }
2044         }
2045     }
2046 
2047     /**
2048      * Removes an {@link AudioRouting.OnRoutingChangedListener} which has been previously added
2049     * to receive rerouting notifications.
2050     * @param listener The previously added {@link AudioRouting.OnRoutingChangedListener} interface
2051     * to remove.
2052     */
2053     @Override
removeOnRoutingChangedListener(AudioRouting.OnRoutingChangedListener listener)2054     public void removeOnRoutingChangedListener(AudioRouting.OnRoutingChangedListener listener) {
2055         synchronized (mRoutingChangeListeners) {
2056             if (mRoutingChangeListeners.containsKey(listener)) {
2057                 mRoutingChangeListeners.remove(listener);
2058                 testDisableNativeRoutingCallbacksLocked();
2059             }
2060         }
2061     }
2062 
2063     //--------------------------------------------------------------------------
2064     // (Re)Routing Info
2065     //--------------------
2066     /**
2067      * Defines the interface by which applications can receive notifications of
2068      * routing changes for the associated {@link AudioRecord}.
2069      *
2070      * @deprecated users should switch to the general purpose
2071      *             {@link AudioRouting.OnRoutingChangedListener} class instead.
2072      */
2073     @Deprecated
2074     public interface OnRoutingChangedListener extends AudioRouting.OnRoutingChangedListener {
2075         /**
2076          * Called when the routing of an AudioRecord changes from either and
2077          * explicit or policy rerouting. Use {@link #getRoutedDevice()} to
2078          * retrieve the newly routed-from device.
2079          */
onRoutingChanged(AudioRecord audioRecord)2080         public void onRoutingChanged(AudioRecord audioRecord);
2081 
2082         @Override
onRoutingChanged(AudioRouting router)2083         default public void onRoutingChanged(AudioRouting router) {
2084             if (router instanceof AudioRecord) {
2085                 onRoutingChanged((AudioRecord) router);
2086             }
2087         }
2088     }
2089 
2090     /**
2091      * Adds an {@link OnRoutingChangedListener} to receive notifications of routing changes
2092      * on this AudioRecord.
2093      * @param listener The {@link OnRoutingChangedListener} interface to receive notifications
2094      * of rerouting events.
2095      * @param handler  Specifies the {@link Handler} object for the thread on which to execute
2096      * the callback. If <code>null</code>, the {@link Handler} associated with the main
2097      * {@link Looper} will be used.
2098      * @deprecated users should switch to the general purpose
2099      *             {@link AudioRouting.OnRoutingChangedListener} class instead.
2100      */
2101     @Deprecated
addOnRoutingChangedListener(OnRoutingChangedListener listener, android.os.Handler handler)2102     public void addOnRoutingChangedListener(OnRoutingChangedListener listener,
2103             android.os.Handler handler) {
2104         addOnRoutingChangedListener((AudioRouting.OnRoutingChangedListener) listener, handler);
2105     }
2106 
2107     /**
2108       * Removes an {@link OnRoutingChangedListener} which has been previously added
2109      * to receive rerouting notifications.
2110      * @param listener The previously added {@link OnRoutingChangedListener} interface to remove.
2111      * @deprecated users should switch to the general purpose
2112      *             {@link AudioRouting.OnRoutingChangedListener} class instead.
2113      */
2114     @Deprecated
removeOnRoutingChangedListener(OnRoutingChangedListener listener)2115     public void removeOnRoutingChangedListener(OnRoutingChangedListener listener) {
2116         removeOnRoutingChangedListener((AudioRouting.OnRoutingChangedListener) listener);
2117     }
2118 
2119     /**
2120      * Sends device list change notification to all listeners.
2121      */
broadcastRoutingChange()2122     private void broadcastRoutingChange() {
2123         AudioManager.resetAudioPortGeneration();
2124         synchronized (mRoutingChangeListeners) {
2125             for (NativeRoutingEventHandlerDelegate delegate : mRoutingChangeListeners.values()) {
2126                 delegate.notifyClient();
2127             }
2128         }
2129     }
2130 
2131     /**
2132      * Sets the period at which the listener is called, if set with
2133      * {@link #setRecordPositionUpdateListener(OnRecordPositionUpdateListener)} or
2134      * {@link #setRecordPositionUpdateListener(OnRecordPositionUpdateListener, Handler)}.
2135      * It is possible for notifications to be lost if the period is too small.
2136      * @param periodInFrames update period expressed in frames
2137      * @return error code or success, see {@link #SUCCESS}, {@link #ERROR_INVALID_OPERATION}
2138      */
setPositionNotificationPeriod(int periodInFrames)2139     public int setPositionNotificationPeriod(int periodInFrames) {
2140         if (mState == STATE_UNINITIALIZED) {
2141             return ERROR_INVALID_OPERATION;
2142         }
2143         return native_set_pos_update_period(periodInFrames);
2144     }
2145 
2146     //--------------------------------------------------------------------------
2147     // Explicit Routing
2148     //--------------------
2149     private AudioDeviceInfo mPreferredDevice = null;
2150 
2151     /**
2152      * Specifies an audio device (via an {@link AudioDeviceInfo} object) to route
2153      * the input to this AudioRecord.
2154      * @param deviceInfo The {@link AudioDeviceInfo} specifying the audio source.
2155      *  If deviceInfo is null, default routing is restored.
2156      * @return true if successful, false if the specified {@link AudioDeviceInfo} is non-null and
2157      * does not correspond to a valid audio input device.
2158      */
2159     @Override
setPreferredDevice(AudioDeviceInfo deviceInfo)2160     public boolean setPreferredDevice(AudioDeviceInfo deviceInfo) {
2161         // Do some validation....
2162         if (deviceInfo != null && !deviceInfo.isSource()) {
2163             return false;
2164         }
2165 
2166         int preferredDeviceId = deviceInfo != null ? deviceInfo.getId() : 0;
2167         boolean status = native_setInputDevice(preferredDeviceId);
2168         if (status == true) {
2169             synchronized (this) {
2170                 mPreferredDevice = deviceInfo;
2171             }
2172         }
2173         return status;
2174     }
2175 
2176     /**
2177      * Returns the selected input specified by {@link #setPreferredDevice}. Note that this
2178      * is not guarenteed to correspond to the actual device being used for recording.
2179      */
2180     @Override
getPreferredDevice()2181     public AudioDeviceInfo getPreferredDevice() {
2182         synchronized (this) {
2183             return mPreferredDevice;
2184         }
2185     }
2186 
2187     //--------------------------------------------------------------------------
2188     // Microphone information
2189     //--------------------
2190     /**
2191      * Returns a lists of {@link MicrophoneInfo} representing the active microphones.
2192      * By querying channel mapping for each active microphone, developer can know how
2193      * the microphone is used by each channels or a capture stream.
2194      * Note that the information about the active microphones may change during a recording.
2195      * See {@link AudioManager#registerAudioDeviceCallback} to be notified of changes
2196      * in the audio devices, querying the active microphones then will return the latest
2197      * information.
2198      *
2199      * @return a lists of {@link MicrophoneInfo} representing the active microphones.
2200      * @throws IOException if an error occurs
2201      */
getActiveMicrophones()2202     public List<MicrophoneInfo> getActiveMicrophones() throws IOException {
2203         ArrayList<MicrophoneInfo> activeMicrophones = new ArrayList<>();
2204         int status = native_get_active_microphones(activeMicrophones);
2205         if (status != AudioManager.SUCCESS) {
2206             if (status != AudioManager.ERROR_INVALID_OPERATION) {
2207                 Log.e(TAG, "getActiveMicrophones failed:" + status);
2208             }
2209             Log.i(TAG, "getActiveMicrophones failed, fallback on routed device info");
2210         }
2211         AudioManager.setPortIdForMicrophones(activeMicrophones);
2212 
2213         // Use routed device when there is not information returned by hal.
2214         if (activeMicrophones.size() == 0) {
2215             AudioDeviceInfo device = getRoutedDevice();
2216             if (device != null) {
2217                 MicrophoneInfo microphone = AudioManager.microphoneInfoFromAudioDeviceInfo(device);
2218                 ArrayList<Pair<Integer, Integer>> channelMapping = new ArrayList<>();
2219                 for (int i = 0; i < mChannelCount; i++) {
2220                     channelMapping.add(new Pair(i, MicrophoneInfo.CHANNEL_MAPPING_DIRECT));
2221                 }
2222                 microphone.setChannelMapping(channelMapping);
2223                 activeMicrophones.add(microphone);
2224             }
2225         }
2226         return activeMicrophones;
2227     }
2228 
2229     //--------------------------------------------------------------------------
2230     // Implementation of AudioRecordingMonitor interface
2231     //--------------------
2232 
2233     AudioRecordingMonitorImpl mRecordingInfoImpl =
2234             new AudioRecordingMonitorImpl((AudioRecordingMonitorClient) this);
2235 
2236     /**
2237      * Register a callback to be notified of audio capture changes via a
2238      * {@link AudioManager.AudioRecordingCallback}. A callback is received when the capture path
2239      * configuration changes (pre-processing, format, sampling rate...) or capture is
2240      * silenced/unsilenced by the system.
2241      * @param executor {@link Executor} to handle the callbacks.
2242      * @param cb non-null callback to register
2243      */
registerAudioRecordingCallback(@onNull @allbackExecutor Executor executor, @NonNull AudioManager.AudioRecordingCallback cb)2244     public void registerAudioRecordingCallback(@NonNull @CallbackExecutor Executor executor,
2245             @NonNull AudioManager.AudioRecordingCallback cb) {
2246         mRecordingInfoImpl.registerAudioRecordingCallback(executor, cb);
2247     }
2248 
2249     /**
2250      * Unregister an audio recording callback previously registered with
2251      * {@link #registerAudioRecordingCallback(Executor, AudioManager.AudioRecordingCallback)}.
2252      * @param cb non-null callback to unregister
2253      */
unregisterAudioRecordingCallback(@onNull AudioManager.AudioRecordingCallback cb)2254     public void unregisterAudioRecordingCallback(@NonNull AudioManager.AudioRecordingCallback cb) {
2255         mRecordingInfoImpl.unregisterAudioRecordingCallback(cb);
2256     }
2257 
2258     /**
2259      * Returns the current active audio recording for this audio recorder.
2260      * @return a valid {@link AudioRecordingConfiguration} if this recorder is active
2261      * or null otherwise.
2262      * @see AudioRecordingConfiguration
2263      */
getActiveRecordingConfiguration()2264     public @Nullable AudioRecordingConfiguration getActiveRecordingConfiguration() {
2265         return mRecordingInfoImpl.getActiveRecordingConfiguration();
2266     }
2267 
2268     //---------------------------------------------------------
2269     // Implementation of AudioRecordingMonitorClient interface
2270     //--------------------
2271     /**
2272      * @hide
2273      */
getPortId()2274     public int getPortId() {
2275         if (mNativeAudioRecordHandle == 0) {
2276             return 0;
2277         }
2278         try {
2279             return native_getPortId();
2280         } catch (IllegalStateException e) {
2281             return 0;
2282         }
2283     }
2284 
2285     //--------------------------------------------------------------------------
2286     // MicrophoneDirection
2287     //--------------------
2288     /**
2289      * Specifies the logical microphone (for processing). Applications can use this to specify
2290      * which side of the device to optimize capture from. Typically used in conjunction with
2291      * the camera capturing video.
2292      *
2293      * @return true if sucessful.
2294      */
setPreferredMicrophoneDirection(@irectionMode int direction)2295     public boolean setPreferredMicrophoneDirection(@DirectionMode int direction) {
2296         return native_set_preferred_microphone_direction(direction) == AudioSystem.SUCCESS;
2297     }
2298 
2299     /**
2300      * Specifies the zoom factor (i.e. the field dimension) for the selected microphone
2301      * (for processing). The selected microphone is determined by the use-case for the stream.
2302      *
2303      * @param zoom the desired field dimension of microphone capture. Range is from -1 (wide angle),
2304      * though 0 (no zoom) to 1 (maximum zoom).
2305      * @return true if sucessful.
2306      */
setPreferredMicrophoneFieldDimension( @loatRangefrom = -1.0, to = 1.0) float zoom)2307     public boolean setPreferredMicrophoneFieldDimension(
2308                             @FloatRange(from = -1.0, to = 1.0) float zoom) {
2309         Preconditions.checkArgument(
2310                 zoom >= -1 && zoom <= 1, "Argument must fall between -1 & 1 (inclusive)");
2311         return native_set_preferred_microphone_field_dimension(zoom) == AudioSystem.SUCCESS;
2312     }
2313 
2314     /**
2315      * Sets a {@link LogSessionId} instance to this AudioRecord for metrics collection.
2316      *
2317      * @param logSessionId a {@link LogSessionId} instance which is used to
2318      *        identify this object to the metrics service. Proper generated
2319      *        Ids must be obtained from the Java metrics service and should
2320      *        be considered opaque. Use
2321      *        {@link LogSessionId#LOG_SESSION_ID_NONE} to remove the
2322      *        logSessionId association.
2323      * @throws IllegalStateException if AudioRecord not initialized.
2324      */
setLogSessionId(@onNull LogSessionId logSessionId)2325     public void setLogSessionId(@NonNull LogSessionId logSessionId) {
2326         Objects.requireNonNull(logSessionId);
2327         if (mState == STATE_UNINITIALIZED) {
2328             throw new IllegalStateException("AudioRecord not initialized");
2329         }
2330         String stringId = logSessionId.getStringId();
2331         native_setLogSessionId(stringId);
2332         mLogSessionId = logSessionId;
2333     }
2334 
2335     /**
2336      * Returns the {@link LogSessionId}.
2337      */
2338     @NonNull
getLogSessionId()2339     public LogSessionId getLogSessionId() {
2340         return mLogSessionId;
2341     }
2342 
2343     //---------------------------------------------------------
2344     // Interface definitions
2345     //--------------------
2346     /**
2347      * Interface definition for a callback to be invoked when an AudioRecord has
2348      * reached a notification marker set by {@link AudioRecord#setNotificationMarkerPosition(int)}
2349      * or for periodic updates on the progress of the record head, as set by
2350      * {@link AudioRecord#setPositionNotificationPeriod(int)}.
2351      */
2352     public interface OnRecordPositionUpdateListener  {
2353         /**
2354          * Called on the listener to notify it that the previously set marker has been reached
2355          * by the recording head.
2356          */
onMarkerReached(AudioRecord recorder)2357         void onMarkerReached(AudioRecord recorder);
2358 
2359         /**
2360          * Called on the listener to periodically notify it that the record head has reached
2361          * a multiple of the notification period.
2362          */
onPeriodicNotification(AudioRecord recorder)2363         void onPeriodicNotification(AudioRecord recorder);
2364     }
2365 
2366 
2367 
2368     //---------------------------------------------------------
2369     // Inner classes
2370     //--------------------
2371 
2372     /**
2373      * Helper class to handle the forwarding of native events to the appropriate listener
2374      * (potentially) handled in a different thread
2375      */
2376     private class NativeEventHandler extends Handler {
2377         private final AudioRecord mAudioRecord;
2378 
NativeEventHandler(AudioRecord recorder, Looper looper)2379         NativeEventHandler(AudioRecord recorder, Looper looper) {
2380             super(looper);
2381             mAudioRecord = recorder;
2382         }
2383 
2384         @Override
handleMessage(Message msg)2385         public void handleMessage(Message msg) {
2386             OnRecordPositionUpdateListener listener = null;
2387             synchronized (mPositionListenerLock) {
2388                 listener = mAudioRecord.mPositionListener;
2389             }
2390 
2391             switch (msg.what) {
2392             case NATIVE_EVENT_MARKER:
2393                 if (listener != null) {
2394                     listener.onMarkerReached(mAudioRecord);
2395                 }
2396                 break;
2397             case NATIVE_EVENT_NEW_POS:
2398                 if (listener != null) {
2399                     listener.onPeriodicNotification(mAudioRecord);
2400                 }
2401                 break;
2402             default:
2403                 loge("Unknown native event type: " + msg.what);
2404                 break;
2405             }
2406         }
2407     }
2408 
2409     //---------------------------------------------------------
2410     // Java methods called from the native side
2411     //--------------------
2412     @SuppressWarnings("unused")
2413     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
postEventFromNative(Object audiorecord_ref, int what, int arg1, int arg2, Object obj)2414     private static void postEventFromNative(Object audiorecord_ref,
2415             int what, int arg1, int arg2, Object obj) {
2416         //logd("Event posted from the native side: event="+ what + " args="+ arg1+" "+arg2);
2417         AudioRecord recorder = (AudioRecord)((WeakReference)audiorecord_ref).get();
2418         if (recorder == null) {
2419             return;
2420         }
2421 
2422         if (what == AudioSystem.NATIVE_EVENT_ROUTING_CHANGE) {
2423             recorder.broadcastRoutingChange();
2424             return;
2425         }
2426 
2427         if (recorder.mEventHandler != null) {
2428             Message m =
2429                 recorder.mEventHandler.obtainMessage(what, arg1, arg2, obj);
2430             recorder.mEventHandler.sendMessage(m);
2431         }
2432 
2433     }
2434 
2435 
2436     //---------------------------------------------------------
2437     // Native methods called from the Java side
2438     //--------------------
2439 
2440     /**
2441      * @deprecated Use native_setup that takes an {@link AttributionSource} object
2442      * @return
2443      */
2444     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R,
2445             publicAlternatives = "{@code AudioRecord.Builder}")
2446     @Deprecated
native_setup(Object audiorecordThis, Object attributes, int[] sampleRate, int channelMask, int channelIndexMask, int audioFormat, int buffSizeInBytes, int[] sessionId, String opPackageName, long nativeRecordInJavaObj, int halInputFlags)2447     private int native_setup(Object audiorecordThis,
2448             Object /*AudioAttributes*/ attributes,
2449             int[] sampleRate, int channelMask, int channelIndexMask, int audioFormat,
2450             int buffSizeInBytes, int[] sessionId, String opPackageName,
2451             long nativeRecordInJavaObj, int halInputFlags) {
2452         AttributionSource attributionSource = AttributionSource.myAttributionSource()
2453                 .withPackageName(opPackageName);
2454         try (ScopedParcelState attributionSourceState = attributionSource.asScopedParcelState()) {
2455             return native_setup(audiorecordThis, attributes, sampleRate, channelMask,
2456                     channelIndexMask, audioFormat, buffSizeInBytes, sessionId,
2457                     attributionSourceState.getParcel(), nativeRecordInJavaObj, 0, halInputFlags);
2458         }
2459     }
2460 
native_setup(Object audiorecordThis, Object attributes, int[] sampleRate, int channelMask, int channelIndexMask, int audioFormat, int buffSizeInBytes, int[] sessionId, @NonNull Parcel attributionSource, long nativeRecordInJavaObj, int maxSharedAudioHistoryMs, int halInputFlags)2461     private native int native_setup(Object audiorecordThis,
2462             Object /*AudioAttributes*/ attributes,
2463             int[] sampleRate, int channelMask, int channelIndexMask, int audioFormat,
2464             int buffSizeInBytes, int[] sessionId, @NonNull Parcel attributionSource,
2465             long nativeRecordInJavaObj, int maxSharedAudioHistoryMs, int halInputFlags);
2466 
2467     // TODO remove: implementation calls directly into implementation of native_release()
native_finalize()2468     private native void native_finalize();
2469 
2470     /**
2471      * @hide
2472      */
2473     @UnsupportedAppUsage
native_release()2474     public native final void native_release();
2475 
native_start(int syncEvent, int sessionId)2476     private native final int native_start(int syncEvent, int sessionId);
2477 
native_stop()2478     private native final void native_stop();
2479 
native_read_in_byte_array(byte[] audioData, int offsetInBytes, int sizeInBytes, boolean isBlocking)2480     private native final int native_read_in_byte_array(byte[] audioData,
2481             int offsetInBytes, int sizeInBytes, boolean isBlocking);
2482 
native_read_in_short_array(short[] audioData, int offsetInShorts, int sizeInShorts, boolean isBlocking)2483     private native final int native_read_in_short_array(short[] audioData,
2484             int offsetInShorts, int sizeInShorts, boolean isBlocking);
2485 
native_read_in_float_array(float[] audioData, int offsetInFloats, int sizeInFloats, boolean isBlocking)2486     private native final int native_read_in_float_array(float[] audioData,
2487             int offsetInFloats, int sizeInFloats, boolean isBlocking);
2488 
native_read_in_direct_buffer(Object jBuffer, int sizeInBytes, boolean isBlocking)2489     private native final int native_read_in_direct_buffer(Object jBuffer,
2490             int sizeInBytes, boolean isBlocking);
2491 
native_get_buffer_size_in_frames()2492     private native final int native_get_buffer_size_in_frames();
2493 
native_set_marker_pos(int marker)2494     private native final int native_set_marker_pos(int marker);
native_get_marker_pos()2495     private native final int native_get_marker_pos();
2496 
native_set_pos_update_period(int updatePeriod)2497     private native final int native_set_pos_update_period(int updatePeriod);
native_get_pos_update_period()2498     private native final int native_get_pos_update_period();
2499 
native_get_min_buff_size( int sampleRateInHz, int channelCount, int audioFormat)2500     static private native final int native_get_min_buff_size(
2501             int sampleRateInHz, int channelCount, int audioFormat);
2502 
native_setInputDevice(int deviceId)2503     private native final boolean native_setInputDevice(int deviceId);
native_getRoutedDeviceId()2504     private native final int native_getRoutedDeviceId();
native_enableDeviceCallback()2505     private native final void native_enableDeviceCallback();
native_disableDeviceCallback()2506     private native final void native_disableDeviceCallback();
2507 
native_get_timestamp(@onNull AudioTimestamp outTimestamp, @AudioTimestamp.Timebase int timebase)2508     private native final int native_get_timestamp(@NonNull AudioTimestamp outTimestamp,
2509             @AudioTimestamp.Timebase int timebase);
2510 
native_get_active_microphones( ArrayList<MicrophoneInfo> activeMicrophones)2511     private native final int native_get_active_microphones(
2512             ArrayList<MicrophoneInfo> activeMicrophones);
2513 
2514     /**
2515      * @throws IllegalStateException
2516      */
native_getPortId()2517     private native int native_getPortId();
2518 
native_set_preferred_microphone_direction(int direction)2519     private native int native_set_preferred_microphone_direction(int direction);
native_set_preferred_microphone_field_dimension(float zoom)2520     private native int native_set_preferred_microphone_field_dimension(float zoom);
2521 
native_setLogSessionId(@ullable String logSessionId)2522     private native void native_setLogSessionId(@Nullable String logSessionId);
2523 
native_shareAudioHistory(@onNull String sharedPackage, long startFromMs)2524     private native int native_shareAudioHistory(@NonNull String sharedPackage, long startFromMs);
2525 
2526     //---------------------------------------------------------
2527     // Utility methods
2528     //------------------
2529 
logd(String msg)2530     private static void logd(String msg) {
2531         Log.d(TAG, msg);
2532     }
2533 
loge(String msg)2534     private static void loge(String msg) {
2535         Log.e(TAG, msg);
2536     }
2537 
2538     public static final class MetricsConstants
2539     {
MetricsConstants()2540         private MetricsConstants() {}
2541 
2542         // MM_PREFIX is slightly different than TAG, used to avoid cut-n-paste errors.
2543         private static final String MM_PREFIX = "android.media.audiorecord.";
2544 
2545         /**
2546          * Key to extract the audio data encoding for this track
2547          * from the {@link AudioRecord#getMetrics} return value.
2548          * The value is a {@code String}.
2549          */
2550         public static final String ENCODING = MM_PREFIX + "encoding";
2551 
2552         /**
2553          * Key to extract the source type for this track
2554          * from the {@link AudioRecord#getMetrics} return value.
2555          * The value is a {@code String}.
2556          */
2557         public static final String SOURCE = MM_PREFIX + "source";
2558 
2559         /**
2560          * Key to extract the estimated latency through the recording pipeline
2561          * from the {@link AudioRecord#getMetrics} return value.
2562          * This is in units of milliseconds.
2563          * The value is an {@code int}.
2564          * @deprecated Not properly supported in the past.
2565          */
2566         @Deprecated
2567         public static final String LATENCY = MM_PREFIX + "latency";
2568 
2569         /**
2570          * Key to extract the sink sample rate for this record track in Hz
2571          * from the {@link AudioRecord#getMetrics} return value.
2572          * The value is an {@code int}.
2573          */
2574         public static final String SAMPLERATE = MM_PREFIX + "samplerate";
2575 
2576         /**
2577          * Key to extract the number of channels being recorded in this record track
2578          * from the {@link AudioRecord#getMetrics} return value.
2579          * The value is an {@code int}.
2580          */
2581         public static final String CHANNELS = MM_PREFIX + "channels";
2582 
2583         /**
2584          * Use for testing only. Do not expose.
2585          * The native channel mask.
2586          * The value is a {@code long}.
2587          * @hide
2588          */
2589         @TestApi
2590         public static final String CHANNEL_MASK = MM_PREFIX + "channelMask";
2591 
2592 
2593         /**
2594          * Use for testing only. Do not expose.
2595          * The port id of this input port in audioserver.
2596          * The value is an {@code int}.
2597          * @hide
2598          */
2599         @TestApi
2600         public static final String PORT_ID = MM_PREFIX + "portId";
2601 
2602         /**
2603          * Use for testing only. Do not expose.
2604          * The buffer frameCount.
2605          * The value is an {@code int}.
2606          * @hide
2607          */
2608         @TestApi
2609         public static final String FRAME_COUNT = MM_PREFIX + "frameCount";
2610 
2611         /**
2612          * Use for testing only. Do not expose.
2613          * The actual record track attributes used.
2614          * The value is a {@code String}.
2615          * @hide
2616          */
2617         @TestApi
2618         public static final String ATTRIBUTES = MM_PREFIX + "attributes";
2619 
2620         /**
2621          * Use for testing only. Do not expose.
2622          * The buffer frameCount
2623          * The value is a {@code double}.
2624          * @hide
2625          */
2626         @TestApi
2627         public static final String DURATION_MS = MM_PREFIX + "durationMs";
2628 
2629         /**
2630          * Use for testing only. Do not expose.
2631          * The number of times the record track has started
2632          * The value is a {@code long}.
2633          * @hide
2634          */
2635         @TestApi
2636         public static final String START_COUNT = MM_PREFIX + "startCount";
2637     }
2638 }
2639