Add AudioRecordingConfiguration changed callback
Add a callback reporting changes in current audio recording configuration for AudioRecord and MediaRecorder. The callback is called when the capture path configuration changes (pre preprocessing, format, sampling rate...) or capture is silenced/unsilenced by the system. Bug: 111438757 Test: CTS tests for AudioRecord and MediaRecorder Change-Id: Ifd5c5b4f5ee8911822a3f05412c84edf2db7858a
This commit is contained in:
@@ -23379,12 +23379,13 @@ package android.media {
|
||||
method public android.media.AudioPresentation.Builder setProgramId(int);
|
||||
}
|
||||
|
||||
public class AudioRecord implements android.media.AudioRouting {
|
||||
public class AudioRecord implements android.media.AudioRecordingMonitor android.media.AudioRouting {
|
||||
ctor public AudioRecord(int, int, int, int, int) throws java.lang.IllegalArgumentException;
|
||||
method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
|
||||
method public deprecated void addOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener, android.os.Handler);
|
||||
method protected void finalize();
|
||||
method public java.util.List<android.media.MicrophoneInfo> getActiveMicrophones() throws java.io.IOException;
|
||||
method public android.media.AudioRecordingConfiguration getActiveRecordingConfiguration();
|
||||
method public int getAudioFormat();
|
||||
method public int getAudioSessionId();
|
||||
method public int getAudioSource();
|
||||
@@ -23409,6 +23410,7 @@ package android.media {
|
||||
method public int read(float[], int, int, int);
|
||||
method public int read(java.nio.ByteBuffer, int);
|
||||
method public int read(java.nio.ByteBuffer, int, int);
|
||||
method public void registerAudioRecordingCallback(java.util.concurrent.Executor, android.media.AudioManager.AudioRecordingCallback);
|
||||
method public void release();
|
||||
method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener);
|
||||
method public deprecated void removeOnRoutingChangedListener(android.media.AudioRecord.OnRoutingChangedListener);
|
||||
@@ -23420,6 +23422,7 @@ package android.media {
|
||||
method public void startRecording() throws java.lang.IllegalStateException;
|
||||
method public void startRecording(android.media.MediaSyncEvent) throws java.lang.IllegalStateException;
|
||||
method public void stop() throws java.lang.IllegalStateException;
|
||||
method public void unregisterAudioRecordingCallback(android.media.AudioManager.AudioRecordingCallback);
|
||||
field public static final int ERROR = -1; // 0xffffffff
|
||||
field public static final int ERROR_BAD_VALUE = -2; // 0xfffffffe
|
||||
field public static final int ERROR_DEAD_OBJECT = -6; // 0xfffffffa
|
||||
@@ -23474,6 +23477,12 @@ package android.media {
|
||||
field public static final android.os.Parcelable.Creator<android.media.AudioRecordingConfiguration> CREATOR;
|
||||
}
|
||||
|
||||
public abstract interface AudioRecordingMonitor {
|
||||
method public abstract android.media.AudioRecordingConfiguration getActiveRecordingConfiguration();
|
||||
method public abstract void registerAudioRecordingCallback(java.util.concurrent.Executor, android.media.AudioManager.AudioRecordingCallback);
|
||||
method public abstract void unregisterAudioRecordingCallback(android.media.AudioManager.AudioRecordingCallback);
|
||||
}
|
||||
|
||||
public abstract interface AudioRouting {
|
||||
method public abstract void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
|
||||
method public abstract android.media.AudioDeviceInfo getPreferredDevice();
|
||||
@@ -25475,11 +25484,12 @@ package android.media {
|
||||
field public static final int MEDIA_TRACK_TYPE_VIDEO = 1; // 0x1
|
||||
}
|
||||
|
||||
public class MediaRecorder implements android.media.AudioRouting {
|
||||
public class MediaRecorder implements android.media.AudioRecordingMonitor android.media.AudioRouting {
|
||||
ctor public MediaRecorder();
|
||||
method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
|
||||
method protected void finalize();
|
||||
method public java.util.List<android.media.MicrophoneInfo> getActiveMicrophones() throws java.io.IOException;
|
||||
method public android.media.AudioRecordingConfiguration getActiveRecordingConfiguration();
|
||||
method public static final int getAudioSourceMax();
|
||||
method public int getMaxAmplitude() throws java.lang.IllegalStateException;
|
||||
method public android.os.PersistableBundle getMetrics();
|
||||
@@ -25488,6 +25498,7 @@ package android.media {
|
||||
method public android.view.Surface getSurface();
|
||||
method public void pause() throws java.lang.IllegalStateException;
|
||||
method public void prepare() throws java.io.IOException, java.lang.IllegalStateException;
|
||||
method public void registerAudioRecordingCallback(java.util.concurrent.Executor, android.media.AudioManager.AudioRecordingCallback);
|
||||
method public void release();
|
||||
method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener);
|
||||
method public void reset();
|
||||
@@ -25523,6 +25534,7 @@ package android.media {
|
||||
method public void setVideoSource(int) throws java.lang.IllegalStateException;
|
||||
method public void start() throws java.lang.IllegalStateException;
|
||||
method public void stop() throws java.lang.IllegalStateException;
|
||||
method public void unregisterAudioRecordingCallback(android.media.AudioManager.AudioRecordingCallback);
|
||||
field public static final int MEDIA_ERROR_SERVER_DIED = 100; // 0x64
|
||||
field public static final int MEDIA_RECORDER_ERROR_UNKNOWN = 1; // 0x1
|
||||
field public static final int MEDIA_RECORDER_INFO_MAX_DURATION_REACHED = 800; // 0x320
|
||||
|
||||
@@ -2953,7 +2953,7 @@ package android.media {
|
||||
field public static final int PLAYER_TYPE_UNKNOWN = -1; // 0xffffffff
|
||||
}
|
||||
|
||||
public class AudioRecord implements android.media.AudioRouting {
|
||||
public class AudioRecord implements android.media.AudioRecordingMonitor android.media.AudioRouting {
|
||||
ctor public AudioRecord(android.media.AudioAttributes, android.media.AudioFormat, int, int) throws java.lang.IllegalArgumentException;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,8 +16,10 @@
|
||||
|
||||
package android.media;
|
||||
|
||||
import android.annotation.CallbackExecutor;
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.SystemApi;
|
||||
import android.annotation.UnsupportedAppUsage;
|
||||
import android.app.ActivityThread;
|
||||
@@ -43,6 +45,7 @@ import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* The AudioRecord class manages the audio resources for Java applications
|
||||
@@ -58,7 +61,7 @@ import java.util.List;
|
||||
* been read yet. Data should be read from the audio hardware in chunks of sizes inferior to
|
||||
* the total recording buffer size.
|
||||
*/
|
||||
public class AudioRecord implements AudioRouting
|
||||
public class AudioRecord implements AudioRouting, AudioRecordingMonitor, AudioRecordingMonitorClient
|
||||
{
|
||||
//---------------------------------------------------------
|
||||
// Constants
|
||||
@@ -1654,6 +1657,56 @@ public class AudioRecord implements AudioRouting
|
||||
return activeMicrophones;
|
||||
}
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Implementation of AudioRecordingMonitor interface
|
||||
//--------------------
|
||||
|
||||
AudioRecordingMonitorImpl mRecordingInfoImpl =
|
||||
new AudioRecordingMonitorImpl((AudioRecordingMonitorClient) this);
|
||||
|
||||
/**
|
||||
* Register a callback to be notified of audio capture changes via a
|
||||
* {@link AudioManager.AudioRecordingCallback}. A callback is received when the capture path
|
||||
* configuration changes (pre-processing, format, sampling rate...) or capture is
|
||||
* silenced/unsilenced by the system.
|
||||
* @param executor {@link Executor} to handle the callbacks.
|
||||
* @param cb non-null callback to register
|
||||
*/
|
||||
public void registerAudioRecordingCallback(@NonNull @CallbackExecutor Executor executor,
|
||||
@NonNull AudioManager.AudioRecordingCallback cb) {
|
||||
mRecordingInfoImpl.registerAudioRecordingCallback(executor, cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister an audio recording callback previously registered with
|
||||
* {@link #registerAudioRecordingCallback(Executor, AudioManager.AudioRecordingCallback)}.
|
||||
* @param cb non-null callback to unregister
|
||||
*/
|
||||
public void unregisterAudioRecordingCallback(@NonNull AudioManager.AudioRecordingCallback cb) {
|
||||
mRecordingInfoImpl.unregisterAudioRecordingCallback(cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current active audio recording for this audio recorder.
|
||||
* @return a valid {@link AudioRecordingConfiguration} if this recorder is active
|
||||
* or null otherwise.
|
||||
* @see AudioRecordingConfiguration
|
||||
*/
|
||||
public @Nullable AudioRecordingConfiguration getActiveRecordingConfiguration() {
|
||||
return mRecordingInfoImpl.getActiveRecordingConfiguration();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// Implementation of AudioRecordingMonitorClient interface
|
||||
//--------------------
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public int getPortId() {
|
||||
return native_getPortId();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// Interface definitions
|
||||
//--------------------
|
||||
|
||||
56
media/java/android/media/AudioRecordingMonitor.java
Normal file
56
media/java/android/media/AudioRecordingMonitor.java
Normal file
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.media;
|
||||
|
||||
import android.annotation.CallbackExecutor;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* AudioRecordingMonitor defines an interface implemented by {@link AudioRecord} and
|
||||
* {@link MediaRecorder} allowing applications to install a callback and be notified of changes
|
||||
* in the capture path while recoding is active.
|
||||
*/
|
||||
public interface AudioRecordingMonitor {
|
||||
/**
|
||||
* Register a callback to be notified of audio capture changes via a
|
||||
* {@link AudioManager.AudioRecordingCallback}. A callback is received when the capture path
|
||||
* configuration changes (pre-processing, format, sampling rate...) or capture is
|
||||
* silenced/unsilenced by the system.
|
||||
* @param executor {@link Executor} to handle the callbacks.
|
||||
* @param cb non-null callback to register
|
||||
*/
|
||||
void registerAudioRecordingCallback(@NonNull @CallbackExecutor Executor executor,
|
||||
@NonNull AudioManager.AudioRecordingCallback cb);
|
||||
|
||||
/**
|
||||
* Unregister an audio recording callback previously registered with
|
||||
* {@link #registerAudioRecordingCallback(Executor, AudioManager.AudioRecordingCallback)}.
|
||||
* @param cb non-null callback to unregister
|
||||
*/
|
||||
void unregisterAudioRecordingCallback(@NonNull AudioManager.AudioRecordingCallback cb);
|
||||
|
||||
/**
|
||||
* Returns the current active audio recording for this audio recorder.
|
||||
* @return a valid {@link AudioRecordingConfiguration} if this recorder is active
|
||||
* or null otherwise.
|
||||
* @see AudioRecordingConfiguration
|
||||
*/
|
||||
@Nullable AudioRecordingConfiguration getActiveRecordingConfiguration();
|
||||
}
|
||||
28
media/java/android/media/AudioRecordingMonitorClient.java
Normal file
28
media/java/android/media/AudioRecordingMonitorClient.java
Normal file
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.media;
|
||||
|
||||
/**
|
||||
* Interface implemented by classes using { @link AudioRecordingMonitor} interface.
|
||||
* @hide
|
||||
*/
|
||||
public interface AudioRecordingMonitorClient {
|
||||
/**
|
||||
* @return the unique port ID allocated by audio framework to this recorder
|
||||
*/
|
||||
int getPortId();
|
||||
}
|
||||
250
media/java/android/media/AudioRecordingMonitorImpl.java
Normal file
250
media/java/android/media/AudioRecordingMonitorImpl.java
Normal file
@@ -0,0 +1,250 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.media;
|
||||
|
||||
import android.annotation.CallbackExecutor;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.os.Binder;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* Implementation of AudioRecordingMonitor interface.
|
||||
* @hide
|
||||
*/
|
||||
public class AudioRecordingMonitorImpl implements AudioRecordingMonitor {
|
||||
|
||||
private static final String TAG = "android.media.AudioRecordingMonitor";
|
||||
|
||||
private static IAudioService sService; //lazy initialization, use getService()
|
||||
|
||||
private final AudioRecordingMonitorClient mClient;
|
||||
|
||||
AudioRecordingMonitorImpl(@NonNull AudioRecordingMonitorClient client) {
|
||||
mClient = client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a callback to be notified of audio capture changes via a
|
||||
* {@link AudioManager.AudioRecordingCallback}. A callback is received when the capture path
|
||||
* configuration changes (pre-processing, format, sampling rate...) or capture is
|
||||
* silenced/unsilenced by the system.
|
||||
* @param executor {@link Executor} to handle the callbacks.
|
||||
* @param cb non-null callback to register
|
||||
*/
|
||||
public void registerAudioRecordingCallback(@NonNull @CallbackExecutor Executor executor,
|
||||
@NonNull AudioManager.AudioRecordingCallback cb) {
|
||||
if (cb == null) {
|
||||
throw new IllegalArgumentException("Illegal null AudioRecordingCallback");
|
||||
}
|
||||
if (executor == null) {
|
||||
throw new IllegalArgumentException("Illegal null Executor");
|
||||
}
|
||||
synchronized (mRecordCallbackLock) {
|
||||
// check if eventCallback already in list
|
||||
for (AudioRecordingCallbackInfo arci : mRecordCallbackList) {
|
||||
if (arci.mCb == cb) {
|
||||
throw new IllegalArgumentException(
|
||||
"AudioRecordingCallback already registered");
|
||||
}
|
||||
}
|
||||
beginRecordingCallbackHandling();
|
||||
mRecordCallbackList.add(new AudioRecordingCallbackInfo(executor, cb));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister an audio recording callback previously registered with
|
||||
* {@link #registerAudioRecordingCallback(Executor, AudioManager.AudioRecordingCallback)}.
|
||||
* @param cb non-null callback to unregister
|
||||
*/
|
||||
public void unregisterAudioRecordingCallback(@NonNull AudioManager.AudioRecordingCallback cb) {
|
||||
if (cb == null) {
|
||||
throw new IllegalArgumentException("Illegal null AudioRecordingCallback argument");
|
||||
}
|
||||
|
||||
synchronized (mRecordCallbackLock) {
|
||||
for (AudioRecordingCallbackInfo arci : mRecordCallbackList) {
|
||||
if (arci.mCb == cb) {
|
||||
// ok to remove while iterating over list as we exit iteration
|
||||
mRecordCallbackList.remove(arci);
|
||||
if (mRecordCallbackList.size() == 0) {
|
||||
endRecordingCallbackHandling();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("AudioRecordingCallback was not registered");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current active audio recording for this audio recorder.
|
||||
* @return a valid {@link AudioRecordingConfiguration} if this recorder is active
|
||||
* or null otherwise.
|
||||
* @see AudioRecordingConfiguration
|
||||
*/
|
||||
public @Nullable AudioRecordingConfiguration getActiveRecordingConfiguration() {
|
||||
final IAudioService service = getService();
|
||||
try {
|
||||
List<AudioRecordingConfiguration> configs = service.getActiveRecordingConfigurations();
|
||||
return getMyConfig(configs);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
private static class AudioRecordingCallbackInfo {
|
||||
final AudioManager.AudioRecordingCallback mCb;
|
||||
final Executor mExecutor;
|
||||
AudioRecordingCallbackInfo(Executor e, AudioManager.AudioRecordingCallback cb) {
|
||||
mExecutor = e;
|
||||
mCb = cb;
|
||||
}
|
||||
}
|
||||
|
||||
private static final int MSG_RECORDING_CONFIG_CHANGE = 1;
|
||||
|
||||
private final Object mRecordCallbackLock = new Object();
|
||||
@GuardedBy("mRecordCallbackLock")
|
||||
@NonNull private LinkedList<AudioRecordingCallbackInfo> mRecordCallbackList =
|
||||
new LinkedList<AudioRecordingCallbackInfo>();
|
||||
@GuardedBy("mRecordCallbackLock")
|
||||
private @Nullable HandlerThread mRecordingCallbackHandlerThread;
|
||||
@GuardedBy("mRecordCallbackLock")
|
||||
private @Nullable volatile Handler mRecordingCallbackHandler;
|
||||
|
||||
@GuardedBy("mRecordCallbackLock")
|
||||
private final IRecordingConfigDispatcher mRecordingCallback =
|
||||
new IRecordingConfigDispatcher.Stub() {
|
||||
@Override
|
||||
public void dispatchRecordingConfigChange(List<AudioRecordingConfiguration> configs) {
|
||||
AudioRecordingConfiguration config = getMyConfig(configs);
|
||||
if (config != null) {
|
||||
synchronized (mRecordCallbackLock) {
|
||||
if (mRecordingCallbackHandler != null) {
|
||||
final Message m = mRecordingCallbackHandler.obtainMessage(
|
||||
MSG_RECORDING_CONFIG_CHANGE/*what*/, config /*obj*/);
|
||||
mRecordingCallbackHandler.sendMessage(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@GuardedBy("mRecordCallbackLock")
|
||||
private void beginRecordingCallbackHandling() {
|
||||
if (mRecordingCallbackHandlerThread == null) {
|
||||
mRecordingCallbackHandlerThread = new HandlerThread(TAG + ".RecordingCallback");
|
||||
mRecordingCallbackHandlerThread.start();
|
||||
final Looper looper = mRecordingCallbackHandlerThread.getLooper();
|
||||
if (looper != null) {
|
||||
mRecordingCallbackHandler = new Handler(looper) {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_RECORDING_CONFIG_CHANGE: {
|
||||
if (msg.obj == null) {
|
||||
return;
|
||||
}
|
||||
ArrayList<AudioRecordingConfiguration> configs =
|
||||
new ArrayList<AudioRecordingConfiguration>();
|
||||
configs.add((AudioRecordingConfiguration) msg.obj);
|
||||
|
||||
final LinkedList<AudioRecordingCallbackInfo> cbInfoList;
|
||||
synchronized (mRecordCallbackLock) {
|
||||
if (mRecordCallbackList.size() == 0) {
|
||||
return;
|
||||
}
|
||||
cbInfoList = new LinkedList<AudioRecordingCallbackInfo>(
|
||||
mRecordCallbackList);
|
||||
}
|
||||
|
||||
final long identity = Binder.clearCallingIdentity();
|
||||
try {
|
||||
for (AudioRecordingCallbackInfo cbi : cbInfoList) {
|
||||
cbi.mExecutor.execute(() ->
|
||||
cbi.mCb.onRecordingConfigChanged(configs));
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(identity);
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
Log.e(TAG, "Unknown event " + msg.what);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
final IAudioService service = getService();
|
||||
try {
|
||||
service.registerRecordingCallback(mRecordingCallback);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@GuardedBy("mRecordCallbackLock")
|
||||
private void endRecordingCallbackHandling() {
|
||||
if (mRecordingCallbackHandlerThread != null) {
|
||||
final IAudioService service = getService();
|
||||
try {
|
||||
service.unregisterRecordingCallback(mRecordingCallback);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
mRecordingCallbackHandlerThread.quit();
|
||||
mRecordingCallbackHandlerThread = null;
|
||||
}
|
||||
}
|
||||
|
||||
AudioRecordingConfiguration getMyConfig(List<AudioRecordingConfiguration> configs) {
|
||||
int portId = mClient.getPortId();
|
||||
for (AudioRecordingConfiguration config : configs) {
|
||||
if (config.getClientPortId() == portId) {
|
||||
return config;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static IAudioService getService() {
|
||||
if (sService != null) {
|
||||
return sService;
|
||||
}
|
||||
IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
|
||||
sService = IAudioService.Stub.asInterface(b);
|
||||
return sService;
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,9 @@
|
||||
|
||||
package android.media;
|
||||
|
||||
import android.annotation.CallbackExecutor;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.RequiresPermission;
|
||||
import android.annotation.SystemApi;
|
||||
import android.annotation.UnsupportedAppUsage;
|
||||
@@ -40,6 +42,8 @@ import java.io.RandomAccessFile;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
|
||||
/**
|
||||
* Used to record audio and video. The recording control is based on a
|
||||
@@ -83,7 +87,9 @@ import java.util.List;
|
||||
* <a href="{@docRoot}guide/topics/media/audio-capture.html">Audio Capture</a> developer guide.</p>
|
||||
* </div>
|
||||
*/
|
||||
public class MediaRecorder implements AudioRouting
|
||||
public class MediaRecorder implements AudioRouting,
|
||||
AudioRecordingMonitor,
|
||||
AudioRecordingMonitorClient
|
||||
{
|
||||
static {
|
||||
System.loadLibrary("media_jni");
|
||||
@@ -304,7 +310,7 @@ public class MediaRecorder implements AudioRouting
|
||||
|
||||
/**
|
||||
* Audio source for preemptible, low-priority software hotword detection
|
||||
* It presents the same gain and pre processing tuning as {@link #VOICE_RECOGNITION}.
|
||||
* It presents the same gain and pre-processing tuning as {@link #VOICE_RECOGNITION}.
|
||||
* <p>
|
||||
* An application should use this audio source when it wishes to do
|
||||
* always-on software hotword detection, while gracefully giving in to any other application
|
||||
@@ -1471,6 +1477,57 @@ public class MediaRecorder implements AudioRouting
|
||||
private native final int native_getActiveMicrophones(
|
||||
ArrayList<MicrophoneInfo> activeMicrophones);
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Implementation of AudioRecordingMonitor interface
|
||||
//--------------------
|
||||
|
||||
AudioRecordingMonitorImpl mRecordingInfoImpl =
|
||||
new AudioRecordingMonitorImpl((AudioRecordingMonitorClient) this);
|
||||
|
||||
/**
|
||||
* Register a callback to be notified of audio capture changes via a
|
||||
* {@link AudioManager.AudioRecordingCallback}. A callback is received when the capture path
|
||||
* configuration changes (pre-processing, format, sampling rate...) or capture is
|
||||
* silenced/unsilenced by the system.
|
||||
* @param executor {@link Executor} to handle the callbacks.
|
||||
* @param cb non-null callback to register
|
||||
*/
|
||||
public void registerAudioRecordingCallback(@NonNull @CallbackExecutor Executor executor,
|
||||
@NonNull AudioManager.AudioRecordingCallback cb) {
|
||||
mRecordingInfoImpl.registerAudioRecordingCallback(executor, cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister an audio recording callback previously registered with
|
||||
* {@link #registerAudioRecordingCallback(Executor, AudioManager.AudioRecordingCallback)}.
|
||||
* @param cb non-null callback to unregister
|
||||
*/
|
||||
public void unregisterAudioRecordingCallback(@NonNull AudioManager.AudioRecordingCallback cb) {
|
||||
mRecordingInfoImpl.unregisterAudioRecordingCallback(cb);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current active audio recording for this audio recorder.
|
||||
* @return a valid {@link AudioRecordingConfiguration} if this recorder is active
|
||||
* or null otherwise.
|
||||
* @see AudioRecordingConfiguration
|
||||
*/
|
||||
public @Nullable AudioRecordingConfiguration getActiveRecordingConfiguration() {
|
||||
return mRecordingInfoImpl.getActiveRecordingConfiguration();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------
|
||||
// Implementation of AudioRecordingMonitorClient interface
|
||||
//--------------------
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public int getPortId() {
|
||||
return native_getPortId();
|
||||
}
|
||||
|
||||
private native int native_getPortId();
|
||||
|
||||
/**
|
||||
* Called from native code when an interesting event happens. This method
|
||||
* just uses the EventHandler system to post the event back to the main app thread.
|
||||
|
||||
@@ -763,6 +763,20 @@ android_media_MediaRecord_getActiveMicrophones(JNIEnv *env,
|
||||
}
|
||||
return jStatus;
|
||||
}
|
||||
|
||||
static jint android_media_MediaRecord_getPortId(JNIEnv *env, jobject thiz) {
|
||||
sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
|
||||
if (mr == NULL) {
|
||||
jniThrowException(env, "java/lang/IllegalStateException", NULL);
|
||||
return (jint)AUDIO_PORT_HANDLE_NONE;
|
||||
}
|
||||
|
||||
audio_port_handle_t portId;
|
||||
process_media_recorder_call(env, mr->getPortId(&portId),
|
||||
"java/lang/RuntimeException", "getPortId failed.");
|
||||
return (jint)portId;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
static const JNINativeMethod gMethods[] = {
|
||||
@@ -801,6 +815,7 @@ static const JNINativeMethod gMethods[] = {
|
||||
{"native_enableDeviceCallback", "(Z)V", (void *)android_media_MediaRecorder_enableDeviceCallback},
|
||||
|
||||
{"native_getActiveMicrophones", "(Ljava/util/ArrayList;)I", (void *)android_media_MediaRecord_getActiveMicrophones},
|
||||
{"native_getPortId", "()I", (void *)android_media_MediaRecord_getPortId},
|
||||
};
|
||||
|
||||
// This function only registers the native methods, and is called from
|
||||
|
||||
Reference in New Issue
Block a user