Merge "aaudio: fix output bugs and improve input performance, add loopback test" into oc-dev
diff --git a/drm/drmserver/DrmManager.cpp b/drm/drmserver/DrmManager.cpp
index 1d835f9..bf04a89 100644
--- a/drm/drmserver/DrmManager.cpp
+++ b/drm/drmserver/DrmManager.cpp
@@ -88,14 +88,9 @@
 }
 
 status_t DrmManager::loadPlugIns() {
-
-    String8 vendorPluginDirPath("/vendor/lib/drm");
-    loadPlugIns(vendorPluginDirPath);
-
     String8 pluginDirPath("/system/lib/drm");
     loadPlugIns(pluginDirPath);
     return DRM_NO_ERROR;
-
 }
 
 status_t DrmManager::loadPlugIns(const String8& plugInDirPath) {
diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp
index 3596f12..f54954a 100644
--- a/drm/libmediadrm/DrmHal.cpp
+++ b/drm/libmediadrm/DrmHal.cpp
@@ -410,6 +410,7 @@
     }
 
     setListener(NULL);
+    mPlugin->setListener(NULL);
     mPlugin.clear();
 
     return OK;
diff --git a/media/libaaudio/src/core/AudioStreamBuilder.cpp b/media/libaaudio/src/core/AudioStreamBuilder.cpp
index f313b58..6c8f7f5 100644
--- a/media/libaaudio/src/core/AudioStreamBuilder.cpp
+++ b/media/libaaudio/src/core/AudioStreamBuilder.cpp
@@ -30,12 +30,6 @@
 #include "legacy/AudioStreamRecord.h"
 #include "legacy/AudioStreamTrack.h"
 
