Merge "NuPlayer: make MediaClock a player-wide resource."
diff --git a/CleanSpec.mk b/CleanSpec.mk
index f3946f0..793cbf4 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -79,6 +79,8 @@
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/soundfx/libaudiopreprocessing.so)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libmediacodecservice.so)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libstagefright_xmlparser@1.0.so)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/libstagefright_soft_*)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib/vndk/libstagefright_soft_*)
 
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
diff --git a/drm/mediadrm/plugins/clearkey/InitDataParser.cpp b/drm/mediadrm/plugins/clearkey/InitDataParser.cpp
index 6a4f8d5..caff393 100644
--- a/drm/mediadrm/plugins/clearkey/InitDataParser.cpp
+++ b/drm/mediadrm/plugins/clearkey/InitDataParser.cpp
@@ -136,7 +136,7 @@
     AString encodedId;
     for (size_t i = 0; i < keyIds.size(); ++i) {
         encodedId.clear();
-        android::encodeBase64(keyIds[i], kKeyIdSize, &encodedId);
+        android::encodeBase64Url(keyIds[i], kKeyIdSize, &encodedId);
         if (i != 0) {
             request.append(",");
         }
diff --git a/drm/mediadrm/plugins/clearkey/tests/InitDataParserUnittest.cpp b/drm/mediadrm/plugins/clearkey/tests/InitDataParserUnittest.cpp
index 84ed242..8c49656 100644
--- a/drm/mediadrm/plugins/clearkey/tests/InitDataParserUnittest.cpp
+++ b/drm/mediadrm/plugins/clearkey/tests/InitDataParserUnittest.cpp
@@ -59,7 +59,7 @@
                   (size_t)requestString.find(kRequestSuffix));
         for (size_t i = 0; i < expectedKeys.size(); ++i) {
             AString encodedIdAString;
-            android::encodeBase64(expectedKeys[i], kKeyIdSize,
+            android::encodeBase64Url(expectedKeys[i], kKeyIdSize,
                                   &encodedIdAString);
             String8 encodedId(encodedIdAString.c_str());
             encodedId.removeAll(kBase64Padding);
@@ -231,5 +231,4 @@
 
     attemptParseExpectingFailure(initData, kCencMimeType);
 }
-
 }  // namespace clearkeydrm
diff --git a/drm/mediadrm/plugins/clearkey/tests/JsonWebKeyUnittest.cpp b/drm/mediadrm/plugins/clearkey/tests/JsonWebKeyUnittest.cpp
index c3b0d84..d9f3ea6 100644
--- a/drm/mediadrm/plugins/clearkey/tests/JsonWebKeyUnittest.cpp
+++ b/drm/mediadrm/plugins/clearkey/tests/JsonWebKeyUnittest.cpp
@@ -284,14 +284,14 @@
                 "\"keys\":"
                     "[{"
                         "\"kid\":\"Y2xlYXJrZXlrZXlpZDAx\""
-                        "\"k\":\"SGVsbG8gRnJpZW5kISE\""
+                        "\"k\":\"SGVsbG8gRnJpZW5kICE-Pw\""
                         "\"kty\":\"oct\""
                         "\"alg\":\"A128KW1\""
                     "}"
                     "{"
                         "\"kty\":\"oct\""
                         "\"alg\":\"A128KW2\""
-                        "\"k\":\"SGVsbG8gRnJpZW5kIQ\""
+                        "\"k\":\"SGVsbG8gRnJpZW5kICE_\""
                         "\"kid\":\"Y2xlYXJrZXlrZXlpZDAy\""
                     "}"
                     "{"
@@ -303,7 +303,7 @@
                     "{"
                         "\"alg\":\"A128KW3\""
                         "\"kid\":\"Y2xlYXJrZXlrZXlpZDAz\""
-                        "\"k\":\"R29vZCBkYXkh\""
+                        "\"k\":\"SGVsbG8gPz4-IEZyaWVuZCA_Pg\""
                         "\"kty\":\"oct\""
                     "}]"
             "}");
@@ -313,8 +313,8 @@
     EXPECT_TRUE(keys.size() == 3);
 
     const String8 clearKeys[] =
-            { String8("Hello Friend!!"), String8("Hello Friend!"),
-              String8("Good day!") };
+            { String8("Hello Friend !>?"), String8("Hello Friend !?"),
+              String8("Hello ?>> Friend ?>") };
     verifyKeys(keys, clearKeys);
 }
 
diff --git a/media/libaaudio/examples/input_monitor/src/input_monitor.cpp b/media/libaaudio/examples/input_monitor/src/input_monitor.cpp
index 2dfd0a7..9feb118 100644
--- a/media/libaaudio/examples/input_monitor/src/input_monitor.cpp
+++ b/media/libaaudio/examples/input_monitor/src/input_monitor.cpp
@@ -61,7 +61,7 @@
     // in a buffer if we hang or crash.
     setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
 
-    printf("%s - Monitor input level using AAudio V0.1.1\n", argv[0]);
+    printf("%s - Monitor input level using AAudio read, V0.1.2\n", argv[0]);
 
     argParser.setFormat(REQUIRED_FORMAT);
     if (argParser.parseArgs(argc, argv)) {
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 9de2eb0..893795b 100644
--- a/media/libaaudio/examples/input_monitor/src/input_monitor_callback.cpp
+++ b/media/libaaudio/examples/input_monitor/src/input_monitor_callback.cpp
@@ -41,7 +41,7 @@
     // Make printf print immediately so that debug info is not stuck
     // in a buffer if we hang or crash.
     setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
-    printf("%s - Display audio input using an AAudio callback\n", argv[0]);
+    printf("%s - Display audio input using an AAudio callback, V0.1.2\n", argv[0]);
 
     result = recorder.open(2, 48000, AAUDIO_FORMAT_PCM_I16,
                        SimpleRecorderDataCallbackProc, SimpleRecorderErrorCallbackProc, &myData);
diff --git a/media/libaaudio/examples/utils/AAudioArgsParser.h b/media/libaaudio/examples/utils/AAudioArgsParser.h
index 30c3ccd..ada37e2 100644
--- a/media/libaaudio/examples/utils/AAudioArgsParser.h
+++ b/media/libaaudio/examples/utils/AAudioArgsParser.h
@@ -150,6 +150,9 @@
                     setChannelCount(atoi(&arg[2]));
                     break;
                 case 'd':
+                    setDeviceId(atoi(&arg[2]));
+                    break;
+                case 's':
                     mDurationSeconds = atoi(&arg[2]);
                     break;
                 case 'm': {
@@ -202,7 +205,8 @@
         printf("      Default values are UNSPECIFIED unless otherwise stated.\n");
         printf("      -b{bufferCapacity} frames\n");
         printf("      -c{channels} for example 2 for stereo\n");
-        printf("      -d{duration} in seconds, default is %d\n", DEFAULT_DURATION_SECONDS);
+        printf("      -d{deviceId} default is %d\n", AAUDIO_UNSPECIFIED);
+        printf("      -s{duration} in seconds, default is %d\n", DEFAULT_DURATION_SECONDS);
         printf("      -m{0|1|2|3} set MMAP policy\n");
         printf("          0 = _UNSPECIFIED, default\n");
         printf("          1 = _NEVER\n");
diff --git a/media/libaaudio/examples/write_sine/src/write_sine.cpp b/media/libaaudio/examples/write_sine/src/write_sine.cpp
index 87fb40b..677fb6c 100644
--- a/media/libaaudio/examples/write_sine/src/write_sine.cpp
+++ b/media/libaaudio/examples/write_sine/src/write_sine.cpp
@@ -57,7 +57,7 @@
     // in a buffer if we hang or crash.
     setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
 
-    printf("%s - Play a sine wave using AAudio V0.1.1\n", argv[0]);
+    printf("%s - Play a sine wave using AAudio V0.1.2\n", argv[0]);
 
     if (argParser.parseArgs(argc, argv)) {
         return EXIT_FAILURE;
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 b5602e9..071ca87 100644
--- a/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
+++ b/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
@@ -40,7 +40,7 @@
     // in a buffer if we hang or crash.
     setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
 
-    printf("%s - Play a sine sweep using an AAudio callback V0.1.2\n", argv[0]);
+    printf("%s - Play a sine sweep using an AAudio callback V0.1.3\n", argv[0]);
 
     myData.schedulerChecked = false;
     myData.forceUnderruns = false; // set true to test AAudioStream_getXRunCount()
diff --git a/media/libaaudio/src/binding/AAudioBinderClient.cpp b/media/libaaudio/src/binding/AAudioBinderClient.cpp
index a268e49..07ee2de 100644
--- a/media/libaaudio/src/binding/AAudioBinderClient.cpp
+++ b/media/libaaudio/src/binding/AAudioBinderClient.cpp
@@ -45,6 +45,7 @@
 using android::IInterface;
 using android::IAAudioService;
 using android::Mutex;
+using android::ProcessState;
 using android::sp;
 using android::wp;
 
@@ -52,15 +53,19 @@
 
 ANDROID_SINGLETON_STATIC_INSTANCE(AAudioBinderClient);
 
+// If we don't keep a strong pointer here then this singleton can get deleted!
+android::sp<AAudioBinderClient> gKeepBinderClient;
+
 AAudioBinderClient::AAudioBinderClient()
         : AAudioServiceInterface()
         , Singleton<AAudioBinderClient>() {
-
+    gKeepBinderClient = this; // so this singleton won't get deleted
     mAAudioClient = new AAudioClient(this);
-    ALOGV("AAudioBinderClient() created mAAudioClient = %p", mAAudioClient.get());
+    ALOGV("AAudioBinderClient() this = %p, created mAAudioClient = %p", this, mAAudioClient.get());
 }
 
 AAudioBinderClient::~AAudioBinderClient() {
+    ALOGV("AAudioBinderClient()::~AAudioBinderClient() destroying %p", this);
     Mutex::Autolock _l(mServiceLock);
     if (mAAudioService != 0) {
         IInterface::asBinder(mAAudioService)->unlinkToDeath(mAAudioClient);
@@ -75,19 +80,19 @@
     bool needToRegister = false;
     {
         Mutex::Autolock _l(mServiceLock);
-        if (mAAudioService == 0) {
+        if (mAAudioService.get() == nullptr) {
             sp<IBinder> binder;
             sp<IServiceManager> sm = defaultServiceManager();
             // Try several times to get the service.
             int retries = 4;
             do {
                 binder = sm->getService(String16(AAUDIO_SERVICE_NAME)); // This will wait a while.
-                if (binder != 0) {
+                if (binder.get() != nullptr) {
                     break;
                 }
             } while (retries-- > 0);
 
-            if (binder != 0) {
+            if (binder.get() != nullptr) {
                 // Ask for notification if the service dies.
                 status_t status = binder->linkToDeath(mAAudioClient);
                 // TODO review what we should do if this fails
@@ -98,7 +103,7 @@
                 mAAudioService = interface_cast<IAAudioService>(binder);
                 needToRegister = true;
                 // Make sure callbacks can be received by mAAudioClient
-                android::ProcessState::self()->startThreadPool();
+                ProcessState::self()->startThreadPool();
             } else {
                 ALOGE("AAudioBinderClient could not connect to %s", AAUDIO_SERVICE_NAME);
             }
@@ -106,7 +111,7 @@
         aaudioService = mAAudioService;
     }
     // Do this outside the mutex lock.
-    if (needToRegister && aaudioService != 0) { // new client?
+    if (needToRegister && aaudioService.get() != nullptr) { // new client?
         aaudioService->registerClient(mAAudioClient);
     }
     return aaudioService;
@@ -117,7 +122,6 @@
     mAAudioService.clear(); // force a reconnect
 }
 
-
 /**
 * @param request info needed to create the stream
 * @param configuration contains information about the created stream
@@ -128,14 +132,12 @@
     aaudio_handle_t stream;
     for (int i = 0; i < 2; i++) {
         const sp<IAAudioService> &service = getAAudioService();
-        if (service == 0) {
-            return AAUDIO_ERROR_NO_SERVICE;
-        }
+        if (service.get() == nullptr) return AAUDIO_ERROR_NO_SERVICE;
 
         stream = service->openStream(request, configurationOutput);
 
         if (stream == AAUDIO_ERROR_NO_SERVICE) {
-            ALOGE("AAudioBinderClient: lost connection to AAudioService.");
+            ALOGE("AAudioBinderClient::openStream lost connection to AAudioService.");
             dropAAudioService(); // force a reconnect
         } else {
             break;
@@ -145,8 +147,8 @@
 }
 
 aaudio_result_t AAudioBinderClient::closeStream(aaudio_handle_t streamHandle) {
-    const sp<IAAudioService> &service = getAAudioService();
-    if (service == 0) return AAUDIO_ERROR_NO_SERVICE;
+    const sp<IAAudioService> service = getAAudioService();
+    if (service.get() == nullptr) return AAUDIO_ERROR_NO_SERVICE;
     return service->closeStream(streamHandle);
 }
 
@@ -155,32 +157,32 @@
 */
 aaudio_result_t AAudioBinderClient::getStreamDescription(aaudio_handle_t streamHandle,
                                                          AudioEndpointParcelable &parcelable) {
-    const sp<IAAudioService> &service = getAAudioService();
-    if (service == 0) return AAUDIO_ERROR_NO_SERVICE;
+    const sp<IAAudioService> service = getAAudioService();
+    if (service.get() == nullptr) return AAUDIO_ERROR_NO_SERVICE;
     return service->getStreamDescription(streamHandle, parcelable);
 }
 
 aaudio_result_t AAudioBinderClient::startStream(aaudio_handle_t streamHandle) {
-    const sp<IAAudioService> &service = getAAudioService();
-    if (service == 0) return AAUDIO_ERROR_NO_SERVICE;
+    const sp<IAAudioService> service = getAAudioService();
+    if (service.get() == nullptr) return AAUDIO_ERROR_NO_SERVICE;
     return service->startStream(streamHandle);
 }
 
 aaudio_result_t AAudioBinderClient::pauseStream(aaudio_handle_t streamHandle) {
-    const sp<IAAudioService> &service = getAAudioService();
-    if (service == 0) return AAUDIO_ERROR_NO_SERVICE;
+    const sp<IAAudioService> service = getAAudioService();
+    if (service.get() == nullptr) return AAUDIO_ERROR_NO_SERVICE;
     return service->pauseStream(streamHandle);
 }
 
 aaudio_result_t AAudioBinderClient::stopStream(aaudio_handle_t streamHandle) {
-    const sp<IAAudioService> &service = getAAudioService();
-    if (service == 0) return AAUDIO_ERROR_NO_SERVICE;
+    const sp<IAAudioService> service = getAAudioService();
+    if (service.get() == nullptr) 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;
+    const sp<IAAudioService> service = getAAudioService();
+    if (service.get() == nullptr) return AAUDIO_ERROR_NO_SERVICE;
     return service->flushStream(streamHandle);
 }
 
@@ -190,8 +192,8 @@
 aaudio_result_t AAudioBinderClient::registerAudioThread(aaudio_handle_t streamHandle,
                                                         pid_t clientThreadId,
                                                         int64_t periodNanoseconds) {
-    const sp<IAAudioService> &service = getAAudioService();
-    if (service == 0) return AAUDIO_ERROR_NO_SERVICE;
+    const sp<IAAudioService> service = getAAudioService();
+    if (service.get() == nullptr) return AAUDIO_ERROR_NO_SERVICE;
     return service->registerAudioThread(streamHandle,
                                         clientThreadId,
                                         periodNanoseconds);
@@ -199,8 +201,8 @@
 
 aaudio_result_t AAudioBinderClient::unregisterAudioThread(aaudio_handle_t streamHandle,
                                                           pid_t clientThreadId) {
-    const sp<IAAudioService> &service = getAAudioService();
-    if (service == 0) return AAUDIO_ERROR_NO_SERVICE;
+    const sp<IAAudioService> service = getAAudioService();
+    if (service.get() == nullptr) return AAUDIO_ERROR_NO_SERVICE;
     return service->unregisterAudioThread(streamHandle,
                                           clientThreadId);
 }
diff --git a/media/libaaudio/src/binding/AAudioBinderClient.h b/media/libaaudio/src/binding/AAudioBinderClient.h
index 89ae85c..f9da8b4 100644
--- a/media/libaaudio/src/binding/AAudioBinderClient.h
+++ b/media/libaaudio/src/binding/AAudioBinderClient.h
@@ -118,13 +118,13 @@
     {
     public:
         AAudioClient(android::wp<AAudioBinderClient> aaudioBinderClient)
-            : mBinderClient(aaudioBinderClient) {
+                : mBinderClient(aaudioBinderClient) {
         }
 
         // implement DeathRecipient
         virtual void binderDied(const android::wp<android::IBinder>& who __unused) {
             android::sp<AAudioBinderClient> client = mBinderClient.promote();
-            if (client != 0) {
+            if (client.get() != nullptr) {
                 client->dropAAudioService();
             }
             ALOGW("AAudio service binderDied()!");
@@ -133,7 +133,7 @@
         // implement BnAAudioClient
         void onStreamChange(aaudio_handle_t handle, int32_t opcode, int32_t value) {
             android::sp<AAudioBinderClient> client = mBinderClient.promote();
-            if (client != 0) {
+            if (client.get() != nullptr) {
                 client->onStreamChange(handle, opcode, value);
             }
         }
@@ -141,10 +141,11 @@
         android::wp<AAudioBinderClient> mBinderClient;
     };
 
+private:
 
-    android::Mutex               mServiceLock;
+    android::Mutex                  mServiceLock;
     android::sp<android::IAAudioService>  mAAudioService;
-    android::sp<AAudioClient>    mAAudioClient;
+    android::sp<AAudioClient>       mAAudioClient;
 
 };
 
diff --git a/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp b/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp
index e763934..153fce3 100644
--- a/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp
+++ b/media/libaaudio/src/binding/AAudioStreamConfiguration.cpp
@@ -46,6 +46,8 @@
     if (status != NO_ERROR) goto error;
     status = parcel->writeInt32((int32_t) getFormat());
     if (status != NO_ERROR) goto error;
+    status = parcel->writeInt32((int32_t) getDirection());
+    if (status != NO_ERROR) goto error;
     status = parcel->writeInt32(getBufferCapacity());
     if (status != NO_ERROR) goto error;
     return NO_ERROR;
@@ -73,9 +75,12 @@
     setFormat(value);
     status = parcel->readInt32(&value);
     if (status != NO_ERROR) goto error;
+    setDirection((aaudio_direction_t) value);
+    status = parcel->readInt32(&value);
+    if (status != NO_ERROR) goto error;
     setBufferCapacity(value);
     return NO_ERROR;
 error:
     ALOGE("AAudioStreamConfiguration.readFromParcel(): read failed = %d", status);
     return status;
-}
\ No newline at end of file
+}
diff --git a/media/libaaudio/src/binding/AAudioStreamRequest.cpp b/media/libaaudio/src/binding/AAudioStreamRequest.cpp
index abdcf5b..1200ab2 100644
--- a/media/libaaudio/src/binding/AAudioStreamRequest.cpp
+++ b/media/libaaudio/src/binding/AAudioStreamRequest.cpp
@@ -46,9 +46,6 @@
     status_t status = parcel->writeInt32((int32_t) mUserId);
     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;
 
@@ -71,10 +68,6 @@
     if (status != NO_ERROR) goto error;
     mUserId = (uid_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;
 
@@ -98,7 +91,6 @@
 void AAudioStreamRequest::dump() const {
     ALOGD("AAudioStreamRequest mUserId    = %d", mUserId);
     ALOGD("AAudioStreamRequest mProcessId = %d", mProcessId);
-    ALOGD("AAudioStreamRequest mDirection = %d", mDirection);
     ALOGD("AAudioStreamRequest mSharingModeMatchRequired = %d", mSharingModeMatchRequired);
     ALOGD("AAudioStreamRequest mInService = %d", mInService);
     mConfiguration.dump();
diff --git a/media/libaaudio/src/binding/AAudioStreamRequest.h b/media/libaaudio/src/binding/AAudioStreamRequest.h
index b0fa96a..492f69d 100644
--- a/media/libaaudio/src/binding/AAudioStreamRequest.h
+++ b/media/libaaudio/src/binding/AAudioStreamRequest.h
@@ -52,14 +52,6 @@
         mProcessId = processId;
     }
 
-    aaudio_direction_t getDirection() const {
-        return mDirection;
-    }
-
-    void setDirection(aaudio_direction_t direction) {
-        mDirection = direction;
-    }
-
     bool isSharingModeMatchRequired() const {
         return mSharingModeMatchRequired;
     }
@@ -94,9 +86,8 @@
 
 protected:
     AAudioStreamConfiguration  mConfiguration;
-    uid_t                      mUserId;
-    pid_t                      mProcessId;
-    aaudio_direction_t         mDirection;
+    uid_t                      mUserId = (uid_t) -1;
+    pid_t                      mProcessId = (pid_t) -1;
     bool                       mSharingModeMatchRequired = false;
     bool                       mInService = false; // Stream opened by AAudioservice
 };
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index bdebf8f..7f2e495 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -105,13 +105,13 @@
     // Build the request to send to the server.
     request.setUserId(getuid());
     request.setProcessId(getpid());
-    request.setDirection(getDirection());
     request.setSharingModeMatchRequired(isSharingModeMatchRequired());
     request.setInService(mInService);
 
     request.getConfiguration().setDeviceId(getDeviceId());
     request.getConfiguration().setSampleRate(getSampleRate());
     request.getConfiguration().setSamplesPerFrame(getSamplesPerFrame());
+    request.getConfiguration().setDirection(getDirection());
     request.getConfiguration().setSharingMode(getSharingMode());
 
     request.getConfiguration().setBufferCapacity(builder.getBufferCapacity());
@@ -357,6 +357,7 @@
     if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
         return AAUDIO_ERROR_INVALID_STATE;
     }
+
     return mServiceInterface.startClient(mServiceStreamHandle, client, clientHandle);
 }
 
@@ -553,12 +554,19 @@
                 wakeTimeNanos += mWakeupDelayNanos;
             }
 
+            currentTimeNanos = AudioClock::getNanoseconds();
+            int64_t earliestWakeTime = currentTimeNanos + mMinimumSleepNanos;
+            // Guarantee a minimum sleep time.
+            if (wakeTimeNanos < earliestWakeTime) {
+                wakeTimeNanos = earliestWakeTime;
+            }
+
             if (wakeTimeNanos > deadlineNanos) {
                 // If we time out, just return the framesWritten so far.
                 // TODO remove after we fix the deadline bug
                 ALOGW("AudioStreamInternal::processData(): entered at %lld nanos, currently %lld",
                       (long long) entryTimeNanos, (long long) currentTimeNanos);
-                ALOGW("AudioStreamInternal::processData(): timed out after %lld nanos",
+                ALOGW("AudioStreamInternal::processData(): TIMEOUT after %lld nanos",
                       (long long) timeoutNanoseconds);
                 ALOGW("AudioStreamInternal::processData(): wakeTime = %lld, deadline = %lld nanos",
                       (long long) wakeTimeNanos, (long long) deadlineNanos);
@@ -569,13 +577,6 @@
                 break;
             }
 
-            currentTimeNanos = AudioClock::getNanoseconds();
-            int64_t earliestWakeTime = currentTimeNanos + mMinimumSleepNanos;
-            // Guarantee a minimum sleep time.
-            if (wakeTimeNanos < earliestWakeTime) {
-                wakeTimeNanos = earliestWakeTime;
-            }
-
             if (ATRACE_ENABLED()) {
                 int32_t fullFrames = mAudioEndpoint.getFullFramesAvailable();
                 ATRACE_INT(fifoName, fullFrames);
diff --git a/media/libaaudio/src/client/AudioStreamInternal.h b/media/libaaudio/src/client/AudioStreamInternal.h
index 13cf16c..3523294 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.h
+++ b/media/libaaudio/src/client/AudioStreamInternal.h
@@ -93,6 +93,10 @@
 
     aaudio_result_t stopClient(audio_port_handle_t clientHandle);
 
+    aaudio_handle_t getServiceHandle() const {
+        return mServiceStreamHandle;
+    }
+
 protected:
 
     aaudio_result_t processData(void *buffer,
diff --git a/media/libaaudio/src/core/AAudioStreamParameters.cpp b/media/libaaudio/src/core/AAudioStreamParameters.cpp
index 65c2b46..82445e7 100644
--- a/media/libaaudio/src/core/AAudioStreamParameters.cpp
+++ b/media/libaaudio/src/core/AAudioStreamParameters.cpp
@@ -34,6 +34,16 @@
 AAudioStreamParameters::AAudioStreamParameters() {}
 AAudioStreamParameters::~AAudioStreamParameters() {}
 
+void AAudioStreamParameters::copyFrom(const AAudioStreamParameters &other) {
+    mSamplesPerFrame = other.mSamplesPerFrame;
+    mSampleRate      = other.mSampleRate;
+    mDeviceId        = other.mDeviceId;
+    mSharingMode     = other.mSharingMode;
+    mAudioFormat     = other.mAudioFormat;
+    mDirection       = other.mDirection;
+    mBufferCapacity  = other.mBufferCapacity;
+}
+
 aaudio_result_t AAudioStreamParameters::validate() const {
     if (mSamplesPerFrame != AAUDIO_UNSPECIFIED
         && (mSamplesPerFrame < SAMPLES_PER_FRAME_MIN || mSamplesPerFrame > SAMPLES_PER_FRAME_MAX)) {
@@ -78,6 +88,16 @@
         return AAUDIO_ERROR_OUT_OF_RANGE;
     }
 
+    switch (mDirection) {
+        case AAUDIO_DIRECTION_INPUT:
+        case AAUDIO_DIRECTION_OUTPUT:
+            break; // valid
+        default:
+            ALOGE("AAudioStreamParameters: direction not valid = %d", mDirection);
+            return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
+            // break;
+    }
+
     return AAUDIO_OK;
 }
 
@@ -87,5 +107,7 @@
     ALOGD("AAudioStreamParameters mSamplesPerFrame = %d", mSamplesPerFrame);
     ALOGD("AAudioStreamParameters mSharingMode     = %d", (int)mSharingMode);
     ALOGD("AAudioStreamParameters mAudioFormat     = %d", (int)mAudioFormat);
+    ALOGD("AAudioStreamParameters mDirection       = %d", mDirection);
     ALOGD("AAudioStreamParameters mBufferCapacity  = %d", mBufferCapacity);
-}
\ No newline at end of file
+}
+
diff --git a/media/libaaudio/src/core/AAudioStreamParameters.h b/media/libaaudio/src/core/AAudioStreamParameters.h
index 97379cc..5e67c93 100644
--- a/media/libaaudio/src/core/AAudioStreamParameters.h
+++ b/media/libaaudio/src/core/AAudioStreamParameters.h
@@ -20,6 +20,7 @@
 #include <stdint.h>
 
 #include <aaudio/AAudio.h>
+#include <utility/AAudioUtilities.h>
 
 namespace aaudio {
 
@@ -79,6 +80,24 @@
         mBufferCapacity = frames;
     }
 
+    aaudio_direction_t getDirection() const {
+        return mDirection;
+    }
+
+    void setDirection(aaudio_direction_t direction) {
+        mDirection = direction;
+    }
+
+    int32_t calculateBytesPerFrame() const {
+        return getSamplesPerFrame() * AAudioConvert_formatToSizeInBytes(getFormat());
+    }
+
+    /**
+     * Copy variables defined in other AAudioStreamParameters instance to this one.
+     * @param other
+     */
+    void copyFrom(const AAudioStreamParameters &other);
+
     virtual aaudio_result_t validate() const;
 
     void dump() const;
@@ -89,9 +108,10 @@
     int32_t                    mDeviceId        = AAUDIO_UNSPECIFIED;
     aaudio_sharing_mode_t      mSharingMode     = AAUDIO_SHARING_MODE_SHARED;
     aaudio_format_t            mAudioFormat     = AAUDIO_FORMAT_UNSPECIFIED;
+    aaudio_direction_t         mDirection       = AAUDIO_DIRECTION_OUTPUT;
     int32_t                    mBufferCapacity  = AAUDIO_UNSPECIFIED;
 };
 
 } /* namespace aaudio */
 
-#endif //AAUDIO_STREAM_PARAMETERS_H
\ No newline at end of file
+#endif //AAUDIO_STREAM_PARAMETERS_H
diff --git a/media/libaaudio/src/core/AudioStreamBuilder.cpp b/media/libaaudio/src/core/AudioStreamBuilder.cpp
index 43a1ef1..09ebb3e 100644
--- a/media/libaaudio/src/core/AudioStreamBuilder.cpp
+++ b/media/libaaudio/src/core/AudioStreamBuilder.cpp
@@ -184,16 +184,6 @@
         return result;
     }
 
-    switch (mDirection) {
-        case AAUDIO_DIRECTION_INPUT:
-        case AAUDIO_DIRECTION_OUTPUT:
-            break; // valid
-        default:
-            ALOGE("AudioStreamBuilder: direction not valid = %d", mDirection);
-            return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
-            // break;
-    }
-
     switch (mPerformanceMode) {
         case AAUDIO_PERFORMANCE_MODE_NONE:
         case AAUDIO_PERFORMANCE_MODE_POWER_SAVING:
diff --git a/media/libaaudio/src/core/AudioStreamBuilder.h b/media/libaaudio/src/core/AudioStreamBuilder.h
index 6e548b1..a43cfa8 100644
--- a/media/libaaudio/src/core/AudioStreamBuilder.h
+++ b/media/libaaudio/src/core/AudioStreamBuilder.h
@@ -35,15 +35,6 @@
 
     ~AudioStreamBuilder();
 
-    aaudio_direction_t getDirection() const {
-        return mDirection;
-    }
-
-    AudioStreamBuilder* setDirection(aaudio_direction_t direction) {
-        mDirection = direction;
-        return this;
-    }
-
     bool isSharingModeMatchRequired() const {
         return mSharingModeMatchRequired;
     }
@@ -113,7 +104,6 @@
 
 private:
     bool                       mSharingModeMatchRequired = false; // must match sharing mode requested
-    aaudio_direction_t         mDirection = AAUDIO_DIRECTION_OUTPUT;
     aaudio_performance_mode_t  mPerformanceMode = AAUDIO_PERFORMANCE_MODE_NONE;
 
     AAudioStream_dataCallback  mDataCallbackProc = nullptr;  // external callback functions
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.cpp b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
index 51440d6..155362c 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
@@ -171,14 +171,13 @@
     setDeviceId(mAudioTrack->getRoutedDeviceId());
     mAudioTrack->addAudioDeviceCallback(mDeviceCallback);
 
-    // Update performance mode based on the actual stream.
+    // Update performance mode based on the actual stream flags.
     // For example, if the sample rate is not allowed then you won't get a FAST track.
     audio_output_flags_t actualFlags = mAudioTrack->getFlags();
     aaudio_performance_mode_t actualPerformanceMode = AAUDIO_PERFORMANCE_MODE_NONE;
-    if ((actualFlags & (AUDIO_OUTPUT_FLAG_FAST | AUDIO_OUTPUT_FLAG_RAW))
-        == (AUDIO_OUTPUT_FLAG_FAST | AUDIO_OUTPUT_FLAG_RAW)) {
+    // We may not get the RAW flag. But as long as we get the FAST flag we can call it LOW_LATENCY.
+    if ((actualFlags & AUDIO_OUTPUT_FLAG_FAST) != 0) {
         actualPerformanceMode = AAUDIO_PERFORMANCE_MODE_LOW_LATENCY;
-
     } else if ((actualFlags & AUDIO_OUTPUT_FLAG_DEEP_BUFFER) != 0) {
         actualPerformanceMode = AAUDIO_PERFORMANCE_MODE_POWER_SAVING;
     }
diff --git a/media/libheif/HeifDecoderImpl.cpp b/media/libheif/HeifDecoderImpl.cpp
index 8b846be..115baff 100644
--- a/media/libheif/HeifDecoderImpl.cpp
+++ b/media/libheif/HeifDecoderImpl.cpp
@@ -25,6 +25,7 @@
 #include <drm/drm_framework_common.h>
 #include <media/IDataSource.h>
 #include <media/mediametadataretriever.h>
+#include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/MediaSource.h>
 #include <private/media/VideoFrame.h>
 #include <utils/Log.h>
@@ -48,7 +49,8 @@
      * Constructs HeifDataSource; will take ownership of |stream|.
      */
     HeifDataSource(HeifStream* stream)
-        : mStream(stream), mReadPos(0), mEOS(false) {}
+        : mStream(stream), mEOS(false),
+          mCachedOffset(0), mCachedSize(0), mCacheBufferSize(0) {}
 
     ~HeifDataSource() override {}
 
@@ -68,17 +70,25 @@
     }
 
 private:
-    /*
-     * Buffer size for passing the read data to mediaserver. Set to 64K
-     * (which is what MediaDataSource Java API's jni implementation uses).
-     */
     enum {
+        /*
+         * Buffer size for passing the read data to mediaserver. Set to 64K
+         * (which is what MediaDataSource Java API's jni implementation uses).
+         */
         kBufferSize = 64 * 1024,
+        /*
+         * Initial and max cache buffer size.
+         */
+        kInitialCacheBufferSize = 4 * 1024 * 1024,
+        kMaxCacheBufferSize = 64 * 1024 * 1024,
     };
     sp<IMemory> mMemory;
     std::unique_ptr<HeifStream> mStream;
-    off64_t mReadPos;
     bool mEOS;
+    std::unique_ptr<uint8_t> mCache;
+    off64_t mCachedOffset;
+    size_t mCachedSize;
+    size_t mCacheBufferSize;
 };
 
 bool HeifDataSource::init() {
@@ -89,25 +99,29 @@
         ALOGE("Failed to allocate shared memory!");
         return false;
     }
+    mCache.reset(new uint8_t[kInitialCacheBufferSize]);
+    if (mCache.get() == nullptr) {
+        ALOGE("mFailed to allocate cache!");
+        return false;
+    }
+    mCacheBufferSize = kInitialCacheBufferSize;
     return true;
 }
 
 ssize_t HeifDataSource::readAt(off64_t offset, size_t size) {
     ALOGV("readAt: offset=%lld, size=%zu", (long long)offset, size);
 
-    if (size == 0) {
-        return mEOS ? ERROR_END_OF_STREAM : 0;
-    }
-
-    if (offset < mReadPos) {
+    if (offset < mCachedOffset) {
         // try seek, then rewind/skip, fail if none worked
         if (mStream->seek(offset)) {
             ALOGV("readAt: seek to offset=%lld", (long long)offset);
-            mReadPos = offset;
+            mCachedOffset = offset;
+            mCachedSize = 0;
             mEOS = false;
         } else if (mStream->rewind()) {
             ALOGV("readAt: rewind to offset=0");
-            mReadPos = 0;
+            mCachedOffset = 0;
+            mCachedSize = 0;
             mEOS = false;
         } else {
             ALOGE("readAt: couldn't seek or rewind!");
@@ -115,38 +129,127 @@
         }
     }
 
-    if (mEOS) {
+    if (mEOS && (offset < mCachedOffset ||
+                 offset >= (off64_t)(mCachedOffset + mCachedSize))) {
         ALOGV("readAt: EOS");
         return ERROR_END_OF_STREAM;
     }
 
-    if (offset > mReadPos) {
-        // skipping
-        size_t skipSize = offset - mReadPos;
-        size_t bytesSkipped = mStream->read(nullptr, skipSize);
-        if (bytesSkipped <= skipSize) {
-            mReadPos += bytesSkipped;
-        }
-        if (bytesSkipped != skipSize) {
-            mEOS = true;
-            return ERROR_END_OF_STREAM;
-        }
+    // at this point, offset must be >= mCachedOffset, other cases should
+    // have been caught above.
+    CHECK(offset >= mCachedOffset);
+
+    if (size == 0) {
+        return 0;
     }
 
+    // Can only read max of kBufferSize
     if (size > kBufferSize) {
         size = kBufferSize;
     }
-    size_t bytesRead = mStream->read(mMemory->pointer(), size);
-    if (bytesRead > size || bytesRead == 0) {
+
+    // copy from cache if the request falls entirely in cache
+    if (offset + size <= mCachedOffset + mCachedSize) {
+        memcpy(mMemory->pointer(), mCache.get() + offset - mCachedOffset, size);
+        return size;
+    }
+
+    // need to fetch more, check if we need to expand the cache buffer.
+    if ((off64_t)(offset + size) > mCachedOffset + kMaxCacheBufferSize) {
+        // it's reaching max cache buffer size, need to roll window, and possibly
+        // expand the cache buffer.
+        size_t newCacheBufferSize = mCacheBufferSize;
+        std::unique_ptr<uint8_t> newCache;
+        uint8_t* dst = mCache.get();
+        if (newCacheBufferSize < kMaxCacheBufferSize) {
+            newCacheBufferSize = kMaxCacheBufferSize;
+            newCache.reset(new uint8_t[newCacheBufferSize]);
+            dst = newCache.get();
+        }
+
+        // when rolling the cache window, try to keep about half the old bytes
+        // in case that the client goes back.
+        off64_t newCachedOffset = offset - (off64_t)(newCacheBufferSize / 2);
+        if (newCachedOffset < mCachedOffset) {
+            newCachedOffset = mCachedOffset;
+        }
+
+        int64_t newCachedSize = (int64_t)(mCachedOffset + mCachedSize) - newCachedOffset;
+        if (newCachedSize > 0) {
+            // in this case, the new cache region partially overlop the old cache,
+            // move the portion of the cache we want to save to the beginning of
+            // the cache buffer.
+            memcpy(dst, mCache.get() + newCachedOffset - mCachedOffset, newCachedSize);
+        } else if (newCachedSize < 0){
+            // in this case, the new cache region is entirely out of the old cache,
+            // in order to guarantee sequential read, we need to skip a number of
+            // bytes before reading.
+            size_t bytesToSkip = -newCachedSize;
+            size_t bytesSkipped = mStream->read(nullptr, bytesToSkip);
+            if (bytesSkipped != bytesToSkip) {
+                // bytesSkipped is invalid, there is not enough bytes to reach
+                // the requested offset.
+                ALOGE("readAt: skip failed, EOS");
+
+                mEOS = true;
+                mCachedOffset = newCachedOffset;
+                mCachedSize = 0;
+                return ERROR_END_OF_STREAM;
+            }
+            // set cache size to 0, since we're not keeping any old cache
+            newCachedSize = 0;
+        }
+
+        if (newCache.get() != nullptr) {
+            mCache.reset(newCache.release());
+            mCacheBufferSize = newCacheBufferSize;
+        }
+        mCachedOffset = newCachedOffset;
+        mCachedSize = newCachedSize;
+
+        ALOGV("readAt: rolling cache window to (%lld, %zu), cache buffer size %zu",
+                (long long)mCachedOffset, mCachedSize, mCacheBufferSize);
+    } else {
+        // expand cache buffer, but no need to roll the window
+        size_t newCacheBufferSize = mCacheBufferSize;
+        while (offset + size > mCachedOffset + newCacheBufferSize) {
+            newCacheBufferSize *= 2;
+        }
+        CHECK(newCacheBufferSize <= kMaxCacheBufferSize);
+        if (mCacheBufferSize < newCacheBufferSize) {
+            uint8_t* newCache = new uint8_t[newCacheBufferSize];
+            memcpy(newCache, mCache.get(), mCachedSize);
+            mCache.reset(newCache);
+            mCacheBufferSize = newCacheBufferSize;
+
+            ALOGV("readAt: current cache window (%lld, %zu), new cache buffer size %zu",
+                    (long long) mCachedOffset, mCachedSize, mCacheBufferSize);
+        }
+    }
+    size_t bytesToRead = offset + size - mCachedOffset - mCachedSize;
+    size_t bytesRead = mStream->read(mCache.get() + mCachedSize, bytesToRead);
+    if (bytesRead > bytesToRead || bytesRead == 0) {
         // bytesRead is invalid
         mEOS = true;
-        return ERROR_END_OF_STREAM;
-    } if (bytesRead < size) {
-        // read some bytes but not all, set EOS and return ERROR_END_OF_STREAM next time
+        bytesRead = 0;
+    } else if (bytesRead < bytesToRead) {
+        // read some bytes but not all, set EOS
         mEOS = true;
     }
-    mReadPos += bytesRead;
-    return bytesRead;
+    mCachedSize += bytesRead;
+    ALOGV("readAt: current cache window (%lld, %zu)",
+            (long long) mCachedOffset, mCachedSize);
+
+    // here bytesAvailable could be negative if offset jumped past EOS.
+    int64_t bytesAvailable = mCachedOffset + mCachedSize - offset;
+    if (bytesAvailable <= 0) {
+        return ERROR_END_OF_STREAM;
+    }
+    if (bytesAvailable < (int64_t)size) {
+        size = bytesAvailable;
+    }
+    memcpy(mMemory->pointer(), mCache.get() + offset - mCachedOffset, size);
+    return size;
 }
 
 status_t HeifDataSource::getSize(off64_t* size) {
@@ -166,13 +269,15 @@
     // output color format should always be set via setOutputColor(), in case
     // it's not, default to HAL_PIXEL_FORMAT_RGB_565.
     mOutputColor(HAL_PIXEL_FORMAT_RGB_565),
-    mCurScanline(0) {
+    mCurScanline(0),
+    mFrameDecoded(false) {
 }
 
 HeifDecoderImpl::~HeifDecoderImpl() {
 }
 
 bool HeifDecoderImpl::init(HeifStream* stream, HeifFrameInfo* frameInfo) {
+    mFrameDecoded = false;
     sp<HeifDataSource> dataSource = new HeifDataSource(stream);
     if (!dataSource->init()) {
         return false;
@@ -216,8 +321,8 @@
 
     if (frameInfo != nullptr) {
         frameInfo->set(
-                videoFrame->mWidth,
-                videoFrame->mHeight,
+                videoFrame->mDisplayWidth,
+                videoFrame->mDisplayHeight,
                 videoFrame->mRotationAngle,
                 videoFrame->mBytesPerPixel,
                 videoFrame->mIccSize,
@@ -256,6 +361,13 @@
 }
 
 bool HeifDecoderImpl::decode(HeifFrameInfo* frameInfo) {
+    // reset scanline pointer
+    mCurScanline = 0;
+
+    if (mFrameDecoded) {
+        return true;
+    }
+
     mFrameMemory = mRetriever->getFrameAtTime(0,
             IMediaSource::ReadOptions::SEEK_PREVIOUS_SYNC, mOutputColor);
     if (mFrameMemory == nullptr || mFrameMemory->pointer() == nullptr) {
@@ -264,6 +376,12 @@
     }
 
     VideoFrame* videoFrame = static_cast<VideoFrame*>(mFrameMemory->pointer());
+    if (videoFrame->mSize == 0 ||
+            mFrameMemory->size() < videoFrame->getFlattenedSize()) {
+        ALOGE("getFrameAtTime: videoFrame size is invalid");
+        return false;
+    }
+
     ALOGV("Decoded dimension %dx%d, display %dx%d, angle %d, rowbytes %d, size %d",
             videoFrame->mWidth,
             videoFrame->mHeight,
@@ -275,13 +393,14 @@
 
     if (frameInfo != nullptr) {
         frameInfo->set(
-                videoFrame->mWidth,
-                videoFrame->mHeight,
+                videoFrame->mDisplayWidth,
+                videoFrame->mDisplayHeight,
                 videoFrame->mRotationAngle,
                 videoFrame->mBytesPerPixel,
                 videoFrame->mIccSize,
                 videoFrame->getFlattenedIccData());
     }
+    mFrameDecoded = true;
     return true;
 }
 
@@ -290,11 +409,12 @@
         return false;
     }
     VideoFrame* videoFrame = static_cast<VideoFrame*>(mFrameMemory->pointer());
-    if (mCurScanline >= videoFrame->mHeight) {
+    if (mCurScanline >= videoFrame->mDisplayHeight) {
+        ALOGE("no more scanline available");
         return false;
     }
     uint8_t* src = videoFrame->getFlattenedData() + videoFrame->mRowBytes * mCurScanline++;
-    memcpy(dst, src, videoFrame->mBytesPerPixel * videoFrame->mWidth);
+    memcpy(dst, src, videoFrame->mBytesPerPixel * videoFrame->mDisplayWidth);
     return true;
 }
 
@@ -306,8 +426,8 @@
 
     uint32_t oldScanline = mCurScanline;
     mCurScanline += count;
-    if (mCurScanline >= videoFrame->mHeight) {
-        mCurScanline = videoFrame->mHeight;
+    if (mCurScanline > videoFrame->mDisplayHeight) {
+        mCurScanline = videoFrame->mDisplayHeight;
     }
     return (mCurScanline > oldScanline) ? (mCurScanline - oldScanline) : 0;
 }
diff --git a/media/libheif/HeifDecoderImpl.h b/media/libheif/HeifDecoderImpl.h
index 2f8f0f8..c2e4ff3 100644
--- a/media/libheif/HeifDecoderImpl.h
+++ b/media/libheif/HeifDecoderImpl.h
@@ -54,6 +54,7 @@
     sp<IMemory> mFrameMemory;
     android_pixel_format_t mOutputColor;
     size_t mCurScanline;
+    bool mFrameDecoded;
 };
 
 } // namespace android
diff --git a/media/libmedia/MediaCodecInfo.cpp b/media/libmedia/MediaCodecInfo.cpp
index 1f188f3..2a74512 100644
--- a/media/libmedia/MediaCodecInfo.cpp
+++ b/media/libmedia/MediaCodecInfo.cpp
@@ -105,11 +105,17 @@
     ProfileLevel profileLevel;
     profileLevel.mProfile = profile;
     profileLevel.mLevel = level;
-    mProfileLevels.push_back(profileLevel);
+    if (mProfileLevelsSorted.indexOf(profileLevel) < 0) {
+        mProfileLevels.push_back(profileLevel);
+        mProfileLevelsSorted.add(profileLevel);
+    }
 }
 
 void MediaCodecInfo::CapabilitiesBuilder::addColorFormat(uint32_t format) {
-    mColorFormats.push(format);
+    if (mColorFormatsSorted.indexOf(format) < 0) {
+        mColorFormats.push(format);
+        mColorFormatsSorted.add(format);
+    }
 }
 
 void MediaCodecInfo::CapabilitiesBuilder::addFlags(uint32_t flags) {
diff --git a/media/libmedia/include/media/IOMX.h b/media/libmedia/include/media/IOMX.h
index d868860..e69c02d 100644
--- a/media/libmedia/include/media/IOMX.h
+++ b/media/libmedia/include/media/IOMX.h
@@ -31,6 +31,7 @@
 
 #include <media/openmax/OMX_Core.h>
 #include <media/openmax/OMX_Video.h>
+#include <media/openmax/OMX_VideoExt.h>
 
 namespace android {
 
diff --git a/media/libmedia/include/media/MediaCodecInfo.h b/media/libmedia/include/media/MediaCodecInfo.h
index 6b50f22..ef641d2 100644
--- a/media/libmedia/include/media/MediaCodecInfo.h
+++ b/media/libmedia/include/media/MediaCodecInfo.h
@@ -40,6 +40,9 @@
     struct ProfileLevel {
         uint32_t mProfile;
         uint32_t mLevel;
+        bool operator <(const ProfileLevel &o) const {
+            return mProfile < o.mProfile || (mProfile == o.mProfile && mLevel < o.mLevel);
+        }
     };
 
     struct Capabilities : public RefBase {
@@ -61,7 +64,9 @@
 
     protected:
         Vector<ProfileLevel> mProfileLevels;
+        SortedVector<ProfileLevel> mProfileLevelsSorted;
         Vector<uint32_t> mColorFormats;
+        SortedVector<uint32_t> mColorFormatsSorted;
         uint32_t mFlags;
         sp<AMessage> mDetails;
 
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index 2c5b22f..e0d253d 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -1234,7 +1234,7 @@
     }
 
     if (mAudioTrack.mSource != NULL) {
-        readBuffer(MEDIA_TRACK_TYPE_AUDIO, seekTimeUs);
+        readBuffer(MEDIA_TRACK_TYPE_AUDIO, seekTimeUs, MediaPlayerSeekMode::SEEK_CLOSEST);
         mAudioLastDequeueTimeUs = seekTimeUs;
     }
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index ac187cc..cd770b6 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -750,12 +750,20 @@
 
         buffer->meta()->setInt32("eos", true);
         reply->setInt32("eos", true);
-    } else if (mSkipRenderingUntilMediaTimeUs >= 0) {
+    }
+
+    if (mSkipRenderingUntilMediaTimeUs >= 0) {
         if (timeUs < mSkipRenderingUntilMediaTimeUs) {
             ALOGV("[%s] dropping buffer at time %lld as requested.",
                      mComponentName.c_str(), (long long)timeUs);
 
             reply->post();
+            if (eos) {
+                notifyResumeCompleteIfNecessary();
+                if (mRenderer != NULL && !isDiscontinuityPending()) {
+                    mRenderer->queueEOS(mIsAudio, ERROR_END_OF_STREAM);
+                }
+            }
             return true;
         }
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 5b3d6e1..323b52d 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -1430,6 +1430,7 @@
     if (audio) {
         // Video might outlive audio. Clear anchor to enable video only case.
         mAnchorTimeMediaUs = -1;
+        mHasAudio = false;
     }
 }
 
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 63ad0e0..0e60b2e 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -4173,11 +4173,12 @@
 // static
 int /* OMX_VIDEO_AVCLEVELTYPE */ ACodec::getAVCLevelFor(
         int width, int height, int rate, int bitrate,
-        OMX_VIDEO_AVCPROFILETYPE profile) {
+        OMX_VIDEO_AVCPROFILEEXTTYPE profile) {
     // convert bitrate to main/baseline profile kbps equivalent
-    switch (profile) {
+    switch ((uint32_t)profile) {
         case OMX_VIDEO_AVCProfileHigh10:
             bitrate = divUp(bitrate, 3000); break;
+        case OMX_VIDEO_AVCProfileConstrainedHigh:
         case OMX_VIDEO_AVCProfileHigh:
             bitrate = divUp(bitrate, 1250); break;
         default:
@@ -8262,6 +8263,17 @@
             }
             builder->addProfileLevel(param.eProfile, param.eLevel);
 
+            // AVC components may not list the constrained profiles explicitly, but
+            // decoders that support a profile also support its constrained version.
+            // Encoders must explicitly support constrained profiles.
+            if (!isEncoder && mime.equalsIgnoreCase(MEDIA_MIMETYPE_VIDEO_AVC)) {
+                if (param.eProfile == OMX_VIDEO_AVCProfileHigh) {
+                    builder->addProfileLevel(OMX_VIDEO_AVCProfileConstrainedHigh, param.eLevel);
+                } else if (param.eProfile == OMX_VIDEO_AVCProfileBaseline) {
+                    builder->addProfileLevel(OMX_VIDEO_AVCProfileConstrainedBaseline, param.eLevel);
+                }
+            }
+
             if (index == kMaxIndicesToCheck) {
                 ALOGW("[%s] stopping checking profiles after %u: %x/%x",
                         name.c_str(), index,
@@ -8275,7 +8287,6 @@
         OMX_VIDEO_PARAM_PORTFORMATTYPE portFormat;
         InitOMXParams(&portFormat);
         portFormat.nPortIndex = isEncoder ? kPortIndexInput : kPortIndexOutput;
-        Vector<uint32_t> supportedColors; // shadow copy to check for duplicates
         for (OMX_U32 index = 0; index <= kMaxIndicesToCheck; ++index) {
             portFormat.nIndex = index;
             status_t err = omxNode->getParameter(
@@ -8289,19 +8300,8 @@
             if (IsFlexibleColorFormat(
                     omxNode, portFormat.eColorFormat, false /* usingNativeWindow */,
                     &flexibleEquivalent)) {
-                bool marked = false;
-                for (size_t i = 0; i < supportedColors.size(); ++i) {
-                    if (supportedColors[i] == flexibleEquivalent) {
-                        marked = true;
-                        break;
-                    }
-                }
-                if (!marked) {
-                    supportedColors.push(flexibleEquivalent);
-                    builder->addColorFormat(flexibleEquivalent);
-                }
+                builder->addColorFormat(flexibleEquivalent);
             }
-            supportedColors.push(portFormat.eColorFormat);
             builder->addColorFormat(portFormat.eColorFormat);
 
             if (index == kMaxIndicesToCheck) {
diff --git a/media/libstagefright/ItemTable.cpp b/media/libstagefright/ItemTable.cpp
index b7ff21b..3ec416b 100644
--- a/media/libstagefright/ItemTable.cpp
+++ b/media/libstagefright/ItemTable.cpp
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "ItemTable"
 //#define LOG_NDEBUG 0
+#define LOG_TAG "ItemTable"
 
 #include <include/ItemTable.h>
 #include <media/MediaDefs.h>
@@ -1434,10 +1434,19 @@
         if (tileIndex < 0) {
             return NULL;
         }
-        meta->setInt32(kKeyGridRows, image->rows);
-        meta->setInt32(kKeyGridCols, image->columns);
+        // when there are tiles, (kKeyWidth, kKeyHeight) is the full tiled area,
+        // and (kKeyDisplayWidth, kKeyDisplayHeight) may be smaller than that.
+        meta->setInt32(kKeyDisplayWidth, image->width);
+        meta->setInt32(kKeyDisplayHeight, image->height);
+        int32_t gridRows = image->rows, gridCols = image->columns;
 
+        // point image to the first tile for grid size and HVCC
         image = &mItemIdToImageMap.editValueAt(tileIndex);
+        meta->setInt32(kKeyWidth, image->width * gridCols);
+        meta->setInt32(kKeyHeight, image->height * gridRows);
+        meta->setInt32(kKeyGridWidth, image->width);
+        meta->setInt32(kKeyGridHeight, image->height);
+        meta->setInt32(kKeyMaxInputSize, image->width * image->height * 1.5);
     }
 
     if (image->hvcc == NULL) {
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 93d4f57..7786c4d 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -432,7 +432,7 @@
 };
 
 MPEG4Writer::MPEG4Writer(int fd) {
-    initInternal(fd);
+    initInternal(fd, true /*isFirstSession*/);
 }
 
 MPEG4Writer::~MPEG4Writer() {
@@ -451,19 +451,26 @@
     }
 }
 
-void MPEG4Writer::initInternal(int fd) {
+void MPEG4Writer::initInternal(int fd, bool isFirstSession) {
     ALOGV("initInternal");
     mFd = dup(fd);
     mNextFd = -1;
     mInitCheck = mFd < 0? NO_INIT: OK;
-    mIsRealTimeRecording = true;
-    mUse4ByteNalLength = true;
-    mUse32BitOffset = true;
-    mIsFileSizeLimitExplicitlyRequested = false;
+
+    mInterleaveDurationUs = 1000000;
+
+    mStartTimestampUs = -1ll;
+    mStartTimeOffsetMs = -1;
     mPaused = false;
     mStarted = false;
     mWriterThreadStarted = false;
     mSendNotify = false;
+
+    // Reset following variables for all the sessions and they will be
+    // initialized in start(MetaData *param).
+    mIsRealTimeRecording = true;
+    mUse4ByteNalLength = true;
+    mUse32BitOffset = true;
     mOffset = 0;
     mMdatOffset = 0;
     mMoovBoxBuffer = NULL;
@@ -472,17 +479,21 @@
     mFreeBoxOffset = 0;
     mStreamableFile = false;
     mEstimatedMoovBoxSize = 0;
-    mMoovExtraSize = 0;
-    mInterleaveDurationUs = 1000000;
     mTimeScale = -1;
-    mStartTimestampUs = -1ll;
-    mLatitudex10000 = 0;
-    mLongitudex10000 = 0;
-    mAreGeoTagsAvailable = false;
-    mStartTimeOffsetMs = -1;
-    mSwitchPending = false;
-    mMetaKeys = new AMessage();
-    addDeviceMeta();
+
+    // Following variables only need to be set for the first recording session.
+    // And they will stay the same for all the recording sessions.
+    if (isFirstSession) {
+        mMoovExtraSize = 0;
+        mMetaKeys = new AMessage();
+        addDeviceMeta();
+        mLatitudex10000 = 0;
+        mLongitudex10000 = 0;
+        mAreGeoTagsAvailable = false;
+        mSwitchPending = false;
+        mIsFileSizeLimitExplicitlyRequested = false;
+    }
+
     // Verify mFd is seekable
     off64_t off = lseek64(mFd, 0, SEEK_SET);
     if (off < 0) {
@@ -873,19 +884,8 @@
 }
 
 status_t MPEG4Writer::pause() {
-    if (mInitCheck != OK) {
-        return OK;
-    }
-    mPaused = true;
-    status_t err = OK;
-    for (List<Track *>::iterator it = mTracks.begin();
-         it != mTracks.end(); ++it) {
-        status_t status = (*it)->pause();
-        if (status != OK) {
-            err = status;
-        }
-    }
-    return err;
+    ALOGW("MPEG4Writer: pause is not supported");
+    return ERROR_UNSUPPORTED;
 }
 
 void MPEG4Writer::stopWriterThread() {
@@ -1833,7 +1833,7 @@
             int fd = mNextFd;
             mNextFd = -1;
             mLock.unlock();
-            initInternal(fd);
+            initInternal(fd, false /*isFirstSession*/);
             start(mStartMeta.get());
             mSwitchPending = false;
             notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_NEXT_OUTPUT_FILE_STARTED, 0);
diff --git a/media/libstagefright/StagefrightMetadataRetriever.cpp b/media/libstagefright/StagefrightMetadataRetriever.cpp
index f36ff97..a53897f 100644
--- a/media/libstagefright/StagefrightMetadataRetriever.cpp
+++ b/media/libstagefright/StagefrightMetadataRetriever.cpp
@@ -250,6 +250,8 @@
          && trackMeta->findInt32(kKeyThumbnailHeight, &thumbnailHeight)
          && trackMeta->findData(kKeyThumbnailHVCC, &type, &data, &size)){
             overrideMeta = new MetaData(*trackMeta);
+            overrideMeta->remove(kKeyDisplayWidth);
+            overrideMeta->remove(kKeyDisplayHeight);
             overrideMeta->setInt32(kKeyWidth, thumbnailWidth);
             overrideMeta->setInt32(kKeyHeight, thumbnailHeight);
             overrideMeta->setData(kKeyHVCC, type, data, size);
@@ -266,32 +268,39 @@
     }
 
     int32_t gridRows = 1, gridCols = 1;
-    int32_t numTiles = 1, tilesDecoded = 0;
     if (overrideMeta == NULL) {
         // check if we're dealing with a tiled heif
-        if (trackMeta->findInt32(kKeyGridRows, &gridRows) && gridRows > 0
-         && trackMeta->findInt32(kKeyGridCols, &gridCols) && gridCols > 0) {
-            int32_t width, height;
+        int32_t gridWidth, gridHeight;
+        if (trackMeta->findInt32(kKeyGridWidth, &gridWidth) && gridWidth > 0
+         && trackMeta->findInt32(kKeyGridHeight, &gridHeight) && gridHeight > 0) {
+            int32_t width, height, displayWidth, displayHeight;
             CHECK(trackMeta->findInt32(kKeyWidth, &width));
             CHECK(trackMeta->findInt32(kKeyHeight, &height));
+            CHECK(trackMeta->findInt32(kKeyDisplayWidth, &displayWidth));
+            CHECK(trackMeta->findInt32(kKeyDisplayHeight, &displayHeight));
 
-            if ((width % gridCols == 0) && (height % gridRows == 0)) {
-                width /= gridCols;
-                height /= gridRows;
-                numTiles = gridCols * gridRows;
-
-                ALOGV("tile: %dx%d, numTiles %d", width, height, numTiles);
+            if (width >= displayWidth && height >= displayHeight
+                    && (width % gridWidth == 0) && (height % gridHeight == 0)) {
+                ALOGV("grid config: %dx%d, display %dx%d, grid %dx%d",
+                        width, height, displayWidth, displayHeight, gridWidth, gridHeight);
 
                 overrideMeta = new MetaData(*trackMeta);
-                overrideMeta->setInt32(kKeyWidth, width);
-                overrideMeta->setInt32(kKeyHeight, height);
+                overrideMeta->remove(kKeyDisplayWidth);
+                overrideMeta->remove(kKeyDisplayHeight);
+                overrideMeta->setInt32(kKeyWidth, gridWidth);
+                overrideMeta->setInt32(kKeyHeight, gridHeight);
+                gridCols = width / gridWidth;
+                gridRows = height / gridHeight;
+            } else {
+                ALOGE("Bad grid config: %dx%d, display %dx%d, grid %dx%d",
+                        width, height, displayWidth, displayHeight, gridWidth, gridHeight);
             }
         }
         if (overrideMeta == NULL) {
-            gridRows = gridCols = numTiles = 1;
             overrideMeta = trackMeta;
         }
     }
+    int32_t numTiles = gridRows * gridCols;
 
     sp<AMessage> videoFormat;
     if (convertMetaDataToMessage(overrideMeta, &videoFormat) != OK) {
@@ -383,6 +392,7 @@
     int64_t targetTimeUs = -1ll;
 
     VideoFrame *frame = NULL;
+    int32_t tilesDecoded = 0;
 
     do {
         size_t inputIndex = -1;
@@ -500,7 +510,7 @@
 
                     if (frame == NULL) {
                         frame = allocVideoFrame(
-                                overrideMeta,
+                                trackMeta,
                                 (crop_right - crop_left + 1) * gridCols,
                                 (crop_bottom - crop_top + 1) * gridRows,
                                 dstBpp,
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index a3bda5d..3ef8f2a 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -237,6 +237,11 @@
     OMX_VIDEO_AVCPROFILETYPE codecProfile;
     OMX_VIDEO_AVCLEVELTYPE codecLevel;
     if (profiles.map(profile, &codecProfile)) {
+        if (profile == 66 && (constraints & 0x40)) {
+            codecProfile = (OMX_VIDEO_AVCPROFILETYPE)OMX_VIDEO_AVCProfileConstrainedBaseline;
+        } else if (profile == 100 && (constraints & 0x0C) == 0x0C) {
+            codecProfile = (OMX_VIDEO_AVCPROFILETYPE)OMX_VIDEO_AVCProfileConstrainedHigh;
+        }
         format->setInt32("profile", codecProfile);
         if (levels.map(level, &codecLevel)) {
             // for 9 && 11 decide level based on profile and constraint_set3 flag
diff --git a/media/libstagefright/codecs/aacdec/Android.bp b/media/libstagefright/codecs/aacdec/Android.bp
index 1daaf49..21c00a1 100644
--- a/media/libstagefright/codecs/aacdec/Android.bp
+++ b/media/libstagefright/codecs/aacdec/Android.bp
@@ -37,4 +37,5 @@
         "libcutils",
         "liblog",
     ],
+    compile_multilib: "32",
 }
diff --git a/media/libstagefright/codecs/aacenc/Android.bp b/media/libstagefright/codecs/aacenc/Android.bp
index 4b478ea..fb368c2 100644
--- a/media/libstagefright/codecs/aacenc/Android.bp
+++ b/media/libstagefright/codecs/aacenc/Android.bp
@@ -145,6 +145,7 @@
         "libutils",
         "liblog",
     ],
+    compile_multilib: "32",
 }
 
 cc_library_shared {
diff --git a/media/libstagefright/codecs/amrnb/dec/Android.bp b/media/libstagefright/codecs/amrnb/dec/Android.bp
index a61fb57..d266dc2 100644
--- a/media/libstagefright/codecs/amrnb/dec/Android.bp
+++ b/media/libstagefright/codecs/amrnb/dec/Android.bp
@@ -104,6 +104,7 @@
         "liblog",
         "libstagefright_amrnb_common",
     ],
+    compile_multilib: "32",
 }
 
 //###############################################################################
diff --git a/media/libstagefright/codecs/amrnb/enc/Android.bp b/media/libstagefright/codecs/amrnb/enc/Android.bp
index 04ed07f..6dc2dc1 100644
--- a/media/libstagefright/codecs/amrnb/enc/Android.bp
+++ b/media/libstagefright/codecs/amrnb/enc/Android.bp
@@ -115,6 +115,7 @@
         "liblog",
         "libstagefright_amrnb_common",
     ],
+    compile_multilib: "32",
 }
 
 //###############################################################################
diff --git a/media/libstagefright/codecs/amrwbenc/Android.bp b/media/libstagefright/codecs/amrwbenc/Android.bp
index d337cde..8968991 100644
--- a/media/libstagefright/codecs/amrwbenc/Android.bp
+++ b/media/libstagefright/codecs/amrwbenc/Android.bp
@@ -178,6 +178,7 @@
         "liblog",
         "libstagefright_enc_common",
     ],
+    compile_multilib: "32",
 }
 
 //###############################################################################
diff --git a/media/libstagefright/codecs/avcdec/Android.bp b/media/libstagefright/codecs/avcdec/Android.bp
index 3fa8d7f..1f43803 100644
--- a/media/libstagefright/codecs/avcdec/Android.bp
+++ b/media/libstagefright/codecs/avcdec/Android.bp
@@ -34,4 +34,5 @@
     },
 
     ldflags: ["-Wl,-Bsymbolic"],
+    compile_multilib: "32",
 }
diff --git a/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp b/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp
index 248ab6d..c342b6c 100644
--- a/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp
+++ b/media/libstagefright/codecs/avcdec/SoftAVCDec.cpp
@@ -48,10 +48,14 @@
         (IVD_CONTROL_API_COMMAND_TYPE_T)IH264D_CMD_CTL_SET_NUM_CORES
 
 static const CodecProfileLevel kProfileLevels[] = {
+    { OMX_VIDEO_AVCProfileConstrainedBaseline, OMX_VIDEO_AVCLevel52 },
+
     { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel52 },
 
     { OMX_VIDEO_AVCProfileMain,     OMX_VIDEO_AVCLevel52 },
 
+    { OMX_VIDEO_AVCProfileConstrainedHigh,     OMX_VIDEO_AVCLevel52 },
+
     { OMX_VIDEO_AVCProfileHigh,     OMX_VIDEO_AVCLevel52 },
 };
 
diff --git a/media/libstagefright/codecs/avcenc/Android.bp b/media/libstagefright/codecs/avcenc/Android.bp
index 6c4311b..66507a7 100644
--- a/media/libstagefright/codecs/avcenc/Android.bp
+++ b/media/libstagefright/codecs/avcenc/Android.bp
@@ -34,4 +34,5 @@
     },
 
     ldflags: ["-Wl,-Bsymbolic"],
+    compile_multilib: "32",
 }
diff --git a/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp b/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp
index fe5a17b..358c743 100644
--- a/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp
+++ b/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp
@@ -74,33 +74,11 @@
 };
 
 static const CodecProfileLevel kProfileLevels[] = {
-    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel1  },
-    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel1b },
-    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel11 },
-    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel12 },
-    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel13 },
-    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel2  },
-    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel21 },
-    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel22 },
-    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel3  },
-    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel31 },
-    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel32 },
-    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel4  },
-    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel41 },
-    { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel1  },
-    { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel1b },
-    { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel11 },
-    { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel12 },
-    { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel13 },
-    { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel2  },
-    { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel21 },
-    { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel22 },
-    { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel3  },
-    { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel31 },
-    { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel32 },
-    { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel4  },
-    { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel41 },
+    { OMX_VIDEO_AVCProfileConstrainedBaseline, OMX_VIDEO_AVCLevel41 },
 
+    { OMX_VIDEO_AVCProfileBaseline, OMX_VIDEO_AVCLevel41 },
+
+    { OMX_VIDEO_AVCProfileMain, OMX_VIDEO_AVCLevel41 },
 };
 
 static size_t GetCPUCoreCount() {
@@ -964,7 +942,8 @@
                 return OMX_ErrorUndefined;
             }
 
-            avcParams->eProfile = OMX_VIDEO_AVCProfileBaseline;
+            // TODO: maintain profile
+            avcParams->eProfile = (OMX_VIDEO_AVCPROFILETYPE)OMX_VIDEO_AVCProfileConstrainedBaseline;
             avcParams->eLevel = omxLevel;
             avcParams->nRefFrames = 1;
             avcParams->bUseHadamard = OMX_TRUE;
diff --git a/media/libstagefright/codecs/flac/dec/Android.bp b/media/libstagefright/codecs/flac/dec/Android.bp
index 5652594..595cfdb 100644
--- a/media/libstagefright/codecs/flac/dec/Android.bp
+++ b/media/libstagefright/codecs/flac/dec/Android.bp
@@ -37,4 +37,5 @@
         "libstagefright_foundation",
         "libutils",
     ],
+    compile_multilib: "32",
 }
diff --git a/media/libstagefright/codecs/flac/enc/Android.bp b/media/libstagefright/codecs/flac/enc/Android.bp
index 6197157..066917b 100644
--- a/media/libstagefright/codecs/flac/enc/Android.bp
+++ b/media/libstagefright/codecs/flac/enc/Android.bp
@@ -37,4 +37,5 @@
         enabled: true,
     },
 
+    compile_multilib: "32",
 }
diff --git a/media/libstagefright/codecs/g711/dec/Android.bp b/media/libstagefright/codecs/g711/dec/Android.bp
index 0e6f468..fff72a8 100644
--- a/media/libstagefright/codecs/g711/dec/Android.bp
+++ b/media/libstagefright/codecs/g711/dec/Android.bp
@@ -31,4 +31,5 @@
             cfi: true,
         },
     },
+    compile_multilib: "32",
 }
diff --git a/media/libstagefright/codecs/gsm/dec/Android.bp b/media/libstagefright/codecs/gsm/dec/Android.bp
index 7be86a4..753eeef 100644
--- a/media/libstagefright/codecs/gsm/dec/Android.bp
+++ b/media/libstagefright/codecs/gsm/dec/Android.bp
@@ -34,4 +34,5 @@
     ],
 
     static_libs: ["libgsm"],
+    compile_multilib: "32",
 }
diff --git a/media/libstagefright/codecs/hevcdec/Android.bp b/media/libstagefright/codecs/hevcdec/Android.bp
index 3fd1652..7fa74d4 100644
--- a/media/libstagefright/codecs/hevcdec/Android.bp
+++ b/media/libstagefright/codecs/hevcdec/Android.bp
@@ -37,4 +37,5 @@
     // requires unsupported dynamic reloc R_ARM_REL32; recompile with -fPIC
     // Bug: 16853291
     ldflags: ["-Wl,-Bsymbolic"],
+    compile_multilib: "32",
 }
diff --git a/media/libstagefright/codecs/m4v_h263/dec/Android.bp b/media/libstagefright/codecs/m4v_h263/dec/Android.bp
index 2619131..1216ae5 100644
--- a/media/libstagefright/codecs/m4v_h263/dec/Android.bp
+++ b/media/libstagefright/codecs/m4v_h263/dec/Android.bp
@@ -109,4 +109,5 @@
             cfi: true,
         },
     },
+    compile_multilib: "32",
 }
diff --git a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
index 411a251..39b67ab 100644
--- a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
+++ b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
@@ -31,20 +31,12 @@
 namespace android {
 
 static const CodecProfileLevel kM4VProfileLevels[] = {
-    { OMX_VIDEO_MPEG4ProfileSimple, OMX_VIDEO_MPEG4Level0 },
-    { OMX_VIDEO_MPEG4ProfileSimple, OMX_VIDEO_MPEG4Level0b },
-    { OMX_VIDEO_MPEG4ProfileSimple, OMX_VIDEO_MPEG4Level1 },
-    { OMX_VIDEO_MPEG4ProfileSimple, OMX_VIDEO_MPEG4Level2 },
     { OMX_VIDEO_MPEG4ProfileSimple, OMX_VIDEO_MPEG4Level3 },
 };
 
 static const CodecProfileLevel kH263ProfileLevels[] = {
-    { OMX_VIDEO_H263ProfileBaseline, OMX_VIDEO_H263Level10 },
-    { OMX_VIDEO_H263ProfileBaseline, OMX_VIDEO_H263Level20 },
     { OMX_VIDEO_H263ProfileBaseline, OMX_VIDEO_H263Level30 },
     { OMX_VIDEO_H263ProfileBaseline, OMX_VIDEO_H263Level45 },
-    { OMX_VIDEO_H263ProfileISWV2,    OMX_VIDEO_H263Level10 },
-    { OMX_VIDEO_H263ProfileISWV2,    OMX_VIDEO_H263Level20 },
     { OMX_VIDEO_H263ProfileISWV2,    OMX_VIDEO_H263Level30 },
     { OMX_VIDEO_H263ProfileISWV2,    OMX_VIDEO_H263Level45 },
 };
diff --git a/media/libstagefright/codecs/m4v_h263/enc/Android.bp b/media/libstagefright/codecs/m4v_h263/enc/Android.bp
index 919b9d4..640718d 100644
--- a/media/libstagefright/codecs/m4v_h263/enc/Android.bp
+++ b/media/libstagefright/codecs/m4v_h263/enc/Android.bp
@@ -95,6 +95,7 @@
             cfi: true,
         },
     },
+    compile_multilib: "32",
 }
 
 //###############################################################################
diff --git a/media/libstagefright/codecs/mp3dec/Android.bp b/media/libstagefright/codecs/mp3dec/Android.bp
index b2e8f9b..273fa31 100644
--- a/media/libstagefright/codecs/mp3dec/Android.bp
+++ b/media/libstagefright/codecs/mp3dec/Android.bp
@@ -115,6 +115,7 @@
     ],
 
     static_libs: ["libstagefright_mp3dec"],
+    compile_multilib: "32",
 }
 
 //###############################################################################
diff --git a/media/libstagefright/codecs/mpeg2dec/Android.bp b/media/libstagefright/codecs/mpeg2dec/Android.bp
index ed51797..15fdde7 100644
--- a/media/libstagefright/codecs/mpeg2dec/Android.bp
+++ b/media/libstagefright/codecs/mpeg2dec/Android.bp
@@ -34,4 +34,5 @@
             cfi: true,
         },
     },
+    compile_multilib: "32",
 }
diff --git a/media/libstagefright/codecs/on2/dec/Android.bp b/media/libstagefright/codecs/on2/dec/Android.bp
index 249ab92..59c1f5d 100644
--- a/media/libstagefright/codecs/on2/dec/Android.bp
+++ b/media/libstagefright/codecs/on2/dec/Android.bp
@@ -34,4 +34,5 @@
             cfi: true,
         },
     },
+    compile_multilib: "32",
 }
diff --git a/media/libstagefright/codecs/on2/enc/Android.bp b/media/libstagefright/codecs/on2/enc/Android.bp
index 0284719..741774c 100644
--- a/media/libstagefright/codecs/on2/enc/Android.bp
+++ b/media/libstagefright/codecs/on2/enc/Android.bp
@@ -36,4 +36,5 @@
         "libutils",
         "liblog",
     ],
+    compile_multilib: "32",
 }
diff --git a/media/libstagefright/codecs/opus/dec/Android.bp b/media/libstagefright/codecs/opus/dec/Android.bp
index d7569a9..88d6ec4 100644
--- a/media/libstagefright/codecs/opus/dec/Android.bp
+++ b/media/libstagefright/codecs/opus/dec/Android.bp
@@ -33,4 +33,5 @@
             cfi: true,
         },
     },
+    compile_multilib: "32",
 }
diff --git a/media/libstagefright/codecs/raw/Android.bp b/media/libstagefright/codecs/raw/Android.bp
index 1bd75c6..f21d46f 100644
--- a/media/libstagefright/codecs/raw/Android.bp
+++ b/media/libstagefright/codecs/raw/Android.bp
@@ -31,4 +31,5 @@
         "libutils",
         "liblog",
     ],
+    compile_multilib: "32",
 }
diff --git a/media/libstagefright/codecs/vorbis/dec/Android.bp b/media/libstagefright/codecs/vorbis/dec/Android.bp
index fedfb67..628b36c 100644
--- a/media/libstagefright/codecs/vorbis/dec/Android.bp
+++ b/media/libstagefright/codecs/vorbis/dec/Android.bp
@@ -29,4 +29,5 @@
             "unsigned-integer-overflow",
         ],
     },
+    compile_multilib: "32",
 }
diff --git a/media/libstagefright/foundation/base64.cpp b/media/libstagefright/foundation/base64.cpp
index cc89064..8f32582 100644
--- a/media/libstagefright/foundation/base64.cpp
+++ b/media/libstagefright/foundation/base64.cpp
@@ -23,6 +23,7 @@
 
 sp<ABuffer> decodeBase64(const AString &s) {
     size_t n = s.size();
+
     if ((n % 4) != 0) {
         return NULL;
     }
@@ -45,7 +46,6 @@
     size_t outLen = (n / 4) * 3 - padding;
 
     sp<ABuffer> buffer = new ABuffer(outLen);
-
     uint8_t *out = buffer->data();
     if (out == NULL || buffer->size() < outLen) {
         return NULL;
@@ -61,9 +61,9 @@
             value = 26 + c - 'a';
         } else if (c >= '0' && c <= '9') {
             value = 52 + c - '0';
-        } else if (c == '+') {
+        } else if (c == '+' || c == '-') {
             value = 62;
-        } else if (c == '/') {
+        } else if (c == '/' || c == '_') {
             value = 63;
         } else if (c != '=') {
             return NULL;
@@ -144,4 +144,26 @@
     }
 }
 
+void encodeBase64Url(
+        const void *_data, size_t size, AString *out) {
+    encodeBase64(_data, size, out);
+
+    if ((-1 != out->find("+")) || (-1 != out->find("/"))) {
+        size_t outLen = out->size();
+        char *base64url = new char[outLen];
+        for (size_t i = 0; i < outLen; ++i) {
+            if (out->c_str()[i] == '+')
+                base64url[i] = '-';
+            else if (out->c_str()[i] == '/')
+                base64url[i] = '_';
+            else
+                base64url[i] = out->c_str()[i];
+        }
+
+        out->setTo(base64url, outLen);
+        delete[] base64url;
+    }
+}
+
+
 }  // namespace android
diff --git a/media/libstagefright/foundation/include/media/stagefright/foundation/base64.h b/media/libstagefright/foundation/include/media/stagefright/foundation/base64.h
index e340b89..abc95e0 100644
--- a/media/libstagefright/foundation/include/media/stagefright/foundation/base64.h
+++ b/media/libstagefright/foundation/include/media/stagefright/foundation/base64.h
@@ -28,6 +28,8 @@
 sp<ABuffer> decodeBase64(const AString &s);
 void encodeBase64(const void *data, size_t size, AString *out);
 
+void encodeBase64Url(const void *data, size_t size, AString *out);
+
 }  // namespace android
 
 #endif  // BASE_64_H_
diff --git a/media/libstagefright/foundation/tests/Android.mk b/media/libstagefright/foundation/tests/Android.mk
index d741c6f..a9e3c76 100644
--- a/media/libstagefright/foundation/tests/Android.mk
+++ b/media/libstagefright/foundation/tests/Android.mk
@@ -9,11 +9,13 @@
 
 LOCAL_SRC_FILES := \
 	AData_test.cpp \
+	Base64_test.cpp \
 	Flagged_test.cpp \
 	TypeTraits_test.cpp \
 	Utils_test.cpp \
 
 LOCAL_SHARED_LIBRARIES := \
+	liblog \
 	libstagefright_foundation \
 	libutils \
 
diff --git a/media/libstagefright/foundation/tests/Base64_test.cpp b/media/libstagefright/foundation/tests/Base64_test.cpp
new file mode 100644
index 0000000..7a4289e
--- /dev/null
+++ b/media/libstagefright/foundation/tests/Base64_test.cpp
@@ -0,0 +1,155 @@
+/*
+ * 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 <utils/Log.h>
+
+#include "gtest/gtest.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/AString.h>
+#include <media/stagefright/foundation/AStringUtils.h>
+#include <media/stagefright/foundation/base64.h>
+
+#include <utils/RefBase.h>
+#include <utils/String8.h>
+
+namespace {
+const android::String8 kBase64Padding("=");
+};
+
+namespace android {
+
+class Base64Test : public ::testing::Test {
+};
+
+void verifyDecode(const AString* expected, const AString* in) {
+    size_t numTests = 0;
+    while (!expected[numTests].empty())
+        ++numTests;
+
+    for (size_t i = 0; i < numTests; ++i) {
+        // Since android::decodeBase64() requires padding characters,
+        // add them so length of encoded text is exactly a multiple of 4.
+        int remainder = in[i].size() % 4;
+        String8 paddedText(in[i].c_str());
+        if (remainder > 0) {
+            for (int i = 0; i < 4 - remainder; ++i) {
+                paddedText.append(kBase64Padding);
+            }
+        }
+        sp<ABuffer> result = decodeBase64(AString(paddedText.string()));
+
+        ASSERT_EQ(AStringUtils::Compare(expected[i].c_str(),
+                reinterpret_cast<char*>(result->data()),
+                expected[i].size(), false), 0);
+    }
+}
+
+void verifyEncode(const AString* expected, const AString* in) {
+    size_t numTests = 0;
+    while (!expected[numTests].empty())
+        ++numTests;
+
+    AString out = AString("");
+    for (size_t i = 0; i < numTests; ++i) {
+        encodeBase64Url(in[i].c_str(), in[i].size(), &out);
+
+        ASSERT_EQ(AStringUtils::Compare(expected[i].c_str(), out.c_str(),
+                expected[i].size(), false), 0);
+    }
+}
+
+TEST_F(Base64Test, TestDecodeBase64) {
+    const AString base64[] = {
+        AString("SGVsbG8gRnJpZW5kIQ"),
+        AString("R29vZCBkYXkh"),
+        AString("")  // string to signal end of array
+    };
+
+    const AString clearText[] = {
+        AString("Hello Friend!"),
+        AString("Good day!"),
+        AString("")
+    };
+
+    verifyDecode(clearText, base64);
+}
+
+TEST_F(Base64Test, TestDecodeBase64Url) {
+    const AString base64Url[] = {
+        AString("SGVsbG8gRnJpZW5kICE-Pw"),
+        AString("SGVsbG8gRnJpZW5kICE_"),
+        AString("SGVsbG8gPz4-IEZyaWVuZCA_Pg"),
+        AString("")
+    };
+
+    const AString clearText[] = {
+        AString("Hello Friend !>?"),
+        AString("Hello Friend !?"),
+        AString("Hello ?>> Friend ?>"),
+        AString("")
+    };
+
+    verifyDecode(clearText, base64Url);
+}
+
+TEST_F(Base64Test, TestDecodeMalformedBase64) {
+    const AString base64Url[] = {
+        AString("1?GawgguFyGrWKav7AX4VKUg"),  // fail on parsing
+        AString("GawgguFyGrWKav7AX4V???"),    // fail on length not multiple of 4
+        AString("GawgguFyGrWKav7AX4VKUg"),    // ditto
+    };
+
+    for (size_t i = 0; i < 3; ++i) {
+        sp<ABuffer> result = decodeBase64(AString(base64Url[i]));
+        EXPECT_TRUE(result == nullptr);
+    }
+}
+
+TEST_F(Base64Test, TestEncodeBase64) {
+    const AString clearText[] = {
+        AString("Hello Friend!"),
+        AString("Good day!"),
+        AString("")
+    };
+
+    const AString base64[] = {
+        AString("SGVsbG8gRnJpZW5kIQ=="),
+        AString("R29vZCBkYXkh"),
+        AString("")
+    };
+
+    verifyEncode(base64, clearText);
+}
+
+TEST_F(Base64Test, TestEncodeBase64Url) {
+    const AString clearText[] = {
+        AString("Hello Friend !>?"),
+        AString("Hello Friend !?"),
+        AString("Hello ?>> Friend ?>"),
+        AString("")
+    };
+
+    const AString base64Url[] = {
+        AString("SGVsbG8gRnJpZW5kICE-Pw=="),
+        AString("SGVsbG8gRnJpZW5kICE_"),
+        AString("SGVsbG8gPz4-IEZyaWVuZCA_Pg"),
+        AString("")
+    };
+
+    verifyEncode(base64Url, clearText);
+}
+
+} // namespace android
diff --git a/media/libstagefright/include/media/stagefright/ACodec.h b/media/libstagefright/include/media/stagefright/ACodec.h
index d049df5..b9f48c4 100644
--- a/media/libstagefright/include/media/stagefright/ACodec.h
+++ b/media/libstagefright/include/media/stagefright/ACodec.h
@@ -94,7 +94,8 @@
     // some OMX components as auto level, and by others as invalid level.
     static int /* OMX_VIDEO_AVCLEVELTYPE */ getAVCLevelFor(
             int width, int height, int rate, int bitrate,
-            OMX_VIDEO_AVCPROFILETYPE profile = OMX_VIDEO_AVCProfileBaseline);
+            OMX_VIDEO_AVCPROFILEEXTTYPE profile =
+                (OMX_VIDEO_AVCPROFILEEXTTYPE)OMX_VIDEO_AVCProfileBaseline);
 
     // Quirk still supported, even though deprecated
     enum Quirks {
diff --git a/media/libstagefright/include/media/stagefright/MPEG4Writer.h b/media/libstagefright/include/media/stagefright/MPEG4Writer.h
index dd357cc..1c7b4a6 100644
--- a/media/libstagefright/include/media/stagefright/MPEG4Writer.h
+++ b/media/libstagefright/include/media/stagefright/MPEG4Writer.h
@@ -204,7 +204,10 @@
     void lock();
     void unlock();
 
-    void initInternal(int fd);
+    // Init all the internal variables for each recording session. Some variables
+    // will only need to be set for the first recording session and they will stay
+    // the same across all the recording sessions.
+    void initInternal(int fd, bool isFirstSession);
 
     // Acquire lock before calling these methods
     off64_t addSample_l(MediaBuffer *buffer);
diff --git a/media/libstagefright/include/media/stagefright/MetaData.h b/media/libstagefright/include/media/stagefright/MetaData.h
index 65ad3e4..6cfde9c 100644
--- a/media/libstagefright/include/media/stagefright/MetaData.h
+++ b/media/libstagefright/include/media/stagefright/MetaData.h
@@ -213,9 +213,9 @@
     kKeyTemporalLayerId  = 'iLyr', // int32_t, temporal layer-id. 0-based (0 => base layer)
     kKeyTemporalLayerCount = 'cLyr', // int32_t, number of temporal layers encoded
 
-    kKeyGridRows         = 'rows', // int32_t, HEIF grid rows
-    kKeyGridCols         = 'clms', // int32_t, HEIF grid columns
-    kKeyIccProfile       = 'prof', // raw data
+    kKeyGridWidth        = 'grdW', // int32_t, HEIF grid width
+    kKeyGridHeight       = 'grdH', // int32_t, HEIF grid height
+    kKeyIccProfile       = 'prof', // raw data, ICC prifile data
 };
 
 enum {
diff --git a/media/libstagefright/omx/Android.bp b/media/libstagefright/omx/Android.bp
index bb05740..46f2dd1 100644
--- a/media/libstagefright/omx/Android.bp
+++ b/media/libstagefright/omx/Android.bp
@@ -65,6 +65,7 @@
         "libhidlmemory",
         "libhidltransport",
         "libnativewindow", // TODO(b/62923479): use header library
+        "libvndksupport",
         "android.hidl.memory@1.0",
         "android.hidl.token@1.0-utils",
         "android.hardware.media@1.0",
diff --git a/media/libstagefright/omx/OMXMaster.cpp b/media/libstagefright/omx/OMXMaster.cpp
index fd97fdc..0967b5f 100644
--- a/media/libstagefright/omx/OMXMaster.cpp
+++ b/media/libstagefright/omx/OMXMaster.cpp
@@ -22,6 +22,8 @@
 #include <media/stagefright/omx/SoftOMXPlugin.h>
 #include <media/stagefright/foundation/ADebug.h>
 
+#include <vndksupport/linker.h>
+
 #include <dlfcn.h>
 #include <fcntl.h>
 
@@ -67,7 +69,7 @@
 }
 
 void OMXMaster::addPlugin(const char *libname) {
-    mVendorLibHandle = dlopen(libname, RTLD_NOW);
+    mVendorLibHandle = android_load_sphal_library(libname, RTLD_NOW);
 
     if (mVendorLibHandle == NULL) {
         return;
diff --git a/media/libstagefright/rtsp/ARTPSource.cpp b/media/libstagefright/rtsp/ARTPSource.cpp
index 576a0a4..4827cd2 100644
--- a/media/libstagefright/rtsp/ARTPSource.cpp
+++ b/media/libstagefright/rtsp/ARTPSource.cpp
@@ -43,7 +43,10 @@
         const sp<AMessage> &notify)
     : mID(id),
       mHighestSeqNumber(0),
+      mPrevExpected(0),
+      mBaseSeqNumber(0),
       mNumBuffersReceived(0),
+      mPrevNumBuffersReceived(0),
       mLastNTPTime(0),
       mLastNTPTimeUpdateUs(0),
       mIssueFIRRequests(false),
@@ -107,6 +110,7 @@
 
     if (mNumBuffersReceived++ == 0) {
         mHighestSeqNumber = seqNum;
+        mBaseSeqNumber = seqNum;
         mQueue.push_back(buffer);
         return true;
     }
@@ -226,6 +230,22 @@
         return;
     }
 
+    uint8_t fraction = 0;
+
+    // According to appendix A.3 in RFC 3550
+    uint32_t expected = mHighestSeqNumber - mBaseSeqNumber + 1;
+    int64_t intervalExpected = expected - mPrevExpected;
+    int64_t intervalReceived = mNumBuffersReceived - mPrevNumBuffersReceived;
+    int64_t intervalPacketLost = intervalExpected - intervalReceived;
+
+    if (intervalExpected > 0 && intervalPacketLost > 0) {
+        fraction = (intervalPacketLost << 8) / intervalExpected;
+    }
+
+    mPrevExpected = expected;
+    mPrevNumBuffersReceived = mNumBuffersReceived;
+    int32_t cumulativePacketLost = (int32_t)expected - mNumBuffersReceived;
+
     uint8_t *data = buffer->data() + buffer->size();
 
     data[0] = 0x80 | 1;
@@ -242,11 +262,11 @@
     data[10] = (mID >> 8) & 0xff;
     data[11] = mID & 0xff;
 
-    data[12] = 0x00;  // fraction lost
+    data[12] = fraction;  // fraction lost
 
-    data[13] = 0x00;  // cumulative lost
-    data[14] = 0x00;
-    data[15] = 0x00;
+    data[13] = cumulativePacketLost >> 16;  // cumulative lost
+    data[14] = (cumulativePacketLost >> 8) & 0xff;
+    data[15] = cumulativePacketLost & 0xff;
 
     data[16] = mHighestSeqNumber >> 24;
     data[17] = (mHighestSeqNumber >> 16) & 0xff;
diff --git a/media/libstagefright/rtsp/ARTPSource.h b/media/libstagefright/rtsp/ARTPSource.h
index b70f94e..f44e83f 100644
--- a/media/libstagefright/rtsp/ARTPSource.h
+++ b/media/libstagefright/rtsp/ARTPSource.h
@@ -49,7 +49,10 @@
 private:
     uint32_t mID;
     uint32_t mHighestSeqNumber;
+    uint32_t mPrevExpected;
+    uint32_t mBaseSeqNumber;
     int32_t mNumBuffersReceived;
+    int32_t mPrevNumBuffersReceived;
 
     List<sp<ABuffer> > mQueue;
     sp<ARTPAssembler> mAssembler;
diff --git a/media/mtp/Android.bp b/media/mtp/Android.bp
index 5d5ae49..543ad5c 100644
--- a/media/mtp/Android.bp
+++ b/media/mtp/Android.bp
@@ -17,13 +17,13 @@
 cc_library_shared {
     name: "libmtp",
     srcs: [
-        "AsyncIO.cpp",
         "MtpDataPacket.cpp",
         "MtpDebug.cpp",
         "MtpDevHandle.cpp",
         "MtpDevice.cpp",
         "MtpDeviceInfo.cpp",
         "MtpEventPacket.cpp",
+        "MtpFfsCompatHandle.cpp",
         "MtpFfsHandle.cpp",
         "MtpObjectInfo.cpp",
         "MtpPacket.cpp",
@@ -35,6 +35,7 @@
         "MtpStorageInfo.cpp",
         "MtpStringBuffer.cpp",
         "MtpUtils.cpp",
+        "PosixAsyncIO.cpp",
     ],
     export_include_dirs: ["."],
     cflags: [
@@ -45,6 +46,7 @@
         "-Werror",
     ],
     shared_libs: [
+        "libasyncio",
         "libbase",
         "libutils",
         "liblog",
diff --git a/media/mtp/AsyncIO.cpp b/media/mtp/AsyncIO.cpp
deleted file mode 100644
index bfb07dc..0000000
--- a/media/mtp/AsyncIO.cpp
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright (C) 2016 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 <android-base/logging.h>
-#include <condition_variable>
-#include <memory>
-#include <mutex>
-#include <queue>
-
-#include "AsyncIO.h"
-
-namespace {
-
-void read_func(struct aiocb *aiocbp) {
-    aiocbp->ret = TEMP_FAILURE_RETRY(pread(aiocbp->aio_fildes,
-                aiocbp->aio_buf, aiocbp->aio_nbytes, aiocbp->aio_offset));
-    if (aiocbp->ret == -1) aiocbp->error = errno;
-}
-
-void write_func(struct aiocb *aiocbp) {
-    aiocbp->ret = TEMP_FAILURE_RETRY(pwrite(aiocbp->aio_fildes,
-                aiocbp->aio_buf, aiocbp->aio_nbytes, aiocbp->aio_offset));
-    if (aiocbp->ret == -1) aiocbp->error = errno;
-}
-
-void splice_read_func(struct aiocb *aiocbp) {
-    loff_t long_offset = aiocbp->aio_offset;
-    aiocbp->ret = TEMP_FAILURE_RETRY(splice(aiocbp->aio_fildes,
-                &long_offset, aiocbp->aio_sink,
-                NULL, aiocbp->aio_nbytes, 0));
-    if (aiocbp->ret == -1) aiocbp->error = errno;
-}
-
-void splice_write_func(struct aiocb *aiocbp) {
-    loff_t long_offset = aiocbp->aio_offset;
-    aiocbp->ret = TEMP_FAILURE_RETRY(splice(aiocbp->aio_fildes, NULL,
-                aiocbp->aio_sink, &long_offset,
-                aiocbp->aio_nbytes, 0));
-    if (aiocbp->ret == -1) aiocbp->error = errno;
-}
-
-std::queue<std::unique_ptr<struct aiocb>> queue;
-std::mutex queue_lock;
-std::condition_variable queue_cond;
-std::condition_variable write_cond;
-int done = 1;
-void splice_write_pool_func(int) {
-    while(1) {
-        std::unique_lock<std::mutex> lk(queue_lock);
-        queue_cond.wait(lk, []{return !queue.empty() || done;});
-        if (queue.empty() && done) {
-            return;
-        }
-        std::unique_ptr<struct aiocb> aiocbp = std::move(queue.front());
-        queue.pop();
-        lk.unlock();
-        write_cond.notify_one();
-        splice_write_func(aiocbp.get());
-        close(aiocbp->aio_fildes);
-    }
-}
-
-void write_pool_func(int) {
-    while(1) {
-        std::unique_lock<std::mutex> lk(queue_lock);
-        queue_cond.wait(lk, []{return !queue.empty() || done;});
-        if (queue.empty() && done) {
-            return;
-        }
-        std::unique_ptr<struct aiocb> aiocbp = std::move(queue.front());
-        queue.pop();
-        lk.unlock();
-        write_cond.notify_one();
-        aiocbp->ret = TEMP_FAILURE_RETRY(pwrite(aiocbp->aio_fildes,
-                    aiocbp->aio_pool_buf.get(), aiocbp->aio_nbytes, aiocbp->aio_offset));
-        if (aiocbp->ret == -1) aiocbp->error = errno;
-    }
-}
-
-constexpr int NUM_THREADS = 1;
-constexpr int MAX_QUEUE_SIZE = 10;
-std::thread pool[NUM_THREADS];
-
-} // end anonymous namespace
-
-aiocb::~aiocb() {
-    CHECK(!thread.joinable());
-}
-
-void aio_pool_init(void(f)(int)) {
-    CHECK(done == 1);
-    done = 0;
-    for (int i = 0; i < NUM_THREADS; i++) {
-        pool[i] = std::thread(f, i);
-    }
-}
-
-void aio_pool_splice_init() {
-    aio_pool_init(splice_write_pool_func);
-}
-
-void aio_pool_write_init() {
-    aio_pool_init(write_pool_func);
-}
-
-void aio_pool_end() {
-    done = 1;
-    for (int i = 0; i < NUM_THREADS; i++) {
-        std::unique_lock<std::mutex> lk(queue_lock);
-        lk.unlock();
-        queue_cond.notify_one();
-    }
-
-    for (int i = 0; i < NUM_THREADS; i++) {
-        pool[i].join();
-    }
-}
-
-// used for both writes and splices depending on which init was used before.
-int aio_pool_write(struct aiocb *aiocbp) {
-    std::unique_lock<std::mutex> lk(queue_lock);
-    write_cond.wait(lk, []{return queue.size() < MAX_QUEUE_SIZE;});
-    queue.push(std::unique_ptr<struct aiocb>(aiocbp));
-    lk.unlock();
-    queue_cond.notify_one();
-    return 0;
-}
-
-int aio_read(struct aiocb *aiocbp) {
-    aiocbp->thread = std::thread(read_func, aiocbp);
-    return 0;
-}
-
-int aio_write(struct aiocb *aiocbp) {
-    aiocbp->thread = std::thread(write_func, aiocbp);
-    return 0;
-}
-
-int aio_splice_read(struct aiocb *aiocbp) {
-    aiocbp->thread = std::thread(splice_read_func, aiocbp);
-    return 0;
-}
-
-int aio_splice_write(struct aiocb *aiocbp) {
-    aiocbp->thread = std::thread(splice_write_func, aiocbp);
-    return 0;
-}
-
-int aio_error(const struct aiocb *aiocbp) {
-    return aiocbp->error;
-}
-
-ssize_t aio_return(struct aiocb *aiocbp) {
-    return aiocbp->ret;
-}
-
-int aio_suspend(struct aiocb *aiocbp[], int n,
-        const struct timespec *) {
-    for (int i = 0; i < n; i++) {
-        aiocbp[i]->thread.join();
-    }
-    return 0;
-}
-
-int aio_cancel(int, struct aiocb *) {
-    // Not implemented
-    return -1;
-}
-
diff --git a/media/mtp/AsyncIO.h b/media/mtp/AsyncIO.h
deleted file mode 100644
index ed80828..0000000
--- a/media/mtp/AsyncIO.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2016 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 _ASYNCIO_H
-#define _ASYNCIO_H
-
-#include <fcntl.h>
-#include <linux/aio_abi.h>
-#include <memory>
-#include <signal.h>
-#include <sys/cdefs.h>
-#include <sys/types.h>
-#include <time.h>
-#include <thread>
-#include <unistd.h>
-
-/**
- * Provides a subset of POSIX aio operations, as well
- * as similar operations with splice and threadpools.
- */
-
-struct aiocb {
-    int aio_fildes;     // Assumed to be the source for splices
-    void *aio_buf;      // Unused for splices
-
-    // Used for threadpool operations only, freed automatically
-    std::unique_ptr<char[]> aio_pool_buf;
-
-    off_t aio_offset;
-    size_t aio_nbytes;
-
-    int aio_sink;       // Unused for non splice r/w
-
-    // Used internally
-    std::thread thread;
-    ssize_t ret;
-    int error;
-
-    ~aiocb();
-};
-
-// Submit a request for IO to be completed
-int aio_read(struct aiocb *);
-int aio_write(struct aiocb *);
-int aio_splice_read(struct aiocb *);
-int aio_splice_write(struct aiocb *);
-
-// Suspend current thread until given IO is complete, at which point
-// its return value and any errors can be accessed
-// All submitted requests must have a corresponding suspend.
-// aiocb->aio_buf must refer to valid memory until after the suspend call
-int aio_suspend(struct aiocb *[], int, const struct timespec *);
-int aio_error(const struct aiocb *);
-ssize_t aio_return(struct aiocb *);
-
-// (Currently unimplemented)
-int aio_cancel(int, struct aiocb *);
-
-// Initialize a threadpool to perform IO. Only one pool can be
-// running at a time.
-void aio_pool_write_init();
-void aio_pool_splice_init();
-// Suspend current thread until all queued work is complete, then ends the threadpool
-void aio_pool_end();
-// Submit IO work for the threadpool to complete. Memory associated with the work is
-// freed automatically when the work is complete.
-int aio_pool_write(struct aiocb *);
-
-#endif // ASYNCIO_H
-
diff --git a/media/mtp/IMtpHandle.h b/media/mtp/IMtpHandle.h
index 0557596..c65bdd0 100644
--- a/media/mtp/IMtpHandle.h
+++ b/media/mtp/IMtpHandle.h
@@ -18,13 +18,13 @@
 
 #include <linux/usb/f_mtp.h>
 
-constexpr char FFS_MTP_EP0[] = "/dev/usb-ffs/mtp/ep0";
+namespace android {
 
 class IMtpHandle {
 public:
     // Return number of bytes read/written, or -1 and errno is set
-    virtual int read(void *data, int len) = 0;
-    virtual int write(const void *data, int len) = 0;
+    virtual int read(void *data, size_t len) = 0;
+    virtual int write(const void *data, size_t len) = 0;
 
     // Return 0 if send/receive is successful, or -1 and errno is set
     virtual int receiveFile(mtp_file_range mfr, bool zero_packet) = 0;
@@ -40,8 +40,7 @@
     virtual ~IMtpHandle() {}
 };
 
-IMtpHandle *get_ffs_handle();
-IMtpHandle *get_mtp_handle();
+}
 
 #endif // _IMTP_HANDLE_H
 
diff --git a/media/mtp/MtpDataPacket.h b/media/mtp/MtpDataPacket.h
index a449d6f..1ddb821 100644
--- a/media/mtp/MtpDataPacket.h
+++ b/media/mtp/MtpDataPacket.h
@@ -20,12 +20,12 @@
 #include "MtpPacket.h"
 #include "mtp.h"
 
-class IMtpHandle;
 struct usb_device;
 struct usb_request;
 
 namespace android {
 
+class IMtpHandle;
 class MtpStringBuffer;
 
 class MtpDataPacket : public MtpPacket {
diff --git a/media/mtp/MtpDevHandle.cpp b/media/mtp/MtpDevHandle.cpp
index 9aa0aec..6aa57ac 100644
--- a/media/mtp/MtpDevHandle.cpp
+++ b/media/mtp/MtpDevHandle.cpp
@@ -14,57 +14,37 @@
  * limitations under the License.
  */
 
-#include <utils/Log.h>
-#include <fcntl.h>
-#include <sys/stat.h>
+#include <android-base/logging.h>
 #include <cutils/properties.h>
 #include <dirent.h>
 #include <errno.h>
+#include <fcntl.h>
 #include <linux/usb/ch9.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/stat.h>
 #include <sys/ioctl.h>
 #include <sys/types.h>
 #include <sys/endian.h>
 #include <unistd.h>
 
-#include <android-base/logging.h>
-#include <android-base/unique_fd.h>
-#include "IMtpHandle.h"
+#include "MtpDevHandle.h"
+
+namespace android {
 
 constexpr char mtp_dev_path[] = "/dev/mtp_usb";
 
-class MtpDevHandle : public IMtpHandle {
-private:
-    android::base::unique_fd mFd;
-
-public:
-    MtpDevHandle();
-    ~MtpDevHandle();
-    int read(void *data, int len);
-    int write(const void *data, int len);
-
-    int receiveFile(mtp_file_range mfr, bool);
-    int sendFile(mtp_file_range mfr);
-    int sendEvent(mtp_event me);
-
-    int start();
-    void close();
-
-    int configure(bool ptp);
-};
-
 MtpDevHandle::MtpDevHandle()
     : mFd(-1) {};
 
 MtpDevHandle::~MtpDevHandle() {}
 
-int MtpDevHandle::read(void *data, int len) {
+int MtpDevHandle::read(void *data, size_t len) {
     return ::read(mFd, data, len);
 }
 
-int MtpDevHandle::write(const void *data, int len) {
+int MtpDevHandle::write(const void *data, size_t len) {
     return ::write(mFd, data, len);
 }
 
@@ -81,7 +61,7 @@
 }
 
 int MtpDevHandle::start() {
-    mFd = android::base::unique_fd(TEMP_FAILURE_RETRY(open(mtp_dev_path, O_RDWR)));
+    mFd.reset(TEMP_FAILURE_RETRY(open(mtp_dev_path, O_RDWR)));
     if (mFd == -1) return -1;
     return 0;
 }
@@ -95,6 +75,4 @@
     return 0;
 }
 
-IMtpHandle *get_mtp_handle() {
-    return new MtpDevHandle();
-}
+} // namespace android
diff --git a/media/mtp/MtpDevHandle.h b/media/mtp/MtpDevHandle.h
new file mode 100644
index 0000000..b0480ed
--- /dev/null
+++ b/media/mtp/MtpDevHandle.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 _MTP_DEV_HANDLE_H
+#define _MTP_DEV_HANDLE_H
+
+#include <android-base/unique_fd.h>
+#include "IMtpHandle.h"
+
+namespace android {
+
+class MtpDevHandle : public IMtpHandle {
+private:
+    android::base::unique_fd mFd;
+
+public:
+    MtpDevHandle();
+    ~MtpDevHandle();
+    int read(void *data, size_t len);
+    int write(const void *data, size_t len);
+
+    int receiveFile(mtp_file_range mfr, bool);
+    int sendFile(mtp_file_range mfr);
+    int sendEvent(mtp_event me);
+
+    int start();
+    void close();
+
+    int configure(bool ptp);
+};
+
+} // namespace android
+
+#endif // _MTP_FFS_HANDLE_H
diff --git a/media/mtp/MtpFfsCompatHandle.cpp b/media/mtp/MtpFfsCompatHandle.cpp
new file mode 100644
index 0000000..3dd73f3
--- /dev/null
+++ b/media/mtp/MtpFfsCompatHandle.cpp
@@ -0,0 +1,342 @@
+/*
+ * 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 <android-base/logging.h>
+#include <android-base/properties.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/functionfs.h>
+#include <mutex>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/endian.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "PosixAsyncIO.h"
+#include "MtpFfsCompatHandle.h"
+#include "mtp.h"
+
+#define FUNCTIONFS_ENDPOINT_ALLOC       _IOR('g', 231, __u32)
+
+namespace {
+
+// Must be divisible by all max packet size values
+constexpr int MAX_FILE_CHUNK_SIZE = 3145728;
+
+// Safe values since some devices cannot handle large DMAs
+// To get good performance, override these with
+// higher values per device using the properties
+// sys.usb.ffs.max_read and sys.usb.ffs.max_write
+constexpr int USB_FFS_MAX_WRITE = MTP_BUFFER_SIZE;
+constexpr int USB_FFS_MAX_READ = MTP_BUFFER_SIZE;
+
+static_assert(USB_FFS_MAX_WRITE > 0, "Max r/w values must be > 0!");
+static_assert(USB_FFS_MAX_READ > 0, "Max r/w values must be > 0!");
+
+constexpr unsigned int MAX_MTP_FILE_SIZE = 0xFFFFFFFF;
+
+constexpr size_t ENDPOINT_ALLOC_RETRIES = 10;
+
+} // anonymous namespace
+
+namespace android {
+
+MtpFfsCompatHandle::MtpFfsCompatHandle() :
+    mMaxWrite(USB_FFS_MAX_WRITE),
+    mMaxRead(USB_FFS_MAX_READ) {}
+
+MtpFfsCompatHandle::~MtpFfsCompatHandle() {}
+
+int MtpFfsCompatHandle::writeHandle(int fd, const void* data, size_t len) {
+    int ret = 0;
+    const char* buf = static_cast<const char*>(data);
+    while (len > 0) {
+        int write_len = std::min(mMaxWrite, len);
+        int n = TEMP_FAILURE_RETRY(::write(fd, buf, write_len));
+
+        if (n < 0) {
+            PLOG(ERROR) << "write ERROR: fd = " << fd << ", n = " << n;
+            return -1;
+        } else if (n < write_len) {
+            errno = EIO;
+            PLOG(ERROR) << "less written than expected";
+            return -1;
+        }
+        buf += n;
+        len -= n;
+        ret += n;
+    }
+    return ret;
+}
+
+int MtpFfsCompatHandle::readHandle(int fd, void* data, size_t len) {
+    int ret = 0;
+    char* buf = static_cast<char*>(data);
+    while (len > 0) {
+        int read_len = std::min(mMaxRead, len);
+        int n = TEMP_FAILURE_RETRY(::read(fd, buf, read_len));
+        if (n < 0) {
+            PLOG(ERROR) << "read ERROR: fd = " << fd << ", n = " << n;
+            return -1;
+        }
+        ret += n;
+        if (n < read_len) // done reading early
+            break;
+        buf += n;
+        len -= n;
+    }
+    return ret;
+}
+
+int MtpFfsCompatHandle::start() {
+    mLock.lock();
+
+    if (!openEndpoints())
+        return -1;
+
+    for (unsigned i = 0; i < NUM_IO_BUFS; i++) {
+        mIobuf[i].bufs.resize(MAX_FILE_CHUNK_SIZE);
+        posix_madvise(mIobuf[i].bufs.data(), MAX_FILE_CHUNK_SIZE,
+                POSIX_MADV_SEQUENTIAL | POSIX_MADV_WILLNEED);
+    }
+
+    // Get device specific r/w size
+    mMaxWrite = android::base::GetIntProperty("sys.usb.ffs.max_write", USB_FFS_MAX_WRITE);
+    mMaxRead = android::base::GetIntProperty("sys.usb.ffs.max_read", USB_FFS_MAX_READ);
+
+    size_t attempts = 0;
+    while (mMaxWrite >= USB_FFS_MAX_WRITE && mMaxRead >= USB_FFS_MAX_READ &&
+            attempts < ENDPOINT_ALLOC_RETRIES) {
+        // If larger contiguous chunks of memory aren't available, attempt to try
+        // smaller allocations.
+        if (ioctl(mBulkIn, FUNCTIONFS_ENDPOINT_ALLOC, static_cast<__u32>(mMaxWrite)) ||
+            ioctl(mBulkOut, FUNCTIONFS_ENDPOINT_ALLOC, static_cast<__u32>(mMaxRead))) {
+            if (errno == ENODEV) {
+                // Driver hasn't enabled endpoints yet.
+                std::this_thread::sleep_for(std::chrono::milliseconds(100));
+                attempts += 1;
+                continue;
+            }
+            mMaxWrite /= 2;
+            mMaxRead /=2;
+        } else {
+            return 0;
+        }
+    }
+    // Try to start MtpServer anyway, with the smallest max r/w values
+    mMaxWrite = USB_FFS_MAX_WRITE;
+    mMaxRead = USB_FFS_MAX_READ;
+    PLOG(ERROR) << "Functionfs could not allocate any memory!";
+    return 0;
+}
+
+int MtpFfsCompatHandle::read(void* data, size_t len) {
+    return readHandle(mBulkOut, data, len);
+}
+
+int MtpFfsCompatHandle::write(const void* data, size_t len) {
+    return writeHandle(mBulkIn, data, len);
+}
+
+int MtpFfsCompatHandle::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;
+    uint64_t offset = mfr.offset;
+    int packet_size = getPacketSize(mBulkOut);
+
+    unsigned char *data = mIobuf[0].bufs.data();
+    unsigned char *data2 = mIobuf[1].bufs.data();
+
+    struct aiocb aio;
+    aio.aio_fildes = mfr.fd;
+    aio.aio_buf = nullptr;
+    struct aiocb *aiol[] = {&aio};
+    int ret = -1;
+    size_t length;
+    bool read = false;
+    bool write = false;
+
+    posix_fadvise(mfr.fd, 0, 0, POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE);
+
+    // Break down the file into pieces that fit in buffers
+    while (file_length > 0 || write) {
+        if (file_length > 0) {
+            length = std::min(static_cast<uint32_t>(MAX_FILE_CHUNK_SIZE), file_length);
+
+            // Read data from USB, handle errors after waiting for write thread.
+            ret = readHandle(mBulkOut, data, length);
+
+            if (file_length != MAX_MTP_FILE_SIZE && ret < static_cast<int>(length)) {
+                ret = -1;
+                errno = EIO;
+            }
+            read = true;
+        }
+
+        if (write) {
+            // get the return status of the last write request
+            aio_suspend(aiol, 1, nullptr);
+
+            int written = aio_return(&aio);
+            if (written == -1) {
+                errno = aio_error(&aio);
+                return -1;
+            }
+            if (static_cast<size_t>(written) < aio.aio_nbytes) {
+                errno = EIO;
+                return -1;
+            }
+            write = false;
+        }
+
+        // If there was an error reading above
+        if (ret == -1) {
+            return -1;
+        }
+
+        if (read) {
+            if (file_length == MAX_MTP_FILE_SIZE) {
+                // For larger files, receive until a short packet is received.
+                if (static_cast<size_t>(ret) < length) {
+                    file_length = 0;
+                }
+            } else {
+                file_length -= ret;
+            }
+            // Enqueue a new write request
+            aio_prepare(&aio, data, length, offset);
+            aio_write(&aio);
+
+            offset += ret;
+            std::swap(data, data2);
+
+            write = true;
+            read = false;
+        }
+    }
+    // Receive an empty packet if size is a multiple of the endpoint size.
+    if (ret % packet_size == 0 || zero_packet) {
+        if (TEMP_FAILURE_RETRY(::read(mBulkOut, data, packet_size)) != 0) {
+            return -1;
+        }
+    }
+    return 0;
+}
+
+int MtpFfsCompatHandle::sendFile(mtp_file_range mfr) {
+    uint64_t file_length = mfr.length;
+    uint32_t given_length = std::min(static_cast<uint64_t>(MAX_MTP_FILE_SIZE),
+            file_length + sizeof(mtp_data_header));
+    uint64_t offset = mfr.offset;
+    int packet_size = getPacketSize(mBulkIn);
+
+    // If file_length is larger than a size_t, truncating would produce the wrong comparison.
+    // Instead, promote the left side to 64 bits, then truncate the small result.
+    int init_read_len = std::min(
+            static_cast<uint64_t>(packet_size - sizeof(mtp_data_header)), file_length);
+
+    unsigned char *data = mIobuf[0].bufs.data();
+    unsigned char *data2 = mIobuf[1].bufs.data();
+
+    posix_fadvise(mfr.fd, 0, 0, POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE);
+
+    struct aiocb aio;
+    aio.aio_fildes = mfr.fd;
+    struct aiocb *aiol[] = {&aio};
+    int ret, length;
+    int error = 0;
+    bool read = false;
+    bool write = false;
+
+    // Send the header data
+    mtp_data_header *header = reinterpret_cast<mtp_data_header*>(data);
+    header->length = htole32(given_length);
+    header->type = htole16(2); /* data packet */
+    header->command = htole16(mfr.command);
+    header->transaction_id = htole32(mfr.transaction_id);
+
+    // Some hosts don't support header/data separation even though MTP allows it
+    // Handle by filling first packet with initial file data
+    if (TEMP_FAILURE_RETRY(pread(mfr.fd, reinterpret_cast<char*>(data) +
+                    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;
+    file_length -= init_read_len;
+    offset += init_read_len;
+    ret = init_read_len + sizeof(mtp_data_header);
+
+    // Break down the file into pieces that fit in buffers
+    while (file_length > 0) {
+        if (read) {
+            // Wait for the previous read to finish
+            aio_suspend(aiol, 1, nullptr);
+            ret = aio_return(&aio);
+            if (ret == -1) {
+                errno = aio_error(&aio);
+                return -1;
+            }
+            if (static_cast<size_t>(ret) < aio.aio_nbytes) {
+                errno = EIO;
+                return -1;
+            }
+
+            file_length -= ret;
+            offset += ret;
+            std::swap(data, data2);
+            read = false;
+            write = true;
+        }
+
+        if (error == -1) {
+            return -1;
+        }
+
+        if (file_length > 0) {
+            length = std::min(static_cast<uint64_t>(MAX_FILE_CHUNK_SIZE), file_length);
+            // Queue up another read
+            aio_prepare(&aio, data, length, offset);
+            aio_read(&aio);
+            read = true;
+        }
+
+        if (write) {
+            if (writeHandle(mBulkIn, data2, ret) == -1) {
+                error = -1;
+            }
+            write = false;
+        }
+    }
+
+    if (ret % packet_size == 0) {
+        // If the last packet wasn't short, send a final empty packet
+        if (TEMP_FAILURE_RETRY(::write(mBulkIn, data, 0)) != 0) {
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+} // namespace android
+
diff --git a/media/mtp/MtpFfsCompatHandle.h b/media/mtp/MtpFfsCompatHandle.h
new file mode 100644
index 0000000..cd61482
--- /dev/null
+++ b/media/mtp/MtpFfsCompatHandle.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2016 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 _MTP_FFS_COMPAT_HANDLE_H
+#define _MTP_FFS_COMPAT_HANDLE_H
+
+#include <MtpFfsHandle.h>
+
+namespace android {
+
+template <class T> class MtpFfsHandleTest;
+
+class MtpFfsCompatHandle : public MtpFfsHandle {
+    template <class T> friend class android::MtpFfsHandleTest;
+private:
+    int writeHandle(int fd, const void *data, size_t len);
+    int readHandle(int fd, void *data, size_t len);
+
+    size_t mMaxWrite;
+    size_t mMaxRead;
+
+public:
+    int read(void* data, size_t len) override;
+    int write(const void* data, size_t len) override;
+    int receiveFile(mtp_file_range mfr, bool zero_packet) override;
+    int sendFile(mtp_file_range mfr) override;
+
+    /**
+     * Open ffs endpoints and allocate necessary kernel and user memory.
+     * Will sleep until endpoints are enabled, for up to 1 second.
+     */
+    int start() override;
+
+    MtpFfsCompatHandle();
+    ~MtpFfsCompatHandle();
+};
+
+} // namespace android
+
+#endif // _MTP_FFS_COMPAT_HANDLE_H
+
diff --git a/media/mtp/MtpFfsHandle.cpp b/media/mtp/MtpFfsHandle.cpp
index 4132fed..89b20e5 100644
--- a/media/mtp/MtpFfsHandle.cpp
+++ b/media/mtp/MtpFfsHandle.cpp
@@ -16,32 +16,29 @@
 
 #include <android-base/logging.h>
 #include <android-base/properties.h>
+#include <asyncio/AsyncIO.h>
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <linux/usb/ch9.h>
 #include <linux/usb/functionfs.h>
-#include <mutex>
+#include <memory>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <sys/endian.h>
+#include <sys/eventfd.h>
 #include <sys/ioctl.h>
 #include <sys/mman.h>
+#include <sys/poll.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
-#include <vector>
 
-#include "AsyncIO.h"
+#include "PosixAsyncIO.h"
 #include "MtpFfsHandle.h"
 #include "mtp.h"
 
-#define cpu_to_le16(x)  htole16(x)
-#define cpu_to_le32(x)  htole32(x)
-
-#define FUNCTIONFS_ENDPOINT_ALLOC       _IOR('g', 231, __u32)
-
 namespace {
 
 constexpr char FFS_MTP_EP_IN[] = "/dev/usb-ffs/mtp/ep1";
@@ -51,23 +48,18 @@
 constexpr int MAX_PACKET_SIZE_FS = 64;
 constexpr int MAX_PACKET_SIZE_HS = 512;
 constexpr int MAX_PACKET_SIZE_SS = 1024;
+constexpr int MAX_PACKET_SIZE_EV = 28;
 
-// Must be divisible by all max packet size values
-constexpr int MAX_FILE_CHUNK_SIZE = 3145728;
+constexpr unsigned AIO_BUFS_MAX = 128;
+constexpr unsigned AIO_BUF_LEN = 16384;
 
-// Safe values since some devices cannot handle large DMAs
-// To get good performance, override these with
-// higher values per device using the properties
-// sys.usb.ffs.max_read and sys.usb.ffs.max_write
-constexpr int USB_FFS_MAX_WRITE = MTP_BUFFER_SIZE;
-constexpr int USB_FFS_MAX_READ = MTP_BUFFER_SIZE;
+constexpr unsigned FFS_NUM_EVENTS = 5;
 
-static_assert(USB_FFS_MAX_WRITE > 0, "Max r/w values must be > 0!");
-static_assert(USB_FFS_MAX_READ > 0, "Max r/w values must be > 0!");
+constexpr unsigned MAX_FILE_CHUNK_SIZE = AIO_BUFS_MAX * AIO_BUF_LEN;
 
-constexpr unsigned int MAX_MTP_FILE_SIZE = 0xFFFFFFFF;
+constexpr uint32_t MAX_MTP_FILE_SIZE = 0xFFFFFFFF;
 
-constexpr size_t ENDPOINT_ALLOC_RETRIES = 10;
+struct timespec ZERO_TIMEOUT = { 0, 0 };
 
 struct func_desc {
     struct usb_interface_descriptor intf;
@@ -143,12 +135,12 @@
     .wMaxPacketSize = MAX_PACKET_SIZE_FS,
 };
 
-const struct usb_endpoint_descriptor_no_audio fs_intr = {
+const struct usb_endpoint_descriptor_no_audio intr = {
     .bLength = USB_DT_ENDPOINT_SIZE,
     .bDescriptorType = USB_DT_ENDPOINT,
     .bEndpointAddress = 3 | USB_DIR_IN,
     .bmAttributes = USB_ENDPOINT_XFER_INT,
-    .wMaxPacketSize = MAX_PACKET_SIZE_FS,
+    .wMaxPacketSize = MAX_PACKET_SIZE_EV,
     .bInterval = 6,
 };
 
@@ -168,15 +160,6 @@
     .wMaxPacketSize = MAX_PACKET_SIZE_HS,
 };
 
-const struct usb_endpoint_descriptor_no_audio hs_intr = {
-    .bLength = USB_DT_ENDPOINT_SIZE,
-    .bDescriptorType = USB_DT_ENDPOINT,
-    .bEndpointAddress = 3 | USB_DIR_IN,
-    .bmAttributes = USB_ENDPOINT_XFER_INT,
-    .wMaxPacketSize = MAX_PACKET_SIZE_HS,
-    .bInterval = 6,
-};
-
 const struct usb_endpoint_descriptor_no_audio ss_sink = {
     .bLength = USB_DT_ENDPOINT_SIZE,
     .bDescriptorType = USB_DT_ENDPOINT,
@@ -193,15 +176,6 @@
     .wMaxPacketSize = MAX_PACKET_SIZE_SS,
 };
 
-const struct usb_endpoint_descriptor_no_audio ss_intr = {
-    .bLength = USB_DT_ENDPOINT_SIZE,
-    .bDescriptorType = USB_DT_ENDPOINT,
-    .bEndpointAddress = 3 | USB_DIR_IN,
-    .bmAttributes = USB_ENDPOINT_XFER_INT,
-    .wMaxPacketSize = MAX_PACKET_SIZE_SS,
-    .bInterval = 6,
-};
-
 const struct usb_ss_ep_comp_descriptor ss_sink_comp = {
     .bLength = sizeof(ss_sink_comp),
     .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
@@ -223,14 +197,14 @@
     .intf = mtp_interface_desc,
     .sink = fs_sink,
     .source = fs_source,
-    .intr = fs_intr,
+    .intr = intr,
 };
 
 const struct func_desc mtp_hs_descriptors = {
     .intf = mtp_interface_desc,
     .sink = hs_sink,
     .source = hs_source,
-    .intr = hs_intr,
+    .intr = intr,
 };
 
 const struct ss_func_desc mtp_ss_descriptors = {
@@ -239,7 +213,7 @@
     .sink_comp = ss_sink_comp,
     .source = ss_source,
     .source_comp = ss_source_comp,
-    .intr = ss_intr,
+    .intr = intr,
     .intr_comp = ss_intr_comp,
 };
 
@@ -247,14 +221,14 @@
     .intf = ptp_interface_desc,
     .sink = fs_sink,
     .source = fs_source,
-    .intr = fs_intr,
+    .intr = intr,
 };
 
 const struct func_desc ptp_hs_descriptors = {
     .intf = ptp_interface_desc,
     .sink = hs_sink,
     .source = hs_source,
-    .intr = hs_intr,
+    .intr = intr,
 };
 
 const struct ss_func_desc ptp_ss_descriptors = {
@@ -263,7 +237,7 @@
     .sink_comp = ss_sink_comp,
     .source = ss_source,
     .source_comp = ss_source_comp,
-    .intr = ss_intr,
+    .intr = intr,
     .intr_comp = ss_intr_comp,
 };
 
@@ -276,24 +250,37 @@
     } __attribute__((packed)) lang0;
 } __attribute__((packed)) strings = {
     .header = {
-        .magic = cpu_to_le32(FUNCTIONFS_STRINGS_MAGIC),
-        .length = cpu_to_le32(sizeof(strings)),
-        .str_count = cpu_to_le32(1),
-        .lang_count = cpu_to_le32(1),
+        .magic = htole32(FUNCTIONFS_STRINGS_MAGIC),
+        .length = htole32(sizeof(strings)),
+        .str_count = htole32(1),
+        .lang_count = htole32(1),
     },
     .lang0 = {
-        .code = cpu_to_le16(0x0409),
+        .code = htole16(0x0409),
         .str1 = STR_INTERFACE,
     },
 };
 
+struct mtp_device_status {
+    uint16_t  wLength;
+    uint16_t  wCode;
+};
+
 } // anonymous namespace
 
 namespace android {
 
-MtpFfsHandle::MtpFfsHandle() :
-    mMaxWrite(USB_FFS_MAX_WRITE),
-    mMaxRead(USB_FFS_MAX_READ) {}
+int MtpFfsHandle::getPacketSize(int ffs_fd) {
+    struct usb_endpoint_descriptor desc;
+    if (ioctl(ffs_fd, FUNCTIONFS_ENDPOINT_DESC, reinterpret_cast<unsigned long>(&desc))) {
+        PLOG(ERROR) << "Could not get FFS bulk-in descriptor";
+        return MAX_PACKET_SIZE_HS;
+    } else {
+        return desc.wMaxPacketSize;
+    }
+}
+
+MtpFfsHandle::MtpFfsHandle() {}
 
 MtpFfsHandle::~MtpFfsHandle() {}
 
@@ -303,13 +290,51 @@
     mBulkOut.reset();
 }
 
+bool MtpFfsHandle::openEndpoints() {
+    if (mBulkIn < 0) {
+        mBulkIn.reset(TEMP_FAILURE_RETRY(open(FFS_MTP_EP_IN, O_RDWR)));
+        if (mBulkIn < 0) {
+            PLOG(ERROR) << FFS_MTP_EP_IN << ": cannot open bulk in ep";
+            return false;
+        }
+    }
+
+    if (mBulkOut < 0) {
+        mBulkOut.reset(TEMP_FAILURE_RETRY(open(FFS_MTP_EP_OUT, O_RDWR)));
+        if (mBulkOut < 0) {
+            PLOG(ERROR) << FFS_MTP_EP_OUT << ": cannot open bulk out ep";
+            return false;
+        }
+    }
+
+    if (mIntr < 0) {
+        mIntr.reset(TEMP_FAILURE_RETRY(open(FFS_MTP_EP_INTR, O_RDWR)));
+        if (mIntr < 0) {
+            PLOG(ERROR) << FFS_MTP_EP_INTR << ": cannot open intr ep";
+            return false;
+        }
+    }
+    return true;
+}
+
+void MtpFfsHandle::advise(int fd) {
+    for (unsigned i = 0; i < NUM_IO_BUFS; i++) {
+        if (posix_madvise(mIobuf[i].bufs.data(), MAX_FILE_CHUNK_SIZE,
+                POSIX_MADV_SEQUENTIAL | POSIX_MADV_WILLNEED) < 0)
+            PLOG(ERROR) << "Failed to madvise";
+    }
+    if (posix_fadvise(fd, 0, 0,
+                POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE | POSIX_FADV_WILLNEED) < 0)
+        PLOG(ERROR) << "Failed to fadvise";
+}
+
 bool MtpFfsHandle::initFunctionfs() {
     ssize_t ret;
     struct desc_v1 v1_descriptor;
     struct desc_v2 v2_descriptor;
 
-    v2_descriptor.header.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2);
-    v2_descriptor.header.length = cpu_to_le32(sizeof(v2_descriptor));
+    v2_descriptor.header.magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2);
+    v2_descriptor.header.length = htole32(sizeof(v2_descriptor));
     v2_descriptor.header.flags = FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC |
                                  FUNCTIONFS_HAS_SS_DESC;
     v2_descriptor.fs_count = 4;
@@ -328,8 +353,8 @@
 
         ret = TEMP_FAILURE_RETRY(::write(mControl, &v2_descriptor, sizeof(v2_descriptor)));
         if (ret < 0) {
-            v1_descriptor.header.magic = cpu_to_le32(FUNCTIONFS_DESCRIPTORS_MAGIC);
-            v1_descriptor.header.length = cpu_to_le32(sizeof(v1_descriptor));
+            v1_descriptor.header.magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC);
+            v1_descriptor.header.length = htole32(sizeof(v1_descriptor));
             v1_descriptor.header.fs_count = 4;
             v1_descriptor.header.hs_count = 4;
             v1_descriptor.fs_descs = mPtp ? ptp_fs_descriptors : mtp_fs_descriptors;
@@ -347,8 +372,6 @@
             goto err;
         }
     }
-    if (mBulkIn > -1 || mBulkOut > -1 || mIntr > -1)
-        LOG(WARNING) << "Endpoints were not closed before configure!";
 
     return true;
 
@@ -361,130 +384,152 @@
     mControl.reset();
 }
 
-int MtpFfsHandle::writeHandle(int fd, const void* data, int len) {
-    LOG(VERBOSE) << "MTP about to write fd = " << fd << ", len=" << len;
-    int ret = 0;
-    const char* buf = static_cast<const char*>(data);
-    while (len > 0) {
-        int write_len = std::min(mMaxWrite, len);
-        int n = TEMP_FAILURE_RETRY(::write(fd, buf, write_len));
-
-        if (n < 0) {
-            PLOG(ERROR) << "write ERROR: fd = " << fd << ", n = " << n;
-            return -1;
-        } else if (n < write_len) {
-            errno = EIO;
-            PLOG(ERROR) << "less written than expected";
-            return -1;
-        }
-        buf += n;
-        len -= n;
-        ret += n;
+int MtpFfsHandle::doAsync(void* data, size_t len, bool read) {
+    struct io_event ioevs[1];
+    if (len > AIO_BUF_LEN) {
+        LOG(ERROR) << "Mtp read/write too large " << len;
+        errno = EINVAL;
+        return -1;
     }
+    mIobuf[0].buf[0] = reinterpret_cast<unsigned char*>(data);
+    if (iobufSubmit(&mIobuf[0], read ? mBulkOut : mBulkIn, len, read) == -1)
+        return -1;
+    int ret = waitEvents(&mIobuf[0], 1, ioevs, nullptr);
+    mIobuf[0].buf[0] = mIobuf[0].bufs.data();
     return ret;
 }
 
-int MtpFfsHandle::readHandle(int fd, void* data, int len) {
-    LOG(VERBOSE) << "MTP about to read fd = " << fd << ", len=" << len;
+int MtpFfsHandle::read(void* data, size_t len) {
+    return doAsync(data, len, true);
+}
+
+int MtpFfsHandle::write(const void* data, size_t len) {
+    return doAsync(const_cast<void*>(data), len, false);
+}
+
+int MtpFfsHandle::handleEvent() {
+
+    std::vector<usb_functionfs_event> events(FFS_NUM_EVENTS);
+    usb_functionfs_event *event = events.data();
+    int nbytes = TEMP_FAILURE_RETRY(::read(mControl, event,
+                events.size() * sizeof(usb_functionfs_event)));
+    if (nbytes == -1) {
+        return -1;
+    }
     int ret = 0;
-    char* buf = static_cast<char*>(data);
-    while (len > 0) {
-        int read_len = std::min(mMaxRead, len);
-        int n = TEMP_FAILURE_RETRY(::read(fd, buf, read_len));
-        if (n < 0) {
-            PLOG(ERROR) << "read ERROR: fd = " << fd << ", n = " << n;
-            return -1;
-        }
-        ret += n;
-        if (n < read_len) // done reading early
+    for (size_t n = nbytes / sizeof *event; n; --n, ++event) {
+        switch (event->type) {
+        case FUNCTIONFS_BIND:
+        case FUNCTIONFS_ENABLE:
+        case FUNCTIONFS_RESUME:
+            ret = 0;
+            errno = 0;
             break;
-        buf += n;
-        len -= n;
-    }
-    return ret;
-}
-
-int MtpFfsHandle::spliceReadHandle(int fd, int pipe_out, int len) {
-    LOG(VERBOSE) << "MTP about to splice read fd = " << fd << ", len=" << len;
-    int ret = 0;
-    loff_t dummyoff;
-    while (len > 0) {
-        int read_len = std::min(mMaxRead, len);
-        dummyoff = 0;
-        int n = TEMP_FAILURE_RETRY(splice(fd, &dummyoff, pipe_out, nullptr, read_len, 0));
-        if (n < 0) {
-            PLOG(ERROR) << "splice read ERROR: fd = " << fd << ", n = " << n;
-            return -1;
-        }
-        ret += n;
-        if (n < read_len) // done reading early
+        case FUNCTIONFS_SUSPEND:
+        case FUNCTIONFS_UNBIND:
+        case FUNCTIONFS_DISABLE:
+            errno = ESHUTDOWN;
+            ret = -1;
             break;
-        len -= n;
+        case FUNCTIONFS_SETUP:
+            if (handleControlRequest(&event->u.setup) == -1)
+                ret = -1;
+            break;
+        default:
+            LOG(ERROR) << "Mtp Event " << event->type << " (unknown)";
+        }
     }
     return ret;
 }
 
-int MtpFfsHandle::read(void* data, int len) {
-    return readHandle(mBulkOut, data, len);
-}
+int MtpFfsHandle::handleControlRequest(const struct usb_ctrlrequest *setup) {
+    uint8_t type = setup->bRequestType;
+    uint8_t code = setup->bRequest;
+    uint16_t length = setup->wLength;
+    uint16_t index = setup->wIndex;
+    uint16_t value = setup->wValue;
+    std::vector<char> buf;
+    buf.resize(length);
+    int ret = 0;
 
-int MtpFfsHandle::write(const void* data, int len) {
-    return writeHandle(mBulkIn, data, len);
+    if (!(type & USB_DIR_IN)) {
+        if (::read(mControl, buf.data(), length) != length) {
+            PLOG(ERROR) << "Mtp error ctrlreq read data";
+        }
+    }
+
+    if ((type & USB_TYPE_MASK) == USB_TYPE_CLASS && index == 0 && value == 0) {
+        switch(code) {
+        case MTP_REQ_RESET:
+        case MTP_REQ_CANCEL:
+            errno = ECANCELED;
+            ret = -1;
+            break;
+        case MTP_REQ_GET_DEVICE_STATUS:
+        {
+            if (length < sizeof(struct mtp_device_status) + 4) {
+                errno = EINVAL;
+                return -1;
+            }
+            struct mtp_device_status *st = reinterpret_cast<struct mtp_device_status*>(buf.data());
+            st->wLength = htole16(sizeof(st));
+            if (mCanceled) {
+                st->wLength += 4;
+                st->wCode = MTP_RESPONSE_TRANSACTION_CANCELLED;
+                uint16_t *endpoints = reinterpret_cast<uint16_t*>(st + 1);
+                endpoints[0] = ioctl(mBulkIn, FUNCTIONFS_ENDPOINT_REVMAP);
+                endpoints[1] = ioctl(mBulkOut, FUNCTIONFS_ENDPOINT_REVMAP);
+                mCanceled = false;
+            } else {
+                st->wCode = MTP_RESPONSE_OK;
+            }
+            length = st->wLength;
+            break;
+        }
+        default:
+            LOG(ERROR) << "Unrecognized Mtp class request! " << code;
+        }
+    } else {
+        LOG(ERROR) << "Unrecognized request type " << type;
+    }
+
+    if (type & USB_DIR_IN) {
+        if (::write(mControl, buf.data(), length) != length) {
+            PLOG(ERROR) << "Mtp error ctrlreq write data";
+        }
+    }
+    return 0;
 }
 
 int MtpFfsHandle::start() {
     mLock.lock();
 
-    mBulkIn.reset(TEMP_FAILURE_RETRY(open(FFS_MTP_EP_IN, O_RDWR)));
-    if (mBulkIn < 0) {
-        PLOG(ERROR) << FFS_MTP_EP_IN << ": cannot open bulk in ep";
+    if (!openEndpoints())
         return -1;
-    }
 
-    mBulkOut.reset(TEMP_FAILURE_RETRY(open(FFS_MTP_EP_OUT, O_RDWR)));
-    if (mBulkOut < 0) {
-        PLOG(ERROR) << FFS_MTP_EP_OUT << ": cannot open bulk out ep";
-        return -1;
-    }
-
-    mIntr.reset(TEMP_FAILURE_RETRY(open(FFS_MTP_EP_INTR, O_RDWR)));
-    if (mIntr < 0) {
-        PLOG(ERROR) << FFS_MTP_EP0 << ": cannot open intr ep";
-        return -1;
-    }
-
-    mBuffer1.resize(MAX_FILE_CHUNK_SIZE);
-    mBuffer2.resize(MAX_FILE_CHUNK_SIZE);
-    posix_madvise(mBuffer1.data(), MAX_FILE_CHUNK_SIZE,
-            POSIX_MADV_SEQUENTIAL | POSIX_MADV_WILLNEED);
-    posix_madvise(mBuffer2.data(), MAX_FILE_CHUNK_SIZE,
-            POSIX_MADV_SEQUENTIAL | POSIX_MADV_WILLNEED);
-
-    // Get device specific r/w size
-    mMaxWrite = android::base::GetIntProperty("sys.usb.ffs.max_write", USB_FFS_MAX_WRITE);
-    mMaxRead = android::base::GetIntProperty("sys.usb.ffs.max_read", USB_FFS_MAX_READ);
-
-    size_t attempts = 0;
-    while (mMaxWrite >= USB_FFS_MAX_WRITE && mMaxRead >= USB_FFS_MAX_READ &&
-            attempts < ENDPOINT_ALLOC_RETRIES) {
-        // If larger contiguous chunks of memory aren't available, attempt to try
-        // smaller allocations.
-        if (ioctl(mBulkIn, FUNCTIONFS_ENDPOINT_ALLOC, static_cast<__u32>(mMaxWrite)) ||
-            ioctl(mBulkOut, FUNCTIONFS_ENDPOINT_ALLOC, static_cast<__u32>(mMaxRead))) {
-            if (errno == ENODEV) {
-                // Driver hasn't enabled endpoints yet.
-                std::this_thread::sleep_for(std::chrono::milliseconds(100));
-                attempts += 1;
-                continue;
-            }
-            mMaxWrite /= 2;
-            mMaxRead /=2;
-        } else {
-            return 0;
+    for (unsigned i = 0; i < NUM_IO_BUFS; i++) {
+        mIobuf[i].bufs.resize(MAX_FILE_CHUNK_SIZE);
+        mIobuf[i].iocb.resize(AIO_BUFS_MAX);
+        mIobuf[i].iocbs.resize(AIO_BUFS_MAX);
+        mIobuf[i].buf.resize(AIO_BUFS_MAX);
+        for (unsigned j = 0; j < AIO_BUFS_MAX; j++) {
+            mIobuf[i].buf[j] = mIobuf[i].bufs.data() + j * AIO_BUF_LEN;
+            mIobuf[i].iocb[j] = &mIobuf[i].iocbs[j];
         }
     }
-    // Try to start MtpServer anyway, with the smallest max r/w values
-    PLOG(ERROR) << "Functionfs could not allocate any memory!";
+
+    memset(&mCtx, 0, sizeof(mCtx));
+    if (io_setup(AIO_BUFS_MAX, &mCtx) < 0) {
+        PLOG(ERROR) << "unable to setup aio";
+        return -1;
+    }
+    mEventFd.reset(eventfd(0, EFD_NONBLOCK));
+    mPollFds[0].fd = mControl;
+    mPollFds[0].events = POLLIN;
+    mPollFds[1].fd = mEventFd;
+    mPollFds[1].events = POLLIN;
+
+    mCanceled = false;
     return 0;
 }
 
@@ -506,215 +551,368 @@
     if (!initFunctionfs()) {
         ret = -1;
     }
+
     mLock.unlock();
     return ret;
 }
 
 void MtpFfsHandle::close() {
+    io_destroy(mCtx);
     closeEndpoints();
     mLock.unlock();
 }
 
-/* Read from USB and write to a local file. */
-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;
-    uint64_t offset = mfr.offset;
-    struct usb_endpoint_descriptor mBulkOut_desc;
-    int packet_size;
+int MtpFfsHandle::waitEvents(struct io_buffer *buf, int min_events, struct io_event *events,
+        int *counter) {
+    int num_events = 0;
+    int ret = 0;
+    int error = 0;
 
-    if (ioctl(mBulkOut, FUNCTIONFS_ENDPOINT_DESC, reinterpret_cast<unsigned long>(&mBulkOut_desc))) {
-        PLOG(ERROR) << "Could not get FFS bulk-out descriptor";
-        packet_size = MAX_PACKET_SIZE_HS;
-    } else {
-        packet_size = mBulkOut_desc.wMaxPacketSize;
+    while (num_events < min_events) {
+        if (poll(mPollFds, 2, 0) == -1) {
+            PLOG(ERROR) << "Mtp error during poll()";
+            return -1;
+        }
+        if (mPollFds[0].revents & POLLIN) {
+            mPollFds[0].revents = 0;
+            if (handleEvent() == -1) {
+                error = errno;
+            }
+        }
+        if (mPollFds[1].revents & POLLIN) {
+            mPollFds[1].revents = 0;
+            uint64_t ev_cnt = 0;
+
+            if (::read(mEventFd, &ev_cnt, sizeof(ev_cnt)) == -1) {
+                PLOG(ERROR) << "Mtp unable to read eventfd";
+                error = errno;
+                continue;
+            }
+
+            // It's possible that io_getevents will return more events than the eventFd reported,
+            // since events may appear in the time between the calls. In this case, the eventFd will
+            // show up as readable next iteration, but there will be fewer or no events to actually
+            // wait for. Thus we never want io_getevents to block.
+            int this_events = TEMP_FAILURE_RETRY(io_getevents(mCtx, 0, AIO_BUFS_MAX, events, &ZERO_TIMEOUT));
+            if (this_events == -1) {
+                PLOG(ERROR) << "Mtp error getting events";
+                error = errno;
+            }
+            // Add up the total amount of data and find errors on the way.
+            for (unsigned j = 0; j < static_cast<unsigned>(this_events); j++) {
+                if (events[j].res < 0) {
+                    errno = -events[j].res;
+                    PLOG(ERROR) << "Mtp got error event at " << j << " and " << buf->actual << " total";
+                    error = errno;
+                }
+                ret += events[j].res;
+            }
+            num_events += this_events;
+            if (counter)
+                *counter += this_events;
+        }
+        if (error) {
+            errno = error;
+            ret = -1;
+            break;
+        }
+    }
+    return ret;
+}
+
+void MtpFfsHandle::cancelTransaction() {
+    // Device cancels by stalling both bulk endpoints.
+    if (::read(mBulkIn, nullptr, 0) != -1 || errno != EBADMSG)
+        PLOG(ERROR) << "Mtp stall failed on bulk in";
+    if (::write(mBulkOut, nullptr, 0) != -1 || errno != EBADMSG)
+        PLOG(ERROR) << "Mtp stall failed on bulk out";
+    mCanceled = true;
+    errno = ECANCELED;
+}
+
+int MtpFfsHandle::cancelEvents(struct iocb **iocb, struct io_event *events, unsigned start,
+        unsigned end) {
+    // Some manpages for io_cancel are out of date and incorrect.
+    // io_cancel will return -EINPROGRESS on success and does
+    // not place the event in the given memory. We have to use
+    // io_getevents to wait for all the events we cancelled.
+    int ret = 0;
+    unsigned num_events = 0;
+    int save_errno = errno;
+    errno = 0;
+
+    for (unsigned j = start; j < end; j++) {
+        if (io_cancel(mCtx, iocb[j], nullptr) != -1 || errno != EINPROGRESS) {
+            PLOG(ERROR) << "Mtp couldn't cancel request " << j;
+        } else {
+            num_events++;
+        }
+    }
+    if (num_events != end - start) {
+        ret = -1;
+        errno = EIO;
+    }
+    int evs = TEMP_FAILURE_RETRY(io_getevents(mCtx, num_events, AIO_BUFS_MAX, events, nullptr));
+    if (static_cast<unsigned>(evs) != num_events) {
+        PLOG(ERROR) << "Mtp couldn't cancel all requests, got " << evs;
+        ret = -1;
     }
 
-    char *data = mBuffer1.data();
-    char *data2 = mBuffer2.data();
+    uint64_t ev_cnt = 0;
+    if (num_events && ::read(mEventFd, &ev_cnt, sizeof(ev_cnt)) == -1)
+        PLOG(ERROR) << "Mtp Unable to read event fd";
+
+    if (ret == 0) {
+        // Restore errno since it probably got overriden with EINPROGRESS.
+        errno = save_errno;
+    }
+    return ret;
+}
+
+int MtpFfsHandle::iobufSubmit(struct io_buffer *buf, int fd, unsigned length, bool read) {
+    int ret = 0;
+    buf->actual = AIO_BUFS_MAX;
+    for (unsigned j = 0; j < AIO_BUFS_MAX; j++) {
+        unsigned rq_length = std::min(AIO_BUF_LEN, length - AIO_BUF_LEN * j);
+        io_prep(buf->iocb[j], fd, buf->buf[j], rq_length, 0, read);
+        buf->iocb[j]->aio_flags |= IOCB_FLAG_RESFD;
+        buf->iocb[j]->aio_resfd = mEventFd;
+
+        // Not enough data, so table is truncated.
+        if (rq_length < AIO_BUF_LEN || length == AIO_BUF_LEN * (j + 1)) {
+            buf->actual = j + 1;
+            break;
+        }
+    }
+
+    ret = io_submit(mCtx, buf->actual, buf->iocb.data());
+    if (ret != static_cast<int>(buf->actual)) {
+        PLOG(ERROR) << "Mtp io_submit got " << ret << " expected " << buf->actual;
+        if (ret != -1) {
+            errno = EIO;
+        }
+        ret = -1;
+    }
+    return ret;
+}
+
+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;
+    uint64_t offset = mfr.offset;
 
     struct aiocb aio;
     aio.aio_fildes = mfr.fd;
     aio.aio_buf = nullptr;
     struct aiocb *aiol[] = {&aio};
-    int ret = -1;
-    size_t length;
-    bool read = false;
-    bool write = false;
 
-    posix_fadvise(mfr.fd, 0, 0, POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE);
+    int ret = -1;
+    unsigned i = 0;
+    size_t length;
+    struct io_event ioevs[AIO_BUFS_MAX];
+    bool has_write = false;
+    bool error = false;
+    bool write_error = false;
+    int packet_size = getPacketSize(mBulkOut);
+    bool short_packet = false;
+    advise(mfr.fd);
 
     // Break down the file into pieces that fit in buffers
-    while (file_length > 0 || write) {
+    while (file_length > 0 || has_write) {
+        // Queue an asynchronous read from USB.
         if (file_length > 0) {
             length = std::min(static_cast<uint32_t>(MAX_FILE_CHUNK_SIZE), file_length);
-
-            // Read data from USB, handle errors after waiting for write thread.
-            ret = readHandle(mBulkOut, data, length);
-
-            if (file_length != MAX_MTP_FILE_SIZE && ret < static_cast<int>(length)) {
-                ret = -1;
-                errno = EIO;
-            }
-            read = true;
+            if (iobufSubmit(&mIobuf[i], mBulkOut, length, true) == -1)
+                error = true;
         }
 
-        if (write) {
-            // get the return status of the last write request
+        // Get the return status of the last write request.
+        if (has_write) {
             aio_suspend(aiol, 1, nullptr);
-
             int written = aio_return(&aio);
-            if (written == -1) {
-                errno = aio_error(&aio);
-                return -1;
-            }
             if (static_cast<size_t>(written) < aio.aio_nbytes) {
-                errno = EIO;
-                return -1;
+                errno = written == -1 ? aio_error(&aio) : EIO;
+                PLOG(ERROR) << "Mtp error writing to disk";
+                write_error = true;
             }
-            write = false;
+            has_write = false;
         }
 
-        // If there was an error reading above
-        if (ret == -1) {
+        if (error) {
             return -1;
         }
 
-        if (read) {
+        // Get the result of the read request, and queue a write to disk.
+        if (file_length > 0) {
+            unsigned num_events = 0;
+            ret = 0;
+            unsigned short_i = mIobuf[i].actual;
+            while (num_events < short_i) {
+                // Get all events up to the short read, if there is one.
+                // We must wait for each event since data transfer could end at any time.
+                int this_events = 0;
+                int event_ret = waitEvents(&mIobuf[i], 1, ioevs, &this_events);
+                num_events += this_events;
+
+                if (event_ret == -1) {
+                    cancelEvents(mIobuf[i].iocb.data(), ioevs, num_events, mIobuf[i].actual);
+                    return -1;
+                }
+                ret += event_ret;
+                for (int j = 0; j < this_events; j++) {
+                    // struct io_event contains a pointer to the associated struct iocb as a __u64.
+                    if (static_cast<__u64>(ioevs[j].res) <
+                            reinterpret_cast<struct iocb*>(ioevs[j].obj)->aio_nbytes) {
+                        // We've found a short event. Store the index since
+                        // events won't necessarily arrive in the order they are queued.
+                        short_i = (ioevs[j].obj - reinterpret_cast<uint64_t>(mIobuf[i].iocbs.data()))
+                            / sizeof(struct iocb) + 1;
+                        short_packet = true;
+                    }
+                }
+            }
+            if (short_packet) {
+                if (cancelEvents(mIobuf[i].iocb.data(), ioevs, short_i, mIobuf[i].actual)) {
+                    write_error = true;
+                }
+            }
             if (file_length == MAX_MTP_FILE_SIZE) {
                 // For larger files, receive until a short packet is received.
                 if (static_cast<size_t>(ret) < length) {
                     file_length = 0;
                 }
+            } else if (ret < static_cast<int>(length)) {
+                // If file is less than 4G and we get a short packet, it's an error.
+                errno = EIO;
+                LOG(ERROR) << "Mtp got unexpected short packet";
+                return -1;
             } else {
-                // Receive an empty packet if size is a multiple of the endpoint size.
                 file_length -= ret;
             }
+
+            if (write_error) {
+                cancelTransaction();
+                return -1;
+            }
+
             // Enqueue a new write request
-            aio.aio_buf = data;
-            aio.aio_sink = mfr.fd;
-            aio.aio_offset = offset;
-            aio.aio_nbytes = ret;
+            aio_prepare(&aio, mIobuf[i].bufs.data(), ret, offset);
             aio_write(&aio);
 
             offset += ret;
-            std::swap(data, data2);
-
-            write = true;
-            read = false;
+            i = (i + 1) % NUM_IO_BUFS;
+            has_write = true;
         }
     }
-    if (ret % packet_size == 0 || zero_packet) {
-        if (TEMP_FAILURE_RETRY(::read(mBulkOut, data, packet_size)) != 0) {
+    if ((ret % packet_size == 0 && !short_packet) || zero_packet) {
+        // Receive an empty packet if size is a multiple of the endpoint size
+        // and we didn't already get an empty packet from the header or large file.
+        if (read(mIobuf[0].bufs.data(), packet_size) != 0) {
             return -1;
         }
     }
     return 0;
 }
 
-/* Read from a local file and send over USB. */
 int MtpFfsHandle::sendFile(mtp_file_range mfr) {
     uint64_t file_length = mfr.length;
     uint32_t given_length = std::min(static_cast<uint64_t>(MAX_MTP_FILE_SIZE),
             file_length + sizeof(mtp_data_header));
     uint64_t offset = mfr.offset;
-    struct usb_endpoint_descriptor mBulkIn_desc;
-    int packet_size;
-
-    if (ioctl(mBulkIn, FUNCTIONFS_ENDPOINT_DESC, reinterpret_cast<unsigned long>(&mBulkIn_desc))) {
-        PLOG(ERROR) << "Could not get FFS bulk-in descriptor";
-        packet_size = MAX_PACKET_SIZE_HS;
-    } else {
-        packet_size = mBulkIn_desc.wMaxPacketSize;
-    }
+    int packet_size = getPacketSize(mBulkIn);
 
     // If file_length is larger than a size_t, truncating would produce the wrong comparison.
     // Instead, promote the left side to 64 bits, then truncate the small result.
     int init_read_len = std::min(
             static_cast<uint64_t>(packet_size - sizeof(mtp_data_header)), file_length);
 
-    char *data = mBuffer1.data();
-    char *data2 = mBuffer2.data();
-
-    posix_fadvise(mfr.fd, 0, 0, POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE);
+    advise(mfr.fd);
 
     struct aiocb aio;
     aio.aio_fildes = mfr.fd;
     struct aiocb *aiol[] = {&aio};
-    int ret, length;
-    int error = 0;
-    bool read = false;
-    bool write = false;
+    int ret = 0;
+    int length, num_read;
+    unsigned i = 0;
+    struct io_event ioevs[AIO_BUFS_MAX];
+    bool error = false;
+    bool has_write = false;
 
     // Send the header data
-    mtp_data_header *header = reinterpret_cast<mtp_data_header*>(data);
-    header->length = __cpu_to_le32(given_length);
-    header->type = __cpu_to_le16(2); /* data packet */
-    header->command = __cpu_to_le16(mfr.command);
-    header->transaction_id = __cpu_to_le32(mfr.transaction_id);
+    mtp_data_header *header = reinterpret_cast<mtp_data_header*>(mIobuf[0].bufs.data());
+    header->length = htole32(given_length);
+    header->type = htole16(2); // data packet
+    header->command = htole16(mfr.command);
+    header->transaction_id = htole32(mfr.transaction_id);
 
     // Some hosts don't support header/data separation even though MTP allows it
     // Handle by filling first packet with initial file data
-    if (TEMP_FAILURE_RETRY(pread(mfr.fd, reinterpret_cast<char*>(data) +
+    if (TEMP_FAILURE_RETRY(pread(mfr.fd, mIobuf[0].bufs.data() +
                     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 (write(mIobuf[0].bufs.data(), sizeof(mtp_data_header) + init_read_len) == -1)
+        return -1;
     file_length -= init_read_len;
     offset += init_read_len;
     ret = init_read_len + sizeof(mtp_data_header);
 
     // Break down the file into pieces that fit in buffers
-    while(file_length > 0) {
-        if (read) {
-            // Wait for the previous read to finish
-            aio_suspend(aiol, 1, nullptr);
-            ret = aio_return(&aio);
-            if (ret == -1) {
-                errno = aio_error(&aio);
-                return -1;
-            }
-            if (static_cast<size_t>(ret) < aio.aio_nbytes) {
-                errno = EIO;
-                return -1;
-            }
-
-            file_length -= ret;
-            offset += ret;
-            std::swap(data, data2);
-            read = false;
-            write = true;
+    while(file_length > 0 || has_write) {
+        if (file_length > 0) {
+            // Queue up a read from disk.
+            length = std::min(static_cast<uint64_t>(MAX_FILE_CHUNK_SIZE), file_length);
+            aio_prepare(&aio, mIobuf[i].bufs.data(), length, offset);
+            aio_read(&aio);
         }
 
-        if (error == -1) {
-            return -1;
+        if (has_write) {
+            // Wait for usb write. Cancel unwritten portion if there's an error.
+            int num_events = 0;
+            if (waitEvents(&mIobuf[(i-1)%NUM_IO_BUFS], mIobuf[(i-1)%NUM_IO_BUFS].actual, ioevs,
+                        &num_events) != ret) {
+                error = true;
+                cancelEvents(mIobuf[(i-1)%NUM_IO_BUFS].iocb.data(), ioevs, num_events,
+                        mIobuf[(i-1)%NUM_IO_BUFS].actual);
+            }
+            has_write = false;
         }
 
         if (file_length > 0) {
-            length = std::min(static_cast<uint64_t>(MAX_FILE_CHUNK_SIZE), file_length);
-            // Queue up another read
-            aio.aio_buf = data;
-            aio.aio_offset = offset;
-            aio.aio_nbytes = length;
-            aio_read(&aio);
-            read = true;
+            // Wait for the previous read to finish
+            aio_suspend(aiol, 1, nullptr);
+            num_read = aio_return(&aio);
+            if (static_cast<size_t>(num_read) < aio.aio_nbytes) {
+                errno = num_read == -1 ? aio_error(&aio) : EIO;
+                PLOG(ERROR) << "Mtp error reading from disk";
+                cancelTransaction();
+                return -1;
+            }
+
+            file_length -= num_read;
+            offset += num_read;
+
+            if (error) {
+                return -1;
+            }
+
+            // Queue up a write to usb.
+            if (iobufSubmit(&mIobuf[i], mBulkIn, num_read, false) == -1) {
+                return -1;
+            }
+            has_write = true;
+            ret = num_read;
         }
 
-        if (write) {
-            if (writeHandle(mBulkIn, data2, ret) == -1) {
-                error = -1;
-            }
-            write = false;
-        }
+        i = (i + 1) % NUM_IO_BUFS;
     }
 
     if (ret % packet_size == 0) {
         // If the last packet wasn't short, send a final empty packet
-        if (TEMP_FAILURE_RETRY(::write(mBulkIn, data, 0)) != 0) {
+        if (write(mIobuf[0].bufs.data(), 0) != 0) {
             return -1;
         }
     }
-
     return 0;
 }
 
@@ -739,7 +937,3 @@
 
 } // namespace android
 
-IMtpHandle *get_ffs_handle() {
-    return new android::MtpFfsHandle();
-}
-
diff --git a/media/mtp/MtpFfsHandle.h b/media/mtp/MtpFfsHandle.h
index b637d65..2f90bd1 100644
--- a/media/mtp/MtpFfsHandle.h
+++ b/media/mtp/MtpFfsHandle.h
@@ -18,26 +18,51 @@
 #define _MTP_FFS_HANDLE_H
 
 #include <android-base/unique_fd.h>
+#include <linux/aio_abi.h>
+#include <mutex>
+#include <sys/poll.h>
+#include <time.h>
+#include <thread>
+#include <vector>
+
 #include <IMtpHandle.h>
 
 namespace android {
 
-class MtpFfsHandleTest;
+constexpr char FFS_MTP_EP0[] = "/dev/usb-ffs/mtp/ep0";
+
+constexpr int NUM_IO_BUFS = 2;
+
+struct io_buffer {
+    std::vector<struct iocb> iocbs;     // Holds memory for all iocbs. Not used directly.
+    std::vector<struct iocb*> iocb;     // Pointers to individual iocbs, for syscalls
+    std::vector<unsigned char> bufs;    // A large buffer, used with filesystem io
+    std::vector<unsigned char*> buf;    // Pointers within the larger buffer, for syscalls
+    unsigned actual;                    // The number of buffers submitted for this request
+};
+
+template <class T> class MtpFfsHandleTest;
 
 class MtpFfsHandle : public IMtpHandle {
-    friend class android::MtpFfsHandleTest;
-private:
-    int writeHandle(int fd, const void *data, int len);
-    int readHandle(int fd, void *data, int len);
-    int spliceReadHandle(int fd, int fd_out, int len);
+    template <class T> friend class android::MtpFfsHandleTest;
+protected:
     bool initFunctionfs();
     void closeConfig();
     void closeEndpoints();
+    void advise(int fd);
+    int handleControlRequest(const struct usb_ctrlrequest *request);
+    int doAsync(void* data, size_t len, bool read);
+    int handleEvent();
+    void cancelTransaction();
     void doSendEvent(mtp_event me);
+    bool openEndpoints();
+
+    static int getPacketSize(int ffs_fd);
 
     bool mPtp;
+    bool mCanceled;
 
-    std::timed_mutex mLock;
+    std::timed_mutex mLock; // protects configure() vs main loop
 
     android::base::unique_fd mControl;
     // "in" from the host's perspective => sink for mtp server
@@ -46,28 +71,35 @@
     android::base::unique_fd mBulkOut;
     android::base::unique_fd mIntr;
 
-    int mMaxWrite;
-    int mMaxRead;
+    aio_context_t mCtx;
 
-    std::vector<char> mBuffer1;
-    std::vector<char> mBuffer2;
+    android::base::unique_fd mEventFd;
+    struct pollfd mPollFds[2];
+
+    struct io_buffer mIobuf[NUM_IO_BUFS];
+
+    // Submit an io request of given length. Return amount submitted or -1.
+    int iobufSubmit(struct io_buffer *buf, int fd, unsigned length, bool read);
+
+    // Cancel submitted requests from start to end in the given array. Return 0 or -1.
+    int cancelEvents(struct iocb **iocb, struct io_event *events, unsigned start, unsigned end);
+
+    // Wait for at minimum the given number of events. Returns the amount of data in the returned
+    // events. Increments counter by the number of events returned.
+    int waitEvents(struct io_buffer *buf, int min_events, struct io_event *events, int *counter);
 
 public:
-    int read(void *data, int len);
-    int write(const void *data, int len);
+    int read(void *data, size_t len) override;
+    int write(const void *data, size_t len) override;
 
-    int receiveFile(mtp_file_range mfr, bool zero_packet);
-    int sendFile(mtp_file_range mfr);
-    int sendEvent(mtp_event me);
+    int receiveFile(mtp_file_range mfr, bool zero_packet) override;
+    int sendFile(mtp_file_range mfr) override;
+    int sendEvent(mtp_event me) override;
 
-    /**
-     * Open ffs endpoints and allocate necessary kernel and user memory.
-     * Will sleep until endpoints are enabled, for up to 1 second.
-     */
-    int start();
-    void close();
+    int start() override;
+    void close() override;
 
-    int configure(bool ptp);
+    int configure(bool ptp) override;
 
     MtpFfsHandle();
     ~MtpFfsHandle();
@@ -86,5 +118,5 @@
 
 } // namespace android
 
-#endif // _MTP_FF_HANDLE_H
+#endif // _MTP_FFS_HANDLE_H
 
diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp
index 2180462..e148b0c 100644
--- a/media/mtp/MtpServer.cpp
+++ b/media/mtp/MtpServer.cpp
@@ -31,6 +31,9 @@
 
 #include "MtpDebug.h"
 #include "MtpDatabase.h"
+#include "MtpDevHandle.h"
+#include "MtpFfsCompatHandle.h"
+#include "MtpFfsHandle.h"
 #include "MtpObjectInfo.h"
 #include "MtpProperty.h"
 #include "MtpServer.h"
@@ -125,16 +128,21 @@
 IMtpHandle* MtpServer::sHandle = nullptr;
 
 int MtpServer::configure(bool usePtp) {
+    bool ffs_ok = access(FFS_MTP_EP0, W_OK) == 0;
     if (sHandle == nullptr) {
-        bool ffs_ok = access(FFS_MTP_EP0, W_OK) == 0;
-        sHandle = ffs_ok ? get_ffs_handle() : get_mtp_handle();
+        if (ffs_ok) {
+            bool aio_compat = android::base::GetBoolProperty("sys.usb.ffs.aio_compat", false);
+            sHandle = aio_compat ? new MtpFfsCompatHandle() : new MtpFfsHandle();
+        } else {
+            sHandle = new MtpDevHandle();
+        }
     }
-
-    int ret = sHandle->configure(usePtp);
-    if (ret) ALOGE("Failed to configure MTP driver!");
-    else android::base::SetProperty("sys.usb.ffs.mtp.ready", "1");
-
-    return ret;
+    if (sHandle->configure(usePtp)) {
+        ALOGE("Failed to configure Mtp driver!");
+        return -1;
+    }
+    android::base::SetProperty("sys.usb.ffs.mtp.ready", "1");
+    return 0;
 }
 
 void MtpServer::addStorage(MtpStorage* storage) {
@@ -878,6 +886,7 @@
         length = fileLength - offset;
 
     const char* filePath = (const char *)pathBuf;
+    ALOGV("sending partial %s %" PRIu64 " %" PRIu32, filePath, offset, length);
     mtp_file_range  mfr;
     mfr.fd = open(filePath, O_RDONLY);
     if (mfr.fd < 0) {
diff --git a/media/mtp/PosixAsyncIO.cpp b/media/mtp/PosixAsyncIO.cpp
new file mode 100644
index 0000000..e67c568
--- /dev/null
+++ b/media/mtp/PosixAsyncIO.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2016 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 <android-base/logging.h>
+#include <condition_variable>
+#include <memory>
+#include <mutex>
+#include <queue>
+#include <unistd.h>
+
+#include "PosixAsyncIO.h"
+
+namespace {
+
+void read_func(struct aiocb *aiocbp) {
+    aiocbp->ret = TEMP_FAILURE_RETRY(pread(aiocbp->aio_fildes,
+                aiocbp->aio_buf, aiocbp->aio_nbytes, aiocbp->aio_offset));
+    if (aiocbp->ret == -1) aiocbp->error = errno;
+}
+
+void write_func(struct aiocb *aiocbp) {
+    aiocbp->ret = TEMP_FAILURE_RETRY(pwrite(aiocbp->aio_fildes,
+                aiocbp->aio_buf, aiocbp->aio_nbytes, aiocbp->aio_offset));
+    if (aiocbp->ret == -1) aiocbp->error = errno;
+}
+
+} // end anonymous namespace
+
+aiocb::~aiocb() {
+    CHECK(!thread.joinable());
+}
+
+int aio_read(struct aiocb *aiocbp) {
+    aiocbp->thread = std::thread(read_func, aiocbp);
+    return 0;
+}
+
+int aio_write(struct aiocb *aiocbp) {
+    aiocbp->thread = std::thread(write_func, aiocbp);
+    return 0;
+}
+
+int aio_error(const struct aiocb *aiocbp) {
+    return aiocbp->error;
+}
+
+ssize_t aio_return(struct aiocb *aiocbp) {
+    return aiocbp->ret;
+}
+
+int aio_suspend(struct aiocb *aiocbp[], int n,
+        const struct timespec *) {
+    for (int i = 0; i < n; i++) {
+        aiocbp[i]->thread.join();
+    }
+    return 0;
+}
+
+void aio_prepare(struct aiocb *aiocbp, void* buf, size_t count, off_t offset) {
+    aiocbp->aio_buf = buf;
+    aiocbp->aio_offset = offset;
+    aiocbp->aio_nbytes = count;
+}
diff --git a/media/mtp/PosixAsyncIO.h b/media/mtp/PosixAsyncIO.h
new file mode 100644
index 0000000..590aaef
--- /dev/null
+++ b/media/mtp/PosixAsyncIO.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2016 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 _POSIXASYNCIO_H
+#define _POSIXASYNCIO_H
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <time.h>
+#include <thread>
+#include <unistd.h>
+
+/**
+ * Provides a subset of POSIX aio operations.
+ */
+
+struct aiocb {
+    int aio_fildes;
+    void *aio_buf;
+
+    off_t aio_offset;
+    size_t aio_nbytes;
+
+    // Used internally
+    std::thread thread;
+    ssize_t ret;
+    int error;
+
+    ~aiocb();
+};
+
+// Submit a request for IO to be completed
+int aio_read(struct aiocb *);
+int aio_write(struct aiocb *);
+
+// Suspend current thread until given IO is complete, at which point
+// its return value and any errors can be accessed
+// All submitted requests must have a corresponding suspend.
+// aiocb->aio_buf must refer to valid memory until after the suspend call
+int aio_suspend(struct aiocb *[], int, const struct timespec *);
+int aio_error(const struct aiocb *);
+ssize_t aio_return(struct aiocb *);
+
+// Helper method for setting aiocb members
+void aio_prepare(struct aiocb *, void*, size_t, off_t);
+
+#endif // POSIXASYNCIO_H
+
diff --git a/media/mtp/mtp.h b/media/mtp/mtp.h
index 13cc859..1c7829d 100644
--- a/media/mtp/mtp.h
+++ b/media/mtp/mtp.h
@@ -494,4 +494,10 @@
 #define MTP_ASSOCIATION_TYPE_UNDEFINED              0x0000
 #define MTP_ASSOCIATION_TYPE_GENERIC_FOLDER         0x0001
 
+// MTP class reqeusts
+#define MTP_REQ_CANCEL              0x64
+#define MTP_REQ_GET_EXT_EVENT_DATA  0x65
+#define MTP_REQ_RESET               0x66
+#define MTP_REQ_GET_DEVICE_STATUS   0x67
+
 #endif // _MTP_H
diff --git a/media/mtp/tests/Android.bp b/media/mtp/tests/Android.bp
index fe7018b..0750208 100644
--- a/media/mtp/tests/Android.bp
+++ b/media/mtp/tests/Android.bp
@@ -31,8 +31,9 @@
 }
 
 cc_test {
-    name: "async_io_test",
-    srcs: ["AsyncIO_test.cpp"],
+    name: "posix_async_io_test",
+    test_suites: ["device-tests"],
+    srcs: ["PosixAsyncIO_test.cpp"],
     shared_libs: [
         "libbase",
         "libmtp",
diff --git a/media/mtp/tests/AsyncIO_test.cpp b/media/mtp/tests/AsyncIO_test.cpp
deleted file mode 100644
index b5f4538..0000000
--- a/media/mtp/tests/AsyncIO_test.cpp
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * Copyright 2016 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 "AsyncIO_test.cpp"
-
-#include <android-base/test_utils.h>
-#include <fcntl.h>
-#include <gtest/gtest.h>
-#include <string>
-#include <unistd.h>
-#include <utils/Log.h>
-
-#include "AsyncIO.h"
-
-namespace android {
-
-constexpr int TEST_PACKET_SIZE = 512;
-constexpr int POOL_COUNT = 10;
-
-static const std::string dummyDataStr =
-    "/*\n * Copyright 2015 The Android Open Source Project\n *\n * Licensed un"
-    "der the Apache License, Version 2.0 (the \"License\");\n * you may not us"
-    "e this file except in compliance with the License.\n * You may obtain a c"
-    "opy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE"
-    "-2.0\n *\n * Unless required by applicable law or agreed to in writing, s"
-    "oftware\n * distributed under the License is distributed on an \"AS IS\" "
-    "BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express o"
-    "r implied.\n * Se";
-
-
-class AsyncIOTest : public ::testing::Test {
-protected:
-    TemporaryFile dummy_file;
-
-    AsyncIOTest() {}
-    ~AsyncIOTest() {}
-};
-
-TEST_F(AsyncIOTest, testRead) {
-    char buf[TEST_PACKET_SIZE + 1];
-    buf[TEST_PACKET_SIZE] = '\0';
-    EXPECT_EQ(write(dummy_file.fd, dummyDataStr.c_str(), TEST_PACKET_SIZE), TEST_PACKET_SIZE);
-    struct aiocb aio;
-    struct aiocb *aiol[] = {&aio};
-    aio.aio_fildes = dummy_file.fd;
-    aio.aio_buf = buf;
-    aio.aio_offset = 0;
-    aio.aio_nbytes = TEST_PACKET_SIZE;
-
-    EXPECT_EQ(aio_read(&aio), 0);
-    EXPECT_EQ(aio_suspend(aiol, 1, nullptr), 0);
-    EXPECT_EQ(aio_return(&aio), TEST_PACKET_SIZE);
-    EXPECT_STREQ(buf, dummyDataStr.c_str());
-}
-
-TEST_F(AsyncIOTest, testWrite) {
-    char buf[TEST_PACKET_SIZE + 1];
-    buf[TEST_PACKET_SIZE] = '\0';
-    struct aiocb aio;
-    struct aiocb *aiol[] = {&aio};
-    aio.aio_fildes = dummy_file.fd;
-    aio.aio_buf = const_cast<char*>(dummyDataStr.c_str());
-    aio.aio_offset = 0;
-    aio.aio_nbytes = TEST_PACKET_SIZE;
-
-    EXPECT_EQ(aio_write(&aio), 0);
-    EXPECT_EQ(aio_suspend(aiol, 1, nullptr), 0);
-    EXPECT_EQ(aio_return(&aio), TEST_PACKET_SIZE);
-    EXPECT_EQ(read(dummy_file.fd, buf, TEST_PACKET_SIZE), TEST_PACKET_SIZE);
-    EXPECT_STREQ(buf, dummyDataStr.c_str());
-}
-
-TEST_F(AsyncIOTest, testError) {
-    char buf[TEST_PACKET_SIZE + 1];
-    buf[TEST_PACKET_SIZE] = '\0';
-    struct aiocb aio;
-    struct aiocb *aiol[] = {&aio};
-    aio.aio_fildes = -1;
-    aio.aio_buf = const_cast<char*>(dummyDataStr.c_str());
-    aio.aio_offset = 0;
-    aio.aio_nbytes = TEST_PACKET_SIZE;
-
-    EXPECT_EQ(aio_write(&aio), 0);
-    EXPECT_EQ(aio_suspend(aiol, 1, nullptr), 0);
-    EXPECT_EQ(aio_return(&aio), -1);
-    EXPECT_EQ(aio_error(&aio), EBADF);
-}
-
-TEST_F(AsyncIOTest, testSpliceRead) {
-    char buf[TEST_PACKET_SIZE + 1];
-    buf[TEST_PACKET_SIZE] = '\0';
-    int pipeFd[2];
-    EXPECT_EQ(pipe(pipeFd), 0);
-    EXPECT_EQ(write(dummy_file.fd, dummyDataStr.c_str(), TEST_PACKET_SIZE), TEST_PACKET_SIZE);
-    struct aiocb aio;
-    struct aiocb *aiol[] = {&aio};
-    aio.aio_fildes = dummy_file.fd;
-    aio.aio_sink = pipeFd[1];
-    aio.aio_offset = 0;
-    aio.aio_nbytes = TEST_PACKET_SIZE;
-
-    EXPECT_EQ(aio_splice_read(&aio), 0);
-    EXPECT_EQ(aio_suspend(aiol, 1, nullptr), 0);
-    EXPECT_EQ(aio_return(&aio), TEST_PACKET_SIZE);
-
-    EXPECT_EQ(read(pipeFd[0], buf, TEST_PACKET_SIZE), TEST_PACKET_SIZE);
-    EXPECT_STREQ(buf, dummyDataStr.c_str());
-}
-
-TEST_F(AsyncIOTest, testSpliceWrite) {
-    char buf[TEST_PACKET_SIZE + 1];
-    buf[TEST_PACKET_SIZE] = '\0';
-    int pipeFd[2];
-    EXPECT_EQ(pipe(pipeFd), 0);
-    EXPECT_EQ(write(pipeFd[1], dummyDataStr.c_str(), TEST_PACKET_SIZE), TEST_PACKET_SIZE);
-    struct aiocb aio;
-    struct aiocb *aiol[] = {&aio};
-    aio.aio_fildes = pipeFd[0];
-    aio.aio_sink = dummy_file.fd;
-    aio.aio_offset = 0;
-    aio.aio_nbytes = TEST_PACKET_SIZE;
-
-    EXPECT_EQ(aio_splice_write(&aio), 0);
-    EXPECT_EQ(aio_suspend(aiol, 1, nullptr), 0);
-    EXPECT_EQ(aio_return(&aio), TEST_PACKET_SIZE);
-    EXPECT_EQ(read(dummy_file.fd, buf, TEST_PACKET_SIZE), TEST_PACKET_SIZE);
-    EXPECT_STREQ(buf, dummyDataStr.c_str());
-}
-
-TEST_F(AsyncIOTest, testPoolWrite) {
-    aio_pool_write_init();
-    char buf[TEST_PACKET_SIZE * POOL_COUNT + 1];
-    buf[TEST_PACKET_SIZE * POOL_COUNT] = '\0';
-
-    for (int i = 0; i < POOL_COUNT; i++) {
-        struct aiocb *aiop = new struct aiocb;
-        aiop->aio_fildes = dummy_file.fd;
-        aiop->aio_pool_buf = std::unique_ptr<char[]>(new char[TEST_PACKET_SIZE]);
-        memcpy(aiop->aio_pool_buf.get(), dummyDataStr.c_str(), TEST_PACKET_SIZE);
-        aiop->aio_offset = i * TEST_PACKET_SIZE;
-        aiop->aio_nbytes = TEST_PACKET_SIZE;
-        EXPECT_EQ(aio_pool_write(aiop), 0);
-    }
-    aio_pool_end();
-    EXPECT_EQ(read(dummy_file.fd, buf, TEST_PACKET_SIZE * POOL_COUNT), TEST_PACKET_SIZE * POOL_COUNT);
-
-    std::stringstream ss;
-    for (int i = 0; i < POOL_COUNT; i++)
-        ss << dummyDataStr;
-
-    EXPECT_STREQ(buf, ss.str().c_str());
-}
-
-TEST_F(AsyncIOTest, testSplicePoolWrite) {
-    aio_pool_splice_init();
-    char buf[TEST_PACKET_SIZE * POOL_COUNT + 1];
-    buf[TEST_PACKET_SIZE * POOL_COUNT] = '\0';
-
-    for (int i = 0; i < POOL_COUNT; i++) {
-        int pipeFd[2];
-        EXPECT_EQ(pipe(pipeFd), 0);
-        EXPECT_EQ(write(pipeFd[1], dummyDataStr.c_str(), TEST_PACKET_SIZE), TEST_PACKET_SIZE);
-        struct aiocb *aiop = new struct aiocb;
-        aiop->aio_fildes = pipeFd[0];
-        aiop->aio_sink = dummy_file.fd;
-        aiop->aio_offset = i * TEST_PACKET_SIZE;
-        aiop->aio_nbytes = TEST_PACKET_SIZE;
-        EXPECT_EQ(aio_pool_write(aiop), 0);
-    }
-    aio_pool_end();
-    EXPECT_EQ(read(dummy_file.fd, buf, TEST_PACKET_SIZE * POOL_COUNT), TEST_PACKET_SIZE * POOL_COUNT);
-
-    std::stringstream ss;
-    for (int i = 0; i < POOL_COUNT; i++)
-        ss << dummyDataStr;
-
-    EXPECT_STREQ(buf, ss.str().c_str());
-}
-
-} // namespace android
diff --git a/media/mtp/tests/MtpFfsHandle_test.cpp b/media/mtp/tests/MtpFfsHandle_test.cpp
index 554f867..8d7301d 100644
--- a/media/mtp/tests/MtpFfsHandle_test.cpp
+++ b/media/mtp/tests/MtpFfsHandle_test.cpp
@@ -26,12 +26,11 @@
 #include <utils/Log.h>
 
 #include "MtpFfsHandle.h"
+#include "MtpFfsCompatHandle.h"
 
 namespace android {
 
-constexpr int MAX_FILE_CHUNK_SIZE = 3 * 1024 * 1024;
-
-constexpr int TEST_PACKET_SIZE = 512;
+constexpr int TEST_PACKET_SIZE = 500;
 constexpr int SMALL_MULT = 30;
 constexpr int MED_MULT = 510;
 
@@ -43,17 +42,19 @@
     "-2.0\n *\n * Unless required by applicable law or agreed to in writing, s"
     "oftware\n * distributed under the License is distributed on an \"AS IS\" "
     "BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express o"
-    "r implied.\n * Se";
+    "r im";
 
 /**
  * Functional tests for the MtpFfsHandle class. Ensures header and data integrity
  * by mocking ffs endpoints as pipes to capture input / output.
  */
+template <class T>
 class MtpFfsHandleTest : public ::testing::Test {
 protected:
-    std::unique_ptr<IMtpHandle> handle;
+    std::unique_ptr<MtpFfsHandle> handle;
 
     // Pipes for reading endpoint data
+    android::base::unique_fd control;
     android::base::unique_fd bulk_in;
     android::base::unique_fd bulk_out;
     android::base::unique_fd intr;
@@ -62,88 +63,144 @@
 
     MtpFfsHandleTest() {
         int fd[2];
-        handle = std::unique_ptr<IMtpHandle>(get_ffs_handle());
-        MtpFfsHandle *ffs_handle = static_cast<MtpFfsHandle*>(handle.get());
-        EXPECT_TRUE(ffs_handle != NULL);
+        handle = std::make_unique<T>();
+
+        EXPECT_EQ(pipe(fd), 0);
+        handle->mControl.reset(fd[0]);
+        control.reset(fd[1]);
 
         EXPECT_EQ(pipe(fd), 0);
         EXPECT_EQ(fcntl(fd[0], F_SETPIPE_SZ, 1048576), 1048576);
         bulk_in.reset(fd[0]);
-        ffs_handle->mBulkIn.reset(fd[1]);
+        handle->mBulkIn.reset(fd[1]);
 
         EXPECT_EQ(pipe(fd), 0);
         EXPECT_EQ(fcntl(fd[0], F_SETPIPE_SZ, 1048576), 1048576);
         bulk_out.reset(fd[1]);
-        ffs_handle->mBulkOut.reset(fd[0]);
+        handle->mBulkOut.reset(fd[0]);
 
         EXPECT_EQ(pipe(fd), 0);
         intr.reset(fd[0]);
-        ffs_handle->mIntr.reset(fd[1]);
+        handle->mIntr.reset(fd[1]);
 
-        ffs_handle->mBuffer1.resize(MAX_FILE_CHUNK_SIZE);
-        ffs_handle->mBuffer2.resize(MAX_FILE_CHUNK_SIZE);
+        handle->start();
     }
 
-    ~MtpFfsHandleTest() {}
+    ~MtpFfsHandleTest() {
+        handle->close();
+    }
 };
 
-TEST_F(MtpFfsHandleTest, testRead) {
-    EXPECT_EQ(write(bulk_out, dummyDataStr.c_str(), TEST_PACKET_SIZE), TEST_PACKET_SIZE);
+typedef ::testing::Types<MtpFfsHandle, MtpFfsCompatHandle> mtpHandles;
+TYPED_TEST_CASE(MtpFfsHandleTest, mtpHandles);
+
+TYPED_TEST(MtpFfsHandleTest, testRead) {
+    EXPECT_EQ(write(this->bulk_out, dummyDataStr.c_str(), TEST_PACKET_SIZE), TEST_PACKET_SIZE);
     char buf[TEST_PACKET_SIZE + 1];
     buf[TEST_PACKET_SIZE] = '\0';
-    EXPECT_EQ(handle->read(buf, TEST_PACKET_SIZE), TEST_PACKET_SIZE);
+    EXPECT_EQ(this->handle->read(buf, TEST_PACKET_SIZE), TEST_PACKET_SIZE);
     EXPECT_STREQ(buf, dummyDataStr.c_str());
 }
 
-TEST_F(MtpFfsHandleTest, testWrite) {
+TYPED_TEST(MtpFfsHandleTest, testWrite) {
     char buf[TEST_PACKET_SIZE + 1];
     buf[TEST_PACKET_SIZE] = '\0';
-    EXPECT_EQ(handle->write(dummyDataStr.c_str(), TEST_PACKET_SIZE), TEST_PACKET_SIZE);
-    EXPECT_EQ(read(bulk_in, buf, TEST_PACKET_SIZE), TEST_PACKET_SIZE);
+    EXPECT_EQ(this->handle->write(dummyDataStr.c_str(), TEST_PACKET_SIZE), TEST_PACKET_SIZE);
+    EXPECT_EQ(read(this->bulk_in, buf, TEST_PACKET_SIZE), TEST_PACKET_SIZE);
     EXPECT_STREQ(buf, dummyDataStr.c_str());
 }
 
-TEST_F(MtpFfsHandleTest, testReceiveFileSmall) {
+TYPED_TEST(MtpFfsHandleTest, testReceiveFileEmpty) {
+    std::stringstream ss;
+    mtp_file_range mfr;
+    int size = 0;
+    char buf[size + 1];
+    buf[size] = '\0';
+
+    mfr.offset = 0;
+    mfr.length = size;
+    mfr.fd = this->dummy_file.fd;
+
+    EXPECT_EQ(write(this->bulk_out, ss.str().c_str(), size), size);
+    EXPECT_EQ(this->handle->receiveFile(mfr, false), 0);
+
+    EXPECT_EQ(read(this->dummy_file.fd, buf, size), size);
+}
+
+TYPED_TEST(MtpFfsHandleTest, testReceiveFileSmall) {
     std::stringstream ss;
     mtp_file_range mfr;
     int size = TEST_PACKET_SIZE * SMALL_MULT;
     char buf[size + 1];
     buf[size] = '\0';
 
+    mfr.offset = 0;
     mfr.length = size;
-    mfr.fd = dummy_file.fd;
+    mfr.fd = this->dummy_file.fd;
     for (int i = 0; i < SMALL_MULT; i++)
         ss << dummyDataStr;
 
-    EXPECT_EQ(write(bulk_out, ss.str().c_str(), size), size);
-    EXPECT_EQ(handle->receiveFile(mfr, false), 0);
+    EXPECT_EQ(write(this->bulk_out, ss.str().c_str(), size), size);
+    EXPECT_EQ(this->handle->receiveFile(mfr, false), 0);
 
-    EXPECT_EQ(read(dummy_file.fd, buf, size), size);
+    EXPECT_EQ(read(this->dummy_file.fd, buf, size), size);
 
     EXPECT_STREQ(buf, ss.str().c_str());
 }
 
-TEST_F(MtpFfsHandleTest, testReceiveFileMed) {
+TYPED_TEST(MtpFfsHandleTest, testReceiveFileMed) {
     std::stringstream ss;
     mtp_file_range mfr;
     int size = TEST_PACKET_SIZE * MED_MULT;
     char buf[size + 1];
     buf[size] = '\0';
 
+    mfr.offset = 0;
     mfr.length = size;
-    mfr.fd = dummy_file.fd;
+    mfr.fd = this->dummy_file.fd;
     for (int i = 0; i < MED_MULT; i++)
         ss << dummyDataStr;
 
-    EXPECT_EQ(write(bulk_out, ss.str().c_str(), size), size);
-    EXPECT_EQ(handle->receiveFile(mfr, false), 0);
+    EXPECT_EQ(write(this->bulk_out, ss.str().c_str(), size), size);
+    EXPECT_EQ(this->handle->receiveFile(mfr, false), 0);
 
-    EXPECT_EQ(read(dummy_file.fd, buf, size), size);
+    EXPECT_EQ(read(this->dummy_file.fd, buf, size), size);
 
     EXPECT_STREQ(buf, ss.str().c_str());
 }
 
-TEST_F(MtpFfsHandleTest, testSendFileSmall) {
+TYPED_TEST(MtpFfsHandleTest, testReceiveFileMedPartial) {
+    std::stringstream ss;
+    mtp_file_range mfr;
+    int size = TEST_PACKET_SIZE * MED_MULT;
+    char buf[size + 1];
+    buf[size] = '\0';
+
+    mfr.fd = this->dummy_file.fd;
+    for (int i = 0; i < MED_MULT; i++)
+        ss << dummyDataStr;
+
+    EXPECT_EQ(write(this->bulk_out, ss.str().c_str(), size), size);
+
+    std::random_device rd;
+    std::mt19937 gen(rd());
+    std::uniform_int_distribution<> dis(1, TEST_PACKET_SIZE);
+    int offset = 0;
+    while (offset != size) {
+        mfr.offset = offset;
+        int length = std::min(size - offset, dis(gen));
+        mfr.length = length;
+
+        EXPECT_EQ(this->handle->receiveFile(mfr, false), 0);
+        offset += length;
+    }
+
+    EXPECT_EQ(read(this->dummy_file.fd, buf, size), size);
+
+    EXPECT_STREQ(buf, ss.str().c_str());
+}
+
+TYPED_TEST(MtpFfsHandleTest, testSendFileSmall) {
     std::stringstream ss;
     mtp_file_range mfr;
     mfr.command = 42;
@@ -154,14 +211,14 @@
     buf[size + sizeof(mtp_data_header)] = '\0';
 
     mfr.length = size;
-    mfr.fd = dummy_file.fd;
+    mfr.fd = this->dummy_file.fd;
     for (int i = 0; i < SMALL_MULT; i++)
         ss << dummyDataStr;
 
-    EXPECT_EQ(write(dummy_file.fd, ss.str().c_str(), size), size);
-    EXPECT_EQ(handle->sendFile(mfr), 0);
+    EXPECT_EQ(write(this->dummy_file.fd, ss.str().c_str(), size), size);
+    EXPECT_EQ(this->handle->sendFile(mfr), 0);
 
-    EXPECT_EQ(read(bulk_in, buf, size + sizeof(mtp_data_header)),
+    EXPECT_EQ(read(this->bulk_in, buf, size + sizeof(mtp_data_header)),
             static_cast<long>(size + sizeof(mtp_data_header)));
 
     struct mtp_data_header *header = reinterpret_cast<struct mtp_data_header*>(buf);
@@ -172,7 +229,7 @@
     EXPECT_EQ(header->transaction_id, static_cast<unsigned int>(1337));
 }
 
-TEST_F(MtpFfsHandleTest, testSendFileMed) {
+TYPED_TEST(MtpFfsHandleTest, testSendFileMed) {
     std::stringstream ss;
     mtp_file_range mfr;
     mfr.command = 42;
@@ -183,14 +240,14 @@
     buf[size + sizeof(mtp_data_header)] = '\0';
 
     mfr.length = size;
-    mfr.fd = dummy_file.fd;
+    mfr.fd = this->dummy_file.fd;
     for (int i = 0; i < MED_MULT; i++)
         ss << dummyDataStr;
 
-    EXPECT_EQ(write(dummy_file.fd, ss.str().c_str(), size), size);
-    EXPECT_EQ(handle->sendFile(mfr), 0);
+    EXPECT_EQ(write(this->dummy_file.fd, ss.str().c_str(), size), size);
+    EXPECT_EQ(this->handle->sendFile(mfr), 0);
 
-    EXPECT_EQ(read(bulk_in, buf, size + sizeof(mtp_data_header)),
+    EXPECT_EQ(read(this->bulk_in, buf, size + sizeof(mtp_data_header)),
             static_cast<long>(size + sizeof(mtp_data_header)));
 
     struct mtp_data_header *header = reinterpret_cast<struct mtp_data_header*>(buf);
@@ -201,10 +258,10 @@
     EXPECT_EQ(header->transaction_id, static_cast<unsigned int>(1337));
 }
 
-TEST_F(MtpFfsHandleTest, testSendFileMedPartial) {
+TYPED_TEST(MtpFfsHandleTest, testSendFileMedPartial) {
     std::stringstream ss;
     mtp_file_range mfr;
-    mfr.fd = dummy_file.fd;
+    mfr.fd = this->dummy_file.fd;
     mfr.command = 42;
     mfr.transaction_id = 1337;
     int size = TEST_PACKET_SIZE * MED_MULT;
@@ -214,7 +271,7 @@
     for (int i = 0; i < MED_MULT; i++)
         ss << dummyDataStr;
 
-    EXPECT_EQ(write(dummy_file.fd, ss.str().c_str(), size), size);
+    EXPECT_EQ(write(this->dummy_file.fd, ss.str().c_str(), size), size);
 
     std::random_device rd;
     std::mt19937 gen(rd());
@@ -225,9 +282,9 @@
         int length = std::min(size - offset, dis(gen));
         mfr.length = length;
         char temp_buf[length + sizeof(mtp_data_header)];
-        EXPECT_EQ(handle->sendFile(mfr), 0);
+        EXPECT_EQ(this->handle->sendFile(mfr), 0);
 
-        EXPECT_EQ(read(bulk_in, temp_buf, length + sizeof(mtp_data_header)),
+        EXPECT_EQ(read(this->bulk_in, temp_buf, length + sizeof(mtp_data_header)),
                 static_cast<long>(length + sizeof(mtp_data_header)));
 
         struct mtp_data_header *header = reinterpret_cast<struct mtp_data_header*>(temp_buf);
@@ -241,7 +298,7 @@
     EXPECT_STREQ(buf, ss.str().c_str());
 }
 
-TEST_F(MtpFfsHandleTest, testSendFileEmpty) {
+TYPED_TEST(MtpFfsHandleTest, testSendFileEmpty) {
     mtp_file_range mfr;
     mfr.command = 42;
     mfr.transaction_id = 1337;
@@ -251,11 +308,11 @@
     buf[size + sizeof(mtp_data_header)] = '\0';
 
     mfr.length = size;
-    mfr.fd = dummy_file.fd;
+    mfr.fd = this->dummy_file.fd;
 
-    EXPECT_EQ(handle->sendFile(mfr), 0);
+    EXPECT_EQ(this->handle->sendFile(mfr), 0);
 
-    EXPECT_EQ(read(bulk_in, buf, size + sizeof(mtp_data_header)),
+    EXPECT_EQ(read(this->bulk_in, buf, size + sizeof(mtp_data_header)),
             static_cast<long>(size + sizeof(mtp_data_header)));
 
     struct mtp_data_header *header = reinterpret_cast<struct mtp_data_header*>(buf);
@@ -265,15 +322,15 @@
     EXPECT_EQ(header->transaction_id, static_cast<unsigned int>(1337));
 }
 
-TEST_F(MtpFfsHandleTest, testSendEvent) {
+TYPED_TEST(MtpFfsHandleTest, testSendEvent) {
     struct mtp_event event;
     event.length = TEST_PACKET_SIZE;
     event.data = const_cast<char*>(dummyDataStr.c_str());
     char buf[TEST_PACKET_SIZE + 1];
     buf[TEST_PACKET_SIZE] = '\0';
 
-    handle->sendEvent(event);
-    read(intr, buf, TEST_PACKET_SIZE);
+    this->handle->sendEvent(event);
+    read(this->intr, buf, TEST_PACKET_SIZE);
     EXPECT_STREQ(buf, dummyDataStr.c_str());
 }
 
diff --git a/media/mtp/tests/PosixAsyncIO_test.cpp b/media/mtp/tests/PosixAsyncIO_test.cpp
new file mode 100644
index 0000000..63b9a35
--- /dev/null
+++ b/media/mtp/tests/PosixAsyncIO_test.cpp
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2016 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 "PosixAsyncIO_test.cpp"
+
+#include <android-base/test_utils.h>
+#include <fcntl.h>
+#include <gtest/gtest.h>
+#include <string>
+#include <unistd.h>
+#include <utils/Log.h>
+
+#include "PosixAsyncIO.h"
+
+namespace android {
+
+constexpr int TEST_PACKET_SIZE = 512;
+
+static const std::string dummyDataStr =
+    "/*\n * Copyright 2015 The Android Open Source Project\n *\n * Licensed un"
+    "der the Apache License, Version 2.0 (the \"License\");\n * you may not us"
+    "e this file except in compliance with the License.\n * You may obtain a c"
+    "opy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE"
+    "-2.0\n *\n * Unless required by applicable law or agreed to in writing, s"
+    "oftware\n * distributed under the License is distributed on an \"AS IS\" "
+    "BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express o"
+    "r implied.\n * Se";
+
+
+class PosixAsyncIOTest : public ::testing::Test {
+protected:
+    TemporaryFile dummy_file;
+
+    PosixAsyncIOTest() {}
+    ~PosixAsyncIOTest() {}
+};
+
+TEST_F(PosixAsyncIOTest, testRead) {
+    char buf[TEST_PACKET_SIZE + 1];
+    buf[TEST_PACKET_SIZE] = '\0';
+    EXPECT_EQ(write(dummy_file.fd, dummyDataStr.c_str(), TEST_PACKET_SIZE), TEST_PACKET_SIZE);
+    struct aiocb aio;
+    struct aiocb *aiol[] = {&aio};
+    aio.aio_fildes = dummy_file.fd;
+    aio.aio_buf = buf;
+    aio.aio_offset = 0;
+    aio.aio_nbytes = TEST_PACKET_SIZE;
+
+    EXPECT_EQ(aio_read(&aio), 0);
+    EXPECT_EQ(aio_suspend(aiol, 1, nullptr), 0);
+    EXPECT_EQ(aio_return(&aio), TEST_PACKET_SIZE);
+    EXPECT_STREQ(buf, dummyDataStr.c_str());
+}
+
+TEST_F(PosixAsyncIOTest, testWrite) {
+    char buf[TEST_PACKET_SIZE + 1];
+    buf[TEST_PACKET_SIZE] = '\0';
+    struct aiocb aio;
+    struct aiocb *aiol[] = {&aio};
+    aio.aio_fildes = dummy_file.fd;
+    aio.aio_buf = const_cast<char*>(dummyDataStr.c_str());
+    aio.aio_offset = 0;
+    aio.aio_nbytes = TEST_PACKET_SIZE;
+
+    EXPECT_EQ(aio_write(&aio), 0);
+    EXPECT_EQ(aio_suspend(aiol, 1, nullptr), 0);
+    EXPECT_EQ(aio_return(&aio), TEST_PACKET_SIZE);
+    EXPECT_EQ(read(dummy_file.fd, buf, TEST_PACKET_SIZE), TEST_PACKET_SIZE);
+    EXPECT_STREQ(buf, dummyDataStr.c_str());
+}
+
+TEST_F(PosixAsyncIOTest, testError) {
+    char buf[TEST_PACKET_SIZE + 1];
+    buf[TEST_PACKET_SIZE] = '\0';
+    struct aiocb aio;
+    struct aiocb *aiol[] = {&aio};
+    aio.aio_fildes = -1;
+    aio.aio_buf = const_cast<char*>(dummyDataStr.c_str());
+    aio.aio_offset = 0;
+    aio.aio_nbytes = TEST_PACKET_SIZE;
+
+    EXPECT_EQ(aio_write(&aio), 0);
+    EXPECT_EQ(aio_suspend(aiol, 1, nullptr), 0);
+    EXPECT_EQ(aio_return(&aio), -1);
+    EXPECT_EQ(aio_error(&aio), EBADF);
+}
+
+} // namespace android
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index be62e6c..f906b6f 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -326,7 +326,7 @@
     sp<MmapThread> thread = mMmapThreads.valueFor(io);
     if (thread != 0) {
         interface = new MmapThreadHandle(thread);
-        thread->configure(attr, streamType, sessionId, callback, portId);
+        thread->configure(attr, streamType, sessionId, callback, *deviceId, portId);
         *handle = portId;
     } else {
         ret = NO_INIT;
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 2bc7bd5..85d19cb 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -7550,6 +7550,8 @@
         AudioHwDevice *hwDev, sp<StreamHalInterface> stream,
         audio_devices_t outDevice, audio_devices_t inDevice, bool systemReady)
     : ThreadBase(audioFlinger, id, outDevice, inDevice, MMAP, systemReady),
+      mSessionId(AUDIO_SESSION_NONE),
+      mDeviceId(AUDIO_PORT_HANDLE_NONE), mPortId(AUDIO_PORT_HANDLE_NONE),
       mHalStream(stream), mHalDevice(hwDev->hwDevice()), mAudioHwDev(hwDev),
       mActiveTracks(&this->mLocalLog)
 {
@@ -7585,11 +7587,13 @@
                                                 audio_stream_type_t streamType __unused,
                                                 audio_session_t sessionId,
                                                 const sp<MmapStreamCallback>& callback,
+                                                audio_port_handle_t deviceId,
                                                 audio_port_handle_t portId)
 {
     mAttr = *attr;
     mSessionId = sessionId;
     mCallback = callback;
+    mDeviceId = deviceId;
     mPortId = portId;
 }
 
@@ -7645,7 +7649,7 @@
         audio_stream_type_t stream = streamType();
         audio_output_flags_t flags =
                 (audio_output_flags_t)(AUDIO_OUTPUT_FLAG_MMAP_NOIRQ | AUDIO_OUTPUT_FLAG_DIRECT);
-        audio_port_handle_t deviceId = AUDIO_PORT_HANDLE_NONE;
+        audio_port_handle_t deviceId = mDeviceId;
         ret = AudioSystem::getOutputForAttr(&mAttr, &io,
                                             mSessionId,
                                             &stream,
@@ -7659,7 +7663,7 @@
         config.sample_rate = mSampleRate;
         config.channel_mask = mChannelMask;
         config.format = mFormat;
-        audio_port_handle_t deviceId = AUDIO_PORT_HANDLE_NONE;
+        audio_port_handle_t deviceId = mDeviceId;
         ret = AudioSystem::getInputForAttr(&mAttr, &io,
                                               mSessionId,
                                               client.clientPid,
@@ -7996,17 +8000,19 @@
         mPrevOutDevice = type;
         sendIoConfigEvent_l(AUDIO_OUTPUT_CONFIG_CHANGED);
         sp<MmapStreamCallback> callback = mCallback.promote();
-        if (callback != 0) {
+        if (mDeviceId != deviceId && callback != 0) {
             callback->onRoutingChanged(deviceId);
         }
+        mDeviceId = deviceId;
     }
     if (!isOutput() && mPrevInDevice != mInDevice) {
         mPrevInDevice = type;
         sendIoConfigEvent_l(AUDIO_INPUT_CONFIG_CHANGED);
         sp<MmapStreamCallback> callback = mCallback.promote();
-        if (callback != 0) {
+        if (mDeviceId != deviceId && callback != 0) {
             callback->onRoutingChanged(deviceId);
         }
+        mDeviceId = deviceId;
     }
     return status;
 }
@@ -8247,9 +8253,10 @@
                                                 audio_stream_type_t streamType,
                                                 audio_session_t sessionId,
                                                 const sp<MmapStreamCallback>& callback,
+                                                audio_port_handle_t deviceId,
                                                 audio_port_handle_t portId)
 {
-    MmapThread::configure(attr, streamType, sessionId, callback, portId);
+    MmapThread::configure(attr, streamType, sessionId, callback, deviceId, portId);
     mStreamType = streamType;
 }
 
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 613d08c..bfd9a3f 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -1482,6 +1482,7 @@
                                       audio_stream_type_t streamType,
                                       audio_session_t sessionId,
                                       const sp<MmapStreamCallback>& callback,
+                                      audio_port_handle_t deviceId,
                                       audio_port_handle_t portId);
 
                 void        disconnect();
@@ -1543,6 +1544,7 @@
 
                 audio_attributes_t      mAttr;
                 audio_session_t         mSessionId;
+                audio_port_handle_t     mDeviceId;
                 audio_port_handle_t     mPortId;
 
                 wp<MmapStreamCallback>  mCallback;
@@ -1565,6 +1567,7 @@
                                       audio_stream_type_t streamType,
                                       audio_session_t sessionId,
                                       const sp<MmapStreamCallback>& callback,
+                                      audio_port_handle_t deviceId,
                                       audio_port_handle_t portId);
 
                 AudioStreamOut* clearOutput();
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
index 8593444..4d3c3b5 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioOutputDescriptor.cpp
@@ -46,17 +46,17 @@
     for (int i = 0; i < NUM_STRATEGIES; i++) {
         mStrategyMutedByDevice[i] = false;
     }
-    if (port != NULL) {
-        port->pickAudioProfile(mSamplingRate, mChannelMask, mFormat);
-        if (port->mGains.size() > 0) {
-            port->mGains[0]->getDefaultConfig(&mGain);
+    if (mPort.get() != nullptr) {
+        mPort->pickAudioProfile(mSamplingRate, mChannelMask, mFormat);
+        if (mPort->mGains.size() > 0) {
+            mPort->mGains[0]->getDefaultConfig(&mGain);
         }
     }
 }
 
 audio_module_handle_t AudioOutputDescriptor::getModuleHandle() const
 {
-    return mPort->getModuleHandle();
+    return mPort.get() != nullptr ? mPort->getModuleHandle() : AUDIO_MODULE_HANDLE_NONE;
 }
 
 audio_port_handle_t AudioOutputDescriptor::getId() const
@@ -175,9 +175,9 @@
     dstConfig->ext.mix.usecase.stream = AUDIO_STREAM_DEFAULT;
 }
 
-void AudioOutputDescriptor::toAudioPort(
-                                                    struct audio_port *port) const
+void AudioOutputDescriptor::toAudioPort(struct audio_port *port) const
 {
+    // Should not be called for duplicated ports, see SwAudioOutputDescriptor::toAudioPortConfig.
     mPort->toAudioPort(port);
     port->id = mId;
     port->ext.mix.hw_module = getModuleHandle();
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 2c88e47..d2a2855 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -1052,7 +1052,7 @@
         flags = (audio_output_flags_t)(flags & ~AUDIO_OUTPUT_FLAG_DIRECT);
         output = selectOutput(outputs, flags, format);
     }
-    ALOGW_IF((output == 0), "getOutput() could not find output for stream %d, samplingRate %d,"
+    ALOGW_IF((output == 0), "getOutput() could not find output for stream %d, samplingRate %d, "
             "format %d, channels %x, flags %x", stream, samplingRate, format, channelMask, flags);
 
     return output;
@@ -1278,9 +1278,9 @@
 
         // apply volume rules for current stream and device if necessary
         checkAndSetVolume(stream,
-                          mVolumeCurves->getVolumeIndex(stream, device),
+                          mVolumeCurves->getVolumeIndex(stream, outputDesc->device()),
                           outputDesc,
-                          device);
+                          outputDesc->device());
 
         // update the outputs if starting an output with a stream that can affect notification
         // routing
@@ -1618,7 +1618,7 @@
         } else if (profileFlags != AUDIO_INPUT_FLAG_NONE) {
             profileFlags = AUDIO_INPUT_FLAG_NONE; // retry
         } else { // fail
-            ALOGW("getInputForDevice() could not find profile for device 0x%X,"
+            ALOGW("getInputForDevice() could not find profile for device 0x%X, "
                   "samplingRate %u, format %#x, channelMask 0x%X, flags %#x",
                     device, samplingRate, format, channelMask, flags);
             return input;
@@ -4478,10 +4478,10 @@
     //      use device for strategy enforced audible
     // 2: we are in call or the strategy phone is active on the output:
     //      use device for strategy phone
-    // 3: the strategy for enforced audible is active but not enforced on the output:
-    //      use the device for strategy enforced audible
-    // 4: the strategy sonification is active on the output:
+    // 3: the strategy sonification is active on the output:
     //      use device for strategy sonification
+    // 4: the strategy for enforced audible is active but not enforced on the output:
+    //      use the device for strategy enforced audible
     // 5: the strategy accessibility is active on the output:
     //      use device for strategy accessibility
     // 6: the strategy "respectful" sonification is active on the output:
@@ -4498,10 +4498,10 @@
     } else if (isInCall() ||
                     isStrategyActive(outputDesc, STRATEGY_PHONE)) {
         device = getDeviceForStrategy(STRATEGY_PHONE, fromCache);
-    } else if (isStrategyActive(outputDesc, STRATEGY_ENFORCED_AUDIBLE)) {
-        device = getDeviceForStrategy(STRATEGY_ENFORCED_AUDIBLE, fromCache);
     } else if (isStrategyActive(outputDesc, STRATEGY_SONIFICATION)) {
         device = getDeviceForStrategy(STRATEGY_SONIFICATION, fromCache);
+    } else if (isStrategyActive(outputDesc, STRATEGY_ENFORCED_AUDIBLE)) {
+        device = getDeviceForStrategy(STRATEGY_ENFORCED_AUDIBLE, fromCache);
     } else if (isStrategyActive(outputDesc, STRATEGY_ACCESSIBILITY)) {
         device = getDeviceForStrategy(STRATEGY_ACCESSIBILITY, fromCache);
     } else if (isStrategyActive(outputDesc, STRATEGY_SONIFICATION_RESPECTFUL)) {
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 02f5424..94e8f3b 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -2439,25 +2439,6 @@
     nsecs_t sensorTimestamp = request.sensorTimestamp;
     nsecs_t shutterTimestamp = request.shutterTimestamp;
 
-    bool skipResultMetadata = false;
-    if (request.requestStatus != OK) {
-        switch (request.requestStatus) {
-            case CAMERA3_MSG_ERROR_DEVICE:
-            case CAMERA3_MSG_ERROR_REQUEST:
-            case CAMERA3_MSG_ERROR_RESULT:
-                skipResultMetadata = true;
-                break;
-            case CAMERA3_MSG_ERROR_BUFFER:
-                //Result metadata should return in this case.
-                skipResultMetadata = false;
-                break;
-            default:
-                SET_ERR("Unknown error message: %d", request.requestStatus);
-                skipResultMetadata = false;
-                break;
-        }
-    }
-
     // Check if it's okay to remove the request from InFlightMap:
     // In the case of a successful request:
     //      all input and output buffers, all result metadata, shutter callback
@@ -2465,7 +2446,7 @@
     // In the case of a unsuccessful request:
     //      all input and output buffers arrived.
     if (request.numBuffersLeft == 0 &&
-            (skipResultMetadata ||
+            (request.skipResultMetadata ||
             (request.haveResultMetadata && shutterTimestamp != 0))) {
         ATRACE_ASYNC_END("frame capture", frameNumber);
 
@@ -2941,6 +2922,11 @@
                     InFlightRequest &r = mInFlightMap.editValueAt(idx);
                     r.requestStatus = msg.error_code;
                     resultExtras = r.resultExtras;
+                    if (hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_RESULT == errorCode
+                            ||  hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_REQUEST ==
+                            errorCode) {
+                        r.skipResultMetadata = true;
+                    }
                     if (hardware::camera2::ICameraDeviceCallbacks::ERROR_CAMERA_RESULT ==
                             errorCode) {
                         // In case of missing result check whether the buffers
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index b5f19d7..363bd88 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -885,6 +885,11 @@
         // For auto-exposure modes, equal to 1/(lower end of target FPS range)
         nsecs_t maxExpectedDuration;
 
+        // Whether the result metadata for this request is to be skipped. The
+        // result metadata should be skipped in the case of
+        // REQUEST/RESULT error.
+        bool skipResultMetadata;
+
         // Default constructor needed by KeyedVector
         InFlightRequest() :
                 shutterTimestamp(0),
@@ -894,7 +899,8 @@
                 numBuffersLeft(0),
                 hasInputBuffer(false),
                 hasCallback(true),
-                maxExpectedDuration(kDefaultExpectedDuration) {
+                maxExpectedDuration(kDefaultExpectedDuration),
+                skipResultMetadata(false) {
         }
 
         InFlightRequest(int numBuffers, CaptureResultExtras extras, bool hasInput,
@@ -907,7 +913,8 @@
                 resultExtras(extras),
                 hasInputBuffer(hasInput),
                 hasCallback(hasAppCallback),
-                maxExpectedDuration(maxDuration) {
+                maxExpectedDuration(maxDuration),
+                skipResultMetadata(false) {
         }
     };
 
diff --git a/services/mediacodec/seccomp_policy/mediacodec-arm.policy b/services/mediacodec/seccomp_policy/mediacodec-arm.policy
index cbd7fb9..4c4b594 100644
--- a/services/mediacodec/seccomp_policy/mediacodec-arm.policy
+++ b/services/mediacodec/seccomp_policy/mediacodec-arm.policy
@@ -68,3 +68,4 @@
 recvmsg: 1
 getpid: 1
 gettid: 1
+process_vm_readv: 1
diff --git a/services/mediaextractor/seccomp_policy/mediaextractor-arm.policy b/services/mediaextractor/seccomp_policy/mediaextractor-arm.policy
index 23d349d..e06ac8c 100644
--- a/services/mediaextractor/seccomp_policy/mediaextractor-arm.policy
+++ b/services/mediaextractor/seccomp_policy/mediaextractor-arm.policy
@@ -61,3 +61,4 @@
 ppoll: 1
 getpid: 1
 gettid: 1
+process_vm_readv: 1
diff --git a/services/mediaextractor/seccomp_policy/mediaextractor-arm64.policy b/services/mediaextractor/seccomp_policy/mediaextractor-arm64.policy
index 42e0d75..4b51457 100644
--- a/services/mediaextractor/seccomp_policy/mediaextractor-arm64.policy
+++ b/services/mediaextractor/seccomp_policy/mediaextractor-arm64.policy
@@ -52,3 +52,4 @@
 ppoll: 1
 getpid: 1
 gettid: 1
+process_vm_readv: 1
diff --git a/services/mediaextractor/seccomp_policy/mediaextractor-x86.policy b/services/mediaextractor/seccomp_policy/mediaextractor-x86.policy
index 76403f2..cdff4db 100644
--- a/services/mediaextractor/seccomp_policy/mediaextractor-x86.policy
+++ b/services/mediaextractor/seccomp_policy/mediaextractor-x86.policy
@@ -56,6 +56,7 @@
 getdents64: 1
 pipe2: 1
 ppoll: 1
+process_vm_readv: 1
 
 # Required by AddressSanitizer
 gettid: 1
diff --git a/services/oboeservice/AAudioEndpointManager.cpp b/services/oboeservice/AAudioEndpointManager.cpp
index ec2f5b9..f41219e 100644
--- a/services/oboeservice/AAudioEndpointManager.cpp
+++ b/services/oboeservice/AAudioEndpointManager.cpp
@@ -26,6 +26,10 @@
 #include <utility/AAudioUtilities.h>
 
 #include "AAudioEndpointManager.h"
+#include "AAudioServiceEndpointShared.h"
+#include "AAudioServiceEndpointMMAP.h"
+#include "AAudioServiceEndpointCapture.h"
+#include "AAudioServiceEndpointPlay.h"
 
 using namespace android;
 using namespace aaudio;
@@ -34,159 +38,241 @@
 
 AAudioEndpointManager::AAudioEndpointManager()
         : Singleton<AAudioEndpointManager>()
-        , mInputs()
-        , mOutputs() {
+        , mSharedStreams()
+        , mExclusiveStreams() {
 }
 
 std::string AAudioEndpointManager::dump() const {
     std::stringstream result;
-    const bool isLocked = AAudio_tryUntilTrue(
-            [this]()->bool { return mLock.try_lock(); } /* f */,
-            50 /* times */,
-            20 /* sleepMs */);
-    if (!isLocked) {
-        result << "EndpointManager may be deadlocked\n";
-    }
+    int index = 0;
 
     result << "AAudioEndpointManager:" << "\n";
-    size_t inputs = mInputs.size();
-    result << "Input Endpoints: " << inputs << "\n";
-    for (const auto &input : mInputs) {
-        result << "  Input: " << input->dump() << "\n";
+
+    const bool isSharedLocked = AAudio_tryUntilTrue(
+            [this]()->bool { return mSharedLock.try_lock(); } /* f */,
+            50 /* times */,
+            20 /* sleepMs */);
+    if (!isSharedLocked) {
+        result << "AAudioEndpointManager Shared may be deadlocked\n";
     }
 
-    size_t outputs = mOutputs.size();
-    result << "Output Endpoints: " << outputs << "\n";
-    for (const auto &output : mOutputs) {
-        result << "  Output: " << output->dump() << "\n";
+    {
+        const bool isExclusiveLocked = AAudio_tryUntilTrue(
+                [this]() -> bool { return mExclusiveLock.try_lock(); } /* f */,
+                50 /* times */,
+                20 /* sleepMs */);
+        if (!isExclusiveLocked) {
+            result << "AAudioEndpointManager Exclusive may be deadlocked\n";
+        }
+
+        result << "Exclusive MMAP Endpoints: " << mExclusiveStreams.size() << "\n";
+        index = 0;
+        for (const auto &output : mExclusiveStreams) {
+            result << "  #" << index++ << ":";
+            result << output->dump() << "\n";
+        }
+
+        if (isExclusiveLocked) {
+            mExclusiveLock.unlock();
+        }
     }
 
-    if (isLocked) {
-        mLock.unlock();
+    result << "Shared Endpoints: " << mSharedStreams.size() << "\n";
+    index = 0;
+    for (const auto &input : mSharedStreams) {
+        result << "  #" << index++ << ":";
+        result << input->dump() << "\n";
+    }
+
+    if (isSharedLocked) {
+        mSharedLock.unlock();
     }
     return result.str();
 }
 
-AAudioServiceEndpoint *AAudioEndpointManager::openEndpoint(AAudioService &audioService,
-        const AAudioStreamConfiguration& configuration, aaudio_direction_t direction) {
-    AAudioServiceEndpoint *endpoint = nullptr;
-    AAudioServiceEndpointCapture *capture = nullptr;
-    AAudioServiceEndpointPlay *player = nullptr;
-    std::lock_guard<std::mutex> lock(mLock);
+
+// Try to find an existing endpoint.
+sp<AAudioServiceEndpoint> AAudioEndpointManager::findExclusiveEndpoint_l(
+        const AAudioStreamConfiguration &configuration) {
+    sp<AAudioServiceEndpoint> endpoint;
+    for (const auto ep : mExclusiveStreams) {
+        if (ep->matches(configuration)) {
+            endpoint = ep;
+            break;
+        }
+    }
+
+    ALOGD("AAudioEndpointManager.findExclusiveEndpoint_l(), found %p for device = %d",
+          endpoint.get(), configuration.getDeviceId());
+    return endpoint;
+}
+
+// Try to find an existing endpoint.
+sp<AAudioServiceEndpointShared> AAudioEndpointManager::findSharedEndpoint_l(
+        const AAudioStreamConfiguration &configuration) {
+    sp<AAudioServiceEndpointShared> endpoint;
+    for (const auto ep  : mSharedStreams) {
+        if (ep->matches(configuration)) {
+            endpoint = ep;
+            break;
+        }
+    }
+
+    ALOGD("AAudioEndpointManager.findSharedEndpoint_l(), found %p for device = %d",
+          endpoint.get(), configuration.getDeviceId());
+    return endpoint;
+}
+
+sp<AAudioServiceEndpoint> AAudioEndpointManager::openEndpoint(AAudioService &audioService,
+                                        const aaudio::AAudioStreamRequest &request,
+                                        aaudio_sharing_mode_t sharingMode) {
+    if (sharingMode == AAUDIO_SHARING_MODE_EXCLUSIVE) {
+        return openExclusiveEndpoint(audioService, request);
+    } else {
+        return openSharedEndpoint(audioService, request);
+    }
+}
+
+sp<AAudioServiceEndpoint> AAudioEndpointManager::openExclusiveEndpoint(
+        AAudioService &aaudioService __unused,
+        const aaudio::AAudioStreamRequest &request) {
+
+    std::lock_guard<std::mutex> lock(mExclusiveLock);
+
+    const AAudioStreamConfiguration &configuration = request.getConstantConfiguration();
 
     // Try to find an existing endpoint.
+    sp<AAudioServiceEndpoint> endpoint = findExclusiveEndpoint_l(configuration);
 
+    // If we find an existing one then this one cannot be exclusive.
+    if (endpoint.get() != nullptr) {
+        ALOGE("AAudioEndpointManager.openExclusiveEndpoint() already in use");
+        // Already open so do not allow a second stream.
+        return nullptr;
+    } else {
+        sp<AAudioServiceEndpointMMAP> endpointMMap = new AAudioServiceEndpointMMAP();
+        ALOGE("AAudioEndpointManager.openEndpoint(),created MMAP %p", endpointMMap.get());
+        endpoint = endpointMMap;
 
-
-    switch (direction) {
-        case AAUDIO_DIRECTION_INPUT:
-            for (AAudioServiceEndpoint *ep : mInputs) {
-                if (ep->matches(configuration)) {
-                    endpoint = ep;
-                    break;
-                }
-            }
-            break;
-        case AAUDIO_DIRECTION_OUTPUT:
-            for (AAudioServiceEndpoint *ep : mOutputs) {
-                if (ep->matches(configuration)) {
-                    endpoint = ep;
-                    break;
-                }
-            }
-            break;
-        default:
-            assert(false); // There are only two possible directions.
-            break;
-    }
-    ALOGD("AAudioEndpointManager::openEndpoint(), found %p for device = %d, dir = %d",
-          endpoint, configuration.getDeviceId(), (int)direction);
-
-    // If we can't find an existing one then open a new one.
-    if (endpoint == nullptr) {
-        // we must call openStream with audioserver identity
-        int64_t token = IPCThreadState::self()->clearCallingIdentity();
-        switch(direction) {
-            case AAUDIO_DIRECTION_INPUT:
-                capture = new AAudioServiceEndpointCapture(audioService);
-                endpoint = capture;
-                break;
-            case AAUDIO_DIRECTION_OUTPUT:
-                player = new AAudioServiceEndpointPlay(audioService);
-                endpoint = player;
-                break;
-            default:
-                break;
+        aaudio_result_t result = endpoint->open(request);
+        if (result != AAUDIO_OK) {
+            ALOGE("AAudioEndpointManager.openEndpoint(), open failed");
+            endpoint.clear();
+        } else {
+            mExclusiveStreams.push_back(endpointMMap);
         }
 
-        if (endpoint != nullptr) {
-            aaudio_result_t result = endpoint->open(configuration);
-            if (result != AAUDIO_OK) {
-                ALOGE("AAudioEndpointManager::findEndpoint(), open failed");
-                delete endpoint;
-                endpoint = nullptr;
-            } else {
-                switch(direction) {
-                    case AAUDIO_DIRECTION_INPUT:
-                        mInputs.push_back(capture);
-                        break;
-                    case AAUDIO_DIRECTION_OUTPUT:
-                        mOutputs.push_back(player);
-                        break;
-                    default:
-                        break;
-                }
-            }
-        }
-        ALOGD("AAudioEndpointManager::openEndpoint(), created %p for device = %d, dir = %d",
-              endpoint, configuration.getDeviceId(), (int)direction);
-        IPCThreadState::self()->restoreCallingIdentity(token);
+        ALOGD("AAudioEndpointManager.openEndpoint(), created %p for device = %d",
+              endpoint.get(), configuration.getDeviceId());
     }
 
-    if (endpoint != nullptr) {
-        ALOGD("AAudioEndpointManager::openEndpoint(), sampleRate = %d, framesPerBurst = %d",
-              endpoint->getSampleRate(), endpoint->getFramesPerBurst());
+    if (endpoint.get() != nullptr) {
         // Increment the reference count under this lock.
-        endpoint->setReferenceCount(endpoint->getReferenceCount() + 1);
+        endpoint->setOpenCount(endpoint->getOpenCount() + 1);
     }
     return endpoint;
 }
 
-void AAudioEndpointManager::closeEndpoint(AAudioServiceEndpoint *serviceEndpoint) {
-    std::lock_guard<std::mutex> lock(mLock);
-    if (serviceEndpoint == nullptr) {
-        return;
-    }
+sp<AAudioServiceEndpoint> AAudioEndpointManager::openSharedEndpoint(
+        AAudioService &aaudioService,
+        const aaudio::AAudioStreamRequest &request) {
 
-    // Decrement the reference count under this lock.
-    int32_t newRefCount = serviceEndpoint->getReferenceCount() - 1;
-    serviceEndpoint->setReferenceCount(newRefCount);
-    ALOGD("AAudioEndpointManager::closeEndpoint(%p) newRefCount = %d",
-          serviceEndpoint, newRefCount);
+    std::lock_guard<std::mutex> lock(mSharedLock);
 
-    // If no longer in use then close and delete it.
-    if (newRefCount <= 0) {
-        aaudio_direction_t direction = serviceEndpoint->getDirection();
-        // Track endpoints based on requested deviceId because UNSPECIFIED
-        // can change to a specific device after opening.
-        int32_t deviceId = serviceEndpoint->getRequestedDeviceId();
+    const AAudioStreamConfiguration &configuration = request.getConstantConfiguration();
+    aaudio_direction_t direction = configuration.getDirection();
 
+    // Try to find an existing endpoint.
+    sp<AAudioServiceEndpointShared> endpoint = findSharedEndpoint_l(configuration);
+
+    // If we can't find an existing one then open a new one.
+    if (endpoint.get() == nullptr) {
+        // we must call openStream with audioserver identity
+        int64_t token = IPCThreadState::self()->clearCallingIdentity();
         switch (direction) {
             case AAUDIO_DIRECTION_INPUT:
-                mInputs.erase(
-                  std::remove(mInputs.begin(), mInputs.end(), serviceEndpoint), mInputs.end());
+                endpoint = new AAudioServiceEndpointCapture(aaudioService);
                 break;
             case AAUDIO_DIRECTION_OUTPUT:
-                mOutputs.erase(
-                  std::remove(mOutputs.begin(), mOutputs.end(), serviceEndpoint), mOutputs.end());
+                endpoint = new AAudioServiceEndpointPlay(aaudioService);
                 break;
             default:
                 break;
         }
 
+        if (endpoint.get() != nullptr) {
+            aaudio_result_t result = endpoint->open(request);
+            if (result != AAUDIO_OK) {
+                ALOGE("AAudioEndpointManager.openEndpoint(), open failed");
+                endpoint.clear();
+            } else {
+                mSharedStreams.push_back(endpoint);
+            }
+        }
+        ALOGD("AAudioEndpointManager.openSharedEndpoint(), created %p for device = %d, dir = %d",
+              endpoint.get(), configuration.getDeviceId(), (int)direction);
+        IPCThreadState::self()->restoreCallingIdentity(token);
+    }
+
+    if (endpoint.get() != nullptr) {
+        // Increment the reference count under this lock.
+        endpoint->setOpenCount(endpoint->getOpenCount() + 1);
+    }
+    return endpoint;
+}
+
+void AAudioEndpointManager::closeEndpoint(sp<AAudioServiceEndpoint>serviceEndpoint) {
+    if (serviceEndpoint->getSharingMode() == AAUDIO_SHARING_MODE_EXCLUSIVE) {
+        return closeExclusiveEndpoint(serviceEndpoint);
+    } else {
+        return closeSharedEndpoint(serviceEndpoint);
+    }
+}
+
+void AAudioEndpointManager::closeExclusiveEndpoint(sp<AAudioServiceEndpoint> serviceEndpoint) {
+    if (serviceEndpoint.get() == nullptr) {
+        return;
+    }
+
+    // Decrement the reference count under this lock.
+    std::lock_guard<std::mutex> lock(mExclusiveLock);
+    int32_t newRefCount = serviceEndpoint->getOpenCount() - 1;
+    serviceEndpoint->setOpenCount(newRefCount);
+    ALOGD("AAudioEndpointManager::closeExclusiveEndpoint(%p) newRefCount = %d",
+          serviceEndpoint.get(), newRefCount);
+
+    // If no longer in use then close and delete it.
+    if (newRefCount <= 0) {
+        mExclusiveStreams.erase(
+                std::remove(mExclusiveStreams.begin(), mExclusiveStreams.end(), serviceEndpoint),
+                mExclusiveStreams.end());
+
         serviceEndpoint->close();
-        ALOGD("AAudioEndpointManager::closeEndpoint() delete %p for device %d, dir = %d",
-              serviceEndpoint, deviceId, (int)direction);
-        delete serviceEndpoint;
+        ALOGD("AAudioEndpointManager::closeExclusiveEndpoint() %p for device %d",
+              serviceEndpoint.get(), serviceEndpoint->getDeviceId());
+    }
+}
+
+void AAudioEndpointManager::closeSharedEndpoint(sp<AAudioServiceEndpoint> serviceEndpoint) {
+    if (serviceEndpoint.get() == nullptr) {
+        return;
+    }
+
+    // Decrement the reference count under this lock.
+    std::lock_guard<std::mutex> lock(mSharedLock);
+    int32_t newRefCount = serviceEndpoint->getOpenCount() - 1;
+    serviceEndpoint->setOpenCount(newRefCount);
+    ALOGD("AAudioEndpointManager::closeSharedEndpoint(%p) newRefCount = %d",
+          serviceEndpoint.get(), newRefCount);
+
+    // If no longer in use then close and delete it.
+    if (newRefCount <= 0) {
+        mSharedStreams.erase(
+                std::remove(mSharedStreams.begin(), mSharedStreams.end(), serviceEndpoint),
+                mSharedStreams.end());
+
+        serviceEndpoint->close();
+        ALOGD("AAudioEndpointManager::closeSharedEndpoint() %p for device %d",
+              serviceEndpoint.get(), serviceEndpoint->getDeviceId());
     }
 }
diff --git a/services/oboeservice/AAudioEndpointManager.h b/services/oboeservice/AAudioEndpointManager.h
index 2511b2f..32c8454 100644
--- a/services/oboeservice/AAudioEndpointManager.h
+++ b/services/oboeservice/AAudioEndpointManager.h
@@ -24,11 +24,12 @@
 #include "binding/AAudioServiceMessage.h"
 #include "AAudioServiceEndpoint.h"
 #include "AAudioServiceEndpointCapture.h"
+#include "AAudioServiceEndpointMMAP.h"
 #include "AAudioServiceEndpointPlay.h"
 
 namespace aaudio {
 
-class AAudioEndpointManager : public android::Singleton<AAudioEndpointManager>{
+class AAudioEndpointManager : public android::Singleton<AAudioEndpointManager> {
 public:
     AAudioEndpointManager();
     ~AAudioEndpointManager() = default;
@@ -49,22 +50,42 @@
      * Find a service endpoint for the given deviceId and direction.
      * If an endpoint does not already exist then try to create one.
      *
-     * @param deviceId
-     * @param direction
-     * @return endpoint or nullptr
+     * @param audioService
+     * @param request
+     * @param sharingMode
+     * @return endpoint or null
      */
-    AAudioServiceEndpoint *openEndpoint(android::AAudioService &audioService,
-                                        const AAudioStreamConfiguration& configuration,
-                                        aaudio_direction_t direction);
+    android::sp<AAudioServiceEndpoint> openEndpoint(android::AAudioService &audioService,
+                                        const aaudio::AAudioStreamRequest &request,
+                                        aaudio_sharing_mode_t sharingMode);
 
-    void closeEndpoint(AAudioServiceEndpoint *serviceEndpoint);
+    void closeEndpoint(android::sp<AAudioServiceEndpoint> serviceEndpoint);
 
 private:
+    android::sp<AAudioServiceEndpoint> openExclusiveEndpoint(android::AAudioService &aaudioService,
+                                                 const aaudio::AAudioStreamRequest &request);
 
-    mutable std::mutex mLock;
+    android::sp<AAudioServiceEndpoint> openSharedEndpoint(android::AAudioService &aaudioService,
+                                              const aaudio::AAudioStreamRequest &request);
 
-    std::vector<AAudioServiceEndpointCapture *> mInputs;
-    std::vector<AAudioServiceEndpointPlay *> mOutputs;
+    android::sp<AAudioServiceEndpoint> findExclusiveEndpoint_l(
+            const AAudioStreamConfiguration& configuration);
+
+    android::sp<AAudioServiceEndpointShared> findSharedEndpoint_l(
+            const AAudioStreamConfiguration& configuration);
+
+    void closeExclusiveEndpoint(android::sp<AAudioServiceEndpoint> serviceEndpoint);
+    void closeSharedEndpoint(android::sp<AAudioServiceEndpoint> serviceEndpoint);
+
+    // Use separate locks because opening a Shared endpoint requires opening an Exclusive one.
+    // That could cause a recursive lock.
+    // Lock mSharedLock before mExclusiveLock.
+    // it is OK to only lock mExclusiveLock.
+    mutable std::mutex                                     mSharedLock;
+    std::vector<android::sp<AAudioServiceEndpointShared>>  mSharedStreams;
+
+    mutable std::mutex                                     mExclusiveLock;
+    std::vector<android::sp<AAudioServiceEndpointMMAP>>    mExclusiveStreams;
 
 };
 
diff --git a/services/oboeservice/AAudioService.cpp b/services/oboeservice/AAudioService.cpp
index 3992719..855ae69 100644
--- a/services/oboeservice/AAudioService.cpp
+++ b/services/oboeservice/AAudioService.cpp
@@ -114,14 +114,12 @@
                 mAudioClient.clientUid == IPCThreadState::self()->getCallingUid()) {
             inService = request.isInService();
         }
-        serviceStream = new AAudioServiceStreamMMAP(mAudioClient, inService);
-        result = serviceStream->open(request, configurationOutput);
+        serviceStream = new AAudioServiceStreamMMAP(*this, inService);
+        result = serviceStream->open(request);
         if (result != AAUDIO_OK) {
-            // fall back to using a shared stream
+            // Clear it so we can possibly fall back to using a shared stream.
             ALOGW("AAudioService::openStream(), could not open in EXCLUSIVE mode");
             serviceStream.clear();
-        } else {
-            configurationOutput.setSharingMode(AAUDIO_SHARING_MODE_EXCLUSIVE);
         }
     }
 
@@ -129,8 +127,7 @@
     if (sharingMode == AAUDIO_SHARING_MODE_SHARED
          || (serviceStream == nullptr && !sharingModeMatchRequired)) {
         serviceStream =  new AAudioServiceStreamShared(*this);
-        result = serviceStream->open(request, configurationOutput);
-        configurationOutput.setSharingMode(AAUDIO_SHARING_MODE_SHARED);
+        result = serviceStream->open(request);
     }
 
     if (result != AAUDIO_OK) {
@@ -149,6 +146,7 @@
             serviceStream->setHandle(handle);
             pid_t pid = request.getProcessId();
             AAudioClientTracker::getInstance().registerClientStream(pid, serviceStream);
+            configurationOutput.copyFrom(*serviceStream);
         }
         return handle;
     }
diff --git a/services/oboeservice/AAudioService.h b/services/oboeservice/AAudioService.h
index 8421efc..ffaf538 100644
--- a/services/oboeservice/AAudioService.h
+++ b/services/oboeservice/AAudioService.h
@@ -28,7 +28,9 @@
 #include "binding/IAAudioService.h"
 #include "binding/AAudioServiceInterface.h"
 
-#include "AAudioServiceStreamBase.h"
+namespace aaudio {
+    class AAudioServiceStreamBase;
+};
 
 namespace android {
 
diff --git a/services/oboeservice/AAudioServiceEndpoint.cpp b/services/oboeservice/AAudioServiceEndpoint.cpp
index 5a78e11..cba5bc8 100644
--- a/services/oboeservice/AAudioServiceEndpoint.cpp
+++ b/services/oboeservice/AAudioServiceEndpoint.cpp
@@ -33,17 +33,14 @@
 #include "core/AudioStreamBuilder.h"
 #include "AAudioServiceEndpoint.h"
 #include "AAudioServiceStreamShared.h"
+#include "AAudioServiceEndpointShared.h"
 
 using namespace android;  // TODO just import names needed
 using namespace aaudio;   // TODO just import names needed
 
-#define MIN_TIMEOUT_NANOS        (1000 * AAUDIO_NANOS_PER_MILLISECOND)
-
-// 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)
+AAudioServiceEndpoint::~AAudioServiceEndpoint() {
+    ALOGD("AAudioServiceEndpoint::~AAudioServiceEndpoint() destroying endpoint %p", this);
+}
 
 std::string AAudioServiceEndpoint::dump() const {
     std::stringstream result;
@@ -53,20 +50,20 @@
             50 /* times */,
             20 /* sleepMs */);
     if (!isLocked) {
-        result << "EndpointManager may be deadlocked\n";
+        result << "AAudioServiceEndpoint may be deadlocked\n";
     }
 
-    AudioStreamInternal     *stream = mStreamInternal;
-    if (stream == nullptr) {
-        result << "null stream!" << "\n";
-    } else {
-        result << "mmap stream: rate = " << stream->getSampleRate() << "\n";
-    }
-
-    result << "    Registered Streams:" << "\n";
+    result << "    Direction:            " << ((getDirection() == AAUDIO_DIRECTION_OUTPUT)
+                                   ? "OUTPUT" : "INPUT") << "\n";
+    result << "    Sample Rate:          " << getSampleRate() << "\n";
+    result << "    Frames Per Burst:     " << mFramesPerBurst << "\n";
+    result << "    Reference Count:      " << mOpenCount << "\n";
+    result << "    Requested Device Id:  " << mRequestedDeviceId << "\n";
+    result << "    Device Id:            " << getDeviceId() << "\n";
+    result << "    Registered Streams:  " << "\n";
     result << AAudioServiceStreamShared::dumpHeader() << "\n";
-    for (const sp<AAudioServiceStreamShared>& sharedStream : mRegisteredStreams) {
-        result << sharedStream->dump() << "\n";
+    for (const auto stream : mRegisteredStreams) {
+        result << stream->dump() << "\n";
     }
 
     if (isLocked) {
@@ -75,113 +72,44 @@
     return result.str();
 }
 
-// Set up an EXCLUSIVE MMAP stream that will be shared.
-aaudio_result_t AAudioServiceEndpoint::open(const AAudioStreamConfiguration& configuration) {
-    mRequestedDeviceId = configuration.getDeviceId();
-    mStreamInternal = getStreamInternal();
-
-    AudioStreamBuilder builder;
-    builder.setSharingMode(AAUDIO_SHARING_MODE_EXCLUSIVE);
-    // Don't fall back to SHARED because that would cause recursion.
-    builder.setSharingModeMatchRequired(true);
-    builder.setDeviceId(mRequestedDeviceId);
-    builder.setFormat(configuration.getFormat());
-    builder.setSampleRate(configuration.getSampleRate());
-    builder.setSamplesPerFrame(configuration.getSamplesPerFrame());
-    builder.setDirection(getDirection());
-    builder.setBufferCapacity(DEFAULT_BUFFER_CAPACITY);
-
-    return getStreamInternal()->open(builder);
-}
-
-aaudio_result_t AAudioServiceEndpoint::close() {
-     return getStreamInternal()->close();
-}
-
-// TODO, maybe use an interface to reduce exposure
-aaudio_result_t AAudioServiceEndpoint::registerStream(sp<AAudioServiceStreamShared>sharedStream) {
-    std::lock_guard<std::mutex> lock(mLockStreams);
-    mRegisteredStreams.push_back(sharedStream);
-    return AAUDIO_OK;
-}
-
-aaudio_result_t AAudioServiceEndpoint::unregisterStream(sp<AAudioServiceStreamShared>sharedStream) {
-    std::lock_guard<std::mutex> lock(mLockStreams);
-    mRegisteredStreams.erase(std::remove(mRegisteredStreams.begin(), mRegisteredStreams.end(), sharedStream),
-              mRegisteredStreams.end());
-    return AAUDIO_OK;
-}
-
-aaudio_result_t AAudioServiceEndpoint::startStream(sp<AAudioServiceStreamShared> sharedStream) {
-    aaudio_result_t result = AAUDIO_OK;
-    if (++mRunningStreams == 1) {
-        // TODO use real-time technique to avoid mutex, eg. atomic command FIFO
-        std::lock_guard<std::mutex> lock(mLockStreams);
-        result = getStreamInternal()->requestStart();
-        startSharingThread_l();
-    }
-    return result;
-}
-
-aaudio_result_t AAudioServiceEndpoint::stopStream(sp<AAudioServiceStreamShared> sharedStream) {
-    // Don't lock here because the disconnectRegisteredStreams also uses the lock.
-    if (--mRunningStreams == 0) { // atomic
-        stopSharingThread();
-        getStreamInternal()->requestStop();
-    }
-    return AAUDIO_OK;
-}
-
-static void *aaudio_endpoint_thread_proc(void *context) {
-    AAudioServiceEndpoint *endpoint = (AAudioServiceEndpoint *) context;
-    if (endpoint != NULL) {
-        return endpoint->callbackLoop();
-    } else {
-        return NULL;
-    }
-}
-
-aaudio_result_t AAudioServiceEndpoint::startSharingThread_l() {
-    // Launch the callback loop thread.
-    int64_t periodNanos = getStreamInternal()->getFramesPerBurst()
-                          * AAUDIO_NANOS_PER_SECOND
-                          / getSampleRate();
-    mCallbackEnabled.store(true);
-    return getStreamInternal()->createThread(periodNanos, aaudio_endpoint_thread_proc, this);
-}
-
-aaudio_result_t AAudioServiceEndpoint::stopSharingThread() {
-    mCallbackEnabled.store(false);
-    aaudio_result_t result = getStreamInternal()->joinThread(NULL);
-    return result;
-}
-
 void AAudioServiceEndpoint::disconnectRegisteredStreams() {
     std::lock_guard<std::mutex> lock(mLockStreams);
-    for(const auto& sharedStream : mRegisteredStreams) {
-        sharedStream->stop();
-        sharedStream->disconnect();
+    for (const auto stream : mRegisteredStreams) {
+        stream->stop();
+        stream->disconnect();
     }
     mRegisteredStreams.clear();
 }
 
+aaudio_result_t AAudioServiceEndpoint::registerStream(sp<AAudioServiceStreamBase>stream) {
+    std::lock_guard<std::mutex> lock(mLockStreams);
+    mRegisteredStreams.push_back(stream);
+    return AAUDIO_OK;
+}
+
+aaudio_result_t AAudioServiceEndpoint::unregisterStream(sp<AAudioServiceStreamBase>stream) {
+    std::lock_guard<std::mutex> lock(mLockStreams);
+    mRegisteredStreams.erase(std::remove(
+            mRegisteredStreams.begin(), mRegisteredStreams.end(), stream),
+                             mRegisteredStreams.end());
+    return AAUDIO_OK;
+}
+
 bool AAudioServiceEndpoint::matches(const AAudioStreamConfiguration& configuration) {
+    if (configuration.getDirection() != getDirection()) {
+        return false;
+    }
     if (configuration.getDeviceId() != AAUDIO_UNSPECIFIED &&
-            configuration.getDeviceId() != mStreamInternal->getDeviceId()) {
+        configuration.getDeviceId() != getDeviceId()) {
         return false;
     }
     if (configuration.getSampleRate() != AAUDIO_UNSPECIFIED &&
-            configuration.getSampleRate() != mStreamInternal->getSampleRate()) {
+        configuration.getSampleRate() != getSampleRate()) {
         return false;
     }
     if (configuration.getSamplesPerFrame() != AAUDIO_UNSPECIFIED &&
-            configuration.getSamplesPerFrame() != mStreamInternal->getSamplesPerFrame()) {
+        configuration.getSamplesPerFrame() != getSamplesPerFrame()) {
         return false;
     }
     return true;
 }
-
-
-aaudio_result_t AAudioServiceEndpoint::getTimestamp(int64_t *positionFrames, int64_t *timeNanos) {
-    return mStreamInternal->getTimestamp(CLOCK_MONOTONIC, positionFrames, timeNanos);
-}
diff --git a/services/oboeservice/AAudioServiceEndpoint.h b/services/oboeservice/AAudioServiceEndpoint.h
index 603d497..2ef6234 100644
--- a/services/oboeservice/AAudioServiceEndpoint.h
+++ b/services/oboeservice/AAudioServiceEndpoint.h
@@ -24,72 +24,93 @@
 
 #include "client/AudioStreamInternal.h"
 #include "client/AudioStreamInternalPlay.h"
+#include "core/AAudioStreamParameters.h"
 #include "binding/AAudioServiceMessage.h"
-#include "AAudioServiceStreamShared.h"
-#include "AAudioServiceStreamMMAP.h"
-#include "AAudioMixer.h"
-#include "AAudioService.h"
+#include "binding/AAudioStreamConfiguration.h"
+
+#include "AAudioServiceStreamBase.h"
 
 namespace aaudio {
 
-class AAudioServiceEndpoint {
+/**
+ * AAudioServiceEndpoint is used by a subclass of AAudioServiceStreamBase
+ * to communicate with the underlying audio device or port.
+ */
+class AAudioServiceEndpoint
+        : public virtual android::RefBase
+        , public AAudioStreamParameters {
 public:
-    virtual ~AAudioServiceEndpoint() = default;
 
-    std::string dump() const;
+    virtual ~AAudioServiceEndpoint();
 
-    virtual aaudio_result_t open(const AAudioStreamConfiguration& configuration);
+    virtual std::string dump() const;
 
-    int32_t getSampleRate() const { return mStreamInternal->getSampleRate(); }
-    int32_t getSamplesPerFrame() const { return mStreamInternal->getSamplesPerFrame();  }
-    int32_t getFramesPerBurst() const { return mStreamInternal->getFramesPerBurst();  }
+    virtual aaudio_result_t open(const aaudio::AAudioStreamRequest &request) = 0;
 
-    aaudio_result_t registerStream(android::sp<AAudioServiceStreamShared> sharedStream);
-    aaudio_result_t unregisterStream(android::sp<AAudioServiceStreamShared> sharedStream);
-    aaudio_result_t startStream(android::sp<AAudioServiceStreamShared> sharedStream);
-    aaudio_result_t stopStream(android::sp<AAudioServiceStreamShared> sharedStream);
-    aaudio_result_t close();
+    virtual aaudio_result_t close() = 0;
+
+    virtual aaudio_result_t registerStream(android::sp<AAudioServiceStreamBase> stream);
+
+    virtual aaudio_result_t unregisterStream(android::sp<AAudioServiceStreamBase> stream);
+
+    virtual aaudio_result_t startStream(android::sp<AAudioServiceStreamBase> stream,
+                                        audio_port_handle_t *clientHandle) = 0;
+
+    virtual aaudio_result_t stopStream(android::sp<AAudioServiceStreamBase> stream,
+                                       audio_port_handle_t clientHandle) = 0;
+
+    virtual aaudio_result_t startClient(const android::AudioClient& client,
+                                        audio_port_handle_t *clientHandle) {
+        ALOGD("AAudioServiceEndpoint::startClient(%p, ...) AAUDIO_ERROR_UNAVAILABLE", &client);
+        return AAUDIO_ERROR_UNAVAILABLE;
+    }
+
+    virtual aaudio_result_t stopClient(audio_port_handle_t clientHandle) {
+        ALOGD("AAudioServiceEndpoint::stopClient(...) AAUDIO_ERROR_UNAVAILABLE");
+        return AAUDIO_ERROR_UNAVAILABLE;
+    }
+
+    /**
+     * @param positionFrames
+     * @param timeNanos
+     * @return AAUDIO_OK or AAUDIO_ERROR_UNAVAILABLE or other negative error
+     */
+    virtual aaudio_result_t getFreeRunningPosition(int64_t *positionFrames, int64_t *timeNanos) = 0;
+
+    virtual aaudio_result_t getTimestamp(int64_t *positionFrames, int64_t *timeNanos) = 0;
+
+    int32_t getFramesPerBurst() const {
+        return mFramesPerBurst;
+    }
 
     int32_t getRequestedDeviceId() const { return mRequestedDeviceId; }
-    int32_t getDeviceId() const { return mStreamInternal->getDeviceId(); }
-
-    aaudio_direction_t getDirection() const { return mStreamInternal->getDirection(); }
-
-    void disconnectRegisteredStreams();
-
-    virtual void *callbackLoop() = 0;
-
-    // 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;
-    }
-
-    aaudio_result_t getTimestamp(int64_t *positionFrames, int64_t *timeNanos);
 
     bool matches(const AAudioStreamConfiguration& configuration);
 
-    virtual AudioStreamInternal *getStreamInternal() = 0;
+    // This should only be called from the AAudioEndpointManager under a mutex.
+    int32_t getOpenCount() const {
+        return mOpenCount;
+    }
 
-    std::atomic<bool>        mCallbackEnabled{false};
+    // This should only be called from the AAudioEndpointManager under a mutex.
+    void setOpenCount(int32_t count) {
+        mOpenCount = count;
+    }
+
+protected:
+    void                     disconnectRegisteredStreams();
 
     mutable std::mutex       mLockStreams;
+    std::vector<android::sp<AAudioServiceStreamBase>> mRegisteredStreams;
 
-    std::vector<android::sp<AAudioServiceStreamShared>> mRegisteredStreams;
+    SimpleDoubleBuffer<Timestamp>  mAtomicTimestamp;
 
-    std::atomic<int>         mRunningStreams{0};
+    android::AudioClient     mMmapClient;   // set in open, used in open and startStream
 
-private:
-    aaudio_result_t startSharingThread_l();
-    aaudio_result_t stopSharingThread();
-
-    AudioStreamInternal     *mStreamInternal = nullptr;
-    int32_t                  mReferenceCount = 0;
+    int32_t                  mFramesPerBurst = 0;
+    int32_t                  mOpenCount = 0;
     int32_t                  mRequestedDeviceId = 0;
+
 };
 
 } /* namespace aaudio */
diff --git a/services/oboeservice/AAudioServiceEndpointCapture.cpp b/services/oboeservice/AAudioServiceEndpointCapture.cpp
index 7a56141..97558ca 100644
--- a/services/oboeservice/AAudioServiceEndpointCapture.cpp
+++ b/services/oboeservice/AAudioServiceEndpointCapture.cpp
@@ -30,20 +30,22 @@
 #include "AAudioServiceEndpoint.h"
 #include "AAudioServiceStreamShared.h"
 #include "AAudioServiceEndpointCapture.h"
+#include "AAudioServiceEndpointShared.h"
 
 using namespace android;  // TODO just import names needed
 using namespace aaudio;   // TODO just import names needed
 
 AAudioServiceEndpointCapture::AAudioServiceEndpointCapture(AAudioService &audioService)
         : mStreamInternalCapture(audioService, true) {
+    mStreamInternal = &mStreamInternalCapture;
 }
 
 AAudioServiceEndpointCapture::~AAudioServiceEndpointCapture() {
     delete mDistributionBuffer;
 }
 
-aaudio_result_t AAudioServiceEndpointCapture::open(const AAudioStreamConfiguration& configuration) {
-    aaudio_result_t result = AAudioServiceEndpoint::open(configuration);
+aaudio_result_t AAudioServiceEndpointCapture::open(const aaudio::AAudioStreamRequest &request) {
+    aaudio_result_t result = AAudioServiceEndpointShared::open(request);
     if (result == AAUDIO_OK) {
         delete mDistributionBuffer;
         int distributionBufferSizeBytes = getStreamInternal()->getFramesPerBurst()
@@ -80,16 +82,19 @@
         { // brackets are for lock_guard
 
             std::lock_guard <std::mutex> lock(mLockStreams);
-            for (const sp<AAudioServiceStreamShared>& clientStream : mRegisteredStreams) {
+            for (const auto clientStream : mRegisteredStreams) {
                 if (clientStream->isRunning()) {
-                    FifoBuffer *fifo = clientStream->getDataFifoBuffer();
+                    AAudioServiceStreamShared *streamShared =
+                            static_cast<AAudioServiceStreamShared *>(clientStream.get());
+
+                    FifoBuffer *fifo = streamShared->getDataFifoBuffer();
 
                     // Determine offset between framePosition in client's stream vs the underlying
                     // MMAP stream.
                     int64_t clientFramesWritten = fifo->getWriteCounter();
                     // There are two indices that refer to the same frame.
                     int64_t positionOffset = mmapFramesRead - clientFramesWritten;
-                    clientStream->setTimestampPositionOffset(positionOffset);
+                    streamShared->setTimestampPositionOffset(positionOffset);
 
                     if (fifo->getFifoControllerBase()->getEmptyFramesAvailable() <
                         getFramesPerBurst()) {
@@ -102,7 +107,7 @@
                     // client buffer. It is sent to the client and used in the timing model
                     // to decide when data will be available to read.
                     Timestamp timestamp(fifo->getWriteCounter(), AudioClock::getNanoseconds());
-                    clientStream->markTransferTime(timestamp);
+                    streamShared->markTransferTime(timestamp);
                 }
             }
         }
diff --git a/services/oboeservice/AAudioServiceEndpointCapture.h b/services/oboeservice/AAudioServiceEndpointCapture.h
index 8a3d72f..971da9a 100644
--- a/services/oboeservice/AAudioServiceEndpointCapture.h
+++ b/services/oboeservice/AAudioServiceEndpointCapture.h
@@ -20,18 +20,18 @@
 #include "client/AudioStreamInternal.h"
 #include "client/AudioStreamInternalCapture.h"
 
+#include "AAudioServiceEndpointShared.h"
+#include "AAudioServiceStreamShared.h"
+
 namespace aaudio {
 
-class AAudioServiceEndpointCapture : public AAudioServiceEndpoint {
+class AAudioServiceEndpointCapture : public AAudioServiceEndpointShared {
 public:
     explicit AAudioServiceEndpointCapture(android::AAudioService &audioService);
     virtual ~AAudioServiceEndpointCapture();
 
-    aaudio_result_t open(const AAudioStreamConfiguration& configuration) override;
+    aaudio_result_t open(const aaudio::AAudioStreamRequest &request) override;
 
-    AudioStreamInternal *getStreamInternal() override {
-        return &mStreamInternalCapture;
-    }
 
     void *callbackLoop() override;
 
diff --git a/services/oboeservice/AAudioServiceEndpointMMAP.cpp b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
new file mode 100644
index 0000000..58213f8
--- /dev/null
+++ b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
@@ -0,0 +1,350 @@
+/*
+ * 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 "AAudioServiceEndpointMMAP"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <algorithm>
+#include <assert.h>
+#include <map>
+#include <mutex>
+#include <sstream>
+#include <utils/Singleton.h>
+#include <vector>
+
+
+#include "AAudioEndpointManager.h"
+#include "AAudioServiceEndpoint.h"
+
+#include "core/AudioStreamBuilder.h"
+#include "AAudioServiceEndpoint.h"
+#include "AAudioServiceStreamShared.h"
+#include "AAudioServiceEndpointPlay.h"
+#include "AAudioServiceEndpointMMAP.h"
+
+
+#define AAUDIO_BUFFER_CAPACITY_MIN    4 * 512
+#define AAUDIO_SAMPLE_RATE_DEFAULT    48000
+
+// This is an estimate of the time difference between the HW and the MMAP time.
+// TODO Get presentation timestamps from the HAL instead of using these estimates.
+#define OUTPUT_ESTIMATED_HARDWARE_OFFSET_NANOS  (3 * AAUDIO_NANOS_PER_MILLISECOND)
+#define INPUT_ESTIMATED_HARDWARE_OFFSET_NANOS   (-1 * AAUDIO_NANOS_PER_MILLISECOND)
+
+using namespace android;  // TODO just import names needed
+using namespace aaudio;   // TODO just import names needed
+
+AAudioServiceEndpointMMAP::AAudioServiceEndpointMMAP()
+        :  mMmapStream(nullptr) {}
+
+AAudioServiceEndpointMMAP::~AAudioServiceEndpointMMAP() {}
+
+std::string AAudioServiceEndpointMMAP::dump() const {
+    std::stringstream result;
+
+    result << "  MMAP: framesTransferred = " << mFramesTransferred.get();
+    result << ", HW nanos = " << mHardwareTimeOffsetNanos;
+    result << ", port handle = " << mPortHandle;
+    result << ", audio data FD = " << mAudioDataFileDescriptor;
+    result << "\n";
+
+    result << "    HW Offset Micros:     " <<
+                                      (getHardwareTimeOffsetNanos()
+                                       / AAUDIO_NANOS_PER_MICROSECOND) << "\n";
+
+    result << AAudioServiceEndpoint::dump();
+    return result.str();
+}
+
+aaudio_result_t AAudioServiceEndpointMMAP::open(const aaudio::AAudioStreamRequest &request) {
+    aaudio_result_t result = AAUDIO_OK;
+    const audio_attributes_t attributes = {
+            .content_type = AUDIO_CONTENT_TYPE_MUSIC,
+            .usage = AUDIO_USAGE_MEDIA,
+            .source = AUDIO_SOURCE_VOICE_RECOGNITION,
+            .flags = AUDIO_FLAG_LOW_LATENCY,
+            .tags = ""
+    };
+    audio_config_base_t config;
+    audio_port_handle_t deviceId;
+
+    int32_t burstMinMicros = AAudioProperty_getHardwareBurstMinMicros();
+    int32_t burstMicros = 0;
+
+    copyFrom(request.getConstantConfiguration());
+
+    mMmapClient.clientUid = request.getUserId();
+    mMmapClient.clientPid = request.getProcessId();
+    mMmapClient.packageName.setTo(String16(""));
+
+    mRequestedDeviceId = deviceId = getDeviceId();
+
+    // Fill in config
+    aaudio_format_t aaudioFormat = getFormat();
+    if (aaudioFormat == AAUDIO_UNSPECIFIED || aaudioFormat == AAUDIO_FORMAT_PCM_FLOAT) {
+        aaudioFormat = AAUDIO_FORMAT_PCM_I16;
+    }
+    config.format = AAudioConvert_aaudioToAndroidDataFormat(aaudioFormat);
+
+    int32_t aaudioSampleRate = getSampleRate();
+    if (aaudioSampleRate == AAUDIO_UNSPECIFIED) {
+        aaudioSampleRate = AAUDIO_SAMPLE_RATE_DEFAULT;
+    }
+    config.sample_rate = aaudioSampleRate;
+
+    int32_t aaudioSamplesPerFrame = getSamplesPerFrame();
+
+    aaudio_direction_t direction = getDirection();
+    if (direction == AAUDIO_DIRECTION_OUTPUT) {
+        config.channel_mask = (aaudioSamplesPerFrame == AAUDIO_UNSPECIFIED)
+                              ? AUDIO_CHANNEL_OUT_STEREO
+                              : audio_channel_out_mask_from_count(aaudioSamplesPerFrame);
+        mHardwareTimeOffsetNanos = OUTPUT_ESTIMATED_HARDWARE_OFFSET_NANOS; // frames at DAC later
+
+    } else if (direction == AAUDIO_DIRECTION_INPUT) {
+        config.channel_mask =  (aaudioSamplesPerFrame == AAUDIO_UNSPECIFIED)
+                               ? AUDIO_CHANNEL_IN_STEREO
+                               : audio_channel_in_mask_from_count(aaudioSamplesPerFrame);
+        mHardwareTimeOffsetNanos = INPUT_ESTIMATED_HARDWARE_OFFSET_NANOS; // frames at ADC earlier
+
+    } else {
+        ALOGE("openMmapStream - invalid direction = %d", direction);
+        return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
+    }
+
+    MmapStreamInterface::stream_direction_t streamDirection =
+            (direction == AAUDIO_DIRECTION_OUTPUT)
+            ? MmapStreamInterface::DIRECTION_OUTPUT
+            : MmapStreamInterface::DIRECTION_INPUT;
+
+    // Open HAL stream. Set mMmapStream
+    status_t status = MmapStreamInterface::openMmapStream(streamDirection,
+                                                          &attributes,
+                                                          &config,
+                                                          mMmapClient,
+                                                          &deviceId,
+                                                          this, // callback
+                                                          mMmapStream,
+                                                          &mPortHandle);
+    ALOGD("AAudioServiceEndpointMMAP::open() mMapClient.uid = %d, pid = %d => portHandle = %d\n",
+          mMmapClient.clientUid,  mMmapClient.clientPid, mPortHandle);
+    if (status != OK) {
+        ALOGE("openMmapStream returned status %d", status);
+        return AAUDIO_ERROR_UNAVAILABLE;
+    }
+
+    if (deviceId == AAUDIO_UNSPECIFIED) {
+        ALOGW("AAudioServiceEndpointMMAP::open() - openMmapStream() failed to set deviceId");
+    }
+    setDeviceId(deviceId);
+
+    // Create MMAP/NOIRQ buffer.
+    int32_t minSizeFrames = getBufferCapacity();
+    if (minSizeFrames <= 0) { // zero will get rejected
+        minSizeFrames = AAUDIO_BUFFER_CAPACITY_MIN;
+    }
+    status = mMmapStream->createMmapBuffer(minSizeFrames, &mMmapBufferinfo);
+    if (status != OK) {
+        ALOGE("AAudioServiceEndpointMMAP::open() - createMmapBuffer() failed with status %d %s",
+              status, strerror(-status));
+        result = AAUDIO_ERROR_UNAVAILABLE;
+        goto error;
+    } else {
+        ALOGD("createMmapBuffer status = %d, buffer_size = %d, burst_size %d"
+                      ", Sharable FD: %s",
+              status,
+              abs(mMmapBufferinfo.buffer_size_frames),
+              mMmapBufferinfo.burst_size_frames,
+              mMmapBufferinfo.buffer_size_frames < 0 ? "Yes" : "No");
+    }
+
+    setBufferCapacity(mMmapBufferinfo.buffer_size_frames);
+    // The audio HAL indicates if the shared memory fd can be shared outside of audioserver
+    // by returning a negative buffer size
+    if (getBufferCapacity() < 0) {
+        // Exclusive mode can be used by client or service.
+        setBufferCapacity(-getBufferCapacity());
+    } else {
+        // Exclusive mode can only be used by the service because the FD cannot be shared.
+        uid_t audioServiceUid = getuid();
+        if ((mMmapClient.clientUid != audioServiceUid) &&
+            getSharingMode() == AAUDIO_SHARING_MODE_EXCLUSIVE) {
+            // Fallback is handled by caller but indicate what is possible in case
+            // this is used in the future
+            setSharingMode(AAUDIO_SHARING_MODE_SHARED);
+            ALOGW("AAudioServiceEndpointMMAP::open() - exclusive FD cannot be used by client");
+            result = AAUDIO_ERROR_UNAVAILABLE;
+            goto error;
+        }
+    }
+
+    // Get information about the stream and pass it back to the caller.
+    setSamplesPerFrame((direction == AAUDIO_DIRECTION_OUTPUT)
+                       ? audio_channel_count_from_out_mask(config.channel_mask)
+                       : audio_channel_count_from_in_mask(config.channel_mask));
+
+    // AAudio creates a copy of this FD and retains ownership of the copy.
+    // Assume that AudioFlinger will close the original shared_memory_fd.
+    mAudioDataFileDescriptor.reset(dup(mMmapBufferinfo.shared_memory_fd));
+    if (mAudioDataFileDescriptor.get() == -1) {
+        ALOGE("AAudioServiceEndpointMMAP::open() - could not dup shared_memory_fd");
+        result = AAUDIO_ERROR_INTERNAL;
+        goto error;
+    }
+    mFramesPerBurst = mMmapBufferinfo.burst_size_frames;
+    setFormat(AAudioConvert_androidToAAudioDataFormat(config.format));
+    setSampleRate(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.
+    do {
+        if (burstMicros > 0) {  // skip first loop
+            mFramesPerBurst *= 2;
+        }
+        burstMicros = mFramesPerBurst * static_cast<int64_t>(1000000) / getSampleRate();
+    } while (burstMicros < burstMinMicros);
+
+    ALOGD("AAudioServiceEndpointMMAP::open() original burst = %d, minMicros = %d, to burst = %d\n",
+          mMmapBufferinfo.burst_size_frames, burstMinMicros, mFramesPerBurst);
+
+    ALOGD("AAudioServiceEndpointMMAP::open() actual rate = %d, channels = %d"
+          ", deviceId = %d, capacity = %d\n",
+          getSampleRate(), getSamplesPerFrame(), deviceId, getBufferCapacity());
+
+    return result;
+
+error:
+    close();
+    return result;
+}
+
+aaudio_result_t AAudioServiceEndpointMMAP::close() {
+
+    if (mMmapStream != 0) {
+        ALOGD("AAudioServiceEndpointMMAP::close() clear() endpoint");
+        // Needs to be explicitly cleared or CTS will fail but it is not clear why.
+        mMmapStream.clear();
+        // 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);
+    }
+
+    return AAUDIO_OK;
+}
+
+aaudio_result_t AAudioServiceEndpointMMAP::startStream(sp<AAudioServiceStreamBase> stream,
+                                                   audio_port_handle_t *clientHandle) {
+    return startClient(mMmapClient, &mPortHandle);
+}
+
+aaudio_result_t AAudioServiceEndpointMMAP::stopStream(sp<AAudioServiceStreamBase> stream,
+                                                  audio_port_handle_t clientHandle) {
+    mFramesTransferred.reset32();
+    return stopClient(mPortHandle);
+}
+
+aaudio_result_t AAudioServiceEndpointMMAP::startClient(const android::AudioClient& client,
+                                                       audio_port_handle_t *clientHandle) {
+    if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL;
+    audio_port_handle_t originalHandle =  *clientHandle;
+    aaudio_result_t result = AAudioConvert_androidToAAudioResult(mMmapStream->start(client,
+                                                                                    clientHandle));
+    ALOGD("AAudioServiceEndpointMMAP::startClient(%p(uid=%d, pid=%d), %d => %d) returns %d",
+          &client, client.clientUid, client.clientPid,
+          originalHandle, *clientHandle, result);
+    return result;
+}
+
+aaudio_result_t AAudioServiceEndpointMMAP::stopClient(audio_port_handle_t clientHandle) {
+    if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL;
+    aaudio_result_t result = AAudioConvert_androidToAAudioResult(mMmapStream->stop(clientHandle));
+    ALOGD("AAudioServiceEndpointMMAP::stopClient(%d) returns %d", clientHandle, result);
+    return result;
+}
+
+// Get free-running DSP or DMA hardware position from the HAL.
+aaudio_result_t AAudioServiceEndpointMMAP::getFreeRunningPosition(int64_t *positionFrames,
+                                                                int64_t *timeNanos) {
+    struct audio_mmap_position position;
+    if (mMmapStream == nullptr) {
+        return AAUDIO_ERROR_NULL;
+    }
+    status_t status = mMmapStream->getMmapPosition(&position);
+    ALOGV("AAudioServiceEndpointMMAP::getFreeRunningPosition() status= %d, pos = %d, nanos = %lld\n",
+          status, position.position_frames, (long long) position.time_nanoseconds);
+    aaudio_result_t result = AAudioConvert_androidToAAudioResult(status);
+    if (result == AAUDIO_ERROR_UNAVAILABLE) {
+        ALOGW("sendCurrentTimestamp(): getMmapPosition() has no position data available");
+    } else if (result != AAUDIO_OK) {
+        ALOGE("sendCurrentTimestamp(): getMmapPosition() returned status %d", status);
+    } else {
+        // Convert 32-bit position to 64-bit position.
+        mFramesTransferred.update32(position.position_frames);
+        *positionFrames = mFramesTransferred.get();
+        *timeNanos = position.time_nanoseconds;
+    }
+    return result;
+}
+
+aaudio_result_t AAudioServiceEndpointMMAP::getTimestamp(int64_t *positionFrames,
+                                                    int64_t *timeNanos) {
+    return 0; // TODO
+}
+
+
+void AAudioServiceEndpointMMAP::onTearDown() {
+    ALOGD("AAudioServiceEndpointMMAP::onTearDown() called");
+    disconnectRegisteredStreams();
+};
+
+void AAudioServiceEndpointMMAP::onVolumeChanged(audio_channel_mask_t channels,
+                                              android::Vector<float> values) {
+    // TODO do we really need a different volume for each channel?
+    float volume = values[0];
+    ALOGD("AAudioServiceEndpointMMAP::onVolumeChanged() volume[0] = %f", volume);
+    std::lock_guard<std::mutex> lock(mLockStreams);
+    for(const auto stream : mRegisteredStreams) {
+        stream->onVolumeChanged(volume);
+    }
+};
+
+void AAudioServiceEndpointMMAP::onRoutingChanged(audio_port_handle_t deviceId) {
+    ALOGD("AAudioServiceEndpointMMAP::onRoutingChanged() called with %d, old = %d",
+          deviceId, getDeviceId());
+    if (getDeviceId() != AUDIO_PORT_HANDLE_NONE  && getDeviceId() != deviceId) {
+        disconnectRegisteredStreams();
+    }
+    setDeviceId(deviceId);
+};
+
+/**
+ * Get an immutable description of the data queue from the HAL.
+ */
+aaudio_result_t AAudioServiceEndpointMMAP::getDownDataDescription(AudioEndpointParcelable &parcelable)
+{
+    // Gather information on the data queue based on HAL info.
+    int32_t bytesPerFrame = calculateBytesPerFrame();
+    int32_t capacityInBytes = getBufferCapacity() * bytesPerFrame;
+    int fdIndex = parcelable.addFileDescriptor(mAudioDataFileDescriptor, capacityInBytes);
+    parcelable.mDownDataQueueParcelable.setupMemory(fdIndex, 0, capacityInBytes);
+    parcelable.mDownDataQueueParcelable.setBytesPerFrame(bytesPerFrame);
+    parcelable.mDownDataQueueParcelable.setFramesPerBurst(mFramesPerBurst);
+    parcelable.mDownDataQueueParcelable.setCapacityInFrames(getBufferCapacity());
+    return AAUDIO_OK;
+}
diff --git a/services/oboeservice/AAudioServiceEndpointMMAP.h b/services/oboeservice/AAudioServiceEndpointMMAP.h
new file mode 100644
index 0000000..16b6269
--- /dev/null
+++ b/services/oboeservice/AAudioServiceEndpointMMAP.h
@@ -0,0 +1,102 @@
+/*
+ * 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 AAUDIO_SERVICE_ENDPOINT_MMAP_H
+#define AAUDIO_SERVICE_ENDPOINT_MMAP_H
+
+#include <atomic>
+#include <functional>
+#include <mutex>
+#include <vector>
+
+#include "client/AudioStreamInternal.h"
+#include "client/AudioStreamInternalPlay.h"
+#include "binding/AAudioServiceMessage.h"
+#include "AAudioServiceEndpointShared.h"
+#include "AAudioServiceStreamShared.h"
+#include "AAudioServiceStreamMMAP.h"
+#include "AAudioMixer.h"
+#include "AAudioService.h"
+
+namespace aaudio {
+
+/**
+ * This is used by AAudioServiceStreamMMAP to access the MMAP devices
+ * through AudioFlinger.
+ */
+class AAudioServiceEndpointMMAP
+        : public AAudioServiceEndpoint
+        , public android::MmapStreamCallback {
+
+public:
+    AAudioServiceEndpointMMAP();
+
+    virtual ~AAudioServiceEndpointMMAP();
+
+    std::string dump() const override;
+
+    aaudio_result_t open(const aaudio::AAudioStreamRequest &request) override;
+
+    aaudio_result_t close() override;
+
+    aaudio_result_t startStream(android::sp<AAudioServiceStreamBase> stream,
+                                audio_port_handle_t *clientHandle) override;
+
+    aaudio_result_t stopStream(android::sp<AAudioServiceStreamBase> stream,
+                               audio_port_handle_t clientHandle) override;
+
+    aaudio_result_t startClient(const android::AudioClient& client,
+                                        audio_port_handle_t *clientHandle)  override;
+
+    aaudio_result_t stopClient(audio_port_handle_t clientHandle)  override;
+
+    aaudio_result_t getFreeRunningPosition(int64_t *positionFrames, int64_t *timeNanos) override;
+
+    aaudio_result_t getTimestamp(int64_t *positionFrames, int64_t *timeNanos) override;
+
+    // -------------- Callback functions for MmapStreamCallback ---------------------
+    void onTearDown() override;
+
+    void onVolumeChanged(audio_channel_mask_t channels,
+                         android::Vector<float> values) override;
+
+    void onRoutingChanged(audio_port_handle_t deviceId) override;
+    // ------------------------------------------------------------------------------
+
+    aaudio_result_t getDownDataDescription(AudioEndpointParcelable &parcelable);
+
+    int64_t getHardwareTimeOffsetNanos() const {
+        return mHardwareTimeOffsetNanos;
+    }
+
+private:
+    MonotonicCounter                          mFramesTransferred;
+
+    // Interface to the AudioFlinger MMAP support.
+    android::sp<android::MmapStreamInterface> mMmapStream;
+    struct audio_mmap_buffer_info             mMmapBufferinfo;
+    audio_port_handle_t                       mPortHandle = AUDIO_PORT_HANDLE_NONE;
+
+    android::base::unique_fd                  mAudioDataFileDescriptor;
+
+    int64_t                                   mHardwareTimeOffsetNanos = 0; // TODO get from HAL
+
+};
+
+} /* namespace aaudio */
+
+#endif //AAUDIO_SERVICE_ENDPOINT_MMAP_H
+
diff --git a/services/oboeservice/AAudioServiceEndpointPlay.cpp b/services/oboeservice/AAudioServiceEndpointPlay.cpp
index a29293b..c42a6e2 100644
--- a/services/oboeservice/AAudioServiceEndpointPlay.cpp
+++ b/services/oboeservice/AAudioServiceEndpointPlay.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "AAudioService"
+#define LOG_TAG "AAudioServiceEndpointPlay"
 //#define LOG_NDEBUG 0
 #include <utils/Log.h>
 
@@ -33,6 +33,7 @@
 #include "AAudioServiceEndpoint.h"
 #include "AAudioServiceStreamShared.h"
 #include "AAudioServiceEndpointPlay.h"
+#include "AAudioServiceEndpointShared.h"
 
 using namespace android;  // TODO just import names needed
 using namespace aaudio;   // TODO just import names needed
@@ -41,13 +42,14 @@
 
 AAudioServiceEndpointPlay::AAudioServiceEndpointPlay(AAudioService &audioService)
         : mStreamInternalPlay(audioService, true) {
+    mStreamInternal = &mStreamInternalPlay;
 }
 
 AAudioServiceEndpointPlay::~AAudioServiceEndpointPlay() {
 }
 
-aaudio_result_t AAudioServiceEndpointPlay::open(const AAudioStreamConfiguration& configuration) {
-    aaudio_result_t result = AAudioServiceEndpoint::open(configuration);
+aaudio_result_t AAudioServiceEndpointPlay::open(const aaudio::AAudioStreamRequest &request) {
+    aaudio_result_t result = AAudioServiceEndpointShared::open(request);
     if (result == AAUDIO_OK) {
         mMixer.allocate(getStreamInternal()->getSamplesPerFrame(),
                         getStreamInternal()->getFramesPerBurst());
@@ -72,35 +74,42 @@
     while (mCallbackEnabled.load() && getStreamInternal()->isActive() && (result >= 0)) {
         // Mix data from each active stream.
         mMixer.clear();
+
         { // brackets are for lock_guard
             int index = 0;
             int64_t mmapFramesWritten = getStreamInternal()->getFramesWritten();
 
             std::lock_guard <std::mutex> lock(mLockStreams);
-            for (const sp<AAudioServiceStreamShared>& clientStream : mRegisteredStreams) {
-                if (clientStream->isRunning()) {
-                    FifoBuffer *fifo = clientStream->getDataFifoBuffer();
-                    // Determine offset between framePosition in client's stream vs the underlying
-                    // MMAP stream.
-                    int64_t clientFramesRead = fifo->getReadCounter();
-                    // These two indices refer to the same frame.
-                    int64_t positionOffset = mmapFramesWritten - clientFramesRead;
-                    clientStream->setTimestampPositionOffset(positionOffset);
-
-                    float volume = 1.0; // to match legacy volume
-                    bool underflowed = mMixer.mix(index, fifo, volume);
-
-                    // This timestamp represents the completion of data being read out of the
-                    // client buffer. It is sent to the client and used in the timing model
-                    // to decide when the client has room to write more data.
-                    Timestamp timestamp(fifo->getReadCounter(), AudioClock::getNanoseconds());
-                    clientStream->markTransferTime(timestamp);
-
-                    if (underflowed) {
-                        clientStream->incrementXRunCount();
-                    }
+            for (const auto clientStream : mRegisteredStreams) {
+                if (!clientStream->isRunning()) {
+                    continue;
                 }
-                index++;
+
+                AAudioServiceStreamShared *streamShared =
+                        static_cast<AAudioServiceStreamShared *>(clientStream.get());
+
+                FifoBuffer *fifo = streamShared->getDataFifoBuffer();
+                // Determine offset between framePosition in client's stream vs the underlying
+                // MMAP stream.
+                int64_t clientFramesRead = fifo->getReadCounter();
+                // These two indices refer to the same frame.
+                int64_t positionOffset = mmapFramesWritten - clientFramesRead;
+                streamShared->setTimestampPositionOffset(positionOffset);
+
+                float volume = 1.0; // to match legacy volume
+                bool underflowed = mMixer.mix(index, fifo, volume);
+
+                // This timestamp represents the completion of data being read out of the
+                // client buffer. It is sent to the client and used in the timing model
+                // to decide when the client has room to write more data.
+                Timestamp timestamp(fifo->getReadCounter(), AudioClock::getNanoseconds());
+                streamShared->markTransferTime(timestamp);
+
+                if (underflowed) {
+                    streamShared->incrementXRunCount();
+                }
+
+                index++; // just used for labelling tracks in systrace
             }
         }
 
@@ -108,7 +117,7 @@
         result = getStreamInternal()->write(mMixer.getOutputBuffer(),
                                             getFramesPerBurst(), timeoutNanos);
         if (result == AAUDIO_ERROR_DISCONNECTED) {
-            disconnectRegisteredStreams();
+            AAudioServiceEndpointShared::disconnectRegisteredStreams();
             break;
         } else if (result != getFramesPerBurst()) {
             ALOGW("AAudioServiceEndpoint(): callbackLoop() wrote %d / %d",
diff --git a/services/oboeservice/AAudioServiceEndpointPlay.h b/services/oboeservice/AAudioServiceEndpointPlay.h
index c22f510..a0a383c 100644
--- a/services/oboeservice/AAudioServiceEndpointPlay.h
+++ b/services/oboeservice/AAudioServiceEndpointPlay.h
@@ -25,6 +25,7 @@
 #include "client/AudioStreamInternal.h"
 #include "client/AudioStreamInternalPlay.h"
 #include "binding/AAudioServiceMessage.h"
+#include "AAudioServiceEndpointShared.h"
 #include "AAudioServiceStreamShared.h"
 #include "AAudioServiceStreamMMAP.h"
 #include "AAudioMixer.h"
@@ -35,16 +36,12 @@
 /**
  * Contains a mixer and a stream for writing the result of the mix.
  */
-class AAudioServiceEndpointPlay : public AAudioServiceEndpoint {
+class AAudioServiceEndpointPlay : public AAudioServiceEndpointShared {
 public:
     explicit AAudioServiceEndpointPlay(android::AAudioService &audioService);
     virtual ~AAudioServiceEndpointPlay();
 
-    aaudio_result_t open(const AAudioStreamConfiguration& configuration) override;
-
-    AudioStreamInternal *getStreamInternal() override {
-        return &mStreamInternalPlay;
-    }
+    aaudio_result_t open(const aaudio::AAudioStreamRequest &request) override;
 
     void *callbackLoop() override;
 
diff --git a/services/oboeservice/AAudioServiceEndpointShared.cpp b/services/oboeservice/AAudioServiceEndpointShared.cpp
new file mode 100644
index 0000000..43d73b7
--- /dev/null
+++ b/services/oboeservice/AAudioServiceEndpointShared.cpp
@@ -0,0 +1,162 @@
+/*
+ * 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 "AAudioServiceEndpointShared"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+
+#include "binding/AAudioServiceMessage.h"
+#include "client/AudioStreamInternal.h"
+#include "client/AudioStreamInternalPlay.h"
+#include "core/AudioStreamBuilder.h"
+
+#include "AAudioServiceEndpointShared.h"
+#include "AAudioServiceStreamShared.h"
+#include "AAudioServiceStreamMMAP.h"
+#include "AAudioMixer.h"
+#include "AAudioService.h"
+
+using namespace android;
+using namespace aaudio;
+
+// This is the maximum size in frames. The effective size can be tuned smaller at runtime.
+#define DEFAULT_BUFFER_CAPACITY   (48 * 8)
+
+std::string AAudioServiceEndpointShared::dump() const {
+    std::stringstream result;
+
+    result << "  SHARED: sharing exclusive stream with handle = 0x"
+           << std::setfill('0') << std::setw(8)
+           << std::hex << mStreamInternal->getServiceHandle()
+           << std::dec << std::setfill(' ');
+    result << "\n";
+    result << "    Running Stream Count: " << mRunningStreamCount << "\n";
+
+    result << AAudioServiceEndpoint::dump();
+    return result.str();
+}
+
+// Share an AudioStreamInternal.
+aaudio_result_t AAudioServiceEndpointShared::open(const aaudio::AAudioStreamRequest &request) {
+    aaudio_result_t result = AAUDIO_OK;
+    const AAudioStreamConfiguration &configuration = request.getConstantConfiguration();
+
+    mRequestedDeviceId = configuration.getDeviceId();
+    setDirection(configuration.getDirection());
+
+    AudioStreamBuilder builder;
+    builder.setSharingMode(AAUDIO_SHARING_MODE_EXCLUSIVE);
+    // Don't fall back to SHARED because that would cause recursion.
+    builder.setSharingModeMatchRequired(true);
+    builder.setDeviceId(mRequestedDeviceId);
+    builder.setFormat(configuration.getFormat());
+    builder.setSampleRate(configuration.getSampleRate());
+    builder.setSamplesPerFrame(configuration.getSamplesPerFrame());
+    builder.setDirection(configuration.getDirection());
+    builder.setBufferCapacity(DEFAULT_BUFFER_CAPACITY);
+
+    result = mStreamInternal->open(builder);
+
+    setSampleRate(mStreamInternal->getSampleRate());
+    setSamplesPerFrame(mStreamInternal->getSamplesPerFrame());
+    setDeviceId(mStreamInternal->getDeviceId());
+    mFramesPerBurst = mStreamInternal->getFramesPerBurst();
+
+    return result;
+}
+
+aaudio_result_t AAudioServiceEndpointShared::close() {
+    return getStreamInternal()->close();
+}
+
+// Glue between C and C++ callbacks.
+static void *aaudio_endpoint_thread_proc(void *context) {
+    AAudioServiceEndpointShared *endpoint = (AAudioServiceEndpointShared *) context;
+    if (endpoint != NULL) {
+        return endpoint->callbackLoop();
+    } else {
+        return NULL;
+    }
+}
+
+aaudio_result_t aaudio::AAudioServiceEndpointShared::startSharingThread_l() {
+    // Launch the callback loop thread.
+    int64_t periodNanos = getStreamInternal()->getFramesPerBurst()
+                          * AAUDIO_NANOS_PER_SECOND
+                          / getSampleRate();
+    mCallbackEnabled.store(true);
+    return getStreamInternal()->createThread(periodNanos, aaudio_endpoint_thread_proc, this);
+}
+
+aaudio_result_t aaudio::AAudioServiceEndpointShared::stopSharingThread() {
+    mCallbackEnabled.store(false);
+    aaudio_result_t result = getStreamInternal()->joinThread(NULL);
+    return result;
+}
+
+aaudio_result_t AAudioServiceEndpointShared::startStream(sp<AAudioServiceStreamBase> sharedStream,
+                                                         audio_port_handle_t *clientHandle) {
+    aaudio_result_t result = AAUDIO_OK;
+    if (++mRunningStreamCount == 1) {
+        // TODO use real-time technique to avoid mutex, eg. atomic command FIFO
+        std::lock_guard<std::mutex> lock(mLockStreams);
+        result = getStreamInternal()->requestStart();
+        startSharingThread_l();
+    }
+    if (result == AAUDIO_OK) {
+        ALOGD("AAudioServiceEndpointShared::startStream() use shared stream client.");
+        result = getStreamInternal()->startClient(sharedStream->getAudioClient(), clientHandle);
+    }
+    return result;
+}
+
+aaudio_result_t AAudioServiceEndpointShared::stopStream(sp<AAudioServiceStreamBase> sharedStream,
+                                                        audio_port_handle_t clientHandle) {
+    // Don't lock here because the disconnectRegisteredStreams also uses the lock.
+
+    // Ignore result.
+    (void) getStreamInternal()->stopClient(clientHandle);
+
+    if (--mRunningStreamCount == 0) { // atomic
+        stopSharingThread();
+        getStreamInternal()->requestStop();
+    }
+    return AAUDIO_OK;
+}
+
+
+// Get timestamp that was written by the real-time service thread, eg. mixer.
+aaudio_result_t AAudioServiceEndpointShared::getFreeRunningPosition(int64_t *positionFrames,
+                                                                  int64_t *timeNanos) {
+    if (mAtomicTimestamp.isValid()) {
+        Timestamp timestamp = mAtomicTimestamp.read();
+        *positionFrames = timestamp.getPosition();
+        *timeNanos = timestamp.getNanoseconds();
+        return AAUDIO_OK;
+    } else {
+        return AAUDIO_ERROR_UNAVAILABLE;
+    }
+}
+
+aaudio_result_t AAudioServiceEndpointShared::getTimestamp(int64_t *positionFrames,
+                                                          int64_t *timeNanos) {
+    return mStreamInternal->getTimestamp(CLOCK_MONOTONIC, positionFrames, timeNanos);
+}
diff --git a/services/oboeservice/AAudioServiceEndpointShared.h b/services/oboeservice/AAudioServiceEndpointShared.h
new file mode 100644
index 0000000..e3bd2c1
--- /dev/null
+++ b/services/oboeservice/AAudioServiceEndpointShared.h
@@ -0,0 +1,78 @@
+/*
+ * 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 AAUDIO_SERVICE_ENDPOINT_SHARED_H
+#define AAUDIO_SERVICE_ENDPOINT_SHARED_H
+
+#include <atomic>
+#include <mutex>
+
+#include "AAudioServiceEndpoint.h"
+#include "client/AudioStreamInternal.h"
+#include "client/AudioStreamInternalPlay.h"
+#include "AAudioServiceStreamShared.h"
+#include "AAudioServiceStreamMMAP.h"
+#include "AAudioService.h"
+
+namespace aaudio {
+
+/**
+ * This Service class corresponds to a Client stream that shares an MMAP device through a mixer
+ * or an input distributor.
+ */
+class AAudioServiceEndpointShared : public AAudioServiceEndpoint {
+
+public:
+
+    std::string dump() const override;
+
+    aaudio_result_t open(const aaudio::AAudioStreamRequest &request) override;
+
+    aaudio_result_t close() override;
+
+    aaudio_result_t startStream(android::sp<AAudioServiceStreamBase> stream,
+                                audio_port_handle_t *clientHandle) override;
+
+    aaudio_result_t stopStream(android::sp<AAudioServiceStreamBase> stream,
+                               audio_port_handle_t clientHandle) override;
+
+    aaudio_result_t getFreeRunningPosition(int64_t *positionFrames, int64_t *timeNanos) override;
+
+    aaudio_result_t getTimestamp(int64_t *positionFrames, int64_t *timeNanos) override;
+
+    virtual void            *callbackLoop() = 0;
+
+    AudioStreamInternal *getStreamInternal() const {
+        return mStreamInternal;
+    };
+
+protected:
+
+    aaudio_result_t          startSharingThread_l();
+
+    aaudio_result_t          stopSharingThread();
+
+    // pointer to object statically allocated in subclasses
+    AudioStreamInternal     *mStreamInternal = nullptr;
+
+    std::atomic<bool>        mCallbackEnabled{false};
+
+    std::atomic<int>         mRunningStreamCount{0};
+};
+
+}
+
+#endif //AAUDIO_SERVICE_ENDPOINT_SHARED_H
diff --git a/services/oboeservice/AAudioServiceStreamBase.cpp b/services/oboeservice/AAudioServiceStreamBase.cpp
index e5f916c..2dc62a0 100644
--- a/services/oboeservice/AAudioServiceStreamBase.cpp
+++ b/services/oboeservice/AAudioServiceStreamBase.cpp
@@ -26,6 +26,9 @@
 #include "binding/AAudioServiceMessage.h"
 #include "utility/AudioClock.h"
 
+#include "AAudioEndpointManager.h"
+#include "AAudioService.h"
+#include "AAudioServiceEndpoint.h"
 #include "AAudioServiceStreamBase.h"
 #include "TimestampScheduler.h"
 
@@ -37,10 +40,11 @@
  * @return
  */
 
-AAudioServiceStreamBase::AAudioServiceStreamBase()
+AAudioServiceStreamBase::AAudioServiceStreamBase(AAudioService &audioService)
         : mUpMessageQueue(nullptr)
         , mAAudioThread()
-        , mAtomicTimestamp() {
+        , mAtomicTimestamp()
+        , mAudioService(audioService) {
     mMmapClient.clientUid = -1;
     mMmapClient.clientPid = -1;
     mMmapClient.packageName = String16("");
@@ -68,49 +72,112 @@
     result << std::setw(6) << mMmapClient.clientUid;
     result << std::setw(4) << (isRunning() ? "yes" : " no");
     result << std::setw(6) << mState;
-    result << std::setw(7) << mAudioFormat;
+    result << std::setw(7) << getFormat();
     result << std::setw(6) << mFramesPerBurst;
-    result << std::setw(5) << mSamplesPerFrame;
-    result << std::setw(9) << mCapacityInFrames;
+    result << std::setw(5) << getSamplesPerFrame();
+    result << std::setw(9) << getBufferCapacity();
 
     return result.str();
 }
 
 aaudio_result_t AAudioServiceStreamBase::open(const aaudio::AAudioStreamRequest &request,
-                     aaudio::AAudioStreamConfiguration &configurationOutput) {
+                                              aaudio_sharing_mode_t sharingMode) {
+    AAudioEndpointManager &mEndpointManager = AAudioEndpointManager::getInstance();
+    aaudio_result_t result = AAUDIO_OK;
 
     mMmapClient.clientUid = request.getUserId();
     mMmapClient.clientPid = request.getProcessId();
-    mMmapClient.packageName.setTo(String16("")); // FIXME what should we do here?
+    mMmapClient.packageName.setTo(String16("")); // TODO What should we do here?
 
-    std::lock_guard<std::mutex> lock(mLockUpMessageQueue);
-    if (mUpMessageQueue != nullptr) {
-        return AAUDIO_ERROR_INVALID_STATE;
-    } else {
+    // Limit scope of lock to avoid recursive lock in close().
+    {
+        std::lock_guard<std::mutex> lock(mUpMessageQueueLock);
+        if (mUpMessageQueue != nullptr) {
+            ALOGE("AAudioServiceStreamBase::open() called twice");
+            return AAUDIO_ERROR_INVALID_STATE;
+        }
+
         mUpMessageQueue = new SharedRingBuffer();
-        return mUpMessageQueue->allocate(sizeof(AAudioServiceMessage), QUEUE_UP_CAPACITY_COMMANDS);
+        result = mUpMessageQueue->allocate(sizeof(AAudioServiceMessage),
+                                           QUEUE_UP_CAPACITY_COMMANDS);
+        if (result != AAUDIO_OK) {
+            goto error;
+        }
+
+        mServiceEndpoint = mEndpointManager.openEndpoint(mAudioService,
+                                                         request,
+                                                         sharingMode);
+        if (mServiceEndpoint == nullptr) {
+            ALOGE("AAudioServiceStreamBase::open() openEndpoint() failed");
+            result = AAUDIO_ERROR_UNAVAILABLE;
+            goto error;
+        }
+        mFramesPerBurst = mServiceEndpoint->getFramesPerBurst();
+        copyFrom(*mServiceEndpoint);
     }
+    return result;
+
+error:
+    close();
+    return result;
 }
 
 aaudio_result_t AAudioServiceStreamBase::close() {
-    if (mState != AAUDIO_STREAM_STATE_CLOSED) {
+    aaudio_result_t result = AAUDIO_OK;
+    if (mState == AAUDIO_STREAM_STATE_CLOSED) {
+        return AAUDIO_OK;
+    }
+
+    stop();
+
+    if (mServiceEndpoint == nullptr) {
+        result = AAUDIO_ERROR_INVALID_STATE;
+    } else {
+        mServiceEndpoint->unregisterStream(this);
+        AAudioEndpointManager &mEndpointManager = AAudioEndpointManager::getInstance();
+        mEndpointManager.closeEndpoint(mServiceEndpoint);
+        mServiceEndpoint.clear();
+    }
+
+    {
+        std::lock_guard<std::mutex> lock(mUpMessageQueueLock);
         stopTimestampThread();
-        std::lock_guard<std::mutex> lock(mLockUpMessageQueue);
         delete mUpMessageQueue;
         mUpMessageQueue = nullptr;
-        mState = AAUDIO_STREAM_STATE_CLOSED;
     }
-    return AAUDIO_OK;
+
+    mState = AAUDIO_STREAM_STATE_CLOSED;
+    return result;
 }
 
+/**
+ * Start the flow of audio data.
+ *
+ * An AAUDIO_SERVICE_EVENT_STARTED will be sent to the client when complete.
+ */
 aaudio_result_t AAudioServiceStreamBase::start() {
     if (isRunning()) {
         return AAUDIO_OK;
     }
-    sendServiceEvent(AAUDIO_SERVICE_EVENT_STARTED);
-    mState = AAUDIO_STREAM_STATE_STARTED;
-    mThreadEnabled.store(true);
-    return mAAudioThread.start(this);
+
+    if (mServiceEndpoint == nullptr) {
+        ALOGE("AAudioServiceStreamBase::start() missing endpoint");
+        return AAUDIO_ERROR_INVALID_STATE;
+    }
+    mClientHandle = AUDIO_PORT_HANDLE_NONE;
+    aaudio_result_t result = mServiceEndpoint->startStream(this, &mClientHandle);
+    if (result != AAUDIO_OK) {
+        ALOGE("AAudioServiceStreamBase::start() mServiceEndpoint returned %d", result);
+        disconnect();
+    } else {
+        if (result == AAUDIO_OK) {
+            sendServiceEvent(AAUDIO_SERVICE_EVENT_STARTED);
+            mState = AAUDIO_STREAM_STATE_STARTED;
+            mThreadEnabled.store(true);
+            result = mAAudioThread.start(this);
+        }
+    }
+    return result;
 }
 
 aaudio_result_t AAudioServiceStreamBase::pause() {
@@ -118,6 +185,16 @@
     if (!isRunning()) {
         return result;
     }
+    if (mServiceEndpoint == nullptr) {
+        ALOGE("AAudioServiceStreamShared::pause() missing endpoint");
+        return AAUDIO_ERROR_INVALID_STATE;
+    }
+    result = mServiceEndpoint->stopStream(this, mClientHandle);
+    if (result != AAUDIO_OK) {
+        ALOGE("AAudioServiceStreamShared::pause() mServiceEndpoint returned %d", result);
+        disconnect(); // TODO should we return or pause Base first?
+    }
+
     sendCurrentTimestamp();
     mThreadEnabled.store(false);
     result = mAAudioThread.stop();
@@ -135,13 +212,27 @@
     if (!isRunning()) {
         return result;
     }
-    // TODO wait for data to be played out
+
+    if (mServiceEndpoint == nullptr) {
+        ALOGE("AAudioServiceStreamShared::stop() missing endpoint");
+        return AAUDIO_ERROR_INVALID_STATE;
+    }
+
     sendCurrentTimestamp(); // warning - this calls a virtual function
     result = stopTimestampThread();
     if (result != AAUDIO_OK) {
         disconnect();
         return result;
     }
+
+    // TODO wait for data to be played out
+    result = mServiceEndpoint->stopStream(this, mClientHandle);
+    if (result != AAUDIO_OK) {
+        ALOGE("AAudioServiceStreamShared::stop() mServiceEndpoint returned %d", result);
+        disconnect();
+        // TODO what to do with result here?
+    }
+
     sendServiceEvent(AAUDIO_SERVICE_EVENT_STOPPED);
     mState = AAUDIO_STREAM_STATE_STOPPED;
     return result;
@@ -157,6 +248,12 @@
 }
 
 aaudio_result_t AAudioServiceStreamBase::flush() {
+    if (mState != AAUDIO_STREAM_STATE_PAUSED) {
+        ALOGE("AAudioServiceStreamBase::flush() stream not paused, state = %s",
+              AAudio_convertStreamStateToText(mState));
+        return AAUDIO_ERROR_INVALID_STATE;
+    }
+    // Data will get flushed when the client receives the FLUSHED event.
     sendServiceEvent(AAUDIO_SERVICE_EVENT_FLUSHED);
     mState = AAUDIO_STREAM_STATE_FLUSHED;
     return AAUDIO_OK;
@@ -166,7 +263,7 @@
 void AAudioServiceStreamBase::run() {
     ALOGD("AAudioServiceStreamBase::run() entering ----------------");
     TimestampScheduler timestampScheduler;
-    timestampScheduler.setBurstPeriod(mFramesPerBurst, mSampleRate);
+    timestampScheduler.setBurstPeriod(mFramesPerBurst, getSampleRate());
     timestampScheduler.start(AudioClock::getNanoseconds());
     int64_t nextTime = timestampScheduler.nextAbsoluteTime();
     while(mThreadEnabled.load()) {
@@ -204,7 +301,7 @@
 }
 
 aaudio_result_t AAudioServiceStreamBase::writeUpMessageQueue(AAudioServiceMessage *command) {
-    std::lock_guard<std::mutex> lock(mLockUpMessageQueue);
+    std::lock_guard<std::mutex> lock(mUpMessageQueueLock);
     if (mUpMessageQueue == nullptr) {
         ALOGE("writeUpMessageQueue(): mUpMessageQueue null! - stream not open");
         return AAUDIO_ERROR_NULL;
@@ -254,3 +351,7 @@
                                     parcelable.mUpMessageQueueParcelable);
     return getDownDataDescription(parcelable);
 }
+
+void AAudioServiceStreamBase::onVolumeChanged(float volume) {
+    sendServiceEvent(AAUDIO_SERVICE_EVENT_VOLUME, volume);
+}
diff --git a/services/oboeservice/AAudioServiceStreamBase.h b/services/oboeservice/AAudioServiceStreamBase.h
index 2f94614..301795d 100644
--- a/services/oboeservice/AAudioServiceStreamBase.h
+++ b/services/oboeservice/AAudioServiceStreamBase.h
@@ -32,22 +32,28 @@
 
 #include "SharedRingBuffer.h"
 #include "AAudioThread.h"
+#include "AAudioService.h"
 
 namespace aaudio {
 
+class AAudioServiceEndpoint;
+
 // We expect the queue to only have a few commands.
 // This should be way more than we need.
 #define QUEUE_UP_CAPACITY_COMMANDS (128)
 
 /**
- * Base class for a stream in the AAudio service.
+ * Each instance of AAudioServiceStreamBase corresponds to a client stream.
+ * It uses a subclass of AAudioServiceEndpoint to communicate with the underlying device or port.
  */
 class AAudioServiceStreamBase
     : public virtual android::RefBase
+    , public AAudioStreamParameters
     , public Runnable  {
 
 public:
-    AAudioServiceStreamBase();
+    AAudioServiceStreamBase(android::AAudioService &aAudioService);
+
     virtual ~AAudioServiceStreamBase();
 
     enum {
@@ -63,39 +69,53 @@
     /**
      * Open the device.
      */
-    virtual aaudio_result_t open(const aaudio::AAudioStreamRequest &request,
-                                 aaudio::AAudioStreamConfiguration &configurationOutput) = 0;
+    virtual aaudio_result_t open(const aaudio::AAudioStreamRequest &request) = 0;
 
     virtual aaudio_result_t close();
 
     /**
-     * Start the flow of data.
+     * Start the flow of audio data.
+     *
+     * This is not guaranteed to be synchronous but it currently is.
+     * An AAUDIO_SERVICE_EVENT_STARTED will be sent to the client when complete.
      */
     virtual aaudio_result_t start();
 
     /**
-     * Stop the flow of data such that start() can resume with loss of data.
-     */
+     * Stop the flow of data so that start() can resume without loss of data.
+     *
+     * This is not guaranteed to be synchronous but it currently is.
+     * An AAUDIO_SERVICE_EVENT_PAUSED will be sent to the client when complete.
+    */
     virtual aaudio_result_t pause();
 
     /**
-     * Stop the flow of data after data in buffer has played.
+     * Stop the flow of data after the currently queued data has finished playing.
+     *
+     * This is not guaranteed to be synchronous but it currently is.
+     * An AAUDIO_SERVICE_EVENT_STOPPED will be sent to the client when complete.
+     *
      */
     virtual aaudio_result_t stop();
 
     aaudio_result_t stopTimestampThread();
 
     /**
-     *  Discard any data held by the underlying HAL or Service.
+     * Discard any data held by the underlying HAL or Service.
+     *
+     * An AAUDIO_SERVICE_EVENT_FLUSHED will be sent to the client when complete.
      */
     virtual aaudio_result_t flush();
 
+
     virtual aaudio_result_t startClient(const android::AudioClient& client __unused,
                                         audio_port_handle_t *clientHandle __unused) {
+        ALOGD("AAudioServiceStreamBase::startClient(%p, ...) AAUDIO_ERROR_UNAVAILABLE", &client);
         return AAUDIO_ERROR_UNAVAILABLE;
     }
 
     virtual aaudio_result_t stopClient(audio_port_handle_t clientHandle __unused) {
+        ALOGD("AAudioServiceStreamBase::stopClient(%d) AAUDIO_ERROR_UNAVAILABLE", clientHandle);
         return AAUDIO_ERROR_UNAVAILABLE;
     }
 
@@ -130,14 +150,14 @@
         return mFramesPerBurst;
     }
 
-    int32_t calculateBytesPerFrame() const {
-        return mSamplesPerFrame * AAudioConvert_formatToSizeInBytes(mAudioFormat);
-    }
-
     void run() override; // to implement Runnable
 
     void disconnect();
 
+    const android::AudioClient &getAudioClient() {
+        return mMmapClient;
+    }
+
     uid_t getOwnerUserId() const {
         return mMmapClient.clientUid;
     }
@@ -157,8 +177,16 @@
         return mState;
     }
 
+    void onVolumeChanged(float volume);
+
 protected:
 
+    /**
+     * Open the device.
+     */
+    aaudio_result_t open(const aaudio::AAudioStreamRequest &request,
+                         aaudio_sharing_mode_t sharingMode);
+
     void setState(aaudio_stream_state_t state) {
         mState = state;
     }
@@ -183,22 +211,21 @@
     pid_t                   mRegisteredClientThread = ILLEGAL_THREAD_ID;
 
     SharedRingBuffer*       mUpMessageQueue;
-    std::mutex              mLockUpMessageQueue;
+    std::mutex              mUpMessageQueueLock;
 
     AAudioThread            mAAudioThread;
     // This is used by one thread to tell another thread to exit. So it must be atomic.
-    std::atomic<bool>       mThreadEnabled;
+    std::atomic<bool>       mThreadEnabled{false};
 
-    aaudio_format_t         mAudioFormat = AAUDIO_FORMAT_UNSPECIFIED;
     int32_t                 mFramesPerBurst = 0;
-    int32_t                 mSamplesPerFrame = AAUDIO_UNSPECIFIED;
-    int32_t                 mSampleRate = AAUDIO_UNSPECIFIED;
-    int32_t                 mCapacityInFrames = AAUDIO_UNSPECIFIED;
-    android::AudioClient    mMmapClient;
+    android::AudioClient    mMmapClient; // set in open, used in MMAP start()
     audio_port_handle_t     mClientHandle = AUDIO_PORT_HANDLE_NONE;
 
     SimpleDoubleBuffer<Timestamp>  mAtomicTimestamp;
 
+    android::AAudioService &mAudioService;
+    android::sp<AAudioServiceEndpoint> mServiceEndpoint;
+
 private:
     aaudio_handle_t         mHandle = -1;
 };
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.cpp b/services/oboeservice/AAudioServiceStreamMMAP.cpp
index 68dcaff..43595a4 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.cpp
+++ b/services/oboeservice/AAudioServiceStreamMMAP.cpp
@@ -19,41 +19,33 @@
 #include <utils/Log.h>
 
 #include <atomic>
+#include <iomanip>
+#include <iostream>
 #include <stdint.h>
 
 #include <utils/String16.h>
 #include <media/nbaio/AudioStreamOutSink.h>
 #include <media/MmapStreamInterface.h>
 
+#include "binding/AudioEndpointParcelable.h"
+#include "utility/AAudioUtilities.h"
+
+#include "AAudioServiceEndpointMMAP.h"
 #include "AAudioServiceStreamBase.h"
 #include "AAudioServiceStreamMMAP.h"
-#include "binding/AudioEndpointParcelable.h"
 #include "SharedMemoryProxy.h"
-#include "utility/AAudioUtilities.h"
 
 using android::base::unique_fd;
 using namespace android;
 using namespace aaudio;
 
-#define AAUDIO_BUFFER_CAPACITY_MIN    4 * 512
-#define AAUDIO_SAMPLE_RATE_DEFAULT    48000
-
-// This is an estimate of the time difference between the HW and the MMAP time.
-// TODO Get presentation timestamps from the HAL instead of using these estimates.
-#define OUTPUT_ESTIMATED_HARDWARE_OFFSET_NANOS  (3 * AAUDIO_NANOS_PER_MILLISECOND)
-#define INPUT_ESTIMATED_HARDWARE_OFFSET_NANOS   (-1 * AAUDIO_NANOS_PER_MILLISECOND)
-
 /**
  * Service Stream that uses an MMAP buffer.
  */
 
-AAudioServiceStreamMMAP::AAudioServiceStreamMMAP(const android::AudioClient& serviceClient,
+AAudioServiceStreamMMAP::AAudioServiceStreamMMAP(android::AAudioService &aAudioService,
                                                  bool inService)
-        : AAudioServiceStreamBase()
-        , mMmapStreamCallback(new MyMmapStreamCallback(*this))
-        , mPreviousFrameCounter(0)
-        , mMmapStream(nullptr)
-        , mServiceClient(serviceClient)
+        : AAudioServiceStreamBase(aAudioService)
         , mInService(inService) {
 }
 
@@ -61,170 +53,32 @@
     if (mState == AAUDIO_STREAM_STATE_CLOSED) {
         return AAUDIO_OK;
     }
+
     stop();
-    if (mMmapStream != 0) {
-        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);
-    }
 
     return AAudioServiceStreamBase::close();
 }
 
 // Open stream on HAL and pass information about the shared memory buffer back to the client.
-aaudio_result_t AAudioServiceStreamMMAP::open(const aaudio::AAudioStreamRequest &request,
-                                       aaudio::AAudioStreamConfiguration &configurationOutput) {
-    const audio_attributes_t attributes = {
-        .content_type = AUDIO_CONTENT_TYPE_MUSIC,
-        .usage = AUDIO_USAGE_MEDIA,
-        .source = AUDIO_SOURCE_VOICE_RECOGNITION,
-        .flags = AUDIO_FLAG_LOW_LATENCY,
-        .tags = ""
-    };
-    audio_config_base_t config;
+aaudio_result_t AAudioServiceStreamMMAP::open(const aaudio::AAudioStreamRequest &request) {
 
-    aaudio_result_t result = AAudioServiceStreamBase::open(request, configurationOutput);
+    sp<AAudioServiceStreamMMAP> keep(this);
+
+    aaudio_result_t result = AAudioServiceStreamBase::open(request,
+                                                           AAUDIO_SHARING_MODE_EXCLUSIVE);
     if (result != AAUDIO_OK) {
         ALOGE("AAudioServiceStreamBase open returned %d", result);
         return result;
     }
 
-    const AAudioStreamConfiguration &configurationInput = request.getConstantConfiguration();
-    audio_port_handle_t deviceId = configurationInput.getDeviceId();
-    aaudio_direction_t direction = request.getDirection();
-
-    // Fill in config
-    aaudio_format_t aaudioFormat = configurationInput.getFormat();
-    if (aaudioFormat == AAUDIO_UNSPECIFIED || aaudioFormat == AAUDIO_FORMAT_PCM_FLOAT) {
-        aaudioFormat = AAUDIO_FORMAT_PCM_I16;
+    result = mServiceEndpoint->registerStream(keep);
+    if (result != AAUDIO_OK) {
+        goto error;
     }
-    config.format = AAudioConvert_aaudioToAndroidDataFormat(aaudioFormat);
-
-    int32_t aaudioSampleRate = configurationInput.getSampleRate();
-    if (aaudioSampleRate == AAUDIO_UNSPECIFIED) {
-        aaudioSampleRate = AAUDIO_SAMPLE_RATE_DEFAULT;
-    }
-    config.sample_rate = aaudioSampleRate;
-
-    int32_t aaudioSamplesPerFrame = configurationInput.getSamplesPerFrame();
-
-    if (direction == AAUDIO_DIRECTION_OUTPUT) {
-        config.channel_mask = (aaudioSamplesPerFrame == AAUDIO_UNSPECIFIED)
-                            ? AUDIO_CHANNEL_OUT_STEREO
-                            : audio_channel_out_mask_from_count(aaudioSamplesPerFrame);
-        mHardwareTimeOffsetNanos = OUTPUT_ESTIMATED_HARDWARE_OFFSET_NANOS; // frames at DAC later
-
-    } else if (direction == AAUDIO_DIRECTION_INPUT) {
-        config.channel_mask =  (aaudioSamplesPerFrame == AAUDIO_UNSPECIFIED)
-                            ? AUDIO_CHANNEL_IN_STEREO
-                            : audio_channel_in_mask_from_count(aaudioSamplesPerFrame);
-        mHardwareTimeOffsetNanos = INPUT_ESTIMATED_HARDWARE_OFFSET_NANOS; // frames at ADC earlier
-
-    } else {
-        ALOGE("openMmapStream - invalid direction = %d", direction);
-        return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
-    }
-
-    MmapStreamInterface::stream_direction_t streamDirection = (direction == AAUDIO_DIRECTION_OUTPUT)
-        ? MmapStreamInterface::DIRECTION_OUTPUT : MmapStreamInterface::DIRECTION_INPUT;
-
-    // Open HAL stream.
-    status_t status = MmapStreamInterface::openMmapStream(streamDirection,
-                                                          &attributes,
-                                                          &config,
-                                                          mMmapClient,
-                                                          &deviceId,
-                                                          mMmapStreamCallback,
-                                                          mMmapStream,
-                                                          &mPortHandle);
-    if (status != OK) {
-        ALOGE("openMmapStream returned status %d", status);
-        return AAUDIO_ERROR_UNAVAILABLE;
-    }
-
-    if (deviceId == AAUDIO_UNSPECIFIED) {
-        ALOGW("AAudioServiceStreamMMAP::open() - openMmapStream() failed to set deviceId");
-    }
-
-    // Create MMAP/NOIRQ buffer.
-    int32_t minSizeFrames = configurationInput.getBufferCapacity();
-    if (minSizeFrames <= 0) { // zero will get rejected
-        minSizeFrames = AAUDIO_BUFFER_CAPACITY_MIN;
-    }
-    status = mMmapStream->createMmapBuffer(minSizeFrames, &mMmapBufferinfo);
-    if (status != OK) {
-        ALOGE("AAudioServiceStreamMMAP::open() - createMmapBuffer() returned status %d",
-              status);
-        return AAUDIO_ERROR_UNAVAILABLE;
-    } else {
-        ALOGD("createMmapBuffer status = %d, buffer_size = %d, burst_size %d"
-                ", Sharable FD: %s",
-              status,
-              abs(mMmapBufferinfo.buffer_size_frames),
-              mMmapBufferinfo.burst_size_frames,
-              mMmapBufferinfo.buffer_size_frames < 0 ? "Yes" : "No");
-    }
-
-    mCapacityInFrames = mMmapBufferinfo.buffer_size_frames;
-    // FIXME: the audio HAL indicates if the shared memory fd can be shared outside of audioserver
-    // by returning a negative buffer size
-    if (mCapacityInFrames < 0) {
-        // Exclusive mode is possible from any client
-        mCapacityInFrames = -mCapacityInFrames;
-    } else {
-        // exclusive mode is only possible if the final fd destination is inside audioserver
-        if ((mMmapClient.clientUid != mServiceClient.clientUid) &&
-                configurationInput.getSharingMode() == AAUDIO_SHARING_MODE_EXCLUSIVE) {
-            // Fallback is handled by caller but indicate what is possible in case
-            // this is used in the future
-            configurationOutput.setSharingMode(AAUDIO_SHARING_MODE_SHARED);
-            return AAUDIO_ERROR_UNAVAILABLE;
-        }
-    }
-
-    // Get information about the stream and pass it back to the caller.
-    mSamplesPerFrame = (direction == AAUDIO_DIRECTION_OUTPUT)
-                           ? audio_channel_count_from_out_mask(config.channel_mask)
-                           : audio_channel_count_from_in_mask(config.channel_mask);
-
-    // AAudio creates a copy of this FD and retains ownership of the copy.
-    // Assume that AudioFlinger will close the original shared_memory_fd.
-    mAudioDataFileDescriptor.reset(dup(mMmapBufferinfo.shared_memory_fd));
-    if (mAudioDataFileDescriptor.get() == -1) {
-        ALOGE("AAudioServiceStreamMMAP::open() - could not dup shared_memory_fd");
-        return AAUDIO_ERROR_INTERNAL; // TODO review
-    }
-    mFramesPerBurst = mMmapBufferinfo.burst_size_frames;
-    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() actual rate = %d, channels = %d, deviceId = %d\n",
-          mSampleRate, mSamplesPerFrame, deviceId);
-
-    // Fill in AAudioStreamConfiguration
-    configurationOutput.setSampleRate(mSampleRate);
-    configurationOutput.setSamplesPerFrame(mSamplesPerFrame);
-    configurationOutput.setFormat(mAudioFormat);
-    configurationOutput.setDeviceId(deviceId);
 
     setState(AAUDIO_STREAM_STATE_OPEN);
+
+error:
     return AAUDIO_OK;
 }
 
@@ -235,18 +89,10 @@
     if (isRunning()) {
         return AAUDIO_OK;
     }
-    if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL;
-    aaudio_result_t result;
-    status_t status = mMmapStream->start(mServiceClient, &mPortHandle);
-    if (status != OK) {
-        ALOGE("AAudioServiceStreamMMAP::start() mMmapStream->start() returned %d", status);
-        disconnect();
-        result = AAudioConvert_androidToAAudioResult(status);
-    } else {
-        result = AAudioServiceStreamBase::start();
-        if (!mInService && result == AAUDIO_OK) {
-            startClient(mMmapClient, &mClientHandle);
-        }
+
+    aaudio_result_t result = AAudioServiceStreamBase::start();
+    if (!mInService && result == AAUDIO_OK) {
+        startClient(mMmapClient, &mClientHandle);
     }
     return result;
 }
@@ -258,70 +104,50 @@
     if (!isRunning()) {
         return AAUDIO_OK;
     }
-    if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL;
-    aaudio_result_t result1 = AAudioServiceStreamBase::pause();
+    aaudio_result_t result = AAudioServiceStreamBase::pause();
+    // TODO put before base::pause()?
     if (!mInService) {
         stopClient(mClientHandle);
     }
-    status_t status = mMmapStream->stop(mPortHandle);
-    mFramesRead.reset32();
-    return (result1 != AAUDIO_OK) ? result1 : AAudioConvert_androidToAAudioResult(status);
+    return result;
 }
 
 aaudio_result_t AAudioServiceStreamMMAP::stop() {
     if (!isRunning()) {
         return AAUDIO_OK;
     }
-    if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL;
-    aaudio_result_t result1 = AAudioServiceStreamBase::stop();
+    aaudio_result_t result = AAudioServiceStreamBase::stop();
+    // TODO put before base::stop()?
     if (!mInService) {
         stopClient(mClientHandle);
     }
-    aaudio_result_t status = mMmapStream->stop(mPortHandle);
-    mFramesRead.reset32();
-    return (result1 != AAUDIO_OK) ? result1 :  AAudioConvert_androidToAAudioResult(status);
-}
-
-/**
- *  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?
-    return AAudioServiceStreamBase::flush();;
+    return result;
 }
 
 aaudio_result_t AAudioServiceStreamMMAP::startClient(const android::AudioClient& client,
-                                                     audio_port_handle_t *clientHandle) {
-    return AAudioConvert_androidToAAudioResult(mMmapStream->start(client, clientHandle));
+                                                       audio_port_handle_t *clientHandle) {
+    aaudio_result_t result = mServiceEndpoint->startClient(client, clientHandle);
+    return result;
 }
 
 aaudio_result_t AAudioServiceStreamMMAP::stopClient(audio_port_handle_t clientHandle) {
-    return AAudioConvert_androidToAAudioResult(mMmapStream->stop(clientHandle));
+    aaudio_result_t result = mServiceEndpoint->stopClient(clientHandle);
+    return result;
 }
 
 // Get free-running DSP or DMA hardware position from the HAL.
 aaudio_result_t AAudioServiceStreamMMAP::getFreeRunningPosition(int64_t *positionFrames,
-                                                                int64_t *timeNanos) {
-    struct audio_mmap_position position;
-    if (mMmapStream == nullptr) {
-        disconnect();
-        return AAUDIO_ERROR_NULL;
-    }
-    status_t status = mMmapStream->getMmapPosition(&position);
-    aaudio_result_t result = AAudioConvert_androidToAAudioResult(status);
-    if (result == AAUDIO_ERROR_UNAVAILABLE) {
-        ALOGW("sendCurrentTimestamp(): getMmapPosition() has no position data yet");
-    } else if (result != AAUDIO_OK) {
-        ALOGE("sendCurrentTimestamp(): getMmapPosition() returned status %d", status);
-        disconnect();
-    } else {
-        mFramesRead.update32(position.position_frames);
-
-        Timestamp timestamp(mFramesRead.get(), position.time_nanoseconds);
+                                                                  int64_t *timeNanos) {
+    sp<AAudioServiceEndpointMMAP> serviceEndpointMMAP{
+            static_cast<AAudioServiceEndpointMMAP *>(mServiceEndpoint.get())};
+    aaudio_result_t result = serviceEndpointMMAP->getFreeRunningPosition(positionFrames, timeNanos);
+    if (result == AAUDIO_OK) {
+        Timestamp timestamp(*positionFrames, *timeNanos);
         mAtomicTimestamp.write(timestamp);
         *positionFrames = timestamp.getPosition();
         *timeNanos = timestamp.getNanoseconds();
+    } else if (result != AAUDIO_ERROR_UNAVAILABLE) {
+        disconnect();
     }
     return result;
 }
@@ -329,51 +155,25 @@
 // Get timestamp that was written by getFreeRunningPosition()
 aaudio_result_t AAudioServiceStreamMMAP::getHardwareTimestamp(int64_t *positionFrames,
                                                                 int64_t *timeNanos) {
+    sp<AAudioServiceEndpointMMAP> serviceEndpointMMAP{
+            static_cast<AAudioServiceEndpointMMAP *>(mServiceEndpoint.get())};
     // TODO Get presentation timestamp from the HAL
     if (mAtomicTimestamp.isValid()) {
         Timestamp timestamp = mAtomicTimestamp.read();
         *positionFrames = timestamp.getPosition();
-        *timeNanos = timestamp.getNanoseconds() + mHardwareTimeOffsetNanos;
+        *timeNanos = timestamp.getNanoseconds() + serviceEndpointMMAP->getHardwareTimeOffsetNanos();
         return AAUDIO_OK;
     } else {
         return AAUDIO_ERROR_UNAVAILABLE;
     }
 }
 
-void AAudioServiceStreamMMAP::onTearDown() {
-    ALOGD("AAudioServiceStreamMMAP::onTearDown() called");
-    disconnect();
-};
-
-void AAudioServiceStreamMMAP::onVolumeChanged(audio_channel_mask_t channels,
-                     android::Vector<float> values) {
-    // TODO do we really need a different volume for each channel?
-    float volume = values[0];
-    ALOGD("AAudioServiceStreamMMAP::onVolumeChanged() volume[0] = %f", volume);
-    sendServiceEvent(AAUDIO_SERVICE_EVENT_VOLUME, volume);
-};
-
-void AAudioServiceStreamMMAP::onRoutingChanged(audio_port_handle_t deviceId) {
-    ALOGD("AAudioServiceStreamMMAP::onRoutingChanged() called with %d, old = %d",
-          deviceId, mDeviceId);
-    if (mDeviceId != AUDIO_PORT_HANDLE_NONE  && mDeviceId != deviceId) {
-        disconnect();
-    }
-    mDeviceId = deviceId;
-};
-
 /**
  * Get an immutable description of the data queue from the HAL.
  */
 aaudio_result_t AAudioServiceStreamMMAP::getDownDataDescription(AudioEndpointParcelable &parcelable)
 {
-    // Gather information on the data queue based on HAL info.
-    int32_t bytesPerFrame = calculateBytesPerFrame();
-    int32_t capacityInBytes = mCapacityInFrames * bytesPerFrame;
-    int fdIndex = parcelable.addFileDescriptor(mAudioDataFileDescriptor, capacityInBytes);
-    parcelable.mDownDataQueueParcelable.setupMemory(fdIndex, 0, capacityInBytes);
-    parcelable.mDownDataQueueParcelable.setBytesPerFrame(bytesPerFrame);
-    parcelable.mDownDataQueueParcelable.setFramesPerBurst(mFramesPerBurst);
-    parcelable.mDownDataQueueParcelable.setCapacityInFrames(mCapacityInFrames);
-    return AAUDIO_OK;
+    sp<AAudioServiceEndpointMMAP> serviceEndpointMMAP{
+            static_cast<AAudioServiceEndpointMMAP *>(mServiceEndpoint.get())};
+    return serviceEndpointMMAP->getDownDataDescription(parcelable);
 }
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.h b/services/oboeservice/AAudioServiceStreamMMAP.h
index e631fd3..bf0aab3 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.h
+++ b/services/oboeservice/AAudioServiceStreamMMAP.h
@@ -37,19 +37,20 @@
 
 namespace aaudio {
 
-    /**
-     * Manage one memory mapped buffer that originated from a HAL.
-     */
-class AAudioServiceStreamMMAP
-    : public AAudioServiceStreamBase
-    , public android::MmapStreamCallback {
+
+/**
+ * These corresponds to an EXCLUSIVE mode MMAP client stream.
+ * It has exclusive use of one AAudioServiceEndpointMMAP to communicate with the underlying
+ * device or port.
+ */
+class AAudioServiceStreamMMAP : public AAudioServiceStreamBase {
 
 public:
-    AAudioServiceStreamMMAP(const android::AudioClient& serviceClient, bool inService);
+    AAudioServiceStreamMMAP(android::AAudioService &aAudioService,
+                            bool inService);
     virtual ~AAudioServiceStreamMMAP() = default;
 
-    aaudio_result_t open(const aaudio::AAudioStreamRequest &request,
-                                 aaudio::AAudioStreamConfiguration &configurationOutput) override;
+    aaudio_result_t open(const aaudio::AAudioStreamRequest &request) override;
 
     /**
      * Start the flow of audio data.
@@ -69,83 +70,28 @@
 
     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.
-     * An AAUDIO_SERVICE_EVENT_FLUSHED will be sent to the client when complete.
-     */
-    aaudio_result_t flush() override;
+    aaudio_result_t startClient(const android::AudioClient& client,
+                                audio_port_handle_t *clientHandle) override;
+
+    aaudio_result_t stopClient(audio_port_handle_t clientHandle) override;
 
     aaudio_result_t close() override;
 
-    virtual aaudio_result_t startClient(const android::AudioClient& client,
-                                        audio_port_handle_t *clientHandle);
-
-    virtual aaudio_result_t stopClient(audio_port_handle_t clientHandle);
-
     /**
      * Send a MMAP/NOIRQ buffer timestamp to the client.
      */
-    aaudio_result_t sendCurrentTimestamp();
-
-    // -------------- Callback functions ---------------------
-    void onTearDown() override;
-
-    void onVolumeChanged(audio_channel_mask_t channels,
-                         android::Vector<float> values) override;
-
-    void onRoutingChanged(audio_port_handle_t deviceId) override;
 
 protected:
 
     aaudio_result_t getDownDataDescription(AudioEndpointParcelable &parcelable) override;
 
     aaudio_result_t getFreeRunningPosition(int64_t *positionFrames, int64_t *timeNanos) override;
-    virtual aaudio_result_t getHardwareTimestamp(int64_t *positionFrames,
-                                                 int64_t *timeNanos) override;
+
+    aaudio_result_t getHardwareTimestamp(int64_t *positionFrames, int64_t *timeNanos) override;
 
 private:
-    // This proxy class was needed to prevent a crash in AudioFlinger
-    // when the stream was closed.
-    class MyMmapStreamCallback : public android::MmapStreamCallback {
-    public:
-        explicit MyMmapStreamCallback(android::MmapStreamCallback &serviceCallback)
-            : mServiceCallback(serviceCallback){}
-        virtual ~MyMmapStreamCallback() = default;
 
-        void onTearDown() override {
-            mServiceCallback.onTearDown();
-        };
-
-        void onVolumeChanged(audio_channel_mask_t channels, android::Vector<float> values) override
-        {
-            mServiceCallback.onVolumeChanged(channels, values);
-        };
-
-        void onRoutingChanged(audio_port_handle_t deviceId) override {
-            mServiceCallback.onRoutingChanged(deviceId);
-        };
-
-    private:
-        android::MmapStreamCallback &mServiceCallback;
-    };
-
-    android::sp<MyMmapStreamCallback>   mMmapStreamCallback;
-    MonotonicCounter                    mFramesWritten;
-    MonotonicCounter                    mFramesRead;
-    int32_t                             mPreviousFrameCounter = 0;   // from HAL
-    int64_t                             mHardwareTimeOffsetNanos = 0; // TODO get from HAL
-
-
-    // Interface to the AudioFlinger MMAP support.
-    android::sp<android::MmapStreamInterface> mMmapStream;
-    struct audio_mmap_buffer_info             mMmapBufferinfo;
-    audio_port_handle_t                       mPortHandle = AUDIO_PORT_HANDLE_NONE;
-    audio_port_handle_t                       mDeviceId = AUDIO_PORT_HANDLE_NONE;
-    android::AudioClient                      mServiceClient;
-    bool                                      mInService = false;
-    android::base::unique_fd                  mAudioDataFileDescriptor;
+    bool                     mInService = false;
 };
 
 } // namespace aaudio
diff --git a/services/oboeservice/AAudioServiceStreamShared.cpp b/services/oboeservice/AAudioServiceStreamShared.cpp
index 57990ce..834f39f 100644
--- a/services/oboeservice/AAudioServiceStreamShared.cpp
+++ b/services/oboeservice/AAudioServiceStreamShared.cpp
@@ -42,10 +42,9 @@
 #define MAX_FRAMES_PER_BUFFER       (32 * 1024)
 
 AAudioServiceStreamShared::AAudioServiceStreamShared(AAudioService &audioService)
-    : mAudioService(audioService)
+    : AAudioServiceStreamBase(audioService)
     , mTimestampPositionOffset(0)
-    , mXRunCount(0)
-    {
+    , mXRunCount(0) {
 }
 
 std::string AAudioServiceStreamShared::dumpHeader() {
@@ -57,6 +56,7 @@
 
 std::string AAudioServiceStreamShared::dump() const {
     std::stringstream result;
+
     result << AAudioServiceStreamBase::dump();
 
     auto fifo = mAudioDataQueue->getFifoBuffer();
@@ -116,87 +116,69 @@
     return capacityInFrames;
 }
 
-aaudio_result_t AAudioServiceStreamShared::open(const aaudio::AAudioStreamRequest &request,
-                     aaudio::AAudioStreamConfiguration &configurationOutput)  {
+aaudio_result_t AAudioServiceStreamShared::open(const aaudio::AAudioStreamRequest &request)  {
 
     sp<AAudioServiceStreamShared> keep(this);
 
-    aaudio_result_t result = AAudioServiceStreamBase::open(request, configurationOutput);
+    aaudio_result_t result = AAudioServiceStreamBase::open(request, AAUDIO_SHARING_MODE_SHARED);
     if (result != AAUDIO_OK) {
         ALOGE("AAudioServiceStreamBase open() returned %d", result);
         return result;
     }
 
     const AAudioStreamConfiguration &configurationInput = request.getConstantConfiguration();
-    aaudio_direction_t direction = request.getDirection();
 
-    AAudioEndpointManager &mEndpointManager = AAudioEndpointManager::getInstance();
-    mServiceEndpoint = mEndpointManager.openEndpoint(mAudioService, configurationInput, direction);
-    if (mServiceEndpoint == nullptr) {
-        ALOGE("AAudioServiceStreamShared::open() mServiceEndPoint = %p", mServiceEndpoint);
-        return AAUDIO_ERROR_UNAVAILABLE;
-    }
 
     // Is the request compatible with the shared endpoint?
-    mAudioFormat = configurationInput.getFormat();
-    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);
+    setFormat(configurationInput.getFormat());
+    if (getFormat() == AAUDIO_FORMAT_UNSPECIFIED) {
+        setFormat(AAUDIO_FORMAT_PCM_FLOAT);
+    } else if (getFormat() != AAUDIO_FORMAT_PCM_FLOAT) {
+        ALOGE("AAudioServiceStreamShared::open() mAudioFormat = %d, need FLOAT", getFormat());
         result = AAUDIO_ERROR_INVALID_FORMAT;
         goto error;
     }
 
-    mSampleRate = configurationInput.getSampleRate();
-    if (mSampleRate == AAUDIO_UNSPECIFIED) {
-        mSampleRate = mServiceEndpoint->getSampleRate();
-    } else if (mSampleRate != mServiceEndpoint->getSampleRate()) {
+    setSampleRate(configurationInput.getSampleRate());
+    if (getSampleRate() == AAUDIO_UNSPECIFIED) {
+        setSampleRate(mServiceEndpoint->getSampleRate());
+    } else if (getSampleRate() != mServiceEndpoint->getSampleRate()) {
         ALOGE("AAudioServiceStreamShared::open() mSampleRate = %d, need %d",
-              mSampleRate, mServiceEndpoint->getSampleRate());
+              getSampleRate(), mServiceEndpoint->getSampleRate());
         result = AAUDIO_ERROR_INVALID_RATE;
         goto error;
     }
 
-    mSamplesPerFrame = configurationInput.getSamplesPerFrame();
-    if (mSamplesPerFrame == AAUDIO_UNSPECIFIED) {
-        mSamplesPerFrame = mServiceEndpoint->getSamplesPerFrame();
-    } else if (mSamplesPerFrame != mServiceEndpoint->getSamplesPerFrame()) {
+    setSamplesPerFrame(configurationInput.getSamplesPerFrame());
+    if (getSamplesPerFrame() == AAUDIO_UNSPECIFIED) {
+        setSamplesPerFrame(mServiceEndpoint->getSamplesPerFrame());
+    } else if (getSamplesPerFrame() != mServiceEndpoint->getSamplesPerFrame()) {
         ALOGE("AAudioServiceStreamShared::open() mSamplesPerFrame = %d, need %d",
-              mSamplesPerFrame, mServiceEndpoint->getSamplesPerFrame());
+              getSamplesPerFrame(), mServiceEndpoint->getSamplesPerFrame());
         result = AAUDIO_ERROR_OUT_OF_RANGE;
         goto error;
     }
 
-    mFramesPerBurst = mServiceEndpoint->getFramesPerBurst();
-    ALOGD("AAudioServiceStreamShared::open() mSampleRate = %d, mFramesPerBurst = %d",
-          mSampleRate, mFramesPerBurst);
-
-    mCapacityInFrames = calculateBufferCapacity(configurationInput.getBufferCapacity(),
-                                     mFramesPerBurst);
-    if (mCapacityInFrames < 0) {
-        result = mCapacityInFrames; // negative error code
-        mCapacityInFrames = 0;
+    setBufferCapacity(calculateBufferCapacity(configurationInput.getBufferCapacity(),
+                                     mFramesPerBurst));
+    if (getBufferCapacity() < 0) {
+        result = getBufferCapacity(); // negative error code
+        setBufferCapacity(0);
         goto error;
     }
 
     // Create audio data shared memory buffer for client.
     mAudioDataQueue = new SharedRingBuffer();
-    result = mAudioDataQueue->allocate(calculateBytesPerFrame(), mCapacityInFrames);
+    result = mAudioDataQueue->allocate(calculateBytesPerFrame(), getBufferCapacity());
     if (result != AAUDIO_OK) {
         ALOGE("AAudioServiceStreamShared::open() could not allocate FIFO with %d frames",
-              mCapacityInFrames);
+              getBufferCapacity());
         result = AAUDIO_ERROR_NO_MEMORY;
         goto error;
     }
 
     ALOGD("AAudioServiceStreamShared::open() actual rate = %d, channels = %d, deviceId = %d",
-          mSampleRate, mSamplesPerFrame, mServiceEndpoint->getDeviceId());
-
-    // Fill in configuration for client.
-    configurationOutput.setSampleRate(mSampleRate);
-    configurationOutput.setSamplesPerFrame(mSamplesPerFrame);
-    configurationOutput.setFormat(mAudioFormat);
-    configurationOutput.setDeviceId(mServiceEndpoint->getDeviceId());
+          getSampleRate(), getSamplesPerFrame(), mServiceEndpoint->getDeviceId());
 
     result = mServiceEndpoint->registerStream(keep);
     if (result != AAUDIO_OK) {
@@ -211,118 +193,14 @@
     return result;
 }
 
-/**
- * Start the flow of audio data.
- *
- * An AAUDIO_SERVICE_EVENT_STARTED will be sent to the client when complete.
- */
-aaudio_result_t AAudioServiceStreamShared::start()  {
-    if (isRunning()) {
-        return AAUDIO_OK;
-    }
-    AAudioServiceEndpoint *endpoint = mServiceEndpoint;
-    if (endpoint == nullptr) {
-        ALOGE("AAudioServiceStreamShared::start() missing endpoint");
-        return AAUDIO_ERROR_INVALID_STATE;
-    }
-    // For output streams, this will add the stream to the mixer.
-    aaudio_result_t result = endpoint->startStream(this);
-    if (result != AAUDIO_OK) {
-        ALOGE("AAudioServiceStreamShared::start() mServiceEndpoint returned %d", result);
-        disconnect();
-    } else {
-        result = endpoint->getStreamInternal()->startClient(mMmapClient, &mClientHandle);
-        if (result == AAUDIO_OK) {
-            result = AAudioServiceStreamBase::start();
-        }
-    }
-    return result;
-}
-
-/**
- * Stop the flow of data so that start() can resume without loss of data.
- *
- * An AAUDIO_SERVICE_EVENT_PAUSED will be sent to the client when complete.
-*/
-aaudio_result_t AAudioServiceStreamShared::pause()  {
-    if (!isRunning()) {
-        return AAUDIO_OK;
-    }
-    AAudioServiceEndpoint *endpoint = mServiceEndpoint;
-    if (endpoint == nullptr) {
-        ALOGE("AAudioServiceStreamShared::pause() missing endpoint");
-        return AAUDIO_ERROR_INVALID_STATE;
-    }
-    endpoint->getStreamInternal()->stopClient(mClientHandle);
-    aaudio_result_t result = endpoint->stopStream(this);
-    if (result != AAUDIO_OK) {
-        ALOGE("AAudioServiceStreamShared::pause() mServiceEndpoint returned %d", result);
-        disconnect(); // TODO should we return or pause Base first?
-    }
-    return AAudioServiceStreamBase::pause();
-}
-
-aaudio_result_t AAudioServiceStreamShared::stop()  {
-    if (!isRunning()) {
-        return AAUDIO_OK;
-    }
-    AAudioServiceEndpoint *endpoint = mServiceEndpoint;
-    if (endpoint == nullptr) {
-        ALOGE("AAudioServiceStreamShared::stop() missing endpoint");
-        return AAUDIO_ERROR_INVALID_STATE;
-    }
-    endpoint->getStreamInternal()->stopClient(mClientHandle);
-    aaudio_result_t result = endpoint->stopStream(this);
-    if (result != AAUDIO_OK) {
-        ALOGE("AAudioServiceStreamShared::stop() mServiceEndpoint returned %d", result);
-        disconnect();
-    }
-    return AAudioServiceStreamBase::stop();
-}
-
-/**
- *  Discard any data held by the underlying HAL or Service.
- *
- * An AAUDIO_SERVICE_EVENT_FLUSHED will be sent to the client when complete.
- */
-aaudio_result_t AAudioServiceStreamShared::flush()  {
-    AAudioServiceEndpoint *endpoint = mServiceEndpoint;
-    if (endpoint == nullptr) {
-        ALOGE("AAudioServiceStreamShared::flush() missing endpoint");
-        return AAUDIO_ERROR_INVALID_STATE;
-    }
-    if (mState != AAUDIO_STREAM_STATE_PAUSED) {
-         ALOGE("AAudioServiceStreamShared::flush() stream not paused, state = %s",
-            AAudio_convertStreamStateToText(mState));
-        return AAUDIO_ERROR_INVALID_STATE;
-    }
-    // Data will get flushed when the client receives the FLUSHED event.
-    return AAudioServiceStreamBase::flush();
-}
 
 aaudio_result_t AAudioServiceStreamShared::close()  {
-    if (mState == AAUDIO_STREAM_STATE_CLOSED) {
-        return AAUDIO_OK;
-    }
+    aaudio_result_t result = AAudioServiceStreamBase::close();
 
-    stop();
+    delete mAudioDataQueue;
+    mAudioDataQueue = nullptr;
 
-    AAudioServiceEndpoint *endpoint = mServiceEndpoint;
-    if (endpoint == nullptr) {
-        return AAUDIO_ERROR_INVALID_STATE;
-    }
-
-    endpoint->unregisterStream(this);
-
-    AAudioEndpointManager &mEndpointManager = AAudioEndpointManager::getInstance();
-    mEndpointManager.closeEndpoint(endpoint);
-    mServiceEndpoint = nullptr;
-
-    if (mAudioDataQueue != nullptr) {
-        delete mAudioDataQueue;
-        mAudioDataQueue = nullptr;
-    }
-    return AAudioServiceStreamBase::close();
+    return result;
 }
 
 /**
@@ -341,9 +219,10 @@
     mAtomicTimestamp.write(timestamp);
 }
 
-// Get timestamp that was written by the real-time service thread, eg. mixer.
+// Get timestamp that was written by mixer or distributor.
 aaudio_result_t AAudioServiceStreamShared::getFreeRunningPosition(int64_t *positionFrames,
-                                                                int64_t *timeNanos) {
+                                                                  int64_t *timeNanos) {
+    // TODO Get presentation timestamp from the HAL
     if (mAtomicTimestamp.isValid()) {
         Timestamp timestamp = mAtomicTimestamp.read();
         *positionFrames = timestamp.getPosition();
@@ -356,7 +235,7 @@
 
 // Get timestamp from lower level service.
 aaudio_result_t AAudioServiceStreamShared::getHardwareTimestamp(int64_t *positionFrames,
-                                                              int64_t *timeNanos) {
+                                                                int64_t *timeNanos) {
 
     aaudio_result_t result = mServiceEndpoint->getTimestamp(positionFrames, timeNanos);
     if (result == AAUDIO_OK) {
diff --git a/services/oboeservice/AAudioServiceStreamShared.h b/services/oboeservice/AAudioServiceStreamShared.h
index 36a56b8..bc86dcc 100644
--- a/services/oboeservice/AAudioServiceStreamShared.h
+++ b/services/oboeservice/AAudioServiceStreamShared.h
@@ -50,37 +50,7 @@
 
     std::string dump() const override;
 
-    aaudio_result_t open(const aaudio::AAudioStreamRequest &request,
-                         aaudio::AAudioStreamConfiguration &configurationOutput) override;
-
-    /**
-     * Start the flow of audio data.
-     *
-     * This is not guaranteed to be synchronous but it currently is.
-     * An AAUDIO_SERVICE_EVENT_STARTED will be sent to the client when complete.
-     */
-    aaudio_result_t start() override;
-
-    /**
-     * Stop the flow of data so that start() can resume without loss of data.
-     *
-     * This is not guaranteed to be synchronous but it currently is.
-     * An AAUDIO_SERVICE_EVENT_PAUSED will be sent to the client when complete.
-    */
-    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.
-     * An AAUDIO_SERVICE_EVENT_FLUSHED will be sent to the client when complete.
-     */
-    aaudio_result_t flush() override;
+    aaudio_result_t open(const aaudio::AAudioStreamRequest &request) override;
 
     aaudio_result_t close() override;
 
@@ -109,8 +79,7 @@
 
     aaudio_result_t getFreeRunningPosition(int64_t *positionFrames, int64_t *timeNanos) override;
 
-    virtual aaudio_result_t getHardwareTimestamp(int64_t *positionFrames,
-                                                 int64_t *timeNanos) override;
+    aaudio_result_t getHardwareTimestamp(int64_t *positionFrames, int64_t *timeNanos) override;
 
     /**
      * @param requestedCapacityFrames
@@ -121,12 +90,11 @@
                                             int32_t framesPerBurst);
 
 private:
-    android::AAudioService  &mAudioService;
-    AAudioServiceEndpoint   *mServiceEndpoint = nullptr;
     SharedRingBuffer        *mAudioDataQueue = nullptr;
 
     std::atomic<int64_t>     mTimestampPositionOffset;
     std::atomic<int32_t>     mXRunCount;
+
 };
 
 } /* namespace aaudio */
diff --git a/services/oboeservice/Android.mk b/services/oboeservice/Android.mk
index a896a7a..1b74ad3 100644
--- a/services/oboeservice/Android.mk
+++ b/services/oboeservice/Android.mk
@@ -31,7 +31,9 @@
     AAudioService.cpp \
     AAudioServiceEndpoint.cpp \
     AAudioServiceEndpointCapture.cpp \
+    AAudioServiceEndpointMMAP.cpp \
     AAudioServiceEndpointPlay.cpp \
+    AAudioServiceEndpointShared.cpp \
     AAudioServiceStreamBase.cpp \
     AAudioServiceStreamMMAP.cpp \
     AAudioServiceStreamShared.cpp \