refactor AudioRecord and AudioFlinger openRecord()

Refactor the mechanism used by audio record tracks to query and
attach to an input mixer/stream in audio flinger. This will:
 - reduce the number of binder transactions needed to create a record track
 - move sample rate, framecount and flags validations to audio server side
 - move audio session allocation to audio server side
 - prepare restriction of certain binder transactions to audioserver only

Also:
 - renamed openRecord() to createRecord() for consistency with AudioTrack
 - make AudioRecord mStatus update consistent when calling default
 contructor + set() or constructor with arguments
 - make AudioClient, CreateTrackXXX and  CreateRecordXXX classes derive
 from Parcelable
 - restore audio session ID validity check in AudioFlinger::createTrack()

Test: CTS tests: AudioRecordTest, AudioRecordingConfigurationTest, AudioPreProcessingTest
Test: manual test for capture use cases: camcorder, OK Google, VoIP calls
Change-Id: I72edaa7ddef1da11c77df21e88378e3aa9012d58
gugelfrei
Eric Laurent 7 years ago
parent eda72c27ca
commit 3e1acc0c58

@ -69,8 +69,7 @@ AudioRecord::AudioRecord(const String16 &opPackageName)
: mActive(false), mStatus(NO_INIT), mOpPackageName(opPackageName),
mSessionId(AUDIO_SESSION_ALLOCATE),
mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT),
mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE), mRoutedDeviceId(AUDIO_PORT_HANDLE_NONE),
mPortId(AUDIO_PORT_HANDLE_NONE)
mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE), mRoutedDeviceId(AUDIO_PORT_HANDLE_NONE)
{
}
@ -97,10 +96,9 @@ AudioRecord::AudioRecord(
mSessionId(AUDIO_SESSION_ALLOCATE),
mPreviousPriority(ANDROID_PRIORITY_NORMAL),
mPreviousSchedulingGroup(SP_DEFAULT),
mProxy(NULL),
mPortId(AUDIO_PORT_HANDLE_NONE)
mProxy(NULL)
{
mStatus = set(inputSource, sampleRate, format, channelMask, frameCount, cbf, user,
(void)set(inputSource, sampleRate, format, channelMask, frameCount, cbf, user,
notificationFrames, false /*threadCanCallJava*/, sessionId, transferType, flags,
uid, pid, pAttributes, selectedDeviceId);
}
@ -151,6 +149,11 @@ status_t AudioRecord::set(
const audio_attributes_t* pAttributes,
audio_port_handle_t selectedDeviceId)
{
status_t status = NO_ERROR;
uint32_t channelCount;
pid_t callingPid;
pid_t myPid;
ALOGV("set(): inputSource %d, sampleRate %u, format %#x, channelMask %#x, frameCount %zu, "
"notificationFrames %u, sessionId %d, transferType %d, flags %#x, opPackageName %s "
"uid %d, pid %d",
@ -170,7 +173,8 @@ status_t AudioRecord::set(
case TRANSFER_CALLBACK:
if (cbf == NULL) {
ALOGE("Transfer type TRANSFER_CALLBACK but cbf == NULL");
return BAD_VALUE;
status = BAD_VALUE;
goto exit;
}
break;
case TRANSFER_OBTAIN:
@ -178,14 +182,16 @@ status_t AudioRecord::set(
break;
default:
ALOGE("Invalid transfer type %d", transferType);
return BAD_VALUE;
status = BAD_VALUE;
goto exit;
}
mTransfer = transferType;
// invariant that mAudioRecord != 0 is true only after set() returns successfully
if (mAudioRecord != 0) {
ALOGE("Track already in use");
return INVALID_OPERATION;
status = INVALID_OPERATION;
goto exit;
}
if (pAttributes == NULL) {
@ -209,16 +215,18 @@ status_t AudioRecord::set(
// AudioFlinger capture only supports linear PCM
if (!audio_is_valid_format(format) || !audio_is_linear_pcm(format)) {
ALOGE("Format %#x is not linear pcm", format);
return BAD_VALUE;
status = BAD_VALUE;
goto exit;
}
mFormat = format;
if (!audio_is_input_channel(channelMask)) {
ALOGE("Invalid channel mask %#x", channelMask);
return BAD_VALUE;
status = BAD_VALUE;
goto exit;
}
mChannelMask = channelMask;
uint32_t channelCount = audio_channel_count_from_in_mask(channelMask);
channelCount = audio_channel_count_from_in_mask(channelMask);
mChannelCount = channelCount;
if (audio_is_linear_pcm(format)) {
@ -227,28 +235,24 @@ status_t AudioRecord::set(
mFrameSize = sizeof(uint8_t);
}
// mFrameCount is initialized in openRecord_l
// mFrameCount is initialized in createRecord_l
mReqFrameCount = frameCount;
mNotificationFramesReq = notificationFrames;
// mNotificationFramesAct is initialized in openRecord_l
// mNotificationFramesAct is initialized in createRecord_l
if (sessionId == AUDIO_SESSION_ALLOCATE) {
mSessionId = (audio_session_t) AudioSystem::newAudioUniqueId(AUDIO_UNIQUE_ID_USE_SESSION);
} else {
mSessionId = sessionId;
}
mSessionId = sessionId;
ALOGV("set(): mSessionId %d", mSessionId);
int callingpid = IPCThreadState::self()->getCallingPid();
int mypid = getpid();
if (uid == AUDIO_UID_INVALID || (callingpid != mypid)) {
callingPid = IPCThreadState::self()->getCallingPid();
myPid = getpid();
if (uid == AUDIO_UID_INVALID || (callingPid != myPid)) {
mClientUid = IPCThreadState::self()->getCallingUid();
} else {
mClientUid = uid;
}
if (pid == -1 || (callingpid != mypid)) {
mClientPid = callingpid;
if (pid == -1 || (callingPid != myPid)) {
mClientPid = callingPid;
} else {
mClientPid = pid;
}
@ -263,7 +267,7 @@ status_t AudioRecord::set(
}
// create the IAudioRecord
status_t status = openRecord_l(0 /*epoch*/, mOpPackageName);
status = createRecord_l(0 /*epoch*/, mOpPackageName);
if (status != NO_ERROR) {
if (mAudioRecordThread != 0) {
@ -271,10 +275,9 @@ status_t AudioRecord::set(
mAudioRecordThread->requestExitAndWait();
mAudioRecordThread.clear();
}
return status;
goto exit;
}
mStatus = NO_ERROR;
mUserData = user;
// TODO: add audio hardware input latency here
mLatency = (1000LL * mFrameCount) / mSampleRate;
@ -289,7 +292,9 @@ status_t AudioRecord::set(
mFramesRead = 0;
mFramesReadServerOffset = 0;
return NO_ERROR;
exit:
mStatus = status;
return status;
}
// -------------------------------------------------------------------------
@ -540,70 +545,29 @@ const char * AudioRecord::convertTransferToText(transfer_type transferType) {
}
// must be called with mLock held
status_t AudioRecord::openRecord_l(const Modulo<uint32_t> &epoch, const String16& opPackageName)
status_t AudioRecord::createRecord_l(const Modulo<uint32_t> &epoch, const String16& opPackageName)
{
const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger();
IAudioFlinger::CreateRecordInput input;
IAudioFlinger::CreateRecordOutput output;
audio_session_t originalSessionId;
sp<media::IAudioRecord> record;
void *iMemPointer;
audio_track_cblk_t* cblk;
status_t status;
if (audioFlinger == 0) {
ALOGE("Could not get audioflinger");
return NO_INIT;
status = NO_INIT;
goto exit;
}
audio_io_handle_t input;
// mFlags (not mOrigFlags) is modified depending on whether fast request is accepted.
// After fast request is denied, we will request again if IAudioRecord is re-created.
status_t status;
// Not a conventional loop, but a retry loop for at most two iterations total.
// Try first maybe with FAST flag then try again without FAST flag if that fails.
// Exits loop normally via a return at the bottom, or with error via a break.
// The sp<> references will be dropped when re-entering scope.
// The lack of indentation is deliberate, to reduce code churn and ease merges.
for (;;) {
audio_config_base_t config = {
.sample_rate = mSampleRate,
.channel_mask = mChannelMask,
.format = mFormat
};
mRoutedDeviceId = mSelectedDeviceId;
status = AudioSystem::getInputForAttr(&mAttributes, &input,
mSessionId,
// FIXME compare to AudioTrack
mClientPid,
mClientUid,
&config,
mFlags, &mRoutedDeviceId, &mPortId);
if (status != NO_ERROR || input == AUDIO_IO_HANDLE_NONE) {
ALOGE("Could not get audio input for session %d, record source %d, sample rate %u, "
"format %#x, channel mask %#x, flags %#x",
mSessionId, mAttributes.source, mSampleRate, mFormat, mChannelMask, mFlags);
return BAD_VALUE;
}
// Now that we have a reference to an I/O handle and have not yet handed it off to AudioFlinger,
// we must release it ourselves if anything goes wrong.
#if 0
size_t afFrameCount;
status = AudioSystem::getFrameCount(input, &afFrameCount);
if (status != NO_ERROR) {
ALOGE("getFrameCount(input=%d) status %d", input, status);
break;
}
#endif
uint32_t afSampleRate;
status = AudioSystem::getSamplingRate(input, &afSampleRate);
if (status != NO_ERROR) {
ALOGE("getSamplingRate(input=%d) status %d", input, status);
break;
}
if (mSampleRate == 0) {
mSampleRate = afSampleRate;
}
// Client can only express a preference for FAST. Server will perform additional tests.
if (mFlags & AUDIO_INPUT_FLAG_FAST) {
bool useCaseAllowed =
@ -622,66 +586,41 @@ status_t AudioRecord::openRecord_l(const Modulo<uint32_t> &epoch, const String16
if (!useCaseAllowed) {
ALOGW("AUDIO_INPUT_FLAG_FAST denied, incompatible transfer = %s",
convertTransferToText(mTransfer));
}
// sample rates must also match
bool sampleRateAllowed = mSampleRate == afSampleRate;
if (!sampleRateAllowed) {
ALOGW("AUDIO_INPUT_FLAG_FAST denied, rates do not match %u Hz, require %u Hz",
mSampleRate, afSampleRate);
}
bool fastAllowed = useCaseAllowed && sampleRateAllowed;
if (!fastAllowed) {
mFlags = (audio_input_flags_t) (mFlags & ~(AUDIO_INPUT_FLAG_FAST |
AUDIO_INPUT_FLAG_RAW));
AudioSystem::releaseInput(input, mSessionId);
continue; // retry
}
}
// The notification frame count is the period between callbacks, as suggested by the client
// but moderated by the server. For record, the calculations are done entirely on server side.
size_t notificationFrames = mNotificationFramesReq;
size_t frameCount = mReqFrameCount;
audio_input_flags_t flags = mFlags;
pid_t tid = -1;
input.attr = mAttributes;
input.config.sample_rate = mSampleRate;
input.config.channel_mask = mChannelMask;
input.config.format = mFormat;
input.clientInfo.clientUid = mClientUid;
input.clientInfo.clientPid = mClientPid;
input.clientInfo.clientTid = -1;
if (mFlags & AUDIO_INPUT_FLAG_FAST) {
if (mAudioRecordThread != 0) {
tid = mAudioRecordThread->getTid();
input.clientInfo.clientTid = mAudioRecordThread->getTid();
}
}
input.opPackageName = opPackageName;
size_t temp = frameCount; // temp may be replaced by a revised value of frameCount,
// but we will still need the original value also
audio_session_t originalSessionId = mSessionId;
sp<IMemory> iMem; // for cblk
sp<IMemory> bufferMem;
sp<media::IAudioRecord> record = audioFlinger->openRecord(input,
mSampleRate,
mFormat,
mChannelMask,
opPackageName,
&temp,
&flags,
mClientPid,
tid,
mClientUid,
&mSessionId,
&notificationFrames,
iMem,
bufferMem,
&status,
mPortId);
ALOGE_IF(originalSessionId != AUDIO_SESSION_ALLOCATE && mSessionId != originalSessionId,
"session ID changed from %d to %d", originalSessionId, mSessionId);
input.flags = mFlags;
// The notification frame count is the period between callbacks, as suggested by the client
// but moderated by the server. For record, the calculations are done entirely on server side.
input.frameCount = mReqFrameCount;
input.notificationFrameCount = mNotificationFramesReq;
input.selectedDeviceId = mSelectedDeviceId;
input.sessionId = mSessionId;
originalSessionId = mSessionId;
record = audioFlinger->createRecord(input,
output,
&status);
if (status != NO_ERROR) {
ALOGE("AudioFlinger could not create record track, status: %d", status);
break;
goto exit;
}
ALOG_ASSERT(record != 0);
@ -689,41 +628,41 @@ status_t AudioRecord::openRecord_l(const Modulo<uint32_t> &epoch, const String16
// so we are no longer responsible for releasing it.
mAwaitBoost = false;
if (mFlags & AUDIO_INPUT_FLAG_FAST) {
if (flags & AUDIO_INPUT_FLAG_FAST) {
ALOGI("AUDIO_INPUT_FLAG_FAST successful; frameCount %zu -> %zu", frameCount, temp);
mAwaitBoost = true;
} else {
ALOGW("AUDIO_INPUT_FLAG_FAST denied by server; frameCount %zu -> %zu", frameCount, temp);
mFlags = (audio_input_flags_t) (mFlags & ~(AUDIO_INPUT_FLAG_FAST |
AUDIO_INPUT_FLAG_RAW));
continue; // retry
}
if (output.flags & AUDIO_INPUT_FLAG_FAST) {
ALOGI("AUDIO_INPUT_FLAG_FAST successful; frameCount %zu -> %zu",
mReqFrameCount, output.frameCount);
mAwaitBoost = true;
}
mFlags = flags;
mFlags = output.flags;
mRoutedDeviceId = output.selectedDeviceId;
mSessionId = output.sessionId;
mSampleRate = output.sampleRate;
if (iMem == 0) {
if (output.cblk == 0) {
ALOGE("Could not get control block");
return NO_INIT;
status = NO_INIT;
goto exit;
}
void *iMemPointer = iMem->pointer();
iMemPointer = output.cblk ->pointer();
if (iMemPointer == NULL) {
ALOGE("Could not get control block pointer");
return NO_INIT;
status = NO_INIT;
goto exit;
}
audio_track_cblk_t* cblk = static_cast<audio_track_cblk_t*>(iMemPointer);
cblk = static_cast<audio_track_cblk_t*>(iMemPointer);
// Starting address of buffers in shared memory.
// The buffers are either immediately after the control block,
// or in a separate area at discretion of server.
void *buffers;
if (bufferMem == 0) {
if (output.buffers == 0) {
buffers = cblk + 1;
} else {
buffers = bufferMem->pointer();
buffers = output.buffers->pointer();
if (buffers == NULL) {
ALOGE("Could not get buffer pointer");
return NO_INIT;
status = NO_INIT;
goto exit;
}
}
@ -733,43 +672,42 @@ status_t AudioRecord::openRecord_l(const Modulo<uint32_t> &epoch, const String16
mDeathNotifier.clear();
}
mAudioRecord = record;
mCblkMemory = iMem;
mBufferMemory = bufferMem;
mCblkMemory = output.cblk;
mBufferMemory = output.buffers;
IPCThreadState::self()->flushCommands();
mCblk = cblk;
// note that temp is the (possibly revised) value of frameCount
if (temp < frameCount || (frameCount == 0 && temp == 0)) {
ALOGW("Requested frameCount %zu but received frameCount %zu", frameCount, temp);
// note that output.frameCount is the (possibly revised) value of mReqFrameCount
if (output.frameCount < mReqFrameCount || (mReqFrameCount == 0 && output.frameCount == 0)) {
ALOGW("Requested frameCount %zu but received frameCount %zu",
mReqFrameCount, output.frameCount);
}
frameCount = temp;
// Make sure that application is notified with sufficient margin before overrun.
// The computation is done on server side.
if (mNotificationFramesReq > 0 && notificationFrames != mNotificationFramesReq) {
if (mNotificationFramesReq > 0 && output.notificationFrameCount != mNotificationFramesReq) {
ALOGW("Server adjusted notificationFrames from %u to %zu for frameCount %zu",
mNotificationFramesReq, notificationFrames, frameCount);
mNotificationFramesReq, output.notificationFrameCount, output.frameCount);
}
mNotificationFramesAct = (uint32_t) notificationFrames;
mNotificationFramesAct = (uint32_t)output.notificationFrameCount;
//mInput != input includes the case where mInput == AUDIO_IO_HANDLE_NONE for first creation
if (mDeviceCallback != 0 && mInput != input) {
if (mDeviceCallback != 0 && mInput != output.inputId) {
if (mInput != AUDIO_IO_HANDLE_NONE) {
AudioSystem::removeAudioDeviceCallback(this, mInput);
}
AudioSystem::addAudioDeviceCallback(this, input);
AudioSystem::addAudioDeviceCallback(this, output.inputId);
}
// We retain a copy of the I/O handle, but don't own the reference
mInput = input;
mInput = output.inputId;
mRefreshRemaining = true;
mFrameCount = frameCount;
mFrameCount = output.frameCount;
// If IAudioRecord is re-created, don't let the requested frameCount
// decrease. This can confuse clients that cache frameCount().
if (frameCount > mReqFrameCount) {
mReqFrameCount = frameCount;
if (mFrameCount > mReqFrameCount) {
mReqFrameCount = mFrameCount;
}
// update proxy
@ -780,17 +718,9 @@ status_t AudioRecord::openRecord_l(const Modulo<uint32_t> &epoch, const String16
mDeathNotifier = new DeathNotifier(this);
IInterface::asBinder(mAudioRecord)->linkToDeath(mDeathNotifier, this);
return NO_ERROR;
// End of retry loop.
// The lack of indentation is deliberate, to reduce code churn and ease merges.
}
// Arrive here on error, via a break
AudioSystem::releaseInput(input, mSessionId);
if (status == NO_ERROR) {
status = NO_INIT;
}
exit:
mStatus = status;
// sp<IAudioTrack> track destructor will cause releaseOutput() to be called by AudioFlinger
return status;
}
@ -1222,12 +1152,12 @@ status_t AudioRecord::restoreRecord_l(const char *from)
mFlags = mOrigFlags;
// if the new IAudioRecord is created, openRecord_l() will modify the
// if the new IAudioRecord is created, createRecord_l() will modify the
// following member variables: mAudioRecord, mCblkMemory, mCblk, mBufferMemory.
// It will also delete the strong references on previous IAudioRecord and IMemory
Modulo<uint32_t> position(mProxy->getPosition());
mNewPosition = position + mUpdatePeriod;
status_t result = openRecord_l(position, mOpPackageName);
status_t result = createRecord_l(position, mOpPackageName);
if (result == NO_ERROR) {
if (mActive) {
// callback thread or sync event hasn't changed

@ -30,7 +30,7 @@ namespace android {
enum {
CREATE_TRACK = IBinder::FIRST_CALL_TRANSACTION,
OPEN_RECORD,
CREATE_RECORD,
SAMPLE_RATE,
RESERVED, // obsolete, was CHANNEL_COUNT
FORMAT,
@ -130,102 +130,39 @@ public:
return track;
}
virtual sp<media::IAudioRecord> openRecord(
audio_io_handle_t input,
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
const String16& opPackageName,
size_t *pFrameCount,
audio_input_flags_t *flags,
pid_t pid,
pid_t tid,
int clientUid,
audio_session_t *sessionId,
size_t *notificationFrames,
sp<IMemory>& cblk,
sp<IMemory>& buffers,
status_t *status,
audio_port_handle_t portId)
virtual sp<media::IAudioRecord> createRecord(const CreateRecordInput& input,
CreateRecordOutput& output,
status_t *status)
{
Parcel data, reply;
sp<media::IAudioRecord> record;
data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
data.writeInt32((int32_t) input);
data.writeInt32(sampleRate);
data.writeInt32(format);
data.writeInt32(channelMask);
data.writeString16(opPackageName);
size_t frameCount = pFrameCount != NULL ? *pFrameCount : 0;
data.writeInt64(frameCount);
audio_input_flags_t lFlags = flags != NULL ? *flags : AUDIO_INPUT_FLAG_NONE;
data.writeInt32(lFlags);
data.writeInt32((int32_t) pid);
data.writeInt32((int32_t) tid);
data.writeInt32((int32_t) clientUid);
audio_session_t lSessionId = AUDIO_SESSION_ALLOCATE;
if (sessionId != NULL) {
lSessionId = *sessionId;
if (status == nullptr) {
return record;
}
data.writeInt32(lSessionId);
data.writeInt64(notificationFrames != NULL ? *notificationFrames : 0);
data.writeInt32(portId);
cblk.clear();
buffers.clear();
status_t lStatus = remote()->transact(OPEN_RECORD, data, &reply);
input.writeToParcel(&data);
status_t lStatus = remote()->transact(CREATE_RECORD, data, &reply);
if (lStatus != NO_ERROR) {
ALOGE("openRecord error: %s", strerror(-lStatus));
} else {
frameCount = reply.readInt64();
if (pFrameCount != NULL) {
*pFrameCount = frameCount;
}
lFlags = (audio_input_flags_t)reply.readInt32();
if (flags != NULL) {
*flags = lFlags;
}
lSessionId = (audio_session_t) reply.readInt32();
if (sessionId != NULL) {
*sessionId = lSessionId;
}
size_t lNotificationFrames = (size_t) reply.readInt64();
if (notificationFrames != NULL) {
*notificationFrames = lNotificationFrames;
}
lStatus = reply.readInt32();
record = interface_cast<media::IAudioRecord>(reply.readStrongBinder());
cblk = interface_cast<IMemory>(reply.readStrongBinder());
if (cblk != 0 && cblk->pointer() == NULL) {
cblk.clear();
}
buffers = interface_cast<IMemory>(reply.readStrongBinder());
if (buffers != 0 && buffers->pointer() == NULL) {
buffers.clear();
}
if (lStatus == NO_ERROR) {
if (record == 0) {
ALOGE("openRecord should have returned an IAudioRecord");
lStatus = UNKNOWN_ERROR;
} else if (cblk == 0) {
ALOGE("openRecord should have returned a cblk");
lStatus = NO_MEMORY;
}
// buffers is permitted to be 0
} else {
if (record != 0 || cblk != 0 || buffers != 0) {
ALOGE("openRecord returned an IAudioRecord, cblk, "
"or buffers but with status %d", lStatus);
}
}
if (lStatus != NO_ERROR) {
record.clear();
cblk.clear();
buffers.clear();
}
ALOGE("createRecord transaction error %d", lStatus);
*status = DEAD_OBJECT;
return record;
}
if (status != NULL) {
*status = lStatus;
*status = reply.readInt32();
if (*status != NO_ERROR) {
ALOGE("createRecord returned error %d", *status);
return record;
}
record = interface_cast<media::IAudioRecord>(reply.readStrongBinder());
if (record == 0) {
ALOGE("createRecord returned a NULL IAudioRecord with status OK");
*status = DEAD_OBJECT;
return record;
}
output.readFromParcel(&reply);
return record;
}
@ -905,7 +842,7 @@ status_t BnAudioFlinger::onTransact(
// TODO should select more wisely the items from the list
switch (code) {
case CREATE_TRACK:
case OPEN_RECORD:
case CREATE_RECORD:
case SET_MASTER_VOLUME:
case SET_MASTER_MUTE:
case SET_STREAM_VOLUME:
@ -948,37 +885,29 @@ status_t BnAudioFlinger::onTransact(
output.writeToParcel(reply);
return NO_ERROR;
} break;
case OPEN_RECORD: {
case CREATE_RECORD: {
CHECK_INTERFACE(IAudioFlinger, data, reply);
audio_io_handle_t input = (audio_io_handle_t) data.readInt32();
uint32_t sampleRate = data.readInt32();
audio_format_t format = (audio_format_t) data.readInt32();
audio_channel_mask_t channelMask = data.readInt32();
const String16& opPackageName = data.readString16();
size_t frameCount = data.readInt64();
audio_input_flags_t flags = (audio_input_flags_t) data.readInt32();
pid_t pid = (pid_t) data.readInt32();
pid_t tid = (pid_t) data.readInt32();
int clientUid = data.readInt32();
audio_session_t sessionId = (audio_session_t) data.readInt32();
size_t notificationFrames = data.readInt64();
audio_port_handle_t portId = (audio_port_handle_t) data.readInt32();
sp<IMemory> cblk;
sp<IMemory> buffers;
status_t status = NO_ERROR;
sp<media::IAudioRecord> record = openRecord(input,
sampleRate, format, channelMask, opPackageName, &frameCount, &flags,
pid, tid, clientUid, &sessionId, &notificationFrames, cblk, buffers,
&status, portId);
CreateRecordInput input;
if (input.readFromParcel((Parcel*)&data) != NO_ERROR) {
reply->writeInt32(DEAD_OBJECT);
return NO_ERROR;
}
status_t status;
CreateRecordOutput output;
sp<media::IAudioRecord> record = createRecord(input,
output,
&status);
LOG_ALWAYS_FATAL_IF((record != 0) != (status == NO_ERROR));
reply->writeInt64(frameCount);
reply->writeInt32(flags);
reply->writeInt32(sessionId);
reply->writeInt64(notificationFrames);
reply->writeInt32(status);
if (status != NO_ERROR) {
return NO_ERROR;
}
reply->writeStrongBinder(IInterface::asBinder(record));
reply->writeStrongBinder(IInterface::asBinder(cblk));
reply->writeStrongBinder(IInterface::asBinder(buffers));
output.writeToParcel(reply);
return NO_ERROR;
} break;
case SAMPLE_RATE: {

@ -16,6 +16,7 @@
package android.media;
/* Native code must specify namespace media (media::IAudioRecord) when referring to this class */
interface IAudioRecord {
/* After it's created the track is not active. Call start() to

@ -19,12 +19,13 @@
#define ANDROID_AUDIO_CLIENT_H
#include <binder/Parcel.h>
#include <binder/Parcelable.h>
#include <system/audio.h>
#include <utils/String16.h>
namespace android {
class AudioClient {
class AudioClient : public Parcelable {
public:
AudioClient() :
clientUid(-1), clientPid(-1), clientTid(-1), packageName("") {}
@ -34,7 +35,7 @@ class AudioClient {
pid_t clientTid;
String16 packageName;
status_t readFromParcel(Parcel *parcel) {
status_t readFromParcel(const Parcel *parcel) override {
clientUid = parcel->readInt32();
clientPid = parcel->readInt32();
clientTid = parcel->readInt32();
@ -42,7 +43,7 @@ class AudioClient {
return NO_ERROR;
}
status_t writeToParcel(Parcel *parcel) const {
status_t writeToParcel(Parcel *parcel) const override {
parcel->writeInt32(clientUid);
parcel->writeInt32(clientPid);
parcel->writeInt32(clientTid);

@ -570,7 +570,7 @@ private:
// caller must hold lock on mLock for all _l methods
status_t openRecord_l(const Modulo<uint32_t> &epoch, const String16& opPackageName);
status_t createRecord_l(const Modulo<uint32_t> &epoch, const String16& opPackageName);
// FIXME enum is faster than strcmp() for parameter 'from'
status_t restoreRecord_l(const char *from);
@ -682,7 +682,6 @@ private:
// May not match the app selection depending on other
// activity and connected devices
wp<AudioSystem::AudioDeviceCallback> mDeviceCallback;
audio_port_handle_t mPortId; // unique ID allocated by audio policy
};

@ -231,7 +231,7 @@ public:
audio_stream_type_t stream,
audio_session_t session);
// Client must successfully hand off the handle reference to AudioFlinger via openRecord(),
// Client must successfully hand off the handle reference to AudioFlinger via createRecord(),
// or release it with releaseInput().
static status_t getInputForAttr(const audio_attributes_t *attr,
audio_io_handle_t *input,

@ -25,6 +25,7 @@
#include <utils/Errors.h>
#include <binder/IInterface.h>
#include <binder/Parcel.h>
#include <binder/Parcelable.h>
#include <media/AudioClient.h>
#include <media/IAudioTrack.h>
#include <media/IAudioFlingerClient.h>
@ -50,9 +51,9 @@ public:
* when calling createTrack() including arguments that will be updated by AudioFlinger
* and returned in CreateTrackOutput object
*/
class CreateTrackInput {
class CreateTrackInput : public Parcelable {
public:
status_t readFromParcel(Parcel *parcel) {
status_t readFromParcel(const Parcel *parcel) override {
/* input arguments*/
memset(&attr, 0, sizeof(audio_attributes_t));
if (parcel->read(&attr, sizeof(audio_attributes_t)) != NO_ERROR) {
@ -63,7 +64,9 @@ public:
if (parcel->read(&config, sizeof(audio_config_t)) != NO_ERROR) {
return DEAD_OBJECT;
}
(void)clientInfo.readFromParcel(parcel);
if (clientInfo.readFromParcel(parcel) != NO_ERROR) {
return DEAD_OBJECT;
}
if (parcel->readInt32() != 0) {
sharedBuffer = interface_cast<IMemory>(parcel->readStrongBinder());
if (sharedBuffer == 0 || sharedBuffer->pointer() == NULL) {
@ -82,7 +85,7 @@ public:
return NO_ERROR;
}
status_t writeToParcel(Parcel *parcel) const {
status_t writeToParcel(Parcel *parcel) const override {
/* input arguments*/
(void)parcel->write(&attr, sizeof(audio_attributes_t));
(void)parcel->write(&config, sizeof(audio_config_t));
@ -125,9 +128,9 @@ public:
* when calling createTrack() including arguments that were passed as I/O for update by
* CreateTrackInput.
*/
class CreateTrackOutput {
class CreateTrackOutput : public Parcelable {
public:
status_t readFromParcel(Parcel *parcel) {
status_t readFromParcel(const Parcel *parcel) override {
/* input/output arguments*/
(void)parcel->read(&flags, sizeof(audio_output_flags_t));
frameCount = parcel->readInt64();
@ -144,7 +147,7 @@ public:
return NO_ERROR;
}
status_t writeToParcel(Parcel *parcel) const {
status_t writeToParcel(Parcel *parcel) const override {
/* input/output arguments*/
(void)parcel->write(&flags, sizeof(audio_output_flags_t));
(void)parcel->writeInt64(frameCount);
@ -176,6 +179,140 @@ public:
audio_io_handle_t outputId;
};
/* CreateRecordInput contains all input arguments sent by AudioRecord to AudioFlinger
* when calling createRecord() including arguments that will be updated by AudioFlinger
* and returned in CreateRecordOutput object
*/
class CreateRecordInput : public Parcelable {
public:
status_t readFromParcel(const Parcel *parcel) override {
/* input arguments*/
memset(&attr, 0, sizeof(audio_attributes_t));
if (parcel->read(&attr, sizeof(audio_attributes_t)) != NO_ERROR) {
return DEAD_OBJECT;
}
attr.tags[AUDIO_ATTRIBUTES_TAGS_MAX_SIZE -1] = '\0';
memset(&config, 0, sizeof(audio_config_base_t));
if (parcel->read(&config, sizeof(audio_config_base_t)) != NO_ERROR) {
return DEAD_OBJECT;
}
if (clientInfo.readFromParcel(parcel) != NO_ERROR) {
return DEAD_OBJECT;
}
opPackageName = parcel->readString16();
/* input/output arguments*/
(void)parcel->read(&flags, sizeof(audio_input_flags_t));
frameCount = parcel->readInt64();
notificationFrameCount = parcel->readInt64();
(void)parcel->read(&selectedDeviceId, sizeof(audio_port_handle_t));
(void)parcel->read(&sessionId, sizeof(audio_session_t));
return NO_ERROR;
}
status_t writeToParcel(Parcel *parcel) const override {
/* input arguments*/
(void)parcel->write(&attr, sizeof(audio_attributes_t));
(void)parcel->write(&config, sizeof(audio_config_base_t));
(void)clientInfo.writeToParcel(parcel);
(void)parcel->writeString16(opPackageName);
/* input/output arguments*/
(void)parcel->write(&flags, sizeof(audio_input_flags_t));
(void)parcel->writeInt64(frameCount);
(void)parcel->writeInt64(notificationFrameCount);
(void)parcel->write(&selectedDeviceId, sizeof(audio_port_handle_t));
(void)parcel->write(&sessionId, sizeof(audio_session_t));
return NO_ERROR;
}
/* input */
audio_attributes_t attr;
audio_config_base_t config;
AudioClient clientInfo;
String16 opPackageName;
/* input/output */
audio_input_flags_t flags;
size_t frameCount;
size_t notificationFrameCount;
audio_port_handle_t selectedDeviceId;
audio_session_t sessionId;
};
/* CreateRecordOutput contains all output arguments returned by AudioFlinger to AudioRecord
* when calling createRecord() including arguments that were passed as I/O for update by
* CreateRecordInput.
*/
class CreateRecordOutput : public Parcelable {
public:
status_t readFromParcel(const Parcel *parcel) override {
/* input/output arguments*/
(void)parcel->read(&flags, sizeof(audio_input_flags_t));
frameCount = parcel->readInt64();
notificationFrameCount = parcel->readInt64();
(void)parcel->read(&selectedDeviceId, sizeof(audio_port_handle_t));
(void)parcel->read(&sessionId, sizeof(audio_session_t));
/* output arguments*/
sampleRate = parcel->readUint32();
(void)parcel->read(&inputId, sizeof(audio_io_handle_t));
if (parcel->readInt32() != 0) {
cblk = interface_cast<IMemory>(parcel->readStrongBinder());
if (cblk == 0 || cblk->pointer() == NULL) {
return BAD_VALUE;
}
}
if (parcel->readInt32() != 0) {
buffers = interface_cast<IMemory>(parcel->readStrongBinder());
if (buffers == 0 || buffers->pointer() == NULL) {
return BAD_VALUE;
}
}
return NO_ERROR;
}
status_t writeToParcel(Parcel *parcel) const override {
/* input/output arguments*/
(void)parcel->write(&flags, sizeof(audio_input_flags_t));
(void)parcel->writeInt64(frameCount);
(void)parcel->writeInt64(notificationFrameCount);
(void)parcel->write(&selectedDeviceId, sizeof(audio_port_handle_t));
(void)parcel->write(&sessionId, sizeof(audio_session_t));
/* output arguments*/
(void)parcel->writeUint32(sampleRate);
(void)parcel->write(&inputId, sizeof(audio_io_handle_t));
if (cblk != 0) {
(void)parcel->writeInt32(1);
(void)parcel->writeStrongBinder(IInterface::asBinder(cblk));
} else {
(void)parcel->writeInt32(0);
}
if (buffers != 0) {
(void)parcel->writeInt32(1);
(void)parcel->writeStrongBinder(IInterface::asBinder(buffers));
} else {
(void)parcel->writeInt32(0);
}
return NO_ERROR;
}
/* input/output */
audio_input_flags_t flags;
size_t frameCount;
size_t notificationFrameCount;
audio_port_handle_t selectedDeviceId;
audio_session_t sessionId;
/* output */
uint32_t sampleRate;
audio_io_handle_t inputId;
sp<IMemory> cblk;
sp<IMemory> buffers;
};
// invariant on exit for all APIs that return an sp<>:
// (return value != 0) == (*status == NO_ERROR)
@ -186,26 +323,9 @@ public:
CreateTrackOutput& output,
status_t *status) = 0;
virtual sp<media::IAudioRecord> openRecord(
// On successful return, AudioFlinger takes over the handle
// reference and will release it when the track is destroyed.
// However on failure, the client is responsible for release.
audio_io_handle_t input,
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
const String16& callingPackage,
size_t *pFrameCount,
audio_input_flags_t *flags,
pid_t pid,
pid_t tid, // -1 means unused, otherwise must be valid non-0
int clientUid,
audio_session_t *sessionId,
size_t *notificationFrames,
sp<IMemory>& cblk,
sp<IMemory>& buffers, // return value 0 means it follows cblk
status_t *status,
audio_port_handle_t portId) = 0;
virtual sp<media::IAudioRecord> createRecord(const CreateRecordInput& input,
CreateRecordOutput& output,
status_t *status) = 0;
// FIXME Surprisingly, format/latency don't work for input handles

@ -674,7 +674,11 @@ sp<IAudioTrack> AudioFlinger::createTrack(const CreateTrackInput& input,
audio_session_t sessionId = input.sessionId;
if (sessionId == AUDIO_SESSION_ALLOCATE) {
sessionId = (audio_session_t) newAudioUniqueId(AUDIO_UNIQUE_ID_USE_SESSION);
} else if (audio_unique_id_get_use(sessionId) != AUDIO_UNIQUE_ID_USE_SESSION) {
lStatus = BAD_VALUE;
goto Exit;
}
output.sessionId = sessionId;
output.outputId = AUDIO_IO_HANDLE_NONE;
output.selectedDeviceId = input.selectedDeviceId;
@ -1568,120 +1572,144 @@ void AudioFlinger::requestLogMerge() {
// ----------------------------------------------------------------------------
sp<media::IAudioRecord> AudioFlinger::openRecord(
audio_io_handle_t input,
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
const String16& opPackageName,
size_t *frameCount,
audio_input_flags_t *flags,
pid_t pid,
pid_t tid,
int clientUid,
audio_session_t *sessionId,
size_t *notificationFrames,
sp<IMemory>& cblk,
sp<IMemory>& buffers,
status_t *status,
audio_port_handle_t portId)
sp<media::IAudioRecord> AudioFlinger::createRecord(const CreateRecordInput& input,
CreateRecordOutput& output,
status_t *status)
{
sp<RecordThread::RecordTrack> recordTrack;
sp<RecordHandle> recordHandle;
sp<Client> client;
status_t lStatus;
audio_session_t lSessionId;
audio_session_t sessionId = input.sessionId;
audio_port_handle_t portId;
cblk.clear();
buffers.clear();
output.cblk.clear();
output.buffers.clear();
bool updatePid = (pid == -1);
bool updatePid = (input.clientInfo.clientPid == -1);
const uid_t callingUid = IPCThreadState::self()->getCallingUid();
uid_t clientUid = input.clientInfo.clientUid;
if (!isTrustedCallingUid(callingUid)) {
ALOGW_IF((uid_t)clientUid != callingUid,
"%s uid %d tried to pass itself off as %d", __FUNCTION__, callingUid, clientUid);
ALOGW_IF(clientUid != callingUid,
"%s uid %d tried to pass itself off as %d",
__FUNCTION__, callingUid, clientUid);
clientUid = callingUid;
updatePid = true;
}
pid_t clientPid = input.clientInfo.clientPid;
if (updatePid) {
const pid_t callingPid = IPCThreadState::self()->getCallingPid();
ALOGW_IF(pid != -1 && pid != callingPid,
ALOGW_IF(clientPid != -1 && clientPid != callingPid,
"%s uid %d pid %d tried to pass itself off as pid %d",
__func__, callingUid, callingPid, pid);
pid = callingPid;
__func__, callingUid, callingPid, clientPid);
clientPid = callingPid;
}
// check calling permissions
if (!recordingAllowed(opPackageName, tid, clientUid)) {
ALOGE("openRecord() permission denied: recording not allowed");
if (!recordingAllowed(input.opPackageName, input.clientInfo.clientTid, clientUid)) {
ALOGE("createRecord() permission denied: recording not allowed");
lStatus = PERMISSION_DENIED;
goto Exit;
}
// further sample rate checks are performed by createRecordTrack_l()
if (sampleRate == 0) {
ALOGE("openRecord() invalid sample rate %u", sampleRate);
// we don't yet support anything other than linear PCM
if (!audio_is_valid_format(input.config.format) || !audio_is_linear_pcm(input.config.format)) {
ALOGE("createRecord() invalid format %#x", input.config.format);
lStatus = BAD_VALUE;
goto Exit;
}
// we don't yet support anything other than linear PCM
if (!audio_is_valid_format(format) || !audio_is_linear_pcm(format)) {
ALOGE("openRecord() invalid format %#x", format);
// further channel mask checks are performed by createRecordTrack_l()
if (!audio_is_input_channel(input.config.channel_mask)) {
ALOGE("createRecord() invalid channel mask %#x", input.config.channel_mask);
lStatus = BAD_VALUE;
goto Exit;
}
// further channel mask checks are performed by createRecordTrack_l()
if (!audio_is_input_channel(channelMask)) {
ALOGE("openRecord() invalid channel mask %#x", channelMask);
if (sessionId == AUDIO_SESSION_ALLOCATE) {
sessionId = (audio_session_t) newAudioUniqueId(AUDIO_UNIQUE_ID_USE_SESSION);
} else if (audio_unique_id_get_use(sessionId) != AUDIO_UNIQUE_ID_USE_SESSION) {
lStatus = BAD_VALUE;
goto Exit;
}
output.sessionId = sessionId;
output.inputId = AUDIO_IO_HANDLE_NONE;
output.selectedDeviceId = input.selectedDeviceId;
output.flags = input.flags;
client = registerPid(clientPid);
// Not a conventional loop, but a retry loop for at most two iterations total.
// Try first maybe with FAST flag then try again without FAST flag if that fails.
// Exits loop via break on no error of got exit on error
// The sp<> references will be dropped when re-entering scope.
// The lack of indentation is deliberate, to reduce code churn and ease merges.
for (;;) {
lStatus = AudioSystem::getInputForAttr(&input.attr, &output.inputId,
sessionId,
// FIXME compare to AudioTrack
clientPid,
clientUid,
&input.config,
output.flags, &output.selectedDeviceId, &portId);
{
Mutex::Autolock _l(mLock);
RecordThread *thread = checkRecordThread_l(input);
RecordThread *thread = checkRecordThread_l(output.inputId);
if (thread == NULL) {
ALOGE("openRecord() checkRecordThread_l failed");
ALOGE("createRecord() checkRecordThread_l failed");
lStatus = BAD_VALUE;
goto Exit;
}
client = registerPid(pid);
ALOGV("createRecord() lSessionId: %d input %d", sessionId, output.inputId);
if (sessionId != NULL && *sessionId != AUDIO_SESSION_ALLOCATE) {
if (audio_unique_id_get_use(*sessionId) != AUDIO_UNIQUE_ID_USE_SESSION) {
lStatus = BAD_VALUE;
goto Exit;
}
lSessionId = *sessionId;
} else {
// if no audio session id is provided, create one here
lSessionId = (audio_session_t) nextUniqueId(AUDIO_UNIQUE_ID_USE_SESSION);
if (sessionId != NULL) {
*sessionId = lSessionId;
}
}
ALOGV("openRecord() lSessionId: %d input %d", lSessionId, input);
output.sampleRate = input.config.sample_rate;
output.frameCount = input.frameCount;
output.notificationFrameCount = input.notificationFrameCount;
recordTrack = thread->createRecordTrack_l(client, sampleRate, format, channelMask,
frameCount, lSessionId, notificationFrames,
clientUid, flags, tid, &lStatus, portId);
recordTrack = thread->createRecordTrack_l(client, &output.sampleRate,
input.config.format, input.config.channel_mask,
&output.frameCount, sessionId,
&output.notificationFrameCount,
clientUid, &output.flags,
input.clientInfo.clientTid,
&lStatus, portId);
LOG_ALWAYS_FATAL_IF((lStatus == NO_ERROR) && (recordTrack == 0));
if (lStatus == NO_ERROR) {
// Check if one effect chain was awaiting for an AudioRecord to be created on this
// session and move it to this thread.
sp<EffectChain> chain = getOrphanEffectChain_l(lSessionId);
if (chain != 0) {
Mutex::Autolock _l(thread->mLock);
thread->addEffectChain_l(chain);
}
// lStatus == BAD_TYPE means FAST flag was rejected: request a new input from
// audio policy manager without FAST constraint
if (lStatus == BAD_TYPE) {
AudioSystem::releaseInput(output.inputId, sessionId);
recordTrack.clear();
continue;
}
if (lStatus != NO_ERROR) {
recordTrack.clear();
goto Exit;
}
// Check if one effect chain was awaiting for an AudioRecord to be created on this
// session and move it to this thread.
sp<EffectChain> chain = getOrphanEffectChain_l(sessionId);
if (chain != 0) {
Mutex::Autolock _l(thread->mLock);
thread->addEffectChain_l(chain);
}
break;
}
// End of retry loop.
// The lack of indentation is deliberate, to reduce code churn and ease merges.
}
output.cblk = recordTrack->getCblk();
output.buffers = recordTrack->getBuffers();
// return handle to client
recordHandle = new RecordHandle(recordTrack);
Exit:
if (lStatus != NO_ERROR) {
// remove local strong reference to Client before deleting the RecordTrack so that the
// Client destructor is called by the TrackBase destructor with mClientLock held
@ -1691,17 +1719,8 @@ sp<media::IAudioRecord> AudioFlinger::openRecord(
Mutex::Autolock _cl(mClientLock);
client.clear();
}
recordTrack.clear();
goto Exit;
}
cblk = recordTrack->getCblk();
buffers = recordTrack->getBuffers();
// return handle to client
recordHandle = new RecordHandle(recordTrack);
Exit:
*status = lStatus;
return recordHandle;
}

@ -118,23 +118,9 @@ public:
CreateTrackOutput& output,
status_t *status);
virtual sp<media::IAudioRecord> openRecord(
audio_io_handle_t input,
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
const String16& opPackageName,
size_t *pFrameCount,
audio_input_flags_t *flags,
pid_t pid,
pid_t tid,
int clientUid,
audio_session_t *sessionId,
size_t *notificationFrames,
sp<IMemory>& cblk,
sp<IMemory>& buffers,
status_t *status /*non-NULL*/,
audio_port_handle_t portId);
virtual sp<media::IAudioRecord> createRecord(const CreateRecordInput& input,
CreateRecordOutput& output,
status_t *status);
virtual uint32_t sampleRate(audio_io_handle_t ioHandle) const;
virtual audio_format_t format(audio_io_handle_t output) const;

@ -6708,12 +6708,12 @@ void AudioFlinger::RecordThread::inputStandBy()
// RecordThread::createRecordTrack_l() must be called with AudioFlinger::mLock held
sp<AudioFlinger::RecordThread::RecordTrack> AudioFlinger::RecordThread::createRecordTrack_l(
const sp<AudioFlinger::Client>& client,
uint32_t sampleRate,
uint32_t *pSampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
size_t *pFrameCount,
audio_session_t sessionId,
size_t *notificationFrames,
size_t *pNotificationFrameCount,
uid_t uid,
audio_input_flags_t *flags,
pid_t tid,
@ -6721,16 +6721,30 @@ sp<AudioFlinger::RecordThread::RecordTrack> AudioFlinger::RecordThread::createRe
audio_port_handle_t portId)
{
size_t frameCount = *pFrameCount;
size_t notificationFrameCount = *pNotificationFrameCount;
sp<RecordTrack> track;
status_t lStatus;
audio_input_flags_t inputFlags = mInput->flags;
audio_input_flags_t requestedFlags = *flags;
uint32_t sampleRate;
lStatus = initCheck();
if (lStatus != NO_ERROR) {
ALOGE("createRecordTrack_l() audio driver not initialized");
goto Exit;
}
if (*pSampleRate == 0) {
*pSampleRate = mSampleRate;
}
sampleRate = *pSampleRate;
// special case for FAST flag considered OK if fast capture is present
if (hasFastCapture()) {
inputFlags = (audio_input_flags_t)(inputFlags | AUDIO_INPUT_FLAG_FAST);
}
// Check if requested flags are compatible with output stream flags
// Check if requested flags are compatible with input stream flags
if ((*flags & inputFlags) != *flags) {
ALOGW("createRecordTrack_l(): mismatch between requested flags (%08x) and"
" input flags (%08x)",
@ -6785,12 +6799,20 @@ sp<AudioFlinger::RecordThread::RecordTrack> AudioFlinger::RecordThread::createRe
}
}
// If FAST or RAW flags were corrected, ask caller to request new input from audio policy
if ((*flags & AUDIO_INPUT_FLAG_FAST) !=
(requestedFlags & AUDIO_INPUT_FLAG_FAST)) {
*flags = (audio_input_flags_t) (*flags & ~(AUDIO_INPUT_FLAG_FAST | AUDIO_INPUT_FLAG_RAW));
lStatus = BAD_TYPE;
goto Exit;
}
// compute track buffer size in frames, and suggest the notification frame count
if (*flags & AUDIO_INPUT_FLAG_FAST) {
// fast track: frame count is exactly the pipe depth
frameCount = mPipeFramesP2;
// ignore requested notificationFrames, and always notify exactly once every HAL buffer
*notificationFrames = mFrameCount;
notificationFrameCount = mFrameCount;
} else {
// not fast track: max notification period is resampled equivalent of one HAL buffer time
// or 20 ms if there is a fast capture
@ -6809,17 +6831,12 @@ sp<AudioFlinger::RecordThread::RecordTrack> AudioFlinger::RecordThread::createRe
const size_t minFrameCount = maxNotificationFrames *
max(kMinNotifications, minNotificationsByMs);
frameCount = max(frameCount, minFrameCount);
if (*notificationFrames == 0 || *notificationFrames > maxNotificationFrames) {
*notificationFrames = maxNotificationFrames;
if (notificationFrameCount == 0 || notificationFrameCount > maxNotificationFrames) {
notificationFrameCount = maxNotificationFrames;
}
}
*pFrameCount = frameCount;
lStatus = initCheck();
if (lStatus != NO_ERROR) {
ALOGE("createRecordTrack_l() audio driver not initialized");
goto Exit;
}
*pNotificationFrameCount = notificationFrameCount;
{ // scope for mLock
Mutex::Autolock _l(mLock);

@ -1327,12 +1327,12 @@ public:
sp<AudioFlinger::RecordThread::RecordTrack> createRecordTrack_l(
const sp<AudioFlinger::Client>& client,
uint32_t sampleRate,
uint32_t *pSampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
size_t *pFrameCount,
audio_session_t sessionId,
size_t *notificationFrames,
size_t *pNotificationFrameCount,
uid_t uid,
audio_input_flags_t *flags,
pid_t tid,

@ -192,7 +192,7 @@ protected:
// where for AudioTrack (but not AudioRecord),
// 8-bit PCM samples are stored as 16-bit
const size_t mFrameCount;// size of track buffer given at createTrack() or
// openRecord(), and then adjusted as needed
// createRecord(), and then adjusted as needed
const audio_session_t mSessionId;
uid_t mUid;

Loading…
Cancel
Save