-// 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 directly.
-#define MMAP_EXCLUSIVE_ENABLED   0
-
 using namespace aaudio;
 
 /*
@@ -53,6 +47,7 @@
                                          AudioStream **audioStreamPtr) {
     *audioStreamPtr = nullptr;
     aaudio_result_t result = AAUDIO_OK;
+
     switch (direction) {
 
         case AAUDIO_DIRECTION_INPUT:
@@ -81,20 +76,30 @@
     return result;
 }
 
+// Try to open using MMAP path if that is enabled.
+// Fall back to Legacy path is MMAP not available.
 aaudio_result_t AudioStreamBuilder::build(AudioStream** streamPtr) {
-    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;
-    }
-
     AudioStream *audioStream = nullptr;
     *streamPtr = nullptr;
 
-    bool tryMMap = ((sharingMode == AAUDIO_SHARING_MODE_SHARED) && MMAP_SHARED_ENABLED) ||
-            ((sharingMode == AAUDIO_SHARING_MODE_EXCLUSIVE) && MMAP_EXCLUSIVE_ENABLED);
+    int32_t mmapEnabled = AAudioProperty_getMMapEnabled();
+    int32_t mmapExclusiveEnabled = AAudioProperty_getMMapExclusiveEnabled();
+    ALOGD("AudioStreamBuilder(): mmapEnabled = %d, mmapExclusiveEnabled = %d",
+          mmapEnabled, mmapExclusiveEnabled);
+
+    aaudio_sharing_mode_t sharingMode = getSharingMode();
+    if ((sharingMode == AAUDIO_SHARING_MODE_EXCLUSIVE)
+        && (mmapExclusiveEnabled == AAUDIO_USE_NEVER)) {
+        ALOGW("AudioStreamBuilder(): EXCLUSIVE sharing mode not supported. Use SHARED.");
+        sharingMode = AAUDIO_SHARING_MODE_SHARED;
+        setSharingMode(sharingMode);
+    }
+
+    bool allowMMap = mmapEnabled != AAUDIO_USE_NEVER;
+    bool allowLegacy = mmapEnabled != AAUDIO_USE_ALWAYS;
+
     aaudio_result_t result = builder_createStream(getDirection(), sharingMode,
-                                                  tryMMap, &audioStream);
+                                                  allowMMap, &audioStream);
     if (result == AAUDIO_OK) {
         // Open the stream using the parameters from the builder.
         result = audioStream->open(*this);
@@ -105,7 +110,7 @@
             delete audioStream;
             audioStream = nullptr;
 
-            if (isMMap) {
+            if (isMMap && allowLegacy) {
                 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,
diff --git a/media/libaaudio/src/utility/AAudioUtilities.cpp b/media/libaaudio/src/utility/AAudioUtilities.cpp
index be2bd10..168ed86 100644
--- a/media/libaaudio/src/utility/AAudioUtilities.cpp
+++ b/media/libaaudio/src/utility/AAudioUtilities.cpp
@@ -18,6 +18,7 @@
 //#define LOG_NDEBUG 0
 #include <utils/Log.h>
 
+#include <cutils/properties.h>
 #include <stdint.h>
 #include <sys/types.h>
 #include <utils/Errors.h>
@@ -322,3 +323,52 @@
     *sizeInBytes = numFrames * bytesPerFrame;
     return AAUDIO_OK;
 }
+
+static int32_t AAudioProperty_getMMapProperty(const char *propName,
+                                              int32_t defaultValue,
+                                              const char * caller) {
+    int32_t prop = property_get_int32(AAUDIO_PROP_MMAP_ENABLED, defaultValue);
+    switch (prop) {
+        case AAUDIO_USE_NEVER:
+        case AAUDIO_USE_ALWAYS:
+        case AAUDIO_USE_AUTO:
+            break;
+        default:
+            ALOGE("%s: invalid = %d", caller, prop);
+            prop = defaultValue;
+            break;
+    }
+    return prop;
+}
+
+int32_t AAudioProperty_getMMapEnabled() {
+    return AAudioProperty_getMMapProperty(AAUDIO_PROP_MMAP_ENABLED,
+                                          AAUDIO_USE_NEVER, __func__);
+}
+
+int32_t AAudioProperty_getMMapExclusiveEnabled() {
+    return AAudioProperty_getMMapProperty(AAUDIO_PROP_MMAP_EXCLUSIVE_ENABLED,
+                                          AAUDIO_USE_NEVER, __func__);
+}
+
+int32_t AAudioProperty_getMixerBursts() {
+    const int32_t defaultBursts = 2; // arbitrary
+    const int32_t maxBursts = 1024; // arbitrary
+    int32_t prop = property_get_int32(AAUDIO_PROP_MIXER_BURSTS, defaultBursts); // use 2 for double buffered
+    if (prop < 1 || prop > maxBursts) {
+        ALOGE("AAudioProperty_getMixerBursts: invalid = %d", prop);
+        prop = defaultBursts;
+    }
+    return prop;
+}
+
+int32_t AAudioProperty_getHardwareBurstMinMicros() {
+    const int32_t defaultMicros = 1000; // arbitrary
+    const int32_t maxMicros = 1000 * 1000; // arbitrary
+    int32_t prop = property_get_int32(AAUDIO_PROP_HW_BURST_MIN_USEC, defaultMicros);
+    if (prop < 1 || prop > maxMicros) {
+        ALOGE("AAudioProperty_getHardwareBurstMinMicros: invalid = %d", prop);
+        prop = defaultMicros;
+    }
+    return prop;
+}
diff --git a/media/libaaudio/src/utility/AAudioUtilities.h b/media/libaaudio/src/utility/AAudioUtilities.h
index 0078cbb..7c383c7 100644
--- a/media/libaaudio/src/utility/AAudioUtilities.h
+++ b/media/libaaudio/src/utility/AAudioUtilities.h
@@ -170,4 +170,54 @@
  */
 int32_t AAudioConvert_formatToSizeInBytes(aaudio_audio_format_t format);
 
