Merge "Enable hdcp for WiFi display" into jb-mr1-dev
diff --git a/include/media/stagefright/MediaCodec.h b/include/media/stagefright/MediaCodec.h
index 8c612d4..cacfa54 100644
--- a/include/media/stagefright/MediaCodec.h
+++ b/include/media/stagefright/MediaCodec.h
@@ -108,6 +108,11 @@
 
     status_t requestIDRFrame();
 
+    // Notification will be posted once there "is something to do", i.e.
+    // an input/output buffer has become available, a format change is
+    // pending, an error is pending.
+    void requestActivityNotification(const sp<AMessage> &notify);
+
 protected:
     virtual ~MediaCodec();
     virtual void onMessageReceived(const sp<AMessage> &msg);
@@ -132,22 +137,23 @@
     };
 
     enum {
-        kWhatInit                       = 'init',
-        kWhatConfigure                  = 'conf',
-        kWhatStart                      = 'strt',
-        kWhatStop                       = 'stop',
-        kWhatRelease                    = 'rele',
-        kWhatDequeueInputBuffer         = 'deqI',
-        kWhatQueueInputBuffer           = 'queI',
-        kWhatDequeueOutputBuffer        = 'deqO',
-        kWhatReleaseOutputBuffer        = 'relO',
-        kWhatGetBuffers                 = 'getB',
-        kWhatFlush                      = 'flus',
-        kWhatGetOutputFormat            = 'getO',
-        kWhatDequeueInputTimedOut       = 'dITO',
-        kWhatDequeueOutputTimedOut      = 'dOTO',
-        kWhatCodecNotify                = 'codc',
-        kWhatRequestIDRFrame            = 'ridr',
+        kWhatInit                           = 'init',
+        kWhatConfigure                      = 'conf',
+        kWhatStart                          = 'strt',
+        kWhatStop                           = 'stop',
+        kWhatRelease                        = 'rele',
+        kWhatDequeueInputBuffer             = 'deqI',
+        kWhatQueueInputBuffer               = 'queI',
+        kWhatDequeueOutputBuffer            = 'deqO',
+        kWhatReleaseOutputBuffer            = 'relO',
+        kWhatGetBuffers                     = 'getB',
+        kWhatFlush                          = 'flus',
+        kWhatGetOutputFormat                = 'getO',
+        kWhatDequeueInputTimedOut           = 'dITO',
+        kWhatDequeueOutputTimedOut          = 'dOTO',
+        kWhatCodecNotify                    = 'codc',
+        kWhatRequestIDRFrame                = 'ridr',
+        kWhatRequestActivityNotification    = 'racN',
     };
 
     enum {
@@ -191,6 +197,8 @@
 
     List<sp<ABuffer> > mCSD;
 
+    sp<AMessage> mActivityNotify;
+
     MediaCodec(const sp<ALooper> &looper);
 
     static status_t PostAndAwaitResponse(
@@ -216,6 +224,8 @@
     status_t setNativeWindow(
             const sp<SurfaceTextureClient> &surfaceTextureClient);
 
+    void postActivityNotificationIfPossible();
+
     DISALLOW_EVIL_CONSTRUCTORS(MediaCodec);
 };
 
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 7f97430..56e6df0 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -333,6 +333,12 @@
     return OK;
 }
 
+void MediaCodec::requestActivityNotification(const sp<AMessage> &notify) {
+    sp<AMessage> msg = new AMessage(kWhatRequestActivityNotification, id());
+    msg->setMessage("notify", notify);
+    msg->post();
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 
 void MediaCodec::cancelPendingDequeueOperations() {
@@ -498,6 +504,7 @@
                             sendErrorReponse = false;
 
                             mFlags |= kFlagStickyError;
+                            postActivityNotificationIfPossible();
 
                             cancelPendingDequeueOperations();
                             break;
@@ -508,6 +515,7 @@
                             sendErrorReponse = false;
 
                             mFlags |= kFlagStickyError;
+                            postActivityNotificationIfPossible();
                             break;
                         }
                     }
@@ -600,6 +608,7 @@
                             (new AMessage)->postReply(mReplyID);
                         } else {
                             mFlags |= kFlagOutputBuffersChanged;
+                            postActivityNotificationIfPossible();
                         }
                     }
                     break;
@@ -638,6 +647,7 @@
 
                     mOutputFormat = msg;
                     mFlags |= kFlagOutputFormatChanged;
+                    postActivityNotificationIfPossible();
                     break;
                 }
 
@@ -669,6 +679,8 @@
                                   err);
 
                             mFlags |= kFlagStickyError;
+                            postActivityNotificationIfPossible();
+
                             cancelPendingDequeueOperations();
                         }
                         break;
