OSDN Git Service

New control block for AudioTrack and AudioRecord
authorGlenn Kasten <gkasten@google.com>
Tue, 18 Dec 2012 23:57:32 +0000 (15:57 -0800)
committerGlenn Kasten <gkasten@google.com>
Wed, 12 Jun 2013 21:33:10 +0000 (14:33 -0700)
Main differences between old and new control block:
 - removes the mutex, which was a potential source of priority inversion
 - circular indices into shared buffer, which is now always a power-of-2 size

Change-Id: I4e9b7fa99858b488ac98a441fa70e31dbba1b865

14 files changed:
include/media/AudioBufferProvider.h
include/media/AudioRecord.h
include/media/AudioTrack.h
include/private/media/AudioTrackShared.h
media/libmedia/AudioRecord.cpp
media/libmedia/AudioTrack.cpp
media/libmedia/AudioTrackShared.cpp
media/libmedia/ToneGenerator.cpp
services/audioflinger/AudioFlinger.h
services/audioflinger/PlaybackTracks.h
services/audioflinger/RecordTracks.h
services/audioflinger/Threads.cpp
services/audioflinger/TrackBase.h
services/audioflinger/Tracks.cpp

index 43e4de7..ef392f0 100644 (file)
@@ -26,6 +26,8 @@ class AudioBufferProvider
 {
 public:
 
+    // FIXME merge with AudioTrackShared::Buffer, AudioTrack::Buffer, and AudioRecord::Buffer
+    //       and rename getNextBuffer() to obtainBuffer()
     struct Buffer {
         Buffer() : raw(NULL), frameCount(0) { }
         union {
@@ -44,6 +46,19 @@ public:
     // pts is the local time when the next sample yielded by getNextBuffer
     // will be rendered.
     // Pass kInvalidPTS if the PTS is unknown or not applicable.
+    // On entry:
+    //  buffer              != NULL
+    //  buffer->raw         unused
+    //  buffer->frameCount  maximum number of desired frames
+    // On successful return:
+    //  status              NO_ERROR
+    //  buffer->raw         non-NULL pointer to buffer->frameCount contiguous available frames
+    //  buffer->frameCount  number of contiguous available frames at buffer->raw,
+    //                      0 < buffer->frameCount <= entry value
+    // On error return:
+    //  status              != NO_ERROR
+    //  buffer->raw         NULL
+    //  buffer->frameCount  0
     virtual status_t getNextBuffer(Buffer* buffer, int64_t pts = kInvalidPTS) = 0;
 
     virtual void releaseBuffer(Buffer* buffer) = 0;
index 38c6548..81be803 100644 (file)
  * limitations under the License.
  */
 
-#ifndef AUDIORECORD_H_
-#define AUDIORECORD_H_
+#ifndef ANDROID_AUDIORECORD_H
+#define ANDROID_AUDIORECORD_H
 
-#include <binder/IMemory.h>
 #include <cutils/sched_policy.h>
 #include <media/AudioSystem.h>
 #include <media/IAudioRecord.h>
-#include <system/audio.h>
-#include <utils/RefBase.h>
-#include <utils/Errors.h>
 #include <utils/threads.h>
 
 namespace android {
 
+// ----------------------------------------------------------------------------
+
 class audio_track_cblk_t;
 class AudioRecordClientProxy;
 
 // ----------------------------------------------------------------------------
 
-class AudioRecord : virtual public RefBase
+class AudioRecord : public RefBase
 {
 public:
 
@@ -49,6 +47,8 @@ public:
                                     // (See setMarkerPosition()).
         EVENT_NEW_POS = 3,          // Record head is at a new position
                                     // (See setPositionUpdatePeriod()).
+        EVENT_NEW_IAUDIORECORD = 4, // IAudioRecord was re-created, either due to re-routing and
+                                    // voluntary invalidation by mediaserver, or mediaserver crash.
     };
 
     /* Client should declare Buffer on the stack and pass address to obtainBuffer()
@@ -58,11 +58,16 @@ public:
     class Buffer
     {
     public:
+        // FIXME use m prefix
         size_t      frameCount;     // number of sample frames corresponding to size;
                                     // on input it is the number of frames available,
                                     // on output is the number of frames actually drained
 
-        size_t      size;           // total size in bytes == frameCount * frameSize
+        size_t      size;           // input/output in bytes == frameCount * frameSize
+                                    // FIXME this is redundant with respect to frameCount,
+                                    // and TRANSFER_OBTAIN mode is broken for 8-bit data
+                                    // since we don't define the frame format
+
         union {
             void*       raw;
             short*      i16;        // signed 16-bit
@@ -84,6 +89,7 @@ public:
      *          - EVENT_OVERRUN: unused.
      *          - EVENT_MARKER: pointer to const uint32_t containing the marker position in frames.
      *          - EVENT_NEW_POS: pointer to const uint32_t containing the new position in frames.
+     *          - EVENT_NEW_IAUDIORECORD: unused.
      */
 
     typedef void (*callback_t)(int event, void* user, void *info);
@@ -101,20 +107,28 @@ public:
                                       audio_format_t format,
                                       audio_channel_mask_t channelMask);
 
+    /* How data is transferred from AudioRecord
+     */
+    enum transfer_type {
+        TRANSFER_DEFAULT,   // not specified explicitly; determine from other parameters
+        TRANSFER_CALLBACK,  // callback EVENT_MORE_DATA
+        TRANSFER_OBTAIN,    // FIXME deprecated: call obtainBuffer() and releaseBuffer()
+        TRANSFER_SYNC,      // synchronous read()
+    };
+
     /* Constructs an uninitialized AudioRecord. No connection with
-     * AudioFlinger takes place.
+     * AudioFlinger takes place.  Use set() after this.
      */
                         AudioRecord();
 
     /* Creates an AudioRecord object and registers it with AudioFlinger.
      * Once created, the track needs to be started before it can be used.
-     * Unspecified values are set to the audio hardware's current
-     * values.
+     * Unspecified values are set to appropriate default values.
      *
      * Parameters:
      *
-     * inputSource:        Select the audio input to record to (e.g. AUDIO_SOURCE_DEFAULT).
-     * sampleRate:         Track sampling rate in Hz.
+     * inputSource:        Select the audio input to record from (e.g. AUDIO_SOURCE_DEFAULT).
+     * sampleRate:         Data sink sampling rate in Hz.
      * format:             Audio format (e.g AUDIO_FORMAT_PCM_16_BIT for signed
      *                     16 bits per sample).
      * channelMask:        Channel mask.
@@ -124,11 +138,13 @@ public:
      *                     be larger if the requested size is not compatible with current audio HAL
      *                     latency.  Zero means to use a default value.
      * cbf:                Callback function. If not null, this function is called periodically
-     *                     to consume new PCM data.
+     *                     to consume new PCM data and inform of marker, position updates, etc.
      * user:               Context for use by the callback receiver.
      * notificationFrames: The callback function is called each time notificationFrames PCM
      *                     frames are ready in record track output buffer.
      * sessionId:          Not yet supported.
+     * transferType:       How data is transferred from AudioRecord.
+     * threadCanCallJava:  Not present in parameter list, and so is fixed at false.
      */
 
                         AudioRecord(audio_source_t inputSource,
@@ -139,22 +155,26 @@ public:
                                     callback_t cbf = NULL,
                                     void* user = NULL,
                                     int notificationFrames = 0,
-                                    int sessionId = 0);
-
+                                    int sessionId = 0,
+                                    transfer_type transferType = TRANSFER_DEFAULT);
 
     /* Terminates the AudioRecord and unregisters it from AudioFlinger.
      * Also destroys all resources associated with the AudioRecord.
      */
                         ~AudioRecord();
 
-
-    /* Initialize an uninitialized AudioRecord.
+    /* Initialize an AudioRecord that was created using the AudioRecord() constructor.
+     * Don't call set() more than once, or after an AudioRecord() constructor that takes parameters.
      * Returned status (from utils/Errors.h) can be:
      *  - NO_ERROR: successful intialization
-     *  - INVALID_OPERATION: AudioRecord is already intitialized or record device is already in use
+     *  - INVALID_OPERATION: AudioRecord is already initialized or record device is already in use
      *  - BAD_VALUE: invalid parameter (channels, format, sampleRate...)
      *  - NO_INIT: audio server or audio hardware not initialized
      *  - PERMISSION_DENIED: recording is not allowed for the requesting process
+     *
+     * Parameters not listed in the AudioRecord constructors above:
+     *
+     * threadCanCallJava:  Whether callbacks are made from an attached thread and thus can call JNI.
      */
             status_t    set(audio_source_t inputSource = AUDIO_SOURCE_DEFAULT,
                             uint32_t sampleRate = 0,
@@ -165,30 +185,29 @@ public:
                             void* user = NULL,
                             int notificationFrames = 0,
                             bool threadCanCallJava = false,
-                            int sessionId = 0);
-
+                            int sessionId = 0,
+                            transfer_type transferType = TRANSFER_DEFAULT);
 
     /* Result of constructing the AudioRecord. This must be checked
      * before using any AudioRecord API (except for set()), because using
      * an uninitialized AudioRecord produces undefined results.
      * See set() method above for possible return codes.
      */
-            status_t    initCheck() const;
+            status_t    initCheck() const   { return mStatus; }
 
     /* Returns this track's estimated latency in milliseconds.
      * This includes the latency due to AudioRecord buffer size,
      * and audio hardware driver.
      */
-            uint32_t     latency() const;
+            uint32_t    latency() const     { return mLatency; }
 
    /* getters, see constructor and set() */
 
-            audio_format_t format() const;
-            uint32_t    channelCount() const;
-            size_t      frameCount() const;
-            size_t      frameSize() const { return mFrameSize; }
-            audio_source_t inputSource() const;
-
+            audio_format_t format() const   { return mFormat; }
+            uint32_t    channelCount() const    { return mChannelCount; }
+            size_t      frameCount() const  { return mFrameCount; }
+            size_t      frameSize() const   { return mFrameSize; }
+            audio_source_t inputSource() const  { return mInputSource; }
 
     /* After it's created the track is not active. Call start() to
      * make it active. If set, the callback will start being called.
@@ -198,26 +217,29 @@ public:
             status_t    start(AudioSystem::sync_event_t event = AudioSystem::SYNC_EVENT_NONE,
                               int triggerSession = 0);
 
-    /* Stop a track. If set, the callback will cease being called and
-     * obtainBuffer returns STOPPED. Note that obtainBuffer() still works
-     * and will drain buffers until the pool is exhausted.
+    /* Stop a track. If set, the callback will cease being called.  Note that obtainBuffer() still
+     * works and will drain buffers until the pool is exhausted, and then will return WOULD_BLOCK.
      */
             void        stop();
             bool        stopped() const;
 
-    /* Get sample rate for this record track in Hz.
+    /* Return the sink sample rate for this record track in Hz.
+     * Unlike AudioTrack, the sample rate is const after initialization, so doesn't need a lock.
      */
-            uint32_t    getSampleRate() const;
+            uint32_t    getSampleRate() const   { return mSampleRate; }
 
     /* Sets marker position. When record reaches the number of frames specified,
      * a callback with event type EVENT_MARKER is called. Calling setMarkerPosition
      * with marker == 0 cancels marker notification callback.
+     * To set a marker at a position which would compute as 0,
+     * a workaround is to the set the marker at a nearby position such as ~0 or 1.
      * If the AudioRecord has been opened with no callback function associated,
      * the operation will fail.
      *
      * Parameters:
      *
-     * marker:   marker position expressed in frames.
+     * marker:   marker position expressed in wrapping (overflow) frame units,
+     *           like the return value of getPosition().
      *
      * Returned status (from utils/Errors.h) can be:
      *  - NO_ERROR: successful operation
@@ -226,13 +248,13 @@ public:
             status_t    setMarkerPosition(uint32_t marker);
             status_t    getMarkerPosition(uint32_t *marker) const;
 
-
     /* Sets position update period. Every time the number of frames specified has been recorded,
      * a callback with event type EVENT_NEW_POS is called.
      * Calling setPositionUpdatePeriod with updatePeriod == 0 cancels new position notification
      * callback.
      * If the AudioRecord has been opened with no callback function associated,
      * the operation will fail.
+     * Extremely small values may be rounded up to a value the implementation can support.
      *
      * Parameters:
      *
@@ -245,13 +267,13 @@ public:
             status_t    setPositionUpdatePeriod(uint32_t updatePeriod);
             status_t    getPositionUpdatePeriod(uint32_t *updatePeriod) const;
 
-
-    /* Gets record head position. The position is the total number of frames
-     * recorded since record start.
+    /* Return the total number of frames recorded since recording started.
+     * The counter will wrap (overflow) periodically, e.g. every ~27 hours at 44.1 kHz.
+     * It is reset to zero by stop().
      *
      * Parameters:
      *
-     *  position:  Address where to return record head position within AudioRecord buffer.
+     *  position:  Address where to return record head position.
      *
      * Returned status (from utils/Errors.h) can be:
      *  - NO_ERROR: successful operation
@@ -276,38 +298,70 @@ public:
      *
      * Returned value:
      *  AudioRecord session ID.
+     *
+     * No lock needed because session ID doesn't change after first set().
      */
-            int    getSessionId() const;
-
-    /* Obtains a buffer of "frameCount" frames. The buffer must be
-     * drained entirely, and then released with releaseBuffer().
-     * If the track is stopped, obtainBuffer() returns
-     * STOPPED instead of NO_ERROR as long as there are buffers available,
-     * at which point NO_MORE_BUFFERS is returned.
+            int    getSessionId() const { return mSessionId; }
+
+    /* Obtains a buffer of up to "audioBuffer->frameCount" full frames.
+     * After draining these frames of data, the caller should release them with releaseBuffer().
+     * If the track buffer is not empty, obtainBuffer() returns as many contiguous
+     * full frames as are available immediately.
+     * If the track buffer is empty and track is stopped, obtainBuffer() returns WOULD_BLOCK
+     * regardless of the value of waitCount.
+     * If the track buffer is empty and track is not stopped, obtainBuffer() blocks with a
+     * maximum timeout based on waitCount; see chart below.
      * Buffers will be returned until the pool
      * is exhausted, at which point obtainBuffer() will either block
-     * or return WOULD_BLOCK depending on the value of the "blocking"
+     * or return WOULD_BLOCK depending on the value of the "waitCount"
      * parameter.
      *
+     * obtainBuffer() and releaseBuffer() are deprecated for direct use by applications,
+     * which should use read() or callback EVENT_MORE_DATA instead.
+     *
      * Interpretation of waitCount:
      *  +n  limits wait time to n * WAIT_PERIOD_MS,
      *  -1  causes an (almost) infinite wait time,
      *   0  non-blocking.
+     *
+     * Buffer fields
+     * On entry:
+     *  frameCount  number of frames requested
+     * After error return:
+     *  frameCount  0
+     *  size        0
+     *  raw         undefined
+     * After successful return:
+     *  frameCount  actual number of frames available, <= number requested
+     *  size        actual number of bytes available
+     *  raw         pointer to the buffer
      */
 
-        enum {
-            NO_MORE_BUFFERS = 0x80000001,   // same name in AudioFlinger.h, ok to be different value
-            STOPPED = 1
-        };
+    /* FIXME Deprecated public API for TRANSFER_OBTAIN mode */
+            status_t    obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
+                                __attribute__((__deprecated__));
 
-            status_t    obtainBuffer(Buffer* audioBuffer, int32_t waitCount);
+private:
+    /* New internal API.
+     * If nonContig is non-NULL, it is an output parameter that will be set to the number of
+     * additional non-contiguous frames that are available immediately.
+     * FIXME We could pass an array of Buffers instead of only one Buffer to obtainBuffer(),
+     * in case the requested amount of frames is in two or more non-contiguous regions.
+     * FIXME requested and elapsed are both relative times.  Consider changing to absolute time.
+     */
+            status_t    obtainBuffer(Buffer* audioBuffer, const struct timespec *requested,
+                                     struct timespec *elapsed = NULL, size_t *nonContig = NULL);
+public:
 
-    /* Release an emptied buffer of "frameCount" frames for AudioFlinger to re-fill. */
+    /* Release an emptied buffer of "audioBuffer->frameCount" frames for AudioFlinger to re-fill. */
+    // FIXME make private when obtainBuffer() for TRANSFER_OBTAIN is removed
             void        releaseBuffer(Buffer* audioBuffer);
 
-
     /* As a convenience we provide a read() interface to the audio buffer.
-     * This is implemented on top of obtainBuffer/releaseBuffer.
+     * Input parameter 'size' is in byte units.
+     * This is implemented on top of obtainBuffer/releaseBuffer. For best
+     * performance use callbacks. Returns actual number of bytes read >= 0,
+     * or a negative status code.
      */
             ssize_t     read(void* buffer, size_t size);
 
@@ -336,68 +390,113 @@ private:
 
                 void        pause();    // suspend thread from execution at next loop boundary
                 void        resume();   // allow thread to execute, if not requested to exit
+                void        pauseConditional();
+                                        // like pause(), but only if prior resume() wasn't latched
 
     private:
         friend class AudioRecord;
         virtual bool        threadLoop();
-        AudioRecord& mReceiver;
+        AudioRecord&        mReceiver;
         virtual ~AudioRecordThread();
         Mutex               mMyLock;    // Thread::mLock is private
         Condition           mMyCond;    // Thread::mThreadExitedCondition is private
         bool                mPaused;    // whether thread is currently paused
+        bool                mResumeLatch;   // whether next pauseConditional() will be a nop
     };
 
             // body of AudioRecordThread::threadLoop()
-            bool processAudioBuffer(const sp<AudioRecordThread>& thread);
-
+            // returns the maximum amount of time before we would like to run again, where:
+            //      0           immediately
+            //      > 0         no later than this many nanoseconds from now
+            //      NS_WHENEVER still active but no particular deadline
+            //      NS_INACTIVE inactive so don't run again until re-started
+            //      NS_NEVER    never again
+            static const nsecs_t NS_WHENEVER = -1, NS_INACTIVE = -2, NS_NEVER = -3;
+            nsecs_t processAudioBuffer(const sp<AudioRecordThread>& thread);
+
+            // caller must hold lock on mLock for all _l methods
             status_t openRecord_l(uint32_t sampleRate,
                                 audio_format_t format,
                                 size_t frameCount,
-                                audio_io_handle_t input);
+                                audio_io_handle_t input,
+                                size_t epoch);
+
             audio_io_handle_t getInput_l();
-            status_t restoreRecord_l(audio_track_cblk_t*& cblk);
+
+            // FIXME enum is faster than strcmp() for parameter 'from'
+            status_t restoreRecord_l(const char *from);
 
     sp<AudioRecordThread>   mAudioRecordThread;
     mutable Mutex           mLock;
 
-    bool                    mActive;            // protected by mLock
+    // Current client state:  false = stopped, true = active.  Protected by mLock.  If more states
+    // are added, consider changing this to enum State { ... } mState as in AudioTrack.
+    bool                    mActive;
 
     // for client callback handler
     callback_t              mCbf;               // callback handler for events, or NULL
-    void*                   mUserData;
+    void*                   mUserData;          // for client callback handler
 
     // for notification APIs
-    uint32_t                mNotificationFrames;
-    uint32_t                mRemainingFrames;
-    uint32_t                mMarkerPosition;    // in frames
+    uint32_t                mNotificationFrames; // frames between each notification callback
+    bool                    mRefreshRemaining;  // processAudioBuffer() should refresh next 2
+
+    // These are private to processAudioBuffer(), and are not protected by a lock
+    uint32_t                mRemainingFrames;       // number of frames to request in obtainBuffer()
+    bool                    mRetryOnPartialBuffer;  // sleep and retry after partial obtainBuffer()
+    int                     mObservedSequence;      // last observed value of mSequence
+
+    uint32_t                mMarkerPosition;    // in wrapping (overflow) frame units
     bool                    mMarkerReached;
     uint32_t                mNewPosition;       // in frames
-    uint32_t                mUpdatePeriod;      // in ms
+    uint32_t                mUpdatePeriod;      // in frames, zero means no EVENT_NEW_POS
+
+    status_t                mStatus;
 
     // constant after constructor or set()
     uint32_t                mSampleRate;
     size_t                  mFrameCount;
     audio_format_t          mFormat;
-    uint8_t                 mChannelCount;
+    uint32_t                mChannelCount;
     size_t                  mFrameSize;         // app-level frame size == AudioFlinger frame size
     audio_source_t          mInputSource;
-    status_t                mStatus;
-    uint32_t                mLatency;
+    uint32_t                mLatency;           // in ms
     audio_channel_mask_t    mChannelMask;
-    audio_io_handle_t       mInput;                     // returned by AudioSystem::getInput()
     int                     mSessionId;
+    transfer_type           mTransfer;
+
+    audio_io_handle_t       mInput;             // returned by AudioSystem::getInput()
 
     // may be changed if IAudioRecord object is re-created
     sp<IAudioRecord>        mAudioRecord;
     sp<IMemory>             mCblkMemory;
-    audio_track_cblk_t*     mCblk;
-    void*                   mBuffers;           // starting address of buffers in shared memory
+    audio_track_cblk_t*     mCblk;              // re-load after mLock.unlock()
 
-    int                     mPreviousPriority;          // before start()
+    int                     mPreviousPriority;  // before start()
     SchedPolicy             mPreviousSchedulingGroup;
-    AudioRecordClientProxy* mProxy;
+
+    // The proxy should only be referenced while a lock is held because the proxy isn't
+    // multi-thread safe.
+    // An exception is that a blocking ClientProxy::obtainBuffer() may be called without a lock,
+    // provided that the caller also holds an extra reference to the proxy and shared memory to keep
+    sp<AudioRecordClientProxy> mProxy;
+
+    bool                    mInOverrun;         // whether recorder is currently in overrun state
+
+private:
+    class DeathNotifier : public IBinder::DeathRecipient {
+    public:
+        DeathNotifier(AudioRecord* audioRecord) : mAudioRecord(audioRecord) { }
+    protected:
+        virtual void        binderDied(const wp<IBinder>& who);
+    private:
+        const wp<AudioRecord> mAudioRecord;
+    };
+
+    sp<DeathNotifier>       mDeathNotifier;
+    uint32_t                mSequence;              // incremented for each new IAudioRecord attempt
 };
 
 }; // namespace android
 
-#endif /*AUDIORECORD_H_*/
+#endif // ANDROID_AUDIORECORD_H
index 8dbc9ee..e9bb76a 100644 (file)
 #ifndef ANDROID_AUDIOTRACK_H
 #define ANDROID_AUDIOTRACK_H
 
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <media/IAudioFlinger.h>
-#include <media/IAudioTrack.h>
-#include <media/AudioSystem.h>
-
-#include <utils/RefBase.h>
-#include <utils/Errors.h>
-#include <binder/IInterface.h>
-#include <binder/IMemory.h>
 #include <cutils/sched_policy.h>
+#include <media/AudioSystem.h>
+#include <media/IAudioTrack.h>
 #include <utils/threads.h>
 
 namespace android {
@@ -37,10 +28,11 @@ namespace android {
 
 class audio_track_cblk_t;
 class AudioTrackClientProxy;
+class StaticAudioTrackClientProxy;
 
 // ----------------------------------------------------------------------------
 
-class AudioTrack : virtual public RefBase
+class AudioTrack : public RefBase
 {
 public:
     enum channel_index {
@@ -49,7 +41,7 @@ public:
         RIGHT  = 1
     };
 
-    /* Events used by AudioTrack callback function (audio_track_cblk_t).
+    /* Events used by AudioTrack callback function (callback_t).
      * Keep in sync with frameworks/base/media/java/android/media/AudioTrack.java NATIVE_EVENT_*.
      */
     enum event_type {
@@ -64,7 +56,10 @@ public:
                                     // (See setMarkerPosition()).
         EVENT_NEW_POS = 4,          // Playback head is at a new position
                                     // (See setPositionUpdatePeriod()).
-        EVENT_BUFFER_END = 5        // Playback head is at the end of the buffer.
+        EVENT_BUFFER_END = 5,       // Playback head is at the end of the buffer.
+                                    // Not currently used by android.media.AudioTrack.
+        EVENT_NEW_IAUDIOTRACK = 6,  // IAudioTrack was re-created, either due to re-routing and
+                                    // voluntary invalidation by mediaserver, or mediaserver crash.
     };
 
     /* Client should declare Buffer on the stack and pass address to obtainBuffer()
@@ -74,19 +69,23 @@ public:
     class Buffer
     {
     public:
+        // FIXME use m prefix
         size_t      frameCount;   // number of sample frames corresponding to size;
                                   // on input it is the number of frames desired,
                                   // on output is the number of frames actually filled
 
-        size_t      size;         // input/output in byte units
+        size_t      size;         // input/output in bytes == frameCount * frameSize
+                                  // FIXME this is redundant with respect to frameCount,
+                                  // and TRANSFER_OBTAIN mode is broken for 8-bit data
+                                  // since we don't define the frame format
+
         union {
             void*       raw;
-            short*      i16;    // signed 16-bit
-            int8_t*     i8;     // unsigned 8-bit, offset by 0x80
+            short*      i16;      // signed 16-bit
+            int8_t*     i8;       // unsigned 8-bit, offset by 0x80
         };
     };
 
-
     /* As a convenience, if a callback is supplied, a handler thread
      * is automatically created with the appropriate priority. This thread
      * invokes the callback when a new buffer becomes available or various conditions occur.
@@ -100,9 +99,10 @@ public:
      *            written.
      *          - EVENT_UNDERRUN: unused.
      *          - EVENT_LOOP_END: pointer to an int indicating the number of loops remaining.
-     *          - EVENT_MARKER: pointer to an uint32_t containing the marker position in frames.
-     *          - EVENT_NEW_POS: pointer to an uint32_t containing the new position in frames.
+     *          - EVENT_MARKER: pointer to const uint32_t containing the marker position in frames.
+     *          - EVENT_NEW_POS: pointer to const uint32_t containing the new position in frames.
      *          - EVENT_BUFFER_END: unused.
+     *          - EVENT_NEW_IAUDIOTRACK: unused.
      */
 
     typedef void (*callback_t)(int event, void* user, void *info);
@@ -114,9 +114,19 @@ public:
      *  - NO_INIT: audio server or audio hardware not initialized
      */
 
-     static status_t getMinFrameCount(size_t* frameCount,
-                                      audio_stream_type_t streamType = AUDIO_STREAM_DEFAULT,
-                                      uint32_t sampleRate = 0);
+    static status_t getMinFrameCount(size_t* frameCount,
+                                     audio_stream_type_t streamType,
+                                     uint32_t sampleRate);
+
+    /* How data is transferred to AudioTrack
+     */
+    enum transfer_type {
+        TRANSFER_DEFAULT,   // not specified explicitly; determine from the other parameters
+        TRANSFER_CALLBACK,  // callback EVENT_MORE_DATA
+        TRANSFER_OBTAIN,    // FIXME deprecated: call obtainBuffer() and releaseBuffer()
+        TRANSFER_SYNC,      // synchronous write()
+        TRANSFER_SHARED,    // shared memory
+    };
 
     /* Constructs an uninitialized AudioTrack. No connection with
      * AudioFlinger takes place.  Use set() after this.
@@ -128,13 +138,13 @@ public:
      * Unspecified values are set to appropriate default values.
      * With this constructor, the track is configured for streaming mode.
      * Data to be rendered is supplied by write() or by the callback EVENT_MORE_DATA.
-     * Intermixing a combination of write() and non-ignored EVENT_MORE_DATA is deprecated.
+     * Intermixing a combination of write() and non-ignored EVENT_MORE_DATA is not allowed.
      *
      * Parameters:
      *
      * streamType:         Select the type of audio stream this track is attached to
      *                     (e.g. AUDIO_STREAM_MUSIC).
-     * sampleRate:         Track sampling rate in Hz.
+     * sampleRate:         Data source sampling rate in Hz.
      * format:             Audio format (e.g AUDIO_FORMAT_PCM_16_BIT for signed
      *                     16 bits per sample).
      * channelMask:        Channel mask.
@@ -149,9 +159,10 @@ public:
      * user:               Context for use by the callback receiver.
      * notificationFrames: The callback function is called each time notificationFrames PCM
      *                     frames have been consumed from track input buffer.
+     *                     This is expressed in units of frames at the initial source sample rate.
      * sessionId:          Specific session ID, or zero to use default.
-     * threadCanCallJava:  Whether callbacks are made from an attached thread and thus can call JNI.
-     *                     If not present in parameter list, then fixed at false.
+     * transferType:       How data is transferred to AudioTrack.
+     * threadCanCallJava:  Not present in parameter list, and so is fixed at false.
      */
 
                         AudioTrack( audio_stream_type_t streamType,
@@ -163,7 +174,8 @@ public:
                                     callback_t cbf       = NULL,
                                     void* user           = NULL,
                                     int notificationFrames = 0,
-                                    int sessionId        = 0);
+                                    int sessionId        = 0,
+                                    transfer_type transferType = TRANSFER_DEFAULT);
 
     /* Creates an audio track and registers it with AudioFlinger.
      * With this constructor, the track is configured for static buffer mode.
@@ -174,7 +186,6 @@ public:
      * The write() method is not supported in this case.
      * It is recommended to pass a callback function to be notified of playback end by an
      * EVENT_UNDERRUN event.
-     * FIXME EVENT_MORE_DATA still occurs; it must be ignored.
      */
 
                         AudioTrack( audio_stream_type_t streamType,
@@ -186,7 +197,8 @@ public:
                                     callback_t cbf      = NULL,
                                     void* user          = NULL,
                                     int notificationFrames = 0,
-                                    int sessionId       = 0);
+                                    int sessionId       = 0,
+                                    transfer_type transferType = TRANSFER_DEFAULT);
 
     /* Terminates the AudioTrack and unregisters it from AudioFlinger.
      * Also destroys all resources associated with the AudioTrack.
@@ -195,7 +207,8 @@ protected:
                         virtual ~AudioTrack();
 public:
 
-    /* Initialize an uninitialized AudioTrack.
+    /* Initialize an AudioTrack that was created using the AudioTrack() constructor.
+     * Don't call set() more than once, or after the AudioTrack() constructors that take parameters.
      * Returned status (from utils/Errors.h) can be:
      *  - NO_ERROR: successful initialization
      *  - INVALID_OPERATION: AudioTrack is already initialized
@@ -203,6 +216,10 @@ public:
      *  - NO_INIT: audio server or audio hardware not initialized
      * If sharedBuffer is non-0, the frameCount parameter is ignored and
      * replaced by the shared buffer's total allocated size in frame units.
+     *
+     * Parameters not listed in the AudioTrack constructors above:
+     *
+     * threadCanCallJava:  Whether callbacks are made from an attached thread and thus can call JNI.
      */
             status_t    set(audio_stream_type_t streamType = AUDIO_STREAM_DEFAULT,
                             uint32_t sampleRate = 0,
@@ -215,7 +232,8 @@ public:
                             int notificationFrames = 0,
                             const sp<IMemory>& sharedBuffer = 0,
                             bool threadCanCallJava = false,
-                            int sessionId       = 0);
+                            int sessionId       = 0,
+                            transfer_type transferType = TRANSFER_DEFAULT);
 
     /* Result of constructing the AudioTrack. This must be checked
      * before using any AudioTrack API (except for set()), because using
@@ -235,14 +253,15 @@ public:
             audio_stream_type_t streamType() const { return mStreamType; }
             audio_format_t format() const   { return mFormat; }
 
-    /* Return frame size in bytes, which for linear PCM is channelCount * (bit depth per channel / 8).
+    /* Return frame size in bytes, which for linear PCM is
+     * channelCount * (bit depth per channel / 8).
      * channelCount is determined from channelMask, and bit depth comes from format.
      * For non-linear formats, the frame size is typically 1 byte.
      */
