Merge "media: clear pause state at stop()" into nyc-dev
diff --git a/camera/aidl/android/hardware/camera2/ICameraDeviceCallbacks.aidl b/camera/aidl/android/hardware/camera2/ICameraDeviceCallbacks.aidl
index ab57db5..755ec8e 100644
--- a/camera/aidl/android/hardware/camera2/ICameraDeviceCallbacks.aidl
+++ b/camera/aidl/android/hardware/camera2/ICameraDeviceCallbacks.aidl
@@ -37,4 +37,11 @@
     oneway void onResultReceived(in CameraMetadataNative result,
                                  in CaptureResultExtras resultExtras);
     oneway void onPrepared(int streamId);
+
+    /**
+     * Repeating request encountered an error and was stopped.
+     *
+     * @param lastFrameNumber Frame number of the last frame of the streaming request.
+     */
+    oneway void onRepeatingRequestError(in long lastFrameNumber);
 }
diff --git a/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl b/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl
index 250f15e..1e8744b 100644
--- a/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl
+++ b/camera/aidl/android/hardware/camera2/ICameraDeviceUser.aidl
@@ -36,7 +36,10 @@
      * Cancel the repeating request specified by requestId
      * Returns the frame number of the last frame that will be produced from this
      * repeating request, or NO_IN_FLIGHT_REPEATING_FRAMES if no frames were produced
-     * by this repeating request
+     * by this repeating request.
+     *
+     * Repeating request may be stopped by camera device due to an error. Canceling a stopped
+     * repeating request will trigger ERROR_ILLEGAL_ARGUMENT.
      */
     long cancelRequest(int requestId);
 
diff --git a/camera/ndk/impl/ACameraDevice.cpp b/camera/ndk/impl/ACameraDevice.cpp
index 0b758b6..bff5547 100644
--- a/camera/ndk/impl/ACameraDevice.cpp
+++ b/camera/ndk/impl/ACameraDevice.cpp
@@ -380,7 +380,11 @@
 
         int64_t lastFrameNumber;
         binder::Status remoteRet = mRemote->cancelRequest(repeatingSequenceId, &lastFrameNumber);
-        if (!remoteRet.isOk()) {
+        if (remoteRet.serviceSpecificErrorCode() ==
+                hardware::ICameraService::ERROR_ILLEGAL_ARGUMENT) {
+            ALOGV("Repeating request is already stopped.");
+            return ACAMERA_OK;
+        } else if (!remoteRet.isOk()) {
             ALOGE("Stop repeating request fails in remote: %s", remoteRet.toString8().string());
             return ACAMERA_ERROR_UNKNOWN;
         }
@@ -1342,4 +1346,24 @@
     return binder::Status::ok();
 }
 
+binder::Status
+CameraDevice::ServiceCallback::onRepeatingRequestError(int64_t lastFrameNumber) {
+    binder::Status ret = binder::Status::ok();
+
+    sp<CameraDevice> dev = mDevice.promote();
+    if (dev == nullptr) {
+        return ret; // device has been closed
+    }
+
+    Mutex::Autolock _l(dev->mDeviceLock);
+
+    int repeatingSequenceId = dev->mRepeatingSequenceId;
+    dev->mRepeatingSequenceId = REQUEST_ID_NONE;
+
+    dev->checkRepeatingSequenceCompleteLocked(repeatingSequenceId, lastFrameNumber);
+
+    return ret;
+}
+
+
 } // namespace android
diff --git a/camera/ndk/impl/ACameraDevice.h b/camera/ndk/impl/ACameraDevice.h
index 3ccf95a..71e364d 100644
--- a/camera/ndk/impl/ACameraDevice.h
+++ b/camera/ndk/impl/ACameraDevice.h
@@ -74,6 +74,7 @@
         binder::Status onResultReceived(const CameraMetadata& metadata,
                               const CaptureResultExtras& resultExtras) override;
         binder::Status onPrepared(int streamId) override;
+        binder::Status onRepeatingRequestError(int64_t lastFrameNumber) override;
       private:
         const wp<CameraDevice> mDevice;
     };
