Merge "media: Write maker and model information into recorded videos." into oc-dev
diff --git a/camera/include/camera/ndk/NdkCameraMetadataTags.h b/camera/include/camera/ndk/NdkCameraMetadataTags.h
index ced6034..8b76cdf 100644
--- a/camera/include/camera/ndk/NdkCameraMetadataTags.h
+++ b/camera/include/camera/ndk/NdkCameraMetadataTags.h
@@ -1542,7 +1542,12 @@
      * request A.</p>
      * <p>Note that when enableZsl is <code>true</code>, it is not guaranteed to get output images captured in the
      * past for requests with STILL_CAPTURE capture intent.</p>
-     * <p>The value of enableZsl in capture templates is always <code>false</code> if present.</p>
+     * <p>For applications targeting SDK versions O and newer, the value of enableZsl in
+     * TEMPLATE_STILL_CAPTURE template may be <code>true</code>. The value in other templates is always
+     * <code>false</code> if present.</p>
+     * <p>For applications targeting SDK versions older than O, the value of enableZsl in all
+     * capture templates is always <code>false</code> if present.</p>
+     * <p>For application-operated ZSL, use CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG template.</p>
      *
      * @see ACAMERA_CONTROL_CAPTURE_INTENT
      * @see ACAMERA_SENSOR_TIMESTAMP
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index 80aad2f..dfd5df7 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -1048,6 +1048,10 @@
                 bool haveVideo = false;
                 for (size_t i = 0; i < numTracks; ++i) {
                     sp<IMediaSource> source = extractor->getTrack(i);
+                    if (source == nullptr) {
+                        fprintf(stderr, "skip NULL track %zu, track count %zu.\n", i, numTracks);
+                        continue;
+                    }
 
                     const char *mime;
                     CHECK(source->getFormat()->findCString(
@@ -1110,6 +1114,10 @@
                 }
 
                 mediaSource = extractor->getTrack(i);
+                if (mediaSource == nullptr) {
+                    fprintf(stderr, "skip NULL track %zu, total tracks %zu.\n", i, numTracks);
+                    return -1;
+                }
             }
         }
 
diff --git a/cmds/stagefright/stream.cpp b/cmds/stagefright/stream.cpp
index 8f9333a..2e1d240 100644
--- a/cmds/stagefright/stream.cpp
+++ b/cmds/stagefright/stream.cpp
@@ -171,7 +171,8 @@
     mWriter = new MPEG2TSWriter(
             this, &MyConvertingStreamSource::WriteDataWrapper);
 
-    for (size_t i = 0; i < extractor->countTracks(); ++i) {
+    size_t numTracks = extractor->countTracks();
+    for (size_t i = 0; i < numTracks; ++i) {
         const sp<MetaData> &meta = extractor->getTrackMetaData(i);
 
         const char *mime;
@@ -181,7 +182,12 @@
             continue;
         }
 
-        CHECK_EQ(mWriter->addSource(extractor->getTrack(i)), (status_t)OK);
+        sp<IMediaSource> track = extractor->getTrack(i);
+        if (track == nullptr) {
+            fprintf(stderr, "skip NULL track %zu, total tracks %zu\n", i, numTracks);
+            continue;
+        }
+        CHECK_EQ(mWriter->addSource(track), (status_t)OK);
     }
 
     CHECK_EQ(mWriter->start(), (status_t)OK);
diff --git a/include/media/Interpolator.h b/include/media/Interpolator.h
index 1b26b87..45a0585 100644
--- a/include/media/Interpolator.h
+++ b/include/media/Interpolator.h
@@ -152,8 +152,8 @@
                         : mLastSlope;
             } else {
                 // finite difference spline
-                m0 = (sec0 + sec) * 0.5;
-                m1 = (sec1 + sec) * 0.5;
+                m0 = (sec0 + sec) * 0.5f;
+                m1 = (sec1 + sec) * 0.5f;
             }
 
             if (monotonic) {
@@ -294,10 +294,21 @@
 
     std::string toString() const {
         std::stringstream ss;
-        ss << "mInterpolatorType: " << mInterpolatorType << std::endl;
+        ss << "Interpolator{mInterpolatorType=" << static_cast<int32_t>(mInterpolatorType);
+        ss << ", mFirstSlope=" << mFirstSlope;
+        ss << ", mLastSlope=" << mLastSlope;
+        ss << ", {";
+        bool first = true;
         for (const auto &pt : *this) {
-            ss << pt.first << " " << pt.second << std::endl;
+            if (first) {
+                first = false;
+                ss << "{";
+            } else {
+                ss << ", {";
+            }
+            ss << pt.first << ", " << pt.second << "}";
         }
+        ss << "}}";
         return ss.str();
     }
 
@@ -322,7 +333,7 @@
 
     // spline cubic polynomial coefficient cache
     std::unordered_map<S, std::tuple<S /* c1 */, S /* c2 */, S /* c3 */>> mMemo;
-};
+}; // Interpolator
 
 } // namespace android
 
diff --git a/include/media/VolumeShaper.h b/include/media/VolumeShaper.h
index f5a74d8..e4c0b5b 100644
--- a/include/media/VolumeShaper.h
+++ b/include/media/VolumeShaper.h
@@ -51,31 +51,68 @@
 
 class VolumeShaper {
 public:
-    using S = float;
-    using T = float;
+    // S and T are like template typenames (matching the Interpolator<S, T>)
+    using S = float; // time type
+    using T = float; // volume type
 
-    static const int kSystemIdMax = 16;
+// Curve and dimension information
+// TODO: member static const or constexpr float initialization not permitted in C++11
+#define MIN_CURVE_TIME    0.f  // type S: start of VolumeShaper curve (normalized)
+#define MAX_CURVE_TIME    1.f  // type S: end of VolumeShaper curve (normalized)
+#define MIN_LINEAR_VOLUME 0.f  // type T: silence / mute audio
+#define MAX_LINEAR_VOLUME 1.f  // type T: max volume, unity gain
+#define MAX_LOG_VOLUME    0.f  // type T: max volume, unity gain in dBFS
 
-    // VolumeShaper::Status is equivalent to status_t if negative
-    // but if non-negative represents the id operated on.
-    // It must be expressible as an int32_t for binder purposes.
+    /* kSystemVolumeShapersMax is the maximum number of system VolumeShapers.
+     * Each system VolumeShapers has a predefined Id, which ranges from 0
+     * to kSystemVolumeShapersMax - 1 and is unique for its usage.
+     *
+     * "1" is reserved for system ducking.
+     */
+    static const int kSystemVolumeShapersMax = 16;
+
+    /* kUserVolumeShapersMax is the maximum number of application
+     * VolumeShapers for a player/track.  Application VolumeShapers are
+     * assigned on creation by the client, and have Ids ranging
+     * from kSystemVolumeShapersMax to INT32_MAX.
+     *
+     * The number of user/application volume shapers is independent to the
+     * system volume shapers. If an application tries to create more than
+     * kUserVolumeShapersMax to a player, then the apply() will fail.
+     * This prevents exhausting server side resources by a potentially malicious
+     * application.
+     */
+    static const int kUserVolumeShapersMax = 16;
+
+    /* VolumeShaper::Status is equivalent to status_t if negative
+     * but if non-negative represents the id operated on.
+     * It must be expressible as an int32_t for binder purposes.
+     */
     using Status = status_t;
 
+    // Local definition for clamp as std::clamp is included in C++17 only.
+    // TODO: use the std::clamp version when Android build uses C++17.
+    template<typename R>
+    static constexpr const R &clamp(const R &v, const R &lo, const R &hi) {
+        return (v < lo) ? lo : (hi < v) ? hi : v;
+    }
+
+    /* VolumeShaper.Configuration derives from the Interpolator class and adds
+     * parameters relating to the volume shape.
+     *
+     * This parallels the Java implementation and the enums must match.
+     * See "frameworks/base/media/java/android/media/VolumeShaper.java" for
+     * details on the Java implementation.
+     */
     class Configuration : public Interpolator<S, T>, public RefBase {
     public:
-        /* VolumeShaper.Configuration derives from the Interpolator class and adds
-         * parameters relating to the volume shape.
-         */
-
-        // TODO document as per VolumeShaper.java flags.
-
-        // must match with VolumeShaper.java in frameworks/base
+        // Must match with VolumeShaper.java in frameworks/base.
         enum Type : int32_t {
             TYPE_ID,
             TYPE_SCALE,
         };
 
-        // must match with VolumeShaper.java in frameworks/base
+        // Must match with VolumeShaper.java in frameworks/base.
         enum OptionFlag : int32_t {
             OPTION_FLAG_NONE           = 0,
             OPTION_FLAG_VOLUME_IN_DBFS = (1 << 0),
@@ -84,19 +121,21 @@
             OPTION_FLAG_ALL            = (OPTION_FLAG_VOLUME_IN_DBFS | OPTION_FLAG_CLOCK_TIME),
         };
 
-        // bring to derived class; must match with VolumeShaper.java in frameworks/base
+        // Bring from base class; must match with VolumeShaper.java in frameworks/base.
         using InterpolatorType = Interpolator<S, T>::InterpolatorType;
 
         Configuration()
             : Interpolator<S, T>()
+            , RefBase()
             , mType(TYPE_SCALE)
             , mOptionFlags(OPTION_FLAG_NONE)
             , mDurationMs(1000.)
             , mId(-1) {
         }
 
-        Configuration(const Configuration &configuration)
+        explicit Configuration(const Configuration &configuration)
             : Interpolator<S, T>(*static_cast<const Interpolator<S, T> *>(&configuration))
+            , RefBase()
             , mType(configuration.mType)
             , mOptionFlags(configuration.mOptionFlags)
             , mDurationMs(configuration.mDurationMs)
@@ -136,8 +175,13 @@
             return mDurationMs;
         }
 
-        void setDurationMs(double durationMs) {
-            mDurationMs = durationMs;
+        status_t setDurationMs(double durationMs) {
+            if (durationMs > 0.) {
+                mDurationMs = durationMs;
+                return NO_ERROR;
+            }
+            // zero, negative, or nan. These values not possible from Java.
+            return BAD_VALUE;
         }
 
         int32_t getId() const {
@@ -145,45 +189,46 @@
         }
 
         void setId(int32_t id) {
+            // We permit a negative id here (representing invalid).
             mId = id;
         }
 
+        /* Adjust the volume to be in linear range from MIN_LINEAR_VOLUME to MAX_LINEAR_VOLUME
+         * and compensate for log dbFS volume as needed.
+         */
         T adjustVolume(T volume) const {
             if ((getOptionFlags() & OPTION_FLAG_VOLUME_IN_DBFS) != 0) {
-                const T out = powf(10.f, volume / 10.);
+                const T out = powf(10.f, volume / 10.f);
                 VS_LOG("in: %f  out: %f", volume, out);
                 volume = out;
             }
-            // clamp
-            if (volume < 0.f) {
-                volume = 0.f;
-            } else if (volume > 1.f) {
-                volume = 1.f;
-            }
-            return volume;
+            return clamp(volume, MIN_LINEAR_VOLUME /* lo */, MAX_LINEAR_VOLUME /* hi */);
         }
 
-        status_t checkCurve() {
+        /* Check if the existing curve is valid.
+         */
+        status_t checkCurve() const {
             if (mType == TYPE_ID) return NO_ERROR;
             if (this->size() < 2) {
                 ALOGE("curve must have at least 2 points");
                 return BAD_VALUE;
             }
-            if (first().first != 0.f || last().first != 1.f) {
-                ALOGE("curve must start at 0.f and end at 1.f");
+            if (first().first != MIN_CURVE_TIME || last().first != MAX_CURVE_TIME) {
+                ALOGE("curve must start at MIN_CURVE_TIME and end at MAX_CURVE_TIME");
                 return BAD_VALUE;
             }
             if ((getOptionFlags() & OPTION_FLAG_VOLUME_IN_DBFS) != 0) {
                 for (const auto &pt : *this) {
-                    if (!(pt.second <= 0.f) /* handle nan */) {
+                    if (!(pt.second <= MAX_LOG_VOLUME) /* handle nan */) {
                         ALOGE("positive volume dbFS");
                         return BAD_VALUE;
                     }
                 }
             } else {
                 for (const auto &pt : *this) {
-                    if (!(pt.second >= 0.f) || !(pt.second <= 1.f) /* handle nan */) {
-                        ALOGE("volume < 0.f or > 1.f");
+                    if (!(pt.second >= MIN_LINEAR_VOLUME)
+                            || !(pt.second <= MAX_LINEAR_VOLUME) /* handle nan */) {
+                        ALOGE("volume < MIN_LINEAR_VOLUME or > MAX_LINEAR_VOLUME");
                         return BAD_VALUE;
                     }
                 }
@@ -191,19 +236,22 @@
             return NO_ERROR;
         }
 
+        /* Clamps the volume curve in the configuration to
+         * the valid range for log or linear scale.
+         */
         void clampVolume() {
             if ((mOptionFlags & OPTION_FLAG_VOLUME_IN_DBFS) != 0) {
                 for (auto it = this->begin(); it != this->end(); ++it) {
-                    if (!(it->second <= 0.f) /* handle nan */) {
-                        it->second = 0.f;
+                    if (!(it->second <= MAX_LOG_VOLUME) /* handle nan */) {
+                        it->second = MAX_LOG_VOLUME;
                     }
                 }
             } else {
                 for (auto it = this->begin(); it != this->end(); ++it) {
-                    if (!(it->second >= 0.f) /* handle nan */) {
-                        it->second = 0.f;
-                    } else if (!(it->second <= 1.f)) {
-                        it->second = 1.f;
+                    if (!(it->second >= MIN_LINEAR_VOLUME) /* handle nan */) {
+                        it->second = MIN_LINEAR_VOLUME;
+                    } else if (!(it->second <= MAX_LINEAR_VOLUME)) {
+                        it->second = MAX_LINEAR_VOLUME;
                     }
                 }
             }
@@ -224,8 +272,9 @@
             if (endVolume == startVolume) {
                 // match with linear ramp
                 const T offset = volume - startVolume;
+                static const T scale =  1.f / (MAX_CURVE_TIME - MIN_CURVE_TIME); // nominally 1.f
                 for (auto it = this->begin(); it != this->end(); ++it) {
-                    it->second = it->second + offset * (1.f - it->first);
+                    it->second = it->second + offset * (MAX_CURVE_TIME - it->first) * scale;
                 }
             } else {
                 const T  scale = (volume - endVolume) / (startVolume - endVolume);
@@ -236,6 +285,7 @@
             clampVolume();
         }
 
+        // The parcel layout must match VolumeShaper.java
         status_t writeToParcel(Parcel *parcel) const {
             if (parcel == nullptr) return BAD_VALUE;
             return parcel->writeInt32((int32_t)mType)
@@ -261,32 +311,40 @@
                             ?: checkCurve();
         }
 
+        // Returns a string for debug printing.
         std::string toString() const {
             std::stringstream ss;
-            ss << "mType: " << mType << std::endl;
-            ss << "mId: " << mId << std::endl;
+            ss << "VolumeShaper::Configuration{mType=" << static_cast<int32_t>(mType);
+            ss << ", mId=" << mId;
             if (mType != TYPE_ID) {
-                ss << "mOptionFlags: " << mOptionFlags << std::endl;
-                ss << "mDurationMs: " << mDurationMs << std::endl;
-                ss << Interpolator<S, T>::toString().c_str();
+                ss << ", mOptionFlags=" << static_cast<int32_t>(mOptionFlags);
+                ss << ", mDurationMs=" << mDurationMs;
+                ss << ", " << Interpolator<S, T>::toString().c_str();
             }
+            ss << "}";
             return ss.str();
         }
 
     private:
-        Type mType;
-        int32_t mId;
-        OptionFlag mOptionFlags;
-        double mDurationMs;
+        Type mType;              // type of configuration
+        int32_t mId;             // A valid id is >= 0.
+        OptionFlag mOptionFlags; // option flags for the configuration.
+        double mDurationMs;      // duration, must be > 0; default is 1000 ms.
     }; // Configuration
 
-    // must match with VolumeShaper.java in frameworks/base
-    // TODO document per VolumeShaper.java flags.
+    /* VolumeShaper::Operation expresses an operation to perform on the
+     * configuration (either explicitly specified or an id).
+     *
+     * This parallels the Java implementation and the enums must match.
+     * See "frameworks/base/media/java/android/media/VolumeShaper.java" for
+     * details on the Java implementation.
+     */
     class Operation : public RefBase {
     public:
+        // Must match with VolumeShaper.java.
         enum Flag : int32_t {
             FLAG_NONE      = 0,
-            FLAG_REVERSE   = (1 << 0),
+            FLAG_REVERSE   = (1 << 0), // the absence of this indicates "play"
             FLAG_TERMINATE = (1 << 1),
             FLAG_JOIN      = (1 << 2),
             FLAG_DELAY     = (1 << 3),
@@ -300,15 +358,19 @@
             : Operation(FLAG_NONE, -1 /* replaceId */) {
         }
 
-        explicit Operation(Flag flags, int replaceId)
+        Operation(Flag flags, int replaceId)
             : Operation(flags, replaceId, std::numeric_limits<S>::quiet_NaN() /* xOffset */) {
         }
 
-        Operation(const Operation &operation)
+        explicit Operation(const Operation &operation)
             : Operation(operation.mFlags, operation.mReplaceId, operation.mXOffset) {
         }
 
-        explicit Operation(Flag flags, int replaceId, S xOffset)
+        explicit Operation(const sp<Operation> &operation)
+            : Operation(*operation.get()) {
+        }
+
+        Operation(Flag flags, int replaceId, S xOffset)
             : mFlags(flags)
             , mReplaceId(replaceId)
             , mXOffset(xOffset) {
@@ -327,13 +389,29 @@
         }
 
         void setXOffset(S xOffset) {
-            mXOffset = xOffset;
+            mXOffset = clamp(xOffset, MIN_CURVE_TIME /* lo */, MAX_CURVE_TIME /* hi */);
         }
 
         Flag getFlags() const {
             return mFlags;
         }
 
+        /* xOffset is the position on the volume curve and may go backwards
+         * if you are in reverse mode. This must be in the range from
+         * [MIN_CURVE_TIME, MAX_CURVE_TIME].
+         *
+         * normalizedTime always increases as time or framecount increases.
+         * normalizedTime is nominally from MIN_CURVE_TIME to MAX_CURVE_TIME when
+         * running through the curve, but could be outside this range afterwards.
+         * If you are reversing, this means the position on the curve, or xOffset,
+         * is computed as MAX_CURVE_TIME - normalizedTime, clamped to
+         * [MIN_CURVE_TIME, MAX_CURVE_TIME].
+         */
+        void setNormalizedTime(S normalizedTime) {
+            setXOffset((mFlags & FLAG_REVERSE) != 0
+                    ? MAX_CURVE_TIME - normalizedTime : normalizedTime);
+        }
+
         status_t setFlags(Flag flags) {
             if ((flags & ~FLAG_ALL) != 0) {
                 ALOGE("flags has invalid bits: %#x", flags);
@@ -360,28 +438,35 @@
 
         std::string toString() const {
             std::stringstream ss;
-            ss << "mFlags: " << mFlags << std::endl;
-            ss << "mReplaceId: " << mReplaceId << std::endl;
-            ss << "mXOffset: " << mXOffset << std::endl;
+            ss << "VolumeShaper::Operation{mFlags=" << static_cast<int32_t>(mFlags) ;
+            ss << ", mReplaceId=" << mReplaceId;
+            ss << ", mXOffset=" << mXOffset;
+            ss << "}";
             return ss.str();
         }
 
     private:
-        Flag mFlags;
-        int32_t mReplaceId;
-        S mXOffset;
+        Flag mFlags;        // operation to do
+        int32_t mReplaceId; // if >= 0 the id to remove in a replace operation.
+        S mXOffset;         // position in the curve to set if a valid number (not nan)
     }; // Operation
 
-    // must match with VolumeShaper.java in frameworks/base
+    /* VolumeShaper.State is returned when requesting the last
+     * state of the VolumeShaper.
+     *
+     * This parallels the Java implementation.
+     * See "frameworks/base/media/java/android/media/VolumeShaper.java" for
+     * details on the Java implementation.
+     */
     class State : public RefBase {
     public:
-        explicit State(T volume, S xOffset)
+        State(T volume, S xOffset)
             : mVolume(volume)
             , mXOffset(xOffset) {
         }
 
         State()
-            : State(-1.f, -1.f) { }
+            : State(NAN, NAN) { }
 
         T getVolume() const {
             return mVolume;
@@ -412,16 +497,18 @@
 
         std::string toString() const {
             std::stringstream ss;
-            ss << "mVolume: " << mVolume << std::endl;
-            ss << "mXOffset: " << mXOffset << std::endl;
+            ss << "VolumeShaper::State{mVolume=" << mVolume;
+            ss << ", mXOffset=" << mXOffset;
+            ss << "}";
             return ss.str();
         }
 
     private:
-        T mVolume;
-        S mXOffset;
+        T mVolume;   // linear volume in the range MIN_LINEAR_VOLUME to MAX_LINEAR_VOLUME
+        S mXOffset;  // position on curve expressed from MIN_CURVE_TIME to MAX_CURVE_TIME
     }; // State
 
+    // Internal helper class to do an affine transform for time and amplitude scaling.
     template <typename R>
     class Translate {
     public:
@@ -452,8 +539,9 @@
 
         std::string toString() const {
             std::stringstream ss;
-            ss << "mOffset: " << mOffset << std::endl;
-            ss << "mScale: " << mScale << std::endl;
+            ss << "VolumeShaper::Translate{mOffset=" << mOffset;
+            ss << ", mScale=" << mScale;
+            ss << "}";
             return ss.str();
         }
 
@@ -477,59 +565,73 @@
         return convertTimespecToUs(tv);
     }
 
-    // TODO: Since we pass configuration and operation as shared pointers
-    // there is a potential risk that the caller may modify these after
-    // delivery.  Currently, we don't require copies made here.
-    explicit VolumeShaper(
+    /* Native implementation of VolumeShaper.  This is NOT mirrored
+     * on the Java side, so we don't need to mimic Java side layout
+     * and data; furthermore, this isn't refcounted as a "RefBase" object.
+     *
+     * Since we pass configuration and operation as shared pointers (like
+     * Java) there is a potential risk that the caller may modify
+     * these after delivery.
+     */
+    VolumeShaper(
             const sp<VolumeShaper::Configuration> &configuration,
             const sp<VolumeShaper::Operation> &operation)
         : mConfiguration(configuration) // we do not make a copy
         , mOperation(operation)         // ditto
         , mStartFrame(-1)
         , mLastVolume(T(1))
-        , mLastXOffset(0.f)
-        , mDelayXOffset(std::numeric_limits<S>::quiet_NaN()) {
+        , mLastXOffset(MIN_CURVE_TIME)
+        , mDelayXOffset(MIN_CURVE_TIME) {
         if (configuration.get() != nullptr
                 && (getFlags() & VolumeShaper::Operation::FLAG_DELAY) == 0) {
             mLastVolume = configuration->first().second;
         }
     }
 
-    void updatePosition(int64_t startFrame, double sampleRate) {
-        double scale = (mConfiguration->last().first - mConfiguration->first().first)
-                        / (mConfiguration->getDurationMs() * 0.001 * sampleRate);
-        const double minScale = 1. / INT64_MAX;
-        scale = std::max(scale, minScale);
-        const S xOffset = std::isnan(mDelayXOffset) ? mConfiguration->first().first : mDelayXOffset;
-        VS_LOG("update position: scale %lf  frameCount:%lld, sampleRate:%lf, xOffset:%f",
-                scale, (long long) startFrame, sampleRate, xOffset);
-
-        mXTranslate.setOffset(startFrame - xOffset / scale);
-        mXTranslate.setScale(scale);
-        VS_LOG("translate: %s", mXTranslate.toString().c_str());
-    }
-
     // We allow a null operation here, though VolumeHandler always provides one.
     VolumeShaper::Operation::Flag getFlags() const {
         return mOperation == nullptr
-                ? VolumeShaper::Operation::FLAG_NONE :mOperation->getFlags();
+                ? VolumeShaper::Operation::FLAG_NONE : mOperation->getFlags();
     }
 
+    /* Returns the last volume and xoffset reported to the AudioFlinger.
+     * If the VolumeShaper has not been started, compute what the volume
+     * should be based on the initial offset specified.
+     */
     sp<VolumeShaper::State> getState() const {
-        return new VolumeShaper::State(mLastVolume, mLastXOffset);
+        if (!isStarted()) {
+            const T volume = computeVolumeFromXOffset(mDelayXOffset);
+            VS_LOG("delayed VolumeShaper, using cached offset:%f for volume:%f",
+                    mDelayXOffset, volume);
+            return new VolumeShaper::State(volume, mDelayXOffset);
+        } else {
+            return new VolumeShaper::State(mLastVolume, mLastXOffset);
+        }
+    }
+
+    S getDelayXOffset() const {
+        return mDelayXOffset;
     }
 
     void setDelayXOffset(S xOffset) {
-        mDelayXOffset = xOffset;
+        mDelayXOffset = clamp(xOffset, MIN_CURVE_TIME /* lo */, MAX_CURVE_TIME /* hi */);
     }
 
+    bool isStarted() const {
+        return mStartFrame >= 0;
+    }
+
+    /* getVolume() updates the last volume/xoffset state so it is not
+     * const, even though logically it may be viewed as const.
+     */
     std::pair<T /* volume */, bool /* active */> getVolume(
             int64_t trackFrameCount, double trackSampleRate) {
         if ((getFlags() & VolumeShaper::Operation::FLAG_DELAY) != 0) {
-            VS_LOG("delayed VolumeShaper, ignoring");
-            mLastVolume = T(1);
-            mLastXOffset = 0.;
-            return std::make_pair(T(1), false);
+            // We haven't had PLAY called yet, so just return the value
+            // as if PLAY were called just now.
+            VS_LOG("delayed VolumeShaper, using cached offset %f", mDelayXOffset);
+            const T volume = computeVolumeFromXOffset(mDelayXOffset);
+            return std::make_pair(volume, false);
         }
         const bool clockTime = (mConfiguration->getOptionFlags()
                 & VolumeShaper::Configuration::OPTION_FLAG_CLOCK_TIME) != 0;
@@ -537,84 +639,110 @@
         const double sampleRate = clockTime ? 1000000 : trackSampleRate;
 
         if (mStartFrame < 0) {
-            updatePosition(frameCount, sampleRate);
+            updatePosition(frameCount, sampleRate, mDelayXOffset);
             mStartFrame = frameCount;
         }
         VS_LOG("frameCount: %lld", (long long)frameCount);
-        S x = mXTranslate((T)frameCount);
-        VS_LOG("translation: %f", x);
+        const S x = mXTranslate((T)frameCount);
+        VS_LOG("translation to normalized time: %f", x);
 
-        // handle reversal of position
-        if (getFlags() & VolumeShaper::Operation::FLAG_REVERSE) {
-            x = 1.f - x;
-            VS_LOG("reversing to %f", x);
-            if (x < mConfiguration->first().first) {
-                mLastXOffset = 1.f;
-                const T volume = mConfiguration->adjustVolume(
-                        mConfiguration->first().second);  // persist last value
-                VS_LOG("persisting volume %f", volume);
-                mLastVolume = volume;
-                return std::make_pair(volume, false);
-            }
-            if (x > mConfiguration->last().first) {
-                mLastXOffset = 0.f;
-                mLastVolume = 1.f;
-                return std::make_pair(T(1), true); // too early
-            }
-        } else {
-            if (x < mConfiguration->first().first) {
-                mLastXOffset = 0.f;
-                mLastVolume = 1.f;
-                return std::make_pair(T(1), true); // too early
-            }
-            if (x > mConfiguration->last().first) {
-                mLastXOffset = 1.f;
-                const T volume = mConfiguration->adjustVolume(
-                        mConfiguration->last().second);  // persist last value
-                VS_LOG("persisting volume %f", volume);
-                mLastVolume = volume;
-                return std::make_pair(volume, false);
-            }
-        }
-        mLastXOffset = x;
-        // x contains the location on the volume curve to use.
-        const T unscaledVolume = mConfiguration->findY(x);
-        const T volume = mConfiguration->adjustVolume(unscaledVolume); // handle log scale
-        VS_LOG("volume: %f  unscaled: %f", volume, unscaledVolume);
-        mLastVolume = volume;
-        return std::make_pair(volume, true);
+        std::tuple<T /* volume */, S /* position */, bool /* active */> vt =
+                computeStateFromNormalizedTime(x);
+
+        mLastVolume = std::get<0>(vt);
+        mLastXOffset = std::get<1>(vt);
+        const bool active = std::get<2>(vt);
+        VS_LOG("rescaled time:%f  volume:%f  xOffset:%f  active:%s",
+                x, mLastVolume, mLastXOffset, active ? "true" : "false");
+        return std::make_pair(mLastVolume, active);
     }
 
     std::string toString() const {
         std::stringstream ss;
-        ss << "StartFrame: " << mStartFrame << std::endl;
-        ss << mXTranslate.toString().c_str();
-        if (mConfiguration.get() == nullptr) {
-            ss << "VolumeShaper::Configuration: nullptr" << std::endl;
-        } else {
-            ss << "VolumeShaper::Configuration:" << std::endl;
-            ss << mConfiguration->toString().c_str();
-        }
-        if (mOperation.get() == nullptr) {
-            ss << "VolumeShaper::Operation: nullptr" << std::endl;
-        } else {
-            ss << "VolumeShaper::Operation:" << std::endl;
-            ss << mOperation->toString().c_str();
-        }
+        ss << "VolumeShaper{mStartFrame=" << mStartFrame;
+        ss << ", mXTranslate=" << mXTranslate.toString().c_str();
+        ss << ", mConfiguration=" <<
+                (mConfiguration.get() == nullptr
+                        ? "nullptr" : mConfiguration->toString().c_str());
+        ss << ", mOperation=" <<
+                (mOperation.get() == nullptr
+                        ? "nullptr" :  mOperation->toString().c_str());
+        ss << "}";
         return ss.str();
     }
 
-    Translate<S> mXTranslate; // x axis translation from frames (in usec for clock time)
+    Translate<S> mXTranslate; // translation from frames (usec for clock time) to normalized time.
     sp<VolumeShaper::Configuration> mConfiguration;
     sp<VolumeShaper::Operation> mOperation;
+
+private:
     int64_t mStartFrame; // starting frame, non-negative when started (in usec for clock time)
     T mLastVolume;       // last computed interpolated volume (y-axis)
     S mLastXOffset;      // last computed interpolated xOffset/time (x-axis)
-    S mDelayXOffset;     // delay xOffset on first volumeshaper start.
+    S mDelayXOffset;     // xOffset to use for first invocation of VolumeShaper.
+
+    // Called internally to adjust mXTranslate for first time start.
+    void updatePosition(int64_t startFrame, double sampleRate, S xOffset) {
+        double scale = (mConfiguration->last().first - mConfiguration->first().first)
+                        / (mConfiguration->getDurationMs() * 0.001 * sampleRate);
+        const double minScale = 1. / static_cast<double>(INT64_MAX);
+        scale = std::max(scale, minScale);
+        VS_LOG("update position: scale %lf  frameCount:%lld, sampleRate:%lf, xOffset:%f",
+                scale, (long long) startFrame, sampleRate, xOffset);
+
+        S normalizedTime = (getFlags() & VolumeShaper::Operation::FLAG_REVERSE) != 0 ?
+                MAX_CURVE_TIME - xOffset : xOffset;
+        mXTranslate.setOffset(static_cast<float>(static_cast<double>(startFrame)
+                                                 - static_cast<double>(normalizedTime) / scale));
+        mXTranslate.setScale(static_cast<float>(scale));
+        VS_LOG("translate: %s", mXTranslate.toString().c_str());
+    }
+
+    T computeVolumeFromXOffset(S xOffset) const {
+        const T unscaledVolume = mConfiguration->findY(xOffset);
+        const T volume = mConfiguration->adjustVolume(unscaledVolume); // handle log scale
+        VS_LOG("computeVolumeFromXOffset %f -> %f -> %f", xOffset, unscaledVolume, volume);
+        return volume;
+    }
+
+    std::tuple<T /* volume */, S /* position */, bool /* active */>
+    computeStateFromNormalizedTime(S x) const {
+        bool active = true;
+        // handle reversal of position
+        if (getFlags() & VolumeShaper::Operation::FLAG_REVERSE) {
+            x = MAX_CURVE_TIME - x;
+            VS_LOG("reversing to %f", x);
+            if (x < MIN_CURVE_TIME) {
+                x = MIN_CURVE_TIME;
+                active = false; // at the end
+            } else if (x > MAX_CURVE_TIME) {
+                x = MAX_CURVE_TIME; //early
+            }
+        } else {
+            if (x < MIN_CURVE_TIME) {
+                x = MIN_CURVE_TIME; // early
+            } else if (x > MAX_CURVE_TIME) {
+                x = MAX_CURVE_TIME;
+                active = false; // at end
+            }
+        }
+        const S xOffset = x;
+        const T volume = computeVolumeFromXOffset(xOffset);
+        return std::make_tuple(volume, xOffset, active);
+    }
 }; // VolumeShaper
 
-// VolumeHandler combines the volume factors of multiple VolumeShapers and handles
-// multiple thread access by synchronizing all public methods.
+/* VolumeHandler combines the volume factors of multiple VolumeShapers associated
+ * with a player.  It is thread safe by synchronizing all public methods.
+ *
+ * This is a native-only implementation.
+ *
+ * The server side VolumeHandler is used to maintain a list of volume handlers,
+ * keep state, and obtain volume.
+ *
+ * The client side VolumeHandler is used to maintain a list of volume handlers,
+ * keep some partial state, and restore if the server dies.
+ */
 class VolumeHandler : public RefBase {
 public:
     using S = float;
@@ -628,12 +756,15 @@
     explicit VolumeHandler(uint32_t sampleRate)
         : mSampleRate((double)sampleRate)
         , mLastFrame(0)
-        , mVolumeShaperIdCounter(VolumeShaper::kSystemIdMax) {
+        , mVolumeShaperIdCounter(VolumeShaper::kSystemVolumeShapersMax)
+        , mLastVolume(1.f, false) {
     }
 
     VolumeShaper::Status applyVolumeShaper(
             const sp<VolumeShaper::Configuration> &configuration,
-            const sp<VolumeShaper::Operation> &operation) {
+            const sp<VolumeShaper::Operation> &operation_in) {
+        // make a local copy of operation, as we modify it.
+        sp<VolumeShaper::Operation> operation(new VolumeShaper::Operation(operation_in));
         VS_LOG("applyVolumeShaper:configuration: %s", configuration->toString().c_str());
         VS_LOG("applyVolumeShaper:operation: %s", operation->toString().c_str());
         AutoMutex _l(mLock);
@@ -656,14 +787,16 @@
         case VolumeShaper::Configuration::TYPE_SCALE: {
             const int replaceId = operation->getReplaceId();
             if (replaceId >= 0) {
+                VS_LOG("replacing %d", replaceId);
                 auto replaceIt = findId_l(replaceId);
                 if (replaceIt == mVolumeShapers.end()) {
                     ALOGW("cannot find replace id: %d", replaceId);
                 } else {
-                    if ((replaceIt->getFlags() & VolumeShaper::Operation::FLAG_JOIN) != 0) {
+                    if ((operation->getFlags() & VolumeShaper::Operation::FLAG_JOIN) != 0) {
                         // For join, we scale the start volume of the current configuration
                         // to match the last-used volume of the replacing VolumeShaper.
                         auto state = replaceIt->getState();
+                        ALOGD("join: state:%s", state->toString().c_str());
                         if (state->getXOffset() >= 0) { // valid
                             const T volume = state->getVolume();
                             ALOGD("join: scaling start volume to %f", volume);
@@ -685,8 +818,22 @@
                 ALOGW("duplicate id, removing old %d", id);
                 (void)mVolumeShapers.erase(oldIt);
             }
-            // create new VolumeShaper
-            mVolumeShapers.emplace_back(configuration, operation);
+
+            /* Check if too many application VolumeShapers (with id >= kSystemVolumeShapersMax).
+             * We check on the server side to ensure synchronization and robustness.
+             *
+             * This shouldn't fail on a replace command unless the replaced id is
+             * already invalid (which *should* be checked in the Java layer).
+             */
+            if (id >= VolumeShaper::kSystemVolumeShapersMax
+                    && numberOfUserVolumeShapers_l() >= VolumeShaper::kUserVolumeShapersMax) {
+                ALOGW("Too many app VolumeShapers, cannot add to VolumeHandler");
+                return VolumeShaper::Status(INVALID_OPERATION);
+            }
+
+            // create new VolumeShaper with default behavior.
+            mVolumeShapers.emplace_back(configuration, new VolumeShaper::Operation());
+            VS_LOG("after adding, number of volumeShapers:%zu", mVolumeShapers.size());
         }
         // fall through to handle the operation
         HANDLE_TYPE_ID:
@@ -697,7 +844,7 @@
                 VS_LOG("couldn't find id: %d", id);
                 return VolumeShaper::Status(INVALID_OPERATION);
             }
-            if ((it->getFlags() & VolumeShaper::Operation::FLAG_TERMINATE) != 0) {
+            if ((operation->getFlags() & VolumeShaper::Operation::FLAG_TERMINATE) != 0) {
                 VS_LOG("terminate id: %d", id);
                 mVolumeShapers.erase(it);
                 break;
@@ -706,29 +853,37 @@
                     & VolumeShaper::Configuration::OPTION_FLAG_CLOCK_TIME) != 0;
             if ((it->getFlags() & VolumeShaper::Operation::FLAG_REVERSE) !=
                     (operation->getFlags() & VolumeShaper::Operation::FLAG_REVERSE)) {
-                const int64_t frameCount = clockTime ? VolumeShaper::getNowUs() : mLastFrame;
-                const S x = it->mXTranslate((T)frameCount);
-                VS_LOG("reverse translation: %f", x);
-                // reflect position
-                S target = 1.f - x;
-                if (target < it->mConfiguration->first().first) {
-                    VS_LOG("clamp to start - begin immediately");
-                    target = 0.;
+                if (it->isStarted()) {
+                    const int64_t frameCount = clockTime ? VolumeShaper::getNowUs() : mLastFrame;
+                    const S x = it->mXTranslate((T)frameCount);
+                    VS_LOG("reverse normalizedTime: %f", x);
+                    // reflect position
+                    S target = MAX_CURVE_TIME - x;
+                    if (target < MIN_CURVE_TIME) {
+                        VS_LOG("clamp to start - begin immediately");
+                        target = MIN_CURVE_TIME;
+                    }
+                    VS_LOG("reverse normalizedTime target: %f", target);
+                    it->mXTranslate.setOffset(it->mXTranslate.getOffset()
+                            + (x - target) / it->mXTranslate.getScale());
                 }
-                VS_LOG("target reverse: %f", target);
-                it->mXTranslate.setOffset(it->mXTranslate.getOffset()
-                        + (x - target) / it->mXTranslate.getScale());
+                // if not started, the delay offset doesn't change.
             }
             const S xOffset = operation->getXOffset();
             if (!std::isnan(xOffset)) {
-                const int64_t frameCount = clockTime ? VolumeShaper::getNowUs() : mLastFrame;
-                const S x = it->mXTranslate((T)frameCount);
-                VS_LOG("xOffset translation: %f", x);
-                const S target = xOffset; // offset
-                VS_LOG("xOffset target x offset: %f", target);
-                it->mXTranslate.setOffset(it->mXTranslate.getOffset()
-                        + (x - target) / it->mXTranslate.getScale());
-                it->setDelayXOffset(xOffset);
+                if (it->isStarted()) {
+                    const int64_t frameCount = clockTime ? VolumeShaper::getNowUs() : mLastFrame;
+                    const S x = it->mXTranslate((T)frameCount);
+                    VS_LOG("normalizedTime translation: %f", x);
+                    const S target =
+                            (operation->getFlags() & VolumeShaper::Operation::FLAG_REVERSE) != 0 ?
+                                    MAX_CURVE_TIME - xOffset : xOffset;
+                    VS_LOG("normalizedTime target x offset: %f", target);
+                    it->mXTranslate.setOffset(it->mXTranslate.getOffset()
+                            + (x - target) / it->mXTranslate.getScale());
+                } else {
+                    it->setDelayXOffset(xOffset);
+                }
             }
             it->mOperation = operation; // replace the operation
         } break;
@@ -746,51 +901,78 @@
         return it->getState();
     }
 
+    /* getVolume() is not const, as it updates internal state.
+     * Once called, any VolumeShapers not already started begin running.
+     */
     std::pair<T /* volume */, bool /* active */> getVolume(int64_t trackFrameCount) {
         AutoMutex _l(mLock);
         mLastFrame = trackFrameCount;
         T volume(1);
         size_t activeCount = 0;
         for (auto it = mVolumeShapers.begin(); it != mVolumeShapers.end();) {
-            std::pair<T, bool> shaperVolume =
+            const std::pair<T, bool> shaperVolume =
                     it->getVolume(trackFrameCount, mSampleRate);
             volume *= shaperVolume.first;
             activeCount += shaperVolume.second;
             ++it;
         }
-        return std::make_pair(volume, activeCount != 0);
+        mLastVolume = std::make_pair(volume, activeCount != 0);
+        VS_LOG("getVolume: <%f, %s>", mLastVolume.first, mLastVolume.second ? "true" : "false");
+        return mLastVolume;
+    }
+
+    /* Used by a client side VolumeHandler to ensure all the VolumeShapers
+     * indicate that they have been started.  Upon a change in audioserver
+     * output sink, this information is used for restoration of the server side
+     * VolumeHandler.
+     */
+    void setStarted() {
+        (void)getVolume(mLastFrame);  // getVolume() will start the individual VolumeShapers.
+    }
+
+    std::pair<T /* volume */, bool /* active */> getLastVolume() const {
+        AutoMutex _l(mLock);
+        return mLastVolume;
     }
 
     std::string toString() const {
         AutoMutex _l(mLock);
         std::stringstream ss;
-        ss << "mSampleRate: " << mSampleRate << std::endl;
-        ss << "mLastFrame: " << mLastFrame << std::endl;
+        ss << "VolumeHandler{mSampleRate=" << mSampleRate;
+        ss << ", mLastFrame=" << mLastFrame;
+        ss << ", mVolumeShapers={";
+        bool first = true;
         for (const auto &shaper : mVolumeShapers) {
+            if (first) {
+                first = false;
+            } else {
+                ss << ", ";
+            }
             ss << shaper.toString().c_str();
         }
+        ss << "}}";
         return ss.str();
     }
 
-    void forall(const std::function<VolumeShaper::Status (
-            const sp<VolumeShaper::Configuration> &configuration,
-            const sp<VolumeShaper::Operation> &operation)> &lambda) {
+    void forall(const std::function<VolumeShaper::Status (const VolumeShaper &)> &lambda) {
         AutoMutex _l(mLock);
+        VS_LOG("forall: mVolumeShapers.size() %zu", mVolumeShapers.size());
         for (const auto &shaper : mVolumeShapers) {
-            VS_LOG("forall applying lambda");
-            (void)lambda(shaper.mConfiguration, shaper.mOperation);
+            VolumeShaper::Status status = lambda(shaper);
+            VS_LOG("forall applying lambda on shaper (%p): %d", &shaper, (int)status);
         }
     }
 
     void reset() {
         AutoMutex _l(mLock);
         mVolumeShapers.clear();
-        mLastFrame = -1;
+        mLastFrame = 0;
         // keep mVolumeShaperIdCounter as is.
     }
 
-    // Sets the configuration id if necessary - This is based on the counter
-    // internal to the VolumeHandler.
+    /* Sets the configuration id if necessary - This is based on the counter
+     * internal to the VolumeHandler.
+     */
     void setIdIfNecessary(const sp<VolumeShaper::Configuration> &configuration) {
         if (configuration->getType() == VolumeShaper::Configuration::TYPE_SCALE) {
             const int id = configuration->getId();
@@ -799,7 +981,7 @@
                 AutoMutex _l(mLock);
                 while (true) {
                     if (mVolumeShaperIdCounter == INT32_MAX) {
-                        mVolumeShaperIdCounter = VolumeShaper::kSystemIdMax;
+                        mVolumeShaperIdCounter = VolumeShaper::kSystemVolumeShapersMax;
                     } else {
                         ++mVolumeShaperIdCounter;
                     }
@@ -825,10 +1007,19 @@
         return it;
     }
 
+    size_t numberOfUserVolumeShapers_l() const {
+        size_t count = 0;
+        for (const auto &shaper : mVolumeShapers) {
+            count += (shaper.mConfiguration->getId() >= VolumeShaper::kSystemVolumeShapersMax);
+        }
+        return count;
+    }
+
     mutable Mutex mLock;
     double mSampleRate; // in samples (frames) per second
-    int64_t mLastFrame; // logging purpose only
+    int64_t mLastFrame; // logging purpose only, 0 on start
     int32_t mVolumeShaperIdCounter; // a counter to return a unique volume shaper id.
+    std::pair<T /* volume */, bool /* active */> mLastVolume;
     std::list<VolumeShaper> mVolumeShapers; // list provides stable iterators on erase
 }; // VolumeHandler
 
diff --git a/include/media/omx/1.0/WGraphicBufferSource.h b/include/media/omx/1.0/WGraphicBufferSource.h
index 0ca5f44..397e576 100644
--- a/include/media/omx/1.0/WGraphicBufferSource.h
+++ b/include/media/omx/1.0/WGraphicBufferSource.h
@@ -67,14 +67,11 @@
 struct LWGraphicBufferSource : public BnGraphicBufferSource {
     sp<TGraphicBufferSource> mBase;
     LWGraphicBufferSource(sp<TGraphicBufferSource> const& base);
-    BnStatus configure(
-            const sp<IOMXNode>& omxNode, int32_t dataSpace) override;
+    BnStatus configure(const sp<IOMXNode>& omxNode, int32_t dataSpace) override;
     BnStatus setSuspend(bool suspend, int64_t timeUs) override;
-    BnStatus setRepeatPreviousFrameDelayUs(
-            int64_t repeatAfterUs) override;
+    BnStatus setRepeatPreviousFrameDelayUs(int64_t repeatAfterUs) override;
     BnStatus setMaxFps(float maxFps) override;
-    BnStatus setTimeLapseConfig(
-            int64_t timePerFrameUs, int64_t timePerCaptureUs) override;
+    BnStatus setTimeLapseConfig(double fps, double captureFps) override;
     BnStatus setStartTimeUs(int64_t startTimeUs) override;
     BnStatus setStopTimeUs(int64_t stopTimeUs) override;
     BnStatus setColorAspects(int32_t aspects) override;
diff --git a/include/ndk/NdkImage.h b/include/ndk/NdkImage.h
index 3d1bacc..66005cb 100644
--- a/include/ndk/NdkImage.h
+++ b/include/ndk/NdkImage.h
@@ -136,7 +136,7 @@
      * <p>
      * Corresponding formats:
      * <ul>
-     * <li>AHardwareBuffer: AHARDWAREBUFFER_FORMAT_R16G16B16A16_SFLOAT</li>
+     * <li>AHardwareBuffer: AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT</li>
      * <li>Vulkan: VK_FORMAT_R16G16B16A16_SFLOAT</li>
      * <li>OpenGL ES: GL_RGBA16F</li>
      * </ul>
diff --git a/include/ndk/NdkImageReader.h b/include/ndk/NdkImageReader.h
index a158da9..e3600c2 100644
--- a/include/ndk/NdkImageReader.h
+++ b/include/ndk/NdkImageReader.h
@@ -307,22 +307,38 @@
  * for the consumer usage. All other parameters and the return values are identical to those passed
  * to {@line AImageReader_new}.
  *
- * @param usage0 specifies how the consumer will access the AImage, using combination of the
- *            AHARDWAREBUFFER_USAGE0 flags described in {@link hardware_buffer.h}.
- *            Passing {@link AHARDWAREBUFFER_USAGE0_CPU_READ_OFTEN} is equivalent to calling
- *            {@link AImageReader_new} with the same parameters. Note that consumers that do not
- *            require CPU access to the buffer should omit {@link
- *            AHARDWAREBUFFER_USAGE0_CPU_READ_OFTEN} to improve performance.
- * @param usage1 specifies how the consumer will access the AImage, using combination of the
- *            AHARDWAREBUFFER_USAGE1 flags described in {@link hardware_buffer.h}.
+ * @param usage specifies how the consumer will access the AImage, using combination of the
+ *            AHARDWAREBUFFER_USAGE flags described in {@link hardware_buffer.h}.
+ *            Passing {@link AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN} is equivalent to calling
+ *            {@link AImageReader_new} with the same parameters.
+ *
+ * Note that not all format and usage flag combination is supported by the {@link AImageReader}.
+ * Below are the combinations supported by the {@link AImageReader}.
+ * <table>
+ * <tr>
+ *   <th>Format</th>
+ *   <th>Compatible usage flags</th>
+ * </tr>
+ * <tr>
+ *   <td>non-{@link AIMAGE_FORMAT_PRIVATE PRIVATE} formats defined in {@link AImage.h}
+ * </td>
+ *   <td>{@link AHARDWAREBUFFER_USAGE_CPU_READ_RARELY} or
+ *   {@link AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN}</td>
+ * </tr>
+ * <tr>
+ *   <td>{@link AIMAGE_FORMAT_RGBA_8888}</td>
+ *   <td>{@link AHARDWAREBUFFER_USAGE_VIDEO_ENCODE} or
+ *   {@link AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE}, or combined</td>
+ * </tr>
+ * </table>
  *
  * @see AImage
  * @see AImageReader_new
  * @see AHardwareBuffer
  */
 media_status_t AImageReader_newWithUsage(
-        int32_t width, int32_t height, int32_t format, uint64_t usage0,
-        uint64_t usage1, int32_t maxImages, /*out*/ AImageReader** reader);
+        int32_t width, int32_t height, int32_t format, uint64_t usage, int32_t maxImages,
+        /*out*/ AImageReader** reader);
 
 /*
  * Acquire the next {@link AImage} from the image reader's queue asynchronously.
diff --git a/media/libaaudio/examples/input_monitor/src/input_monitor_callback.cpp b/media/libaaudio/examples/input_monitor/src/input_monitor_callback.cpp
index b482e93..7c34252 100644
--- a/media/libaaudio/examples/input_monitor/src/input_monitor_callback.cpp
+++ b/media/libaaudio/examples/input_monitor/src/input_monitor_callback.cpp
@@ -24,7 +24,7 @@
 #include <time.h>
 #include <aaudio/AAudio.h>
 
-#define NUM_SECONDS           10
+#define NUM_SECONDS           5
 #define NANOS_PER_MICROSECOND ((int64_t)1000)
 #define NANOS_PER_MILLISECOND (NANOS_PER_MICROSECOND * 1000)
 #define NANOS_PER_SECOND      (NANOS_PER_MILLISECOND * 1000)
@@ -33,13 +33,13 @@
 #define SHARING_MODE  AAUDIO_SHARING_MODE_SHARED
 
 /**
- * Simple wrapper for AAudio that opens a default stream and then calls
- * a callback function to fill the output buffers.
+ * Simple wrapper for AAudio that opens an input stream and then calls
+ * a callback function to process the input data.
  */
-class SimpleAAudioPlayer {
+class SimpleAAudioRecorder {
 public:
-    SimpleAAudioPlayer() {}
-    ~SimpleAAudioPlayer() {
+    SimpleAAudioRecorder() {}
+    ~SimpleAAudioRecorder() {
         close();
     };
 
@@ -71,6 +71,15 @@
         }
         return AAudioStream_getSamplesPerFrame(mStream);;
     }
+    /**
+     * Only call this after open() has been called.
+     */
+    int64_t getFramesRead() {
+        if (mStream == nullptr) {
+            return AAUDIO_ERROR_INVALID_STATE;
+        }
+        return AAudioStream_getFramesRead(mStream);;
+    }
 
     /**
      * Open a stream
@@ -85,7 +94,7 @@
         AAudioStreamBuilder_setDirection(mBuilder, AAUDIO_DIRECTION_INPUT);
         AAudioStreamBuilder_setSharingMode(mBuilder, mRequestedSharingMode);
         AAudioStreamBuilder_setDataCallback(mBuilder, proc, userContext);
-        AAudioStreamBuilder_setFormat(mBuilder, AAUDIO_FORMAT_PCM_I16);
+        AAudioStreamBuilder_setFormat(mBuilder, AAUDIO_FORMAT_PCM_FLOAT);
 
         // Open an AAudioStream using the Builder.
         result = AAudioStreamBuilder_openStream(mBuilder, &mStream);
@@ -121,11 +130,10 @@
     }
 
     // Write zero data to fill up the buffer and prevent underruns.
-    // Assume format is PCM_I16. TODO use floats.
     aaudio_result_t prime() {
         int32_t samplesPerFrame = AAudioStream_getSamplesPerFrame(mStream);
         const int numFrames = 32; // arbitrary
-        int16_t zeros[numFrames * samplesPerFrame];
+        float zeros[numFrames * samplesPerFrame];
         memset(zeros, 0, sizeof(zeros));
         aaudio_result_t result = numFrames;
         while (result == numFrames) {
@@ -151,8 +159,16 @@
             fprintf(stderr, "ERROR - AAudioStream_requestStop() returned %d %s\n",
                     result, AAudio_convertResultToText(result));
         }
-        int32_t xRunCount = AAudioStream_getXRunCount(mStream);
-        printf("AAudioStream_getXRunCount %d\n", xRunCount);
+        return result;
+    }
+
+    // Pause the stream. AAudio will stop calling your callback function.
+    aaudio_result_t pause() {
+        aaudio_result_t result = AAudioStream_requestPause(mStream);
+        if (result != AAUDIO_OK) {
+            fprintf(stderr, "ERROR - AAudioStream_requestPause() returned %d %s\n",
+                    result, AAudio_convertResultToText(result));
+        }
         return result;
     }
 
@@ -227,7 +243,7 @@
 int main(int argc, char **argv)
 {
     (void)argc; // unused
-    SimpleAAudioPlayer player;
+    SimpleAAudioRecorder recorder;
     PeakTrackerData_t myData = {0.0};
     aaudio_result_t result;
     const int displayRateHz = 20; // arbitrary
@@ -238,37 +254,60 @@
     setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
     printf("%s - Display audio input using an AAudio callback\n", argv[0]);
 
-    player.setSharingMode(SHARING_MODE);
+    recorder.setSharingMode(SHARING_MODE);
 
-    result = player.open(MyDataCallbackProc, &myData);
+    result = recorder.open(MyDataCallbackProc, &myData);
     if (result != AAUDIO_OK) {
-        fprintf(stderr, "ERROR -  player.open() returned %d\n", result);
+        fprintf(stderr, "ERROR -  recorder.open() returned %d\n", result);
         goto error;
     }
-    printf("player.getFramesPerSecond() = %d\n", player.getFramesPerSecond());
-    printf("player.getSamplesPerFrame() = %d\n", player.getSamplesPerFrame());
+    printf("recorder.getFramesPerSecond() = %d\n", recorder.getFramesPerSecond());
+    printf("recorder.getSamplesPerFrame() = %d\n", recorder.getSamplesPerFrame());
 
-    result = player.start();
+    result = recorder.start();
     if (result != AAUDIO_OK) {
-        fprintf(stderr, "ERROR -  player.start() returned %d\n", result);
+        fprintf(stderr, "ERROR -  recorder.start() returned %d\n", result);
         goto error;
     }
 
-    printf("Sleep for %d seconds while audio plays in a callback thread.\n", NUM_SECONDS);
-   for (int i = 0; i < loopsNeeded; i++)
+    printf("Sleep for %d seconds while audio record in a callback thread.\n", NUM_SECONDS);
+    for (int i = 0; i < loopsNeeded; i++)
     {
         const struct timespec request = { .tv_sec = 0,
                 .tv_nsec = NANOS_PER_SECOND / displayRateHz };
         (void) clock_nanosleep(CLOCK_MONOTONIC, 0 /*flags*/, &request, NULL /*remain*/);
+        printf("%08d: ", (int)recorder.getFramesRead());
+        displayPeakLevel(myData.peakLevel);
+    }
+    printf("Woke up. Stop for a moment.\n");
+
+    result = recorder.stop();
+    if (result != AAUDIO_OK) {
+        goto error;
+    }
+    sleep(1);
+    result = recorder.start();
+    if (result != AAUDIO_OK) {
+        fprintf(stderr, "ERROR -  recorder.start() returned %d\n", result);
+        goto error;
+    }
+
+    printf("Sleep for %d seconds while audio records in a callback thread.\n", NUM_SECONDS);
+    for (int i = 0; i < loopsNeeded; i++)
+    {
+        const struct timespec request = { .tv_sec = 0,
+                .tv_nsec = NANOS_PER_SECOND / displayRateHz };
+        (void) clock_nanosleep(CLOCK_MONOTONIC, 0 /*flags*/, &request, NULL /*remain*/);
+        printf("%08d: ", (int)recorder.getFramesRead());
         displayPeakLevel(myData.peakLevel);
     }
     printf("Woke up now.\n");
 
-    result = player.stop();
+    result = recorder.stop();
     if (result != AAUDIO_OK) {
         goto error;
     }
-    result = player.close();
+    result = recorder.close();
     if (result != AAUDIO_OK) {
         goto error;
     }
@@ -276,7 +315,7 @@
     printf("SUCCESS\n");
     return EXIT_SUCCESS;
 error:
-    player.close();
+    recorder.close();
     printf("exiting - AAudio result = %d = %s\n", result, AAudio_convertResultToText(result));
     return EXIT_FAILURE;
 }
diff --git a/media/libaaudio/examples/write_sine/jni/Android.mk b/media/libaaudio/examples/write_sine/jni/Android.mk
index 5a884e1..0bda008 100644
--- a/media/libaaudio/examples/write_sine/jni/Android.mk
+++ b/media/libaaudio/examples/write_sine/jni/Android.mk
@@ -4,7 +4,8 @@
 LOCAL_MODULE_TAGS := tests
 LOCAL_C_INCLUDES := \
     $(call include-path-for, audio-utils) \
-    frameworks/av/media/libaaudio/include
+    frameworks/av/media/libaaudio/include \
+    frameworks/av/media/libaaudio/src
 
 # NDK recommends using this kind of relative path instead of an absolute path.
 LOCAL_SRC_FILES:= ../src/write_sine.cpp
diff --git a/media/libaaudio/examples/write_sine/src/write_sine.cpp b/media/libaaudio/examples/write_sine/src/write_sine.cpp
index d8e5ec1..57a5273 100644
--- a/media/libaaudio/examples/write_sine/src/write_sine.cpp
+++ b/media/libaaudio/examples/write_sine/src/write_sine.cpp
@@ -22,12 +22,16 @@
 #include <aaudio/AAudio.h>
 #include "SineGenerator.h"
 
-#define SAMPLE_RATE   48000
-#define NUM_SECONDS   10
+#define SAMPLE_RATE           48000
+#define NUM_SECONDS           5
 #define NANOS_PER_MICROSECOND ((int64_t)1000)
 #define NANOS_PER_MILLISECOND (NANOS_PER_MICROSECOND * 1000)
 #define NANOS_PER_SECOND      (NANOS_PER_MILLISECOND * 1000)
 
+#define REQUESTED_FORMAT  AAUDIO_FORMAT_PCM_I16
+#define REQUESTED_SHARING_MODE  AAUDIO_SHARING_MODE_SHARED
+//#define REQUESTED_SHARING_MODE  AAUDIO_SHARING_MODE_EXCLUSIVE
+
 static const char *getSharingModeText(aaudio_sharing_mode_t mode) {
     const char *modeText = "unknown";
     switch (mode) {
@@ -59,27 +63,25 @@
 
     aaudio_result_t result = AAUDIO_OK;
 
-    const int requestedSamplesPerFrame = 2;
-    int actualSamplesPerFrame = 0;
+    const int requestedChannelCount = 2;
+    int actualChannelCount = 0;
     const int requestedSampleRate = SAMPLE_RATE;
     int actualSampleRate = 0;
-    const aaudio_audio_format_t requestedDataFormat = AAUDIO_FORMAT_PCM_I16;
-    aaudio_audio_format_t actualDataFormat = AAUDIO_FORMAT_PCM_I16;
+    aaudio_audio_format_t actualDataFormat = AAUDIO_FORMAT_UNSPECIFIED;
 
-    //const aaudio_sharing_mode_t requestedSharingMode = AAUDIO_SHARING_MODE_EXCLUSIVE;
-    const aaudio_sharing_mode_t requestedSharingMode = AAUDIO_SHARING_MODE_SHARED;
     aaudio_sharing_mode_t actualSharingMode = AAUDIO_SHARING_MODE_SHARED;
 
     AAudioStreamBuilder *aaudioBuilder = nullptr;
     AAudioStream *aaudioStream = nullptr;
     aaudio_stream_state_t state = AAUDIO_STREAM_STATE_UNINITIALIZED;
-    int32_t framesPerBurst = 0;
-    int32_t framesPerWrite = 0;
-    int32_t bufferCapacity = 0;
-    int32_t framesToPlay = 0;
-    int32_t framesLeft = 0;
-    int32_t xRunCount = 0;
-    int16_t *data = nullptr;
+    int32_t  framesPerBurst = 0;
+    int32_t  framesPerWrite = 0;
+    int32_t  bufferCapacity = 0;
+    int32_t  framesToPlay = 0;
+    int32_t  framesLeft = 0;
+    int32_t  xRunCount = 0;
+    float   *floatData = nullptr;
+    int16_t *shortData = nullptr;
 
     SineGenerator sineOsc1;
     SineGenerator sineOsc2;
@@ -98,9 +100,13 @@
 
     // Request stream properties.
     AAudioStreamBuilder_setSampleRate(aaudioBuilder, requestedSampleRate);
-    AAudioStreamBuilder_setSamplesPerFrame(aaudioBuilder, requestedSamplesPerFrame);
-    AAudioStreamBuilder_setFormat(aaudioBuilder, requestedDataFormat);
-    AAudioStreamBuilder_setSharingMode(aaudioBuilder, requestedSharingMode);
+    AAudioStreamBuilder_setChannelCount(aaudioBuilder, requestedChannelCount);
+    AAudioStreamBuilder_setFormat(aaudioBuilder, REQUESTED_FORMAT);
+    AAudioStreamBuilder_setSharingMode(aaudioBuilder, REQUESTED_SHARING_MODE);
+
+    AAudioStreamBuilder_setPerformanceMode(aaudioBuilder, AAUDIO_PERFORMANCE_MODE_NONE);
+    //AAudioStreamBuilder_setPerformanceMode(aaudioBuilder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
+    //AAudioStreamBuilder_setPerformanceMode(aaudioBuilder, AAUDIO_PERFORMANCE_MODE_POWER_SAVING);
 
     // Create an AAudioStream using the Builder.
     result = AAudioStreamBuilder_openStream(aaudioBuilder, &aaudioStream);
@@ -118,21 +124,21 @@
     sineOsc1.setup(440.0, actualSampleRate);
     sineOsc2.setup(660.0, actualSampleRate);
 
-    actualSamplesPerFrame = AAudioStream_getSamplesPerFrame(aaudioStream);
-    printf("SamplesPerFrame: requested = %d, actual = %d\n",
-            requestedSamplesPerFrame, actualSamplesPerFrame);
+    actualChannelCount = AAudioStream_getChannelCount(aaudioStream);
+    printf("ChannelCount: requested = %d, actual = %d\n",
+            requestedChannelCount, actualChannelCount);
 
     actualSharingMode = AAudioStream_getSharingMode(aaudioStream);
     printf("SharingMode: requested = %s, actual = %s\n",
-            getSharingModeText(requestedSharingMode),
+            getSharingModeText(REQUESTED_SHARING_MODE),
             getSharingModeText(actualSharingMode));
 
     // This is the number of frames that are read in one chunk by a DMA controller
     // or a DSP or a mixer.
     framesPerBurst = AAudioStream_getFramesPerBurst(aaudioStream);
-    printf("DataFormat: framesPerBurst = %d\n",framesPerBurst);
+    printf("Buffer: bufferSize = %d\n", AAudioStream_getBufferSizeInFrames(aaudioStream));
     bufferCapacity = AAudioStream_getBufferCapacityInFrames(aaudioStream);
-    printf("DataFormat: bufferCapacity = %d, remainder = %d\n",
+    printf("Buffer: bufferCapacity = %d, remainder = %d\n",
            bufferCapacity, bufferCapacity % framesPerBurst);
 
     // Some DMA might use very short bursts of 16 frames. We don't need to write such small
@@ -141,17 +147,22 @@
     while (framesPerWrite < 48) {
         framesPerWrite *= 2;
     }
-    printf("DataFormat: framesPerWrite = %d\n",framesPerWrite);
+    printf("Buffer: framesPerBurst = %d\n",framesPerBurst);
+    printf("Buffer: framesPerWrite = %d\n",framesPerWrite);
 
     actualDataFormat = AAudioStream_getFormat(aaudioStream);
-    printf("DataFormat: requested = %d, actual = %d\n", requestedDataFormat, actualDataFormat);
+    printf("DataFormat: requested = %d, actual = %d\n", REQUESTED_FORMAT, actualDataFormat);
     // TODO handle other data formats
 
+    printf("PerformanceMode: %d\n", AAudioStream_getPerformanceMode(aaudioStream));
+
     // Allocate a buffer for the audio data.
-    data = new int16_t[framesPerWrite * actualSamplesPerFrame];
-    if (data == nullptr) {
-        fprintf(stderr, "ERROR - could not allocate data buffer\n");
-        result = AAUDIO_ERROR_NO_MEMORY;
+    if (actualDataFormat == AAUDIO_FORMAT_PCM_FLOAT) {
+        floatData = new float[framesPerWrite * actualChannelCount];
+    } else if (actualDataFormat == AAUDIO_FORMAT_PCM_I16) {
+        shortData = new int16_t[framesPerWrite * actualChannelCount];
+    } else {
+        printf("ERROR Unsupported data format!\n");
         goto finish;
     }
 
@@ -170,26 +181,41 @@
     framesToPlay = actualSampleRate * NUM_SECONDS;
     framesLeft = framesToPlay;
     while (framesLeft > 0) {
-        // Render sine waves to left and right channels.
-        sineOsc1.render(&data[0], actualSamplesPerFrame, framesPerWrite);
-        if (actualSamplesPerFrame > 1) {
-            sineOsc2.render(&data[1], actualSamplesPerFrame, framesPerWrite);
+
+        if (actualDataFormat == AAUDIO_FORMAT_PCM_FLOAT) {
+            // Render sine waves to left and right channels.
+            sineOsc1.render(&floatData[0], actualChannelCount, framesPerWrite);
+            if (actualChannelCount > 1) {
+                sineOsc2.render(&floatData[1], actualChannelCount, framesPerWrite);
+            }
+        } else if (actualDataFormat == AAUDIO_FORMAT_PCM_I16) {
+            // Render sine waves to left and right channels.
+            sineOsc1.render(&shortData[0], actualChannelCount, framesPerWrite);
+            if (actualChannelCount > 1) {
+                sineOsc2.render(&shortData[1], actualChannelCount, framesPerWrite);
+            }
         }
 
         // Write audio data to the stream.
-        int64_t timeoutNanos = 100 * NANOS_PER_MILLISECOND;
-        int minFrames = (framesToPlay < framesPerWrite) ? framesToPlay : framesPerWrite;
-        int actual = AAudioStream_write(aaudioStream, data, minFrames, timeoutNanos);
+        int64_t timeoutNanos = 1000 * NANOS_PER_MILLISECOND;
+        int32_t minFrames = (framesToPlay < framesPerWrite) ? framesToPlay : framesPerWrite;
+        int32_t actual = 0;
+        if (actualDataFormat == AAUDIO_FORMAT_PCM_FLOAT) {
+            actual = AAudioStream_write(aaudioStream, floatData, minFrames, timeoutNanos);
+        } else if (actualDataFormat == AAUDIO_FORMAT_PCM_I16) {
+            actual = AAudioStream_write(aaudioStream, shortData, minFrames, timeoutNanos);
+        }
         if (actual < 0) {
-            fprintf(stderr, "ERROR - AAudioStream_write() returned %zd\n", actual);
+            fprintf(stderr, "ERROR - AAudioStream_write() returned %d\n", actual);
             goto finish;
         } else if (actual == 0) {
-            fprintf(stderr, "WARNING - AAudioStream_write() returned %zd\n", actual);
+            fprintf(stderr, "WARNING - AAudioStream_write() returned %d\n", actual);
             goto finish;
         }
         framesLeft -= actual;
 
         // Use timestamp to estimate latency.
+        /*
         {
             int64_t presentationFrame;
             int64_t presentationTime;
@@ -208,13 +234,15 @@
                 printf("estimatedLatencyMillis %d\n", (int)estimatedLatencyMillis);
             }
         }
+         */
     }
 
     xRunCount = AAudioStream_getXRunCount(aaudioStream);
     printf("AAudioStream_getXRunCount %d\n", xRunCount);
 
 finish:
-    delete[] data;
+    delete[] floatData;
+    delete[] shortData;
     AAudioStream_close(aaudioStream);
     AAudioStreamBuilder_delete(aaudioBuilder);
     printf("exiting - AAudio result = %d = %s\n", result, AAudio_convertResultToText(result));
diff --git a/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp b/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
index 9414236..1a66f35 100644
--- a/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
+++ b/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
@@ -31,8 +31,6 @@
 //#define SHARING_MODE  AAUDIO_SHARING_MODE_EXCLUSIVE
 #define SHARING_MODE  AAUDIO_SHARING_MODE_SHARED
 
-#define  CALLBACK_SIZE_FRAMES    128
-
 // TODO refactor common code into a single SimpleAAudio class
 /**
  * Simple wrapper for AAudio that opens a default stream and then calls
@@ -67,11 +65,11 @@
     /**
      * Only call this after open() has been called.
      */
-    int32_t getSamplesPerFrame() {
+    int32_t getChannelCount() {
         if (mStream == nullptr) {
             return AAUDIO_ERROR_INVALID_STATE;
         }
-        return AAudioStream_getSamplesPerFrame(mStream);;
+        return AAudioStream_getChannelCount(mStream);;
     }
 
     /**
@@ -87,8 +85,12 @@
         AAudioStreamBuilder_setSharingMode(mBuilder, mRequestedSharingMode);
         AAudioStreamBuilder_setDataCallback(mBuilder, dataProc, userContext);
         AAudioStreamBuilder_setFormat(mBuilder, AAUDIO_FORMAT_PCM_FLOAT);
-        AAudioStreamBuilder_setFramesPerDataCallback(mBuilder, CALLBACK_SIZE_FRAMES);
- //       AAudioStreamBuilder_setBufferCapacityInFrames(mBuilder, CALLBACK_SIZE_FRAMES * 4);
+ //       AAudioStreamBuilder_setFramesPerDataCallback(mBuilder, CALLBACK_SIZE_FRAMES);
+        AAudioStreamBuilder_setBufferCapacityInFrames(mBuilder, 48 * 8);
+
+        //AAudioStreamBuilder_setPerformanceMode(mBuilder, AAUDIO_PERFORMANCE_MODE_NONE);
+        AAudioStreamBuilder_setPerformanceMode(mBuilder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
+        //AAudioStreamBuilder_setPerformanceMode(mBuilder, AAUDIO_PERFORMANCE_MODE_POWER_SAVING);
 
         // Open an AAudioStream using the Builder.
         result = AAudioStreamBuilder_openStream(mBuilder, &mStream);
@@ -100,7 +102,6 @@
                AAudioStream_getBufferSizeInFrames(mStream));
         printf("AAudioStream_getBufferCapacityInFrames() = %d\n",
                AAudioStream_getBufferCapacityInFrames(mStream));
-        return result;
 
      finish1:
         AAudioStreamBuilder_delete(mBuilder);
@@ -121,7 +122,7 @@
 
     // Write zero data to fill up the buffer and prevent underruns.
     aaudio_result_t prime() {
-        int32_t samplesPerFrame = AAudioStream_getSamplesPerFrame(mStream);
+        int32_t samplesPerFrame = AAudioStream_getChannelCount(mStream);
         const int numFrames = 32;
         float zeros[numFrames * samplesPerFrame];
         memset(zeros, 0, sizeof(zeros));
@@ -136,7 +137,7 @@
      aaudio_result_t start() {
         aaudio_result_t result = AAudioStream_requestStart(mStream);
         if (result != AAUDIO_OK) {
-            fprintf(stderr, "ERROR - AAudioStream_requestStart() returned %d %s\n",
+            printf("ERROR - AAudioStream_requestStart() returned %d %s\n",
                     result, AAudio_convertResultToText(result));
         }
         return result;
@@ -146,7 +147,7 @@
     aaudio_result_t stop() {
         aaudio_result_t result = AAudioStream_requestStop(mStream);
         if (result != AAUDIO_OK) {
-            fprintf(stderr, "ERROR - AAudioStream_requestStop() returned %d %s\n",
+            printf("ERROR - AAudioStream_requestStop() returned %d %s\n",
                     result, AAudio_convertResultToText(result));
         }
         int32_t xRunCount = AAudioStream_getXRunCount(mStream);
@@ -169,9 +170,6 @@
 typedef struct SineThreadedData_s {
     SineGenerator  sineOsc1;
     SineGenerator  sineOsc2;
-    // Remove these variables used for testing.
-    int32_t        numFrameCounts;
-    int32_t        frameCounts[MAX_FRAME_COUNT_RECORDS];
     int            scheduler;
     bool           schedulerChecked;
 } SineThreadedData_t;
@@ -186,16 +184,12 @@
 
     SineThreadedData_t *sineData = (SineThreadedData_t *) userData;
 
-    if (sineData->numFrameCounts < MAX_FRAME_COUNT_RECORDS) {
-        sineData->frameCounts[sineData->numFrameCounts++] = numFrames;
-    }
-
     if (!sineData->schedulerChecked) {
         sineData->scheduler = sched_getscheduler(gettid());
         sineData->schedulerChecked = true;
     }
 
-    int32_t samplesPerFrame = AAudioStream_getSamplesPerFrame(stream);
+    int32_t samplesPerFrame = AAudioStream_getChannelCount(stream);
     // This code only plays on the first one or two channels.
     // TODO Support arbitrary number of channels.
     switch (AAudioStream_getFormat(stream)) {
@@ -240,7 +234,6 @@
 
     player.setSharingMode(SHARING_MODE);
 
-    myData.numFrameCounts = 0;
     myData.schedulerChecked = false;
 
     result = player.open(MyDataCallbackProc, &myData);
@@ -249,7 +242,7 @@
         goto error;
     }
     printf("player.getFramesPerSecond() = %d\n", player.getFramesPerSecond());
-    printf("player.getSamplesPerFrame() = %d\n", player.getSamplesPerFrame());
+    printf("player.getChannelCount() = %d\n", player.getChannelCount());
     myData.sineOsc1.setup(440.0, 48000);
     myData.sineOsc1.setSweep(300.0, 600.0, 5.0);
     myData.sineOsc2.setup(660.0, 48000);
@@ -288,22 +281,21 @@
             printf("Stream state is %d %s!\n", state, AAudio_convertStreamStateToText(state));
             break;
         }
+        printf("framesWritten = %d\n", (int) AAudioStream_getFramesWritten(player.getStream()));
     }
     printf("Woke up now.\n");
 
+    printf("call stop()\n");
     result = player.stop();
     if (result != AAUDIO_OK) {
         goto error;
     }
+    printf("call close()\n");
     result = player.close();
     if (result != AAUDIO_OK) {
         goto error;
     }
 
-    // Report data gathered in the callback.
-    for (int i = 0; i < myData.numFrameCounts; i++) {
-        printf("numFrames[%4d] = %4d\n", i, myData.frameCounts[i]);
-    }
     if (myData.schedulerChecked) {
         printf("scheduler = 0x%08x, SCHED_FIFO = 0x%08X\n",
                myData.scheduler,
diff --git a/media/libaaudio/examples/write_sine/static/Android.mk b/media/libaaudio/examples/write_sine/static/Android.mk
index e4da6a8..3fee08a 100644
--- a/media/libaaudio/examples/write_sine/static/Android.mk
+++ b/media/libaaudio/examples/write_sine/static/Android.mk
@@ -4,6 +4,7 @@
 LOCAL_MODULE_TAGS := examples
 LOCAL_C_INCLUDES := \
     $(call include-path-for, audio-utils) \
+    frameworks/av/media/libaaudio/src \
     frameworks/av/media/libaaudio/include
 
 # NDK recommends using this kind of relative path instead of an absolute path.
diff --git a/media/libaaudio/include/aaudio/AAudio.h b/media/libaaudio/include/aaudio/AAudio.h
index 4c1ea55..c21caa4 100644
--- a/media/libaaudio/include/aaudio/AAudio.h
+++ b/media/libaaudio/include/aaudio/AAudio.h
@@ -125,6 +125,25 @@
 };
 typedef int32_t aaudio_sharing_mode_t;
 
+
+enum {
+    /**
+     * No particular performance needs. Default.
+     */
+            AAUDIO_PERFORMANCE_MODE_NONE = 10,
+
+    /**
+     * Extending battery life is most important.
+     */
+            AAUDIO_PERFORMANCE_MODE_POWER_SAVING,
+
+    /**
+     * Reducing latency is most important.
+     */
+            AAUDIO_PERFORMANCE_MODE_LOW_LATENCY
+};
+typedef int32_t aaudio_performance_mode_t;
+
 typedef struct AAudioStreamStruct         AAudioStream;
 typedef struct AAudioStreamBuilderStruct  AAudioStreamBuilder;
 
@@ -206,20 +225,28 @@
                                                        int32_t sampleRate);
 
 /**
- * Request a number of samples per frame.
+ * Request a number of channels for the stream.
  *
  * The stream may be opened with a different value.
  * So the application should query for the actual value after the stream is opened.
  *
  * The default, if you do not call this function, is AAUDIO_UNSPECIFIED.
  *
- * Note, this quantity is sometimes referred to as "channel count".
+ * Note, this quantity is sometimes referred to as "samples per frame".
  *
  * @param builder reference provided by AAudio_createStreamBuilder()
- * @param samplesPerFrame Number of samples in one frame, ie. numChannels.
+ * @param channelCount Number of channels desired.
  */
+AAUDIO_API void AAudioStreamBuilder_setChannelCount(AAudioStreamBuilder* builder,
+                                                   int32_t channelCount);
+
+/**
+ *
+ * @deprecated use AAudioStreamBuilder_setChannelCount()
+ */
+// TODO remove as soon as the NDK and OS are in sync, before RC1
 AAUDIO_API void AAudioStreamBuilder_setSamplesPerFrame(AAudioStreamBuilder* builder,
-                                                   int32_t samplesPerFrame);
+                                                       int32_t samplesPerFrame);
 
 /**
  * Request a sample data format, for example AAUDIO_FORMAT_PCM_I16.
@@ -271,6 +298,18 @@
  */
 AAUDIO_API void AAudioStreamBuilder_setBufferCapacityInFrames(AAudioStreamBuilder* builder,
                                                                  int32_t numFrames);
+
+/**
+ * Set the requested performance mode.
+ *
+ * The default, if you do not call this function, is AAUDIO_PERFORMANCE_MODE_NONE.
+ *
+ * @param builder reference provided by AAudio_createStreamBuilder()
+ * @param mode the desired performance mode, eg. AAUDIO_PERFORMANCE_MODE_LOW_LATENCY
+ */
+AAUDIO_API void AAudioStreamBuilder_setPerformanceMode(AAudioStreamBuilder* builder,
+                                                aaudio_performance_mode_t mode);
+
 /**
  * Return one of these values from the data callback function.
  */
@@ -468,6 +507,9 @@
  * Use AAudioStream_Start() to resume playback after a pause.
  * After this call the state will be in AAUDIO_STREAM_STATE_PAUSING or AAUDIO_STREAM_STATE_PAUSED.
  *
+ * This will return AAUDIO_ERROR_UNIMPLEMENTED for input streams.
+ * For input streams use AAudioStream_requestStop().
+ *
  * @param stream reference provided by AAudioStreamBuilder_openStream()
  * @return AAUDIO_OK or a negative error.
  */
@@ -480,6 +522,8 @@
  * Frame counters are not reset by a flush. They may be advanced.
  * After this call the state will be in AAUDIO_STREAM_STATE_FLUSHING or AAUDIO_STREAM_STATE_FLUSHED.
  *
+ * This will return AAUDIO_ERROR_UNIMPLEMENTED for input streams.
+ *
  * @param stream reference provided by AAudioStreamBuilder_openStream()
  * @return AAUDIO_OK or a negative error.
  */
@@ -665,6 +709,9 @@
  *
  * An underrun or overrun can cause an audible "pop" or "glitch".
  *
+ * Note that some INPUT devices may not support this function.
+ * In that case a 0 will always be returned.
+ *
  * @param stream reference provided by AAudioStreamBuilder_openStream()
  * @return the underrun or overrun count
  */
@@ -677,8 +724,18 @@
 AAUDIO_API int32_t AAudioStream_getSampleRate(AAudioStream* stream);
 
 /**
+ * A stream has one or more channels of data.
+ * A frame will contain one sample for each channel.
+ *
+ * @param stream reference provided by AAudioStreamBuilder_openStream()
+ * @return actual number of channels
+ */
+AAUDIO_API int32_t AAudioStream_getChannelCount(AAudioStream* stream);
+
+/**
  * The samplesPerFrame is also known as channelCount.
  *
+ * @deprecated use AAudioStream_getChannelCount()
  * @param stream reference provided by AAudioStreamBuilder_openStream()
  * @return actual samples per frame
  */
@@ -704,6 +761,13 @@
 AAUDIO_API aaudio_sharing_mode_t AAudioStream_getSharingMode(AAudioStream* stream);
 
 /**
+ * Get the performance mode used by the stream.
+ *
+ * @param stream reference provided by AAudioStreamBuilder_openStream()
+ */
+AAUDIO_API aaudio_performance_mode_t AAudioStream_getPerformanceMode(AAudioStream* stream);
+
+/**
  * @param stream reference provided by AAudioStreamBuilder_openStream()
  * @return direction
  */
diff --git a/media/libaaudio/libaaudio.map.txt b/media/libaaudio/libaaudio.map.txt
index 1024e1f..8f74800 100644
--- a/media/libaaudio/libaaudio.map.txt
+++ b/media/libaaudio/libaaudio.map.txt
@@ -3,12 +3,14 @@
     AAudio_convertResultToText;
     AAudio_convertStreamStateToText;
     AAudio_createStreamBuilder;
+    AAudioStreamBuilder_setPerformanceMode;
     AAudioStreamBuilder_setDeviceId;
     AAudioStreamBuilder_setDataCallback;
     AAudioStreamBuilder_setErrorCallback;
     AAudioStreamBuilder_setFramesPerDataCallback;
     AAudioStreamBuilder_setSampleRate;
     AAudioStreamBuilder_setSamplesPerFrame;
+    AAudioStreamBuilder_setChannelCount;
     AAudioStreamBuilder_setFormat;
     AAudioStreamBuilder_setSharingMode;
     AAudioStreamBuilder_setDirection;
@@ -32,6 +34,8 @@
     AAudioStream_getXRunCount;
     AAudioStream_getSampleRate;
     AAudioStream_getSamplesPerFrame;
+    AAudioStream_getChannelCount;
+    AAudioStream_getPerformanceMode;
     AAudioStream_getDeviceId;
     AAudioStream_getFormat;
     AAudioStream_getSharingMode;
diff --git a/media/libaaudio/src/Android.mk b/media/libaaudio/src/Android.mk
index b5bb75f..f43c0ad 100644
--- a/media/libaaudio/src/Android.mk
+++ b/media/libaaudio/src/Android.mk
@@ -39,6 +39,7 @@
     utility/FixedBlockAdapter.cpp \
     utility/FixedBlockReader.cpp \
     utility/FixedBlockWriter.cpp \
+    utility/LinearRamp.cpp \
     fifo/FifoBuffer.cpp \
     fifo/FifoControllerBase.cpp \
     client/AudioEndpoint.cpp \
@@ -93,6 +94,7 @@
     utility/FixedBlockAdapter.cpp \
     utility/FixedBlockReader.cpp \
     utility/FixedBlockWriter.cpp \
+    utility/LinearRamp.cpp \
     fifo/FifoBuffer.cpp \
     fifo/FifoControllerBase.cpp \
     client/AudioEndpoint.cpp \
diff --git a/media/libaaudio/src/binding/AAudioBinderClient.cpp b/media/libaaudio/src/binding/AAudioBinderClient.cpp
index 8315c40..3f1bba3 100644
--- a/media/libaaudio/src/binding/AAudioBinderClient.cpp
+++ b/media/libaaudio/src/binding/AAudioBinderClient.cpp
@@ -75,6 +75,10 @@
     return gAAudioService;
 }
 
+static void dropAAudioService() {
+    Mutex::Autolock _l(gServiceLock);
+    gAAudioService.clear(); // force a reconnect
+}
 
 AAudioBinderClient::AAudioBinderClient()
         : AAudioServiceInterface() {}
@@ -88,14 +92,26 @@
 */
 aaudio_handle_t AAudioBinderClient::openStream(const AAudioStreamRequest &request,
                                                AAudioStreamConfiguration &configurationOutput) {
+    aaudio_handle_t stream;
+    for (int i = 0; i < 2; i++) {
+        const sp<IAAudioService> &service = getAAudioService();
+        if (service == 0) {
+            return AAUDIO_ERROR_NO_SERVICE;
+        }
 
-    const sp<IAAudioService> &service = getAAudioService();
-    if (service == 0) return AAUDIO_ERROR_NO_SERVICE;
-    return service->openStream(request, configurationOutput);
+        stream = service->openStream(request, configurationOutput);
+
+        if (stream == AAUDIO_ERROR_NO_SERVICE) {
+            ALOGE("AAudioBinderClient: lost connection to AAudioService.");
+            dropAAudioService(); // force a reconnect
+        } else {
+            break;
+        }
+    }
+    return stream;
 }
 
 aaudio_result_t AAudioBinderClient::closeStream(aaudio_handle_t streamHandle) {
-
     const sp<IAAudioService> &service = getAAudioService();
     if (service == 0) return AAUDIO_ERROR_NO_SERVICE;
     return service->closeStream(streamHandle);
@@ -106,37 +122,33 @@
 */
 aaudio_result_t AAudioBinderClient::getStreamDescription(aaudio_handle_t streamHandle,
                                                          AudioEndpointParcelable &parcelable) {
-
     const sp<IAAudioService> &service = getAAudioService();
     if (service == 0) return AAUDIO_ERROR_NO_SERVICE;
     return service->getStreamDescription(streamHandle, parcelable);
 }
 
-/**
-* Start the flow of data.
-*/
 aaudio_result_t AAudioBinderClient::startStream(aaudio_handle_t streamHandle) {
     const sp<IAAudioService> &service = getAAudioService();
     if (service == 0) return AAUDIO_ERROR_NO_SERVICE;
     return service->startStream(streamHandle);
 }
 
-/**
-* Stop the flow of data such that start() can resume without loss of data.
-*/
 aaudio_result_t AAudioBinderClient::pauseStream(aaudio_handle_t streamHandle) {
     const sp<IAAudioService> &service = getAAudioService();
     if (service == 0) return AAUDIO_ERROR_NO_SERVICE;
-    return service->startStream(streamHandle);
+    return service->pauseStream(streamHandle);
 }
 
-/**
-*  Discard any data held by the underlying HAL or Service.
-*/
+aaudio_result_t AAudioBinderClient::stopStream(aaudio_handle_t streamHandle) {
+    const sp<IAAudioService> &service = getAAudioService();
+    if (service == 0) return AAUDIO_ERROR_NO_SERVICE;
+    return service->stopStream(streamHandle);
+}
+
 aaudio_result_t AAudioBinderClient::flushStream(aaudio_handle_t streamHandle) {
     const sp<IAAudioService> &service = getAAudioService();
     if (service == 0) return AAUDIO_ERROR_NO_SERVICE;
-    return service->startStream(streamHandle);
+    return service->flushStream(streamHandle);
 }
 
 /**
@@ -163,5 +175,3 @@
                                           clientProcessId,
                                           clientThreadId);
 }
-
-
diff --git a/media/libaaudio/src/binding/AAudioBinderClient.h b/media/libaaudio/src/binding/AAudioBinderClient.h
index 1497177..ca2da29 100644
--- a/media/libaaudio/src/binding/AAudioBinderClient.h
+++ b/media/libaaudio/src/binding/AAudioBinderClient.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef AAUDIO_AAUDIO_BINDER_CLIENT_H
-#define AAUDIO_AAUDIO_BINDER_CLIENT_H
+#ifndef ANDROID_AAUDIO_AAUDIO_BINDER_CLIENT_H
+#define ANDROID_AAUDIO_AAUDIO_BINDER_CLIENT_H
 
 #include <aaudio/AAudio.h>
 #include "AAudioServiceDefinitions.h"
@@ -66,6 +66,8 @@
      */
     aaudio_result_t pauseStream(aaudio_handle_t streamHandle) override;
 
+    aaudio_result_t stopStream(aaudio_handle_t streamHandle) override;
+
     /**
      *  Discard any data held by the underlying HAL or Service.
      * This is asynchronous. When complete, the service will send a FLUSHED event.
@@ -89,4 +91,4 @@
 
 } /* namespace aaudio */
 
-#endif //AAUDIO_AAUDIO_BINDER_CLIENT_H
+#endif //ANDROID_AAUDIO_AAUDIO_BINDER_CLIENT_H
diff --git a/media/libaaudio/src/binding/AAudioServiceDefinitions.h b/media/libaaudio/src/binding/AAudioServiceDefinitions.h
index 0d5bae5..2de560b 100644
--- a/media/libaaudio/src/binding/AAudioServiceDefinitions.h
+++ b/media/libaaudio/src/binding/AAudioServiceDefinitions.h
@@ -35,6 +35,7 @@
     GET_STREAM_DESCRIPTION,
     START_STREAM,
     PAUSE_STREAM,
+    STOP_STREAM,
     FLUSH_STREAM,
     REGISTER_AUDIO_THREAD,
     UNREGISTER_AUDIO_THREAD
diff --git a/media/libaaudio/src/binding/AAudioServiceInterface.h b/media/libaaudio/src/binding/AAudioServiceInterface.h
index 62fd894..824e5bc 100644
--- a/media/libaaudio/src/binding/AAudioServiceInterface.h
+++ b/media/libaaudio/src/binding/AAudioServiceInterface.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef AAUDIO_BINDING_AAUDIO_SERVICE_INTERFACE_H
-#define AAUDIO_BINDING_AAUDIO_SERVICE_INTERFACE_H
+#ifndef ANDROID_AAUDIO_BINDING_AAUDIO_SERVICE_INTERFACE_H
+#define ANDROID_AAUDIO_BINDING_AAUDIO_SERVICE_INTERFACE_H
 
 #include "binding/AAudioServiceDefinitions.h"
 #include "binding/AAudioStreamRequest.h"
@@ -63,6 +63,11 @@
     virtual aaudio_result_t pauseStream(aaudio_handle_t streamHandle) = 0;
 
     /**
+     * Stop the flow of data after data currently inthe buffer has played.
+     */
+    virtual aaudio_result_t stopStream(aaudio_handle_t streamHandle) = 0;
+
+    /**
      *  Discard any data held by the underlying HAL or Service.
      */
     virtual aaudio_result_t flushStream(aaudio_handle_t streamHandle) = 0;
@@ -82,4 +87,4 @@
 
 } /* namespace aaudio */
 
-#endif //AAUDIO_BINDING_AAUDIO_SERVICE_INTERFACE_H
+#endif //ANDROID_AAUDIO_BINDING_AAUDIO_SERVICE_INTERFACE_H
diff --git a/media/libaaudio/src/binding/AAudioServiceMessage.h b/media/libaaudio/src/binding/AAudioServiceMessage.h
index 19d6d52..b4377fb 100644
--- a/media/libaaudio/src/binding/AAudioServiceMessage.h
+++ b/media/libaaudio/src/binding/AAudioServiceMessage.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef AAUDIO_AAUDIO_SERVICE_MESSAGE_H
-#define AAUDIO_AAUDIO_SERVICE_MESSAGE_H
+#ifndef ANDROID_AAUDIO_AAUDIO_SERVICE_MESSAGE_H
+#define ANDROID_AAUDIO_AAUDIO_SERVICE_MESSAGE_H
 
 #include <stdint.h>
 
@@ -35,6 +35,7 @@
 typedef enum aaudio_service_event_e : uint32_t {
     AAUDIO_SERVICE_EVENT_STARTED,
     AAUDIO_SERVICE_EVENT_PAUSED,
+    AAUDIO_SERVICE_EVENT_STOPPED,
     AAUDIO_SERVICE_EVENT_FLUSHED,
     AAUDIO_SERVICE_EVENT_CLOSED,
     AAUDIO_SERVICE_EVENT_DISCONNECTED,
@@ -63,4 +64,4 @@
 
 } /* namespace aaudio */
 
-#endif //AAUDIO_AAUDIO_SERVICE_MESSAGE_H
+#endif //ANDROID_AAUDIO_AAUDIO_SERVICE_MESSAGE_H
diff --git a/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp b/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp
index c2bcd05..09eaa42 100644
--- a/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp
+++ b/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp
@@ -43,7 +43,6 @@
     status = parcel->writeInt32(mSamplesPerFrame);
     if (status != NO_ERROR) goto error;
     status = parcel->writeInt32((int32_t) mSharingMode);
-    ALOGD("AAudioStreamConfiguration.writeToParcel(): mSharingMode = %d", mSharingMode);
     if (status != NO_ERROR) goto error;
     status = parcel->writeInt32((int32_t) mAudioFormat);
     if (status != NO_ERROR) goto error;
@@ -66,7 +65,6 @@
     status = parcel->readInt32(&temp);
     if (status != NO_ERROR) goto error;
     mSharingMode = (aaudio_sharing_mode_t) temp;
-    ALOGD("AAudioStreamConfiguration.readFromParcel(): mSharingMode = %d", mSharingMode);
     status = parcel->readInt32(&temp);
     if (status != NO_ERROR) goto error;
     mAudioFormat = (aaudio_audio_format_t) temp;
diff --git a/media/libaaudio/src/binding/AAudioStreamConfiguration.h b/media/libaaudio/src/binding/AAudioStreamConfiguration.h
index 78c4983..b1e4a7d 100644
--- a/media/libaaudio/src/binding/AAudioStreamConfiguration.h
+++ b/media/libaaudio/src/binding/AAudioStreamConfiguration.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef BINDING_AAUDIO_STREAM_CONFIGURATION_H
-#define BINDING_AAUDIO_STREAM_CONFIGURATION_H
+#ifndef ANDROID_BINDING_AAUDIO_STREAM_CONFIGURATION_H
+#define ANDROID_BINDING_AAUDIO_STREAM_CONFIGURATION_H
 
 #include <stdint.h>
 
@@ -101,4 +101,4 @@
 
 } /* namespace aaudio */
 
-#endif //BINDING_AAUDIO_STREAM_CONFIGURATION_H
+#endif //ANDROID_BINDING_AAUDIO_STREAM_CONFIGURATION_H
diff --git a/media/libaaudio/src/binding/AAudioStreamRequest.cpp b/media/libaaudio/src/binding/AAudioStreamRequest.cpp
index ec21f8a..a5c27b9 100644
--- a/media/libaaudio/src/binding/AAudioStreamRequest.cpp
+++ b/media/libaaudio/src/binding/AAudioStreamRequest.cpp
@@ -49,6 +49,10 @@
     if (status != NO_ERROR) goto error;
     status = parcel->writeInt32((int32_t) mDirection);
     if (status != NO_ERROR) goto error;
+
+    status = parcel->writeBool(mSharingModeMatchRequired);
+    if (status != NO_ERROR) goto error;
+
     status = mConfiguration.writeToParcel(parcel);
     if (status != NO_ERROR) goto error;
     return NO_ERROR;
@@ -63,12 +67,18 @@
     status_t status = parcel->readInt32(&temp);
     if (status != NO_ERROR) goto error;
     mUserId = (uid_t) temp;
+
     status = parcel->readInt32(&temp);
     if (status != NO_ERROR) goto error;
     mProcessId = (pid_t) temp;
+
     status = parcel->readInt32(&temp);
     if (status != NO_ERROR) goto error;
     mDirection = (aaudio_direction_t) temp;
+
+    status = parcel->readBool(&mSharingModeMatchRequired);
+    if (status != NO_ERROR) goto error;
+
     status = mConfiguration.readFromParcel(parcel);
     if (status != NO_ERROR) goto error;
     return NO_ERROR;
diff --git a/media/libaaudio/src/binding/AAudioStreamRequest.h b/media/libaaudio/src/binding/AAudioStreamRequest.h
index 992e978..77138da 100644
--- a/media/libaaudio/src/binding/AAudioStreamRequest.h
+++ b/media/libaaudio/src/binding/AAudioStreamRequest.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef BINDING_AAUDIO_STREAM_REQUEST_H
-#define BINDING_AAUDIO_STREAM_REQUEST_H
+#ifndef ANDROID_BINDING_AAUDIO_STREAM_REQUEST_H
+#define ANDROID_BINDING_AAUDIO_STREAM_REQUEST_H
 
 #include <stdint.h>
 
@@ -60,6 +60,15 @@
         mDirection = direction;
     }
 
+    bool isSharingModeMatchRequired() const {
+        return mSharingModeMatchRequired;
+    }
+
+    void setSharingModeMatchRequired(bool required) {
+        mSharingModeMatchRequired = required;
+    }
+
+
     const AAudioStreamConfiguration &getConstantConfiguration() const {
         return mConfiguration;
     }
@@ -81,8 +90,9 @@
     uid_t                      mUserId;
     pid_t                      mProcessId;
     aaudio_direction_t         mDirection;
+    bool                       mSharingModeMatchRequired = false;
 };
 
 } /* namespace aaudio */
 
-#endif //BINDING_AAUDIO_STREAM_REQUEST_H
+#endif //ANDROID_BINDING_AAUDIO_STREAM_REQUEST_H
diff --git a/media/libaaudio/src/binding/AudioEndpointParcelable.h b/media/libaaudio/src/binding/AudioEndpointParcelable.h
index 4a1cb72..993075c 100644
--- a/media/libaaudio/src/binding/AudioEndpointParcelable.h
+++ b/media/libaaudio/src/binding/AudioEndpointParcelable.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef BINDING_AUDIOENDPOINTPARCELABLE_H
-#define BINDING_AUDIOENDPOINTPARCELABLE_H
+#ifndef ANDROID_BINDING_AUDIO_ENDPOINT_PARCELABLE_H
+#define ANDROID_BINDING_AUDIO_ENDPOINT_PARCELABLE_H
 
 #include <stdint.h>
 
@@ -75,4 +75,4 @@
 
 } /* namespace aaudio */
 
-#endif //BINDING_AUDIOENDPOINTPARCELABLE_H
+#endif //ANDROID_BINDING_AUDIO_ENDPOINT_PARCELABLE_H
diff --git a/media/libaaudio/src/binding/IAAudioService.cpp b/media/libaaudio/src/binding/IAAudioService.cpp
index 03fc088..b8ef611 100644
--- a/media/libaaudio/src/binding/IAAudioService.cpp
+++ b/media/libaaudio/src/binding/IAAudioService.cpp
@@ -45,16 +45,25 @@
         Parcel data, reply;
         // send command
         data.writeInterfaceToken(IAAudioService::getInterfaceDescriptor());
-        ALOGE("BpAAudioService::client openStream request dump --------------------");
-        request.dump();
+        ALOGV("BpAAudioService::client openStream --------------------");
+        // request.dump();
         request.writeToParcel(&data);
         status_t err = remote()->transact(OPEN_STREAM, data, &reply);
+        ALOGV("BpAAudioService::client openStream returned %d", err);
         if (err != NO_ERROR) {
+            ALOGE("BpAAudioService::client openStream transact failed %d", err);
             return AAudioConvert_androidToAAudioResult(err);
         }
         // parse reply
         aaudio_handle_t stream;
-        reply.readInt32(&stream);
+        err = reply.readInt32(&stream);
+        if (err != NO_ERROR) {
+            ALOGE("BpAAudioService::client transact(OPEN_STREAM) readInt %d", err);
+            return AAudioConvert_androidToAAudioResult(err);
+        } else if (stream < 0) {
+            ALOGE("BpAAudioService::client OPEN_STREAM passed stream %d", stream);
+            return stream;
+        }
         err = configurationOutput.readFromParcel(&reply);
         if (err != NO_ERROR) {
             ALOGE("BpAAudioService::client openStream readFromParcel failed %d", err);
@@ -71,6 +80,7 @@
         data.writeInt32(streamHandle);
         status_t err = remote()->transact(CLOSE_STREAM, data, &reply);
         if (err != NO_ERROR) {
+            ALOGE("BpAAudioService::client closeStream transact failed %d", err);
             return AAudioConvert_androidToAAudioResult(err);
         }
         // parse reply
@@ -145,6 +155,21 @@
         return res;
     }
 
+    virtual aaudio_result_t stopStream(aaudio_handle_t streamHandle) override {
+        Parcel data, reply;
+        // send command
+        data.writeInterfaceToken(IAAudioService::getInterfaceDescriptor());
+        data.writeInt32(streamHandle);
+        status_t err = remote()->transact(STOP_STREAM, data, &reply);
+        if (err != NO_ERROR) {
+            return AAudioConvert_androidToAAudioResult(err);
+        }
+        // parse reply
+        aaudio_result_t res;
+        reply.readInt32(&res);
+        return res;
+    }
+
     virtual aaudio_result_t flushStream(aaudio_handle_t streamHandle) override {
         Parcel data, reply;
         // send command
@@ -226,11 +251,11 @@
         case OPEN_STREAM: {
             request.readFromParcel(&data);
 
-            ALOGD("BnAAudioService::client openStream request dump --------------------");
-            request.dump();
+            //ALOGD("BnAAudioService::client openStream request dump --------------------");
+            //request.dump();
 
             stream = openStream(request, configuration);
-            ALOGV("BnAAudioService::onTransact OPEN_STREAM server handle = 0x%08X", stream);
+            //ALOGD("BnAAudioService::onTransact OPEN_STREAM server handle = 0x%08X", stream);
             reply->writeInt32(stream);
             configuration.writeToParcel(reply);
             return NO_ERROR;
@@ -238,18 +263,17 @@
 
         case CLOSE_STREAM: {
             data.readInt32(&stream);
-            ALOGV("BnAAudioService::onTransact CLOSE_STREAM 0x%08X", stream);
             result = closeStream(stream);
+            //ALOGD("BnAAudioService::onTransact CLOSE_STREAM 0x%08X, result = %d",
+            //      stream, result);
             reply->writeInt32(result);
             return NO_ERROR;
         } break;
 
         case GET_STREAM_DESCRIPTION: {
             data.readInt32(&stream);
-            ALOGI("BnAAudioService::onTransact GET_STREAM_DESCRIPTION 0x%08X", stream);
             aaudio::AudioEndpointParcelable parcelable;
             result = getStreamDescription(stream, parcelable);
-            ALOGI("BnAAudioService::onTransact getStreamDescription() returns %d", result);
             if (result != AAUDIO_OK) {
                 return AAudioConvert_aaudioToAndroidStatus(result);
             }
@@ -277,7 +301,16 @@
             data.readInt32(&stream);
             result = pauseStream(stream);
             ALOGV("BnAAudioService::onTransact PAUSE_STREAM 0x%08X, result = %d",
-                    stream, result);
+                  stream, result);
+            reply->writeInt32(result);
+            return NO_ERROR;
+        } break;
+
+        case STOP_STREAM: {
+            data.readInt32(&stream);
+            result = stopStream(stream);
+            ALOGV("BnAAudioService::onTransact STOP_STREAM 0x%08X, result = %d",
+                  stream, result);
             reply->writeInt32(result);
             return NO_ERROR;
         } break;
diff --git a/media/libaaudio/src/binding/IAAudioService.h b/media/libaaudio/src/binding/IAAudioService.h
index ab7fd1b..44a5e12 100644
--- a/media/libaaudio/src/binding/IAAudioService.h
+++ b/media/libaaudio/src/binding/IAAudioService.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef BINDING_IAAUDIOSERVICE_H
-#define BINDING_IAAUDIOSERVICE_H
+#ifndef ANDROID_AAUDIO_IAAUDIO_SERVICE_H
+#define ANDROID_AAUDIO_IAAUDIO_SERVICE_H
 
 #include <stdint.h>
 #include <utils/RefBase.h>
@@ -69,6 +69,12 @@
     virtual aaudio_result_t pauseStream(aaudio::aaudio_handle_t streamHandle) = 0;
 
     /**
+     * Stop the flow of data such that the data currently in the buffer is played.
+     * This is asynchronous. When complete, the service will send a STOPPED event.
+     */
+    virtual aaudio_result_t stopStream(aaudio::aaudio_handle_t streamHandle) = 0;
+
+    /**
      *  Discard any data held by the underlying HAL or Service.
      * This is asynchronous. When complete, the service will send a FLUSHED event.
      */
@@ -97,4 +103,4 @@
 
 } /* namespace android */
 
-#endif //BINDING_IAAUDIOSERVICE_H
+#endif //ANDROID_AAUDIO_IAAUDIO_SERVICE_H
diff --git a/media/libaaudio/src/binding/RingBufferParcelable.cpp b/media/libaaudio/src/binding/RingBufferParcelable.cpp
index 05451f9..6b74b21 100644
--- a/media/libaaudio/src/binding/RingBufferParcelable.cpp
+++ b/media/libaaudio/src/binding/RingBufferParcelable.cpp
@@ -79,29 +79,49 @@
  * The read and write must be symmetric.
  */
 status_t RingBufferParcelable::writeToParcel(Parcel* parcel) const {
-    parcel->writeInt32(mCapacityInFrames);
+    status_t status = parcel->writeInt32(mCapacityInFrames);
+    if (status != NO_ERROR) goto error;
     if (mCapacityInFrames > 0) {
-        parcel->writeInt32(mBytesPerFrame);
-        parcel->writeInt32(mFramesPerBurst);
-        parcel->writeInt32(mFlags);
-        mReadCounterParcelable.writeToParcel(parcel);
-        mWriteCounterParcelable.writeToParcel(parcel);
-        mDataParcelable.writeToParcel(parcel);
+        status = parcel->writeInt32(mBytesPerFrame);
+        if (status != NO_ERROR) goto error;
+        status = parcel->writeInt32(mFramesPerBurst);
+        if (status != NO_ERROR) goto error;
+        status = parcel->writeInt32(mFlags);
+        if (status != NO_ERROR) goto error;
+        status = mReadCounterParcelable.writeToParcel(parcel);
+        if (status != NO_ERROR) goto error;
+        status = mWriteCounterParcelable.writeToParcel(parcel);
+        if (status != NO_ERROR) goto error;
+        status = mDataParcelable.writeToParcel(parcel);
+        if (status != NO_ERROR) goto error;
     }
-    return NO_ERROR; // TODO check for errors above
+    return NO_ERROR;
+error:
+    ALOGE("RingBufferParcelable::writeToParcel() error = %d", status);
+    return status;
 }
 
 status_t RingBufferParcelable::readFromParcel(const Parcel* parcel) {
-    parcel->readInt32(&mCapacityInFrames);
+    status_t status = parcel->readInt32(&mCapacityInFrames);
+    if (status != NO_ERROR) goto error;
     if (mCapacityInFrames > 0) {
-        parcel->readInt32(&mBytesPerFrame);
-        parcel->readInt32(&mFramesPerBurst);
-        parcel->readInt32((int32_t *)&mFlags);
-        mReadCounterParcelable.readFromParcel(parcel);
-        mWriteCounterParcelable.readFromParcel(parcel);
-        mDataParcelable.readFromParcel(parcel);
+        status = parcel->readInt32(&mBytesPerFrame);
+        if (status != NO_ERROR) goto error;
+        status = parcel->readInt32(&mFramesPerBurst);
+        if (status != NO_ERROR) goto error;
+        status = parcel->readInt32((int32_t *)&mFlags);
+        if (status != NO_ERROR) goto error;
+        status = mReadCounterParcelable.readFromParcel(parcel);
+        if (status != NO_ERROR) goto error;
+        status = mWriteCounterParcelable.readFromParcel(parcel);
+        if (status != NO_ERROR) goto error;
+        status = mDataParcelable.readFromParcel(parcel);
+        if (status != NO_ERROR) goto error;
     }
-    return NO_ERROR; // TODO check for errors above
+    return NO_ERROR;
+error:
+    ALOGE("RingBufferParcelable::readFromParcel() error = %d", status);
+    return status;
 }
 
 aaudio_result_t RingBufferParcelable::resolve(SharedMemoryParcelable *memoryParcels, RingBufferDescriptor *descriptor) {
@@ -141,7 +161,7 @@
         ALOGE("RingBufferParcelable invalid mBytesPerFrame = %d", mBytesPerFrame);
         return AAUDIO_ERROR_INTERNAL;
     }
-    if (mFramesPerBurst < 0 || mFramesPerBurst >= 1024) {
+    if (mFramesPerBurst < 0 || mFramesPerBurst >= 16 * 1024) {
         ALOGE("RingBufferParcelable invalid mFramesPerBurst = %d", mFramesPerBurst);
         return AAUDIO_ERROR_INTERNAL;
     }
diff --git a/media/libaaudio/src/binding/RingBufferParcelable.h b/media/libaaudio/src/binding/RingBufferParcelable.h
index 5fc5d00..bd562f2 100644
--- a/media/libaaudio/src/binding/RingBufferParcelable.h
+++ b/media/libaaudio/src/binding/RingBufferParcelable.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef BINDING_RINGBUFFER_PARCELABLE_H
-#define BINDING_RINGBUFFER_PARCELABLE_H
+#ifndef ANDROID_AAUDIO_RINGBUFFER_PARCELABLE_H
+#define ANDROID_AAUDIO_RINGBUFFER_PARCELABLE_H
 
 #include <stdint.h>
 
@@ -82,4 +82,4 @@
 
 } /* namespace aaudio */
 
-#endif //BINDING_RINGBUFFER_PARCELABLE_H
+#endif //ANDROID_AAUDIO_RINGBUFFER_PARCELABLE_H
diff --git a/media/libaaudio/src/binding/SharedMemoryParcelable.cpp b/media/libaaudio/src/binding/SharedMemoryParcelable.cpp
index 649c884..1094d9e 100644
--- a/media/libaaudio/src/binding/SharedMemoryParcelable.cpp
+++ b/media/libaaudio/src/binding/SharedMemoryParcelable.cpp
@@ -50,7 +50,8 @@
     if (status != NO_ERROR) return status;
     if (mSizeInBytes > 0) {
         status = parcel->writeDupFileDescriptor(mFd);
-        ALOGE_IF(status != NO_ERROR, "SharedMemoryParcelable writeDupFileDescriptor failed : %d", status);
+        ALOGE_IF(status != NO_ERROR, "SharedMemoryParcelable writeDupFileDescriptor failed : %d",
+                 status);
     }
     return status;
 }
@@ -61,30 +62,37 @@
         return status;
     }
     if (mSizeInBytes > 0) {
-// FIXME        mFd = dup(parcel->readFileDescriptor());
-        // Why is the ALSA resource not getting freed?!
-        mFd = fcntl(parcel->readFileDescriptor(), F_DUPFD_CLOEXEC, 0);
+        mOriginalFd = parcel->readFileDescriptor();
+        ALOGV("SharedMemoryParcelable::readFromParcel() LEAK? mOriginalFd = %d\n", mOriginalFd);
+        mFd = fcntl(mOriginalFd, F_DUPFD_CLOEXEC, 0);
+        ALOGV("SharedMemoryParcelable::readFromParcel() LEAK? mFd = %d\n", mFd);
         if (mFd == -1) {
             status = -errno;
-            ALOGE("SharedMemoryParcelable readFileDescriptor fcntl() failed : %d", status);
+            ALOGE("SharedMemoryParcelable readFromParcel fcntl() failed : %d", status);
         }
     }
     return status;
 }
 
 aaudio_result_t SharedMemoryParcelable::close() {
-    if (mResolvedAddress != nullptr) {
+    if (mResolvedAddress != MMAP_UNRESOLVED_ADDRESS) {
         int err = munmap(mResolvedAddress, mSizeInBytes);
         if (err < 0) {
             ALOGE("SharedMemoryParcelable::close() munmap() failed %d", err);
             return AAudioConvert_androidToAAudioResult(err);
         }
-        mResolvedAddress = nullptr;
+        mResolvedAddress = MMAP_UNRESOLVED_ADDRESS;
     }
     if (mFd != -1) {
+        ALOGV("SharedMemoryParcelable::close() LEAK? mFd = %d\n", mFd);
         ::close(mFd);
         mFd = -1;
     }
+    if (mOriginalFd != -1) {
+        ALOGV("SharedMemoryParcelable::close() LEAK? mOriginalFd = %d\n", mOriginalFd);
+        ::close(mOriginalFd);
+        mOriginalFd = -1;
+    }
     return AAUDIO_OK;
 }
 
@@ -100,16 +108,12 @@
               offsetInBytes, sizeInBytes, mSizeInBytes);
         return AAUDIO_ERROR_OUT_OF_RANGE;
     }
-    if (mResolvedAddress == nullptr) {
-        /* TODO remove
-        int fd = fcntl(mFd, F_DUPFD_CLOEXEC, 0);
-        ALOGE_IF(fd==-1, "cannot dup fd=%d, size=%zd, (%s)",
-                    mFd, mSizeInBytes, strerror(errno));
-        */
+    if (mResolvedAddress == MMAP_UNRESOLVED_ADDRESS) {
         mResolvedAddress = (uint8_t *) mmap(0, mSizeInBytes, PROT_READ|PROT_WRITE,
                                           MAP_SHARED, mFd, 0);
-        if (mResolvedAddress == nullptr) {
-            ALOGE("SharedMemoryParcelable mmap failed for fd = %d", mFd);
+        if (mResolvedAddress == MMAP_UNRESOLVED_ADDRESS) {
+            ALOGE("SharedMemoryParcelable mmap failed for fd = %d, errno = %s",
+                  mFd, strerror(errno));
             return AAUDIO_ERROR_INTERNAL;
         }
     }
diff --git a/media/libaaudio/src/binding/SharedMemoryParcelable.h b/media/libaaudio/src/binding/SharedMemoryParcelable.h
index 22e16f0..4b94b46 100644
--- a/media/libaaudio/src/binding/SharedMemoryParcelable.h
+++ b/media/libaaudio/src/binding/SharedMemoryParcelable.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef BINDING_SHAREDMEMORYPARCELABLE_H
-#define BINDING_SHAREDMEMORYPARCELABLE_H
+#ifndef ANDROID_AAUDIO_SHARED_MEMORY_PARCELABLE_H
+#define ANDROID_AAUDIO_SHARED_MEMORY_PARCELABLE_H
 
 #include <stdint.h>
 
@@ -64,11 +64,15 @@
     void dump();
 
 protected:
-    int mFd = -1;
-    int32_t mSizeInBytes = 0;
-    uint8_t *mResolvedAddress = nullptr;
+
+#define MMAP_UNRESOLVED_ADDRESS    reinterpret_cast<uint8_t*>(MAP_FAILED)
+
+    int      mFd = -1;
+    int      mOriginalFd = -1;
+    int32_t  mSizeInBytes = 0;
+    uint8_t *mResolvedAddress = MMAP_UNRESOLVED_ADDRESS;
 };
 
 } /* namespace aaudio */
 
-#endif //BINDING_SHAREDMEMORYPARCELABLE_H
+#endif //ANDROID_AAUDIO_SHARED_MEMORY_PARCELABLE_H
diff --git a/media/libaaudio/src/binding/SharedRegionParcelable.h b/media/libaaudio/src/binding/SharedRegionParcelable.h
index e9f5785..f6babfd 100644
--- a/media/libaaudio/src/binding/SharedRegionParcelable.h
+++ b/media/libaaudio/src/binding/SharedRegionParcelable.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef BINDING_SHAREDREGIONPARCELABLE_H
-#define BINDING_SHAREDREGIONPARCELABLE_H
+#ifndef ANDROID_AAUDIO_SHARED_REGION_PARCELABLE_H
+#define ANDROID_AAUDIO_SHARED_REGION_PARCELABLE_H
 
 #include <stdint.h>
 
@@ -59,4 +59,4 @@
 
 } /* namespace aaudio */
 
-#endif //BINDING_SHAREDREGIONPARCELABLE_H
+#endif //ANDROID_AAUDIO_SHARED_REGION_PARCELABLE_H
diff --git a/media/libaaudio/src/client/AudioEndpoint.cpp b/media/libaaudio/src/client/AudioEndpoint.cpp
index fe049b2..027d66d 100644
--- a/media/libaaudio/src/client/AudioEndpoint.cpp
+++ b/media/libaaudio/src/client/AudioEndpoint.cpp
@@ -28,6 +28,9 @@
 using namespace android;
 using namespace aaudio;
 
+#define RIDICULOUSLY_LARGE_BUFFER_CAPACITY   (256 * 1024)
+#define RIDICULOUSLY_LARGE_FRAME_SIZE        4096
+
 AudioEndpoint::AudioEndpoint()
     : mOutputFreeRunning(false)
     , mDataReadCounter(0)
@@ -45,50 +48,58 @@
         ALOGE("AudioEndpoint_validateQueueDescriptor() NULL descriptor");
         return AAUDIO_ERROR_NULL;
     }
-    if (descriptor->capacityInFrames <= 0) {
+
+    if (descriptor->capacityInFrames < 1
+        || descriptor->capacityInFrames > RIDICULOUSLY_LARGE_BUFFER_CAPACITY) {
         ALOGE("AudioEndpoint_validateQueueDescriptor() bad capacityInFrames = %d",
               descriptor->capacityInFrames);
         return AAUDIO_ERROR_OUT_OF_RANGE;
     }
-    if (descriptor->bytesPerFrame <= 1) {
+
+    // Reject extreme values to catch bugs and prevent numeric overflows.
+    if (descriptor->bytesPerFrame < 1
+        || descriptor->bytesPerFrame > RIDICULOUSLY_LARGE_FRAME_SIZE) {
         ALOGE("AudioEndpoint_validateQueueDescriptor() bad bytesPerFrame = %d",
               descriptor->bytesPerFrame);
         return AAUDIO_ERROR_OUT_OF_RANGE;
     }
+
     if (descriptor->dataAddress == nullptr) {
         ALOGE("AudioEndpoint_validateQueueDescriptor() NULL dataAddress");
         return AAUDIO_ERROR_NULL;
     }
-    ALOGD("AudioEndpoint_validateQueueDescriptor %s, dataAddress at %p ====================",
+    ALOGV("AudioEndpoint_validateQueueDescriptor %s, dataAddress at %p ====================",
           type,
           descriptor->dataAddress);
-    ALOGD("AudioEndpoint_validateQueueDescriptor  readCounter at %p, writeCounter at %p",
+    ALOGV("AudioEndpoint_validateQueueDescriptor  readCounter at %p, writeCounter at %p",
           descriptor->readCounterAddress,
           descriptor->writeCounterAddress);
 
     // Try to READ from the data area.
     // This code will crash if the mmap failed.
     uint8_t value = descriptor->dataAddress[0];
-    ALOGD("AudioEndpoint_validateQueueDescriptor() dataAddress[0] = %d, then try to write",
+    ALOGV("AudioEndpoint_validateQueueDescriptor() dataAddress[0] = %d, then try to write",
         (int) value);
     // Try to WRITE to the data area.
     descriptor->dataAddress[0] = value * 3;
-    ALOGD("AudioEndpoint_validateQueueDescriptor() wrote successfully");
+    ALOGV("AudioEndpoint_validateQueueDescriptor() wrote successfully");
 
     if (descriptor->readCounterAddress) {
         fifo_counter_t counter = *descriptor->readCounterAddress;
-        ALOGD("AudioEndpoint_validateQueueDescriptor() *readCounterAddress = %d, now write",
+        ALOGV("AudioEndpoint_validateQueueDescriptor() *readCounterAddress = %d, now write",
               (int) counter);
         *descriptor->readCounterAddress = counter;
-        ALOGD("AudioEndpoint_validateQueueDescriptor() wrote readCounterAddress successfully");
+        ALOGV("AudioEndpoint_validateQueueDescriptor() wrote readCounterAddress successfully");
     }
+
     if (descriptor->writeCounterAddress) {
         fifo_counter_t counter = *descriptor->writeCounterAddress;
-        ALOGD("AudioEndpoint_validateQueueDescriptor() *writeCounterAddress = %d, now write",
+        ALOGV("AudioEndpoint_validateQueueDescriptor() *writeCounterAddress = %d, now write",
               (int) counter);
         *descriptor->writeCounterAddress = counter;
-        ALOGD("AudioEndpoint_validateQueueDescriptor() wrote writeCounterAddress successfully");
+        ALOGV("AudioEndpoint_validateQueueDescriptor() wrote writeCounterAddress successfully");
     }
+
     return AAUDIO_OK;
 }
 
@@ -104,18 +115,26 @@
 
 aaudio_result_t AudioEndpoint::configure(const EndpointDescriptor *pEndpointDescriptor)
 {
-    // TODO maybe remove after debugging
     aaudio_result_t result = AudioEndpoint_validateDescriptor(pEndpointDescriptor);
     if (result != AAUDIO_OK) {
-        ALOGD("AudioEndpoint_validateQueueDescriptor returned %d %s",
+        ALOGE("AudioEndpoint_validateQueueDescriptor returned %d %s",
               result, AAudio_convertResultToText(result));
         return result;
     }
 
+    // ============================ up message queue =============================
     const RingBufferDescriptor *descriptor = &pEndpointDescriptor->upMessageQueueDescriptor;
-    assert(descriptor->bytesPerFrame == sizeof(AAudioServiceMessage));
-    assert(descriptor->readCounterAddress != nullptr);
-    assert(descriptor->writeCounterAddress != nullptr);
+    if(descriptor->bytesPerFrame != sizeof(AAudioServiceMessage)) {
+        ALOGE("AudioEndpoint::configure() bytesPerFrame != sizeof(AAudioServiceMessage) = %d",
+              descriptor->bytesPerFrame);
+        return AAUDIO_ERROR_INTERNAL;
+    }
+
+    if(descriptor->readCounterAddress == nullptr || descriptor->writeCounterAddress == nullptr) {
+        ALOGE("AudioEndpoint_validateQueueDescriptor() NULL counter address");
+        return AAUDIO_ERROR_NULL;
+    }
+
     mUpCommandQueue = new FifoBuffer(
             descriptor->bytesPerFrame,
             descriptor->capacityInFrames,
@@ -123,29 +142,13 @@
             descriptor->writeCounterAddress,
             descriptor->dataAddress
     );
-    /* TODO mDownCommandQueue
-    if (descriptor->capacityInFrames > 0) {
-        descriptor = &pEndpointDescriptor->downMessageQueueDescriptor;
-        mDownCommandQueue = new FifoBuffer(
-                descriptor->capacityInFrames,
-                descriptor->bytesPerFrame,
-                descriptor->readCounterAddress,
-                descriptor->writeCounterAddress,
-                descriptor->dataAddress
-        );
-    }
-     */
+
+    // ============================ down data queue =============================
     descriptor = &pEndpointDescriptor->downDataQueueDescriptor;
-    assert(descriptor->capacityInFrames > 0);
-    assert(descriptor->bytesPerFrame > 1);
-    assert(descriptor->bytesPerFrame < 4 * 16); // FIXME just for initial debugging
-    assert(descriptor->framesPerBurst > 0);
-    assert(descriptor->framesPerBurst < 8 * 1024); // FIXME just for initial debugging
-    assert(descriptor->dataAddress != nullptr);
-    ALOGD("AudioEndpoint::configure() data framesPerBurst = %d", descriptor->framesPerBurst);
-    ALOGD("AudioEndpoint::configure() data readCounterAddress = %p", descriptor->readCounterAddress);
+    ALOGV("AudioEndpoint::configure() data framesPerBurst = %d", descriptor->framesPerBurst);
+    ALOGV("AudioEndpoint::configure() data readCounterAddress = %p", descriptor->readCounterAddress);
     mOutputFreeRunning = descriptor->readCounterAddress == nullptr;
-    ALOGD("AudioEndpoint::configure() mOutputFreeRunning = %d", mOutputFreeRunning ? 1 : 0);
+    ALOGV("AudioEndpoint::configure() mOutputFreeRunning = %d", mOutputFreeRunning ? 1 : 0);
     int64_t *readCounterAddress = (descriptor->readCounterAddress == nullptr)
                                   ? &mDataReadCounter
                                   : descriptor->readCounterAddress;
@@ -172,8 +175,6 @@
 
 aaudio_result_t AudioEndpoint::writeDataNow(const void *buffer, int32_t numFrames)
 {
-    // TODO Make it easier for the AAudioStreamInternal to scale floats and write shorts
-    // TODO Similar to block adapter write through technique. Add a DataConverter.
     return mDownDataQueue->write(buffer, numFrames);
 }
 
diff --git a/media/libaaudio/src/client/AudioEndpoint.h b/media/libaaudio/src/client/AudioEndpoint.h
index a24a705..46a3fc5 100644
--- a/media/libaaudio/src/client/AudioEndpoint.h
+++ b/media/libaaudio/src/client/AudioEndpoint.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef AAUDIO_AUDIO_ENDPOINT_H
-#define AAUDIO_AUDIO_ENDPOINT_H
+#ifndef ANDROID_AAUDIO_AUDIO_ENDPOINT_H
+#define ANDROID_AAUDIO_AUDIO_ENDPOINT_H
 
 #include <aaudio/AAudio.h>
 
@@ -93,4 +93,4 @@
 
 } // namespace aaudio
 
-#endif //AAUDIO_AUDIO_ENDPOINT_H
+#endif //ANDROID_AAUDIO_AUDIO_ENDPOINT_H
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index 7304205..eee860e 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -32,17 +32,15 @@
 #include "binding/AAudioStreamConfiguration.h"
 #include "binding/IAAudioService.h"
 #include "binding/AAudioServiceMessage.h"
-#include "fifo/FifoBuffer.h"
-
 #include "core/AudioStreamBuilder.h"
+#include "fifo/FifoBuffer.h"
+#include "utility/LinearRamp.h"
+
 #include "AudioStreamInternal.h"
 
 #define LOG_TIMESTAMPS   0
 
 using android::String16;
-using android::IServiceManager;
-using android::defaultServiceManager;
-using android::interface_cast;
 using android::Mutex;
 using android::WrappingBuffer;
 
@@ -53,7 +51,10 @@
 // Wait at least this many times longer than the operation should take.
 #define MIN_TIMEOUT_OPERATIONS    4
 
-#define ALOG_CONDITION   (mInService == false)
+//static int64_t s_logCounter = 0;
+//#define MYLOG_CONDITION   (mInService == true && s_logCounter++ < 500)
+//#define MYLOG_CONDITION   (s_logCounter++ < 500000)
+#define MYLOG_CONDITION   (1)
 
 AudioStreamInternal::AudioStreamInternal(AAudioServiceInterface  &serviceInterface, bool inService)
         : AudioStream()
@@ -62,8 +63,7 @@
         , mServiceStreamHandle(AAUDIO_HANDLE_INVALID)
         , mFramesPerBurst(16)
         , mServiceInterface(serviceInterface)
-        , mInService(inService)
-{
+        , mInService(inService) {
 }
 
 AudioStreamInternal::~AudioStreamInternal() {
@@ -84,27 +84,26 @@
     if (getFormat() == AAUDIO_UNSPECIFIED) {
         setFormat(AAUDIO_FORMAT_PCM_FLOAT);
     }
+    // Request FLOAT for the shared mixer.
+    request.getConfiguration().setAudioFormat(AAUDIO_FORMAT_PCM_FLOAT);
 
     // Build the request to send to the server.
     request.setUserId(getuid());
     request.setProcessId(getpid());
     request.setDirection(getDirection());
+    request.setSharingModeMatchRequired(isSharingModeMatchRequired());
 
     request.getConfiguration().setDeviceId(getDeviceId());
     request.getConfiguration().setSampleRate(getSampleRate());
     request.getConfiguration().setSamplesPerFrame(getSamplesPerFrame());
-    request.getConfiguration().setAudioFormat(getFormat());
-    aaudio_sharing_mode_t sharingMode = getSharingMode();
-    ALOGE("AudioStreamInternal.open(): sharingMode %d", sharingMode);
-    request.getConfiguration().setSharingMode(sharingMode);
+    request.getConfiguration().setSharingMode(getSharingMode());
+
     request.getConfiguration().setBufferCapacity(builder.getBufferCapacity());
 
     mServiceStreamHandle = mServiceInterface.openStream(request, configuration);
-    ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal.open(): openStream returned mServiceStreamHandle = 0x%08X",
-         (unsigned int)mServiceStreamHandle);
     if (mServiceStreamHandle < 0) {
         result = mServiceStreamHandle;
-        ALOGE("AudioStreamInternal.open(): openStream() returned %d", result);
+        ALOGE("AudioStreamInternal.open(): %s openStream() returned %d", getLocationName(), result);
     } else {
         result = configuration.validate();
         if (result != AAUDIO_OK) {
@@ -120,10 +119,9 @@
         mDeviceFormat = configuration.getAudioFormat();
 
         result = mServiceInterface.getStreamDescription(mServiceStreamHandle, mEndPointParcelable);
-        ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal.open(): getStreamDescriptor(0x%08X) returns %d",
-              mServiceStreamHandle, result);
         if (result != AAUDIO_OK) {
-            ALOGE("AudioStreamInternal.open(): getStreamDescriptor returns %d", result);
+            ALOGE("AudioStreamInternal.open(): %s getStreamDescriptor returns %d",
+                  getLocationName(), result);
             mServiceInterface.closeStream(mServiceStreamHandle);
             return result;
         }
@@ -140,8 +138,19 @@
         mAudioEndpoint.configure(&mEndpointDescriptor);
 
         mFramesPerBurst = mEndpointDescriptor.downDataQueueDescriptor.framesPerBurst;
-        assert(mFramesPerBurst >= 16);
-        assert(mEndpointDescriptor.downDataQueueDescriptor.capacityInFrames < 10 * 1024);
+        int32_t capacity = mEndpointDescriptor.downDataQueueDescriptor.capacityInFrames;
+
+        ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal.open() %s framesPerBurst = %d, capacity = %d",
+                 getLocationName(), mFramesPerBurst, capacity);
+        // Validate result from server.
+        if (mFramesPerBurst < 16 || mFramesPerBurst > 16 * 1024) {
+            ALOGE("AudioStream::open(): framesPerBurst out of range = %d", mFramesPerBurst);
+            return AAUDIO_ERROR_OUT_OF_RANGE;
+        }
+        if (capacity < mFramesPerBurst || capacity > 32 * 1024) {
+            ALOGE("AudioStream::open(): bufferCapacity out of range = %d", capacity);
+            return AAUDIO_ERROR_OUT_OF_RANGE;
+        }
 
         mClockModel.setSampleRate(getSampleRate());
         mClockModel.setFramesPerBurst(mFramesPerBurst);
@@ -149,7 +158,8 @@
         if (getDataCallbackProc()) {
             mCallbackFrames = builder.getFramesPerDataCallback();
             if (mCallbackFrames > getBufferCapacity() / 2) {
-                ALOGE("AudioStreamInternal.open(): framesPerCallback too large");
+                ALOGE("AudioStreamInternal.open(): framesPerCallback too large = %d, capacity = %d",
+                      mCallbackFrames, getBufferCapacity());
                 mServiceInterface.closeStream(mServiceStreamHandle);
                 return AAUDIO_ERROR_OUT_OF_RANGE;
 
@@ -175,7 +185,8 @@
 }
 
 aaudio_result_t AudioStreamInternal::close() {
-    ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal.close(): mServiceStreamHandle = 0x%08X", mServiceStreamHandle);
+    ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal.close(): mServiceStreamHandle = 0x%08X",
+             mServiceStreamHandle);
     if (mServiceStreamHandle != AAUDIO_HANDLE_INVALID) {
         aaudio_handle_t serviceStreamHandle = mServiceStreamHandle;
         mServiceStreamHandle = AAUDIO_HANDLE_INVALID;
@@ -233,7 +244,7 @@
 
     ALOGD("AudioStreamInternal(): callbackLoop() exiting, result = %d, isPlaying() = %d",
           result, (int) isPlaying());
-    return NULL; // TODO review
+    return NULL;
 }
 
 static void *aaudio_callback_thread_proc(void *context)
@@ -250,7 +261,7 @@
 aaudio_result_t AudioStreamInternal::requestStart()
 {
     int64_t startTime;
-    ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal(): start()");
+    ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal(): start()");
     if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
         return AAUDIO_ERROR_INVALID_STATE;
     }
@@ -275,8 +286,10 @@
 int64_t AudioStreamInternal::calculateReasonableTimeout(int32_t framesPerOperation) {
 
     // Wait for at least a second or some number of callbacks to join the thread.
-    int64_t timeoutNanoseconds = (MIN_TIMEOUT_OPERATIONS * framesPerOperation * AAUDIO_NANOS_PER_SECOND)
-                         / getSampleRate();
+    int64_t timeoutNanoseconds = (MIN_TIMEOUT_OPERATIONS
+                                  * framesPerOperation
+                                  * AAUDIO_NANOS_PER_SECOND)
+                                  / getSampleRate();
     if (timeoutNanoseconds < MIN_TIMEOUT_NANOS) { // arbitrary number of seconds
         timeoutNanoseconds = MIN_TIMEOUT_NANOS;
     }
@@ -295,28 +308,34 @@
 
 aaudio_result_t AudioStreamInternal::requestPauseInternal()
 {
-    ALOGD("AudioStreamInternal(): pause()");
     if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
+        ALOGE("AudioStreamInternal(): requestPauseInternal() mServiceStreamHandle invalid = 0x%08X",
+              mServiceStreamHandle);
         return AAUDIO_ERROR_INVALID_STATE;
     }
 
     mClockModel.stop(AudioClock::getNanoseconds());
     setState(AAUDIO_STREAM_STATE_PAUSING);
-    return mServiceInterface.startStream(mServiceStreamHandle);
+    return mServiceInterface.pauseStream(mServiceStreamHandle);
 }
 
 aaudio_result_t AudioStreamInternal::requestPause()
 {
+    ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal(): %s requestPause()", getLocationName());
     aaudio_result_t result = stopCallback();
     if (result != AAUDIO_OK) {
         return result;
     }
-    return requestPauseInternal();
+    result = requestPauseInternal();
+    ALOGD("AudioStreamInternal(): requestPause() returns %d", result);
+    return result;
 }
 
 aaudio_result_t AudioStreamInternal::requestFlush() {
-    ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal(): flush()");
+    ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal(): requestFlush()");
     if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
+        ALOGE("AudioStreamInternal(): requestFlush() mServiceStreamHandle invalid = 0x%08X",
+              mServiceStreamHandle);
         return AAUDIO_ERROR_INVALID_STATE;
     }
 
@@ -325,35 +344,45 @@
 }
 
 void AudioStreamInternal::onFlushFromServer() {
-    ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal(): onFlushFromServer()");
+    ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal(): onFlushFromServer()");
     int64_t readCounter = mAudioEndpoint.getDownDataReadCounter();
     int64_t writeCounter = mAudioEndpoint.getDownDataWriteCounter();
+
     // Bump offset so caller does not see the retrograde motion in getFramesRead().
     int64_t framesFlushed = writeCounter - readCounter;
     mFramesOffsetFromService += framesFlushed;
+
     // Flush written frames by forcing writeCounter to readCounter.
     // This is because we cannot move the read counter in the hardware.
     mAudioEndpoint.setDownDataWriteCounter(readCounter);
 }
 
+aaudio_result_t AudioStreamInternal::requestStopInternal()
+{
+    if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
+        ALOGE("AudioStreamInternal(): requestStopInternal() mServiceStreamHandle invalid = 0x%08X",
+              mServiceStreamHandle);
+        return AAUDIO_ERROR_INVALID_STATE;
+    }
+
+    mClockModel.stop(AudioClock::getNanoseconds());
+    setState(AAUDIO_STREAM_STATE_STOPPING);
+    return mServiceInterface.stopStream(mServiceStreamHandle);
+}
+
 aaudio_result_t AudioStreamInternal::requestStop()
 {
-    // TODO better implementation of requestStop()
-    aaudio_result_t result = requestPause();
-    if (result == AAUDIO_OK) {
-        aaudio_stream_state_t state;
-        result = waitForStateChange(AAUDIO_STREAM_STATE_PAUSING,
-                                    &state,
-                                    500 * AAUDIO_NANOS_PER_MILLISECOND);// TODO temporary code
-        if (result == AAUDIO_OK) {
-            result = requestFlush();
-        }
+    ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal(): %s requestStop()", getLocationName());
+    aaudio_result_t result = stopCallback();
+    if (result != AAUDIO_OK) {
+        return result;
     }
+    result = requestStopInternal();
+    ALOGD("AudioStreamInternal(): requestStop() returns %d", result);
     return result;
 }
 
 aaudio_result_t AudioStreamInternal::registerThread() {
-    ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal(): registerThread()");
     if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
         return AAUDIO_ERROR_INVALID_STATE;
     }
@@ -364,7 +393,6 @@
 }
 
 aaudio_result_t AudioStreamInternal::unregisterThread() {
-    ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal(): unregisterThread()");
     if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
         return AAUDIO_ERROR_INVALID_STATE;
     }
@@ -374,7 +402,7 @@
 aaudio_result_t AudioStreamInternal::getTimestamp(clockid_t clockId,
                            int64_t *framePosition,
                            int64_t *timeNanoseconds) {
-    // TODO implement using real HAL
+    // TODO Generate in server and pass to client. Return latest.
     int64_t time = AudioClock::getNanoseconds();
     *framePosition = mClockModel.convertTimeToPosition(time);
     *timeNanoseconds = time + (10 * AAUDIO_NANOS_PER_MILLISECOND); // Fake hardware delay
@@ -394,16 +422,16 @@
     static int64_t oldTime = 0;
     int64_t framePosition = command.timestamp.position;
     int64_t nanoTime = command.timestamp.timestamp;
-    ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal() timestamp says framePosition = %08lld at nanoTime %llu",
+    ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal() timestamp says framePosition = %08lld at nanoTime %llu",
          (long long) framePosition,
          (long long) nanoTime);
     int64_t nanosDelta = nanoTime - oldTime;
     if (nanosDelta > 0 && oldTime > 0) {
         int64_t framesDelta = framePosition - oldPosition;
         int64_t rate = (framesDelta * AAUDIO_NANOS_PER_SECOND) / nanosDelta;
-        ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal() - framesDelta = %08lld", (long long) framesDelta);
-        ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal() - nanosDelta = %08lld", (long long) nanosDelta);
-        ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal() - measured rate = %llu", (unsigned long long) rate);
+        ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal() - framesDelta = %08lld", (long long) framesDelta);
+        ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal() - nanosDelta = %08lld", (long long) nanosDelta);
+        ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal() - measured rate = %llu", (unsigned long long) rate);
     }
     oldPosition = framePosition;
     oldTime = nanoTime;
@@ -422,23 +450,27 @@
 
 aaudio_result_t AudioStreamInternal::onEventFromServer(AAudioServiceMessage *message) {
     aaudio_result_t result = AAUDIO_OK;
-    ALOGD_IF(ALOG_CONDITION, "processCommands() got event %d", message->event.event);
+    ALOGD_IF(MYLOG_CONDITION, "processCommands() got event %d", message->event.event);
     switch (message->event.event) {
         case AAUDIO_SERVICE_EVENT_STARTED:
-            ALOGD_IF(ALOG_CONDITION, "processCommands() got AAUDIO_SERVICE_EVENT_STARTED");
+            ALOGD_IF(MYLOG_CONDITION, "processCommands() got AAUDIO_SERVICE_EVENT_STARTED");
             setState(AAUDIO_STREAM_STATE_STARTED);
             break;
         case AAUDIO_SERVICE_EVENT_PAUSED:
-            ALOGD_IF(ALOG_CONDITION, "processCommands() got AAUDIO_SERVICE_EVENT_PAUSED");
+            ALOGD_IF(MYLOG_CONDITION, "processCommands() got AAUDIO_SERVICE_EVENT_PAUSED");
             setState(AAUDIO_STREAM_STATE_PAUSED);
             break;
+        case AAUDIO_SERVICE_EVENT_STOPPED:
+            ALOGD_IF(MYLOG_CONDITION, "processCommands() got AAUDIO_SERVICE_EVENT_STOPPED");
+            setState(AAUDIO_STREAM_STATE_STOPPED);
+            break;
         case AAUDIO_SERVICE_EVENT_FLUSHED:
-            ALOGD_IF(ALOG_CONDITION, "processCommands() got AAUDIO_SERVICE_EVENT_FLUSHED");
+            ALOGD_IF(MYLOG_CONDITION, "processCommands() got AAUDIO_SERVICE_EVENT_FLUSHED");
             setState(AAUDIO_STREAM_STATE_FLUSHED);
             onFlushFromServer();
             break;
         case AAUDIO_SERVICE_EVENT_CLOSED:
-            ALOGD_IF(ALOG_CONDITION, "processCommands() got AAUDIO_SERVICE_EVENT_CLOSED");
+            ALOGD_IF(MYLOG_CONDITION, "processCommands() got AAUDIO_SERVICE_EVENT_CLOSED");
             setState(AAUDIO_STREAM_STATE_CLOSED);
             break;
         case AAUDIO_SERVICE_EVENT_DISCONNECTED:
@@ -447,8 +479,9 @@
             ALOGW("WARNING - processCommands() AAUDIO_SERVICE_EVENT_DISCONNECTED");
             break;
         case AAUDIO_SERVICE_EVENT_VOLUME:
-            mVolume = message->event.dataDouble;
-            ALOGD_IF(ALOG_CONDITION, "processCommands() AAUDIO_SERVICE_EVENT_VOLUME %f", mVolume);
+            mVolumeRamp.setTarget((float) message->event.dataDouble);
+            ALOGD_IF(MYLOG_CONDITION, "processCommands() AAUDIO_SERVICE_EVENT_VOLUME %f",
+                     message->event.dataDouble);
             break;
         default:
             ALOGW("WARNING - processCommands() Unrecognized event = %d",
@@ -463,7 +496,7 @@
     aaudio_result_t result = AAUDIO_OK;
 
     while (result == AAUDIO_OK) {
-        //ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal::processCommands() - looping, %d", result);
+        //ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal::processCommands() - looping, %d", result);
         AAudioServiceMessage message;
         if (mAudioEndpoint.readUpCommand(&message) != 1) {
             break; // no command this time, no problem
@@ -478,7 +511,7 @@
             break;
 
         default:
-            ALOGW("WARNING - AudioStreamInternal::processCommands() Unrecognized what = %d",
+            ALOGE("WARNING - AudioStreamInternal::processCommands() Unrecognized what = %d",
                  (int) message.what);
             result = AAUDIO_ERROR_UNEXPECTED_VALUE;
             break;
@@ -497,19 +530,13 @@
     int64_t currentTimeNanos = AudioClock::getNanoseconds();
     int64_t deadlineNanos = currentTimeNanos + timeoutNanoseconds;
     int32_t framesLeft = numFrames;
-    //ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal::write(%p, %d) at time %08llu , mState = %s",
-    //      buffer, numFrames, (unsigned long long) currentTimeNanos,
-    //      AAudio_convertStreamStateToText(getState()));
 
     // Write until all the data has been written or until a timeout occurs.
     while (framesLeft > 0) {
-        //ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal::write() loop: framesLeft = %d, loopCount = %d  =====",
-        //      framesLeft, loopCount++);
         // The call to writeNow() will not block. It will just write as much as it can.
         int64_t wakeTimeNanos = 0;
         aaudio_result_t framesWritten = writeNow(source, framesLeft,
                                                currentTimeNanos, &wakeTimeNanos);
-        //ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal::write() loop: framesWritten = %d", framesWritten);
         if (framesWritten < 0) {
             ALOGE("AudioStreamInternal::write() loop: writeNow returned %d", framesWritten);
             result = framesWritten;
@@ -522,7 +549,6 @@
         if (timeoutNanoseconds == 0) {
             break; // don't block
         } else if (framesLeft > 0) {
-            //ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal:: original wakeTimeNanos %lld", (long long) wakeTimeNanos);
             // clip the wake time to something reasonable
             if (wakeTimeNanos < currentTimeNanos) {
                 wakeTimeNanos = currentTimeNanos;
@@ -534,16 +560,13 @@
                 break;
             }
 
-            //ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal:: sleep until %lld, dur = %lld", (long long) wakeTimeNanos,
-            //        (long long) (wakeTimeNanos - currentTimeNanos));
-            AudioClock::sleepForNanos(wakeTimeNanos - currentTimeNanos);
+            int64_t sleepForNanos = wakeTimeNanos - currentTimeNanos;
+            AudioClock::sleepForNanos(sleepForNanos);
             currentTimeNanos = AudioClock::getNanoseconds();
         }
     }
 
     // return error or framesWritten
-    //ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal::write() result = %d, framesLeft = %d, #%d",
-    //      result, framesLeft, loopCount);
     (void) loopCount;
     return (result < 0) ? result : numFrames - framesLeft;
 }
@@ -551,18 +574,13 @@
 // Write as much data as we can without blocking.
 aaudio_result_t AudioStreamInternal::writeNow(const void *buffer, int32_t numFrames,
                                          int64_t currentNanoTime, int64_t *wakeTimePtr) {
-
-    //ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal::writeNow(%p) - enter", buffer);
-    {
-        aaudio_result_t result = processCommands();
-        //ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal::writeNow() - processCommands() returned %d", result);
-        if (result != AAUDIO_OK) {
-            return result;
-        }
+    aaudio_result_t result = processCommands();
+    if (result != AAUDIO_OK) {
+        return result;
     }
 
     if (mAudioEndpoint.isOutputFreeRunning()) {
-        ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal::writeNow() - update read counter");
+        //ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal::writeNow() - update read counter");
         // Update data queue based on the timing model.
         int64_t estimatedReadCounter = mClockModel.convertTimeToPosition(currentNanoTime);
         mAudioEndpoint.setDownDataReadCounter(estimatedReadCounter);
@@ -575,9 +593,9 @@
     }
 
     // Write some data to the buffer.
-    //ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal::writeNow() - writeNowWithConversion(%d)", numFrames);
+    //ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal::writeNow() - writeNowWithConversion(%d)", numFrames);
     int32_t framesWritten = writeNowWithConversion(buffer, numFrames);
-    //ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal::writeNow() - tried to write %d frames, wrote %d",
+    //ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal::writeNow() - tried to write %d frames, wrote %d",
     //    numFrames, framesWritten);
 
     // Calculate an ideal time to wake up.
@@ -585,7 +603,7 @@
         // By default wake up a few milliseconds from now.  // TODO review
         int64_t wakeTime = currentNanoTime + (1 * AAUDIO_NANOS_PER_MILLISECOND);
         aaudio_stream_state_t state = getState();
-        //ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal::writeNow() - wakeTime based on %s",
+        //ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal::writeNow() - wakeTime based on %s",
         //      AAudio_convertStreamStateToText(state));
         switch (state) {
             case AAUDIO_STREAM_STATE_OPEN:
@@ -612,7 +630,7 @@
         *wakeTimePtr = wakeTime;
 
     }
-//    ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal::writeNow finished: now = %llu, read# = %llu, wrote# = %llu",
+//    ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal::writeNow finished: now = %llu, read# = %llu, wrote# = %llu",
 //         (unsigned long long)currentNanoTime,
 //         (unsigned long long)mAudioEndpoint.getDownDataReadCounter(),
 //         (unsigned long long)mAudioEndpoint.getDownDataWriteCounter());
@@ -620,12 +638,11 @@
 }
 
 
-// TODO this function needs a major cleanup.
 aaudio_result_t AudioStreamInternal::writeNowWithConversion(const void *buffer,
                                        int32_t numFrames) {
-    // ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal::writeNowWithConversion(%p, %d)", buffer, numFrames);
+    // ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal::writeNowWithConversion(%p, %d)",
+    //              buffer, numFrames);
     WrappingBuffer wrappingBuffer;
-    mAudioEndpoint.getEmptyRoomAvailable(&wrappingBuffer);
     uint8_t *source = (uint8_t *) buffer;
     int32_t framesLeft = numFrames;
 
@@ -640,27 +657,72 @@
             if (framesToWrite > framesAvailable) {
                 framesToWrite = framesAvailable;
             }
-            int32_t numBytes = getBytesPerFrame();
-            // TODO handle volume scaling
-            if (getFormat() == mDeviceFormat) {
-                // Copy straight through.
-                memcpy(wrappingBuffer.data[partIndex], source, numBytes);
-            } else if (getFormat() == AAUDIO_FORMAT_PCM_FLOAT
-                    && mDeviceFormat == AAUDIO_FORMAT_PCM_I16) {
-                // Data conversion.
-                AAudioConvert_floatToPcm16(
-                        (const float *) source,
-                        framesToWrite * getSamplesPerFrame(),
-                        (int16_t *) wrappingBuffer.data[partIndex]);
-            } else {
-                // TODO handle more conversions
-                ALOGE("AudioStreamInternal::writeNowWithConversion() unsupported formats: %d, %d",
-                      getFormat(), mDeviceFormat);
-                return AAUDIO_ERROR_UNEXPECTED_VALUE;
+            int32_t numBytes = getBytesPerFrame() * framesToWrite;
+            int32_t numSamples = framesToWrite * getSamplesPerFrame();
+            // Data conversion.
+            float levelFrom;
+            float levelTo;
+            bool ramping = mVolumeRamp.nextSegment(framesToWrite * getSamplesPerFrame(),
+                                    &levelFrom, &levelTo);
+            // The formats are validated when the stream is opened so we do not have to
+            // check for illegal combinations here.
+            if (getFormat() == AAUDIO_FORMAT_PCM_FLOAT) {
+                if (mDeviceFormat == AAUDIO_FORMAT_PCM_FLOAT) {
+                    AAudio_linearRamp(
+                            (const float *) source,
+                            (float *) wrappingBuffer.data[partIndex],
+                            framesToWrite,
+                            getSamplesPerFrame(),
+                            levelFrom,
+                            levelTo);
+                } else if (mDeviceFormat == AAUDIO_FORMAT_PCM_I16) {
+                    if (ramping) {
+                        AAudioConvert_floatToPcm16(
+                                (const float *) source,
+                                (int16_t *) wrappingBuffer.data[partIndex],
+                                framesToWrite,
+                                getSamplesPerFrame(),
+                                levelFrom,
+                                levelTo);
+                    } else {
+                        AAudioConvert_floatToPcm16(
+                                (const float *) source,
+                                (int16_t *) wrappingBuffer.data[partIndex],
+                                numSamples,
+                                levelTo);
+                    }
+                }
+            } else if (getFormat() == AAUDIO_FORMAT_PCM_I16) {
+                if (mDeviceFormat == AAUDIO_FORMAT_PCM_FLOAT) {
+                    if (ramping) {
+                        AAudioConvert_pcm16ToFloat(
+                                (const int16_t *) source,
+                                (float *) wrappingBuffer.data[partIndex],
+                                framesToWrite,
+                                getSamplesPerFrame(),
+                                levelFrom,
+                                levelTo);
+                    } else {
+                        AAudioConvert_pcm16ToFloat(
+                                (const int16_t *) source,
+                                (float *) wrappingBuffer.data[partIndex],
+                                numSamples,
+                                levelTo);
+                    }
+                } else if (mDeviceFormat == AAUDIO_FORMAT_PCM_I16) {
+                    AAudio_linearRamp(
+                            (const int16_t *) source,
+                            (int16_t *) wrappingBuffer.data[partIndex],
+                            framesToWrite,
+                            getSamplesPerFrame(),
+                            levelFrom,
+                            levelTo);
+                }
             }
-
             source += numBytes;
             framesLeft -= framesToWrite;
+        } else {
+            break;
         }
         partIndex++;
     }
@@ -670,7 +732,7 @@
     if (framesWritten > 0) {
         incrementFramesWritten(framesWritten);
     }
-    // ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal::writeNowWithConversion() returns %d", framesWritten);
+    // ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal::writeNowWithConversion() returns %d", framesWritten);
     return framesWritten;
 }
 
@@ -680,7 +742,15 @@
 
 aaudio_result_t AudioStreamInternal::setBufferSize(int32_t requestedFrames) {
     int32_t actualFrames = 0;
+    // Round to the next highest burst size.
+    if (getFramesPerBurst() > 0) {
+        int32_t numBursts = (requestedFrames + getFramesPerBurst() - 1) / getFramesPerBurst();
+        requestedFrames = numBursts * getFramesPerBurst();
+    }
+
     aaudio_result_t result = mAudioEndpoint.setBufferSizeInFrames(requestedFrames, &actualFrames);
+    ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal::setBufferSize() %s req = %d => %d",
+             getLocationName(), requestedFrames, actualFrames);
     if (result < 0) {
         return result;
     } else {
@@ -714,8 +784,14 @@
     } else {
         mLastFramesRead = framesRead;
     }
-    ALOGD_IF(ALOG_CONDITION, "AudioStreamInternal::getFramesRead() returns %lld", (long long)framesRead);
+    ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal::getFramesRead() returns %lld", (long long)framesRead);
     return framesRead;
 }
 
-// TODO implement getTimestamp
+int64_t AudioStreamInternal::getFramesWritten()
+{
+    int64_t getFramesWritten = mAudioEndpoint.getDownDataWriteCounter()
+            + mFramesOffsetFromService;
+    ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal::getFramesWritten() returns %lld", (long long)getFramesWritten);
+    return getFramesWritten;
+}
diff --git a/media/libaaudio/src/client/AudioStreamInternal.h b/media/libaaudio/src/client/AudioStreamInternal.h
index 1aa3b0f..ee602c1 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.h
+++ b/media/libaaudio/src/client/AudioStreamInternal.h
@@ -14,19 +14,19 @@
  * limitations under the License.
  */
 
-#ifndef AAUDIO_AUDIOSTREAMINTERNAL_H
-#define AAUDIO_AUDIOSTREAMINTERNAL_H
+#ifndef ANDROID_AAUDIO_AUDIO_STREAM_INTERNAL_H
+#define ANDROID_AAUDIO_AUDIO_STREAM_INTERNAL_H
 
 #include <stdint.h>
 #include <aaudio/AAudio.h>
 
 #include "binding/IAAudioService.h"
 #include "binding/AudioEndpointParcelable.h"
+#include "binding/AAudioServiceInterface.h"
 #include "client/IsochronousClockModel.h"
 #include "client/AudioEndpoint.h"
 #include "core/AudioStream.h"
-
-#include "binding/AAudioServiceInterface.h"
+#include "utility/LinearRamp.h"
 
 using android::sp;
 using android::IAAudioService;
@@ -49,15 +49,11 @@
 
     aaudio_result_t requestStop() override;
 
-    // TODO use aaudio_clockid_t all the way down to AudioClock
     aaudio_result_t getTimestamp(clockid_t clockId,
                                        int64_t *framePosition,
                                        int64_t *timeNanoseconds) override;
 
-
-
     virtual aaudio_result_t updateStateWhileWaiting() override;
-
     // =========== End ABSTRACT methods ===========================
 
     aaudio_result_t open(const AudioStreamBuilder &builder) override;
@@ -77,6 +73,7 @@
     int32_t getFramesPerBurst() const override;
 
     int64_t getFramesRead() override;
+    int64_t getFramesWritten() override;
 
     int32_t getXRunCount() const override {
         return mXRunCount;
@@ -89,11 +86,17 @@
     // Called internally from 'C'
     void *callbackLoop();
 
+
+    bool isMMap() override {
+        return true;
+    }
+
 protected:
 
     aaudio_result_t processCommands();
 
     aaudio_result_t requestPauseInternal();
+    aaudio_result_t requestStopInternal();
 
     aaudio_result_t stopCallback();
 
@@ -129,6 +132,11 @@
                                      int32_t numFrames);
     void processTimestamp(uint64_t position, int64_t time);
 
+
+    const char *getLocationName() const {
+        return mInService ? "SERVICE" : "CLIENT";
+    }
+
     // Adjust timing model based on timestamp from service.
 
     IsochronousClockModel    mClockModel;      // timing model for chasing the HAL
@@ -148,7 +156,7 @@
     int64_t                  mLastFramesRead = 0; // used to prevent retrograde motion
     int32_t                  mFramesPerBurst;     // frames per HAL transfer
     int32_t                  mXRunCount = 0;      // how many underrun events?
-    float                    mVolume = 1.0;       // volume that the server told us to use
+    LinearRamp               mVolumeRamp;
 
     AAudioServiceInterface  &mServiceInterface;   // abstract interface to the service
 
@@ -158,4 +166,4 @@
 
 } /* namespace aaudio */
 
-#endif //AAUDIO_AUDIOSTREAMINTERNAL_H
+#endif //ANDROID_AAUDIO_AUDIO_STREAM_INTERNAL_H
diff --git a/media/libaaudio/src/client/IsochronousClockModel.cpp b/media/libaaudio/src/client/IsochronousClockModel.cpp
index c278c8b..21e3e70 100644
--- a/media/libaaudio/src/client/IsochronousClockModel.cpp
+++ b/media/libaaudio/src/client/IsochronousClockModel.cpp
@@ -101,13 +101,13 @@
             // or we may be drifting due to a slow HW clock.
             mMarkerFramePosition = framePosition;
             mMarkerNanoTime = nanoTime;
-            ALOGI("processTimestamp() - STATE_RUNNING - %d < %d micros - EARLY",
+            ALOGV("processTimestamp() - STATE_RUNNING - %d < %d micros - EARLY",
                  (int) (nanosDelta / 1000), (int)(expectedNanosDelta / 1000));
         } else if (nanosDelta > (expectedNanosDelta + mMaxLatenessInNanos)) {
             // Later than expected timestamp.
             mMarkerFramePosition = framePosition;
             mMarkerNanoTime = nanoTime - mMaxLatenessInNanos;
-            ALOGI("processTimestamp() - STATE_RUNNING - %d > %d + %d micros - LATE",
+            ALOGV("processTimestamp() - STATE_RUNNING - %d > %d + %d micros - LATE",
                  (int) (nanosDelta / 1000), (int)(expectedNanosDelta / 1000),
                  (int) (mMaxLatenessInNanos / 1000));
         }
diff --git a/media/libaaudio/src/client/IsochronousClockModel.h b/media/libaaudio/src/client/IsochronousClockModel.h
index 205c341..0314f55 100644
--- a/media/libaaudio/src/client/IsochronousClockModel.h
+++ b/media/libaaudio/src/client/IsochronousClockModel.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef AAUDIO_ISOCHRONOUS_CLOCK_MODEL_H
-#define AAUDIO_ISOCHRONOUS_CLOCK_MODEL_H
+#ifndef ANDROID_AAUDIO_ISOCHRONOUS_CLOCK_MODEL_H
+#define ANDROID_AAUDIO_ISOCHRONOUS_CLOCK_MODEL_H
 
 #include <stdint.h>
 
@@ -106,4 +106,4 @@
 
 } /* namespace aaudio */
 
-#endif //AAUDIO_ISOCHRONOUS_CLOCK_MODEL_H
+#endif //ANDROID_AAUDIO_ISOCHRONOUS_CLOCK_MODEL_H
diff --git a/media/libaaudio/src/core/AAudioAudio.cpp b/media/libaaudio/src/core/AAudioAudio.cpp
index 9d69423..59032d5 100644
--- a/media/libaaudio/src/core/AAudioAudio.cpp
+++ b/media/libaaudio/src/core/AAudioAudio.cpp
@@ -114,8 +114,15 @@
     return AAUDIO_OK;
 }
 
+AAUDIO_API void AAudioStreamBuilder_setPerformanceMode(AAudioStreamBuilder* builder,
+                                                       aaudio_performance_mode_t mode)
+{
+    AudioStreamBuilder *streamBuilder = convertAAudioBuilderToStreamBuilder(builder);
+    streamBuilder->setPerformanceMode(mode);
+}
+
 AAUDIO_API void AAudioStreamBuilder_setDeviceId(AAudioStreamBuilder* builder,
-                                                     int32_t deviceId)
+                                                int32_t deviceId)
 {
     AudioStreamBuilder *streamBuilder = convertAAudioBuilderToStreamBuilder(builder);
     streamBuilder->setDeviceId(deviceId);
@@ -128,8 +135,15 @@
     streamBuilder->setSampleRate(sampleRate);
 }
 
+AAUDIO_API void AAudioStreamBuilder_setChannelCount(AAudioStreamBuilder* builder,
+                                                       int32_t channelCount)
+{
+    AudioStreamBuilder *streamBuilder = convertAAudioBuilderToStreamBuilder(builder);
+    streamBuilder->setSamplesPerFrame(channelCount);
+}
+
 AAUDIO_API void AAudioStreamBuilder_setSamplesPerFrame(AAudioStreamBuilder* builder,
-                                                   int32_t samplesPerFrame)
+                                                       int32_t samplesPerFrame)
 {
     AudioStreamBuilder *streamBuilder = convertAAudioBuilderToStreamBuilder(builder);
     streamBuilder->setSamplesPerFrame(samplesPerFrame);
@@ -168,16 +182,15 @@
                                                     void *userData)
 {
     AudioStreamBuilder *streamBuilder = convertAAudioBuilderToStreamBuilder(builder);
-    ALOGD("AAudioStreamBuilder_setCallback(): userData = %p", userData);
     streamBuilder->setDataCallbackProc(callback);
     streamBuilder->setDataCallbackUserData(userData);
 }
+
 AAUDIO_API void AAudioStreamBuilder_setErrorCallback(AAudioStreamBuilder* builder,
                                                  AAudioStream_errorCallback callback,
                                                  void *userData)
 {
     AudioStreamBuilder *streamBuilder = convertAAudioBuilderToStreamBuilder(builder);
-    ALOGD("AAudioStreamBuilder_setCallback(): userData = %p", userData);
     streamBuilder->setErrorCallbackProc(callback);
     streamBuilder->setErrorCallbackUserData(userData);
 }
@@ -186,29 +199,24 @@
                                                 int32_t frames)
 {
     AudioStreamBuilder *streamBuilder = convertAAudioBuilderToStreamBuilder(builder);
-    ALOGD("%s: frames = %d", __func__, frames);
     streamBuilder->setFramesPerDataCallback(frames);
 }
 
-static aaudio_result_t  AAudioInternal_openStream(AudioStreamBuilder *streamBuilder,
-                                              AAudioStream** streamPtr)
-{
-    AudioStream *audioStream = nullptr;
-    aaudio_result_t result = streamBuilder->build(&audioStream);
-    if (result != AAUDIO_OK) {
-        return result;
-    } else {
-        *streamPtr = (AAudioStream*) audioStream;
-        return AAUDIO_OK;
-    }
-}
-
 AAUDIO_API aaudio_result_t  AAudioStreamBuilder_openStream(AAudioStreamBuilder* builder,
                                                      AAudioStream** streamPtr)
 {
-    ALOGD("AAudioStreamBuilder_openStream(): builder = %p", builder);
+    AudioStream *audioStream = nullptr;
+    ALOGD("AAudioStreamBuilder_openStream() ----------------------------------------------");
     AudioStreamBuilder *streamBuilder = COMMON_GET_FROM_BUILDER_OR_RETURN(streamPtr);
-    return AAudioInternal_openStream(streamBuilder, streamPtr);
+    aaudio_result_t result = streamBuilder->build(&audioStream);
+    ALOGD("AAudioStreamBuilder_openStream() returns %d -----------------------------------",
+          result);
+    if (result == AAUDIO_OK) {
+        *streamPtr = (AAudioStream*) audioStream;
+    } else {
+        *streamPtr = nullptr;
+    }
+    return result;
 }
 
 AAUDIO_API aaudio_result_t  AAudioStreamBuilder_delete(AAudioStreamBuilder* builder)
@@ -228,6 +236,7 @@
     if (audioStream != nullptr) {
         audioStream->close();
         delete audioStream;
+        ALOGD("AAudioStream_close() ----------------------------------------------");
         return AAUDIO_OK;
     }
     return AAUDIO_ERROR_INVALID_HANDLE;
@@ -334,6 +343,12 @@
     return audioStream->getSampleRate();
 }
 
+AAUDIO_API int32_t AAudioStream_getChannelCount(AAudioStream* stream)
+{
+    AudioStream *audioStream = convertAAudioStreamToAudioStream(stream);
+    return audioStream->getSamplesPerFrame();
+}
+
 AAUDIO_API int32_t AAudioStream_getSamplesPerFrame(AAudioStream* stream)
 {
     AudioStream *audioStream = convertAAudioStreamToAudioStream(stream);
@@ -395,6 +410,12 @@
     return audioStream->getXRunCount();
 }
 
+AAUDIO_API aaudio_performance_mode_t AAudioStream_getPerformanceMode(AAudioStream* stream)
+{
+    AudioStream *audioStream = convertAAudioStreamToAudioStream(stream);
+    return audioStream->getPerformanceMode();
+}
+
 AAUDIO_API int32_t AAudioStream_getDeviceId(AAudioStream* stream)
 {
     AudioStream *audioStream = convertAAudioStreamToAudioStream(stream);
diff --git a/media/libaaudio/src/core/AudioStream.cpp b/media/libaaudio/src/core/AudioStream.cpp
index 7c0b5ae..d1698bf 100644
--- a/media/libaaudio/src/core/AudioStream.cpp
+++ b/media/libaaudio/src/core/AudioStream.cpp
@@ -38,7 +38,6 @@
 
 aaudio_result_t AudioStream::open(const AudioStreamBuilder& builder)
 {
-
     // Copy parameters from the Builder because the Builder may be deleted after this call.
     mSamplesPerFrame = builder.getSamplesPerFrame();
     mSampleRate = builder.getSampleRate();
@@ -46,23 +45,58 @@
     mFormat = builder.getFormat();
     mDirection = builder.getDirection();
     mSharingMode = builder.getSharingMode();
+    mSharingModeMatchRequired = builder.isSharingModeMatchRequired();
+
+    mPerformanceMode = builder.getPerformanceMode();
 
     // callbacks
     mFramesPerDataCallback = builder.getFramesPerDataCallback();
     mDataCallbackProc = builder.getDataCallbackProc();
     mErrorCallbackProc = builder.getErrorCallbackProc();
     mDataCallbackUserData = builder.getDataCallbackUserData();
+    mErrorCallbackUserData = builder.getErrorCallbackUserData();
 
-    // TODO validate more parameters.
-    if (mErrorCallbackProc != nullptr && mDataCallbackProc == nullptr) {
-        ALOGE("AudioStream::open(): disconnect callback cannot be used without a data callback.");
-        return AAUDIO_ERROR_UNEXPECTED_VALUE;
+    // This is very helpful for debugging in the future.
+    ALOGI("AudioStream.open(): rate = %d, channels = %d, format = %d, sharing = %d",
+          mSampleRate, mSamplesPerFrame, mFormat, mSharingMode);
+
+    // Check for values that are ridiculously out of range to prevent math overflow exploits.
+    // The service will do a better check.
+    if (mSamplesPerFrame < 0 || mSamplesPerFrame > 128) {
+        ALOGE("AudioStream::open(): samplesPerFrame out of range = %d", mSamplesPerFrame);
+        return AAUDIO_ERROR_OUT_OF_RANGE;
+    }
+
+    switch(mFormat) {
+        case AAUDIO_FORMAT_UNSPECIFIED:
+        case AAUDIO_FORMAT_PCM_I16:
+        case AAUDIO_FORMAT_PCM_FLOAT:
+            break; // valid
+        default:
+            ALOGE("AudioStream::open(): audioFormat not valid = %d", mFormat);
+            return AAUDIO_ERROR_INVALID_FORMAT;
+            // break;
+    }
+
+    if (mSampleRate < 0 || mSampleRate > 1000000) {
+        ALOGE("AudioStream::open(): mSampleRate out of range = %d", mSampleRate);
+        return AAUDIO_ERROR_INVALID_RATE;
     }
     if (mDirection != AAUDIO_DIRECTION_INPUT && mDirection != AAUDIO_DIRECTION_OUTPUT) {
         ALOGE("AudioStream::open(): illegal direction %d", mDirection);
         return AAUDIO_ERROR_UNEXPECTED_VALUE;
     }
 
+    switch(mPerformanceMode) {
+        case AAUDIO_PERFORMANCE_MODE_NONE:
+        case AAUDIO_PERFORMANCE_MODE_POWER_SAVING:
+        case AAUDIO_PERFORMANCE_MODE_LOW_LATENCY:
+            break;
+        default:
+            ALOGE("AudioStream::open(): illegal performanceMode %d", mPerformanceMode);
+            return AAUDIO_ERROR_UNEXPECTED_VALUE;
+    }
+
     return AAUDIO_OK;
 }
 
@@ -70,27 +104,6 @@
     close();
 }
 
-aaudio_result_t AudioStream::waitForStateTransition(aaudio_stream_state_t startingState,
-                                               aaudio_stream_state_t endingState,
-                                               int64_t timeoutNanoseconds)
-{
-    aaudio_stream_state_t state = getState();
-    aaudio_stream_state_t nextState = state;
-    if (state == startingState && state != endingState) {
-        aaudio_result_t result = waitForStateChange(state, &nextState, timeoutNanoseconds);
-        if (result != AAUDIO_OK) {
-            return result;
-        }
-    }
-// It's OK if the expected transition has already occurred.
-// But if we reach an unexpected state then that is an error.
-    if (nextState != endingState) {
-        return AAUDIO_ERROR_UNEXPECTED_STATE;
-    } else {
-        return AAUDIO_OK;
-    }
-}
-
 aaudio_result_t AudioStream::waitForStateChange(aaudio_stream_state_t currentState,
                                                 aaudio_stream_state_t *nextState,
                                                 int64_t timeoutNanoseconds)
@@ -100,7 +113,6 @@
         return result;
     }
 
-    // TODO replace this when similar functionality added to AudioTrack.cpp
     int64_t durationNanos = 20 * AAUDIO_NANOS_PER_MILLISECOND; // arbitrary
     aaudio_stream_state_t state = getState();
     while (state == currentState && timeoutNanoseconds > 0) {
@@ -123,16 +135,15 @@
     return (state == currentState) ? AAUDIO_ERROR_TIMEOUT : AAUDIO_OK;
 }
 
-// This registers the app's background audio thread with the server before
+// This registers the callback thread with the server before
 // passing control to the app. This gives the server an opportunity to boost
 // the thread's performance characteristics.
 void* AudioStream::wrapUserThread() {
     void* procResult = nullptr;
     mThreadRegistrationResult = registerThread();
     if (mThreadRegistrationResult == AAUDIO_OK) {
-        // Call application procedure. This may take a very long time.
+        // Run callback loop. This may take a very long time.
         procResult = mThreadProc(mThreadArg);
-        ALOGD("AudioStream::mThreadProc() returned");
         mThreadRegistrationResult = unregisterThread();
     }
     return procResult;
@@ -145,6 +156,8 @@
     return audioStream->wrapUserThread();
 }
 
+// This is not exposed in the API.
+// But it is still used internally to implement callbacks for MMAP mode.
 aaudio_result_t AudioStream::createThread(int64_t periodNanoseconds,
                                      aaudio_audio_thread_proc_t threadProc,
                                      void* threadArg)
@@ -161,8 +174,7 @@
     setPeriodNanoseconds(periodNanoseconds);
     int err = pthread_create(&mThread, nullptr, AudioStream_internalThreadProc, this);
     if (err != 0) {
-        // TODO convert errno to aaudio_result_t
-        return AAUDIO_ERROR_INTERNAL;
+        return AAudioConvert_androidToAAudioResult(-errno);
     } else {
         mHasThread = true;
         return AAUDIO_OK;
@@ -182,7 +194,6 @@
     int err = pthread_join(mThread, returnArg);
 #endif
     mHasThread = false;
-    // TODO convert errno to aaudio_result_t
-    return err ? AAUDIO_ERROR_INTERNAL : mThreadRegistrationResult;
+    return err ? AAudioConvert_androidToAAudioResult(-errno) : mThreadRegistrationResult;
 }
 
diff --git a/media/libaaudio/src/core/AudioStream.h b/media/libaaudio/src/core/AudioStream.h
index 073b9a1..c49b46b 100644
--- a/media/libaaudio/src/core/AudioStream.h
+++ b/media/libaaudio/src/core/AudioStream.h
@@ -134,6 +134,10 @@
         return mState == AAUDIO_STREAM_STATE_STARTING || mState == AAUDIO_STREAM_STATE_STARTED;
     }
 
+    virtual bool isMMap() {
+        return false;
+    }
+
     aaudio_result_t getSampleRate() const {
         return mSampleRate;
     }
@@ -146,6 +150,14 @@
         return mSamplesPerFrame;
     }
 
+    virtual int32_t getPerformanceMode() const {
+        return mPerformanceMode;
+    }
+
+    void setPerformanceMode(aaudio_performance_mode_t performanceMode) {
+        mPerformanceMode = performanceMode;
+    }
+
     int32_t getDeviceId() const {
         return mDeviceId;
     }
@@ -154,6 +166,10 @@
         return mSharingMode;
     }
 
+    bool isSharingModeMatchRequired() const {
+        return mSharingModeMatchRequired;
+    }
+
     aaudio_direction_t getDirection() const {
         return mDirection;
     }
@@ -219,24 +235,14 @@
 protected:
 
     virtual int64_t incrementFramesWritten(int32_t frames) {
-        return static_cast<int64_t>(mFramesWritten.increment(frames));
+        return mFramesWritten.increment(frames);
     }
 
     virtual int64_t incrementFramesRead(int32_t frames) {
-        return static_cast<int64_t>(mFramesRead.increment(frames));
+        return mFramesRead.increment(frames);
     }
 
     /**
-     * Wait for a transition from one state to another.
-     * @return AAUDIO_OK if the endingState was observed, or AAUDIO_ERROR_UNEXPECTED_STATE
-     *   if any state that was not the startingState or endingState was observed
-     *   or AAUDIO_ERROR_TIMEOUT
-     */
-    virtual aaudio_result_t waitForStateTransition(aaudio_stream_state_t startingState,
-                                                   aaudio_stream_state_t endingState,
-                                                   int64_t timeoutNanoseconds);
-
-    /**
      * This should not be called after the open() call.
      */
     void setSampleRate(int32_t sampleRate) {
@@ -294,10 +300,13 @@
     int32_t                mSampleRate = AAUDIO_UNSPECIFIED;
     int32_t                mDeviceId = AAUDIO_UNSPECIFIED;
     aaudio_sharing_mode_t  mSharingMode = AAUDIO_SHARING_MODE_SHARED;
+    bool                   mSharingModeMatchRequired = false; // must match sharing mode requested
     aaudio_audio_format_t  mFormat = AAUDIO_FORMAT_UNSPECIFIED;
     aaudio_direction_t     mDirection = AAUDIO_DIRECTION_OUTPUT;
     aaudio_stream_state_t  mState = AAUDIO_STREAM_STATE_UNINITIALIZED;
 
+    aaudio_performance_mode_t mPerformanceMode = AAUDIO_PERFORMANCE_MODE_NONE;
+
     // callback ----------------------------------
 
     AAudioStream_dataCallback   mDataCallbackProc = nullptr;  // external callback functions
diff --git a/media/libaaudio/src/core/AudioStreamBuilder.cpp b/media/libaaudio/src/core/AudioStreamBuilder.cpp
index b135a4b..9bc2ef2 100644
--- a/media/libaaudio/src/core/AudioStreamBuilder.cpp
+++ b/media/libaaudio/src/core/AudioStreamBuilder.cpp
@@ -30,9 +30,10 @@
 #include "legacy/AudioStreamRecord.h"
 #include "legacy/AudioStreamTrack.h"
 
-// Enable a mixer in AAudio service that will mix stream to an ALSA MMAP buffer.
+// Enable a mixer in AAudio service that will mix streams to an ALSA MMAP buffer.
 #define MMAP_SHARED_ENABLED      0
-// Enable AAUDIO_SHARING_MODE_EXCLUSIVE that uses an ALSA MMAP buffer.
+
+// Enable AAUDIO_SHARING_MODE_EXCLUSIVE that uses an ALSA MMAP buffer directly.
 #define MMAP_EXCLUSIVE_ENABLED   0
 
 using namespace aaudio;
@@ -46,66 +47,80 @@
 AudioStreamBuilder::~AudioStreamBuilder() {
 }
 
+static aaudio_result_t builder_createStream(aaudio_direction_t direction,
+                                         aaudio_sharing_mode_t sharingMode,
+                                         bool tryMMap,
+                                         AudioStream **audioStreamPtr) {
+    *audioStreamPtr = nullptr;
+    aaudio_result_t result = AAUDIO_OK;
+    switch (direction) {
+
+        case AAUDIO_DIRECTION_INPUT:
+            if (sharingMode == AAUDIO_SHARING_MODE_SHARED) {
+                *audioStreamPtr = new AudioStreamRecord();
+            } else {
+                ALOGE("AudioStreamBuilder(): bad sharing mode = %d for input", sharingMode);
+                result = AAUDIO_ERROR_ILLEGAL_ARGUMENT;
+            }
+            break;
+
+        case AAUDIO_DIRECTION_OUTPUT:
+            if (tryMMap) {
+                // TODO use a singleton for the AAudioBinderClient
+                AAudioBinderClient *aaudioClient = new AAudioBinderClient();
+                *audioStreamPtr = new AudioStreamInternal(*aaudioClient, false);
+            } else {
+                *audioStreamPtr = new AudioStreamTrack();
+            }
+            break;
+
+        default:
+            ALOGE("AudioStreamBuilder(): bad direction = %d", direction);
+            result = AAUDIO_ERROR_ILLEGAL_ARGUMENT;
+    }
+    return result;
+}
+
 aaudio_result_t AudioStreamBuilder::build(AudioStream** streamPtr) {
-    AudioStream* audioStream = nullptr;
-    AAudioBinderClient *aaudioClient = nullptr;
-    const aaudio_sharing_mode_t sharingMode = getSharingMode();
-    ALOGD("AudioStreamBuilder.build() sharingMode = %d", sharingMode);
-    switch (getDirection()) {
+    aaudio_sharing_mode_t sharingMode = getSharingMode();
+    if ((sharingMode == AAUDIO_SHARING_MODE_EXCLUSIVE) && (MMAP_EXCLUSIVE_ENABLED == 0)) {
+        ALOGE("AudioStreamBuilder(): EXCLUSIVE sharing mode not supported");
+        return AAUDIO_ERROR_UNAVAILABLE;
+    }
 
-    case AAUDIO_DIRECTION_INPUT:
-        switch (sharingMode) {
-            case AAUDIO_SHARING_MODE_SHARED:
-                audioStream = new(std::nothrow) AudioStreamRecord();
-                break;
-            default:
-                ALOGE("AudioStreamBuilder(): bad sharing mode = %d", sharingMode);
-                return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
-                break;
+    AudioStream *audioStream = nullptr;
+    *streamPtr = nullptr;
+
+    bool tryMMap = (sharingMode == AAUDIO_SHARING_MODE_SHARED) && MMAP_SHARED_ENABLED;
+    aaudio_result_t result = builder_createStream(getDirection(), sharingMode,
+                                                  tryMMap, &audioStream);
+    if (result == AAUDIO_OK) {
+        // Open the stream using the parameters from the builder.
+        result = audioStream->open(*this);
+        if (result == AAUDIO_OK) {
+            *streamPtr = audioStream;
+        } else {
+            bool isMMap = audioStream->isMMap();
+            delete audioStream;
+            audioStream = nullptr;
+
+            if (isMMap) {
+                ALOGD("AudioStreamBuilder.build() MMAP stream did not open so try Legacy path");
+                // If MMAP stream failed to open then TRY using a legacy stream.
+                result = builder_createStream(getDirection(), sharingMode,
+                                              false, &audioStream);
+                if (result == AAUDIO_OK) {
+                    result = audioStream->open(*this);
+                    if (result == AAUDIO_OK) {
+                        *streamPtr = audioStream;
+                    } else {
+                        delete audioStream;
+                    }
+                }
+            }
         }
-        break;
-
-    case AAUDIO_DIRECTION_OUTPUT:
-        switch (sharingMode) {
-            case AAUDIO_SHARING_MODE_SHARED:
-#if MMAP_SHARED_ENABLED
-                aaudioClient = new AAudioBinderClient();
-                audioStream = new(std::nothrow) AudioStreamInternal(*aaudioClient, false);
-#else
-                audioStream = new(std::nothrow) AudioStreamTrack();
-#endif
-                break;
-#if MMAP_EXCLUSIVE_ENABLED
-            case AAUDIO_SHARING_MODE_EXCLUSIVE:
-                aaudioClient = new AAudioBinderClient();
-                audioStream = new(std::nothrow) AudioStreamInternal(*aaudioClient, false);
-                break;
-#endif
-            default:
-                ALOGE("AudioStreamBuilder(): bad sharing mode = %d", sharingMode);
-                return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
-                break;
-        }
-        break;
-
-    default:
-        ALOGE("AudioStreamBuilder(): bad direction = %d", getDirection());
-        return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
-        break;
     }
-    if (audioStream == nullptr) {
-        delete aaudioClient;
-        return AAUDIO_ERROR_NO_MEMORY;
-    }
-    ALOGD("AudioStreamBuilder(): created audioStream = %p", audioStream);
 
-    // TODO maybe move this out of build and pass the builder to the constructors
-    // Open the stream using the parameters from the builder.
-    const aaudio_result_t result = audioStream->open(*this);
-    if (result != AAUDIO_OK) {
-        delete audioStream;
-    } else {
-        *streamPtr = audioStream;
-    }
+    ALOGD("AudioStreamBuilder(): returned %d", result);
     return result;
 }
diff --git a/media/libaaudio/src/core/AudioStreamBuilder.h b/media/libaaudio/src/core/AudioStreamBuilder.h
index c0ee6fe..569ca63 100644
--- a/media/libaaudio/src/core/AudioStreamBuilder.h
+++ b/media/libaaudio/src/core/AudioStreamBuilder.h
@@ -82,6 +82,15 @@
         return this;
     }
 
+    bool isSharingModeMatchRequired() const {
+        return mSharingModeMatchRequired;
+    }
+
+    AudioStreamBuilder* setSharingModeMatchRequired(bool required) {
+        mSharingModeMatchRequired = required;
+        return this;
+    }
+
     int32_t getBufferCapacity() const {
         return mBufferCapacity;
     }
@@ -91,6 +100,15 @@
         return this;
     }
 
+    int32_t getPerformanceMode() const {
+        return mPerformanceMode;
+    }
+
+    AudioStreamBuilder* setPerformanceMode(aaudio_performance_mode_t performanceMode) {
+        mPerformanceMode = performanceMode;
+        return this;
+    }
+
     int32_t getDeviceId() const {
         return mDeviceId;
     }
@@ -109,7 +127,6 @@
         return this;
     }
 
-
     void *getDataCallbackUserData() const {
         return mDataCallbackUserData;
     }
@@ -149,13 +166,15 @@
     aaudio_result_t build(AudioStream **streamPtr);
 
 private:
-    int32_t                mSamplesPerFrame = AAUDIO_UNSPECIFIED;
-    int32_t                mSampleRate = AAUDIO_UNSPECIFIED;
-    int32_t                mDeviceId = AAUDIO_DEVICE_UNSPECIFIED;
-    aaudio_sharing_mode_t  mSharingMode = AAUDIO_SHARING_MODE_SHARED;
-    aaudio_audio_format_t  mFormat = AAUDIO_FORMAT_UNSPECIFIED;
-    aaudio_direction_t     mDirection = AAUDIO_DIRECTION_OUTPUT;
-    int32_t                mBufferCapacity = AAUDIO_UNSPECIFIED;
+    int32_t                    mSamplesPerFrame = AAUDIO_UNSPECIFIED;
+    int32_t                    mSampleRate = AAUDIO_UNSPECIFIED;
+    int32_t                    mDeviceId = AAUDIO_DEVICE_UNSPECIFIED;
+    aaudio_sharing_mode_t      mSharingMode = AAUDIO_SHARING_MODE_SHARED;
+    bool                       mSharingModeMatchRequired = false; // must match sharing mode requested
+    aaudio_audio_format_t      mFormat = AAUDIO_FORMAT_UNSPECIFIED;
+    aaudio_direction_t         mDirection = AAUDIO_DIRECTION_OUTPUT;
+    int32_t                    mBufferCapacity = AAUDIO_UNSPECIFIED;
+    aaudio_performance_mode_t  mPerformanceMode = AAUDIO_PERFORMANCE_MODE_NONE;
 
     AAudioStream_dataCallback  mDataCallbackProc = nullptr;  // external callback functions
     void                      *mDataCallbackUserData = nullptr;
diff --git a/media/libaaudio/src/fifo/FifoBuffer.cpp b/media/libaaudio/src/fifo/FifoBuffer.cpp
index 857780c..6b4a772 100644
--- a/media/libaaudio/src/fifo/FifoBuffer.cpp
+++ b/media/libaaudio/src/fifo/FifoBuffer.cpp
@@ -60,14 +60,11 @@
         , mFramesUnderrunCount(0)
         , mUnderrunCount(0)
 {
-    // TODO Handle possible failures to allocate. Move out of constructor?
     mFifo = new FifoControllerIndirect(capacityInFrames,
                                        capacityInFrames,
                                        readIndexAddress,
                                        writeIndexAddress);
     mStorageOwned = false;
-    ALOGD("FifoProcessor: capacityInFrames = %d, bytesPerFrame = %d",
-          capacityInFrames, bytesPerFrame);
 }
 
 FifoBuffer::~FifoBuffer() {
@@ -132,8 +129,6 @@
     while (framesLeft > 0 && partIndex < WrappingBuffer::SIZE) {
         fifo_frames_t framesToRead = framesLeft;
         fifo_frames_t framesAvailable = wrappingBuffer.numFrames[partIndex];
-        //ALOGD("FifoProcessor::read() framesAvailable = %d, partIndex = %d",
-        //      framesAvailable, partIndex);
         if (framesAvailable > 0) {
             if (framesToRead > framesAvailable) {
                 framesToRead = framesAvailable;
@@ -143,6 +138,8 @@
 
             destination += numBytes;
             framesLeft -= framesToRead;
+        } else {
+            break;
         }
         partIndex++;
     }
@@ -172,6 +169,8 @@
 
             source += numBytes;
             framesLeft -= framesToWrite;
+        } else {
+            break;
         }
         partIndex++;
     }
diff --git a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
index baa24c9..f89234a 100644
--- a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
@@ -83,6 +83,7 @@
             }
             if (callbackResult == AAUDIO_CALLBACK_RESULT_CONTINUE) {
                 audioBuffer->size = audioBuffer->frameCount * getBytesPerFrame();
+                incrementClientFrameCounter(audioBuffer->frameCount);
             } else {
                 audioBuffer->size = 0;
             }
@@ -108,3 +109,23 @@
     }
 }
 
+aaudio_result_t AudioStreamLegacy::getBestTimestamp(clockid_t clockId,
+                                                   int64_t *framePosition,
+                                                   int64_t *timeNanoseconds,
+                                                   ExtendedTimestamp *extendedTimestamp) {
+    int timebase;
+    switch (clockId) {
+        case CLOCK_BOOTTIME:
+            timebase = ExtendedTimestamp::TIMEBASE_BOOTTIME;
+            break;
+        case CLOCK_MONOTONIC:
+            timebase = ExtendedTimestamp::TIMEBASE_MONOTONIC;
+            break;
+        default:
+            ALOGE("getTimestamp() - Unrecognized clock type %d", (int) clockId);
+            return AAUDIO_ERROR_UNEXPECTED_VALUE;
+            break;
+    }
+    status_t status = extendedTimestamp->getBestTimestamp(framePosition, timeNanoseconds, timebase);
+    return AAudioConvert_androidToAAudioResult(status);
+}
diff --git a/media/libaaudio/src/legacy/AudioStreamLegacy.h b/media/libaaudio/src/legacy/AudioStreamLegacy.h
index c109ee7..38f1a56 100644
--- a/media/libaaudio/src/legacy/AudioStreamLegacy.h
+++ b/media/libaaudio/src/legacy/AudioStreamLegacy.h
@@ -17,6 +17,7 @@
 #ifndef LEGACY_AUDIO_STREAM_LEGACY_H
 #define LEGACY_AUDIO_STREAM_LEGACY_H
 
+#include <media/AudioTimestamp.h>
 
 #include <aaudio/AAudio.h>
 
@@ -70,7 +71,15 @@
     // Implement FixedBlockProcessor
     int32_t onProcessFixedBlock(uint8_t *buffer, int32_t numBytes) override;
 
+    virtual int64_t incrementClientFrameCounter(int32_t frames)  = 0;
+
 protected:
+
+    aaudio_result_t getBestTimestamp(clockid_t clockId,
+                                     int64_t *framePosition,
+                                     int64_t *timeNanoseconds,
+                                     android::ExtendedTimestamp *extendedTimestamp);
+
     FixedBlockAdapter         *mBlockAdapter = nullptr;
     aaudio_wrapping_frames_t   mPositionWhenStarting = 0;
     int32_t                    mCallbackBufferSize = 0;
diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.cpp b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
index f0a6ceb..eb6bfd5 100644
--- a/media/libaaudio/src/legacy/AudioStreamRecord.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
@@ -177,10 +177,13 @@
 
 aaudio_result_t AudioStreamRecord::requestPause()
 {
+    // This does not make sense for an input stream.
+    // There is no real difference between pause() and stop().
     return AAUDIO_ERROR_UNIMPLEMENTED;
 }
 
 aaudio_result_t AudioStreamRecord::requestFlush() {
+    // This does not make sense for an input stream.
     return AAUDIO_ERROR_UNIMPLEMENTED;
 }
 
@@ -259,7 +262,7 @@
 
 int32_t AudioStreamRecord::getXRunCount() const
 {
-    return AAUDIO_ERROR_UNIMPLEMENTED; // TODO implement when AudioRecord supports it
+    return 0; // TODO implement when AudioRecord supports it
 }
 
 int32_t AudioStreamRecord::getFramesPerBurst() const
@@ -275,20 +278,5 @@
     if (status != NO_ERROR) {
         return AAudioConvert_androidToAAudioResult(status);
     }
-    // TODO Merge common code into AudioStreamLegacy after rebasing.
-    int timebase;
-    switch(clockId) {
-        case CLOCK_BOOTTIME:
-            timebase = ExtendedTimestamp::TIMEBASE_BOOTTIME;
-            break;
-        case CLOCK_MONOTONIC:
-            timebase = ExtendedTimestamp::TIMEBASE_MONOTONIC;
-            break;
-        default:
-            ALOGE("getTimestamp() - Unrecognized clock type %d", (int) clockId);
-            return AAUDIO_ERROR_UNEXPECTED_VALUE;
-            break;
-    }
-    status = extendedTimestamp.getBestTimestamp(framePosition, timeNanoseconds, timebase);
-    return AAudioConvert_androidToAAudioResult(status);
+    return getBestTimestamp(clockId, framePosition, timeNanoseconds, &extendedTimestamp);
 }
diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.h b/media/libaaudio/src/legacy/AudioStreamRecord.h
index 897a5b3..f4a78e1 100644
--- a/media/libaaudio/src/legacy/AudioStreamRecord.h
+++ b/media/libaaudio/src/legacy/AudioStreamRecord.h
@@ -68,6 +68,10 @@
     // This is public so it can be called from the C callback function.
     void processCallback(int event, void *info) override;
 
+    int64_t incrementClientFrameCounter(int32_t frames) override {
+        return incrementFramesRead(frames);
+    }
+
 private:
     android::sp<android::AudioRecord> mAudioRecord;
     // adapts between variable sized blocks and fixed size blocks
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.cpp b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
index 1bb9e53..a7c7673 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
@@ -61,22 +61,36 @@
     ALOGD("AudioStreamTrack::open = %p", this);
 
     // Try to create an AudioTrack
-    // TODO Support UNSPECIFIED in AudioTrack. For now, use stereo if unspecified.
+    // Use stereo if unspecified.
     int32_t samplesPerFrame = (getSamplesPerFrame() == AAUDIO_UNSPECIFIED)
                               ? 2 : getSamplesPerFrame();
     audio_channel_mask_t channelMask = audio_channel_out_mask_from_count(samplesPerFrame);
     ALOGD("AudioStreamTrack::open(), samplesPerFrame = %d, channelMask = 0x%08x",
             samplesPerFrame, channelMask);
 
-    // TODO add more performance options
-    audio_output_flags_t flags = (audio_output_flags_t) AUDIO_OUTPUT_FLAG_FAST;
+    audio_output_flags_t flags = AUDIO_OUTPUT_FLAG_NONE;
+    switch(getPerformanceMode()) {
+        case AAUDIO_PERFORMANCE_MODE_LOW_LATENCY:
+            // Bypass the normal mixer and go straight to the FAST mixer.
+            flags = (audio_output_flags_t)(AUDIO_OUTPUT_FLAG_FAST | AUDIO_OUTPUT_FLAG_RAW);
+            break;
+
+        case AAUDIO_PERFORMANCE_MODE_POWER_SAVING:
+            // This uses a mixer that wakes up less often than the FAST mixer.
+            flags = AUDIO_OUTPUT_FLAG_DEEP_BUFFER;
+            break;
+
+        case AAUDIO_PERFORMANCE_MODE_NONE:
+        default:
+            // No flags. Use a normal mixer in front of the FAST mixer.
+            break;
+    }
 
     int32_t frameCount = builder.getBufferCapacity();
     ALOGD("AudioStreamTrack::open(), requested buffer capacity %d", frameCount);
 
     int32_t notificationFrames = 0;
 
-    // TODO implement an unspecified AudioTrack format then use that.
     audio_format_t format = (getFormat() == AAUDIO_FORMAT_UNSPECIFIED)
             ? AUDIO_FORMAT_PCM_FLOAT
             : AAudioConvert_aaudioToAndroidDataFormat(getFormat());
@@ -149,9 +163,8 @@
 
 aaudio_result_t AudioStreamTrack::close()
 {
-    // TODO maybe add close() or release() to AudioTrack API then call it from here
     if (getState() != AAUDIO_STREAM_STATE_CLOSED) {
-        mAudioTrack.clear(); // TODO is this right?
+        mAudioTrack.clear();
         setState(AAUDIO_STREAM_STATE_CLOSED);
     }
     mFixedBlockReader.close();
@@ -276,7 +289,7 @@
             if (err != OK) {
                 return AAudioConvert_androidToAAudioResult(err);
             } else if (position == 0) {
-                // Advance frames read to match written.
+                // TODO Advance frames read to match written.
                 setState(AAUDIO_STREAM_STATE_FLUSHED);
             }
         }
@@ -354,6 +367,8 @@
     case AAUDIO_STREAM_STATE_STARTING:
     case AAUDIO_STREAM_STATE_STARTED:
     case AAUDIO_STREAM_STATE_STOPPING:
+    case AAUDIO_STREAM_STATE_PAUSING:
+    case AAUDIO_STREAM_STATE_PAUSED:
         result = mAudioTrack->getPosition(&position);
         if (result == OK) {
             mFramesRead.update32(position);
@@ -373,20 +388,5 @@
     if (status != NO_ERROR) {
         return AAudioConvert_androidToAAudioResult(status);
     }
-    // TODO Merge common code into AudioStreamLegacy after rebasing.
-    int timebase;
-    switch (clockId) {
-        case CLOCK_BOOTTIME:
-            timebase = ExtendedTimestamp::TIMEBASE_BOOTTIME;
-            break;
-        case CLOCK_MONOTONIC:
-            timebase = ExtendedTimestamp::TIMEBASE_MONOTONIC;
-            break;
-        default:
-            ALOGE("getTimestamp() - Unrecognized clock type %d", (int) clockId);
-            return AAUDIO_ERROR_UNEXPECTED_VALUE;
-            break;
-    }
-    status = extendedTimestamp.getBestTimestamp(framePosition, timeNanoseconds, timebase);
-    return AAudioConvert_androidToAAudioResult(status);
+    return getBestTimestamp(clockId, framePosition, timeNanoseconds, &extendedTimestamp);
 }
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.h b/media/libaaudio/src/legacy/AudioStreamTrack.h
index 29f5d15..186a08e 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.h
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.h
@@ -68,6 +68,10 @@
     // This is public so it can be called from the C callback function.
     void processCallback(int event, void *info) override;
 
+    int64_t incrementClientFrameCounter(int32_t frames) override {
+        return incrementFramesWritten(frames);
+    }
+
 private:
 
     android::sp<android::AudioTrack> mAudioTrack;
diff --git a/media/libaaudio/src/utility/AAudioUtilities.cpp b/media/libaaudio/src/utility/AAudioUtilities.cpp
index 87b8b0d..be2bd10 100644
--- a/media/libaaudio/src/utility/AAudioUtilities.cpp
+++ b/media/libaaudio/src/utility/AAudioUtilities.cpp
@@ -27,6 +27,11 @@
 
 using namespace android;
 
+// This is 3 dB, (10^(3/20)), to match the maximum headroom in AudioTrack for float data.
+// It is designed to allow occasional transient peaks.
+#define MAX_HEADROOM (1.41253754f)
+#define MIN_HEADROOM (0 - MAX_HEADROOM)
+
 int32_t AAudioConvert_formatToSizeInBytes(aaudio_audio_format_t format) {
     int32_t size = AAUDIO_ERROR_ILLEGAL_ARGUMENT;
     switch (format) {
@@ -42,24 +47,153 @@
     return size;
 }
 
-// TODO This similar to a function in audio_utils. Consider using that instead.
-void AAudioConvert_floatToPcm16(const float *source, int32_t numSamples, int16_t *destination) {
+
+// TODO expose and call clamp16_from_float function in primitives.h
+static inline int16_t clamp16_from_float(float f) {
+    /* Offset is used to expand the valid range of [-1.0, 1.0) into the 16 lsbs of the
+     * floating point significand. The normal shift is 3<<22, but the -15 offset
+     * is used to multiply by 32768.
+     */
+    static const float offset = (float)(3 << (22 - 15));
+    /* zero = (0x10f << 22) =  0x43c00000 (not directly used) */
+    static const int32_t limneg = (0x10f << 22) /*zero*/ - 32768; /* 0x43bf8000 */
+    static const int32_t limpos = (0x10f << 22) /*zero*/ + 32767; /* 0x43c07fff */
+
+    union {
+        float f;
+        int32_t i;
+    } u;
+
+    u.f = f + offset; /* recenter valid range */
+    /* Now the valid range is represented as integers between [limneg, limpos].
+     * Clamp using the fact that float representation (as an integer) is an ordered set.
+     */
+    if (u.i < limneg)
+        u.i = -32768;
+    else if (u.i > limpos)
+        u.i = 32767;
+    return u.i; /* Return lower 16 bits, the part of interest in the significand. */
+}
+
+// Same but without clipping.
+// Convert -1.0f to +1.0f to -32768 to +32767
+static inline int16_t floatToInt16(float f) {
+    static const float offset = (float)(3 << (22 - 15));
+    union {
+        float f;
+        int32_t i;
+    } u;
+    u.f = f + offset; /* recenter valid range */
+    return u.i; /* Return lower 16 bits, the part of interest in the significand. */
+}
+
+static float clipAndClampFloatToPcm16(float sample, float scaler) {
+    // Clip to valid range of a float sample to prevent excessive volume.
+    if (sample > MAX_HEADROOM) sample = MAX_HEADROOM;
+    else if (sample < MIN_HEADROOM) sample = MIN_HEADROOM;
+
+    // Scale and convert to a short.
+    float fval = sample * scaler;
+    return clamp16_from_float(fval);
+}
+
+void AAudioConvert_floatToPcm16(const float *source,
+                                int16_t *destination,
+                                int32_t numSamples,
+                                float amplitude) {
+    float scaler = amplitude;
     for (int i = 0; i < numSamples; i++) {
-        float fval = source[i];
-        fval += 1.0; // to avoid discontinuity at 0.0 caused by truncation
-        fval *= 32768.0f;
-        int32_t sample = (int32_t) fval;
-        // clip to 16-bit range
-        if (sample < 0) sample = 0;
-        else if (sample > 0x0FFFF) sample = 0x0FFFF;
-        sample -= 32768; // center at zero
-        destination[i] = (int16_t) sample;
+        float sample = *source++;
+        *destination++ = clipAndClampFloatToPcm16(sample, scaler);
     }
 }
 
-void AAudioConvert_pcm16ToFloat(const float *source, int32_t numSamples, int16_t *destination) {
+void AAudioConvert_floatToPcm16(const float *source,
+                                int16_t *destination,
+                                int32_t numFrames,
+                                int32_t samplesPerFrame,
+                                float amplitude1,
+                                float amplitude2) {
+    float scaler = amplitude1;
+    // divide by numFrames so that we almost reach amplitude2
+    float delta = (amplitude2 - amplitude1) / numFrames;
+    for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) {
+        for (int sampleIndex = 0; sampleIndex < samplesPerFrame; sampleIndex++) {
+            float sample = *source++;
+            *destination++ = clipAndClampFloatToPcm16(sample, scaler);
+        }
+        scaler += delta;
+    }
+}
+
+#define SHORT_SCALE  32768
+
+void AAudioConvert_pcm16ToFloat(const int16_t *source,
+                                float *destination,
+                                int32_t numSamples,
+                                float amplitude) {
+    float scaler = amplitude / SHORT_SCALE;
     for (int i = 0; i < numSamples; i++) {
-        destination[i] = source[i] * (1.0f / 32768.0f);
+        destination[i] = source[i] * scaler;
+    }
+}
+
+// This code assumes amplitude1 and amplitude2 are between 0.0 and 1.0
+void AAudioConvert_pcm16ToFloat(const int16_t *source,
+                                float *destination,
+                                int32_t numFrames,
+                                int32_t samplesPerFrame,
+                                float amplitude1,
+                                float amplitude2) {
+    float scaler = amplitude1 / SHORT_SCALE;
+    float delta = (amplitude2 - amplitude1) / (SHORT_SCALE * (float) numFrames);
+    for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) {
+        for (int sampleIndex = 0; sampleIndex < samplesPerFrame; sampleIndex++) {
+            *destination++ = *source++ * scaler;
+        }
+        scaler += delta;
+    }
+}
+
+// This code assumes amplitude1 and amplitude2 are between 0.0 and 1.0
+void AAudio_linearRamp(const float *source,
+                       float *destination,
+                       int32_t numFrames,
+                       int32_t samplesPerFrame,
+                       float amplitude1,
+                       float amplitude2) {
+    float scaler = amplitude1;
+    float delta = (amplitude2 - amplitude1) / numFrames;
+    for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) {
+        for (int sampleIndex = 0; sampleIndex < samplesPerFrame; sampleIndex++) {
+            float sample = *source++;
+
+            // Clip to valid range of a float sample to prevent excessive volume.
+            if (sample > MAX_HEADROOM) sample = MAX_HEADROOM;
+            else if (sample < MIN_HEADROOM) sample = MIN_HEADROOM;
+
+            *destination++ = sample * scaler;
+        }
+        scaler += delta;
+    }
+}
+
+// This code assumes amplitude1 and amplitude2 are between 0.0 and 1.0
+void AAudio_linearRamp(const int16_t *source,
+                       int16_t *destination,
+                       int32_t numFrames,
+                       int32_t samplesPerFrame,
+                       float amplitude1,
+                       float amplitude2) {
+    float scaler = amplitude1 / SHORT_SCALE;
+    float delta = (amplitude2 - amplitude1) / (SHORT_SCALE * (float) numFrames);
+    for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) {
+        for (int sampleIndex = 0; sampleIndex < samplesPerFrame; sampleIndex++) {
+            // No need to clip because int16_t range is inherently limited.
+            float sample =  *source++ * scaler;
+            *destination++ =  floatToInt16(sample);
+        }
+        scaler += delta;
     }
 }
 
@@ -75,16 +209,32 @@
         status = DEAD_OBJECT;
         break;
     case AAUDIO_ERROR_INVALID_STATE:
+    case AAUDIO_ERROR_UNEXPECTED_STATE:
         status = INVALID_OPERATION;
         break;
-    case AAUDIO_ERROR_UNEXPECTED_VALUE: // TODO redundant?
+    case AAUDIO_ERROR_UNEXPECTED_VALUE:
+    case AAUDIO_ERROR_INVALID_RATE:
+    case AAUDIO_ERROR_INVALID_FORMAT:
     case AAUDIO_ERROR_ILLEGAL_ARGUMENT:
+    case AAUDIO_ERROR_OUT_OF_RANGE:
         status = BAD_VALUE;
         break;
     case AAUDIO_ERROR_WOULD_BLOCK:
         status = WOULD_BLOCK;
         break;
-    // TODO add more result codes
+    case AAUDIO_ERROR_NULL:
+        status = UNEXPECTED_NULL;
+        break;
+    // TODO translate these result codes
+    case AAUDIO_ERROR_INCOMPATIBLE:
+    case AAUDIO_ERROR_INTERNAL:
+    case AAUDIO_ERROR_INVALID_QUERY:
+    case AAUDIO_ERROR_UNIMPLEMENTED:
+    case AAUDIO_ERROR_UNAVAILABLE:
+    case AAUDIO_ERROR_NO_FREE_HANDLES:
+    case AAUDIO_ERROR_NO_MEMORY:
+    case AAUDIO_ERROR_TIMEOUT:
+    case AAUDIO_ERROR_NO_SERVICE:
     default:
         status = UNKNOWN_ERROR;
         break;
@@ -103,18 +253,20 @@
         result = AAUDIO_ERROR_INVALID_HANDLE;
         break;
     case DEAD_OBJECT:
-        result = AAUDIO_ERROR_DISCONNECTED;
+        result = AAUDIO_ERROR_NO_SERVICE;
         break;
     case INVALID_OPERATION:
         result = AAUDIO_ERROR_INVALID_STATE;
         break;
-    case BAD_VALUE:
-        result = AAUDIO_ERROR_UNEXPECTED_VALUE;
-        break;
+        case UNEXPECTED_NULL:
+            result = AAUDIO_ERROR_NULL;
+            break;
+        case BAD_VALUE:
+            result = AAUDIO_ERROR_UNEXPECTED_VALUE;
+            break;
     case WOULD_BLOCK:
         result = AAUDIO_ERROR_WOULD_BLOCK;
         break;
-    // TODO add more status codes
     default:
         result = AAUDIO_ERROR_INTERNAL;
         break;
diff --git a/media/libaaudio/src/utility/AAudioUtilities.h b/media/libaaudio/src/utility/AAudioUtilities.h
index 3dc501e..0078cbb 100644
--- a/media/libaaudio/src/utility/AAudioUtilities.h
+++ b/media/libaaudio/src/utility/AAudioUtilities.h
@@ -35,9 +35,120 @@
  */
 aaudio_result_t AAudioConvert_androidToAAudioResult(android::status_t status);
 
-void AAudioConvert_floatToPcm16(const float *source, int32_t numSamples, int16_t *destination);
+/**
+ * Convert an array of floats to an array of int16_t.
+ *
+ * @param source
+ * @param destination
+ * @param numSamples number of values in the array
+ * @param amplitude level between 0.0 and 1.0
+ */
+void AAudioConvert_floatToPcm16(const float *source,
+                                int16_t *destination,
+                                int32_t numSamples,
+                                float amplitude);
 
-void AAudioConvert_pcm16ToFloat(const int16_t *source, int32_t numSamples, float *destination);
+/**
+ * Convert floats to int16_t and scale by a linear ramp.
+ *
+ * The ramp stops just short of reaching amplitude2 so that the next
+ * ramp can start at amplitude2 without causing a discontinuity.
+ *
+ * @param source
+ * @param destination
+ * @param numFrames
+ * @param samplesPerFrame AKA number of channels
+ * @param amplitude1 level at start of ramp, between 0.0 and 1.0
+ * @param amplitude2 level past end of ramp, between 0.0 and 1.0
+ */
+void AAudioConvert_floatToPcm16(const float *source,
+                                int16_t *destination,
+                                int32_t numFrames,
+                                int32_t samplesPerFrame,
+                                float amplitude1,
+                                float amplitude2);
+
+/**
+ * Convert int16_t array to float array ranging from -1.0 to +1.0.
+ * @param source
+ * @param destination
+ * @param numSamples
+ */
+//void AAudioConvert_pcm16ToFloat(const int16_t *source, int32_t numSamples,
+//                                float *destination);
+
+/**
+ *
+ * Convert int16_t array to float array ranging from +/- amplitude.
+ * @param source
+ * @param destination
+ * @param numSamples
+ * @param amplitude
+ */
+void AAudioConvert_pcm16ToFloat(const int16_t *source,
+                                float *destination,
+                                int32_t numSamples,
+                                float amplitude);
+
+/**
+ * Convert floats to int16_t and scale by a linear ramp.
+ *
+ * The ramp stops just short of reaching amplitude2 so that the next
+ * ramp can start at amplitude2 without causing a discontinuity.
+ *
+ * @param source
+ * @param destination
+ * @param numFrames
+ * @param samplesPerFrame AKA number of channels
+ * @param amplitude1 level at start of ramp, between 0.0 and 1.0
+ * @param amplitude2 level at end of ramp, between 0.0 and 1.0
+ */
+void AAudioConvert_pcm16ToFloat(const int16_t *source,
+                                float *destination,
+                                int32_t numFrames,
+                                int32_t samplesPerFrame,
+                                float amplitude1,
+                                float amplitude2);
+
+/**
+ * Scale floats by a linear ramp.
+ *
+ * The ramp stops just short of reaching amplitude2 so that the next
+ * ramp can start at amplitude2 without causing a discontinuity.
+ *
+ * @param source
+ * @param destination
+ * @param numFrames
+ * @param samplesPerFrame
+ * @param amplitude1
+ * @param amplitude2
+ */
+void AAudio_linearRamp(const float *source,
+                       float *destination,
+                       int32_t numFrames,
+                       int32_t samplesPerFrame,
+                       float amplitude1,
+                       float amplitude2);
+
+/**
+ * Scale int16_t's by a linear ramp.
+ *
+ * The ramp stops just short of reaching amplitude2 so that the next
+ * ramp can start at amplitude2 without causing a discontinuity.
+ *
+ * @param source
+ * @param destination
+ * @param numFrames
+ * @param samplesPerFrame
+ * @param amplitude1
+ * @param amplitude2
+ */
+void AAudio_linearRamp(const int16_t *source,
+                       int16_t *destination,
+                       int32_t numFrames,
+                       int32_t samplesPerFrame,
+                       float amplitude1,
+                       float amplitude2);
 
 /**
  * Calculate the number of bytes and prevent numeric overflow.
diff --git a/media/libaaudio/src/utility/FixedBlockAdapter.cpp b/media/libaaudio/src/utility/FixedBlockAdapter.cpp
index f4666af..63495f0 100644
--- a/media/libaaudio/src/utility/FixedBlockAdapter.cpp
+++ b/media/libaaudio/src/utility/FixedBlockAdapter.cpp
@@ -25,7 +25,7 @@
 int32_t FixedBlockAdapter::open(int32_t bytesPerFixedBlock)
 {
     mSize = bytesPerFixedBlock;
-    mStorage = new uint8_t[bytesPerFixedBlock]; // TODO use std::nothrow
+    mStorage = new uint8_t[bytesPerFixedBlock];
     mPosition = 0;
     return 0;
 }
diff --git a/media/libaaudio/src/utility/LinearRamp.cpp b/media/libaaudio/src/utility/LinearRamp.cpp
new file mode 100644
index 0000000..1714bbf
--- /dev/null
+++ b/media/libaaudio/src/utility/LinearRamp.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LinearRamp.h"
+
+bool LinearRamp::isRamping() {
+    float target = mTarget.load();
+    if (target != mLevelTo) {
+        // Update target. Continue from previous level.
+        mLevelTo = target;
+        mRemaining = mLengthInFrames;
+        return true;
+    } else {
+        return mRemaining > 0;
+    }
+}
+
+bool LinearRamp::nextSegment(int32_t frames, float *levelFrom, float *levelTo) {
+    bool ramping = isRamping();
+    *levelFrom = mLevelFrom;
+    if (ramping) {
+        float level;
+        if (frames >= mRemaining) {
+            level = mLevelTo;
+            mRemaining = 0;
+        } else {
+            // Interpolate to a point along the full ramp.
+            level = mLevelFrom + (frames * (mLevelTo - mLevelFrom) / mRemaining);
+            mRemaining -= frames;
+        }
+        mLevelFrom = level; // for next ramp
+        *levelTo = level;
+    } else {
+        *levelTo = mLevelTo;
+    }
+    return ramping;
+}
\ No newline at end of file
diff --git a/media/libaaudio/src/utility/LinearRamp.h b/media/libaaudio/src/utility/LinearRamp.h
new file mode 100644
index 0000000..ff09dce
--- /dev/null
+++ b/media/libaaudio/src/utility/LinearRamp.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AAUDIO_LINEAR_RAMP_H
+#define AAUDIO_LINEAR_RAMP_H
+
+#include <atomic>
+#include <stdint.h>
+
+/**
+ * Generate segments along a linear ramp.
+ * The ramp target can be updated from another thread.
+ * When the target is updated, a new ramp is started from the current position.
+ *
+ * The first ramp starts at 0.0.
+ *
+ */
+class LinearRamp {
+public:
+    LinearRamp() {
+        mTarget.store(1.0f);
+    }
+
+    void setLengthInFrames(int32_t frames) {
+        mLengthInFrames = frames;
+    }
+
+    int32_t getLengthInFrames() {
+        return mLengthInFrames;
+    }
+
+    /**
+     * This may be called by another thread.
+     * @param target
+     */
+    void setTarget(float target) {
+        mTarget.store(target);
+    }
+
+    float getTarget() {
+        return mTarget.load();
+    }
+
+    /**
+     * Force the nextSegment to start from this level.
+     *
+     * WARNING: this can cause a discontinuity if called while the ramp is being used.
+     * Only call this when setting the initial ramp.
+     *
+     * @param level
+     */
+    void forceCurrent(float level) {
+        mLevelFrom = level;
+        mLevelTo = level; // forces a ramp if it does not match target
+    }
+
+    float getCurrent() {
+        return mLevelFrom;
+    }
+
+    /**
+     * Get levels for next ramp segment.
+     *
+     * @param frames number of frames in the segment
+     * @param levelFrom pointer to starting amplitude
+     * @param levelTo pointer to ending amplitude
+     * @return true if ramp is still moving towards the target
+     */
+    bool nextSegment(int32_t frames, float *levelFrom, float *levelTo);
+
+private:
+
+    bool isRamping();
+
+    std::atomic<float>   mTarget;
+
+    int32_t mLengthInFrames  = 48000 / 50; // 20 msec at 48000 Hz
+    int32_t mRemaining       = 0;
+    float   mLevelFrom       = 0.0f;
+    float   mLevelTo         = 0.0f;
+};
+
+
+#endif //AAUDIO_LINEAR_RAMP_H
diff --git a/media/libaaudio/tests/Android.mk b/media/libaaudio/tests/Android.mk
index 06c9364..01360b1 100644
--- a/media/libaaudio/tests/Android.mk
+++ b/media/libaaudio/tests/Android.mk
@@ -35,3 +35,15 @@
 LOCAL_STATIC_LIBRARIES := libaaudio
 LOCAL_MODULE := test_block_adapter
 include $(BUILD_NATIVE_TEST)
+
+include $(CLEAR_VARS)
+LOCAL_C_INCLUDES := \
+    $(call include-path-for, audio-utils) \
+    frameworks/av/media/libaaudio/include \
+    frameworks/av/media/libaaudio/src
+LOCAL_SRC_FILES:= test_linear_ramp.cpp
+LOCAL_SHARED_LIBRARIES := libaudioclient libaudioutils libbinder \
+                          libcutils liblog libmedia libutils
+LOCAL_STATIC_LIBRARIES := libaaudio
+LOCAL_MODULE := test_linear_ramp
+include $(BUILD_NATIVE_TEST)
diff --git a/media/libaaudio/tests/test_linear_ramp.cpp b/media/libaaudio/tests/test_linear_ramp.cpp
new file mode 100644
index 0000000..5c53982
--- /dev/null
+++ b/media/libaaudio/tests/test_linear_ramp.cpp
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <iostream>
+
+#include <gtest/gtest.h>
+
+#include "utility/AAudioUtilities.h"
+#include "utility/LinearRamp.h"
+
+
+TEST(test_linear_ramp, linear_ramp_segments) {
+    LinearRamp ramp;
+    const float source[4] = {1.0f, 1.0f, 1.0f, 1.0f };
+    float destination[4] = {1.0f, 1.0f, 1.0f, 1.0f };
+
+    float levelFrom = -1.0f;
+    float levelTo = -1.0f;
+    ramp.setLengthInFrames(8);
+    ramp.setTarget(8.0f);
+
+    ASSERT_EQ(8, ramp.getLengthInFrames());
+
+    bool ramping = ramp.nextSegment(4, &levelFrom, &levelTo);
+    ASSERT_EQ(1, ramping);
+    ASSERT_EQ(0.0f, levelFrom);
+    ASSERT_EQ(4.0f, levelTo);
+
+    AAudio_linearRamp(source, destination, 4, 1, levelFrom, levelTo);
+    ASSERT_EQ(0.0f, destination[0]);
+    ASSERT_EQ(1.0f, destination[1]);
+    ASSERT_EQ(2.0f, destination[2]);
+    ASSERT_EQ(3.0f, destination[3]);
+
+    ramping = ramp.nextSegment(4, &levelFrom, &levelTo);
+    ASSERT_EQ(1, ramping);
+    ASSERT_EQ(4.0f, levelFrom);
+    ASSERT_EQ(8.0f, levelTo);
+
+    AAudio_linearRamp(source, destination, 4, 1, levelFrom, levelTo);
+    ASSERT_EQ(4.0f, destination[0]);
+    ASSERT_EQ(5.0f, destination[1]);
+    ASSERT_EQ(6.0f, destination[2]);
+    ASSERT_EQ(7.0f, destination[3]);
+
+    ramping = ramp.nextSegment(4, &levelFrom, &levelTo);
+    ASSERT_EQ(0, ramping);
+    ASSERT_EQ(8.0f, levelFrom);
+    ASSERT_EQ(8.0f, levelTo);
+
+    AAudio_linearRamp(source, destination, 4, 1, levelFrom, levelTo);
+    ASSERT_EQ(8.0f, destination[0]);
+    ASSERT_EQ(8.0f, destination[1]);
+    ASSERT_EQ(8.0f, destination[2]);
+    ASSERT_EQ(8.0f, destination[3]);
+
+};
+
+
+TEST(test_linear_ramp, linear_ramp_forced) {
+    LinearRamp ramp;
+    const float source[4] = {1.0f, 1.0f, 1.0f, 1.0f };
+    float destination[4] = {1.0f, 1.0f, 1.0f, 1.0f };
+
+    float levelFrom = -1.0f;
+    float levelTo = -1.0f;
+    ramp.setLengthInFrames(4);
+    ramp.setTarget(8.0f);
+    ramp.forceCurrent(4.0f);
+    ASSERT_EQ(4.0f, ramp.getCurrent());
+
+    bool ramping = ramp.nextSegment(4, &levelFrom, &levelTo);
+    ASSERT_EQ(1, ramping);
+    ASSERT_EQ(4.0f, levelFrom);
+    ASSERT_EQ(8.0f, levelTo);
+
+    AAudio_linearRamp(source, destination, 4, 1, levelFrom, levelTo);
+    ASSERT_EQ(4.0f, destination[0]);
+    ASSERT_EQ(5.0f, destination[1]);
+    ASSERT_EQ(6.0f, destination[2]);
+    ASSERT_EQ(7.0f, destination[3]);
+
+    ramping = ramp.nextSegment(4, &levelFrom, &levelTo);
+    ASSERT_EQ(0, ramping);
+    ASSERT_EQ(8.0f, levelFrom);
+    ASSERT_EQ(8.0f, levelTo);
+
+    AAudio_linearRamp(source, destination, 4, 1, levelFrom, levelTo);
+    ASSERT_EQ(8.0f, destination[0]);
+    ASSERT_EQ(8.0f, destination[1]);
+    ASSERT_EQ(8.0f, destination[2]);
+    ASSERT_EQ(8.0f, destination[3]);
+
+};
+
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index 3a0ce5e..38d90bc 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -652,6 +652,9 @@
             get_sched_policy(0, &mPreviousSchedulingGroup);
             androidSetThreadPriority(0, ANDROID_PRIORITY_AUDIO);
         }
+
+        // Start our local VolumeHandler for restoration purposes.
+        mVolumeHandler->setStarted();
     } else {
         ALOGE("start() status %d", status);
         mState = previousState;
@@ -2254,17 +2257,20 @@
             }
         }
         // restore volume handler
-        mVolumeHandler->forall([this](const sp<VolumeShaper::Configuration> &configuration,
-                const sp<VolumeShaper::Operation> &operation) -> VolumeShaper::Status {
-            sp<VolumeShaper::Operation> operationToEnd = new VolumeShaper::Operation(*operation);
+        mVolumeHandler->forall([this](const VolumeShaper &shaper) -> VolumeShaper::Status {
+            sp<VolumeShaper::Operation> operationToEnd =
+                    new VolumeShaper::Operation(shaper.mOperation);
             // TODO: Ideally we would restore to the exact xOffset position
             // as returned by getVolumeShaperState(), but we don't have that
             // information when restoring at the client unless we periodically poll
             // the server or create shared memory state.
             //
-            // For now, we simply advance to the end of the VolumeShaper effect.
-            operationToEnd->setXOffset(1.f);
-            return mAudioTrack->applyVolumeShaper(configuration, operationToEnd);
+            // For now, we simply advance to the end of the VolumeShaper effect
+            // if it has been started.
+            if (shaper.isStarted()) {
+                operationToEnd->setNormalizedTime(1.f);
+            }
+            return mAudioTrack->applyVolumeShaper(shaper.mConfiguration, operationToEnd);
         });
 
         if (mState == STATE_ACTIVE) {
@@ -2334,19 +2340,36 @@
     AutoMutex lock(mLock);
     mVolumeHandler->setIdIfNecessary(configuration);
     VolumeShaper::Status status = mAudioTrack->applyVolumeShaper(configuration, operation);
+
+    if (status == DEAD_OBJECT) {
+        if (restoreTrack_l("applyVolumeShaper") == OK) {
+            status = mAudioTrack->applyVolumeShaper(configuration, operation);
+        }
+    }
     if (status >= 0) {
         // save VolumeShaper for restore
         mVolumeHandler->applyVolumeShaper(configuration, operation);
+        if (mState == STATE_ACTIVE || mState == STATE_STOPPING) {
+            mVolumeHandler->setStarted();
+        }
+    } else {
+        // warn only if not an expected restore failure.
+        ALOGW_IF(!((isOffloadedOrDirect_l() || mDoNotReconnect) && status == DEAD_OBJECT),
+                "applyVolumeShaper failed: %d", status);
     }
     return status;
 }
 
 sp<VolumeShaper::State> AudioTrack::getVolumeShaperState(int id)
 {
-    // TODO: To properly restore the AudioTrack
-    // we will need to save the last state in AudioTrackShared.
     AutoMutex lock(mLock);
-    return mAudioTrack->getVolumeShaperState(id);
+    sp<VolumeShaper::State> state = mAudioTrack->getVolumeShaperState(id);
+    if (state.get() == nullptr && (mCblk->mFlags & CBLK_INVALID) != 0) {
+        if (restoreTrack_l("getVolumeShaperState") == OK) {
+            state = mAudioTrack->getVolumeShaperState(id);
+        }
+    }
+    return state;
 }
 
 status_t AudioTrack::getTimestamp(ExtendedTimestamp *timestamp)
diff --git a/media/libaudioclient/include/media/AudioTrack.h b/media/libaudioclient/include/media/AudioTrack.h
index 16eb225..a4c8d53 100644
--- a/media/libaudioclient/include/media/AudioTrack.h
+++ b/media/libaudioclient/include/media/AudioTrack.h
@@ -874,6 +874,10 @@
      */
             bool hasStarted(); // not const
 
+            bool isPlaying() {
+                AutoMutex lock(mLock);
+                return mState == STATE_ACTIVE || mState == STATE_STOPPING;
+            }
 protected:
     /* copying audio tracks is not allowed */
                         AudioTrack(const AudioTrack& other);
diff --git a/media/libaudiohal/Android.mk b/media/libaudiohal/Android.mk
index 68a1f7b..e592169 100644
--- a/media/libaudiohal/Android.mk
+++ b/media/libaudiohal/Android.mk
@@ -5,30 +5,37 @@
 LOCAL_SHARED_LIBRARIES := \
     libcutils   \
     liblog      \
-    libutils
+    libutils    \
+    libhardware
+
+LOCAL_SRC_FILES := \
+    DeviceHalLocal.cpp          \
+    DevicesFactoryHalHybrid.cpp \
+    DevicesFactoryHalLocal.cpp  \
+    StreamHalLocal.cpp
+
+LOCAL_CFLAGS := -Wall -Werror
 
 ifeq ($(USE_LEGACY_LOCAL_AUDIO_HAL), true)
 
 # Use audiohal directly w/o hwbinder middleware.
 # This is for performance comparison and debugging only.
 
-LOCAL_SRC_FILES := \
-    DeviceHalLocal.cpp          \
-    DevicesFactoryHalLocal.cpp  \
+LOCAL_SRC_FILES += \
     EffectBufferHalLocal.cpp    \
-    EffectHalLocal.cpp          \
     EffectsFactoryHalLocal.cpp  \
-    StreamHalLocal.cpp
+    EffectHalLocal.cpp
 
 LOCAL_SHARED_LIBRARIES += \
-    libeffects  \
-    libhardware
+    libeffects
+
+LOCAL_CFLAGS += -DUSE_LEGACY_LOCAL_AUDIO_HAL
 
 else  # if !USE_LEGACY_LOCAL_AUDIO_HAL
 
-LOCAL_SRC_FILES := \
+LOCAL_SRC_FILES += \
     ConversionHelperHidl.cpp   \
-    HalDeathHandlerHidl.cpp   \
+    HalDeathHandlerHidl.cpp    \
     DeviceHalHidl.cpp          \
     DevicesFactoryHalHidl.cpp  \
     EffectBufferHalHidl.cpp    \
@@ -60,6 +67,4 @@
 
 LOCAL_MODULE := libaudiohal
 
-LOCAL_CFLAGS := -Wall -Werror
-
 include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libaudiohal/DevicesFactoryHalHidl.cpp b/media/libaudiohal/DevicesFactoryHalHidl.cpp
index fc2645e..31da263 100644
--- a/media/libaudiohal/DevicesFactoryHalHidl.cpp
+++ b/media/libaudiohal/DevicesFactoryHalHidl.cpp
@@ -33,11 +33,6 @@
 
 namespace android {
 
-// static
-sp<DevicesFactoryHalInterface> DevicesFactoryHalInterface::create() {
-    return new DevicesFactoryHalHidl();
-}
-
 DevicesFactoryHalHidl::DevicesFactoryHalHidl() {
     mDevicesFactory = IDevicesFactory::getService();
     if (mDevicesFactory != 0) {
diff --git a/media/libaudiohal/DevicesFactoryHalHidl.h b/media/libaudiohal/DevicesFactoryHalHidl.h
index a26dec1..e2f1ad1 100644
--- a/media/libaudiohal/DevicesFactoryHalHidl.h
+++ b/media/libaudiohal/DevicesFactoryHalHidl.h
@@ -36,7 +36,7 @@
     virtual status_t openDevice(const char *name, sp<DeviceHalInterface> *device);
 
   private:
-    friend class DevicesFactoryHalInterface;
+    friend class DevicesFactoryHalHybrid;
 
     sp<IDevicesFactory> mDevicesFactory;
 
diff --git a/media/libaudiohal/DevicesFactoryHalHybrid.cpp b/media/libaudiohal/DevicesFactoryHalHybrid.cpp
new file mode 100644
index 0000000..454b03b
--- /dev/null
+++ b/media/libaudiohal/DevicesFactoryHalHybrid.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "DevicesFactoryHalHybrid"
+//#define LOG_NDEBUG 0
+
+#include "DevicesFactoryHalHybrid.h"
+#include "DevicesFactoryHalLocal.h"
+#ifndef USE_LEGACY_LOCAL_AUDIO_HAL
+#include "DevicesFactoryHalHidl.h"
+#endif
+
+namespace android {
+
+// static
+sp<DevicesFactoryHalInterface> DevicesFactoryHalInterface::create() {
+    return new DevicesFactoryHalHybrid();
+}
+
+DevicesFactoryHalHybrid::DevicesFactoryHalHybrid()
+        : mLocalFactory(new DevicesFactoryHalLocal()),
+          mHidlFactory(
+#ifdef USE_LEGACY_LOCAL_AUDIO_HAL
+                  nullptr
+#else
+                  new DevicesFactoryHalHidl()
+#endif
+                       ) {
+}
+
+DevicesFactoryHalHybrid::~DevicesFactoryHalHybrid() {
+}
+
+status_t DevicesFactoryHalHybrid::openDevice(const char *name, sp<DeviceHalInterface> *device) {
+    if (mHidlFactory != 0 && strcmp(AUDIO_HARDWARE_MODULE_ID_A2DP, name) != 0) {
+        return mHidlFactory->openDevice(name, device);
+    }
+    return mLocalFactory->openDevice(name, device);
+}
+
+} // namespace android
diff --git a/media/libaudiohal/DevicesFactoryHalHybrid.h b/media/libaudiohal/DevicesFactoryHalHybrid.h
new file mode 100644
index 0000000..abd57d6
--- /dev/null
+++ b/media/libaudiohal/DevicesFactoryHalHybrid.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HYBRID_H
+#define ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HYBRID_H
+
+#include <media/audiohal/DevicesFactoryHalInterface.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+class DevicesFactoryHalHybrid : public DevicesFactoryHalInterface
+{
+  public:
+    // Opens a device with the specified name. To close the device, it is
+    // necessary to release references to the returned object.
+    virtual status_t openDevice(const char *name, sp<DeviceHalInterface> *device);
+
+  private:
+    friend class DevicesFactoryHalInterface;
+
+    // Can not be constructed directly by clients.
+    DevicesFactoryHalHybrid();
+
+    virtual ~DevicesFactoryHalHybrid();
+
+    sp<DevicesFactoryHalInterface> mLocalFactory;
+    sp<DevicesFactoryHalInterface> mHidlFactory;
+};
+
+} // namespace android
+
+#endif // ANDROID_HARDWARE_DEVICES_FACTORY_HAL_HYBRID_H
diff --git a/media/libaudiohal/DevicesFactoryHalLocal.cpp b/media/libaudiohal/DevicesFactoryHalLocal.cpp
index cd9a9e7..13a9acd 100644
--- a/media/libaudiohal/DevicesFactoryHalLocal.cpp
+++ b/media/libaudiohal/DevicesFactoryHalLocal.cpp
@@ -27,11 +27,6 @@
 
 namespace android {
 
-// static
-sp<DevicesFactoryHalInterface> DevicesFactoryHalInterface::create() {
-    return new DevicesFactoryHalLocal();
-}
-
 static status_t load_audio_interface(const char *if_name, audio_hw_device_t **dev)
 {
     const hw_module_t *mod;
diff --git a/media/libaudiohal/DevicesFactoryHalLocal.h b/media/libaudiohal/DevicesFactoryHalLocal.h
index 58ce4ff..b9d18ab 100644
--- a/media/libaudiohal/DevicesFactoryHalLocal.h
+++ b/media/libaudiohal/DevicesFactoryHalLocal.h
@@ -33,7 +33,7 @@
     virtual status_t openDevice(const char *name, sp<DeviceHalInterface> *device);
 
   private:
-    friend class DevicesFactoryHalInterface;
+    friend class DevicesFactoryHalHybrid;
 
     // Can not be constructed directly by clients.
     DevicesFactoryHalLocal() {}
diff --git a/media/libaudiohal/EffectBufferHalHidl.cpp b/media/libaudiohal/EffectBufferHalHidl.cpp
index d6a41a2..8b5201b 100644
--- a/media/libaudiohal/EffectBufferHalHidl.cpp
+++ b/media/libaudiohal/EffectBufferHalHidl.cpp
@@ -47,7 +47,7 @@
 status_t EffectBufferHalInterface::mirror(
         void* external, size_t size, sp<EffectBufferHalInterface>* buffer) {
     sp<EffectBufferHalInterface> tempBuffer = new EffectBufferHalHidl(size);
-    status_t result = reinterpret_cast<EffectBufferHalHidl*>(tempBuffer.get())->init();
+    status_t result = static_cast<EffectBufferHalHidl*>(tempBuffer.get())->init();
     if (result == OK) {
         tempBuffer->setExternalData(external);
         *buffer = tempBuffer;
@@ -56,7 +56,8 @@
 }
 
 EffectBufferHalHidl::EffectBufferHalHidl(size_t size)
-        : mBufferSize(size), mExternalData(nullptr), mAudioBuffer{0, {nullptr}} {
+        : mBufferSize(size), mFrameCountChanged(false),
+          mExternalData(nullptr), mAudioBuffer{0, {nullptr}} {
     mHidlBuffer.id = makeUniqueId();
     mHidlBuffer.frameCount = 0;
 }
@@ -107,6 +108,13 @@
 void EffectBufferHalHidl::setFrameCount(size_t frameCount) {
     mHidlBuffer.frameCount = frameCount;
     mAudioBuffer.frameCount = frameCount;
+    mFrameCountChanged = true;
+}
+
+bool EffectBufferHalHidl::checkFrameCountChange() {
+    bool result = mFrameCountChanged;
+    mFrameCountChanged = false;
+    return result;
 }
 
 void EffectBufferHalHidl::setExternalData(void* external) {
diff --git a/media/libaudiohal/EffectBufferHalHidl.h b/media/libaudiohal/EffectBufferHalHidl.h
index 6e9fd0b..66a81c2 100644
--- a/media/libaudiohal/EffectBufferHalHidl.h
+++ b/media/libaudiohal/EffectBufferHalHidl.h
@@ -37,6 +37,7 @@
 
     virtual void setExternalData(void* external);
     virtual void setFrameCount(size_t frameCount);
+    virtual bool checkFrameCountChange();
 
     virtual void update();
     virtual void commit();
@@ -51,6 +52,7 @@
     static uint64_t makeUniqueId();
 
     const size_t mBufferSize;
+    bool mFrameCountChanged;
     void* mExternalData;
     AudioBuffer mHidlBuffer;
     sp<IMemory> mMemory;
diff --git a/media/libaudiohal/EffectBufferHalLocal.cpp b/media/libaudiohal/EffectBufferHalLocal.cpp
index 9fe2c7b..7951c8e 100644
--- a/media/libaudiohal/EffectBufferHalLocal.cpp
+++ b/media/libaudiohal/EffectBufferHalLocal.cpp
@@ -39,13 +39,13 @@
 
 EffectBufferHalLocal::EffectBufferHalLocal(size_t size)
         : mOwnBuffer(new uint8_t[size]),
-          mBufferSize(size),
+          mBufferSize(size), mFrameCountChanged(false),
           mAudioBuffer{0, {mOwnBuffer.get()}} {
 }
 
 EffectBufferHalLocal::EffectBufferHalLocal(void* external, size_t size)
         : mOwnBuffer(nullptr),
-          mBufferSize(size),
+          mBufferSize(size), mFrameCountChanged(false),
           mAudioBuffer{0, {external}} {
 }
 
@@ -62,6 +62,7 @@
 
 void EffectBufferHalLocal::setFrameCount(size_t frameCount) {
     mAudioBuffer.frameCount = frameCount;
+    mFrameCountChanged = true;
 }
 
 void EffectBufferHalLocal::setExternalData(void* external) {
@@ -69,6 +70,12 @@
     mAudioBuffer.raw = external;
 }
 
+bool EffectBufferHalLocal::checkFrameCountChange() {
+    bool result = mFrameCountChanged;
+    mFrameCountChanged = false;
+    return result;
+}
+
 void EffectBufferHalLocal::update() {
 }
 
diff --git a/media/libaudiohal/EffectBufferHalLocal.h b/media/libaudiohal/EffectBufferHalLocal.h
index 202d878..d2b624b 100644
--- a/media/libaudiohal/EffectBufferHalLocal.h
+++ b/media/libaudiohal/EffectBufferHalLocal.h
@@ -32,6 +32,7 @@
 
     virtual void setExternalData(void* external);
     virtual void setFrameCount(size_t frameCount);
+    virtual bool checkFrameCountChange();
 
     virtual void update();
     virtual void commit();
@@ -43,6 +44,7 @@
 
     std::unique_ptr<uint8_t[]> mOwnBuffer;
     const size_t mBufferSize;
+    bool mFrameCountChanged;
     audio_buffer_t mAudioBuffer;
 
     // Can not be constructed directly by clients.
diff --git a/media/libaudiohal/EffectHalHidl.cpp b/media/libaudiohal/EffectHalHidl.cpp
index 0babfda..b49b975 100644
--- a/media/libaudiohal/EffectHalHidl.cpp
+++ b/media/libaudiohal/EffectHalHidl.cpp
@@ -168,13 +168,20 @@
     return OK;
 }
 
+bool EffectHalHidl::needToResetBuffers() {
+    if (mBuffersChanged) return true;
+    bool inBufferFrameCountUpdated = mInBuffer->checkFrameCountChange();
+    bool outBufferFrameCountUpdated = mOutBuffer->checkFrameCountChange();
+    return inBufferFrameCountUpdated || outBufferFrameCountUpdated;
+}
+
 status_t EffectHalHidl::processImpl(uint32_t mqFlag) {
     if (mEffect == 0 || mInBuffer == 0 || mOutBuffer == 0) return NO_INIT;
     status_t status;
     if (!mStatusMQ && (status = prepareForProcessing()) != OK) {
         return status;
     }
-    if (mBuffersChanged && (status = setProcessBuffers()) != OK) {
+    if (needToResetBuffers() && (status = setProcessBuffers()) != OK) {
         return status;
     }
     // The data is already in the buffers, just need to flush it and wake up the server side.
@@ -202,8 +209,8 @@
 
 status_t EffectHalHidl::setProcessBuffers() {
     Return<Result> ret = mEffect->setProcessBuffers(
-            reinterpret_cast<EffectBufferHalHidl*>(mInBuffer.get())->hidlBuffer(),
-            reinterpret_cast<EffectBufferHalHidl*>(mOutBuffer.get())->hidlBuffer());
+            static_cast<EffectBufferHalHidl*>(mInBuffer.get())->hidlBuffer(),
+            static_cast<EffectBufferHalHidl*>(mOutBuffer.get())->hidlBuffer());
     if (ret.isOk() && ret == Result::OK) {
         mBuffersChanged = false;
         return OK;
diff --git a/media/libaudiohal/EffectHalHidl.h b/media/libaudiohal/EffectHalHidl.h
index c8db36f..6ffdaf1 100644
--- a/media/libaudiohal/EffectHalHidl.h
+++ b/media/libaudiohal/EffectHalHidl.h
@@ -58,6 +58,9 @@
     // Free resources on the remote side.
     virtual status_t close();
 
+    // Whether it's a local implementation.
+    virtual bool isLocal() const { return false; }
+
     uint64_t effectId() const { return mEffectId; }
 
     static void effectDescriptorToHal(
@@ -92,6 +95,7 @@
 
     status_t getConfigImpl(uint32_t cmdCode, uint32_t *replySize, void *pReplyData);
     status_t prepareForProcessing();
+    bool needToResetBuffers();
     status_t processImpl(uint32_t mqFlag);
     status_t setConfigImpl(
             uint32_t cmdCode, uint32_t cmdSize, void *pCmdData,
diff --git a/media/libaudiohal/EffectHalLocal.h b/media/libaudiohal/EffectHalLocal.h
index b499462..693fb50 100644
--- a/media/libaudiohal/EffectHalLocal.h
+++ b/media/libaudiohal/EffectHalLocal.h
@@ -48,6 +48,9 @@
     // Free resources on the remote side.
     virtual status_t close();
 
+    // Whether it's a local implementation.
+    virtual bool isLocal() const { return true; }
+
     effect_handle_t handle() const { return mHandle; }
 
   private:
diff --git a/media/libaudiohal/StreamHalLocal.cpp b/media/libaudiohal/StreamHalLocal.cpp
index b25e518..05800a0 100644
--- a/media/libaudiohal/StreamHalLocal.cpp
+++ b/media/libaudiohal/StreamHalLocal.cpp
@@ -79,11 +79,13 @@
 }
 
 status_t StreamHalLocal::addEffect(sp<EffectHalInterface> effect) {
+    LOG_ALWAYS_FATAL_IF(!effect->isLocal(), "Only local effects can be added for a local stream");
     return mStream->add_audio_effect(mStream,
             static_cast<EffectHalLocal*>(effect.get())->handle());
 }
 
 status_t StreamHalLocal::removeEffect(sp<EffectHalInterface> effect) {
+    LOG_ALWAYS_FATAL_IF(!effect->isLocal(), "Only local effects can be removed for a local stream");
     return mStream->remove_audio_effect(mStream,
             static_cast<EffectHalLocal*>(effect.get())->handle());
 }
@@ -162,7 +164,7 @@
     // correctly the case when the callback is invoked while StreamOutHalLocal's destructor is
     // already running, because the destructor is invoked after the refcount has been atomically
     // decremented.
-    wp<StreamOutHalLocal> weakSelf(reinterpret_cast<StreamOutHalLocal*>(cookie));
+    wp<StreamOutHalLocal> weakSelf(static_cast<StreamOutHalLocal*>(cookie));
     sp<StreamOutHalLocal> self = weakSelf.promote();
     if (self == 0) return 0;
     sp<StreamOutHalInterfaceCallback> callback = self->mCallback.promote();
diff --git a/media/libaudiohal/include/EffectBufferHalInterface.h b/media/libaudiohal/include/EffectBufferHalInterface.h
index 6fa7940..e862f6e 100644
--- a/media/libaudiohal/include/EffectBufferHalInterface.h
+++ b/media/libaudiohal/include/EffectBufferHalInterface.h
@@ -39,6 +39,8 @@
 
     virtual void setExternalData(void* external) = 0;
     virtual void setFrameCount(size_t frameCount) = 0;
+    virtual bool checkFrameCountChange() = 0;  // returns whether frame count has been updated
+                                               // since the last call to this method
 
     virtual void update() = 0;  // copies data from the external buffer, noop for allocated buffers
     virtual void commit() = 0;  // copies data to the external buffer, noop for allocated buffers
diff --git a/media/libaudiohal/include/EffectHalInterface.h b/media/libaudiohal/include/EffectHalInterface.h
index 7f9a6fd..92622aa 100644
--- a/media/libaudiohal/include/EffectHalInterface.h
+++ b/media/libaudiohal/include/EffectHalInterface.h
@@ -52,6 +52,9 @@
     // Free resources on the remote side.
     virtual status_t close() = 0;
 
+    // Whether it's a local implementation.
+    virtual bool isLocal() const = 0;
+
   protected:
     // Subclasses can not be constructed directly by clients.
     EffectHalInterface() {}
diff --git a/media/libaudioprocessing/AudioMixer.cpp b/media/libaudioprocessing/AudioMixer.cpp
index a7d9f0f..05b726a 100644
--- a/media/libaudioprocessing/AudioMixer.cpp
+++ b/media/libaudioprocessing/AudioMixer.cpp
@@ -302,6 +302,12 @@
 void AudioMixer::track_t::unprepareForDownmix() {
     ALOGV("AudioMixer::unprepareForDownmix(%p)", this);
 
+    if (mPostDownmixReformatBufferProvider != nullptr) {
+        // release any buffers held by the mPostDownmixReformatBufferProvider
+        // before deallocating the downmixerBufferProvider.
+        mPostDownmixReformatBufferProvider->reset();
+    }
+
     mDownmixRequiresFormat = AUDIO_FORMAT_INVALID;
     if (downmixerBufferProvider != NULL) {
         // this track had previously been configured with a downmixer, delete it
diff --git a/media/libmedia/aidl/android/IGraphicBufferSource.aidl b/media/libmedia/aidl/android/IGraphicBufferSource.aidl
index 325c631..f3c7abc 100644
--- a/media/libmedia/aidl/android/IGraphicBufferSource.aidl
+++ b/media/libmedia/aidl/android/IGraphicBufferSource.aidl
@@ -28,10 +28,10 @@
     void setSuspend(boolean suspend, long suspendTimeUs);
     void setRepeatPreviousFrameDelayUs(long repeatAfterUs);
     void setMaxFps(float maxFps);
-    void setTimeLapseConfig(long timePerFrameUs, long timePerCaptureUs);
+    void setTimeLapseConfig(double fps, double captureFps);
     void setStartTimeUs(long startTimeUs);
     void setStopTimeUs(long stopTimeUs);
     void setColorAspects(int aspects);
     void setTimeOffsetUs(long timeOffsetsUs);
     void signalEndOfInputStream();
-}
\ No newline at end of file
+}
diff --git a/media/libmedia/include/media/IMediaExtractor.h b/media/libmedia/include/media/IMediaExtractor.h
index cf1b9fb..ab40f53 100644
--- a/media/libmedia/include/media/IMediaExtractor.h
+++ b/media/libmedia/include/media/IMediaExtractor.h
@@ -34,6 +34,9 @@
     DECLARE_META_INTERFACE(MediaExtractor);
 
     virtual size_t countTracks() = 0;
+    // This function could return NULL IMediaSource even when index is within the
+    // track count returned by countTracks, since it's possible the track is malformed
+    // and it's not detected during countTracks call.
     virtual sp<IMediaSource> getTrack(size_t index) = 0;
 
     enum GetTrackMetaDataFlags {
diff --git a/media/libmedia/omx/1.0/WGraphicBufferSource.cpp b/media/libmedia/omx/1.0/WGraphicBufferSource.cpp
index b4e2975..4c543fa 100644
--- a/media/libmedia/omx/1.0/WGraphicBufferSource.cpp
+++ b/media/libmedia/omx/1.0/WGraphicBufferSource.cpp
@@ -53,9 +53,8 @@
 }
 
 BnStatus LWGraphicBufferSource::setTimeLapseConfig(
-        int64_t timePerFrameUs, int64_t timePerCaptureUs) {
-    return toBinderStatus(mBase->setTimeLapseConfig(
-            timePerFrameUs, timePerCaptureUs));
+        double fps, double captureFps) {
+    return toBinderStatus(mBase->setTimeLapseConfig(fps, captureFps));
 }
 
 BnStatus LWGraphicBufferSource::setStartTimeUs(
diff --git a/media/libmediametrics/MediaAnalyticsItem.cpp b/media/libmediametrics/MediaAnalyticsItem.cpp
index 229e747..43881b3 100644
--- a/media/libmediametrics/MediaAnalyticsItem.cpp
+++ b/media/libmediametrics/MediaAnalyticsItem.cpp
@@ -34,6 +34,7 @@
 #include <binder/IServiceManager.h>
 #include <media/IMediaAnalyticsService.h>
 #include <media/MediaAnalyticsItem.h>
+#include <private/android_filesystem_config.h>
 
 namespace android {
 
@@ -776,6 +777,24 @@
         return NULL;
     }
 
+    // completely skip logging from certain UIDs. We do this here
+    // to avoid the multi-second timeouts while we learn that
+    // sepolicy will not let us find the service.
+    // We do this only for a select set of UIDs
+    // The sepolicy protection is still in place, we just want a faster
+    // response from this specific, small set of uids.
+    {
+        uid_t uid = getuid();
+        switch (uid) {
+            case AID_RADIO:     // telephony subsystem, RIL
+                return NULL;
+                break;
+            default:
+                // let sepolicy deny access if appropriate
+                break;
+        }
+    }
+
     {
         Mutex::Autolock _l(sInitMutex);
         const char *badness = "";
diff --git a/media/libmediametrics/include/MediaAnalyticsItem.h b/media/libmediametrics/include/MediaAnalyticsItem.h
index f050e7f..dc501b2 100644
--- a/media/libmediametrics/include/MediaAnalyticsItem.h
+++ b/media/libmediametrics/include/MediaAnalyticsItem.h
@@ -41,6 +41,7 @@
     friend class MediaAnalyticsService;
     friend class IMediaAnalyticsService;
     friend class MediaMetricsJNI;
+    friend class MetricsSummarizer;
 
     public:
 
@@ -231,7 +232,6 @@
         size_t mPropCount;
         size_t mPropSize;
         Prop *mProps;
-
 };
 
 } // namespace android
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index b082654..18fd857 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -275,20 +275,6 @@
     ALOGV("MediaPlayerService created");
     mNextConnId = 1;
 
-    mBatteryAudio.refCount = 0;
-    for (int i = 0; i < NUM_AUDIO_DEVICES; i++) {
-        mBatteryAudio.deviceOn[i] = 0;
-        mBatteryAudio.lastTime[i] = 0;
-        mBatteryAudio.totalTime[i] = 0;
-    }
-    // speaker is on by default
-    mBatteryAudio.deviceOn[SPEAKER] = 1;
-
-    // reset battery stats
-    // if the mediaserver has crashed, battery stats could be left
-    // in bad state, reset the state upon service start.
-    BatteryNotifier::getInstance().noteResetVideo();
-
     MediaPlayerFactory::registerBuiltinFactories();
 }
 
@@ -2029,12 +2015,23 @@
     ALOGV("setVolume");
     t->setVolume(mLeftVolume, mRightVolume);
 
-    // Dispatch any queued VolumeShapers when the track was not open.
-    mVolumeHandler->forall([&t](const sp<VolumeShaper::Configuration> &configuration,
-            const sp<VolumeShaper::Operation> &operation) -> VolumeShaper::Status {
-        return t->applyVolumeShaper(configuration, operation);
+    // Restore VolumeShapers for the MediaPlayer in case the track was recreated
+    // due to an output sink error (e.g. offload to non-offload switch).
+    mVolumeHandler->forall([&t](const VolumeShaper &shaper) -> VolumeShaper::Status {
+        sp<VolumeShaper::Operation> operationToEnd =
+                new VolumeShaper::Operation(shaper.mOperation);
+        // TODO: Ideally we would restore to the exact xOffset position
+        // as returned by getVolumeShaperState(), but we don't have that
+        // information when restoring at the client unless we periodically poll
+        // the server or create shared memory state.
+        //
+        // For now, we simply advance to the end of the VolumeShaper effect
+        // if it has been started.
+        if (shaper.isStarted()) {
+            operationToEnd->setNormalizedTime(1.f);
+        }
+        return t->applyVolumeShaper(shaper.mConfiguration, operationToEnd);
     });
-    mVolumeHandler->reset(); // After dispatching, clear VolumeShaper queue.
 
     mSampleRateHz = sampleRate;
     mFlags = flags;
@@ -2075,7 +2072,11 @@
     if (mTrack != 0) {
         mTrack->setVolume(mLeftVolume, mRightVolume);
         mTrack->setAuxEffectSendLevel(mSendLevel);
-        return mTrack->start();
+        status_t status = mTrack->start();
+        if (status == NO_ERROR) {
+            mVolumeHandler->setStarted();
+        }
+        return status;
     }
     return NO_INIT;
 }
@@ -2279,13 +2280,21 @@
     Mutex::Autolock lock(mLock);
     ALOGV("AudioOutput::applyVolumeShaper");
 
-    // We take ownership of the VolumeShaper if set before the track is created.
     mVolumeHandler->setIdIfNecessary(configuration);
+
+    VolumeShaper::Status status;
     if (mTrack != 0) {
-        return mTrack->applyVolumeShaper(configuration, operation);
+        status = mTrack->applyVolumeShaper(configuration, operation);
+        if (status >= 0) {
+            (void)mVolumeHandler->applyVolumeShaper(configuration, operation);
+            if (mTrack->isPlaying()) { // match local AudioTrack to properly restore.
+                mVolumeHandler->setStarted();
+            }
+        }
     } else {
-        return mVolumeHandler->applyVolumeShaper(configuration, operation);
+        status = mVolumeHandler->applyVolumeShaper(configuration, operation);
     }
+    return status;
 }
 
 sp<VolumeShaper::State> MediaPlayerService::AudioOutput::getVolumeShaperState(int id)
@@ -2463,7 +2472,31 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-void MediaPlayerService::addBatteryData(uint32_t params)
+void MediaPlayerService::addBatteryData(uint32_t params) {
+    mBatteryTracker.addBatteryData(params);
+}
+
+status_t MediaPlayerService::pullBatteryData(Parcel* reply) {
+    return mBatteryTracker.pullBatteryData(reply);
+}
+
+MediaPlayerService::BatteryTracker::BatteryTracker() {
+    mBatteryAudio.refCount = 0;
+    for (int i = 0; i < NUM_AUDIO_DEVICES; i++) {
+        mBatteryAudio.deviceOn[i] = 0;
+        mBatteryAudio.lastTime[i] = 0;
+        mBatteryAudio.totalTime[i] = 0;
+    }
+    // speaker is on by default
+    mBatteryAudio.deviceOn[SPEAKER] = 1;
+
+    // reset battery stats
+    // if the mediaserver has crashed, battery stats could be left
+    // in bad state, reset the state upon service start.
+    BatteryNotifier::getInstance().noteResetVideo();
+}
+
+void MediaPlayerService::BatteryTracker::addBatteryData(uint32_t params)
 {
     Mutex::Autolock lock(mLock);
 
@@ -2603,7 +2636,7 @@
     }
 }
 
-status_t MediaPlayerService::pullBatteryData(Parcel* reply) {
+status_t MediaPlayerService::BatteryTracker::pullBatteryData(Parcel* reply) {
     Mutex::Autolock lock(mLock);
 
     // audio output devices usage
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index 009fe73..06b9cad 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -246,51 +246,62 @@
         CAMERA_PROCESS_DEATH = 4
     };
 
-    // For battery usage tracking purpose
-    struct BatteryUsageInfo {
-        // how many streams are being played by one UID
-        int     refCount;
-        // a temp variable to store the duration(ms) of audio codecs
-        // when we start a audio codec, we minus the system time from audioLastTime
-        // when we pause it, we add the system time back to the audioLastTime
-        // so after the pause, audioLastTime = pause time - start time
-        // if multiple audio streams are played (or recorded), then audioLastTime
-        // = the total playing time of all the streams
-        int32_t audioLastTime;
-        // when all the audio streams are being paused, we assign audioLastTime to
-        // this variable, so this value could be provided to the battery app
-        // in the next pullBatteryData call
-        int32_t audioTotalTime;
-
-        int32_t videoLastTime;
-        int32_t videoTotalTime;
-    };
-    KeyedVector<int, BatteryUsageInfo>    mBatteryData;
-
-    enum {
-        SPEAKER,
-        OTHER_AUDIO_DEVICE,
-        SPEAKER_AND_OTHER,
-        NUM_AUDIO_DEVICES
-    };
-
-    struct BatteryAudioFlingerUsageInfo {
-        int refCount; // how many audio streams are being played
-        int deviceOn[NUM_AUDIO_DEVICES]; // whether the device is currently used
-        int32_t lastTime[NUM_AUDIO_DEVICES]; // in ms
-        // totalTime[]: total time of audio output devices usage
-        int32_t totalTime[NUM_AUDIO_DEVICES]; // in ms
-    };
-
-    // This varialble is used to record the usage of audio output device
-    // for battery app
-    BatteryAudioFlingerUsageInfo mBatteryAudio;
-
     // Collect info of the codec usage from media player and media recorder
     virtual void                addBatteryData(uint32_t params);
     // API for the Battery app to pull the data of codecs usage
     virtual status_t            pullBatteryData(Parcel* reply);
 private:
+    struct BatteryTracker {
+        BatteryTracker();
+        // Collect info of the codec usage from media player and media recorder
+        void addBatteryData(uint32_t params);
+        // API for the Battery app to pull the data of codecs usage
+        status_t pullBatteryData(Parcel* reply);
+
+    private:
+        // For battery usage tracking purpose
+        struct BatteryUsageInfo {
+            // how many streams are being played by one UID
+            int     refCount;
+            // a temp variable to store the duration(ms) of audio codecs
+            // when we start a audio codec, we minus the system time from audioLastTime
+            // when we pause it, we add the system time back to the audioLastTime
+            // so after the pause, audioLastTime = pause time - start time
+            // if multiple audio streams are played (or recorded), then audioLastTime
+            // = the total playing time of all the streams
+            int32_t audioLastTime;
+            // when all the audio streams are being paused, we assign audioLastTime to
+            // this variable, so this value could be provided to the battery app
+            // in the next pullBatteryData call
+            int32_t audioTotalTime;
+
+            int32_t videoLastTime;
+            int32_t videoTotalTime;
+        };
+        KeyedVector<int, BatteryUsageInfo>    mBatteryData;
+
+        enum {
+            SPEAKER,
+            OTHER_AUDIO_DEVICE,
+            SPEAKER_AND_OTHER,
+            NUM_AUDIO_DEVICES
+        };
+
+        struct BatteryAudioFlingerUsageInfo {
+            int refCount; // how many audio streams are being played
+            int deviceOn[NUM_AUDIO_DEVICES]; // whether the device is currently used
+            int32_t lastTime[NUM_AUDIO_DEVICES]; // in ms
+            // totalTime[]: total time of audio output devices usage
+            int32_t totalTime[NUM_AUDIO_DEVICES]; // in ms
+        };
+
+        // This varialble is used to record the usage of audio output device
+        // for battery app
+        BatteryAudioFlingerUsageInfo mBatteryAudio;
+
+        mutable Mutex mLock;
+    };
+    BatteryTracker mBatteryTracker;
 
     class Client : public BnMediaPlayer {
         // IMediaPlayer interface
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 3b7c61b..e1d762f 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -163,7 +163,7 @@
     // TBD mTrackEveryTimeDurationUs = 0;
     mAnalyticsItem->setInt32(kRecorderCaptureFpsEnable, mCaptureFpsEnable);
     mAnalyticsItem->setDouble(kRecorderCaptureFps, mCaptureFps);
-    // TBD mTimeBetweenCaptureUs = -1;
+    // TBD mCaptureFps = -1.0;
     // TBD mCameraSourceTimeLapse = NULL;
     // TBD mMetaDataStoredInVideoBuffers = kMetadataBufferTypeInvalid;
     // TBD mEncoderProfiles = MediaProfiles::getInstance();
@@ -709,18 +709,11 @@
 status_t StagefrightRecorder::setParamCaptureFps(double fps) {
     ALOGV("setParamCaptureFps: %.2f", fps);
 
-    // FPS value is from Java layer where double follows IEEE-754, which is not
-    // necessarily true for double here.
-    constexpr double kIeee754Epsilon = 2.220446049250313e-16;
-    constexpr double kEpsilon = std::max(std::numeric_limits<double>::epsilon(), kIeee754Epsilon);
-    // Not allowing fps less than 1 frame / day minus epsilon.
-    if (fps < 1.0 / 86400 - kEpsilon) {
-        ALOGE("fps (%lf) is out of range (>= 1 frame / day)", fps);
+    if (!(fps >= 1.0 / 86400)) {
+        ALOGE("FPS is too small");
         return BAD_VALUE;
     }
-
     mCaptureFps = fps;
-    mTimeBetweenCaptureUs = std::llround(1e6 / fps);
     return OK;
 }
 
@@ -1574,16 +1567,15 @@
     videoSize.width = mVideoWidth;
     videoSize.height = mVideoHeight;
     if (mCaptureFpsEnable) {
-        if (mTimeBetweenCaptureUs < 0) {
-            ALOGE("Invalid mTimeBetweenTimeLapseFrameCaptureUs value: %lld",
-                    (long long)mTimeBetweenCaptureUs);
+        if (!(mCaptureFps > 0.)) {
+            ALOGE("Invalid mCaptureFps value: %lf", mCaptureFps);
             return BAD_VALUE;
         }
 
         mCameraSourceTimeLapse = CameraSourceTimeLapse::CreateFromCamera(
                 mCamera, mCameraProxy, mCameraId, mClientName, mClientUid, mClientPid,
                 videoSize, mFrameRate, mPreviewSurface,
-                mTimeBetweenCaptureUs);
+                std::llround(1e6 / mCaptureFps));
         *cameraSource = mCameraSourceTimeLapse;
     } else {
         *cameraSource = CameraSource::CreateFromCamera(
@@ -1679,12 +1671,11 @@
 
         // set up time lapse/slow motion for surface source
         if (mCaptureFpsEnable) {
-            if (mTimeBetweenCaptureUs <= 0) {
-                ALOGE("Invalid mTimeBetweenCaptureUs value: %lld",
-                        (long long)mTimeBetweenCaptureUs);
+            if (!(mCaptureFps > 0.)) {
+                ALOGE("Invalid mCaptureFps value: %lf", mCaptureFps);
                 return BAD_VALUE;
             }
-            format->setInt64("time-lapse", mTimeBetweenCaptureUs);
+            format->setDouble("time-lapse-fps", mCaptureFps);
         }
     }
 
@@ -2075,8 +2066,7 @@
     mMaxFileSizeBytes = 0;
     mTrackEveryTimeDurationUs = 0;
     mCaptureFpsEnable = false;
-    mCaptureFps = 0.0;
-    mTimeBetweenCaptureUs = -1;
+    mCaptureFps = -1.0;
     mCameraSourceTimeLapse = NULL;
     mMetaDataStoredInVideoBuffers = kMetadataBufferTypeInvalid;
     mEncoderProfiles = MediaProfiles::getInstance();
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index aedca3f..22b09d4 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -156,6 +156,7 @@
     extractor = MediaExtractor::Create(mDataSource, NULL);
 
     if (extractor == NULL) {
+        ALOGE("initFromDataSource, cannot create extractor!");
         return UNKNOWN_ERROR;
     }
 
@@ -171,6 +172,7 @@
 
     size_t numtracks = extractor->countTracks();
     if (numtracks == 0) {
+        ALOGE("initFromDataSource, source has no track!");
         return UNKNOWN_ERROR;
     }
 
@@ -377,6 +379,11 @@
                             source.get(), mFd, (long long)mOffset, (long long)mLength);
                     if (source.get() != nullptr) {
                         mDataSource = DataSource::CreateFromIDataSource(source);
+                        if (mDataSource != nullptr) {
+                            // Close the local file descriptor as it is not needed anymore.
+                            close(mFd);
+                            mFd = -1;
+                        }
                     } else {
                         ALOGW("extractor service cannot make data source");
                     }
@@ -388,7 +395,9 @@
                 ALOGD("FileSource local");
                 mDataSource = new FileSource(mFd, mOffset, mLength);
             }
-
+            // TODO: close should always be done on mFd, see the lines following
+            // DataSource::CreateFromIDataSource above,
+            // and the FileSource constructor should dup the mFd argument as needed.
             mFd = -1;
         }
 
@@ -1952,12 +1961,12 @@
     size_t psshsize;
 
     if (!mFileMeta->findData(kKeyPssh, &type, &pssh, &psshsize)) {
-        ALOGE("checkDrmInfo: No PSSH");
+        ALOGV("checkDrmInfo: No PSSH");
         return OK; // source without DRM info
     }
 
     Parcel parcel;
-    NuPlayerDrm::retrieveDrmInfo(pssh, psshsize, mMimes, &parcel);
+    NuPlayerDrm::retrieveDrmInfo(pssh, psshsize, &parcel);
     ALOGV("checkDrmInfo: MEDIA_DRM_INFO PSSH size: %d  Parcel size: %d  objects#: %d",
           (int)psshsize, (int)parcel.dataSize(), (int)parcel.objectsCount());
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index d048777..0d4c730 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -202,7 +202,8 @@
       mPaused(false),
       mPausedByClient(true),
       mPausedForBuffering(false),
-      mIsDrmProtected(false) {
+      mIsDrmProtected(false),
+      mDataSourceType(DATA_SOURCE_TYPE_NONE) {
     clearFlushComplete();
 }
 
@@ -225,6 +226,7 @@
 
     msg->setObject("source", new StreamingSource(notify, source));
     msg->post();
+    mDataSourceType = DATA_SOURCE_TYPE_STREAM;
 }
 
 static bool IsHTTPLiveURL(const char *url) {
@@ -258,10 +260,12 @@
     if (IsHTTPLiveURL(url)) {
         source = new HTTPLiveSource(notify, httpService, url, headers);
         ALOGV("setDataSourceAsync HTTPLiveSource %s", url);
+        mDataSourceType = DATA_SOURCE_TYPE_HTTP_LIVE;
     } else if (!strncasecmp(url, "rtsp://", 7)) {
         source = new RTSPSource(
                 notify, httpService, url, headers, mUIDValid, mUID);
         ALOGV("setDataSourceAsync RTSPSource %s", url);
+        mDataSourceType = DATA_SOURCE_TYPE_RTSP;
     } else if ((!strncasecmp(url, "http://", 7)
                 || !strncasecmp(url, "https://", 8))
                     && ((len >= 4 && !strcasecmp(".sdp", &url[len - 4]))
@@ -269,6 +273,7 @@
         source = new RTSPSource(
                 notify, httpService, url, headers, mUIDValid, mUID, true);
         ALOGV("setDataSourceAsync RTSPSource http/https/.sdp %s", url);
+        mDataSourceType = DATA_SOURCE_TYPE_RTSP;
     } else {
         ALOGV("setDataSourceAsync GenericSource %s", url);
 
@@ -282,6 +287,9 @@
         } else {
             ALOGE("Failed to set data source!");
         }
+
+        // regardless of success/failure
+        mDataSourceType = DATA_SOURCE_TYPE_GENERIC_URL;
     }
     msg->setObject("source", source);
     msg->post();
@@ -307,6 +315,7 @@
 
     msg->setObject("source", source);
     msg->post();
+    mDataSourceType = DATA_SOURCE_TYPE_GENERIC_FD;
 }
 
 void NuPlayer::setDataSourceAsync(const sp<DataSource> &dataSource) {
@@ -323,6 +332,7 @@
 
     msg->setObject("source", source);
     msg->post();
+    mDataSourceType = DATA_SOURCE_TYPE_MEDIA;
 }
 
 status_t NuPlayer::getDefaultBufferingSettings(
@@ -2651,6 +2661,32 @@
     }
 }
 
+const char *NuPlayer::getDataSourceType() {
+    switch (mDataSourceType) {
+        case DATA_SOURCE_TYPE_HTTP_LIVE:
+            return "HTTPLive";
+
+        case DATA_SOURCE_TYPE_RTSP:
+            return "RTSP";
+
+        case DATA_SOURCE_TYPE_GENERIC_URL:
+            return "GenURL";
+
+        case DATA_SOURCE_TYPE_GENERIC_FD:
+            return "GenFD";
+
+        case DATA_SOURCE_TYPE_MEDIA:
+            return "Media";
+
+        case DATA_SOURCE_TYPE_STREAM:
+            return "Stream";
+
+        case DATA_SOURCE_TYPE_NONE:
+        default:
+            return "None";
+    }
+ }
+
 // Modular DRM begin
 status_t NuPlayer::prepareDrm(const uint8_t uuid[16], const Vector<uint8_t> &drmSessionId)
 {
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h
index d542749..c69835f 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h
@@ -93,6 +93,8 @@
     status_t prepareDrm(const uint8_t uuid[16], const Vector<uint8_t> &drmSessionId);
     status_t releaseDrm();
 
+    const char *getDataSourceType();
+
 protected:
     virtual ~NuPlayer();
 
@@ -236,6 +238,18 @@
     sp<ICrypto> mCrypto;
     bool mIsDrmProtected;
 
+    typedef enum {
+        DATA_SOURCE_TYPE_NONE,
+        DATA_SOURCE_TYPE_HTTP_LIVE,
+        DATA_SOURCE_TYPE_RTSP,
+        DATA_SOURCE_TYPE_GENERIC_URL,
+        DATA_SOURCE_TYPE_GENERIC_FD,
+        DATA_SOURCE_TYPE_MEDIA,
+        DATA_SOURCE_TYPE_STREAM,
+    } DATA_SOURCE_TYPE;
+
+    std::atomic<DATA_SOURCE_TYPE> mDataSourceType;
+
     inline const sp<DecoderBase> &getDecoder(bool audio) {
         return audio ? mAudioDecoder : mVideoDecoder;
     }
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 9e579f9..a4a5861 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -95,7 +95,11 @@
 }
 
 NuPlayer::Decoder::~Decoder() {
-    mCodec->release();
+    // Need to stop looper first since mCodec could be accessed on the mDecoderLooper.
+    stopLooper();
+    if (mCodec != NULL) {
+        mCodec->release();
+    }
     releaseAndResetMediaBuffers();
 }
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp
index 1210dc9..d0de7b0 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.cpp
@@ -43,8 +43,7 @@
 }
 
 NuPlayer::DecoderBase::~DecoderBase() {
-    mDecoderLooper->unregisterHandler(id());
-    mDecoderLooper->stop();
+    stopLooper();
 }
 
 static
@@ -73,6 +72,11 @@
     mDecoderLooper->registerHandler(this);
 }
 
+void NuPlayer::DecoderBase::stopLooper() {
+    mDecoderLooper->unregisterHandler(id());
+    mDecoderLooper->stop();
+}
+
 void NuPlayer::DecoderBase::setParameters(const sp<AMessage> &params) {
     sp<AMessage> msg = new AMessage(kWhatSetParameters, this);
     msg->setMessage("params", params);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h
index dcdfcaf..d44c396 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderBase.h
@@ -69,6 +69,8 @@
 
     virtual ~DecoderBase();
 
+    void stopLooper();
+
     virtual void onMessageReceived(const sp<AMessage> &msg);
 
     virtual void onConfigure(const sp<AMessage> &format) = 0;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index 01008b4..0c06976 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -53,6 +53,7 @@
 static const char *kPlayerPlaying = "android.media.mediaplayer.playingMs";
 static const char *kPlayerError = "android.media.mediaplayer.err";
 static const char *kPlayerErrorCode = "android.media.mediaplayer.errcode";
+static const char *kPlayerDataSourceType = "android.media.mediaplayer.dataSource";
 
 
 NuPlayerDriver::NuPlayerDriver(pid_t pid)
@@ -570,6 +571,8 @@
     mAnalyticsItem->setInt64(kPlayerDuration, duration_ms);
 
     mAnalyticsItem->setInt64(kPlayerPlaying, (mPlayingTimeUs+500)/1000 );
+
+    mAnalyticsItem->setCString(kPlayerDataSourceType, mPlayer->getDataSourceType());
 }
 
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDrm.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDrm.cpp
index ce6cedc..b7c9db7 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDrm.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDrm.cpp
@@ -205,26 +205,8 @@
 }
 
 // Parcel has only private copy constructor so passing it in rather than returning
-void NuPlayerDrm::retrieveDrmInfo(const void *pssh, size_t psshsize,
-        const Vector<String8> &mimes_in, Parcel *parcel)
+void NuPlayerDrm::retrieveDrmInfo(const void *pssh, size_t psshsize, Parcel *parcel)
 {
-    // 0) Make mimes a vector of unique items while keeping the original order; video first
-    Vector<String8> mimes;
-    for (size_t j = 0; j < mimes_in.size(); j++) {
-        String8 mime = mimes_in[j];
-        bool exists = false;
-        for (size_t i = 0; i < mimes.size() && !exists; i++) {
-            if (mimes[i] == mime) {
-                exists = true;
-            }
-        } // for i
-
-        if (!exists) {
-            mimes.add(mime);
-        }
-    } // for j
-
-
     // 1) PSSH bytes
     parcel->writeUint32(psshsize);
     parcel->writeByteArray(psshsize, (const uint8_t*)pssh);
@@ -242,18 +224,6 @@
         ALOGV("retrieveDrmInfo: MEDIA_DRM_INFO  supportedScheme[%zu] %s", i,
                 uuid.toHexString().string());
     }
-
-    // TODO: remove mimes after it's removed from Java DrmInfo
-    // 3) mimes
-    parcel->writeUint32(mimes.size());
-    for (size_t i = 0; i < mimes.size(); i++) {
-        // writing as String16 so the Java framework side can unpack it to Java String
-        String16 mime(mimes[i]);
-        parcel->writeString16(mime);
-
-        ALOGV("retrieveDrmInfo: MEDIA_DRM_INFO  MIME[%zu] %s",
-                i, mimes[i].string());
-    }
 }
 
 ////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDrm.h b/media/libmediaplayerservice/nuplayer/NuPlayerDrm.h
index 6704bd1..6b8a2d9 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDrm.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDrm.h
@@ -84,8 +84,7 @@
         static sp<ICrypto> createCryptoAndPlugin(const uint8_t uuid[16],
                 const Vector<uint8_t> &drmSessionId, status_t &status);
         // Parcel has only private copy constructor so passing it in rather than returning
-        static void retrieveDrmInfo(const void *pssh, size_t psshsize,
-                const Vector<String8> &mimes_in, Parcel *parcel);
+        static void retrieveDrmInfo(const void *pssh, size_t psshsize, Parcel *parcel);
 
         ////////////////////////////////////////////////////////////////////////////////////////////
         /// Helpers for NuPlayerDecoder
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 9fe61703..5775b43 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -1010,6 +1010,13 @@
             }
 
             // EOS
+            if (mPaused) {
+                // Do not notify EOS when paused.
+                // This is needed to avoid switch to next clip while in pause.
+                ALOGV("onDrainAudioQueue(): Do not notify EOS when paused");
+                return false;
+            }
+
             int64_t postEOSDelayUs = 0;
             if (mAudioSink->needsTrailingPadding()) {
                 postEOSDelayUs = getPendingAudioPlayoutDurationUs(ALooper::GetNowUs());
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 63b9571..9f1be22 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -251,6 +251,7 @@
 
     virtual PortMode getPortMode(OMX_U32 portIndex);
 
+    virtual void stateExited();
     virtual bool onMessageReceived(const sp<AMessage> &msg);
 
     virtual bool onOMXEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2);
@@ -546,13 +547,14 @@
       mRepeatFrameDelayUs(-1ll),
       mMaxPtsGapUs(-1ll),
       mMaxFps(-1),
-      mTimePerFrameUs(-1ll),
-      mTimePerCaptureUs(-1ll),
+      mFps(-1.0),
+      mCaptureFps(-1.0),
       mCreateInputBuffersSuspended(false),
       mLatency(0),
       mTunneled(false),
       mDescribeColorAspectsIndex((OMX_INDEXTYPE)0),
       mDescribeHDRStaticInfoIndex((OMX_INDEXTYPE)0),
+      mStateGeneration(0),
       mVendorExtensionsStatus(kExtensionsUnchecked) {
     mUninitializedState = new UninitializedState(this);
     mLoadedState = new LoadedState(this);
@@ -1061,6 +1063,34 @@
         return err;
     }
 
+    OMX_INDEXTYPE index;
+    err = mOMXNode->getExtensionIndex(
+            "OMX.google.android.index.AndroidNativeBufferConsumerUsage",
+            &index);
+
+    if (err != OK) {
+        // allow failure
+        err = OK;
+    } else {
+        int usageBits = 0;
+        if (nativeWindow->query(
+                nativeWindow,
+                NATIVE_WINDOW_CONSUMER_USAGE_BITS,
+                &usageBits) == OK) {
+            OMX_PARAM_U32TYPE params;
+            InitOMXParams(&params);
+            params.nPortIndex = kPortIndexOutput;
+            params.nU32 = (OMX_U32)usageBits;
+
+            err = mOMXNode->setParameter(index, &params, sizeof(params));
+
+            if (err != OK) {
+                ALOGE("Fail to set AndroidNativeBufferConsumerUsage: %d", err);
+                return err;
+            }
+        }
+    }
+
     OMX_U32 usage = 0;
     err = mOMXNode->getGraphicBufferUsage(kPortIndexOutput, &usage);
     if (err != 0) {
@@ -1104,7 +1134,8 @@
 
     if (err == OK) {
         err = setupNativeWindowSizeFormatAndUsage(
-                mNativeWindow.get(), &mNativeWindowUsageBits, preregister /* reconnect */);
+                mNativeWindow.get(), &mNativeWindowUsageBits,
+                preregister && !mTunneled /* reconnect */);
     }
     if (err != OK) {
         mNativeWindowUsageBits = 0;
@@ -1802,8 +1833,8 @@
             mMaxFps = -1;
         }
 
-        if (!msg->findInt64("time-lapse", &mTimePerCaptureUs)) {
-            mTimePerCaptureUs = -1ll;
+        if (!msg->findDouble("time-lapse-fps", &mCaptureFps)) {
+            mCaptureFps = -1.0;
         }
 
         if (!msg->findInt32(
@@ -3739,17 +3770,18 @@
 
     def.nBufferSize = (video_def->nStride * video_def->nSliceHeight * 3) / 2;
 
-    float frameRate;
-    if (!msg->findFloat("frame-rate", &frameRate)) {
+    float framerate;
+    if (!msg->findFloat("frame-rate", &framerate)) {
         int32_t tmp;
         if (!msg->findInt32("frame-rate", &tmp)) {
             return INVALID_OPERATION;
         }
-        frameRate = (float)tmp;
-        mTimePerFrameUs = (int64_t) (1000000.0f / frameRate);
+        mFps = (double)tmp;
+    } else {
+        mFps = (double)framerate;
     }
 
-    video_def->xFramerate = (OMX_U32)(frameRate * 65536.0f);
+    video_def->xFramerate = (OMX_U32)(mFps * 65536);
     video_def->eCompressionFormat = OMX_VIDEO_CodingUnused;
     // this is redundant as it was already set up in setVideoPortFormatType
     // FIXME for now skip this only for flexible YUV formats
@@ -5321,6 +5353,10 @@
     return KEEP_BUFFERS;
 }
 
+void ACodec::BaseState::stateExited() {
+    ++mCodec->mStateGeneration;
+}
+
 bool ACodec::BaseState::onMessageReceived(const sp<AMessage> &msg) {
     switch (msg->what()) {
         case kWhatInputBufferFilled:
@@ -5394,6 +5430,14 @@
             ALOGE_IF("[%s] failed to release codec instance: err=%d",
                        mCodec->mComponentName.c_str(), err);
             mCodec->mCallback->onReleaseCompleted();
+
+            mCodec->changeState(mCodec->mUninitializedState);
+            break;
+        }
+
+        case ACodec::kWhatForceStateTransition:
+        {
+            ALOGV("Already transitioned --- ignore");
             break;
         }
 
@@ -6597,11 +6641,10 @@
         }
     }
 
-    if (mCodec->mTimePerCaptureUs > 0ll
-            && mCodec->mTimePerFrameUs > 0ll) {
+    if (mCodec->mCaptureFps > 0. && mCodec->mFps > 0.) {
         err = statusFromBinderStatus(
                 mCodec->mGraphicBufferSource->setTimeLapseConfig(
-                        mCodec->mTimePerFrameUs, mCodec->mTimePerCaptureUs));
+                        mCodec->mFps, mCodec->mCaptureFps));
 
         if (err != OK) {
             ALOGE("[%s] Unable to configure time lapse (err %d)",
@@ -6988,7 +7031,6 @@
 
 void ACodec::ExecutingState::stateEntered() {
     ALOGV("[%s] Now Executing", mCodec->mComponentName.c_str());
-
     mCodec->mRenderTracker.clear(systemTime(CLOCK_MONOTONIC));
     mCodec->processDeferredMessages();
 }
@@ -7614,6 +7656,30 @@
     mCallback->onSignaledInputEOS(err);
 }
 
+void ACodec::forceStateTransition(int generation) {
+    if (generation != mStateGeneration) {
+        ALOGV("Ignoring stale force state transition message: #%d (now #%d)",
+                generation, mStateGeneration);
+        return;
+    }
+    ALOGE("State machine stuck");
+    // Error must have already been signalled to the client.
+
+    // Deferred messages will be handled at LoadedState at the end of the
+    // transition.
+    mShutdownInProgress = true;
+    // No shutdown complete callback at the end of the transition.
+    mExplicitShutdown = false;
+    mKeepComponentAllocated = true;
+
+    status_t err = mOMXNode->sendCommand(OMX_CommandStateSet, OMX_StateIdle);
+    if (err != OK) {
+        // TODO: do some recovery here.
+    } else {
+        changeState(mExecutingToIdleState);
+    }
+}
+
 bool ACodec::ExecutingState::onOMXFrameRendered(int64_t mediaTimeUs, nsecs_t systemNano) {
     mCodec->onFrameRendered(mediaTimeUs, systemNano);
     return true;
@@ -7680,7 +7746,14 @@
 
     switch (msg->what()) {
         case kWhatFlush:
-        case kWhatShutdown:
+        case kWhatShutdown: {
+            if (mCodec->mFatalError) {
+                sp<AMessage> msg = new AMessage(ACodec::kWhatForceStateTransition, mCodec);
+                msg->setInt32("generation", mCodec->mStateGeneration);
+                msg->post(3000000);
+            }
+            // fall-through
+        }
         case kWhatResume:
         case kWhatSetParameters:
         {
@@ -7693,6 +7766,16 @@
             break;
         }
 
+        case kWhatForceStateTransition:
+        {
+            int32_t generation = 0;
+            CHECK(msg->findInt32("generation", &generation));
+            mCodec->forceStateTransition(generation);
+
+            handled = true;
+            break;
+        }
+
         default:
             handled = BaseState::onMessageReceived(msg);
             break;
@@ -7752,15 +7835,7 @@
 
                 if (err != OK) {
                     mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err));
-
-                    // This is technically not correct, but appears to be
-                    // the only way to free the component instance.
-                    // Controlled transitioning from excecuting->idle
-                    // and idle->loaded seem impossible probably because
-                    // the output port never finishes re-enabling.
-                    mCodec->mShutdownInProgress = true;
-                    mCodec->mKeepComponentAllocated = false;
-                    mCodec->changeState(mCodec->mLoadedState);
+                    ALOGE("Error occurred while disabling the output port");
                 }
 
                 return true;
@@ -7785,7 +7860,7 @@
         }
 
         default:
-            return false;
+            return BaseState::onOMXEvent(event, data1, data2);
     }
 }
 
@@ -7987,6 +8062,11 @@
         case kWhatShutdown:
         {
             mCodec->deferMessage(msg);
+            if (mCodec->mFatalError) {
+                sp<AMessage> msg = new AMessage(ACodec::kWhatForceStateTransition, mCodec);
+                msg->setInt32("generation", mCodec->mStateGeneration);
+                msg->post(3000000);
+            }
             break;
         }
 
@@ -7997,6 +8077,16 @@
             break;
         }
 
+        case kWhatForceStateTransition:
+        {
+            int32_t generation = 0;
+            CHECK(msg->findInt32("generation", &generation));
+            mCodec->forceStateTransition(generation);
+
+            handled = true;
+            break;
+        }
+
         default:
             handled = BaseState::onMessageReceived(msg);
             break;
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 61b8f9d..372b11a 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -78,6 +78,7 @@
         libaudioutils \
         libbinder \
         libcamera_client \
+        libcrypto \
         libcutils \
         libdl \
         libdrmframework \
diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp
index 9965462..c91c82b 100644
--- a/media/libstagefright/MediaExtractor.cpp
+++ b/media/libstagefright/MediaExtractor.cpp
@@ -158,7 +158,7 @@
     if (mime == NULL) {
         float confidence;
         if (!sniff(source, &tmp, &confidence, &meta)) {
-            ALOGV("FAILED to autodetect media content.");
+            ALOGW("FAILED to autodetect media content.");
 
             return NULL;
         }
diff --git a/media/libstagefright/NuMediaExtractor.cpp b/media/libstagefright/NuMediaExtractor.cpp
index e3ca516..51f1ba3 100644
--- a/media/libstagefright/NuMediaExtractor.cpp
+++ b/media/libstagefright/NuMediaExtractor.cpp
@@ -297,6 +297,10 @@
 
     sp<IMediaSource> source = mImpl->getTrack(index);
 
+    if (source == nullptr) {
+        return ERROR_MALFORMED;
+    }
+
     status_t ret = source->start();
     if (ret != OK) {
         return ret;
diff --git a/media/libstagefright/OMXClient.cpp b/media/libstagefright/OMXClient.cpp
index 02d275b..5f9aa01 100644
--- a/media/libstagefright/OMXClient.cpp
+++ b/media/libstagefright/OMXClient.cpp
@@ -37,6 +37,10 @@
 OMXClient::OMXClient() {
 }
 
+status_t OMXClient::connect() {
+    return connect(nullptr);
+}
+
 status_t OMXClient::connect(bool* trebleFlag) {
     if (property_get_bool("persist.media.treble_omx", true)) {
         if (trebleFlag != nullptr) {
diff --git a/media/libstagefright/SurfaceUtils.cpp b/media/libstagefright/SurfaceUtils.cpp
index b6b315d..b7c1598 100644
--- a/media/libstagefright/SurfaceUtils.cpp
+++ b/media/libstagefright/SurfaceUtils.cpp
@@ -91,9 +91,19 @@
             return err;
         }
 
-        // Check if the ANativeWindow uses hardware protected buffers.
-        if (queuesToNativeWindow != 1 && !(consumerUsage & GRALLOC_USAGE_PROTECTED)) {
-            ALOGE("native window could not be authenticated");
+        // Check if the consumer end of the ANativeWindow can handle protected content.
+        int isConsumerProtected = 0;
+        err = nativeWindow->query(
+                nativeWindow, NATIVE_WINDOW_CONSUMER_IS_PROTECTED, &isConsumerProtected);
+        if (err != NO_ERROR) {
+            ALOGE("error query native window: %s (%d)", strerror(-err), -err);
+            return err;
+        }
+
+        // Deny queuing into native window if neither condition is satisfied.
+        if (queuesToNativeWindow != 1 && isConsumerProtected != 1) {
+            ALOGE("native window cannot handle protected buffers: the consumer should either be "
+                  "a hardware composer or support hardware protection");
             return PERMISSION_DENIED;
         }
     }
diff --git a/media/libstagefright/httplive/PlaylistFetcher.cpp b/media/libstagefright/httplive/PlaylistFetcher.cpp
index bbcea51..00cf142 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.cpp
+++ b/media/libstagefright/httplive/PlaylistFetcher.cpp
@@ -26,6 +26,7 @@
 #include "include/avc_utils.h"
 #include "include/ID3.h"
 #include "mpeg2ts/AnotherPacketSource.h"
+#include "mpeg2ts/HlsSampleDecryptor.h"
 
 #include <media/stagefright/foundation/ABitReader.h>
 #include <media/stagefright/foundation/ABuffer.h>
@@ -36,7 +37,6 @@
 
 #include <ctype.h>
 #include <inttypes.h>
-#include <openssl/aes.h>
 
 #define FLOGV(fmt, ...) ALOGV("[fetcher-%d] " fmt, mFetcherID, ##__VA_ARGS__)
 #define FSLOGV(stream, fmt, ...) ALOGV("[fetcher-%d] [%s] " fmt, mFetcherID, \
@@ -167,11 +167,15 @@
       mFirstPTSValid(false),
       mFirstTimeUs(-1ll),
       mVideoBuffer(new AnotherPacketSource(NULL)),
+      mSampleAesKeyItemChanged(false),
       mThresholdRatio(-1.0f),
       mDownloadState(new DownloadState()),
       mHasMetadata(false) {
     memset(mPlaylistHash, 0, sizeof(mPlaylistHash));
     mHTTPDownloader = mSession->getHTTPDownloader();
+
+    memset(mKeyData, 0, sizeof(mKeyData));
+    memset(mAESInitVec, 0, sizeof(mAESInitVec));
 }
 
 PlaylistFetcher::~PlaylistFetcher() {
@@ -306,6 +310,15 @@
         }
     }
 
+    // TODO: Revise this when we add support for KEYFORMAT
+    // If method has changed (e.g., -> NONE); sufficient to check at the segment boundary
+    if (mSampleAesKeyItem != NULL && first && found && method != "SAMPLE-AES") {
+        ALOGI("decryptBuffer: resetting mSampleAesKeyItem(%p) with method %s",
+                mSampleAesKeyItem.get(), method.c_str());
+        mSampleAesKeyItem = NULL;
+        mSampleAesKeyItemChanged = true;
+    }
+
     if (!found) {
         method = "NONE";
     }
@@ -313,6 +326,8 @@
 
     if (method == "NONE") {
         return OK;
+    } else if (method == "SAMPLE-AES") {
+        ALOGV("decryptBuffer: Non-Widevine SAMPLE-AES is supported now.");
     } else if (!(method == "AES-128")) {
         ALOGE("Unsupported cipher method '%s'", method.c_str());
         return ERROR_UNSUPPORTED;
@@ -345,6 +360,79 @@
         mAESKeyForURI.add(keyURI, key);
     }
 
+    if (first) {
+        // If decrypting the first block in a file, read the iv from the manifest
+        // or derive the iv from the file's sequence number.
+
+        unsigned char AESInitVec[AES_BLOCK_SIZE];
+        AString iv;
+        if (itemMeta->findString("cipher-iv", &iv)) {
+            if ((!iv.startsWith("0x") && !iv.startsWith("0X"))
+                    || iv.size() > 16 * 2 + 2) {
+                ALOGE("malformed cipher IV '%s'.", iv.c_str());
+                return ERROR_MALFORMED;
+            }
+
+            while (iv.size() < 16 * 2 + 2) {
+                iv.insert("0", 1, 2);
+            }
+
+            memset(AESInitVec, 0, sizeof(AESInitVec));
+            for (size_t i = 0; i < 16; ++i) {
+                char c1 = tolower(iv.c_str()[2 + 2 * i]);
+                char c2 = tolower(iv.c_str()[3 + 2 * i]);
+                if (!isxdigit(c1) || !isxdigit(c2)) {
+                    ALOGE("malformed cipher IV '%s'.", iv.c_str());
+                    return ERROR_MALFORMED;
+                }
+                uint8_t nibble1 = isdigit(c1) ? c1 - '0' : c1 - 'a' + 10;
+                uint8_t nibble2 = isdigit(c2) ? c2 - '0' : c2 - 'a' + 10;
+
+                AESInitVec[i] = nibble1 << 4 | nibble2;
+            }
+        } else {
+            memset(AESInitVec, 0, sizeof(AESInitVec));
+            AESInitVec[15] = mSeqNumber & 0xff;
+            AESInitVec[14] = (mSeqNumber >> 8) & 0xff;
+            AESInitVec[13] = (mSeqNumber >> 16) & 0xff;
+            AESInitVec[12] = (mSeqNumber >> 24) & 0xff;
+        }
+
+        bool newKey = memcmp(mKeyData, key->data(), AES_BLOCK_SIZE) != 0;
+        bool newInitVec = memcmp(mAESInitVec, AESInitVec, AES_BLOCK_SIZE) != 0;
+        bool newSampleAesKeyItem = newKey || newInitVec;
+        ALOGV("decryptBuffer: SAMPLE-AES newKeyItem %d/%d (Key %d initVec %d)",
+                mSampleAesKeyItemChanged, newSampleAesKeyItem, newKey, newInitVec);
+
+        if (newSampleAesKeyItem) {
+            memcpy(mKeyData, key->data(), AES_BLOCK_SIZE);
+            memcpy(mAESInitVec, AESInitVec, AES_BLOCK_SIZE);
+
+            if (method == "SAMPLE-AES") {
+                mSampleAesKeyItemChanged = true;
+
+                sp<ABuffer> keyDataBuffer = ABuffer::CreateAsCopy(mKeyData, sizeof(mKeyData));
+                sp<ABuffer> initVecBuffer = ABuffer::CreateAsCopy(mAESInitVec, sizeof(mAESInitVec));
+
+                // always allocating a new one rather than updating the old message
+                // lower layer might still have a reference to the old message
+                mSampleAesKeyItem = new AMessage();
+                mSampleAesKeyItem->setBuffer("keyData", keyDataBuffer);
+                mSampleAesKeyItem->setBuffer("initVec", initVecBuffer);
+
+                ALOGV("decryptBuffer: New SampleAesKeyItem: Key: %s  IV: %s",
+                        HlsSampleDecryptor::aesBlockToStr(mKeyData).c_str(),
+                        HlsSampleDecryptor::aesBlockToStr(mAESInitVec).c_str());
+            } // SAMPLE-AES
+        } // newSampleAesKeyItem
+    } // first
+
+    if (method == "SAMPLE-AES") {
+        ALOGV("decryptBuffer: skipping full-seg decrypt for SAMPLE-AES");
+        return OK;
+    }
+
+
     AES_KEY aes_key;
     if (AES_set_decrypt_key(key->data(), 128, &aes_key) != 0) {
         ALOGE("failed to set AES decryption key.");
@@ -361,44 +449,6 @@
         return ERROR_MALFORMED;
     }
 
-    if (first) {
-        // If decrypting the first block in a file, read the iv from the manifest
-        // or derive the iv from the file's sequence number.
-
-        AString iv;
-        if (itemMeta->findString("cipher-iv", &iv)) {
-            if ((!iv.startsWith("0x") && !iv.startsWith("0X"))
-                    || iv.size() > 16 * 2 + 2) {
-                ALOGE("malformed cipher IV '%s'.", iv.c_str());
-                return ERROR_MALFORMED;
-            }
-
-            while (iv.size() < 16 * 2 + 2) {
-                iv.insert("0", 1, 2);
-            }
-
-            memset(mAESInitVec, 0, sizeof(mAESInitVec));
-            for (size_t i = 0; i < 16; ++i) {
-                char c1 = tolower(iv.c_str()[2 + 2 * i]);
-                char c2 = tolower(iv.c_str()[3 + 2 * i]);
-                if (!isxdigit(c1) || !isxdigit(c2)) {
-                    ALOGE("malformed cipher IV '%s'.", iv.c_str());
-                    return ERROR_MALFORMED;
-                }
-                uint8_t nibble1 = isdigit(c1) ? c1 - '0' : c1 - 'a' + 10;
-                uint8_t nibble2 = isdigit(c2) ? c2 - '0' : c2 - 'a' + 10;
-
-                mAESInitVec[i] = nibble1 << 4 | nibble2;
-            }
-        } else {
-            memset(mAESInitVec, 0, sizeof(mAESInitVec));
-            mAESInitVec[15] = mSeqNumber & 0xff;
-            mAESInitVec[14] = (mSeqNumber >> 8) & 0xff;
-            mAESInitVec[13] = (mSeqNumber >> 16) & 0xff;
-            mAESInitVec[12] = (mSeqNumber >> 24) & 0xff;
-        }
-    }
-
     AES_cbc_encrypt(
             buffer->data(), buffer->data(), buffer->size(),
             &aes_key, mAESInitVec, AES_DECRYPT);
@@ -409,7 +459,7 @@
 status_t PlaylistFetcher::checkDecryptPadding(const sp<ABuffer> &buffer) {
     AString method;
     CHECK(buffer->meta()->findString("cipher-method", &method));
-    if (method == "NONE") {
+    if (method == "NONE" || method == "SAMPLE-AES") {
         return OK;
     }
 
@@ -1656,6 +1706,11 @@
         mNextPTSTimeUs = -1ll;
     }
 
+    if (mSampleAesKeyItemChanged) {
+        mTSParser->signalNewSampleAesKey(mSampleAesKeyItem);
+        mSampleAesKeyItemChanged = false;
+    }
+
     size_t offset = 0;
     while (offset + 188 <= buffer->size()) {
         status_t err = mTSParser->feedTSPacket(buffer->data() + offset, 188);
@@ -2038,10 +2093,24 @@
         }
     }
 
+    sp<HlsSampleDecryptor> sampleDecryptor = NULL;
+    if (mSampleAesKeyItem != NULL) {
+        ALOGV("extractAndQueueAccessUnits[%d] SampleAesKeyItem: Key: %s  IV: %s",
+                mSeqNumber,
+                HlsSampleDecryptor::aesBlockToStr(mKeyData).c_str(),
+                HlsSampleDecryptor::aesBlockToStr(mAESInitVec).c_str());
+
+        sampleDecryptor = new HlsSampleDecryptor(mSampleAesKeyItem);
+    }
+
+    int frameId = 0;
+
     size_t offset = 0;
     while (offset < buffer->size()) {
         const uint8_t *adtsHeader = buffer->data() + offset;
         CHECK_LT(offset + 5, buffer->size());
+        // non-const pointer for decryption if needed
+        uint8_t *adtsFrame = buffer->data() + offset;
 
         unsigned aac_frame_length =
             ((adtsHeader[3] & 3) << 11)
@@ -2099,6 +2168,18 @@
             }
         }
 
+        if (sampleDecryptor != NULL) {
+            bool protection_absent = (adtsHeader[1] & 0x1);
+            size_t headerSize = protection_absent ? 7 : 9;
+            if (frameId == 0) {
+                ALOGV("extractAndQueueAAC[%d] protection_absent %d (%02x) headerSize %zu",
+                        mSeqNumber, protection_absent, adtsHeader[1], headerSize);
+            }
+
+            sampleDecryptor->processAAC(headerSize, adtsFrame, aac_frame_length);
+        }
+        frameId++;
+
         sp<ABuffer> unit = new ABuffer(aac_frame_length);
         memcpy(unit->data(), adtsHeader, aac_frame_length);
 
diff --git a/media/libstagefright/httplive/PlaylistFetcher.h b/media/libstagefright/httplive/PlaylistFetcher.h
index ee7d3a1..d7db54a 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.h
+++ b/media/libstagefright/httplive/PlaylistFetcher.h
@@ -19,6 +19,7 @@
 #define PLAYLIST_FETCHER_H_
 
 #include <media/stagefright/foundation/AHandler.h>
+#include <openssl/aes.h>
 
 #include "mpeg2ts/ATSParser.h"
 #include "LiveSession.h"
@@ -175,7 +176,10 @@
     // Stores the initialization vector to decrypt the next block of cipher text, which can
     // either be derived from the sequence number, read from the manifest, or copied from
     // the last block of cipher text (cipher-block chaining).
-    unsigned char mAESInitVec[16];
+    unsigned char mAESInitVec[AES_BLOCK_SIZE];
+    unsigned char mKeyData[AES_BLOCK_SIZE];
+    bool mSampleAesKeyItemChanged;
+    sp<AMessage> mSampleAesKeyItem;
 
     Mutex mThresholdLock;
     float mThresholdRatio;
diff --git a/media/libstagefright/include/ACodec.h b/media/libstagefright/include/ACodec.h
index 6c1a5c6..22b8657 100644
--- a/media/libstagefright/include/ACodec.h
+++ b/media/libstagefright/include/ACodec.h
@@ -150,6 +150,7 @@
         kWhatSubmitOutputMetadataBufferIfEOS = 'subm',
         kWhatOMXDied                 = 'OMXd',
         kWhatReleaseCodecInstance    = 'relC',
+        kWhatForceStateTransition    = 'fstt',
     };
 
     enum {
@@ -293,8 +294,8 @@
     int64_t mRepeatFrameDelayUs;
     int64_t mMaxPtsGapUs;
     float mMaxFps;
-    int64_t mTimePerFrameUs;
-    int64_t mTimePerCaptureUs;
+    double mFps;
+    double mCaptureFps;
     bool mCreateInputBuffersSuspended;
     uint32_t mLatency;
 
@@ -305,6 +306,8 @@
 
     std::shared_ptr<ACodecBufferChannel> mBufferChannel;
 
+    int32_t mStateGeneration;
+
     enum {
         kExtensionsUnchecked,
         kExtensionsNone,
@@ -569,6 +572,9 @@
     // Send EOS on input stream.
     void onSignalEndOfInputStream();
 
+    // Force EXEC->IDLE->LOADED shutdown sequence if not stale.
+    void forceStateTransition(int generation);
+
     DISALLOW_EVIL_CONSTRUCTORS(ACodec);
 };
 
diff --git a/media/libstagefright/include/OMXClient.h b/media/libstagefright/include/OMXClient.h
index 315f19b..203a181 100644
--- a/media/libstagefright/include/OMXClient.h
+++ b/media/libstagefright/include/OMXClient.h
@@ -26,7 +26,9 @@
 public:
     OMXClient();
 
-    status_t connect(bool* trebleFlag = nullptr);
+    status_t connect();
+    status_t connect(bool* trebleFlag);
+
     status_t connectLegacy();
     status_t connectTreble();
     void disconnect();
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index 8099edb..31edb21 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -105,6 +105,8 @@
 
     void updateCasSessions();
 
+    void signalNewSampleAesKey(const sp<AMessage> &keyItem);
+
 private:
     struct StreamInfo {
         unsigned mType;
@@ -119,6 +121,7 @@
     bool mFirstPTSValid;
     uint64_t mFirstPTS;
     int64_t mLastRecoveredPTS;
+    sp<AMessage> mSampleAesKeyItem;
 
     status_t parseProgramMap(ABitReader *br);
     int64_t recoverPTS(uint64_t PTS_33bit);
@@ -168,6 +171,8 @@
     bool isVideo() const;
     bool isMeta() const;
 
+    void signalNewSampleAesKey(const sp<AMessage> &keyItem);
+
 protected:
     virtual ~Stream();
 
@@ -194,6 +199,8 @@
     ElementaryStreamQueue *mQueue;
 
     bool mScrambled;
+    bool mSampleEncrypted;
+    sp<AMessage> mSampleAesKeyItem;
     sp<IMemory> mMem;
     sp<MemoryDealer> mDealer;
     sp<ABuffer> mDescrambledBuffer;
@@ -586,6 +593,10 @@
             sp<Stream> stream = new Stream(
                     this, info.mPID, info.mType, PCR_PID, info.mCASystemId);
 
+            if (mSampleAesKeyItem != NULL) {
+                stream->signalNewSampleAesKey(mSampleAesKeyItem);
+            }
+
             isAddingScrambledStream |= info.mCASystemId >= 0;
             mStreams.add(info.mPID, stream);
         }
@@ -710,22 +721,32 @@
       mPrevPTS(0),
       mQueue(NULL),
       mScrambled(CA_system_ID >= 0) {
-    ALOGV("new stream PID 0x%02x, type 0x%02x, scrambled %d",
-            elementaryPID, streamType, mScrambled);
 
-    uint32_t flags = (isVideo() && mScrambled) ?
-            ElementaryStreamQueue::kFlag_ScrambledData : 0;
+    mSampleEncrypted =
+            mStreamType == STREAMTYPE_H264_ENCRYPTED ||
+            mStreamType == STREAMTYPE_AAC_ENCRYPTED  ||
+            mStreamType == STREAMTYPE_AC3_ENCRYPTED;
+
+    ALOGV("new stream PID 0x%02x, type 0x%02x, scrambled %d, SampleEncrypted: %d",
+            elementaryPID, streamType, mScrambled, mSampleEncrypted);
+
+    uint32_t flags =
+            (isVideo() && mScrambled) ? ElementaryStreamQueue::kFlag_ScrambledData :
+            (mSampleEncrypted) ? ElementaryStreamQueue::kFlag_SampleEncryptedData :
+            0;
 
     ElementaryStreamQueue::Mode mode = ElementaryStreamQueue::INVALID;
 
     switch (mStreamType) {
         case STREAMTYPE_H264:
+        case STREAMTYPE_H264_ENCRYPTED:
             mode = ElementaryStreamQueue::H264;
             flags |= (mProgram->parserFlags() & ALIGNED_VIDEO_DATA) ?
                     ElementaryStreamQueue::kFlag_AlignedData : 0;
             break;
 
         case STREAMTYPE_MPEG2_AUDIO_ADTS:
+        case STREAMTYPE_AAC_ENCRYPTED:
             mode = ElementaryStreamQueue::AAC;
             break;
 
@@ -745,6 +766,7 @@
 
         case STREAMTYPE_LPCM_AC3:
         case STREAMTYPE_AC3:
+        case STREAMTYPE_AC3_ENCRYPTED:
             mode = ElementaryStreamQueue::AC3;
             break;
 
@@ -761,6 +783,10 @@
     mQueue = new ElementaryStreamQueue(mode, flags);
 
     if (mQueue != NULL) {
+        if (mSampleAesKeyItem != NULL) {
+            mQueue->signalNewSampleAesKey(mSampleAesKeyItem);
+        }
+
         ensureBufferCapacity(kInitialStreamBufferSize);
 
         if (mScrambled && (isAudio() || isVideo())) {
@@ -913,6 +939,7 @@
 bool ATSParser::Stream::isVideo() const {
     switch (mStreamType) {
         case STREAMTYPE_H264:
+        case STREAMTYPE_H264_ENCRYPTED:
         case STREAMTYPE_MPEG1_VIDEO:
         case STREAMTYPE_MPEG2_VIDEO:
         case STREAMTYPE_MPEG4_VIDEO:
@@ -930,6 +957,8 @@
         case STREAMTYPE_MPEG2_AUDIO_ADTS:
         case STREAMTYPE_LPCM_AC3:
         case STREAMTYPE_AC3:
+        case STREAMTYPE_AAC_ENCRYPTED:
+        case STREAMTYPE_AC3_ENCRYPTED:
             return true;
 
         default:
@@ -1454,7 +1483,7 @@
     mPrevPTS = PTS;
 #endif
 
-    ALOGV("onPayloadData mStreamType=0x%02x", mStreamType);
+    ALOGV("onPayloadData mStreamType=0x%02x size: %zu", mStreamType, size);
 
     int64_t timeUs = 0ll;  // no presentation timestamp available.
     if (PTS_DTS_flags == 2 || PTS_DTS_flags == 3) {
@@ -1492,6 +1521,8 @@
                 }
                 mSource = new AnotherPacketSource(meta);
                 mSource->queueAccessUnit(accessUnit);
+                ALOGV("onPayloadData: created AnotherPacketSource PID 0x%08x of type 0x%02x",
+                        mElementaryPID, mStreamType);
             }
         } else if (mQueue->getFormat() != NULL) {
             // After a discontinuity we invalidate the queue's format
@@ -1730,6 +1761,9 @@
             if (!found) {
                 mPrograms.push(
                         new Program(this, program_number, programMapPID, mLastRecoveredPTS));
+                if (mSampleAesKeyItem != NULL) {
+                    mPrograms.top()->signalNewSampleAesKey(mSampleAesKeyItem);
+                }
             }
 
             if (mPSISections.indexOfKey(programMapPID) < 0) {
@@ -2228,4 +2262,40 @@
     ALOGV("crc: %08x\n", crc);
     return (crc == 0);
 }
+
+// SAMPLE_AES key handling
+// TODO: Merge these to their respective class after Widevine-HLS
+void ATSParser::signalNewSampleAesKey(const sp<AMessage> &keyItem) {
+    ALOGD("signalNewSampleAesKey: %p", keyItem.get());
+
+    mSampleAesKeyItem = keyItem;
+
+    // a NULL key item will propagate to existing ElementaryStreamQueues
+    for (size_t i = 0; i < mPrograms.size(); ++i) {
+        mPrograms[i]->signalNewSampleAesKey(keyItem);
+    }
+}
+
+void ATSParser::Program::signalNewSampleAesKey(const sp<AMessage> &keyItem) {
+    ALOGD("Program::signalNewSampleAesKey: %p", keyItem.get());
+
+    mSampleAesKeyItem = keyItem;
+
+    // a NULL key item will propagate to existing ElementaryStreamQueues
+    for (size_t i = 0; i < mStreams.size(); ++i) {
+        mStreams[i]->signalNewSampleAesKey(keyItem);
+    }
+}
+
+void ATSParser::Stream::signalNewSampleAesKey(const sp<AMessage> &keyItem) {
+    ALOGD("Stream::signalNewSampleAesKey: 0x%04x size = %zu keyItem: %p",
+          mElementaryPID, mBuffer->size(), keyItem.get());
+
+    // a NULL key item will propagate to existing ElementaryStreamQueues
+    mSampleAesKeyItem = keyItem;
+
+    flush(NULL);
+    mQueue->signalNewSampleAesKey(keyItem);
+}
+
 }  // namespace android
diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h
index 4a88713..374e011 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.h
+++ b/media/libstagefright/mpeg2ts/ATSParser.h
@@ -131,6 +131,8 @@
 
     int64_t getFirstPTSTimeUs();
 
+    void signalNewSampleAesKey(const sp<AMessage> &keyItem);
+
     enum {
         // From ISO/IEC 13818-1: 2000 (E), Table 2-29
         STREAMTYPE_RESERVED             = 0x00,
@@ -149,6 +151,11 @@
         // Stream type 0x83 is non-standard,
         // it could be LPCM or TrueHD AC3
         STREAMTYPE_LPCM_AC3             = 0x83,
+
+        //Sample Encrypted types
+        STREAMTYPE_H264_ENCRYPTED       = 0xDB,
+        STREAMTYPE_AAC_ENCRYPTED        = 0xCF,
+        STREAMTYPE_AC3_ENCRYPTED        = 0xC1,
     };
 
 protected:
@@ -181,6 +188,8 @@
 
     size_t mNumTSPacketsParsed;
 
+    sp<AMessage> mSampleAesKeyItem;
+
     void parseProgramAssociationTable(ABitReader *br);
     void parseProgramMap(ABitReader *br);
     // Parse PES packet where br is pointing to. If the PES contains a sync
diff --git a/media/libstagefright/mpeg2ts/Android.mk b/media/libstagefright/mpeg2ts/Android.mk
index 5140e66..20acfe7 100644
--- a/media/libstagefright/mpeg2ts/Android.mk
+++ b/media/libstagefright/mpeg2ts/Android.mk
@@ -7,6 +7,7 @@
         ATSParser.cpp             \
         CasManager.cpp            \
         ESQueue.cpp               \
+        HlsSampleDecryptor.cpp    \
         MPEG2PSExtractor.cpp      \
         MPEG2TSExtractor.cpp      \
 
@@ -18,7 +19,9 @@
 LOCAL_SANITIZE := unsigned-integer-overflow signed-integer-overflow cfi
 LOCAL_SANITIZE_DIAG := cfi
 
-LOCAL_SHARED_LIBRARIES := libmedia
+LOCAL_SHARED_LIBRARIES := \
+        libcrypto \
+        libmedia \
 
 LOCAL_MODULE:= libstagefright_mpeg2ts
 
diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp
index ae7ec77..f1b44ae 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.cpp
+++ b/media/libstagefright/mpeg2ts/ESQueue.cpp
@@ -42,7 +42,15 @@
     : mMode(mode),
       mFlags(flags),
       mEOSReached(false),
-      mCASystemId(0) {
+      mCASystemId(0),
+      mAUIndex(0) {
+
+    ALOGV("ElementaryStreamQueue(%p) mode %x  flags %x  isScrambled %d  isSampleEncrypted %d",
+            this, mode, flags, isScrambled(), isSampleEncrypted());
+
+    // Create the decryptor anyway since we don't know the use-case unless key is provided
+    // Won't decrypt if key info not available (e.g., scanner/extractor just parsing ts files)
+    mSampleDecryptor = isSampleEncrypted() ? new HlsSampleDecryptor : NULL;
 }
 
 sp<MetaData> ElementaryStreamQueue::getFormat() {
@@ -659,6 +667,9 @@
     unsigned syncStartPos = 0;  // in bytes
     unsigned payloadSize = 0;
     sp<MetaData> format = new MetaData;
+
+    ALOGV("dequeueAccessUnit_AC3[%d]: mBuffer %p(%zu)", mAUIndex, mBuffer->data(), mBuffer->size());
+
     while (true) {
         if (syncStartPos + 2 >= mBuffer->size()) {
             return NULL;
@@ -671,6 +682,10 @@
         if (payloadSize > 0) {
             break;
         }
+
+        ALOGV("dequeueAccessUnit_AC3[%d]: syncStartPos %u payloadSize %u",
+                mAUIndex, syncStartPos, payloadSize);
+
         ++syncStartPos;
     }
 
@@ -683,14 +698,22 @@
         mFormat = format;
     }
 
-    sp<ABuffer> accessUnit = new ABuffer(syncStartPos + payloadSize);
-    memcpy(accessUnit->data(), mBuffer->data(), syncStartPos + payloadSize);
 
     int64_t timeUs = fetchTimestamp(syncStartPos + payloadSize);
     if (timeUs < 0ll) {
         ALOGE("negative timeUs");
         return NULL;
     }
+
+    // Not decrypting if key info not available (e.g., scanner/extractor parsing ts files)
+    if (mSampleDecryptor != NULL) {
+        mSampleDecryptor->processAC3(mBuffer->data() + syncStartPos, payloadSize);
+    }
+    mAUIndex++;
+
+    sp<ABuffer> accessUnit = new ABuffer(syncStartPos + payloadSize);
+    memcpy(accessUnit->data(), mBuffer->data(), syncStartPos + payloadSize);
+
     accessUnit->meta()->setInt64("timeUs", timeUs);
     accessUnit->meta()->setInt32("isSync", 1);
 
@@ -791,6 +814,17 @@
         return NULL;
     }
 
+    ALOGV("dequeueAccessUnit_AAC[%d]: mBuffer %zu info.mLength %zu",
+            mAUIndex, mBuffer->size(), info.mLength);
+
+    struct ADTSPosition {
+        size_t offset;
+        size_t headerSize;
+        size_t length;
+    };
+
+    Vector<ADTSPosition> frames;
+
     // The idea here is consume all AAC frames starting at offsets before
     // info.mLength so we can assign a meaningful timestamp without
     // having to interpolate.
@@ -811,7 +845,7 @@
             return NULL;
         }
         bits.skipBits(3);  // ID, layer
-        bool protection_absent __unused = bits.getBits(1) != 0;
+        bool protection_absent = bits.getBits(1) != 0;
 
         if (mFormat == NULL) {
             unsigned profile = bits.getBits(2);
@@ -873,11 +907,36 @@
             return NULL;
         }
 
-        size_t headerSize __unused = protection_absent ? 7 : 9;
+        size_t headerSize = protection_absent ? 7 : 9;
+
+        // tracking the frame positions first then decrypt only if an accessUnit to be generated
+        if (mSampleDecryptor != NULL) {
+            ADTSPosition frame = {
+                .offset     = offset,
+                .headerSize = headerSize,
+                .length     = aac_frame_length
+            };
+
+            frames.push(frame);
+        }
 
         offset += aac_frame_length;
     }
 
+    // Decrypting only if the loop didn't exit early and an accessUnit is about to be generated
+    // Not decrypting if key info not available (e.g., scanner/extractor parsing ts files)
+    if (mSampleDecryptor != NULL) {
+        for (size_t frameId = 0; frameId < frames.size(); frameId++) {
+            const ADTSPosition &frame = frames.itemAt(frameId);
+
+            mSampleDecryptor->processAAC(frame.headerSize,
+                    mBuffer->data() + frame.offset, frame.length);
+//            ALOGV("dequeueAccessUnitAAC[%zu]: while offset %zu headerSize %zu frame_len %zu",
+//                    frameId, frame.offset, frame.headerSize, frame.length);
+        }
+    }
+    mAUIndex++;
+
     int64_t timeUs = fetchTimestamp(offset);
 
     sp<ABuffer> accessUnit = new ABuffer(offset);
@@ -970,6 +1029,9 @@
     size_t nalSize;
     bool foundSlice = false;
     bool foundIDR = false;
+
+    ALOGV("dequeueAccessUnit_H264[%d] %p/%zu", mAUIndex, data, size);
+
     while ((err = getNextNALUnit(&data, &size, &nalStart, &nalSize)) == OK) {
         if (nalSize == 0) continue;
 
@@ -981,6 +1043,7 @@
                 foundIDR = true;
             }
             if (foundSlice) {
+                //TODO: Shouldn't this have been called with nalSize-1?
                 ABitReader br(nalStart + 1, nalSize);
                 unsigned first_mb_in_slice = parseUE(&br);
 
@@ -1021,6 +1084,7 @@
 
             size_t dstOffset = 0;
             size_t seiIndex = 0;
+            size_t shrunkBytes = 0;
             for (size_t i = 0; i < nals.size(); ++i) {
                 const NALPosition &pos = nals.itemAt(i);
 
@@ -1047,11 +1111,30 @@
 
                 memcpy(accessUnit->data() + dstOffset, "\x00\x00\x00\x01", 4);
 
-                memcpy(accessUnit->data() + dstOffset + 4,
-                       mBuffer->data() + pos.nalOffset,
-                       pos.nalSize);
+                if (mSampleDecryptor != NULL && (nalType == 1 || nalType == 5)) {
+                    uint8_t *nalData = mBuffer->data() + pos.nalOffset;
+                    size_t newSize = mSampleDecryptor->processNal(nalData, pos.nalSize);
+                    // Note: the data can shrink due to unescaping
+                    memcpy(accessUnit->data() + dstOffset + 4,
+                            nalData,
+                            newSize);
+                    dstOffset += newSize + 4;
 
-                dstOffset += pos.nalSize + 4;
+                    size_t thisShrunkBytes = pos.nalSize - newSize;
+                    //ALOGV("dequeueAccessUnitH264[%d]: nalType: %d -> %zu (%zu)",
+                    //        nalType, (int)pos.nalSize, newSize, thisShrunkBytes);
+
+                    shrunkBytes += thisShrunkBytes;
+                }
+                else {
+                    memcpy(accessUnit->data() + dstOffset + 4,
+                            mBuffer->data() + pos.nalOffset,
+                            pos.nalSize);
+
+                    dstOffset += pos.nalSize + 4;
+                    //ALOGV("dequeueAccessUnitH264 [%d] %d @%d",
+                    //        nalType, (int)pos.nalSize, (int)pos.nalOffset);
+                }
             }
 
 #if !LOG_NDEBUG
@@ -1082,6 +1165,18 @@
                 mFormat = MakeAVCCodecSpecificData(accessUnit);
             }
 
+            if (mSampleDecryptor != NULL && shrunkBytes > 0) {
+                size_t adjustedSize = accessUnit->size() - shrunkBytes;
+                ALOGV("dequeueAccessUnitH264[%d]: AU size adjusted %zu -> %zu",
+                        mAUIndex, accessUnit->size(), adjustedSize);
+                accessUnit->setRange(0, adjustedSize);
+            }
+
+            ALOGV("dequeueAccessUnitH264[%d]: AU %p(%zu) dstOffset:%zu, nals:%zu, totalSize:%zu ",
+                    mAUIndex, accessUnit->data(), accessUnit->size(),
+                    dstOffset, nals.size(), totalSize);
+            mAUIndex++;
+
             return accessUnit;
         }
 
@@ -1612,4 +1707,15 @@
     return accessUnit;
 }
 
+void ElementaryStreamQueue::signalNewSampleAesKey(const sp<AMessage> &keyItem) {
+    if (mSampleDecryptor == NULL) {
+        ALOGE("signalNewSampleAesKey: Stream %x is not encrypted; keyItem: %p",
+                mMode, keyItem.get());
+        return;
+    }
+
+    mSampleDecryptor->signalNewSampleAesKey(keyItem);
+}
+
+
 }  // namespace android
diff --git a/media/libstagefright/mpeg2ts/ESQueue.h b/media/libstagefright/mpeg2ts/ESQueue.h
index 11e1af7..ffcb502 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.h
+++ b/media/libstagefright/mpeg2ts/ESQueue.h
@@ -19,11 +19,14 @@
 #define ES_QUEUE_H_
 
 #include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/AMessage.h>
 #include <utils/Errors.h>
 #include <utils/List.h>
 #include <utils/RefBase.h>
 #include <vector>
 
+#include "HlsSampleDecryptor.h"
+
 namespace android {
 
 struct ABuffer;
@@ -46,6 +49,7 @@
         // Data appended to the queue is always at access unit boundaries.
         kFlag_AlignedData = 1,
         kFlag_ScrambledData = 2,
+        kFlag_SampleEncryptedData = 4,
     };
     explicit ElementaryStreamQueue(Mode mode, uint32_t flags = 0);
 
@@ -69,6 +73,8 @@
 
     void setCasInfo(int32_t systemId, const std::vector<uint8_t> &sessionId);
 
+    void signalNewSampleAesKey(const sp<AMessage> &keyItem);
+
 private:
     struct RangeInfo {
         int64_t mTimestampUs;
@@ -100,6 +106,13 @@
 
     sp<MetaData> mFormat;
 
+    sp<HlsSampleDecryptor> mSampleDecryptor;
+    int mAUIndex;
+
+    bool isSampleEncrypted() const {
+        return (mFlags & kFlag_SampleEncryptedData) != 0;
+    }
+
     sp<ABuffer> dequeueAccessUnitH264();
     sp<ABuffer> dequeueAccessUnitAAC();
     sp<ABuffer> dequeueAccessUnitAC3();
diff --git a/media/libstagefright/mpeg2ts/HlsSampleDecryptor.cpp b/media/libstagefright/mpeg2ts/HlsSampleDecryptor.cpp
new file mode 100644
index 0000000..e32f676
--- /dev/null
+++ b/media/libstagefright/mpeg2ts/HlsSampleDecryptor.cpp
@@ -0,0 +1,336 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "HlsSampleDecryptor"
+
+#include "HlsSampleDecryptor.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/Utils.h>
+
+
+namespace android {
+
+HlsSampleDecryptor::HlsSampleDecryptor()
+    : mValidKeyInfo(false) {
+}
+
+HlsSampleDecryptor::HlsSampleDecryptor(const sp<AMessage> &sampleAesKeyItem)
+    : mValidKeyInfo(false) {
+
+    signalNewSampleAesKey(sampleAesKeyItem);
+}
+
+void HlsSampleDecryptor::signalNewSampleAesKey(const sp<AMessage> &sampleAesKeyItem) {
+
+    if (sampleAesKeyItem == NULL) {
+        mValidKeyInfo = false;
+        ALOGW("signalNewSampleAesKey: sampleAesKeyItem is NULL");
+        return;
+    }
+
+    sp<ABuffer> keyDataBuffer, initVecBuffer;
+    sampleAesKeyItem->findBuffer("keyData", &keyDataBuffer);
+    sampleAesKeyItem->findBuffer("initVec", &initVecBuffer);
+
+    if (keyDataBuffer != NULL && keyDataBuffer->size() == AES_BLOCK_SIZE &&
+        initVecBuffer != NULL && initVecBuffer->size() == AES_BLOCK_SIZE) {
+
+        ALOGV("signalNewSampleAesKey: Key: %s  IV: %s",
+              aesBlockToStr(keyDataBuffer->data()).c_str(),
+              aesBlockToStr(initVecBuffer->data()).c_str());
+
+        uint8_t KeyData[AES_BLOCK_SIZE];
+        memcpy(KeyData, keyDataBuffer->data(), AES_BLOCK_SIZE);
+        memcpy(mAESInitVec, initVecBuffer->data(), AES_BLOCK_SIZE);
+
+        mValidKeyInfo = (AES_set_decrypt_key(KeyData, 8*AES_BLOCK_SIZE/*128*/, &mAesKey) == 0);
+        if (!mValidKeyInfo) {
+            ALOGE("signalNewSampleAesKey: failed to set AES decryption key.");
+        }
+
+    } else {
+        // Media scanner might try extract/parse the TS files without knowing the key.
+        // Otherwise, shouldn't get here (unless an invalid playlist has swaped SAMPLE-AES with
+        // NONE method while still sample-encrypted stream is parsed).
+
+        mValidKeyInfo = false;
+        ALOGE("signalNewSampleAesKey Can't decrypt; keyDataBuffer: %p(%zu) initVecBuffer: %p(%zu)",
+              keyDataBuffer.get(), (keyDataBuffer.get() == NULL)? -1 : keyDataBuffer->size(),
+              initVecBuffer.get(), (initVecBuffer.get() == NULL)? -1 : initVecBuffer->size());
+    }
+}
+
+size_t HlsSampleDecryptor::processNal(uint8_t *nalData, size_t nalSize) {
+
+    unsigned nalType = nalData[0] & 0x1f;
+    if (!mValidKeyInfo) {
+        ALOGV("processNal[%d]: (%p)/%zu Skipping due to invalid key", nalType, nalData, nalSize);
+        return nalSize;
+    }
+
+    bool isEncrypted = (nalSize > VIDEO_CLEAR_LEAD + AES_BLOCK_SIZE);
+    ALOGV("processNal[%d]: (%p)/%zu isEncrypted: %d", nalType, nalData, nalSize, isEncrypted);
+
+    if (isEncrypted) {
+        // Encrypted NALUs have extra start code emulation prevention that must be
+        // stripped out before we can decrypt it.
+        size_t newSize = unescapeStream(nalData, nalSize);
+
+        ALOGV("processNal:unescapeStream[%d]: %zu -> %zu", nalType, nalSize, newSize);
+        nalSize = newSize;
+
+        //Encrypted_nal_unit () {
+        //    nal_unit_type_byte                // 1 byte
+        //    unencrypted_leader                // 31 bytes
+        //    while (bytes_remaining() > 0) {
+        //        if (bytes_remaining() > 16) {
+        //            encrypted_block           // 16 bytes
+        //        }
+        //        unencrypted_block           // MIN(144, bytes_remaining()) bytes
+        //    }
+        //}
+
+        size_t offset = VIDEO_CLEAR_LEAD;
+        size_t remainingBytes = nalSize - VIDEO_CLEAR_LEAD;
+
+        // a copy of initVec as decryptBlock updates it
+        unsigned char AESInitVec[AES_BLOCK_SIZE];
+        memcpy(AESInitVec, mAESInitVec, AES_BLOCK_SIZE);
+
+        while (remainingBytes > 0) {
+            // encrypted_block: protected block uses 10% skip encryption
+            if (remainingBytes > AES_BLOCK_SIZE) {
+                uint8_t *encrypted = nalData + offset;
+                status_t ret = decryptBlock(encrypted, AES_BLOCK_SIZE, AESInitVec);
+                if (ret != OK) {
+                    ALOGE("processNal failed with %d", ret);
+                    return nalSize; // revisit this
+                }
+
+                offset += AES_BLOCK_SIZE;
+                remainingBytes -= AES_BLOCK_SIZE;
+            }
+
+            // unencrypted_block
+            size_t clearBytes = std::min(remainingBytes, (size_t)(9 * AES_BLOCK_SIZE));
+
+            offset += clearBytes;
+            remainingBytes -= clearBytes;
+        } // while
+
+    } else { // isEncrypted == false
+        ALOGV("processNal[%d]: Unencrypted NALU  (%p)/%zu", nalType, nalData, nalSize);
+    }
+
+    return nalSize;
+}
+
+void HlsSampleDecryptor::processAAC(size_t adtsHdrSize, uint8_t *data, size_t size) {
+
+    if (!mValidKeyInfo) {
+        ALOGV("processAAC: (%p)/%zu Skipping due to invalid key", data, size);
+        return;
+    }
+
+    // ADTS header is included in the size
+    size_t offset = adtsHdrSize;
+    size_t remainingBytes = size - adtsHdrSize;
+
+    bool isEncrypted = (remainingBytes >= AUDIO_CLEAR_LEAD + AES_BLOCK_SIZE);
+    ALOGV("processAAC: header: %zu data: %p(%zu) isEncrypted: %d",
+          adtsHdrSize, data, size, isEncrypted);
+
+    //Encrypted_AAC_Frame () {
+    //    ADTS_Header                        // 7 or 9 bytes
+    //    unencrypted_leader                 // 16 bytes
+    //    while (bytes_remaining() >= 16) {
+    //        encrypted_block                // 16 bytes
+    //    }
+    //    unencrypted_trailer                // 0-15 bytes
+    //}
+
+    // with lead bytes
+    if (remainingBytes >= AUDIO_CLEAR_LEAD) {
+        offset += AUDIO_CLEAR_LEAD;
+        remainingBytes -= AUDIO_CLEAR_LEAD;
+
+        // encrypted_block
+        if (remainingBytes >= AES_BLOCK_SIZE) {
+
+            size_t encryptedBytes = (remainingBytes / AES_BLOCK_SIZE) * AES_BLOCK_SIZE;
+            unsigned char AESInitVec[AES_BLOCK_SIZE];
+            memcpy(AESInitVec, mAESInitVec, AES_BLOCK_SIZE);
+
+            // decrypting all blocks at once
+            uint8_t *encrypted = data + offset;
+            status_t ret = decryptBlock(encrypted, encryptedBytes, AESInitVec);
+            if (ret != OK) {
+                ALOGE("processAAC: decryptBlock failed with %d", ret);
+                return;
+            }
+
+            offset += encryptedBytes;
+            remainingBytes -= encryptedBytes;
+        } // encrypted
+
+        // unencrypted_trailer
+        size_t clearBytes = remainingBytes;
+        if (clearBytes > 0) {
+            CHECK(clearBytes < AES_BLOCK_SIZE);
+        }
+
+    } else { // without lead bytes
+        ALOGV("processAAC: Unencrypted frame (without lead bytes) size %zu = %zu (hdr) + %zu (rem)",
+              size, adtsHdrSize, remainingBytes);
+    }
+
+}
+
+void HlsSampleDecryptor::processAC3(uint8_t *data, size_t size) {
+
+    if (!mValidKeyInfo) {
+        ALOGV("processAC3: (%p)/%zu Skipping due to invalid key", data, size);
+        return;
+    }
+
+    bool isEncrypted = (size >= AUDIO_CLEAR_LEAD + AES_BLOCK_SIZE);
+    ALOGV("processAC3 %p(%zu) isEncrypted: %d", data, size, isEncrypted);
+
+    //Encrypted_AC3_Frame () {
+    //    unencrypted_leader                 // 16 bytes
+    //    while (bytes_remaining() >= 16) {
+    //        encrypted_block                // 16 bytes
+    //    }
+    //    unencrypted_trailer                // 0-15 bytes
+    //}
+
+    if (size >= AUDIO_CLEAR_LEAD) {
+        // unencrypted_leader
+        size_t offset = AUDIO_CLEAR_LEAD;
+        size_t remainingBytes = size - AUDIO_CLEAR_LEAD;
+
+        if (remainingBytes >= AES_BLOCK_SIZE) {
+
+            size_t encryptedBytes = (remainingBytes / AES_BLOCK_SIZE) * AES_BLOCK_SIZE;
+
+            // encrypted_block
+            unsigned char AESInitVec[AES_BLOCK_SIZE];
+            memcpy(AESInitVec, mAESInitVec, AES_BLOCK_SIZE);
+
+            // decrypting all blocks at once
+            uint8_t *encrypted = data + offset;
+            status_t ret = decryptBlock(encrypted, encryptedBytes, AESInitVec);
+            if (ret != OK) {
+                ALOGE("processAC3: decryptBlock failed with %d", ret);
+                return;
+            }
+
+            offset += encryptedBytes;
+            remainingBytes -= encryptedBytes;
+        } // encrypted
+
+        // unencrypted_trailer
+        size_t clearBytes = remainingBytes;
+        if (clearBytes > 0) {
+            CHECK(clearBytes < AES_BLOCK_SIZE);
+        }
+
+    } else {
+        ALOGV("processAC3: Unencrypted frame (without lead bytes) size %zu", size);
+    }
+}
+
+// Unescapes data replacing occurrences of [0, 0, 3] with [0, 0] and returns the new size
+size_t HlsSampleDecryptor::unescapeStream(uint8_t *data, size_t limit) const {
+    Vector<size_t> scratchEscapePositions;
+    size_t position = 0;
+
+    while (position < limit) {
+        position = findNextUnescapeIndex(data, position, limit);
+        if (position < limit) {
+            scratchEscapePositions.add(position);
+            position += 3;
+        }
+    }
+
+    size_t scratchEscapeCount = scratchEscapePositions.size();
+    size_t escapedPosition = 0; // The position being read from.
+    size_t unescapedPosition = 0; // The position being written to.
+    for (size_t i = 0; i < scratchEscapeCount; i++) {
+        size_t nextEscapePosition = scratchEscapePositions[i];
+        //TODO: add 2 and get rid of the later = 0 assignments
+        size_t copyLength = nextEscapePosition - escapedPosition;
+        memmove(data+unescapedPosition, data+escapedPosition, copyLength);
+        unescapedPosition += copyLength;
+        data[unescapedPosition++] = 0;
+        data[unescapedPosition++] = 0;
+        escapedPosition += copyLength + 3;
+    }
+
+    size_t unescapedLength = limit - scratchEscapeCount;
+    size_t remainingLength = unescapedLength - unescapedPosition;
+    memmove(data+unescapedPosition, data+escapedPosition, remainingLength);
+
+    return unescapedLength;
+}
+
+size_t HlsSampleDecryptor::findNextUnescapeIndex(uint8_t *data, size_t offset, size_t limit) const {
+    for (size_t i = offset; i < limit - 2; i++) {
+        //TODO: speed
+        if (data[i] == 0x00 && data[i + 1] == 0x00 && data[i + 2] == 0x03) {
+            return i;
+        }
+    }
+    return limit;
+}
+
+status_t HlsSampleDecryptor::decryptBlock(uint8_t *buffer, size_t size,
+        uint8_t AESInitVec[AES_BLOCK_SIZE]) {
+    if (size == 0) {
+        return OK;
+    }
+
+    if ((size % AES_BLOCK_SIZE) != 0) {
+        ALOGE("decryptBlock: size (%zu) not a multiple of block size", size);
+        return ERROR_MALFORMED;
+    }
+
+    ALOGV("decryptBlock: %p (%zu)", buffer, size);
+
+    AES_cbc_encrypt(buffer, buffer, size, &mAesKey, AESInitVec, AES_DECRYPT);
+
+    return OK;
+}
+
+AString HlsSampleDecryptor::aesBlockToStr(uint8_t block[AES_BLOCK_SIZE]) {
+    AString result;
+
+    if (block == NULL) {
+        result = AString("null");
+    } else {
+        result = AStringPrintf("0x%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
+            block[0], block[1], block[2], block[3], block[4], block[5], block[6], block[7],
+            block[8], block[9], block[10], block[11], block[12], block[13], block[14], block[15]);
+    }
+
+    return result;
+}
+
+
+}  // namespace android
diff --git a/media/libstagefright/mpeg2ts/HlsSampleDecryptor.h b/media/libstagefright/mpeg2ts/HlsSampleDecryptor.h
new file mode 100644
index 0000000..2c76620
--- /dev/null
+++ b/media/libstagefright/mpeg2ts/HlsSampleDecryptor.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SAMPLE_AES_PROCESSOR_H_
+
+#define SAMPLE_AES_PROCESSOR_H_
+
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/AString.h>
+
+#include <openssl/aes.h>
+
+#include <utils/Errors.h>
+#include <utils/List.h>
+#include <utils/RefBase.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+struct HlsSampleDecryptor : RefBase {
+
+    HlsSampleDecryptor();
+    explicit HlsSampleDecryptor(const sp<AMessage> &sampleAesKeyItem);
+
+    void signalNewSampleAesKey(const sp<AMessage> &sampleAesKeyItem);
+
+    size_t processNal(uint8_t *nalData, size_t nalSize);
+    void processAAC(size_t adtsHdrSize, uint8_t *data, size_t size);
+    void processAC3(uint8_t *data, size_t size);
+
+    static AString aesBlockToStr(uint8_t block[AES_BLOCK_SIZE]);
+
+private:
+    size_t unescapeStream(uint8_t *data, size_t limit) const;
+    size_t findNextUnescapeIndex(uint8_t *data, size_t offset, size_t limit) const;
+    status_t decryptBlock(uint8_t *buffer, size_t size, uint8_t AESInitVec[AES_BLOCK_SIZE]);
+
+    static const int VIDEO_CLEAR_LEAD = 32;
+    static const int AUDIO_CLEAR_LEAD = 16;
+
+    AES_KEY mAesKey;
+    uint8_t mAESInitVec[AES_BLOCK_SIZE];
+    bool mValidKeyInfo;
+
+    DISALLOW_EVIL_CONSTRUCTORS(HlsSampleDecryptor);
+};
+
+}  // namespace android
+
+#endif // SAMPLE_AES_PROCESSOR_H_
diff --git a/media/libstagefright/omx/1.0/Omx.cpp b/media/libstagefright/omx/1.0/Omx.cpp
index 365ea5d..64b2c08 100644
--- a/media/libstagefright/omx/1.0/Omx.cpp
+++ b/media/libstagefright/omx/1.0/Omx.cpp
@@ -180,26 +180,22 @@
         return OK;
     }
 
-    wp<IBase> observer;
     {
         Mutex::Autolock autoLock(mLock);
         ssize_t observerIndex = mNode2Observer.indexOfKey(instance.get());
-        if (observerIndex < 0) {
-            return OK;
-        }
-        observer = mNode2Observer.valueAt(observerIndex);
-        ssize_t nodeIndex = mLiveNodes.indexOfKey(observer);
-        if (nodeIndex < 0) {
-            return OK;
-        }
-        mNode2Observer.removeItemsAt(observerIndex);
-        mLiveNodes.removeItemsAt(nodeIndex);
-    }
-
-    {
-        sp<IBase> sObserver = observer.promote();
-        if (sObserver != nullptr) {
-            sObserver->unlinkToDeath(this);
+        if (observerIndex >= 0) {
+            wp<IBase> observer = mNode2Observer.valueAt(observerIndex);
+            ssize_t nodeIndex = mLiveNodes.indexOfKey(observer);
+            if (nodeIndex >= 0) {
+                mNode2Observer.removeItemsAt(observerIndex);
+                mLiveNodes.removeItemsAt(nodeIndex);
+                sp<IBase> sObserver = observer.promote();
+                if (sObserver != nullptr) {
+                    sObserver->unlinkToDeath(this);
+                }
+            } else {
+                LOG(WARNING) << "Inconsistent observer record";
+            }
         }
     }
 
diff --git a/media/libstagefright/omx/1.0/OmxStore.cpp b/media/libstagefright/omx/1.0/OmxStore.cpp
new file mode 100644
index 0000000..0e37af9
--- /dev/null
+++ b/media/libstagefright/omx/1.0/OmxStore.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <ios>
+#include <list>
+
+#include <android-base/logging.h>
+
+#include "Conversion.h"
+#include "OmxStore.h"
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace omx {
+namespace V1_0 {
+namespace implementation {
+
+OmxStore::OmxStore() {
+}
+
+OmxStore::~OmxStore() {
+}
+
+Return<void> OmxStore::listServiceAttributes(listServiceAttributes_cb _hidl_cb) {
+    _hidl_cb(toStatus(NO_ERROR), hidl_vec<ServiceAttribute>());
+    return Void();
+}
+
+Return<void> OmxStore::getNodePrefix(getNodePrefix_cb _hidl_cb) {
+    _hidl_cb(hidl_string());
+    return Void();
+}
+
+Return<void> OmxStore::listRoles(listRoles_cb _hidl_cb) {
+    _hidl_cb(hidl_vec<RoleInfo>());
+    return Void();
+}
+
+Return<sp<IOmx>> OmxStore::getOmx(hidl_string const& omxName) {
+    return IOmx::tryGetService(omxName);
+}
+
+// Methods from ::android::hidl::base::V1_0::IBase follow.
+
+IOmxStore* HIDL_FETCH_IOmxStore(const char* /* name */) {
+    return new OmxStore();
+}
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace omx
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
diff --git a/media/libstagefright/omx/1.0/OmxStore.h b/media/libstagefright/omx/1.0/OmxStore.h
new file mode 100644
index 0000000..f377f5a
--- /dev/null
+++ b/media/libstagefright/omx/1.0/OmxStore.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_MEDIA_OMX_V1_0_OMXSTORE_H
+#define ANDROID_HARDWARE_MEDIA_OMX_V1_0_OMXSTORE_H
+
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+
+#include <android/hardware/media/omx/1.0/IOmxStore.h>
+
+namespace android {
+namespace hardware {
+namespace media {
+namespace omx {
+namespace V1_0 {
+namespace implementation {
+
+using ::android::hardware::media::omx::V1_0::IOmxStore;
+using ::android::hardware::media::omx::V1_0::IOmx;
+using ::android::hardware::media::omx::V1_0::Status;
+using ::android::hidl::base::V1_0::IBase;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::sp;
+using ::android::wp;
+
+struct OmxStore : public IOmxStore {
+    OmxStore();
+    virtual ~OmxStore();
+
+    // Methods from IOmx
+    Return<void> listServiceAttributes(listServiceAttributes_cb) override;
+    Return<void> getNodePrefix(getNodePrefix_cb) override;
+    Return<void> listRoles(listRoles_cb) override;
+    Return<sp<IOmx>> getOmx(hidl_string const&) override;
+};
+
+extern "C" IOmxStore* HIDL_FETCH_IOmxStore(const char* name);
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace omx
+}  // namespace media
+}  // namespace hardware
+}  // namespace android
+
+#endif  // ANDROID_HARDWARE_MEDIA_OMX_V1_0_OMXSTORE_H
diff --git a/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp b/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp
index 36bd624..650db8e 100644
--- a/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp
+++ b/media/libstagefright/omx/1.0/WGraphicBufferProducer.cpp
@@ -248,7 +248,7 @@
 
 Return<int32_t> TWGraphicBufferProducer::setSidebandStream(const hidl_handle& stream) {
     return static_cast<int32_t>(mBase->setSidebandStream(NativeHandle::create(
-            native_handle_clone(stream), true)));
+            stream ? native_handle_clone(stream) : NULL, true)));
 }
 
 Return<void> TWGraphicBufferProducer::allocateBuffers(
diff --git a/media/libstagefright/omx/1.0/WGraphicBufferSource.cpp b/media/libstagefright/omx/1.0/WGraphicBufferSource.cpp
index 3c2face..e876306 100644
--- a/media/libstagefright/omx/1.0/WGraphicBufferSource.cpp
+++ b/media/libstagefright/omx/1.0/WGraphicBufferSource.cpp
@@ -192,8 +192,8 @@
 }
 
 Return<Status> TWGraphicBufferSource::setTimeLapseConfig(
-        int64_t timePerFrameUs, int64_t timePerCaptureUs) {
-    return toStatus(mBase->setTimeLapseConfig(timePerFrameUs, timePerCaptureUs));
+        double fps, double captureFps) {
+    return toStatus(mBase->setTimeLapseConfig(fps, captureFps));
 }
 
 Return<Status> TWGraphicBufferSource::setStartTimeUs(int64_t startTimeUs) {
@@ -204,6 +204,13 @@
     return toStatus(mBase->setStopTimeUs(stopTimeUs));
 }
 
+Return<void> TWGraphicBufferSource::getStopTimeOffsetUs(
+        getStopTimeOffsetUs_cb _hidl_cb) {
+    // TODO: Implement this when needed.
+    _hidl_cb(Status::OK, 0);
+    return Void();
+}
+
 Return<Status> TWGraphicBufferSource::setColorAspects(
         const ColorAspects& aspects) {
     return toStatus(mBase->setColorAspects(toCompactColorAspects(aspects)));
diff --git a/media/libstagefright/omx/1.0/WGraphicBufferSource.h b/media/libstagefright/omx/1.0/WGraphicBufferSource.h
index 73b86b8..4549c97 100644
--- a/media/libstagefright/omx/1.0/WGraphicBufferSource.h
+++ b/media/libstagefright/omx/1.0/WGraphicBufferSource.h
@@ -78,10 +78,10 @@
     Return<Status> setSuspend(bool suspend, int64_t timeUs) override;
     Return<Status> setRepeatPreviousFrameDelayUs(int64_t repeatAfterUs) override;
     Return<Status> setMaxFps(float maxFps) override;
-    Return<Status> setTimeLapseConfig(
-            int64_t timePerFrameUs, int64_t timePerCaptureUs) override;
+    Return<Status> setTimeLapseConfig(double fps, double captureFps) override;
     Return<Status> setStartTimeUs(int64_t startTimeUs) override;
     Return<Status> setStopTimeUs(int64_t stopTimeUs) override;
+    Return<void> getStopTimeOffsetUs(getStopTimeOffsetUs_cb _hidl_cb) override;
     Return<Status> setColorAspects(const ColorAspects& aspects) override;
     Return<Status> setTimeOffsetUs(int64_t timeOffsetUs) override;
     Return<Status> signalEndOfInputStream() override;
diff --git a/media/libstagefright/omx/Android.mk b/media/libstagefright/omx/Android.mk
index 90333ef..29e2ccc 100644
--- a/media/libstagefright/omx/Android.mk
+++ b/media/libstagefright/omx/Android.mk
@@ -15,6 +15,7 @@
         SoftVideoDecoderOMXComponent.cpp   \
         SoftVideoEncoderOMXComponent.cpp   \
         1.0/Omx.cpp                        \
+        1.0/OmxStore.cpp                   \
         1.0/WGraphicBufferProducer.cpp     \
         1.0/WProducerListener.cpp          \
         1.0/WGraphicBufferSource.cpp       \
diff --git a/media/libstagefright/omx/BWGraphicBufferSource.cpp b/media/libstagefright/omx/BWGraphicBufferSource.cpp
index 4e0f6dd..f2a454f 100644
--- a/media/libstagefright/omx/BWGraphicBufferSource.cpp
+++ b/media/libstagefright/omx/BWGraphicBufferSource.cpp
@@ -145,9 +145,9 @@
 }
 
 ::android::binder::Status BWGraphicBufferSource::setTimeLapseConfig(
-        int64_t timePerFrameUs, int64_t timePerCaptureUs) {
+        double fps, double captureFps) {
     return Status::fromStatusT(mBase->setTimeLapseConfig(
-            timePerFrameUs, timePerCaptureUs));
+            fps, captureFps));
 }
 
 ::android::binder::Status BWGraphicBufferSource::setStartTimeUs(
diff --git a/media/libstagefright/omx/BWGraphicBufferSource.h b/media/libstagefright/omx/BWGraphicBufferSource.h
index f1ce2af..43763c2 100644
--- a/media/libstagefright/omx/BWGraphicBufferSource.h
+++ b/media/libstagefright/omx/BWGraphicBufferSource.h
@@ -50,7 +50,7 @@
             int64_t repeatAfterUs) override;
     Status setMaxFps(float maxFps) override;
     Status setTimeLapseConfig(
-            int64_t timePerFrameUs, int64_t timePerCaptureUs) override;
+            double fps, double captureFps) override;
     Status setStartTimeUs(int64_t startTimeUs) override;
     Status setStopTimeUs(int64_t stopTimeUs) override;
     Status setColorAspects(int32_t aspects) override;
diff --git a/media/libstagefright/omx/GraphicBufferSource.cpp b/media/libstagefright/omx/GraphicBufferSource.cpp
index 5257b50..103a3e9 100644
--- a/media/libstagefright/omx/GraphicBufferSource.cpp
+++ b/media/libstagefright/omx/GraphicBufferSource.cpp
@@ -42,6 +42,7 @@
 
 #include <functional>
 #include <memory>
+#include <cmath>
 
 namespace android {
 
@@ -270,8 +271,11 @@
     mRepeatLastFrameGeneration(0),
     mOutstandingFrameRepeatCount(0),
     mFrameRepeatBlockedOnCodecBuffer(false),
-    mTimePerCaptureUs(-1ll),
-    mTimePerFrameUs(-1ll),
+    mFps(-1.0),
+    mCaptureFps(-1.0),
+    mBaseCaptureUs(-1ll),
+    mBaseFrameUs(-1ll),
+    mFrameCount(0),
     mPrevCaptureUs(-1ll),
     mPrevFrameUs(-1ll),
     mInputBufferTimeOffsetUs(0ll) {
@@ -713,26 +717,31 @@
     int64_t timeUs = bufferTimeNs / 1000;
     timeUs += mInputBufferTimeOffsetUs;
 
-    if (mTimePerCaptureUs > 0ll
-            && (mTimePerCaptureUs > 2 * mTimePerFrameUs
-            || mTimePerFrameUs > 2 * mTimePerCaptureUs)) {
+    if (mCaptureFps > 0.
+            && (mFps > 2 * mCaptureFps
+            || mCaptureFps > 2 * mFps)) {
         // Time lapse or slow motion mode
         if (mPrevCaptureUs < 0ll) {
             // first capture
-            mPrevCaptureUs = timeUs;
+            mPrevCaptureUs = mBaseCaptureUs = timeUs;
             // adjust the first sample timestamp.
-            mPrevFrameUs = (timeUs * mTimePerFrameUs) / mTimePerCaptureUs;
+            mPrevFrameUs = mBaseFrameUs =
+                    std::llround((timeUs * mCaptureFps) / mFps);
+            mFrameCount = 0;
         } else {
             // snap to nearest capture point
-            int64_t nFrames = (timeUs + mTimePerCaptureUs / 2 - mPrevCaptureUs)
-                    / mTimePerCaptureUs;
+            int64_t nFrames = std::llround(
+                    (timeUs - mPrevCaptureUs) * mCaptureFps);
             if (nFrames <= 0) {
                 // skip this frame as it's too close to previous capture
                 ALOGV("skipping frame, timeUs %lld", static_cast<long long>(timeUs));
                 return false;
             }
-            mPrevCaptureUs += mTimePerCaptureUs * nFrames;
-            mPrevFrameUs += mTimePerFrameUs * nFrames;
+            mFrameCount += nFrames;
+            mPrevCaptureUs = mBaseCaptureUs + std::llround(
+                    mFrameCount / mCaptureFps);
+            mPrevFrameUs = mBaseFrameUs + std::llround(
+                    mFrameCount / mFps);
         }
 
         ALOGV("timeUs %lld, captureUs %lld, frameUs %lld",
@@ -757,7 +766,6 @@
 status_t GraphicBufferSource::submitBuffer_l(const VideoBuffer &item) {
     CHECK(!mFreeCodecBuffers.empty());
     IOMX::buffer_id codecBufferId = *mFreeCodecBuffers.begin();
-    mFreeCodecBuffers.erase(mFreeCodecBuffers.begin());
 
     ALOGV("submitBuffer_l [slot=%d, bufferId=%d]", item.mBuffer->getSlot(), codecBufferId);
 
@@ -788,6 +796,8 @@
         return err;
     }
 
+    mFreeCodecBuffers.erase(mFreeCodecBuffers.begin());
+
     ssize_t cbix = mSubmittedCodecBuffers.add(codecBufferId, buffer);
     ALOGV("emptyGraphicBuffer succeeded, bufferId=%u@%zd bufhandle=%p",
             codecBufferId, cbix, graphicBuffer->handle);
@@ -806,7 +816,6 @@
         return;
     }
     IOMX::buffer_id codecBufferId = *mFreeCodecBuffers.begin();
-    mFreeCodecBuffers.erase(mFreeCodecBuffers.begin());
 
     // We reject any additional incoming graphic buffers. There is no acquired buffer used for EOS
     status_t err = mOMXNode->emptyBuffer(
@@ -814,6 +823,7 @@
     if (err != OK) {
         ALOGW("emptyDirectBuffer EOS failed: 0x%x", err);
     } else {
+        mFreeCodecBuffers.erase(mFreeCodecBuffers.begin());
         ssize_t cbix = mSubmittedCodecBuffers.add(codecBufferId, nullptr);
         ALOGV("submitEndOfInputStream_l: buffer submitted, bufferId=%u@%zd", codecBufferId, cbix);
         mEndOfStreamSent = true;
@@ -888,7 +898,7 @@
     ++mNumAvailableUnacquiredBuffers;
 
     // For BufferQueue we cannot acquire a buffer if we cannot immediately feed it to the codec
-    // OR we are discarding this buffer (acquiring and immediately releasing it), which makes
+    // UNLESS we are discarding this buffer (acquiring and immediately releasing it), which makes
     // this an ugly logic.
     // NOTE: We could also rely on our debug counter but that is meant only as a debug counter.
     if (!areWeDiscardingAvailableBuffers_l() && mFreeCodecBuffers.empty()) {
@@ -897,6 +907,7 @@
         ALOGV("onFrameAvailable: cannot acquire buffer right now, do it later");
 
         ++mRepeatLastFrameGeneration; // cancel any pending frame repeat
+        return;
     }
 
     VideoBuffer buffer;
@@ -1054,10 +1065,13 @@
         mOutstandingFrameRepeatCount = 0;
         mLatestBuffer.mBuffer.reset();
         mFrameRepeatBlockedOnCodecBuffer = false;
-        mTimePerCaptureUs = -1ll;
-        mTimePerFrameUs = -1ll;
+        mFps = -1.0;
+        mCaptureFps = -1.0;
+        mBaseCaptureUs = -1ll;
+        mBaseFrameUs = -1ll;
         mPrevCaptureUs = -1ll;
         mPrevFrameUs = -1ll;
+        mFrameCount = 0;
         mInputBufferTimeOffsetUs = 0;
         mStopTimeUs = -1;
         mActionQueue.clear();
@@ -1202,18 +1216,18 @@
     return OK;
 }
 
-status_t GraphicBufferSource::setTimeLapseConfig(int64_t timePerFrameUs, int64_t timePerCaptureUs) {
-    ALOGV("setTimeLapseConfig: timePerFrameUs=%lld, timePerCaptureUs=%lld",
-            (long long)timePerFrameUs, (long long)timePerCaptureUs);
+status_t GraphicBufferSource::setTimeLapseConfig(double fps, double captureFps) {
+    ALOGV("setTimeLapseConfig: fps=%lg, captureFps=%lg",
+            fps, captureFps);
 
     Mutex::Autolock autoLock(mMutex);
 
-    if (mExecuting || timePerFrameUs <= 0ll || timePerCaptureUs <= 0ll) {
+    if (mExecuting || !(fps > 0) || !(captureFps > 0)) {
         return INVALID_OPERATION;
     }
 
-    mTimePerFrameUs = timePerFrameUs;
-    mTimePerCaptureUs = timePerCaptureUs;
+    mFps = fps;
+    mCaptureFps = captureFps;
 
     return OK;
 }
diff --git a/media/libstagefright/omx/GraphicBufferSource.h b/media/libstagefright/omx/GraphicBufferSource.h
index 635cfd6..3df1aa1 100644
--- a/media/libstagefright/omx/GraphicBufferSource.h
+++ b/media/libstagefright/omx/GraphicBufferSource.h
@@ -162,7 +162,7 @@
     // Sets the time lapse (or slow motion) parameters.
     // When set, the sample's timestamp will be modified to playback framerate,
     // and capture timestamp will be modified to capture rate.
-    status_t setTimeLapseConfig(int64_t timePerFrameUs, int64_t timePerCaptureUs);
+    status_t setTimeLapseConfig(double fps, double captureFps);
 
     // Sets the start time us (in system time), samples before which should
     // be dropped and not submitted to encoder
@@ -417,27 +417,39 @@
     // Time lapse / slow motion configuration
     // --------------------------------------
 
-    // desired time interval between captured frames (capture interval) - value <= 0 if undefined
-    int64_t mTimePerCaptureUs;
+    // desired frame rate for encoding - value <= 0 if undefined
+    double mFps;
 
-    // desired time interval between encoded frames (media time interval) - value <= 0 if undefined
-    int64_t mTimePerFrameUs;
+    // desired frame rate for capture - value <= 0 if undefined
+    double mCaptureFps;
 
-    // Time lapse mode is enabled if capture interval is defined and it is more than twice the
-    // media time interval (if defined). In this mode frames that come in between the capture
-    // interval are dropped and the media timestamp is adjusted to have exactly the desired
-    // media time interval.
+    // Time lapse mode is enabled if the capture frame rate is defined and it is
+    // smaller than half the encoding frame rate (if defined). In this mode,
+    // frames that come in between the capture interval (the reciprocal of the
+    // capture frame rate) are dropped and the encoding timestamp is adjusted to
+    // match the desired encoding frame rate.
     //
-    // Slow motion mode is enabled if both media and capture intervals are defined and the media
-    // time interval is more than twice the capture interval. In this mode frames that come in
-    // between the capture interval are dropped (though there isn't expected to be any, but there
-    // could eventually be a frame drop if the actual capture interval is smaller than the
-    // configured capture interval). The media timestamp is adjusted to have exactly the desired
-    // media time interval.
+    // Slow motion mode is enabled if both encoding and capture frame rates are
+    // defined and the encoding frame rate is less than half the capture frame
+    // rate. In this mode, the source is expected to produce frames with an even
+    // timestamp interval (after rounding) with the configured capture fps. The
+    // first source timestamp is used as the source base time. Afterwards, the
+    // timestamp of each source frame is snapped to the nearest expected capture
+    // timestamp and scaled to match the configured encoding frame rate.
 
     // These modes must be enabled before using this source.
 
-    // adjusted capture timestamp for previous frame
+    // adjusted capture timestamp of the base frame
+    int64_t mBaseCaptureUs;
+
+    // adjusted encoding timestamp of the base frame
+    int64_t mBaseFrameUs;
+
+    // number of frames from the base time
+    int64_t mFrameCount;
+
+    // adjusted capture timestamp for previous frame (negative if there were
+    // none)
     int64_t mPrevCaptureUs;
 
     // adjusted media timestamp for previous frame (negative if there were none)
diff --git a/media/libstagefright/omx/OMXNodeInstance.cpp b/media/libstagefright/omx/OMXNodeInstance.cpp
index d0f64ca..db99ef2 100644
--- a/media/libstagefright/omx/OMXNodeInstance.cpp
+++ b/media/libstagefright/omx/OMXNodeInstance.cpp
@@ -1861,7 +1861,7 @@
 }
 
 void OMXNodeInstance::codecBufferFilled(omx_message &msg) {
-    Mutex::Autolock autoLock(mBufferIDLock);
+    Mutex::Autolock autoLock(mLock);
 
     if (mMaxTimestampGapUs <= 0ll || mRestorePtsFailed) {
         return;
diff --git a/media/ndk/NdkImage.cpp b/media/ndk/NdkImage.cpp
index 95fdf36..6d28d1b 100644
--- a/media/ndk/NdkImage.cpp
+++ b/media/ndk/NdkImage.cpp
@@ -31,12 +31,10 @@
 
 #define ALIGN(x, mask) ( ((x) + (mask) - 1) & ~((mask) - 1) )
 
-AImage::AImage(AImageReader* reader, int32_t format, uint64_t usage0, uint64_t usage1,
-        BufferItem* buffer, int64_t timestamp,
-        int32_t width, int32_t height, int32_t numPlanes) :
-        mReader(reader), mFormat(format), mUsage0(usage0), mUsage1(usage1),
-        mBuffer(buffer), mLockedBuffer(nullptr), mTimestamp(timestamp),
-        mWidth(width), mHeight(height), mNumPlanes(numPlanes) {
+AImage::AImage(AImageReader* reader, int32_t format, uint64_t usage, BufferItem* buffer,
+        int64_t timestamp, int32_t width, int32_t height, int32_t numPlanes) :
+        mReader(reader), mFormat(format), mUsage(usage), mBuffer(buffer), mLockedBuffer(nullptr),
+        mTimestamp(timestamp), mWidth(width), mHeight(height), mNumPlanes(numPlanes) {
 }
 
 // Can only be called by free() with mLock hold
@@ -178,9 +176,9 @@
         return AMEDIA_ERROR_INVALID_OBJECT;
     }
 
-    if ((mUsage0 & AHARDWAREBUFFER_USAGE0_CPU_READ_OFTEN) == 0) {
+    if ((mUsage & AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN) == 0) {
         ALOGE("%s: AImage %p does not have any software read usage bits set, usage=%" PRIu64 "",
-              __FUNCTION__, this, mUsage0);
+              __FUNCTION__, this, mUsage);
         return AMEDIA_IMGREADER_CANNOT_LOCK_IMAGE;
     }
 
@@ -191,13 +189,10 @@
 
     auto lockedBuffer = std::make_unique<CpuConsumer::LockedBuffer>();
 
-    uint64_t producerUsage;
-    uint64_t consumerUsage;
-    android_hardware_HardwareBuffer_convertToGrallocUsageBits(
-            &producerUsage, &consumerUsage, mUsage0, mUsage1);
+    uint64_t grallocUsage = android_hardware_HardwareBuffer_convertToGrallocUsageBits(mUsage);
 
     status_t ret =
-            lockImageFromBuffer(mBuffer, consumerUsage, mBuffer->mFence->dup(), lockedBuffer.get());
+            lockImageFromBuffer(mBuffer, grallocUsage, mBuffer->mFence->dup(), lockedBuffer.get());
     if (ret != OK) {
         ALOGE("%s: AImage %p failed to lock, error=%d", __FUNCTION__, this, ret);
         return AMEDIA_IMGREADER_CANNOT_LOCK_IMAGE;
diff --git a/media/ndk/NdkImagePriv.h b/media/ndk/NdkImagePriv.h
index 1fcb495..e9073d5 100644
--- a/media/ndk/NdkImagePriv.h
+++ b/media/ndk/NdkImagePriv.h
@@ -32,9 +32,8 @@
 
 // TODO: this only supports ImageReader
 struct AImage {
-    AImage(AImageReader* reader, int32_t format, uint64_t usage0, uint64_t usage1,
-           BufferItem* buffer, int64_t timestamp,
-           int32_t width, int32_t height, int32_t numPlanes);
+    AImage(AImageReader* reader, int32_t format, uint64_t usage, BufferItem* buffer,
+           int64_t timestamp, int32_t width, int32_t height, int32_t numPlanes);
 
     // free all resources while keeping object alive. Caller must obtain reader lock
     void close() { close(-1); }
@@ -75,8 +74,7 @@
     // When reader is close, AImage will only accept close API call
     wp<AImageReader>           mReader;
     const int32_t              mFormat;
-    const uint64_t             mUsage0;  // AHARDWAREBUFFER_USAGE0* flags.
-    const uint64_t             mUsage1;  // AHARDWAREBUFFER_USAGE1* flags.
+    const uint64_t             mUsage;  // AHARDWAREBUFFER_USAGE_* flags.
     BufferItem*                mBuffer;
     std::unique_ptr<CpuConsumer::LockedBuffer> mLockedBuffer;
     const int64_t              mTimestamp;
diff --git a/media/ndk/NdkImageReader.cpp b/media/ndk/NdkImageReader.cpp
index c449611..5d1a20b 100644
--- a/media/ndk/NdkImageReader.cpp
+++ b/media/ndk/NdkImageReader.cpp
@@ -238,14 +238,12 @@
 AImageReader::AImageReader(int32_t width,
                            int32_t height,
                            int32_t format,
-                           uint64_t usage0,
-                           uint64_t usage1,
+                           uint64_t usage,
                            int32_t maxImages)
     : mWidth(width),
       mHeight(height),
       mFormat(format),
-      mUsage0(usage0),
-      mUsage1(usage1),
+      mUsage(usage),
       mMaxImages(maxImages),
       mNumPlanes(getNumPlanesForFormat(format)),
       mFrameListener(new FrameListener(this)),
@@ -256,20 +254,14 @@
     PublicFormat publicFormat = static_cast<PublicFormat>(mFormat);
     mHalFormat = android_view_Surface_mapPublicFormatToHalFormat(publicFormat);
     mHalDataSpace = android_view_Surface_mapPublicFormatToHalDataspace(publicFormat);
-
-    uint64_t producerUsage;
-    uint64_t consumerUsage;
-    android_hardware_HardwareBuffer_convertToGrallocUsageBits(
-            &producerUsage, &consumerUsage, mUsage0, mUsage1);
-    // Strip out producerUsage here.
-    mHalUsage = android_convertGralloc1To0Usage(0, consumerUsage);
+    mHalUsage = android_hardware_HardwareBuffer_convertToGrallocUsageBits(mUsage);
 
     sp<IGraphicBufferProducer> gbProducer;
     sp<IGraphicBufferConsumer> gbConsumer;
     BufferQueue::createBufferQueue(&gbProducer, &gbConsumer);
 
-    String8 consumerName = String8::format("ImageReader-%dx%df%xu%" PRIu64 "u%" PRIu64 "m%d-%d-%d",
-            mWidth, mHeight, mFormat, mUsage0, mUsage1, mMaxImages, getpid(),
+    String8 consumerName = String8::format("ImageReader-%dx%df%xu%" PRIu64 "m%d-%d-%d",
+            mWidth, mHeight, mFormat, mUsage, mMaxImages, getpid(),
             createProcessUniqueId());
 
     mBufferItemConsumer =
@@ -445,10 +437,10 @@
     }
 
     if (mHalFormat == HAL_PIXEL_FORMAT_BLOB) {
-        *image = new AImage(this, mFormat, mUsage0, mUsage1, buffer, buffer->mTimestamp,
+        *image = new AImage(this, mFormat, mUsage, buffer, buffer->mTimestamp,
                 readerWidth, readerHeight, mNumPlanes);
     } else {
-        *image = new AImage(this, mFormat, mUsage0, mUsage1, buffer, buffer->mTimestamp,
+        *image = new AImage(this, mFormat, mUsage, buffer, buffer->mTimestamp,
                 bufferWidth, bufferHeight, mNumPlanes);
     }
     mAcquiredImages.push_back(*image);
@@ -587,12 +579,12 @@
         /*out*/AImageReader** reader) {
     ALOGV("%s", __FUNCTION__);
     return AImageReader_newWithUsage(
-            width, height, format, AHARDWAREBUFFER_USAGE0_CPU_READ_OFTEN, 0, maxImages, reader);
+            width, height, format, AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN, maxImages, reader);
 }
 
 EXPORT
 media_status_t AImageReader_newWithUsage(
-        int32_t width, int32_t height, int32_t format, uint64_t usage0, uint64_t usage1,
+        int32_t width, int32_t height, int32_t format, uint64_t usage,
         int32_t maxImages, /*out*/ AImageReader** reader) {
     ALOGV("%s", __FUNCTION__);
 
@@ -626,7 +618,7 @@
     }
 
     AImageReader* tmpReader = new AImageReader(
-        width, height, format, usage0, usage1, maxImages);
+        width, height, format, usage, maxImages);
     if (tmpReader == nullptr) {
         ALOGE("%s: AImageReader allocation failed", __FUNCTION__);
         return AMEDIA_ERROR_UNKNOWN;
diff --git a/media/ndk/NdkImageReaderPriv.h b/media/ndk/NdkImageReaderPriv.h
index a233ec8..35af169 100644
--- a/media/ndk/NdkImageReaderPriv.h
+++ b/media/ndk/NdkImageReaderPriv.h
@@ -55,8 +55,7 @@
     AImageReader(int32_t width,
                  int32_t height,
                  int32_t format,
-                 uint64_t usage0,
-                 uint64_t usage1,
+                 uint64_t usage,
                  int32_t maxImages);
     ~AImageReader();
 
@@ -117,8 +116,7 @@
     const int32_t mWidth;
     const int32_t mHeight;
     const int32_t mFormat;
-    const uint64_t mUsage0;  // AHARDWAREBUFFER_USAGE0* flags.
-    const uint64_t mUsage1;  // AHARDWAREBUFFER_USAGE1* flags.
+    const uint64_t mUsage;  // AHARDWAREBUFFER_USAGE_* flags.
     const int32_t mMaxImages;
 
     // TODO(jwcai) Seems completely unused in AImageReader class.
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 42e9c6b..9f19dfd 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -2676,12 +2676,14 @@
 // checkPlaybackThread_l() must be called with AudioFlinger::mLock held
 AudioFlinger::VolumeInterface *AudioFlinger::getVolumeInterface_l(audio_io_handle_t output) const
 {
-    VolumeInterface *volumeInterface = (VolumeInterface *)mPlaybackThreads.valueFor(output).get();
+    VolumeInterface *volumeInterface = mPlaybackThreads.valueFor(output).get();
     if (volumeInterface == nullptr) {
         MmapThread *mmapThread = mMmapThreads.valueFor(output).get();
         if (mmapThread != nullptr) {
             if (mmapThread->isOutput()) {
-                volumeInterface = (VolumeInterface *)mmapThread;
+                MmapPlaybackThread *mmapPlaybackThread =
+                        static_cast<MmapPlaybackThread *>(mmapThread);
+                volumeInterface = mmapPlaybackThread;
             }
         }
     }
@@ -2692,11 +2694,13 @@
 {
     Vector <VolumeInterface *> volumeInterfaces;
     for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
-        volumeInterfaces.add((VolumeInterface *)mPlaybackThreads.valueAt(i).get());
+        volumeInterfaces.add(mPlaybackThreads.valueAt(i).get());
     }
     for (size_t i = 0; i < mMmapThreads.size(); i++) {
         if (mMmapThreads.valueAt(i)->isOutput()) {
-            volumeInterfaces.add((VolumeInterface *)mMmapThreads.valueAt(i).get());
+            MmapPlaybackThread *mmapPlaybackThread =
+                    static_cast<MmapPlaybackThread *>(mMmapThreads.valueAt(i).get());
+            volumeInterfaces.add(mmapPlaybackThread);
         }
     }
     return volumeInterfaces;
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 3665875..a6857fe 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -2962,6 +2962,10 @@
     // and then that string will be logged at the next convenient opportunity.
     const char *logString = NULL;
 
+    // Estimated time for next buffer to be written to hal. This is used only on
+    // suspended mode (for now) to help schedule the wait time until next iteration.
+    nsecs_t timeLoopNextNs = 0;
+
     checkSilentMode_l();
 #if 0
     int z = 0; // used in logFormat example
@@ -3327,6 +3331,29 @@
             } else {
                 ATRACE_BEGIN("sleep");
                 Mutex::Autolock _l(mLock);
+                // suspended requires accurate metering of sleep time.
+                if (isSuspended()) {
+                    // advance by expected sleepTime
+                    timeLoopNextNs += microseconds((nsecs_t)mSleepTimeUs);
+                    const nsecs_t nowNs = systemTime();
+
+                    // compute expected next time vs current time.
+                    // (negative deltas are treated as delays).
+                    nsecs_t deltaNs = timeLoopNextNs - nowNs;
+                    if (deltaNs < -kMaxNextBufferDelayNs) {
+                        // Delays longer than the max allowed trigger a reset.
+                        ALOGV("DelayNs: %lld, resetting timeLoopNextNs", (long long) deltaNs);
+                        deltaNs = microseconds((nsecs_t)mSleepTimeUs);
+                        timeLoopNextNs = nowNs + deltaNs;
+                    } else if (deltaNs < 0) {
+                        // Delays within the max delay allowed: zero the delta/sleepTime
+                        // to help the system catch up in the next iteration(s)
+                        ALOGV("DelayNs: %lld, catching-up", (long long) deltaNs);
+                        deltaNs = 0;
+                    }
+                    // update sleep time (which is >= 0)
+                    mSleepTimeUs = deltaNs / 1000;
+                }
                 if (!mSignalPending && mConfigEvents.isEmpty() && !exitPending()) {
                     mWaitWorkCV.waitRelative(mLock, microseconds((nsecs_t)mSleepTimeUs));
                 }
@@ -8296,22 +8323,24 @@
             mEffectChains[0]->setVolume_l(&vol, &vol);
             volume = (float)vol / (1 << 24);
         }
-
-        mOutput->stream->setVolume(volume, volume);
-
-        sp<MmapStreamCallback> callback = mCallback.promote();
-        if (callback != 0) {
-            int channelCount;
-            if (isOutput()) {
-                channelCount = audio_channel_count_from_out_mask(mChannelMask);
+        // Try to use HW volume control and fall back to SW control if not implemented
+        if (mOutput->stream->setVolume(volume, volume) != NO_ERROR) {
+            sp<MmapStreamCallback> callback = mCallback.promote();
+            if (callback != 0) {
+                int channelCount;
+                if (isOutput()) {
+                    channelCount = audio_channel_count_from_out_mask(mChannelMask);
+                } else {
+                    channelCount = audio_channel_count_from_in_mask(mChannelMask);
+                }
+                Vector<float> values;
+                for (int i = 0; i < channelCount; i++) {
+                    values.add(volume);
+                }
+                callback->onVolumeChanged(mChannelMask, values);
             } else {
-                channelCount = audio_channel_count_from_in_mask(mChannelMask);
+                ALOGW("Could not set MMAP stream volume: no volume callback!");
             }
-            Vector<float> values;
-            for (int i = 0; i < channelCount; i++) {
-                values.add(volume);
-            }
-            callback->onVolumeChanged(mChannelMask, values);
         }
     }
 }
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 7469710..80b368e 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -623,6 +623,12 @@
     // 14 tracks max per client allows for 2 misbehaving application leaving 4 available tracks.
     static const uint32_t kMaxTracksPerUid = 14;
 
+    // Maximum delay (in nanoseconds) for upcoming buffers in suspend mode, otherwise
+    // if delay is greater, the estimated time for timeLoopNextNs is reset.
+    // This allows for catch-up to be done for small delays, while resetting the estimate
+    // for initial conditions or large delays.
+    static const nsecs_t kMaxNextBufferDelayNs = 100000000;
+
     PlaybackThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
                    audio_io_handle_t id, audio_devices_t device, type_t type, bool systemReady);
     virtual             ~PlaybackThread();
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 5e07e3b..8fefb36 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -489,7 +489,7 @@
 /*static*/ void AudioFlinger::PlaybackThread::Track::appendDumpHeader(String8& result)
 {
     result.append("    Name Active Client Type      Fmt Chn mask Session fCount S F SRate  "
-                  "L dB  R dB    Server Main buf  Aux buf Flags UndFrmCnt  Flushed\n");
+                  "L dB  R dB  VS dB    Server Main buf  Aux buf Flags UndFrmCnt  Flushed\n");
 }
 
 void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size, bool active)
@@ -555,8 +555,11 @@
         nowInUnderrun = '?';
         break;
     }
-    snprintf(&buffer[8], size-8, " %6s %6u %4u %08X %08X %7u %6zu %1c %1d %5u %5.2g %5.2g  "
-                                 "%08X %08zX %08zX 0x%03X %9u%c %7u\n",
+
+    std::pair<float /* volume */, bool /* active */> vsVolume = mVolumeHandler->getLastVolume();
+    snprintf(&buffer[8], size - 8, " %6s %6u %4u %08X %08X %7u %6zu %1c %1d %5u "
+                                   "%5.2g %5.2g %5.2g%c  "
+                                   "%08X %08zX %08zX 0x%03X %9u%c %7u\n",
             active ? "yes" : "no",
             (mClient == 0) ? getpid_cached : mClient->pid(),
             mStreamType,
@@ -569,6 +572,8 @@
             mAudioTrackServerProxy->getSampleRate(),
             20.0 * log10(float_from_gain(gain_minifloat_unpack_left(vlr))),
             20.0 * log10(float_from_gain(gain_minifloat_unpack_right(vlr))),
+            20.0 * log10(vsVolume.first), // VolumeShaper(s) total volume
+            vsVolume.second ? 'A' : ' ',  // if any VolumeShapers active
             mCblk->mServer,
             (size_t)mMainBuffer, // use %zX as %p appends 0x
             (size_t)mAuxBuffer,  // use %zX as %p appends 0x
@@ -592,7 +597,9 @@
     status_t status = mServerProxy->obtainBuffer(&buf);
     buffer->frameCount = buf.mFrameCount;
     buffer->raw = buf.mRaw;
-    if (buf.mFrameCount == 0) {
+    if (buf.mFrameCount == 0 && !isStopping() && !isStopped() && !isPaused()) {
+        ALOGV("underrun,  framesReady(%zu) < framesDesired(%zd), state: %d",
+                buf.mFrameCount, desiredFrames, mState);
         mAudioTrackServerProxy->tallyUnderrunFrames(desiredFrames);
     } else {
         mAudioTrackServerProxy->tallyUnderrunFrames(0);
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index e3a23f9..aaa6134 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -1007,11 +1007,14 @@
         if (offloadInfo != NULL) {
             config.offload_info = *offloadInfo;
         }
+        DeviceVector outputDevices = mAvailableOutputDevices.getDevicesFromType(device);
+        String8 address = outputDevices.size() > 0 ? outputDevices.itemAt(0)->mAddress
+                : String8("");
         status = mpClientInterface->openOutput(profile->getModuleHandle(),
                                                &output,
                                                &config,
                                                &outputDesc->mDevice,
-                                               String8(""),
+                                               address,
                                                &outputDesc->mLatency,
                                                outputDesc->mFlags);
 
@@ -1699,6 +1702,12 @@
     config.channel_mask = profileChannelMask;
     config.format = profileFormat;
 
+    if (address == "") {
+        DeviceVector inputDevices = mAvailableInputDevices.getDevicesFromType(device);
+        //   the inputs vector must be of size 1, but we don't want to crash here
+        address = inputDevices.size() > 0 ? inputDevices.itemAt(0)->mAddress : String8("");
+    }
+
     status_t status = mpClientInterface->openInput(profile->getModuleHandle(),
                                                    &input,
                                                    &config,
diff --git a/services/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk
index 2d78f99..ef2f3b3 100644
--- a/services/camera/libcameraservice/Android.mk
+++ b/services/camera/libcameraservice/Android.mk
@@ -27,7 +27,6 @@
     CameraFlashlight.cpp \
     common/Camera2ClientBase.cpp \
     common/CameraDeviceBase.cpp \
-    common/CameraModule.cpp \
     common/CameraProviderManager.cpp \
     common/FrameProcessorBase.cpp \
     api1/CameraClient.cpp \
diff --git a/services/camera/libcameraservice/CameraFlashlight.cpp b/services/camera/libcameraservice/CameraFlashlight.cpp
index 4537ae6..836972a 100644
--- a/services/camera/libcameraservice/CameraFlashlight.cpp
+++ b/services/camera/libcameraservice/CameraFlashlight.cpp
@@ -36,16 +36,9 @@
 // CameraFlashlight implementation begins
 // used by camera service to control flashflight.
 /////////////////////////////////////////////////////////////////////
-CameraFlashlight::CameraFlashlight(CameraModule* cameraModule,
-        camera_module_callbacks_t* callbacks) :
-        mCameraModule(cameraModule),
-        mCallbacks(callbacks),
-        mFlashlightMapInitialized(false) {
-}
 
 CameraFlashlight::CameraFlashlight(sp<CameraProviderManager> providerManager,
         camera_module_callbacks_t* callbacks) :
-        mCameraModule(nullptr),
         mProviderManager(providerManager),
         mCallbacks(callbacks),
         mFlashlightMapInitialized(false) {
@@ -61,52 +54,12 @@
         return INVALID_OPERATION;
     }
 
-    status_t res = OK;
-
-    if (mCameraModule == nullptr) {
-        if (mProviderManager->supportSetTorchMode(cameraId.string())) {
-            mFlashControl = new ProviderFlashControl(mProviderManager);
-        } else {
-            // Only HAL1 devices do not support setTorchMode
-            mFlashControl =
-                    new CameraHardwareInterfaceFlashControl(mProviderManager, *mCallbacks);
-        }
-    } else if (mCameraModule->getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_4) {
-        mFlashControl = new ModuleFlashControl(*mCameraModule);
-        if (mFlashControl == NULL) {
-            ALOGV("%s: cannot create flash control for module api v2.4+",
-                     __FUNCTION__);
-            return NO_MEMORY;
-        }
+    if (mProviderManager->supportSetTorchMode(cameraId.string())) {
+        mFlashControl = new ProviderFlashControl(mProviderManager);
     } else {
-        uint32_t deviceVersion = CAMERA_DEVICE_API_VERSION_1_0;
-
-        if (mCameraModule->getModuleApiVersion() >=
-                    CAMERA_MODULE_API_VERSION_2_0) {
-            camera_info info;
-            res = mCameraModule->getCameraInfo(
-                    atoi(cameraId.string()), &info);
-            if (res) {
-                ALOGE("%s: failed to get camera info for camera %s",
-                        __FUNCTION__, cameraId.string());
-                return res;
-            }
-            deviceVersion = info.device_version;
-        }
-
-        if (deviceVersion >= CAMERA_DEVICE_API_VERSION_3_0) {
-            CameraDeviceClientFlashControl *flashControl =
-                    new CameraDeviceClientFlashControl(*mCameraModule,
-                                                       *mCallbacks);
-            if (!flashControl) {
-                return NO_MEMORY;
-            }
-
-            mFlashControl = flashControl;
-        } else {
-            mFlashControl =
-                    new CameraHardwareInterfaceFlashControl(mCameraModule, *mCallbacks);
-        }
+        // Only HAL1 devices do not support setTorchMode
+        mFlashControl =
+                new CameraHardwareInterfaceFlashControl(mProviderManager, *mCallbacks);
     }
 
     return OK;
@@ -170,11 +123,7 @@
 }
 
 int CameraFlashlight::getNumberOfCameras() {
-    if (mCameraModule) {
-        return mCameraModule->getNumberOfCameras();
-    } else {
-        return mProviderManager->getStandardCameraCount();
-    }
+    return mProviderManager->getAPI1CompatibleCameraCount();
 }
 
 status_t CameraFlashlight::findFlashUnits() {
@@ -184,16 +133,10 @@
     std::vector<String8> cameraIds;
     int numberOfCameras = getNumberOfCameras();
     cameraIds.resize(numberOfCameras);
-    if (mCameraModule) {
-        for (size_t i = 0; i < cameraIds.size(); i++) {
-            cameraIds[i] = String8::format("%zu", i);
-        }
-    } else {
-        // No module, must be provider
-        std::vector<std::string> ids = mProviderManager->getStandardCameraDeviceIds();
-        for (size_t i = 0; i < cameraIds.size(); i++) {
-            cameraIds[i] = String8(ids[i].c_str());
-        }
+    // No module, must be provider
+    std::vector<std::string> ids = mProviderManager->getAPI1CompatibleCameraDeviceIds();
+    for (size_t i = 0; i < cameraIds.size(); i++) {
+        cameraIds[i] = String8(ids[i].c_str());
     }
 
     mHasFlashlightMap.clear();
@@ -251,9 +194,7 @@
 
 bool CameraFlashlight::isBackwardCompatibleMode(const String8& cameraId) {
     bool backwardCompatibleMode = false;
-    if (mCameraModule && mCameraModule->getModuleApiVersion() < CAMERA_MODULE_API_VERSION_2_4) {
-        backwardCompatibleMode = true;
-    } else if (mProviderManager != nullptr &&
+    if (mProviderManager != nullptr &&
             !mProviderManager->supportSetTorchMode(cameraId.string())) {
         backwardCompatibleMode = true;
     }
@@ -368,366 +309,13 @@
 // ProviderFlashControl implementation ends
 
 /////////////////////////////////////////////////////////////////////
-// ModuleFlashControl implementation begins
-// Flash control for camera module v2.4 and above.
-/////////////////////////////////////////////////////////////////////
-ModuleFlashControl::ModuleFlashControl(CameraModule& cameraModule) :
-        mCameraModule(&cameraModule) {
-}
-
-ModuleFlashControl::~ModuleFlashControl() {
-}
-
-status_t ModuleFlashControl::hasFlashUnit(const String8& cameraId, bool *hasFlash) {
-    if (!hasFlash) {
-        return BAD_VALUE;
-    }
-
-    *hasFlash = false;
-    Mutex::Autolock l(mLock);
-
-    camera_info info;
-    status_t res = mCameraModule->getCameraInfo(atoi(cameraId.string()),
-            &info);
-    if (res != 0) {
-        return res;
-    }
-
-    CameraMetadata metadata;
-    metadata = info.static_camera_characteristics;
-    camera_metadata_entry flashAvailable =
-            metadata.find(ANDROID_FLASH_INFO_AVAILABLE);
-    if (flashAvailable.count == 1 && flashAvailable.data.u8[0] == 1) {
-        *hasFlash = true;
-    }
-
-    return OK;
-}
-
-status_t ModuleFlashControl::setTorchMode(const String8& cameraId, bool enabled) {
-    ALOGV("%s: set camera %s torch mode to %d", __FUNCTION__,
-            cameraId.string(), enabled);
-
-    Mutex::Autolock l(mLock);
-    return mCameraModule->setTorchMode(cameraId.string(), enabled);
-}
-// ModuleFlashControl implementation ends
-
-/////////////////////////////////////////////////////////////////////
-// CameraDeviceClientFlashControl implementation begins
-// Flash control for camera module <= v2.3 and camera HAL v2-v3
-/////////////////////////////////////////////////////////////////////
-CameraDeviceClientFlashControl::CameraDeviceClientFlashControl(
-        CameraModule& cameraModule,
-        const camera_module_callbacks_t& callbacks) :
-        mCameraModule(&cameraModule),
-        mCallbacks(&callbacks),
-        mTorchEnabled(false),
-        mMetadata(NULL),
-        mStreaming(false) {
-}
-
-CameraDeviceClientFlashControl::~CameraDeviceClientFlashControl() {
-    disconnectCameraDevice();
-    if (mMetadata) {
-        delete mMetadata;
-    }
-
-    mSurface.clear();
-    mSurfaceTexture.clear();
-    mProducer.clear();
-    mConsumer.clear();
-
-    if (mTorchEnabled) {
-        if (mCallbacks) {
-            ALOGV("%s: notify the framework that torch was turned off",
-                    __FUNCTION__);
-            mCallbacks->torch_mode_status_change(mCallbacks,
-                    mCameraId.string(), TORCH_MODE_STATUS_AVAILABLE_OFF);
-        }
-    }
-}
-
-status_t CameraDeviceClientFlashControl::initializeSurface(
-        sp<CameraDeviceBase> &device, int32_t width, int32_t height) {
-    status_t res;
-    BufferQueue::createBufferQueue(&mProducer, &mConsumer);
-
-    mSurfaceTexture = new GLConsumer(mConsumer, 0, GLConsumer::TEXTURE_EXTERNAL,
-            true, true);
-    if (mSurfaceTexture == NULL) {
-        return NO_MEMORY;
-    }
-
-    int32_t format = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
-    res = mSurfaceTexture->setDefaultBufferSize(width, height);
-    if (res) {
-        return res;
-    }
-    res = mSurfaceTexture->setDefaultBufferFormat(format);
-    if (res) {
-        return res;
-    }
-
-    mSurface = new Surface(mProducer, /*useAsync*/ true);
-    if (mSurface == NULL) {
-        return NO_MEMORY;
-    }
-    res = device->createStream(mSurface, width, height, format,
-            HAL_DATASPACE_UNKNOWN, CAMERA3_STREAM_ROTATION_0, &mStreamId);
-    if (res) {
-        return res;
-    }
-
-    res = device->configureStreams();
-    if (res) {
-        return res;
-    }
-
-    return res;
-}
-
-status_t CameraDeviceClientFlashControl::getSmallestSurfaceSize(
-        const camera_info& info, int32_t *width, int32_t *height) {
-    if (!width || !height) {
-        return BAD_VALUE;
-    }
-
-    int32_t w = INT32_MAX;
-    int32_t h = 1;
-
-    CameraMetadata metadata;
-    metadata = info.static_camera_characteristics;
-    camera_metadata_entry streamConfigs =
-            metadata.find(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
-    for (size_t i = 0; i < streamConfigs.count; i += 4) {
-        int32_t fmt = streamConfigs.data.i32[i];
-        if (fmt == ANDROID_SCALER_AVAILABLE_FORMATS_IMPLEMENTATION_DEFINED) {
-            int32_t ww = streamConfigs.data.i32[i + 1];
-            int32_t hh = streamConfigs.data.i32[i + 2];
-
-            if (w * h > ww * hh) {
-                w = ww;
-                h = hh;
-            }
-        }
-    }
-
-    // if stream configuration is not found, try available processed sizes.
-    if (streamConfigs.count == 0) {
-        camera_metadata_entry availableProcessedSizes =
-            metadata.find(ANDROID_SCALER_AVAILABLE_PROCESSED_SIZES);
-        for (size_t i = 0; i < availableProcessedSizes.count; i += 2) {
-            int32_t ww = availableProcessedSizes.data.i32[i];
-            int32_t hh = availableProcessedSizes.data.i32[i + 1];
-            if (w * h > ww * hh) {
-                w = ww;
-                h = hh;
-            }
-        }
-    }
-
-    if (w == INT32_MAX) {
-        return NAME_NOT_FOUND;
-    }
-
-    *width = w;
-    *height = h;
-
-    return OK;
-}
-
-status_t CameraDeviceClientFlashControl::connectCameraDevice(
-        const String8& cameraId) {
-    camera_info info;
-    status_t res = mCameraModule->getCameraInfo(atoi(cameraId.string()), &info);
-    if (res != 0) {
-        ALOGE("%s: failed to get camera info for camera %s", __FUNCTION__,
-                cameraId.string());
-        return res;
-    }
-
-    sp<CameraDeviceBase> device =
-            new Camera3Device(cameraId);
-    if (device == NULL) {
-        return NO_MEMORY;
-    }
-
-    res = device->initialize(mCameraModule);
-    if (res) {
-        return res;
-    }
-
-    int32_t width, height;
-    res = getSmallestSurfaceSize(info, &width, &height);
-    if (res) {
-        return res;
-    }
-    res = initializeSurface(device, width, height);
-    if (res) {
-        return res;
-    }
-
-    mCameraId = cameraId;
-    mStreaming = (info.device_version <= CAMERA_DEVICE_API_VERSION_3_1);
-    mDevice = device;
-
-    return OK;
-}
-
-status_t CameraDeviceClientFlashControl::disconnectCameraDevice() {
-    if (mDevice != NULL) {
-        mDevice->disconnect();
-        mDevice.clear();
-    }
-
-    return OK;
-}
-
-
-
-status_t CameraDeviceClientFlashControl::hasFlashUnit(const String8& cameraId,
-        bool *hasFlash) {
-    ALOGV("%s: checking if camera %s has a flash unit", __FUNCTION__,
-            cameraId.string());
-
-    Mutex::Autolock l(mLock);
-    return hasFlashUnitLocked(cameraId, hasFlash);
-
-}
-
-status_t CameraDeviceClientFlashControl::hasFlashUnitLocked(
-        const String8& cameraId, bool *hasFlash) {
-    if (!hasFlash) {
-        return BAD_VALUE;
-    }
-
-    camera_info info;
-    status_t res = mCameraModule->getCameraInfo(
-            atoi(cameraId.string()), &info);
-    if (res != 0) {
-        ALOGE("%s: failed to get camera info for camera %s", __FUNCTION__,
-                cameraId.string());
-        return res;
-    }
-
-    CameraMetadata metadata;
-    metadata = info.static_camera_characteristics;
-    camera_metadata_entry flashAvailable =
-            metadata.find(ANDROID_FLASH_INFO_AVAILABLE);
-    if (flashAvailable.count == 1 && flashAvailable.data.u8[0] == 1) {
-        *hasFlash = true;
-    }
-
-    return OK;
-}
-
-status_t CameraDeviceClientFlashControl::submitTorchEnabledRequest() {
-    status_t res;
-
-    if (mMetadata == NULL) {
-        mMetadata = new CameraMetadata();
-        if (mMetadata == NULL) {
-            return NO_MEMORY;
-        }
-        res = mDevice->createDefaultRequest(
-                CAMERA3_TEMPLATE_PREVIEW, mMetadata);
-        if (res) {
-            return res;
-        }
-    }
-
-    uint8_t torchOn = ANDROID_FLASH_MODE_TORCH;
-    mMetadata->update(ANDROID_FLASH_MODE, &torchOn, 1);
-    mMetadata->update(ANDROID_REQUEST_OUTPUT_STREAMS, &mStreamId, 1);
-
-    uint8_t aeMode = ANDROID_CONTROL_AE_MODE_ON;
-    mMetadata->update(ANDROID_CONTROL_AE_MODE, &aeMode, 1);
-
-    int32_t requestId = 0;
-    mMetadata->update(ANDROID_REQUEST_ID, &requestId, 1);
-
-    if (mStreaming) {
-        res = mDevice->setStreamingRequest(*mMetadata);
-    } else {
-        res = mDevice->capture(*mMetadata);
-    }
-    return res;
-}
-
-
-
-
-status_t CameraDeviceClientFlashControl::setTorchMode(
-        const String8& cameraId, bool enabled) {
-    bool hasFlash = false;
-
-    Mutex::Autolock l(mLock);
-    status_t res = hasFlashUnitLocked(cameraId, &hasFlash);
-
-    // pre-check
-    if (enabled) {
-        // invalid camera?
-        if (res) {
-            return -EINVAL;
-        }
-        // no flash unit?
-        if (!hasFlash) {
-            return -ENOSYS;
-        }
-        // already opened for a different device?
-        if (mDevice != NULL && cameraId != mCameraId) {
-            return BAD_INDEX;
-        }
-    } else if (mDevice == NULL || cameraId != mCameraId) {
-        // disabling the torch mode of an un-opened or different device.
-        return OK;
-    } else {
-        // disabling the torch mode of currently opened device
-        disconnectCameraDevice();
-        mTorchEnabled = false;
-        mCallbacks->torch_mode_status_change(mCallbacks,
-            cameraId.string(), TORCH_MODE_STATUS_AVAILABLE_OFF);
-        return OK;
-    }
-
-    if (mDevice == NULL) {
-        res = connectCameraDevice(cameraId);
-        if (res) {
-            return res;
-        }
-    }
-
-    res = submitTorchEnabledRequest();
-    if (res) {
-        return res;
-    }
-
-    mTorchEnabled = true;
-    mCallbacks->torch_mode_status_change(mCallbacks,
-            cameraId.string(), TORCH_MODE_STATUS_AVAILABLE_ON);
-    return OK;
-}
-// CameraDeviceClientFlashControl implementation ends
-
-
-/////////////////////////////////////////////////////////////////////
 // CameraHardwareInterfaceFlashControl implementation begins
 // Flash control for camera module <= v2.3 and camera HAL v1
 /////////////////////////////////////////////////////////////////////
-CameraHardwareInterfaceFlashControl::CameraHardwareInterfaceFlashControl(
-        CameraModule* cameraModule,
-        const camera_module_callbacks_t& callbacks) :
-        mCameraModule(cameraModule),
-        mProviderManager(nullptr),
-        mCallbacks(&callbacks),
-        mTorchEnabled(false) {
-}
 
 CameraHardwareInterfaceFlashControl::CameraHardwareInterfaceFlashControl(
         sp<CameraProviderManager> manager,
         const camera_module_callbacks_t& callbacks) :
-        mCameraModule(nullptr),
         mProviderManager(manager),
         mCallbacks(&callbacks),
         mTorchEnabled(false) {
@@ -931,12 +519,7 @@
     sp<CameraHardwareInterface> device =
             new CameraHardwareInterface(cameraId.string());
 
-    status_t res;
-    if (mCameraModule != nullptr) {
-        res = device->initialize(mCameraModule);
-    } else {
-        res = device->initialize(mProviderManager);
-    }
+    status_t res = device->initialize(mProviderManager);
     if (res) {
         ALOGE("%s: initializing camera %s failed", __FUNCTION__,
                 cameraId.string());
diff --git a/services/camera/libcameraservice/CameraFlashlight.h b/services/camera/libcameraservice/CameraFlashlight.h
index 98f269a..c86ee85 100644
--- a/services/camera/libcameraservice/CameraFlashlight.h
+++ b/services/camera/libcameraservice/CameraFlashlight.h
@@ -23,7 +23,6 @@
 #include <utils/KeyedVector.h>
 #include <utils/SortedVector.h>
 #include "common/CameraProviderManager.h"
-#include "common/CameraModule.h"
 #include "common/CameraDeviceBase.h"
 #include "device1/CameraHardwareInterface.h"
 
@@ -55,8 +54,6 @@
  */
 class CameraFlashlight : public virtual VirtualLightRefBase {
     public:
-        CameraFlashlight(CameraModule* cameraModule,
-                camera_module_callbacks_t* callbacks);
         CameraFlashlight(sp<CameraProviderManager> providerManager,
                 camera_module_callbacks_t* callbacks);
         virtual ~CameraFlashlight();
@@ -100,7 +97,6 @@
 
         sp<FlashControlBase> mFlashControl;
 
-        CameraModule *mCameraModule;
         sp<CameraProviderManager> mProviderManager;
 
         const camera_module_callbacks_t *mCallbacks;
@@ -132,86 +128,11 @@
 };
 
 /**
- * Flash control for camera module v2.4 and above.
- */
-class ModuleFlashControl : public FlashControlBase {
-    public:
-        ModuleFlashControl(CameraModule& cameraModule);
-        virtual ~ModuleFlashControl();
-
-        // FlashControlBase
-        status_t hasFlashUnit(const String8& cameraId, bool *hasFlash);
-        status_t setTorchMode(const String8& cameraId, bool enabled);
-
-    private:
-        CameraModule *mCameraModule;
-
-        Mutex mLock;
-};
-
-/**
- * Flash control for camera module <= v2.3 and camera HAL v2-v3
- */
-class CameraDeviceClientFlashControl : public FlashControlBase {
-    public:
-        CameraDeviceClientFlashControl(CameraModule& cameraModule,
-                const camera_module_callbacks_t& callbacks);
-        virtual ~CameraDeviceClientFlashControl();
-
-        // FlashControlBase
-        status_t setTorchMode(const String8& cameraId, bool enabled);
-        status_t hasFlashUnit(const String8& cameraId, bool *hasFlash);
-
-    private:
-        // connect to a camera device
-        status_t connectCameraDevice(const String8& cameraId);
-        // disconnect and free mDevice
-        status_t disconnectCameraDevice();
-
-        // initialize a surface
-        status_t initializeSurface(sp<CameraDeviceBase>& device, int32_t width,
-                int32_t height);
-
-        // submit a request to enable the torch mode
-        status_t submitTorchEnabledRequest();
-
-        // get the smallest surface size of IMPLEMENTATION_DEFINED
-        status_t getSmallestSurfaceSize(const camera_info& info, int32_t *width,
-                    int32_t *height);
-
-        // protected by mLock
-        status_t hasFlashUnitLocked(const String8& cameraId, bool *hasFlash);
-
-        CameraModule *mCameraModule;
-        const camera_module_callbacks_t *mCallbacks;
-        String8 mCameraId;
-        bool mTorchEnabled;
-        CameraMetadata *mMetadata;
-        // WORKAROUND: will be set to true for HAL v2 devices where
-        // setStreamingRequest() needs to be call for torch mode settings to
-        // take effect.
-        bool mStreaming;
-
-        sp<CameraDeviceBase> mDevice;
-
-        sp<IGraphicBufferProducer> mProducer;
-        sp<IGraphicBufferConsumer>  mConsumer;
-        sp<GLConsumer> mSurfaceTexture;
-        sp<Surface> mSurface;
-        int32_t mStreamId;
-
-        Mutex mLock;
-};
-
-/**
  * Flash control for camera module <= v2.3 and camera HAL v1
  */
 class CameraHardwareInterfaceFlashControl : public FlashControlBase {
     public:
         CameraHardwareInterfaceFlashControl(
-                CameraModule* cameraModule,
-                const camera_module_callbacks_t& callbacks);
-        CameraHardwareInterfaceFlashControl(
                 sp<CameraProviderManager> manager,
                 const camera_module_callbacks_t& callbacks);
         virtual ~CameraHardwareInterfaceFlashControl();
@@ -244,7 +165,6 @@
         // function, keepDeviceOpen is ignored.
         status_t hasFlashUnitLocked(const String8& cameraId, bool *hasFlash, bool keepDeviceOpen);
 
-        CameraModule *mCameraModule;
         sp<CameraProviderManager> mProviderManager;
         const camera_module_callbacks_t *mCallbacks;
         sp<CameraHardwareInterface> mDevice;
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 39351e7..0031441 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -167,7 +167,7 @@
 CameraService::CameraService() :
         mEventLog(DEFAULT_EVENT_LOG_LENGTH),
         mNumberOfCameras(0), mNumberOfNormalCameras(0),
-        mSoundRef(0), mInitialized(false), mModule(nullptr) {
+        mSoundRef(0), mInitialized(false) {
     ALOGI("CameraService started (pid=%d)", getpid());
 
     this->camera_device_status_change = android::camera_device_status_change;
@@ -189,13 +189,7 @@
 
     status_t res = INVALID_OPERATION;
 
-    bool disableTreble = property_get_bool("camera.disable_treble", false);
-    if (disableTreble) {
-        ALOGI("Treble disabled - using legacy path");
-        res = loadLegacyHalModule();
-    } else {
-        res = enumerateProviders();
-    }
+    res = enumerateProviders();
     if (res == OK) {
         mInitialized = true;
     }
@@ -203,108 +197,6 @@
     CameraService::pingCameraServiceProxy();
 }
 
-status_t CameraService::loadLegacyHalModule() {
-    camera_module_t *rawModule;
-    int err = hw_get_module(CAMERA_HARDWARE_MODULE_ID,
-            (const hw_module_t **)&rawModule);
-    if (err < 0) {
-        ALOGE("Could not load camera HAL module: %d (%s)", err, strerror(-err));
-        logServiceError("Could not load camera HAL module", err);
-        return INVALID_OPERATION;
-    }
-
-    mModule = new CameraModule(rawModule);
-    err = mModule->init();
-    if (err != OK) {
-        ALOGE("Could not initialize camera HAL module: %d (%s)", err,
-            strerror(-err));
-        logServiceError("Could not initialize camera HAL module", err);
-
-        delete mModule;
-        mModule = nullptr;
-        return INVALID_OPERATION;
-    }
-    ALOGI("Loaded \"%s\" camera module", mModule->getModuleName());
-
-    mNumberOfCameras = mModule->getNumberOfCameras();
-    mNumberOfNormalCameras = mNumberOfCameras;
-
-    // Setup vendor tags before we call get_camera_info the first time
-    // because HAL might need to setup static vendor keys in get_camera_info
-    VendorTagDescriptor::clearGlobalVendorTagDescriptor();
-    if (mModule->getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_2) {
-        setUpVendorTags();
-    }
-
-    mFlashlight = new CameraFlashlight(mModule, this);
-    status_t res = mFlashlight->findFlashUnits();
-    if (res) {
-        // impossible because we haven't open any camera devices.
-        ALOGE("Failed to find flash units.");
-    }
-
-    int latestStrangeCameraId = INT_MAX;
-    for (int i = 0; i < mNumberOfCameras; i++) {
-        String8 cameraId = String8::format("%d", i);
-
-        // Get camera info
-
-        struct camera_info info;
-        bool haveInfo = true;
-        status_t rc = mModule->getCameraInfo(i, &info);
-        if (rc != NO_ERROR) {
-            ALOGE("%s: Received error loading camera info for device %d, cost and"
-                    " conflicting devices fields set to defaults for this device.",
-                    __FUNCTION__, i);
-            haveInfo = false;
-        }
-
-        // Check for backwards-compatibility support
-        if (haveInfo) {
-            if (checkCameraCapabilities(i, info, &latestStrangeCameraId) != OK) {
-                delete mModule;
-                mModule = nullptr;
-                return INVALID_OPERATION;
-            }
-        }
-
-        // Defaults to use for cost and conflicting devices
-        int cost = 100;
-        char** conflicting_devices = nullptr;
-        size_t conflicting_devices_length = 0;
-
-        // If using post-2.4 module version, query the cost + conflicting devices from the HAL
-        if (mModule->getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_4 && haveInfo) {
-            cost = info.resource_cost;
-            conflicting_devices = info.conflicting_devices;
-            conflicting_devices_length = info.conflicting_devices_length;
-        }
-
-        std::set<String8> conflicting;
-        for (size_t i = 0; i < conflicting_devices_length; i++) {
-            conflicting.emplace(String8(conflicting_devices[i]));
-        }
-
-        // Initialize state for each camera device
-        {
-            Mutex::Autolock lock(mCameraStatesLock);
-            mCameraStates.emplace(cameraId, std::make_shared<CameraState>(cameraId, cost,
-                    conflicting));
-        }
-
-        if (mFlashlight->hasFlashUnit(cameraId)) {
-            mTorchStatusMap.add(cameraId,
-                    TorchModeStatus::AVAILABLE_OFF);
-        }
-    }
-
-    if (mModule->getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_1) {
-        mModule->setCallbacks(this);
-    }
-
-    return OK;
-}
-
 status_t CameraService::enumerateProviders() {
     mCameraProviderManager = new CameraProviderManager();
     status_t res;
@@ -316,7 +208,8 @@
     }
 
     mNumberOfCameras = mCameraProviderManager->getCameraCount();
-    mNumberOfNormalCameras = mCameraProviderManager->getStandardCameraCount();
+    mNumberOfNormalCameras =
+            mCameraProviderManager->getAPI1CompatibleCameraCount();
 
     // Setup vendor tags before we call get_camera_info the first time
     // because HAL might need to setup static vendor keys in get_camera_info
@@ -380,10 +273,6 @@
 }
 
 CameraService::~CameraService() {
-    if (mModule) {
-        delete mModule;
-        mModule = nullptr;
-    }
     VendorTagDescriptor::clearGlobalVendorTagDescriptor();
 }
 
@@ -548,28 +437,13 @@
     }
 
     Status ret = Status::ok();
-    if (mModule != nullptr) {
-        struct camera_info info;
-        ret = filterGetInfoErrorCode(mModule->getCameraInfo(cameraId, &info));
-
-        if (ret.isOk()) {
-            cameraInfo->facing = info.facing;
-            cameraInfo->orientation = info.orientation;
-            // CameraInfo is for android.hardware.Camera which does not
-            // support external camera facing. The closest approximation would be
-            // front camera.
-            if (cameraInfo->facing == CAMERA_FACING_EXTERNAL) {
-                cameraInfo->facing = hardware::CAMERA_FACING_FRONT;
-            }
-        }
-    } else {
-        status_t err = mCameraProviderManager->getCameraInfo(std::to_string(cameraId), cameraInfo);
-        if (err != OK) {
-            ret = STATUS_ERROR_FMT(ERROR_INVALID_OPERATION,
-                    "Error retrieving camera info from device %d: %s (%d)", cameraId,
-                    strerror(-err), err);
-        }
+    status_t err = mCameraProviderManager->getCameraInfo(std::to_string(cameraId), cameraInfo);
+    if (err != OK) {
+        ret = STATUS_ERROR_FMT(ERROR_INVALID_OPERATION,
+                "Error retrieving camera info from device %d: %s (%d)", cameraId,
+                strerror(-err), err);
     }
+
     return ret;
 }
 
@@ -598,34 +472,12 @@
 
     Status ret{};
 
-    if (mModule != nullptr) {
-        int id = cameraIdToInt(String8(cameraId));
-
-        if (id < 0 || id >= mNumberOfCameras) {
-            ALOGE("%s: Invalid camera id: %d", __FUNCTION__, id);
-            return STATUS_ERROR_FMT(ERROR_ILLEGAL_ARGUMENT,
-                    "Invalid camera id: %d", id);
-        }
-
-        int version = getDeviceVersion(String8(cameraId));
-        if (version < CAMERA_DEVICE_API_VERSION_3_0) {
-            return STATUS_ERROR_FMT(ERROR_ILLEGAL_ARGUMENT, "Can't get camera characteristics"
-                    " for devices with HAL version < 3.0, %d is version %x", id, version);
-        }
-
-        struct camera_info info;
-        ret = filterGetInfoErrorCode(mModule->getCameraInfo(id, &info));
-        if (ret.isOk()) {
-            *cameraInfo = info.static_camera_characteristics;
-        }
-    } else {
-        status_t res = mCameraProviderManager->getCameraCharacteristics(
-                String8(cameraId).string(), cameraInfo);
-        if (res != OK) {
-            return STATUS_ERROR_FMT(ERROR_INVALID_OPERATION, "Unable to retrieve camera "
-                    "characteristics for device %s: %s (%d)", String8(cameraId).string(),
-                    strerror(-res), res);
-        }
+    status_t res = mCameraProviderManager->getCameraCharacteristics(
+            String8(cameraId).string(), cameraInfo);
+    if (res != OK) {
+        return STATUS_ERROR_FMT(ERROR_INVALID_OPERATION, "Unable to retrieve camera "
+                "characteristics for device %s: %s (%d)", String8(cameraId).string(),
+                strerror(-res), res);
     }
 
     return ret;
@@ -682,39 +534,20 @@
 
     int deviceVersion = 0;
 
-    if (mModule != nullptr) {
-        int id = cameraIdToInt(cameraId);
-        if (id < 0) return -1;
+    status_t res;
+    hardware::hidl_version maxVersion{0,0};
+    res = mCameraProviderManager->getHighestSupportedVersion(cameraId.string(),
+            &maxVersion);
+    if (res != OK) return -1;
+    deviceVersion = HARDWARE_DEVICE_API_VERSION(maxVersion.get_major(), maxVersion.get_minor());
 
-        struct camera_info info;
-        if (mModule->getCameraInfo(id, &info) != OK) {
-            return -1;
-        }
-
-        if (mModule->getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_0) {
-            deviceVersion = info.device_version;
-        } else {
-            deviceVersion = CAMERA_DEVICE_API_VERSION_1_0;
-        }
-
-        if (facing) {
-            *facing = info.facing;
-        }
-    } else {
-        status_t res;
-        hardware::hidl_version maxVersion{0,0};
-        res = mCameraProviderManager->getHighestSupportedVersion(cameraId.string(),
-                &maxVersion);
+    hardware::CameraInfo info;
+    if (facing) {
+        res = mCameraProviderManager->getCameraInfo(cameraId.string(), &info);
         if (res != OK) return -1;
-        deviceVersion = HARDWARE_DEVICE_API_VERSION(maxVersion.get_major(), maxVersion.get_minor());
-
-        hardware::CameraInfo info;
-        if (facing) {
-            res = mCameraProviderManager->getCameraInfo(cameraId.string(), &info);
-            if (res != OK) return -1;
-            *facing = info.facing;
-        }
+        *facing = info.facing;
     }
+
     return deviceVersion;
 }
 
@@ -735,45 +568,6 @@
     }
 }
 
-bool CameraService::setUpVendorTags() {
-    ATRACE_CALL();
-    if (mModule == nullptr) return false;
-
-    vendor_tag_ops_t vOps = vendor_tag_ops_t();
-
-    // Check if vendor operations have been implemented
-    if (!mModule->isVendorTagDefined()) {
-        ALOGI("%s: No vendor tags defined for this device.", __FUNCTION__);
-        return false;
-    }
-
-    mModule->getVendorTagOps(&vOps);
-
-    // Ensure all vendor operations are present
-    if (vOps.get_tag_count == NULL || vOps.get_all_tags == NULL ||
-            vOps.get_section_name == NULL || vOps.get_tag_name == NULL ||
-            vOps.get_tag_type == NULL) {
-        ALOGE("%s: Vendor tag operations not fully defined. Ignoring definitions."
-               , __FUNCTION__);
-        return false;
-    }
-
-    // Read all vendor tag definitions into a descriptor
-    sp<VendorTagDescriptor> desc;
-    status_t res;
-    if ((res = VendorTagDescriptor::createDescriptorFromOps(&vOps, /*out*/desc))
-            != OK) {
-        ALOGE("%s: Could not generate descriptor from vendor tag operations,"
-              "received error %s (%d). Camera clients will not be able to use"
-              "vendor tags", __FUNCTION__, strerror(res), res);
-        return false;
-    }
-
-    // Set the global descriptor to use with camera metadata
-    VendorTagDescriptor::setAsGlobalVendorTagDescriptor(desc);
-    return true;
-}
-
 Status CameraService::makeClient(const sp<CameraService>& cameraService,
         const sp<IInterface>& cameraCb, const String16& packageName, const String8& cameraId,
         int facing, int clientPid, uid_t clientUid, int servicePid, bool legacyMode,
@@ -1403,25 +1197,6 @@
 
     ATRACE_CALL();
     String8 id = String8::format("%d", cameraId);
-    if (mModule != nullptr) {
-        int apiVersion = mModule->getModuleApiVersion();
-        if (halVersion != CAMERA_HAL_API_VERSION_UNSPECIFIED &&
-                apiVersion < CAMERA_MODULE_API_VERSION_2_3) {
-            /*
-             * Either the HAL version is unspecified in which case this just creates
-             * a camera client selected by the latest device version, or
-             * it's a particular version in which case the HAL must supported
-             * the open_legacy call
-             */
-            String8 msg = String8::format("Camera HAL module version %x too old for connectLegacy!",
-                    apiVersion);
-            ALOGE("%s: %s",
-                    __FUNCTION__, msg.string());
-            logRejected(id, getCallingPid(), String8(clientPackageName),
-                    msg);
-            return STATUS_ERROR(ERROR_ILLEGAL_ARGUMENT, msg.string());
-        }
-    }
 
     Status ret = Status::ok();
     sp<Client> client = nullptr;
@@ -1564,12 +1339,7 @@
         LOG_ALWAYS_FATAL_IF(client.get() == nullptr, "%s: CameraService in invalid state",
                 __FUNCTION__);
 
-        if (mModule != nullptr) {
-            err = client->initialize(mModule);
-        } else {
-            err = client->initialize(mCameraProviderManager);
-        }
-
+        err = client->initialize(mCameraProviderManager);
         if (err != OK) {
             ALOGE("%s: Could not initialize client from HAL.", __FUNCTION__);
             // Errors could be from the HAL module open call or from AppOpsManager
@@ -1988,83 +1758,6 @@
     return ret;
 }
 
-
-/**
- * Check camera capabilities, such as support for basic color operation
- * Also check that the device HAL version is still in support
- */
-int CameraService::checkCameraCapabilities(int id, camera_info info, int *latestStrangeCameraId) {
-    if (mModule == nullptr) return NO_INIT;
-
-    // device_version undefined in CAMERA_MODULE_API_VERSION_1_0,
-    // All CAMERA_MODULE_API_VERSION_1_0 devices are backward-compatible
-    if (mModule->getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_0) {
-        // Verify the device version is in the supported range
-        switch (info.device_version) {
-            case CAMERA_DEVICE_API_VERSION_1_0:
-            case CAMERA_DEVICE_API_VERSION_3_0:
-            case CAMERA_DEVICE_API_VERSION_3_1:
-            case CAMERA_DEVICE_API_VERSION_3_2:
-            case CAMERA_DEVICE_API_VERSION_3_3:
-            case CAMERA_DEVICE_API_VERSION_3_4:
-                // in support
-                break;
-            case CAMERA_DEVICE_API_VERSION_2_0:
-            case CAMERA_DEVICE_API_VERSION_2_1:
-                // no longer supported
-            default:
-                ALOGE("%s: Device %d has HAL version %x, which is not supported",
-                        __FUNCTION__, id, info.device_version);
-                String8 msg = String8::format(
-                        "Unsupported device HAL version %x for device %d",
-                        info.device_version, id);
-                logServiceError(msg.string(), NO_INIT);
-                return NO_INIT;
-        }
-    }
-
-    // Assume all devices pre-v3.3 are backward-compatible
-    bool isBackwardCompatible = true;
-    if (mModule->getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_0
-            && info.device_version >= CAMERA_DEVICE_API_VERSION_3_3) {
-        isBackwardCompatible = false;
-        status_t res;
-        camera_metadata_ro_entry_t caps;
-        res = find_camera_metadata_ro_entry(
-            info.static_camera_characteristics,
-            ANDROID_REQUEST_AVAILABLE_CAPABILITIES,
-            &caps);
-        if (res != 0) {
-            ALOGW("%s: Unable to find camera capabilities for camera device %d",
-                    __FUNCTION__, id);
-            caps.count = 0;
-        }
-        for (size_t i = 0; i < caps.count; i++) {
-            if (caps.data.u8[i] ==
-                    ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE) {
-                isBackwardCompatible = true;
-                break;
-            }
-        }
-    }
-
-    if (!isBackwardCompatible) {
-        mNumberOfNormalCameras--;
-        *latestStrangeCameraId = id;
-    } else {
-        if (id > *latestStrangeCameraId) {
-            ALOGE("%s: Normal camera ID %d higher than strange camera ID %d. "
-                    "This is not allowed due backward-compatibility requirements",
-                    __FUNCTION__, id, *latestStrangeCameraId);
-            logServiceError("Invalid order of camera devices", NO_INIT);
-            mNumberOfCameras = 0;
-            mNumberOfNormalCameras = 0;
-            return NO_INIT;
-        }
-    }
-    return OK;
-}
-
 std::shared_ptr<CameraService::CameraState> CameraService::getCameraState(
         const String8& cameraId) const {
     std::shared_ptr<CameraState> state;
@@ -2815,61 +2508,13 @@
                     cameraId.string());
         }
 
-        if (mModule != nullptr) {
-            dprintf(fd, "== Camera HAL device %s static information: ==\n", cameraId.string());
-
-            camera_info info;
-            status_t rc = mModule->getCameraInfo(cameraIdToInt(cameraId), &info);
-            int deviceVersion = -1;
-            if (rc != OK) {
-                dprintf(fd, "  Error reading static information!\n");
-            } else {
-                dprintf(fd, "  Facing: %s\n",
-                        info.facing == CAMERA_FACING_BACK ? "BACK" :
-                        info.facing == CAMERA_FACING_FRONT ? "FRONT" : "EXTERNAL");
-                dprintf(fd, "  Orientation: %d\n", info.orientation);
-
-                if (mModule->getModuleApiVersion() < CAMERA_MODULE_API_VERSION_2_0) {
-                    deviceVersion = CAMERA_DEVICE_API_VERSION_1_0;
-                } else {
-                    deviceVersion = info.device_version;
-                }
-            }
-
-            auto conflicting = state.second->getConflicting();
-            dprintf(fd, "  Resource Cost: %d\n", state.second->getCost());
-            dprintf(fd, "  Conflicting Devices:");
-            for (auto& id : conflicting) {
-                dprintf(fd, " %s", id.string());
-            }
-            if (conflicting.size() == 0) {
-                dprintf(fd, " NONE");
-            }
-            dprintf(fd, "\n");
-
-            dprintf(fd, "  Device version: %#x\n", deviceVersion);
-            if (deviceVersion >= CAMERA_DEVICE_API_VERSION_3_0) {
-                dprintf(fd, "  Device static metadata:\n");
-                dump_indented_camera_metadata(info.static_camera_characteristics,
-                        fd, /*verbosity*/2, /*indentation*/4);
-            }
-        }
-
     }
 
     if (stateLocked) mCameraStatesLock.unlock();
 
     if (locked) mServiceLock.unlock();
 
-    if (mModule == nullptr) {
-        mCameraProviderManager->dump(fd, args);
-    } else {
-        dprintf(fd, "\n== Camera Module HAL static info: ==\n");
-        dprintf(fd, "Camera module HAL API version: 0x%x\n", mModule->getHalApiVersion());
-        dprintf(fd, "Camera module API version: 0x%x\n", mModule->getModuleApiVersion());
-        dprintf(fd, "Camera module name: %s\n", mModule->getModuleName());
-        dprintf(fd, "Camera module author: %s\n", mModule->getModuleAuthor());
-    }
+    mCameraProviderManager->dump(fd, args);
 
     dprintf(fd, "\n== Vendor tags: ==\n\n");
 
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index e49fe62..7d81993 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -37,7 +37,6 @@
 
 #include "CameraFlashlight.h"
 
-#include "common/CameraModule.h"
 #include "common/CameraProviderManager.h"
 #include "media/RingBuffer.h"
 #include "utils/AutoConditionLock.h"
@@ -198,7 +197,6 @@
 
     class BasicClient : public virtual RefBase {
     public:
-        virtual status_t       initialize(CameraModule *module) = 0;
         virtual status_t       initialize(sp<CameraProviderManager> manager) = 0;
         virtual binder::Status disconnect();
 
@@ -508,9 +506,6 @@
     // Delay-load the Camera HAL module
     virtual void onFirstRef();
 
-    // Load the legacy HAL module
-    status_t loadLegacyHalModule();
-
     // Eumerate all camera providers in the system
     status_t enumerateProviders();
 
@@ -568,11 +563,6 @@
     std::set<userid_t> mAllowedUsers;
 
     /**
-     * Check camera capabilities, such as support for basic color operation
-     */
-    int checkCameraCapabilities(int id, camera_info info, int *latestStrangeCameraId);
-
-    /**
      * Get the camera state for a given camera id.
      *
      * This acquires mCameraStatesLock.
@@ -687,7 +677,6 @@
     // Basic flag on whether the camera subsystem is in a usable state
     bool                mInitialized;
 
-    CameraModule*       mModule;
     sp<CameraProviderManager> mCameraProviderManager;
 
     // Guarded by mStatusListenerMutex
@@ -745,10 +734,6 @@
     // IBinder::DeathRecipient implementation
     virtual void        binderDied(const wp<IBinder> &who);
 
-    // Helpers
-
-    bool                setUpVendorTags();
-
     /**
      * Initialize and cache the metadata used by the HAL1 shim for a given cameraId.
      *
diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp
index 335e999..a28518e 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.cpp
+++ b/services/camera/libcameraservice/api1/Camera2Client.cpp
@@ -68,10 +68,6 @@
     mLegacyMode = legacyMode;
 }
 
-status_t Camera2Client::initialize(CameraModule *module) {
-    return initializeImpl(module);
-}
-
 status_t Camera2Client::initialize(sp<CameraProviderManager> manager) {
     return initializeImpl(manager);
 }
diff --git a/services/camera/libcameraservice/api1/Camera2Client.h b/services/camera/libcameraservice/api1/Camera2Client.h
index 9738aca..72315d4 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.h
+++ b/services/camera/libcameraservice/api1/Camera2Client.h
@@ -100,7 +100,6 @@
 
     virtual ~Camera2Client();
 
-    virtual status_t initialize(CameraModule *module) override;
     virtual status_t initialize(sp<CameraProviderManager> manager) override;
 
     virtual status_t dump(int fd, const Vector<String16>& args);
diff --git a/services/camera/libcameraservice/api1/CameraClient.cpp b/services/camera/libcameraservice/api1/CameraClient.cpp
index df8726e..075c2e3 100644
--- a/services/camera/libcameraservice/api1/CameraClient.cpp
+++ b/services/camera/libcameraservice/api1/CameraClient.cpp
@@ -62,16 +62,7 @@
     LOG1("CameraClient::CameraClient X (pid %d, id %d)", callingPid, cameraId);
 }
 
-status_t CameraClient::initialize(CameraModule *module) {
-    return initializeImpl<CameraModule*>(module);
-}
-
 status_t CameraClient::initialize(sp<CameraProviderManager> manager) {
-    return initializeImpl<sp<CameraProviderManager>>(manager);
-}
-
-template<typename TProviderPtr>
-status_t CameraClient::initializeImpl(TProviderPtr providerPtr) {
     int callingPid = getCallingPid();
     status_t res;
 
@@ -87,7 +78,7 @@
     snprintf(camera_device_name, sizeof(camera_device_name), "%d", mCameraId);
 
     mHardware = new CameraHardwareInterface(camera_device_name);
-    res = mHardware->initialize(providerPtr);
+    res = mHardware->initialize(manager);
     if (res != OK) {
         ALOGE("%s: Camera %d: unable to initialize device: %s (%d)",
                 __FUNCTION__, mCameraId, strerror(-res), res);
@@ -478,14 +469,23 @@
 // stop recording mode
 void CameraClient::stopRecording() {
     LOG1("stopRecording (pid %d)", getCallingPid());
-    Mutex::Autolock lock(mLock);
-    if (checkPidAndHardware() != NO_ERROR) return;
+    {
+        Mutex::Autolock lock(mLock);
+        if (checkPidAndHardware() != NO_ERROR) return;
 
-    disableMsgType(CAMERA_MSG_VIDEO_FRAME);
-    mHardware->stopRecording();
-    sCameraService->playSound(CameraService::SOUND_RECORDING_STOP);
+        disableMsgType(CAMERA_MSG_VIDEO_FRAME);
+        mHardware->stopRecording();
+        sCameraService->playSound(CameraService::SOUND_RECORDING_STOP);
 
-    mPreviewBuffer.clear();
+        mPreviewBuffer.clear();
+    }
+
+    {
+        Mutex::Autolock l(mAvailableCallbackBuffersLock);
+        if (!mAvailableCallbackBuffers.empty()) {
+            mAvailableCallbackBuffers.clear();
+        }
+    }
 }
 
 // release a recording frame
diff --git a/services/camera/libcameraservice/api1/CameraClient.h b/services/camera/libcameraservice/api1/CameraClient.h
index 1073384..7f93fef 100644
--- a/services/camera/libcameraservice/api1/CameraClient.h
+++ b/services/camera/libcameraservice/api1/CameraClient.h
@@ -72,7 +72,6 @@
             bool legacyMode = false);
     ~CameraClient();
 
-    virtual status_t initialize(CameraModule *module) override;
     virtual status_t initialize(sp<CameraProviderManager> manager) override;
 
     virtual status_t dump(int fd, const Vector<String16>& args);
@@ -81,9 +80,6 @@
 
 private:
 
-    template<typename TProviderPtr>
-    status_t initializeImpl(TProviderPtr providerPtr);
-
     // check whether the calling process matches mClientPid.
     status_t                checkPid() const;
     status_t                checkPidAndHardware() const;  // also check mHardware != 0
diff --git a/services/camera/libcameraservice/api1/client2/FrameProcessor.cpp b/services/camera/libcameraservice/api1/client2/FrameProcessor.cpp
index 733a78e..6e21126 100644
--- a/services/camera/libcameraservice/api1/client2/FrameProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/FrameProcessor.cpp
@@ -41,11 +41,7 @@
     {
         SharedParameters::Lock l(client->getParameters());
 
-        if (client->getCameraDeviceVersion() >= CAMERA_DEVICE_API_VERSION_3_2) {
-            mUsePartialResult = (mNumPartialResults > 1);
-        } else {
-            mUsePartialResult = l.mParameters.quirks.partialResults;
-        }
+        mUsePartialResult = (mNumPartialResults > 1);
 
         // Initialize starting 3A state
         m3aState.afTriggerId = l.mParameters.afTriggerCounter;
@@ -76,16 +72,7 @@
 
     bool isPartialResult = false;
     if (mUsePartialResult) {
-        if (client->getCameraDeviceVersion() >= CAMERA_DEVICE_API_VERSION_3_2) {
-            isPartialResult = frame.mResultExtras.partialResultCount < mNumPartialResults;
-        } else {
-            camera_metadata_entry_t entry;
-            entry = frame.mMetadata.find(ANDROID_QUIRKS_PARTIAL_RESULT);
-            if (entry.count > 0 &&
-                    entry.data.u8[0] == ANDROID_QUIRKS_PARTIAL_RESULT_PARTIAL) {
-                isPartialResult = true;
-            }
-        }
+        isPartialResult = frame.mResultExtras.partialResultCount < mNumPartialResults;
     }
 
     if (!isPartialResult && processFaceDetect(frame.mMetadata, client) != OK) {
@@ -291,16 +278,8 @@
     gotAllStates &= updatePendingState<uint8_t>(metadata, ANDROID_CONTROL_AWB_STATE,
             &pendingState.awbState, frameNumber, cameraId);
 
-    if (client->getCameraDeviceVersion() >= CAMERA_DEVICE_API_VERSION_3_2) {
-        pendingState.afTriggerId = frame.mResultExtras.afTriggerId;
-        pendingState.aeTriggerId = frame.mResultExtras.precaptureTriggerId;
-    } else {
-        gotAllStates &= updatePendingState<int32_t>(metadata,
-                ANDROID_CONTROL_AF_TRIGGER_ID, &pendingState.afTriggerId, frameNumber, cameraId);
-
-        gotAllStates &= updatePendingState<int32_t>(metadata,
-            ANDROID_CONTROL_AE_PRECAPTURE_ID, &pendingState.aeTriggerId, frameNumber, cameraId);
-    }
+    pendingState.afTriggerId = frame.mResultExtras.afTriggerId;
+    pendingState.aeTriggerId = frame.mResultExtras.precaptureTriggerId;
 
     if (!gotAllStates) {
         // If not all states are received, put the pending state to mPending3AStates.
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.cpp b/services/camera/libcameraservice/api1/client2/Parameters.cpp
index 1c78a08..a305bc7 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.cpp
+++ b/services/camera/libcameraservice/api1/client2/Parameters.cpp
@@ -2854,32 +2854,14 @@
     }
     sizes->clear();
 
-    if (mDeviceVersion >= CAMERA_DEVICE_API_VERSION_3_2) {
-        Vector<StreamConfiguration> scs = getStreamConfigurations();
-        for (size_t i=0; i < scs.size(); i++) {
-            const StreamConfiguration &sc = scs[i];
-            if (sc.isInput == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT &&
-                    sc.format == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED &&
-                    sc.width <= limit.width && sc.height <= limit.height) {
-                Size sz = {sc.width, sc.height};
-                sizes->push(sz);
-            }
-        }
-    } else {
-        const size_t SIZE_COUNT = sizeof(Size) / sizeof(int);
-        camera_metadata_ro_entry_t availableProcessedSizes =
-            staticInfo(ANDROID_SCALER_AVAILABLE_PROCESSED_SIZES, SIZE_COUNT);
-        if (availableProcessedSizes.count < SIZE_COUNT) return BAD_VALUE;
-
-        Size filteredSize;
-        for (size_t i = 0; i < availableProcessedSizes.count; i += SIZE_COUNT) {
-            filteredSize.width = availableProcessedSizes.data.i32[i];
-            filteredSize.height = availableProcessedSizes.data.i32[i+1];
-                // Need skip the preview sizes that are too large.
-                if (filteredSize.width <= limit.width &&
-                        filteredSize.height <= limit.height) {
-                    sizes->push(filteredSize);
-                }
+    Vector<StreamConfiguration> scs = getStreamConfigurations();
+    for (size_t i=0; i < scs.size(); i++) {
+        const StreamConfiguration &sc = scs[i];
+        if (sc.isInput == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT &&
+                sc.format == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED &&
+                sc.width <= limit.width && sc.height <= limit.height) {
+            Size sz = {sc.width, sc.height};
+            sizes->push(sz);
         }
     }
 
@@ -2934,10 +2916,6 @@
     const int STREAM_HEIGHT_OFFSET = 2;
     const int STREAM_IS_INPUT_OFFSET = 3;
     Vector<StreamConfiguration> scs;
-    if (mDeviceVersion < CAMERA_DEVICE_API_VERSION_3_2) {
-        ALOGE("StreamConfiguration is only valid after device HAL 3.2!");
-        return scs;
-    }
 
     camera_metadata_ro_entry_t availableStreamConfigs =
                 staticInfo(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
@@ -2953,37 +2931,10 @@
 }
 
 int64_t Parameters::getJpegStreamMinFrameDurationNs(Parameters::Size size) {
-    if (mDeviceVersion >= CAMERA_DEVICE_API_VERSION_3_2) {
-        return getMinFrameDurationNs(size, HAL_PIXEL_FORMAT_BLOB);
-    } else {
-        Vector<Size> availableJpegSizes = getAvailableJpegSizes();
-        size_t streamIdx = availableJpegSizes.size();
-        for (size_t i = 0; i < availableJpegSizes.size(); i++) {
-            if (availableJpegSizes[i].width == size.width &&
-                    availableJpegSizes[i].height == size.height) {
-                streamIdx = i;
-                break;
-            }
-        }
-        if (streamIdx != availableJpegSizes.size()) {
-            camera_metadata_ro_entry_t jpegMinDurations =
-                    staticInfo(ANDROID_SCALER_AVAILABLE_JPEG_MIN_DURATIONS);
-            if (streamIdx < jpegMinDurations.count) {
-                return jpegMinDurations.data.i64[streamIdx];
-            }
-        }
-    }
-    ALOGE("%s: cannot find min frame duration for jpeg size %dx%d",
-            __FUNCTION__, size.width, size.height);
-    return -1;
+    return getMinFrameDurationNs(size, HAL_PIXEL_FORMAT_BLOB);
 }
 
 int64_t Parameters::getMinFrameDurationNs(Parameters::Size size, int fmt) {
-    if (mDeviceVersion < CAMERA_DEVICE_API_VERSION_3_2) {
-        ALOGE("Min frame duration for HAL 3.1 or lower is not supported");
-        return -1;
-    }
-
     const int STREAM_DURATION_SIZE = 4;
     const int STREAM_FORMAT_OFFSET = 0;
     const int STREAM_WIDTH_OFFSET = 1;
@@ -3005,11 +2956,6 @@
 }
 
 bool Parameters::isFpsSupported(const Vector<Size> &sizes, int format, int32_t fps) {
-    // Skip the check for older HAL version, as the min duration is not supported.
-    if (mDeviceVersion < CAMERA_DEVICE_API_VERSION_3_2) {
-        return true;
-    }
-
     // Get min frame duration for each size and check if the given fps range can be supported.
     for (size_t i = 0 ; i < sizes.size(); i++) {
         int64_t minFrameDuration = getMinFrameDurationNs(sizes[i], format);
@@ -3030,48 +2976,29 @@
 
 SortedVector<int32_t> Parameters::getAvailableOutputFormats() {
     SortedVector<int32_t> outputFormats; // Non-duplicated output formats
-    if (mDeviceVersion >= CAMERA_DEVICE_API_VERSION_3_2) {
-        Vector<StreamConfiguration> scs = getStreamConfigurations();
-        for (size_t i = 0; i < scs.size(); i++) {
-            const StreamConfiguration &sc = scs[i];
-            if (sc.isInput == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT) {
-                outputFormats.add(sc.format);
-            }
-        }
-    } else {
-        camera_metadata_ro_entry_t availableFormats = staticInfo(ANDROID_SCALER_AVAILABLE_FORMATS);
-        for (size_t i = 0; i < availableFormats.count; i++) {
-            outputFormats.add(availableFormats.data.i32[i]);
+    Vector<StreamConfiguration> scs = getStreamConfigurations();
+    for (size_t i = 0; i < scs.size(); i++) {
+        const StreamConfiguration &sc = scs[i];
+        if (sc.isInput == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT) {
+            outputFormats.add(sc.format);
         }
     }
+
     return outputFormats;
 }
 
 Vector<Parameters::Size> Parameters::getAvailableJpegSizes() {
     Vector<Parameters::Size> jpegSizes;
-    if (mDeviceVersion >= CAMERA_DEVICE_API_VERSION_3_2) {
-        Vector<StreamConfiguration> scs = getStreamConfigurations();
-        for (size_t i = 0; i < scs.size(); i++) {
-            const StreamConfiguration &sc = scs[i];
-            if (sc.isInput == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT &&
-                    sc.format == HAL_PIXEL_FORMAT_BLOB) {
-                Size sz = {sc.width, sc.height};
-                jpegSizes.add(sz);
-            }
-        }
-    } else {
-        const int JPEG_SIZE_ENTRY_COUNT = 2;
-        const int WIDTH_OFFSET = 0;
-        const int HEIGHT_OFFSET = 1;
-        camera_metadata_ro_entry_t availableJpegSizes =
-            staticInfo(ANDROID_SCALER_AVAILABLE_JPEG_SIZES);
-        for (size_t i = 0; i < availableJpegSizes.count; i+= JPEG_SIZE_ENTRY_COUNT) {
-            int width = availableJpegSizes.data.i32[i + WIDTH_OFFSET];
-            int height = availableJpegSizes.data.i32[i + HEIGHT_OFFSET];
-            Size sz = {width, height};
+    Vector<StreamConfiguration> scs = getStreamConfigurations();
+    for (size_t i = 0; i < scs.size(); i++) {
+        const StreamConfiguration &sc = scs[i];
+        if (sc.isInput == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT &&
+                sc.format == HAL_PIXEL_FORMAT_BLOB) {
+            Size sz = {sc.width, sc.height};
             jpegSizes.add(sz);
         }
     }
+
     return jpegSizes;
 }
 
diff --git a/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp b/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
index bf92a2b..d79e430 100644
--- a/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
@@ -114,16 +114,11 @@
         }
 
         // Use CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG for ZSL streaming case.
-        if (client->getCameraDeviceVersion() >= CAMERA_DEVICE_API_VERSION_3_0) {
-            if (params.useZeroShutterLag() && !params.recordingHint) {
-                res = device->createDefaultRequest(CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG,
-                        &mPreviewRequest);
-            } else {
-                res = device->createDefaultRequest(CAMERA3_TEMPLATE_PREVIEW,
-                        &mPreviewRequest);
-            }
+        if (params.useZeroShutterLag() && !params.recordingHint) {
+            res = device->createDefaultRequest(
+                    CAMERA3_TEMPLATE_ZERO_SHUTTER_LAG, &mPreviewRequest);
         } else {
-            res = device->createDefaultRequest(CAMERA2_TEMPLATE_PREVIEW,
+            res = device->createDefaultRequest(CAMERA3_TEMPLATE_PREVIEW,
                     &mPreviewRequest);
         }
 
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index f2e8df8..0429e7f 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -82,10 +82,6 @@
     ALOGI("CameraDeviceClient %s: Opened", cameraId.string());
 }
 
-status_t CameraDeviceClient::initialize(CameraModule *module) {
-    return initializeImpl(module);
-}
-
 status_t CameraDeviceClient::initialize(sp<CameraProviderManager> manager) {
     return initializeImpl(manager);
 }
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h
index 2a95c88..2bf73a0 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.h
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h
@@ -149,7 +149,6 @@
             int servicePid);
     virtual ~CameraDeviceClient();
 
-    virtual status_t      initialize(CameraModule *module) override;
     virtual status_t      initialize(sp<CameraProviderManager> manager) override;
 
     virtual status_t      dump(int fd, const Vector<String16>& args);
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.cpp b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
index 93a584b..32ee273 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.cpp
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
@@ -79,11 +79,6 @@
 }
 
 template <typename TClientBase>
-status_t Camera2ClientBase<TClientBase>::initialize(CameraModule *module) {
-    return initializeImpl(module);
-}
-
-template <typename TClientBase>
 status_t Camera2ClientBase<TClientBase>::initialize(sp<CameraProviderManager> manager) {
     return initializeImpl(manager);
 }
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.h b/services/camera/libcameraservice/common/Camera2ClientBase.h
index a4c08ef..e898d5d 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.h
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.h
@@ -18,7 +18,6 @@
 #define ANDROID_SERVERS_CAMERA_CAMERA2CLIENT_BASE_H
 
 #include "common/CameraDeviceBase.h"
-#include "common/CameraModule.h"
 #include "camera/CaptureResult.h"
 
 namespace android {
@@ -56,7 +55,6 @@
                       int servicePid);
     virtual ~Camera2ClientBase();
 
-    virtual status_t      initialize(CameraModule *module);
     virtual status_t      initialize(sp<CameraProviderManager> manager);
     virtual status_t      dumpClient(int fd, const Vector<String16>& args);
 
diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h
index 4f788ae..d9059f3 100644
--- a/services/camera/libcameraservice/common/CameraDeviceBase.h
+++ b/services/camera/libcameraservice/common/CameraDeviceBase.h
@@ -30,7 +30,6 @@
 #include "hardware/camera3.h"
 #include "camera/CameraMetadata.h"
 #include "camera/CaptureResult.h"
-#include "common/CameraModule.h"
 #include "gui/IGraphicBufferProducer.h"
 #include "device3/Camera3StreamInterface.h"
 #include "binder/Status.h"
@@ -55,7 +54,6 @@
      */
     virtual const String8& getId() const = 0;
 
-    virtual status_t initialize(CameraModule *module) = 0;
     virtual status_t initialize(sp<CameraProviderManager> manager) = 0;
     virtual status_t disconnect() = 0;
 
@@ -307,11 +305,6 @@
     virtual status_t prepare(int maxCount, int streamId) = 0;
 
     /**
-     * Get the HAL device version.
-     */
-    virtual uint32_t getDeviceVersion() = 0;
-
-    /**
      * Set the deferred consumer surface and finish the rest of the stream configuration.
      */
     virtual status_t setConsumerSurfaces(int streamId,
diff --git a/services/camera/libcameraservice/common/CameraModule.cpp b/services/camera/libcameraservice/common/CameraModule.cpp
deleted file mode 100644
index 073144c..0000000
--- a/services/camera/libcameraservice/common/CameraModule.cpp
+++ /dev/null
@@ -1,403 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "CameraModule"
-#define ATRACE_TAG ATRACE_TAG_CAMERA
-//#define LOG_NDEBUG 0
-
-#include <utils/Trace.h>
-
-#include "CameraModule.h"
-
-namespace android {
-
-void CameraModule::deriveCameraCharacteristicsKeys(
-        uint32_t deviceVersion, CameraMetadata &chars) {
-    ATRACE_CALL();
-
-    Vector<int32_t> derivedCharKeys;
-    Vector<int32_t> derivedRequestKeys;
-    Vector<int32_t> derivedResultKeys;
-    // Keys added in HAL3.3
-    if (deviceVersion < CAMERA_DEVICE_API_VERSION_3_3) {
-        Vector<uint8_t> controlModes;
-        uint8_t data = ANDROID_CONTROL_AE_LOCK_AVAILABLE_TRUE;
-        chars.update(ANDROID_CONTROL_AE_LOCK_AVAILABLE, &data, /*count*/1);
-        data = ANDROID_CONTROL_AWB_LOCK_AVAILABLE_TRUE;
-        chars.update(ANDROID_CONTROL_AWB_LOCK_AVAILABLE, &data, /*count*/1);
-        controlModes.push(ANDROID_CONTROL_MODE_AUTO);
-        camera_metadata_entry entry = chars.find(ANDROID_CONTROL_AVAILABLE_SCENE_MODES);
-        if (entry.count > 1 || entry.data.u8[0] != ANDROID_CONTROL_SCENE_MODE_DISABLED) {
-            controlModes.push(ANDROID_CONTROL_MODE_USE_SCENE_MODE);
-        }
-
-        // Only advertise CONTROL_OFF mode if 3A manual controls are supported.
-        bool isManualAeSupported = false;
-        bool isManualAfSupported = false;
-        bool isManualAwbSupported = false;
-        entry = chars.find(ANDROID_CONTROL_AE_AVAILABLE_MODES);
-        if (entry.count > 0) {
-            for (size_t i = 0; i < entry.count; i++) {
-                if (entry.data.u8[i] == ANDROID_CONTROL_AE_MODE_OFF) {
-                    isManualAeSupported = true;
-                    break;
-                }
-            }
-        }
-        entry = chars.find(ANDROID_CONTROL_AF_AVAILABLE_MODES);
-        if (entry.count > 0) {
-            for (size_t i = 0; i < entry.count; i++) {
-                if (entry.data.u8[i] == ANDROID_CONTROL_AF_MODE_OFF) {
-                    isManualAfSupported = true;
-                    break;
-                }
-            }
-        }
-        entry = chars.find(ANDROID_CONTROL_AWB_AVAILABLE_MODES);
-        if (entry.count > 0) {
-            for (size_t i = 0; i < entry.count; i++) {
-                if (entry.data.u8[i] == ANDROID_CONTROL_AWB_MODE_OFF) {
-                    isManualAwbSupported = true;
-                    break;
-                }
-            }
-        }
-        if (isManualAeSupported && isManualAfSupported && isManualAwbSupported) {
-            controlModes.push(ANDROID_CONTROL_MODE_OFF);
-        }
-
-        chars.update(ANDROID_CONTROL_AVAILABLE_MODES, controlModes);
-
-        entry = chars.find(ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS);
-        // HAL3.2 devices passing existing CTS test should all support all LSC modes and LSC map
-        bool lensShadingModeSupported = false;
-        if (entry.count > 0) {
-            for (size_t i = 0; i < entry.count; i++) {
-                if (entry.data.i32[i] == ANDROID_SHADING_MODE) {
-                    lensShadingModeSupported = true;
-                    break;
-                }
-            }
-        }
-        Vector<uint8_t> lscModes;
-        Vector<uint8_t> lscMapModes;
-        lscModes.push(ANDROID_SHADING_MODE_FAST);
-        lscModes.push(ANDROID_SHADING_MODE_HIGH_QUALITY);
-        lscMapModes.push(ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_OFF);
-        if (lensShadingModeSupported) {
-            lscModes.push(ANDROID_SHADING_MODE_OFF);
-            lscMapModes.push(ANDROID_STATISTICS_LENS_SHADING_MAP_MODE_ON);
-        }
-        chars.update(ANDROID_SHADING_AVAILABLE_MODES, lscModes);
-        chars.update(ANDROID_STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES, lscMapModes);
-
-        derivedCharKeys.push(ANDROID_CONTROL_AE_LOCK_AVAILABLE);
-        derivedCharKeys.push(ANDROID_CONTROL_AWB_LOCK_AVAILABLE);
-        derivedCharKeys.push(ANDROID_CONTROL_AVAILABLE_MODES);
-        derivedCharKeys.push(ANDROID_SHADING_AVAILABLE_MODES);
-        derivedCharKeys.push(ANDROID_STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES);
-
-        // Need update android.control.availableHighSpeedVideoConfigurations since HAL3.3
-        // adds batch size to this array.
-        entry = chars.find(ANDROID_CONTROL_AVAILABLE_HIGH_SPEED_VIDEO_CONFIGURATIONS);
-        if (entry.count > 0) {
-            Vector<int32_t> highSpeedConfig;
-            for (size_t i = 0; i < entry.count; i += 4) {
-                highSpeedConfig.add(entry.data.i32[i]); // width
-                highSpeedConfig.add(entry.data.i32[i + 1]); // height
-                highSpeedConfig.add(entry.data.i32[i + 2]); // fps_min
-                highSpeedConfig.add(entry.data.i32[i + 3]); // fps_max
-                highSpeedConfig.add(1); // batchSize_max. default to 1 for HAL3.2
-            }
-            chars.update(ANDROID_CONTROL_AVAILABLE_HIGH_SPEED_VIDEO_CONFIGURATIONS,
-                    highSpeedConfig);
-        }
-    }
-
-    // Keys added in HAL3.4
-    if (deviceVersion < CAMERA_DEVICE_API_VERSION_3_4) {
-        // Check if HAL supports RAW_OPAQUE output
-        camera_metadata_entry entry = chars.find(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
-        bool supportRawOpaque = false;
-        bool supportAnyRaw = false;
-        const int STREAM_CONFIGURATION_SIZE = 4;
-        const int STREAM_FORMAT_OFFSET = 0;
-        const int STREAM_WIDTH_OFFSET = 1;
-        const int STREAM_HEIGHT_OFFSET = 2;
-        const int STREAM_IS_INPUT_OFFSET = 3;
-        Vector<int32_t> rawOpaqueSizes;
-
-        for (size_t i=0; i < entry.count; i += STREAM_CONFIGURATION_SIZE) {
-            int32_t format = entry.data.i32[i + STREAM_FORMAT_OFFSET];
-            int32_t width = entry.data.i32[i + STREAM_WIDTH_OFFSET];
-            int32_t height = entry.data.i32[i + STREAM_HEIGHT_OFFSET];
-            int32_t isInput = entry.data.i32[i + STREAM_IS_INPUT_OFFSET];
-            if (isInput == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT &&
-                    format == HAL_PIXEL_FORMAT_RAW_OPAQUE) {
-                supportRawOpaque = true;
-                rawOpaqueSizes.push(width);
-                rawOpaqueSizes.push(height);
-                // 2 bytes per pixel. This rough estimation is only used when
-                // HAL does not fill in the opaque raw size
-                rawOpaqueSizes.push(width * height *2);
-            }
-            if (isInput == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT &&
-                    (format == HAL_PIXEL_FORMAT_RAW16 ||
-                     format == HAL_PIXEL_FORMAT_RAW10 ||
-                     format == HAL_PIXEL_FORMAT_RAW12 ||
-                     format == HAL_PIXEL_FORMAT_RAW_OPAQUE)) {
-                supportAnyRaw = true;
-            }
-        }
-
-        if (supportRawOpaque) {
-            entry = chars.find(ANDROID_SENSOR_OPAQUE_RAW_SIZE);
-            if (entry.count == 0) {
-                // Fill in estimated value if HAL does not list it
-                chars.update(ANDROID_SENSOR_OPAQUE_RAW_SIZE, rawOpaqueSizes);
-                derivedCharKeys.push(ANDROID_SENSOR_OPAQUE_RAW_SIZE);
-            }
-        }
-
-        // Check if HAL supports any RAW output, if so, fill in postRawSensitivityBoost range
-        if (supportAnyRaw) {
-            int32_t defaultRange[2] = {100, 100};
-            entry = chars.find(ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST_RANGE);
-            if (entry.count == 0) {
-                // Fill in default value (100, 100)
-                chars.update(
-                        ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST_RANGE,
-                        defaultRange, 2);
-                derivedCharKeys.push(ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST_RANGE);
-                // Actual request/results will be derived by camera device.
-                derivedRequestKeys.push(ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST);
-                derivedResultKeys.push(ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST);
-            }
-        }
-    }
-
-    // Always add a default for the pre-correction active array if the vendor chooses to omit this
-    camera_metadata_entry entry = chars.find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
-    if (entry.count == 0) {
-        Vector<int32_t> preCorrectionArray;
-        entry = chars.find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE);
-        preCorrectionArray.appendArray(entry.data.i32, entry.count);
-        chars.update(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE, preCorrectionArray);
-        derivedCharKeys.push(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
-    }
-
-    // Add those newly added keys to AVAILABLE_CHARACTERISTICS_KEYS
-    // This has to be done at this end of this function.
-    if (derivedCharKeys.size() > 0) {
-        appendAvailableKeys(
-                chars, ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS, derivedCharKeys);
-    }
-    if (derivedRequestKeys.size() > 0) {
-        appendAvailableKeys(
-                chars, ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS, derivedRequestKeys);
-    }
-    if (derivedResultKeys.size() > 0) {
-        appendAvailableKeys(
-                chars, ANDROID_REQUEST_AVAILABLE_RESULT_KEYS, derivedResultKeys);
-    }
-    return;
-}
-
-void CameraModule::appendAvailableKeys(CameraMetadata &chars,
-        int32_t keyTag, const Vector<int32_t>& appendKeys) {
-    camera_metadata_entry entry = chars.find(keyTag);
-    Vector<int32_t> availableKeys;
-    availableKeys.setCapacity(entry.count + appendKeys.size());
-    for (size_t i = 0; i < entry.count; i++) {
-        availableKeys.push(entry.data.i32[i]);
-    }
-    for (size_t i = 0; i < appendKeys.size(); i++) {
-        availableKeys.push(appendKeys[i]);
-    }
-    chars.update(keyTag, availableKeys);
-}
-
-CameraModule::CameraModule(camera_module_t *module) {
-    if (module == NULL) {
-        ALOGE("%s: camera hardware module must not be null", __FUNCTION__);
-        assert(0);
-    }
-    mModule = module;
-}
-
-CameraModule::~CameraModule()
-{
-    while (mCameraInfoMap.size() > 0) {
-        camera_info cameraInfo = mCameraInfoMap.editValueAt(0);
-        if (cameraInfo.static_camera_characteristics != NULL) {
-            free_camera_metadata(
-                    const_cast<camera_metadata_t*>(cameraInfo.static_camera_characteristics));
-        }
-        mCameraInfoMap.removeItemsAt(0);
-    }
-}
-
-int CameraModule::init() {
-    ATRACE_CALL();
-    int res = OK;
-    if (getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_4 &&
-            mModule->init != NULL) {
-        ATRACE_BEGIN("camera_module->init");
-        res = mModule->init();
-        ATRACE_END();
-    }
-    mCameraInfoMap.setCapacity(getNumberOfCameras());
-    return res;
-}
-
-int CameraModule::getCameraInfo(int cameraId, struct camera_info *info) {
-    ATRACE_CALL();
-    Mutex::Autolock lock(mCameraInfoLock);
-    if (cameraId < 0) {
-        ALOGE("%s: Invalid camera ID %d", __FUNCTION__, cameraId);
-        return -EINVAL;
-    }
-
-    // Only override static_camera_characteristics for API2 devices
-    int apiVersion = mModule->common.module_api_version;
-    if (apiVersion < CAMERA_MODULE_API_VERSION_2_0) {
-        int ret;
-        ATRACE_BEGIN("camera_module->get_camera_info");
-        ret = mModule->get_camera_info(cameraId, info);
-        // Fill in this so CameraService won't be confused by
-        // possibly 0 device_version
-        info->device_version = CAMERA_DEVICE_API_VERSION_1_0;
-        ATRACE_END();
-        return ret;
-    }
-
-    ssize_t index = mCameraInfoMap.indexOfKey(cameraId);
-    if (index == NAME_NOT_FOUND) {
-        // Get camera info from raw module and cache it
-        camera_info rawInfo, cameraInfo;
-        ATRACE_BEGIN("camera_module->get_camera_info");
-        int ret = mModule->get_camera_info(cameraId, &rawInfo);
-        ATRACE_END();
-        if (ret != 0) {
-            return ret;
-        }
-        int deviceVersion = rawInfo.device_version;
-        if (deviceVersion < CAMERA_DEVICE_API_VERSION_3_0) {
-            // static_camera_characteristics is invalid
-            *info = rawInfo;
-            return ret;
-        }
-        CameraMetadata m;
-        m = rawInfo.static_camera_characteristics;
-        deriveCameraCharacteristicsKeys(rawInfo.device_version, m);
-        cameraInfo = rawInfo;
-        cameraInfo.static_camera_characteristics = m.release();
-        index = mCameraInfoMap.add(cameraId, cameraInfo);
-    }
-
-    assert(index != NAME_NOT_FOUND);
-    // return the cached camera info
-    *info = mCameraInfoMap[index];
-    return OK;
-}
-
-int CameraModule::open(const char* id, struct hw_device_t** device) {
-    int res;
-    ATRACE_BEGIN("camera_module->open");
-    res = filterOpenErrorCode(mModule->common.methods->open(&mModule->common, id, device));
-    ATRACE_END();
-    return res;
-}
-
-int CameraModule::openLegacy(
-        const char* id, uint32_t halVersion, struct hw_device_t** device) {
-    int res;
-    ATRACE_BEGIN("camera_module->open_legacy");
-    res = mModule->open_legacy(&mModule->common, id, halVersion, device);
-    ATRACE_END();
-    return res;
-}
-
-int CameraModule::getNumberOfCameras() {
-    int numCameras;
-    ATRACE_BEGIN("camera_module->get_number_of_cameras");
-    numCameras = mModule->get_number_of_cameras();
-    ATRACE_END();
-    return numCameras;
-}
-
-int CameraModule::setCallbacks(const camera_module_callbacks_t *callbacks) {
-    int res;
-    ATRACE_BEGIN("camera_module->set_callbacks");
-    res = mModule->set_callbacks(callbacks);
-    ATRACE_END();
-    return res;
-}
-
-bool CameraModule::isVendorTagDefined() {
-    return mModule->get_vendor_tag_ops != NULL;
-}
-
-void CameraModule::getVendorTagOps(vendor_tag_ops_t* ops) {
-    if (mModule->get_vendor_tag_ops) {
-        ATRACE_BEGIN("camera_module->get_vendor_tag_ops");
-        mModule->get_vendor_tag_ops(ops);
-        ATRACE_END();
-    }
-}
-
-int CameraModule::setTorchMode(const char* camera_id, bool enable) {
-    int res;
-    ATRACE_BEGIN("camera_module->set_torch_mode");
-    res = mModule->set_torch_mode(camera_id, enable);
-    ATRACE_END();
-    return res;
-}
-
-status_t CameraModule::filterOpenErrorCode(status_t err) {
-    switch(err) {
-        case NO_ERROR:
-        case -EBUSY:
-        case -EINVAL:
-        case -EUSERS:
-            return err;
-        default:
-            break;
-    }
-    return -ENODEV;
-}
-
-uint16_t CameraModule::getModuleApiVersion() {
-    return mModule->common.module_api_version;
-}
-
-const char* CameraModule::getModuleName() {
-    return mModule->common.name;
-}
-
-uint16_t CameraModule::getHalApiVersion() {
-    return mModule->common.hal_api_version;
-}
-
-const char* CameraModule::getModuleAuthor() {
-    return mModule->common.author;
-}
-
-void* CameraModule::getDso() {
-    return mModule->common.dso;
-}
-
-}; // namespace android
diff --git a/services/camera/libcameraservice/common/CameraModule.h b/services/camera/libcameraservice/common/CameraModule.h
deleted file mode 100644
index d131a26..0000000
--- a/services/camera/libcameraservice/common/CameraModule.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_SERVERS_CAMERA_CAMERAMODULE_H
-#define ANDROID_SERVERS_CAMERA_CAMERAMODULE_H
-
-#include <hardware/camera.h>
-#include <camera/CameraMetadata.h>
-#include <utils/Mutex.h>
-#include <utils/KeyedVector.h>
-
-namespace android {
-/**
- * A wrapper class for HAL camera module.
- *
- * This class wraps camera_module_t returned from HAL to provide a wrapped
- * get_camera_info implementation which CameraService generates some
- * camera characteristics keys defined in newer HAL version on an older HAL.
- */
-class CameraModule {
-public:
-    explicit CameraModule(camera_module_t *module);
-    virtual ~CameraModule();
-
-    // Must be called after construction
-    // Returns OK on success, NO_INIT on failure
-    int init();
-
-    int getCameraInfo(int cameraId, struct camera_info *info);
-    int getNumberOfCameras(void);
-    int open(const char* id, struct hw_device_t** device);
-    int openLegacy(const char* id, uint32_t halVersion, struct hw_device_t** device);
-    int setCallbacks(const camera_module_callbacks_t *callbacks);
-    bool isVendorTagDefined();
-    void getVendorTagOps(vendor_tag_ops_t* ops);
-    int setTorchMode(const char* camera_id, bool enable);
-    uint16_t getModuleApiVersion();
-    const char* getModuleName();
-    uint16_t getHalApiVersion();
-    const char* getModuleAuthor();
-    // Only used by CameraModuleFixture native test. Do NOT use elsewhere.
-    void *getDso();
-
-private:
-    // Derive camera characteristics keys defined after HAL device version
-    static void deriveCameraCharacteristicsKeys(uint32_t deviceVersion, CameraMetadata &chars);
-    // Helper function to append available[request|result|chars]Keys
-    static void appendAvailableKeys(CameraMetadata &chars,
-            int32_t keyTag, const Vector<int32_t>& appendKeys);
-    status_t filterOpenErrorCode(status_t err);
-    camera_module_t *mModule;
-    KeyedVector<int, camera_info> mCameraInfoMap;
-    Mutex mCameraInfoLock;
-};
-
-} // namespace android
-
-#endif
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp
index f3a81cb..38fe1b6 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.cpp
+++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp
@@ -82,12 +82,16 @@
     return count;
 }
 
-int CameraProviderManager::getStandardCameraCount() const {
+int CameraProviderManager::getAPI1CompatibleCameraCount() const {
     std::lock_guard<std::mutex> lock(mInterfaceMutex);
     int count = 0;
     for (auto& provider : mProviders) {
         if (kStandardProviderTypes.find(provider->getType()) != std::string::npos) {
-            count += provider->mUniqueDeviceCount;
+            for (auto& device : provider->mDevices) {
+                if (device->isAPI1Compatible()) {
+                    count++;
+                }
+            }
         }
     }
     return count;
@@ -104,13 +108,15 @@
     return deviceIds;
 }
 
-std::vector<std::string> CameraProviderManager::getStandardCameraDeviceIds() const {
+std::vector<std::string> CameraProviderManager::getAPI1CompatibleCameraDeviceIds() const {
     std::lock_guard<std::mutex> lock(mInterfaceMutex);
     std::vector<std::string> deviceIds;
     for (auto& provider : mProviders) {
         if (kStandardProviderTypes.find(provider->getType()) != std::string::npos) {
-            for (auto& id : provider->mUniqueCameraIds) {
-                deviceIds.push_back(id);
+            for (auto& device : provider->mDevices) {
+                if (device->isAPI1Compatible()) {
+                    deviceIds.push_back(device->mId);
+                }
             }
         }
     }
@@ -983,6 +989,20 @@
 
     return OK;
 }
+bool CameraProviderManager::ProviderInfo::DeviceInfo3::isAPI1Compatible() const {
+    bool isBackwardCompatible = false;
+    camera_metadata_ro_entry_t caps = mCameraCharacteristics.find(
+            ANDROID_REQUEST_AVAILABLE_CAPABILITIES);
+    for (size_t i = 0; i < caps.count; i++) {
+        if (caps.data.u8[i] ==
+                ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE) {
+            isBackwardCompatible = true;
+            break;
+        }
+    }
+
+    return isBackwardCompatible;
+}
 
 status_t CameraProviderManager::ProviderInfo::DeviceInfo3::getCameraCharacteristics(
         CameraMetadata *characteristics) const {
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.h b/services/camera/libcameraservice/common/CameraProviderManager.h
index 2df4fd5..3afc1d9 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.h
+++ b/services/camera/libcameraservice/common/CameraProviderManager.h
@@ -125,16 +125,16 @@
     int getCameraCount() const;
 
     /**
-     * Retrieve the number of 'standard' cameras; these are internal and
+     * Retrieve the number of API1 compatible cameras; these are internal and
      * backwards-compatible. This is the set of cameras that will be
      * accessible via the old camera API, with IDs in range of
-     * [0, getStandardCameraCount()-1]. This value is not expected to change dynamically.
+     * [0, getAPI1CompatibleCameraCount()-1]. This value is not expected to change dynamically.
      */
-    int getStandardCameraCount() const;
+    int getAPI1CompatibleCameraCount() const;
 
     std::vector<std::string> getCameraDeviceIds() const;
 
-    std::vector<std::string> getStandardCameraDeviceIds() const;
+    std::vector<std::string> getAPI1CompatibleCameraDeviceIds() const;
 
     /**
      * Return true if a device with a given ID and major version exists
@@ -291,6 +291,7 @@
             bool hasFlashUnit() const { return mHasFlashUnit; }
             virtual status_t setTorchMode(bool enabled) = 0;
             virtual status_t getCameraInfo(hardware::CameraInfo *info) const = 0;
+            virtual bool isAPI1Compatible() const = 0;
             virtual status_t getCameraCharacteristics(CameraMetadata *characteristics) const {
                 (void) characteristics;
                 return INVALID_OPERATION;
@@ -321,7 +322,8 @@
 
             virtual status_t setTorchMode(bool enabled) override;
             virtual status_t getCameraInfo(hardware::CameraInfo *info) const override;
-
+            //In case of Device1Info assume that we are always API1 compatible
+            virtual bool isAPI1Compatible() const override { return true; }
             DeviceInfo1(const std::string& name, const metadata_vendor_id_t tagId,
                     const std::string &id, uint16_t minorVersion,
                     const hardware::camera::common::V1_0::CameraResourceCost& resourceCost,
@@ -338,6 +340,7 @@
 
             virtual status_t setTorchMode(bool enabled) override;
             virtual status_t getCameraInfo(hardware::CameraInfo *info) const override;
+            virtual bool isAPI1Compatible() const override;
             virtual status_t getCameraCharacteristics(
                     CameraMetadata *characteristics) const override;
 
diff --git a/services/camera/libcameraservice/common/FrameProcessorBase.cpp b/services/camera/libcameraservice/common/FrameProcessorBase.cpp
index 9e78f88..41c953b 100644
--- a/services/camera/libcameraservice/common/FrameProcessorBase.cpp
+++ b/services/camera/libcameraservice/common/FrameProcessorBase.cpp
@@ -32,8 +32,7 @@
     mDevice(device),
     mNumPartialResults(1) {
     sp<CameraDeviceBase> cameraDevice = device.promote();
-    if (cameraDevice != 0 &&
-            cameraDevice->getDeviceVersion() >= CAMERA_DEVICE_API_VERSION_3_2) {
+    if (cameraDevice != 0) {
         CameraMetadata staticInfo = cameraDevice->info();
         camera_metadata_entry_t entry = staticInfo.find(ANDROID_REQUEST_PARTIAL_RESULT_COUNT);
         if (entry.count > 0) {
@@ -171,18 +170,8 @@
     camera_metadata_ro_entry_t entry;
 
     // Check if this result is partial.
-    bool isPartialResult = false;
-    if (device->getDeviceVersion() >= CAMERA_DEVICE_API_VERSION_3_2) {
-        isPartialResult = result.mResultExtras.partialResultCount < mNumPartialResults;
-    } else {
-        entry = result.mMetadata.find(ANDROID_QUIRKS_PARTIAL_RESULT);
-        if (entry.count != 0 &&
-                entry.data.u8[0] == ANDROID_QUIRKS_PARTIAL_RESULT_PARTIAL) {
-            ALOGV("%s: Camera %s: This is a partial result",
-                    __FUNCTION__, device->getId().string());
-            isPartialResult = true;
-        }
-    }
+    bool isPartialResult =
+            result.mResultExtras.partialResultCount < mNumPartialResults;
 
     // TODO: instead of getting requestID from CameraMetadata, we should get it
     // from CaptureResultExtras. This will require changing Camera2Device.
diff --git a/services/camera/libcameraservice/device1/CameraHardwareInterface.cpp b/services/camera/libcameraservice/device1/CameraHardwareInterface.cpp
index 9df7cd4..469c86c 100644
--- a/services/camera/libcameraservice/device1/CameraHardwareInterface.cpp
+++ b/services/camera/libcameraservice/device1/CameraHardwareInterface.cpp
@@ -41,37 +41,6 @@
     }
 }
 
-status_t CameraHardwareInterface::initialize(CameraModule *module)
-{
-    if (mHidlDevice != nullptr) {
-        ALOGE("%s: camera hardware interface has been initialized to HIDL path!", __FUNCTION__);
-        return INVALID_OPERATION;
-    }
-    ALOGI("Opening camera %s", mName.string());
-    camera_info info;
-    status_t res = module->getCameraInfo(atoi(mName.string()), &info);
-    if (res != OK) {
-        return res;
-    }
-
-    int rc = OK;
-    if (module->getModuleApiVersion() >= CAMERA_MODULE_API_VERSION_2_3 &&
-        info.device_version > CAMERA_DEVICE_API_VERSION_1_0) {
-        // Open higher version camera device as HAL1.0 device.
-        rc = module->openLegacy(mName.string(),
-                                 CAMERA_DEVICE_API_VERSION_1_0,
-                                 (hw_device_t **)&mDevice);
-    } else {
-        rc = module->open(mName.string(), (hw_device_t **)&mDevice);
-    }
-    if (rc != OK) {
-        ALOGE("Could not open camera %s: %d", mName.string(), rc);
-        return rc;
-    }
-    initHalPreviewWindow();
-    return rc;
-}
-
 status_t CameraHardwareInterface::initialize(sp<CameraProviderManager> manager) {
     if (mDevice) {
         ALOGE("%s: camera hardware interface has been initialized to libhardware path!",
diff --git a/services/camera/libcameraservice/device1/CameraHardwareInterface.h b/services/camera/libcameraservice/device1/CameraHardwareInterface.h
index 907065f..1c38d00 100644
--- a/services/camera/libcameraservice/device1/CameraHardwareInterface.h
+++ b/services/camera/libcameraservice/device1/CameraHardwareInterface.h
@@ -28,7 +28,6 @@
 #include <system/window.h>
 #include <hardware/camera.h>
 
-#include <common/CameraModule.h>
 #include <common/CameraProviderManager.h>
 
 namespace android {
@@ -107,7 +106,6 @@
 
     ~CameraHardwareInterface();
 
-    status_t initialize(CameraModule *module);
     status_t initialize(sp<CameraProviderManager> manager);
 
     /** Set the ANativeWindow to which preview frames are sent */
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index b64488c..4706319 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -94,82 +94,6 @@
     return mId;
 }
 
-/**
- * CameraDeviceBase interface
- */
-
-status_t Camera3Device::initialize(CameraModule *module)
-{
-    ATRACE_CALL();
-    Mutex::Autolock il(mInterfaceLock);
-    Mutex::Autolock l(mLock);
-
-    ALOGV("%s: Initializing device for camera %s", __FUNCTION__, mId.string());
-    if (mStatus != STATUS_UNINITIALIZED) {
-        CLOGE("Already initialized!");
-        return INVALID_OPERATION;
-    }
-
-    /** Open HAL device */
-
-    status_t res;
-
-    camera3_device_t *device;
-
-    ATRACE_BEGIN("CameraHal::open");
-    res = module->open(mId.string(),
-            reinterpret_cast<hw_device_t**>(&device));
-    ATRACE_END();
-
-    if (res != OK) {
-        SET_ERR_L("Could not open camera: %s (%d)", strerror(-res), res);
-        return res;
-    }
-
-    /** Cross-check device version */
-    if (device->common.version < CAMERA_DEVICE_API_VERSION_3_2) {
-        SET_ERR_L("Could not open camera: "
-                "Camera device should be at least %x, reports %x instead",
-                CAMERA_DEVICE_API_VERSION_3_2,
-                device->common.version);
-        device->common.close(&device->common);
-        return BAD_VALUE;
-    }
-
-    camera_info info;
-    res = module->getCameraInfo(atoi(mId), &info);
-    if (res != OK) return res;
-
-    if (info.device_version != device->common.version) {
-        SET_ERR_L("HAL reporting mismatched camera_info version (%x)"
-                " and device version (%x).",
-                info.device_version, device->common.version);
-        device->common.close(&device->common);
-        return BAD_VALUE;
-    }
-
-    /** Initialize device with callback functions */
-
-    ATRACE_BEGIN("CameraHal::initialize");
-    res = device->ops->initialize(device, this);
-    ATRACE_END();
-
-    if (res != OK) {
-        SET_ERR_L("Unable to initialize HAL device: %s (%d)",
-                strerror(-res), res);
-        device->common.close(&device->common);
-        return res;
-    }
-
-    /** Everything is good to go */
-
-    mDeviceVersion = device->common.version;
-    mDeviceInfo = info.static_camera_characteristics;
-    mInterface = std::make_unique<HalInterface>(device);
-
-    return initializeCommonLocked();
-}
-
 status_t Camera3Device::initialize(sp<CameraProviderManager> manager) {
     ATRACE_CALL();
     Mutex::Autolock il(mInterfaceLock);
@@ -212,8 +136,7 @@
     if (!requestQueueRet.isOk()) {
         ALOGE("Transaction error when getting request metadata fmq: %s, not use it",
                 requestQueueRet.description().c_str());
-        queue = nullptr;
-        // Don't use the queue onwards.
+        return DEAD_OBJECT;
     }
     auto resultQueueRet = session->getCaptureResultMetadataQueue(
         [&queue = mResultMetadataQueue](const auto& descriptor) {
@@ -227,13 +150,9 @@
     if (!resultQueueRet.isOk()) {
         ALOGE("Transaction error when getting result metadata queue from camera session: %s",
                 resultQueueRet.description().c_str());
-        mResultMetadataQueue = nullptr;
-        // Don't use the queue onwards.
+        return DEAD_OBJECT;
     }
 
-    // TODO: camera service will absorb 3_2/3_3/3_4 differences in the future
-    //       for now use 3_4 to keep legacy devices working
-    mDeviceVersion = CAMERA_DEVICE_API_VERSION_3_4;
     mInterface = std::make_unique<HalInterface>(session, queue);
     std::string providerType;
     mVendorTagId = manager->getProviderTagIdLocked(mId.string());
@@ -262,17 +181,8 @@
 
     mTagMonitor.initialize(mVendorTagId);
 
-    bool aeLockAvailable = false;
-    camera_metadata_entry aeLockAvailableEntry = mDeviceInfo.find(
-            ANDROID_CONTROL_AE_LOCK_AVAILABLE);
-    if (aeLockAvailableEntry.count > 0) {
-        aeLockAvailable = (aeLockAvailableEntry.data.u8[0] ==
-                ANDROID_CONTROL_AE_LOCK_AVAILABLE_TRUE);
-    }
-
     /** Start up request queue thread */
-    mRequestThread = new RequestThread(this, mStatusTracker, mInterface.get(), mDeviceVersion,
-            aeLockAvailable);
+    mRequestThread = new RequestThread(this, mStatusTracker, mInterface.get());
     res = mRequestThread->run(String8::format("C3Dev-%s-ReqQueue", mId.string()).string());
     if (res != OK) {
         SET_ERR_L("Unable to start request queue thread: %s (%d)",
@@ -284,13 +194,6 @@
 
     mPreparerThread = new PreparerThread();
 
-    // Determine whether we need to derive sensitivity boost values for older devices.
-    // If post-RAW sensitivity boost range is listed, so should post-raw sensitivity control
-    // be listed (as the default value 100)
-    if (mDeviceInfo.exists(ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST_RANGE)) {
-        mDerivePostRawSensKey = true;
-    }
-
     internalUpdateStatusLocked(STATUS_UNCONFIGURED);
     mNextStreamId = 0;
     mDummyStreamId = NO_STREAM;
@@ -306,19 +209,11 @@
     }
 
     // Will the HAL be sending in early partial result metadata?
-    if (mDeviceVersion >= CAMERA_DEVICE_API_VERSION_3_2) {
-        camera_metadata_entry partialResultsCount =
-                mDeviceInfo.find(ANDROID_REQUEST_PARTIAL_RESULT_COUNT);
-        if (partialResultsCount.count > 0) {
-            mNumPartialResults = partialResultsCount.data.i32[0];
-            mUsePartialResult = (mNumPartialResults > 1);
-        }
-    } else {
-        camera_metadata_entry partialResultsQuirk =
-                mDeviceInfo.find(ANDROID_QUIRKS_USE_PARTIAL_RESULT);
-        if (partialResultsQuirk.count > 0 && partialResultsQuirk.data.u8[0] == 1) {
-            mUsePartialResult = true;
-        }
+    camera_metadata_entry partialResultsCount =
+            mDeviceInfo.find(ANDROID_REQUEST_PARTIAL_RESULT_COUNT);
+    if (partialResultsCount.count > 0) {
+        mNumPartialResults = partialResultsCount.data.i32[0];
+        mUsePartialResult = (mNumPartialResults > 1);
     }
 
     camera_metadata_entry configs =
@@ -434,48 +329,32 @@
 
 Camera3Device::Size Camera3Device::getMaxJpegResolution() const {
     int32_t maxJpegWidth = 0, maxJpegHeight = 0;
-    if (mDeviceVersion >= CAMERA_DEVICE_API_VERSION_3_2) {
-        const int STREAM_CONFIGURATION_SIZE = 4;
-        const int STREAM_FORMAT_OFFSET = 0;
-        const int STREAM_WIDTH_OFFSET = 1;
-        const int STREAM_HEIGHT_OFFSET = 2;
-        const int STREAM_IS_INPUT_OFFSET = 3;
-        camera_metadata_ro_entry_t availableStreamConfigs =
-                mDeviceInfo.find(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
-        if (availableStreamConfigs.count == 0 ||
-                availableStreamConfigs.count % STREAM_CONFIGURATION_SIZE != 0) {
-            return Size(0, 0);
-        }
+    const int STREAM_CONFIGURATION_SIZE = 4;
+    const int STREAM_FORMAT_OFFSET = 0;
+    const int STREAM_WIDTH_OFFSET = 1;
+    const int STREAM_HEIGHT_OFFSET = 2;
+    const int STREAM_IS_INPUT_OFFSET = 3;
+    camera_metadata_ro_entry_t availableStreamConfigs =
+            mDeviceInfo.find(ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
+    if (availableStreamConfigs.count == 0 ||
+            availableStreamConfigs.count % STREAM_CONFIGURATION_SIZE != 0) {
+        return Size(0, 0);
+    }
 
-        // Get max jpeg size (area-wise).
-        for (size_t i=0; i < availableStreamConfigs.count; i+= STREAM_CONFIGURATION_SIZE) {
-            int32_t format = availableStreamConfigs.data.i32[i + STREAM_FORMAT_OFFSET];
-            int32_t width = availableStreamConfigs.data.i32[i + STREAM_WIDTH_OFFSET];
-            int32_t height = availableStreamConfigs.data.i32[i + STREAM_HEIGHT_OFFSET];
-            int32_t isInput = availableStreamConfigs.data.i32[i + STREAM_IS_INPUT_OFFSET];
-            if (isInput == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT
-                    && format == HAL_PIXEL_FORMAT_BLOB &&
-                    (width * height > maxJpegWidth * maxJpegHeight)) {
-                maxJpegWidth = width;
-                maxJpegHeight = height;
-            }
-        }
-    } else {
-        camera_metadata_ro_entry availableJpegSizes =
-                mDeviceInfo.find(ANDROID_SCALER_AVAILABLE_JPEG_SIZES);
-        if (availableJpegSizes.count == 0 || availableJpegSizes.count % 2 != 0) {
-            return Size(0, 0);
-        }
-
-        // Get max jpeg size (area-wise).
-        for (size_t i = 0; i < availableJpegSizes.count; i += 2) {
-            if ((availableJpegSizes.data.i32[i] * availableJpegSizes.data.i32[i + 1])
-                    > (maxJpegWidth * maxJpegHeight)) {
-                maxJpegWidth = availableJpegSizes.data.i32[i];
-                maxJpegHeight = availableJpegSizes.data.i32[i + 1];
-            }
+    // Get max jpeg size (area-wise).
+    for (size_t i=0; i < availableStreamConfigs.count; i+= STREAM_CONFIGURATION_SIZE) {
+        int32_t format = availableStreamConfigs.data.i32[i + STREAM_FORMAT_OFFSET];
+        int32_t width = availableStreamConfigs.data.i32[i + STREAM_WIDTH_OFFSET];
+        int32_t height = availableStreamConfigs.data.i32[i + STREAM_HEIGHT_OFFSET];
+        int32_t isInput = availableStreamConfigs.data.i32[i + STREAM_IS_INPUT_OFFSET];
+        if (isInput == ANDROID_SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT
+                && format == HAL_PIXEL_FORMAT_BLOB &&
+                (width * height > maxJpegWidth * maxJpegHeight)) {
+            maxJpegWidth = width;
+            maxJpegHeight = height;
         }
     }
+
     return Size(maxJpegWidth, maxJpegHeight);
 }
 
@@ -497,31 +376,6 @@
     return measured;
 }
 
-/**
- * Map Android N dataspace definitions back to Android M definitions, for
- * use with HALv3.3 or older.
- *
- * Only map where correspondences exist, and otherwise preserve the value.
- */
-android_dataspace Camera3Device::mapToLegacyDataspace(android_dataspace dataSpace) {
-    switch (dataSpace) {
-        case HAL_DATASPACE_V0_SRGB_LINEAR:
-            return HAL_DATASPACE_SRGB_LINEAR;
-        case HAL_DATASPACE_V0_SRGB:
-            return HAL_DATASPACE_SRGB;
-        case HAL_DATASPACE_V0_JFIF:
-            return HAL_DATASPACE_JFIF;
-        case HAL_DATASPACE_V0_BT601_625:
-            return HAL_DATASPACE_BT601_625;
-        case HAL_DATASPACE_V0_BT601_525:
-            return HAL_DATASPACE_BT601_525;
-        case HAL_DATASPACE_V0_BT709:
-            return HAL_DATASPACE_BT709;
-        default:
-            return dataSpace;
-    }
-}
-
 hardware::graphics::common::V1_0::PixelFormat Camera3Device::mapToPixelFormat(
         int frameworkFormat) {
     return (hardware::graphics::common::V1_0::PixelFormat) frameworkFormat;
@@ -1365,32 +1219,17 @@
     assert(mStatus != STATUS_ACTIVE);
 
     sp<Camera3OutputStream> newStream;
-    // Overwrite stream set id to invalid for HAL3.2 or lower, as buffer manager does support
-    // such devices.
-    if (mDeviceVersion <= CAMERA_DEVICE_API_VERSION_3_2) {
-        streamSetId = CAMERA3_STREAM_SET_ID_INVALID;
-    }
 
     if (consumers.size() == 0 && !hasDeferredConsumer) {
         ALOGE("%s: Number of consumers cannot be smaller than 1", __FUNCTION__);
         return BAD_VALUE;
     }
-    // HAL3.1 doesn't support deferred consumer stream creation as it requires buffer registration
-    // which requires a consumer surface to be available.
-    if (hasDeferredConsumer && mDeviceVersion < CAMERA_DEVICE_API_VERSION_3_2) {
-        ALOGE("HAL3.1 doesn't support deferred consumer stream creation");
-        return BAD_VALUE;
-    }
 
     if (hasDeferredConsumer && format != HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) {
         ALOGE("Deferred consumer stream creation only support IMPLEMENTATION_DEFINED format");
         return BAD_VALUE;
     }
 
-    // Use legacy dataspace values for older HALs
-    if (mDeviceVersion <= CAMERA_DEVICE_API_VERSION_3_3) {
-        dataSpace = mapToLegacyDataspace(dataSpace);
-    }
     if (format == HAL_PIXEL_FORMAT_BLOB) {
         ssize_t blobBufferSize;
         if (dataSpace != HAL_DATASPACE_DEPTH) {
@@ -1433,15 +1272,7 @@
     }
     newStream->setStatusTracker(mStatusTracker);
 
-    /**
-     * Camera3 Buffer manager is only supported by HAL3.3 onwards, as the older HALs ( < HAL3.2)
-     * requires buffers to be statically allocated for internal static buffer registration, while
-     * the buffers provided by buffer manager are really dynamically allocated. For HAL3.2, because
-     * not all HAL implementation supports dynamic buffer registeration, exlude it as well.
-     */
-    if (mDeviceVersion > CAMERA_DEVICE_API_VERSION_3_2) {
-        newStream->setBufferManager(mBufferManager);
-    }
+    newStream->setBufferManager(mBufferManager);
 
     res = mOutputStreams.add(mNextStreamId, newStream);
     if (res < 0) {
@@ -1659,15 +1490,6 @@
     set_camera_metadata_vendor_id(rawRequest, mVendorTagId);
     mRequestTemplateCache[templateId].acquire(rawRequest);
 
-    // Derive some new keys for backward compatibility
-    if (mDerivePostRawSensKey && !mRequestTemplateCache[templateId].exists(
-            ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST)) {
-        int32_t defaultBoost[1] = {100};
-        mRequestTemplateCache[templateId].update(
-                ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST,
-                defaultBoost, 1);
-    }
-
     *request = mRequestTemplateCache[templateId];
     return OK;
 }
@@ -1919,15 +1741,7 @@
         mRequestThread->clear(/*out*/frameNumber);
     }
 
-    status_t res;
-    if (mDeviceVersion >= CAMERA_DEVICE_API_VERSION_3_1) {
-        res = mRequestThread->flush();
-    } else {
-        Mutex::Autolock l(mLock);
-        res = waitUntilDrainedLocked();
-    }
-
-    return res;
+    return mRequestThread->flush();
 }
 
 status_t Camera3Device::prepare(int streamId) {
@@ -1968,14 +1782,6 @@
     Mutex::Autolock il(mInterfaceLock);
     Mutex::Autolock l(mLock);
 
-    // Teardown can only be accomplished on devices that don't require register_stream_buffers,
-    // since we cannot call register_stream_buffers except right after configure_streams.
-    if (mDeviceVersion < CAMERA_DEVICE_API_VERSION_3_2) {
-        ALOGE("%s: Unable to tear down streams on device HAL v%x",
-                __FUNCTION__, mDeviceVersion);
-        return NO_INIT;
-    }
-
     sp<Camera3StreamInterface> stream;
     ssize_t outputStreamIdx = mOutputStreams.indexOfKey(streamId);
     if (outputStreamIdx == NAME_NOT_FOUND) {
@@ -2013,12 +1819,6 @@
     return OK;
 }
 
-uint32_t Camera3Device::getDeviceVersion() {
-    ATRACE_CALL();
-    Mutex::Autolock il(mInterfaceLock);
-    return mDeviceVersion;
-}
-
 /**
  * Methods called by subclasses
  */
@@ -2511,14 +2311,13 @@
 
 status_t Camera3Device::registerInFlight(uint32_t frameNumber,
         int32_t numBuffers, CaptureResultExtras resultExtras, bool hasInput,
-        const AeTriggerCancelOverride_t &aeTriggerCancelOverride,
         bool hasAppCallback) {
     ATRACE_CALL();
     Mutex::Autolock l(mInFlightLock);
 
     ssize_t res;
     res = mInFlightMap.add(frameNumber, InFlightRequest(numBuffers, resultExtras, hasInput,
-            aeTriggerCancelOverride, hasAppCallback));
+            hasAppCallback));
     if (res < 0) return res;
 
     if (mInFlightMap.size() == 1) {
@@ -2603,8 +2402,8 @@
     }
 }
 
-void Camera3Device::insertResultLocked(CaptureResult *result, uint32_t frameNumber,
-            const AeTriggerCancelOverride_t &aeTriggerCancelOverride) {
+void Camera3Device::insertResultLocked(CaptureResult *result,
+        uint32_t frameNumber) {
     if (result == nullptr) return;
 
     camera_metadata_t *meta = const_cast<camera_metadata_t *>(
@@ -2623,8 +2422,6 @@
         return;
     }
 
-    overrideResultForPrecaptureCancel(&result->mMetadata, aeTriggerCancelOverride);
-
     // Valid result, insert into queue
     List<CaptureResult>::iterator queuedResult =
             mResultQueue.insert(mResultQueue.end(), CaptureResult(*result));
@@ -2639,15 +2436,14 @@
 
 
 void Camera3Device::sendPartialCaptureResult(const camera_metadata_t * partialResult,
-        const CaptureResultExtras &resultExtras, uint32_t frameNumber,
-        const AeTriggerCancelOverride_t &aeTriggerCancelOverride) {
+        const CaptureResultExtras &resultExtras, uint32_t frameNumber) {
     Mutex::Autolock l(mOutputLock);
 
     CaptureResult captureResult;
     captureResult.mResultExtras = resultExtras;
     captureResult.mMetadata = partialResult;
 
-    insertResultLocked(&captureResult, frameNumber, aeTriggerCancelOverride);
+    insertResultLocked(&captureResult, frameNumber);
 }
 
 
@@ -2655,8 +2451,7 @@
         CaptureResultExtras &resultExtras,
         CameraMetadata &collectedPartialResult,
         uint32_t frameNumber,
-        bool reprocess,
-        const AeTriggerCancelOverride_t &aeTriggerCancelOverride) {
+        bool reprocess) {
     if (pendingMetadata.isEmpty())
         return;
 
@@ -2690,15 +2485,6 @@
         captureResult.mMetadata.append(collectedPartialResult);
     }
 
-    // Derive some new keys for backward compaibility
-    if (mDerivePostRawSensKey && !captureResult.mMetadata.exists(
-            ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST)) {
-        int32_t defaultBoost[1] = {100};
-        captureResult.mMetadata.update(
-                ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST,
-                defaultBoost, 1);
-    }
-
     captureResult.mMetadata.sort();
 
     // Check that there's a timestamp in the result metadata
@@ -2712,7 +2498,7 @@
     mTagMonitor.monitorMetadata(TagMonitor::RESULT,
             frameNumber, timestamp.data.i64[0], captureResult.mMetadata);
 
-    insertResultLocked(&captureResult, frameNumber, aeTriggerCancelOverride);
+    insertResultLocked(&captureResult, frameNumber);
 }
 
 /**
@@ -2732,10 +2518,7 @@
         return;
     }
 
-    // For HAL3.2 or above, If HAL doesn't support partial, it must always set
-    // partial_result to 1 when metadata is included in this result.
     if (!mUsePartialResult &&
-            mDeviceVersion >= CAMERA_DEVICE_API_VERSION_3_2 &&
             result->result != NULL &&
             result->partial_result != 1) {
         SET_ERR("Result is malformed for frame %d: partial_result %u must be 1"
@@ -2780,39 +2563,21 @@
 
         // Check if this result carries only partial metadata
         if (mUsePartialResult && result->result != NULL) {
-            if (mDeviceVersion >= CAMERA_DEVICE_API_VERSION_3_2) {
-                if (result->partial_result > mNumPartialResults || result->partial_result < 1) {
-                    SET_ERR("Result is malformed for frame %d: partial_result %u must be  in"
-                            " the range of [1, %d] when metadata is included in the result",
-                            frameNumber, result->partial_result, mNumPartialResults);
-                    return;
-                }
-                isPartialResult = (result->partial_result < mNumPartialResults);
-                if (isPartialResult) {
-                    request.collectedPartialResult.append(result->result);
-                }
-            } else {
-                camera_metadata_ro_entry_t partialResultEntry;
-                res = find_camera_metadata_ro_entry(result->result,
-                        ANDROID_QUIRKS_PARTIAL_RESULT, &partialResultEntry);
-                if (res != NAME_NOT_FOUND &&
-                        partialResultEntry.count > 0 &&
-                        partialResultEntry.data.u8[0] ==
-                        ANDROID_QUIRKS_PARTIAL_RESULT_PARTIAL) {
-                    // A partial result. Flag this as such, and collect this
-                    // set of metadata into the in-flight entry.
-                    isPartialResult = true;
-                    request.collectedPartialResult.append(
-                        result->result);
-                    request.collectedPartialResult.erase(
-                        ANDROID_QUIRKS_PARTIAL_RESULT);
-                }
+            if (result->partial_result > mNumPartialResults || result->partial_result < 1) {
+                SET_ERR("Result is malformed for frame %d: partial_result %u must be  in"
+                        " the range of [1, %d] when metadata is included in the result",
+                        frameNumber, result->partial_result, mNumPartialResults);
+                return;
+            }
+            isPartialResult = (result->partial_result < mNumPartialResults);
+            if (isPartialResult) {
+                request.collectedPartialResult.append(result->result);
             }
 
             if (isPartialResult && request.hasCallback) {
                 // Send partial capture result
-                sendPartialCaptureResult(result->result, request.resultExtras, frameNumber,
-                        request.aeTriggerCancelOverride);
+                sendPartialCaptureResult(result->result, request.resultExtras,
+                        frameNumber);
             }
         }
 
@@ -2877,8 +2642,8 @@
                 CameraMetadata metadata;
                 metadata = result->result;
                 sendCaptureResult(metadata, request.resultExtras,
-                    collectedPartialResult, frameNumber, hasInputBufferInRequest,
-                    request.aeTriggerCancelOverride);
+                    collectedPartialResult, frameNumber,
+                    hasInputBufferInRequest);
             }
         }
 
@@ -3058,7 +2823,7 @@
                 // send pending result and buffers
                 sendCaptureResult(r.pendingMetadata, r.resultExtras,
                     r.collectedPartialResult, msg.frame_number,
-                    r.hasInputBuffer, r.aeTriggerCancelOverride);
+                    r.hasInputBuffer);
             }
             returnOutputBuffers(r.pendingOutputBuffers.array(),
                 r.pendingOutputBuffers.size(), r.shutterTimestamp);
@@ -3096,9 +2861,6 @@
  * HalInterface inner class methods
  */
 
-Camera3Device::HalInterface::HalInterface(camera3_device_t *device) :
-        mHal3Device(device) {}
-
 Camera3Device::HalInterface::HalInterface(
             sp<ICameraDeviceSession> &session,
             std::shared_ptr<RequestMetadataQueue> queue) :
@@ -3171,7 +2933,7 @@
                 // Unknown template ID
                 return BAD_VALUE;
         }
-        mHidlSession->constructDefaultRequestSettings(id,
+        auto err = mHidlSession->constructDefaultRequestSettings(id,
                 [&status, &requestTemplate]
                 (common::V1_0::Status s, const device::V3_2::CameraMetadata& request) {
                     status = s;
@@ -3193,7 +2955,12 @@
                         }
                     }
                 });
-        res = CameraProviderManager::mapToStatusT(status);
+        if (!err.isOk()) {
+            ALOGE("%s: Transaction error: %s", __FUNCTION__, err.description().c_str());
+            res = DEAD_OBJECT;
+        } else {
+            res = CameraProviderManager::mapToStatusT(status);
+        }
     }
     return res;
 }
@@ -3267,12 +3034,17 @@
 
         HalStreamConfiguration finalConfiguration;
         common::V1_0::Status status;
-        mHidlSession->configureStreams(requestedConfiguration,
+        auto err = mHidlSession->configureStreams(requestedConfiguration,
                 [&status, &finalConfiguration]
                 (common::V1_0::Status s, const HalStreamConfiguration& halConfiguration) {
                     finalConfiguration = halConfiguration;
                     status = s;
                 });
+        if (!err.isOk()) {
+            ALOGE("%s: Transaction error: %s", __FUNCTION__, err.description().c_str());
+            return DEAD_OBJECT;
+        }
+
         if (status != common::V1_0::Status::OK ) {
             return CameraProviderManager::mapToStatusT(status);
         }
@@ -3458,12 +3230,15 @@
             captureRequest->fmqSettingsSize = 0u;
         }
     }
-    mHidlSession->processCaptureRequest(captureRequests, cachesToRemove,
+    auto err = mHidlSession->processCaptureRequest(captureRequests, cachesToRemove,
             [&status, &numRequestProcessed] (auto s, uint32_t n) {
                 status = s;
                 *numRequestProcessed = n;
             });
-
+    if (!err.isOk()) {
+        ALOGE("%s: Transaction error: %s", __FUNCTION__, err.description().c_str());
+        return DEAD_OBJECT;
+    }
     if (status == common::V1_0::Status::OK && *numRequestProcessed != batchSize) {
         ALOGE("%s: processCaptureRequest returns OK but processed %d/%zu requests",
                 __FUNCTION__, *numRequestProcessed, batchSize);
@@ -3501,7 +3276,13 @@
     if (mHal3Device != nullptr) {
         res = mHal3Device->ops->flush(mHal3Device);
     } else {
-        res = CameraProviderManager::mapToStatusT(mHidlSession->flush());
+        auto err = mHidlSession->flush();
+        if (!err.isOk()) {
+            ALOGE("%s: Transaction error: %s", __FUNCTION__, err.description().c_str());
+            res = DEAD_OBJECT;
+        } else {
+            res = CameraProviderManager::mapToStatusT(err);
+        }
     }
     return res;
 }
@@ -3527,7 +3308,11 @@
     if (mHal3Device != nullptr) {
         mHal3Device->common.close(&mHal3Device->common);
     } else {
-        mHidlSession->close();
+        auto err = mHidlSession->close();
+        // Interface will be dead shortly anyway, so don't log errors
+        if (!err.isOk()) {
+            res = DEAD_OBJECT;
+        }
     }
     return res;
 }
@@ -3606,14 +3391,11 @@
 
 Camera3Device::RequestThread::RequestThread(wp<Camera3Device> parent,
         sp<StatusTracker> statusTracker,
-        HalInterface* interface,
-        uint32_t deviceVersion,
-        bool aeLockAvailable) :
+        HalInterface* interface) :
         Thread(/*canCallJava*/false),
         mParent(parent),
         mStatusTracker(statusTracker),
         mInterface(interface),
-        mDeviceVersion(deviceVersion),
         mListener(nullptr),
         mId(getId(parent)),
         mReconfigured(false),
@@ -3625,7 +3407,6 @@
         mCurrentPreCaptureTriggerId(0),
         mRepeatingLastFrameNumber(
             hardware::camera2::ICameraDeviceUser::NO_IN_FLIGHT_REPEATING_FRAMES),
-        mAeLockAvailable(aeLockAvailable),
         mPrepareVideoStream(false) {
     mStatusId = statusTracker->addComponent();
 }
@@ -3820,11 +3601,7 @@
     ATRACE_CALL();
     Mutex::Autolock l(mFlushLock);
 
-    if (mDeviceVersion >= CAMERA_DEVICE_API_VERSION_3_1) {
-        return mInterface->flush();
-    }
-
-    return -ENOTSUP;
+    return mInterface->flush();
 }
 
 void Camera3Device::RequestThread::setPaused(bool paused) {
@@ -3857,65 +3634,6 @@
     mRequestSignal.signal();
 }
 
-
-/**
- * For devices <= CAMERA_DEVICE_API_VERSION_3_2, AE_PRECAPTURE_TRIGGER_CANCEL is not supported so
- * we need to override AE_PRECAPTURE_TRIGGER_CANCEL to AE_PRECAPTURE_TRIGGER_IDLE and AE_LOCK_OFF
- * to AE_LOCK_ON to start cancelling AE precapture. If AE lock is not available, it still overrides
- * AE_PRECAPTURE_TRIGGER_CANCEL to AE_PRECAPTURE_TRIGGER_IDLE but doesn't add AE_LOCK_ON to the
- * request.
- */
-void Camera3Device::RequestThread::handleAePrecaptureCancelRequest(const sp<CaptureRequest>& request) {
-    request->mAeTriggerCancelOverride.applyAeLock = false;
-    request->mAeTriggerCancelOverride.applyAePrecaptureTrigger = false;
-
-    if (mDeviceVersion > CAMERA_DEVICE_API_VERSION_3_2) {
-        return;
-    }
-
-    camera_metadata_entry_t aePrecaptureTrigger =
-            request->mSettings.find(ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER);
-    if (aePrecaptureTrigger.count > 0 &&
-            aePrecaptureTrigger.data.u8[0] == ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL) {
-        // Always override CANCEL to IDLE
-        uint8_t aePrecaptureTrigger = ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_IDLE;
-        request->mSettings.update(ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER, &aePrecaptureTrigger, 1);
-        request->mAeTriggerCancelOverride.applyAePrecaptureTrigger = true;
-        request->mAeTriggerCancelOverride.aePrecaptureTrigger =
-                ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL;
-
-        if (mAeLockAvailable == true) {
-            camera_metadata_entry_t aeLock = request->mSettings.find(ANDROID_CONTROL_AE_LOCK);
-            if (aeLock.count == 0 ||  aeLock.data.u8[0] == ANDROID_CONTROL_AE_LOCK_OFF) {
-                uint8_t aeLock = ANDROID_CONTROL_AE_LOCK_ON;
-                request->mSettings.update(ANDROID_CONTROL_AE_LOCK, &aeLock, 1);
-                request->mAeTriggerCancelOverride.applyAeLock = true;
-                request->mAeTriggerCancelOverride.aeLock = ANDROID_CONTROL_AE_LOCK_OFF;
-            }
-        }
-    }
-}
-
-/**
- * Override result metadata for cancelling AE precapture trigger applied in
- * handleAePrecaptureCancelRequest().
- */
-void Camera3Device::overrideResultForPrecaptureCancel(
-        CameraMetadata *result, const AeTriggerCancelOverride_t &aeTriggerCancelOverride) {
-    if (aeTriggerCancelOverride.applyAeLock) {
-        // Only devices <= v3.2 should have this override
-        assert(mDeviceVersion <= CAMERA_DEVICE_API_VERSION_3_2);
-        result->update(ANDROID_CONTROL_AE_LOCK, &aeTriggerCancelOverride.aeLock, 1);
-    }
-
-    if (aeTriggerCancelOverride.applyAePrecaptureTrigger) {
-        // Only devices <= v3.2 should have this override
-        assert(mDeviceVersion <= CAMERA_DEVICE_API_VERSION_3_2);
-        result->update(ANDROID_CONTROL_AE_PRECAPTURE_TRIGGER,
-                &aeTriggerCancelOverride.aePrecaptureTrigger, 1);
-    }
-}
-
 void Camera3Device::RequestThread::checkAndStopRepeatingRequest() {
     bool surfaceAbandoned = false;
     int64_t lastFrameNumber = 0;
@@ -4283,7 +4001,6 @@
         res = parent->registerInFlight(halRequest->frame_number,
                 totalNumBuffers, captureRequest->mResultExtras,
                 /*hasInput*/halRequest->input_buffer != NULL,
-                captureRequest->mAeTriggerCancelOverride,
                 hasCallback);
         ALOGVV("%s: registered in flight requestId = %" PRId32 ", frameNumber = %" PRId64
                ", burstId = %" PRId32 ".",
@@ -4542,8 +4259,6 @@
         }
     }
 
-    handleAePrecaptureCancelRequest(nextRequest);
-
     return nextRequest;
 }
 
@@ -4629,9 +4344,7 @@
                 request->mResultExtras.afTriggerId = triggerId;
                 mCurrentAfTriggerId = triggerId;
             }
-            if (parent->mDeviceVersion >= CAMERA_DEVICE_API_VERSION_3_2) {
-                continue; // Trigger ID tag is deprecated since device HAL 3.2
-            }
+            continue;
         }
 
         camera_metadata_entry entry = metadata.find(tag);
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index eaffad8..1ca6811 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -87,7 +87,6 @@
     const String8& getId() const override;
 
     // Transitions to idle state on success.
-    status_t initialize(CameraModule *module) override;
     status_t initialize(sp<CameraProviderManager> manager) override;
     status_t disconnect() override;
     status_t dump(int fd, const Vector<String16> &args) override;
@@ -166,8 +165,6 @@
 
     status_t prepare(int maxCount, int streamId) override;
 
-    uint32_t getDeviceVersion() override;
-
     ssize_t getJpegBufferSize(uint32_t width, uint32_t height) const override;
     ssize_t getPointCloudBufferSize() const;
     ssize_t getRawOpaqueBufferSize(int32_t width, int32_t height) const;
@@ -235,7 +232,6 @@
      */
     class HalInterface : public camera3::Camera3StreamBufferFreedListener {
       public:
-        HalInterface(camera3_device_t *device);
         HalInterface(sp<hardware::camera::device::V3_2::ICameraDeviceSession> &session,
                      std::shared_ptr<RequestMetadataQueue> queue);
         HalInterface(const HalInterface &other);
@@ -294,9 +290,7 @@
 
                 size_t result = 1;
                 result = 31 * result + buf->numFds;
-                result = 31 * result + buf->numInts;
-                int length = buf->numFds + buf->numInts;
-                for (int i = 0; i < length; i++) {
+                for (int i = 0; i < buf->numFds; i++) {
                     result = 31 * result + buf->data[i];
                 }
                 return result;
@@ -305,9 +299,8 @@
 
         struct BufferComparator {
             bool operator()(const buffer_handle_t& buf1, const buffer_handle_t& buf2) const {
-                if (buf1->numFds == buf2->numFds && buf1->numInts == buf2->numInts) {
-                    int length = buf1->numFds + buf1->numInts;
-                    for (int i = 0; i < length; i++) {
+                if (buf1->numFds == buf2->numFds) {
+                    for (int i = 0; i < buf1->numFds; i++) {
                         if (buf1->data[i] != buf2->data[i]) {
                             return false;
                         }
@@ -345,12 +338,6 @@
 
     CameraMetadata             mRequestTemplateCache[CAMERA3_TEMPLATE_COUNT];
 
-    uint32_t                   mDeviceVersion;
-
-    // whether Camera3Device should derive ANDROID_CONTROL_POST_RAW_SENSITIVITY_BOOST for
-    // backward compatibility. Should not be changed after initialization.
-    bool                       mDerivePostRawSensKey = false;
-
     struct Size {
         uint32_t width;
         uint32_t height;
@@ -408,13 +395,6 @@
     // words, camera device shouldn't be open during CPU suspend.
     nsecs_t                    mTimestampOffset;
 
-    typedef struct AeTriggerCancelOverride {
-        bool applyAeLock;
-        uint8_t aeLock;
-        bool applyAePrecaptureTrigger;
-        uint8_t aePrecaptureTrigger;
-    } AeTriggerCancelOverride_t;
-
     class CaptureRequest : public LightRefBase<CaptureRequest> {
       public:
         CameraMetadata                      mSettings;
@@ -424,9 +404,6 @@
                                             mOutputStreams;
         SurfaceMap                          mOutputSurfaces;
         CaptureResultExtras                 mResultExtras;
-        // Used to cancel AE precapture trigger for devices doesn't support
-        // CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL
-        AeTriggerCancelOverride_t           mAeTriggerCancelOverride;
         // The number of requests that should be submitted to HAL at a time.
         // For example, if batch size is 8, this request and the following 7
         // requests will be submitted to HAL at a time. The batch size for
@@ -602,11 +579,6 @@
     static nsecs_t getMonoToBoottimeOffset();
 
     /**
-     * Helper function to map between legacy and new dataspace enums
-     */
-    static android_dataspace mapToLegacyDataspace(android_dataspace dataSpace);
-
-    /**
      * Helper functions to map between framework and HIDL values
      */
     static hardware::graphics::common::V1_0::PixelFormat mapToPixelFormat(int frameworkFormat);
@@ -651,9 +623,7 @@
 
         RequestThread(wp<Camera3Device> parent,
                 sp<camera3::StatusTracker> statusTracker,
-                HalInterface* interface,
-                uint32_t deviceVersion,
-                bool aeLockAvailable);
+                HalInterface* interface);
         ~RequestThread();
 
         void     setNotificationListener(wp<NotificationListener> listener);
@@ -787,9 +757,6 @@
         // If the input request is in mRepeatingRequests. Must be called with mRequestLock hold
         bool isRepeatingRequestLocked(const sp<CaptureRequest>&);
 
-        // Handle AE precapture trigger cancel for devices <= CAMERA_DEVICE_API_VERSION_3_2.
-        void handleAePrecaptureCancelRequest(const sp<CaptureRequest>& request);
-
         // Clear repeating requests. Must be called with mRequestLock held.
         status_t clearRepeatingRequestsLocked(/*out*/ int64_t *lastFrameNumber = NULL);
 
@@ -802,7 +769,6 @@
         wp<Camera3Device>  mParent;
         wp<camera3::StatusTracker>  mStatusTracker;
         HalInterface*      mInterface;
-        uint32_t           mDeviceVersion;
 
         wp<NotificationListener> mListener;
 
@@ -852,9 +818,6 @@
 
         int64_t            mRepeatingLastFrameNumber;
 
-        // Whether the device supports AE lock
-        bool               mAeLockAvailable;
-
         // Flag indicating if we should prepare video stream for video requests.
         bool               mPrepareVideoStream;
     };
@@ -895,10 +858,6 @@
         // the shutter event.
         Vector<camera3_stream_buffer_t> pendingOutputBuffers;
 
-        // Used to cancel AE precapture trigger for devices doesn't support
-        // CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL
-        AeTriggerCancelOverride_t aeTriggerCancelOverride;
-
         // Whether this inflight request's shutter and result callback are to be
         // called. The policy is that if the request is the last one in the constrained
         // high speed recording request list, this flag will be true. If the request list
@@ -913,12 +872,11 @@
                 haveResultMetadata(false),
                 numBuffersLeft(0),
                 hasInputBuffer(false),
-                aeTriggerCancelOverride({false, 0, false, 0}),
                 hasCallback(true) {
         }
 
         InFlightRequest(int numBuffers, CaptureResultExtras extras, bool hasInput,
-                AeTriggerCancelOverride aeTriggerCancelOverride, bool hasAppCallback) :
+                bool hasAppCallback) :
                 shutterTimestamp(0),
                 sensorTimestamp(0),
                 requestStatus(OK),
@@ -926,7 +884,6 @@
                 numBuffersLeft(numBuffers),
                 resultExtras(extras),
                 hasInputBuffer(hasInput),
-                aeTriggerCancelOverride(aeTriggerCancelOverride),
                 hasCallback(hasAppCallback) {
         }
     };
@@ -940,14 +897,7 @@
 
     status_t registerInFlight(uint32_t frameNumber,
             int32_t numBuffers, CaptureResultExtras resultExtras, bool hasInput,
-            const AeTriggerCancelOverride_t &aeTriggerCancelOverride, bool callback);
-
-    /**
-     * Override result metadata for cancelling AE precapture trigger applied in
-     * handleAePrecaptureCancelRequest().
-     */
-    void overrideResultForPrecaptureCancel(CameraMetadata* result,
-            const AeTriggerCancelOverride_t &aeTriggerCancelOverride);
+            bool callback);
 
     /**
      * Tracking for idle detection
@@ -1042,21 +992,19 @@
 
     // Send a partial capture result.
     void sendPartialCaptureResult(const camera_metadata_t * partialResult,
-            const CaptureResultExtras &resultExtras, uint32_t frameNumber,
-            const AeTriggerCancelOverride_t &aeTriggerCancelOverride);
+            const CaptureResultExtras &resultExtras, uint32_t frameNumber);
 
     // Send a total capture result given the pending metadata and result extras,
     // partial results, and the frame number to the result queue.
     void sendCaptureResult(CameraMetadata &pendingMetadata,
             CaptureResultExtras &resultExtras,
             CameraMetadata &collectedPartialResult, uint32_t frameNumber,
-            bool reprocess, const AeTriggerCancelOverride_t &aeTriggerCancelOverride);
+            bool reprocess);
 
     // Insert the result to the result queue after updating frame number and overriding AE
     // trigger cancel.
     // mOutputLock must be held when calling this function.
-    void insertResultLocked(CaptureResult *result, uint32_t frameNumber,
-            const AeTriggerCancelOverride_t &aeTriggerCancelOverride);
+    void insertResultLocked(CaptureResult *result, uint32_t frameNumber);
 
     /**** Scope for mInFlightLock ****/
 
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index 2b1a899..b45ef77 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -663,89 +663,6 @@
     }
 }
 
-status_t Camera3Stream::registerBuffersLocked(camera3_device *hal3Device) {
-    ATRACE_CALL();
-
-    /**
-     * >= CAMERA_DEVICE_API_VERSION_3_2:
-     *
-     * camera3_device_t->ops->register_stream_buffers() is not called and must
-     * be NULL.
-     */
-    if (hal3Device->common.version >= CAMERA_DEVICE_API_VERSION_3_2) {
-        ALOGV("%s: register_stream_buffers unused as of HAL3.2", __FUNCTION__);
-
-        if (hal3Device->ops->register_stream_buffers != NULL) {
-            ALOGE("%s: register_stream_buffers is deprecated in HAL3.2; "
-                    "must be set to NULL in camera3_device::ops", __FUNCTION__);
-            return INVALID_OPERATION;
-        }
-
-        return OK;
-    }
-
-    ALOGV("%s: register_stream_buffers using deprecated code path", __FUNCTION__);
-
-    status_t res;
-
-    size_t bufferCount = getBufferCountLocked();
-
-    Vector<buffer_handle_t*> buffers;
-    buffers.insertAt(/*prototype_item*/NULL, /*index*/0, bufferCount);
-
-    camera3_stream_buffer_set bufferSet = camera3_stream_buffer_set();
-    bufferSet.stream = this;
-    bufferSet.num_buffers = bufferCount;
-    bufferSet.buffers = buffers.editArray();
-
-    Vector<camera3_stream_buffer_t> streamBuffers;
-    streamBuffers.insertAt(camera3_stream_buffer_t(), /*index*/0, bufferCount);
-
-    // Register all buffers with the HAL. This means getting all the buffers
-    // from the stream, providing them to the HAL with the
-    // register_stream_buffers() method, and then returning them back to the
-    // stream in the error state, since they won't have valid data.
-    //
-    // Only registered buffers can be sent to the HAL.
-
-    uint32_t bufferIdx = 0;
-    for (; bufferIdx < bufferCount; bufferIdx++) {
-        res = getBufferLocked( &streamBuffers.editItemAt(bufferIdx) );
-        if (res != OK) {
-            ALOGE("%s: Unable to get buffer %d for registration with HAL",
-                    __FUNCTION__, bufferIdx);
-            // Skip registering, go straight to cleanup
-            break;
-        }
-
-        sp<Fence> fence = new Fence(streamBuffers[bufferIdx].acquire_fence);
-        fence->waitForever("Camera3Stream::registerBuffers");
-
-        buffers.editItemAt(bufferIdx) = streamBuffers[bufferIdx].buffer;
-    }
-    if (bufferIdx == bufferCount) {
-        // Got all buffers, register with HAL
-        ALOGV("%s: Registering %zu buffers with camera HAL",
-                __FUNCTION__, bufferCount);
-        ATRACE_BEGIN("camera3->register_stream_buffers");
-        res = hal3Device->ops->register_stream_buffers(hal3Device,
-                &bufferSet);
-        ATRACE_END();
-    }
-
-    // Return all valid buffers to stream, in ERROR state to indicate
-    // they weren't filled.
-    for (size_t i = 0; i < bufferIdx; i++) {
-        streamBuffers.editItemAt(i).release_fence = -1;
-        streamBuffers.editItemAt(i).status = CAMERA3_BUFFER_STATUS_ERROR;
-        returnBufferLocked(streamBuffers[i], 0);
-    }
-
-    mPrepared = true;
-
-    return res;
-}
-
 status_t Camera3Stream::getBufferLocked(camera3_stream_buffer *,
         const std::vector<size_t>&) {
     ALOGE("%s: This type of stream does not support output", __FUNCTION__);
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h
index 27ef86d..9cdc1b3 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.h
+++ b/services/camera/libcameraservice/device3/Camera3Stream.h
@@ -474,9 +474,6 @@
     Condition mInputBufferReturnedSignal;
     static const nsecs_t kWaitForBufferDuration = 3000000000LL; // 3000 ms
 
-    // Gets all buffers from endpoint and registers them with the HAL.
-    status_t registerBuffersLocked(camera3_device *hal3Device);
-
     void fireBufferListenersLocked(const camera3_stream_buffer& buffer,
                                   bool acquired, bool output);
     List<wp<Camera3StreamBufferListener> > mBufferListenerList;
diff --git a/services/mediaanalytics/Android.mk b/services/mediaanalytics/Android.mk
index f7197af..9e2813e 100644
--- a/services/mediaanalytics/Android.mk
+++ b/services/mediaanalytics/Android.mk
@@ -5,7 +5,12 @@
 include $(CLEAR_VARS)
 
 LOCAL_SRC_FILES:= \
-    main_mediametrics.cpp          \
+    main_mediametrics.cpp              \
+    MetricsSummarizerCodec.cpp         \
+    MetricsSummarizerExtractor.cpp     \
+    MetricsSummarizerPlayer.cpp        \
+    MetricsSummarizerRecorder.cpp      \
+    MetricsSummarizer.cpp              \
     MediaAnalyticsService.cpp
 
 LOCAL_SHARED_LIBRARIES := \
diff --git a/services/mediaanalytics/MediaAnalyticsService.cpp b/services/mediaanalytics/MediaAnalyticsService.cpp
index 35c1f5b..876c685 100644
--- a/services/mediaanalytics/MediaAnalyticsService.cpp
+++ b/services/mediaanalytics/MediaAnalyticsService.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -20,6 +20,7 @@
 #define LOG_TAG "MediaAnalyticsService"
 #include <utils/Log.h>
 
+#include <stdint.h>
 #include <inttypes.h>
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -70,11 +71,28 @@
 
 #include "MediaAnalyticsService.h"
 
+#include "MetricsSummarizer.h"
+#include "MetricsSummarizerCodec.h"
+#include "MetricsSummarizerExtractor.h"
+#include "MetricsSummarizerPlayer.h"
+#include "MetricsSummarizerRecorder.h"
+
 
 namespace android {
 
 
-#define DEBUG_QUEUE     0
+
+// summarized records
+// up to 48 sets, each covering an hour -- at least 2 days of coverage
+// (will be longer if there are hours without any media action)
+static const nsecs_t kNewSetIntervalNs = 3600*(1000*1000*1000ll);
+static const int kMaxRecordSets = 48;
+// individual records kept in memory
+static const int kMaxRecords    = 100;
+
+
+static const char *kServiceName = "media.metrics";
+
 
 //using android::status_t;
 //using android::OK;
@@ -85,18 +103,67 @@
 
 void MediaAnalyticsService::instantiate() {
     defaultServiceManager()->addService(
-            String16("media.metrics"), new MediaAnalyticsService());
+            String16(kServiceName), new MediaAnalyticsService());
 }
 
-// XXX: add dynamic controls for mMaxRecords
+// handle sets of summarizers
+MediaAnalyticsService::SummarizerSet::SummarizerSet() {
+    mSummarizers = new List<MetricsSummarizer *>();
+}
+MediaAnalyticsService::SummarizerSet::~SummarizerSet() {
+    // empty the list
+    List<MetricsSummarizer *> *l = mSummarizers;
+    while (l->size() > 0) {
+        MetricsSummarizer *summarizer = *(l->begin());
+        l->erase(l->begin());
+        delete summarizer;
+    }
+}
+
+void MediaAnalyticsService::newSummarizerSet() {
+    ALOGD("MediaAnalyticsService::newSummarizerSet");
+    MediaAnalyticsService::SummarizerSet *set = new MediaAnalyticsService::SummarizerSet();
+    nsecs_t now = systemTime(SYSTEM_TIME_REALTIME);
+    set->setStarted(now);
+
+    set->appendSummarizer(new MetricsSummarizerExtractor("extractor"));
+    set->appendSummarizer(new MetricsSummarizerCodec("codec"));
+    set->appendSummarizer(new MetricsSummarizerPlayer("nuplayer"));
+    set->appendSummarizer(new MetricsSummarizerRecorder("recorder"));
+
+    // ALWAYS at the end, since it catches everything
+    set->appendSummarizer(new MetricsSummarizer(NULL));
+
+    // inject this set at the BACK of the list.
+    mSummarizerSets->push_back(set);
+    mCurrentSet = set;
+
+    // limit the # that we have
+    if (mMaxRecordSets > 0) {
+        List<SummarizerSet *> *l = mSummarizerSets;
+        while (l->size() > (size_t) mMaxRecordSets) {
+            ALOGD("Deleting oldest record set....");
+            MediaAnalyticsService::SummarizerSet *oset = *(l->begin());
+            l->erase(l->begin());
+            delete oset;
+            mSetsDiscarded++;
+        }
+    }
+}
+
 MediaAnalyticsService::MediaAnalyticsService()
-        : mMaxRecords(100) {
+        : mMaxRecords(kMaxRecords),
+          mMaxRecordSets(kMaxRecordSets),
+          mNewSetInterval(kNewSetIntervalNs) {
 
     ALOGD("MediaAnalyticsService created");
     // clear our queues
     mOpen = new List<MediaAnalyticsItem *>();
     mFinalized = new List<MediaAnalyticsItem *>();
 
+    mSummarizerSets = new List<MediaAnalyticsService::SummarizerSet *>();
+    newSummarizerSet();
+
     mItemsSubmitted = 0;
     mItemsFinalized = 0;
     mItemsDiscarded = 0;
@@ -109,7 +176,13 @@
 MediaAnalyticsService::~MediaAnalyticsService() {
         ALOGD("MediaAnalyticsService destroyed");
 
-    // XXX: clean out mOpen and mFinalized
+    // clean out mOpen and mFinalized
+    delete mOpen;
+    mOpen = NULL;
+    delete mFinalized;
+    mFinalized = NULL;
+
+    // XXX: clean out the summaries
 }
 
 
@@ -145,7 +218,7 @@
         case AID_MEDIA_EX:
         case AID_MEDIA_DRM:
             // trusted source, only override default values
-            isTrusted = true;
+                isTrusted = true;
             if (uid_given == (-1)) {
                 item->setUid(uid);
             }
@@ -197,10 +270,12 @@
                 oitem = NULL;
             } else {
                 oitem->setFinalized(true);
+                summarize(oitem);
                 saveItem(mFinalized, oitem, 0);
             }
             // new record could itself be marked finalized...
             if (finalizing) {
+                summarize(item);
                 saveItem(mFinalized, item, 0);
                 mItemsFinalized++;
             } else {
@@ -211,6 +286,7 @@
             // combine the records, send it to finalized if appropriate
             oitem->merge(item);
             if (finalizing) {
+                summarize(oitem);
                 saveItem(mFinalized, oitem, 0);
                 mItemsFinalized++;
             }
@@ -229,6 +305,7 @@
                 delete item;
                 item = NULL;
             } else {
+                summarize(item);
                 saveItem(mFinalized, item, 0);
                 mItemsFinalized++;
             }
@@ -239,26 +316,6 @@
     return id;
 }
 
-List<MediaAnalyticsItem *> *MediaAnalyticsService::getMediaAnalyticsItemList(bool finished, nsecs_t ts) {
-    // this might never get called; the binder interface maps to the full parm list
-    // on the client side before making the binder call.
-    // but this lets us be sure...
-    List<MediaAnalyticsItem*> *list;
-    list = getMediaAnalyticsItemList(finished, ts, MediaAnalyticsItem::kKeyAny);
-    return list;
-}
-
-List<MediaAnalyticsItem *> *MediaAnalyticsService::getMediaAnalyticsItemList(bool , nsecs_t , MediaAnalyticsItem::Key ) {
-
-    // XXX: implement the get-item-list semantics
-
-    List<MediaAnalyticsItem *> *list = NULL;
-    // set up our query on the persistent data
-    // slurp in all of the pieces
-    // return that
-    return list;
-}
-
 status_t MediaAnalyticsService::dump(int fd, const Vector<String16>& args)
 {
     const size_t SIZE = 512;
@@ -277,15 +334,21 @@
 
     // crack any parameters
     bool clear = false;
+    bool summary = false;
     nsecs_t ts_since = 0;
+    String16 summaryOption("-summary");
     String16 clearOption("-clear");
     String16 sinceOption("-since");
     String16 helpOption("-help");
+    String16 onlyOption("-only");
+    const char *only = NULL;
     int n = args.size();
     for (int i = 0; i < n; i++) {
         String8 myarg(args[i]);
         if (args[i] == clearOption) {
             clear = true;
+        } else if (args[i] == summaryOption) {
+            summary = true;
         } else if (args[i] == sinceOption) {
             i++;
             if (i < n) {
@@ -301,12 +364,27 @@
             }
             // command line is milliseconds; internal units are nano-seconds
             ts_since *= 1000*1000;
+        } else if (args[i] == onlyOption) {
+            i++;
+            if (i < n) {
+                String8 value(args[i]);
+                const char *p = value.string();
+                char *q = strdup(p);
+                if (q != NULL) {
+                    if (only != NULL) {
+                        free((void*)only);
+                    }
+                only = q;
+                }
+            }
         } else if (args[i] == helpOption) {
             result.append("Recognized parameters:\n");
             result.append("-help        this help message\n");
+            result.append("-summary     show summary info\n");
             result.append("-clear       clears out saved records\n");
-            result.append("-since XXX   include records since XXX\n");
-            result.append("             (XXX is milliseconds since the UNIX epoch)\n");
+            result.append("-only X      process records for component X\n");
+            result.append("-since X     include records since X\n");
+            result.append("             (X is milliseconds since the UNIX epoch)\n");
             write(fd, result.string(), result.size());
             return NO_ERROR;
         }
@@ -314,9 +392,42 @@
 
     Mutex::Autolock _l(mLock);
 
-    snprintf(buffer, SIZE, "Dump of the mediametrics process:\n");
+    // we ALWAYS dump this piece
+    snprintf(buffer, SIZE, "Dump of the %s process:\n", kServiceName);
     result.append(buffer);
 
+    dumpHeaders(result, ts_since);
+
+    // only want 1, to avoid confusing folks that parse the output
+    if (summary) {
+        dumpSummaries(result, ts_since, only);
+    } else {
+        dumpRecent(result, ts_since, only);
+    }
+
+
+    if (clear) {
+        // remove everything from the finalized queue
+        while (mFinalized->size() > 0) {
+            MediaAnalyticsItem * oitem = *(mFinalized->begin());
+            mFinalized->erase(mFinalized->begin());
+            delete oitem;
+            mItemsDiscarded++;
+        }
+
+        // shall we clear the summary data too?
+
+    }
+
+    write(fd, result.string(), result.size());
+    return NO_ERROR;
+}
+
+// dump headers
+void MediaAnalyticsService::dumpHeaders(String8 &result, nsecs_t ts_since) {
+    const size_t SIZE = 512;
+    char buffer[SIZE];
+
     int enabled = MediaAnalyticsItem::isEnabled();
     if (enabled) {
         snprintf(buffer, SIZE, "Metrics gathering: enabled\n");
@@ -331,50 +442,71 @@
         " Discarded: %" PRId64 "\n",
         mItemsSubmitted, mItemsFinalized, mItemsDiscarded);
     result.append(buffer);
+    snprintf(buffer, SIZE,
+        "Summary Sets Discarded: %" PRId64 "\n", mSetsDiscarded);
+    result.append(buffer);
     if (ts_since != 0) {
         snprintf(buffer, SIZE,
             "Dumping Queue entries more recent than: %" PRId64 "\n",
             (int64_t) ts_since);
         result.append(buffer);
     }
+}
+
+// dump summary info
+void MediaAnalyticsService::dumpSummaries(String8 &result, nsecs_t ts_since, const char *only) {
+    const size_t SIZE = 512;
+    char buffer[SIZE];
+    int slot = 0;
+
+    snprintf(buffer, SIZE, "\nSummarized Metrics:\n");
+    result.append(buffer);
+
+    // have each of the distillers dump records
+    if (mSummarizerSets != NULL) {
+        List<SummarizerSet *>::iterator itSet = mSummarizerSets->begin();
+        for (; itSet != mSummarizerSets->end(); itSet++) {
+            nsecs_t when = (*itSet)->getStarted();
+            if (when < ts_since) {
+                continue;
+            }
+            List<MetricsSummarizer *> *list = (*itSet)->getSummarizers();
+            List<MetricsSummarizer *>::iterator it = list->begin();
+            for (; it != list->end(); it++) {
+                if (only != NULL && strcmp(only, (*it)->getKey()) != 0) {
+                    ALOGV("Told to omit '%s'", (*it)->getKey());
+                }
+                AString distilled = (*it)->dumpSummary(slot, only);
+                result.append(distilled.c_str());
+            }
+        }
+    }
+}
+
+// the recent, detailed queues
+void MediaAnalyticsService::dumpRecent(String8 &result, nsecs_t ts_since, const char * only) {
+    const size_t SIZE = 512;
+    char buffer[SIZE];
 
     // show the recently recorded records
     snprintf(buffer, sizeof(buffer), "\nFinalized Metrics (oldest first):\n");
     result.append(buffer);
-    result.append(this->dumpQueue(mFinalized, ts_since));
+    result.append(this->dumpQueue(mFinalized, ts_since, only));
 
     snprintf(buffer, sizeof(buffer), "\nIn-Progress Metrics (newest first):\n");
     result.append(buffer);
-    result.append(this->dumpQueue(mOpen, ts_since));
+    result.append(this->dumpQueue(mOpen, ts_since, only));
 
     // show who is connected and injecting records?
     // talk about # records fed to the 'readers'
     // talk about # records we discarded, perhaps "discarded w/o reading" too
-
-    if (clear) {
-        // remove everything from the finalized queue
-        while (mFinalized->size() > 0) {
-            MediaAnalyticsItem * oitem = *(mFinalized->begin());
-            if (DEBUG_QUEUE) {
-                ALOGD("zap old record: key %s sessionID %" PRId64 " ts %" PRId64 "",
-                    oitem->getKey().c_str(), oitem->getSessionID(),
-                    oitem->getTimestamp());
-            }
-            mFinalized->erase(mFinalized->begin());
-            mItemsDiscarded++;
-        }
-    }
-
-    write(fd, result.string(), result.size());
-    return NO_ERROR;
 }
-
 // caller has locked mLock...
 String8 MediaAnalyticsService::dumpQueue(List<MediaAnalyticsItem *> *theList) {
-    return dumpQueue(theList, (nsecs_t) 0);
+    return dumpQueue(theList, (nsecs_t) 0, NULL);
 }
 
-String8 MediaAnalyticsService::dumpQueue(List<MediaAnalyticsItem *> *theList, nsecs_t ts_since) {
+String8 MediaAnalyticsService::dumpQueue(List<MediaAnalyticsItem *> *theList, nsecs_t ts_since, const char * only) {
     String8 result;
     int slot = 0;
 
@@ -387,6 +519,11 @@
             if (when < ts_since) {
                 continue;
             }
+            if (only != NULL &&
+                strcmp(only, (*it)->getKey().c_str()) != 0) {
+                ALOGV("Omit '%s', it's not '%s'", (*it)->getKey().c_str(), only);
+                continue;
+            }
             AString entry = (*it)->toString();
             result.appendFormat("%5d: %s\n", slot, entry.c_str());
             slot++;
@@ -405,13 +542,6 @@
 
     Mutex::Autolock _l(mLock);
 
-    if (DEBUG_QUEUE) {
-        ALOGD("Inject a record: session %" PRId64 " ts %" PRId64 "",
-            item->getSessionID(), item->getTimestamp());
-        String8 before = dumpQueue(l);
-        ALOGD("Q before insert: %s", before.string());
-    }
-
     // adding at back of queue (fifo order)
     if (front)  {
         l->push_front(item);
@@ -419,30 +549,15 @@
         l->push_back(item);
     }
 
-    if (DEBUG_QUEUE) {
-        String8 after = dumpQueue(l);
-        ALOGD("Q after insert: %s", after.string());
-    }
-
     // keep removing old records the front until we're in-bounds
     if (mMaxRecords > 0) {
         while (l->size() > (size_t) mMaxRecords) {
             MediaAnalyticsItem * oitem = *(l->begin());
-            if (DEBUG_QUEUE) {
-                ALOGD("zap old record: key %s sessionID %" PRId64 " ts %" PRId64 "",
-                    oitem->getKey().c_str(), oitem->getSessionID(),
-                    oitem->getTimestamp());
-            }
             l->erase(l->begin());
             delete oitem;
             mItemsDiscarded++;
         }
     }
-
-    if (DEBUG_QUEUE) {
-        String8 after = dumpQueue(l);
-        ALOGD("Q after cleanup: %s", after.string());
-    }
 }
 
 // are they alike enough that nitem can be folded into oitem?
@@ -515,29 +630,14 @@
 
     Mutex::Autolock _l(mLock);
 
-    if(DEBUG_QUEUE) {
-        String8 before = dumpQueue(l);
-        ALOGD("Q before delete: %s", before.string());
-    }
-
     for (List<MediaAnalyticsItem *>::iterator it = l->begin();
         it != l->end(); it++) {
         if ((*it)->getSessionID() != item->getSessionID())
             continue;
-
-        if (DEBUG_QUEUE) {
-            ALOGD(" --- removing record for SessionID %" PRId64 "", item->getSessionID());
-            ALOGD("drop record at %s:%d", __FILE__, __LINE__);
-        }
         delete *it;
         l->erase(it);
         break;
     }
-
-    if (DEBUG_QUEUE) {
-        String8 after = dumpQueue(l);
-        ALOGD("Q after delete: %s", after.string());
-    }
 }
 
 static AString allowedKeys[] =
@@ -579,5 +679,43 @@
     return false;
 }
 
+// insert into the appropriate summarizer.
+// we make our own copy to save/summarize
+void MediaAnalyticsService::summarize(MediaAnalyticsItem *item) {
+
+    ALOGV("MediaAnalyticsService::summarize()");
+
+    if (item == NULL) {
+        return;
+    }
+
+    nsecs_t now = systemTime(SYSTEM_TIME_REALTIME);
+    if (mCurrentSet == NULL
+        || (mCurrentSet->getStarted() + mNewSetInterval < now)) {
+        newSummarizerSet();
+    }
+
+    if (mCurrentSet == NULL) {
+        return;
+    }
+
+    List<MetricsSummarizer *> *summarizers = mCurrentSet->getSummarizers();
+    List<MetricsSummarizer *>::iterator it = summarizers->begin();
+    for (; it != summarizers->end(); it++) {
+        if ((*it)->isMine(*item)) {
+            break;
+        }
+    }
+    if (it == summarizers->end()) {
+        ALOGD("no handler for type %s", item->getKey().c_str());
+        return;               // no handler
+    }
+
+    // invoke the summarizer. summarizer will make whatever copies
+    // it wants; the caller retains ownership of item.
+
+    (*it)->handleRecord(item);
+
+}
 
 } // namespace android
diff --git a/services/mediaanalytics/MediaAnalyticsService.h b/services/mediaanalytics/MediaAnalyticsService.h
index d2b0f09..6685967 100644
--- a/services/mediaanalytics/MediaAnalyticsService.h
+++ b/services/mediaanalytics/MediaAnalyticsService.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -28,6 +28,8 @@
 
 #include <media/IMediaAnalyticsService.h>
 
+#include "MetricsSummarizer.h"
+
 
 namespace android {
 
@@ -39,12 +41,6 @@
     // on this side, caller surrenders ownership
     virtual int64_t submit(MediaAnalyticsItem *item, bool forcenew);
 
-    virtual List<MediaAnalyticsItem *>
-            *getMediaAnalyticsItemList(bool finished, int64_t ts);
-    virtual List<MediaAnalyticsItem *>
-            *getMediaAnalyticsItemList(bool finished, int64_t ts, MediaAnalyticsItem::Key key);
-
-
     static  void            instantiate();
     virtual status_t        dump(int fd, const Vector<String16>& args);
 
@@ -58,6 +54,7 @@
     int64_t mItemsSubmitted;
     int64_t mItemsFinalized;
     int64_t mItemsDiscarded;
+    int64_t mSetsDiscarded;
     MediaAnalyticsItem::SessionID_t mLastSessionID;
 
     // partitioned a bit so we don't over serialize
@@ -67,6 +64,10 @@
     // the most we hold in memory
     // up to this many in each queue (open, finalized)
     int32_t mMaxRecords;
+    // # of sets of summaries
+    int32_t mMaxRecordSets;
+    // nsecs until we start a new record set
+    nsecs_t mNewSetInterval;
 
     // input validation after arrival from client
     bool contentValid(MediaAnalyticsItem *item, bool isTrusted);
@@ -82,12 +83,47 @@
     MediaAnalyticsItem *findItem(List<MediaAnalyticsItem *> *,
                                      MediaAnalyticsItem *, bool removeit);
 
+    // summarizers
+    void summarize(MediaAnalyticsItem *item);
+    class SummarizerSet {
+        nsecs_t mStarted;
+        List<MetricsSummarizer *> *mSummarizers;
+
+      public:
+        void appendSummarizer(MetricsSummarizer *s) {
+            if (s) {
+                mSummarizers->push_back(s);
+            }
+        };
+        nsecs_t getStarted() { return mStarted;}
+        void setStarted(nsecs_t started) {mStarted = started;}
+        List<MetricsSummarizer *> *getSummarizers() { return mSummarizers;}
+
+        SummarizerSet();
+        ~SummarizerSet();
+    };
+    void newSummarizerSet();
+    List<SummarizerSet *> *mSummarizerSets;
+    SummarizerSet *mCurrentSet;
+    List<MetricsSummarizer *> *getFirstSet() {
+        List<SummarizerSet *>::iterator first = mSummarizerSets->begin();
+        if (first != mSummarizerSets->end()) {
+            return (*first)->getSummarizers();
+        }
+        return NULL;
+    }
+
     void saveItem(MediaAnalyticsItem);
     void saveItem(List<MediaAnalyticsItem *> *, MediaAnalyticsItem *, int);
     void deleteItem(List<MediaAnalyticsItem *> *, MediaAnalyticsItem *);
 
+    // support for generating output
     String8 dumpQueue(List<MediaAnalyticsItem*> *);
-    String8 dumpQueue(List<MediaAnalyticsItem*> *, nsecs_t);
+    String8 dumpQueue(List<MediaAnalyticsItem*> *, nsecs_t, const char *only);
+
+    void dumpHeaders(String8 &result, nsecs_t ts_since);
+    void dumpSummaries(String8 &result, nsecs_t ts_since, const char * only);
+    void dumpRecent(String8 &result, nsecs_t ts_since, const char * only);
 
 };
 
diff --git a/services/mediaanalytics/MetricsSummarizer.cpp b/services/mediaanalytics/MetricsSummarizer.cpp
new file mode 100644
index 0000000..6d5787e
--- /dev/null
+++ b/services/mediaanalytics/MetricsSummarizer.cpp
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "MetricsSummarizer"
+#include <utils/Log.h>
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <inttypes.h>
+
+#include <utils/threads.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/List.h>
+
+#include <media/IMediaAnalyticsService.h>
+
+#include "MetricsSummarizer.h"
+
+
+namespace android {
+
+#define DEBUG_SORT      0
+#define DEBUG_QUEUE     0
+
+
+MetricsSummarizer::MetricsSummarizer(const char *key)
+    : mIgnorables(NULL)
+{
+    ALOGV("MetricsSummarizer::MetricsSummarizer");
+
+    if (key == NULL) {
+        mKey = key;
+    } else {
+        mKey = strdup(key);
+    }
+
+    mSummaries = new List<MediaAnalyticsItem *>();
+}
+
+MetricsSummarizer::~MetricsSummarizer()
+{
+    ALOGV("MetricsSummarizer::~MetricsSummarizer");
+    if (mKey) {
+        free((void *)mKey);
+        mKey = NULL;
+    }
+
+    // clear the list of items we have saved
+    while (mSummaries->size() > 0) {
+        MediaAnalyticsItem * oitem = *(mSummaries->begin());
+        if (DEBUG_QUEUE) {
+            ALOGD("zap old record: key %s sessionID %" PRId64 " ts %" PRId64 "",
+                oitem->getKey().c_str(), oitem->getSessionID(),
+                oitem->getTimestamp());
+        }
+        mSummaries->erase(mSummaries->begin());
+        delete oitem;
+    }
+}
+
+// so we know what summarizer we were using
+const char *MetricsSummarizer::getKey() {
+    const char *value = mKey;
+    if (value == NULL) {
+        value = "unknown";
+    }
+    return value;
+}
+
+// should the record be given to this summarizer
+bool MetricsSummarizer::isMine(MediaAnalyticsItem &item)
+{
+    if (mKey == NULL)
+        return true;
+    AString itemKey = item.getKey();
+    if (strcmp(mKey, itemKey.c_str()) != 0) {
+        return false;
+    }
+    return true;
+}
+
+AString MetricsSummarizer::dumpSummary(int &slot)
+{
+    return dumpSummary(slot, NULL);
+}
+
+AString MetricsSummarizer::dumpSummary(int &slot, const char *only)
+{
+    AString value = "";
+
+    List<MediaAnalyticsItem *>::iterator it = mSummaries->begin();
+    if (it != mSummaries->end()) {
+        char buf[16];   // enough for "#####: "
+        for (; it != mSummaries->end(); it++) {
+            if (only != NULL && strcmp(only, (*it)->getKey().c_str()) != 0) {
+                continue;
+            }
+            AString entry = (*it)->toString();
+            snprintf(buf, sizeof(buf), "%5d: ", slot);
+            value.append(buf);
+            value.append(entry.c_str());
+            value.append("\n");
+            slot++;
+        }
+    }
+    return value;
+}
+
+void MetricsSummarizer::setIgnorables(const char **ignorables) {
+    mIgnorables = ignorables;
+}
+
+const char **MetricsSummarizer::getIgnorables() {
+    return mIgnorables;
+}
+
+void MetricsSummarizer::handleRecord(MediaAnalyticsItem *item) {
+
+    ALOGV("MetricsSummarizer::handleRecord() for %s",
+                item == NULL ? "<nothing>" : item->toString().c_str());
+
+    if (item == NULL) {
+        return;
+    }
+
+    List<MediaAnalyticsItem *>::iterator it = mSummaries->begin();
+    for (; it != mSummaries->end(); it++) {
+        bool good = sameAttributes((*it), item, getIgnorables());
+        ALOGV("Match against %s says %d",
+              (*it)->toString().c_str(), good);
+        if (good)
+            break;
+    }
+    if (it == mSummaries->end()) {
+            ALOGV("save new record");
+            item = item->dup();
+            if (item == NULL) {
+                ALOGE("unable to save MediaMetrics record");
+            }
+            sortProps(item);
+            item->setInt32("aggregated",1);
+            mSummaries->push_back(item);
+    } else {
+            ALOGV("increment existing record");
+            (*it)->addInt32("aggregated",1);
+            mergeRecord(*(*it), *item);
+    }
+}
+
+void MetricsSummarizer::mergeRecord(MediaAnalyticsItem &/*have*/, MediaAnalyticsItem &/*item*/) {
+    // default is no further massaging.
+    ALOGV("MetricsSummarizer::mergeRecord() [default]");
+    return;
+}
+
+
+//
+// Comparators
+//
+
+// testing that all of 'single' is in 'summ'
+// and that the values match.
+// 'summ' may have extra fields.
+// 'ignorable' is a set of things that we don't worry about matching up
+// (usually time- or count-based values we'll sum elsewhere)
+bool MetricsSummarizer::sameAttributes(MediaAnalyticsItem *summ, MediaAnalyticsItem *single, const char **ignorable) {
+
+    if (single == NULL || summ == NULL) {
+        return false;
+    }
+    ALOGV("MetricsSummarizer::sameAttributes(): summ %s", summ->toString().c_str());
+    ALOGV("MetricsSummarizer::sameAttributes(): single %s", single->toString().c_str());
+
+    // this can be made better.
+    for(size_t i=0;i<single->mPropCount;i++) {
+        MediaAnalyticsItem::Prop *prop1 = &(single->mProps[i]);
+        const char *attrName = prop1->mName;
+        ALOGV("compare on attr '%s'", attrName);
+
+        // is it something we should ignore
+        if (ignorable != NULL) {
+            const char **ig = ignorable;
+            while (*ig) {
+                if (strcmp(*ig, attrName) == 0) {
+                    break;
+                }
+                ig++;
+            }
+            if (*ig) {
+                ALOGV("we don't mind that it has attr '%s'", attrName);
+                continue;
+            }
+        }
+
+        MediaAnalyticsItem::Prop *prop2 = summ->findProp(attrName);
+        if (prop2 == NULL) {
+            ALOGV("summ doesn't have this attr");
+            return false;
+        }
+        if (prop1->mType != prop2->mType) {
+            ALOGV("mismatched attr types");
+            return false;
+        }
+        switch (prop1->mType) {
+            case MediaAnalyticsItem::kTypeInt32:
+                if (prop1->u.int32Value != prop2->u.int32Value)
+                    return false;
+                break;
+            case MediaAnalyticsItem::kTypeInt64:
+                if (prop1->u.int64Value != prop2->u.int64Value)
+                    return false;
+                break;
+            case MediaAnalyticsItem::kTypeDouble:
+                // XXX: watch out for floating point comparisons!
+                if (prop1->u.doubleValue != prop2->u.doubleValue)
+                    return false;
+                break;
+            case MediaAnalyticsItem::kTypeCString:
+                if (strcmp(prop1->u.CStringValue, prop2->u.CStringValue) != 0)
+                    return false;
+                break;
+            case MediaAnalyticsItem::kTypeRate:
+                if (prop1->u.rate.count != prop2->u.rate.count)
+                    return false;
+                if (prop1->u.rate.duration != prop2->u.rate.duration)
+                    return false;
+                break;
+            default:
+                return false;
+        }
+    }
+
+    return true;
+}
+
+bool MetricsSummarizer::sameAttributesId(MediaAnalyticsItem *summ, MediaAnalyticsItem *single, const char **ignorable) {
+
+    // verify same user
+    if (summ->mPid != single->mPid)
+        return false;
+
+    // and finally do the more expensive validation of the attributes
+    return sameAttributes(summ, single, ignorable);
+}
+
+int MetricsSummarizer::PropSorter(const void *a, const void *b) {
+    MediaAnalyticsItem::Prop *ai = (MediaAnalyticsItem::Prop *)a;
+    MediaAnalyticsItem::Prop *bi = (MediaAnalyticsItem::Prop *)b;
+    return strcmp(ai->mName, bi->mName);
+}
+
+// we sort in the summaries so that it looks pretty in the dumpsys
+void MetricsSummarizer::sortProps(MediaAnalyticsItem *item) {
+    if (item->mPropCount != 0) {
+        if (DEBUG_SORT) {
+            ALOGD("sortProps(pre): %s", item->toString().c_str());
+        }
+        qsort(item->mProps, item->mPropCount,
+              sizeof(MediaAnalyticsItem::Prop), MetricsSummarizer::PropSorter);
+        if (DEBUG_SORT) {
+            ALOGD("sortProps(pst): %s", item->toString().c_str());
+        }
+    }
+}
+
+} // namespace android
diff --git a/services/mediaanalytics/MetricsSummarizer.h b/services/mediaanalytics/MetricsSummarizer.h
new file mode 100644
index 0000000..0b64eac
--- /dev/null
+++ b/services/mediaanalytics/MetricsSummarizer.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef ANDROID_METRICSSUMMARIZER_H
+#define ANDROID_METRICSSUMMARIZER_H
+
+#include <utils/threads.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/List.h>
+
+#include <media/IMediaAnalyticsService.h>
+
+
+namespace android {
+
+class MetricsSummarizer
+{
+
+ public:
+
+    MetricsSummarizer(const char *key);
+    virtual ~MetricsSummarizer();
+
+    // show the key
+    const char * getKey();
+
+    // should the record be given to this summarizer
+    bool isMine(MediaAnalyticsItem &item);
+
+    // hand the record to this summarizer
+    void handleRecord(MediaAnalyticsItem *item);
+
+    virtual void mergeRecord(MediaAnalyticsItem &have, MediaAnalyticsItem &incoming);
+
+    // dump the summarized records (for dumpsys)
+    AString dumpSummary(int &slot);
+    AString dumpSummary(int &slot, const char *only);
+
+    void setIgnorables(const char **);
+    const char **getIgnorables();
+
+ protected:
+
+    // various comparators
+    // "do these records have same attributes and values in those attrs"
+    // ditto, but watch for "error" fields
+    bool sameAttributes(MediaAnalyticsItem *summ, MediaAnalyticsItem *single, const char **ignoreables);
+    // attributes + from the same app/userid
+    bool sameAttributesId(MediaAnalyticsItem *summ, MediaAnalyticsItem *single, const char **ignoreables);
+
+    static int PropSorter(const void *a, const void *b);
+    void sortProps(MediaAnalyticsItem *item);
+
+ private:
+    const char *mKey;
+    const char **mIgnorables;
+    List<MediaAnalyticsItem *> *mSummaries;
+
+
+};
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_METRICSSUMMARIZER_H
diff --git a/services/mediaanalytics/MetricsSummarizerCodec.cpp b/services/mediaanalytics/MetricsSummarizerCodec.cpp
new file mode 100644
index 0000000..8c74782
--- /dev/null
+++ b/services/mediaanalytics/MetricsSummarizerCodec.cpp
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "MetricsSummarizerCodec"
+#include <utils/Log.h>
+
+
+#include <utils/threads.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/List.h>
+
+#include <media/IMediaAnalyticsService.h>
+
+#include "MetricsSummarizer.h"
+#include "MetricsSummarizerCodec.h"
+
+
+
+
+namespace android {
+
+MetricsSummarizerCodec::MetricsSummarizerCodec(const char *key)
+    : MetricsSummarizer(key)
+{
+    ALOGV("MetricsSummarizerCodec::MetricsSummarizerCodec");
+}
+
+
+} // namespace android
diff --git a/services/mediaanalytics/MetricsSummarizerCodec.h b/services/mediaanalytics/MetricsSummarizerCodec.h
new file mode 100644
index 0000000..c01196f
--- /dev/null
+++ b/services/mediaanalytics/MetricsSummarizerCodec.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef ANDROID_METRICSSUMMARIZERCODEC_H
+#define ANDROID_METRICSSUMMARIZERCODEC_H
+
+#include <utils/threads.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/List.h>
+
+#include <media/IMediaAnalyticsService.h>
+#include "MetricsSummarizer.h"
+
+
+namespace android {
+
+class MetricsSummarizerCodec : public MetricsSummarizer
+{
+
+ public:
+
+    MetricsSummarizerCodec(const char *key);
+    virtual ~MetricsSummarizerCodec() {};
+
+};
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_METRICSSUMMARIZERCODEC_H
diff --git a/services/mediaanalytics/MetricsSummarizerExtractor.cpp b/services/mediaanalytics/MetricsSummarizerExtractor.cpp
new file mode 100644
index 0000000..190f87d
--- /dev/null
+++ b/services/mediaanalytics/MetricsSummarizerExtractor.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "MetricsSummarizerExtractor"
+#include <utils/Log.h>
+
+#include <utils/threads.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/List.h>
+
+#include <media/IMediaAnalyticsService.h>
+
+#include "MetricsSummarizer.h"
+#include "MetricsSummarizerExtractor.h"
+
+
+
+
+namespace android {
+
+MetricsSummarizerExtractor::MetricsSummarizerExtractor(const char *key)
+    : MetricsSummarizer(key)
+{
+    ALOGV("MetricsSummarizerExtractor::MetricsSummarizerExtractor");
+}
+
+} // namespace android
diff --git a/services/mediaanalytics/MetricsSummarizerExtractor.h b/services/mediaanalytics/MetricsSummarizerExtractor.h
new file mode 100644
index 0000000..eee052b
--- /dev/null
+++ b/services/mediaanalytics/MetricsSummarizerExtractor.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef ANDROID_METRICSSUMMARIZEREXTRACTOR_H
+#define ANDROID_METRICSSUMMARIZEREXTRACTOR_H
+
+#include <utils/threads.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/List.h>
+
+#include <media/IMediaAnalyticsService.h>
+#include "MetricsSummarizer.h"
+
+
+namespace android {
+
+class MetricsSummarizerExtractor : public MetricsSummarizer
+{
+
+ public:
+
+    MetricsSummarizerExtractor(const char *key);
+    virtual ~MetricsSummarizerExtractor() {};
+
+};
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_METRICSSUMMARIZEREXTRACTOR_H
diff --git a/services/mediaanalytics/MetricsSummarizerPlayer.cpp b/services/mediaanalytics/MetricsSummarizerPlayer.cpp
new file mode 100644
index 0000000..5162059
--- /dev/null
+++ b/services/mediaanalytics/MetricsSummarizerPlayer.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "MetricsSummarizerPlayer"
+#include <utils/Log.h>
+
+#include <stdint.h>
+#include <inttypes.h>
+
+#include <utils/threads.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/List.h>
+
+#include <media/IMediaAnalyticsService.h>
+
+#include "MetricsSummarizer.h"
+#include "MetricsSummarizerPlayer.h"
+
+
+
+
+namespace android {
+
+static const char *player_ignorable[] = {
+    "android.media.mediaplayer.durationMs",
+    "android.media.mediaplayer.playingMs",
+    "android.media.mediaplayer.frames",
+    "android.media.mediaplayer.dropped",
+    0
+};
+
+MetricsSummarizerPlayer::MetricsSummarizerPlayer(const char *key)
+    : MetricsSummarizer(key)
+{
+    ALOGV("MetricsSummarizerPlayer::MetricsSummarizerPlayer");
+    setIgnorables(player_ignorable);
+}
+
+void MetricsSummarizerPlayer::mergeRecord(MediaAnalyticsItem &summation, MediaAnalyticsItem &item) {
+
+    ALOGV("MetricsSummarizerPlayer::mergeRecord()");
+
+    //
+    // we sum time & frames.
+    // be careful about our special "-1" values that indicate 'unknown'
+    // treat those as 0 [basically, not summing them into the totals].
+    int64_t duration = 0;
+    if (item.getInt64("android.media.mediaplayer.durationMs", &duration)) {
+        ALOGV("found durationMs of %" PRId64, duration);
+        summation.addInt64("android.media.mediaplayer.durationMs",duration);
+    }
+    int64_t playing = 0;
+    if (item.getInt64("android.media.mediaplayer.playingMs", &playing))
+        ALOGV("found playingMs of %" PRId64, playing);
+        if (playing >= 0) {
+            summation.addInt64("android.media.mediaplayer.playingMs",playing);
+        }
+    int64_t frames = 0;
+    if (item.getInt64("android.media.mediaplayer.frames", &frames))
+        ALOGV("found framess of %" PRId64, frames);
+        if (frames >= 0) {
+            summation.addInt64("android.media.mediaplayer.frames",frames);
+        }
+    int64_t dropped = 0;
+    if (item.getInt64("android.media.mediaplayer.dropped", &dropped))
+        ALOGV("found dropped of %" PRId64, dropped);
+        if (dropped >= 0) {
+            summation.addInt64("android.media.mediaplayer.dropped",dropped);
+        }
+}
+
+} // namespace android
diff --git a/services/mediaanalytics/MetricsSummarizerPlayer.h b/services/mediaanalytics/MetricsSummarizerPlayer.h
new file mode 100644
index 0000000..ad1bf74
--- /dev/null
+++ b/services/mediaanalytics/MetricsSummarizerPlayer.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef ANDROID_METRICSSUMMARIZERPLAYER_H
+#define ANDROID_METRICSSUMMARIZERPLAYER_H
+
+#include <utils/threads.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/List.h>
+
+#include <media/IMediaAnalyticsService.h>
+#include "MetricsSummarizer.h"
+
+
+namespace android {
+
+class MetricsSummarizerPlayer : public MetricsSummarizer
+{
+
+ public:
+
+    MetricsSummarizerPlayer(const char *key);
+    virtual ~MetricsSummarizerPlayer() {};
+
+    virtual void mergeRecord(MediaAnalyticsItem &have, MediaAnalyticsItem &incoming);
+
+};
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_METRICSSUMMARIZERPLAYER_H
diff --git a/services/mediaanalytics/MetricsSummarizerRecorder.cpp b/services/mediaanalytics/MetricsSummarizerRecorder.cpp
new file mode 100644
index 0000000..c2919c3
--- /dev/null
+++ b/services/mediaanalytics/MetricsSummarizerRecorder.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "MetricsSummarizerRecorder"
+#include <utils/Log.h>
+
+#include <stdint.h>
+#include <inttypes.h>
+
+#include <utils/threads.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/List.h>
+
+#include <media/IMediaAnalyticsService.h>
+
+#include "MetricsSummarizer.h"
+#include "MetricsSummarizerRecorder.h"
+
+
+
+
+namespace android {
+
+MetricsSummarizerRecorder::MetricsSummarizerRecorder(const char *key)
+    : MetricsSummarizer(key)
+{
+    ALOGV("MetricsSummarizerRecorder::MetricsSummarizerRecorder");
+}
+
+} // namespace android
diff --git a/services/mediaanalytics/MetricsSummarizerRecorder.h b/services/mediaanalytics/MetricsSummarizerRecorder.h
new file mode 100644
index 0000000..963baab
--- /dev/null
+++ b/services/mediaanalytics/MetricsSummarizerRecorder.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef ANDROID_METRICSSUMMARIZERRECORDER_H
+#define ANDROID_METRICSSUMMARIZERRECORDER_H
+
+#include <utils/threads.h>
+#include <utils/Errors.h>
+#include <utils/KeyedVector.h>
+#include <utils/String8.h>
+#include <utils/List.h>
+
+#include <media/IMediaAnalyticsService.h>
+#include "MetricsSummarizer.h"
+
+
+namespace android {
+
+class MetricsSummarizerRecorder : public MetricsSummarizer
+{
+
+ public:
+
+    MetricsSummarizerRecorder(const char *key);
+    virtual ~MetricsSummarizerRecorder() {};
+
+};
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_METRICSSUMMARIZERRECORDER_H
diff --git a/services/mediacodec/main_codecservice.cpp b/services/mediacodec/main_codecservice.cpp
index c4e4cff..c59944a 100644
--- a/services/mediacodec/main_codecservice.cpp
+++ b/services/mediacodec/main_codecservice.cpp
@@ -31,9 +31,9 @@
 #include "MediaCodecService.h"
 #include "minijail.h"
 
-#include <android/hardware/media/omx/1.0/IOmx.h>
 #include <hidl/HidlTransportSupport.h>
 #include <omx/1.0/Omx.h>
+#include <omx/1.0/OmxStore.h>
 
 using namespace android;
 
@@ -61,17 +61,23 @@
 
     if (treble) {
         using namespace ::android::hardware::media::omx::V1_0;
+        sp<IOmxStore> omxStore = new implementation::OmxStore();
+        if (omxStore == nullptr) {
+            LOG(ERROR) << "Cannot create IOmxStore HAL service.";
+        } else if (omxStore->registerAsService() != OK) {
+            LOG(ERROR) << "Cannot register IOmxStore HAL service.";
+        }
         sp<IOmx> omx = new implementation::Omx();
         if (omx == nullptr) {
-            LOG(ERROR) << "Cannot create a Treble IOmx service.";
+            LOG(ERROR) << "Cannot create IOmx HAL service.";
         } else if (omx->registerAsService() != OK) {
-            LOG(ERROR) << "Cannot register a Treble IOmx service.";
+            LOG(ERROR) << "Cannot register IOmx HAL service.";
         } else {
-            LOG(INFO) << "Treble IOmx service created.";
+            LOG(INFO) << "Treble OMX service created.";
         }
     } else {
         MediaCodecService::instantiate();
-        LOG(INFO) << "Non-Treble IOMX service created.";
+        LOG(INFO) << "Non-Treble OMX service created.";
     }
 
     ProcessState::self()->startThreadPool();
diff --git a/services/mediacodec/seccomp_policy/mediacodec-arm.policy b/services/mediacodec/seccomp_policy/mediacodec-arm.policy
index 890d777..b8a5e90 100644
--- a/services/mediacodec/seccomp_policy/mediacodec-arm.policy
+++ b/services/mediacodec/seccomp_policy/mediacodec-arm.policy
@@ -29,6 +29,7 @@
 setpriority: 1
 getuid32: 1
 fstat64: 1
+fstatfs64: 1
 pread64: 1
 faccessat: 1
 readlinkat: 1
diff --git a/services/mediadrm/Android.mk b/services/mediadrm/Android.mk
index 1d5fa07..fa3a02b 100644
--- a/services/mediadrm/Android.mk
+++ b/services/mediadrm/Android.mk
@@ -43,8 +43,9 @@
 
 # TODO: Some legacy DRM plugins only support 32-bit. They need to be migrated to
 # 64-bit. (b/18948909) Once all of a device's legacy DRM plugins support 64-bit,
-# that device can turn on ENABLE_MEDIADRM_64 to build this service as 64-bit.
-ifneq ($(ENABLE_MEDIADRM_64), true)
+# that device can turn on TARGET_ENABLE_MEDIADRM_64 to build this service as
+# 64-bit.
+ifneq ($(TARGET_ENABLE_MEDIADRM_64), true)
 LOCAL_32_BIT_ONLY := true
 endif
 
diff --git a/services/mediaextractor/seccomp_policy/mediaextractor-arm.policy b/services/mediaextractor/seccomp_policy/mediaextractor-arm.policy
index 4e4ce30..7e8af1a 100644
--- a/services/mediaextractor/seccomp_policy/mediaextractor-arm.policy
+++ b/services/mediaextractor/seccomp_policy/mediaextractor-arm.policy
@@ -20,6 +20,7 @@
 lseek: 1
 writev: 1
 fstatat64: 1
+fstatfs64: 1
 fstat64: 1
 restart_syscall: 1
 exit: 1
diff --git a/services/mediaextractor/seccomp_policy/mediaextractor-arm64.policy b/services/mediaextractor/seccomp_policy/mediaextractor-arm64.policy
index 1683adb..aa8be5b 100644
--- a/services/mediaextractor/seccomp_policy/mediaextractor-arm64.policy
+++ b/services/mediaextractor/seccomp_policy/mediaextractor-arm64.policy
@@ -14,6 +14,7 @@
 madvise: 1
 getuid: 1
 fstat: 1
+fstatfs: 1
 read: 1
 setpriority: 1
 sigaltstack: 1
diff --git a/services/mediaextractor/seccomp_policy/mediaextractor-x86.policy b/services/mediaextractor/seccomp_policy/mediaextractor-x86.policy
index 83725cd..b5a6503 100644
--- a/services/mediaextractor/seccomp_policy/mediaextractor-x86.policy
+++ b/services/mediaextractor/seccomp_policy/mediaextractor-x86.policy
@@ -22,6 +22,7 @@
 setpriority: 1
 sigaltstack: 1
 fstatat64: 1
+fstatfs64: 1
 fstat64: 1
 restart_syscall: 1
 exit: 1
diff --git a/services/oboeservice/AAudioEndpointManager.cpp b/services/oboeservice/AAudioEndpointManager.cpp
index 84fa227..65b17bc 100644
--- a/services/oboeservice/AAudioEndpointManager.cpp
+++ b/services/oboeservice/AAudioEndpointManager.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "AAudioService"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
 #include <assert.h>
 #include <map>
 #include <mutex>
@@ -28,13 +32,18 @@
 ANDROID_SINGLETON_STATIC_INSTANCE(AAudioEndpointManager);
 
 AAudioEndpointManager::AAudioEndpointManager()
-        : Singleton<AAudioEndpointManager>() {
+        : Singleton<AAudioEndpointManager>()
+        , mInputs()
+        , mOutputs() {
 }
 
-AAudioServiceEndpoint *AAudioEndpointManager::findEndpoint(AAudioService &audioService, int32_t deviceId,
+AAudioServiceEndpoint *AAudioEndpointManager::openEndpoint(AAudioService &audioService, int32_t deviceId,
                                                            aaudio_direction_t direction) {
     AAudioServiceEndpoint *endpoint = nullptr;
     std::lock_guard<std::mutex> lock(mLock);
+
+    // Try to find an existing endpoint.
+    ALOGD("AAudioEndpointManager::openEndpoint(), device = %d, dir = %d", deviceId, direction);
     switch (direction) {
         case AAUDIO_DIRECTION_INPUT:
             endpoint = mInputs[deviceId];
@@ -48,11 +57,11 @@
     }
 
     // If we can't find an existing one then open one.
-    ALOGD("AAudioEndpointManager::findEndpoint(), found %p", endpoint);
+    ALOGD("AAudioEndpointManager::openEndpoint(), found %p", endpoint);
     if (endpoint == nullptr) {
         endpoint = new AAudioServiceEndpoint(audioService);
         if (endpoint->open(deviceId, direction) != AAUDIO_OK) {
-            ALOGD("AAudioEndpointManager::findEndpoint(), open failed");
+            ALOGE("AAudioEndpointManager::findEndpoint(), open failed");
             delete endpoint;
             endpoint = nullptr;
         } else {
@@ -66,22 +75,37 @@
             }
         }
     }
+
+    if (endpoint != nullptr) {
+        // Increment the reference count under this lock.
+        endpoint->setReferenceCount(endpoint->getReferenceCount() + 1);
+    }
+
     return endpoint;
 }
 
-// FIXME add reference counter for serviceEndpoints and removed on last use.
-
-void AAudioEndpointManager::removeEndpoint(AAudioServiceEndpoint *serviceEndpoint) {
-    aaudio_direction_t direction = serviceEndpoint->getDirection();
-    int32_t deviceId = serviceEndpoint->getDeviceId();
-
+void AAudioEndpointManager::closeEndpoint(AAudioServiceEndpoint *serviceEndpoint) {
     std::lock_guard<std::mutex> lock(mLock);
-    switch(direction) {
-        case AAUDIO_DIRECTION_INPUT:
-            mInputs.erase(deviceId);
-            break;
-        case AAUDIO_DIRECTION_OUTPUT:
-            mOutputs.erase(deviceId);
-            break;
+    if (serviceEndpoint == nullptr) {
+        return;
     }
-}
\ No newline at end of file
+
+    // Decrement the reference count under this lock.
+    int32_t newRefCount = serviceEndpoint->getReferenceCount() - 1;
+    serviceEndpoint->setReferenceCount(newRefCount);
+    if (newRefCount <= 0) {
+        aaudio_direction_t direction = serviceEndpoint->getDirection();
+        int32_t deviceId = serviceEndpoint->getDeviceId();
+
+        switch (direction) {
+            case AAUDIO_DIRECTION_INPUT:
+                mInputs.erase(deviceId);
+                break;
+            case AAUDIO_DIRECTION_OUTPUT:
+                mOutputs.erase(deviceId);
+                break;
+        }
+        serviceEndpoint->close();
+        delete serviceEndpoint;
+    }
+}
diff --git a/services/oboeservice/AAudioEndpointManager.h b/services/oboeservice/AAudioEndpointManager.h
index 48b27f0..bbcfc1d 100644
--- a/services/oboeservice/AAudioEndpointManager.h
+++ b/services/oboeservice/AAudioEndpointManager.h
@@ -39,11 +39,11 @@
      * @param direction
      * @return endpoint or nullptr
      */
-    AAudioServiceEndpoint *findEndpoint(android::AAudioService &audioService,
+    AAudioServiceEndpoint *openEndpoint(android::AAudioService &audioService,
                                         int32_t deviceId,
                                         aaudio_direction_t direction);
 
-    void removeEndpoint(AAudioServiceEndpoint *serviceEndpoint);
+    void closeEndpoint(AAudioServiceEndpoint *serviceEndpoint);
 
 private:
 
diff --git a/services/oboeservice/AAudioMixer.cpp b/services/oboeservice/AAudioMixer.cpp
index 70da339..43203d4 100644
--- a/services/oboeservice/AAudioMixer.cpp
+++ b/services/oboeservice/AAudioMixer.cpp
@@ -41,7 +41,7 @@
     memset(mOutputBuffer, 0, mBufferSizeInBytes);
 }
 
-void AAudioMixer::mix(FifoBuffer *fifo, float volume) {
+bool AAudioMixer::mix(FifoBuffer *fifo, float volume) {
     WrappingBuffer wrappingBuffer;
     float *destination = mOutputBuffer;
     fifo_frames_t framesLeft = mFramesPerBurst;
@@ -67,9 +67,10 @@
     }
     fifo->getFifoControllerBase()->advanceReadIndex(mFramesPerBurst - framesLeft);
     if (framesLeft > 0) {
-        ALOGW("AAudioMixer::mix() UNDERFLOW by %d / %d frames ----- UNDERFLOW !!!!!!!!!!",
-              framesLeft, mFramesPerBurst);
+        //ALOGW("AAudioMixer::mix() UNDERFLOW by %d / %d frames ----- UNDERFLOW !!!!!!!!!!",
+        //      framesLeft, mFramesPerBurst);
     }
+    return (framesLeft > 0); // did not get all the frames we needed, ie. "underflow"
 }
 
 void AAudioMixer::mixPart(float *destination, float *source, int32_t numFrames, float volume) {
diff --git a/services/oboeservice/AAudioMixer.h b/services/oboeservice/AAudioMixer.h
index 2191183..9155fec 100644
--- a/services/oboeservice/AAudioMixer.h
+++ b/services/oboeservice/AAudioMixer.h
@@ -31,7 +31,13 @@
 
     void clear();
 
-    void mix(android::FifoBuffer *fifo, float volume);
+    /**
+     * Mix from this FIFO
+     * @param fifo
+     * @param volume
+     * @return true if underflowed
+     */
+    bool mix(android::FifoBuffer *fifo, float volume);
 
     void mixPart(float *destination, float *source, int32_t numFrames, float volume);
 
diff --git a/services/oboeservice/AAudioService.cpp b/services/oboeservice/AAudioService.cpp
index 723ef63..816d5ab 100644
--- a/services/oboeservice/AAudioService.cpp
+++ b/services/oboeservice/AAudioService.cpp
@@ -54,8 +54,8 @@
     aaudio_result_t result = AAUDIO_OK;
     AAudioServiceStreamBase *serviceStream = nullptr;
     const AAudioStreamConfiguration &configurationInput = request.getConstantConfiguration();
+    bool sharingModeMatchRequired = request.isSharingModeMatchRequired();
     aaudio_sharing_mode_t sharingMode = configurationInput.getSharingMode();
-    ALOGE("AAudioService::openStream(): sharingMode = %d", sharingMode);
 
     if (sharingMode != AAUDIO_SHARING_MODE_EXCLUSIVE && sharingMode != AAUDIO_SHARING_MODE_SHARED) {
         ALOGE("AAudioService::openStream(): unrecognized sharing mode = %d", sharingMode);
@@ -77,8 +77,9 @@
     }
 
     // if SHARED requested or if EXCLUSIVE failed
-    if (serviceStream == nullptr) {
-        ALOGD("AAudioService::openStream(), sharingMode = AAUDIO_SHARING_MODE_SHARED");
+    if (sharingMode == AAUDIO_SHARING_MODE_SHARED
+         || (serviceStream == nullptr && !sharingModeMatchRequired)) {
+        ALOGD("AAudioService::openStream(), try AAUDIO_SHARING_MODE_SHARED");
         serviceStream =  new AAudioServiceStreamShared(*this);
         result = serviceStream->open(request, configurationOutput);
         configurationOutput.setSharingMode(AAUDIO_SHARING_MODE_SHARED);
@@ -126,9 +127,7 @@
         ALOGE("AAudioService::getStreamDescription(), illegal stream handle = 0x%0x", streamHandle);
         return AAUDIO_ERROR_INVALID_HANDLE;
     }
-    ALOGD("AAudioService::getStreamDescription(), handle = 0x%08x", streamHandle);
     aaudio_result_t result = serviceStream->getDescription(parcelable);
-    ALOGD("AAudioService::getStreamDescription(), result = %d", result);
     // parcelable.dump();
     return result;
 }
@@ -140,7 +139,6 @@
         return AAUDIO_ERROR_INVALID_HANDLE;
     }
     aaudio_result_t result = serviceStream->start();
-    ALOGD("AAudioService::startStream(), serviceStream->start() returned %d", result);
     return result;
 }
 
@@ -154,6 +152,16 @@
     return result;
 }
 
+aaudio_result_t AAudioService::stopStream(aaudio_handle_t streamHandle) {
+    AAudioServiceStreamBase *serviceStream = convertHandleToServiceStream(streamHandle);
+    if (serviceStream == nullptr) {
+        ALOGE("AAudioService::pauseStream(), illegal stream handle = 0x%0x", streamHandle);
+        return AAUDIO_ERROR_INVALID_HANDLE;
+    }
+    aaudio_result_t result = serviceStream->stop();
+    return result;
+}
+
 aaudio_result_t AAudioService::flushStream(aaudio_handle_t streamHandle) {
     AAudioServiceStreamBase *serviceStream = convertHandleToServiceStream(streamHandle);
     if (serviceStream == nullptr) {
@@ -168,7 +176,6 @@
                                                          pid_t clientThreadId,
                                                          int64_t periodNanoseconds) {
     AAudioServiceStreamBase *serviceStream = convertHandleToServiceStream(streamHandle);
-    ALOGD("AAudioService::registerAudioThread(), serviceStream = %p", serviceStream);
     if (serviceStream == nullptr) {
         ALOGE("AAudioService::registerAudioThread(), illegal stream handle = 0x%0x", streamHandle);
         return AAUDIO_ERROR_INVALID_HANDLE;
@@ -193,7 +200,6 @@
                                                      pid_t clientProcessId,
                                                      pid_t clientThreadId) {
     AAudioServiceStreamBase *serviceStream = convertHandleToServiceStream(streamHandle);
-    ALOGI("AAudioService::unregisterAudioThread(), serviceStream = %p", serviceStream);
     if (serviceStream == nullptr) {
         ALOGE("AAudioService::unregisterAudioThread(), illegal stream handle = 0x%0x",
               streamHandle);
diff --git a/services/oboeservice/AAudioService.h b/services/oboeservice/AAudioService.h
index 5a7a2b6..f5a7d2f 100644
--- a/services/oboeservice/AAudioService.h
+++ b/services/oboeservice/AAudioService.h
@@ -57,6 +57,8 @@
 
     virtual aaudio_result_t pauseStream(aaudio_handle_t streamHandle);
 
+    virtual aaudio_result_t stopStream(aaudio_handle_t streamHandle);
+
     virtual aaudio_result_t flushStream(aaudio_handle_t streamHandle);
 
     virtual aaudio_result_t registerAudioThread(aaudio_handle_t streamHandle,
diff --git a/services/oboeservice/AAudioServiceEndpoint.cpp b/services/oboeservice/AAudioServiceEndpoint.cpp
index 80551c9..d3e182a 100644
--- a/services/oboeservice/AAudioServiceEndpoint.cpp
+++ b/services/oboeservice/AAudioServiceEndpoint.cpp
@@ -14,6 +14,17 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "AAudioService"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <assert.h>
+#include <map>
+#include <mutex>
+#include <utils/Singleton.h>
+
+#include "AAudioEndpointManager.h"
+#include "AAudioServiceEndpoint.h"
 #include <algorithm>
 #include <mutex>
 #include <vector>
@@ -30,6 +41,13 @@
 // Wait at least this many times longer than the operation should take.
 #define MIN_TIMEOUT_OPERATIONS    4
 
+// This is the maximum size in frames. The effective size can be tuned smaller at runtime.
+#define DEFAULT_BUFFER_CAPACITY   (48 * 8)
+
+// Use 2 for "double buffered"
+#define BUFFER_SIZE_IN_BURSTS     2
+#define BURSTS_PER_MIX_LOOP       1
+
 // The mStreamInternal will use a service interface that does not go through Binder.
 AAudioServiceEndpoint::AAudioServiceEndpoint(AAudioService &audioService)
         : mStreamInternal(audioService, true)
@@ -43,11 +61,18 @@
 aaudio_result_t AAudioServiceEndpoint::open(int32_t deviceId, aaudio_direction_t direction) {
     AudioStreamBuilder builder;
     builder.setSharingMode(AAUDIO_SHARING_MODE_EXCLUSIVE);
+    // Don't fall back to SHARED because that would cause recursion.
+    builder.setSharingModeMatchRequired(true);
     builder.setDeviceId(deviceId);
     builder.setDirection(direction);
+    builder.setBufferCapacity(DEFAULT_BUFFER_CAPACITY);
+
     aaudio_result_t result = mStreamInternal.open(builder);
     if (result == AAUDIO_OK) {
         mMixer.allocate(mStreamInternal.getSamplesPerFrame(), mStreamInternal.getFramesPerBurst());
+
+        int32_t desiredBufferSize = BUFFER_SIZE_IN_BURSTS * mStreamInternal.getFramesPerBurst();
+        mStreamInternal.setBufferSize(desiredBufferSize);
     }
     return result;
 }
@@ -58,15 +83,12 @@
 
 // TODO, maybe use an interface to reduce exposure
 aaudio_result_t AAudioServiceEndpoint::registerStream(AAudioServiceStreamShared *sharedStream) {
-    ALOGD("AAudioServiceEndpoint::registerStream(%p)", sharedStream);
-    // TODO use real-time technique to avoid mutex, eg. atomic command FIFO
     std::lock_guard<std::mutex> lock(mLockStreams);
     mRegisteredStreams.push_back(sharedStream);
     return AAUDIO_OK;
 }
 
 aaudio_result_t AAudioServiceEndpoint::unregisterStream(AAudioServiceStreamShared *sharedStream) {
-    ALOGD("AAudioServiceEndpoint::unregisterStream(%p)", sharedStream);
     std::lock_guard<std::mutex> lock(mLockStreams);
     mRegisteredStreams.erase(std::remove(mRegisteredStreams.begin(), mRegisteredStreams.end(), sharedStream),
               mRegisteredStreams.end());
@@ -75,7 +97,6 @@
 
 aaudio_result_t AAudioServiceEndpoint::startStream(AAudioServiceStreamShared *sharedStream) {
     // TODO use real-time technique to avoid mutex, eg. atomic command FIFO
-    ALOGD("AAudioServiceEndpoint(): startStream() entering");
     std::lock_guard<std::mutex> lock(mLockStreams);
     mRunningStreams.push_back(sharedStream);
     if (mRunningStreams.size() == 1) {
@@ -106,13 +127,10 @@
 
 // Render audio in the application callback and then write the data to the stream.
 void *AAudioServiceEndpoint::callbackLoop() {
-    aaudio_result_t result = AAUDIO_OK;
-
     ALOGD("AAudioServiceEndpoint(): callbackLoop() entering");
+    int32_t underflowCount = 0;
 
-    result = mStreamInternal.requestStart();
-    ALOGD("AAudioServiceEndpoint(): callbackLoop() after requestStart()  %d, isPlaying() = %d",
-          result, (int) mStreamInternal.isPlaying());
+    aaudio_result_t result = mStreamInternal.requestStart();
 
     // result might be a frame count
     while (mCallbackEnabled.load() && mStreamInternal.isPlaying() && (result >= 0)) {
@@ -123,12 +141,14 @@
             for(AAudioServiceStreamShared *sharedStream : mRunningStreams) {
                 FifoBuffer *fifo = sharedStream->getDataFifoBuffer();
                 float volume = 0.5; // TODO get from system
-                mMixer.mix(fifo, volume);
+                bool underflowed = mMixer.mix(fifo, volume);
+                underflowCount += underflowed ? 1 : 0;
+                // TODO log underflows in each stream
+                sharedStream->markTransferTime(AudioClock::getNanoseconds());
             }
         }
 
         // Write audio data to stream using a blocking write.
-        ALOGD("AAudioServiceEndpoint(): callbackLoop() write(%d)", getFramesPerBurst());
         int64_t timeoutNanos = calculateReasonableTimeout(mStreamInternal.getFramesPerBurst());
         result = mStreamInternal.write(mMixer.getOutputBuffer(), getFramesPerBurst(), timeoutNanos);
         if (result == AAUDIO_ERROR_DISCONNECTED) {
@@ -141,11 +161,9 @@
         }
     }
 
-    ALOGD("AAudioServiceEndpoint(): callbackLoop() exiting, result = %d, isPlaying() = %d",
-          result, (int) mStreamInternal.isPlaying());
-
     result = mStreamInternal.requestStop();
 
+    ALOGD("AAudioServiceEndpoint(): callbackLoop() exiting, %d underflows", underflowCount);
     return NULL; // TODO review
 }
 
diff --git a/services/oboeservice/AAudioServiceEndpoint.h b/services/oboeservice/AAudioServiceEndpoint.h
index 020d38a..a4ceae6 100644
--- a/services/oboeservice/AAudioServiceEndpoint.h
+++ b/services/oboeservice/AAudioServiceEndpoint.h
@@ -56,6 +56,16 @@
 
     void *callbackLoop();
 
+    // This should only be called from the AAudioEndpointManager under a mutex.
+    int32_t getReferenceCount() const {
+        return mReferenceCount;
+    }
+
+    // This should only be called from the AAudioEndpointManager under a mutex.
+    void setReferenceCount(int32_t count) {
+        mReferenceCount = count;
+    }
+
 private:
     aaudio_result_t startMixer_l();
     aaudio_result_t stopMixer_l();
@@ -64,13 +74,14 @@
 
     AudioStreamInternal      mStreamInternal;
     AAudioMixer              mMixer;
-    AAudioServiceStreamMMAP  mStreamMMAP;
 
     std::atomic<bool>        mCallbackEnabled;
+    int32_t                  mReferenceCount = 0;
 
     std::mutex               mLockStreams;
     std::vector<AAudioServiceStreamShared *> mRegisteredStreams;
     std::vector<AAudioServiceStreamShared *> mRunningStreams;
+
 };
 
 } /* namespace aaudio */
diff --git a/services/oboeservice/AAudioServiceStreamBase.cpp b/services/oboeservice/AAudioServiceStreamBase.cpp
index b15043d..8248f8b 100644
--- a/services/oboeservice/AAudioServiceStreamBase.cpp
+++ b/services/oboeservice/AAudioServiceStreamBase.cpp
@@ -59,10 +59,12 @@
     std::lock_guard<std::mutex> lock(mLockUpMessageQueue);
     delete mUpMessageQueue;
     mUpMessageQueue = nullptr;
+
     return AAUDIO_OK;
 }
 
 aaudio_result_t AAudioServiceStreamBase::start() {
+    ALOGD("AAudioServiceStreamBase::start() send AAUDIO_SERVICE_EVENT_STARTED");
     sendServiceEvent(AAUDIO_SERVICE_EVENT_STARTED);
     mState = AAUDIO_STREAM_STATE_STARTED;
     mThreadEnabled.store(true);
@@ -78,14 +80,37 @@
         processError();
         return result;
     }
+    ALOGD("AAudioServiceStreamBase::pause() send AAUDIO_SERVICE_EVENT_PAUSED");
     sendServiceEvent(AAUDIO_SERVICE_EVENT_PAUSED);
     mState = AAUDIO_STREAM_STATE_PAUSED;
     return result;
 }
 
+aaudio_result_t AAudioServiceStreamBase::stop() {
+    // TODO wait for data to be played out
+    sendCurrentTimestamp();
+    mThreadEnabled.store(false);
+    aaudio_result_t result = mAAudioThread.stop();
+    if (result != AAUDIO_OK) {
+        processError();
+        return result;
+    }
+    ALOGD("AAudioServiceStreamBase::stop() send AAUDIO_SERVICE_EVENT_STOPPED");
+    sendServiceEvent(AAUDIO_SERVICE_EVENT_STOPPED);
+    mState = AAUDIO_STREAM_STATE_STOPPED;
+    return result;
+}
+
+aaudio_result_t AAudioServiceStreamBase::flush() {
+    ALOGD("AAudioServiceStreamBase::flush() send AAUDIO_SERVICE_EVENT_FLUSHED");
+    sendServiceEvent(AAUDIO_SERVICE_EVENT_FLUSHED);
+    mState = AAUDIO_STREAM_STATE_FLUSHED;
+    return AAUDIO_OK;
+}
+
 // implement Runnable
 void AAudioServiceStreamBase::run() {
-    ALOGD("AAudioServiceStreamMMAP::run() entering ----------------");
+    ALOGD("AAudioServiceStreamBase::run() entering ----------------");
     TimestampScheduler timestampScheduler;
     timestampScheduler.setBurstPeriod(mFramesPerBurst, mSampleRate);
     timestampScheduler.start(AudioClock::getNanoseconds());
@@ -102,7 +127,7 @@
             AudioClock::sleepUntilNanoTime(nextTime);
         }
     }
-    ALOGD("AAudioServiceStreamMMAP::run() exiting ----------------");
+    ALOGD("AAudioServiceStreamBase::run() exiting ----------------");
 }
 
 void AAudioServiceStreamBase::processError() {
@@ -122,6 +147,10 @@
 
 aaudio_result_t AAudioServiceStreamBase::writeUpMessageQueue(AAudioServiceMessage *command) {
     std::lock_guard<std::mutex> lock(mLockUpMessageQueue);
+    if (mUpMessageQueue == nullptr) {
+        ALOGE("writeUpMessageQueue(): mUpMessageQueue null! - stream not open");
+        return AAUDIO_ERROR_NULL;
+    }
     int32_t count = mUpMessageQueue->getFifoBuffer()->write(command, 1);
     if (count != 1) {
         ALOGE("writeUpMessageQueue(): Queue full. Did client die?");
@@ -133,9 +162,11 @@
 
 aaudio_result_t AAudioServiceStreamBase::sendCurrentTimestamp() {
     AAudioServiceMessage command;
+    //ALOGD("sendCurrentTimestamp() called");
     aaudio_result_t result = getFreeRunningPosition(&command.timestamp.position,
                                                     &command.timestamp.timestamp);
     if (result == AAUDIO_OK) {
+        //ALOGD("sendCurrentTimestamp(): position %d", (int) command.timestamp.position);
         command.what = AAudioServiceMessage::code::TIMESTAMP;
         result = writeUpMessageQueue(&command);
     }
diff --git a/services/oboeservice/AAudioServiceStreamBase.h b/services/oboeservice/AAudioServiceStreamBase.h
index 91eec35..9318c2e 100644
--- a/services/oboeservice/AAudioServiceStreamBase.h
+++ b/services/oboeservice/AAudioServiceStreamBase.h
@@ -17,6 +17,7 @@
 #ifndef AAUDIO_AAUDIO_SERVICE_STREAM_BASE_H
 #define AAUDIO_AAUDIO_SERVICE_STREAM_BASE_H
 
+#include <assert.h>
 #include <mutex>
 
 #include "fifo/FifoBuffer.h"
@@ -60,17 +61,22 @@
     /**
      * Start the flow of data.
      */
-    virtual aaudio_result_t start() = 0;
+    virtual aaudio_result_t start();
 
     /**
      * Stop the flow of data such that start() can resume with loss of data.
      */
-    virtual aaudio_result_t pause() = 0;
+    virtual aaudio_result_t pause();
+
+    /**
+     * Stop the flow of data after data in buffer has played.
+     */
+    virtual aaudio_result_t stop();
 
     /**
      *  Discard any data held by the underlying HAL or Service.
      */
-    virtual aaudio_result_t flush() = 0;
+    virtual aaudio_result_t flush();
 
     // -------------------------------------------------------------------
 
@@ -127,9 +133,6 @@
     // This is used by one thread to tell another thread to exit. So it must be atomic.
     std::atomic<bool>   mThreadEnabled;
 
-
-    int                mAudioDataFileDescriptor = -1;
-
     aaudio_audio_format_t mAudioFormat = AAUDIO_FORMAT_UNSPECIFIED;
     int32_t            mFramesPerBurst = 0;
     int32_t            mSamplesPerFrame = AAUDIO_UNSPECIFIED;
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.cpp b/services/oboeservice/AAudioServiceStreamMMAP.cpp
index b70c625..cadc2a4 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.cpp
+++ b/services/oboeservice/AAudioServiceStreamMMAP.cpp
@@ -55,6 +55,18 @@
 aaudio_result_t AAudioServiceStreamMMAP::close() {
     ALOGD("AAudioServiceStreamMMAP::close() called, %p", mMmapStream.get());
     mMmapStream.clear(); // TODO review. Is that all we have to do?
+    // Apparently the above close is asynchronous. An attempt to open a new device
+    // right after a close can fail. Also some callbacks may still be in flight!
+    // FIXME Make closing synchronous.
+    AudioClock::sleepForNanos(100 * AAUDIO_NANOS_PER_MILLISECOND);
+
+    if (mAudioDataFileDescriptor != -1) {
+        ALOGV("AAudioServiceStreamMMAP: LEAK? close(mAudioDataFileDescriptor = %d)\n",
+              mAudioDataFileDescriptor);
+        ::close(mAudioDataFileDescriptor);
+        mAudioDataFileDescriptor = -1;
+    }
+
     return AAudioServiceStreamBase::close();
 }
 
@@ -79,8 +91,8 @@
     const AAudioStreamConfiguration &configurationInput = request.getConstantConfiguration();
     audio_port_handle_t deviceId = configurationInput.getDeviceId();
 
-    ALOGI("open request dump()");
-    request.dump();
+    // ALOGI("open request dump()");
+    // request.dump();
 
     mMmapClient.clientUid = request.getUserId();
     mMmapClient.clientPid = request.getProcessId();
@@ -120,6 +132,9 @@
     MmapStreamInterface::stream_direction_t streamDirection = (direction == AAUDIO_DIRECTION_OUTPUT)
         ? MmapStreamInterface::DIRECTION_OUTPUT : MmapStreamInterface::DIRECTION_INPUT;
 
+    ALOGD("AAudioServiceStreamMMAP::open() request devId = %d, sRate = %d",
+          deviceId, config.sample_rate);
+
     // Open HAL stream.
     status_t status = MmapStreamInterface::openMmapStream(streamDirection,
                                                           &attributes,
@@ -156,11 +171,16 @@
                            : audio_channel_count_from_in_mask(config.channel_mask);
 
     mAudioDataFileDescriptor = mMmapBufferinfo.shared_memory_fd;
+    ALOGV("AAudioServiceStreamMMAP::open LEAK? mAudioDataFileDescriptor = %d\n",
+          mAudioDataFileDescriptor);
     mFramesPerBurst = mMmapBufferinfo.burst_size_frames;
     mCapacityInFrames = mMmapBufferinfo.buffer_size_frames;
     mAudioFormat = AAudioConvert_androidToAAudioDataFormat(config.format);
     mSampleRate = config.sample_rate;
 
+    ALOGD("AAudioServiceStreamMMAP::open() got devId = %d, sRate = %d",
+          deviceId, config.sample_rate);
+
     // Fill in AAudioStreamConfiguration
     configurationOutput.setSampleRate(mSampleRate);
     configurationOutput.setSamplesPerFrame(mSamplesPerFrame);
@@ -198,16 +218,25 @@
     return (result1 != AAUDIO_OK) ? result1 : result2;
 }
 
+aaudio_result_t AAudioServiceStreamMMAP::stop() {
+    if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL;
+
+    aaudio_result_t result1 = AAudioServiceStreamBase::stop();
+    aaudio_result_t result2 = mMmapStream->stop(mPortHandle);
+    mFramesRead.reset32();
+    return (result1 != AAUDIO_OK) ? result1 : result2;
+}
+
 /**
  *  Discard any data held by the underlying HAL or Service.
  */
 aaudio_result_t AAudioServiceStreamMMAP::flush() {
     if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL;
     // TODO how do we flush an MMAP/NOIRQ buffer? sync pointers?
-    ALOGD("AAudioServiceStreamMMAP::pause() send AAUDIO_SERVICE_EVENT_FLUSHED");
+    ALOGD("AAudioServiceStreamMMAP::flush() send AAUDIO_SERVICE_EVENT_FLUSHED");
     sendServiceEvent(AAUDIO_SERVICE_EVENT_FLUSHED);
     mState = AAUDIO_STREAM_STATE_FLUSHED;
-    return AAUDIO_OK;
+    return AAudioServiceStreamBase::flush();;
 }
 
 
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.h b/services/oboeservice/AAudioServiceStreamMMAP.h
index f121c5c..fe75a10 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.h
+++ b/services/oboeservice/AAudioServiceStreamMMAP.h
@@ -66,6 +66,8 @@
     */
     aaudio_result_t pause() override;
 
+    aaudio_result_t stop() override;
+
     /**
      *  Discard any data held by the underlying HAL or Service.
      *
@@ -125,6 +127,7 @@
     MonotonicCounter                    mFramesWritten;
     MonotonicCounter                    mFramesRead;
     int32_t                             mPreviousFrameCounter = 0;   // from HAL
+    int                                 mAudioDataFileDescriptor = -1;
 
     // Interface to the AudioFlinger MMAP support.
     android::sp<android::MmapStreamInterface> mMmapStream;
diff --git a/services/oboeservice/AAudioServiceStreamShared.cpp b/services/oboeservice/AAudioServiceStreamShared.cpp
index cd9336b..713d1f8 100644
--- a/services/oboeservice/AAudioServiceStreamShared.cpp
+++ b/services/oboeservice/AAudioServiceStreamShared.cpp
@@ -61,7 +61,7 @@
 
     ALOGD("AAudioServiceStreamShared::open(), direction = %d", direction);
     AAudioEndpointManager &mEndpointManager = AAudioEndpointManager::getInstance();
-    mServiceEndpoint = mEndpointManager.findEndpoint(mAudioService, deviceId, direction);
+    mServiceEndpoint = mEndpointManager.openEndpoint(mAudioService, deviceId, direction);
     ALOGD("AAudioServiceStreamShared::open(), mServiceEndPoint = %p", mServiceEndpoint);
     if (mServiceEndpoint == nullptr) {
         return AAUDIO_ERROR_UNAVAILABLE;
@@ -72,6 +72,7 @@
     if (mAudioFormat == AAUDIO_FORMAT_UNSPECIFIED) {
         mAudioFormat = AAUDIO_FORMAT_PCM_FLOAT;
     } else if (mAudioFormat != AAUDIO_FORMAT_PCM_FLOAT) {
+        ALOGE("AAudioServiceStreamShared::open(), mAudioFormat = %d, need FLOAT", mAudioFormat);
         return AAUDIO_ERROR_INVALID_FORMAT;
     }
 
@@ -79,6 +80,8 @@
     if (mSampleRate == AAUDIO_FORMAT_UNSPECIFIED) {
         mSampleRate = mServiceEndpoint->getSampleRate();
     } else if (mSampleRate != mServiceEndpoint->getSampleRate()) {
+        ALOGE("AAudioServiceStreamShared::open(), mAudioFormat = %d, need %d",
+              mSampleRate, mServiceEndpoint->getSampleRate());
         return AAUDIO_ERROR_INVALID_RATE;
     }
 
@@ -86,17 +89,22 @@
     if (mSamplesPerFrame == AAUDIO_FORMAT_UNSPECIFIED) {
         mSamplesPerFrame = mServiceEndpoint->getSamplesPerFrame();
     } else if (mSamplesPerFrame != mServiceEndpoint->getSamplesPerFrame()) {
+        ALOGE("AAudioServiceStreamShared::open(), mSamplesPerFrame = %d, need %d",
+              mSamplesPerFrame, mServiceEndpoint->getSamplesPerFrame());
         return AAUDIO_ERROR_OUT_OF_RANGE;
     }
 
     // Determine this stream's shared memory buffer capacity.
     mFramesPerBurst = mServiceEndpoint->getFramesPerBurst();
     int32_t minCapacityFrames = configurationInput.getBufferCapacity();
-    int32_t numBursts = (minCapacityFrames + mFramesPerBurst - 1) / mFramesPerBurst;
-    if (numBursts < MIN_BURSTS_PER_BUFFER) {
-        numBursts = MIN_BURSTS_PER_BUFFER;
-    } else if (numBursts > MAX_BURSTS_PER_BUFFER) {
-        numBursts = MAX_BURSTS_PER_BUFFER;
+    int32_t numBursts = MAX_BURSTS_PER_BUFFER;
+    if (minCapacityFrames != AAUDIO_UNSPECIFIED) {
+        numBursts = (minCapacityFrames + mFramesPerBurst - 1) / mFramesPerBurst;
+        if (numBursts < MIN_BURSTS_PER_BUFFER) {
+            numBursts = MIN_BURSTS_PER_BUFFER;
+        } else if (numBursts > MAX_BURSTS_PER_BUFFER) {
+            numBursts = MAX_BURSTS_PER_BUFFER;
+        }
     }
     mCapacityInFrames = numBursts * mFramesPerBurst;
     ALOGD("AAudioServiceStreamShared::open(), mCapacityInFrames = %d", mCapacityInFrames);
@@ -122,8 +130,12 @@
  * An AAUDIO_SERVICE_EVENT_STARTED will be sent to the client when complete.
  */
 aaudio_result_t AAudioServiceStreamShared::start()  {
+    AAudioServiceEndpoint *endpoint = mServiceEndpoint;
+    if (endpoint == nullptr) {
+        return AAUDIO_ERROR_INVALID_STATE;
+    }
     // Add this stream to the mixer.
-    aaudio_result_t result = mServiceEndpoint->startStream(this);
+    aaudio_result_t result = endpoint->startStream(this);
     if (result != AAUDIO_OK) {
         ALOGE("AAudioServiceStreamShared::start() mServiceEndpoint returned %d", result);
         processError();
@@ -139,15 +151,31 @@
  * An AAUDIO_SERVICE_EVENT_PAUSED will be sent to the client when complete.
 */
 aaudio_result_t AAudioServiceStreamShared::pause()  {
+    AAudioServiceEndpoint *endpoint = mServiceEndpoint;
+    if (endpoint == nullptr) {
+        return AAUDIO_ERROR_INVALID_STATE;
+    }
     // Add this stream to the mixer.
-    aaudio_result_t result = mServiceEndpoint->stopStream(this);
+    aaudio_result_t result = endpoint->stopStream(this);
+    if (result != AAUDIO_OK) {
+        ALOGE("AAudioServiceStreamShared::pause() mServiceEndpoint returned %d", result);
+        processError();
+    }
+    return AAudioServiceStreamBase::pause();
+}
+
+aaudio_result_t AAudioServiceStreamShared::stop()  {
+    AAudioServiceEndpoint *endpoint = mServiceEndpoint;
+    if (endpoint == nullptr) {
+        return AAUDIO_ERROR_INVALID_STATE;
+    }
+    // Add this stream to the mixer.
+    aaudio_result_t result = endpoint->stopStream(this);
     if (result != AAUDIO_OK) {
         ALOGE("AAudioServiceStreamShared::stop() mServiceEndpoint returned %d", result);
         processError();
-    } else {
-        result = AAudioServiceStreamBase::start();
     }
-    return AAUDIO_OK;
+    return AAudioServiceStreamBase::stop();
 }
 
 /**
@@ -157,15 +185,25 @@
  */
 aaudio_result_t AAudioServiceStreamShared::flush()  {
     // TODO make sure we are paused
-    return AAUDIO_OK;
+    // TODO actually flush the data
+    return AAudioServiceStreamBase::flush() ;
 }
 
 aaudio_result_t AAudioServiceStreamShared::close()  {
     pause();
     // TODO wait for pause() to synchronize
-    mServiceEndpoint->unregisterStream(this);
-    mServiceEndpoint->close();
-    mServiceEndpoint = nullptr;
+    AAudioServiceEndpoint *endpoint = mServiceEndpoint;
+    if (endpoint != nullptr) {
+        endpoint->unregisterStream(this);
+
+        AAudioEndpointManager &mEndpointManager = AAudioEndpointManager::getInstance();
+        mEndpointManager.closeEndpoint(endpoint);
+        mServiceEndpoint = nullptr;
+    }
+    if (mAudioDataQueue != nullptr) {
+        delete mAudioDataQueue;
+        mAudioDataQueue = nullptr;
+    }
     return AAudioServiceStreamBase::close();
 }
 
@@ -189,10 +227,15 @@
     mServiceEndpoint = nullptr;
 }
 
+void AAudioServiceStreamShared::markTransferTime(int64_t nanoseconds) {
+    mMarkedPosition = mAudioDataQueue->getFifoBuffer()->getReadCounter();
+    mMarkedTime = nanoseconds;
+}
 
 aaudio_result_t AAudioServiceStreamShared::getFreeRunningPosition(int64_t *positionFrames,
                                                                 int64_t *timeNanos) {
-    *positionFrames = mAudioDataQueue->getFifoBuffer()->getReadCounter();
-    *timeNanos = AudioClock::getNanoseconds();
+    // TODO get these two numbers as an atomic pair
+    *positionFrames = mMarkedPosition;
+    *timeNanos = mMarkedTime;
     return AAUDIO_OK;
 }
diff --git a/services/oboeservice/AAudioServiceStreamShared.h b/services/oboeservice/AAudioServiceStreamShared.h
index f6df7ce..b981387 100644
--- a/services/oboeservice/AAudioServiceStreamShared.h
+++ b/services/oboeservice/AAudioServiceStreamShared.h
@@ -66,6 +66,11 @@
     aaudio_result_t pause() override;
 
     /**
+     * Stop the flow of data after data in buffer has played.
+     */
+    aaudio_result_t stop() override;
+
+    /**
      *  Discard any data held by the underlying HAL or Service.
      *
      * This is not guaranteed to be synchronous but it currently is.
@@ -77,6 +82,11 @@
 
     android::FifoBuffer *getDataFifoBuffer() { return mAudioDataQueue->getFifoBuffer(); }
 
+    /* Keep a record of when a buffer transfer completed.
+     * This allows for a more accurate timing model.
+     */
+    void markTransferTime(int64_t nanoseconds);
+
     void onStop();
 
     void onDisconnect();
@@ -91,6 +101,9 @@
     android::AAudioService  &mAudioService;
     AAudioServiceEndpoint   *mServiceEndpoint = nullptr;
     SharedRingBuffer        *mAudioDataQueue;
+
+    int64_t                  mMarkedPosition = 0;
+    int64_t                  mMarkedTime = 0;
 };
 
 } /* namespace aaudio */
diff --git a/services/oboeservice/Android.mk b/services/oboeservice/Android.mk
index a9c80ae..afb477e 100644
--- a/services/oboeservice/Android.mk
+++ b/services/oboeservice/Android.mk
@@ -48,8 +48,7 @@
     libcutils \
     libmediautils \
     libutils \
-    liblog \
-    libtinyalsa
+    liblog
 
 include $(BUILD_SHARED_LIBRARY)
 
diff --git a/services/oboeservice/SharedRingBuffer.cpp b/services/oboeservice/SharedRingBuffer.cpp
index efcc9d6..6b3fb4c 100644
--- a/services/oboeservice/SharedRingBuffer.cpp
+++ b/services/oboeservice/SharedRingBuffer.cpp
@@ -18,6 +18,8 @@
 //#define LOG_NDEBUG 0
 #include <utils/Log.h>
 
+#include <sys/mman.h>
+
 #include "binding/RingBufferParcelable.h"
 #include "binding/AudioEndpointParcelable.h"
 
@@ -31,9 +33,13 @@
     if (mSharedMemory != nullptr) {
         delete mFifoBuffer;
         munmap(mSharedMemory, mSharedMemorySizeInBytes);
-        close(mFileDescriptor);
         mSharedMemory = nullptr;
     }
+    if (mFileDescriptor != -1) {
+        ALOGV("SharedRingBuffer: LEAK? close(mFileDescriptor = %d)\n", mFileDescriptor);
+        close(mFileDescriptor);
+        mFileDescriptor = -1;
+    }
 }
 
 aaudio_result_t SharedRingBuffer::allocate(fifo_frames_t   bytesPerFrame,
@@ -44,10 +50,12 @@
     mDataMemorySizeInBytes = bytesPerFrame * capacityInFrames;
     mSharedMemorySizeInBytes = mDataMemorySizeInBytes + (2 * (sizeof(fifo_counter_t)));
     mFileDescriptor = ashmem_create_region("AAudioSharedRingBuffer", mSharedMemorySizeInBytes);
+    ALOGV("SharedRingBuffer::allocate() LEAK? mFileDescriptor = %d\n", mFileDescriptor);
     if (mFileDescriptor < 0) {
         ALOGE("SharedRingBuffer::allocate() ashmem_create_region() failed %d", errno);
         return AAUDIO_ERROR_INTERNAL;
     }
+
     int err = ashmem_set_prot_region(mFileDescriptor, PROT_READ|PROT_WRITE); // TODO error handling?
     if (err < 0) {
         ALOGE("SharedRingBuffer::allocate() ashmem_set_prot_region() failed %d", errno);
@@ -73,9 +81,9 @@
             (fifo_counter_t *) &mSharedMemory[SHARED_RINGBUFFER_WRITE_OFFSET];
     uint8_t *dataAddress = &mSharedMemory[SHARED_RINGBUFFER_DATA_OFFSET];
 
-    mFifoBuffer = new(std::nothrow) FifoBuffer(bytesPerFrame, capacityInFrames,
+    mFifoBuffer = new FifoBuffer(bytesPerFrame, capacityInFrames,
                                  readCounterAddress, writeCounterAddress, dataAddress);
-    return (mFifoBuffer == nullptr) ? AAUDIO_ERROR_NO_MEMORY : AAUDIO_OK;
+    return AAUDIO_OK;
 }
 
 void SharedRingBuffer::fillParcelable(AudioEndpointParcelable &endpointParcelable,