-            uint32_t    channelCount() const { return mChannelCount; }
+            size_t      frameSize() const   { return mFrameSize; }
 
+            uint32_t    channelCount() const { return mChannelCount; }
             uint32_t    frameCount() const  { return mFrameCount; }
-            size_t      frameSize() const   { return mFrameSize; }
 
     /* Return the static buffer specified in constructor or set(), or 0 for streaming mode */
             sp<IMemory> sharedBuffer() const { return mSharedBuffer; }
@@ -255,10 +274,9 @@ public:
 
     /* Stop a track.
      * In static buffer mode, the track is stopped immediately.
-     * In streaming mode, the callback will cease being called and
-     * obtainBuffer returns STOPPED. Note that obtainBuffer() still works
-     * and will fill up buffers until the pool is exhausted.
-     * The stop does not occur immediately: any data remaining in the buffer
+     * In streaming mode, the callback will cease being called.  Note that obtainBuffer() still
+     * works and will fill up buffers until the pool is exhausted, and then will return WOULD_BLOCK.
+     * In streaming mode the stop does not occur immediately: any data remaining in the buffer
      * is first drained, mixed, and output, and only then is the track marked as stopped.
      */
             void        stop();
@@ -272,7 +290,7 @@ public:
             void        flush();
 
     /* Pause a track. After pause, the callback will cease being called and
-     * obtainBuffer returns STOPPED. Note that obtainBuffer() still works
+     * obtainBuffer returns WOULD_BLOCK. Note that obtainBuffer() still works
      * and will fill up buffers until the pool is exhausted.
      * Volume is ramped down over the next mix buffer following the pause request,
      * and then the track is marked as paused.  It can be resumed with ramp up by start().
@@ -296,11 +314,11 @@ public:
             status_t    setAuxEffectSendLevel(float level);
             void        getAuxEffectSendLevel(float* level) const;
 
-    /* Set sample rate for this track in Hz, mostly used for games' sound effects
+    /* Set source sample rate for this track in Hz, mostly used for games' sound effects
      */
             status_t    setSampleRate(uint32_t sampleRate);
 
-    /* Return current sample rate in Hz, or 0 if unknown */
+    /* Return current source sample rate in Hz, or 0 if unknown */
             uint32_t    getSampleRate() const;
 
     /* Enables looping and sets the start and end points of looping.
@@ -322,7 +340,7 @@ public:
      *      loopCount != 0 implies 0 <= loopStart < loopEnd <= frameCount().
      *
      * If the loop period (loopEnd - loopStart) is too small for the implementation to support,
-     * setLoop() will return BAD_VALUE.
+     * setLoop() will return BAD_VALUE.  loopCount must be >= -1.
      *
      */
             status_t    setLoop(uint32_t loopStart, uint32_t loopEnd, int loopCount);
@@ -330,7 +348,7 @@ public:
     /* Sets marker position. When playback reaches the number of frames specified, a callback with
      * event type EVENT_MARKER is called. Calling setMarkerPosition with marker == 0 cancels marker
      * notification callback.  To set a marker at a position which would compute as 0,
-     * a workaround is to the set the marker at a nearby position such as -1 or 1.
+     * a workaround is to the set the marker at a nearby position such as ~0 or 1.
      * If the AudioTrack has been opened with no callback function associated, the operation will
      * fail.
      *
@@ -390,16 +408,22 @@ public:
     /* Return the total number of frames played since playback start.
      * The counter will wrap (overflow) periodically, e.g. every ~27 hours at 44.1 kHz.
      * It is reset to zero by flush(), reload(), and stop().
+     *
+     * Parameters:
+     *
+     *  position:  Address where to return play head position.
+     *
+     * Returned status (from utils/Errors.h) can be:
+     *  - NO_ERROR: successful operation
+     *  - BAD_VALUE:  position is NULL
      */
-            status_t    getPosition(uint32_t *position);
+            status_t    getPosition(uint32_t *position) const;
 
-#if 0
     /* For static buffer mode only, this returns the current playback position in frames
      * relative to start of buffer.  It is analogous to the new API for
      * setLoop() and setPosition().  After underrun, the position will be at end of buffer.
      */
             status_t    getBufferPosition(uint32_t *position);
-#endif
 
     /* Forces AudioTrack buffer full condition. When playing a static buffer, this method avoids
      * rewriting the buffer before restarting playback after a stop.
@@ -446,15 +470,19 @@ public:
      */
             status_t    attachAuxEffect(int effectId);
 
-    /* Obtains a buffer of "frameCount" frames. The buffer must be
-     * filled entirely, and then released with releaseBuffer().
-     * If the track is stopped, obtainBuffer() returns
-     * STOPPED instead of NO_ERROR as long as there are buffers available,
-     * at which point NO_MORE_BUFFERS is returned.
+    /* Obtains a buffer of up to "audioBuffer->frameCount" empty slots for frames.
+     * After filling these slots with data, the caller should release them with releaseBuffer().
+     * If the track buffer is not full, obtainBuffer() returns as many contiguous
+     * [empty slots for] frames as are available immediately.
+     * If the track buffer is full and track is stopped, obtainBuffer() returns WOULD_BLOCK
+     * regardless of the value of waitCount.
+     * If the track buffer is full and track is not stopped, obtainBuffer() blocks with a
+     * maximum timeout based on waitCount; see chart below.
      * Buffers will be returned until the pool
      * is exhausted, at which point obtainBuffer() will either block
-     * or return WOULD_BLOCK depending on the value of the "blocking"
+     * or return WOULD_BLOCK depending on the value of the "waitCount"
      * parameter.
+     * Each sample is 16-bit signed PCM.
      *
      * obtainBuffer() and releaseBuffer() are deprecated for direct use by applications,
      * which should use write() or callback EVENT_MORE_DATA instead.
@@ -477,24 +505,35 @@ public:
      *  raw         pointer to the buffer
      */
 
-        enum {
-            NO_MORE_BUFFERS = 0x80000001,   // same name in AudioFlinger.h, ok to be different value
-            STOPPED = 1
-        };
+    /* FIXME Deprecated public API for TRANSFER_OBTAIN mode */
+            status_t    obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
+                                __attribute__((__deprecated__));
 
-            status_t    obtainBuffer(Buffer* audioBuffer, int32_t waitCount);
+private:
+    /* New internal API
+     * If nonContig is non-NULL, it is an output parameter that will be set to the number of
+     * additional non-contiguous frames that are available immediately.
+     * FIXME We could pass an array of Buffers instead of only one Buffer to obtainBuffer(),
+     * in case the requested amount of frames is in two or more non-contiguous regions.
+     * FIXME requested and elapsed are both relative times.  Consider changing to absolute time.
+     */
+            status_t    obtainBuffer(Buffer* audioBuffer, const struct timespec *requested,
+                                     struct timespec *elapsed = NULL, size_t *nonContig = NULL);
+public:
 
-    /* Release a filled buffer of "frameCount" frames for AudioFlinger to process. */
+    /* Release a filled buffer of "audioBuffer->frameCount" frames for AudioFlinger to process. */
+    // FIXME make private when obtainBuffer() for TRANSFER_OBTAIN is removed
             void        releaseBuffer(Buffer* audioBuffer);
 
     /* As a convenience we provide a write() interface to the audio buffer.
+     * Input parameter 'size' is in byte units.
      * This is implemented on top of obtainBuffer/releaseBuffer. For best
      * performance use callbacks. Returns actual number of bytes written >= 0,
      * or one of the following negative status codes:
      *      INVALID_OPERATION   AudioTrack is configured for shared buffer mode
      *      BAD_VALUE           size is invalid
-     *      STOPPED             AudioTrack was stopped during the write
-     *      NO_MORE_BUFFERS     when obtainBuffer() returns same
+     *      WOULD_BLOCK         when obtainBuffer() returns same, or
+     *                          AudioTrack was stopped during the write
      *      or any other error code returned by IAudioTrack::start() or restoreTrack_l().
      * Not supported for static buffer mode.
      */
@@ -503,7 +542,13 @@ public:
     /*
      * Dumps the state of an audio track.
      */
-            status_t dump(int fd, const Vector<String16>& args) const;
+            status_t    dump(int fd, const Vector<String16>& args) const;
+
+    /*
+     * Return the total number of frames which AudioFlinger desired but were unavailable,
+     * and thus which resulted in an underrun.  Reset to zero by stop().
+     */
+            uint32_t    getUnderrunFrames() const;
 
 protected:
     /* copying audio tracks is not allowed */
@@ -522,19 +567,29 @@ protected:
 
                 void        pause();    // suspend thread from execution at next loop boundary
                 void        resume();   // allow thread to execute, if not requested to exit
+                void        pauseConditional();
+                                        // like pause(), but only if prior resume() wasn't latched
 
     private:
         friend class AudioTrack;
         virtual bool        threadLoop();
-        AudioTrack& mReceiver;
-        ~AudioTrackThread();
+        AudioTrack&         mReceiver;
+        virtual ~AudioTrackThread();
         Mutex               mMyLock;    // Thread::mLock is private
         Condition           mMyCond;    // Thread::mThreadExitedCondition is private
         bool                mPaused;    // whether thread is currently paused
+        bool                mResumeLatch;   // whether next pauseConditional() will be a nop
     };
 
             // body of AudioTrackThread::threadLoop()
-            bool processAudioBuffer(const sp<AudioTrackThread>& thread);
+            // returns the maximum amount of time before we would like to run again, where:
+            //      0           immediately
+            //      > 0         no later than this many nanoseconds from now
+            //      NS_WHENEVER still active but no particular deadline
+            //      NS_INACTIVE inactive so don't run again until re-started
+            //      NS_NEVER    never again
+            static const nsecs_t NS_WHENEVER = -1, NS_INACTIVE = -2, NS_NEVER = -3;
+            nsecs_t processAudioBuffer(const sp<AudioTrackThread>& thread);
 
             // caller must hold lock on mLock for all _l methods
             status_t createTrack_l(audio_stream_type_t streamType,
@@ -543,20 +598,24 @@ protected:
                                  size_t frameCount,
                                  audio_output_flags_t flags,
                                  const sp<IMemory>& sharedBuffer,
-                                 audio_io_handle_t output);
+                                 audio_io_handle_t output,
+                                 size_t epoch);
 
-            // can only be called when !mActive
+            // can only be called when mState != STATE_ACTIVE
             void flush_l();
 
-            status_t setLoop_l(uint32_t loopStart, uint32_t loopEnd, int loopCount);
+            void setLoop_l(uint32_t loopStart, uint32_t loopEnd, int loopCount);
             audio_io_handle_t getOutput_l();
-            status_t restoreTrack_l(audio_track_cblk_t*& cblk, bool fromStart);
-            bool stopped_l() const { return !mActive; }
 
+            // FIXME enum is faster than strcmp() for parameter 'from'
+            status_t restoreTrack_l(const char *from);
+
+    // may be changed if IAudioTrack is re-created
     sp<IAudioTrack>         mAudioTrack;
     sp<IMemory>             mCblkMemory;
-    sp<AudioTrackThread>    mAudioTrackThread;
+    audio_track_cblk_t*     mCblk;                  // re-load after mLock.unlock()
 
+    sp<AudioTrackThread>    mAudioTrackThread;
     float                   mVolume[2];
     float                   mSendLevel;
     uint32_t                mSampleRate;
@@ -564,62 +623,89 @@ protected:
     size_t                  mReqFrameCount;         // frame count to request the next time a new
                                                     // IAudioTrack is needed
 
-    audio_track_cblk_t*     mCblk;                  // re-load after mLock.unlock()
-
-            // Starting address of buffers in shared memory.  If there is a shared buffer, mBuffers
-            // is the value of pointer() for the shared buffer, otherwise mBuffers points
-            // immediately after the control block.  This address is for the mapping within client
-            // address space.  AudioFlinger::TrackBase::mBuffer is for the server address space.
-    void*                   mBuffers;
 
+    // constant after constructor or set()
     audio_format_t          mFormat;                // as requested by client, not forced to 16-bit
     audio_stream_type_t     mStreamType;
     uint32_t                mChannelCount;
     audio_channel_mask_t    mChannelMask;
+    transfer_type           mTransfer;
 
-                // mFrameSize is equal to mFrameSizeAF for non-PCM or 16-bit PCM data.
-                // For 8-bit PCM data, mFrameSizeAF is
-                // twice as large because data is expanded to 16-bit before being stored in buffer.
+    // mFrameSize is equal to mFrameSizeAF for non-PCM or 16-bit PCM data.  For 8-bit PCM data, it's
+    // twice as large as mFrameSize because data is expanded to 16-bit before it's stored in buffer.
     size_t                  mFrameSize;             // app-level frame size
     size_t                  mFrameSizeAF;           // AudioFlinger frame size
 
     status_t                mStatus;
-    uint32_t                mLatency;
 
-    bool                    mActive;                // protected by mLock
+    // can change dynamically when IAudioTrack invalidated
+    uint32_t                mLatency;               // in ms
+
+    // Indicates the current track state.  Protected by mLock.
+    enum State {
+        STATE_ACTIVE,
+        STATE_STOPPED,
+        STATE_PAUSED,
+        STATE_FLUSHED,
+    }                       mState;
 
     callback_t              mCbf;                   // callback handler for events, or NULL
     void*                   mUserData;              // for client callback handler
 
     // for notification APIs
     uint32_t                mNotificationFramesReq; // requested number of frames between each
-                                                    // notification callback
+                                                    // notification callback,
+                                                    // at initial source sample rate
     uint32_t                mNotificationFramesAct; // actual number of frames between each
-                                                    // notification callback
+                                                    // notification callback,
+                                                    // at initial source sample rate
+    bool                    mRefreshRemaining;      // processAudioBuffer() should refresh next 2
+
+    // These are private to processAudioBuffer(), and are not protected by a lock
+    uint32_t                mRemainingFrames;       // number of frames to request in obtainBuffer()
+    bool                    mRetryOnPartialBuffer;  // sleep and retry after partial obtainBuffer()
+    int                     mObservedSequence;      // last observed value of mSequence
+
     sp<IMemory>             mSharedBuffer;
-    int                     mLoopCount;
-    uint32_t                mRemainingFrames;
+    uint32_t                mLoopPeriod;            // in frames, zero means looping is disabled
     uint32_t                mMarkerPosition;        // in wrapping (overflow) frame units
     bool                    mMarkerReached;
     uint32_t                mNewPosition;           // in frames
-    uint32_t                mUpdatePeriod;          // in frames
+    uint32_t                mUpdatePeriod;          // in frames, zero means no EVENT_NEW_POS
 
-    bool                    mFlushed; // FIXME will be made obsolete by making flush() synchronous
     audio_output_flags_t    mFlags;
     int                     mSessionId;
     int                     mAuxEffectId;
 
-    // When locking both mLock and mCblk->lock, must lock in this order to avoid deadlock:
-    //      1. mLock
-    //      2. mCblk->lock
-    // It is OK to lock only mCblk->lock.
     mutable Mutex           mLock;
 
     bool                    mIsTimed;
     int                     mPreviousPriority;          // before start()
     SchedPolicy             mPreviousSchedulingGroup;
-    AudioTrackClientProxy*  mProxy;
     bool                    mAwaitBoost;    // thread should wait for priority boost before running
+
+    // The proxy should only be referenced while a lock is held because the proxy isn't
+    // multi-thread safe, especially the SingleStateQueue part of the proxy.
+    // An exception is that a blocking ClientProxy::obtainBuffer() may be called without a lock,
+    // provided that the caller also holds an extra reference to the proxy and shared memory to keep
+    // them around in case they are replaced during the obtainBuffer().
+    sp<StaticAudioTrackClientProxy> mStaticProxy;   // for type safety only
+    sp<AudioTrackClientProxy>       mProxy;         // primary owner of the memory
+
+    bool                    mInUnderrun;            // whether track is currently in underrun state
+
+private:
+    class DeathNotifier : public IBinder::DeathRecipient {
+    public:
+        DeathNotifier(AudioTrack* audioTrack) : mAudioTrack(audioTrack) { }
+    protected:
+        virtual void        binderDied(const wp<IBinder>& who);
+    private:
+        const wp<AudioTrack> mAudioTrack;
+    };
+
+    sp<DeathNotifier>       mDeathNotifier;
+    uint32_t                mSequence;              // incremented for each new IAudioTrack attempt
 };
 
 class TimedAudioTrack : public AudioTrack
index 41e20f8..681f557 100644 (file)
 
 #include <utils/threads.h>
 #include <utils/Log.h>
+#include <utils/RefBase.h>
+#include <media/nbaio/roundup.h>
+#include <media/SingleStateQueue.h>
+#include <private/media/StaticAudioTrackState.h>
 
 namespace android {
 
 // ----------------------------------------------------------------------------
 
-// Maximum cumulated timeout milliseconds before restarting audioflinger thread
-#define MAX_STARTUP_TIMEOUT_MS  3000    // Longer timeout period at startup to cope with A2DP
-                                        // init time
-#define MAX_RUN_TIMEOUT_MS      1000
-#define WAIT_PERIOD_MS          10
-
-#define CBLK_UNDERRUN   0x01 // set: underrun (out) or overrrun (in), clear: no underrun or overrun
+#define CBLK_UNDERRUN   0x01 // set by server immediately on output underrun, cleared by client
 #define CBLK_FORCEREADY 0x02 // set: track is considered ready immediately by AudioFlinger,
                              // clear: track is ready when buffer full
 #define CBLK_INVALID    0x04 // track buffer invalidated by AudioFlinger, need to re-create
-#define CBLK_DISABLED   0x08 // track disabled by AudioFlinger due to underrun, need to re-start
+#define CBLK_DISABLED   0x08 // output track disabled by AudioFlinger due to underrun,
+                             // need to re-start.  Unlike CBLK_UNDERRUN, this is not set
+                             // immediately, but only after a long string of underruns.
+// 0x10 unused
+#define CBLK_LOOP_CYCLE 0x20 // set by server each time a loop cycle other than final one completes
+#define CBLK_LOOP_FINAL 0x40 // set by server when the final loop cycle completes
+#define CBLK_BUFFER_END 0x80 // set by server when the position reaches end of buffer if not looping
+#define CBLK_OVERRUN   0x100 // set by server immediately on input overrun, cleared by client
+#define CBLK_INTERRUPT 0x200 // set by client on interrupt(), cleared by client in obtainBuffer()
 
 struct AudioTrackSharedStreaming {
     // similar to NBAIO MonoPipe
-    volatile int32_t mFront;
-    volatile int32_t mRear;
+    // in continuously incrementing frame units, take modulo buffer size, which must be a power of 2
+    volatile int32_t mFront;    // read by server
+    volatile int32_t mRear;     // write by client
+    volatile int32_t mFlush;    // incremented by client to indicate a request to flush;
+                                // server notices and discards all data between mFront and mRear
+    volatile uint32_t mUnderrunFrames;  // server increments for each unavailable but desired frame
 };
 
-// future
+typedef SingleStateQueue<StaticAudioTrackState> StaticAudioTrackSingleStateQueue;
+
 struct AudioTrackSharedStatic {
-    int mReserved;
+    StaticAudioTrackSingleStateQueue::Shared
+                    mSingleStateQueue;
+    size_t          mBufferPosition;    // updated asynchronously by server,
+                                        // "for entertainment purposes only"
 };
 
 // ----------------------------------------------------------------------------
@@ -55,65 +69,61 @@ struct AudioTrackSharedStatic {
 // Important: do not add any virtual methods, including ~
 struct audio_track_cblk_t
 {
+                // Since the control block is always located in shared memory, this constructor
+                // is only used for placement new().  It is never used for regular new() or stack.
+                            audio_track_cblk_t();
+                /*virtual*/ ~audio_track_cblk_t() { }
+
                 friend class Proxy;
+                friend class ClientProxy;
                 friend class AudioTrackClientProxy;
                 friend class AudioRecordClientProxy;
                 friend class ServerProxy;
+                friend class AudioTrackServerProxy;
+                friend class AudioRecordServerProxy;
 
     // The data members are grouped so that members accessed frequently and in the same context
     // are in the same line of data cache.
-                Mutex       lock;           // sizeof(int)
-                Condition   cv;             // sizeof(int)
-
-                // next 4 are offsets within "buffers"
-    volatile    uint32_t    user;
-    volatile    uint32_t    server;
-                uint32_t    userBase;
-                uint32_t    serverBase;
 
-                int         mPad1;          // unused, but preserves cache line alignment
+    volatile    uint32_t    server;     // updated asynchronously by server,
+                                        // "for entertainment purposes only"
 
                 size_t      frameCount_;    // used during creation to pass actual track buffer size
                                             // from AudioFlinger to client, and not referenced again
-                                            // FIXME remove here and replace by createTrack() in/out parameter
+                                            // FIXME remove here and replace by createTrack() in/out
+                                            // parameter
                                             // renamed to "_" to detect incorrect use
 
-                // Cache line boundary (32 bytes)
+    volatile    int32_t     mFutex;     // semaphore: down (P) by client,
+                                        // up (V) by server or binderDied() or interrupt()
+
+private:
 
-                uint32_t    loopStart;
-                uint32_t    loopEnd;        // read-only for server, read/write for client
-                int         loopCount;      // read/write for client
+                size_t      mMinimum;       // server wakes up client if available >= mMinimum
 
                 // Channel volumes are fixed point U4.12, so 0x1000 means 1.0.
                 // Left channel is in [0:15], right channel is in [16:31].
                 // Always read and write the combined pair atomically.
                 // For AudioTrack only, not used by AudioRecord.
-private:
                 uint32_t    mVolumeLR;
 
                 uint32_t    mSampleRate;    // AudioTrack only: client's requested sample rate in Hz
                                             // or 0 == default. Write-only client, read-only server.
 
+                // client write-only, server read-only
+                uint16_t    mSendLevel;      // Fixed point U4.12 so 0x1000 means 1.0
+
                 uint8_t     mPad2;           // unused
 
 public:
                 // read-only for client, server writes once at initialization and is then read-only
                 uint8_t     mName;           // normal tracks: track name, fast tracks: track index
 
-                // used by client only
-                uint16_t    bufferTimeoutMs; // Maximum cumulated timeout before restarting
-                                             // audioflinger
-
-                uint16_t    waitTimeMs;      // Cumulated wait time, used by client only
-private:
-                // client write-only, server read-only
-                uint16_t    mSendLevel;      // Fixed point U4.12 so 0x1000 means 1.0
-public:
     volatile    int32_t     flags;
 
                 // Cache line boundary (32 bytes)
 
-#if 0
+public:
                 union {
                     AudioTrackSharedStreaming   mStreaming;
                     AudioTrackSharedStatic      mStatic;
@@ -121,25 +131,6 @@ public:
                 } u;
 
                 // Cache line boundary (32 bytes)
-#endif
-
-                // Since the control block is always located in shared memory, this constructor
-                // is only used for placement new().  It is never used for regular new() or stack.
-                            audio_track_cblk_t();
-
-private:
-                // if there is a shared buffer, "buffers" is the value of pointer() for the shared
-                // buffer, otherwise "buffers" points immediately after the control block
-                void*       buffer(void *buffers, uint32_t frameSize, size_t offset) const;
-
-                bool        tryLock();
-
-                // isOut == true means AudioTrack, isOut == false means AudioRecord
-                bool        stepServer(size_t stepCount, size_t frameCount, bool isOut);
-                uint32_t    stepUser(size_t stepCount, size_t frameCount, bool isOut);
-                uint32_t    framesAvailable(size_t frameCount, bool isOut);
-                uint32_t    framesAvailable_l(size_t frameCount, bool isOut);
-                uint32_t    framesReady(bool isOut);
 };
 
 // ----------------------------------------------------------------------------
@@ -147,29 +138,31 @@ private:
 // Proxy for shared memory control block, to isolate callers from needing to know the details.
 // There is exactly one ClientProxy and one ServerProxy per shared memory control block.
 // The proxies are located in normal memory, and are not multi-thread safe within a given side.
-class Proxy {
+class Proxy : public RefBase {
 protected:
-    Proxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount, size_t frameSize)
-        : mCblk(cblk), mBuffers(buffers), mFrameCount(frameCount), mFrameSize(frameSize) { }
+    Proxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount, size_t frameSize, bool isOut,
+            bool clientInServer);
     virtual ~Proxy() { }
 
 public:
-    void*   buffer(size_t offset) const {
-        return mCblk->buffer(mBuffers, mFrameSize, offset);
-    }
+    struct Buffer {
+        size_t  mFrameCount;            // number of frames available in this buffer
+        void*   mRaw;                   // pointer to first frame
+        size_t  mNonContig;             // number of additional non-contiguous frames available
+    };
 
 protected:
     // These refer to shared memory, and are virtual addresses with respect to the current process.
     // They may have different virtual addresses within the other process.
-    audio_track_cblk_t* const   mCblk;          // the control block
-    void* const                 mBuffers;       // starting address of buffers
-
-    const size_t                mFrameCount;    // not necessarily a power of 2
-    const size_t                mFrameSize;     // in bytes
-#if 0
-    const size_t                mFrameCountP2;  // mFrameCount rounded to power of 2, streaming mode
-#endif
-
+    audio_track_cblk_t* const   mCblk;  // the control block
+    void* const     mBuffers;           // starting address of buffers
+
+    const size_t    mFrameCount;        // not necessarily a power of 2
+    const size_t    mFrameSize;         // in bytes
+    const size_t    mFrameCountP2;      // mFrameCount rounded to power of 2, streaming mode
+    const bool      mIsOut;             // true for AudioTrack, false for AudioRecord
+    const bool      mClientInServer;    // true for OutputTrack, false for AudioTrack & AudioRecord
+    bool            mIsShutdown;        // latch set to true when shared memory corruption detected
 };
 
 // ----------------------------------------------------------------------------
@@ -177,9 +170,86 @@ protected:
 // Proxy seen by AudioTrack client and AudioRecord client
 class ClientProxy : public Proxy {
 protected:
-    ClientProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount, size_t frameSize)
-        : Proxy(cblk, buffers, frameCount, frameSize) { }
+    ClientProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount, size_t frameSize,
+            bool isOut, bool clientInServer);
     virtual ~ClientProxy() { }
+
+public:
+    static const struct timespec kForever;
+    static const struct timespec kNonBlocking;
+
+    // Obtain a buffer with filled frames (reading) or empty frames (writing).
+    // It is permitted to call obtainBuffer() multiple times in succession, without any intervening
+    // calls to releaseBuffer().  In that case, the final obtainBuffer() is the one that effectively
+    // sets or extends the unreleased frame count.
+    // On entry:
+    //  buffer->mFrameCount should be initialized to maximum number of desired frames,
+    //      which must be > 0.
+    //  buffer->mNonContig is unused.
+    //  buffer->mRaw is unused.
+    //  requested is the requested timeout in local monotonic delta time units:
+    //      NULL or &kNonBlocking means non-blocking (zero timeout).
+    //      &kForever means block forever (infinite timeout).
+    //      Other values mean a specific timeout in local monotonic delta time units.
+    //  elapsed is a pointer to a location that will hold the total local monotonic time that
+    //      elapsed while blocked, or NULL if not needed.
+    // On exit:
+    //  buffer->mFrameCount has the actual number of contiguous available frames,
+    //      which is always 0 when the return status != NO_ERROR.
+    //  buffer->mNonContig is the number of additional non-contiguous available frames.
+    //  buffer->mRaw is a pointer to the first available frame,
+    //      or NULL when buffer->mFrameCount == 0.
+    // The return status is one of:
+    //  NO_ERROR    Success, buffer->mFrameCount > 0.
+    //  WOULD_BLOCK Non-blocking mode and no frames are available.
+    //  TIMED_OUT   Timeout occurred before any frames became available.
+    //              This can happen even for infinite timeout, due to a spurious wakeup.
+    //              In this case, the caller should investigate and then re-try as appropriate.
+    //  DEAD_OBJECT Server has died or invalidated, caller should destroy this proxy and re-create.
+    //  -EINTR      Call has been interrupted.  Look around to see why, and then perhaps try again.
+    //  NO_INIT     Shared memory is corrupt.
+    //  BAD_VALUE   On entry buffer == NULL or buffer->mFrameCount == 0.
+    status_t    obtainBuffer(Buffer* buffer, const struct timespec *requested = NULL,
+            struct timespec *elapsed = NULL);
+
+    // Release (some of) the frames last obtained.
+    // On entry, buffer->mFrameCount should have the number of frames to release,
+    // which must (cumulatively) be <= the number of frames last obtained but not yet released.
+    // buffer->mRaw is ignored, but is normally same pointer returned by last obtainBuffer().
+    // It is permitted to call releaseBuffer() multiple times to release the frames in chunks.
+    // On exit:
+    //  buffer->mFrameCount is zero.
+    //  buffer->mRaw is NULL.
+    void        releaseBuffer(Buffer* buffer);
+
+    // Call after detecting server's death
+    void        binderDied();
+
+    // Call to force an obtainBuffer() to return quickly with -EINTR
+    void        interrupt();
+
+    size_t      getPosition() {
+        return mEpoch + mCblk->server;
+    }
+
+    void        setEpoch(size_t epoch) {
+        mEpoch = epoch;
+    }
+
+    void        setMinimum(size_t minimum) {
+        mCblk->mMinimum = minimum;
+    }
+
+    // Return the number of frames that would need to be obtained and released
+    // in order for the client to be aligned at start of buffer
+    virtual size_t  getMisalignment();
+
+    size_t      getEpoch() const {
+        return mEpoch;
+    }
+
+private:
+    size_t      mEpoch;
 };
 
 // ----------------------------------------------------------------------------