+
+// Note that this code may be replaced by Settings or by some other system configuration tool.
+
+enum : int32_t {
+    // Related feature is disabled
+    AAUDIO_USE_NEVER = 0,
+    // If related feature works then use it. Otherwise fall back to something else.
+    AAUDIO_USE_AUTO = 1,
+    // Related feature must be used. If not available then fail.
+    AAUDIO_USE_ALWAYS = 2
+};
+
+#define AAUDIO_PROP_MMAP_ENABLED           "aaudio.mmap_enabled"
+
+/**
+ * Read system property.
+ * @return AAUDIO_USE_NEVER or AAUDIO_USE_AUTO or AAUDIO_USE_ALWAYS
+ */
+int32_t AAudioProperty_getMMapEnabled();
+
+#define AAUDIO_PROP_MMAP_EXCLUSIVE_ENABLED "aaudio.mmap_exclusive_enabled"
+
+/**
+ * Read system property.
+ * @return AAUDIO_USE_NEVER or AAUDIO_USE_AUTO or AAUDIO_USE_ALWAYS
+ */
+int32_t AAudioProperty_getMMapExclusiveEnabled();
+
+#define AAUDIO_PROP_MIXER_BURSTS           "aaudio.mixer_bursts"
+
+/**
+ * Read system property.
+ * @return number of bursts per mixer cycle
+ */
+int32_t AAudioProperty_getMixerBursts();
+
+#define AAUDIO_PROP_HW_BURST_MIN_USEC      "aaudio.hw_burst_min_usec"
+
+/**
+ * Read system property.
+ * This is handy in case the DMA is bursting too quickly for the CPU to keep up.
+ * For example, there may be a DMA burst every 100 usec but you only
+ * want to feed the MMAP buffer every 2000 usec.
+ *
+ * This will affect the framesPerBurst for an MMAP stream.
+ *
+ * @return minimum number of microseconds for a MMAP HW burst
+ */
+int32_t AAudioProperty_getHardwareBurstMinMicros();
+
 #endif //UTILITY_AAUDIO_UTILITIES_H
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index 22b09d4..00a1f9c 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -98,6 +98,7 @@
     mBufferingMonitor->stop();
 
     mIsDrmProtected = false;
+    mIsDrmReleased = false;
     mIsSecure = false;
     mMimes.clear();
 }
@@ -690,6 +691,17 @@
           break;
       }
 
+      case kWhatReleaseDrm:
+      {
+          status_t status = onReleaseDrm();
+          sp<AMessage> response = new AMessage;
+          response->setInt32("status", status);
+          sp<AReplyToken> replyID;
+          CHECK(msg->senderAwaitsResponse(&replyID));
+          response->postReply(replyID);
+          break;
+      }
+
       default:
           Source::onMessageReceived(msg);
           break;
@@ -839,6 +851,13 @@
         return -EWOULDBLOCK;
     }
 
+    // If has gone through stop/releaseDrm sequence, we no longer send down any buffer b/c
+    // the codec's crypto object has gone away (b/37960096).
+    // Note: This will be unnecessary when stop() changes behavior and releases codec (b/35248283).
+    if (!mStarted && mIsDrmReleased) {
+        return -EWOULDBLOCK;
+    }
+
     Track *track = audio ? &mAudioTrack : &mVideoTrack;
 
     if (track->mSource == NULL) {
@@ -1897,11 +1916,31 @@
     return status;
 }
 
+status_t NuPlayer::GenericSource::releaseDrm()
+{
+    ALOGV("releaseDrm");
+
+    sp<AMessage> msg = new AMessage(kWhatReleaseDrm, this);
+
+    // synchronous call to update the source states before the player proceedes with crypto cleanup
+    sp<AMessage> response;
+    status_t status = msg->postAndAwaitResponse(&response);
+
+    if (status == OK && response != NULL) {
+        ALOGD("releaseDrm ret: OK ");
+    } else {
+        ALOGE("releaseDrm err: %d", status);
+    }
+
+    return status;
+}
+
 status_t NuPlayer::GenericSource::onPrepareDrm(const sp<AMessage> &msg)
 {
     ALOGV("onPrepareDrm ");
 
     mIsDrmProtected = false;
+    mIsDrmReleased = false;
     mIsSecure = false;
 
     uint8_t *uuid;
@@ -1949,8 +1988,26 @@
     return status;
 }
 