@@ -680,6 +692,8 @@
                         ++mDequeueInputTimeoutGeneration;
                         mFlags &= ~kFlagDequeueInputPending;
                         mDequeueInputReplyID = 0;
+                    } else {
+                        postActivityNotificationIfPossible();
                     }
                     break;
                 }
@@ -709,7 +723,10 @@
                         ++mDequeueOutputTimeoutGeneration;
                         mFlags &= ~kFlagDequeueOutputPending;
                         mDequeueOutputReplyID = 0;
+                    } else {
+                        postActivityNotificationIfPossible();
                     }
+
                     break;
                 }
 
@@ -1145,6 +1162,15 @@
             break;
         }
 
+        case kWhatRequestActivityNotification:
+        {
+            CHECK(mActivityNotify == NULL);
+            CHECK(msg->findMessage("notify", &mActivityNotify));
+
+            postActivityNotificationIfPossible();
+            break;
+        }
+
         default:
             TRESPASS();
     }
@@ -1210,6 +1236,8 @@
         mFlags &= ~kFlagOutputFormatChanged;
         mFlags &= ~kFlagOutputBuffersChanged;
         mFlags &= ~kFlagStickyError;
+
+        mActivityNotify.clear();
     }
 
     mState = newState;
@@ -1477,4 +1505,19 @@
     return OK;
 }
 
+void MediaCodec::postActivityNotificationIfPossible() {
+    if (mActivityNotify == NULL) {
+        return;
+    }
+
+    if ((mFlags & (kFlagStickyError
+                    | kFlagOutputBuffersChanged
+                    | kFlagOutputFormatChanged))
+            || !mAvailPortBuffers[kPortIndexInput].empty()
+            || !mAvailPortBuffers[kPortIndexOutput].empty()) {
+        mActivityNotify->post();
+        mActivityNotify.clear();
+    }
+}
+
 }  // namespace android
diff --git a/media/libstagefright/wifi-display/source/Converter.cpp b/media/libstagefright/wifi-display/source/Converter.cpp
index 6f336c7..0e8c9af 100644
--- a/media/libstagefright/wifi-display/source/Converter.cpp
+++ b/media/libstagefright/wifi-display/source/Converter.cpp
@@ -128,7 +128,7 @@
     mOutputFormat->setString("mime", outputMIME.c_str());
 
     int32_t audioBitrate = getBitrate("media.wfd.audio-bitrate", 128000);
-    int32_t videoBitrate = getBitrate("media.wfd.video-bitrate", 5000000);
+    int32_t videoBitrate = getBitrate("media.wfd.video-bitrate", 2500000);
 
     ALOGI("using audio bitrate of %d bps, video bitrate of %d bps",
           audioBitrate, videoBitrate);