@@ -187,8 +257,10 @@ protected:
 // Proxy used by AudioTrack client, which also includes AudioFlinger::PlaybackThread::OutputTrack
 class AudioTrackClientProxy : public ClientProxy {
 public:
-    AudioTrackClientProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount, size_t frameSize)
-        : ClientProxy(cblk, buffers, frameCount, frameSize) { }
+    AudioTrackClientProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount,
+            size_t frameSize, bool clientInServer = false)
+        : ClientProxy(cblk, buffers, frameCount, frameSize, true /*isOut*/,
+          clientInServer) { }
     virtual ~AudioTrackClientProxy() { }
 
     // No barriers on the following operations, so the ordering of loads/stores
@@ -208,27 +280,36 @@ public:
         mCblk->mSampleRate = sampleRate;
     }
 
-    // called by:
-    //   PlaybackThread::OutputTrack::write
-    //   AudioTrack::createTrack_l
-    //   AudioTrack::releaseBuffer
-    //   AudioTrack::reload
-    //   AudioTrack::restoreTrack_l (2 places)
-    size_t      stepUser(size_t stepCount) {
-        return mCblk->stepUser(stepCount, mFrameCount, true /*isOut*/);
+    virtual void flush();
+
+    virtual uint32_t    getUnderrunFrames() const {
+        return mCblk->u.mStreaming.mUnderrunFrames;
     }
+};
+
+class StaticAudioTrackClientProxy : public AudioTrackClientProxy {
+public:
+    StaticAudioTrackClientProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount,
+            size_t frameSize);
+    virtual ~StaticAudioTrackClientProxy() { }
+
+    virtual void    flush();
+
+#define MIN_LOOP    16  // minimum length of each loop iteration in frames
+            void    setLoop(size_t loopStart, size_t loopEnd, int loopCount);
+            size_t  getBufferPosition();
 
-    // called by AudioTrack::obtainBuffer and AudioTrack::processBuffer
-    size_t      framesAvailable() {
-        return mCblk->framesAvailable(mFrameCount, true /*isOut*/);
+    virtual size_t  getMisalignment() {
+        return 0;
     }
 
-    // called by AudioTrack::obtainBuffer and PlaybackThread::OutputTrack::obtainBuffer
-    // FIXME remove this API since it assumes a lock that should be invisible to caller
-    size_t      framesAvailable_l() {
-        return mCblk->framesAvailable_l(mFrameCount, true /*isOut*/);
+    virtual uint32_t    getUnderrunFrames() const {
+        return 0;
     }
 
+private:
+    StaticAudioTrackSingleStateQueue::Mutator   mMutator;
+    size_t          mBufferPosition;    // so that getBufferPosition() appears to be synchronous
 };
 
 // ----------------------------------------------------------------------------
@@ -236,60 +317,122 @@ public:
 // Proxy used by AudioRecord client
 class AudioRecordClientProxy : public ClientProxy {
 public:
-    AudioRecordClientProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount, size_t frameSize)
-        : ClientProxy(cblk, buffers, frameCount, frameSize) { }
+    AudioRecordClientProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount,
+            size_t frameSize)
+        : ClientProxy(cblk, buffers, frameCount, frameSize,
+            false /*isOut*/, false /*clientInServer*/) { }
     ~AudioRecordClientProxy() { }
-
-    // called by AudioRecord::releaseBuffer
-    size_t      stepUser(size_t stepCount) {
-        return mCblk->stepUser(stepCount, mFrameCount, false /*isOut*/);
-    }
-
-    // called by AudioRecord::processBuffer
-    size_t      framesAvailable() {
-        return mCblk->framesAvailable(mFrameCount, false /*isOut*/);
-    }
-
-    // called by AudioRecord::obtainBuffer
-    size_t      framesReady() {
-        return mCblk->framesReady(false /*isOut*/);
-    }
-
 };
 
 // ----------------------------------------------------------------------------
 
 // Proxy used by AudioFlinger server
 class ServerProxy : public Proxy {
+protected:
+    ServerProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount, size_t frameSize,
+            bool isOut, bool clientInServer);
 public:
-    ServerProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount, size_t frameSize, bool isOut)
-        : Proxy(cblk, buffers, frameCount, frameSize), mIsOut(isOut) { }
     virtual ~ServerProxy() { }
 
-    // for AudioTrack and AudioRecord
-    bool        step(size_t stepCount) { return mCblk->stepServer(stepCount, mFrameCount, mIsOut); }
+    // Obtain a buffer with filled frames (writing) or empty frames (reading).
+    // It is permitted to call obtainBuffer() multiple times in succession, without any intervening
+    // calls to releaseBuffer().  In that case, the final obtainBuffer() is the one that effectively
+    // sets or extends the unreleased frame count.
+    // Always non-blocking.
+    // On entry:
+    //  buffer->mFrameCount should be initialized to maximum number of desired frames,
+    //      which must be > 0.
+    //  buffer->mNonContig is unused.
+    //  buffer->mRaw is unused.
+    // On exit:
+    //  buffer->mFrameCount has the actual number of contiguous available frames,
+    //      which is always 0 when the return status != NO_ERROR.
+    //  buffer->mNonContig is the number of additional non-contiguous available frames.
+    //  buffer->mRaw is a pointer to the first available frame,
+    //      or NULL when buffer->mFrameCount == 0.
+    // The return status is one of:
+    //  NO_ERROR    Success, buffer->mFrameCount > 0.
+    //  WOULD_BLOCK No frames are available.
+    //  NO_INIT     Shared memory is corrupt.
+    virtual status_t    obtainBuffer(Buffer* buffer);
+
+    // Release (some of) the frames last obtained.
+    // On entry, buffer->mFrameCount should have the number of frames to release,
+    // which must (cumulatively) be <= the number of frames last obtained but not yet released.
+    // It is permitted to call releaseBuffer() multiple times to release the frames in chunks.
+    // buffer->mRaw is ignored, but is normally same pointer returned by last obtainBuffer().
+    // On exit:
+    //  buffer->mFrameCount is zero.
+    //  buffer->mRaw is NULL.
+    virtual void        releaseBuffer(Buffer* buffer);
 
+protected:
+    size_t      mUnreleased;    // unreleased frames remaining from most recent obtainBuffer()
+    size_t      mAvailToClient; // estimated frames available to client prior to releaseBuffer()
+private:
+    int32_t     mFlush;         // our copy of cblk->u.mStreaming.mFlush, for streaming output only
+    bool        mDeferWake;     // whether another releaseBuffer() is expected soon
+};
+
+// Proxy used by AudioFlinger for servicing AudioTrack
+class AudioTrackServerProxy : public ServerProxy {
+public:
+    AudioTrackServerProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount,
+            size_t frameSize, bool clientInServer = false)
+        : ServerProxy(cblk, buffers, frameCount, frameSize, true /*isOut*/, clientInServer) { }
+protected:
+    virtual ~AudioTrackServerProxy() { }
+
+public:
     // return value of these methods must be validated by the caller
     uint32_t    getSampleRate() const { return mCblk->mSampleRate; }
     uint16_t    getSendLevel_U4_12() const { return mCblk->mSendLevel; }
     uint32_t    getVolumeLR() const { return mCblk->mVolumeLR; }
 
-    // for AudioTrack only
-    size_t      framesReady() {
-        ALOG_ASSERT(mIsOut);
-        return mCblk->framesReady(true);
-    }
+    // estimated total number of filled frames available to server to read,
+    // which may include non-contiguous frames
+    virtual size_t      framesReady();
+
+    // Currently AudioFlinger will call framesReady() for a fast track from two threads:
+    // FastMixer thread, and normal mixer thread.  This is dangerous, as the proxy is intended
+    // to be called from at most one thread of server, and one thread of client.
+    // As a temporary workaround, this method informs the proxy implementation that it
+    // should avoid doing a state queue poll from within framesReady().
+    // FIXME Change AudioFlinger to not call framesReady() from normal mixer thread.
+    virtual void        framesReadyIsCalledByMultipleThreads() { }
+};
 
-    // for AudioRecord only, called by RecordThread::RecordTrack::getNextBuffer
-    // FIXME remove this API since it assumes a lock that should be invisible to caller
-    size_t      framesAvailableIn_l() {
-        ALOG_ASSERT(!mIsOut);
-        return mCblk->framesAvailable_l(mFrameCount, false);
-    }
+class StaticAudioTrackServerProxy : public AudioTrackServerProxy {
+public:
+    StaticAudioTrackServerProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount,
+            size_t frameSize);
+protected:
+    virtual ~StaticAudioTrackServerProxy() { }
+
+public:
+    virtual size_t      framesReady();
+    virtual void        framesReadyIsCalledByMultipleThreads();
+    virtual status_t    obtainBuffer(Buffer* buffer);
+    virtual void        releaseBuffer(Buffer* buffer);
 
 private:
-    const bool  mIsOut;     // true for AudioTrack, false for AudioRecord
+    ssize_t             pollPosition(); // poll for state queue update, and return current position
+    StaticAudioTrackSingleStateQueue::Observer  mObserver;
+    size_t              mPosition;  // server's current play position in frames, relative to 0
+    size_t              mEnd;       // cached value computed from mState, safe for asynchronous read
+    bool                mFramesReadyIsCalledByMultipleThreads;
+    StaticAudioTrackState   mState;
+};
 
+// Proxy used by AudioFlinger for servicing AudioRecord
+class AudioRecordServerProxy : public ServerProxy {
+public:
+    AudioRecordServerProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount,
+            size_t frameSize)
+        : ServerProxy(cblk, buffers, frameCount, frameSize, false /*isOut*/,
+            false /*clientInServer*/) { }
+protected:
+    virtual ~AudioRecordServerProxy() { }
 };
 
 // ----------------------------------------------------------------------------
index a2b8ae2..9faa497 100644 (file)
 #define LOG_TAG "AudioRecord"
 
 #include <sys/resource.h>
-#include <sys/types.h>
-
 #include <binder/IPCThreadState.h>
-#include <cutils/atomic.h>
-#include <cutils/compiler.h>
 #include <media/AudioRecord.h>
-#include <media/AudioSystem.h>
-#include <system/audio.h>
 #include <utils/Log.h>
-
 #include <private/media/AudioTrackShared.h>
 
+#define WAIT_PERIOD_MS          10
+
 namespace android {
 // ---------------------------------------------------------------------------
 
@@ -41,7 +36,9 @@ status_t AudioRecord::getMinFrameCount(
         audio_format_t format,
         audio_channel_mask_t channelMask)
 {
-    if (frameCount == NULL) return BAD_VALUE;
+    if (frameCount == NULL) {
+        return BAD_VALUE;
+    }
 
     // default to 0 in case of error
     *frameCount = 0;
@@ -75,8 +72,7 @@ status_t AudioRecord::getMinFrameCount(
 
 AudioRecord::AudioRecord()
     : mStatus(NO_INIT), mSessionId(0),
-      mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT),
-      mProxy(NULL)
+      mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(SP_DEFAULT)
 {
 }
 
@@ -89,14 +85,15 @@ AudioRecord::AudioRecord(
         callback_t cbf,
         void* user,
         int notificationFrames,
-        int sessionId)
+        int sessionId,
+        transfer_type transferType)
     : mStatus(NO_INIT), mSessionId(0),
       mPreviousPriority(ANDROID_PRIORITY_NORMAL),
       mPreviousSchedulingGroup(SP_DEFAULT),
       mProxy(NULL)
 {
-    mStatus = set(inputSource, sampleRate, format, channelMask,
-            frameCount, cbf, user, notificationFrames, false /*threadCanCallJava*/, sessionId);
+    mStatus = set(inputSource, sampleRate, format, channelMask, frameCount, cbf, user,
+            notificationFrames, false /*threadCanCallJava*/, sessionId, transferType);
 }
 
 AudioRecord::~AudioRecord()
@@ -111,11 +108,13 @@ AudioRecord::~AudioRecord()
             mAudioRecordThread->requestExitAndWait();
             mAudioRecordThread.clear();
         }
-        mAudioRecord.clear();
+        if (mAudioRecord != 0) {
+            mAudioRecord->asBinder()->unlinkToDeath(mDeathNotifier, this);
+            mAudioRecord.clear();
+        }
         IPCThreadState::self()->flushCommands();
         AudioSystem::releaseAudioSessionId(mSessionId);
     }