+status_t NuPlayer::GenericSource::onReleaseDrm()
+{
+    if (mIsDrmProtected) {
+        mIsDrmProtected = false;
+        // to prevent returning any more buffer after stop/releaseDrm (b/37960096)
+        mIsDrmReleased = true;
+        ALOGV("onReleaseDrm: mIsDrmProtected is reset.");
+    } else {
+        ALOGE("onReleaseDrm: mIsDrmProtected is already false.");
+    }
+
+    return OK;
+}
+
 status_t NuPlayer::GenericSource::checkDrmInfo()
 {
+    // clearing the flag at prepare in case the player is reused after stop/releaseDrm with the
+    // same source without being reset (called by prepareAsync/initFromDataSource)
+    mIsDrmReleased = false;
+
     if (mFileMeta == NULL) {
         ALOGI("checkDrmInfo: No metadata");
         return OK; // letting the caller responds accordingly
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.h b/media/libmediaplayerservice/nuplayer/GenericSource.h
index 64f21a6..b0c6695 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.h
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.h
@@ -91,6 +91,8 @@
     virtual status_t prepareDrm(
             const uint8_t uuid[16], const Vector<uint8_t> &drmSessionId, sp<ICrypto> *crypto);
 
+    virtual status_t releaseDrm();
+
 
 protected:
     virtual ~GenericSource();
@@ -119,6 +121,7 @@
         kWhatSecureDecodersInstantiated,
         // Modular DRM
         kWhatPrepareDrm,
+        kWhatReleaseDrm,
     };
 
     struct Track {
@@ -308,10 +311,12 @@
 
     // Modular DRM
     bool mIsDrmProtected;
+    bool mIsDrmReleased;
     Vector<String8> mMimes;
 
     status_t checkDrmInfo();
     status_t onPrepareDrm(const sp<AMessage> &msg);
+    status_t onReleaseDrm();
 
     DISALLOW_EVIL_CONSTRUCTORS(GenericSource);
 };
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index 0d4c730..6ded392 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -2787,6 +2787,11 @@
 
     status_t status;
     if (mCrypto != NULL) {
+        // notifying the source first before removing crypto from codec
+        if (mSource != NULL) {
+            mSource->releaseDrm();
+        }
+
         status=OK;
         // first making sure the codecs have released their crypto reference
         const sp<DecoderBase> &videoDecoder = getDecoder(false/*audio*/);
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index f2a4d06..0bb4dbb 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -469,6 +469,22 @@
         const char *mime;
         CHECK(track->meta->findCString(kKeyMIMEType, &mime));
         if (!strncasecmp("video/", mime, 6)) {
+            // MPEG2 tracks do not provide CSD, so read the stream header
+            if (!strcmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG2)) {
+                off64_t offset;
+                size_t size;
+                if (track->sampleTable->getMetaDataForSample(
+                            0 /* sampleIndex */, &offset, &size, NULL /* sampleTime */) == OK) {
+                    if (size > kMaxTrackHeaderSize) {
+                        size = kMaxTrackHeaderSize;
+                    }
+                    uint8_t header[kMaxTrackHeaderSize];
+                    if (mDataSource->readAt(offset, &header, size) == (ssize_t)size) {
+                        track->meta->setData(kKeyStreamHeader, 'mdat', header, size);
+                    }
+                }
+            }
+
             if (mMoofOffset > 0) {
                 int64_t duration;
                 if (track->meta->findInt64(kKeyDuration, &duration)) {
@@ -489,22 +505,6 @@
                             ((int64_t)sampleTime * 1000000) / track->timescale);
                 }
             }
-
-            // MPEG2 tracks do not provide CSD, so read the stream header
-            if (!strcmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG2)) {
-                off64_t offset;
-                size_t size;
-                if (track->sampleTable->getMetaDataForSample(
-                            0 /* sampleIndex */, &offset, &size, NULL /* sampleTime */) == OK) {
-                    if (size > kMaxTrackHeaderSize) {
-                        size = kMaxTrackHeaderSize;
-                    }
-                    uint8_t header[kMaxTrackHeaderSize];
-                    if (mDataSource->readAt(offset, &header, size) == (ssize_t)size) {
-                        track->meta->setData(kKeyStreamHeader, 'mdat', header, size);
-                    }
-                }
-            }
         }
     }
 