@@ -242,16 +242,18 @@
 #if ENABLE_SILENCE_DETECTION
                 if (!mIsVideo) {
                     if (IsSilence(accessUnit)) {
-                        if (!mInSilentMode) {
-                            int64_t nowUs = ALooper::GetNowUs();
+                        if (mInSilentMode) {
+                            break;
+                        }
 
-                            if (mFirstSilentFrameUs < 0ll) {
-                                mFirstSilentFrameUs = nowUs;
-                            } else if (nowUs >= mFirstSilentFrameUs + 1000000ll) {
-                                mInSilentMode = true;
-                                ALOGI("audio in silent mode now.");
-                                break;
-                            }
+                        int64_t nowUs = ALooper::GetNowUs();
+
+                        if (mFirstSilentFrameUs < 0ll) {
+                            mFirstSilentFrameUs = nowUs;
+                        } else if (nowUs >= mFirstSilentFrameUs + 10000000ll) {
+                            mInSilentMode = true;
+                            ALOGI("audio in silent mode now.");
+                            break;
                         }
                     } else {
                         if (mInSilentMode) {
@@ -272,8 +274,17 @@
             break;
         }
 
-        case kWhatDoMoreWork:
+        case kWhatEncoderActivity:
         {
+#if 0
+            int64_t whenUs;
+            if (msg->findInt64("whenUs", &whenUs)) {
+                int64_t nowUs = ALooper::GetNowUs();
+                ALOGI("[%s] kWhatEncoderActivity after %lld us",
+                      mIsVideo ? "video" : "audio", nowUs - whenUs);
+            }
+#endif
+
             mDoMoreWorkPending = false;
 
             if (mEncoder == NULL) {
@@ -326,7 +337,17 @@
     }
 
     mDoMoreWorkPending = true;
-    (new AMessage(kWhatDoMoreWork, id()))->post(mIsVideo ? 10000ll : 5000ll);
+
+#if 1
+    if (mEncoderActivityNotify == NULL) {
+        mEncoderActivityNotify = new AMessage(kWhatEncoderActivity, id());
+    }
+    mEncoder->requestActivityNotification(mEncoderActivityNotify->dup());
+#else
+    sp<AMessage> notify = new AMessage(kWhatEncoderActivity, id());
+    notify->setInt64("whenUs", ALooper::GetNowUs());
+    mEncoder->requestActivityNotification(notify);
+#endif
 }
 
 status_t Converter::feedEncoderInputBuffers() {
@@ -373,15 +394,23 @@
 }
 
 status_t Converter::doMoreWork() {
-    size_t bufferIndex;
-    status_t err = mEncoder->dequeueInputBuffer(&bufferIndex);
-
-    if (err == OK) {
-        mAvailEncoderInputIndices.push_back(bufferIndex);
-        feedEncoderInputBuffers();
-    }
+    status_t err;
 
     for (;;) {
+        size_t bufferIndex;
+        err = mEncoder->dequeueInputBuffer(&bufferIndex);
+
+        if (err != OK) {
+            break;
+        }
+
+        mAvailEncoderInputIndices.push_back(bufferIndex);
+    }
+
+    feedEncoderInputBuffers();
+
+    for (;;) {
+        size_t bufferIndex;
         size_t offset;
         size_t size;
         int64_t timeUs;
@@ -404,9 +433,8 @@
             sp<ABuffer> buffer = new ABuffer(size);
             buffer->meta()->setInt64("timeUs", timeUs);
 
-            if (!mIsVideo) {
-                ALOGV("audio time %lld us (%.2f secs)", timeUs, timeUs / 1E6);
-            }
+            ALOGV("[%s] time %lld us (%.2f secs)",
+                  mIsVideo ? "video" : "audio", timeUs, timeUs / 1E6);
 
             memcpy(buffer->data(),
                    mEncoderOutputBuffers.itemAt(bufferIndex)->base() + offset,
diff --git a/media/libstagefright/wifi-display/source/Converter.h b/media/libstagefright/wifi-display/source/Converter.h
index 93ff72f..8d45395 100644
--- a/media/libstagefright/wifi-display/source/Converter.h
+++ b/media/libstagefright/wifi-display/source/Converter.h
@@ -58,6 +58,7 @@
         kWhatRequestIDRFrame,
         kWhatShutdown,
         kWhatMediaPullerNotify,
+        kWhatEncoderActivity,
     };
 
     void shutdownAsync();
@@ -75,6 +76,7 @@
     sp<AMessage> mOutputFormat;
 
     sp<MediaCodec> mEncoder;
+    sp<AMessage> mEncoderActivityNotify;
 
     Vector<sp<ABuffer> > mEncoderInputBuffers;
     Vector<sp<ABuffer> > mEncoderOutputBuffers;
diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.cpp b/media/libstagefright/wifi-display/source/PlaybackSession.cpp
index c91b4c8..ffdafed 100644
--- a/media/libstagefright/wifi-display/source/PlaybackSession.cpp
+++ b/media/libstagefright/wifi-display/source/PlaybackSession.cpp
@@ -919,13 +919,22 @@
 
     source->setUseAbsoluteTimestamps();
 
+#if 1
     sp<RepeaterSource> videoSource =
         new RepeaterSource(source, 30.0 /* rateHz */);
+#endif
 
+#if 1
     size_t numInputBuffers;
     status_t err = addSource(
             true /* isVideo */, videoSource, true /* isRepeaterSource */,
             &numInputBuffers);
+#else
+    size_t numInputBuffers;
+    status_t err = addSource(
+            true /* isVideo */, source, false /* isRepeaterSource */,
+            &numInputBuffers);
+#endif
 
     if (err != OK) {
         return err;
diff --git a/services/camera/libcameraservice/Camera2Client.cpp b/services/camera/libcameraservice/Camera2Client.cpp
index 1c650f6..948b59f 100644
--- a/services/camera/libcameraservice/Camera2Client.cpp
+++ b/services/camera/libcameraservice/Camera2Client.cpp
@@ -47,9 +47,10 @@
         const sp<ICameraClient>& cameraClient,
         int cameraId,
         int cameraFacing,
-        int clientPid):
+        int clientPid,
+        int servicePid):
         Client(cameraService, cameraClient,
-                cameraId, cameraFacing, clientPid),
+                cameraId, cameraFacing, clientPid, servicePid),
         mSharedCameraClient(cameraClient),
         mParameters(cameraId, cameraFacing)
 {
@@ -64,10 +65,10 @@
 
 status_t Camera2Client::checkPid(const char* checkLocation) const {
     int callingPid = getCallingPid();
-    if (callingPid == mClientPid) return NO_ERROR;
+    if (callingPid == mClientPid || callingPid == mServicePid) return NO_ERROR;
 
     ALOGE("%s: attempt to use a locked camera from a different process"
-            " (old pid %d, new pid %d)", checkLocation, mClientPid, callingPid);
+            " (old pid %d, new pid %d, servicePid %d)", checkLocation, mClientPid, callingPid, mServicePid);
     return PERMISSION_DENIED;
 }
 
@@ -138,8 +139,15 @@
 
     mDestructionStarted = true;
 
-    SharedParameters::Lock l(mParameters);
-    if (l.mParameters.state != Parameters::DISCONNECTED) {
+    Parameters::State state;
+    // warning:
+    //   holding on to locks more than necessary may be hazardous to your health
+    {
+        SharedParameters::Lock l(mParameters);
+        state = l.mParameters.state;
+    }
+
+    if (state != Parameters::DISCONNECTED) {
         // Rewrite mClientPid to allow shutdown by CameraService
         mClientPid = getCallingPid();
         disconnect();
@@ -315,6 +323,21 @@
             getCaptureStreamId());
     result.appendFormat("    Recording stream ID: %d\n",
             getRecordingStreamId());
+
+    result.append("  Quirks for this camera:\n");
+    bool haveQuirk = false;
+    if (p.quirks.triggerAfWithAuto) {
+        result.appendFormat("    triggerAfWithAuto\n");
+        haveQuirk = true;
+    }
+    if (p.quirks.useZslFormat) {
+        result.appendFormat("    useZslFormat\n");
+        haveQuirk = true;
+    }
+    if (!haveQuirk) {
+        result.appendFormat("    none\n");
+    }
+
     write(fd, result.string(), result.size());
 
     mStreamingProcessor->dump(fd, args);
@@ -404,9 +427,6 @@
     mCameraClient = client;
     mSharedCameraClient = client;
 
-    SharedParameters::Lock l(mParameters);
-    l.mParameters.state = Parameters::STOPPED;
-
     return OK;
 }
 
@@ -588,6 +608,9 @@
 status_t Camera2Client::startPreviewL(Parameters &params, bool restart) {
     ATRACE_CALL();
     status_t res;
+
+    ALOGV("%s: state == %d, restart = %d", __FUNCTION__, params.state, restart);
+
     if (params.state == Parameters::PREVIEW && !restart) {
         // Succeed attempt to re-enter preview state
         ALOGI("%s: Not starting preview; already in preview state.",
@@ -777,6 +800,9 @@
 
 status_t Camera2Client::startRecordingL(Parameters &params, bool restart) {
     status_t res;
+
+    ALOGV("%s: state == %d, restart = %d", __FUNCTION__, params.state, restart);
+
     switch (params.state) {
         case Parameters::STOPPED:
             res = startPreviewL(params, false);
@@ -1415,6 +1441,8 @@
 status_t Camera2Client::updateRequests(Parameters &params) {
     status_t res;
 
+    ALOGV("%s: Camera %d: state = %d", __FUNCTION__, getCameraId(), params.state);
+
     res = mStreamingProcessor->updatePreviewRequest(params);
     if (res != OK) {
         ALOGE("%s: Camera %d: Unable to update preview request: %s (%d)",
diff --git a/services/camera/libcameraservice/Camera2Client.h b/services/camera/libcameraservice/Camera2Client.h
index 3a9d307..fb1dcde 100644
--- a/services/camera/libcameraservice/Camera2Client.h
+++ b/services/camera/libcameraservice/Camera2Client.h
@@ -74,7 +74,8 @@
             const sp<ICameraClient>& cameraClient,
             int cameraId,
             int cameraFacing,
-            int clientPid);
+            int clientPid,
+            int servicePid);
     virtual ~Camera2Client();
 
     status_t initialize(camera_module_t *module);
diff --git a/services/camera/libcameraservice/Camera2Device.cpp b/services/camera/libcameraservice/Camera2Device.cpp
index 2e4098e..25b7a58 100644
--- a/services/camera/libcameraservice/Camera2Device.cpp
+++ b/services/camera/libcameraservice/Camera2Device.cpp
@@ -42,6 +42,7 @@
 Camera2Device::~Camera2Device()
 {
     ATRACE_CALL();
+    ALOGV("%s: Tearing down for camera id %d", __FUNCTION__, mId);
     disconnect();
 }
 
@@ -1114,9 +1115,17 @@
     if (mState >= CONNECTED) {
         res = native_window_api_disconnect(mConsumerInterface.get(),
                 NATIVE_WINDOW_API_CAMERA);
-        if (res != OK) {
-            ALOGE("%s: Unable to disconnect stream %d from native window",
-                    __FUNCTION__, mId);
+
+        /* this is not an error. if client calling process dies,
+           the window will also die and all calls to it will return
+           DEAD_OBJECT, thus it's already "disconnected" */
+        if (res == DEAD_OBJECT) {
+            ALOGW("%s: While disconnecting stream %d from native window, the"
+                  " native window died from under us", __FUNCTION__, mId);
+        }
+        else if (res != OK) {
+            ALOGE("%s: Unable to disconnect stream %d from native window (error %d %s)",
+                    __FUNCTION__, mId, res, strerror(-res));
             return res;
         }
     }
diff --git a/services/camera/libcameraservice/CameraClient.cpp b/services/camera/libcameraservice/CameraClient.cpp
index c9c816a..5b59ef9 100644
--- a/services/camera/libcameraservice/CameraClient.cpp
+++ b/services/camera/libcameraservice/CameraClient.cpp
@@ -40,9 +40,9 @@
 
 CameraClient::CameraClient(const sp<CameraService>& cameraService,
         const sp<ICameraClient>& cameraClient,
-        int cameraId, int cameraFacing, int clientPid):
+        int cameraId, int cameraFacing, int clientPid, int servicePid):
         Client(cameraService, cameraClient,
-                cameraId, cameraFacing, clientPid)
+                cameraId, cameraFacing, clientPid, servicePid)
 {
     int callingPid = getCallingPid();
     LOG1("CameraClient::CameraClient E (pid %d, id %d)", callingPid, cameraId);
@@ -124,7 +124,7 @@
 
 status_t CameraClient::checkPid() const {
     int callingPid = getCallingPid();
-    if (callingPid == mClientPid) return NO_ERROR;
+    if (callingPid == mClientPid || callingPid == mServicePid) return NO_ERROR;
 
     ALOGW("attempt to use a locked camera from a different process"
          " (old pid %d, new pid %d)", mClientPid, callingPid);
diff --git a/services/camera/libcameraservice/CameraClient.h b/services/camera/libcameraservice/CameraClient.h
index 256298d..2f31c4e 100644
--- a/services/camera/libcameraservice/CameraClient.h
+++ b/services/camera/libcameraservice/CameraClient.h
@@ -55,7 +55,8 @@
             const sp<ICameraClient>& cameraClient,
             int cameraId,
             int cameraFacing,
-            int clientPid);
+            int clientPid,
+            int servicePid);
     ~CameraClient();
 
     status_t initialize(camera_module_t *module);
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 878afde..4d48d8d 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -196,11 +196,11 @@
     switch(deviceVersion) {
       case CAMERA_DEVICE_API_VERSION_1_0:
         client = new CameraClient(this, cameraClient, cameraId,
-                info.facing, callingPid);
+                info.facing, callingPid, getpid());
         break;
       case CAMERA_DEVICE_API_VERSION_2_0:
         client = new Camera2Client(this, cameraClient, cameraId,
-                info.facing, callingPid);
+                info.facing, callingPid, getpid());
         break;
       default:
         ALOGE("Unknown camera device HAL version: %d", deviceVersion);
@@ -211,8 +211,10 @@
         return NULL;
     }
 
+    cameraClient->asBinder()->linkToDeath(this);
+
     mClient[cameraId] = client;
-    LOG1("CameraService::connect X (id %d)", cameraId);
+    LOG1("CameraService::connect X (id %d, this pid is %d)", cameraId, getpid());
     return client;
 }
 
@@ -220,12 +222,29 @@
     int callingPid = getCallingPid();
     LOG1("CameraService::removeClient E (pid %d)", callingPid);
 
-    for (int i = 0; i < mNumberOfCameras; i++) {
-        // Declare this before the lock to make absolutely sure the
-        // destructor won't be called with the lock held.
-        sp<Client> client;
+    // Declare this before the lock to make absolutely sure the
+    // destructor won't be called with the lock held.
+    Mutex::Autolock lock(mServiceLock);
 
-        Mutex::Autolock lock(mServiceLock);
+    int outIndex;
+    sp<Client> client = findClientUnsafe(cameraClient, outIndex);
+
+    if (client != 0) {
+        // Found our camera, clear and leave.
+        LOG1("removeClient: clear camera %d", outIndex);
+        mClient[outIndex].clear();
+
+        client->unlinkToDeath(this);
+    }
+
+    LOG1("CameraService::removeClient X (pid %d)", callingPid);
+}
+
+sp<CameraService::Client> CameraService::findClientUnsafe(
+                        const sp<ICameraClient>& cameraClient, int& outIndex) {
+    sp<Client> client;
+
+    for (int i = 0; i < mNumberOfCameras; i++) {
 
         // This happens when we have already disconnected (or this is
         // just another unused camera).
@@ -235,20 +254,21 @@
         // Client::~Client() -> disconnect() -> removeClient().
         client = mClient[i].promote();
 
-        if (client == 0) {
+        // Clean up stale client entry
+        if (client == NULL) {
             mClient[i].clear();
             continue;
         }
 
         if (cameraClient->asBinder() == client->getCameraClient()->asBinder()) {
-            // Found our camera, clear and leave.
-            LOG1("removeClient: clear camera %d", i);
-            mClient[i].clear();
-            break;
+            // Found our camera
+            outIndex = i;
+            return client;
         }
     }
 
-    LOG1("CameraService::removeClient X (pid %d)", callingPid);
+    outIndex = -1;
+    return NULL;
 }
 
 CameraService::Client* CameraService::getClientByIdUnsafe(int cameraId) {
@@ -261,6 +281,21 @@
     return &mClientLock[cameraId];
 }
 
+/*virtual*/sp<CameraService::Client> CameraService::getClientByRemote(
+                                const sp<ICameraClient>& cameraClient) {
+
+    // Declare this before the lock to make absolutely sure the
+    // destructor won't be called with the lock held.
+    sp<Client> client;
+
+    Mutex::Autolock lock(mServiceLock);
+
+    int outIndex;
+    client = findClientUnsafe(cameraClient, outIndex);
+
+    return client;
+}
+
 status_t CameraService::onTransact(
     uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
     // Permission checks
@@ -292,10 +327,14 @@
 // the hardware first.
 void CameraService::setCameraBusy(int cameraId) {
     android_atomic_write(1, &mBusy[cameraId]);
+
+    ALOGV("setCameraBusy cameraId=%d", cameraId);
 }
 
 void CameraService::setCameraFree(int cameraId) {
     android_atomic_write(0, &mBusy[cameraId]);
+
+    ALOGV("setCameraFree cameraId=%d", cameraId);
 }
 
 // We share the media players for shutter and recording sound for all clients.
@@ -350,7 +389,7 @@
 
 CameraService::Client::Client(const sp<CameraService>& cameraService,
         const sp<ICameraClient>& cameraClient,
-        int cameraId, int cameraFacing, int clientPid) {
+        int cameraId, int cameraFacing, int clientPid, int servicePid) {
     int callingPid = getCallingPid();
     LOG1("Client::Client E (pid %d, id %d)", callingPid, cameraId);
 
@@ -359,6 +398,7 @@
     mCameraId = cameraId;
     mCameraFacing = cameraFacing;
     mClientPid = clientPid;
+    mServicePid = servicePid;
     mDestructionStarted = false;
 
     cameraService->setCameraBusy(cameraId);
@@ -514,4 +554,26 @@
     return NO_ERROR;
 }
 
+/*virtual*/void CameraService::binderDied(
+    const wp<IBinder> &who) {
+
+    ALOGV("java clients' binder died");
+
+    sp<IBinder> whoStrong = who.promote();
+
+    if (whoStrong == 0) {
+        ALOGV("java clients' binder death already cleaned up (normal case)");
+        return;
+    }
+
+    sp<ICameraClient> iCamClient = interface_cast<ICameraClient>(whoStrong);
+
+    sp<Client> cameraClient = getClientByRemote(iCamClient);
+    ALOGW("Disconnecting camera client %p since the binder for it "
+          "died (this pid %d)", cameraClient.get(), getCallingPid());
+
+    cameraClient->disconnect();
+
+}
+
 }; // namespace android
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 630fca7..f1e7df6 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -34,7 +34,8 @@
 
 class CameraService :
     public BinderService<CameraService>,
-    public BnCameraService
+    public BnCameraService,
+    public IBinder::DeathRecipient
 {
     friend class BinderService<CameraService>;
 public:
@@ -54,6 +55,8 @@
     virtual Client*     getClientByIdUnsafe(int cameraId);
     virtual Mutex*      getClientLockById(int cameraId);
 
+    virtual sp<Client>  getClientByRemote(const sp<ICameraClient>& cameraClient);
+
     virtual status_t    dump(int fd, const Vector<String16>& args);
     virtual status_t    onTransact(uint32_t code, const Parcel& data,
                                    Parcel* reply, uint32_t flags);
@@ -100,7 +103,8 @@
                 const sp<ICameraClient>& cameraClient,
                 int cameraId,
                 int cameraFacing,
-                int clientPid);
+                int clientPid,
+                int servicePid);
         ~Client();
 
         // return our camera client
@@ -128,6 +132,7 @@
         int                             mCameraId;       // immutable after constructor
         int                             mCameraFacing;   // immutable after constructor
         pid_t                           mClientPid;
+        pid_t                           mServicePid;     // immutable after constructor
 
     };
 
@@ -137,6 +142,9 @@
     Mutex               mClientLock[MAX_CAMERAS]; // prevent Client destruction inside callbacks
     int                 mNumberOfCameras;
 
+    // needs to be called with mServiceLock held
+    sp<Client>          findClientUnsafe(const sp<ICameraClient>& cameraClient, int& outIndex);
+
     // atomics to record whether the hardware is allocated to some client.
     volatile int32_t    mBusy[MAX_CAMERAS];
     void                setCameraBusy(int cameraId);
@@ -150,6 +158,9 @@
     int                 mSoundRef;  // reference count (release all MediaPlayer when 0)
 
     camera_module_t *mModule;
+
+    // IBinder::DeathRecipient implementation
+    virtual void binderDied(const wp<IBinder> &who);
 };
 
 } // namespace android
diff --git a/services/camera/libcameraservice/camera2/CallbackProcessor.cpp b/services/camera/libcameraservice/camera2/CallbackProcessor.cpp
index ede97a6..3e9c255 100644
--- a/services/camera/libcameraservice/camera2/CallbackProcessor.cpp
+++ b/services/camera/libcameraservice/camera2/CallbackProcessor.cpp
@@ -86,6 +86,8 @@
             // Since size should only change while preview is not running,
             // assuming that all existing use of old callback stream is
             // completed.
+            ALOGV("%s: Camera %d: Deleting stream %d since the buffer dimensions changed",
+                __FUNCTION__, client->getCameraId(), mCallbackStreamId);
             res = device->deleteStream(mCallbackStreamId);
             if (res != OK) {
                 ALOGE("%s: Camera %d: Unable to delete old output stream "
diff --git a/services/camera/libcameraservice/camera2/CaptureSequencer.cpp b/services/camera/libcameraservice/camera2/CaptureSequencer.cpp
index c9498c2..98aa380 100644
--- a/services/camera/libcameraservice/camera2/CaptureSequencer.cpp
+++ b/services/camera/libcameraservice/camera2/CaptureSequencer.cpp
@@ -289,13 +289,6 @@
     client->registerFrameListener(mCaptureId,
             this);
 
-    res = client->getCameraDevice()->clearStreamingRequest();
-    if (res != OK) {
-        ALOGE("%s: Camera %d: Unable to stop preview for ZSL capture: "
-                "%s (%d)",
-                __FUNCTION__, client->getCameraId(), strerror(-res), res);
-        return DONE;
-    }
     // TODO: Actually select the right thing here.
     res = processor->pushToReprocess(mCaptureId);
     if (res != OK) {
diff --git a/services/camera/libcameraservice/camera2/JpegProcessor.cpp b/services/camera/libcameraservice/camera2/JpegProcessor.cpp
index 7b368fa..a353679 100644
--- a/services/camera/libcameraservice/camera2/JpegProcessor.cpp
+++ b/services/camera/libcameraservice/camera2/JpegProcessor.cpp
@@ -107,6 +107,8 @@
         }
         if (currentWidth != (uint32_t)params.pictureWidth ||
                 currentHeight != (uint32_t)params.pictureHeight) {
+            ALOGV("%s: Camera %d: Deleting stream %d since the buffer dimensions changed",
+                __FUNCTION__, client->getCameraId(), mCaptureStreamId);
             res = device->deleteStream(mCaptureStreamId);
             if (res != OK) {
                 ALOGE("%s: Camera %d: Unable to delete old output stream "
diff --git a/services/camera/libcameraservice/camera2/Parameters.cpp b/services/camera/libcameraservice/camera2/Parameters.cpp
index fd44a3e..74f4a7e 100644
--- a/services/camera/libcameraservice/camera2/Parameters.cpp
+++ b/services/camera/libcameraservice/camera2/Parameters.cpp
@@ -53,6 +53,9 @@
     res = buildFastInfo();
     if (res != OK) return res;
 
+    res = buildQuirks();
+    if (res != OK) return res;
+
     camera_metadata_ro_entry_t availableProcessedSizes =
         staticInfo(ANDROID_SCALER_AVAILABLE_PROCESSED_SIZES, 2);
     if (!availableProcessedSizes.count) return NO_INIT;
@@ -891,6 +894,21 @@
     return OK;
 }
 
+status_t Parameters::buildQuirks() {
+    camera_metadata_ro_entry_t entry;
+    entry = info->find(ANDROID_QUIRKS_TRIGGER_AF_WITH_AUTO);
+    quirks.triggerAfWithAuto = (entry.count != 0 && entry.data.u8[0] == 1);
+    ALOGV_IF(quirks.triggerAfWithAuto, "Camera %d: Quirk triggerAfWithAuto enabled",
+            cameraId);
+
+    entry = info->find(ANDROID_QUIRKS_USE_ZSL_FORMAT);
+    quirks.useZslFormat = (entry.count != 0 && entry.data.u8[0] == 1);
+    ALOGV_IF(quirks.useZslFormat, "Camera %d: Quirk useZslFormat enabled",
+            cameraId);
+
+    return OK;
+}
+
 camera_metadata_ro_entry_t Parameters::staticInfo(uint32_t tag,
         size_t minCount, size_t maxCount) const {
     status_t res;
@@ -1039,6 +1057,10 @@
             validatedParams.previewFpsRange[1] =
                     availableFrameRates.data.i32[i+1];
         }
+        newParams.set(CameraParameters::KEY_PREVIEW_FPS_RANGE,
+                String8::format("%d,%d",
+                        validatedParams.previewFpsRange[0] * kFpsToApiScale,
+                        validatedParams.previewFpsRange[1] * kFpsToApiScale));
     }
 
     // PICTURE_SIZE
diff --git a/services/camera/libcameraservice/camera2/Parameters.h b/services/camera/libcameraservice/camera2/Parameters.h
index c587ca5..47eb993 100644
--- a/services/camera/libcameraservice/camera2/Parameters.h
+++ b/services/camera/libcameraservice/camera2/Parameters.h
@@ -195,8 +195,10 @@
     // Sets up default parameters
     status_t initialize(const CameraMetadata *info);
 
-    // Build fast device info
+    // Build fast-access device static info from static info
     status_t buildFastInfo();
+    // Query for quirks from static info
+    status_t buildQuirks();
 
     // Get entry from camera static characteristics information. min/maxCount
     // are used for error checking the number of values in the entry. 0 for
diff --git a/services/camera/libcameraservice/camera2/StreamingProcessor.cpp b/services/camera/libcameraservice/camera2/StreamingProcessor.cpp
index 140138d..744b7ed 100644
--- a/services/camera/libcameraservice/camera2/StreamingProcessor.cpp
+++ b/services/camera/libcameraservice/camera2/StreamingProcessor.cpp
@@ -170,6 +170,9 @@
         if (client == 0) return INVALID_OPERATION;
         sp<Camera2Device> device = client->getCameraDevice();
 
+        ALOGV("%s: for cameraId %d on streamId %d",
+            __FUNCTION__, client->getCameraId(), mPreviewStreamId);
+
         res = device->waitUntilDrained();
         if (res != OK) {
             ALOGE("%s: Error waiting for preview to drain: %s (%d)",
@@ -351,6 +354,8 @@
     sp<Camera2Client> client = mClient.promote();
     if (client == 0) return INVALID_OPERATION;
 
+    ALOGV("%s: Camera %d: type = %d", __FUNCTION__, client->getCameraId(), type);
+
     Mutex::Autolock m(mMutex);
 
     CameraMetadata &request = (type == PREVIEW) ?
diff --git a/services/camera/libcameraservice/camera2/ZslProcessor.cpp b/services/camera/libcameraservice/camera2/ZslProcessor.cpp
index 9516f97..7977f9d 100644
--- a/services/camera/libcameraservice/camera2/ZslProcessor.cpp
+++ b/services/camera/libcameraservice/camera2/ZslProcessor.cpp
@@ -147,6 +147,8 @@
                         client->getCameraId(), strerror(-res), res);
                 return res;
             }
+            ALOGV("%s: Camera %d: Deleting stream %d since the buffer dimensions changed",
+                __FUNCTION__, client->getCameraId(), mZslStreamId);
             res = device->deleteStream(mZslStreamId);
             if (res != OK) {
                 ALOGE("%s: Camera %d: Unable to delete old output stream "
@@ -161,9 +163,12 @@
     if (mZslStreamId == NO_STREAM) {
         // Create stream for HAL production
         // TODO: Sort out better way to select resolution for ZSL
+        int streamType = params.quirks.useZslFormat ?
+                (int)CAMERA2_HAL_PIXEL_FORMAT_ZSL :
+                (int)HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
         res = device->createStream(mZslWindow,
                 params.fastInfo.arrayWidth, params.fastInfo.arrayHeight,
-                CAMERA2_HAL_PIXEL_FORMAT_ZSL, 0,
+                streamType, 0,
                 &mZslStreamId);
         if (res != OK) {
             ALOGE("%s: Camera %d: Can't create output stream for ZSL: "
@@ -292,6 +297,14 @@
             return INVALID_OPERATION;
         }
 
+        res = client->getCameraDevice()->clearStreamingRequest();
+        if (res != OK) {
+            ALOGE("%s: Camera %d: Unable to stop preview for ZSL capture: "
+                "%s (%d)",
+                __FUNCTION__, client->getCameraId(), strerror(-res), res);
+            return INVALID_OPERATION;
+        }
+        // TODO: have push-and-clear be atomic
         res = client->getCameraDevice()->pushReprocessBuffer(mZslReprocessStreamId,
                 handle, this);
         if (res != OK) {