-    delete mProxy;
 }
 
 status_t AudioRecord::set(
@@ -128,8 +127,32 @@ status_t AudioRecord::set(
         void* user,
         int notificationFrames,
         bool threadCanCallJava,
-        int sessionId)
+        int sessionId,
+        transfer_type transferType)
 {
+    switch (transferType) {
+    case TRANSFER_DEFAULT:
+        if (cbf == NULL || threadCanCallJava) {
+            transferType = TRANSFER_SYNC;
+        } else {
+            transferType = TRANSFER_CALLBACK;
+        }
+        break;
+    case TRANSFER_CALLBACK:
+        if (cbf == NULL) {
+            ALOGE("Transfer type TRANSFER_CALLBACK but cbf == NULL");
+            return BAD_VALUE;
+        }
+        break;
+    case TRANSFER_OBTAIN:
+    case TRANSFER_SYNC:
+        break;
+    default:
+        ALOGE("Invalid transfer type %d", transferType);
+        return BAD_VALUE;
+    }
+    mTransfer = transferType;
+
     // FIXME "int" here is legacy and will be replaced by size_t later
     if (frameCountInt < 0) {
         ALOGE("Invalid frame count %d", frameCountInt);
@@ -143,6 +166,7 @@ status_t AudioRecord::set(
     AutoMutex lock(mLock);
 
     if (mAudioRecord != 0) {
+        ALOGE("Track already in use");
         return INVALID_OPERATION;
     }
 
@@ -159,14 +183,16 @@ status_t AudioRecord::set(
     if (format == AUDIO_FORMAT_DEFAULT) {
         format = AUDIO_FORMAT_PCM_16_BIT;
     }
+
     // validate parameters
     if (!audio_is_valid_format(format)) {
-        ALOGE("Invalid format");
+        ALOGE("Invalid format %d", format);
         return BAD_VALUE;
     }
     mFormat = format;
 
     if (!audio_is_input_channel(channelMask)) {
+        ALOGE("Invalid channel mask %#x", channelMask);
         return BAD_VALUE;
     }
     mChannelMask = channelMask;
@@ -200,6 +226,7 @@ status_t AudioRecord::set(
     size_t minFrameCount = 0;
     status_t status = getMinFrameCount(&minFrameCount, sampleRate, format, channelMask);
     if (status != NO_ERROR) {
+        ALOGE("getMinFrameCount() failed; status %d", status);
         return status;
     }
     ALOGV("AudioRecord::set() minFrameCount = %d", minFrameCount);
@@ -207,6 +234,7 @@ status_t AudioRecord::set(
     if (frameCount == 0) {
         frameCount = minFrameCount;
     } else if (frameCount < minFrameCount) {
+        ALOGE("frameCount %u < minFrameCount %u", frameCount, minFrameCount);
         return BAD_VALUE;
     }
 
@@ -215,7 +243,7 @@ status_t AudioRecord::set(
     }
 
     // create the IAudioRecord
-    status = openRecord_l(sampleRate, format, frameCount, input);
+    status = openRecord_l(sampleRate, format, frameCount, input, 0 /*epoch*/);
     if (status != NO_ERROR) {
         return status;
     }
@@ -233,7 +261,7 @@ status_t AudioRecord::set(
     mActive = false;
     mCbf = cbf;
     mNotificationFrames = notificationFrames;
-    mRemainingFrames = notificationFrames;
+    mRefreshRemaining = true;
     mUserData = user;
     // TODO: add audio hardware input latency here
     mLatency = (1000*mFrameCount) / sampleRate;
@@ -244,117 +272,78 @@ status_t AudioRecord::set(
     mInputSource = inputSource;
     mInput = input;
     AudioSystem::acquireAudioSessionId(mSessionId);
+    mSequence = 1;
+    mObservedSequence = mSequence;
+    mInOverrun = false;
 
     return NO_ERROR;
 }
 
-status_t AudioRecord::initCheck() const
-{
-    return mStatus;
-}
-
-// -------------------------------------------------------------------------
-
-uint32_t AudioRecord::latency() const
-{
-    return mLatency;
-}
-
-audio_format_t AudioRecord::format() const
-{
-    return mFormat;
-}
-
-uint32_t AudioRecord::channelCount() const
-{
-    return mChannelCount;
-}
-
-size_t AudioRecord::frameCount() const
-{
-    return mFrameCount;
-}
-
-audio_source_t AudioRecord::inputSource() const
-{
-    return mInputSource;
-}
-
 // -------------------------------------------------------------------------
 
 status_t AudioRecord::start(AudioSystem::sync_event_t event, int triggerSession)
 {
-    status_t ret = NO_ERROR;
-    sp<AudioRecordThread> t = mAudioRecordThread;
-
     ALOGV("start, sync event %d trigger session %d", event, triggerSession);
 
     AutoMutex lock(mLock);
-    // acquire a strong reference on the IAudioRecord and IMemory so that they cannot be destroyed
-    // while we are accessing the cblk
-    sp<IAudioRecord> audioRecord = mAudioRecord;
-    sp<IMemory> iMem = mCblkMemory;
-    audio_track_cblk_t* cblk = mCblk;
+    if (mActive) {
+        return NO_ERROR;
+    }
 
-    if (!mActive) {
-        mActive = true;
+    // reset current position as seen by client to 0
+    mProxy->setEpoch(mProxy->getEpoch() - mProxy->getPosition());
 
-        cblk->lock.lock();
-        if (!(cblk->flags & CBLK_INVALID)) {
-            cblk->lock.unlock();
-            ALOGV("mAudioRecord->start()");
-            ret = mAudioRecord->start(event, triggerSession);
-            cblk->lock.lock();
-            if (ret == DEAD_OBJECT) {
-                android_atomic_or(CBLK_INVALID, &cblk->flags);
-            }
-        }
-        if (cblk->flags & CBLK_INVALID) {
-            audio_track_cblk_t* temp = cblk;
-            ret = restoreRecord_l(temp);
-            cblk = temp;
+    mNewPosition = mProxy->getPosition() + mUpdatePeriod;
+    int32_t flags = android_atomic_acquire_load(&mCblk->flags);
+
+    status_t status = NO_ERROR;
+    if (!(flags & CBLK_INVALID)) {
+        ALOGV("mAudioRecord->start()");
+        status = mAudioRecord->start(event, triggerSession);
+        if (status == DEAD_OBJECT) {
+            flags |= CBLK_INVALID;
         }
-        cblk->lock.unlock();
-        if (ret == NO_ERROR) {
-            mNewPosition = cblk->user + mUpdatePeriod;
-            cblk->bufferTimeoutMs = (event == AudioSystem::SYNC_EVENT_NONE) ? MAX_RUN_TIMEOUT_MS :
-                                            AudioSystem::kSyncRecordStartTimeOutMs;
-            cblk->waitTimeMs = 0;
-            if (t != 0) {
-                t->resume();
-            } else {
-                mPreviousPriority = getpriority(PRIO_PROCESS, 0);
-                get_sched_policy(0, &mPreviousSchedulingGroup);
-                androidSetThreadPriority(0, ANDROID_PRIORITY_AUDIO);
-            }
+    }
+    if (flags & CBLK_INVALID) {
+        status = restoreRecord_l("start");
+    }
+
+    if (status != NO_ERROR) {
+        ALOGE("start() status %d", status);
+    } else {
+        mActive = true;
+        sp<AudioRecordThread> t = mAudioRecordThread;
+        if (t != 0) {
+            t->resume();
         } else {
-            mActive = false;
+            mPreviousPriority = getpriority(PRIO_PROCESS, 0);
+            get_sched_policy(0, &mPreviousSchedulingGroup);
+            androidSetThreadPriority(0, ANDROID_PRIORITY_AUDIO);
         }
     }
 
-    return ret;
+    return status;
 }
 
 void AudioRecord::stop()
 {
-    sp<AudioRecordThread> t = mAudioRecordThread;
-
-    ALOGV("stop");
-
     AutoMutex lock(mLock);
-    if (mActive) {
-        mActive = false;
-        mCblk->cv.signal();
-        mAudioRecord->stop();
-        // the record head position will reset to 0, so if a marker is set, we need
-        // to activate it again
-        mMarkerReached = false;
-        if (t != 0) {
-            t->pause();
-        } else {
-            setpriority(PRIO_PROCESS, 0, mPreviousPriority);
-            set_sched_policy(0, mPreviousSchedulingGroup);
-        }
+    if (!mActive) {
+        return;
+    }
+
+    mActive = false;
+    mProxy->interrupt();
+    mAudioRecord->stop();
+    // the record head position will reset to 0, so if a marker is set, we need
+    // to activate it again
+    mMarkerReached = false;
+    sp<AudioRecordThread> t = mAudioRecordThread;
+    if (t != 0) {
+        t->pause();
+    } else {
+        setpriority(PRIO_PROCESS, 0, mPreviousPriority);
+        set_sched_policy(0, mPreviousSchedulingGroup);
     }
 }
 
@@ -364,14 +353,11 @@ bool AudioRecord::stopped() const
     return !mActive;
 }
 
-uint32_t AudioRecord::getSampleRate() const
-{
-    return mSampleRate;
-}
-
 status_t AudioRecord::setMarkerPosition(uint32_t marker)
 {
-    if (mCbf == NULL) return INVALID_OPERATION;
+    if (mCbf == NULL) {
+        return INVALID_OPERATION;
+    }
 
     AutoMutex lock(mLock);
     mMarkerPosition = marker;
@@ -382,7 +368,9 @@ status_t AudioRecord::setMarkerPosition(uint32_t marker)
 
 status_t AudioRecord::getMarkerPosition(uint32_t *marker) const
 {
-    if (marker == NULL) return BAD_VALUE;
+    if (marker == NULL) {
+        return BAD_VALUE;
+    }
 
     AutoMutex lock(mLock);
     *marker = mMarkerPosition;
@@ -392,13 +380,12 @@ status_t AudioRecord::getMarkerPosition(uint32_t *marker) const
 
 status_t AudioRecord::setPositionUpdatePeriod(uint32_t updatePeriod)
 {
-    if (mCbf == NULL) return INVALID_OPERATION;
-
-    uint32_t curPosition;
-    getPosition(&curPosition);
+    if (mCbf == NULL) {
+        return INVALID_OPERATION;
+    }
 
     AutoMutex lock(mLock);
-    mNewPosition = curPosition + updatePeriod;
+    mNewPosition = mProxy->getPosition() + updatePeriod;
     mUpdatePeriod = updatePeriod;
 
     return NO_ERROR;
@@ -406,7 +393,9 @@ status_t AudioRecord::setPositionUpdatePeriod(uint32_t updatePeriod)
 
 status_t AudioRecord::getPositionUpdatePeriod(uint32_t *updatePeriod) const
 {
-    if (updatePeriod == NULL) return BAD_VALUE;
+    if (updatePeriod == NULL) {
+        return BAD_VALUE;
+    }
 
     AutoMutex lock(mLock);
     *updatePeriod = mUpdatePeriod;
@@ -416,10 +405,12 @@ status_t AudioRecord::getPositionUpdatePeriod(uint32_t *updatePeriod) const
 
 status_t AudioRecord::getPosition(uint32_t *position) const
 {
-    if (position == NULL) return BAD_VALUE;
+    if (position == NULL) {
+        return BAD_VALUE;
+    }
 
     AutoMutex lock(mLock);
-    *position = mCblk->user;
+    *position = mProxy->getPosition();
 
     return NO_ERROR;
 }
@@ -427,7 +418,7 @@ status_t AudioRecord::getPosition(uint32_t *position) const
 unsigned int AudioRecord::getInputFramesLost() const
 {
     // no need to check mActive, because if inactive this will return 0, which is what we want
-    return AudioSystem::getInputFramesLost(mInput);
+    return AudioSystem::getInputFramesLost(getInput());
 }
 
 // -------------------------------------------------------------------------
@@ -437,7 +428,8 @@ status_t AudioRecord::openRecord_l(
         uint32_t sampleRate,
         audio_format_t format,
         size_t frameCount,
-        audio_io_handle_t input)
+        audio_io_handle_t input,
+        size_t epoch)
 {
     status_t status;
     const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger();
@@ -447,7 +439,7 @@ status_t AudioRecord::openRecord_l(
     }
 
     pid_t tid = -1;
-    // FIXME see similar logic at AudioTrack
+    // FIXME see similar logic at AudioTrack for tid
 
     int originalSessionId = mSessionId;
     sp<IAudioRecord> record = audioFlinger->openRecord(input,
@@ -470,133 +462,138 @@ status_t AudioRecord::openRecord_l(
         ALOGE("Could not get control block");
         return NO_INIT;
     }
-    mAudioRecord.clear();
+    if (mAudioRecord != 0) {
+        mAudioRecord->asBinder()->unlinkToDeath(mDeathNotifier, this);
+        mDeathNotifier.clear();
+    }
     mAudioRecord = record;
-    mCblkMemory.clear();
     mCblkMemory = iMem;
     audio_track_cblk_t* cblk = static_cast<audio_track_cblk_t*>(iMem->pointer());
     mCblk = cblk;
-    mBuffers = (char*)cblk + sizeof(audio_track_cblk_t);
-    cblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS;
-    cblk->waitTimeMs = 0;
+
+    // starting address of buffers in shared memory
+    void *buffers = (char*)cblk + sizeof(audio_track_cblk_t);
 
     // update proxy
-    delete mProxy;
-    mProxy = new AudioRecordClientProxy(cblk, mBuffers, frameCount, mFrameSize);
+    mProxy = new AudioRecordClientProxy(cblk, buffers, frameCount, mFrameSize);
+    mProxy->setEpoch(epoch);
+    mProxy->setMinimum(mNotificationFrames);
+
+    mDeathNotifier = new DeathNotifier(this);
+    mAudioRecord->asBinder()->linkToDeath(mDeathNotifier, this);
 
     return NO_ERROR;
 }
 
 status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
 {
-    ALOG_ASSERT(mStatus == NO_ERROR && mProxy != NULL);
+    if (audioBuffer == NULL) {
+        return BAD_VALUE;
+    }
+    if (mTransfer != TRANSFER_OBTAIN) {
+        audioBuffer->frameCount = 0;
+        audioBuffer->size = 0;
+        audioBuffer->raw = NULL;
+        return INVALID_OPERATION;
+    }
 
-    AutoMutex lock(mLock);
-    bool active;
-    status_t result = NO_ERROR;
-    audio_track_cblk_t* cblk = mCblk;
-    uint32_t framesReq = audioBuffer->frameCount;
-    uint32_t waitTimeMs = (waitCount < 0) ? cblk->bufferTimeoutMs : WAIT_PERIOD_MS;
-
-    audioBuffer->frameCount  = 0;
-    audioBuffer->size        = 0;
-
-    size_t framesReady = mProxy->framesReady();
-
-    if (framesReady == 0) {
-        cblk->lock.lock();
-        goto start_loop_here;
-        while (framesReady == 0) {
-            active = mActive;
-            if (CC_UNLIKELY(!active)) {
-                cblk->lock.unlock();
-                return NO_MORE_BUFFERS;
-            }
-            if (CC_UNLIKELY(!waitCount)) {
-                cblk->lock.unlock();
-                return WOULD_BLOCK;
-            }
-            if (!(cblk->flags & CBLK_INVALID)) {
-                mLock.unlock();
-                // this condition is in shared memory, so if IAudioRecord and control block
-                // are replaced due to mediaserver death or IAudioRecord invalidation then
-                // cv won't be signalled, but fortunately the timeout will limit the wait
-                result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs));
-                cblk->lock.unlock();
-                mLock.lock();
-                if (!mActive) {
-                    return status_t(STOPPED);
-                }
-                // IAudioRecord may have been re-created while mLock was unlocked
-                cblk = mCblk;
-                cblk->lock.lock();
-            }
-            if (cblk->flags & CBLK_INVALID) {
-                goto create_new_record;
-            }
-            if (CC_UNLIKELY(result != NO_ERROR)) {
-                cblk->waitTimeMs += waitTimeMs;
-                if (cblk->waitTimeMs >= cblk->bufferTimeoutMs) {
-                    ALOGW(   "obtainBuffer timed out (is the CPU pegged?) "
-                            "user=%08x, server=%08x", cblk->user, cblk->server);
-                    cblk->lock.unlock();
-                    // callback thread or sync event hasn't changed
-                    result = mAudioRecord->start(AudioSystem::SYNC_EVENT_SAME, 0);
-                    cblk->lock.lock();
-                    if (result == DEAD_OBJECT) {
-                        android_atomic_or(CBLK_INVALID, &cblk->flags);
-create_new_record:
-                        audio_track_cblk_t* temp = cblk;
-                        result = AudioRecord::restoreRecord_l(temp);
-                        cblk = temp;
-                    }
-                    if (result != NO_ERROR) {
-                        ALOGW("obtainBuffer create Track error %d", result);
-                        cblk->lock.unlock();
-                        return result;
+    const struct timespec *requested;
+    if (waitCount == -1) {
+        requested = &ClientProxy::kForever;
+    } else if (waitCount == 0) {
+        requested = &ClientProxy::kNonBlocking;
+    } else if (waitCount > 0) {
+        long long ms = WAIT_PERIOD_MS * (long long) waitCount;
+        struct timespec timeout;
+        timeout.tv_sec = ms / 1000;
+        timeout.tv_nsec = (int) (ms % 1000) * 1000000;
+        requested = &timeout;
+    } else {
+        ALOGE("%s invalid waitCount %d", __func__, waitCount);
+        requested = NULL;
+    }
+    return obtainBuffer(audioBuffer, requested);
+}
+
+status_t AudioRecord::obtainBuffer(Buffer* audioBuffer, const struct timespec *requested,
+        struct timespec *elapsed, size_t *nonContig)
+{
+    // previous and new IAudioRecord sequence numbers are used to detect track re-creation
+    uint32_t oldSequence = 0;
+    uint32_t newSequence;
+
+    Proxy::Buffer buffer;
+    status_t status = NO_ERROR;
+
+    static const int32_t kMaxTries = 5;
+    int32_t tryCounter = kMaxTries;
+
+    do {
+        // obtainBuffer() is called with mutex unlocked, so keep extra references to these fields to
+        // keep them from going away if another thread re-creates the track during obtainBuffer()
+        sp<AudioRecordClientProxy> proxy;
+        sp<IMemory> iMem;
+        {
+            // start of lock scope
+            AutoMutex lock(mLock);
+
+            newSequence = mSequence;
+            // did previous obtainBuffer() fail due to media server death or voluntary invalidation?
+            if (status == DEAD_OBJECT) {
+                // re-create track, unless someone else has already done so
+                if (newSequence == oldSequence) {
+                    status = restoreRecord_l("obtainBuffer");
+                    if (status != NO_ERROR) {
+                        break;
                     }
-                    cblk->waitTimeMs = 0;
-                }
-                if (--waitCount == 0) {
-                    cblk->lock.unlock();
-                    return TIMED_OUT;
                 }
             }
-            // read the server count again
-start_loop_here:
-            framesReady = mProxy->framesReady();
-        }
-        cblk->lock.unlock();
-    }
+            oldSequence = newSequence;
 
-    cblk->waitTimeMs = 0;
-    // reset time out to running value after obtaining a buffer
-    cblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS;
+            // Keep the extra references
+            proxy = mProxy;
+            iMem = mCblkMemory;
 
-    if (framesReq > framesReady) {
-        framesReq = framesReady;
-    }
+            // Non-blocking if track is stopped
+            if (!mActive) {
+                requested = &ClientProxy::kNonBlocking;
+            }
 
-    uint32_t u = cblk->user;
-    uint32_t bufferEnd = cblk->userBase + mFrameCount;
+        }   // end of lock scope
 
-    if (framesReq > bufferEnd - u) {
-        framesReq = bufferEnd - u;
-    }
+        buffer.mFrameCount = audioBuffer->frameCount;
+        // FIXME starts the requested timeout and elapsed over from scratch
+        status = proxy->obtainBuffer(&buffer, requested, elapsed);
+
+    } while ((status == DEAD_OBJECT) && (tryCounter-- > 0));
 
-    audioBuffer->frameCount  = framesReq;
-    audioBuffer->size        = framesReq * mFrameSize;
-    audioBuffer->raw         = mProxy->buffer(u);
-    active = mActive;
-    return active ? status_t(NO_ERROR) : status_t(STOPPED);
+    audioBuffer->frameCount = buffer.mFrameCount;
+    audioBuffer->size = buffer.mFrameCount * mFrameSize;
+    audioBuffer->raw = buffer.mRaw;
+    if (nonContig != NULL) {
+        *nonContig = buffer.mNonContig;
+    }
+    return status;
 }
 
 void AudioRecord::releaseBuffer(Buffer* audioBuffer)
 {
-    ALOG_ASSERT(mStatus == NO_ERROR && mProxy != NULL);
+    // all TRANSFER_* are valid
+
+    size_t stepCount = audioBuffer->size / mFrameSize;
+    if (stepCount == 0) {
+        return;
+    }
+
+    Proxy::Buffer buffer;
+    buffer.mFrameCount = stepCount;
+    buffer.mRaw = audioBuffer->raw;
 
     AutoMutex lock(mLock);
-    (void) mProxy->stepUser(audioBuffer->frameCount);
+    mInOverrun = false;
+    mProxy->releaseBuffer(&buffer);
+
+    // the server does not automatically disable recorder on overrun, so no need to restart
 }
 
 audio_io_handle_t AudioRecord::getInput() const
@@ -616,215 +613,304 @@ audio_io_handle_t AudioRecord::getInput_l()
     return mInput;
 }
 
-int AudioRecord::getSessionId() const
-{
-    // no lock needed because session ID doesn't change after first set()
-    return mSessionId;
-}
-
 // -------------------------------------------------------------------------
 
 ssize_t AudioRecord::read(void* buffer, size_t userSize)
 {
-    ssize_t read = 0;
-    Buffer audioBuffer;
-    int8_t *dst = static_cast<int8_t*>(buffer);
+    if (mTransfer != TRANSFER_SYNC) {
+        return INVALID_OPERATION;
+    }
 
-    if (ssize_t(userSize) < 0) {
-        // sanity-check. user is most-likely passing an error code.
-        ALOGE("AudioRecord::read(buffer=%p, size=%u (%d)",
-                buffer, userSize, userSize);
+    if (ssize_t(userSize) < 0 || (buffer == NULL && userSize != 0)) {
+        // sanity-check. user is most-likely passing an error code, and it would
+        // make the return value ambiguous (actualSize vs error).
+        ALOGE("AudioRecord::read(buffer=%p, size=%u (%d)", buffer, userSize, userSize);
         return BAD_VALUE;
     }
 
-    mLock.lock();
-    // acquire a strong reference on the IAudioRecord and IMemory so that they cannot be destroyed
-    // while we are accessing the cblk
-    sp<IAudioRecord> audioRecord = mAudioRecord;
-    sp<IMemory> iMem = mCblkMemory;
-    mLock.unlock();
-
-    do {
+    ssize_t read = 0;
+    Buffer audioBuffer;
 
-        audioBuffer.frameCount = userSize/frameSize();
+    while (userSize >= mFrameSize) {
+        audioBuffer.frameCount = userSize / mFrameSize;
 
-        // By using a wait count corresponding to twice the timeout period in
-        // obtainBuffer() we give a chance to recover once for a read timeout
-        // (if media_server crashed for instance) before returning a length of
-        // 0 bytes read to the client
-        status_t err = obtainBuffer(&audioBuffer, ((2 * MAX_RUN_TIMEOUT_MS) / WAIT_PERIOD_MS));
+        status_t err = obtainBuffer(&audioBuffer, &ClientProxy::kForever);
         if (err < 0) {
-            // out of buffers, return #bytes written
-            if (err == status_t(NO_MORE_BUFFERS)) {
+            if (read > 0) {
                 break;
             }
-            if (err == status_t(TIMED_OUT)) {
-                // return partial transfer count
-                return read;
-            }
             return ssize_t(err);
         }
 
         size_t bytesRead = audioBuffer.size;
-        memcpy(dst, audioBuffer.i8, bytesRead);
-
-        dst += bytesRead;
+        memcpy(buffer, audioBuffer.i8, bytesRead);
+        buffer = ((char *) buffer) + bytesRead;
         userSize -= bytesRead;
         read += bytesRead;
 
         releaseBuffer(&audioBuffer);
-    } while (userSize);
+    }
 
     return read;
 }
 
 // -------------------------------------------------------------------------
 
-bool AudioRecord::processAudioBuffer(const sp<AudioRecordThread>& thread)
+nsecs_t AudioRecord::processAudioBuffer(const sp<AudioRecordThread>& thread)
 {
-    Buffer audioBuffer;
-    uint32_t frames = mRemainingFrames;
-    size_t readSize;
-
     mLock.lock();
-    // acquire a strong reference on the IAudioRecord and IMemory so that they cannot be destroyed
-    // while we are accessing the cblk
-    sp<IAudioRecord> audioRecord = mAudioRecord;
-    sp<IMemory> iMem = mCblkMemory;
-    audio_track_cblk_t* cblk = mCblk;
+
+    // Can only reference mCblk while locked
+    int32_t flags = android_atomic_and(~CBLK_OVERRUN, &mCblk->flags);
+
+    // Check for track invalidation
+    if (flags & CBLK_INVALID) {
+        (void) restoreRecord_l("processAudioBuffer");
+        mLock.unlock();
+        // Run again immediately, but with a new IAudioRecord
+        return 0;
+    }
+
     bool active = mActive;
-    uint32_t markerPosition = mMarkerPosition;
-    uint32_t newPosition = mNewPosition;
-    uint32_t user = cblk->user;
-    // determine whether a marker callback will be needed, while locked
-    bool needMarker = !mMarkerReached && (mMarkerPosition > 0) && (user >= mMarkerPosition);
-    if (needMarker) {
-        mMarkerReached = true;
-    }
-    // determine the number of new position callback(s) that will be needed, while locked
+
+    // Manage overrun callback, must be done under lock to avoid race with releaseBuffer()
+    bool newOverrun = false;
+    if (flags & CBLK_OVERRUN) {
+        if (!mInOverrun) {
+            mInOverrun = true;
+            newOverrun = true;
+        }
+    }
+
+    // Get current position of server
+    size_t position = mProxy->getPosition();
+
+    // Manage marker callback
+    bool markerReached = false;
+    size_t markerPosition = mMarkerPosition;
+    // FIXME fails for wraparound, need 64 bits
+    if (!mMarkerReached && (markerPosition > 0) && (position >= markerPosition)) {
+        mMarkerReached = markerReached = true;
+    }
+
+    // Determine the number of new position callback(s) that will be needed, while locked
+    size_t newPosCount = 0;
+    size_t newPosition = mNewPosition;
     uint32_t updatePeriod = mUpdatePeriod;
-    uint32_t needNewPos = updatePeriod > 0 && user >= newPosition ?
-            ((user - newPosition) / updatePeriod) + 1 : 0;
-    mNewPosition = newPosition + updatePeriod * needNewPos;
+    // FIXME fails for wraparound, need 64 bits
+    if (updatePeriod > 0 && position >= newPosition) {
+        newPosCount = ((position - newPosition) / updatePeriod) + 1;
+        mNewPosition += updatePeriod * newPosCount;
+    }
+
+    // Cache other fields that will be needed soon
+    size_t notificationFrames = mNotificationFrames;
+    if (mRefreshRemaining) {
+        mRefreshRemaining = false;
+        mRemainingFrames = notificationFrames;
+        mRetryOnPartialBuffer = false;
+    }
+    size_t misalignment = mProxy->getMisalignment();
+    int32_t sequence = mSequence;
+
+    // These fields don't need to be cached, because they are assigned only by set():
+    //      mTransfer, mCbf, mUserData, mSampleRate
+
     mLock.unlock();
 
-    // perform marker callback, while unlocked
-    if (needMarker) {
+    // perform callbacks while unlocked
+    if (newOverrun) {
+        mCbf(EVENT_OVERRUN, mUserData, NULL);
+    }
+    if (markerReached) {
         mCbf(EVENT_MARKER, mUserData, &markerPosition);
     }
-
-    // perform new position callback(s), while unlocked
-    for (; needNewPos > 0; --needNewPos) {
-        uint32_t temp = newPosition;
+    while (newPosCount > 0) {
+        size_t temp = newPosition;
         mCbf(EVENT_NEW_POS, mUserData, &temp);
         newPosition += updatePeriod;
+        newPosCount--;
+    }
+    if (mObservedSequence != sequence) {
+        mObservedSequence = sequence;
+        mCbf(EVENT_NEW_IAUDIORECORD, mUserData, NULL);
     }
 
-    do {
-        audioBuffer.frameCount = frames;
-        // Calling obtainBuffer() with a wait count of 1
-        // limits wait time to WAIT_PERIOD_MS. This prevents from being
-        // stuck here not being able to handle timed events (position, markers).
-        status_t err = obtainBuffer(&audioBuffer, 1);
-        if (err < NO_ERROR) {
-            if (err != TIMED_OUT) {
-                ALOGE_IF(err != status_t(NO_MORE_BUFFERS),
-                        "Error obtaining an audio buffer, giving up.");
-                return false;
+    // if inactive, then don't run me again until re-started
+    if (!active) {
+        return NS_INACTIVE;
+    }
+
+    // Compute the estimated time until the next timed event (position, markers)
+    uint32_t minFrames = ~0;
+    if (!markerReached && position < markerPosition) {
+        minFrames = markerPosition - position;
+    }
+    if (updatePeriod > 0 && updatePeriod < minFrames) {
+        minFrames = updatePeriod;
+    }
+
+    // If > 0, poll periodically to recover from a stuck server.  A good value is 2.
+    static const uint32_t kPoll = 0;
+    if (kPoll > 0 && mTransfer == TRANSFER_CALLBACK && kPoll * notificationFrames < minFrames) {
+        minFrames = kPoll * notificationFrames;
+    }
+
+    // Convert frame units to time units
+    nsecs_t ns = NS_WHENEVER;
+    if (minFrames != (uint32_t) ~0) {
+        // This "fudge factor" avoids soaking CPU, and compensates for late progress by server
+        static const nsecs_t kFudgeNs = 10000000LL; // 10 ms
+        ns = ((minFrames * 1000000000LL) / mSampleRate) + kFudgeNs;
+    }
+
+    // If not supplying data by EVENT_MORE_DATA, then we're done
+    if (mTransfer != TRANSFER_CALLBACK) {
+        return ns;
+    }
+
+    struct timespec timeout;
+    const struct timespec *requested = &ClientProxy::kForever;
+    if (ns != NS_WHENEVER) {
+        timeout.tv_sec = ns / 1000000000LL;
+        timeout.tv_nsec = ns % 1000000000LL;
+        ALOGV("timeout %ld.%03d", timeout.tv_sec, (int) timeout.tv_nsec / 1000000);
+        requested = &timeout;
+    }
+
+    while (mRemainingFrames > 0) {
+
+        Buffer audioBuffer;
+        audioBuffer.frameCount = mRemainingFrames;
+        size_t nonContig;
+        status_t err = obtainBuffer(&audioBuffer, requested, NULL, &nonContig);
+        LOG_ALWAYS_FATAL_IF((err != NO_ERROR) != (audioBuffer.frameCount == 0),
+                "obtainBuffer() err=%d frameCount=%u", err, audioBuffer.frameCount);
+        requested = &ClientProxy::kNonBlocking;
+        size_t avail = audioBuffer.frameCount + nonContig;
+        ALOGV("obtainBuffer(%u) returned %u = %u + %u",
+                mRemainingFrames, avail, audioBuffer.frameCount, nonContig);
+        if (err != NO_ERROR) {
+            if (err == TIMED_OUT || err == WOULD_BLOCK || err == -EINTR) {
+                break;
+            }
+            ALOGE("Error %d obtaining an audio buffer, giving up.", err);
+            return NS_NEVER;
+        }
+
+        if (mRetryOnPartialBuffer) {
+            mRetryOnPartialBuffer = false;
+            if (avail < mRemainingFrames) {
+                int64_t myns = ((mRemainingFrames - avail) *
+                        1100000000LL) / mSampleRate;
+                if (ns < 0 || myns < ns) {
+                    ns = myns;
+                }
+                return ns;
             }
-            break;
         }
-        if (err == status_t(STOPPED)) return false;
 
         size_t reqSize = audioBuffer.size;
         mCbf(EVENT_MORE_DATA, mUserData, &audioBuffer);
-        readSize = audioBuffer.size;
+        size_t readSize = audioBuffer.size;
 
         // Sanity check on returned size
-        if (ssize_t(readSize) <= 0) {
-            // The callback is done filling buffers
+        if (ssize_t(readSize) < 0 || readSize > reqSize) {
+            ALOGE("EVENT_MORE_DATA requested %u bytes but callback returned %d bytes",
+                    reqSize, (int) readSize);
+            return NS_NEVER;
+        }
+
+        if (readSize == 0) {
+            // The callback is done consuming buffers
             // Keep this thread going to handle timed events and
-            // still try to get more data in intervals of WAIT_PERIOD_MS
+            // still try to provide more data in intervals of WAIT_PERIOD_MS
             // but don't just loop and block the CPU, so wait
-            usleep(WAIT_PERIOD_MS*1000);
-            break;
+            return WAIT_PERIOD_MS * 1000000LL;
         }
-        if (readSize > reqSize) readSize = reqSize;
 
-        audioBuffer.size = readSize;
-        audioBuffer.frameCount = readSize/frameSize();
-        frames -= audioBuffer.frameCount;
+        size_t releasedFrames = readSize / mFrameSize;
+        audioBuffer.frameCount = releasedFrames;
+        mRemainingFrames -= releasedFrames;
+        if (misalignment >= releasedFrames) {
+            misalignment -= releasedFrames;
+        } else {
+            misalignment = 0;
+        }
 
         releaseBuffer(&audioBuffer);
 
-    } while (frames);
+        // FIXME here is where we would repeat EVENT_MORE_DATA again on same advanced buffer
+        // if callback doesn't like to accept the full chunk
+        if (readSize < reqSize) {
+            continue;
+        }
 
+        // There could be enough non-contiguous frames available to satisfy the remaining request
+        if (mRemainingFrames <= nonContig) {
+            continue;
+        }
 
-    // Manage overrun callback
-    if (active && (mProxy->framesAvailable() == 0)) {
-        // The value of active is stale, but we are almost sure to be active here because
-        // otherwise we would have exited when obtainBuffer returned STOPPED earlier.
-        ALOGV("Overrun user: %x, server: %x, flags %04x", cblk->user, cblk->server, cblk->flags);
-        if (!(android_atomic_or(CBLK_UNDERRUN, &cblk->flags) & CBLK_UNDERRUN)) {
-            mCbf(EVENT_OVERRUN, mUserData, NULL);
+#if 0
+        // This heuristic tries to collapse a series of EVENT_MORE_DATA that would total to a
+        // sum <= notificationFrames.  It replaces that series by at most two EVENT_MORE_DATA
+        // that total to a sum == notificationFrames.
+        if (0 < misalignment && misalignment <= mRemainingFrames) {
+            mRemainingFrames = misalignment;
+            return (mRemainingFrames * 1100000000LL) / mSampleRate;
         }
-    }
+#endif
 
-    if (frames == 0) {
-        mRemainingFrames = mNotificationFrames;
-    } else {
-        mRemainingFrames = frames;
     }
-    return true;
+    mRemainingFrames = notificationFrames;
+    mRetryOnPartialBuffer = true;
+
+    // A lot has transpired since ns was calculated, so run again immediately and re-calculate
+    return 0;
 }
 
-// must be called with mLock and cblk.lock held. Callers must also hold strong references on
-// the IAudioRecord and IMemory in case they are recreated here.
-// If the IAudioRecord is successfully restored, the cblk pointer is updated
-status_t AudioRecord::restoreRecord_l(audio_track_cblk_t*& refCblk)
+status_t AudioRecord::restoreRecord_l(const char *from)
 {
+    ALOGW("dead IAudioRecord, creating a new one from %s()", from);
+    ++mSequence;
     status_t result;
 
-    audio_track_cblk_t* cblk = refCblk;
-    audio_track_cblk_t* newCblk = cblk;
-    ALOGW("dead IAudioRecord, creating a new one");
-
-    // signal old cblk condition so that other threads waiting for available buffers stop
-    // waiting now
-    cblk->cv.broadcast();
-    cblk->lock.unlock();
-
     // if the new IAudioRecord is created, openRecord_l() will modify the
     // following member variables: mAudioRecord, mCblkMemory and mCblk.
     // It will also delete the strong references on previous IAudioRecord and IMemory
-    result = openRecord_l(mSampleRate, mFormat, mFrameCount, getInput_l());
+    size_t position = mProxy->getPosition();
+    mNewPosition = position + mUpdatePeriod;
+    result = openRecord_l(mSampleRate, mFormat, mFrameCount, getInput_l(), position);
     if (result == NO_ERROR) {
-        newCblk = mCblk;
-        // callback thread or sync event hasn't changed
-        result = mAudioRecord->start(AudioSystem::SYNC_EVENT_SAME, 0);
+        if (mActive) {
+            // callback thread or sync event hasn't changed
+            // FIXME this fails if we have a new AudioFlinger instance
+            result = mAudioRecord->start(AudioSystem::SYNC_EVENT_SAME, 0);
+        }
     }
     if (result != NO_ERROR) {
+        ALOGW("restoreRecord_l() failed status %d", result);
         mActive = false;
     }
 
-    ALOGV("restoreRecord_l() status %d mActive %d cblk %p, old cblk %p flags %08x old flags %08x",
-        result, mActive, newCblk, cblk, newCblk->flags, cblk->flags);
-
-    if (result == NO_ERROR) {
-        // from now on we switch to the newly created cblk
-        refCblk = newCblk;
-    }
-    newCblk->lock.lock();
+    return result;
+}
 
-    ALOGW_IF(result != NO_ERROR, "restoreRecord_l() error %d", result);
+// =========================================================================
 
-    return result;
+void AudioRecord::DeathNotifier::binderDied(const wp<IBinder>& who)
+{
+    sp<AudioRecord> audioRecord = mAudioRecord.promote();
+    if (audioRecord != 0) {
+        AutoMutex lock(audioRecord->mLock);
+        audioRecord->mProxy->binderDied();
+    }
 }
 
 // =========================================================================
 
 AudioRecord::AudioRecordThread::AudioRecordThread(AudioRecord& receiver, bool bCanCallJava)
-    : Thread(bCanCallJava), mReceiver(receiver), mPaused(true)
+    : Thread(bCanCallJava), mReceiver(receiver), mPaused(true), mResumeLatch(false)
 {
 }
 
@@ -842,10 +928,26 @@ bool AudioRecord::AudioRecordThread::threadLoop()
             return true;
         }
     }
-    if (!mReceiver.processAudioBuffer(this)) {
-        pause();
+    nsecs_t ns =  mReceiver.processAudioBuffer(this);
+    switch (ns) {
+    case 0:
+        return true;
+    case NS_WHENEVER:
+        sleep(1);
+        return true;
+    case NS_INACTIVE:
+        pauseConditional();
+        return true;
+    case NS_NEVER:
+        return false;
+    default:
+        LOG_ALWAYS_FATAL_IF(ns < 0, "processAudioBuffer() returned %lld", ns);
+        struct timespec req;
+        req.tv_sec = ns / 1000000000LL;
+        req.tv_nsec = ns % 1000000000LL;
+        nanosleep(&req, NULL /*rem*/);
+        return true;
     }
-    return true;
 }
 
 void AudioRecord::AudioRecordThread::requestExit()
@@ -859,6 +961,17 @@ void AudioRecord::AudioRecordThread::pause()
 {
     AutoMutex _l(mMyLock);
     mPaused = true;
+    mResumeLatch = false;
+}
+
+void AudioRecord::AudioRecordThread::pauseConditional()
+{
+    AutoMutex _l(mMyLock);
+    if (mResumeLatch) {
+        mResumeLatch = false;
+    } else {
+        mPaused = true;
+    }
 }
 
 void AudioRecord::AudioRecordThread::resume()
@@ -866,7 +979,10 @@ void AudioRecord::AudioRecordThread::resume()
     AutoMutex _l(mMyLock);
     if (mPaused) {
         mPaused = false;
+        mResumeLatch = false;
         mMyCond.signal();
+    } else {
+        mResumeLatch = true;
     }
 }
 
index 77fc6f6..faca054 100644 (file)
 //#define LOG_NDEBUG 0
 #define LOG_TAG "AudioTrack"
 
-#include <stdint.h>
-#include <sys/types.h>
-#include <limits.h>
-
-#include <sched.h>
 #include <sys/resource.h>
-
-#include <private/media/AudioTrackShared.h>
-
-#include <media/AudioSystem.h>
+#include <audio_utils/primitives.h>
+#include <binder/IPCThreadState.h>
 #include <media/AudioTrack.h>
-
 #include <utils/Log.h>
-#include <binder/Parcel.h>
-#include <binder/IPCThreadState.h>
-#include <utils/Timers.h>
-#include <utils/Atomic.h>
-
-#include <cutils/bitops.h>
-#include <cutils/compiler.h>
+#include <private/media/AudioTrackShared.h>
 
-#include <system/audio.h>
-#include <system/audio_policy.h>
-
-#include <audio_utils/primitives.h>
+#define WAIT_PERIOD_MS          10
 
 namespace android {
 // ---------------------------------------------------------------------------
@@ -82,7 +65,9 @@ status_t AudioTrack::getMinFrameCount(
 
     // Ensure that buffer depth covers at least audio hardware latency
     uint32_t minBufCount = afLatency / ((1000 * afFrameCount) / afSampleRate);
-    if (minBufCount < 2) minBufCount = 2;
+    if (minBufCount < 2) {
+        minBufCount = 2;
+    }
 
     *frameCount = (sampleRate == 0) ? afFrameCount * minBufCount :
             afFrameCount * minBufCount * sampleRate / afSampleRate;
@@ -97,8 +82,7 @@ AudioTrack::AudioTrack()
     : mStatus(NO_INIT),
       mIsTimed(false),
       mPreviousPriority(ANDROID_PRIORITY_NORMAL),
-      mPreviousSchedulingGroup(SP_DEFAULT),
-      mProxy(NULL)
+      mPreviousSchedulingGroup(SP_DEFAULT)
 {
 }
 
@@ -112,16 +96,16 @@ AudioTrack::AudioTrack(
         callback_t cbf,
         void* user,
         int notificationFrames,
-        int sessionId)
+        int sessionId,
+        transfer_type transferType)
     : mStatus(NO_INIT),
       mIsTimed(false),
       mPreviousPriority(ANDROID_PRIORITY_NORMAL),
-      mPreviousSchedulingGroup(SP_DEFAULT),
-      mProxy(NULL)
+      mPreviousSchedulingGroup(SP_DEFAULT)
 {
     mStatus = set(streamType, sampleRate, format, channelMask,
             frameCount, flags, cbf, user, notificationFrames,
-            0 /*sharedBuffer*/, false /*threadCanCallJava*/, sessionId);
+            0 /*sharedBuffer*/, false /*threadCanCallJava*/, sessionId, transferType);
 }
 
 AudioTrack::AudioTrack(
@@ -134,27 +118,20 @@ AudioTrack::AudioTrack(
         callback_t cbf,
         void* user,
         int notificationFrames,
-        int sessionId)
+        int sessionId,
+        transfer_type transferType)
     : mStatus(NO_INIT),
       mIsTimed(false),
       mPreviousPriority(ANDROID_PRIORITY_NORMAL),
-      mPreviousSchedulingGroup(SP_DEFAULT),
-      mProxy(NULL)
+      mPreviousSchedulingGroup(SP_DEFAULT)
 {
-    if (sharedBuffer == 0) {
-        ALOGE("sharedBuffer must be non-0");
-        mStatus = BAD_VALUE;
-        return;
-    }
     mStatus = set(streamType, sampleRate, format, channelMask,
             0 /*frameCount*/, flags, cbf, user, notificationFrames,
-            sharedBuffer, false /*threadCanCallJava*/, sessionId);
+            sharedBuffer, false /*threadCanCallJava*/, sessionId, transferType);
 }
 
 AudioTrack::~AudioTrack()
 {
-    ALOGV_IF(mSharedBuffer != 0, "Destructor sharedBuffer: %p", mSharedBuffer->pointer());
-
     if (mStatus == NO_ERROR) {
         // Make sure that callback function exits in the case where
         // it is looping on buffer full condition in obtainBuffer().
@@ -165,11 +142,13 @@ AudioTrack::~AudioTrack()
             mAudioTrackThread->requestExitAndWait();
             mAudioTrackThread.clear();
         }
-        mAudioTrack.clear();
+        if (mAudioTrack != 0) {
+            mAudioTrack->asBinder()->unlinkToDeath(mDeathNotifier, this);
+            mAudioTrack.clear();
+        }
         IPCThreadState::self()->flushCommands();
         AudioSystem::releaseAudioSessionId(mSessionId);
     }
-    delete mProxy;
 }
 
 status_t AudioTrack::set(
@@ -184,8 +163,44 @@ status_t AudioTrack::set(
         int notificationFrames,
         const sp<IMemory>& sharedBuffer,
         bool threadCanCallJava,
-        int sessionId)
+        int sessionId,
+        transfer_type transferType)
 {
+    switch (transferType) {
+    case TRANSFER_DEFAULT:
+        if (sharedBuffer != 0) {
+            transferType = TRANSFER_SHARED;
+        } else if (cbf == NULL || threadCanCallJava) {
+            transferType = TRANSFER_SYNC;
+        } else {
+            transferType = TRANSFER_CALLBACK;
+        }
+        break;
+    case TRANSFER_CALLBACK:
+        if (cbf == NULL || sharedBuffer != 0) {
+            ALOGE("Transfer type TRANSFER_CALLBACK but cbf == NULL || sharedBuffer != 0");
+            return BAD_VALUE;
+        }
+        break;
+    case TRANSFER_OBTAIN:
+    case TRANSFER_SYNC:
+        if (sharedBuffer != 0) {
+            ALOGE("Transfer type TRANSFER_OBTAIN but sharedBuffer != 0");
+            return BAD_VALUE;
+        }
+        break;
+    case TRANSFER_SHARED:
+        if (sharedBuffer == 0) {
+            ALOGE("Transfer type TRANSFER_SHARED but sharedBuffer == 0");
+            return BAD_VALUE;
+        }
+        break;
+    default:
+        ALOGE("Invalid transfer type %d", transferType);
+        return BAD_VALUE;
+    }
+    mTransfer = transferType;
+
     // FIXME "int" here is legacy and will be replaced by size_t later
     if (frameCountInt < 0) {
         ALOGE("Invalid frame count %d", frameCountInt);
@@ -199,6 +214,7 @@ status_t AudioTrack::set(
     ALOGV("set() streamType %d frameCount %u flags %04x", streamType, frameCount, flags);
 
     AutoMutex lock(mLock);
+
     if (mAudioTrack != 0) {
         ALOGE("Track already in use");
         return INVALID_OPERATION;
@@ -228,7 +244,7 @@ status_t AudioTrack::set(
 
     // validate parameters
     if (!audio_is_valid_format(format)) {
-        ALOGE("Invalid format");
+        ALOGE("Invalid format %d", format);
         return BAD_VALUE;
     }
 
@@ -281,6 +297,7 @@ status_t AudioTrack::set(
     mFrameCount = frameCount;
     mReqFrameCount = frameCount;
     mNotificationFramesReq = notificationFrames;
+    mNotificationFramesAct = 0;
     mSessionId = sessionId;
     mAuxEffectId = 0;
     mFlags = flags;
@@ -298,7 +315,8 @@ status_t AudioTrack::set(
                                   frameCount,
                                   flags,
                                   sharedBuffer,
-                                  output);
+                                  output,
+                                  0 /*epoch*/);
 
     if (status != NO_ERROR) {
         if (mAudioTrackThread != 0) {
@@ -309,20 +327,21 @@ status_t AudioTrack::set(
     }
 
     mStatus = NO_ERROR;
-
     mStreamType = streamType;
     mFormat = format;
-
     mSharedBuffer = sharedBuffer;
-    mActive = false;
+    mState = STATE_STOPPED;
     mUserData = user;
-    mLoopCount = 0;
+    mLoopPeriod = 0;
     mMarkerPosition = 0;
     mMarkerReached = false;
     mNewPosition = 0;
     mUpdatePeriod = 0;
-    mFlushed = false;
     AudioSystem::acquireAudioSessionId(mSessionId);
+    mSequence = 1;
+    mObservedSequence = mSequence;
+    mInUnderrun = false;
+
     return NO_ERROR;
 }
 
@@ -330,87 +349,45 @@ status_t AudioTrack::set(
 
 void AudioTrack::start()
 {
-    sp<AudioTrackThread> t = mAudioTrackThread;
-
-    ALOGV("start %p", this);
-
     AutoMutex lock(mLock);
-    // acquire a strong reference on the IMemory and IAudioTrack so that they cannot be destroyed
-    // while we are accessing the cblk
-    sp<IAudioTrack> audioTrack = mAudioTrack;
-    sp<IMemory> iMem = mCblkMemory;
-    audio_track_cblk_t* cblk = mCblk;
+    if (mState == STATE_ACTIVE) {
+        return;
+    }
 
-    if (!mActive) {
-        mFlushed = false;
-        mActive = true;
-        mNewPosition = cblk->server + mUpdatePeriod;
-        cblk->lock.lock();
-        cblk->bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS;
-        cblk->waitTimeMs = 0;
-        android_atomic_and(~CBLK_DISABLED, &cblk->flags);
-        if (t != 0) {
-            t->resume();
-        } else {
-            mPreviousPriority = getpriority(PRIO_PROCESS, 0);
-            get_sched_policy(0, &mPreviousSchedulingGroup);
-            androidSetThreadPriority(0, ANDROID_PRIORITY_AUDIO);
-        }
+    mInUnderrun = true;
 
-        ALOGV("start %p before lock cblk %p", this, cblk);
-        status_t status = NO_ERROR;
-        if (!(cblk->flags & CBLK_INVALID)) {
-            cblk->lock.unlock();
-            ALOGV("mAudioTrack->start()");
-            status = mAudioTrack->start();
-            cblk->lock.lock();
-            if (status == DEAD_OBJECT) {
-                android_atomic_or(CBLK_INVALID, &cblk->flags);
-            }
-        }
-        if (cblk->flags & CBLK_INVALID) {
-            audio_track_cblk_t* temp = cblk;
-            status = restoreTrack_l(temp, true /*fromStart*/);
-            cblk = temp;
-        }
-        cblk->lock.unlock();
-        if (status != NO_ERROR) {
-            ALOGV("start() failed");
-            mActive = false;
-            if (t != 0) {
-                t->pause();
-            } else {
-                setpriority(PRIO_PROCESS, 0, mPreviousPriority);
-                set_sched_policy(0, mPreviousSchedulingGroup);
-            }
-        }
+    State previousState = mState;
+    mState = STATE_ACTIVE;
+    if (previousState == STATE_STOPPED || previousState == STATE_FLUSHED) {
+        // reset current position as seen by client to 0
+        mProxy->setEpoch(mProxy->getEpoch() - mProxy->getPosition());
     }
+    mNewPosition = mProxy->getPosition() + mUpdatePeriod;
+    int32_t flags = android_atomic_and(~CBLK_DISABLED, &mCblk->flags);
 
-}
-
-void AudioTrack::stop()
-{
     sp<AudioTrackThread> t = mAudioTrackThread;
+    if (t != 0) {
+        t->resume();
+    } else {
+        mPreviousPriority = getpriority(PRIO_PROCESS, 0);
+        get_sched_policy(0, &mPreviousSchedulingGroup);
+        androidSetThreadPriority(0, ANDROID_PRIORITY_AUDIO);
+    }
 
-    ALOGV("stop %p", this);
-
-    AutoMutex lock(mLock);
-    if (mActive) {
-        mActive = false;
-        mCblk->cv.signal();
-        mAudioTrack->stop();
-        // Cancel loops (If we are in the middle of a loop, playback
-        // would not stop until loopCount reaches 0).
-        setLoop_l(0, 0, 0);
-        // the playback head position will reset to 0, so if a marker is set, we need
-        // to activate it again
-        mMarkerReached = false;
-        // Force flush if a shared buffer is used otherwise audioflinger
-        // will not stop before end of buffer is reached.
-        // It may be needed to make sure that we stop playback, likely in case looping is on.
-        if (mSharedBuffer != 0) {
-            flush_l();
+    status_t status = NO_ERROR;
+    if (!(flags & CBLK_INVALID)) {
+        status = mAudioTrack->start();
+        if (status == DEAD_OBJECT) {
+            flags |= CBLK_INVALID;
         }
+    }
+    if (flags & CBLK_INVALID) {
+        status = restoreTrack_l("start");
+    }
+
+    if (status != NO_ERROR) {
+        ALOGE("start() status %d", status);
+        mState = previousState;
         if (t != 0) {
             t->pause();
         } else {
@@ -419,57 +396,85 @@ void AudioTrack::stop()
         }
     }
 
+    // FIXME discarding status
+}
+
+void AudioTrack::stop()
+{
+    AutoMutex lock(mLock);
+    // FIXME pause then stop should not be a nop
+    if (mState != STATE_ACTIVE) {
+        return;
+    }
+
+    mState = STATE_STOPPED;
+    mProxy->interrupt();
+    mAudioTrack->stop();
+    // the playback head position will reset to 0, so if a marker is set, we need
+    // to activate it again
+    mMarkerReached = false;
+#if 0
+    // Force flush if a shared buffer is used otherwise audioflinger
+    // will not stop before end of buffer is reached.
+    // It may be needed to make sure that we stop playback, likely in case looping is on.
+    if (mSharedBuffer != 0) {
+        flush_l();
+    }
+#endif
+    sp<AudioTrackThread> t = mAudioTrackThread;
+    if (t != 0) {
+        t->pause();
+    } else {
+        setpriority(PRIO_PROCESS, 0, mPreviousPriority);
+        set_sched_policy(0, mPreviousSchedulingGroup);
+    }
 }
 
 bool AudioTrack::stopped() const
 {
     AutoMutex lock(mLock);
-    return stopped_l();
+    return mState != STATE_ACTIVE;
 }
 
 void AudioTrack::flush()
 {
+    if (mSharedBuffer != 0) {
+        return;
+    }
     AutoMutex lock(mLock);
-    if (!mActive && mSharedBuffer == 0) {
-        flush_l();
+    if (mState == STATE_ACTIVE || mState == STATE_FLUSHED) {
+        return;
     }
+    flush_l();
 }
 
 void AudioTrack::flush_l()
 {
-    ALOGV("flush");
-    ALOG_ASSERT(!mActive);
+    ALOG_ASSERT(mState != STATE_ACTIVE);
 
     // clear playback marker and periodic update counter
     mMarkerPosition = 0;
     mMarkerReached = false;
     mUpdatePeriod = 0;
 
-    mFlushed = true;
+    mState = STATE_FLUSHED;
+    mProxy->flush();
     mAudioTrack->flush();
-    // Release AudioTrack callback thread in case it was waiting for new buffers
-    // in AudioTrack::obtainBuffer()
-    mCblk->cv.signal();
 }
 
 void AudioTrack::pause()
 {
-    ALOGV("pause");
     AutoMutex lock(mLock);
-    if (mActive) {
-        mActive = false;
-        mCblk->cv.signal();
-        mAudioTrack->pause();
+    if (mState != STATE_ACTIVE) {
+        return;
     }
+    mState = STATE_PAUSED;
+    mProxy->interrupt();
+    mAudioTrack->pause();
 }
 
 status_t AudioTrack::setVolume(float left, float right)
 {
-    if (mStatus != NO_ERROR) {
-        return mStatus;
-    }
-    ALOG_ASSERT(mProxy != NULL);
-
     if (left < 0.0f || left > 1.0f || right < 0.0f || right > 1.0f) {
         return BAD_VALUE;
     }
@@ -490,18 +495,11 @@ status_t AudioTrack::setVolume(float volume)
 
 status_t AudioTrack::setAuxEffectSendLevel(float level)
 {
-    ALOGV("setAuxEffectSendLevel(%f)", level);
-
-    if (mStatus != NO_ERROR) {
-        return mStatus;
-    }
-    ALOG_ASSERT(mProxy != NULL);
-
     if (level < 0.0f || level > 1.0f) {
         return BAD_VALUE;
     }
-    AutoMutex lock(mLock);
 
+    AutoMutex lock(mLock);
     mSendLevel = level;
     mProxy->setSendLevel(level);
 
@@ -511,18 +509,17 @@ status_t AudioTrack::setAuxEffectSendLevel(float level)
 void AudioTrack::getAuxEffectSendLevel(float* level) const
 {
     if (level != NULL) {
-        *level  = mSendLevel;
+        *level = mSendLevel;
     }
 }
 
 status_t AudioTrack::setSampleRate(uint32_t rate)
 {
-    uint32_t afSamplingRate;
-
     if (mIsTimed) {
         return INVALID_OPERATION;
     }
 
+    uint32_t afSamplingRate;
     if (AudioSystem::getOutputSamplingRate(&afSamplingRate, mStreamType) != NO_ERROR) {
         return NO_INIT;
     }
@@ -550,78 +547,44 @@ uint32_t AudioTrack::getSampleRate() const
 
 status_t AudioTrack::setLoop(uint32_t loopStart, uint32_t loopEnd, int loopCount)
 {
-    AutoMutex lock(mLock);
-    return setLoop_l(loopStart, loopEnd, loopCount);
-}
-
-// must be called with mLock held
-status_t AudioTrack::setLoop_l(uint32_t loopStart, uint32_t loopEnd, int loopCount)
-{
     if (mSharedBuffer == 0 || mIsTimed) {
         return INVALID_OPERATION;
     }
 
-    if (loopCount < 0 && loopCount != -1) {
-        return BAD_VALUE;
-    }
-
-#if 0
-    // This will be for the new interpretation of loopStart and loopEnd
-
-    if (loopCount != 0) {
-        if (loopStart >= mFrameCount || loopEnd >= mFrameCount || loopStart >= loopEnd) {
-            return BAD_VALUE;
-        }
-        uint32_t periodFrames = loopEnd - loopStart;
-        if (periodFrames < PERIOD_FRAMES_MIN) {
-            return BAD_VALUE;
-        }
-    }
-
-    // The remainder of this code still uses the old interpretation
-#endif
-
-    audio_track_cblk_t* cblk = mCblk;
-
-    Mutex::Autolock _l(cblk->lock);
-
     if (loopCount == 0) {
-        cblk->loopStart = UINT_MAX;
-        cblk->loopEnd = UINT_MAX;
-        cblk->loopCount = 0;
-        mLoopCount = 0;
-        return NO_ERROR;
-    }
-
-    if (loopStart >= loopEnd ||
-        loopEnd - loopStart > mFrameCount ||
-        cblk->server > loopStart) {
-        ALOGE("setLoop invalid value: loopStart %d, loopEnd %d, loopCount %d, framecount %d, "
-              "user %d", loopStart, loopEnd, loopCount, mFrameCount, cblk->user);
+        ;
+    } else if (loopCount >= -1 && loopStart < loopEnd && loopEnd <= mFrameCount &&
+            loopEnd - loopStart >= MIN_LOOP) {
+        ;
+    } else {
         return BAD_VALUE;
     }
 
-    if ((mSharedBuffer != 0) && (loopEnd > mFrameCount)) {
-        ALOGE("setLoop invalid value: loop markers beyond data: loopStart %d, loopEnd %d, "
-            "framecount %d",
-            loopStart, loopEnd, mFrameCount);
-        return BAD_VALUE;
+    AutoMutex lock(mLock);
+    // See setPosition() regarding setting parameters such as loop points or position while active
+    if (mState == STATE_ACTIVE) {
+        return INVALID_OPERATION;
     }
-
-    cblk->loopStart = loopStart;
-    cblk->loopEnd = loopEnd;
-    cblk->loopCount = loopCount;
-    mLoopCount = loopCount;
-
+    setLoop_l(loopStart, loopEnd, loopCount);
     return NO_ERROR;
 }
 
+void AudioTrack::setLoop_l(uint32_t loopStart, uint32_t loopEnd, int loopCount)
+{
+    // FIXME If setting a loop also sets position to start of loop, then
+    //       this is correct.  Otherwise it should be removed.
+    mNewPosition = mProxy->getPosition() + mUpdatePeriod;
+    mLoopPeriod = loopCount != 0 ? loopEnd - loopStart : 0;
+    mStaticProxy->setLoop(loopStart, loopEnd, loopCount);
+}
+
 status_t AudioTrack::setMarkerPosition(uint32_t marker)
 {
     if (mCbf == NULL) {
         return INVALID_OPERATION;
     }
 
+    AutoMutex lock(mLock);
     mMarkerPosition = marker;
     mMarkerReached = false;
 
@@ -634,6 +597,7 @@ status_t AudioTrack::getMarkerPosition(uint32_t *marker) const
         return BAD_VALUE;
     }
 
+    AutoMutex lock(mLock);
     *marker = mMarkerPosition;
 
     return NO_ERROR;
@@ -645,9 +609,8 @@ status_t AudioTrack::setPositionUpdatePeriod(uint32_t updatePeriod)
         return INVALID_OPERATION;
     }
 
-    uint32_t curPosition;
-    getPosition(&curPosition);
-    mNewPosition = curPosition + updatePeriod;
+    AutoMutex lock(mLock);
+    mNewPosition = mProxy->getPosition() + updatePeriod;
     mUpdatePeriod = updatePeriod;
 
     return NO_ERROR;
@@ -659,6 +622,7 @@ status_t AudioTrack::getPositionUpdatePeriod(uint32_t *updatePeriod) const
         return BAD_VALUE;
     }
 
+    AutoMutex lock(mLock);
     *updatePeriod = mUpdatePeriod;
 
     return NO_ERROR;
@@ -669,49 +633,44 @@ status_t AudioTrack::setPosition(uint32_t position)
     if (mSharedBuffer == 0 || mIsTimed) {
         return INVALID_OPERATION;
     }
-
-    AutoMutex lock(mLock);
-
-    if (!stopped_l()) {
-        return INVALID_OPERATION;
-    }
-
-#if 0
-    // This will be for the new interpretation of position
-
-    if (position >= mFrameCount) {
+    if (position > mFrameCount) {
         return BAD_VALUE;
     }
 
-    // The remainder of this code still uses the old interpretation
-#endif
-
-    audio_track_cblk_t* cblk = mCblk;
-    Mutex::Autolock _l(cblk->lock);
-
-    if (position > cblk->user) {
-        return BAD_VALUE;
+    AutoMutex lock(mLock);
+    // Currently we require that the player is inactive before setting parameters such as position
+    // or loop points.  Otherwise, there could be a race condition: the application could read the
+    // current position, compute a new position or loop parameters, and then set that position or
+    // loop parameters but it would do the "wrong" thing since the position has continued to advance
+    // in the mean time.  If we ever provide a sequencer in server, we could allow a way for the app
+    // to specify how it wants to handle such scenarios.
+    if (mState == STATE_ACTIVE) {
+        return INVALID_OPERATION;
     }
-
-    cblk->server = position;
-    android_atomic_or(CBLK_FORCEREADY, &cblk->flags);
+    mNewPosition = mProxy->getPosition() + mUpdatePeriod;
+    mLoopPeriod = 0;
+    // FIXME Check whether loops and setting position are incompatible in old code.
+    // If we use setLoop for both purposes we lose the capability to set the position while looping.
+    mStaticProxy->setLoop(position, mFrameCount, 0);
 
     return NO_ERROR;
 }
 
-status_t AudioTrack::getPosition(uint32_t *position)
+status_t AudioTrack::getPosition(uint32_t *position) const
 {
     if (position == NULL) {
         return BAD_VALUE;
     }
+
     AutoMutex lock(mLock);
-    *position = mFlushed ? 0 : mCblk->server;
+    // IAudioTrack::stop() isn't synchronous; we don't know when presentation completes
+    *position = (mState == STATE_STOPPED || mState == STATE_FLUSHED) ? 0 :
+            mProxy->getPosition();
 
     return NO_ERROR;
 }
 
-#if 0
-status_t AudioTrack::getBufferPosition(uint32_t *position)
+status_t AudioTrack::getBufferPosition(size_t *position)
 {
     if (mSharedBuffer == 0 || mIsTimed) {
         return INVALID_OPERATION;
@@ -719,33 +678,28 @@ status_t AudioTrack::getBufferPosition(uint32_t *position)
     if (position == NULL) {
         return BAD_VALUE;
     }
-    *position = 0;
 
+    AutoMutex lock(mLock);
+    *position = mStaticProxy->getBufferPosition();
     return NO_ERROR;
 }
-#endif
 
 status_t AudioTrack::reload()
 {
-    if (mStatus != NO_ERROR) {
-        return mStatus;
-    }
-    ALOG_ASSERT(mProxy != NULL);
-
     if (mSharedBuffer == 0 || mIsTimed) {
         return INVALID_OPERATION;
     }
 
     AutoMutex lock(mLock);
-
-    if (!stopped_l()) {
+    // See setPosition() regarding setting parameters such as loop points or position while active
+    if (mState == STATE_ACTIVE) {
         return INVALID_OPERATION;
     }
-
-    flush_l();
-
-    (void) mProxy->stepUser(mFrameCount);
-
+    mNewPosition = mUpdatePeriod;
+    mLoopPeriod = 0;
+    // FIXME The new code cannot reload while keeping a loop specified.
+    // Need to check how the old code handled this, and whether it's a significant change.
+    mStaticProxy->setLoop(0, mFrameCount, 0);
     return NO_ERROR;
 }
 
@@ -764,7 +718,7 @@ audio_io_handle_t AudioTrack::getOutput_l()
 
 status_t AudioTrack::attachAuxEffect(int effectId)
 {
-    ALOGV("attachAuxEffect(%d)", effectId);
+    AutoMutex lock(mLock);
     status_t status = mAudioTrack->attachAuxEffect(effectId);
     if (status == NO_ERROR) {
         mAuxEffectId = effectId;
@@ -782,7 +736,8 @@ status_t AudioTrack::createTrack_l(
         size_t frameCount,
         audio_output_flags_t flags,
         const sp<IMemory>& sharedBuffer,
-        audio_io_handle_t output)
+        audio_io_handle_t output,
+        size_t epoch)
 {
     status_t status;
     const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger();
@@ -792,7 +747,8 @@ status_t AudioTrack::createTrack_l(
     }
 
     uint32_t afLatency;
-    if (AudioSystem::getLatency(output, streamType, &afLatency) != NO_ERROR) {
+    if ((status = AudioSystem::getLatency(output, streamType, &afLatency)) != NO_ERROR) {
+        ALOGE("getLatency(%d) failed status %d", output, status);
         return NO_INIT;
     }
 
@@ -820,7 +776,10 @@ status_t AudioTrack::createTrack_l(
             frameCount = sharedBuffer->size();
         } else if (frameCount == 0) {
             size_t afFrameCount;
-            if (AudioSystem::getFrameCount(output, streamType, &afFrameCount) != NO_ERROR) {
+            status = AudioSystem::getFrameCount(output, streamType, &afFrameCount);
+            if (status != NO_ERROR) {
+                ALOGE("getFrameCount(output=%d, streamType=%d) status %d", output, streamType,
+                        status);
                 return NO_INIT;
             }
             frameCount = afFrameCount;
@@ -851,11 +810,16 @@ status_t AudioTrack::createTrack_l(
 
         // FIXME move these calculations and associated checks to server
         uint32_t afSampleRate;
-        if (AudioSystem::getSamplingRate(output, streamType, &afSampleRate) != NO_ERROR) {
+        status = AudioSystem::getSamplingRate(output, streamType, &afSampleRate);
+        if (status != NO_ERROR) {
+            ALOGE("getSamplingRate(output=%d, streamType=%d) status %d", output, streamType,
+                    status);
             return NO_INIT;
         }
         size_t afFrameCount;
-        if (AudioSystem::getFrameCount(output, streamType, &afFrameCount) != NO_ERROR) {
+        status = AudioSystem::getFrameCount(output, streamType, &afFrameCount);
+        if (status != NO_ERROR) {
+            ALOGE("getFrameCount(output=%d, streamType=%d) status %d", output, streamType, status);
             return NO_INIT;
         }
 
@@ -875,12 +839,9 @@ status_t AudioTrack::createTrack_l(
         if (frameCount == 0) {
             frameCount = minFrameCount;
         }
-        if (mNotificationFramesAct == 0) {
-            mNotificationFramesAct = frameCount/2;
-        }
         // Make sure that application is notified with sufficient margin
         // before underrun
-        if (mNotificationFramesAct > frameCount/2) {
+        if (mNotificationFramesAct == 0 || mNotificationFramesAct > frameCount/2) {
             mNotificationFramesAct = frameCount/2;
         }
         if (frameCount < minFrameCount) {
@@ -930,6 +891,10 @@ status_t AudioTrack::createTrack_l(
         ALOGE("Could not get control block");
         return NO_INIT;
     }
+    if (mAudioTrack != 0) {
+        mAudioTrack->asBinder()->unlinkToDeath(mDeathNotifier, this);
+        mDeathNotifier.clear();
+    }
     mAudioTrack = track;
     mCblkMemory = iMem;
     audio_track_cblk_t* cblk = static_cast<audio_track_cblk_t*>(iMem->pointer());
@@ -947,26 +912,38 @@ status_t AudioTrack::createTrack_l(
         if (trackFlags & IAudioFlinger::TRACK_FAST) {
             ALOGV("AUDIO_OUTPUT_FLAG_FAST successful; frameCount %u", frameCount);
             mAwaitBoost = true;
+            if (sharedBuffer == 0) {
+                // double-buffering is not required for fast tracks, due to tighter scheduling
+                if (mNotificationFramesAct == 0 || mNotificationFramesAct > frameCount) {
+                    mNotificationFramesAct = frameCount;
+                }
+            }
         } else {
             ALOGV("AUDIO_OUTPUT_FLAG_FAST denied by server; frameCount %u", frameCount);
             // once denied, do not request again if IAudioTrack is re-created
             flags = (audio_output_flags_t) (flags & ~AUDIO_OUTPUT_FLAG_FAST);
             mFlags = flags;
-        }
-        if (sharedBuffer == 0) {
-            mNotificationFramesAct = frameCount/2;
+            if (sharedBuffer == 0) {
+                if (mNotificationFramesAct == 0 || mNotificationFramesAct > frameCount/2) {
+                    mNotificationFramesAct = frameCount/2;
+                }
+            }
         }
     }
+    mRefreshRemaining = true;
+
+    // Starting address of buffers in shared memory.  If there is a shared buffer, buffers
+    // is the value of pointer() for the shared buffer, otherwise buffers points
+    // immediately after the control block.  This address is for the mapping within client
+    // address space.  AudioFlinger::TrackBase::mBuffer is for the server address space.
+    void* buffers;
     if (sharedBuffer == 0) {
-        mBuffers = (char*)cblk + sizeof(audio_track_cblk_t);
+        buffers = (char*)cblk + sizeof(audio_track_cblk_t);
     } else {
-        mBuffers = sharedBuffer->pointer();
+        buffers = sharedBuffer->pointer();
     }
 
     mAudioTrack->attachAuxEffect(mAuxEffectId);
-    cblk->bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS;
-    cblk->waitTimeMs = 0;
-    mRemainingFrames = mNotificationFramesAct;
     // FIXME don't believe this lie
     mLatency = afLatency + (1000*frameCount) / sampleRate;
     mFrameCount = frameCount;
@@ -977,147 +954,143 @@ status_t AudioTrack::createTrack_l(
     }
 
     // update proxy
-    delete mProxy;
-    mProxy = new AudioTrackClientProxy(cblk, mBuffers, frameCount, mFrameSizeAF);
+    if (sharedBuffer == 0) {
+        mStaticProxy.clear();
+        mProxy = new AudioTrackClientProxy(cblk, buffers, frameCount, mFrameSizeAF);
+    } else {
+        mStaticProxy = new StaticAudioTrackClientProxy(cblk, buffers, frameCount, mFrameSizeAF);
+        mProxy = mStaticProxy;
+    }
     mProxy->setVolumeLR((uint32_t(uint16_t(mVolume[RIGHT] * 0x1000)) << 16) |
             uint16_t(mVolume[LEFT] * 0x1000));
     mProxy->setSendLevel(mSendLevel);
     mProxy->setSampleRate(mSampleRate);
-    if (sharedBuffer != 0) {
-        // Force buffer full condition as data is already present in shared memory
-        mProxy->stepUser(frameCount);
-    }
+    mProxy->setEpoch(epoch);
+    mProxy->setMinimum(mNotificationFramesAct);
+
+    mDeathNotifier = new DeathNotifier(this);
+    mAudioTrack->asBinder()->linkToDeath(mDeathNotifier, this);
 
     return NO_ERROR;
 }
 
 status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, int32_t waitCount)
 {
-    ALOG_ASSERT(mStatus == NO_ERROR && mProxy != NULL);
+    if (audioBuffer == NULL) {
+        return BAD_VALUE;
+    }
+    if (mTransfer != TRANSFER_OBTAIN) {
+        audioBuffer->frameCount = 0;
+        audioBuffer->size = 0;
+        audioBuffer->raw = NULL;
+        return INVALID_OPERATION;
+    }
+
+    const struct timespec *requested;
+    if (waitCount == -1) {
+        requested = &ClientProxy::kForever;
+    } else if (waitCount == 0) {
+        requested = &ClientProxy::kNonBlocking;
+    } else if (waitCount > 0) {
+        long long ms = WAIT_PERIOD_MS * (long long) waitCount;
+        struct timespec timeout;
+        timeout.tv_sec = ms / 1000;
+        timeout.tv_nsec = (int) (ms % 1000) * 1000000;
+        requested = &timeout;
+    } else {
+        ALOGE("%s invalid waitCount %d", __func__, waitCount);
+        requested = NULL;
+    }
+    return obtainBuffer(audioBuffer, requested);
+}
+
+status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, const struct timespec *requested,
+        struct timespec *elapsed, size_t *nonContig)
+{
+    // previous and new IAudioTrack sequence numbers are used to detect track re-creation
+    uint32_t oldSequence = 0;
+    uint32_t newSequence;
 
-    AutoMutex lock(mLock);
-    bool active;
-    status_t result = NO_ERROR;
-    audio_track_cblk_t* cblk = mCblk;
-    uint32_t framesReq = audioBuffer->frameCount;
-    uint32_t waitTimeMs = (waitCount < 0) ? cblk->bufferTimeoutMs : WAIT_PERIOD_MS;
+    Proxy::Buffer buffer;
+    status_t status = NO_ERROR;
 
-    audioBuffer->frameCount  = 0;
-    audioBuffer->size = 0;
+    static const int32_t kMaxTries = 5;
+    int32_t tryCounter = kMaxTries;
 
-    size_t framesAvail = mProxy->framesAvailable();
+    do {
+        // obtainBuffer() is called with mutex unlocked, so keep extra references to these fields to
+        // keep them from going away if another thread re-creates the track during obtainBuffer()
+        sp<AudioTrackClientProxy> proxy;
+        sp<IMemory> iMem;
 
-    cblk->lock.lock();
-    if (cblk->flags & CBLK_INVALID) {
-        goto create_new_track;
-    }
-    cblk->lock.unlock();
-
-    if (framesAvail == 0) {
-        cblk->lock.lock();
-        goto start_loop_here;
-        while (framesAvail == 0) {
-            active = mActive;
-            if (CC_UNLIKELY(!active)) {
-                ALOGV("Not active and NO_MORE_BUFFERS");
-                cblk->lock.unlock();
-                return NO_MORE_BUFFERS;
-            }
-            if (CC_UNLIKELY(!waitCount)) {
-                cblk->lock.unlock();
-                return WOULD_BLOCK;
-            }
-            if (!(cblk->flags & CBLK_INVALID)) {
-                mLock.unlock();
-                // this condition is in shared memory, so if IAudioTrack and control block
-                // are replaced due to mediaserver death or IAudioTrack invalidation then
-                // cv won't be signalled, but fortunately the timeout will limit the wait
-                result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs));
-                cblk->lock.unlock();
-                mLock.lock();
-                if (!mActive) {
-                    return status_t(STOPPED);
-                }
-                // IAudioTrack may have been re-created while mLock was unlocked
-                cblk = mCblk;
-                cblk->lock.lock();
-            }
+        {   // start of lock scope
+            AutoMutex lock(mLock);
 
-            if (cblk->flags & CBLK_INVALID) {
-                goto create_new_track;
-            }
-            if (CC_UNLIKELY(result != NO_ERROR)) {
-                cblk->waitTimeMs += waitTimeMs;
-                if (cblk->waitTimeMs >= cblk->bufferTimeoutMs) {
-                    // timing out when a loop has been set and we have already written upto loop end
-                    // is a normal condition: no need to wake AudioFlinger up.
-                    if (cblk->user < cblk->loopEnd) {
-                        ALOGW("obtainBuffer timed out (is the CPU pegged?) %p name=%#x user=%08x, "
-                              "server=%08x", this, cblk->mName, cblk->user, cblk->server);
-                        //unlock cblk mutex before calling mAudioTrack->start() (see issue #1617140)
-                        cblk->lock.unlock();
-                        result = mAudioTrack->start();
-                        cblk->lock.lock();
-                        if (result == DEAD_OBJECT) {
-                            android_atomic_or(CBLK_INVALID, &cblk->flags);
-create_new_track:
-                            audio_track_cblk_t* temp = cblk;
-                            result = restoreTrack_l(temp, false /*fromStart*/);
-                            cblk = temp;
-                        }
-                        if (result != NO_ERROR) {
-                            ALOGW("obtainBuffer create Track error %d", result);
-                            cblk->lock.unlock();
-                            return result;
-                        }
+            newSequence = mSequence;
+            // did previous obtainBuffer() fail due to media server death or voluntary invalidation?
+            if (status == DEAD_OBJECT) {
+                // re-create track, unless someone else has already done so
+                if (newSequence == oldSequence) {
+                    status = restoreTrack_l("obtainBuffer");
+                    if (status != NO_ERROR) {
+                        break;
                     }
-                    cblk->waitTimeMs = 0;
                 }
+            }
+            oldSequence = newSequence;
 
-                if (--waitCount == 0) {
-                    cblk->lock.unlock();
-                    return TIMED_OUT;
-                }
+            // Keep the extra references
+            proxy = mProxy;
+            iMem = mCblkMemory;
+
+            // Non-blocking if track is stopped or paused
+            if (mState != STATE_ACTIVE) {
+                requested = &ClientProxy::kNonBlocking;
             }
-            // read the server count again
-        start_loop_here:
-            framesAvail = mProxy->framesAvailable_l();
-        }
-        cblk->lock.unlock();
-    }
 
-    cblk->waitTimeMs = 0;
+        }   // end of lock scope
 
-    if (framesReq > framesAvail) {
-        framesReq = framesAvail;
-    }
+        buffer.mFrameCount = audioBuffer->frameCount;
+        // FIXME starts the requested timeout and elapsed over from scratch
+        status = proxy->obtainBuffer(&buffer, requested, elapsed);
 
-    uint32_t u = cblk->user;
-    uint32_t bufferEnd = cblk->userBase + mFrameCount;
+    } while ((status == DEAD_OBJECT) && (tryCounter-- > 0));
 
-    if (framesReq > bufferEnd - u) {
-        framesReq = bufferEnd - u;
+    audioBuffer->frameCount = buffer.mFrameCount;
+    audioBuffer->size = buffer.mFrameCount * mFrameSizeAF;
+    audioBuffer->raw = buffer.mRaw;
+    if (nonContig != NULL) {
+        *nonContig = buffer.mNonContig;
     }
-
-    audioBuffer->frameCount = framesReq;
-    audioBuffer->size = framesReq * mFrameSizeAF;
-    audioBuffer->raw = mProxy->buffer(u);
-    active = mActive;
-    return active ? status_t(NO_ERROR) : status_t(STOPPED);
+    return status;
 }
 
 void AudioTrack::releaseBuffer(Buffer* audioBuffer)
 {
-    ALOG_ASSERT(mStatus == NO_ERROR && mProxy != NULL);
+    if (mTransfer == TRANSFER_SHARED) {
+        return;
+    }
+
+    size_t stepCount = audioBuffer->size / mFrameSizeAF;
+    if (stepCount == 0) {
+        return;
+    }
+
+    Proxy::Buffer buffer;
+    buffer.mFrameCount = stepCount;
+    buffer.mRaw = audioBuffer->raw;
 
     AutoMutex lock(mLock);
-    audio_track_cblk_t* cblk = mCblk;
-    (void) mProxy->stepUser(audioBuffer->frameCount);
-    if (audioBuffer->frameCount > 0) {
-        // restart track if it was disabled by audioflinger due to previous underrun
-        if (mActive && (cblk->flags & CBLK_DISABLED)) {
-            android_atomic_and(~CBLK_DISABLED, &cblk->flags);
-            ALOGW("releaseBuffer() track %p name=%#x disabled, restarting", this, cblk->mName);
+    mInUnderrun = false;
+    mProxy->releaseBuffer(&buffer);
+
+    // restart track if it was disabled by audioflinger due to previous underrun
+    if (mState == STATE_ACTIVE) {
+        audio_track_cblk_t* cblk = mCblk;
+        if (android_atomic_and(~CBLK_DISABLED, &cblk->flags) & CBLK_DISABLED) {
+            ALOGW("releaseBuffer() track %p name=%#x disabled due to previous underrun, restarting",
+                    this, cblk->mName);
+            // FIXME ignoring status
             mAudioTrack->start();
         }
     }
@@ -1127,68 +1100,46 @@ void AudioTrack::releaseBuffer(Buffer* audioBuffer)
 
 ssize_t AudioTrack::write(const void* buffer, size_t userSize)
 {
-
-    if (mSharedBuffer != 0 || mIsTimed) {
+    if (mTransfer != TRANSFER_SYNC || mIsTimed) {
         return INVALID_OPERATION;
     }
 
-    if (ssize_t(userSize) < 0) {
+    if (ssize_t(userSize) < 0 || (buffer == NULL && userSize != 0)) {
         // Sanity-check: user is most-likely passing an error code, and it would
         // make the return value ambiguous (actualSize vs error).
-        ALOGE("AudioTrack::write(buffer=%p, size=%u (%d)",
-                buffer, userSize, userSize);
+        ALOGE("AudioTrack::write(buffer=%p, size=%u (%d)", buffer, userSize, userSize);
         return BAD_VALUE;
     }
 
-    ALOGV("write %p: %d bytes, mActive=%d", this, userSize, mActive);
-
-    if (userSize == 0) {
-        return 0;
-    }
-
-    // acquire a strong reference on the IMemory and IAudioTrack so that they cannot be destroyed
-    // while we are accessing the cblk
-    mLock.lock();
-    sp<IAudioTrack> audioTrack = mAudioTrack;
-    sp<IMemory> iMem = mCblkMemory;
-    mLock.unlock();
-
-    // since mLock is unlocked the IAudioTrack and shared memory may be re-created,
-    // so all cblk references might still refer to old shared memory, but that should be benign
-
-    ssize_t written = 0;
-    const int8_t *src = (const int8_t *)buffer;
+    size_t written = 0;
     Buffer audioBuffer;
-    size_t frameSz = frameSize();
 
-    do {
-        audioBuffer.frameCount = userSize/frameSz;
+    while (userSize >= mFrameSize) {
+        audioBuffer.frameCount = userSize / mFrameSize;
 
-        status_t err = obtainBuffer(&audioBuffer, -1);
+        status_t err = obtainBuffer(&audioBuffer, &ClientProxy::kForever);
         if (err < 0) {
-            // out of buffers, return #bytes written
-            if (err == status_t(NO_MORE_BUFFERS)) {
+            if (written > 0) {
                 break;
             }
             return ssize_t(err);
         }
 
         size_t toWrite;
-
         if (mFormat == AUDIO_FORMAT_PCM_8_BIT && !(mFlags & AUDIO_OUTPUT_FLAG_DIRECT)) {
             // Divide capacity by 2 to take expansion into account
-            toWrite = audioBuffer.size>>1;
-            memcpy_to_i16_from_u8(audioBuffer.i16, (const uint8_t *) src, toWrite);
+            toWrite = audioBuffer.size >> 1;
+            memcpy_to_i16_from_u8(audioBuffer.i16, (const uint8_t *) buffer, toWrite);
         } else {
             toWrite = audioBuffer.size;
-            memcpy(audioBuffer.i8, src, toWrite);
+            memcpy(audioBuffer.i8, buffer, toWrite);
         }
-        src += toWrite;
+        buffer = ((const char *) buffer) + toWrite;
         userSize -= toWrite;
         written += toWrite;
 
         releaseBuffer(&audioBuffer);
-    } while (userSize >= frameSz);
+    }
 
     return written;
 }
@@ -1204,10 +1155,12 @@ status_t TimedAudioTrack::allocateTimedBuffer(size_t size, sp<IMemory>* buffer)
     AutoMutex lock(mLock);
     status_t result = UNKNOWN_ERROR;
 
+#if 1
     // acquire a strong reference on the IMemory and IAudioTrack so that they cannot be destroyed
     // while we are accessing the cblk
     sp<IAudioTrack> audioTrack = mAudioTrack;
     sp<IMemory> iMem = mCblkMemory;
+#endif
 
     // If the track is not invalid already, try to allocate a buffer.  alloc
     // fails indicating that the server is dead, flag the track as invalid so
@@ -1223,13 +1176,9 @@ status_t TimedAudioTrack::allocateTimedBuffer(size_t size, sp<IMemory>* buffer)
     // If the track is invalid at this point, attempt to restore it. and try the
     // allocation one more time.
     if (cblk->flags & CBLK_INVALID) {
-        cblk->lock.lock();
-        audio_track_cblk_t* temp = cblk;
-        result = restoreTrack_l(temp, false /*fromStart*/);
-        cblk = temp;
-        cblk->lock.unlock();
+        result = restoreTrack_l("allocateTimedBuffer");
 
-        if (result == OK) {
+        if (result == NO_ERROR) {
             result = mAudioTrack->allocateTimedBuffer(size, buffer);
         }
     }
@@ -1246,9 +1195,10 @@ status_t TimedAudioTrack::queueTimedBuffer(const sp<IMemory>& buffer,
         audio_track_cblk_t* cblk = mCblk;
         // restart track if it was disabled by audioflinger due to previous underrun
         if (buffer->size() != 0 && status == NO_ERROR &&
-                mActive && (cblk->flags & CBLK_DISABLED)) {
+                (mState == STATE_ACTIVE) && (cblk->flags & CBLK_DISABLED)) {
             android_atomic_and(~CBLK_DISABLED, &cblk->flags);
             ALOGW("queueTimedBuffer() track %p disabled, restarting", this);
+            // FIXME ignoring status
             mAudioTrack->start();
         }
     }
@@ -1263,12 +1213,8 @@ status_t TimedAudioTrack::setMediaTimeTransform(const LinearTransform& xform,
 
 // -------------------------------------------------------------------------
 
-bool AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread)
+nsecs_t AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread)
 {
-    Buffer audioBuffer;
-    uint32_t frames;
-    size_t writtenSize;
-
     mLock.lock();
     if (mAwaitBoost) {
         mAwaitBoost = false;
@@ -1289,86 +1235,181 @@ bool AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread)
         }
         return true;
     }
-    // acquire a strong reference on the IMemory and IAudioTrack so that they cannot be destroyed
-    // while we are accessing the cblk
-    sp<IAudioTrack> audioTrack = mAudioTrack;
-    sp<IMemory> iMem = mCblkMemory;
-    audio_track_cblk_t* cblk = mCblk;
-    bool active = mActive;
-    mLock.unlock();
 
-    // since mLock is unlocked the IAudioTrack and shared memory may be re-created,
-    // so all cblk references might still refer to old shared memory, but that should be benign
+    // Can only reference mCblk while locked
+    int32_t flags = android_atomic_and(
+        ~(CBLK_UNDERRUN | CBLK_LOOP_CYCLE | CBLK_LOOP_FINAL | CBLK_BUFFER_END), &mCblk->flags);
 
-    // Manage underrun callback
-    if (active && (mProxy->framesAvailable() == mFrameCount)) {
-        ALOGV("Underrun user: %x, server: %x, flags %04x", cblk->user, cblk->server, cblk->flags);
-        if (!(android_atomic_or(CBLK_UNDERRUN, &cblk->flags) & CBLK_UNDERRUN)) {
-            mCbf(EVENT_UNDERRUN, mUserData, 0);
-            if (cblk->server == mFrameCount) {
-                mCbf(EVENT_BUFFER_END, mUserData, 0);
-            }
-            if (mSharedBuffer != 0) {
-                return false;
-            }
-        }
+    // Check for track invalidation
+    if (flags & CBLK_INVALID) {
+        (void) restoreTrack_l("processAudioBuffer");
+        mLock.unlock();
+        // Run again immediately, but with a new IAudioTrack
+        return 0;
     }
 
-    // Manage loop end callback
-    while (mLoopCount > cblk->loopCount) {
-        int loopCount = -1;
-        mLoopCount--;
-        if (mLoopCount >= 0) loopCount = mLoopCount;
+    bool active = mState == STATE_ACTIVE;
 
-        mCbf(EVENT_LOOP_END, mUserData, (void *)&loopCount);
+    // Manage underrun callback, must be done under lock to avoid race with releaseBuffer()
+    bool newUnderrun = false;
+    if (flags & CBLK_UNDERRUN) {
+#if 0
+        // Currently in shared buffer mode, when the server reaches the end of buffer,
+        // the track stays active in continuous underrun state.  It's up to the application
+        // to pause or stop the track, or set the position to a new offset within buffer.
+        // This was some experimental code to auto-pause on underrun.   Keeping it here
+        // in "if 0" so we can re-visit this if we add a real sequencer for shared memory content.
+        if (mTransfer == TRANSFER_SHARED) {
+            mState = STATE_PAUSED;
+            active = false;
+        }
+#endif
+        if (!mInUnderrun) {
+            mInUnderrun = true;
+            newUnderrun = true;
+        }
     }
 
+    // Get current position of server
+    size_t position = mProxy->getPosition();
+
     // Manage marker callback
-    if (!mMarkerReached && (mMarkerPosition > 0)) {
-        if (cblk->server >= mMarkerPosition) {
-            mCbf(EVENT_MARKER, mUserData, (void *)&mMarkerPosition);
-            mMarkerReached = true;
-        }
+    bool markerReached = false;
+    size_t markerPosition = mMarkerPosition;
+    // FIXME fails for wraparound, need 64 bits
+    if (!mMarkerReached && (markerPosition > 0) && (position >= markerPosition)) {
+        mMarkerReached = markerReached = true;
+    }
+
+    // Determine number of new position callback(s) that will be needed, while locked
+    size_t newPosCount = 0;
+    size_t newPosition = mNewPosition;
+    size_t updatePeriod = mUpdatePeriod;
+    // FIXME fails for wraparound, need 64 bits
+    if (updatePeriod > 0 && position >= newPosition) {
+        newPosCount = ((position - newPosition) / updatePeriod) + 1;
+        mNewPosition += updatePeriod * newPosCount;
+    }
+
+    // Cache other fields that will be needed soon
+    uint32_t loopPeriod = mLoopPeriod;
+    uint32_t sampleRate = mSampleRate;
+    size_t notificationFrames = mNotificationFramesAct;
+    if (mRefreshRemaining) {
+        mRefreshRemaining = false;
+        mRemainingFrames = notificationFrames;
+        mRetryOnPartialBuffer = false;
+    }
+    size_t misalignment = mProxy->getMisalignment();
+    int32_t sequence = mSequence;
+
+    // These fields don't need to be cached, because they are assigned only by set():
+    //     mTransfer, mCbf, mUserData, mFormat, mFrameSize, mFrameSizeAF, mFlags
+    // mFlags is also assigned by createTrack_l(), but not the bit we care about.
+
+    mLock.unlock();
+
+    // perform callbacks while unlocked
+    if (newUnderrun) {
+        mCbf(EVENT_UNDERRUN, mUserData, NULL);
+    }
+    // FIXME we will miss loops if loop cycle was signaled several times since last call
+    //       to processAudioBuffer()
+    if (flags & (CBLK_LOOP_CYCLE | CBLK_LOOP_FINAL)) {
+        mCbf(EVENT_LOOP_END, mUserData, NULL);
+    }
+    if (flags & CBLK_BUFFER_END) {
+        mCbf(EVENT_BUFFER_END, mUserData, NULL);
+    }
+    if (markerReached) {
+        mCbf(EVENT_MARKER, mUserData, &markerPosition);
+    }
+    while (newPosCount > 0) {
+        size_t temp = newPosition;
+        mCbf(EVENT_NEW_POS, mUserData, &temp);
+        newPosition += updatePeriod;
+        newPosCount--;
+    }
+    if (mObservedSequence != sequence) {
+        mObservedSequence = sequence;
+        mCbf(EVENT_NEW_IAUDIOTRACK, mUserData, NULL);
     }
 
-    // Manage new position callback
-    if (mUpdatePeriod > 0) {
-        while (cblk->server >= mNewPosition) {
-            mCbf(EVENT_NEW_POS, mUserData, (void *)&mNewPosition);
-            mNewPosition += mUpdatePeriod;
-        }
+    // if inactive, then don't run me again until re-started
+    if (!active) {
+        return NS_INACTIVE;
     }
 
-    // If Shared buffer is used, no data is requested from client.
-    if (mSharedBuffer != 0) {
-        frames = 0;
-    } else {
-        frames = mRemainingFrames;
+    // Compute the estimated time until the next timed event (position, markers, loops)
+    // FIXME only for non-compressed audio
+    uint32_t minFrames = ~0;
+    if (!markerReached && position < markerPosition) {
+        minFrames = markerPosition - position;
+    }
+    if (loopPeriod > 0 && loopPeriod < minFrames) {
+        minFrames = loopPeriod;
+    }
+    if (updatePeriod > 0 && updatePeriod < minFrames) {
+        minFrames = updatePeriod;
     }
 
-    // See description of waitCount parameter at declaration of obtainBuffer().
-    // The logic below prevents us from being stuck below at obtainBuffer()
-    // not being able to handle timed events (position, markers, loops).
-    int32_t waitCount = -1;
-    if (mUpdatePeriod || (!mMarkerReached && mMarkerPosition) || mLoopCount) {
-        waitCount = 1;
+    // If > 0, poll periodically to recover from a stuck server.  A good value is 2.
+    static const uint32_t kPoll = 0;
+    if (kPoll > 0 && mTransfer == TRANSFER_CALLBACK && kPoll * notificationFrames < minFrames) {
+        minFrames = kPoll * notificationFrames;
     }
 
-    do {
+    // Convert frame units to time units
+    nsecs_t ns = NS_WHENEVER;
+    if (minFrames != (uint32_t) ~0) {
+        // This "fudge factor" avoids soaking CPU, and compensates for late progress by server
+        static const nsecs_t kFudgeNs = 10000000LL; // 10 ms
+        ns = ((minFrames * 1000000000LL) / sampleRate) + kFudgeNs;
+    }
+
+    // If not supplying data by EVENT_MORE_DATA, then we're done
+    if (mTransfer != TRANSFER_CALLBACK) {
+        return ns;
+    }
 
-        audioBuffer.frameCount = frames;
+    struct timespec timeout;
+    const struct timespec *requested = &ClientProxy::kForever;
+    if (ns != NS_WHENEVER) {
+        timeout.tv_sec = ns / 1000000000LL;
+        timeout.tv_nsec = ns % 1000000000LL;
+        ALOGV("timeout %ld.%03d", timeout.tv_sec, (int) timeout.tv_nsec / 1000000);
+        requested = &timeout;
+    }
+
+    while (mRemainingFrames > 0) {
 
-        status_t err = obtainBuffer(&audioBuffer, waitCount);
-        if (err < NO_ERROR) {
-            if (err != TIMED_OUT) {
-                ALOGE_IF(err != status_t(NO_MORE_BUFFERS),
-                        "Error obtaining an audio buffer, giving up.");
-                return false;
+        Buffer audioBuffer;
+        audioBuffer.frameCount = mRemainingFrames;
+        size_t nonContig;
+        status_t err = obtainBuffer(&audioBuffer, requested, NULL, &nonContig);
+        LOG_ALWAYS_FATAL_IF((err != NO_ERROR) != (audioBuffer.frameCount == 0),
+                "obtainBuffer() err=%d frameCount=%u", err, audioBuffer.frameCount);
+        requested = &ClientProxy::kNonBlocking;
+        size_t avail = audioBuffer.frameCount + nonContig;
+        ALOGV("obtainBuffer(%u) returned %u = %u + %u",
+                mRemainingFrames, avail, audioBuffer.frameCount, nonContig);
+        if (err != NO_ERROR) {
+            if (err == TIMED_OUT || err == WOULD_BLOCK || err == -EINTR) {
+                return 0;
             }
-            break;
+            ALOGE("Error %d obtaining an audio buffer, giving up.", err);
+            return NS_NEVER;
         }
-        if (err == status_t(STOPPED)) {
-            return false;
+
+        if (mRetryOnPartialBuffer) {
+            mRetryOnPartialBuffer = false;
+            if (avail < mRemainingFrames) {
+                int64_t myns = ((mRemainingFrames - avail) * 1100000000LL) / sampleRate;
+                if (ns < 0 || myns < ns) {
+                    ns = myns;
+                }
+                return ns;
+            }
         }
 
         // Divide buffer size by 2 to take into account the expansion
@@ -1380,66 +1421,76 @@ bool AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread)
 
         size_t reqSize = audioBuffer.size;
         mCbf(EVENT_MORE_DATA, mUserData, &audioBuffer);
-        writtenSize = audioBuffer.size;
+        size_t writtenSize = audioBuffer.size;
+        size_t writtenFrames = writtenSize / mFrameSize;
 
         // Sanity check on returned size
-        if (ssize_t(writtenSize) <= 0) {
+        if (ssize_t(writtenSize) < 0 || writtenSize > reqSize) {
+            ALOGE("EVENT_MORE_DATA requested %u bytes but callback returned %d bytes",
+                    reqSize, (int) writtenSize);
+            return NS_NEVER;
+        }
+
+        if (writtenSize == 0) {
             // The callback is done filling buffers
             // Keep this thread going to handle timed events and
             // still try to get more data in intervals of WAIT_PERIOD_MS
             // but don't just loop and block the CPU, so wait
-            usleep(WAIT_PERIOD_MS*1000);
-            break;
-        }
-
-        if (writtenSize > reqSize) {
-            writtenSize = reqSize;
+            return WAIT_PERIOD_MS * 1000000LL;
         }
 
         if (mFormat == AUDIO_FORMAT_PCM_8_BIT && !(mFlags & AUDIO_OUTPUT_FLAG_DIRECT)) {
             // 8 to 16 bit conversion, note that source and destination are the same address
             memcpy_to_i16_from_u8(audioBuffer.i16, (const uint8_t *) audioBuffer.i8, writtenSize);
-            writtenSize <<= 1;
+            audioBuffer.size <<= 1;
         }
 
-        audioBuffer.size = writtenSize;
-        // NOTE: cblk->frameSize is not equal to AudioTrack::frameSize() for
-        // 8 bit PCM data: in this case,  cblk->frameSize is based on a sample size of
-        // 16 bit.
-        audioBuffer.frameCount = writtenSize / mFrameSizeAF;
-
-        frames -= audioBuffer.frameCount;
+        size_t releasedFrames = audioBuffer.size / mFrameSizeAF;
+        audioBuffer.frameCount = releasedFrames;
+        mRemainingFrames -= releasedFrames;
+        if (misalignment >= releasedFrames) {
+            misalignment -= releasedFrames;
+        } else {
+            misalignment = 0;
+        }
 
         releaseBuffer(&audioBuffer);
-    }
-    while (frames);
 
-    if (frames == 0) {
-        mRemainingFrames = mNotificationFramesAct;
-    } else {
-        mRemainingFrames = frames;
+        // FIXME here is where we would repeat EVENT_MORE_DATA again on same advanced buffer
+        // if callback doesn't like to accept the full chunk
+        if (writtenSize < reqSize) {
+            continue;
+        }
+
+        // There could be enough non-contiguous frames available to satisfy the remaining request
+        if (mRemainingFrames <= nonContig) {
+            continue;
+        }
+
+#if 0
+        // This heuristic tries to collapse a series of EVENT_MORE_DATA that would total to a
+        // sum <= notificationFrames.  It replaces that series by at most two EVENT_MORE_DATA
+        // that total to a sum == notificationFrames.
+        if (0 < misalignment && misalignment <= mRemainingFrames) {
+            mRemainingFrames = misalignment;
+            return (mRemainingFrames * 1100000000LL) / sampleRate;
+        }
+#endif
+
     }
-    return true;
+    mRemainingFrames = notificationFrames;
+    mRetryOnPartialBuffer = true;
+
+    // A lot has transpired since ns was calculated, so run again immediately and re-calculate
+    return 0;
 }
 
-// must be called with mLock and refCblk.lock held. Callers must also hold strong references on
-// the IAudioTrack and IMemory in case they are recreated here.
-// If the IAudioTrack is successfully restored, the refCblk pointer is updated
-// FIXME Don't depend on caller to hold strong references.
-status_t AudioTrack::restoreTrack_l(audio_track_cblk_t*& refCblk, bool fromStart)
+status_t AudioTrack::restoreTrack_l(const char *from)
 {
+    ALOGW("dead IAudioTrack, creating a new one from %s()", from);
+    ++mSequence;
     status_t result;
 
-    audio_track_cblk_t* cblk = refCblk;
-    audio_track_cblk_t* newCblk = cblk;
-    ALOGW("dead IAudioTrack, creating a new one from %s",
-        fromStart ? "start()" : "obtainBuffer()");
-
-    // signal old cblk condition so that other threads waiting for available buffers stop
-    // waiting now
-    cblk->cv.broadcast();
-    cblk->lock.unlock();
-
     // refresh the audio configuration cache in this process to make sure we get new
     // output parameters in getOutput_l() and createTrack_l()
     AudioSystem::clearAudioConfigCache();
@@ -1447,68 +1498,47 @@ status_t AudioTrack::restoreTrack_l(audio_track_cblk_t*& refCblk, bool fromStart
     // if the new IAudioTrack is created, createTrack_l() will modify the
     // following member variables: mAudioTrack, mCblkMemory and mCblk.
     // It will also delete the strong references on previous IAudioTrack and IMemory
+    size_t position = mProxy->getPosition();
+    mNewPosition = position + mUpdatePeriod;
+    size_t bufferPosition = mStaticProxy != NULL ? mStaticProxy->getBufferPosition() : 0;
     result = createTrack_l(mStreamType,
                            mSampleRate,
                            mFormat,
                            mReqFrameCount,  // so that frame count never goes down
                            mFlags,
                            mSharedBuffer,
-                           getOutput_l());
+                           getOutput_l(),
+                           position /*epoch*/);
 
     if (result == NO_ERROR) {
-        uint32_t user = cblk->user;
-        uint32_t server = cblk->server;
+        // continue playback from last known position, but
+        // don't attempt to restore loop after invalidation; it's difficult and not worthwhile
+        if (mStaticProxy != NULL) {
+            mLoopPeriod = 0;
+            mStaticProxy->setLoop(bufferPosition, mFrameCount, 0);
+        }
+        // FIXME How do we simulate the fact that all frames present in the buffer at the time of
+        //       track destruction have been played? This is critical for SoundPool implementation
+        //       This must be broken, and needs to be tested/debugged.
+#if 0
         // restore write index and set other indexes to reflect empty buffer status
-        newCblk = mCblk;
-        newCblk->user = user;
-        newCblk->server = user;
-        newCblk->userBase = user;
-        newCblk->serverBase = user;
-        // restore loop: this is not guaranteed to succeed if new frame count is not
-        // compatible with loop length
-        setLoop_l(cblk->loopStart, cblk->loopEnd, cblk->loopCount);
-        size_t frames = 0;
-        if (!fromStart) {
-            newCblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS;
+        if (!strcmp(from, "start")) {
             // Make sure that a client relying on callback events indicating underrun or
             // the actual amount of audio frames played (e.g SoundPool) receives them.
             if (mSharedBuffer == 0) {
-                if (user > server) {
-                    frames = ((user - server) > mFrameCount) ?
-                            mFrameCount : (user - server);
-                    memset(mBuffers, 0, frames * mFrameSizeAF);
-                }
                 // restart playback even if buffer is not completely filled.
-                android_atomic_or(CBLK_FORCEREADY, &newCblk->flags);
+                android_atomic_or(CBLK_FORCEREADY, &mCblk->flags);
             }
         }
-        if (mSharedBuffer != 0) {
-            frames = mFrameCount;
-        }
-        if (frames > 0) {
-            // stepUser() clears CBLK_UNDERRUN flag enabling underrun callbacks to
-            // the client
-            mProxy->stepUser(frames);
-        }
-        if (mActive) {
+#endif
+        if (mState == STATE_ACTIVE) {
             result = mAudioTrack->start();
-            ALOGW_IF(result != NO_ERROR, "restoreTrack_l() start() failed status %d", result);
-        }
-        if (fromStart && result == NO_ERROR) {
-            mNewPosition = newCblk->server + mUpdatePeriod;
         }
     }
-    ALOGW_IF(result != NO_ERROR, "restoreTrack_l() failed status %d", result);
-    ALOGV("restoreTrack_l() status %d mActive %d cblk %p, old cblk %p flags %08x old flags %08x",
-        result, mActive, newCblk, cblk, newCblk->flags, cblk->flags);
-
-    if (result == NO_ERROR) {
-        // from now on we switch to the newly created cblk
-        refCblk = newCblk;
+    if (result != NO_ERROR) {
+        ALOGW("restoreTrack_l() failed status %d", result);
+        mState = STATE_STOPPED;
     }
-    newCblk->lock.lock();
-
-    ALOGW_IF(result != NO_ERROR, "restoreTrack_l() error %d", result);
 
     return result;
 }
@@ -1529,16 +1559,33 @@ status_t AudioTrack::dump(int fd, const Vector<String16>& args) const
     result.append(buffer);
     snprintf(buffer, 255, "  sample rate(%u), status(%d)\n", mSampleRate, mStatus);
     result.append(buffer);
-    snprintf(buffer, 255, "  active(%d), latency (%d)\n", mActive, mLatency);
+    snprintf(buffer, 255, "  state(%d), latency (%d)\n", mState, mLatency);
     result.append(buffer);
     ::write(fd, result.string(), result.size());
     return NO_ERROR;
 }
 
+uint32_t AudioTrack::getUnderrunFrames() const
+{
+    AutoMutex lock(mLock);
+    return mProxy->getUnderrunFrames();
+}
+
+// =========================================================================
+
+void AudioTrack::DeathNotifier::binderDied(const wp<IBinder>& who)
+{
+    sp<AudioTrack> audioTrack = mAudioTrack.promote();
+    if (audioTrack != 0) {
+        AutoMutex lock(audioTrack->mLock);
+        audioTrack->mProxy->binderDied();
+    }
+}
+
 // =========================================================================
 
 AudioTrack::AudioTrackThread::AudioTrackThread(AudioTrack& receiver, bool bCanCallJava)
-    : Thread(bCanCallJava), mReceiver(receiver), mPaused(true)
+    : Thread(bCanCallJava), mReceiver(receiver), mPaused(true), mResumeLatch(false)
 {
 }
 
@@ -1556,10 +1603,26 @@ bool AudioTrack::AudioTrackThread::threadLoop()
             return true;
         }
     }
-    if (!mReceiver.processAudioBuffer(this)) {
-        pause();
+    nsecs_t ns = mReceiver.processAudioBuffer(this);
+    switch (ns) {
+    case 0:
+        return true;
+    case NS_WHENEVER:
+        sleep(1);
+        return true;
+    case NS_INACTIVE:
+        pauseConditional();
+        return true;
+    case NS_NEVER:
+        return false;
+    default:
+        LOG_ALWAYS_FATAL_IF(ns < 0, "processAudioBuffer() returned %lld", ns);
+        struct timespec req;
+        req.tv_sec = ns / 1000000000LL;
+        req.tv_nsec = ns % 1000000000LL;
+        nanosleep(&req, NULL /*rem*/);
+        return true;
     }
-    return true;
 }
 
 void AudioTrack::AudioTrackThread::requestExit()
@@ -1573,6 +1636,17 @@ void AudioTrack::AudioTrackThread::pause()
 {
     AutoMutex _l(mMyLock);
     mPaused = true;
+    mResumeLatch = false;
+}
+
+void AudioTrack::AudioTrackThread::pauseConditional()
+{
+    AutoMutex _l(mMyLock);
+    if (mResumeLatch) {
+        mResumeLatch = false;
+    } else {
+        mPaused = true;
+    }
 }
 
 void AudioTrack::AudioTrackThread::resume()
@@ -1580,7 +1654,10 @@ void AudioTrack::AudioTrackThread::resume()
     AutoMutex _l(mMyLock);
     if (mPaused) {
         mPaused = false;
+        mResumeLatch = false;
         mMyCond.signal();
+    } else {
+        mResumeLatch = true;
     }
 }
 
index 13d47c9..f034164 100644 (file)
 
 #include <private/media/AudioTrackShared.h>
 #include <utils/Log.h>
+extern "C" {
+#include "../private/bionic_futex.h"
+}
 
 namespace android {
 
 audio_track_cblk_t::audio_track_cblk_t()
-    : lock(Mutex::SHARED), cv(Condition::SHARED), user(0), server(0),
-    userBase(0), serverBase(0), frameCount_(0),
-    loopStart(UINT_MAX), loopEnd(UINT_MAX), loopCount(0), mVolumeLR(0x10001000),
-    mSampleRate(0), mSendLevel(0), flags(0)
+    : server(0), frameCount_(0), mFutex(0), mMinimum(0),
+    mVolumeLR(0x10001000), mSampleRate(0), mSendLevel(0), mName(0), flags(0)
 {
+    memset(&u, 0, sizeof(u));
 }
 
-uint32_t audio_track_cblk_t::stepUser(size_t stepCount, size_t frameCount, bool isOut)
+// ---------------------------------------------------------------------------
+
+Proxy::Proxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount, size_t frameSize,
+        bool isOut, bool clientInServer)
+    : mCblk(cblk), mBuffers(buffers), mFrameCount(frameCount), mFrameSize(frameSize),
+      mFrameCountP2(roundup(frameCount)), mIsOut(isOut), mClientInServer(clientInServer),
+      mIsShutdown(false)
 {
-    ALOGV("stepuser %08x %08x %d", user, server, stepCount);
+}
 
-    uint32_t u = user;
-    u += stepCount;
-    // Ensure that user is never ahead of server for AudioRecord
-    if (isOut) {
-        // If stepServer() has been called once, switch to normal obtainBuffer() timeout period
-        if (bufferTimeoutMs == MAX_STARTUP_TIMEOUT_MS-1) {
-            bufferTimeoutMs = MAX_RUN_TIMEOUT_MS;
-        }
-    } else if (u > server) {
-        ALOGW("stepUser occurred after track reset");
-        u = server;
+// ---------------------------------------------------------------------------
+
+ClientProxy::ClientProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount,
+        size_t frameSize, bool isOut, bool clientInServer)
+    : Proxy(cblk, buffers, frameCount, frameSize, isOut, clientInServer), mEpoch(0)
+{
+}
+
+const struct timespec ClientProxy::kForever = {INT_MAX /*tv_sec*/, 0 /*tv_nsec*/};
+const struct timespec ClientProxy::kNonBlocking = {0 /*tv_sec*/, 0 /*tv_nsec*/};
+
+#define MEASURE_NS 10000000 // attempt to provide accurate timeouts if requested >= MEASURE_NS
+
+// To facilitate quicker recovery from server failure, this value limits the timeout per each futex
+// wait.  However it does not protect infinite timeouts.  If defined to be zero, there is no limit.
+// FIXME May not be compatible with audio tunneling requirements where timeout should be in the
+// order of minutes.
+#define MAX_SEC    5
+
+status_t ClientProxy::obtainBuffer(Buffer* buffer, const struct timespec *requested,
+        struct timespec *elapsed)
+{
+    if (buffer == NULL || buffer->mFrameCount == 0) {
+        ALOGE("%s BAD_VALUE", __func__);
+        return BAD_VALUE;
     }
+    struct timespec total;          // total elapsed time spent waiting
+    total.tv_sec = 0;
+    total.tv_nsec = 0;
+    bool measure = elapsed != NULL; // whether to measure total elapsed time spent waiting
 
-    if (u >= frameCount) {
-        // common case, user didn't just wrap
-        if (u - frameCount >= userBase ) {
-            userBase += frameCount;
+    status_t status;
+    enum {
+        TIMEOUT_ZERO,       // requested == NULL || *requested == 0
+        TIMEOUT_INFINITE,   // *requested == infinity
+        TIMEOUT_FINITE,     // 0 < *requested < infinity
+        TIMEOUT_CONTINUE,   // additional chances after TIMEOUT_FINITE
+    } timeout;
+    if (requested == NULL) {
+        timeout = TIMEOUT_ZERO;
+    } else if (requested->tv_sec == 0 && requested->tv_nsec == 0) {
+        timeout = TIMEOUT_ZERO;
+    } else if (requested->tv_sec == INT_MAX) {
+        timeout = TIMEOUT_INFINITE;
+    } else {
+        timeout = TIMEOUT_FINITE;
+        if (requested->tv_sec > 0 || requested->tv_nsec >= MEASURE_NS) {
+            measure = true;
+        }
+    }
+    struct timespec before;
+    bool beforeIsValid = false;
+    audio_track_cblk_t* cblk = mCblk;
+    bool ignoreInitialPendingInterrupt = true;
+    // check for shared memory corruption
+    if (mIsShutdown) {
+        status = NO_INIT;
+        goto end;
+    }
+    for (;;) {
+        int32_t flags = android_atomic_and(~CBLK_INTERRUPT, &cblk->flags);
+        // check for track invalidation by server, or server death detection
+        if (flags & CBLK_INVALID) {
+            ALOGV("Track invalidated");
+            status = DEAD_OBJECT;
+            goto end;
+        }
+        // check for obtainBuffer interrupted by client
+        if (!ignoreInitialPendingInterrupt && (flags & CBLK_INTERRUPT)) {
+            ALOGV("obtainBuffer() interrupted by client");
+            status = -EINTR;
+            goto end;
+        }
+        ignoreInitialPendingInterrupt = false;
+        // compute number of frames available to write (AudioTrack) or read (AudioRecord)
+        int32_t front;
+        int32_t rear;
+        if (mIsOut) {
+            // The barrier following the read of mFront is probably redundant.
+            // We're about to perform a conditional branch based on 'filled',
+            // which will force the processor to observe the read of mFront
+            // prior to allowing data writes starting at mRaw.
+            // However, the processor may support speculative execution,
+            // and be unable to undo speculative writes into shared memory.
+            // The barrier will prevent such speculative execution.
+            front = android_atomic_acquire_load(&cblk->u.mStreaming.mFront);
+            rear = cblk->u.mStreaming.mRear;
+        } else {
+            // On the other hand, this barrier is required.
+            rear = android_atomic_acquire_load(&cblk->u.mStreaming.mRear);
+            front = cblk->u.mStreaming.mFront;
+        }
+        ssize_t filled = rear - front;
+        // pipe should not be overfull
+        if (!(0 <= filled && (size_t) filled <= mFrameCount)) {
+            ALOGE("Shared memory control block is corrupt (filled=%d); shutting down", filled);
+            mIsShutdown = true;
+            status = NO_INIT;
+            goto end;
+        }
+        // don't allow filling pipe beyond the nominal size
+        size_t avail = mIsOut ? mFrameCount - filled : filled;
+        if (avail > 0) {
+            // 'avail' may be non-contiguous, so return only the first contiguous chunk
+            size_t part1;
+            if (mIsOut) {
+                rear &= mFrameCountP2 - 1;
+                part1 = mFrameCountP2 - rear;
+            } else {
+                front &= mFrameCountP2 - 1;
+                part1 = mFrameCountP2 - front;
+            }
+            if (part1 > avail) {
+                part1 = avail;
+            }
+            if (part1 > buffer->mFrameCount) {
+                part1 = buffer->mFrameCount;
+            }
+            buffer->mFrameCount = part1;
+            buffer->mRaw = part1 > 0 ?
+                    &((char *) mBuffers)[(mIsOut ? rear : front) * mFrameSize] : NULL;
+            buffer->mNonContig = avail - part1;
+            // mUnreleased = part1;
+            status = NO_ERROR;
+            break;
+        }
+        struct timespec remaining;
+        const struct timespec *ts;
+        switch (timeout) {
+        case TIMEOUT_ZERO:
+            status = WOULD_BLOCK;
+            goto end;
+        case TIMEOUT_INFINITE:
+            ts = NULL;
+            break;
+        case TIMEOUT_FINITE:
+            timeout = TIMEOUT_CONTINUE;
+            if (MAX_SEC == 0) {
+                ts = requested;
+                break;
+            }
+            // fall through
+        case TIMEOUT_CONTINUE:
+            // FIXME we do not retry if requested < 10ms? needs documentation on this state machine
+            if (!measure || requested->tv_sec < total.tv_sec ||
+                    (requested->tv_sec == total.tv_sec && requested->tv_nsec <= total.tv_nsec)) {
+                status = TIMED_OUT;
+                goto end;
+            }
+            remaining.tv_sec = requested->tv_sec - total.tv_sec;
+            if ((remaining.tv_nsec = requested->tv_nsec - total.tv_nsec) < 0) {
+                remaining.tv_nsec += 1000000000;
+                remaining.tv_sec++;
+            }
+            if (0 < MAX_SEC && MAX_SEC < remaining.tv_sec) {
+                remaining.tv_sec = MAX_SEC;
+                remaining.tv_nsec = 0;
+            }
+            ts = &remaining;
+            break;
+        default:
+            LOG_FATAL("%s timeout=%d", timeout);
+            ts = NULL;
+            break;
+        }
+        int32_t old = android_atomic_dec(&cblk->mFutex);
+        if (old <= 0) {
+            int rc;
+            if (measure && !beforeIsValid) {
+                clock_gettime(CLOCK_MONOTONIC, &before);
+                beforeIsValid = true;
+            }
+            int ret = __futex_syscall4(&cblk->mFutex,
+                    mClientInServer ? FUTEX_WAIT_PRIVATE : FUTEX_WAIT, old - 1, ts);
+            // update total elapsed time spent waiting
+            if (measure) {
+                struct timespec after;
+                clock_gettime(CLOCK_MONOTONIC, &after);
+                total.tv_sec += after.tv_sec - before.tv_sec;
+                long deltaNs = after.tv_nsec - before.tv_nsec;
+                if (deltaNs < 0) {
+                    deltaNs += 1000000000;
+                    total.tv_sec--;
+                }
+                if ((total.tv_nsec += deltaNs) >= 1000000000) {
+                    total.tv_nsec -= 1000000000;
+                    total.tv_sec++;
+                }
+                before = after;
+                beforeIsValid = true;
+            }
+            switch (ret) {
+            case 0:             // normal wakeup by server, or by binderDied()
+            case -EWOULDBLOCK:  // benign race condition with server
+            case -EINTR:        // wait was interrupted by signal or other spurious wakeup
+            case -ETIMEDOUT:    // time-out expired
+                break;
+            default:
+                ALOGE("%s unexpected error %d", __func__, ret);
+                status = -ret;
+                goto end;
+            }
         }
-    } else if (u >= userBase + frameCount) {
-        // user just wrapped
-        userBase += frameCount;
     }
 
-    user = u;
-
-    // Clear flow control error condition as new data has been written/read to/from buffer.
-    if (flags & CBLK_UNDERRUN) {
-        android_atomic_and(~CBLK_UNDERRUN, &flags);
+end:
+    if (status != NO_ERROR) {
+        buffer->mFrameCount = 0;
+        buffer->mRaw = NULL;
+        buffer->mNonContig = 0;
+    }
+    if (elapsed != NULL) {
+        *elapsed = total;
     }
+    if (requested == NULL) {
+        requested = &kNonBlocking;
+    }
+    if (measure) {
+        ALOGV("requested %d.%03d elapsed %d.%03d", requested->tv_sec, requested->tv_nsec / 1000000,
+                total.tv_sec, total.tv_nsec / 1000000);
+    }
+    return status;
+}
 
-    return u;
+void ClientProxy::releaseBuffer(Buffer* buffer)
+{
+    size_t stepCount = buffer->mFrameCount;
+    // FIXME
+    //  check mUnreleased
+    //  verify that stepCount <= frameCount returned by the last obtainBuffer()
+    //  verify stepCount not > total frame count of pipe
+    if (stepCount == 0) {
+        return;
+    }
+    audio_track_cblk_t* cblk = mCblk;
+    // Both of these barriers are required
+    if (mIsOut) {
+        int32_t rear = cblk->u.mStreaming.mRear;
+        android_atomic_release_store(stepCount + rear, &cblk->u.mStreaming.mRear);
+    } else {
+        int32_t front = cblk->u.mStreaming.mFront;
+        android_atomic_release_store(stepCount + front, &cblk->u.mStreaming.mFront);
+    }
 }
 
-bool audio_track_cblk_t::stepServer(size_t stepCount, size_t frameCount, bool isOut)
+void ClientProxy::binderDied()
 {
-    ALOGV("stepserver %08x %08x %d", user, server, stepCount);
+    audio_track_cblk_t* cblk = mCblk;
+    if (!(android_atomic_or(CBLK_INVALID, &cblk->flags) & CBLK_INVALID)) {
+        // it seems that a FUTEX_WAKE_PRIVATE will not wake a FUTEX_WAIT, even within same process
+        (void) __futex_syscall3(&cblk->mFutex, mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE,
+                1);
+    }
+}
 
-    if (!tryLock()) {
-        ALOGW("stepServer() could not lock cblk");
-        return false;
+void ClientProxy::interrupt()
+{
+    audio_track_cblk_t* cblk = mCblk;
+    if (!(android_atomic_or(CBLK_INTERRUPT, &cblk->flags) & CBLK_INTERRUPT)) {
+        (void) __futex_syscall3(&cblk->mFutex, mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE,
+                1);
     }
+}
 
-    uint32_t s = server;
-    bool flushed = (s == user);
+size_t ClientProxy::getMisalignment()
+{
+    audio_track_cblk_t* cblk = mCblk;
+    return (mFrameCountP2 - (mIsOut ? cblk->u.mStreaming.mRear : cblk->u.mStreaming.mFront)) &
+            (mFrameCountP2 - 1);
+}
 
-    s += stepCount;
-    if (isOut) {
-        // Mark that we have read the first buffer so that next time stepUser() is called
-        // we switch to normal obtainBuffer() timeout period
-        if (bufferTimeoutMs == MAX_STARTUP_TIMEOUT_MS) {
-            bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS - 1;
-        }
-        // It is possible that we receive a flush()
-        // while the mixer is processing a block: in this case,
-        // stepServer() is called After the flush() has reset u & s and
-        // we have s > u
-        if (flushed) {
-            ALOGW("stepServer occurred after track reset");
-            s = user;
+// ---------------------------------------------------------------------------
+
+void AudioTrackClientProxy::flush()
+{
+    mCblk->u.mStreaming.mFlush++;
+}
+
+// ---------------------------------------------------------------------------
+
+StaticAudioTrackClientProxy::StaticAudioTrackClientProxy(audio_track_cblk_t* cblk, void *buffers,
+        size_t frameCount, size_t frameSize)
+    : AudioTrackClientProxy(cblk, buffers, frameCount, frameSize),
+      mMutator(&cblk->u.mStatic.mSingleStateQueue), mBufferPosition(0)
+{
+}
+
+void StaticAudioTrackClientProxy::flush()
+{
+    LOG_FATAL("static flush");
+}
+
+void StaticAudioTrackClientProxy::setLoop(size_t loopStart, size_t loopEnd, int loopCount)
+{
+    StaticAudioTrackState newState;
+    newState.mLoopStart = loopStart;
+    newState.mLoopEnd = loopEnd;
+    newState.mLoopCount = loopCount;
+    mBufferPosition = loopStart;
+    (void) mMutator.push(newState);
+}
+
+size_t StaticAudioTrackClientProxy::getBufferPosition()
+{
+    size_t bufferPosition;
+    if (mMutator.ack()) {
+        bufferPosition = mCblk->u.mStatic.mBufferPosition;
+        if (bufferPosition > mFrameCount) {
+            bufferPosition = mFrameCount;
         }
+    } else {
+        bufferPosition = mBufferPosition;
     }
+    return bufferPosition;
+}
+
+// ---------------------------------------------------------------------------
 
-    if (s >= loopEnd) {
-        ALOGW_IF(s > loopEnd, "stepServer: s %u > loopEnd %u", s, loopEnd);
-        s = loopStart;
-        if (--loopCount == 0) {
-            loopEnd = UINT_MAX;
-            loopStart = UINT_MAX;
+ServerProxy::ServerProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount,
+        size_t frameSize, bool isOut, bool clientInServer)
+    : Proxy(cblk, buffers, frameCount, frameSize, isOut, clientInServer), mUnreleased(0),
+      mAvailToClient(0), mFlush(0), mDeferWake(false)
+{
+}
+
+status_t ServerProxy::obtainBuffer(Buffer* buffer)
+{
+    if (mIsShutdown) {
+        buffer->mFrameCount = 0;
+        buffer->mRaw = NULL;
+        buffer->mNonContig = 0;
+        mUnreleased = 0;
+        return NO_INIT;
+    }
+    audio_track_cblk_t* cblk = mCblk;
+    // compute number of frames available to write (AudioTrack) or read (AudioRecord),
+    // or use previous cached value from framesReady(), with added barrier if it omits.
+    int32_t front;
+    int32_t rear;
+    // See notes on barriers at ClientProxy::obtainBuffer()
+    if (mIsOut) {
+        int32_t flush = cblk->u.mStreaming.mFlush;
+        rear = android_atomic_acquire_load(&cblk->u.mStreaming.mRear);
+        if (flush != mFlush) {
+            front = rear;
+            mFlush = flush;
+        } else {
+            front = cblk->u.mStreaming.mFront;
         }
+    } else {
+        front = android_atomic_acquire_load(&cblk->u.mStreaming.mFront);
+        rear = cblk->u.mStreaming.mRear;
+    }
+    ssize_t filled = rear - front;
+    // pipe should not already be overfull
+    if (!(0 <= filled && (size_t) filled <= mFrameCount)) {
+        ALOGE("Shared memory control block is corrupt (filled=%d); shutting down", filled);
+        mIsShutdown = true;
+    }
+    if (mIsShutdown) {
+        buffer->mFrameCount = 0;
+        buffer->mRaw = NULL;
+        buffer->mNonContig = 0;
+        mUnreleased = 0;
+        return NO_INIT;
     }
+    // don't allow filling pipe beyond the nominal size
+    size_t availToServer;
+    if (mIsOut) {
+        availToServer = filled;
+        mAvailToClient = mFrameCount - filled;
+    } else {
+        availToServer = mFrameCount - filled;
+        mAvailToClient = filled;
+    }
+    // 'availToServer' may be non-contiguous, so return only the first contiguous chunk
+    size_t part1;
+    if (mIsOut) {
+        front &= mFrameCountP2 - 1;
+        part1 = mFrameCountP2 - front;
+    } else {
+        rear &= mFrameCountP2 - 1;
+        part1 = mFrameCountP2 - rear;
+    }
+    if (part1 > availToServer) {
+        part1 = availToServer;
+    }
+    size_t ask = buffer->mFrameCount;
+    if (part1 > ask) {
+        part1 = ask;
+    }
+    // is assignment redundant in some cases?
+    buffer->mFrameCount = part1;
+    buffer->mRaw = part1 > 0 ?
+            &((char *) mBuffers)[(mIsOut ? front : rear) * mFrameSize] : NULL;
+    buffer->mNonContig = availToServer - part1;
+    mUnreleased = part1;
+    // optimization to avoid waking up the client too early
+    // FIXME need to test for recording
+    mDeferWake = part1 < ask && availToServer >= ask;
+    return part1 > 0 ? NO_ERROR : WOULD_BLOCK;
+}
 
-    if (s >= frameCount) {
-        // common case, server didn't just wrap
-        if (s - frameCount >= serverBase ) {
-            serverBase += frameCount;
-        }
-    } else if (s >= serverBase + frameCount) {
-        // server just wrapped
-        serverBase += frameCount;
+void ServerProxy::releaseBuffer(Buffer* buffer)
+{
+    if (mIsShutdown) {
+        buffer->mFrameCount = 0;
+        buffer->mRaw = NULL;
+        buffer->mNonContig = 0;
+        return;
+    }
+    size_t stepCount = buffer->mFrameCount;
+    LOG_ALWAYS_FATAL_IF(stepCount > mUnreleased);
+    if (stepCount == 0) {
+        buffer->mRaw = NULL;
+        buffer->mNonContig = 0;
+        return;
+    }
+    mUnreleased -= stepCount;
+    audio_track_cblk_t* cblk = mCblk;
+    if (mIsOut) {
+        int32_t front = cblk->u.mStreaming.mFront;
+        android_atomic_release_store(stepCount + front, &cblk->u.mStreaming.mFront);
+    } else {
+        int32_t rear = cblk->u.mStreaming.mRear;
+        android_atomic_release_store(stepCount + rear, &cblk->u.mStreaming.mRear);
     }
 
-    server = s;
+    mCblk->server += stepCount;
 
-    if (!(flags & CBLK_INVALID)) {
-        cv.signal();
+    size_t half = mFrameCount / 2;
+    if (half == 0) {
+        half = 1;
+    }
+    size_t minimum = cblk->mMinimum;
+    if (minimum == 0) {
+        minimum = mIsOut ? half : 1;
+    } else if (minimum > half) {
+        minimum = half;
+    }
+    if (!mDeferWake && mAvailToClient + stepCount >= minimum) {
+        ALOGV("mAvailToClient=%u stepCount=%u minimum=%u", mAvailToClient, stepCount, minimum);
+        // could client be sleeping, or not need this increment and counter overflows?
+        int32_t old = android_atomic_inc(&cblk->mFutex);
+        if (old == -1) {
+            (void) __futex_syscall3(&cblk->mFutex,
+                    mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE, 1);
+        }
     }
-    lock.unlock();
-    return true;
+
+    buffer->mFrameCount = 0;
+    buffer->mRaw = NULL;
+    buffer->mNonContig = 0;
 }
 
-void* audio_track_cblk_t::buffer(void *buffers, size_t frameSize, uint32_t offset) const
+// ---------------------------------------------------------------------------
+
+size_t AudioTrackServerProxy::framesReady()
 {
-    return (int8_t *)buffers + (offset - userBase) * frameSize;
+    LOG_ALWAYS_FATAL_IF(!mIsOut);
+
+    if (mIsShutdown) {
+        return 0;
+    }
+    audio_track_cblk_t* cblk = mCblk;
+    // the acquire might not be necessary since not doing a subsequent read
+    int32_t rear = android_atomic_acquire_load(&cblk->u.mStreaming.mRear);
+    ssize_t filled = rear - cblk->u.mStreaming.mFront;
+    // pipe should not already be overfull
+    if (!(0 <= filled && (size_t) filled <= mFrameCount)) {
+        ALOGE("Shared memory control block is corrupt (filled=%d); shutting down", filled);
+        mIsShutdown = true;
+        return 0;
+    }
+    //  cache this value for later use by obtainBuffer(), with added barrier
+    //  and racy if called by normal mixer thread
+    // ignores flush(), so framesReady() may report a larger mFrameCount than obtainBuffer()
+    return filled;
 }
 
-uint32_t audio_track_cblk_t::framesAvailable(size_t frameCount, bool isOut)
+// ---------------------------------------------------------------------------
+
+StaticAudioTrackServerProxy::StaticAudioTrackServerProxy(audio_track_cblk_t* cblk, void *buffers,
+        size_t frameCount, size_t frameSize)
+    : AudioTrackServerProxy(cblk, buffers, frameCount, frameSize),
+      mObserver(&cblk->u.mStatic.mSingleStateQueue), mPosition(0),
+      mEnd(frameCount), mFramesReadyIsCalledByMultipleThreads(false)
 {
-    Mutex::Autolock _l(lock);
-    return framesAvailable_l(frameCount, isOut);
+    mState.mLoopStart = 0;
+    mState.mLoopEnd = 0;
+    mState.mLoopCount = 0;
 }
 
-uint32_t audio_track_cblk_t::framesAvailable_l(size_t frameCount, bool isOut)
+void StaticAudioTrackServerProxy::framesReadyIsCalledByMultipleThreads()
 {
-    uint32_t u = user;
-    uint32_t s = server;
+    mFramesReadyIsCalledByMultipleThreads = true;
+}
 
-    if (isOut) {
-        uint32_t limit = (s < loopStart) ? s : loopStart;
-        return limit + frameCount - u;
-    } else {
-        return frameCount + u - s;
+size_t StaticAudioTrackServerProxy::framesReady()
+{
+    // FIXME
+    // This is racy if called by normal mixer thread,
+    // as we're reading 2 independent variables without a lock.
+    // Can't call mObserver.poll(), as we might be called from wrong thread.
+    // If looping is enabled, should return a higher number (since includes non-contiguous).
+    size_t position = mPosition;
+    if (!mFramesReadyIsCalledByMultipleThreads) {
+        ssize_t positionOrStatus = pollPosition();
+        if (positionOrStatus >= 0) {
+            position = (size_t) positionOrStatus;
+        }
     }
+    size_t end = mEnd;
+    return position < end ? end - position : 0;
 }
 
-uint32_t audio_track_cblk_t::framesReady(bool isOut)
+ssize_t StaticAudioTrackServerProxy::pollPosition()
 {
-    uint32_t u = user;
-    uint32_t s = server;
-
-    if (isOut) {
-        if (u < loopEnd) {
-            return u - s;
-        } else {
-            // do not block on mutex shared with client on AudioFlinger side
-            if (!tryLock()) {
-                ALOGW("framesReady() could not lock cblk");
-                return 0;
+    size_t position = mPosition;
+    StaticAudioTrackState state;
+    if (mObserver.poll(state)) {
+        bool valid = false;
+        size_t loopStart = state.mLoopStart;
+        size_t loopEnd = state.mLoopEnd;
+        if (state.mLoopCount == 0) {
+            if (loopStart > mFrameCount) {
+                loopStart = mFrameCount;
             }
-            uint32_t frames = UINT_MAX;
-            if (loopCount >= 0) {
-                frames = (loopEnd - loopStart)*loopCount + u - s;
+            // ignore loopEnd
+            mPosition = position = loopStart;
+            mEnd = mFrameCount;
+            mState.mLoopCount = 0;
+            valid = true;
+        } else {
+            if (loopStart < loopEnd && loopEnd <= mFrameCount &&
+                    loopEnd - loopStart >= MIN_LOOP) {
+                if (!(loopStart <= position && position < loopEnd)) {
+                    mPosition = position = loopStart;
+                }
+                mEnd = loopEnd;
+                mState = state;
+                valid = true;
             }
-            lock.unlock();
-            return frames;
         }
-    } else {
-        return s - u;
+        if (!valid) {
+            ALOGE("%s client pushed an invalid state, shutting down", __func__);
+            mIsShutdown = true;
+            return (ssize_t) NO_INIT;
+        }
+        mCblk->u.mStatic.mBufferPosition = position;
     }
+    return (ssize_t) position;
 }
 
-bool audio_track_cblk_t::tryLock()
+status_t StaticAudioTrackServerProxy::obtainBuffer(Buffer* buffer)
 {
-    // the code below simulates lock-with-timeout
-    // we MUST do this to protect the AudioFlinger server
-    // as this lock is shared with the client.
-    status_t err;
+    if (mIsShutdown) {
+        buffer->mFrameCount = 0;
+        buffer->mRaw = NULL;
+        buffer->mNonContig = 0;
+        mUnreleased = 0;
+        return NO_INIT;
+    }
+    ssize_t positionOrStatus = pollPosition();
+    if (positionOrStatus < 0) {
+        buffer->mFrameCount = 0;
+        buffer->mRaw = NULL;
+        buffer->mNonContig = 0;
+        mUnreleased = 0;
+        return (status_t) positionOrStatus;
+    }
+    size_t position = (size_t) positionOrStatus;
+    size_t avail;
+    if (position < mEnd) {
+        avail = mEnd - position;
+        size_t wanted = buffer->mFrameCount;
+        if (avail < wanted) {
+            buffer->mFrameCount = avail;
+        } else {
+            avail = wanted;
+        }
+        buffer->mRaw = &((char *) mBuffers)[position * mFrameSize];
+    } else {
+        avail = 0;
+        buffer->mFrameCount = 0;
+        buffer->mRaw = NULL;
+    }
+    buffer->mNonContig = 0;     // FIXME should be > 0 for looping
+    mUnreleased = avail;
+    return NO_ERROR;
+}
 
-    err = lock.tryLock();
-    if (err == -EBUSY) { // just wait a bit
-        usleep(1000);
-        err = lock.tryLock();
+void StaticAudioTrackServerProxy::releaseBuffer(Buffer* buffer)
+{
+    size_t stepCount = buffer->mFrameCount;
+    LOG_ALWAYS_FATAL_IF(stepCount > mUnreleased);
+    if (stepCount == 0) {
+        buffer->mRaw = NULL;
+        buffer->mNonContig = 0;
+        return;
     }
-    if (err != NO_ERROR) {
-        // probably, the client just died.
-        return false;
+    mUnreleased -= stepCount;
+    audio_track_cblk_t* cblk = mCblk;
+    size_t position = mPosition;
+    size_t newPosition = position + stepCount;
+    int32_t setFlags = 0;
+    if (!(position <= newPosition && newPosition <= mFrameCount)) {
+        ALOGW("%s newPosition %u outside [%u, %u]", __func__, newPosition, position, mFrameCount);
+        newPosition = mFrameCount;
+    } else if (mState.mLoopCount != 0 && newPosition == mState.mLoopEnd) {
+        if (mState.mLoopCount == -1 || --mState.mLoopCount != 0) {
+            newPosition = mState.mLoopStart;
+            setFlags = CBLK_LOOP_CYCLE;
+        } else {
+            mEnd = mFrameCount;     // this is what allows playback to continue after the loop
+            setFlags = CBLK_LOOP_FINAL;
+        }
     }
-    return true;
+    if (newPosition == mFrameCount) {
+        setFlags |= CBLK_BUFFER_END;
+    }
+    mPosition = newPosition;
+
+    cblk->server += stepCount;
+    cblk->u.mStatic.mBufferPosition = newPosition;
+    if (setFlags != 0) {
+        (void) android_atomic_or(setFlags, &cblk->flags);
+        // this would be a good place to wake a futex
+    }
+
+    buffer->mFrameCount = 0;
+    buffer->mRaw = NULL;
+    buffer->mNonContig = 0;
 }
 
+// ---------------------------------------------------------------------------
+
 }   // namespace android
index ebe1ba1..f9ad31d 100644 (file)
@@ -1060,7 +1060,9 @@ bool ToneGenerator::initAudioTrack() {
                       this, // user
                       0,    // notificationFrames
                       0,    // sharedBuffer
-                      mThreadCanCallJava);
+                      mThreadCanCallJava,
+                      0,    // sessionId
+                      AudioTrack::TRANSFER_CALLBACK);
 
     if (mpAudioTrack->initCheck() != NO_ERROR) {
         ALOGE("AudioTrack->initCheck failed");
index cf68848..05dbab1 100644 (file)
@@ -56,6 +56,7 @@
 #include <powermanager/IPowerManager.h>
 
 #include <media/nbaio/NBLog.h>
+#include <private/media/AudioTrackShared.h>
 
 namespace android {
 
index a749d7a..b1286d3 100644 (file)
@@ -46,6 +46,8 @@ public:
             void        destroy();
             int         name() const { return mName; }
 
+    virtual uint32_t    sampleRate() const;
+
             audio_stream_type_t streamType() const {
                 return mStreamType;
             }
@@ -139,6 +141,7 @@ private:
                                         // 'volatile' means accessed without lock or
                                         // barrier, but is read/written atomically
     bool                mIsInvalid; // non-resettable latch, set by invalidate()
+    AudioTrackServerProxy*  mAudioTrackServerProxy;
 };  // end of Track
 
 class TimedTrack : public Track {
@@ -255,10 +258,6 @@ public:
 
 private:
 
-    enum {
-        NO_MORE_BUFFERS = 0x80000001,   // same in AudioTrack.h, ok to be different value
-    };
-
     status_t            obtainBuffer(AudioBufferProvider::Buffer* buffer,
                                      uint32_t waitTimeMs);
     void                clearBufferQueue();
index 6c0d1d3..ffe3e9f 100644 (file)
@@ -57,4 +57,5 @@ private:
     // releaseBuffer() not overridden
 
     bool                mOverflow;  // overflow on most recent attempt to fill client buffer
+    AudioRecordServerProxy* mAudioRecordServerProxy;
 };
index ee52fcb..0773534 100644 (file)
@@ -139,7 +139,7 @@ static const int kPriorityFastMixer = 3;
 // FIXME It would be better for client to tell AudioFlinger whether it wants double-buffering or
 // N-buffering, so AudioFlinger could allocate the right amount of memory.
 // See the client's minBufCount and mNotificationFramesAct calculations for details.
-static const int kFastTrackMultiplier = 2;
+static const int kFastTrackMultiplier = 1;
 
 // ----------------------------------------------------------------------------
 
@@ -1327,7 +1327,7 @@ status_t AudioFlinger::PlaybackThread::addTrack_l(const sp<Track>& track)
         // the track is newly added, make sure it fills up all its
         // buffers before playing. This is to ensure the client will
         // effectively get the latency it requested.
-        track->mFillingUpStatus = Track::FS_FILLING;
+        track->mFillingUpStatus = track->sharedBuffer() != 0 ? Track::FS_FILLED : Track::FS_FILLING;
         track->mResetDone = false;
         track->mPresentationCompleteFrames = 0;
         mActiveTracks.add(track);
@@ -2596,24 +2596,35 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac
         // app does not call stop() and relies on underrun to stop:
         // hence the test on (mMixerStatus == MIXER_TRACKS_READY) meaning the track was mixed
         // during last round
+        size_t desiredFrames;
+        if (t->sampleRate() == mSampleRate) {
+            desiredFrames = mNormalFrameCount;
+        } else {
+            // +1 for rounding and +1 for additional sample needed for interpolation
+            desiredFrames = (mNormalFrameCount * t->sampleRate()) / mSampleRate + 1 + 1;
+            // add frames already consumed but not yet released by the resampler
+            // because cblk->framesReady() will include these frames
+            desiredFrames += mAudioMixer->getUnreleasedFrames(track->name());
+            // the minimum track buffer size is normally twice the number of frames necessary
+            // to fill one buffer and the resampler should not leave more than one buffer worth
+            // of unreleased frames after each pass, but just in case...
+            ALOG_ASSERT(desiredFrames <= cblk->frameCount_);
+        }
         uint32_t minFrames = 1;
         if ((track->sharedBuffer() == 0) && !track->isStopped() && !track->isPausing() &&
                 (mMixerStatusIgnoringFastTracks == MIXER_TRACKS_READY)) {
-            if (t->sampleRate() == mSampleRate) {
-                minFrames = mNormalFrameCount;
-            } else {
-                // +1 for rounding and +1 for additional sample needed for interpolation
-                minFrames = (mNormalFrameCount * t->sampleRate()) / mSampleRate + 1 + 1;
-                // add frames already consumed but not yet released by the resampler
-                // because cblk->framesReady() will include these frames
-                minFrames += mAudioMixer->getUnreleasedFrames(track->name());
-                // the minimum track buffer size is normally twice the number of frames necessary
-                // to fill one buffer and the resampler should not leave more than one buffer worth
-                // of unreleased frames after each pass, but just in case...
-                ALOG_ASSERT(minFrames <= cblk->frameCount_);
-            }
+            minFrames = desiredFrames;
         }
-        if ((track->framesReady() >= minFrames) && track->isReady() &&
+        // It's not safe to call framesReady() for a static buffer track, so assume it's ready
+        size_t framesReady;
+        if (track->sharedBuffer() == 0) {
+            framesReady = track->framesReady();
+        } else if (track->isStopped()) {
+            framesReady = 0;
+        } else {
+            framesReady = 1;
+        }
+        if ((framesReady >= minFrames) && track->isReady() &&
                 !track->isPaused() && !track->isTerminated())
         {
             ALOGVV("track %d u=%08x, s=%08x [OK] on thread %p", name, cblk->user, cblk->server,
@@ -2664,7 +2675,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac
                 // read original volumes with volume control
                 float typeVolume = mStreamTypes[track->streamType()].volume;
                 float v = masterVolume * typeVolume;
-                ServerProxy *proxy = track->mServerProxy;
+                AudioTrackServerProxy *proxy = track->mAudioTrackServerProxy;
                 uint32_t vlr = proxy->getVolumeLR();
                 vl = vlr & 0xFFFF;
                 vr = vlr >> 16;
@@ -2737,7 +2748,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac
                 AudioMixer::CHANNEL_MASK, (void *)track->channelMask());
             // limit track sample rate to 2 x output sample rate, which changes at re-configuration
             uint32_t maxSampleRate = mSampleRate * 2;
-            uint32_t reqSampleRate = track->mServerProxy->getSampleRate();
+            uint32_t reqSampleRate = track->mAudioTrackServerProxy->getSampleRate();
             if (reqSampleRate == 0) {
                 reqSampleRate = mSampleRate;
             } else if (reqSampleRate > maxSampleRate) {
@@ -2768,6 +2779,13 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac
                 mixerStatus = MIXER_TRACKS_READY;
             }
         } else {
+            // only implemented for normal tracks, not fast tracks
+            if (framesReady < desiredFrames && !track->isStopped() && !track->isPaused()) {
+                // we missed desiredFrames whatever the actual number of frames missing was
+                cblk->u.mStreaming.mUnderrunFrames += desiredFrames;
+                // FIXME also wake futex so that underrun is noticed more quickly
+                (void) android_atomic_or(CBLK_UNDERRUN, &cblk->flags);
+            }
             // clear effect chain input buffer if an active track underruns to avoid sending
             // previous audio buffer again to effects
             chain = getEffectChain_l(track->sessionId());
@@ -3170,7 +3188,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::DirectOutputThread::prep
             } else {
                 float typeVolume = mStreamTypes[track->streamType()].volume;
                 float v = mMasterVolume * typeVolume;
-                uint32_t vlr = track->mServerProxy->getVolumeLR();
+                uint32_t vlr = track->mAudioTrackServerProxy->getVolumeLR();
                 float v_clamped = v * (vlr & 0xFFFF);
                 if (v_clamped > MAX_GAIN) {
                     v_clamped = MAX_GAIN;
@@ -3696,7 +3714,8 @@ bool AudioFlinger::RecordThread::threadLoop()
             }
 
             buffer.frameCount = mFrameCount;
-            if (CC_LIKELY(mActiveTrack->getNextBuffer(&buffer) == NO_ERROR)) {
+            status_t status = mActiveTrack->getNextBuffer(&buffer);
+            if (CC_LIKELY(status == NO_ERROR)) {
                 readOnce = true;
                 size_t framesOut = buffer.frameCount;
                 if (mResampler == NULL) {
index fac7071..55d96fa 100644 (file)
@@ -74,7 +74,7 @@ protected:
 
     audio_channel_mask_t channelMask() const { return mChannelMask; }
 
-    uint32_t sampleRate() const; // FIXME inline after cblk sr moved
+    virtual uint32_t sampleRate() const { return mSampleRate; }
 
     // Return a pointer to the start of a contiguous slice of the track buffer.
     // Parameter 'offset' is the requested start position, expressed in
index 41a763d..bfc197c 100644 (file)
@@ -98,7 +98,7 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase(
 
     // ALOGD("Creating track with %d buffers @ %d bytes", bufferCount, bufferSize);
     size_t size = sizeof(audio_track_cblk_t);
-    size_t bufferSize = frameCount * mFrameSize;
+    size_t bufferSize = (sharedBuffer == 0 ? roundup(frameCount) : frameCount) * mFrameSize;
     if (sharedBuffer == 0) {
         size += bufferSize;
     }
@@ -124,22 +124,16 @@ AudioFlinger::ThreadBase::TrackBase::TrackBase(
         new(mCblk) audio_track_cblk_t();
         // clear all buffers
         mCblk->frameCount_ = frameCount;
-// uncomment the following lines to quickly test 32-bit wraparound
-//      mCblk->user = 0xffff0000;
-//      mCblk->server = 0xffff0000;
-//      mCblk->userBase = 0xffff0000;
-//      mCblk->serverBase = 0xffff0000;
         if (sharedBuffer == 0) {
             mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t);
             memset(mBuffer, 0, bufferSize);
-            // Force underrun condition to avoid false underrun callback until first data is
-            // written to buffer (other flags are cleared)
-            mCblk->flags = CBLK_UNDERRUN;
         } else {
             mBuffer = sharedBuffer->pointer();
+#if 0
+            mCblk->flags = CBLK_FORCEREADY;     // FIXME hack, need to fix the track ready logic
+#endif
         }
         mBufferEnd = (uint8_t *)mBuffer + bufferSize;
-        mServerProxy = new ServerProxy(mCblk, mBuffer, frameCount, mFrameSize, isOut);
 
 #ifdef TEE_SINK
         if (mTeeSinkTrackEnabled) {
@@ -199,51 +193,17 @@ void AudioFlinger::ThreadBase::TrackBase::releaseBuffer(AudioBufferProvider::Buf
     }
 #endif
 
-    buffer->raw = NULL;
-    mStepCount = buffer->frameCount;
-    // FIXME See note at getNextBuffer()
-    (void) step();      // ignore return value of step()
+    ServerProxy::Buffer buf;
+    buf.mFrameCount = buffer->frameCount;
+    buf.mRaw = buffer->raw;
     buffer->frameCount = 0;
-}
-
-bool AudioFlinger::ThreadBase::TrackBase::step() {
-    bool result = mServerProxy->step(mStepCount);
-    if (!result) {
-        ALOGV("stepServer failed acquiring cblk mutex");
-        mStepServerFailed = true;
-    }
-    return result;
+    buffer->raw = NULL;
+    mServerProxy->releaseBuffer(&buf);
 }
 
 void AudioFlinger::ThreadBase::TrackBase::reset() {
-    audio_track_cblk_t* cblk = this->cblk();
-
-    cblk->user = 0;
-    cblk->server = 0;
-    cblk->userBase = 0;
-    cblk->serverBase = 0;
-    mStepServerFailed = false;
     ALOGV("TrackBase::reset");
-}
-
-uint32_t AudioFlinger::ThreadBase::TrackBase::sampleRate() const {
-    return mServerProxy->getSampleRate();
-}
-
-void* AudioFlinger::ThreadBase::TrackBase::getBuffer(uint32_t offset, uint32_t frames) const {
-    audio_track_cblk_t* cblk = this->cblk();
-    int8_t *bufferStart = (int8_t *)mBuffer + (offset-cblk->serverBase) * mFrameSize;
-    int8_t *bufferEnd = bufferStart + frames * mFrameSize;
-
-    // Check validity of returned pointer in case the track control block would have been corrupted.
-    ALOG_ASSERT(!(bufferStart < mBuffer || bufferStart > bufferEnd || bufferEnd > mBufferEnd),
-            "TrackBase::getBuffer buffer out of range:\n"
-                "    start: %p, end %p , mBuffer %p mBufferEnd %p\n"
-                "    server %u, serverBase %u, user %u, userBase %u, frameSize %u",
-                bufferStart, bufferEnd, mBuffer, mBufferEnd,
-                cblk->server, cblk->serverBase, cblk->user, cblk->userBase, mFrameSize);
-
-    return bufferStart;
+    // FIXME still needed?
 }
 
 status_t AudioFlinger::ThreadBase::TrackBase::setSyncEvent(const sp<SyncEvent>& event)
@@ -362,9 +322,18 @@ AudioFlinger::PlaybackThread::Track::Track(
     mFastIndex(-1),
     mUnderrunCount(0),
     mCachedVolume(1.0),
-    mIsInvalid(false)
+    mIsInvalid(false),
+    mAudioTrackServerProxy(NULL)
 {
     if (mCblk != NULL) {
+        if (sharedBuffer == 0) {
+            mAudioTrackServerProxy = new AudioTrackServerProxy(mCblk, mBuffer, frameCount,
+                    mFrameSize);
+        } else {
+            mAudioTrackServerProxy = new StaticAudioTrackServerProxy(mCblk, mBuffer, frameCount,
+                    mFrameSize);
+        }
+        mServerProxy = mAudioTrackServerProxy;
         // to avoid leaking a track name, do not allocate one unless there is an mCblk
         mName = thread->getTrackName_l(channelMask, sessionId);
         mCblk->mName = mName;
@@ -374,6 +343,7 @@ AudioFlinger::PlaybackThread::Track::Track(
         }
         // only allocate a fast track index if we were able to allocate a normal track name
         if (flags & IAudioFlinger::TRACK_FAST) {
+            mAudioTrackServerProxy->framesReadyIsCalledByMultipleThreads();
             ALOG_ASSERT(thread->mFastTrackAvailMask != 0);
             int i = __builtin_ctz(thread->mFastTrackAvailMask);
             ALOG_ASSERT(0 < i && i < (int)FastMixerState::kMaxFastTracks);
@@ -432,12 +402,12 @@ void AudioFlinger::PlaybackThread::Track::destroy()
 /*static*/ void AudioFlinger::PlaybackThread::Track::appendDumpHeader(String8& result)
 {
     result.append("   Name Client Type Fmt Chn mask   Session StpCnt fCount S F SRate  "
-                  "L dB  R dB    Server      User     Main buf    Aux Buf  Flags Underruns\n");
+                  "L dB  R dB    Server    Main buf    Aux Buf  Flags Underruns\n");
 }
 
 void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size)
 {
-    uint32_t vlr = mServerProxy->getVolumeLR();
+    uint32_t vlr = mAudioTrackServerProxy->getVolumeLR();
     if (isFastTrack()) {
         sprintf(buffer, "   F %2d", mFastIndex);
     } else {
@@ -496,7 +466,7 @@ void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size)
         break;
     }
     snprintf(&buffer[7], size-7, " %6d %4u %3u 0x%08x %7u %6u %6u %1c %1d %5u %5.2g %5.2g  "
-            "0x%08x 0x%08x 0x%08x 0x%08x %#5x %9u%c\n",
+            "0x%08x 0x%08x 0x%08x %#5x %9u%c\n",
             (mClient == 0) ? getpid_cached : mClient->pid(),
             mStreamType,
             mFormat,
@@ -506,11 +476,10 @@ void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size)
             mFrameCount,
             stateChar,
             mFillingUpStatus,
-            mServerProxy->getSampleRate(),
+            mAudioTrackServerProxy->getSampleRate(),
             20.0 * log10((vlr & 0xFFFF) / 4096.0),
             20.0 * log10((vlr >> 16) / 4096.0),
             mCblk->server,
-            mCblk->user,
             (int)mMainBuffer,
             (int)mAuxBuffer,
             mCblk->flags,
@@ -518,53 +487,27 @@ void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size)
             nowInUnderrun);
 }
 
+uint32_t AudioFlinger::PlaybackThread::Track::sampleRate() const {
+    return mAudioTrackServerProxy->getSampleRate();
+}
+
 // AudioBufferProvider interface
 status_t AudioFlinger::PlaybackThread::Track::getNextBuffer(
         AudioBufferProvider::Buffer* buffer, int64_t pts)
 {
-    audio_track_cblk_t* cblk = this->cblk();
-    uint32_t framesReady;
-    uint32_t framesReq = buffer->frameCount;
-
-    // Check if last stepServer failed, try to step now
-    if (mStepServerFailed) {
-        // FIXME When called by fast mixer, this takes a mutex with tryLock().
-        //       Since the fast mixer is higher priority than client callback thread,
-        //       it does not result in priority inversion for client.
-        //       But a non-blocking solution would be preferable to avoid
-        //       fast mixer being unable to tryLock(), and
-        //       to avoid the extra context switches if the client wakes up,
-        //       discovers the mutex is locked, then has to wait for fast mixer to unlock.
-        if (!step())  goto getNextBuffer_exit;
-        ALOGV("stepServer recovered");
-        mStepServerFailed = false;
+    ServerProxy::Buffer buf;
+    size_t desiredFrames = buffer->frameCount;
+    buf.mFrameCount = desiredFrames;
+    status_t status = mServerProxy->obtainBuffer(&buf);
+    buffer->frameCount = buf.mFrameCount;
+    buffer->raw = buf.mRaw;
+    if (buf.mFrameCount == 0) {
+        // only implemented so far for normal tracks, not fast tracks
+        mCblk->u.mStreaming.mUnderrunFrames += desiredFrames;
+        // FIXME also wake futex so that underrun is noticed more quickly
+        (void) android_atomic_or(CBLK_UNDERRUN, &mCblk->flags);
     }
-
-    // FIXME Same as above
-    framesReady = mServerProxy->framesReady();
-
-    if (CC_LIKELY(framesReady)) {
-        uint32_t s = cblk->server;
-        uint32_t bufferEnd = cblk->serverBase + mFrameCount;
-
-        bufferEnd = (cblk->loopEnd < bufferEnd) ? cblk->loopEnd : bufferEnd;
-        if (framesReq > framesReady) {
-            framesReq = framesReady;
-        }
-        if (framesReq > bufferEnd - s) {
-            framesReq = bufferEnd - s;
-        }
-
-        buffer->raw = getBuffer(s, framesReq);
-        buffer->frameCount = framesReq;
-        return NO_ERROR;
-    }
-
-getNextBuffer_exit:
-    buffer->raw = NULL;
-    buffer->frameCount = 0;
-    ALOGV("getNextBuffer() no more data for track %d on thread %p", mName, mThread.unsafe_get());
-    return NOT_ENOUGH_DATA;
+    return status;
 }
 
 // Note that framesReady() takes a mutex on the control block using tryLock().
@@ -576,7 +519,7 @@ getNextBuffer_exit:
 // the tryLock() could block for up to 1 ms, and a sequence of these could delay fast mixer.
 // FIXME Replace AudioTrackShared control block implementation by a non-blocking FIFO queue.
 size_t AudioFlinger::PlaybackThread::Track::framesReady() const {
-    return mServerProxy->framesReady();
+    return mAudioTrackServerProxy->framesReady();
 }
 
 // Don't call for fast tracks; the framesReady() could result in priority inversion
@@ -732,7 +675,6 @@ void AudioFlinger::PlaybackThread::Track::reset()
         // Force underrun condition to avoid false underrun callback until first data is
         // written to buffer
         android_atomic_and(~CBLK_FORCEREADY, &mCblk->flags);
-        android_atomic_or(CBLK_UNDERRUN, &mCblk->flags);
         mFillingUpStatus = FS_FILLING;
         mResetDone = true;
         if (mState == FLUSHED) {
@@ -833,7 +775,7 @@ uint32_t AudioFlinger::PlaybackThread::Track::getVolumeLR()
 {
     // called by FastMixer, so not allowed to take any locks, block, or do I/O including logs
     ALOG_ASSERT(isFastTrack() && (mCblk != NULL));
-    uint32_t vlr = mServerProxy->getVolumeLR();
+    uint32_t vlr = mAudioTrackServerProxy->getVolumeLR();
     uint32_t vl = vlr & 0xFFFF;
     uint32_t vr = vlr >> 16;
     // track volumes come from shared memory, so can't be trusted and must be clamped
@@ -870,9 +812,12 @@ status_t AudioFlinger::PlaybackThread::Track::setSyncEvent(const sp<SyncEvent>&
 
 void AudioFlinger::PlaybackThread::Track::invalidate()
 {
-    // FIXME should use proxy
-    android_atomic_or(CBLK_INVALID, &mCblk->flags);
-    mCblk->cv.signal();
+    // FIXME should use proxy, and needs work
+    audio_track_cblk_t* cblk = mCblk;
+    android_atomic_or(CBLK_INVALID, &cblk->flags);
+    android_atomic_release_store(0x40000000, &cblk->mFutex);
+    // client is not in server, so FUTEX_WAKE is needed instead of FUTEX_WAKE_PRIVATE
+    (void) __futex_syscall3(&cblk->mFutex, FUTEX_WAKE, INT_MAX);
     mIsInvalid = true;
 }
 
@@ -1418,6 +1363,8 @@ AudioFlinger::PlaybackThread::OutputTrack::OutputTrack(
         mClientProxy->setVolumeLR((uint32_t(uint16_t(0x1000)) << 16) | uint16_t(0x1000));
         mClientProxy->setSendLevel(0.0);
         mClientProxy->setSampleRate(sampleRate);
+        mClientProxy = new AudioTrackClientProxy(mCblk, mBuffer, mFrameCount, mFrameSize,
+                true /*clientInServer*/);
     } else {
         ALOGW("Error creating output track on thread %p", playbackThread);
     }
@@ -1498,9 +1445,10 @@ bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t fr
         if (mOutBuffer.frameCount == 0) {
             mOutBuffer.frameCount = pInBuffer->frameCount;
             nsecs_t startTime = systemTime();
-            if (obtainBuffer(&mOutBuffer, waitTimeLeftMs) == (status_t)NO_MORE_BUFFERS) {
-                ALOGV("OutputTrack::write() %p thread %p no more output buffers", this,
-                        mThread.unsafe_get());
+            status_t status = obtainBuffer(&mOutBuffer, waitTimeLeftMs);
+            if (status != NO_ERROR) {
+                ALOGV("OutputTrack::write() %p thread %p no more output buffers; status %d", this,
+                        mThread.unsafe_get(), status);
                 outputBufferFull = true;
                 break;
             }
@@ -1515,7 +1463,10 @@ bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t fr
         uint32_t outFrames = pInBuffer->frameCount > mOutBuffer.frameCount ? mOutBuffer.frameCount :
                 pInBuffer->frameCount;
         memcpy(mOutBuffer.raw, pInBuffer->raw, outFrames * channelCount * sizeof(int16_t));
-        mClientProxy->stepUser(outFrames);
+        Proxy::Buffer buf;
+        buf.mFrameCount = outFrames;
+        buf.mRaw = NULL;
+        mClientProxy->releaseBuffer(&buf);
         pInBuffer->frameCount -= outFrames;
         pInBuffer->i16 += outFrames * channelCount;
         mOutBuffer.frameCount -= outFrames;
@@ -1559,8 +1510,10 @@ bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t fr
     // If no more buffers are pending, fill output track buffer to make sure it is started
     // by output mixer.
     if (frames == 0 && mBufferQueue.size() == 0) {
-        if (mCblk->user < mFrameCount) {
-            frames = mFrameCount - mCblk->user;
+        // FIXME borken, replace by getting framesReady() from proxy
+        size_t user = 0;    // was mCblk->user
+        if (user < mFrameCount) {
+            frames = mFrameCount - user;
             pInBuffer = new Buffer;
             pInBuffer->mBuffer = new int16_t[frames * channelCount];
             pInBuffer->frameCount = frames;
@@ -1578,46 +1531,17 @@ bool AudioFlinger::PlaybackThread::OutputTrack::write(int16_t* data, uint32_t fr
 status_t AudioFlinger::PlaybackThread::OutputTrack::obtainBuffer(
         AudioBufferProvider::Buffer* buffer, uint32_t waitTimeMs)
 {
-    audio_track_cblk_t* cblk = mCblk;
-    uint32_t framesReq = buffer->frameCount;
-
-    ALOGVV("OutputTrack::obtainBuffer user %d, server %d", cblk->user, cblk->server);
-    buffer->frameCount  = 0;
-
-    size_t framesAvail;
-    {
-        Mutex::Autolock _l(cblk->lock);
-
-        // read the server count again
-        while (!(framesAvail = mClientProxy->framesAvailable_l())) {
-            if (CC_UNLIKELY(!mActive)) {
-                ALOGV("Not active and NO_MORE_BUFFERS");
-                return NO_MORE_BUFFERS;
-            }
-            status_t result = cblk->cv.waitRelative(cblk->lock, milliseconds(waitTimeMs));
-            if (result != NO_ERROR) {
-                return NO_MORE_BUFFERS;
-            }
-        }
-    }
-
-    if (framesReq > framesAvail) {
-        framesReq = framesAvail;
-    }
-
-    uint32_t u = cblk->user;
-    uint32_t bufferEnd = cblk->userBase + mFrameCount;
-
-    if (framesReq > bufferEnd - u) {
-        framesReq = bufferEnd - u;
-    }
-
-    buffer->frameCount  = framesReq;
-    buffer->raw         = mClientProxy->buffer(u);
-    return NO_ERROR;
+    ClientProxy::Buffer buf;
+    buf.mFrameCount = buffer->frameCount;
+    struct timespec timeout;
+    timeout.tv_sec = waitTimeMs / 1000;
+    timeout.tv_nsec = (int) (waitTimeMs % 1000) * 1000000;
+    status_t status = mClientProxy->obtainBuffer(&buf, &timeout);
+    buffer->frameCount = buf.mFrameCount;
+    buffer->raw = buf.mRaw;
+    return status;
 }
 
-
 void AudioFlinger::PlaybackThread::OutputTrack::clearBufferQueue()
 {
     size_t size = mBufferQueue.size();
@@ -1688,6 +1612,11 @@ AudioFlinger::RecordThread::RecordTrack::RecordTrack(
         mOverflow(false)
 {
     ALOGV("RecordTrack constructor, size %d", (int)mBufferEnd - (int)mBuffer);
+    if (mCblk != NULL) {
+        mAudioRecordServerProxy = new AudioRecordServerProxy(mCblk, mBuffer, frameCount,
+                mFrameSize);
+        mServerProxy = mAudioRecordServerProxy;
+    }
 }
 
 AudioFlinger::RecordThread::RecordTrack::~RecordTrack()
@@ -1699,42 +1628,16 @@ AudioFlinger::RecordThread::RecordTrack::~RecordTrack()
 status_t AudioFlinger::RecordThread::RecordTrack::getNextBuffer(AudioBufferProvider::Buffer* buffer,
         int64_t pts)
 {
-    audio_track_cblk_t* cblk = this->cblk();
-    uint32_t framesAvail;
-    uint32_t framesReq = buffer->frameCount;
-
-    // Check if last stepServer failed, try to step now
-    if (mStepServerFailed) {
-        if (!step()) {
-            goto getNextBuffer_exit;
-        }
-        ALOGV("stepServer recovered");
-        mStepServerFailed = false;
+    ServerProxy::Buffer buf;
+    buf.mFrameCount = buffer->frameCount;
+    status_t status = mServerProxy->obtainBuffer(&buf);
+    buffer->frameCount = buf.mFrameCount;
+    buffer->raw = buf.mRaw;
+    if (buf.mFrameCount == 0) {
+        // FIXME also wake futex so that overrun is noticed more quickly
+        (void) android_atomic_or(CBLK_OVERRUN, &mCblk->flags);
     }
-
-    // FIXME lock is not actually held, so overrun is possible
-    framesAvail = mServerProxy->framesAvailableIn_l();
-
-    if (CC_LIKELY(framesAvail)) {
-        uint32_t s = cblk->server;
-        uint32_t bufferEnd = cblk->serverBase + mFrameCount;
-
-        if (framesReq > framesAvail) {
-            framesReq = framesAvail;
-        }
-        if (framesReq > bufferEnd - s) {
-            framesReq = bufferEnd - s;
-        }
-
-        buffer->raw = getBuffer(s, framesReq);
-        buffer->frameCount = framesReq;
-        return NO_ERROR;
-    }
-
-getNextBuffer_exit:
-    buffer->raw = NULL;
-    buffer->frameCount = 0;
-    return NOT_ENOUGH_DATA;
+    return status;
 }
 
 status_t AudioFlinger::RecordThread::RecordTrack::start(AudioSystem::sync_event_t event,
@@ -1790,12 +1693,12 @@ void AudioFlinger::RecordThread::RecordTrack::destroy()
 
 /*static*/ void AudioFlinger::RecordThread::RecordTrack::appendDumpHeader(String8& result)
 {
-    result.append("   Clien Fmt Chn mask   Session Step S Serv     User   FrameCount\n");
+    result.append("   Clien Fmt Chn mask   Session Step S Serv   FrameCount\n");
 }
 
 void AudioFlinger::RecordThread::RecordTrack::dump(char* buffer, size_t size)
 {
-    snprintf(buffer, size, "   %05d %03u 0x%08x %05d   %04u %01d %08x %08x %05d\n",
+    snprintf(buffer, size, "   %05d %03u 0x%08x %05d   %04u %01d %08x %05d\n",
             (mClient == 0) ? getpid_cached : mClient->pid(),
             mFormat,
             mChannelMask,
@@ -1803,7 +1706,6 @@ void AudioFlinger::RecordThread::RecordTrack::dump(char* buffer, size_t size)
             mStepCount,
             mState,
             mCblk->server,
-            mCblk->user,
             mFrameCount);
 }