@@ -1240,6 +1240,7 @@
             ALOGV("allocated pssh @ %p", pssh.data);
             ssize_t requested = (ssize_t) pssh.datalen;
             if (mDataSource->readAt(data_offset + 24, pssh.data, requested) < requested) {
+                delete[] pssh.data;
                 return ERROR_IO;
             }
             mPssh.push_back(pssh);
diff --git a/media/libstagefright/OggExtractor.cpp b/media/libstagefright/OggExtractor.cpp
index 0343786..e31c37c 100644
--- a/media/libstagefright/OggExtractor.cpp
+++ b/media/libstagefright/OggExtractor.cpp
@@ -697,7 +697,21 @@
             if (buffer != NULL) {
                 fullSize += buffer->range_length();
             }
-            MediaBuffer *tmp = new MediaBuffer(fullSize);
+            if (fullSize > 16 * 1024 * 1024) { // arbitrary limit of 16 MB packet size
+                if (buffer != NULL) {
+                    buffer->release();
+                }
+                ALOGE("b/36592202");
+                return ERROR_MALFORMED;
+            }
+            MediaBuffer *tmp = new (std::nothrow) MediaBuffer(fullSize);
+            if (tmp == NULL) {
+                if (buffer != NULL) {
+                    buffer->release();
+                }
+                ALOGE("b/36592202");
+                return ERROR_MALFORMED;
+            }
             if (buffer != NULL) {
                 memcpy(tmp->data(), buffer->data(), buffer->range_length());
                 tmp->set_range(0, buffer->range_length());
diff --git a/media/mtp/IMtpHandle.h b/media/mtp/IMtpHandle.h
index 9185255..0557596 100644
--- a/media/mtp/IMtpHandle.h
+++ b/media/mtp/IMtpHandle.h
@@ -27,7 +27,7 @@
     virtual int write(const void *data, int len) = 0;
 
     // Return 0 if send/receive is successful, or -1 and errno is set
-    virtual int receiveFile(mtp_file_range mfr) = 0;
+    virtual int receiveFile(mtp_file_range mfr, bool zero_packet) = 0;
     virtual int sendFile(mtp_file_range mfr) = 0;
     virtual int sendEvent(mtp_event me) = 0;
 
diff --git a/media/mtp/MtpDevHandle.cpp b/media/mtp/MtpDevHandle.cpp
index afc0525..9aa0aec 100644
--- a/media/mtp/MtpDevHandle.cpp
+++ b/media/mtp/MtpDevHandle.cpp
@@ -45,7 +45,7 @@
     int read(void *data, int len);
     int write(const void *data, int len);
 
-    int receiveFile(mtp_file_range mfr);
+    int receiveFile(mtp_file_range mfr, bool);
     int sendFile(mtp_file_range mfr);
     int sendEvent(mtp_event me);
 
@@ -68,7 +68,7 @@
     return ::write(mFd, data, len);
 }
 
-int MtpDevHandle::receiveFile(mtp_file_range mfr) {
+int MtpDevHandle::receiveFile(mtp_file_range mfr, bool) {
     return ioctl(mFd, MTP_RECEIVE_FILE, reinterpret_cast<unsigned long>(&mfr));
 }
 
diff --git a/media/mtp/MtpFfsHandle.cpp b/media/mtp/MtpFfsHandle.cpp
index c78002c..c50af2f 100644
--- a/media/mtp/MtpFfsHandle.cpp
+++ b/media/mtp/MtpFfsHandle.cpp
@@ -516,7 +516,7 @@
 }
 
 /* Read from USB and write to a local file. */
