Merge tag 'android-4.4.1_r1' into HEAD
Android 4.4.1 Release 1
Conflicts:
media/libmediaplayerservice/StagefrightRecorder.cpp
media/libstagefright/AudioSource.cpp
media/libstagefright/AwesomePlayer.cpp
media/libstagefright/DataSource.cpp
media/libstagefright/OMXCodec.cpp
services/audioflinger/RecordTracks.h
services/audioflinger/Threads.cpp
services/audioflinger/Threads.h
services/audioflinger/Tracks.cpp
Change-Id: Ie17f9030275faae7a0f907b8ccc3195a7657742d
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 7bc1c7d..2145e17 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -473,6 +473,7 @@
pid_t tid,
int *sessionId,
String8& name,
+ int clientUid,
status_t *status)
{
sp<PlaybackThread::Track> track;
@@ -508,6 +509,7 @@
}
pid_t pid = IPCThreadState::self()->getCallingPid();
+
client = registerPid_l(pid);
ALOGV("createTrack() sessionId: %d", (sessionId == NULL) ? -2 : *sessionId);
@@ -535,7 +537,7 @@
ALOGV("createTrack() lSessionId: %d", lSessionId);
track = thread->createTrack_l(client, streamType, sampleRate, format,
- channelMask, frameCount, sharedBuffer, lSessionId, flags, tid, &lStatus);
+ channelMask, frameCount, sharedBuffer, lSessionId, flags, tid, clientUid, &lStatus);
// move effect chain to this output thread if an effect on same session was waiting
// for a track to be created
@@ -1590,8 +1592,11 @@
// create new record track.
// The record track uses one track in mHardwareMixerThread by convention.
+ // TODO: the uid should be passed in as a parameter to openRecord
recordTrack = thread->createRecordTrack_l(client, sampleRate, format, channelMask,
- frameCount, lSessionId, flags, tid, &lStatus);
+ frameCount, lSessionId,
+ IPCThreadState::self()->getCallingUid(),
+ flags, tid, &lStatus);
LOG_ALWAYS_FATAL_IF((recordTrack != 0) != (lStatus == NO_ERROR));
}
if (lStatus != NO_ERROR) {
@@ -2734,6 +2739,7 @@
strategy,
sessionId,
effect->id());
+ AudioSystem::setEffectEnabled(effect->id(), effect->isEnabled());
}
effect = chain->getEffectFromId_l(0);
}
@@ -2748,6 +2754,7 @@
strategy,
sessionId,
removed[i]->id());
+ AudioSystem::setEffectEnabled(effect->id(), effect->isEnabled());
}
}
}
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 97de2f6..c0c9b99 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -122,6 +122,7 @@
pid_t tid,
int *sessionId,
String8& name,
+ int clientUid,
status_t *status);
#ifdef QCOM_HARDWARE
virtual sp<IDirectTrack> createDirectTrack(
diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h
index a2e2511..43b77f3 100644
--- a/services/audioflinger/PlaybackTracks.h
+++ b/services/audioflinger/PlaybackTracks.h
@@ -31,6 +31,7 @@
size_t frameCount,
const sp<IMemory>& sharedBuffer,
int sessionId,
+ int uid,
IAudioFlinger::track_flags_t flags);
virtual ~Track();
@@ -165,7 +166,8 @@
audio_channel_mask_t channelMask,
size_t frameCount,
const sp<IMemory>& sharedBuffer,
- int sessionId);
+ int sessionId,
+ int uid);
virtual ~TimedTrack();
class TimedBuffer {
@@ -208,7 +210,8 @@
audio_channel_mask_t channelMask,
size_t frameCount,
const sp<IMemory>& sharedBuffer,
- int sessionId);
+ int sessionId,
+ int uid);
void timedYieldSamples_l(AudioBufferProvider::Buffer* buffer);
void timedYieldSilence_l(uint32_t numFrames,
@@ -255,7 +258,8 @@
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
- size_t frameCount);
+ size_t frameCount,
+ int uid);
virtual ~OutputTrack();
virtual status_t start(AudioSystem::sync_event_t event =
diff --git a/services/audioflinger/RecordTracks.h b/services/audioflinger/RecordTracks.h
index 4b0c4d8..8e20d5a 100644
--- a/services/audioflinger/RecordTracks.h
+++ b/services/audioflinger/RecordTracks.h
@@ -31,7 +31,8 @@
#ifdef QCOM_HARDWARE
uint32_t flags,
#endif
- int sessionId);
+ int sessionId,
+ int uid);
virtual ~RecordTrack();
virtual status_t start(AudioSystem::sync_event_t event, int triggerSession);
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 7559d4c..c315101 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -283,6 +283,7 @@
// mSampleRate, mFrameCount, mChannelMask, mChannelCount, mFrameSize, and mFormat are
// set by PlaybackThread::readOutputParameters() or RecordThread::readInputParameters()
mParamStatus(NO_ERROR),
+ //FIXME: mStandby should be true here. Is this some kind of hack?
mStandby(false), mOutDevice(outDevice), mInDevice(inDevice),
mAudioSource(AUDIO_SOURCE_DEFAULT), mId(id),
// mName will be set by concrete (non-virtual) subclass
@@ -521,17 +522,7 @@
void AudioFlinger::ThreadBase::acquireWakeLock_l(int uid)
{
- if (mPowerManager == 0) {
- // use checkService() to avoid blocking if power service is not up yet
- sp<IBinder> binder =
- defaultServiceManager()->checkService(String16("power"));
- if (binder == 0) {
- ALOGW("Thread %s cannot connect to the power manager service", mName);
- } else {
- mPowerManager = interface_cast<IPowerManager>(binder);
- binder->linkToDeath(mDeathRecipient);
- }
- }
+ getPowerManager_l();
if (mPowerManager != 0) {
sp<IBinder> binder = new BBinder();
status_t status;
@@ -571,6 +562,41 @@
}
}
+void AudioFlinger::ThreadBase::updateWakeLockUids(const SortedVector<int> &uids) {
+ Mutex::Autolock _l(mLock);
+ updateWakeLockUids_l(uids);
+}
+
+void AudioFlinger::ThreadBase::getPowerManager_l() {
+
+ if (mPowerManager == 0) {
+ // use checkService() to avoid blocking if power service is not up yet
+ sp<IBinder> binder =
+ defaultServiceManager()->checkService(String16("power"));
+ if (binder == 0) {
+ ALOGW("Thread %s cannot connect to the power manager service", mName);
+ } else {
+ mPowerManager = interface_cast<IPowerManager>(binder);
+ binder->linkToDeath(mDeathRecipient);
+ }
+ }
+}
+
+void AudioFlinger::ThreadBase::updateWakeLockUids_l(const SortedVector<int> &uids) {
+
+ getPowerManager_l();
+ if (mWakeLockToken == NULL) {
+ ALOGE("no wake lock to update!");
+ return;
+ }
+ if (mPowerManager != 0) {
+ sp<IBinder> binder = new BBinder();
+ status_t status;
+ status = mPowerManager->updateWakeLockUids(mWakeLockToken, uids.size(), uids.array());
+ ALOGV("acquireWakeLock_l() %s status %d", mName, status);
+ }
+}
+
void AudioFlinger::ThreadBase::clearPowerManager()
{
Mutex::Autolock _l(mLock);
@@ -1029,6 +1055,7 @@
: ThreadBase(audioFlinger, id, device, AUDIO_DEVICE_NONE, type),
mNormalFrameCount(0), mMixBuffer(NULL),
mAllocMixBuffer(NULL), mSuspended(0), mBytesWritten(0),
+ mActiveTracksGeneration(0),
// mStreamTypes[] initialized in constructor body
mOutput(output),
mLastWriteTime(0), mNumWrites(0), mNumDelayedWrites(0), mInWrite(false),
@@ -1212,6 +1239,7 @@
int sessionId,
IAudioFlinger::track_flags_t *flags,
pid_t tid,
+ int uid,
status_t *status)
{
sp<Track> track;
@@ -1358,10 +1386,10 @@
if (!isTimed) {
track = new Track(this, client, streamType, sampleRate, format,
- channelMask, frameCount, sharedBuffer, sessionId, *flags);
+ channelMask, frameCount, sharedBuffer, sessionId, uid, *flags);
} else {
track = TimedTrack::create(this, client, streamType, sampleRate, format,
- channelMask, frameCount, sharedBuffer, sessionId);
+ channelMask, frameCount, sharedBuffer, sessionId, uid);
}
if (track == 0 || track->getCblk() == NULL || track->name() < 0) {
lStatus = NO_MEMORY;
@@ -1497,6 +1525,9 @@
track->mResetDone = false;
track->mPresentationCompleteFrames = 0;
mActiveTracks.add(track);
+ mWakeLockUids.add(track->uid());
+ mActiveTracksGeneration++;
+ mLatestActiveTrack = track;
sp<EffectChain> chain = getEffectChain_l(track->sessionId());
if (chain != 0) {
ALOGV("addTrack_l() starting track on chain %p for session %d", chain.get(),
@@ -1970,7 +2001,7 @@
mNumWrites++;
mInWrite = false;
-
+ mStandby = false;
return bytesWritten;
}
@@ -2199,6 +2230,8 @@
// FIXME could this be made local to while loop?
writeFrames = 0;
+ int lastGeneration = 0;
+
cacheParameters_l();
sleepTime = idleSleepTime;
@@ -2255,6 +2288,8 @@
break;
}
releaseWakeLock_l();
+ mWakeLockUids.clear();
+ mActiveTracksGeneration++;
ALOGV("wait async completion");
mWaitWorkCV.wait(mLock);
ALOGV("async completion/wake");
@@ -2285,6 +2320,8 @@
}
releaseWakeLock_l();
+ mWakeLockUids.clear();
+ mActiveTracksGeneration++;
// wait until we have something to do...
ALOGV("%s going to sleep", myName.string());
mWaitWorkCV.wait(mLock);
@@ -2309,11 +2346,18 @@
// mMixerStatusIgnoringFastTracks is also updated internally
mMixerStatus = prepareTracks_l(&tracksToRemove);
+ // compare with previously applied list
+ if (lastGeneration != mActiveTracksGeneration) {
+ // update wakelock
+ updateWakeLockUids_l(mWakeLockUids);
+ lastGeneration = mActiveTracksGeneration;
+ }
+
// prevent any changes in effect chain list and in each effect chain
// during mixing and effect process as the audio buffers could be deleted
// or modified if an effect is created or deleted
lockEffectChains_l(effectChains);
- }
+ } // mLock scope ends
if (mBytesRemaining == 0) {
mCurrentWriteLength = 0;
@@ -2402,7 +2446,6 @@
}
}
- mStandby = false;
} else {
usleep(sleepTime);
}
@@ -2445,6 +2488,8 @@
}
#endif
releaseWakeLock();
+ mWakeLockUids.clear();
+ mActiveTracksGeneration++;
ALOGV("Thread %p type %d exiting", this, mType);
return false;
@@ -2458,6 +2503,8 @@
for (size_t i=0 ; i<count ; i++) {
const sp<Track>& track = tracksToRemove.itemAt(i);
mActiveTracks.remove(track);
+ mWakeLockUids.remove(track->uid());
+ mActiveTracksGeneration++;
ALOGV("removeTracks_l removing track on session %d", track->sessionId());
sp<EffectChain> chain = getEffectChain_l(track->sessionId());
if (chain != 0) {
@@ -3666,6 +3713,12 @@
Track* const track = t.get();
audio_track_cblk_t* cblk = track->cblk();
+ // Only consider last track started for volume and mixer state control.
+ // In theory an older track could underrun and restart after the new one starts
+ // but as we only care about the transition phase between two tracks on a
+ // direct output, it is not a problem to ignore the underrun case.
+ sp<Track> l = mLatestActiveTrack.promote();
+ bool last = l.get() == track;
// The first time a track is added we wait
// for all its buffers to be filled before processing it
@@ -3675,11 +3728,6 @@
} else {
minFrames = 1;
}
- // Only consider last track started for volume and mixer state control.
- // This is the last entry in mActiveTracks unless a track underruns.
- // As we only care about the transition phase between two tracks on a
- // direct output, it is not a problem to ignore the underrun case.
- bool last = (i == (count - 1));
if ((track->framesReady() >= minFrames) && track->isReady() &&
!track->isPaused() && !track->isTerminated())
@@ -3706,7 +3754,7 @@
} else {
// clear effect chain input buffer if the last active track started underruns
// to avoid sending previous audio buffer again to effects
- if (!mEffectChains.isEmpty() && (i == (count -1))) {
+ if (!mEffectChains.isEmpty() && last) {
mEffectChains[0]->clearInputBuffer();
}
@@ -3718,7 +3766,8 @@
// TODO: implement behavior for compressed audio
size_t audioHALFrames = (latency_l() * mSampleRate) / 1000;
size_t framesWritten = mBytesWritten / mFrameSize;
- if (mStandby || track->presentationComplete(framesWritten, audioHALFrames) ||
+ if (mStandby || !last ||
+ track->presentationComplete(framesWritten, audioHALFrames) ||
track->isTerminated()) {
if (track->isStopped()) {
track->reset();
@@ -3732,6 +3781,8 @@
if (--(track->mRetryCount) <= 0) {
ALOGV("BUFFER TIMEOUT: remove(%d) from active list", track->name());
tracksToRemove->add(track);
+ // indicate to client process that the track was disabled because of underrun;
+ // it will then automatically call start() when data is available
android_atomic_or(CBLK_DISABLED, &cblk->mFlags);
} else if (last) {
mixerStatus = MIXER_TRACKS_ENABLED;
@@ -3996,6 +4047,8 @@
mPausedBytesRemaining(0),
mPreviousTrack(NULL)
{
+ //FIXME: mStandby should be set to true by ThreadBase constructor
+ mStandby = true;
}
void AudioFlinger::OffloadThread::threadLoop_exit()
@@ -4032,24 +4085,13 @@
}
Track* const track = t.get();
audio_track_cblk_t* cblk = track->cblk();
- if (mPreviousTrack != NULL) {
- if (t.get() != mPreviousTrack) {
- // Flush any data still being written from last track
- mBytesRemaining = 0;
- if (mPausedBytesRemaining) {
- // Last track was paused so we also need to flush saved
- // mixbuffer state and invalidate track so that it will
- // re-submit that unwritten data when it is next resumed
- mPausedBytesRemaining = 0;
- // Invalidate is a bit drastic - would be more efficient
- // to have a flag to tell client that some of the
- // previously written data was lost
- mPreviousTrack->invalidate();
- }
- }
- }
- mPreviousTrack = t.get();
- bool last = (i == (count - 1));
+ // Only consider last track started for volume and mixer state control.
+ // In theory an older track could underrun and restart after the new one starts
+ // but as we only care about the transition phase between two tracks on a
+ // direct output, it is not a problem to ignore the underrun case.
+ sp<Track> l = mLatestActiveTrack.promote();
+ bool last = l.get() == track;
+
if (track->isPausing()) {
track->setPaused();
if (last) {
@@ -4097,6 +4139,31 @@
}
if (last) {
+ sp<Track> previousTrack = mPreviousTrack.promote();
+ if (previousTrack != 0) {
+ if (track != previousTrack.get()) {
+ // Flush any data still being written from last track
+ mBytesRemaining = 0;
+ if (mPausedBytesRemaining) {
+ // Last track was paused so we also need to flush saved
+ // mixbuffer state and invalidate track so that it will
+ // re-submit that unwritten data when it is next resumed
+ mPausedBytesRemaining = 0;
+ // Invalidate is a bit drastic - would be more efficient
+ // to have a flag to tell client that some of the
+ // previously written data was lost
+ previousTrack->invalidate();
+ }
+ // flush data already sent to the DSP if changing audio session as audio
+ // comes from a different source. Also invalidate previous track to force a
+ // seek when resuming.
+ if (previousTrack->sessionId() != track->sessionId()) {
+ previousTrack->invalidate();
+ mFlushPending = true;
+ }
+ }
+ }
+ mPreviousTrack = track;
// reset retry count
track->mRetryCount = kMaxTrackRetriesOffload;
mActiveTrack = t;
@@ -4115,14 +4182,18 @@
track->mState = TrackBase::STOPPING_2; // so presentation completes after drain
// do not drain if no data was ever sent to HAL (mStandby == true)
if (last && !mStandby) {
- sleepTime = 0;
- standbyTime = systemTime() + standbyDelay;
- mixerStatus = MIXER_DRAIN_TRACK;
- mDrainSequence += 2;
+ // do not modify drain sequence if we are already draining. This happens
+ // when resuming from pause after drain.
+ if ((mDrainSequence & 1) == 0) {
+ sleepTime = 0;
+ standbyTime = systemTime() + standbyDelay;
+ mixerStatus = MIXER_DRAIN_TRACK;
+ mDrainSequence += 2;
+ }
if (mHwPaused) {
// It is possible to move from PAUSED to STOPPING_1 without
// a resume so we must ensure hardware is running
- mOutput->stream->resume(mOutput->stream);
+ doHwResume = true;
mHwPaused = false;
}
}
@@ -4146,6 +4217,9 @@
ALOGV("OffloadThread: BUFFER TIMEOUT: remove(%d) from active list",
track->name());
tracksToRemove->add(track);
+ // indicate to client process that the track was disabled because of underrun;
+ // it will then automatically call start() when data is available
+ android_atomic_or(CBLK_DISABLED, &cblk->mFlags);
} else if (last){
mixerStatus = MIXER_TRACKS_ENABLED;
}
@@ -4159,7 +4233,7 @@
// If a flush is pending and a track is active but the HW is not paused, force a HW pause
// before flush and then resume HW. This can happen in case of pause/flush/resume
// if resume is received before pause is executed.
- if (doHwPause || (mFlushPending && !mHwPaused && (count != 0))) {
+ if (!mStandby && (doHwPause || (mFlushPending && !mHwPaused && (count != 0)))) {
mOutput->stream->pause(mOutput->stream);
if (!doHwPause) {
doHwResume = true;
@@ -4169,7 +4243,7 @@
flushHw_l();
mFlushPending = false;
}
- if (doHwResume) {
+ if (!mStandby && doHwResume) {
mOutput->stream->resume(mOutput->stream);
}
@@ -4291,6 +4365,7 @@
for (size_t i = 0; i < outputTracks.size(); i++) {
outputTracks[i]->write(mMixBuffer, writeFrames);
}
+ mStandby = false;
return (ssize_t)mixBufferSize;
}
@@ -4325,7 +4400,8 @@
mSampleRate,
mFormat,
mChannelMask,
- frameCount);
+ frameCount,
+ IPCThreadState::self()->getCallingUid());
if (outputTrack->cblk() != NULL) {
thread->setStreamVolume(AUDIO_STREAM_CNT, 1.0f);
mOutputTracks.add(outputTrack);
@@ -4427,7 +4503,6 @@
snprintf(mName, kNameLength, "AudioIn_%X", id);
readInputParameters();
- mClientUid = IPCThreadState::self()->getCallingUid();
}
@@ -4459,7 +4534,11 @@
nsecs_t lastWarning = 0;
inputStandBy();
- acquireWakeLock(mClientUid);
+ {
+ Mutex::Autolock _l(mLock);
+ activeTrack = mActiveTrack;
+ acquireWakeLock_l(activeTrack != 0 ? activeTrack->uid() : -1);
+ }
// used to verify we've read at least once before evaluating how many bytes were read
bool readOnce = false;
@@ -4472,6 +4551,12 @@
{ // scope for mLock
Mutex::Autolock _l(mLock);
checkForNewParameters_l();
+ if (mActiveTrack != 0 && activeTrack != mActiveTrack) {
+ SortedVector<int> tmp;
+ tmp.add(mActiveTrack->uid());
+ updateWakeLockUids_l(tmp);
+ }
+ activeTrack = mActiveTrack;
if (mActiveTrack == 0 && mConfigEvents.isEmpty()) {
standby();
@@ -4484,7 +4569,7 @@
// go to sleep
mWaitWorkCV.wait(mLock);
ALOGV("RecordThread: loop starting");
- acquireWakeLock_l(mClientUid);
+ acquireWakeLock_l(mActiveTrack != 0 ? mActiveTrack->uid() : -1);
continue;
}
if (mActiveTrack != 0) {
@@ -4720,6 +4805,7 @@
audio_channel_mask_t channelMask,
size_t frameCount,
int sessionId,
+ int uid,
IAudioFlinger::track_flags_t *flags,
pid_t tid,
status_t *status)
@@ -4793,9 +4879,9 @@
track = new RecordTrack(this, client, sampleRate,
#ifdef QCOM_HARDWARE
- format, channelMask, frameCount, *flags, sessionId);
+ format, channelMask, frameCount, *flags, sessionId, uid);
#else
- format, channelMask, frameCount, sessionId);
+ format, channelMask, frameCount, sessionId, uid);
#endif
if (track->getCblk() == 0) {
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 0d01d05..8036a28 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -245,6 +245,9 @@
void acquireWakeLock_l(int uid = -1);
void releaseWakeLock();
void releaseWakeLock_l();
+ void updateWakeLockUids(const SortedVector<int> &uids);
+ void updateWakeLockUids_l(const SortedVector<int> &uids);
+ void getPowerManager_l();
void setEffectSuspended_l(const effect_uuid_t *type,
bool suspend,
int sessionId);
@@ -425,6 +428,7 @@
int sessionId,
IAudioFlinger::track_flags_t *flags,
pid_t tid,
+ int uid,
status_t *status);
AudioStreamOut* getOutput() const;
@@ -499,6 +503,9 @@
void setMasterMute_l(bool muted) { mMasterMute = muted; }
protected:
SortedVector< wp<Track> > mActiveTracks; // FIXME check if this could be sp<>
+ SortedVector<int> mWakeLockUids;
+ int mActiveTracksGeneration;
+ wp<Track> mLatestActiveTrack; // latest track added to mActiveTracks
// Allocate a track name for a given channel mask.
// Returns name >= 0 if successful, -1 on failure.
@@ -759,7 +766,7 @@
bool mFlushPending;
size_t mPausedWriteLength; // length in bytes of write interrupted by pause
size_t mPausedBytesRemaining; // bytes still waiting in mixbuffer after resume
- Track *mPreviousTrack; // used to detect track switch
+ wp<Track> mPreviousTrack; // used to detect track switch
};
class AsyncCallbackThread : public Thread {
@@ -877,6 +884,7 @@
audio_channel_mask_t channelMask,
size_t frameCount,
int sessionId,
+ int uid,
IAudioFlinger::track_flags_t *flags,
pid_t tid,
status_t *status);
@@ -957,5 +965,4 @@
// For dumpsys
const sp<NBAIO_Sink> mTeeSink;
- int mClientUid;
};
diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h
index 358d74a..ab83955 100644
--- a/services/audioflinger/TrackBase.h
+++ b/services/audioflinger/TrackBase.h
@@ -48,6 +48,7 @@
#endif
const sp<IMemory>& sharedBuffer,
int sessionId,
+ int uid,
bool isOut);
virtual ~TrackBase();
@@ -57,6 +58,7 @@
sp<IMemory> getCblk() const { return mCblkMemory; }
audio_track_cblk_t* cblk() const { return mCblk; }
int sessionId() const { return mSessionId; }
+ int uid() const { return mUid; }
virtual status_t setSyncEvent(const sp<SyncEvent>& event);
protected:
@@ -137,6 +139,7 @@
uint32_t mFlags;
#endif
const int mSessionId;
+ int mUid;
Vector < sp<SyncEvent> >mSyncEvents;
const bool mIsOut;
ServerProxy* mServerProxy;
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index dfae5d9..8297b5c 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -71,6 +71,7 @@
#endif
const sp<IMemory>& sharedBuffer,
int sessionId,
+ int clientUid,
bool isOut)
: RefBase(),
mThread(thread),
@@ -99,6 +100,18 @@
mId(android_atomic_inc(&nextTrackId)),
mTerminated(false)
{
+ // if the caller is us, trust the specified uid
+ if (IPCThreadState::self()->getCallingPid() != getpid_cached || clientUid == -1) {
+ int newclientUid = IPCThreadState::self()->getCallingUid();
+ if (clientUid != -1 && clientUid != newclientUid) {
+ ALOGW("uid %d tried to pass itself off as %d", newclientUid, clientUid);
+ }
+ clientUid = newclientUid;
+ }
+ // clientUid contains the uid of the app that is responsible for this track, so we can blame
+ // battery usage on it.
+ mUid = clientUid;
+
// client == 0 implies sharedBuffer == 0
ALOG_ASSERT(!(client == 0 && sharedBuffer != 0));
@@ -374,14 +387,15 @@
size_t frameCount,
const sp<IMemory>& sharedBuffer,
int sessionId,
+ int uid,
IAudioFlinger::track_flags_t flags)
#ifdef QCOM_HARDWARE
: TrackBase(thread, client, sampleRate, format, channelMask, frameCount,
((audio_stream_type_t)streamType == AUDIO_STREAM_VOICE_CALL)? IAudioFlinger::TRACK_VOICE_COMMUNICATION:0x0,
- sharedBuffer, sessionId, true /*isOut*/),
+ sharedBuffer, sessionId, uid, true /*isOut*/),
#else
: TrackBase(thread, client, sampleRate, format, channelMask, frameCount, sharedBuffer,
- sessionId, true /*isOut*/),
+ sessionId, uid, true /*isOut*/),
#endif
mFillingUpStatus(FS_INVALID),
// mRetryCount initialized later when needed
@@ -905,6 +919,7 @@
dstChain->strategy(),
AUDIO_SESSION_OUTPUT_MIX,
effect->id());
+ AudioSystem::setEffectEnabled(effect->id(), effect->isEnabled());
}
status = playbackThread->attachAuxEffect(this, EffectId);
}
@@ -1030,13 +1045,14 @@
audio_channel_mask_t channelMask,
size_t frameCount,
const sp<IMemory>& sharedBuffer,
- int sessionId) {
+ int sessionId,
+ int uid) {
if (!client->reserveTimedTrack())
return 0;
return new TimedTrack(
thread, client, streamType, sampleRate, format, channelMask, frameCount,
- sharedBuffer, sessionId);
+ sharedBuffer, sessionId, uid);
}
AudioFlinger::PlaybackThread::TimedTrack::TimedTrack(
@@ -1048,9 +1064,10 @@
audio_channel_mask_t channelMask,
size_t frameCount,
const sp<IMemory>& sharedBuffer,
- int sessionId)
+ int sessionId,
+ int uid)
: Track(thread, client, streamType, sampleRate, format, channelMask,
- frameCount, sharedBuffer, sessionId, IAudioFlinger::TRACK_TIMED),
+ frameCount, sharedBuffer, sessionId, uid, IAudioFlinger::TRACK_TIMED),
mQueueHeadInFlight(false),
mTrimQueueHeadOnRelease(false),
mFramesPendingInQueue(0),
@@ -1543,9 +1560,10 @@
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
- size_t frameCount)
+ size_t frameCount,
+ int uid)
: Track(playbackThread, NULL, AUDIO_STREAM_CNT, sampleRate, format, channelMask, frameCount,
- NULL, 0, IAudioFlinger::TRACK_DEFAULT),
+ NULL, 0, uid, IAudioFlinger::TRACK_DEFAULT),
mActive(false), mSourceThread(sourceThread), mClientProxy(NULL)
{
@@ -1808,13 +1826,14 @@
#ifdef QCOM_HARDWARE
uint32_t flags,
#endif
- int sessionId)
+ int sessionId,
+ int uid)
#ifdef QCOM_HARDWARE
: TrackBase(thread, client, sampleRate, format, channelMask, frameCount,
- flags, 0 /*sharedBuffer*/, sessionId, false /*isOut*/),
+ flags, 0 /*sharedBuffer*/, sessionId, uid, false /*isOut*/),
#else
: TrackBase(thread, client, sampleRate, format,
- channelMask, frameCount, 0 /*sharedBuffer*/, sessionId, false /*isOut*/),
+ channelMask, frameCount, 0 /*sharedBuffer*/, sessionId, uid, false /*isOut*/),
#endif
mOverflow(false)
{
diff --git a/services/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk
index 1a567b8..9fc4c40 100644
--- a/services/camera/libcameraservice/Android.mk
+++ b/services/camera/libcameraservice/Android.mk
@@ -35,6 +35,7 @@
device3/Camera3ZslStream.cpp \
device3/StatusTracker.cpp \
gui/RingBufferConsumer.cpp \
+ utils/CameraTraces.cpp \
LOCAL_SHARED_LIBRARIES:= \
libui \
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 8ff9cb1..8d97dd4 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -45,6 +45,7 @@
#include "api1/Camera2Client.h"
#include "api_pro/ProCamera2Client.h"
#include "api2/CameraDeviceClient.h"
+#include "utils/CameraTraces.h"
#include "CameraDeviceFactory.h"
namespace android {
@@ -1258,6 +1259,10 @@
if (locked) mServiceLock.unlock();
+ // Dump camera traces if there were any
+ write(fd, "\n", 1);
+ camera3::CameraTraces::dump(fd, args);
+
// change logging level
int n = args.size();
for (int i = 0; i + 1 < n; i++) {
diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp
index df3b162..0b6ca5c 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.cpp
+++ b/services/camera/libcameraservice/api1/Camera2Client.cpp
@@ -76,13 +76,15 @@
return res;
}
- SharedParameters::Lock l(mParameters);
+ {
+ SharedParameters::Lock l(mParameters);
- res = l.mParameters.initialize(&(mDevice->info()));
- if (res != OK) {
- ALOGE("%s: Camera %d: unable to build defaults: %s (%d)",
- __FUNCTION__, mCameraId, strerror(-res), res);
- return NO_INIT;
+ res = l.mParameters.initialize(&(mDevice->info()));
+ if (res != OK) {
+ ALOGE("%s: Camera %d: unable to build defaults: %s (%d)",
+ __FUNCTION__, mCameraId, strerror(-res), res);
+ return NO_INIT;
+ }
}
String8 threadName;
@@ -135,6 +137,7 @@
mCallbackProcessor->run(threadName.string());
if (gLogLevel >= 1) {
+ SharedParameters::Lock l(mParameters);
ALOGD("%s: Default parameters converted from camera %d:", __FUNCTION__,
mCameraId);
ALOGD("%s", l.mParameters.paramsFlattened.string());
@@ -353,6 +356,10 @@
result.appendFormat(" meteringCropRegion\n");
haveQuirk = true;
}
+ if (p.quirks.partialResults) {
+ result.appendFormat(" usePartialResult\n");
+ haveQuirk = true;
+ }
if (!haveQuirk) {
result.appendFormat(" none\n");
}
diff --git a/services/camera/libcameraservice/api1/client2/FrameProcessor.cpp b/services/camera/libcameraservice/api1/client2/FrameProcessor.cpp
index c34cb12..19acae4 100644
--- a/services/camera/libcameraservice/api1/client2/FrameProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/FrameProcessor.cpp
@@ -29,13 +29,27 @@
namespace camera2 {
FrameProcessor::FrameProcessor(wp<CameraDeviceBase> device,
- wp<Camera2Client> client) :
+ sp<Camera2Client> client) :
FrameProcessorBase(device),
mClient(client),
- mLastFrameNumberOfFaces(0) {
+ mLastFrameNumberOfFaces(0),
+ mLast3AFrameNumber(-1) {
sp<CameraDeviceBase> d = device.promote();
mSynthesize3ANotify = !(d->willNotify3A());
+
+ {
+ SharedParameters::Lock l(client->getParameters());
+ mUsePartialQuirk = l.mParameters.quirks.partialResults;
+
+ // Initialize starting 3A state
+ m3aState.afTriggerId = l.mParameters.afTriggerCounter;
+ m3aState.aeTriggerId = l.mParameters.precaptureTriggerCounter;
+ // Check if lens is fixed-focus
+ if (l.mParameters.focusMode == Parameters::FOCUS_MODE_FIXED) {
+ m3aState.afMode = ANDROID_CONTROL_AF_MODE_OFF;
+ }
+ }
}
FrameProcessor::~FrameProcessor() {
@@ -49,20 +63,25 @@
return false;
}
- if (processFaceDetect(frame, client) != OK) {
+ bool partialResult = false;
+ if (mUsePartialQuirk) {
+ camera_metadata_entry_t entry;
+ entry = frame.find(ANDROID_QUIRKS_PARTIAL_RESULT);
+ if (entry.count > 0 &&
+ entry.data.u8[0] == ANDROID_QUIRKS_PARTIAL_RESULT_PARTIAL) {
+ partialResult = true;
+ }
+ }
+
+ if (!partialResult && processFaceDetect(frame, client) != OK) {
return false;
}
if (mSynthesize3ANotify) {
- // Ignoring missing fields for now
process3aState(frame, client);
}
- if (!FrameProcessorBase::processSingleFrame(frame, device)) {
- return false;
- }
-
- return true;
+ return FrameProcessorBase::processSingleFrame(frame, device);
}
status_t FrameProcessor::processFaceDetect(const CameraMetadata &frame,
@@ -198,86 +217,75 @@
ATRACE_CALL();
camera_metadata_ro_entry_t entry;
- int mId = client->getCameraId();
+ int cameraId = client->getCameraId();
entry = frame.find(ANDROID_REQUEST_FRAME_COUNT);
int32_t frameNumber = entry.data.i32[0];
+ // Don't send 3A notifications for the same frame number twice
+ if (frameNumber <= mLast3AFrameNumber) {
+ ALOGV("%s: Already sent 3A for frame number %d, skipping",
+ __FUNCTION__, frameNumber);
+ return OK;
+ }
+
+ mLast3AFrameNumber = frameNumber;
+
// Get 3A states from result metadata
bool gotAllStates = true;
AlgState new3aState;
- entry = frame.find(ANDROID_CONTROL_AE_STATE);
- if (entry.count == 0) {
- ALOGE("%s: Camera %d: No AE state provided by HAL for frame %d!",
- __FUNCTION__, mId, frameNumber);
- gotAllStates = false;
- } else {
- new3aState.aeState =
- static_cast<camera_metadata_enum_android_control_ae_state>(
- entry.data.u8[0]);
- }
+ // TODO: Also use AE mode, AE trigger ID
- entry = frame.find(ANDROID_CONTROL_AF_STATE);
- if (entry.count == 0) {
- ALOGE("%s: Camera %d: No AF state provided by HAL for frame %d!",
- __FUNCTION__, mId, frameNumber);
- gotAllStates = false;
- } else {
- new3aState.afState =
- static_cast<camera_metadata_enum_android_control_af_state>(
- entry.data.u8[0]);
- }
+ gotAllStates &= get3aResult<uint8_t>(frame, ANDROID_CONTROL_AF_MODE,
+ &new3aState.afMode, frameNumber, cameraId);
- entry = frame.find(ANDROID_CONTROL_AWB_STATE);
- if (entry.count == 0) {
- ALOGE("%s: Camera %d: No AWB state provided by HAL for frame %d!",
- __FUNCTION__, mId, frameNumber);
- gotAllStates = false;
- } else {
- new3aState.awbState =
- static_cast<camera_metadata_enum_android_control_awb_state>(
- entry.data.u8[0]);
- }
+ gotAllStates &= get3aResult<uint8_t>(frame, ANDROID_CONTROL_AWB_MODE,
+ &new3aState.awbMode, frameNumber, cameraId);
- int32_t afTriggerId = 0;
- entry = frame.find(ANDROID_CONTROL_AF_TRIGGER_ID);
- if (entry.count == 0) {
- ALOGE("%s: Camera %d: No AF trigger ID provided by HAL for frame %d!",
- __FUNCTION__, mId, frameNumber);
- gotAllStates = false;
- } else {
- afTriggerId = entry.data.i32[0];
- }
+ gotAllStates &= get3aResult<uint8_t>(frame, ANDROID_CONTROL_AE_STATE,
+ &new3aState.aeState, frameNumber, cameraId);
- int32_t aeTriggerId = 0;
- entry = frame.find(ANDROID_CONTROL_AE_PRECAPTURE_ID);
- if (entry.count == 0) {
- ALOGE("%s: Camera %d: No AE precapture trigger ID provided by HAL"
- " for frame %d!",
- __FUNCTION__, mId, frameNumber);
- gotAllStates = false;
- } else {
- aeTriggerId = entry.data.i32[0];
- }
+ gotAllStates &= get3aResult<uint8_t>(frame, ANDROID_CONTROL_AF_STATE,
+ &new3aState.afState, frameNumber, cameraId);
+
+ gotAllStates &= get3aResult<uint8_t>(frame, ANDROID_CONTROL_AWB_STATE,
+ &new3aState.awbState, frameNumber, cameraId);
+
+ gotAllStates &= get3aResult<int32_t>(frame, ANDROID_CONTROL_AF_TRIGGER_ID,
+ &new3aState.afTriggerId, frameNumber, cameraId);
+
+ gotAllStates &= get3aResult<int32_t>(frame, ANDROID_CONTROL_AE_PRECAPTURE_ID,
+ &new3aState.aeTriggerId, frameNumber, cameraId);
if (!gotAllStates) return BAD_VALUE;
if (new3aState.aeState != m3aState.aeState) {
- ALOGV("%s: AE state changed from 0x%x to 0x%x",
- __FUNCTION__, m3aState.aeState, new3aState.aeState);
- client->notifyAutoExposure(new3aState.aeState, aeTriggerId);
+ ALOGV("%s: Camera %d: AE state %d->%d",
+ __FUNCTION__, cameraId,
+ m3aState.aeState, new3aState.aeState);
+ client->notifyAutoExposure(new3aState.aeState, new3aState.aeTriggerId);
}
- if (new3aState.afState != m3aState.afState) {
- ALOGV("%s: AF state changed from 0x%x to 0x%x",
- __FUNCTION__, m3aState.afState, new3aState.afState);
- client->notifyAutoFocus(new3aState.afState, afTriggerId);
+
+ if (new3aState.afState != m3aState.afState ||
+ new3aState.afMode != m3aState.afMode ||
+ new3aState.afTriggerId != m3aState.afTriggerId) {
+ ALOGV("%s: Camera %d: AF state %d->%d. AF mode %d->%d. Trigger %d->%d",
+ __FUNCTION__, cameraId,
+ m3aState.afState, new3aState.afState,
+ m3aState.afMode, new3aState.afMode,
+ m3aState.afTriggerId, new3aState.afTriggerId);
+ client->notifyAutoFocus(new3aState.afState, new3aState.afTriggerId);
}
- if (new3aState.awbState != m3aState.awbState) {
- ALOGV("%s: AWB state changed from 0x%x to 0x%x",
- __FUNCTION__, m3aState.awbState, new3aState.awbState);
- client->notifyAutoWhitebalance(new3aState.awbState, aeTriggerId);
+ if (new3aState.awbState != m3aState.awbState ||
+ new3aState.awbMode != m3aState.awbMode) {
+ ALOGV("%s: Camera %d: AWB state %d->%d. AWB mode %d->%d",
+ __FUNCTION__, cameraId,
+ m3aState.awbState, new3aState.awbState,
+ m3aState.awbMode, new3aState.awbMode);
+ client->notifyAutoWhitebalance(new3aState.awbState,
+ new3aState.aeTriggerId);
}
m3aState = new3aState;
@@ -285,6 +293,39 @@
return OK;
}
+template<typename Src, typename T>
+bool FrameProcessor::get3aResult(const CameraMetadata& result, int32_t tag,
+ T* value, int32_t frameNumber, int cameraId) {
+ camera_metadata_ro_entry_t entry;
+ if (value == NULL) {
+ ALOGE("%s: Camera %d: Value to write to is NULL",
+ __FUNCTION__, cameraId);
+ return false;
+ }
+
+ entry = result.find(tag);
+ if (entry.count == 0) {
+ ALOGE("%s: Camera %d: No %s provided by HAL for frame %d!",
+ __FUNCTION__, cameraId,
+ get_camera_metadata_tag_name(tag), frameNumber);
+ return false;
+ } else {
+ switch(sizeof(Src)){
+ case sizeof(uint8_t):
+ *value = static_cast<T>(entry.data.u8[0]);
+ break;
+ case sizeof(int32_t):
+ *value = static_cast<T>(entry.data.i32[0]);
+ break;
+ default:
+ ALOGE("%s: Camera %d: Unsupported source",
+ __FUNCTION__, cameraId);
+ return false;
+ }
+ }
+ return true;
+}
+
void FrameProcessor::callbackFaceDetection(sp<Camera2Client> client,
const camera_frame_metadata &metadata) {
diff --git a/services/camera/libcameraservice/api1/client2/FrameProcessor.h b/services/camera/libcameraservice/api1/client2/FrameProcessor.h
index 2a17d45..856ad32 100644
--- a/services/camera/libcameraservice/api1/client2/FrameProcessor.h
+++ b/services/camera/libcameraservice/api1/client2/FrameProcessor.h
@@ -39,7 +39,7 @@
*/
class FrameProcessor : public FrameProcessorBase {
public:
- FrameProcessor(wp<CameraDeviceBase> device, wp<Camera2Client> client);
+ FrameProcessor(wp<CameraDeviceBase> device, sp<Camera2Client> client);
~FrameProcessor();
private:
@@ -61,18 +61,44 @@
status_t process3aState(const CameraMetadata &frame,
const sp<Camera2Client> &client);
+ // Helper for process3aState
+ template<typename Src, typename T>
+ bool get3aResult(const CameraMetadata& result, int32_t tag, T* value,
+ int32_t frameNumber, int cameraId);
+
+
struct AlgState {
+ // TODO: also track AE mode
+ camera_metadata_enum_android_control_af_mode afMode;
+ camera_metadata_enum_android_control_awb_mode awbMode;
+
camera_metadata_enum_android_control_ae_state aeState;
camera_metadata_enum_android_control_af_state afState;
camera_metadata_enum_android_control_awb_state awbState;
+ int32_t afTriggerId;
+ int32_t aeTriggerId;
+
+ // These defaults need to match those in Parameters.cpp
AlgState() :
+ afMode(ANDROID_CONTROL_AF_MODE_AUTO),
+ awbMode(ANDROID_CONTROL_AWB_MODE_AUTO),
aeState(ANDROID_CONTROL_AE_STATE_INACTIVE),
afState(ANDROID_CONTROL_AF_STATE_INACTIVE),
- awbState(ANDROID_CONTROL_AWB_STATE_INACTIVE) {
+ awbState(ANDROID_CONTROL_AWB_STATE_INACTIVE),
+ afTriggerId(0),
+ aeTriggerId(0) {
}
} m3aState;
+ // Whether the partial result quirk is enabled for this device
+ bool mUsePartialQuirk;
+
+ // Track most recent frame number for which 3A notifications were sent for.
+ // Used to filter against sending 3A notifications for the same frame
+ // several times.
+ int32_t mLast3AFrameNumber;
+
// Emit FaceDetection event to java if faces changed
void callbackFaceDetection(sp<Camera2Client> client,
const camera_frame_metadata &metadata);
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.cpp b/services/camera/libcameraservice/api1/client2/Parameters.cpp
index 8a4e75c..08af566 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.cpp
+++ b/services/camera/libcameraservice/api1/client2/Parameters.cpp
@@ -183,8 +183,7 @@
// still have to do something sane for them
// NOTE: Not scaled like FPS range values are.
- previewFps = fpsFromRange(previewFpsRange[0], previewFpsRange[1]);
- lastSetPreviewFps = previewFps;
+ int previewFps = fpsFromRange(previewFpsRange[0], previewFpsRange[1]);
params.set(CameraParameters::KEY_PREVIEW_FRAME_RATE,
previewFps);
@@ -1047,6 +1046,11 @@
ALOGV_IF(quirks.meteringCropRegion, "Camera %d: Quirk meteringCropRegion"
" enabled", cameraId);
+ entry = info->find(ANDROID_QUIRKS_USE_PARTIAL_RESULT);
+ quirks.partialResults = (entry.count != 0 && entry.data.u8[0] == 1);
+ ALOGV_IF(quirks.partialResults, "Camera %d: Quirk usePartialResult"
+ " enabled", cameraId);
+
return OK;
}
@@ -1129,13 +1133,22 @@
// PREVIEW_FPS_RANGE
bool fpsRangeChanged = false;
+ int32_t lastSetFpsRange[2];
+
+ params.getPreviewFpsRange(&lastSetFpsRange[0], &lastSetFpsRange[1]);
+ lastSetFpsRange[0] /= kFpsToApiScale;
+ lastSetFpsRange[1] /= kFpsToApiScale;
+
newParams.getPreviewFpsRange(&validatedParams.previewFpsRange[0],
&validatedParams.previewFpsRange[1]);
validatedParams.previewFpsRange[0] /= kFpsToApiScale;
validatedParams.previewFpsRange[1] /= kFpsToApiScale;
- if (validatedParams.previewFpsRange[0] != previewFpsRange[0] ||
- validatedParams.previewFpsRange[1] != previewFpsRange[1]) {
+ // Compare the FPS range value from the last set() to the current set()
+ // to determine if the client has changed it
+ if (validatedParams.previewFpsRange[0] != lastSetFpsRange[0] ||
+ validatedParams.previewFpsRange[1] != lastSetFpsRange[1]) {
+
fpsRangeChanged = true;
camera_metadata_ro_entry_t availablePreviewFpsRanges =
staticInfo(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES, 2);
@@ -1153,16 +1166,6 @@
validatedParams.previewFpsRange[1]);
return BAD_VALUE;
}
- validatedParams.previewFps =
- fpsFromRange(validatedParams.previewFpsRange[0],
- validatedParams.previewFpsRange[1]);
-
- // Update our last-seen single preview FPS, needed for disambiguating
- // when the application is intending to use the deprecated single-FPS
- // setting vs. the range FPS setting
- validatedParams.lastSetPreviewFps = newParams.getPreviewFrameRate();
-
- newParams.setPreviewFrameRate(validatedParams.previewFps);
}
// PREVIEW_FORMAT
@@ -1200,12 +1203,11 @@
// PREVIEW_FRAME_RATE Deprecated, only use if the preview fps range is
// unchanged this time. The single-value FPS is the same as the minimum of
// the range. To detect whether the application has changed the value of
- // previewFps, compare against their last-set preview FPS instead of the
- // single FPS we may have synthesized from a range FPS set.
+ // previewFps, compare against their last-set preview FPS.
if (!fpsRangeChanged) {
- validatedParams.previewFps = newParams.getPreviewFrameRate();
- if (validatedParams.previewFps != lastSetPreviewFps ||
- recordingHintChanged) {
+ int previewFps = newParams.getPreviewFrameRate();
+ int lastSetPreviewFps = params.getPreviewFrameRate();
+ if (previewFps != lastSetPreviewFps || recordingHintChanged) {
camera_metadata_ro_entry_t availableFrameRates =
staticInfo(ANDROID_CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES);
/**
@@ -1218,8 +1220,8 @@
* Either way, in case of multiple ranges, break the tie by
* selecting the smaller range.
*/
- int targetFps = validatedParams.previewFps;
- // all ranges which have targetFps
+
+ // all ranges which have previewFps
Vector<Range> candidateRanges;
for (i = 0; i < availableFrameRates.count; i+=2) {
Range r = {
@@ -1227,13 +1229,13 @@
availableFrameRates.data.i32[i+1]
};
- if (r.min <= targetFps && targetFps <= r.max) {
+ if (r.min <= previewFps && previewFps <= r.max) {
candidateRanges.push(r);
}
}
if (candidateRanges.isEmpty()) {
ALOGE("%s: Requested preview frame rate %d is not supported",
- __FUNCTION__, validatedParams.previewFps);
+ __FUNCTION__, previewFps);
return BAD_VALUE;
}
// most applicable range with targetFps
@@ -1272,14 +1274,6 @@
validatedParams.previewFpsRange[1],
validatedParams.recordingHint);
}
- newParams.set(CameraParameters::KEY_PREVIEW_FPS_RANGE,
- String8::format("%d,%d",
- validatedParams.previewFpsRange[0] * kFpsToApiScale,
- validatedParams.previewFpsRange[1] * kFpsToApiScale));
- // Update our last-seen single preview FPS, needed for disambiguating
- // when the application is intending to use the deprecated single-FPS
- // setting vs. the range FPS setting
- validatedParams.lastSetPreviewFps = validatedParams.previewFps;
}
// PICTURE_SIZE
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.h b/services/camera/libcameraservice/api1/client2/Parameters.h
index bcbdb99..32dbd42 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.h
+++ b/services/camera/libcameraservice/api1/client2/Parameters.h
@@ -46,8 +46,6 @@
int previewWidth, previewHeight;
int32_t previewFpsRange[2];
- int lastSetPreviewFps; // the last single FPS value seen in a set call
- int previewFps; // deprecated, here only for tracking changes
int previewFormat;
int previewTransform; // set by CAMERA_CMD_SET_DISPLAY_ORIENTATION
@@ -209,6 +207,7 @@
bool triggerAfWithAuto;
bool useZslFormat;
bool meteringCropRegion;
+ bool partialResults;
} quirks;
/**
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index 72126c1..1cdf8dc 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -81,7 +81,8 @@
mFrameProcessor->registerListener(FRAME_PROCESSOR_LISTENER_MIN_ID,
FRAME_PROCESSOR_LISTENER_MAX_ID,
- /*listener*/this);
+ /*listener*/this,
+ /*quirkSendPartials*/true);
return OK;
}
diff --git a/services/camera/libcameraservice/common/FrameProcessorBase.cpp b/services/camera/libcameraservice/common/FrameProcessorBase.cpp
index 52906ee..f2064fb 100644
--- a/services/camera/libcameraservice/common/FrameProcessorBase.cpp
+++ b/services/camera/libcameraservice/common/FrameProcessorBase.cpp
@@ -37,11 +37,11 @@
}
status_t FrameProcessorBase::registerListener(int32_t minId,
- int32_t maxId, wp<FilteredListener> listener) {
+ int32_t maxId, wp<FilteredListener> listener, bool quirkSendPartials) {
Mutex::Autolock l(mInputMutex);
ALOGV("%s: Registering listener for frame id range %d - %d",
__FUNCTION__, minId, maxId);
- RangeListener rListener = { minId, maxId, listener };
+ RangeListener rListener = { minId, maxId, listener, quirkSendPartials };
mRangeListeners.push_back(rListener);
return OK;
}
@@ -145,6 +145,16 @@
ATRACE_CALL();
camera_metadata_ro_entry_t entry;
+ // Quirks: Don't deliver partial results to listeners that don't want them
+ bool quirkIsPartial = false;
+ entry = frame.find(ANDROID_QUIRKS_PARTIAL_RESULT);
+ if (entry.count != 0 &&
+ entry.data.u8[0] == ANDROID_QUIRKS_PARTIAL_RESULT_PARTIAL) {
+ ALOGV("%s: Camera %d: Not forwarding partial result to listeners",
+ __FUNCTION__, device->getId());
+ quirkIsPartial = true;
+ }
+
entry = frame.find(ANDROID_REQUEST_ID);
if (entry.count == 0) {
ALOGE("%s: Camera %d: Error reading frame id",
@@ -160,7 +170,8 @@
List<RangeListener>::iterator item = mRangeListeners.begin();
while (item != mRangeListeners.end()) {
if (requestId >= item->minId &&
- requestId < item->maxId) {
+ requestId < item->maxId &&
+ (!quirkIsPartial || item->quirkSendPartials) ) {
sp<FilteredListener> listener = item->listener.promote();
if (listener == 0) {
item = mRangeListeners.erase(item);
diff --git a/services/camera/libcameraservice/common/FrameProcessorBase.h b/services/camera/libcameraservice/common/FrameProcessorBase.h
index 4d80ebf..89b608a 100644
--- a/services/camera/libcameraservice/common/FrameProcessorBase.h
+++ b/services/camera/libcameraservice/common/FrameProcessorBase.h
@@ -44,9 +44,11 @@
};
// Register a listener for a range of IDs [minId, maxId). Multiple listeners
- // can be listening to the same range
+ // can be listening to the same range.
+ // QUIRK: sendPartials controls whether partial results will be sent.
status_t registerListener(int32_t minId, int32_t maxId,
- wp<FilteredListener> listener);
+ wp<FilteredListener> listener,
+ bool quirkSendPartials = true);
status_t removeListener(int32_t minId, int32_t maxId,
wp<FilteredListener> listener);
@@ -64,6 +66,7 @@
int32_t minId;
int32_t maxId;
wp<FilteredListener> listener;
+ bool quirkSendPartials;
};
List<RangeListener> mRangeListeners;
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 6f2dc85..3dbc1b0 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -41,6 +41,7 @@
#include <utils/Trace.h>
#include <utils/Timers.h>
+#include "utils/CameraTraces.h"
#include "device3/Camera3Device.h"
#include "device3/Camera3OutputStream.h"
#include "device3/Camera3InputStream.h"
@@ -54,6 +55,7 @@
mId(id),
mHal3Device(NULL),
mStatus(STATUS_UNINITIALIZED),
+ mUsePartialResultQuirk(false),
mNextResultFrameNumber(0),
mNextShutterFrameNumber(0),
mListener(NULL)
@@ -192,6 +194,15 @@
mNeedConfig = true;
mPauseStateNotify = false;
+ /** Check for quirks */
+
+ // Will the HAL be sending in early partial result metadata?
+ camera_metadata_entry partialResultsQuirk =
+ mDeviceInfo.find(ANDROID_QUIRKS_USE_PARTIAL_RESULT);
+ if (partialResultsQuirk.count > 0 && partialResultsQuirk.data.u8[0] == 1) {
+ mUsePartialResultQuirk = true;
+ }
+
return OK;
}
@@ -1363,6 +1374,10 @@
// But only do error state transition steps for the first error
if (mStatus == STATUS_ERROR || mStatus == STATUS_UNINITIALIZED) return;
+ // Save stack trace. View by dumping it later.
+ CameraTraces::saveTrace();
+ // TODO: consider adding errorCause and client pid/procname
+
mErrorCause = errorCause;
mRequestThread->setPaused(true);
@@ -1386,6 +1401,175 @@
}
/**
+ * QUIRK(partial results)
+ * Check if all 3A fields are ready, and send off a partial 3A-only result
+ * to the output frame queue
+ */
+bool Camera3Device::processPartial3AQuirk(
+ int32_t frameNumber, int32_t requestId,
+ const CameraMetadata& partial) {
+
+ // Check if all 3A states are present
+ // The full list of fields is
+ // android.control.afMode
+ // android.control.awbMode
+ // android.control.aeState
+ // android.control.awbState
+ // android.control.afState
+ // android.control.afTriggerID
+ // android.control.aePrecaptureID
+ // TODO: Add android.control.aeMode
+
+ bool gotAllStates = true;
+
+ uint8_t afMode;
+ uint8_t awbMode;
+ uint8_t aeState;
+ uint8_t afState;
+ uint8_t awbState;
+ int32_t afTriggerId;
+ int32_t aeTriggerId;
+
+ gotAllStates &= get3AResult(partial, ANDROID_CONTROL_AF_MODE,
+ &afMode, frameNumber);
+
+ gotAllStates &= get3AResult(partial, ANDROID_CONTROL_AWB_MODE,
+ &awbMode, frameNumber);
+
+ gotAllStates &= get3AResult(partial, ANDROID_CONTROL_AE_STATE,
+ &aeState, frameNumber);
+
+ gotAllStates &= get3AResult(partial, ANDROID_CONTROL_AF_STATE,
+ &afState, frameNumber);
+
+ gotAllStates &= get3AResult(partial, ANDROID_CONTROL_AWB_STATE,
+ &awbState, frameNumber);
+
+ gotAllStates &= get3AResult(partial, ANDROID_CONTROL_AF_TRIGGER_ID,
+ &afTriggerId, frameNumber);
+
+ gotAllStates &= get3AResult(partial, ANDROID_CONTROL_AE_PRECAPTURE_ID,
+ &aeTriggerId, frameNumber);
+
+ if (!gotAllStates) return false;
+
+ ALOGVV("%s: Camera %d: Frame %d, Request ID %d: AF mode %d, AWB mode %d, "
+ "AF state %d, AE state %d, AWB state %d, "
+ "AF trigger %d, AE precapture trigger %d",
+ __FUNCTION__, mId, frameNumber, requestId,
+ afMode, awbMode,
+ afState, aeState, awbState,
+ afTriggerId, aeTriggerId);
+
+ // Got all states, so construct a minimal result to send
+ // In addition to the above fields, this means adding in
+ // android.request.frameCount
+ // android.request.requestId
+ // android.quirks.partialResult
+
+ const size_t kMinimal3AResultEntries = 10;
+
+ Mutex::Autolock l(mOutputLock);
+
+ CameraMetadata& min3AResult =
+ *mResultQueue.insert(
+ mResultQueue.end(),
+ CameraMetadata(kMinimal3AResultEntries, /*dataCapacity*/ 0));
+
+ if (!insert3AResult(min3AResult, ANDROID_REQUEST_FRAME_COUNT,
+ &frameNumber, frameNumber)) {
+ return false;
+ }
+
+ if (!insert3AResult(min3AResult, ANDROID_REQUEST_ID,
+ &requestId, frameNumber)) {
+ return false;
+ }
+
+ static const uint8_t partialResult = ANDROID_QUIRKS_PARTIAL_RESULT_PARTIAL;
+ if (!insert3AResult(min3AResult, ANDROID_QUIRKS_PARTIAL_RESULT,
+ &partialResult, frameNumber)) {
+ return false;
+ }
+
+ if (!insert3AResult(min3AResult, ANDROID_CONTROL_AF_MODE,
+ &afMode, frameNumber)) {
+ return false;
+ }
+
+ if (!insert3AResult(min3AResult, ANDROID_CONTROL_AWB_MODE,
+ &awbMode, frameNumber)) {
+ return false;
+ }
+
+ if (!insert3AResult(min3AResult, ANDROID_CONTROL_AE_STATE,
+ &aeState, frameNumber)) {
+ return false;
+ }
+
+ if (!insert3AResult(min3AResult, ANDROID_CONTROL_AF_STATE,
+ &afState, frameNumber)) {
+ return false;
+ }
+
+ if (!insert3AResult(min3AResult, ANDROID_CONTROL_AWB_STATE,
+ &awbState, frameNumber)) {
+ return false;
+ }
+
+ if (!insert3AResult(min3AResult, ANDROID_CONTROL_AF_TRIGGER_ID,
+ &afTriggerId, frameNumber)) {
+ return false;
+ }
+
+ if (!insert3AResult(min3AResult, ANDROID_CONTROL_AE_PRECAPTURE_ID,
+ &aeTriggerId, frameNumber)) {
+ return false;
+ }
+
+ mResultSignal.signal();
+
+ return true;
+}
+
+template<typename T>
+bool Camera3Device::get3AResult(const CameraMetadata& result, int32_t tag,
+ T* value, int32_t frameNumber) {
+ (void) frameNumber;
+
+ camera_metadata_ro_entry_t entry;
+
+ entry = result.find(tag);
+ if (entry.count == 0) {
+ ALOGVV("%s: Camera %d: Frame %d: No %s provided by HAL!", __FUNCTION__,
+ mId, frameNumber, get_camera_metadata_tag_name(tag));
+ return false;
+ }
+
+ if (sizeof(T) == sizeof(uint8_t)) {
+ *value = entry.data.u8[0];
+ } else if (sizeof(T) == sizeof(int32_t)) {
+ *value = entry.data.i32[0];
+ } else {
+ ALOGE("%s: Unexpected type", __FUNCTION__);
+ return false;
+ }
+ return true;
+}
+
+template<typename T>
+bool Camera3Device::insert3AResult(CameraMetadata& result, int32_t tag,
+ const T* value, int32_t frameNumber) {
+ if (result.update(tag, value, 1) != NO_ERROR) {
+ mResultQueue.erase(--mResultQueue.end(), mResultQueue.end());
+ SET_ERR("Frame %d: Failed to set %s in partial metadata",
+ frameNumber, get_camera_metadata_tag_name(tag));
+ return false;
+ }
+ return true;
+}
+
+/**
* Camera HAL device callback methods
*/
@@ -1400,6 +1584,8 @@
frameNumber);
return;
}
+ bool partialResultQuirk = false;
+ CameraMetadata collectedQuirkResult;
// Get capture timestamp from list of in-flight requests, where it was added
// by the shutter notification for this frame. Then update the in-flight
@@ -1415,24 +1601,58 @@
return;
}
InFlightRequest &request = mInFlightMap.editValueAt(idx);
+
+ // Check if this result carries only partial metadata
+ if (mUsePartialResultQuirk && result->result != NULL) {
+ camera_metadata_ro_entry_t partialResultEntry;
+ res = find_camera_metadata_ro_entry(result->result,
+ ANDROID_QUIRKS_PARTIAL_RESULT, &partialResultEntry);
+ if (res != NAME_NOT_FOUND &&
+ partialResultEntry.count > 0 &&
+ partialResultEntry.data.u8[0] ==
+ ANDROID_QUIRKS_PARTIAL_RESULT_PARTIAL) {
+ // A partial result. Flag this as such, and collect this
+ // set of metadata into the in-flight entry.
+ partialResultQuirk = true;
+ request.partialResultQuirk.collectedResult.append(
+ result->result);
+ request.partialResultQuirk.collectedResult.erase(
+ ANDROID_QUIRKS_PARTIAL_RESULT);
+ // Fire off a 3A-only result if possible
+ if (!request.partialResultQuirk.haveSent3A) {
+ request.partialResultQuirk.haveSent3A =
+ processPartial3AQuirk(frameNumber,
+ request.requestId,
+ request.partialResultQuirk.collectedResult);
+ }
+ }
+ }
+
timestamp = request.captureTimestamp;
/**
- * One of the following must happen before it's legal to call process_capture_result:
+ * One of the following must happen before it's legal to call process_capture_result,
+ * unless partial metadata is being provided:
* - CAMERA3_MSG_SHUTTER (expected during normal operation)
* - CAMERA3_MSG_ERROR (expected during flush)
*/
- if (request.requestStatus == OK && timestamp == 0) {
+ if (request.requestStatus == OK && timestamp == 0 && !partialResultQuirk) {
SET_ERR("Called before shutter notify for frame %d",
frameNumber);
return;
}
- if (result->result != NULL) {
+ // Did we get the (final) result metadata for this capture?
+ if (result->result != NULL && !partialResultQuirk) {
if (request.haveResultMetadata) {
SET_ERR("Called multiple times with metadata for frame %d",
frameNumber);
return;
}
+ if (mUsePartialResultQuirk &&
+ !request.partialResultQuirk.collectedResult.isEmpty()) {
+ collectedQuirkResult.acquire(
+ request.partialResultQuirk.collectedResult);
+ }
request.haveResultMetadata = true;
}
@@ -1444,6 +1664,7 @@
return;
}
+ // Check if everything has arrived for this result (buffers and metadata)
if (request.haveResultMetadata && request.numBuffersLeft == 0) {
ATRACE_ASYNC_END("frame capture", frameNumber);
mInFlightMap.removeItemsAt(idx, 1);
@@ -1458,9 +1679,12 @@
}
// Process the result metadata, if provided
- if (result->result != NULL) {
+ bool gotResult = false;
+ if (result->result != NULL && !partialResultQuirk) {
Mutex::Autolock l(mOutputLock);
+ gotResult = true;
+
if (frameNumber != mNextResultFrameNumber) {
SET_ERR("Out-of-order capture result metadata submitted! "
"(got frame number %d, expecting %d)",
@@ -1469,19 +1693,26 @@
}
mNextResultFrameNumber++;
- CameraMetadata &captureResult =
- *mResultQueue.insert(mResultQueue.end(), CameraMetadata());
-
+ CameraMetadata captureResult;
captureResult = result->result;
+
if (captureResult.update(ANDROID_REQUEST_FRAME_COUNT,
(int32_t*)&frameNumber, 1) != OK) {
SET_ERR("Failed to set frame# in metadata (%d)",
frameNumber);
+ gotResult = false;
} else {
ALOGVV("%s: Camera %d: Set frame# in metadata (%d)",
__FUNCTION__, mId, frameNumber);
}
+ // Append any previous partials to form a complete result
+ if (mUsePartialResultQuirk && !collectedQuirkResult.isEmpty()) {
+ captureResult.append(collectedQuirkResult);
+ }
+
+ captureResult.sort();
+
// Check that there's a timestamp in the result metadata
camera_metadata_entry entry =
@@ -1489,10 +1720,19 @@
if (entry.count == 0) {
SET_ERR("No timestamp provided by HAL for frame %d!",
frameNumber);
+ gotResult = false;
} else if (timestamp != entry.data.i64[0]) {
SET_ERR("Timestamp mismatch between shutter notify and result"
" metadata for frame %d (%lld vs %lld respectively)",
frameNumber, timestamp, entry.data.i64[0]);
+ gotResult = false;
+ }
+
+ if (gotResult) {
+ // Valid result, insert into queue
+ CameraMetadata& queuedResult =
+ *mResultQueue.insert(mResultQueue.end(), CameraMetadata());
+ queuedResult.swap(captureResult);
}
} // scope for mOutputLock
@@ -1512,7 +1752,7 @@
// Finally, signal any waiters for new frames
- if (result->result != NULL) {
+ if (gotResult) {
mResultSignal.signal();
}
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index 12252c8..468f641 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -188,6 +188,9 @@
// Need to hold on to stream references until configure completes.
Vector<sp<camera3::Camera3StreamInterface> > mDeletedStreams;
+ // Whether quirk ANDROID_QUIRKS_USE_PARTIAL_RESULT is enabled
+ bool mUsePartialResultQuirk;
+
/**** End scope for mLock ****/
class CaptureRequest : public LightRefBase<CaptureRequest> {
@@ -445,6 +448,18 @@
// buffers
int numBuffersLeft;
+ // Fields used by the partial result quirk only
+ struct PartialResultQuirkInFlight {
+ // Set by process_capture_result once 3A has been sent to clients
+ bool haveSent3A;
+ // Result metadata collected so far, when partial results are in use
+ CameraMetadata collectedResult;
+
+ PartialResultQuirkInFlight():
+ haveSent3A(false) {
+ }
+ } partialResultQuirk;
+
// Default constructor needed by KeyedVector
InFlightRequest() :
requestId(0),
@@ -472,6 +487,22 @@
int32_t numBuffers);
/**
+ * For the partial result quirk, check if all 3A state fields are available
+ * and if so, queue up 3A-only result to the client. Returns true if 3A
+ * is sent.
+ */
+ bool processPartial3AQuirk(int32_t frameNumber, int32_t requestId,
+ const CameraMetadata& partial);
+
+ // Helpers for reading and writing 3A metadata into to/from partial results
+ template<typename T>
+ bool get3AResult(const CameraMetadata& result, int32_t tag,
+ T* value, int32_t frameNumber);
+
+ template<typename T>
+ bool insert3AResult(CameraMetadata &result, int32_t tag, const T* value,
+ int32_t frameNumber);
+ /**
* Tracking for idle detection
*/
sp<camera3::StatusTracker> mStatusTracker;
diff --git a/services/camera/libcameraservice/utils/CameraTraces.cpp b/services/camera/libcameraservice/utils/CameraTraces.cpp
new file mode 100644
index 0000000..346e15f
--- /dev/null
+++ b/services/camera/libcameraservice/utils/CameraTraces.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#define LOG_TAG "CameraTraces"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+//#define LOG_NDEBUG 0
+
+#include "utils/CameraTraces.h"
+#include <utils/ProcessCallStack.h>
+
+#include <utils/Mutex.h>
+#include <utils/List.h>
+
+#include <utils/Log.h>
+#include <cutils/trace.h>
+
+namespace android {
+namespace camera3 {
+
+struct CameraTracesImpl {
+ Mutex tracesLock;
+ List<ProcessCallStack> pcsList;
+}; // class CameraTraces::Impl;
+
+static CameraTracesImpl gImpl;
+CameraTracesImpl& CameraTraces::sImpl = gImpl;
+
+void CameraTraces::saveTrace() {
+ ALOGV("%s: begin", __FUNCTION__);
+ ATRACE_BEGIN("CameraTraces::saveTrace");
+ Mutex::Autolock al(sImpl.tracesLock);
+
+ List<ProcessCallStack>& pcsList = sImpl.pcsList;
+
+ // Insert new ProcessCallStack, and immediately crawl all the threads
+ pcsList.push_front(ProcessCallStack());
+ ProcessCallStack& pcs = *pcsList.begin();
+ pcs.update();
+
+ if (pcsList.size() > MAX_TRACES) {
+ // Prune list periodically and discard oldest entry
+ pcsList.erase(--pcsList.end());
+ }
+
+ IF_ALOGV() {
+ pcs.log(LOG_TAG, ANDROID_LOG_VERBOSE);
+ }
+
+ ALOGD("Process trace saved. Use dumpsys media.camera to view.");
+
+ ATRACE_END();
+}
+
+status_t CameraTraces::dump(int fd, const Vector<String16> &args __attribute__((unused))) {
+ ALOGV("%s: fd = %d", __FUNCTION__, fd);
+ Mutex::Autolock al(sImpl.tracesLock);
+ List<ProcessCallStack>& pcsList = sImpl.pcsList;
+
+ if (fd < 0) {
+ ALOGW("%s: Negative FD (%d)", __FUNCTION__, fd);
+ return BAD_VALUE;
+ }
+
+ fdprintf(fd, "Camera traces (%zu):\n", pcsList.size());
+
+ if (pcsList.empty()) {
+ fdprintf(fd, " No camera traces collected.\n");
+ }
+
+ // Print newest items first
+ List<ProcessCallStack>::iterator it, end;
+ for (it = pcsList.begin(), end = pcsList.end(); it != end; ++it) {
+ const ProcessCallStack& pcs = *it;
+ pcs.dump(fd, DUMP_INDENT);
+ }
+
+ return OK;
+}
+
+}; // namespace camera3
+}; // namespace android
diff --git a/services/camera/libcameraservice/utils/CameraTraces.h b/services/camera/libcameraservice/utils/CameraTraces.h
new file mode 100644
index 0000000..d10dbc9
--- /dev/null
+++ b/services/camera/libcameraservice/utils/CameraTraces.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#ifndef ANDROID_SERVERS_CAMERA_TRACES_H_
+#define ANDROID_SERVERS_CAMERA_TRACES_H_
+
+#include <utils/Errors.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+
+namespace android {
+namespace camera3 {
+
+class CameraTracesImpl;
+
+// Collect a list of the process's stack traces
+class CameraTraces {
+public:
+ /**
+ * Save the current stack trace for each thread in the process. At most
+ * MAX_TRACES will be saved, after which the oldest traces will be discarded.
+ *
+ * <p>Use CameraTraces::dump to print out the traces.</p>
+ */
+ static void saveTrace();
+
+ /**
+ * Prints all saved traces to the specified file descriptor.
+ *
+ * <p>Each line is indented by DUMP_INDENT spaces.</p>
+ */
+ static status_t dump(int fd, const Vector<String16>& args);
+
+private:
+ enum {
+ // Don't collect more than 100 traces. Discard oldest.
+ MAX_TRACES = 100,
+
+ // Insert 2 spaces when dumping the traces
+ DUMP_INDENT = 2,
+ };
+
+ CameraTraces();
+ ~CameraTraces();
+ CameraTraces(CameraTraces& rhs);
+
+ static CameraTracesImpl& sImpl;
+}; // class CameraTraces
+
+}; // namespace camera3
+}; // namespace android
+
+#endif // ANDROID_SERVERS_CAMERA_TRACES_H_