diff --git a/camera/tests/CameraBinderTests.cpp b/camera/tests/CameraBinderTests.cpp
index 0b687b4..828a758 100644
--- a/camera/tests/CameraBinderTests.cpp
+++ b/camera/tests/CameraBinderTests.cpp
@@ -149,7 +149,8 @@
         PREPARED,
         RUNNING,
         SENT_RESULT,
-        UNINITIALIZED
+        UNINITIALIZED,
+        REPEATING_REQUEST_ERROR,
     };
 
 protected:
@@ -215,6 +216,15 @@
         return binder::Status::ok();
     }
 
+    virtual binder::Status onRepeatingRequestError(int64_t lastFrameNumber) {
+        (void) lastFrameNumber;
+        Mutex::Autolock l(mLock);
+        mLastStatus = REPEATING_REQUEST_ERROR;
+        mStatusesHit.push_back(mLastStatus);
+        mStatusCondition.broadcast();
+        return binder::Status::ok();
+    }
+
     // Test helper functions:
 
     bool hadError() const {
diff --git a/media/libmedia/IMediaExtractor.cpp b/media/libmedia/IMediaExtractor.cpp
index b13b69f..f142ccc 100644
--- a/media/libmedia/IMediaExtractor.cpp
+++ b/media/libmedia/IMediaExtractor.cpp
@@ -216,13 +216,15 @@
     return str;
 }
 