-int MtpFfsHandle::receiveFile(mtp_file_range mfr) {
+int MtpFfsHandle::receiveFile(mtp_file_range mfr, bool zero_packet) {
     // When receiving files, the incoming length is given in 32 bits.
     // A >4G file is given as 0xFFFFFFFF
     uint32_t file_length = mfr.length;
@@ -538,7 +538,7 @@
     aio.aio_fildes = mfr.fd;
     aio.aio_buf = nullptr;
     struct aiocb *aiol[] = {&aio};
-    int ret;
+    int ret = -1;
     size_t length;
     bool read = false;
     bool write = false;
@@ -590,11 +590,6 @@
             } else {
                 // Receive an empty packet if size is a multiple of the endpoint size.
                 file_length -= ret;
-                if (file_length == 0 && ret % packet_size == 0) {
-                    if (TEMP_FAILURE_RETRY(::read(mBulkOut, data, packet_size)) != 0) {
-                        return -1;
-                    }
-                }
             }
             // Enqueue a new write request
             aio.aio_buf = data;
@@ -610,6 +605,11 @@
             read = false;
         }
     }
+    if (ret % packet_size == 0 || zero_packet) {
+        if (TEMP_FAILURE_RETRY(::read(mBulkOut, data, packet_size)) != 0) {
+            return -1;
+        }
+    }
     return 0;
 }
 
@@ -660,10 +660,9 @@
                     sizeof(mtp_data_header), init_read_len, offset))
             != init_read_len) return -1;
     if (writeHandle(mBulkIn, data, sizeof(mtp_data_header) + init_read_len) == -1) return -1;
-    if (file_length == static_cast<unsigned>(init_read_len)) return 0;
     file_length -= init_read_len;
     offset += init_read_len;
-    ret = 0;
+    ret = init_read_len + sizeof(mtp_data_header);
 
     // Break down the file into pieces that fit in buffers
     while(file_length > 0) {
diff --git a/media/mtp/MtpFfsHandle.h b/media/mtp/MtpFfsHandle.h
index 7491a1b..98669ff 100644
--- a/media/mtp/MtpFfsHandle.h
+++ b/media/mtp/MtpFfsHandle.h
@@ -55,7 +55,7 @@
     int read(void *data, int len);
     int write(const void *data, int len);
 
-    int receiveFile(mtp_file_range mfr);
+    int receiveFile(mtp_file_range mfr, bool zero_packet);
     int sendFile(mtp_file_range mfr);
     int sendEvent(mtp_event me);
 
diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp
index 88dabff..5c33265 100644
--- a/media/mtp/MtpServer.cpp
+++ b/media/mtp/MtpServer.cpp
@@ -1052,23 +1052,22 @@
         ALOGE("failed to write initial data");
         result = MTP_RESPONSE_GENERAL_ERROR;
     } else {
-        if (mSendObjectFileSize - initialData > 0) {
-            mfr.offset = initialData;
-            if (mSendObjectFileSize == 0xFFFFFFFF) {
-                // tell driver to read until it receives a short packet
-                mfr.length = 0xFFFFFFFF;
-            } else {
-                mfr.length = mSendObjectFileSize - initialData;
-            }
+        mfr.offset = initialData;
+        if (mSendObjectFileSize == 0xFFFFFFFF) {
+            // tell driver to read until it receives a short packet
+            mfr.length = 0xFFFFFFFF;
+        } else {
+            mfr.length = mSendObjectFileSize - initialData;
+        }
 
-            mfr.command = 0;
-            mfr.transaction_id = 0;
+        mfr.command = 0;
+        mfr.transaction_id = 0;
 
-            // transfer the file
-            ret = sHandle->receiveFile(mfr);
-            if ((ret < 0) && (errno == ECANCELED)) {
-                isCanceled = true;
-            }
+        // transfer the file
+        ret = sHandle->receiveFile(mfr, mfr.length == 0 &&
+                initialData == MTP_BUFFER_SIZE - MTP_CONTAINER_HEADER_SIZE);
+        if ((ret < 0) && (errno == ECANCELED)) {
+            isCanceled = true;
         }
     }
     struct stat sstat;
@@ -1256,19 +1255,18 @@
     if (ret < 0) {
         ALOGE("failed to write initial data");
     } else {
-        if (length > 0) {
-            mtp_file_range  mfr;
-            mfr.fd = edit->mFD;
-            mfr.offset = offset;
-            mfr.length = length;
-            mfr.command = 0;
-            mfr.transaction_id = 0;
+        mtp_file_range  mfr;
+        mfr.fd = edit->mFD;
+        mfr.offset = offset;
+        mfr.length = length;
+        mfr.command = 0;
+        mfr.transaction_id = 0;
 
-            // transfer the file
-            ret = sHandle->receiveFile(mfr);
-            if ((ret < 0) && (errno == ECANCELED)) {
-                isCanceled = true;
-            }
+        // transfer the file
+        ret = sHandle->receiveFile(mfr, mfr.length == 0 &&
+                initialData == MTP_BUFFER_SIZE - MTP_CONTAINER_HEADER_SIZE);
+        if ((ret < 0) && (errno == ECANCELED)) {
+            isCanceled = true;
         }
     }
     if (ret < 0) {
diff --git a/media/mtp/tests/MtpFfsHandle_test.cpp b/media/mtp/tests/MtpFfsHandle_test.cpp
index e575148..554f867 100644
--- a/media/mtp/tests/MtpFfsHandle_test.cpp
+++ b/media/mtp/tests/MtpFfsHandle_test.cpp
@@ -116,7 +116,7 @@
         ss << dummyDataStr;
 
     EXPECT_EQ(write(bulk_out, ss.str().c_str(), size), size);
-    EXPECT_EQ(handle->receiveFile(mfr), 0);
+    EXPECT_EQ(handle->receiveFile(mfr, false), 0);
 
     EXPECT_EQ(read(dummy_file.fd, buf, size), size);
 
@@ -136,7 +136,7 @@
         ss << dummyDataStr;
 
     EXPECT_EQ(write(bulk_out, ss.str().c_str(), size), size);
-    EXPECT_EQ(handle->receiveFile(mfr), 0);
+    EXPECT_EQ(handle->receiveFile(mfr, false), 0);
 
     EXPECT_EQ(read(dummy_file.fd, buf, size), size);
 
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp
index 65633db..b9d6843 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.cpp
+++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp
@@ -87,11 +87,7 @@
     int count = 0;
     for (auto& provider : mProviders) {
         if (kStandardProviderTypes.find(provider->getType()) != std::string::npos) {
-            for (auto& device : provider->mDevices) {
-                if (device->isAPI1Compatible()) {
-                    count++;
-                }
-            }
+            count += provider->mUniqueAPI1CompatibleCameraIds.size();
         }
     }
     return count;
@@ -113,10 +109,8 @@
     std::vector<std::string> deviceIds;
     for (auto& provider : mProviders) {
         if (kStandardProviderTypes.find(provider->getType()) != std::string::npos) {
-            for (auto& device : provider->mDevices) {
-                if (device->isAPI1Compatible()) {
-                    deviceIds.push_back(device->mId);
-                }
+            for (auto& id : provider->mUniqueAPI1CompatibleCameraIds) {
+                deviceIds.push_back(id);
             }
         }
     }
@@ -466,6 +460,7 @@
         mProviderName(providerName),
         mInterface(interface),
         mProviderTagid(generateVendorTagId(providerName)),
+        mUniqueDeviceCount(0),
         mManager(manager) {
     (void) mManager;
 }
@@ -539,6 +534,9 @@
 
     for (auto& device : mDevices) {
         mUniqueCameraIds.insert(device->mId);
+        if (device->isAPI1Compatible()) {
+            mUniqueAPI1CompatibleCameraIds.insert(device->mId);
+        }
     }
     mUniqueDeviceCount = mUniqueCameraIds.size();
 
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.h b/services/camera/libcameraservice/common/CameraProviderManager.h
index 9ba7f91..e82282f 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.h
+++ b/services/camera/libcameraservice/common/CameraProviderManager.h
@@ -315,6 +315,7 @@
         std::vector<std::unique_ptr<DeviceInfo>> mDevices;
         std::set<std::string> mUniqueCameraIds;
         int mUniqueDeviceCount;
+        std::set<std::string> mUniqueAPI1CompatibleCameraIds;
 
         // HALv1-specific camera fields, including the actual device interface
         struct DeviceInfo1 : public DeviceInfo {
diff --git a/services/oboeservice/AAudioServiceEndpoint.cpp b/services/oboeservice/AAudioServiceEndpoint.cpp
index d3e182a..a2e6d33 100644
--- a/services/oboeservice/AAudioServiceEndpoint.cpp
+++ b/services/oboeservice/AAudioServiceEndpoint.cpp
@@ -44,10 +44,6 @@
 // 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)
@@ -71,7 +67,13 @@
     if (result == AAUDIO_OK) {
         mMixer.allocate(mStreamInternal.getSamplesPerFrame(), mStreamInternal.getFramesPerBurst());
 
-        int32_t desiredBufferSize = BUFFER_SIZE_IN_BURSTS * mStreamInternal.getFramesPerBurst();
+        int32_t burstsPerBuffer = AAudioProperty_getMixerBursts();
+        if (burstsPerBuffer == 0) {
+            mLatencyTuningEnabled = true;
+            burstsPerBuffer = 2;
+        }
+        ALOGD("AAudioServiceEndpoint(): burstsPerBuffer = %d", burstsPerBuffer);
+        int32_t desiredBufferSize = burstsPerBuffer * mStreamInternal.getFramesPerBurst();
         mStreamInternal.setBufferSize(desiredBufferSize);
     }
     return result;
@@ -117,7 +119,6 @@
 
 static void *aaudio_mixer_thread_proc(void *context) {
     AAudioServiceEndpoint *stream = (AAudioServiceEndpoint *) context;
-    //LOGD("AudioStreamAAudio(): oboe_callback_thread, stream = %p", stream);
     if (stream != NULL) {
         return stream->callbackLoop();
     } else {
diff --git a/services/oboeservice/AAudioServiceEndpoint.h b/services/oboeservice/AAudioServiceEndpoint.h
index a4ceae6..d0c2f53 100644
--- a/services/oboeservice/AAudioServiceEndpoint.h
+++ b/services/oboeservice/AAudioServiceEndpoint.h
@@ -77,6 +77,7 @@
 
     std::atomic<bool>        mCallbackEnabled;
     int32_t                  mReferenceCount = 0;
+    bool                     mLatencyTuningEnabled = false; // TODO implement tuning
 
     std::mutex               mLockStreams;
     std::vector<AAudioServiceStreamShared *> mRegisteredStreams;
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.cpp b/services/oboeservice/AAudioServiceStreamMMAP.cpp
index cadc2a4..78a1583 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.cpp
+++ b/services/oboeservice/AAudioServiceStreamMMAP.cpp
@@ -178,6 +178,21 @@
     mAudioFormat = AAudioConvert_androidToAAudioDataFormat(config.format);
     mSampleRate = config.sample_rate;
 
+    // Scale up the burst size to meet the minimum equivalent in microseconds.
+    // This is to avoid waking the CPU too often when the HW burst is very small
+    // or at high sample rates.
+    int32_t burstMinMicros = AAudioProperty_getHardwareBurstMinMicros();
+    int32_t burstMicros = 0;
+    do {
+        if (burstMicros > 0) {  // skip first loop
+            mFramesPerBurst *= 2;
+        }
+        burstMicros = mFramesPerBurst * static_cast<int64_t>(1000000) / mSampleRate;
+    } while (burstMicros < burstMinMicros);
+
+    ALOGD("AAudioServiceStreamMMAP::open() original burst = %d, minMicros = %d, final burst = %d\n",
+          mMmapBufferinfo.burst_size_frames, burstMinMicros, mFramesPerBurst);
+
     ALOGD("AAudioServiceStreamMMAP::open() got devId = %d, sRate = %d",
           deviceId, config.sample_rate);