-static Vector<ExtractorInstance> extractors;
+static Vector<ExtractorInstance> sExtractors;
+static Mutex sExtractorsLock;
 
 void registerMediaSource(
         const sp<IMediaExtractor> &ex,
         const sp<IMediaSource> &source) {
-    for (size_t i = 0; i < extractors.size(); i++) {
-        ExtractorInstance &instance = extractors.editItemAt(i);
+    Mutex::Autolock lock(sExtractorsLock);
+    for (size_t i = 0; i < sExtractors.size(); i++) {
+        ExtractorInstance &instance = sExtractors.editItemAt(i);
         sp<IMediaExtractor> extractor = instance.extractor.promote();
         if (extractor != NULL && extractor == ex) {
             if (instance.tracks.size() > 5) {
@@ -246,19 +248,25 @@
     ex.owner = IPCThreadState::self()->getCallingPid();
     ex.extractor = extractor;
 
-    if (extractors.size() > 10) {
-        extractors.resize(10);
+    {
+        Mutex::Autolock lock(sExtractorsLock);
+        if (sExtractors.size() > 10) {
+            sExtractors.resize(10);
+        }
+        sExtractors.push_front(ex);
     }
-    extractors.push_front(ex);
 }
 
 status_t dumpExtractors(int fd, const Vector<String16>&) {
     String8 out;
     out.append("Recent extractors, most recent first:\n");
-    for (size_t i = 0; i < extractors.size(); i++) {
-        const ExtractorInstance &instance = extractors.itemAt(i);
-        out.append("  ");
-        out.append(instance.toString());
+    {
+        Mutex::Autolock lock(sExtractorsLock);
+        for (size_t i = 0; i < sExtractors.size(); i++) {
+            const ExtractorInstance &instance = sExtractors.itemAt(i);
+            out.append("  ");
+            out.append(instance.toString());
+        }
     }
     write(fd, out.string(), out.size());
     return OK;
diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk
index 7f41143..edad4be 100644
--- a/media/libmediaplayerservice/Android.mk
+++ b/media/libmediaplayerservice/Android.mk
@@ -32,6 +32,7 @@
     libgui                      \
     libmedia                    \
     libmediautils               \
+    libmemunreachable           \
     libsonivox                  \
     libstagefright              \
     libstagefright_foundation   \
@@ -54,6 +55,7 @@
     $(TOP)/frameworks/av/include/camera                             \
     $(TOP)/frameworks/native/include/media/openmax                  \
     $(TOP)/external/tremolo/Tremolo                                 \
+    libcore/include                                                 \
 
 LOCAL_CFLAGS += -Werror -Wno-error=deprecated-declarations -Wall
 LOCAL_CLANG := true
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 424215e..9b081e9 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -62,6 +62,7 @@
 #include <media/stagefright/foundation/ALooperRoster.h>
 #include <mediautils/BatteryNotifier.h>
 
+#include <memunreachable/memunreachable.h>
 #include <system/audio.h>
 
 #include <private/android_filesystem_config.h>
@@ -536,14 +537,23 @@
         gLooperRoster.dump(fd, args);
 
         bool dumpMem = false;
+        bool unreachableMemory = false;
         for (size_t i = 0; i < args.size(); i++) {
             if (args[i] == String16("-m")) {
                 dumpMem = true;
+            } else if (args[i] == String16("--unreachable")) {
+                unreachableMemory = true;
             }
         }
         if (dumpMem) {
             dumpMemoryAddresses(fd);
         }
+        if (unreachableMemory) {
+            result.append("\nDumping unreachable memory:\n");
+            // TODO - should limit be an argument parameter?
+            std::string s = GetUnreachableMemoryString(true /* contents */, 10000 /* limit */);
+            result.append(s.c_str(), s.size());
+        }
     }
     write(fd, result.string(), result.size());
     return NO_ERROR;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index 42a82ac..7b000fa 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -400,14 +400,20 @@
 }
 
 void NuPlayer::resetAsync() {
-    if (mSource != NULL) {
+    sp<Source> source;
+    {
+        Mutex::Autolock autoLock(mSourceLock);
+        source = mSource;
+    }
+
+    if (source != NULL) {
         // During a reset, the data source might be unresponsive already, we need to
         // disconnect explicitly so that reads exit promptly.
         // We can't queue the disconnect request to the looper, as it might be
         // queued behind a stuck read and never gets processed.
         // Doing a disconnect outside the looper to allows the pending reads to exit
         // (either successfully or with error).
-        mSource->disconnect();
+        source->disconnect();
     }
 
     (new AMessage(kWhatReset, this))->post();
@@ -484,6 +490,7 @@
             sp<RefBase> obj;
             CHECK(msg->findObject("source", &obj));
             if (obj != NULL) {
+                Mutex::Autolock autoLock(mSourceLock);
                 mSource = static_cast<Source *>(obj.get());
             } else {
                 err = UNKNOWN_ERROR;
@@ -1998,6 +2005,7 @@
     if (mSource != NULL) {
         mSource->stop();
 
+        Mutex::Autolock autoLock(mSourceLock);
         mSource.clear();
     }
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h
index 369590b..f6eb49e 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h
@@ -140,6 +140,7 @@
     bool mUIDValid;
     uid_t mUID;
     pid_t mPID;
+    Mutex mSourceLock;  // guard |mSource|.
     sp<Source> mSource;
     uint32_t mSourceFlags;
     sp<Surface> mSurface;
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 7a8f4c0..db20590 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -2261,6 +2261,7 @@
 
     HevcParameterSets paramSets;
     if (parseHEVCCodecSpecificData(data, size, paramSets) != OK) {
+        ALOGE("failed parsing codec specific data");
         return ERROR_MALFORMED;
     }
 
@@ -2271,8 +2272,9 @@
         return NO_MEMORY;
     }
     status_t err = paramSets.makeHvcc((uint8_t *)mCodecSpecificData,
-            &mCodecSpecificDataSize, mOwner->useNalLengthFour() ? 5 : 2);
+            &mCodecSpecificDataSize, mOwner->useNalLengthFour() ? 4 : 2);
     if (err != OK) {
+        ALOGE("failed constructing HVCC atom");
         return err;
     }
 
diff --git a/media/libstagefright/OggExtractor.cpp b/media/libstagefright/OggExtractor.cpp
index 9162f80..37e8e9c 100644
--- a/media/libstagefright/OggExtractor.cpp
+++ b/media/libstagefright/OggExtractor.cpp
@@ -713,6 +713,7 @@
                     packetSize);
 
             if (n < (ssize_t)packetSize) {
+                buffer->release();
                 ALOGV("failed to read %zu bytes at %#016llx, got %zd bytes",
                         packetSize, (long long)dataOffset, n);
                 return ERROR_IO;
diff --git a/media/libstagefright/codecs/g711/dec/SoftG711.cpp b/media/libstagefright/codecs/g711/dec/SoftG711.cpp
index 958e7c4..9f7b590 100644
--- a/media/libstagefright/codecs/g711/dec/SoftG711.cpp
+++ b/media/libstagefright/codecs/g711/dec/SoftG711.cpp
@@ -240,6 +240,15 @@
             mSignalledError = true;
         }
 
+        if (inHeader->nFilledLen * sizeof(int16_t) > outHeader->nAllocLen) {
+            ALOGE("output buffer too small (%d).", outHeader->nAllocLen);
+            android_errorWriteLog(0x534e4554, "27793163");
+
+            notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+            mSignalledError = true;
+            return;
+        }
+
         const uint8_t *inputptr = inHeader->pBuffer + inHeader->nOffset;
 
         if (mIsMLaw) {
diff --git a/media/libstagefright/codecs/gsm/dec/SoftGSM.cpp b/media/libstagefright/codecs/gsm/dec/SoftGSM.cpp
index 7916c45..04d5a33 100644
--- a/media/libstagefright/codecs/gsm/dec/SoftGSM.cpp
+++ b/media/libstagefright/codecs/gsm/dec/SoftGSM.cpp
@@ -228,6 +228,14 @@
             mSignalledError = true;
         }
 
+        if (outHeader->nAllocLen < (inHeader->nFilledLen / kMSGSMFrameSize) * 320) {
+            ALOGE("output buffer is not large enough (%d).", outHeader->nAllocLen);
+            android_errorWriteLog(0x534e4554, "27793367");
+            notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+            mSignalledError = true;
+            return;
+        }
+
         uint8_t *inputptr = inHeader->pBuffer + inHeader->nOffset;
 
         int n = mSignalledError ? 0 : DecodeGSM(mGsm,
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index fe2f9a6..bf18975 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -1080,8 +1080,16 @@
     mOutputRoutes.incRouteActivity(session);
 
     audio_devices_t newDevice;
+    AudioMix *policyMix = NULL;
+    const char *address = NULL;
     if (outputDesc->mPolicyMix != NULL) {
-        newDevice = AUDIO_DEVICE_OUT_REMOTE_SUBMIX;
+        policyMix = outputDesc->mPolicyMix;
+        address = policyMix->mDeviceAddress.string();
+        if ((policyMix->mRouteFlags & MIX_ROUTE_FLAG_RENDER) == MIX_ROUTE_FLAG_RENDER) {
+            newDevice = policyMix->mDeviceType;
+        } else {
+            newDevice = AUDIO_DEVICE_OUT_REMOTE_SUBMIX;
+        }
     } else if (mOutputRoutes.hasRouteChanged(session)) {
         newDevice = getNewOutputDevice(outputDesc, false /*fromCache*/);
         checkStrategyRoute(getStrategy(stream), output);
@@ -1091,7 +1099,7 @@
 
     uint32_t delayMs = 0;
 
-    status_t status = startSource(outputDesc, stream, newDevice, &delayMs);
+    status_t status = startSource(outputDesc, stream, newDevice, address, &delayMs);
 
     if (status != NO_ERROR) {
         mOutputRoutes.decRouteActivity(session);
@@ -1099,11 +1107,11 @@
     }
     // Automatically enable the remote submix input when output is started on a re routing mix
     // of type MIX_TYPE_RECORDERS
-    if (audio_is_remote_submix_device(newDevice) && outputDesc->mPolicyMix != NULL &&
-            outputDesc->mPolicyMix->mMixType == MIX_TYPE_RECORDERS) {
+    if (audio_is_remote_submix_device(newDevice) && policyMix != NULL &&
+            policyMix->mMixType == MIX_TYPE_RECORDERS) {
             setDeviceConnectionStateInt(AUDIO_DEVICE_IN_REMOTE_SUBMIX,
                     AUDIO_POLICY_DEVICE_STATE_AVAILABLE,
-                    outputDesc->mPolicyMix->mDeviceAddress,
+                    address,
                     "remote-submix");
     }
 
@@ -1117,6 +1125,7 @@
 status_t AudioPolicyManager::startSource(sp<AudioOutputDescriptor> outputDesc,
                                              audio_stream_type_t stream,
                                              audio_devices_t device,
+                                             const char *address,
                                              uint32_t *delayMs)
 {
     // cannot start playback of STREAM_TTS if any other output is being used
@@ -1173,7 +1182,7 @@
                 }
             }
         }
-        uint32_t muteWaitMs = setOutputDevice(outputDesc, device, force);
+        uint32_t muteWaitMs = setOutputDevice(outputDesc, device, force, 0, NULL, address);
 
         // handle special case for sonification while in call
         if (isInCall()) {
@@ -1848,7 +1857,8 @@
             if (!streamsMatchForvolume(stream, (audio_stream_type_t)curStream)) {
                 continue;
             }
-            if (!desc->isStreamActive((audio_stream_type_t)curStream)) {
+            if (!(desc->isStreamActive((audio_stream_type_t)curStream) ||
+                    (isInCall() && (curStream == AUDIO_STREAM_VOICE_CALL)))) {
                 continue;
             }
             routing_strategy curStrategy = getStrategy((audio_stream_type_t)curStream);
@@ -2097,7 +2107,6 @@
                         && (patch->mPatch.sinks[0].ext.device.type == device)
                         && (strncmp(patch->mPatch.sinks[0].ext.device.address, address.string(),
                                 AUDIO_DEVICE_MAX_ADDRESS_LEN) == 0)) {
-
                     if (mPolicyMixes.registerMix(address, mixes[i], desc) != NO_ERROR) {
                         res = INVALID_OPERATION;
                     } else {
@@ -2976,7 +2985,7 @@
             return INVALID_OPERATION;
         }
         uint32_t delayMs = 0;
-        status = startSource(outputDesc, stream, sinkDevice, &delayMs);
+        status = startSource(outputDesc, stream, sinkDevice, NULL, &delayMs);
 
         if (status != NO_ERROR) {
             mpClientInterface->releaseAudioPatch(sourceDesc->mPatchDesc->mAfPatchHandle, 0);
@@ -3200,6 +3209,10 @@
             }
             sp<SwAudioOutputDescriptor> outputDesc = new SwAudioOutputDescriptor(outProfile,
                                                                                  mpClientInterface);
+            const DeviceVector &supportedDevices = outProfile->getSupportedDevices();
+            const DeviceVector &devicesForType = supportedDevices.getDevicesFromType(profileType);
+            String8 address = devicesForType.size() > 0 ? devicesForType.itemAt(0)->mAddress
+                    : String8("");
 
             outputDesc->mDevice = profileType;
             audio_config_t config = AUDIO_CONFIG_INITIALIZER;
@@ -3211,7 +3224,7 @@
                                                             &output,
                                                             &config,
                                                             &outputDesc->mDevice,
-                                                            String8(""),
+                                                            address,
                                                             &outputDesc->mLatency,
                                                             outputDesc->mFlags);
 
@@ -3224,7 +3237,6 @@
                 outputDesc->mChannelMask = config.channel_mask;
                 outputDesc->mFormat = config.format;
 
-                const DeviceVector &supportedDevices = outProfile->getSupportedDevices();
                 for (size_t k = 0; k  < supportedDevices.size(); k++) {
                     ssize_t index = mAvailableOutputDevices.indexOf(supportedDevices[k]);
                     // give a valid ID to an attached device once confirmed it is reachable
@@ -3239,7 +3251,10 @@
                 addOutput(output, outputDesc);
                 setOutputDevice(outputDesc,
                                 outputDesc->mDevice,
-                                true);
+                                true,
+                                0,
+                                NULL,
+                                address.string());
             }
         }
         // open input streams needed to access attached devices to validate
@@ -4614,9 +4629,13 @@
     if (device == AUDIO_DEVICE_NONE) {
         resetOutputDevice(outputDesc, delayMs, NULL);
     } else {
-        DeviceVector deviceList = (address == NULL) ?
-                mAvailableOutputDevices.getDevicesFromType(device)
-                : mAvailableOutputDevices.getDevicesFromTypeAddr(device, String8(address));
+        DeviceVector deviceList;
+        if ((address == NULL) || (strlen(address) == 0)) {
+            deviceList = mAvailableOutputDevices.getDevicesFromType(device);
+        } else {
+            deviceList = mAvailableOutputDevices.getDevicesFromTypeAddr(device, String8(address));
+        }
+
         if (!deviceList.isEmpty()) {
             struct audio_patch patch;
             outputDesc->toAudioPortConfig(&patch.sources[0]);
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index 0420679..94357b9 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -484,6 +484,7 @@
         status_t startSource(sp<AudioOutputDescriptor> outputDesc,
                              audio_stream_type_t stream,
                              audio_devices_t device,
+                             const char *address,
                              uint32_t *delayMs);
         status_t stopSource(sp<AudioOutputDescriptor> outputDesc,
                             audio_stream_type_t stream,
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index b6c9900..63e44fd 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -75,6 +75,7 @@
     Camera2ClientBase(cameraService, remoteCallback, clientPackageName,
                 cameraId, cameraFacing, clientPid, clientUid, servicePid),
     mInputStream(),
+    mStreamingRequestId(REQUEST_ID_NONE),
     mRequestIdCounter(0) {
 
     ATRACE_CALL();
@@ -233,7 +234,7 @@
             res = STATUS_ERROR(CameraService::ERROR_INVALID_OPERATION,
                     msg.string());
         } else {
-            mStreamingRequestList.push_back(submitInfo->mRequestId);
+            mStreamingRequestId = submitInfo->mRequestId;
         }
     } else {
         err = mDevice->captureList(metadataRequestList, &(submitInfo->mLastFrameNumber));
@@ -270,17 +271,9 @@
         return STATUS_ERROR(CameraService::ERROR_DISCONNECTED, "Camera device no longer alive");
     }
 
-    Vector<int>::iterator it, end;
-    for (it = mStreamingRequestList.begin(), end = mStreamingRequestList.end();
-         it != end; ++it) {
-        if (*it == requestId) {
-            break;
-        }
-    }
-
-    if (it == end) {
-        String8 msg = String8::format("Camera %d: Did not find request ID %d in list of "
-                "streaming requests", mCameraId, requestId);
+    if (mStreamingRequestId != requestId) {
+        String8 msg = String8::format("Camera %d: Canceling request ID %d doesn't match "
+                "current request ID %d", mCameraId, requestId, mStreamingRequestId);
         ALOGE("%s: %s", __FUNCTION__, msg.string());
         return STATUS_ERROR(CameraService::ERROR_ILLEGAL_ARGUMENT, msg.string());
     }
@@ -290,7 +283,7 @@
     if (err == OK) {
         ALOGV("%s: Camera %d: Successfully cleared streaming request",
               __FUNCTION__, mCameraId);
-        mStreamingRequestList.erase(it);
+        mStreamingRequestId = REQUEST_ID_NONE;
     } else {
         res = STATUS_ERROR_FMT(CameraService::ERROR_INVALID_OPERATION,
                 "Camera %d: Error clearing streaming request: %s (%d)",
@@ -767,7 +760,7 @@
     }
 
     // FIXME: Also need check repeating burst.
-    if (!mStreamingRequestList.isEmpty()) {
+    if (mStreamingRequestId != REQUEST_ID_NONE) {
         String8 msg = String8::format(
             "Camera %d: Try to waitUntilIdle when there are active streaming requests",
             mCameraId);
@@ -799,7 +792,7 @@
         return STATUS_ERROR(CameraService::ERROR_DISCONNECTED, "Camera device no longer alive");
     }
 
-    mStreamingRequestList.clear();
+    mStreamingRequestId = REQUEST_ID_NONE;
     status_t err = mDevice->flush(lastFrameNumber);
     if (err != OK) {
         res = STATUS_ERROR_FMT(CameraService::ERROR_INVALID_OPERATION,
@@ -982,6 +975,17 @@
     }
 }
 
+void CameraDeviceClient::notifyRepeatingRequestError(long lastFrameNumber) {
+    sp<hardware::camera2::ICameraDeviceCallbacks> remoteCb = getRemoteCallback();
+
+    if (remoteCb != 0) {
+        remoteCb->onRepeatingRequestError(lastFrameNumber);
+    }
+
+    Mutex::Autolock icl(mBinderSerializationLock);
+    mStreamingRequestId = REQUEST_ID_NONE;
+}
+
 void CameraDeviceClient::notifyIdle() {
     // Thread safe. Don't bother locking.
     sp<hardware::camera2::ICameraDeviceCallbacks> remoteCb = getRemoteCallback();
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h
index 38137a2..3660a18 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.h
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h
@@ -160,6 +160,7 @@
                              const CaptureResultExtras& resultExtras);
     virtual void notifyShutter(const CaptureResultExtras& resultExtras, nsecs_t timestamp);
     virtual void notifyPrepared(int streamId);
+    virtual void notifyRepeatingRequestError(long lastFrameNumber);
 
     /**
      * Interface used by independent components of CameraDeviceClient.
@@ -205,8 +206,9 @@
         int32_t id;
     } mInputStream;
 
-    // Request ID
-    Vector<int> mStreamingRequestList;
+    // Streaming request ID
+    int32_t mStreamingRequestId;
+    static const int32_t REQUEST_ID_NONE = -1;
 
     int32_t mRequestIdCounter;
 
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.cpp b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
index 2cc150d..c0d6da6 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.cpp
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.cpp
@@ -306,6 +306,14 @@
 }
 
 template <typename TClientBase>
+void Camera2ClientBase<TClientBase>::notifyRepeatingRequestError(long lastFrameNumber) {
+    (void)lastFrameNumber;
+
+    ALOGV("%s: Repeating request was stopped. Last frame number is %ld",
+            __FUNCTION__, lastFrameNumber);
+}
+
+template <typename TClientBase>
 int Camera2ClientBase<TClientBase>::getCameraId() const {
     return TClientBase::mCameraId;
 }
diff --git a/services/camera/libcameraservice/common/Camera2ClientBase.h b/services/camera/libcameraservice/common/Camera2ClientBase.h
index 6eea2f4..4f60034 100644
--- a/services/camera/libcameraservice/common/Camera2ClientBase.h
+++ b/services/camera/libcameraservice/common/Camera2ClientBase.h
@@ -73,6 +73,7 @@
     virtual void          notifyAutoWhitebalance(uint8_t newState,
                                                  int triggerId);
     virtual void          notifyPrepared(int streamId);
+    virtual void          notifyRepeatingRequestError(long lastFrameNumber);
 
     int                   getCameraId() const;
     const sp<CameraDeviceBase>&
diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h
index d570d4b..35ec531 100644
--- a/services/camera/libcameraservice/common/CameraDeviceBase.h
+++ b/services/camera/libcameraservice/common/CameraDeviceBase.h
@@ -209,6 +209,7 @@
         virtual void notifyAutoExposure(uint8_t newState, int triggerId) = 0;
         virtual void notifyAutoWhitebalance(uint8_t newState,
                 int triggerId) = 0;
+        virtual void notifyRepeatingRequestError(long lastFrameNumber) = 0;
       protected:
         virtual ~NotificationListener();
     };
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index e395935..0de80bd 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -2807,6 +2807,11 @@
 
 status_t Camera3Device::RequestThread::clearRepeatingRequests(/*out*/int64_t *lastFrameNumber) {
     Mutex::Autolock l(mRequestLock);
+    return clearRepeatingRequestsLocked(lastFrameNumber);
+
+}
+
+status_t Camera3Device::RequestThread::clearRepeatingRequestsLocked(/*out*/int64_t *lastFrameNumber) {
     mRepeatingRequests.clear();
     if (lastFrameNumber != NULL) {
         *lastFrameNumber = mRepeatingLastFrameNumber;
@@ -2961,6 +2966,22 @@
     }
 }
 
+void Camera3Device::RequestThread::checkAndStopRepeatingRequest() {
+    Mutex::Autolock l(mRequestLock);
+    // Check all streams needed by repeating requests are still valid. Otherwise, stop
+    // repeating requests.
+    for (const auto& request : mRepeatingRequests) {
+        for (const auto& s : request->mOutputStreams) {
+            if (s->isAbandoned()) {
+                int64_t lastFrameNumber = 0;
+                clearRepeatingRequestsLocked(&lastFrameNumber);
+                mListener->notifyRepeatingRequestError(lastFrameNumber);
+                return;
+            }
+        }
+    }
+}
+
 bool Camera3Device::RequestThread::threadLoop() {
     ATRACE_CALL();
     status_t res;
@@ -2992,6 +3013,8 @@
     if (res == TIMED_OUT) {
         // Not a fatal error if getting output buffers time out.
         cleanUpFailedRequests(/*sendRequestError*/ true);
+        // Check if any stream is abandoned.
+        checkAndStopRepeatingRequest();
         return true;
     } else if (res != OK) {
         cleanUpFailedRequests(/*sendRequestError*/ false);
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index 349fb4c..0366ef6 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -565,6 +565,9 @@
         // ERROR state to mark them as not having valid data. mNextRequests will be cleared.
         void cleanUpFailedRequests(bool sendRequestError);
 
+        // Stop the repeating request if any of its output streams is abandoned.
+        void checkAndStopRepeatingRequest();
+
         // Pause handling
         bool               waitIfPaused();
         void               unpauseForNewRequests();
@@ -578,6 +581,9 @@
         // Handle AE precapture trigger cancel for devices <= CAMERA_DEVICE_API_VERSION_3_2.
         void handleAePrecaptureCancelRequest(sp<CaptureRequest> request);
 
+        // Clear repeating requests. Must be called with mRequestLock held.
+        status_t clearRepeatingRequestsLocked(/*out*/ int64_t *lastFrameNumber = NULL);
+
         wp<Camera3Device>  mParent;
         wp<camera3::StatusTracker>  mStatusTracker;
         camera3_device_t  *mHal3Device;
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
index 1e6452f..d2b98e6 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
@@ -157,6 +157,13 @@
         if (res != OK) {
             ALOGE("%s: Stream %d: Can't dequeue next output buffer: %s (%d)",
                     __FUNCTION__, mId, strerror(-res), res);
+
+            // Only transition to STATE_ABANDONED from STATE_CONFIGURED. (If it is STATE_PREPARING,
+            // let prepareNextBuffer handle the error.)
+            if (res == NO_INIT && mState == STATE_CONFIGURED) {
+                mState = STATE_ABANDONED;
+            }
+
             return res;
         }
     }
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index 80dce84..96d62d4 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -323,6 +323,11 @@
     return mState == STATE_PREPARING;
 }
 
+bool Camera3Stream::isAbandoned() const {
+    Mutex::Autolock l(mLock);
+    return mState == STATE_ABANDONED;
+}
+
 status_t Camera3Stream::prepareNextBuffer() {
     ATRACE_CALL();
 
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h
index 810383d..0755700 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.h
+++ b/services/camera/libcameraservice/device3/Camera3Stream.h
@@ -95,6 +95,8 @@
  *    STATE_PREPARING      => STATE_CONFIGURED:
  *        When sufficient prepareNextBuffer calls have been made to allocate
  *        all stream buffers, or cancelPrepare is called.
+ *    STATE_CONFIGURED     => STATE_ABANDONED:
+ *        When the buffer queue of the stream is abandoned.
  *
  * Status Tracking:
  *    Each stream is tracked by StatusTracker as a separate component,
@@ -353,6 +355,11 @@
     void             removeBufferListener(
             const sp<Camera3StreamBufferListener>& listener);
 
+    /**
+     * Return if the buffer queue of the stream is abandoned.
+     */
+    bool             isAbandoned() const;
+
   protected:
     const int mId;
     /**
@@ -380,7 +387,8 @@
         STATE_IN_CONFIG,
         STATE_IN_RECONFIG,
         STATE_CONFIGURED,
-        STATE_PREPARING
+        STATE_PREPARING,
+        STATE_ABANDONED
     } mState;
 
     mutable Mutex mLock;
diff --git a/services/camera/libcameraservice/device3/Camera3StreamInterface.h b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
index 3f7e7a7..6cb7a54 100644
--- a/services/camera/libcameraservice/device3/Camera3StreamInterface.h
+++ b/services/camera/libcameraservice/device3/Camera3StreamInterface.h
@@ -262,6 +262,11 @@
     virtual status_t disconnect() = 0;
 
     /**
+     * Return if the buffer queue of the stream is abandoned.
+     */
+    virtual bool isAbandoned() const = 0;
+
+    /**
      * Debug dump of the stream's state.
      */
     virtual void     dump(int fd, const Vector<String16> &args) const = 0;