Merge "Event driven wake for AudioRecordThread notification changes"
diff --git a/camera/Android.mk b/camera/Android.mk
index da5ac59..df7279f 100644
--- a/camera/Android.mk
+++ b/camera/Android.mk
@@ -35,6 +35,7 @@
 	camera2/ICameraDeviceUser.cpp \
 	camera2/ICameraDeviceCallbacks.cpp \
 	camera2/CaptureRequest.cpp \
+	camera2/OutputConfiguration.cpp \
 	ProCamera.cpp \
 	CameraBase.cpp \
 	CameraUtils.cpp \
diff --git a/camera/CameraParameters.cpp b/camera/CameraParameters.cpp
index 3dbf75e..68969cf 100644
--- a/camera/CameraParameters.cpp
+++ b/camera/CameraParameters.cpp
@@ -530,4 +530,8 @@
         -1;
 }
 
+bool CameraParameters::isEmpty() const {
+    return mMap.isEmpty();
+}
+
 }; // namespace android
diff --git a/camera/camera2/ICameraDeviceUser.cpp b/camera/camera2/ICameraDeviceUser.cpp
index d1d63d5..89c6fb7 100644
--- a/camera/camera2/ICameraDeviceUser.cpp
+++ b/camera/camera2/ICameraDeviceUser.cpp
@@ -26,6 +26,7 @@
 #include <gui/Surface.h>
 #include <camera/CameraMetadata.h>
 #include <camera/camera2/CaptureRequest.h>
+#include <camera/camera2/OutputConfiguration.h>
 
 namespace android {
 
@@ -208,17 +209,16 @@
         return reply.readInt32();
     }
 
-    virtual status_t createStream(
-                          const sp<IGraphicBufferProducer>& bufferProducer)
+    virtual status_t createStream(const OutputConfiguration& outputConfiguration)
     {
         Parcel data, reply;
         data.writeInterfaceToken(ICameraDeviceUser::getInterfaceDescriptor());
-
-        data.writeInt32(1); // marker that bufferProducer is not null
-        data.writeString16(String16("unknown_name")); // name of surface
-        sp<IBinder> b(IInterface::asBinder(bufferProducer));
-        data.writeStrongBinder(b);
-
+        if (outputConfiguration.getGraphicBufferProducer() != NULL) {
+            data.writeInt32(1); // marker that OutputConfiguration is not null. Mimic aidl behavior
+            outputConfiguration.writeToParcel(data);
+        } else {
+            data.writeInt32(0);
+        }
         remote()->transact(CREATE_STREAM, data, &reply);
 
         reply.readExceptionCode();
@@ -394,22 +394,14 @@
         case CREATE_STREAM: {
             CHECK_INTERFACE(ICameraDeviceUser, data, reply);
 
-            sp<IGraphicBufferProducer> bp;
+            status_t ret = BAD_VALUE;
             if (data.readInt32() != 0) {
-                String16 name = readMaybeEmptyString16(data);
-                bp = interface_cast<IGraphicBufferProducer>(
-                        data.readStrongBinder());
-
-                ALOGV("%s: CREATE_STREAM: bp = %p, name = %s", __FUNCTION__,
-                      bp.get(), String8(name).string());
+                OutputConfiguration outputConfiguration(data);
+                ret = createStream(outputConfiguration);
             } else {
-                ALOGV("%s: CREATE_STREAM: bp = unset, name = unset",
-                      __FUNCTION__);
+                ALOGE("%s: cannot take an empty OutputConfiguration", __FUNCTION__);
             }
 
-            status_t ret;
-            ret = createStream(bp);
-
             reply->writeNoException();
             ALOGV("%s: CREATE_STREAM: write noException", __FUNCTION__);
             reply->writeInt32(ret);
diff --git a/camera/camera2/OutputConfiguration.cpp b/camera/camera2/OutputConfiguration.cpp
new file mode 100644
index 0000000..24acaa0
--- /dev/null
+++ b/camera/camera2/OutputConfiguration.cpp
@@ -0,0 +1,79 @@
+/*
+**
+** Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#define LOG_TAG "OutputConfiguration"
+#include <utils/Log.h>
+
+#include <camera/camera2/OutputConfiguration.h>
+#include <binder/Parcel.h>
+
+namespace android {
+
+
+const int OutputConfiguration::INVALID_ROTATION = -1;
+
+// Read empty strings without printing a false error message.
+String16 OutputConfiguration::readMaybeEmptyString16(const Parcel& parcel) {
+    size_t len;
+    const char16_t* str = parcel.readString16Inplace(&len);
+    if (str != NULL) {
+        return String16(str, len);
+    } else {
+        return String16();
+    }
+}
+
+sp<IGraphicBufferProducer> OutputConfiguration::getGraphicBufferProducer() const {
+    return mGbp;
+}
+
+int OutputConfiguration::getRotation() const {
+    return mRotation;
+}
+
+OutputConfiguration::OutputConfiguration(const Parcel& parcel) {
+    status_t err;
+    int rotation = 0;
+    if ((err = parcel.readInt32(&rotation)) != OK) {
+        ALOGE("%s: Failed to read rotation from parcel", __FUNCTION__);
+        mGbp = NULL;
+        mRotation = INVALID_ROTATION;
+        return;
+    }
+
+    String16 name = readMaybeEmptyString16(parcel);
+    const sp<IGraphicBufferProducer>& gbp =
+            interface_cast<IGraphicBufferProducer>(parcel.readStrongBinder());
+    mGbp = gbp;
+    mRotation = rotation;
+
+    ALOGV("%s: OutputConfiguration: bp = %p, name = %s", __FUNCTION__,
+          gbp.get(), String8(name).string());
+}
+
+status_t OutputConfiguration::writeToParcel(Parcel& parcel) const {
+
+    parcel.writeInt32(mRotation);
+    parcel.writeString16(String16("unknown_name")); // name of surface
+    sp<IBinder> b(IInterface::asBinder(mGbp));
+    parcel.writeStrongBinder(b);
+
+    return OK;
+}
+
+}; // namespace android
+
diff --git a/include/camera/CameraParameters.h b/include/camera/CameraParameters.h
index c6074fc..ba33ffe 100644
--- a/include/camera/CameraParameters.h
+++ b/include/camera/CameraParameters.h
@@ -108,6 +108,9 @@
      */
     void getSupportedPreviewFormats(Vector<int>& formats) const;
 
+    // Returns true if no keys are present
+    bool isEmpty() const;
+
     // Parameter keys to communicate between camera application and driver.
     // The access (read/write, read only, or write only) is viewed from the
     // perspective of applications, not driver.
diff --git a/include/camera/camera2/ICameraDeviceUser.h b/include/camera/camera2/ICameraDeviceUser.h
index bfc2aa0..e9f1f5a 100644
--- a/include/camera/camera2/ICameraDeviceUser.h
+++ b/include/camera/camera2/ICameraDeviceUser.h
@@ -27,9 +27,9 @@
 
 class ICameraDeviceUserClient;
 class IGraphicBufferProducer;
-class Surface;
 class CaptureRequest;
 class CameraMetadata;
+class OutputConfiguration;
 
 enum {
     NO_IN_FLIGHT_REPEATING_FRAMES = -1,
@@ -100,8 +100,8 @@
     virtual status_t        endConfigure() = 0;
 
     virtual status_t        deleteStream(int streamId) = 0;
-    virtual status_t        createStream(
-            const sp<IGraphicBufferProducer>& bufferProducer) = 0;
+
+    virtual status_t        createStream(const OutputConfiguration& outputConfiguration) = 0;
 
     // Create a request object from a template.
     virtual status_t        createDefaultRequest(int templateId,
diff --git a/include/camera/camera2/OutputConfiguration.h b/include/camera/camera2/OutputConfiguration.h
new file mode 100644
index 0000000..e6b679f
--- /dev/null
+++ b/include/camera/camera2/OutputConfiguration.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_CAMERA2_OUTPUTCONFIGURATION_H
+#define ANDROID_HARDWARE_CAMERA2_OUTPUTCONFIGURATION_H
+
+#include <utils/RefBase.h>
+#include <gui/IGraphicBufferProducer.h>
+
+namespace android {
+
+class Surface;
+
+class OutputConfiguration : public virtual RefBase {
+public:
+
+    static const int INVALID_ROTATION;
+    sp<IGraphicBufferProducer> getGraphicBufferProducer() const;
+    int                        getRotation() const;
+
+    /**
+     * Keep impl up-to-date with OutputConfiguration.java in frameworks/base
+     */
+    status_t                   writeToParcel(Parcel& parcel) const;
+    // getGraphicBufferProducer will be NULL if error occurred
+    // getRotation will be INVALID_ROTATION if error occurred
+    OutputConfiguration(const Parcel& parcel);
+
+private:
+    sp<IGraphicBufferProducer> mGbp;
+    int                        mRotation;
+
+    // helper function
+    static String16 readMaybeEmptyString16(const Parcel& parcel);
+};
+}; // namespace android
+
+#endif
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 4bccfa8..a2ec51c 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -566,12 +566,22 @@
 }
 
 bool NuPlayer::Renderer::onDrainAudioQueue() {
-#if 0
+    // TODO: This call to getPosition checks if AudioTrack has been created
+    // in AudioSink before draining audio. If AudioTrack doesn't exist, then
+    // CHECKs on getPosition will fail.
+    // We still need to figure out why AudioTrack is not created when
+    // this function is called. One possible reason could be leftover
+    // audio. Another possible place is to check whether decoder
+    // has received INFO_FORMAT_CHANGED as the first buffer since
+    // AudioSink is opened there, and possible interactions with flush
+    // immediately after start. Investigate error message
+    // "vorbis_dsp_synthesis returned -135", along with RTSP.
     uint32_t numFramesPlayed;
     if (mAudioSink->getPosition(&numFramesPlayed) != OK) {
         return false;
     }
 
+#if 0
     ssize_t numFramesAvailableToWrite =
         mAudioSink->frameCount() - (mNumFramesWritten - numFramesPlayed);
 
diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp
index a94754b..f5328a6 100644
--- a/media/libstagefright/httplive/LiveSession.cpp
+++ b/media/libstagefright/httplive/LiveSession.cpp
@@ -50,13 +50,75 @@
 namespace android {
 
 // static
-// Number of recently-read bytes to use for bandwidth estimation
-const size_t LiveSession::kBandwidthHistoryBytes = 200 * 1024;
 // High water mark to start up switch or report prepared)
 const int64_t LiveSession::kHighWaterMark = 8000000ll;
 const int64_t LiveSession::kMidWaterMark = 5000000ll;
 const int64_t LiveSession::kLowWaterMark = 3000000ll;
 
+struct LiveSession::BandwidthEstimator : public RefBase {
+    BandwidthEstimator();
+
+    void addBandwidthMeasurement(size_t numBytes, int64_t delayUs);
+    bool estimateBandwidth(int32_t *bandwidth);
+
+private:
+    // Bandwidth estimation parameters
+    static const int32_t kMaxBandwidthHistoryItems = 20;
+    static const int64_t kMaxBandwidthHistoryWindowUs = 3000000ll; // 3 sec
+
+    struct BandwidthEntry {
+        int64_t mDelayUs;
+        size_t mNumBytes;
+    };
+
+    Mutex mLock;
+    List<BandwidthEntry> mBandwidthHistory;
+    int64_t mTotalTransferTimeUs;
+    size_t mTotalTransferBytes;
+
+    DISALLOW_EVIL_CONSTRUCTORS(BandwidthEstimator);
+};
+
+LiveSession::BandwidthEstimator::BandwidthEstimator() :
+    mTotalTransferTimeUs(0),
+    mTotalTransferBytes(0) {
+}
+
+void LiveSession::BandwidthEstimator::addBandwidthMeasurement(
+        size_t numBytes, int64_t delayUs) {
+    AutoMutex autoLock(mLock);
+
+    BandwidthEntry entry;
+    entry.mDelayUs = delayUs;
+    entry.mNumBytes = numBytes;
+    mTotalTransferTimeUs += delayUs;
+    mTotalTransferBytes += numBytes;
+    mBandwidthHistory.push_back(entry);
+
+    // trim old samples, keeping at least kMaxBandwidthHistoryItems samples,
+    // and total transfer time at least kMaxBandwidthHistoryWindowUs.
+    while (mBandwidthHistory.size() > kMaxBandwidthHistoryItems) {
+        List<BandwidthEntry>::iterator it = mBandwidthHistory.begin();
+        if (mTotalTransferTimeUs - it->mDelayUs < kMaxBandwidthHistoryWindowUs) {
+            break;
+        }
+        mTotalTransferTimeUs -= it->mDelayUs;
+        mTotalTransferBytes -= it->mNumBytes;
+        mBandwidthHistory.erase(mBandwidthHistory.begin());
+    }
+}
+
+bool LiveSession::BandwidthEstimator::estimateBandwidth(int32_t *bandwidthBps) {
+    AutoMutex autoLock(mLock);
+
+    if (mBandwidthHistory.size() < 2) {
+        return false;
+    }
+
+    *bandwidthBps = ((double)mTotalTransferBytes * 8E6 / mTotalTransferTimeUs);
+    return true;
+}
+
 LiveSession::LiveSession(
         const sp<AMessage> &notify, uint32_t flags,
         const sp<IMediaHTTPService> &httpService)
@@ -66,10 +128,10 @@
       mInPreparationPhase(true),
       mHTTPDataSource(new MediaHTTP(mHTTPService->makeHTTPConnection())),
       mCurBandwidthIndex(-1),
+      mBandwidthEstimator(new BandwidthEstimator()),
       mStreamMask(0),
       mNewStreamMask(0),
       mSwapMask(0),
-      mCheckBandwidthGeneration(0),
       mSwitchGeneration(0),
       mSubtitleGeneration(0),
       mLastDequeuedTimeUs(0ll),
@@ -89,13 +151,6 @@
         mPacketSources.add(indexToType(i), new AnotherPacketSource(NULL /* meta */));
         mPacketSources2.add(indexToType(i), new AnotherPacketSource(NULL /* meta */));
     }
-
-    size_t numHistoryItems = kBandwidthHistoryBytes /
-            PlaylistFetcher::kDownloadBlockSize + 1;
-    if (numHistoryItems < 5) {
-        numHistoryItems = 5;
-    }
-    mHTTPDataSource->setBandwidthHistorySize(numHistoryItems);
 }
 
 LiveSession::~LiveSession() {
@@ -948,8 +1003,15 @@
 }
 #endif
 
-size_t LiveSession::getBandwidthIndex() {
-    if (mBandwidthItems.size() == 0) {
+void LiveSession::addBandwidthMeasurement(size_t numBytes, int64_t delayUs) {
+    mBandwidthEstimator->addBandwidthMeasurement(numBytes, delayUs);
+}
+
+size_t LiveSession::getBandwidthIndex(int32_t bandwidthBps) {
+    if (mBandwidthItems.size() < 2) {
+        // shouldn't be here if we only have 1 bandwidth, check
+        // logic to get rid of redundant bandwidth polling
+        ALOGW("getBandwidthIndex() called for single bandwidth playlist!");
         return 0;
     }
 
@@ -967,15 +1029,6 @@
     }
 
     if (index < 0) {
-        int32_t bandwidthBps;
-        if (mHTTPDataSource != NULL
-                && mHTTPDataSource->estimateBandwidth(&bandwidthBps)) {
-            ALOGV("bandwidth estimated at %.2f kbps", bandwidthBps / 1024.0f);
-        } else {
-            ALOGV("no bandwidth estimate.");
-            return 0;  // Pick the lowest bandwidth stream by default.
-        }
-
         char value[PROPERTY_VALUE_MAX];
         if (property_get("media.httplive.max-bw", value, NULL)) {
             char *end;
@@ -992,15 +1045,9 @@
 
         index = mBandwidthItems.size() - 1;
         while (index > 0) {
-            // consider only 80% of the available bandwidth, but if we are switching up,
-            // be even more conservative (70%) to avoid overestimating and immediately
-            // switching back.
-            size_t adjustedBandwidthBps = bandwidthBps;
-            if (index > mCurBandwidthIndex) {
-                adjustedBandwidthBps = adjustedBandwidthBps * 7 / 10;
-            } else {
-                adjustedBandwidthBps = adjustedBandwidthBps * 8 / 10;
-            }
+            // be conservative (70%) to avoid overestimating and immediately
+            // switching down again.
+            size_t adjustedBandwidthBps = bandwidthBps * 7 / 10;
             if (mBandwidthItems.itemAt(index).mBandwidth <= adjustedBandwidthBps) {
                 break;
             }
@@ -1577,9 +1624,9 @@
 
 void LiveSession::onPollBuffering() {
     ALOGV("onPollBuffering: mSwitchInProgress %d, mReconfigurationInProgress %d, "
-            "mInPreparationPhase %d, mStreamMask 0x%x",
+            "mInPreparationPhase %d, mCurBandwidthIndex %d, mStreamMask 0x%x",
         mSwitchInProgress, mReconfigurationInProgress,
-        mInPreparationPhase, mStreamMask);
+        mInPreparationPhase, mCurBandwidthIndex, mStreamMask);
 
     bool low, mid, high;
     if (checkBuffering(low, mid, high)) {
@@ -1588,8 +1635,8 @@
         }
 
         // don't switch before we report prepared
-        if (!mInPreparationPhase && (low || high)) {
-            switchBandwidthIfNeeded(high);
+        if (!mInPreparationPhase) {
+            switchBandwidthIfNeeded(high, !mid);
         }
     }
 
@@ -1704,11 +1751,35 @@
     return false;
 }
 
-void LiveSession::switchBandwidthIfNeeded(bool canSwitchUp) {
-    ssize_t bandwidthIndex = getBandwidthIndex();
+void LiveSession::switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow) {
+    // no need to check bandwidth if we only have 1 bandwidth settings
+    if (mBandwidthItems.size() < 2) {
+        return;
+    }
 
-    if ((canSwitchUp && bandwidthIndex > mCurBandwidthIndex)
-            || (!canSwitchUp && bandwidthIndex < mCurBandwidthIndex)) {
+    int32_t bandwidthBps;
+    if (mBandwidthEstimator->estimateBandwidth(&bandwidthBps)) {
+        ALOGV("bandwidth estimated at %.2f kbps", bandwidthBps / 1024.0f);
+    } else {
+        ALOGV("no bandwidth estimate.");
+        return;
+    }
+
+    int32_t curBandwidth = mBandwidthItems.itemAt(mCurBandwidthIndex).mBandwidth;
+    bool bandwidthLow = bandwidthBps < (int32_t)curBandwidth * 8 / 10;
+    bool bandwidthHigh = bandwidthBps > (int32_t)curBandwidth * 12 / 10;
+
+    if ((bufferHigh && bandwidthHigh) || (bufferLow && bandwidthLow)) {
+        ssize_t bandwidthIndex = getBandwidthIndex(bandwidthBps);
+
+        if (bandwidthIndex == mCurBandwidthIndex
+                || (bufferHigh && bandwidthIndex < mCurBandwidthIndex)
+                || (bufferLow && bandwidthIndex > mCurBandwidthIndex)) {
+            return;
+        }
+
+        ALOGI("#### Initiate Bandwidth Switch: %d => %d",
+                mCurBandwidthIndex, bandwidthIndex);
         changeConfiguration(-1, bandwidthIndex, false);
     }
 }
diff --git a/media/libstagefright/httplive/LiveSession.h b/media/libstagefright/httplive/LiveSession.h
index 3b0a9a4..685fefa 100644
--- a/media/libstagefright/httplive/LiveSession.h
+++ b/media/libstagefright/httplive/LiveSession.h
@@ -106,7 +106,6 @@
         kWhatDisconnect                 = 'disc',
         kWhatSeek                       = 'seek',
         kWhatFetcherNotify              = 'notf',
-        kWhatCheckBandwidth             = 'bndw',
         kWhatChangeConfiguration        = 'chC0',
         kWhatChangeConfiguration2       = 'chC2',
         kWhatChangeConfiguration3       = 'chC3',
@@ -115,11 +114,11 @@
         kWhatPollBuffering              = 'poll',
     };
 
-    static const size_t kBandwidthHistoryBytes;
     static const int64_t kHighWaterMark;
     static const int64_t kMidWaterMark;
     static const int64_t kLowWaterMark;
 
+    struct BandwidthEstimator;
     struct BandwidthItem {
         size_t mPlaylistIndex;
         unsigned long mBandwidth;
@@ -170,6 +169,7 @@
 
     Vector<BandwidthItem> mBandwidthItems;
     ssize_t mCurBandwidthIndex;
+    sp<BandwidthEstimator> mBandwidthEstimator;
 
     sp<M3UParser> mPlaylist;
 
@@ -195,7 +195,6 @@
     // * a forced bandwidth switch termination in cancelSwitch on the live looper.
     Mutex mSwapMutex;
 
-    int32_t mCheckBandwidthGeneration;
     int32_t mSwitchGeneration;
     int32_t mSubtitleGeneration;
 
@@ -249,7 +248,8 @@
     sp<M3UParser> fetchPlaylist(
             const char *url, uint8_t *curPlaylistHash, bool *unchanged);
 
-    size_t getBandwidthIndex();
+    void addBandwidthMeasurement(size_t numBytes, int64_t delayUs);
+    size_t getBandwidthIndex(int32_t bandwidthBps);
     int64_t latestMediaSegmentStartTimeUs();
 
     static int SortByBandwidth(const BandwidthItem *, const BandwidthItem *);
@@ -273,7 +273,7 @@
     void cancelPollBuffering();
     void onPollBuffering();
     bool checkBuffering(bool &low, bool &mid, bool &high);
-    void switchBandwidthIfNeeded(bool canSwitchUp);
+    void switchBandwidthIfNeeded(bool bufferHigh, bool bufferLow);
 
     void finishDisconnect();
 
diff --git a/media/libstagefright/httplive/PlaylistFetcher.cpp b/media/libstagefright/httplive/PlaylistFetcher.cpp
index 7f818a8..a447010 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.cpp
+++ b/media/libstagefright/httplive/PlaylistFetcher.cpp
@@ -648,23 +648,23 @@
         targetDurationUs = targetDurationSecs * 1000000ll;
     }
 
-    int64_t durationToBufferUs = kMinBufferedDurationUs;
-
     int64_t bufferedDurationUs = 0ll;
-    status_t finalResult = NOT_ENOUGH_DATA;
+    status_t finalResult = OK;
     if (mStreamTypeMask == LiveSession::STREAMTYPE_SUBTITLES) {
         sp<AnotherPacketSource> packetSource =
             mPacketSources.valueFor(LiveSession::STREAMTYPE_SUBTITLES);
 
         bufferedDurationUs =
                 packetSource->getBufferedDurationUs(&finalResult);
-        finalResult = OK;
     } else {
-        // Use max stream duration to prevent us from waiting on a non-existent stream;
-        // when we cannot make out from the manifest what streams are included in a playlist
-        // we might assume extra streams.
+        // Use min stream duration, but ignore streams that never have any packet
+        // enqueued to prevent us from waiting on a non-existent stream;
+        // when we cannot make out from the manifest what streams are included in
+        // a playlist we might assume extra streams.
+        bufferedDurationUs = -1ll;
         for (size_t i = 0; i < mPacketSources.size(); ++i) {
-            if ((mStreamTypeMask & mPacketSources.keyAt(i)) == 0) {
+            if ((mStreamTypeMask & mPacketSources.keyAt(i)) == 0
+                    || mPacketSources[i]->getLatestEnqueuedMeta() == NULL) {
                 continue;
             }
 
@@ -672,24 +672,19 @@
                 mPacketSources.valueAt(i)->getBufferedDurationUs(&finalResult);
             ALOGV("buffered %" PRId64 " for stream %d",
                     bufferedStreamDurationUs, mPacketSources.keyAt(i));
-            if (bufferedStreamDurationUs > bufferedDurationUs) {
+            if (bufferedDurationUs == -1ll
+                 || bufferedStreamDurationUs < bufferedDurationUs) {
                 bufferedDurationUs = bufferedStreamDurationUs;
             }
         }
-    }
-    downloadMore = (bufferedDurationUs < durationToBufferUs);
-
-    // signal start if buffered up at least the target size
-    if (!mPrepared && bufferedDurationUs > targetDurationUs && downloadMore) {
-        mPrepared = true;
-
-        ALOGV("prepared, buffered=%" PRId64 " > %" PRId64 "",
-                bufferedDurationUs, targetDurationUs);
+        if (bufferedDurationUs == -1ll) {
+            bufferedDurationUs = 0ll;
+        }
     }
 
-    if (finalResult == OK && downloadMore) {
+    if (finalResult == OK && bufferedDurationUs < kMinBufferedDurationUs) {
         ALOGV("monitoring, buffered=%" PRId64 " < %" PRId64 "",
-                bufferedDurationUs, durationToBufferUs);
+                bufferedDurationUs, kMinBufferedDurationUs);
         // delay the next download slightly; hopefully this gives other concurrent fetchers
         // a better chance to run.
         // onDownloadNext();
@@ -697,13 +692,16 @@
         msg->setInt32("generation", mMonitorQueueGeneration);
         msg->post(1000l);
     } else {
-        // Nothing to do yet, try again in a second.
-        int64_t delayUs = mPrepared ? kMaxMonitorDelayUs : targetDurationUs / 2;
+        // We'd like to maintain buffering above durationToBufferUs, so try
+        // again when buffer just about to go below durationToBufferUs
+        // (or after targetDurationUs / 2, whichever is smaller).
+        int64_t delayUs = bufferedDurationUs - kMinBufferedDurationUs + 1000000ll;
+        if (delayUs > targetDurationUs / 2) {
+            delayUs = targetDurationUs / 2;
+        }
         ALOGV("pausing for %" PRId64 ", buffered=%" PRId64 " > %" PRId64 "",
-                delayUs, bufferedDurationUs, durationToBufferUs);
-        // :TRICKY: need to enforce minimum delay because the delay to
-        // refresh the playlist will become 0
-        postMonitorQueue(delayUs, mPrepared ? targetDurationUs * 2 : 0);
+                delayUs, bufferedDurationUs, kMinBufferedDurationUs);
+        postMonitorQueue(delayUs);
     }
 }
 
@@ -986,9 +984,20 @@
     // block-wise download
     ssize_t bytesRead;
     do {
+        int64_t startUs = ALooper::GetNowUs();
+
         bytesRead = mSession->fetchFile(
                 uri.c_str(), &buffer, range_offset, range_length, kDownloadBlockSize, &source);
 
+        // add sample for bandwidth estimation (excluding subtitles)
+        if (bytesRead > 0
+                && (mStreamTypeMask
+                        & (LiveSession::STREAMTYPE_AUDIO
+                        | LiveSession::STREAMTYPE_VIDEO))) {
+            int64_t delayUs = ALooper::GetNowUs() - startUs;
+            mSession->addBandwidthMeasurement(bytesRead, delayUs);
+        }
+
         if (bytesRead < 0) {
             status_t err = bytesRead;
             ALOGE("failed to fetch .ts segment at url '%s'", uri.c_str());
diff --git a/services/audioflinger/FastCaptureDumpState.cpp b/services/audioflinger/FastCaptureDumpState.cpp
index 00f8da0..53eeba5 100644
--- a/services/audioflinger/FastCaptureDumpState.cpp
+++ b/services/audioflinger/FastCaptureDumpState.cpp
@@ -14,7 +14,13 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "FastCaptureDumpState"
+//define LOG_NDEBUG 0
+
+#include "Configuration.h"
+#include <utils/Log.h>
 #include "FastCaptureDumpState.h"
+#include "FastCaptureState.h"
 
 namespace android {
 
@@ -27,4 +33,21 @@
 {
 }
 
+void FastCaptureDumpState::dump(int fd) const
+{
+    if (mCommand == FastCaptureState::INITIAL) {
+        dprintf(fd, "  FastCapture not initialized\n");
+        return;
+    }
+    double measuredWarmupMs = (mMeasuredWarmupTs.tv_sec * 1000.0) +
+            (mMeasuredWarmupTs.tv_nsec / 1000000.0);
+    double periodSec = (double) mFrameCount / mSampleRate;
+    dprintf(fd, "  FastCapture command=%s readSequence=%u framesRead=%u\n"
+                "              readErrors=%u sampleRate=%u frameCount=%zu\n"
+                "              measuredWarmup=%.3g ms, warmupCycles=%u period=%.2f ms\n",
+                FastCaptureState::commandToString(mCommand), mReadSequence, mFramesRead,
+                mReadErrors, mSampleRate, mFrameCount, measuredWarmupMs, mWarmupCycles,
+                periodSec * 1e3);
+}
+
 }   // android
diff --git a/services/audioflinger/FastCaptureDumpState.h b/services/audioflinger/FastCaptureDumpState.h
index ee99099..6f9c4c3 100644
--- a/services/audioflinger/FastCaptureDumpState.h
+++ b/services/audioflinger/FastCaptureDumpState.h
@@ -27,6 +27,8 @@
     FastCaptureDumpState();
     /*virtual*/ ~FastCaptureDumpState();
 
+    void dump(int fd) const;    // should only be called on a stable copy, not the original
+
     // FIXME by renaming, could pull up many of these to FastThreadDumpState
     uint32_t mReadSequence;     // incremented before and after each read()
     uint32_t mFramesRead;       // total number of frames read successfully
diff --git a/services/audioflinger/FastMixerDumpState.cpp b/services/audioflinger/FastMixerDumpState.cpp
index 65fbf2b..b10942b 100644
--- a/services/audioflinger/FastMixerDumpState.cpp
+++ b/services/audioflinger/FastMixerDumpState.cpp
@@ -64,7 +64,7 @@
     }
     double measuredWarmupMs = (mMeasuredWarmupTs.tv_sec * 1000.0) +
             (mMeasuredWarmupTs.tv_nsec / 1000000.0);
-    double mixPeriodSec = (double) mFrameCount / (double) mSampleRate;
+    double mixPeriodSec = (double) mFrameCount / mSampleRate;
     dprintf(fd, "  FastMixer command=%s writeSequence=%u framesWritten=%u\n"
                 "            numTracks=%u writeErrors=%u underruns=%u overruns=%u\n"
                 "            sampleRate=%u frameCount=%zu measuredWarmup=%.3g ms, warmupCycles=%u\n"
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index c1da6bc..9881764 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -6164,6 +6164,10 @@
     }
     dprintf(fd, "  Fast capture thread: %s\n", hasFastCapture() ? "yes" : "no");
     dprintf(fd, "  Fast track available: %s\n", mFastTrackAvail ? "yes" : "no");
+
+    //  Make a non-atomic copy of fast capture dump state so it won't change underneath us
+    const FastCaptureDumpState copy(mFastCaptureDumpState);
+    copy.dump(fd);
 }
 
 void AudioFlinger::RecordThread::dumpTracks(int fd, const Vector<String16>& args __unused)
diff --git a/services/camera/libcameraservice/Android.mk b/services/camera/libcameraservice/Android.mk
index 5d6423a..de841c8 100644
--- a/services/camera/libcameraservice/Android.mk
+++ b/services/camera/libcameraservice/Android.mk
@@ -54,6 +54,7 @@
     device3/StatusTracker.cpp \
     gui/RingBufferConsumer.cpp \
     utils/CameraTraces.cpp \
+    utils/AutoConditionLock.cpp \
 
 LOCAL_SHARED_LIBRARIES:= \
     libui \
diff --git a/services/camera/libcameraservice/CameraDeviceFactory.cpp b/services/camera/libcameraservice/CameraDeviceFactory.cpp
index bfef50e..6589e27 100644
--- a/services/camera/libcameraservice/CameraDeviceFactory.cpp
+++ b/services/camera/libcameraservice/CameraDeviceFactory.cpp
@@ -48,6 +48,7 @@
         case CAMERA_DEVICE_API_VERSION_3_0:
         case CAMERA_DEVICE_API_VERSION_3_1:
         case CAMERA_DEVICE_API_VERSION_3_2:
+        case CAMERA_DEVICE_API_VERSION_3_3:
             device = new Camera3Device(cameraId);
             break;
         default:
diff --git a/services/camera/libcameraservice/CameraFlashlight.cpp b/services/camera/libcameraservice/CameraFlashlight.cpp
index 7a79750..6fda9b2 100644
--- a/services/camera/libcameraservice/CameraFlashlight.cpp
+++ b/services/camera/libcameraservice/CameraFlashlight.cpp
@@ -110,6 +110,20 @@
     status_t res = OK;
     Mutex::Autolock l(mLock);
 
+    if (mOpenedCameraIds.indexOf(cameraId) != NAME_NOT_FOUND) {
+        // This case is needed to avoid state corruption during the following call sequence:
+        // CameraService::setTorchMode for camera ID 0 begins, does torch status checks
+        // CameraService::connect for camera ID 0 begins, calls prepareDeviceOpen, ends
+        // CameraService::setTorchMode for camera ID 0 continues, calls
+        //        CameraFlashlight::setTorchMode
+
+        // TODO: Move torch status checks and state updates behind this CameraFlashlight lock
+        // to avoid other similar race conditions.
+        ALOGE("%s: Camera device %s is in use, cannot set torch mode.",
+                __FUNCTION__, cameraId.string());
+        return -EBUSY;
+    }
+
     if (mFlashControl == NULL) {
         if (enabled == false) {
             return OK;
@@ -389,7 +403,7 @@
         return NO_MEMORY;
     }
     res = device->createStream(mAnw, width, height, format,
-            HAL_DATASPACE_UNKNOWN, &mStreamId);
+            HAL_DATASPACE_UNKNOWN, CAMERA3_STREAM_ROTATION_0, &mStreamId);
     if (res) {
         return res;
     }
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 6f37f16..55f7a40 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -17,9 +17,14 @@
 #define LOG_TAG "CameraService"
 //#define LOG_NDEBUG 0
 
+#include <algorithm>
+#include <climits>
 #include <stdio.h>
-#include <string.h>
+#include <cstring>
+#include <ctime>
+#include <string>
 #include <sys/types.h>
+#include <inttypes.h>
 #include <pthread.h>
 
 #include <binder/AppOpsManager.h>
@@ -27,6 +32,7 @@
 #include <binder/IServiceManager.h>
 #include <binder/MemoryBase.h>
 #include <binder/MemoryHeapBase.h>
+#include <binder/ProcessInfoService.h>
 #include <cutils/atomic.h>
 #include <cutils/properties.h>
 #include <cutils/multiuser.h>
@@ -67,25 +73,16 @@
 
 // ----------------------------------------------------------------------------
 
-static int getCallingPid() {
-    return IPCThreadState::self()->getCallingPid();
-}
-
-static int getCallingUid() {
-    return IPCThreadState::self()->getCallingUid();
-}
-
 extern "C" {
 static void camera_device_status_change(
         const struct camera_module_callbacks* callbacks,
         int camera_id,
         int new_status) {
     sp<CameraService> cs = const_cast<CameraService*>(
-                                static_cast<const CameraService*>(callbacks));
+            static_cast<const CameraService*>(callbacks));
 
-    cs->onDeviceStatusChanged(
-        camera_id,
-        new_status);
+    cs->onDeviceStatusChanged(static_cast<camera_device_status_t>(camera_id),
+            static_cast<camera_device_status_t>(new_status));
 }
 
 static void torch_mode_status_change(
@@ -128,23 +125,20 @@
 static CameraService *gCameraService;
 
 CameraService::CameraService()
-    :mSoundRef(0), mModule(0), mFlashlight(0)
+    : mEventLog(DEFAULT_EVICTION_LOG_LENGTH), mSoundRef(0), mModule(0), mFlashlight(0)
 {
     ALOGI("CameraService started (pid=%d)", getpid());
     gCameraService = this;
 
-    for (size_t i = 0; i < MAX_CAMERAS; ++i) {
-        mStatusList[i] = ICameraServiceListener::STATUS_PRESENT;
-    }
-
     this->camera_device_status_change = android::camera_device_status_change;
     this->torch_mode_status_change = android::torch_mode_status_change;
 
+    mServiceLockWrapper = std::make_shared<WaitableMutexWrapper>(&mServiceLock);
 }
 
 void CameraService::onFirstRef()
 {
-    LOG1("CameraService::onFirstRef");
+    ALOGI("CameraService process starting");
 
     BnCameraService::onFirstRef();
 
@@ -157,27 +151,53 @@
     else {
         mModule = new CameraModule(rawModule);
         const hw_module_t *common = mModule->getRawModule();
-        ALOGI("Loaded \"%s\" cameraCa module", common->name);
+        ALOGI("Loaded \"%s\" camera module", common->name);
         mNumberOfCameras = mModule->getNumberOfCameras();
-        if (mNumberOfCameras > MAX_CAMERAS) {
-            ALOGE("Number of cameras(%d) > MAX_CAMERAS(%d).",
-                    mNumberOfCameras, MAX_CAMERAS);
-            mNumberOfCameras = MAX_CAMERAS;
-        }
 
         mFlashlight = new CameraFlashlight(*mModule, *this);
         status_t res = mFlashlight->findFlashUnits();
         if (res) {
             // impossible because we haven't open any camera devices.
-            ALOGE("failed to find flash units.");
+            ALOGE("Failed to find flash units.");
         }
 
         for (int i = 0; i < mNumberOfCameras; i++) {
-            setCameraFree(i);
+            String8 cameraId = String8::format("%d", i);
 
-            String8 cameraName = String8::format("%d", i);
-            if (mFlashlight->hasFlashUnit(cameraName)) {
-                mTorchStatusMap.add(cameraName,
+            // Defaults to use for cost and conflicting devices
+            int cost = 100;
+            char** conflicting_devices = nullptr;
+            size_t conflicting_devices_length = 0;
+
+            // If using post-2.4 module version, query the cost + conflicting devices from the HAL
+            if (common->module_api_version >= CAMERA_MODULE_API_VERSION_2_4) {
+                struct camera_info info;
+                status_t rc = mModule->getCameraInfo(i, &info);
+                if (rc == NO_ERROR) {
+                    cost = info.resource_cost;
+                    conflicting_devices = info.conflicting_devices;
+                    conflicting_devices_length = info.conflicting_devices_length;
+                } else {
+                    ALOGE("%s: Received error loading camera info for device %d, cost and"
+                            " conflicting devices fields set to defaults for this device.",
+                            __FUNCTION__, i);
+                }
+            }
+
+            std::set<String8> conflicting;
+            for (size_t i = 0; i < conflicting_devices_length; i++) {
+                conflicting.emplace(String8(conflicting_devices[i]));
+            }
+
+            // Initialize state for each camera device
+            {
+                Mutex::Autolock lock(mCameraStatesLock);
+                mCameraStates.emplace(cameraId, std::make_shared<CameraState>(cameraId, cost,
+                        conflicting));
+            }
+
+            if (mFlashlight->hasFlashUnit(cameraId)) {
+                mTorchStatusMap.add(cameraId,
                         ICameraServiceListener::TORCH_STATUS_AVAILABLE_OFF);
             }
         }
@@ -197,81 +217,70 @@
 }
 
 CameraService::~CameraService() {
-    for (int i = 0; i < mNumberOfCameras; i++) {
-        if (mBusy[i]) {
-            ALOGE("camera %d is still in use in destructor!", i);
-        }
-    }
-
     if (mModule) {
         delete mModule;
+        mModule = nullptr;
     }
     VendorTagDescriptor::clearGlobalVendorTagDescriptor();
-    gCameraService = NULL;
+    gCameraService = nullptr;
 }
 
-void CameraService::onDeviceStatusChanged(int cameraId,
-                                          int newStatus)
-{
-    ALOGV("%s: Status changed for cameraId=%d, newStatus=%d", __FUNCTION__,
+void CameraService::onDeviceStatusChanged(camera_device_status_t  cameraId,
+        camera_device_status_t newStatus) {
+    ALOGI("%s: Status changed for cameraId=%d, newStatus=%d", __FUNCTION__,
           cameraId, newStatus);
 
-    if (cameraId < 0 || cameraId >= MAX_CAMERAS) {
+    String8 id = String8::format("%d", cameraId);
+    std::shared_ptr<CameraState> state = getCameraState(id);
+
+    if (state == nullptr) {
         ALOGE("%s: Bad camera ID %d", __FUNCTION__, cameraId);
         return;
     }
 
-    if ((int)getStatus(cameraId) == newStatus) {
-        ALOGE("%s: State transition to the same status 0x%x not allowed",
-              __FUNCTION__, (uint32_t)newStatus);
+    ICameraServiceListener::Status oldStatus = state->getStatus();
+
+    if (oldStatus == static_cast<ICameraServiceListener::Status>(newStatus)) {
+        ALOGE("%s: State transition to the same status %#x not allowed", __FUNCTION__, newStatus);
         return;
     }
 
-    /* don't do this in updateStatus
-       since it is also called from connect and we could get into a deadlock */
     if (newStatus == CAMERA_DEVICE_STATUS_NOT_PRESENT) {
-        Vector<sp<BasicClient> > clientsToDisconnect;
+        sp<BasicClient> clientToDisconnect;
         {
-           Mutex::Autolock al(mServiceLock);
+            // Don't do this in updateStatus to avoid deadlock over mServiceLock
+            Mutex::Autolock lock(mServiceLock);
 
-           /* Remove cached parameters from shim cache */
-           mShimParams.removeItem(cameraId);
+            // Set the device status to NOT_PRESENT, clients will no longer be able to connect
+            // to this device until the status changes
+            updateStatus(ICameraServiceListener::STATUS_NOT_PRESENT, id);
 
-           /* Find all clients that we need to disconnect */
-           sp<BasicClient> client = mClient[cameraId].promote();
-           if (client.get() != NULL) {
-               clientsToDisconnect.push_back(client);
-           }
+            // Remove cached shim parameters
+            state->setShimParams(CameraParameters());
 
-           int i = cameraId;
-           for (size_t j = 0; j < mProClientList[i].size(); ++j) {
-               sp<ProClient> cl = mProClientList[i][j].promote();
-               if (cl != NULL) {
-                   clientsToDisconnect.push_back(cl);
-               }
-           }
+            // Remove the client from the list of active clients
+            clientToDisconnect = removeClientLocked(id);
+
+            // Notify the client of disconnection
+            clientToDisconnect->notifyError(ICameraDeviceCallbacks::ERROR_CAMERA_DISCONNECTED,
+                    CaptureResultExtras{});
         }
 
-        /* now disconnect them. don't hold the lock
-           or we can get into a deadlock */
+        ALOGI("%s: Client for camera ID %s evicted due to device status change from HAL",
+                __FUNCTION__, id.string());
 
-        for (size_t i = 0; i < clientsToDisconnect.size(); ++i) {
-            sp<BasicClient> client = clientsToDisconnect[i];
-
-            client->disconnect();
-            /**
-             * The remote app will no longer be able to call methods on the
-             * client since the client PID will be reset to 0
-             */
+        // Disconnect client
+        if (clientToDisconnect.get() != nullptr) {
+            // Ensure not in binder RPC so client disconnect PID checks work correctly
+            LOG_ALWAYS_FATAL_IF(getCallingPid() != getpid(),
+                    "onDeviceStatusChanged must be called from the camera service process!");
+            clientToDisconnect->disconnect();
         }
 
-        ALOGV("%s: After unplug, disconnected %zu clients",
-              __FUNCTION__, clientsToDisconnect.size());
+    } else {
+        updateStatus(static_cast<ICameraServiceListener::Status>(newStatus), id);
     }
 
-    updateStatus(
-            static_cast<ICameraServiceListener::Status>(newStatus), cameraId);
-
 }
 
 void CameraService::onTorchStatusChanged(const String8& cameraId,
@@ -304,9 +313,11 @@
         return;
     }
 
-    Vector<sp<ICameraServiceListener> >::const_iterator it;
-    for (it = mListenerList.begin(); it != mListenerList.end(); ++it) {
-        (*it)->onTorchStatusChanged(newStatus, String16(cameraId.string()));
+    {
+        Mutex::Autolock lock(mStatusListenerLock);
+        for (auto& i : mListenerList) {
+            i->onTorchStatusChanged(newStatus, String16{cameraId});
+        }
     }
 }
 
@@ -333,6 +344,15 @@
     return rc;
 }
 
+int CameraService::cameraIdToInt(const String8& cameraId) {
+    errno = 0;
+    size_t pos = 0;
+    int ret = stoi(std::string{cameraId.string()}, &pos);
+    if (errno != 0 || pos != cameraId.size()) {
+        return -1;
+    }
+    return ret;
+}
 
 status_t CameraService::generateShimMetadata(int cameraId, /*out*/CameraMetadata* cameraInfo) {
     status_t ret = OK;
@@ -466,6 +486,54 @@
     return ret;
 }
 
+int CameraService::getCallingPid() {
+    return IPCThreadState::self()->getCallingPid();
+}
+
+int CameraService::getCallingUid() {
+    return IPCThreadState::self()->getCallingUid();
+}
+
+String8 CameraService::getFormattedCurrentTime() {
+    time_t now = time(nullptr);
+    char formattedTime[64];
+    strftime(formattedTime, sizeof(formattedTime), "%m-%d %H:%M:%S", localtime(&now));
+    return String8(formattedTime);
+}
+
+int CameraService::getCameraPriorityFromProcState(int procState) {
+    // Find the priority for the camera usage based on the process state.  Higher priority clients
+    // win for evictions.
+    // Note: Unlike the ordering for ActivityManager, persistent system processes will always lose
+    //       the camera to the top/foreground applications.
+    switch(procState) {
+        case PROCESS_STATE_TOP: // User visible
+            return 100;
+        case PROCESS_STATE_IMPORTANT_FOREGROUND: // Foreground
+            return 90;
+        case PROCESS_STATE_PERSISTENT: // Persistent system services
+        case PROCESS_STATE_PERSISTENT_UI:
+            return 80;
+        case PROCESS_STATE_IMPORTANT_BACKGROUND: // "Important" background processes
+            return 70;
+        case PROCESS_STATE_BACKUP: // Everything else
+        case PROCESS_STATE_HEAVY_WEIGHT:
+        case PROCESS_STATE_SERVICE:
+        case PROCESS_STATE_RECEIVER:
+        case PROCESS_STATE_HOME:
+        case PROCESS_STATE_LAST_ACTIVITY:
+        case PROCESS_STATE_CACHED_ACTIVITY:
+        case PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
+        case PROCESS_STATE_CACHED_EMPTY:
+            return 1;
+        case PROCESS_STATE_NONEXISTENT:
+            return -1;
+        default:
+            ALOGE("%s: Received unknown process state from ActivityManagerService!", __FUNCTION__);
+            return -1;
+    }
+}
+
 status_t CameraService::getCameraVendorTagDescriptor(/*out*/sp<VendorTagDescriptor>& desc) {
     if (!mModule) {
         ALOGE("%s: camera hardware module doesn't exist", __FUNCTION__);
@@ -545,54 +613,90 @@
     return true;
 }
 
-status_t CameraService::initializeShimMetadata(int cameraId) {
-    int pid = getCallingPid();
-    int uid = getCallingUid();
-    status_t ret = validateConnect(cameraId, uid);
-    if (ret != OK) {
-        // Error already logged by callee
-        return ret;
+status_t CameraService::makeClient(const sp<CameraService>& cameraService,
+        const sp<IInterface>& cameraCb, const String16& packageName, const String8& cameraId,
+        int facing, int clientPid, uid_t clientUid, int servicePid, bool legacyMode,
+        int halVersion, int deviceVersion, apiLevel effectiveApiLevel,
+        /*out*/sp<BasicClient>* client) {
+
+    // TODO: Update CameraClients + HAL interface to use strings for Camera IDs
+    int id = cameraIdToInt(cameraId);
+    if (id == -1) {
+        ALOGE("%s: Invalid camera ID %s, cannot convert to integer.", __FUNCTION__,
+                cameraId.string());
+        return BAD_VALUE;
     }
 
-    bool needsNewClient = false;
-    sp<Client> client;
+    if (halVersion < 0 || halVersion == deviceVersion) {
+        // Default path: HAL version is unspecified by caller, create CameraClient
+        // based on device version reported by the HAL.
+        switch(deviceVersion) {
+          case CAMERA_DEVICE_API_VERSION_1_0:
+            if (effectiveApiLevel == API_1) {  // Camera1 API route
+                sp<ICameraClient> tmp = static_cast<ICameraClient*>(cameraCb.get());
+                *client = new CameraClient(cameraService, tmp, packageName, id, facing,
+                        clientPid, clientUid, getpid(), legacyMode);
+            } else { // Camera2 API route
+                ALOGW("Camera using old HAL version: %d", deviceVersion);
+                return -EOPNOTSUPP;
+            }
+            break;
+          case CAMERA_DEVICE_API_VERSION_2_0:
+          case CAMERA_DEVICE_API_VERSION_2_1:
+          case CAMERA_DEVICE_API_VERSION_3_0:
+          case CAMERA_DEVICE_API_VERSION_3_1:
+          case CAMERA_DEVICE_API_VERSION_3_2:
+          case CAMERA_DEVICE_API_VERSION_3_3:
+            if (effectiveApiLevel == API_1) { // Camera1 API route
+                sp<ICameraClient> tmp = static_cast<ICameraClient*>(cameraCb.get());
+                *client = new Camera2Client(cameraService, tmp, packageName, id, facing,
+                        clientPid, clientUid, servicePid, legacyMode);
+            } else { // Camera2 API route
+                sp<ICameraDeviceCallbacks> tmp =
+                        static_cast<ICameraDeviceCallbacks*>(cameraCb.get());
+                *client = new CameraDeviceClient(cameraService, tmp, packageName, id,
+                        facing, clientPid, clientUid, servicePid);
+            }
+            break;
+          default:
+            // Should not be reachable
+            ALOGE("Unknown camera device HAL version: %d", deviceVersion);
+            return INVALID_OPERATION;
+        }
+    } else {
+        // A particular HAL version is requested by caller. Create CameraClient
+        // based on the requested HAL version.
+        if (deviceVersion > CAMERA_DEVICE_API_VERSION_1_0 &&
+            halVersion == CAMERA_DEVICE_API_VERSION_1_0) {
+            // Only support higher HAL version device opened as HAL1.0 device.
+            sp<ICameraClient> tmp = static_cast<ICameraClient*>(cameraCb.get());
+            *client = new CameraClient(cameraService, tmp, packageName, id, facing,
+                    clientPid, clientUid, servicePid, legacyMode);
+        } else {
+            // Other combinations (e.g. HAL3.x open as HAL2.x) are not supported yet.
+            ALOGE("Invalid camera HAL version %x: HAL %x device can only be"
+                    " opened as HAL %x device", halVersion, deviceVersion,
+                    CAMERA_DEVICE_API_VERSION_1_0);
+            return INVALID_OPERATION;
+        }
+    }
+    return NO_ERROR;
+}
+
+status_t CameraService::initializeShimMetadata(int cameraId) {
+    int uid = getCallingUid();
 
     String16 internalPackageName("media");
-    {   // Scope for service lock
-        Mutex::Autolock lock(mServiceLock);
-        if (mClient[cameraId] != NULL) {
-            client = static_cast<Client*>(mClient[cameraId].promote().get());
-        }
-        if (client == NULL) {
-            needsNewClient = true;
-            ret = connectHelperLocked(/*out*/client,
-                                      /*cameraClient*/NULL, // Empty binder callbacks
-                                      cameraId,
-                                      internalPackageName,
-                                      uid,
-                                      pid);
-
-            if (ret != OK) {
-                // Error already logged by callee
-                return ret;
-            }
-        }
-
-        if (client == NULL) {
-            ALOGE("%s: Could not connect to client camera device.", __FUNCTION__);
-            return BAD_VALUE;
-        }
-
-        String8 rawParams = client->getParameters();
-        CameraParameters params(rawParams);
-        mShimParams.add(cameraId, params);
+    String8 id = String8::format("%d", cameraId);
+    status_t ret = NO_ERROR;
+    sp<Client> tmp = nullptr;
+    if ((ret = connectHelper<ICameraClient,Client>(sp<ICameraClient>{nullptr}, id,
+            static_cast<int>(CAMERA_HAL_API_VERSION_UNSPECIFIED), internalPackageName, uid, API_1,
+            false, true, tmp)) != NO_ERROR) {
+        ALOGE("%s: Error %d (%s) initializing shim metadata.", __FUNCTION__, ret, strerror(ret));
+        return ret;
     }
-
-    // Close client if one was opened solely for this call
-    if (needsNewClient) {
-        client->disconnect();
-    }
-    return OK;
+    return NO_ERROR;
 }
 
 status_t CameraService::getLegacyParametersLazy(int cameraId,
@@ -608,42 +712,54 @@
         return BAD_VALUE;
     }
 
-    ssize_t index = -1;
-    {   // Scope for service lock
+    String8 id = String8::format("%d", cameraId);
+
+    // Check if we already have parameters
+    {
+        // Scope for service lock
         Mutex::Autolock lock(mServiceLock);
-        index = mShimParams.indexOfKey(cameraId);
-        // Release service lock so initializeShimMetadata can be called correctly.
-
-        if (index >= 0) {
-            *parameters = mShimParams[index];
+        auto cameraState = getCameraState(id);
+        if (cameraState == nullptr) {
+            ALOGE("%s: Invalid camera ID: %s", __FUNCTION__, id.string());
+            return BAD_VALUE;
+        }
+        CameraParameters p = cameraState->getShimParams();
+        if (!p.isEmpty()) {
+            *parameters = p;
+            return NO_ERROR;
         }
     }
 
-    if (index < 0) {
-        int64_t token = IPCThreadState::self()->clearCallingIdentity();
-        ret = initializeShimMetadata(cameraId);
-        IPCThreadState::self()->restoreCallingIdentity(token);
-        if (ret != OK) {
-            // Error already logged by callee
-            return ret;
+    int64_t token = IPCThreadState::self()->clearCallingIdentity();
+    ret = initializeShimMetadata(cameraId);
+    IPCThreadState::self()->restoreCallingIdentity(token);
+    if (ret != NO_ERROR) {
+        // Error already logged by callee
+        return ret;
+    }
+
+    // Check for parameters again
+    {
+        // Scope for service lock
+        Mutex::Autolock lock(mServiceLock);
+        auto cameraState = getCameraState(id);
+        if (cameraState == nullptr) {
+            ALOGE("%s: Invalid camera ID: %s", __FUNCTION__, id.string());
+            return BAD_VALUE;
         }
-
-        {   // Scope for service lock
-            Mutex::Autolock lock(mServiceLock);
-            index = mShimParams.indexOfKey(cameraId);
-
-            LOG_ALWAYS_FATAL_IF(index < 0, "index should have been initialized");
-
-            *parameters = mShimParams[index];
+        CameraParameters p = cameraState->getShimParams();
+        if (!p.isEmpty()) {
+            *parameters = p;
+            return NO_ERROR;
         }
     }
 
-    return OK;
+    ALOGE("%s: Parameters were not initialized, or were empty.  Device may not be present.",
+            __FUNCTION__);
+    return INVALID_OPERATION;
 }
 
-status_t CameraService::validateConnect(int cameraId,
-                                    /*inout*/
-                                    int& clientUid) const {
+status_t CameraService::validateConnect(const String8& cameraId, /*inout*/int& clientUid) const {
 
     int callingPid = getCallingPid();
 
@@ -652,23 +768,25 @@
     } else {
         // We only trust our own process to forward client UIDs
         if (callingPid != getpid()) {
-            ALOGE("CameraService::connect X (pid %d) rejected (don't trust clientUid)",
+            ALOGE("CameraService::connect X (PID %d) rejected (don't trust clientUid)",
                     callingPid);
             return PERMISSION_DENIED;
         }
     }
 
     if (!mModule) {
-        ALOGE("Camera HAL module not loaded");
+        ALOGE("CameraService::connect X (PID %d) rejected (camera HAL module not loaded)",
+                callingPid);
         return -ENODEV;
     }
 
-    if (cameraId < 0 || cameraId >= mNumberOfCameras) {
-        ALOGE("CameraService::connect X (pid %d) rejected (invalid cameraId %d).",
-            callingPid, cameraId);
+    if (getCameraState(cameraId) == nullptr) {
+        ALOGE("CameraService::connect X (PID %d) rejected (invalid camera ID %s)", callingPid,
+                cameraId.string());
         return -ENODEV;
     }
 
+    // Check device policy for this camera
     char value[PROPERTY_VALUE_MAX];
     char key[PROPERTY_KEY_MAX];
     int clientUserId = multiuser_get_user_id(clientUid);
@@ -676,142 +794,218 @@
     property_get(key, value, "0");
     if (strcmp(value, "1") == 0) {
         // Camera is disabled by DevicePolicyManager.
-        ALOGI("Camera is disabled. connect X (pid %d) rejected", callingPid);
+        ALOGE("CameraService::connect X (PID %d) rejected (camera %s is disabled by device "
+                "policy)", callingPid, cameraId.string());
         return -EACCES;
     }
 
-    ICameraServiceListener::Status currentStatus = getStatus(cameraId);
+    return checkIfDeviceIsUsable(cameraId);
+}
+
+status_t CameraService::checkIfDeviceIsUsable(const String8& cameraId) const {
+    auto cameraState = getCameraState(cameraId);
+    int callingPid = getCallingPid();
+    if (cameraState == nullptr) {
+        ALOGE("CameraService::connect X (PID %d) rejected (invalid camera ID %s)", callingPid,
+                cameraId.string());
+        return -ENODEV;
+    }
+
+    ICameraServiceListener::Status currentStatus = cameraState->getStatus();
     if (currentStatus == ICameraServiceListener::STATUS_NOT_PRESENT) {
-        ALOGI("Camera is not plugged in,"
-               " connect X (pid %d) rejected", callingPid);
+        ALOGE("CameraService::connect X (PID %d) rejected (camera %s is not connected)",
+                callingPid, cameraId.string());
         return -ENODEV;
     } else if (currentStatus == ICameraServiceListener::STATUS_ENUMERATING) {
-        ALOGI("Camera is enumerating,"
-               " connect X (pid %d) rejected", callingPid);
+        ALOGE("CameraService::connect X (PID %d) rejected, (camera %s is initializing)",
+                callingPid, cameraId.string());
         return -EBUSY;
     }
-    // Else don't check for STATUS_NOT_AVAILABLE.
-    //  -- It's done implicitly in canConnectUnsafe /w the mBusy array
 
-    return OK;
+    return NO_ERROR;
 }
 
-bool CameraService::canConnectUnsafe(int cameraId,
-                                     const String16& clientPackageName,
-                                     const sp<IBinder>& remoteCallback,
-                                     sp<BasicClient> &client) {
-    String8 clientName8(clientPackageName);
-    int callingPid = getCallingPid();
+void CameraService::finishConnectLocked(const sp<BasicClient>& client,
+        const CameraService::DescriptorPtr& desc) {
 
-    if (mClient[cameraId] != 0) {
-        client = mClient[cameraId].promote();
-        if (client != 0) {
-            if (remoteCallback == client->getRemote()) {
-                LOG1("CameraService::connect X (pid %d) (the same client)",
-                     callingPid);
-                return true;
-            } else {
-                // TODOSC: need to support 1 regular client,
-                // multiple shared clients here
-                ALOGW("CameraService::connect X (pid %d) rejected"
-                      " (existing client).", callingPid);
-                return false;
+    // Make a descriptor for the incoming client
+    auto clientDescriptor = CameraService::CameraClientManager::makeClientDescriptor(client, desc);
+    auto evicted = mActiveClientManager.addAndEvict(clientDescriptor);
+
+    logConnected(desc->getKey(), static_cast<int>(desc->getOwnerId()),
+            String8(client->getPackageName()));
+
+    if (evicted.size() > 0) {
+        // This should never happen - clients should already have been removed in disconnect
+        for (auto& i : evicted) {
+            ALOGE("%s: Invalid state: Client for camera %s was not removed in disconnect",
+                    __FUNCTION__, i->getKey().string());
+        }
+
+        LOG_ALWAYS_FATAL("%s: Invalid state for CameraService, clients not evicted properly",
+                __FUNCTION__);
+    }
+}
+
+status_t CameraService::handleEvictionsLocked(const String8& cameraId, int clientPid,
+        apiLevel effectiveApiLevel, const sp<IBinder>& remoteCallback, const String8& packageName,
+        /*out*/
+        sp<BasicClient>* client,
+        std::shared_ptr<resource_policy::ClientDescriptor<String8, sp<BasicClient>>>* partial) {
+
+    status_t ret = NO_ERROR;
+    std::vector<sp<BasicClient>> evictedClients;
+    DescriptorPtr clientDescriptor;
+    {
+        if (effectiveApiLevel == API_1) {
+            // If we are using API1, any existing client for this camera ID with the same remote
+            // should be returned rather than evicted to allow MediaRecorder to work properly.
+
+            auto current = mActiveClientManager.get(cameraId);
+            if (current != nullptr) {
+                auto clientSp = current->getValue();
+                if (clientSp.get() != nullptr) { // should never be needed
+                    if (clientSp->getRemote() == remoteCallback) {
+                        ALOGI("CameraService::connect X (PID %d) (second call from same"
+                                "app binder, returning the same client)", clientPid);
+                        *client = clientSp;
+                        return NO_ERROR;
+                    }
+                }
             }
         }
-        mClient[cameraId].clear();
-    }
 
-    /*
-    mBusy is set to false as the last step of the Client destructor,
-    after which it is guaranteed that the Client destructor has finished (
-    including any inherited destructors)
+        // Return error if the device was unplugged or removed by the HAL for some reason
+        if ((ret = checkIfDeviceIsUsable(cameraId)) != NO_ERROR) {
+            return ret;
+        }
 
-    We only need this for a Client subclasses since we don't allow
-    multiple Clents to be opened concurrently, but multiple BasicClient
-    would be fine
-    */
-    if (mBusy[cameraId]) {
-        ALOGW("CameraService::connect X (pid %d, \"%s\") rejected"
-                " (camera %d is still busy).", callingPid,
-                clientName8.string(), cameraId);
-        return false;
-    }
+        // Get current active client PIDs
+        std::vector<int> ownerPids(mActiveClientManager.getAllOwners());
+        ownerPids.push_back(clientPid);
 
-    return true;
-}
+        // Use the value +PROCESS_STATE_NONEXISTENT, to avoid taking
+        // address of PROCESS_STATE_NONEXISTENT as a reference argument
+        // for the vector constructor. PROCESS_STATE_NONEXISTENT does
+        // not have an out-of-class definition.
+        std::vector<int> priorities(ownerPids.size(), +PROCESS_STATE_NONEXISTENT);
 
-status_t CameraService::connectHelperLocked(
-        /*out*/
-        sp<Client>& client,
-        /*in*/
-        const sp<ICameraClient>& cameraClient,
-        int cameraId,
-        const String16& clientPackageName,
-        int clientUid,
-        int callingPid,
-        int halVersion,
-        bool legacyMode) {
+        // Get priorites of all active PIDs
+        ProcessInfoService::getProcessStatesFromPids(ownerPids.size(), &ownerPids[0],
+                /*out*/&priorities[0]);
 
-    // give flashlight a chance to close devices if necessary.
-    mFlashlight->prepareDeviceOpen(String8::format("%d", cameraId));
+        // Update all active clients' priorities
+        std::map<int,int> pidToPriorityMap;
+        for (size_t i = 0; i < ownerPids.size() - 1; i++) {
+            pidToPriorityMap.emplace(ownerPids[i], getCameraPriorityFromProcState(priorities[i]));
+        }
+        mActiveClientManager.updatePriorities(pidToPriorityMap);
 
-    int facing = -1;
-    int deviceVersion = getDeviceVersion(cameraId, &facing);
-
-    if (halVersion < 0 || halVersion == deviceVersion) {
-        // Default path: HAL version is unspecified by caller, create CameraClient
-        // based on device version reported by the HAL.
-        switch(deviceVersion) {
-          case CAMERA_DEVICE_API_VERSION_1_0:
-            client = new CameraClient(this, cameraClient,
-                    clientPackageName, cameraId,
-                    facing, callingPid, clientUid, getpid(), legacyMode);
-            break;
-          case CAMERA_DEVICE_API_VERSION_2_0:
-          case CAMERA_DEVICE_API_VERSION_2_1:
-          case CAMERA_DEVICE_API_VERSION_3_0:
-          case CAMERA_DEVICE_API_VERSION_3_1:
-          case CAMERA_DEVICE_API_VERSION_3_2:
-            client = new Camera2Client(this, cameraClient,
-                    clientPackageName, cameraId,
-                    facing, callingPid, clientUid, getpid(), legacyMode);
-            break;
-          case -1:
-            ALOGE("Invalid camera id %d", cameraId);
+        // Get state for the given cameraId
+        auto state = getCameraState(cameraId);
+        if (state == nullptr) {
+            ALOGE("CameraService::connect X (PID %d) rejected (no camera device with ID %s)",
+                clientPid, cameraId.string());
             return BAD_VALUE;
-          default:
-            ALOGE("Unknown camera device HAL version: %d", deviceVersion);
-            return INVALID_OPERATION;
         }
-    } else {
-        // A particular HAL version is requested by caller. Create CameraClient
-        // based on the requested HAL version.
-        if (deviceVersion > CAMERA_DEVICE_API_VERSION_1_0 &&
-            halVersion == CAMERA_DEVICE_API_VERSION_1_0) {
-            // Only support higher HAL version device opened as HAL1.0 device.
-            client = new CameraClient(this, cameraClient,
-                    clientPackageName, cameraId,
-                    facing, callingPid, clientUid, getpid(), legacyMode);
-        } else {
-            // Other combinations (e.g. HAL3.x open as HAL2.x) are not supported yet.
-            ALOGE("Invalid camera HAL version %x: HAL %x device can only be"
-                    " opened as HAL %x device", halVersion, deviceVersion,
-                    CAMERA_DEVICE_API_VERSION_1_0);
-            return INVALID_OPERATION;
+
+        // Make descriptor for incoming client
+        clientDescriptor = CameraClientManager::makeClientDescriptor(cameraId,
+                sp<BasicClient>{nullptr}, static_cast<int32_t>(state->getCost()),
+                state->getConflicting(),
+                getCameraPriorityFromProcState(priorities[priorities.size() - 1]), clientPid);
+
+        // Find clients that would be evicted
+        auto evicted = mActiveClientManager.wouldEvict(clientDescriptor);
+
+        // If the incoming client was 'evicted,' higher priority clients have the camera in the
+        // background, so we cannot do evictions
+        if (std::find(evicted.begin(), evicted.end(), clientDescriptor) != evicted.end()) {
+            ALOGE("CameraService::connect X (PID %d) rejected (existing client(s) with higher"
+                    " priority).", clientPid);
+
+            sp<BasicClient> clientSp = clientDescriptor->getValue();
+            String8 curTime = getFormattedCurrentTime();
+            auto incompatibleClients =
+                    mActiveClientManager.getIncompatibleClients(clientDescriptor);
+
+            String8 msg = String8::format("%s : DENIED connect device %s client for package %s "
+                    "(PID %d, priority %d)", curTime.string(),
+                    cameraId.string(), packageName.string(), clientPid,
+                    getCameraPriorityFromProcState(priorities[priorities.size() - 1]));
+
+            for (auto& i : incompatibleClients) {
+                msg.appendFormat("\n   - Blocked by existing device %s client for package %s"
+                        "(PID %" PRId32 ", priority %" PRId32 ")", i->getKey().string(),
+                        String8{i->getValue()->getPackageName()}.string(), i->getOwnerId(),
+                        i->getPriority());
+            }
+
+            // Log the client's attempt
+            mEventLog.add(msg);
+
+            return -EBUSY;
+        }
+
+        for (auto& i : evicted) {
+            sp<BasicClient> clientSp = i->getValue();
+            if (clientSp.get() == nullptr) {
+                ALOGE("%s: Invalid state: Null client in active client list.", __FUNCTION__);
+
+                // TODO: Remove this
+                LOG_ALWAYS_FATAL("%s: Invalid state for CameraService, null client in active list",
+                        __FUNCTION__);
+                mActiveClientManager.remove(i);
+                continue;
+            }
+
+            ALOGE("CameraService::connect evicting conflicting client for camera ID %s",
+                    i->getKey().string());
+            evictedClients.push_back(clientSp);
+
+            String8 curTime = getFormattedCurrentTime();
+
+            // Log the clients evicted
+            mEventLog.add(String8::format("%s : EVICT device %s client for package %s (PID %"
+                    PRId32 ", priority %" PRId32 ")\n   - Evicted by device %s client for "
+                    "package %s (PID %d, priority %" PRId32 ")", curTime.string(),
+                    i->getKey().string(), String8{clientSp->getPackageName()}.string(),
+                    i->getOwnerId(), i->getPriority(), cameraId.string(),
+                    packageName.string(), clientPid,
+                    getCameraPriorityFromProcState(priorities[priorities.size() - 1])));
+
+            // Notify the client of disconnection
+            clientSp->notifyError(ICameraDeviceCallbacks::ERROR_CAMERA_DISCONNECTED,
+                    CaptureResultExtras());
         }
     }
 
-    status_t status = connectFinishUnsafe(client, client->getRemote());
-    if (status != OK) {
-        // this is probably not recoverable.. maybe the client can try again
-        return status;
+    // Do not hold mServiceLock while disconnecting clients, but retain the condition blocking
+    // other clients from connecting in mServiceLockWrapper if held
+    mServiceLock.unlock();
+
+    // Clear caller identity temporarily so client disconnect PID checks work correctly
+    int64_t token = IPCThreadState::self()->clearCallingIdentity();
+
+    // Destroy evicted clients
+    for (auto& i : evictedClients) {
+        // Disconnect is blocking, and should only have returned when HAL has cleaned up
+        i->disconnect(); // Clients will remove themselves from the active client list here
+    }
+    evictedClients.clear();
+
+    IPCThreadState::self()->restoreCallingIdentity(token);
+
+    // Once clients have been disconnected, relock
+    mServiceLock.lock();
+
+    // Check again if the device was unplugged or something while we weren't holding mServiceLock
+    if ((ret = checkIfDeviceIsUsable(cameraId)) != NO_ERROR) {
+        return ret;
     }
 
-    mClient[cameraId] = client;
-    LOG1("CameraService::connect X (id %d, this pid is %d)", cameraId,
-         getpid());
-
-    return OK;
+    *partial = clientDescriptor;
+    return NO_ERROR;
 }
 
 status_t CameraService::connect(
@@ -822,47 +1016,18 @@
         /*out*/
         sp<ICamera>& device) {
 
-    String8 clientName8(clientPackageName);
-    int callingPid = getCallingPid();
+    status_t ret = NO_ERROR;
+    String8 id = String8::format("%d", cameraId);
+    sp<Client> client = nullptr;
+    ret = connectHelper<ICameraClient,Client>(cameraClient, id, CAMERA_HAL_API_VERSION_UNSPECIFIED,
+            clientPackageName, clientUid, API_1, false, false, /*out*/client);
 
-    LOG1("CameraService::connect E (pid %d \"%s\", id %d)", callingPid,
-            clientName8.string(), cameraId);
-
-    status_t status = validateConnect(cameraId, /*inout*/clientUid);
-    if (status != OK) {
-        return status;
+    if(ret != NO_ERROR) {
+        return ret;
     }
 
-
-    sp<Client> client;
-    {
-        Mutex::Autolock lock(mServiceLock);
-        sp<BasicClient> clientTmp;
-        if (!canConnectUnsafe(cameraId, clientPackageName,
-                              IInterface::asBinder(cameraClient),
-                              /*out*/clientTmp)) {
-            return -EBUSY;
-        } else if (client.get() != NULL) {
-            device = static_cast<Client*>(clientTmp.get());
-            return OK;
-        }
-
-        status = connectHelperLocked(/*out*/client,
-                                     cameraClient,
-                                     cameraId,
-                                     clientPackageName,
-                                     clientUid,
-                                     callingPid);
-        if (status != OK) {
-            return status;
-        }
-
-    }
-    // important: release the mutex here so the client can call back
-    //    into the service from its destructor (can be at the end of the call)
-
     device = client;
-    return OK;
+    return NO_ERROR;
 }
 
 status_t CameraService::connectLegacy(
@@ -887,70 +1052,51 @@
         return INVALID_OPERATION;
     }
 
-    String8 clientName8(clientPackageName);
-    int callingPid = getCallingPid();
+    status_t ret = NO_ERROR;
+    String8 id = String8::format("%d", cameraId);
+    sp<Client> client = nullptr;
+    ret = connectHelper<ICameraClient,Client>(cameraClient, id, halVersion, clientPackageName,
+            clientUid, API_1, true, false, /*out*/client);
 
-    LOG1("CameraService::connect legacy E (pid %d \"%s\", id %d)", callingPid,
-            clientName8.string(), cameraId);
-
-    status_t status = validateConnect(cameraId, /*inout*/clientUid);
-    if (status != OK) {
-        return status;
+    if(ret != NO_ERROR) {
+        return ret;
     }
 
-    sp<Client> client;
-    {
-        Mutex::Autolock lock(mServiceLock);
-        sp<BasicClient> clientTmp;
-        if (!canConnectUnsafe(cameraId, clientPackageName,
-                              IInterface::asBinder(cameraClient),
-                              /*out*/clientTmp)) {
-            return -EBUSY;
-        } else if (client.get() != NULL) {
-            device = static_cast<Client*>(clientTmp.get());
-            return OK;
-        }
-
-        status = connectHelperLocked(/*out*/client,
-                                     cameraClient,
-                                     cameraId,
-                                     clientPackageName,
-                                     clientUid,
-                                     callingPid,
-                                     halVersion,
-                                     /*legacyMode*/true);
-        if (status != OK) {
-            return status;
-        }
-
-    }
-    // important: release the mutex here so the client can call back
-    //    into the service from its destructor (can be at the end of the call)
-
     device = client;
-    return OK;
+    return NO_ERROR;
 }
 
-bool CameraService::validCameraIdForSetTorchMode(const String8& cameraId) {
-    // invalid string for int
-    if (cameraId.string() == NULL) {
-        return false;
-    }
-    errno = 0;
-    char *endptr;
-    long id = strtol(cameraId.string(), &endptr, 10); // base 10
-    if (errno || id > INT_MAX || id < INT_MIN || *endptr != 0) {
-        return false;
+status_t CameraService::connectPro(const sp<IProCameraCallbacks>& cameraCb,
+                                   int cameraId,
+                                   const String16& clientPackageName,
+                                   int clientUid,
+                                   /*out*/
+                                   sp<IProCameraUser>& device) {
+    ALOGE("%s: Unimplemented, please use connectDevice", __FUNCTION__);
+    return INVALID_OPERATION;
+}
+
+status_t CameraService::connectDevice(
+        const sp<ICameraDeviceCallbacks>& cameraCb,
+        int cameraId,
+        const String16& clientPackageName,
+        int clientUid,
+        /*out*/
+        sp<ICameraDeviceUser>& device) {
+
+    status_t ret = NO_ERROR;
+    String8 id = String8::format("%d", cameraId);
+    sp<CameraDeviceClient> client = nullptr;
+    ret = connectHelper<ICameraDeviceCallbacks,CameraDeviceClient>(cameraCb, id,
+            CAMERA_HAL_API_VERSION_UNSPECIFIED, clientPackageName, clientUid, API_2, false, false,
+            /*out*/client);
+
+    if(ret != NO_ERROR) {
+        return ret;
     }
 
-    // id matches one of the plugged-in devices?
-    ICameraServiceListener::Status deviceStatus = getStatus(id);
-    if (deviceStatus != ICameraServiceListener::STATUS_PRESENT &&
-            deviceStatus != ICameraServiceListener::STATUS_NOT_AVAILABLE) {
-        return false;
-    }
-
-    return true;
+    device = client;
+    return NO_ERROR;
 }
 
 status_t CameraService::setTorchMode(const String16& cameraId, bool enabled,
@@ -963,7 +1109,15 @@
     String8 id = String8(cameraId.string());
 
     // verify id is valid.
-    if (validCameraIdForSetTorchMode(id) == false) {
+    auto state = getCameraState(id);
+    if (state == nullptr) {
+        ALOGE("%s: camera id is invalid %s", id.string());
+        return -EINVAL;
+    }
+
+    ICameraServiceListener::Status cameraStatus = state->getStatus();
+    if (cameraStatus != ICameraServiceListener::STATUS_PRESENT &&
+            cameraStatus != ICameraServiceListener::STATUS_NOT_AVAILABLE) {
         ALOGE("%s: camera id is invalid %s", id.string());
         return -EINVAL;
     }
@@ -979,8 +1133,7 @@
         }
 
         if (status == ICameraServiceListener::TORCH_STATUS_NOT_AVAILABLE) {
-            if (getStatus(atoi(id.string())) ==
-                        ICameraServiceListener::STATUS_NOT_AVAILABLE) {
+            if (cameraStatus == ICameraServiceListener::STATUS_NOT_AVAILABLE) {
                 ALOGE("%s: torch mode of camera %s is not available because "
                         "camera is in use", __FUNCTION__, id.string());
                 return -EBUSY;
@@ -1022,174 +1175,6 @@
     return OK;
 }
 
-status_t CameraService::connectFinishUnsafe(const sp<BasicClient>& client,
-                                            const sp<IBinder>& remoteCallback) {
-    status_t status = client->initialize(mModule);
-    if (status != OK) {
-        ALOGE("%s: Could not initialize client from HAL module.", __FUNCTION__);
-        return status;
-    }
-    if (remoteCallback != NULL) {
-        remoteCallback->linkToDeath(this);
-    }
-
-    return OK;
-}
-
-status_t CameraService::connectPro(
-                                        const sp<IProCameraCallbacks>& cameraCb,
-                                        int cameraId,
-                                        const String16& clientPackageName,
-                                        int clientUid,
-                                        /*out*/
-                                        sp<IProCameraUser>& device)
-{
-    if (cameraCb == 0) {
-        ALOGE("%s: Callback must not be null", __FUNCTION__);
-        return BAD_VALUE;
-    }
-
-    String8 clientName8(clientPackageName);
-    int callingPid = getCallingPid();
-
-    LOG1("CameraService::connectPro E (pid %d \"%s\", id %d)", callingPid,
-            clientName8.string(), cameraId);
-    status_t status = validateConnect(cameraId, /*inout*/clientUid);
-    if (status != OK) {
-        return status;
-    }
-
-    sp<ProClient> client;
-    {
-        Mutex::Autolock lock(mServiceLock);
-        {
-            sp<BasicClient> client;
-            if (!canConnectUnsafe(cameraId, clientPackageName,
-                                  IInterface::asBinder(cameraCb),
-                                  /*out*/client)) {
-                return -EBUSY;
-            }
-        }
-
-        int facing = -1;
-        int deviceVersion = getDeviceVersion(cameraId, &facing);
-
-        switch(deviceVersion) {
-          case CAMERA_DEVICE_API_VERSION_1_0:
-            ALOGE("Camera id %d uses HALv1, doesn't support ProCamera",
-                  cameraId);
-            return -EOPNOTSUPP;
-            break;
-          case CAMERA_DEVICE_API_VERSION_2_0:
-          case CAMERA_DEVICE_API_VERSION_2_1:
-          case CAMERA_DEVICE_API_VERSION_3_0:
-          case CAMERA_DEVICE_API_VERSION_3_1:
-          case CAMERA_DEVICE_API_VERSION_3_2:
-            client = new ProCamera2Client(this, cameraCb, clientPackageName,
-                    cameraId, facing, callingPid, clientUid, getpid());
-            break;
-          case -1:
-            ALOGE("Invalid camera id %d", cameraId);
-            return BAD_VALUE;
-          default:
-            ALOGE("Unknown camera device HAL version: %d", deviceVersion);
-            return INVALID_OPERATION;
-        }
-
-        status_t status = connectFinishUnsafe(client, client->getRemote());
-        if (status != OK) {
-            return status;
-        }
-
-        mProClientList[cameraId].push(client);
-
-        LOG1("CameraService::connectPro X (id %d, this pid is %d)", cameraId,
-                getpid());
-    }
-    // important: release the mutex here so the client can call back
-    //    into the service from its destructor (can be at the end of the call)
-    device = client;
-    return OK;
-}
-
-status_t CameraService::connectDevice(
-        const sp<ICameraDeviceCallbacks>& cameraCb,
-        int cameraId,
-        const String16& clientPackageName,
-        int clientUid,
-        /*out*/
-        sp<ICameraDeviceUser>& device)
-{
-
-    String8 clientName8(clientPackageName);
-    int callingPid = getCallingPid();
-
-    LOG1("CameraService::connectDevice E (pid %d \"%s\", id %d)", callingPid,
-            clientName8.string(), cameraId);
-
-    status_t status = validateConnect(cameraId, /*inout*/clientUid);
-    if (status != OK) {
-        return status;
-    }
-
-    sp<CameraDeviceClient> client;
-    {
-        Mutex::Autolock lock(mServiceLock);
-        {
-            sp<BasicClient> client;
-            if (!canConnectUnsafe(cameraId, clientPackageName,
-                                  IInterface::asBinder(cameraCb),
-                                  /*out*/client)) {
-                return -EBUSY;
-            }
-        }
-
-        int facing = -1;
-        int deviceVersion = getDeviceVersion(cameraId, &facing);
-
-        // give flashlight a chance to close devices if necessary.
-        mFlashlight->prepareDeviceOpen(String8::format("%d", cameraId));
-
-        switch(deviceVersion) {
-          case CAMERA_DEVICE_API_VERSION_1_0:
-            ALOGW("Camera using old HAL version: %d", deviceVersion);
-            return -EOPNOTSUPP;
-           // TODO: don't allow 2.0  Only allow 2.1 and higher
-          case CAMERA_DEVICE_API_VERSION_2_0:
-          case CAMERA_DEVICE_API_VERSION_2_1:
-          case CAMERA_DEVICE_API_VERSION_3_0:
-          case CAMERA_DEVICE_API_VERSION_3_1:
-          case CAMERA_DEVICE_API_VERSION_3_2:
-            client = new CameraDeviceClient(this, cameraCb, clientPackageName,
-                    cameraId, facing, callingPid, clientUid, getpid());
-            break;
-          case -1:
-            ALOGE("Invalid camera id %d", cameraId);
-            return BAD_VALUE;
-          default:
-            ALOGE("Unknown camera device HAL version: %d", deviceVersion);
-            return INVALID_OPERATION;
-        }
-
-        status_t status = connectFinishUnsafe(client, client->getRemote());
-        if (status != OK) {
-            // this is probably not recoverable.. maybe the client can try again
-            return status;
-        }
-
-        LOG1("CameraService::connectDevice X (id %d, this pid is %d)", cameraId,
-                getpid());
-
-        mClient[cameraId] = client;
-    }
-    // important: release the mutex here so the client can call back
-    //    into the service from its destructor (can be at the end of the call)
-
-    device = client;
-    return OK;
-}
-
-
 status_t CameraService::addListener(
                                 const sp<ICameraServiceListener>& listener) {
     ALOGV("%s: Add listener %p", __FUNCTION__, listener.get());
@@ -1201,23 +1186,29 @@
 
     Mutex::Autolock lock(mServiceLock);
 
-    Vector<sp<ICameraServiceListener> >::iterator it, end;
-    for (it = mListenerList.begin(); it != mListenerList.end(); ++it) {
-        if (IInterface::asBinder(*it) == IInterface::asBinder(listener)) {
-            ALOGW("%s: Tried to add listener %p which was already subscribed",
-                  __FUNCTION__, listener.get());
-            return ALREADY_EXISTS;
+    {
+        Mutex::Autolock lock(mStatusListenerLock);
+        for (auto& it : mListenerList) {
+            if (IInterface::asBinder(it) == IInterface::asBinder(listener)) {
+                ALOGW("%s: Tried to add listener %p which was already subscribed",
+                      __FUNCTION__, listener.get());
+                return ALREADY_EXISTS;
+            }
         }
+
+        mListenerList.push_back(listener);
     }
 
-    mListenerList.push_back(listener);
 
     /* Immediately signal current status to this listener only */
     {
-        Mutex::Autolock m(mStatusMutex) ;
-        int numCams = getNumberOfCameras();
-        for (int i = 0; i < numCams; ++i) {
-            listener->onStatusChanged(mStatusList[i], i);
+        Mutex::Autolock lock(mCameraStatesLock);
+        for (auto& i : mCameraStates) {
+            // TODO: Update binder to use String16 for camera IDs and remove;
+            int id = cameraIdToInt(i.first);
+            if (id == -1) continue;
+
+            listener->onStatusChanged(i.second->getStatus(), id);
         }
     }
 
@@ -1228,13 +1219,12 @@
             String16 id = String16(mTorchStatusMap.keyAt(i).string());
             listener->onTorchStatusChanged(mTorchStatusMap.valueAt(i), id);
         }
-
     }
 
     return OK;
 }
-status_t CameraService::removeListener(
-                                const sp<ICameraServiceListener>& listener) {
+
+status_t CameraService::removeListener(const sp<ICameraServiceListener>& listener) {
     ALOGV("%s: Remove listener %p", __FUNCTION__, listener.get());
 
     if (listener == 0) {
@@ -1244,11 +1234,13 @@
 
     Mutex::Autolock lock(mServiceLock);
 
-    Vector<sp<ICameraServiceListener> >::iterator it;
-    for (it = mListenerList.begin(); it != mListenerList.end(); ++it) {
-        if (IInterface::asBinder(*it) == IInterface::asBinder(listener)) {
-            mListenerList.erase(it);
-            return OK;
+    {
+        Mutex::Autolock lock(mStatusListenerLock);
+        for (auto it = mListenerList.begin(); it != mListenerList.end(); it++) {
+            if (IInterface::asBinder(*it) == IInterface::asBinder(listener)) {
+                mListenerList.erase(it);
+                return OK;
+            }
         }
     }
 
@@ -1258,10 +1250,7 @@
     return BAD_VALUE;
 }
 
-status_t CameraService::getLegacyParameters(
-            int cameraId,
-            /*out*/
-            String16* parameters) {
+status_t CameraService::getLegacyParameters(int cameraId, /*out*/String16* parameters) {
     ALOGV("%s: for camera ID = %d", __FUNCTION__, cameraId);
 
     if (parameters == NULL) {
@@ -1316,6 +1305,7 @@
             return OK;
         }
       case CAMERA_DEVICE_API_VERSION_3_2:
+      case CAMERA_DEVICE_API_VERSION_3_3:
         ALOGV("%s: Camera id %d uses HAL3.2 or newer, supports api1/api2 directly",
                 __FUNCTION__, cameraId);
         return OK;
@@ -1330,127 +1320,107 @@
     return OK;
 }
 
-void CameraService::removeClientByRemote(const wp<IBinder>& remoteBinder) {
-    int callingPid = getCallingPid();
-    LOG1("CameraService::removeClientByRemote E (pid %d)", callingPid);
-
-    // Declare this before the lock to make absolutely sure the
-    // destructor won't be called with the lock held.
+void CameraService::removeByClient(const BasicClient* client) {
     Mutex::Autolock lock(mServiceLock);
-
-    int outIndex;
-    sp<BasicClient> client = findClientUnsafe(remoteBinder, outIndex);
-
-    if (client != 0) {
-        // Found our camera, clear and leave.
-        LOG1("removeClient: clear camera %d", outIndex);
-
-        sp<IBinder> remote = client->getRemote();
-        if (remote != NULL) {
-            remote->unlinkToDeath(this);
-        }
-
-        mClient[outIndex].clear();
-    } else {
-
-        sp<ProClient> clientPro = findProClientUnsafe(remoteBinder);
-
-        if (clientPro != NULL) {
-            // Found our camera, clear and leave.
-            LOG1("removeClient: clear pro %p", clientPro.get());
-
-            IInterface::asBinder(clientPro->getRemoteCallback())->unlinkToDeath(this);
+    for (auto& i : mActiveClientManager.getAll()) {
+        auto clientSp = i->getValue();
+        if (clientSp.get() == client) {
+            mActiveClientManager.remove(i);
         }
     }
-
-    LOG1("CameraService::removeClientByRemote X (pid %d)", callingPid);
 }
 
-sp<CameraService::ProClient> CameraService::findProClientUnsafe(
-                        const wp<IBinder>& cameraCallbacksRemote)
-{
-    sp<ProClient> clientPro;
+bool CameraService::evictClientIdByRemote(const wp<IBinder>& remote) {
+    const int callingPid = getCallingPid();
+    const int servicePid = getpid();
+    bool ret = false;
+    {
+        // Acquire mServiceLock and prevent other clients from connecting
+        std::unique_ptr<AutoConditionLock> lock =
+                AutoConditionLock::waitAndAcquire(mServiceLockWrapper);
 
-    for (int i = 0; i < mNumberOfCameras; ++i) {
-        Vector<size_t> removeIdx;
 
-        for (size_t j = 0; j < mProClientList[i].size(); ++j) {
-            wp<ProClient> cl = mProClientList[i][j];
+        std::vector<sp<BasicClient>> evicted;
+        for (auto& i : mActiveClientManager.getAll()) {
+            auto clientSp = i->getValue();
+            if (clientSp.get() == nullptr) {
+                ALOGE("%s: Dead client still in mActiveClientManager.", __FUNCTION__);
+                mActiveClientManager.remove(i);
+                continue;
+            }
+            if (remote == clientSp->getRemote() && (callingPid == servicePid ||
+                    callingPid == clientSp->getClientPid())) {
+                mActiveClientManager.remove(i);
+                evicted.push_back(clientSp);
 
-            sp<ProClient> clStrong = cl.promote();
-            if (clStrong != NULL && clStrong->getRemote() == cameraCallbacksRemote) {
-                clientPro = clStrong;
-                break;
-            } else if (clStrong == NULL) {
-                // mark to clean up dead ptr
-                removeIdx.push(j);
+                // Notify the client of disconnection
+                clientSp->notifyError(ICameraDeviceCallbacks::ERROR_CAMERA_DISCONNECTED,
+                        CaptureResultExtras());
             }
         }
 
-        // remove stale ptrs (in reverse so the indices dont change)
-        for (ssize_t j = (ssize_t)removeIdx.size() - 1; j >= 0; --j) {
-            mProClientList[i].removeAt(removeIdx[j]);
+        // Do not hold mServiceLock while disconnecting clients, but retain the condition blocking
+        // other clients from connecting in mServiceLockWrapper if held
+        mServiceLock.unlock();
+
+        for (auto& i : evicted) {
+            if (i.get() != nullptr) {
+                i->disconnect();
+                ret = true;
+            }
         }
 
-    }
+        // Reacquire mServiceLock
+        mServiceLock.lock();
 
-    return clientPro;
+    } // lock is destroyed, allow further connect calls
+
+    return ret;
 }
 
-sp<CameraService::BasicClient> CameraService::findClientUnsafe(
-                        const wp<IBinder>& cameraClient, int& outIndex) {
-    sp<BasicClient> client;
 
-    for (int i = 0; i < mNumberOfCameras; i++) {
-
-        // This happens when we have already disconnected (or this is
-        // just another unused camera).
-        if (mClient[i] == 0) continue;
-
-        // Promote mClient. It can fail if we are called from this path:
-        // Client::~Client() -> disconnect() -> removeClientByRemote().
-        client = mClient[i].promote();
-
-        // Clean up stale client entry
-        if (client == NULL) {
-            mClient[i].clear();
-            continue;
-        }
-
-        if (cameraClient == client->getRemote()) {
-            // Found our camera
-            outIndex = i;
-            return client;
+std::shared_ptr<CameraService::CameraState> CameraService::getCameraState(
+        const String8& cameraId) const {
+    std::shared_ptr<CameraState> state;
+    {
+        Mutex::Autolock lock(mCameraStatesLock);
+        auto iter = mCameraStates.find(cameraId);
+        if (iter != mCameraStates.end()) {
+            state = iter->second;
         }
     }
-
-    outIndex = -1;
-    return NULL;
+    return state;
 }
 
-CameraService::BasicClient* CameraService::getClientByIdUnsafe(int cameraId) {
-    if (cameraId < 0 || cameraId >= mNumberOfCameras) return NULL;
-    return mClient[cameraId].unsafe_get();
+sp<CameraService::BasicClient> CameraService::removeClientLocked(const String8& cameraId) {
+    // Remove from active clients list
+    auto clientDescriptorPtr = mActiveClientManager.remove(cameraId);
+    if (clientDescriptorPtr == nullptr) {
+        ALOGW("%s: Could not evict client, no client for camera ID %s", __FUNCTION__,
+                cameraId.string());
+        return sp<BasicClient>{nullptr};
+    }
+
+    return clientDescriptorPtr->getValue();
 }
 
-Mutex* CameraService::getClientLockById(int cameraId) {
-    if (cameraId < 0 || cameraId >= mNumberOfCameras) return NULL;
-    return &mClientLock[cameraId];
+
+void CameraService::logDisconnected(const String8& cameraId, int clientPid,
+        const String8& clientPackage) {
+
+    String8 curTime = getFormattedCurrentTime();
+    // Log the clients evicted
+    mEventLog.add(String8::format("%s : DISCONNECT device %s client for package %s (PID %d)",
+            curTime.string(), cameraId.string(), clientPackage.string(), clientPid));
 }
 
-sp<CameraService::BasicClient> CameraService::getClientByRemote(
-                                const wp<IBinder>& cameraClient) {
+void CameraService::logConnected(const String8& cameraId, int clientPid,
+        const String8& clientPackage) {
 
-    // Declare this before the lock to make absolutely sure the
-    // destructor won't be called with the lock held.
-    sp<BasicClient> client;
-
-    Mutex::Autolock lock(mServiceLock);
-
-    int outIndex;
-    client = findClientUnsafe(cameraClient, outIndex);
-
-    return client;
+    String8 curTime = getFormattedCurrentTime();
+    // Log the clients evicted
+    mEventLog.add(String8::format("%s : CONNECT device %s client for package %s (PID %d)",
+            curTime.string(), cameraId.string(), clientPackage.string(), clientPid));
 }
 
 status_t CameraService::onTransact(
@@ -1479,24 +1449,6 @@
     return BnCameraService::onTransact(code, data, reply, flags);
 }
 
-// The reason we need this busy bit is a new CameraService::connect() request
-// may come in while the previous Client's destructor has not been run or is
-// still running. If the last strong reference of the previous Client is gone
-// but the destructor has not been finished, we should not allow the new Client
-// to be created because we need to wait for the previous Client to tear down
-// 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.
 // A reference count is kept to determine when we will actually release the
 // media players.
@@ -1565,7 +1517,6 @@
 
     mRemoteCallback = cameraClient;
 
-    cameraService->setCameraBusy(cameraId);
     cameraService->loadSound();
 
     LOG1("Client::Client X (pid %d, id %d)", callingPid, cameraId);
@@ -1587,7 +1538,7 @@
         int cameraId, int cameraFacing,
         int clientPid, uid_t clientUid,
         int servicePid):
-        mClientPackageName(clientPackageName)
+        mClientPackageName(clientPackageName), mDisconnected(false)
 {
     mCameraService = cameraService;
     mRemoteBinder = remoteCallback;
@@ -1606,14 +1557,34 @@
 }
 
 void CameraService::BasicClient::disconnect() {
-    ALOGV("BasicClient::disconnect");
-    mCameraService->removeClientByRemote(mRemoteBinder);
+    if (mDisconnected) return;
+    mDisconnected = true;;
+
+    mCameraService->removeByClient(this);
+    mCameraService->logDisconnected(String8::format("%d", mCameraId), mClientPid,
+            String8(mClientPackageName));
+
+    sp<IBinder> remote = getRemote();
+    if (remote != nullptr) {
+        remote->unlinkToDeath(mCameraService);
+    }
 
     finishCameraOps();
+    ALOGI("%s: Disconnected client for camera %d for PID %d", __FUNCTION__, mCameraId, mClientPid);
+
     // client shouldn't be able to call into us anymore
     mClientPid = 0;
 }
 
+String16 CameraService::BasicClient::getPackageName() const {
+    return mClientPackageName;
+}
+
+
+int CameraService::BasicClient::getClientPid() const {
+    return mClientPid;
+}
+
 status_t CameraService::BasicClient::startCameraOps() {
     int32_t res;
     // Notify app ops that the camera is not available
@@ -1639,7 +1610,7 @@
 
     // Transition device availability listeners from PRESENT -> NOT_AVAILABLE
     mCameraService->updateStatus(ICameraServiceListener::STATUS_NOT_AVAILABLE,
-            mCameraId);
+            String8::format("%d", mCameraId));
 
     return OK;
 }
@@ -1652,18 +1623,12 @@
                 mClientPackageName);
         mOpsActive = false;
 
-        // Notify device availability listeners that this camera is available
-        // again
+        auto rejected = {ICameraServiceListener::STATUS_NOT_PRESENT,
+                ICameraServiceListener::STATUS_ENUMERATING};
 
-        StatusVector rejectSourceStates;
-        rejectSourceStates.push_back(ICameraServiceListener::STATUS_NOT_PRESENT);
-        rejectSourceStates.push_back(ICameraServiceListener::STATUS_ENUMERATING);
-
-        // Transition to PRESENT if the camera is not in either of above 2
-        // states
+        // Transition to PRESENT if the camera is not in either of the rejected states
         mCameraService->updateStatus(ICameraServiceListener::STATUS_PRESENT,
-                mCameraId,
-                &rejectSourceStates);
+                String8::format("%d", mCameraId), rejected);
 
         // Notify flashlight that a camera device is closed.
         mCameraService->mFlashlight->deviceClosed(
@@ -1710,26 +1675,15 @@
 
 // ----------------------------------------------------------------------------
 
-Mutex* CameraService::Client::getClientLockFromCookie(void* user) {
-    return gCameraService->getClientLockById((int)(intptr_t) user);
-}
-
-// Provide client pointer for callbacks. Client lock returned from getClientLockFromCookie should
-// be acquired for this to be safe
-CameraService::Client* CameraService::Client::getClientFromCookie(void* user) {
-    BasicClient *basicClient = gCameraService->getClientByIdUnsafe((int)(intptr_t) user);
-    // OK: only CameraClient calls this, and they already cast anyway.
-    Client* client = static_cast<Client*>(basicClient);
-
-    // This could happen if the Client is in the process of shutting down (the
-    // last strong reference is gone, but the destructor hasn't finished
-    // stopping the hardware).
-    if (client == NULL) return NULL;
-
-    // destruction already started, so should not be accessed
-    if (client->mDestructionStarted) return NULL;
-
-    return client;
+// Provide client strong pointer for callbacks.
+sp<CameraService::Client> CameraService::Client::getClientFromCookie(void* user) {
+    String8 cameraId = String8::format("%d", (int)(intptr_t) user);
+    auto clientDescriptor = gCameraService->mActiveClientManager.get(cameraId);
+    if (clientDescriptor != nullptr) {
+        return sp<Client>{
+                static_cast<Client*>(clientDescriptor->getValue().get())};
+    }
+    return sp<Client>{nullptr};
 }
 
 void CameraService::Client::notifyError(ICameraDeviceCallbacks::CameraErrorCode errorCode,
@@ -1741,7 +1695,6 @@
 void CameraService::Client::disconnect() {
     ALOGV("Client::disconnect");
     BasicClient::disconnect();
-    mCameraService->setCameraFree(mCameraId);
 }
 
 CameraService::Client::OpsCallback::OpsCallback(wp<BasicClient> client):
@@ -1784,6 +1737,104 @@
 }
 
 // ----------------------------------------------------------------------------
+//                  CameraState
+// ----------------------------------------------------------------------------
+
+CameraService::CameraState::CameraState(const String8& id, int cost,
+        const std::set<String8>& conflicting) : mId(id),
+        mStatus(ICameraServiceListener::STATUS_PRESENT), mCost(cost), mConflicting(conflicting) {}
+
+CameraService::CameraState::~CameraState() {}
+
+ICameraServiceListener::Status CameraService::CameraState::getStatus() const {
+    Mutex::Autolock lock(mStatusLock);
+    return mStatus;
+}
+
+CameraParameters CameraService::CameraState::getShimParams() const {
+    return mShimParams;
+}
+
+void CameraService::CameraState::setShimParams(const CameraParameters& params) {
+    mShimParams = params;
+}
+
+int CameraService::CameraState::getCost() const {
+    return mCost;
+}
+
+std::set<String8> CameraService::CameraState::getConflicting() const {
+    return mConflicting;
+}
+
+String8 CameraService::CameraState::getId() const {
+    return mId;
+}
+
+// ----------------------------------------------------------------------------
+//                  CameraClientManager
+// ----------------------------------------------------------------------------
+
+CameraService::CameraClientManager::~CameraClientManager() {}
+
+sp<CameraService::BasicClient> CameraService::CameraClientManager::getCameraClient(
+        const String8& id) const {
+    auto descriptor = get(id);
+    if (descriptor == nullptr) {
+        return sp<BasicClient>{nullptr};
+    }
+    return descriptor->getValue();
+}
+
+String8 CameraService::CameraClientManager::toString() const {
+    auto all = getAll();
+    String8 ret("[");
+    bool hasAny = false;
+    for (auto& i : all) {
+        hasAny = true;
+        String8 key = i->getKey();
+        int32_t cost = i->getCost();
+        int32_t pid = i->getOwnerId();
+        int32_t priority = i->getPriority();
+        auto conflicting = i->getConflicting();
+        auto clientSp = i->getValue();
+        String8 packageName;
+        if (clientSp.get() != nullptr) {
+            packageName = String8{clientSp->getPackageName()};
+        }
+        ret.appendFormat("\n(Camera ID: %s, Cost: %" PRId32 ", PID: %" PRId32 ", Priority: %"
+                PRId32 ", ", key.string(), cost, pid, priority);
+
+        if (packageName.size() != 0) {
+            ret.appendFormat("Client Package Name: %s", packageName.string());
+        }
+
+        ret.append(", Conflicting Client Devices: {");
+        for (auto& j : conflicting) {
+            ret.appendFormat("%s, ", j.string());
+        }
+        ret.append("})");
+    }
+    if (hasAny) ret.append("\n");
+    ret.append("]\n");
+    return ret;
+}
+
+CameraService::DescriptorPtr CameraService::CameraClientManager::makeClientDescriptor(
+        const String8& key, const sp<BasicClient>& value, int32_t cost,
+        const std::set<String8>& conflictingKeys, int32_t priority, int32_t ownerId) {
+
+    return std::make_shared<resource_policy::ClientDescriptor<String8, sp<BasicClient>>>(
+            key, value, cost, conflictingKeys, priority, ownerId);
+}
+
+CameraService::DescriptorPtr CameraService::CameraClientManager::makeClientDescriptor(
+        const sp<BasicClient>& value, const CameraService::DescriptorPtr& partial) {
+    return makeClientDescriptor(partial->getKey(), value, partial->getCost(),
+            partial->getConflicting(), partial->getPriority(), partial->getOwnerId());
+}
+
+// ----------------------------------------------------------------------------
 
 static const int kDumpLockRetries = 50;
 static const int kDumpLockSleep = 60000;
@@ -1826,11 +1877,14 @@
         }
 
         const hw_module_t* common = mModule->getRawModule();
-        result = String8::format("Camera module HAL API version: 0x%x\n", common->hal_api_version);
-        result.appendFormat("Camera module API version: 0x%x\n", common->module_api_version);
+        result = String8::format("Camera module HAL API version: %#x\n", common->hal_api_version);
+        result.appendFormat("Camera module API version: %#x\n", common->module_api_version);
         result.appendFormat("Camera module name: %s\n", common->name);
         result.appendFormat("Camera module author: %s\n", common->author);
-        result.appendFormat("Number of camera devices: %d\n\n", mNumberOfCameras);
+        result.appendFormat("Number of camera devices: %d\n", mNumberOfCameras);
+        String8 activeClientString = mActiveClientManager.toString();
+        result.appendFormat("Active Camera Clients:\n%s", activeClientString.string());
+
 
         sp<VendorTagDescriptor> desc = VendorTagDescriptor::getGlobalVendorTagDescriptor();
         if (desc == NULL) {
@@ -1845,11 +1899,31 @@
             desc->dump(fd, /*verbosity*/2, /*indentation*/4);
         }
 
-        for (int i = 0; i < mNumberOfCameras; i++) {
-            result = String8::format("Camera %d static information:\n", i);
+        result = String8("Prior client events (most recent at top):\n");
+
+        for (const auto& msg : mEventLog) {
+            result.appendFormat("%s\n", msg.string());
+        }
+
+        if (mEventLog.size() == DEFAULT_EVICTION_LOG_LENGTH) {
+            result.append("...\n");
+        }
+
+        write(fd, result.string(), result.size());
+
+        bool stateLocked = tryLock(mCameraStatesLock);
+        if (!stateLocked) {
+            result = String8::format("CameraStates in use, may be deadlocked\n");
+            write(fd, result.string(), result.size());
+        }
+
+        for (auto& state : mCameraStates) {
+            String8 cameraId = state.first;
+            result = String8::format("Camera %s information:\n", cameraId.string());
             camera_info info;
 
-            status_t rc = mModule->getCameraInfo(i, &info);
+            // TODO: Change getCameraInfo + HAL to use String cameraIds
+            status_t rc = mModule->getCameraInfo(cameraIdToInt(cameraId), &info);
             if (rc != OK) {
                 result.appendFormat("  Error reading static information!\n");
                 write(fd, result.string(), result.size());
@@ -1863,7 +1937,19 @@
                 } else {
                     deviceVersion = info.device_version;
                 }
-                result.appendFormat("  Device version: 0x%x\n", deviceVersion);
+
+                auto conflicting = state.second->getConflicting();
+                result.appendFormat("  Resource Cost: %d\n", state.second->getCost());
+                result.appendFormat("  Conflicting Devices:");
+                for (auto& id : conflicting) {
+                    result.appendFormat(" %s", cameraId.string());
+                }
+                if (conflicting.size() == 0) {
+                    result.appendFormat(" NONE");
+                }
+                result.appendFormat("\n");
+
+                result.appendFormat("  Device version: %#x\n", deviceVersion);
                 if (deviceVersion >= CAMERA_DEVICE_API_VERSION_2_0) {
                     result.appendFormat("  Device static metadata:\n");
                     write(fd, result.string(), result.size());
@@ -1872,19 +1958,38 @@
                 } else {
                     write(fd, result.string(), result.size());
                 }
+
+                CameraParameters p = state.second->getShimParams();
+                if (!p.isEmpty()) {
+                    result = String8::format("  Camera1 API shim is using parameters:\n        ");
+                    write(fd, result.string(), result.size());
+                    p.dump(fd, args);
+                }
             }
 
-            sp<BasicClient> client = mClient[i].promote();
-            if (client == 0) {
-                result = String8::format("  Device is closed, no client instance\n");
+            auto clientDescriptor = mActiveClientManager.get(cameraId);
+            if (clientDescriptor == nullptr) {
+                result = String8::format("  Device %s is closed, no client instance\n",
+                        cameraId.string());
                 write(fd, result.string(), result.size());
                 continue;
             }
             hasClient = true;
-            result = String8::format("  Device is open. Client instance dump:\n");
+            result = String8::format("  Device %s is open. Client instance dump:\n\n",
+                    cameraId.string());
+            result.appendFormat("Client priority level: %d\n", clientDescriptor->getPriority());
+            result.appendFormat("Client PID: %d\n", clientDescriptor->getOwnerId());
+
+            auto client = clientDescriptor->getValue();
+            result.appendFormat("Client package: %s\n",
+                    String8(client->getPackageName()).string());
             write(fd, result.string(), result.size());
+
             client->dump(fd, args);
         }
+
+        if (stateLocked) mCameraStatesLock.unlock();
+
         if (!hasClient) {
             result = String8::format("\nNo active camera clients yet.\n");
             write(fd, result.string(), result.size());
@@ -1908,7 +2013,6 @@
                 write(fd, result.string(), result.size());
             }
         }
-
     }
     return NO_ERROR;
 }
@@ -1931,124 +2035,68 @@
     }
 }
 
-/*virtual*/void CameraService::binderDied(
-    const wp<IBinder> &who) {
+/*virtual*/void CameraService::binderDied(const wp<IBinder> &who) {
 
     /**
       * While tempting to promote the wp<IBinder> into a sp,
       * it's actually not supported by the binder driver
       */
 
-    ALOGV("java clients' binder died");
-
     // check torch client
     handleTorchClientBinderDied(who);
 
     // check camera device client
-    sp<BasicClient> cameraClient = getClientByRemote(who);
-
-    if (cameraClient == 0) {
-        ALOGV("java clients' binder death already cleaned up (normal case)");
+    if(!evictClientIdByRemote(who)) {
+        ALOGV("%s: Java client's binder death already cleaned up (normal case)", __FUNCTION__);
         return;
     }
 
-    ALOGW("Disconnecting camera client %p since the binder for it "
-          "died (this pid %d)", cameraClient.get(), getCallingPid());
-
-    cameraClient->disconnect();
-
+    ALOGE("%s: Java client's binder died, removing it from the list of active clients",
+            __FUNCTION__);
 }
 
-void CameraService::updateStatus(ICameraServiceListener::Status status,
-                                 int32_t cameraId,
-                                 const StatusVector *rejectSourceStates) {
-    // do not lock mServiceLock here or can get into a deadlock from
-    //  connect() -> ProClient::disconnect -> updateStatus
-    Mutex::Autolock lock(mStatusMutex);
-
-    ICameraServiceListener::Status oldStatus = mStatusList[cameraId];
-
-    mStatusList[cameraId] = status;
-
-    if (oldStatus != status) {
-        ALOGV("%s: Status has changed for camera ID %d from 0x%x to 0x%x",
-              __FUNCTION__, cameraId, (uint32_t)oldStatus, (uint32_t)status);
-
-        if (oldStatus == ICameraServiceListener::STATUS_NOT_PRESENT &&
-            (status != ICameraServiceListener::STATUS_PRESENT &&
-             status != ICameraServiceListener::STATUS_ENUMERATING)) {
-
-            ALOGW("%s: From NOT_PRESENT can only transition into PRESENT"
-                  " or ENUMERATING", __FUNCTION__);
-            mStatusList[cameraId] = oldStatus;
-            return;
-        }
-
-        if (rejectSourceStates != NULL) {
-            const StatusVector &rejectList = *rejectSourceStates;
-            StatusVector::const_iterator it = rejectList.begin();
-
-            /**
-             * Sometimes we want to conditionally do a transition.
-             * For example if a client disconnects, we want to go to PRESENT
-             * only if we weren't already in NOT_PRESENT or ENUMERATING.
-             */
-            for (; it != rejectList.end(); ++it) {
-                if (oldStatus == *it) {
-                    ALOGV("%s: Rejecting status transition for Camera ID %d, "
-                          " since the source state was was in one of the bad "
-                          " states.", __FUNCTION__, cameraId);
-                    mStatusList[cameraId] = oldStatus;
-                    return;
-                }
-            }
-        }
-
-        /**
-          * ProClients lose their exclusive lock.
-          * - Done before the CameraClient can initialize the HAL device,
-          *   since we want to be able to close it before they get to initialize
-          */
-        if (status == ICameraServiceListener::STATUS_NOT_AVAILABLE) {
-            Vector<wp<ProClient> > proClients(mProClientList[cameraId]);
-            Vector<wp<ProClient> >::const_iterator it;
-
-            for (it = proClients.begin(); it != proClients.end(); ++it) {
-                sp<ProClient> proCl = it->promote();
-                if (proCl.get() != NULL) {
-                    proCl->onExclusiveLockStolen();
-                }
-            }
-        }
-
-        if (status == ICameraServiceListener::STATUS_NOT_PRESENT ||
-            status == ICameraServiceListener::STATUS_NOT_AVAILABLE) {
-            // update torch status to not available when the camera device
-            // becomes not present or not available.
-            onTorchStatusChanged(String8::format("%d", cameraId),
-                    ICameraServiceListener::TORCH_STATUS_NOT_AVAILABLE);
-        } else if (status == ICameraServiceListener::STATUS_PRESENT) {
-            // update torch status to available when the camera device becomes
-            // present or available
-            onTorchStatusChanged(String8::format("%d", cameraId),
-                    ICameraServiceListener::TORCH_STATUS_AVAILABLE_OFF);
-        }
-
-        Vector<sp<ICameraServiceListener> >::const_iterator it;
-        for (it = mListenerList.begin(); it != mListenerList.end(); ++it) {
-            (*it)->onStatusChanged(status, cameraId);
-        }
-    }
+void CameraService::updateStatus(ICameraServiceListener::Status status, const String8& cameraId) {
+    updateStatus(status, cameraId, {});
 }
 
-ICameraServiceListener::Status CameraService::getStatus(int cameraId) const {
-    if (cameraId < 0 || cameraId >= MAX_CAMERAS) {
-        ALOGE("%s: Invalid camera ID %d", __FUNCTION__, cameraId);
-        return ICameraServiceListener::STATUS_UNKNOWN;
+void CameraService::updateStatus(ICameraServiceListener::Status status, const String8& cameraId,
+        std::initializer_list<ICameraServiceListener::Status> rejectSourceStates) {
+    // Do not lock mServiceLock here or can get into a deadlock from
+    // connect() -> disconnect -> updateStatus
+
+    auto state = getCameraState(cameraId);
+
+    if (state == nullptr) {
+        ALOGW("%s: Could not update the status for %s, no such device exists", __FUNCTION__,
+                cameraId.string());
+        return;
     }
 
-    Mutex::Autolock al(mStatusMutex);
-    return mStatusList[cameraId];
+    // Update the status for this camera state, then send the onStatusChangedCallbacks to each
+    // of the listeners with both the mStatusStatus and mStatusListenerLock held
+    state->updateStatus(status, cameraId, rejectSourceStates, [this]
+            (const String8& cameraId, ICameraServiceListener::Status status) {
+
+            // Update torch status
+            if (status == ICameraServiceListener::STATUS_NOT_PRESENT ||
+                status == ICameraServiceListener::STATUS_NOT_AVAILABLE) {
+                // Update torch status to not available when the camera device becomes not present
+                // or not available.
+                onTorchStatusChanged(cameraId, ICameraServiceListener::TORCH_STATUS_NOT_AVAILABLE);
+            } else if (status == ICameraServiceListener::STATUS_PRESENT) {
+                // Update torch status to available when the camera device becomes present or
+                // available
+                onTorchStatusChanged(cameraId, ICameraServiceListener::TORCH_STATUS_AVAILABLE_OFF);
+            }
+
+            Mutex::Autolock lock(mStatusListenerLock);
+
+            for (auto& listener : mListenerList) {
+                // TODO: Refactor status listeners to use strings for Camera IDs and remove this.
+                int id = cameraIdToInt(cameraId);
+                if (id != -1) listener->onStatusChanged(status, id);
+            }
+        });
 }
 
 status_t CameraService::getTorchStatusLocked(
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 22afc8c..53420e5 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -38,11 +38,15 @@
 #include <camera/ICameraServiceListener.h>
 #include "CameraFlashlight.h"
 
-
 #include "common/CameraModule.h"
+#include "utils/AutoConditionLock.h"
+#include "utils/ClientManager.h"
+#include "utils/RingBuffer.h"
 
-/* This needs to be increased if we can have more cameras */
-#define MAX_CAMERAS 2
+#include <set>
+#include <string>
+#include <map>
+#include <memory>
 
 namespace android {
 
@@ -62,6 +66,34 @@
     class Client;
     class BasicClient;
 
+    enum apiLevel {
+        API_1 = 1,
+        API_2 = 2
+    };
+
+    // Process States (mirrors frameworks/base/core/java/android/app/ActivityManager.java)
+    static const int PROCESS_STATE_NONEXISTENT = -1;
+    static const int PROCESS_STATE_PERSISTENT = 0;
+    static const int PROCESS_STATE_PERSISTENT_UI = 1;
+    static const int PROCESS_STATE_TOP = 2;
+    static const int PROCESS_STATE_IMPORTANT_FOREGROUND = 3;
+    static const int PROCESS_STATE_IMPORTANT_BACKGROUND = 4;
+    static const int PROCESS_STATE_BACKUP = 5;
+    static const int PROCESS_STATE_HEAVY_WEIGHT = 6;
+    static const int PROCESS_STATE_SERVICE = 7;
+    static const int PROCESS_STATE_RECEIVER = 8;
+    static const int PROCESS_STATE_HOME = 9;
+    static const int PROCESS_STATE_LAST_ACTIVITY = 10;
+    static const int PROCESS_STATE_CACHED_ACTIVITY = 11;
+    static const int PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 12;
+    static const int PROCESS_STATE_CACHED_EMPTY = 13;
+
+    // 3 second busy timeout when other clients are connecting
+    static const nsecs_t DEFAULT_CONNECT_TIMEOUT_NS = 3000000000;
+
+    // Default number of messages to store in eviction log
+    static const size_t DEFAULT_EVICTION_LOG_LENGTH = 50;
+
     // Implementation of BinderService<T>
     static char const* getServiceName() { return "media.camera"; }
 
@@ -70,8 +102,8 @@
 
     /////////////////////////////////////////////////////////////////////
     // HAL Callbacks
-    virtual void        onDeviceStatusChanged(int cameraId,
-                                              int newStatus);
+    virtual void        onDeviceStatusChanged(camera_device_status_t cameraId,
+                                              camera_device_status_t newStatus);
     virtual void        onTorchStatusChanged(const String8& cameraId,
                                              ICameraServiceListener::TorchStatus
                                                    newStatus);
@@ -132,7 +164,6 @@
 
     /////////////////////////////////////////////////////////////////////
     // Client functionality
-    virtual void        removeClientByRemote(const wp<IBinder>& remoteBinder);
 
     enum sound_kind {
         SOUND_SHUTTER = 0,
@@ -155,11 +186,6 @@
     /////////////////////////////////////////////////////////////////////
     // CameraClient functionality
 
-    // returns plain pointer of client. Note that mClientLock should be acquired to
-    // prevent the client from destruction. The result can be NULL.
-    virtual BasicClient* getClientByIdUnsafe(int cameraId);
-    virtual Mutex*      getClientLockById(int cameraId);
-
     class BasicClient : public virtual RefBase {
     public:
         virtual status_t    initialize(CameraModule *module) = 0;
@@ -176,6 +202,15 @@
 
         virtual status_t    dump(int fd, const Vector<String16>& args) = 0;
 
+        // Return the package name for this client
+        virtual String16 getPackageName() const;
+
+        // Notify client about a fatal error
+        virtual void notifyError(ICameraDeviceCallbacks::CameraErrorCode errorCode,
+                const CaptureResultExtras& resultExtras) = 0;
+
+        // Get the PID of the application client using this
+        virtual int getClientPid() const;
     protected:
         BasicClient(const sp<CameraService>& cameraService,
                 const sp<IBinder>& remoteCallback,
@@ -202,6 +237,7 @@
         pid_t                           mClientPid;
         uid_t                           mClientUid;      // immutable after constructor
         pid_t                           mServicePid;     // immutable after constructor
+        bool                            mDisconnected;
 
         // - The app-side Binder interface to receive callbacks from us
         sp<IBinder>                     mRemoteBinder;   // immutable after constructor
@@ -210,10 +246,6 @@
         status_t                        startCameraOps();
         status_t                        finishCameraOps();
 
-        // Notify client about a fatal error
-        virtual void                    notifyError(
-                ICameraDeviceCallbacks::CameraErrorCode errorCode,
-                const CaptureResultExtras& resultExtras) = 0;
     private:
         AppOpsManager                   mAppOpsManager;
 
@@ -285,13 +317,11 @@
             return asBinder(this);
         }
 
-    protected:
-        static Mutex*        getClientLockFromCookie(void* user);
-        // convert client from cookie. Client lock should be acquired before getting Client.
-        static Client*       getClientFromCookie(void* user);
-
         virtual void         notifyError(ICameraDeviceCallbacks::CameraErrorCode errorCode,
                                          const CaptureResultExtras& resultExtras);
+    protected:
+        // Convert client from cookie.
+        static sp<CameraService::Client> getClientFromCookie(void* user);
 
         // Initialized in constructor
 
@@ -338,55 +368,231 @@
         // Callbacks from camera service
         virtual void          onExclusiveLockStolen() = 0;
 
-    protected:
         virtual void          notifyError(ICameraDeviceCallbacks::CameraErrorCode errorCode,
                                           const CaptureResultExtras& resultExtras);
+    protected:
 
         sp<IProCameraCallbacks> mRemoteCallback;
     }; // class ProClient
 
+    typedef std::shared_ptr<resource_policy::ClientDescriptor<String8,
+            sp<CameraService::BasicClient>>> DescriptorPtr;
+
+    /**
+     * A container class for managing active camera clients that are using HAL devices.  Active
+     * clients are represented by ClientDescriptor objects that contain strong pointers to the
+     * actual BasicClient subclass binder interface implementation.
+     *
+     * This class manages the eviction behavior for the camera clients.  See the parent class
+     * implementation in utils/ClientManager for the specifics of this behavior.
+     */
+    class CameraClientManager :
+            public resource_policy::ClientManager<String8, sp<CameraService::BasicClient>> {
+    public:
+        virtual ~CameraClientManager();
+
+        /**
+         * Return a strong pointer to the active BasicClient for this camera ID, or an empty
+         * if none exists.
+         */
+        sp<CameraService::BasicClient> getCameraClient(const String8& id) const;
+
+        /**
+         * Return a string describing the current state.
+         */
+        String8 toString() const;
+
+        /**
+         * Make a ClientDescriptor object wrapping the given BasicClient strong pointer.
+         */
+        static DescriptorPtr makeClientDescriptor(const String8& key, const sp<BasicClient>& value,
+                int32_t cost, const std::set<String8>& conflictingKeys, int32_t priority,
+                int32_t ownerId);
+
+        /**
+         * Make a ClientDescriptor object wrapping the given BasicClient strong pointer with
+         * values intialized from a prior ClientDescriptor.
+         */
+        static DescriptorPtr makeClientDescriptor(const sp<BasicClient>& value,
+                const CameraService::DescriptorPtr& partial);
+
+    }; // class CameraClientManager
+
 private:
 
+    /**
+     * Container class for the state of each logical camera device, including: ID, status, and
+     * dependencies on other devices.  The mapping of camera ID -> state saved in mCameraStates
+     * represents the camera devices advertised by the HAL (and any USB devices, when we add
+     * those).
+     *
+     * This container does NOT represent an active camera client.  These are represented using
+     * the ClientDescriptors stored in mActiveClientManager.
+     */
+    class CameraState {
+    public:
+        /**
+         * Make a new CameraState and set the ID, cost, and conflicting devices using the values
+         * returned in the HAL's camera_info struct for each device.
+         */
+        CameraState(const String8& id, int cost, const std::set<String8>& conflicting);
+        virtual ~CameraState();
+
+        /**
+         * Return the status for this device.
+         *
+         * This method acquires mStatusLock.
+         */
+        ICameraServiceListener::Status getStatus() const;
+
+        /**
+         * This function updates the status for this camera device, unless the given status
+         * is in the given list of rejected status states, and execute the function passed in
+         * with a signature onStatusUpdateLocked(const String8&, ICameraServiceListener::Status)
+         * if the status has changed.
+         *
+         * This method is idempotent, and will not result in the function passed to
+         * onStatusUpdateLocked being called more than once for the same arguments.
+         * This method aquires mStatusLock.
+         */
+        template<class Func>
+        void updateStatus(ICameraServiceListener::Status status, const String8& cameraId,
+                std::initializer_list<ICameraServiceListener::Status> rejectSourceStates,
+                Func onStatusUpdatedLocked);
+
+        /**
+         * Return the last set CameraParameters object generated from the information returned by
+         * the HAL for this device (or an empty CameraParameters object if none has been set).
+         */
+        CameraParameters getShimParams() const;
+
+        /**
+         * Set the CameraParameters for this device.
+         */
+        void setShimParams(const CameraParameters& params);
+
+        /**
+         * Return the resource_cost advertised by the HAL for this device.
+         */
+        int getCost() const;
+
+        /**
+         * Return a set of the IDs of conflicting devices advertised by the HAL for this device.
+         */
+        std::set<String8> getConflicting() const;
+
+        /**
+         * Return the ID of this camera device.
+         */
+        String8 getId() const;
+
+    private:
+        const String8 mId;
+        ICameraServiceListener::Status mStatus; // protected by mStatusLock
+        const int mCost;
+        std::set<String8> mConflicting;
+        mutable Mutex mStatusLock;
+        CameraParameters mShimParams;
+    }; // class CameraState
+
     // Delay-load the Camera HAL module
     virtual void onFirstRef();
 
-    // Step 1. Check if we can connect, before we acquire the service lock.
-    status_t            validateConnect(int cameraId,
-                                        /*inout*/
-                                        int& clientUid) const;
+    // Check if we can connect, before we acquire the service lock.
+    status_t validateConnect(const String8& cameraId, /*inout*/int& clientUid) const;
 
-    // Step 2. Check if we can connect, after we acquire the service lock.
-    bool                canConnectUnsafe(int cameraId,
-                                         const String16& clientPackageName,
-                                         const sp<IBinder>& remoteCallback,
-                                         /*out*/
-                                         sp<BasicClient> &client);
+    // Handle active client evictions, and update service state.
+    // Only call with with mServiceLock held.
+    status_t handleEvictionsLocked(const String8& cameraId, int clientPid,
+        apiLevel effectiveApiLevel, const sp<IBinder>& remoteCallback, const String8& packageName,
+        /*out*/
+        sp<BasicClient>* client,
+        std::shared_ptr<resource_policy::ClientDescriptor<String8, sp<BasicClient>>>* partial);
 
-    // When connection is successful, initialize client and track its death
-    status_t            connectFinishUnsafe(const sp<BasicClient>& client,
-                                            const sp<IBinder>& remoteCallback);
+    // Single implementation shared between the various connect calls
+    template<class CALLBACK, class CLIENT>
+    status_t connectHelper(const sp<CALLBACK>& cameraCb, const String8& cameraId, int halVersion,
+            const String16& clientPackageName, int clientUid, apiLevel effectiveApiLevel,
+            bool legacyMode, bool shimUpdateOnly, /*out*/sp<CLIENT>& device);
 
-    virtual sp<BasicClient>  getClientByRemote(const wp<IBinder>& cameraClient);
 
+    // Lock guarding camera service state
     Mutex               mServiceLock;
-    // either a Client or CameraDeviceClient
-    wp<BasicClient>     mClient[MAX_CAMERAS];  // protected by mServiceLock
-    Mutex               mClientLock[MAX_CAMERAS]; // prevent Client destruction inside callbacks
+
+    // Condition to use with mServiceLock, used to handle simultaneous connect calls from clients
+    std::shared_ptr<WaitableMutexWrapper> mServiceLockWrapper;
+
+    // Return NO_ERROR if the device with a give ID can be connected to
+    status_t checkIfDeviceIsUsable(const String8& cameraId) const;
+
+    // Container for managing currently active application-layer clients
+    CameraClientManager mActiveClientManager;
+
+    // Mapping from camera ID -> state for each device, map is protected by mCameraStatesLock
+    std::map<String8, std::shared_ptr<CameraState>> mCameraStates;
+
+    // Mutex guarding mCameraStates map
+    mutable Mutex mCameraStatesLock;
+
+    // Circular buffer for storing event logging for dumps
+    RingBuffer<String8> mEventLog;
+
+    /**
+     * Get the camera state for a given camera id.
+     *
+     * This acquires mCameraStatesLock.
+     */
+    std::shared_ptr<CameraService::CameraState> getCameraState(const String8& cameraId) const;
+
+    /**
+     * Evict client who's remote binder has died.  Returns true if this client was in the active
+     * list and was disconnected.
+     *
+     * This method acquires mServiceLock.
+     */
+    bool evictClientIdByRemote(const wp<IBinder>& cameraClient);
+
+    /**
+     * Remove the given client from the active clients list; does not disconnect the client.
+     *
+     * This method acquires mServiceLock.
+     */
+    void removeByClient(const BasicClient* client);
+
+    /**
+     * Add new client to active clients list after conflicting clients have disconnected using the
+     * values set in the partial descriptor passed in to construct the actual client descriptor.
+     * This is typically called at the end of a connect call.
+     *
+     * This method must be called with mServiceLock held.
+     */
+    void finishConnectLocked(const sp<BasicClient>& client, const DescriptorPtr& desc);
+
+    /**
+     * Returns the integer corresponding to the given camera ID string, or -1 on failure.
+     */
+    static int cameraIdToInt(const String8& cameraId);
+
+    /**
+     * Remove a single client corresponding to the given camera id from the list of active clients.
+     * If none exists, return an empty strongpointer.
+     *
+     * This method must be called with mServiceLock held.
+     */
+    sp<CameraService::BasicClient> removeClientLocked(const String8& cameraId);
+
+    /**
+     * Add a event log message that a client has been disconnected.
+     */
+    void logDisconnected(const String8& cameraId, int clientPid, const String8& clientPackage);
+
+    /**
+     * Add a event log message that a client has been connected.
+     */
+    void logConnected(const String8& cameraId, int clientPid, const String8& clientPackage);
+
     int                 mNumberOfCameras;
 
-    typedef wp<ProClient> weak_pro_client_ptr;
-    Vector<weak_pro_client_ptr> mProClientList[MAX_CAMERAS];
-
-    // needs to be called with mServiceLock held
-    sp<BasicClient>     findClientUnsafe(const wp<IBinder>& cameraClient, int& outIndex);
-    sp<ProClient>       findProClientUnsafe(
-                                     const wp<IBinder>& cameraCallbacksRemote);
-
-    // atomics to record whether the hardware is allocated to some client.
-    volatile int32_t    mBusy[MAX_CAMERAS];
-    void                setCameraBusy(int cameraId);
-    void                setCameraFree(int cameraId);
-
     // sounds
     MediaPlayer*        newMediaPlayer(const char *file);
 
@@ -396,24 +602,21 @@
 
     CameraModule*     mModule;
 
-    Vector<sp<ICameraServiceListener> >
-                        mListenerList;
+    // Guarded by mStatusListenerMutex
+    std::vector<sp<ICameraServiceListener>> mListenerList;
+    Mutex       mStatusListenerLock;
 
-    // guard only mStatusList and the broadcasting of ICameraServiceListener
-    mutable Mutex       mStatusMutex;
-    ICameraServiceListener::Status
-                        mStatusList[MAX_CAMERAS];
-
-    // Read the current status (locks mStatusMutex)
-    ICameraServiceListener::Status
-                        getStatus(int cameraId) const;
-
-    typedef Vector<ICameraServiceListener::Status> StatusVector;
-    // Broadcast the new status if it changed (locks the service mutex)
-    void                updateStatus(
-                            ICameraServiceListener::Status status,
-                            int32_t cameraId,
-                            const StatusVector *rejectSourceStates = NULL);
+    /**
+     * Update the status for the given camera id (if that device exists), and broadcast the
+     * status update to all current ICameraServiceListeners if the status has changed.  Any
+     * statuses in rejectedSourceStates will be ignored.
+     *
+     * This method must be idempotent.
+     * This method acquires mStatusLock and mStatusListenerLock.
+     */
+    void updateStatus(ICameraServiceListener::Status status, const String8& cameraId,
+            std::initializer_list<ICameraServiceListener::Status> rejectedSourceStates);
+    void updateStatus(ICameraServiceListener::Status status, const String8& cameraId);
 
     // flashlight control
     sp<CameraFlashlight> mFlashlight;
@@ -435,9 +638,6 @@
     void onTorchStatusChangedLocked(const String8& cameraId,
             ICameraServiceListener::TorchStatus newStatus);
 
-    // validate the camera id for use of setting a torch mode.
-    bool validCameraIdForSetTorchMode(const String8& cameraId);
-
     // get a camera's torch status. mTorchStatusMutex should be locked.
     status_t getTorchStatusLocked(const String8 &cameraId,
             ICameraServiceListener::TorchStatus *status) const;
@@ -451,19 +651,9 @@
 
     // Helpers
 
-    bool                isValidCameraId(int cameraId);
-
     bool                setUpVendorTags();
 
     /**
-     * A mapping of camera ids to CameraParameters returned by that camera device.
-     *
-     * This cache is used to generate CameraCharacteristic metadata when using
-     * the HAL1 shim.
-     */
-    KeyedVector<int, CameraParameters>    mShimParams;
-
-    /**
      * Initialize and cache the metadata used by the HAL1 shim for a given cameraId.
      *
      * Returns OK on success, or a negative error code.
@@ -486,25 +676,190 @@
      */
     status_t            generateShimMetadata(int cameraId, /*out*/CameraMetadata* cameraInfo);
 
+    static int getCallingPid();
+
+    static int getCallingUid();
+
     /**
-     * Connect a new camera client.  This should only be used while holding the
-     * mutex for mServiceLock.
-     *
-     * Returns OK on success, or a negative error code.
+     * Get the current system time as a formatted string.
      */
-    status_t            connectHelperLocked(
-            /*out*/
-            sp<Client>& client,
-            /*in*/
-            const sp<ICameraClient>& cameraClient,
-            int cameraId,
-            const String16& clientPackageName,
-            int clientUid,
-            int callingPid,
-            int halVersion = CAMERA_HAL_API_VERSION_UNSPECIFIED,
-            bool legacyMode = false);
+    static String8 getFormattedCurrentTime();
+
+    /**
+     * Get the camera eviction priority from the current process state given by ActivityManager.
+     */
+    static int getCameraPriorityFromProcState(int procState);
+
+    static status_t makeClient(const sp<CameraService>& cameraService,
+            const sp<IInterface>& cameraCb, const String16& packageName, const String8& cameraId,
+            int facing, int clientPid, uid_t clientUid, int servicePid, bool legacyMode,
+            int halVersion, int deviceVersion, apiLevel effectiveApiLevel,
+            /*out*/sp<BasicClient>* client);
 };
 
+template<class Func>
+void CameraService::CameraState::updateStatus(ICameraServiceListener::Status status,
+        const String8& cameraId,
+        std::initializer_list<ICameraServiceListener::Status> rejectSourceStates,
+        Func onStatusUpdatedLocked) {
+    Mutex::Autolock lock(mStatusLock);
+    ICameraServiceListener::Status oldStatus = mStatus;
+    mStatus = status;
+
+    if (oldStatus == status) {
+        return;
+    }
+
+    ALOGV("%s: Status has changed for camera ID %s from %#x to %#x", __FUNCTION__,
+            cameraId.string(), oldStatus, status);
+
+    if (oldStatus == ICameraServiceListener::STATUS_NOT_PRESENT &&
+        (status != ICameraServiceListener::STATUS_PRESENT &&
+         status != ICameraServiceListener::STATUS_ENUMERATING)) {
+
+        ALOGW("%s: From NOT_PRESENT can only transition into PRESENT or ENUMERATING",
+                __FUNCTION__);
+        mStatus = oldStatus;
+        return;
+    }
+
+    /**
+     * Sometimes we want to conditionally do a transition.
+     * For example if a client disconnects, we want to go to PRESENT
+     * only if we weren't already in NOT_PRESENT or ENUMERATING.
+     */
+    for (auto& rejectStatus : rejectSourceStates) {
+        if (oldStatus == rejectStatus) {
+            ALOGV("%s: Rejecting status transition for Camera ID %s,  since the source "
+                    "state was was in one of the bad states.", __FUNCTION__, cameraId.string());
+            mStatus = oldStatus;
+            return;
+        }
+    }
+
+    onStatusUpdatedLocked(cameraId, status);
+}
+
+
+template<class CALLBACK, class CLIENT>
+status_t CameraService::connectHelper(const sp<CALLBACK>& cameraCb, const String8& cameraId,
+        int halVersion, const String16& clientPackageName, int clientUid,
+        apiLevel effectiveApiLevel, bool legacyMode, bool shimUpdateOnly,
+        /*out*/sp<CLIENT>& device) {
+    status_t ret = NO_ERROR;
+    String8 clientName8(clientPackageName);
+    int clientPid = getCallingPid();
+
+    ALOGI("CameraService::connect call E (PID %d \"%s\", camera ID %s) for HAL version %d and "
+            "Camera API version %d", clientPid, clientName8.string(), cameraId.string(),
+            halVersion, static_cast<int>(effectiveApiLevel));
+
+    // Enforce client permissions and do basic sanity checks
+    if((ret = validateConnect(cameraId, /*inout*/clientUid)) != NO_ERROR) {
+        return ret;
+    }
+
+    sp<CLIENT> client = nullptr;
+    {
+        // Acquire mServiceLock and prevent other clients from connecting
+        std::unique_ptr<AutoConditionLock> lock =
+                AutoConditionLock::waitAndAcquire(mServiceLockWrapper, DEFAULT_CONNECT_TIMEOUT_NS);
+        if (lock == nullptr) {
+            ALOGE("CameraService::connect X (PID %d) rejected (too many other clients connecting)."
+                    , clientPid);
+            return -EBUSY;
+        }
+
+        // Check the shim parameters after acquiring lock, if they have already been updated and
+        // we were doing a shim update, return immediately
+        if (shimUpdateOnly) {
+            auto cameraState = getCameraState(cameraId);
+            if (cameraState != nullptr) {
+                if (!cameraState->getShimParams().isEmpty()) return NO_ERROR;
+            }
+        }
+
+        sp<BasicClient> clientTmp = nullptr;
+        std::shared_ptr<resource_policy::ClientDescriptor<String8, sp<BasicClient>>> partial;
+        if ((ret = handleEvictionsLocked(cameraId, clientPid, effectiveApiLevel,
+                IInterface::asBinder(cameraCb), clientName8, /*out*/&clientTmp,
+                /*out*/&partial)) != NO_ERROR) {
+            return ret;
+        }
+
+        if (clientTmp.get() != nullptr) {
+            // Handle special case for API1 MediaRecorder where the existing client is returned
+            device = static_cast<CLIENT*>(clientTmp.get());
+            return NO_ERROR;
+        }
+
+        // give flashlight a chance to close devices if necessary.
+        mFlashlight->prepareDeviceOpen(cameraId);
+
+        // TODO: Update getDeviceVersion + HAL interface to use strings for Camera IDs
+        int id = cameraIdToInt(cameraId);
+        if (id == -1) {
+            ALOGE("%s: Invalid camera ID %s, cannot get device version from HAL.", __FUNCTION__,
+                    cameraId.string());
+            return BAD_VALUE;
+        }
+
+        int facing = -1;
+        int deviceVersion = getDeviceVersion(id, /*out*/&facing);
+        sp<BasicClient> tmp = nullptr;
+        if((ret = makeClient(this, cameraCb, clientPackageName, cameraId, facing, clientPid,
+                clientUid, getpid(), legacyMode, halVersion, deviceVersion, effectiveApiLevel,
+                /*out*/&tmp)) != NO_ERROR) {
+            return ret;
+        }
+        client = static_cast<CLIENT*>(tmp.get());
+
+        LOG_ALWAYS_FATAL_IF(client.get() == nullptr, "%s: CameraService in invalid state",
+                __FUNCTION__);
+
+        if ((ret = client->initialize(mModule)) != OK) {
+            ALOGE("%s: Could not initialize client from HAL module.", __FUNCTION__);
+            return ret;
+        }
+
+        sp<IBinder> remoteCallback = client->getRemote();
+        if (remoteCallback != nullptr) {
+            remoteCallback->linkToDeath(this);
+        }
+
+        // Update shim paremeters for legacy clients
+        if (effectiveApiLevel == API_1) {
+            // Assume we have always received a Client subclass for API1
+            sp<Client> shimClient = reinterpret_cast<Client*>(client.get());
+            String8 rawParams = shimClient->getParameters();
+            CameraParameters params(rawParams);
+
+            auto cameraState = getCameraState(cameraId);
+            if (cameraState != nullptr) {
+                cameraState->setShimParams(params);
+            } else {
+                ALOGE("%s: Cannot update shim parameters for camera %s, no such device exists.",
+                        __FUNCTION__, cameraId.string());
+            }
+        }
+
+        if (shimUpdateOnly) {
+            // If only updating legacy shim parameters, immediately disconnect client
+            mServiceLock.unlock();
+            client->disconnect();
+            mServiceLock.lock();
+        } else {
+            // Otherwise, add client to active clients list
+            finishConnectLocked(client, partial);
+        }
+    } // lock is destroyed, allow further connect calls
+
+    // Important: release the mutex here so the client can call back into the service from its
+    // destructor (can be at the end of the call)
+    device = client;
+    return NO_ERROR;
+}
+
 } // namespace android
 
 #endif
diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp
index 5dbdeb2..6f44aee 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.cpp
+++ b/services/camera/libcameraservice/api1/Camera2Client.cpp
@@ -163,11 +163,9 @@
 
 status_t Camera2Client::dump(int fd, const Vector<String16>& args) {
     String8 result;
-    result.appendFormat("Client2[%d] (%p) Client: %s PID: %d, dump:\n",
-            mCameraId,
+    result.appendFormat("Client2[%d] (%p) PID: %d, dump:\n", mCameraId,
             (getRemoteCallback() != NULL ?
                     (IInterface::asBinder(getRemoteCallback()).get()) : NULL),
-            String8(mClientPackageName).string(),
             mClientPid);
     result.append("  State: ");
 #define CASE_APPEND_ENUM(x) case x: result.append(#x "\n"); break;
diff --git a/services/camera/libcameraservice/api1/CameraClient.cpp b/services/camera/libcameraservice/api1/CameraClient.cpp
index 6bea3b6..e552633 100644
--- a/services/camera/libcameraservice/api1/CameraClient.cpp
+++ b/services/camera/libcameraservice/api1/CameraClient.cpp
@@ -99,12 +99,7 @@
 
 // tear down the client
 CameraClient::~CameraClient() {
-    // this lock should never be NULL
-    Mutex* lock = mCameraService->getClientLockById(mCameraId);
-    lock->lock();
     mDestructionStarted = true;
-    // client will not be accessed from callback. should unlock to prevent dead-lock in disconnect
-    lock->unlock();
     int callingPid = getCallingPid();
     LOG1("CameraClient::~CameraClient E (pid %d, this %p)", callingPid, this);
 
@@ -116,11 +111,11 @@
     const size_t SIZE = 256;
     char buffer[SIZE];
 
-    size_t len = snprintf(buffer, SIZE, "Client[%d] (%p) PID: %d\n",
+    size_t len = snprintf(buffer, SIZE, "Client[%d] (%p) with UID %d\n",
             mCameraId,
             (getRemoteCallback() != NULL ?
                     IInterface::asBinder(getRemoteCallback()).get() : NULL),
-            mClientPid);
+            mClientUid);
     len = (len > SIZE - 1) ? SIZE - 1 : len;
     write(fd, buffer, len);
 
@@ -677,6 +672,13 @@
                 LOG1("lockIfMessageWanted(%d): waited for %d ms",
                     msgType, sleepCount * CHECK_MESSAGE_INTERVAL);
             }
+
+            // If messages are no longer enabled after acquiring lock, release and drop message
+            if ((mMsgEnabled & msgType) == 0) {
+                mLock.unlock();
+                break;
+            }
+
             return true;
         }
         if (sleepCount++ == 0) {
@@ -702,26 +704,13 @@
 //      (others)                        c->dataCallback
 // dataCallbackTimestamp
 //      (others)                        c->dataCallbackTimestamp
-//
-// NOTE: the *Callback functions grab mLock of the client before passing
-// control to handle* functions. So the handle* functions must release the
-// lock before calling the ICameraClient's callbacks, so those callbacks can
-// invoke methods in the Client class again (For example, the preview frame
-// callback may want to releaseRecordingFrame). The handle* functions must
-// release the lock after all accesses to member variables, so it must be
-// handled very carefully.
 
 void CameraClient::notifyCallback(int32_t msgType, int32_t ext1,
         int32_t ext2, void* user) {
     LOG2("notifyCallback(%d)", msgType);
 
-    Mutex* lock = getClientLockFromCookie(user);
-    if (lock == NULL) return;
-    Mutex::Autolock alock(*lock);
-
-    CameraClient* client =
-            static_cast<CameraClient*>(getClientFromCookie(user));
-    if (client == NULL) return;
+    sp<CameraClient> client = static_cast<CameraClient*>(getClientFromCookie(user).get());
+    if (client.get() == nullptr) return;
 
     if (!client->lockIfMessageWanted(msgType)) return;
 
@@ -740,13 +729,8 @@
         const sp<IMemory>& dataPtr, camera_frame_metadata_t *metadata, void* user) {
     LOG2("dataCallback(%d)", msgType);
 
-    Mutex* lock = getClientLockFromCookie(user);
-    if (lock == NULL) return;
-    Mutex::Autolock alock(*lock);
-
-    CameraClient* client =
-            static_cast<CameraClient*>(getClientFromCookie(user));
-    if (client == NULL) return;
+    sp<CameraClient> client = static_cast<CameraClient*>(getClientFromCookie(user).get());
+    if (client.get() == nullptr) return;
 
     if (!client->lockIfMessageWanted(msgType)) return;
     if (dataPtr == 0 && metadata == NULL) {
@@ -778,13 +762,8 @@
         int32_t msgType, const sp<IMemory>& dataPtr, void* user) {
     LOG2("dataCallbackTimestamp(%d)", msgType);
 
-    Mutex* lock = getClientLockFromCookie(user);
-    if (lock == NULL) return;
-    Mutex::Autolock alock(*lock);
-
-    CameraClient* client =
-            static_cast<CameraClient*>(getClientFromCookie(user));
-    if (client == NULL) return;
+    sp<CameraClient> client = static_cast<CameraClient*>(getClientFromCookie(user).get());
+    if (client.get() == nullptr) return;
 
     if (!client->lockIfMessageWanted(msgType)) return;
 
diff --git a/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp b/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp
index fd4e714..5c8f750 100644
--- a/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/CallbackProcessor.cpp
@@ -154,8 +154,8 @@
                 params.previewWidth, params.previewHeight,
                 callbackFormat, params.previewFormat);
         res = device->createStream(mCallbackWindow,
-                params.previewWidth, params.previewHeight,
-                callbackFormat, HAL_DATASPACE_JFIF, &mCallbackStreamId);
+                params.previewWidth, params.previewHeight, callbackFormat,
+                HAL_DATASPACE_JFIF, CAMERA3_STREAM_ROTATION_0, &mCallbackStreamId);
         if (res != OK) {
             ALOGE("%s: Camera %d: Can't create output stream for callbacks: "
                     "%s (%d)", __FUNCTION__, mId,
diff --git a/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp b/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp
index 5b387f9..34798bf 100644
--- a/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/JpegProcessor.cpp
@@ -146,7 +146,7 @@
         res = device->createStream(mCaptureWindow,
                 params.pictureWidth, params.pictureHeight,
                 HAL_PIXEL_FORMAT_BLOB, HAL_DATASPACE_JFIF,
-                &mCaptureStreamId);
+                CAMERA3_STREAM_ROTATION_0, &mCaptureStreamId);
         if (res != OK) {
             ALOGE("%s: Camera %d: Can't create output stream for capture: "
                     "%s (%d)", __FUNCTION__, mId,
diff --git a/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp b/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
index a5a2fec..b6071f6 100644
--- a/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
@@ -183,7 +183,7 @@
         res = device->createStream(mPreviewWindow,
                 params.previewWidth, params.previewHeight,
                 CAMERA2_HAL_PIXEL_FORMAT_OPAQUE, HAL_DATASPACE_UNKNOWN,
-                &mPreviewStreamId);
+                CAMERA3_STREAM_ROTATION_0, &mPreviewStreamId);
         if (res != OK) {
             ALOGE("%s: Camera %d: Unable to create preview stream: %s (%d)",
                     __FUNCTION__, mId, strerror(-res), res);
@@ -426,8 +426,8 @@
         // TODO: Wire this in from encoder side
         res = device->createStream(mRecordingWindow,
                 params.videoWidth, params.videoHeight,
-                CAMERA2_HAL_PIXEL_FORMAT_OPAQUE,
-                HAL_DATASPACE_BT709, &mRecordingStreamId);
+                CAMERA2_HAL_PIXEL_FORMAT_OPAQUE, HAL_DATASPACE_BT709,
+                CAMERA3_STREAM_ROTATION_0, &mRecordingStreamId);
         if (res != OK) {
             ALOGE("%s: Camera %d: Can't create output stream for recording: "
                     "%s (%d)", __FUNCTION__, mId,
diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp b/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
index 68aca2d..a03f9c7 100644
--- a/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
@@ -185,8 +185,8 @@
                 (int)CAMERA2_HAL_PIXEL_FORMAT_ZSL :
                 (int)HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
         res = device->createStream(mZslWindow,
-                params.fastInfo.arrayWidth, params.fastInfo.arrayHeight,
-                streamType, HAL_DATASPACE_UNKNOWN, &mZslStreamId);
+                params.fastInfo.arrayWidth, params.fastInfo.arrayHeight, streamType,
+                HAL_DATASPACE_UNKNOWN, CAMERA3_STREAM_ROTATION_0, &mZslStreamId);
         if (res != OK) {
             ALOGE("%s: Camera %d: Can't create output stream for ZSL: "
                     "%s (%d)", __FUNCTION__, mId,
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index dde1779..0e2311c 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -314,8 +314,7 @@
     return res;
 }
 
-status_t CameraDeviceClient::createStream(
-                      const sp<IGraphicBufferProducer>& bufferProducer)
+status_t CameraDeviceClient::createStream(const OutputConfiguration &outputConfiguration)
 {
     ATRACE_CALL();
 
@@ -324,6 +323,8 @@
 
     Mutex::Autolock icl(mBinderSerializationLock);
 
+
+    sp<IGraphicBufferProducer> bufferProducer = outputConfiguration.getGraphicBufferProducer();
     if (bufferProducer == NULL) {
         ALOGE("%s: bufferProducer must not be null", __FUNCTION__);
         return BAD_VALUE;
@@ -413,7 +414,9 @@
 
     int streamId = -1;
     res = mDevice->createStream(anw, width, height, format, dataSpace,
-            &streamId);
+                                static_cast<camera3_stream_rotation_t>
+                                        (outputConfiguration.getRotation()),
+                                &streamId);
 
     if (res == OK) {
         mStreamMap.add(binder, streamId);
@@ -595,9 +598,7 @@
             mCameraId,
             (getRemoteCallback() != NULL ?
                     IInterface::asBinder(getRemoteCallback()).get() : NULL) );
-    result.appendFormat("  Current client: %s (PID %d, UID %u)\n",
-            String8(mClientPackageName).string(),
-            mClientPid, mClientUid);
+    result.appendFormat("  Current client UID %u\n", mClientUid);
 
     result.append("  State:\n");
     result.appendFormat("    Request ID counter: %d\n", mRequestIdCounter);
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.h b/services/camera/libcameraservice/api2/CameraDeviceClient.h
index c89c269..a3dbb90 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.h
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.h
@@ -19,6 +19,7 @@
 
 #include <camera/camera2/ICameraDeviceUser.h>
 #include <camera/camera2/ICameraDeviceCallbacks.h>
+#include <camera/camera2/OutputConfiguration.h>
 
 #include "CameraService.h"
 #include "common/FrameProcessorBase.h"
@@ -83,8 +84,7 @@
     // Returns -EBUSY if device is not idle
     virtual status_t      deleteStream(int streamId);
 
-    virtual status_t      createStream(
-            const sp<IGraphicBufferProducer>& bufferProducer);
+    virtual status_t      createStream(const OutputConfiguration &outputConfiguration);
 
     // Create a request object from a template.
     virtual status_t      createDefaultRequest(int templateId,
diff --git a/services/camera/libcameraservice/api_pro/ProCamera2Client.cpp b/services/camera/libcameraservice/api_pro/ProCamera2Client.cpp
index ba93554..a977494 100644
--- a/services/camera/libcameraservice/api_pro/ProCamera2Client.cpp
+++ b/services/camera/libcameraservice/api_pro/ProCamera2Client.cpp
@@ -281,7 +281,8 @@
     }
 
     return mDevice->createStream(window, width, height, format,
-                                 HAL_DATASPACE_UNKNOWN, streamId);
+                                 HAL_DATASPACE_UNKNOWN, CAMERA3_STREAM_ROTATION_0,
+                                 streamId);
 }
 
 // Create a request object from a template.
diff --git a/services/camera/libcameraservice/common/CameraDeviceBase.h b/services/camera/libcameraservice/common/CameraDeviceBase.h
index 8764504..fe55b9e 100644
--- a/services/camera/libcameraservice/common/CameraDeviceBase.h
+++ b/services/camera/libcameraservice/common/CameraDeviceBase.h
@@ -100,14 +100,14 @@
             nsecs_t timeout) = 0;
 
     /**
-     * Create an output stream of the requested size, format, and dataspace
+     * Create an output stream of the requested size, format, rotation and dataspace
      *
      * For HAL_PIXEL_FORMAT_BLOB formats, the width and height should be the
      * logical dimensions of the buffer, not the number of bytes.
      */
     virtual status_t createStream(sp<ANativeWindow> consumer,
             uint32_t width, uint32_t height, int format,
-            android_dataspace dataSpace, int *id) = 0;
+            android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id) = 0;
 
     /**
      * Create an input reprocess stream that uses buffers from an existing
diff --git a/services/camera/libcameraservice/device2/Camera2Device.cpp b/services/camera/libcameraservice/device2/Camera2Device.cpp
index ee862a2..878986b 100644
--- a/services/camera/libcameraservice/device2/Camera2Device.cpp
+++ b/services/camera/libcameraservice/device2/Camera2Device.cpp
@@ -242,7 +242,7 @@
 
 status_t Camera2Device::createStream(sp<ANativeWindow> consumer,
         uint32_t width, uint32_t height, int format,
-        android_dataspace /*dataSpace*/, int *id) {
+        android_dataspace /*dataSpace*/, camera3_stream_rotation_t rotation, int *id) {
     ATRACE_CALL();
     status_t res;
     ALOGV("%s: E", __FUNCTION__);
diff --git a/services/camera/libcameraservice/device2/Camera2Device.h b/services/camera/libcameraservice/device2/Camera2Device.h
index e4c2856..9b32fa6 100644
--- a/services/camera/libcameraservice/device2/Camera2Device.h
+++ b/services/camera/libcameraservice/device2/Camera2Device.h
@@ -58,7 +58,7 @@
     virtual status_t waitUntilRequestReceived(int32_t requestId, nsecs_t timeout);
     virtual status_t createStream(sp<ANativeWindow> consumer,
             uint32_t width, uint32_t height, int format,
-            android_dataspace dataSpace, int *id);
+            android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id);
     virtual status_t createReprocessStreamFromStream(int outputId, int *id);
     virtual status_t getStreamInfo(int id,
             uint32_t *width, uint32_t *height, uint32_t *format);
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 529d249..8236788 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -802,12 +802,12 @@
 
 status_t Camera3Device::createStream(sp<ANativeWindow> consumer,
         uint32_t width, uint32_t height, int format, android_dataspace dataSpace,
-        int *id) {
+        camera3_stream_rotation_t rotation, int *id) {
     ATRACE_CALL();
     Mutex::Autolock il(mInterfaceLock);
     Mutex::Autolock l(mLock);
-    ALOGV("Camera %d: Creating new stream %d: %d x %d, format %d, dataspace %d",
-            mId, mNextStreamId, width, height, format, dataSpace);
+    ALOGV("Camera %d: Creating new stream %d: %d x %d, format %d, dataspace %d rotation %d",
+            mId, mNextStreamId, width, height, format, dataSpace, rotation);
 
     status_t res;
     bool wasActive = false;
@@ -847,10 +847,10 @@
         }
 
         newStream = new Camera3OutputStream(mNextStreamId, consumer,
-                width, height, jpegBufferSize, format, dataSpace);
+                width, height, jpegBufferSize, format, dataSpace, rotation);
     } else {
         newStream = new Camera3OutputStream(mNextStreamId, consumer,
-                width, height, format, dataSpace);
+                width, height, format, dataSpace, rotation);
     }
     newStream->setStatusTracker(mStatusTracker);
 
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index e2ad1fa..a77548d 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -96,7 +96,7 @@
     // stream, reconfiguring device, and unpausing.
     virtual status_t createStream(sp<ANativeWindow> consumer,
             uint32_t width, uint32_t height, int format,
-            android_dataspace dataSpace, int *id);
+            android_dataspace dataSpace, camera3_stream_rotation_t rotation, int *id);
     virtual status_t createInputStream(
             uint32_t width, uint32_t height, int format,
             int *id);
diff --git a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp b/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
index 6201484..01edfff 100644
--- a/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3DummyStream.cpp
@@ -28,7 +28,7 @@
 
 Camera3DummyStream::Camera3DummyStream(int id) :
         Camera3IOStreamBase(id, CAMERA3_STREAM_OUTPUT, DUMMY_WIDTH, DUMMY_HEIGHT,
-                /*maxSize*/0, DUMMY_FORMAT, DUMMY_DATASPACE) {
+                /*maxSize*/0, DUMMY_FORMAT, DUMMY_DATASPACE, DUMMY_ROTATION) {
 
 }
 
diff --git a/services/camera/libcameraservice/device3/Camera3DummyStream.h b/services/camera/libcameraservice/device3/Camera3DummyStream.h
index 7f52d84..d023c57 100644
--- a/services/camera/libcameraservice/device3/Camera3DummyStream.h
+++ b/services/camera/libcameraservice/device3/Camera3DummyStream.h
@@ -76,6 +76,7 @@
     static const int DUMMY_HEIGHT = 240;
     static const int DUMMY_FORMAT = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED;
     static const android_dataspace DUMMY_DATASPACE = HAL_DATASPACE_UNKNOWN;
+    static const camera3_stream_rotation_t DUMMY_ROTATION = CAMERA3_STREAM_ROTATION_0;
     static const uint32_t DUMMY_USAGE = GRALLOC_USAGE_HW_COMPOSER;
 
     /**
diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
index ff0acbb..8696413 100644
--- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
+++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.cpp
@@ -31,9 +31,9 @@
 
 Camera3IOStreamBase::Camera3IOStreamBase(int id, camera3_stream_type_t type,
         uint32_t width, uint32_t height, size_t maxSize, int format,
-        android_dataspace dataSpace) :
+        android_dataspace dataSpace, camera3_stream_rotation_t rotation) :
         Camera3Stream(id, type,
-                width, height, maxSize, format, dataSpace),
+                width, height, maxSize, format, dataSpace, rotation),
         mTotalBufferCount(0),
         mHandoutTotalBufferCount(0),
         mHandoutOutputBufferCount(0),
diff --git a/services/camera/libcameraservice/device3/Camera3IOStreamBase.h b/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
index 83d4350..abcf2b1 100644
--- a/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
+++ b/services/camera/libcameraservice/device3/Camera3IOStreamBase.h
@@ -34,7 +34,7 @@
   protected:
     Camera3IOStreamBase(int id, camera3_stream_type_t type,
             uint32_t width, uint32_t height, size_t maxSize, int format,
-            android_dataspace dataSpace);
+            android_dataspace dataSpace, camera3_stream_rotation_t rotation);
 
   public:
 
diff --git a/services/camera/libcameraservice/device3/Camera3InputStream.cpp b/services/camera/libcameraservice/device3/Camera3InputStream.cpp
index 87907bf..6bf671e 100644
--- a/services/camera/libcameraservice/device3/Camera3InputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3InputStream.cpp
@@ -29,8 +29,8 @@
 
 Camera3InputStream::Camera3InputStream(int id,
         uint32_t width, uint32_t height, int format) :
-        Camera3IOStreamBase(id, CAMERA3_STREAM_INPUT, width, height,
-                            /*maxSize*/0, format, HAL_DATASPACE_UNKNOWN) {
+        Camera3IOStreamBase(id, CAMERA3_STREAM_INPUT, width, height, /*maxSize*/0,
+                            format, HAL_DATASPACE_UNKNOWN, CAMERA3_STREAM_ROTATION_0) {
 
     if (format == HAL_PIXEL_FORMAT_BLOB) {
         ALOGE("%s: Bad format, BLOB not supported", __FUNCTION__);
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
index 103d90b..96bed0d 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
@@ -34,9 +34,9 @@
 Camera3OutputStream::Camera3OutputStream(int id,
         sp<ANativeWindow> consumer,
         uint32_t width, uint32_t height, int format,
-        android_dataspace dataSpace) :
+        android_dataspace dataSpace, camera3_stream_rotation_t rotation) :
         Camera3IOStreamBase(id, CAMERA3_STREAM_OUTPUT, width, height,
-                            /*maxSize*/0, format, dataSpace),
+                            /*maxSize*/0, format, dataSpace, rotation),
         mConsumer(consumer),
         mTransform(0),
         mTraceFirstBuffer(true) {
@@ -50,9 +50,9 @@
 Camera3OutputStream::Camera3OutputStream(int id,
         sp<ANativeWindow> consumer,
         uint32_t width, uint32_t height, size_t maxSize, int format,
-        android_dataspace dataSpace) :
+        android_dataspace dataSpace, camera3_stream_rotation_t rotation) :
         Camera3IOStreamBase(id, CAMERA3_STREAM_OUTPUT, width, height, maxSize,
-                            format, dataSpace),
+                            format, dataSpace, rotation),
         mConsumer(consumer),
         mTransform(0),
         mTraceFirstBuffer(true) {
@@ -72,10 +72,11 @@
 Camera3OutputStream::Camera3OutputStream(int id, camera3_stream_type_t type,
                                          uint32_t width, uint32_t height,
                                          int format,
-                                         android_dataspace dataSpace) :
+                                         android_dataspace dataSpace,
+                                         camera3_stream_rotation_t rotation) :
         Camera3IOStreamBase(id, type, width, height,
                             /*maxSize*/0,
-                            format, dataSpace),
+                            format, dataSpace, rotation),
         mTransform(0) {
 
     // Subclasses expected to initialize mConsumer themselves
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.h b/services/camera/libcameraservice/device3/Camera3OutputStream.h
index f016d60..12b2ebb 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.h
@@ -40,7 +40,7 @@
      */
     Camera3OutputStream(int id, sp<ANativeWindow> consumer,
             uint32_t width, uint32_t height, int format,
-            android_dataspace dataSpace);
+            android_dataspace dataSpace, camera3_stream_rotation_t rotation);
 
     /**
      * Set up a stream for formats that have a variable buffer size for the same
@@ -48,7 +48,7 @@
      */
     Camera3OutputStream(int id, sp<ANativeWindow> consumer,
             uint32_t width, uint32_t height, size_t maxSize, int format,
-            android_dataspace dataSpace);
+            android_dataspace dataSpace, camera3_stream_rotation_t rotation);
 
     virtual ~Camera3OutputStream();
 
@@ -67,7 +67,7 @@
   protected:
     Camera3OutputStream(int id, camera3_stream_type_t type,
             uint32_t width, uint32_t height, int format,
-            android_dataspace dataSpace);
+            android_dataspace dataSpace, camera3_stream_rotation_t rotation);
 
     /**
      * Note that we release the lock briefly in this function
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index f829741..4acbce3 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -47,7 +47,7 @@
 Camera3Stream::Camera3Stream(int id,
         camera3_stream_type type,
         uint32_t width, uint32_t height, size_t maxSize, int format,
-        android_dataspace dataSpace) :
+        android_dataspace dataSpace, camera3_stream_rotation_t rotation) :
     camera3_stream(),
     mId(id),
     mName(String8::format("Camera3Stream[%d]", id)),
@@ -60,6 +60,7 @@
     camera3_stream::height = height;
     camera3_stream::format = format;
     camera3_stream::data_space = dataSpace;
+    camera3_stream::rotation = rotation;
     camera3_stream::usage = 0;
     camera3_stream::max_buffers = 0;
     camera3_stream::priv = NULL;
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.h b/services/camera/libcameraservice/device3/Camera3Stream.h
index 72f3ee9..aba27fe 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.h
+++ b/services/camera/libcameraservice/device3/Camera3Stream.h
@@ -266,7 +266,7 @@
 
     Camera3Stream(int id, camera3_stream_type type,
             uint32_t width, uint32_t height, size_t maxSize, int format,
-            android_dataspace dataSpace);
+            android_dataspace dataSpace, camera3_stream_rotation_t rotation);
 
     /**
      * Interface to be implemented by derived classes
diff --git a/services/camera/libcameraservice/device3/Camera3ZslStream.cpp b/services/camera/libcameraservice/device3/Camera3ZslStream.cpp
index 5bf7a4c..10d7f2e 100644
--- a/services/camera/libcameraservice/device3/Camera3ZslStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3ZslStream.cpp
@@ -115,7 +115,7 @@
         Camera3OutputStream(id, CAMERA3_STREAM_BIDIRECTIONAL,
                             width, height,
                             HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED,
-                            HAL_DATASPACE_UNKNOWN),
+                            HAL_DATASPACE_UNKNOWN, CAMERA3_STREAM_ROTATION_0),
         mDepth(bufferCount) {
 
     sp<IGraphicBufferProducer> producer;
diff --git a/services/camera/libcameraservice/utils/AutoConditionLock.cpp b/services/camera/libcameraservice/utils/AutoConditionLock.cpp
new file mode 100644
index 0000000..c8ee965
--- /dev/null
+++ b/services/camera/libcameraservice/utils/AutoConditionLock.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "AutoConditionLock.h"
+
+namespace android {
+
+WaitableMutexWrapper::WaitableMutexWrapper(Mutex* mutex) : mMutex{mutex}, mState{false} {}
+
+WaitableMutexWrapper::~WaitableMutexWrapper() {}
+
+// Locks manager-owned mutex
+AutoConditionLock::AutoConditionLock(const std::shared_ptr<WaitableMutexWrapper>& manager) :
+        mManager{manager}, mAutoLock{manager->mMutex} {}
+
+// Unlocks manager-owned mutex
+AutoConditionLock::~AutoConditionLock() {
+    // Unset the condition and wake everyone up before releasing lock
+    mManager->mState = false;
+    mManager->mCondition.broadcast();
+}
+
+std::unique_ptr<AutoConditionLock> AutoConditionLock::waitAndAcquire(
+        const std::shared_ptr<WaitableMutexWrapper>& manager, nsecs_t waitTime) {
+
+    if (manager == nullptr || manager->mMutex == nullptr) {
+        // Bad input, return null
+        return std::unique_ptr<AutoConditionLock>{nullptr};
+    }
+
+    // Acquire scoped lock
+    std::unique_ptr<AutoConditionLock> scopedLock(new AutoConditionLock(manager));
+
+    // Figure out what time in the future we should hit the timeout
+    nsecs_t failTime = systemTime(SYSTEM_TIME_MONOTONIC) + waitTime;
+
+    // Wait until we timeout, or success
+    while(manager->mState) {
+        status_t ret = manager->mCondition.waitRelative(*(manager->mMutex), waitTime);
+        if (ret != NO_ERROR) {
+            // Timed out or whatever, return null
+            return std::unique_ptr<AutoConditionLock>{nullptr};
+        }
+        waitTime = failTime - systemTime(SYSTEM_TIME_MONOTONIC);
+    }
+
+    // Set the condition and return
+    manager->mState = true;
+    return scopedLock;
+}
+
+std::unique_ptr<AutoConditionLock> AutoConditionLock::waitAndAcquire(
+        const std::shared_ptr<WaitableMutexWrapper>& manager) {
+
+    if (manager == nullptr || manager->mMutex == nullptr) {
+        // Bad input, return null
+        return std::unique_ptr<AutoConditionLock>{nullptr};
+    }
+
+    // Acquire scoped lock
+    std::unique_ptr<AutoConditionLock> scopedLock(new AutoConditionLock(manager));
+
+    // Wait until we timeout, or success
+    while(manager->mState) {
+        status_t ret = manager->mCondition.wait(*(manager->mMutex));
+        if (ret != NO_ERROR) {
+            // Timed out or whatever, return null
+            return std::unique_ptr<AutoConditionLock>{nullptr};
+        }
+    }
+
+    // Set the condition and return
+    manager->mState = true;
+    return scopedLock;
+}
+
+}; // namespace android
diff --git a/services/camera/libcameraservice/utils/AutoConditionLock.h b/services/camera/libcameraservice/utils/AutoConditionLock.h
new file mode 100644
index 0000000..9a3eafc
--- /dev/null
+++ b/services/camera/libcameraservice/utils/AutoConditionLock.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef ANDROID_SERVICE_UTILS_SCOPED_CONDITION_H
+#define ANDROID_SERVICE_UTILS_SCOPED_CONDITION_H
+
+#include <utils/Timers.h>
+#include <utils/Condition.h>
+#include <utils/Errors.h>
+#include <utils/Mutex.h>
+
+#include <memory>
+
+namespace android {
+
+/**
+ * WaitableMutexWrapper can be used with AutoConditionLock to construct scoped locks for the
+ * wrapped Mutex with timeouts for lock acquisition.
+ */
+class WaitableMutexWrapper {
+    friend class AutoConditionLock;
+public:
+    /**
+     * Construct the ConditionManger with the given Mutex.
+     */
+    WaitableMutexWrapper(Mutex* mutex);
+
+    virtual ~WaitableMutexWrapper();
+private:
+    Mutex* mMutex;
+    bool mState;
+    Condition mCondition;
+};
+
+/**
+ * AutoConditionLock is a scoped lock similar to Mutex::Autolock, but allows timeouts to be
+ * specified for lock acquisition.
+ *
+ * AutoConditionLock is used with a WaitableMutexWrapper to lock/unlock the WaitableMutexWrapper's
+ * wrapped Mutex, and wait/set/signal the WaitableMutexWrapper's wrapped condition. To use this,
+ * call AutoConditionLock::waitAndAcquire to get an instance.  This will:
+ *      - Lock the given WaitableMutexWrapper's mutex.
+ *      - Wait for the WaitableMutexWrapper's condition to become false, or timeout.
+ *      - Set the WaitableMutexWrapper's condition to true.
+ *
+ * When the AutoConditionLock goes out of scope and is destroyed, this will:
+ *      - Set the WaitableMutexWrapper's condition to false.
+ *      - Signal threads waiting on this condition to wakeup.
+ *      - Release WaitableMutexWrapper's mutex.
+ */
+class AutoConditionLock final {
+public:
+    AutoConditionLock() = delete;
+    AutoConditionLock(const AutoConditionLock& other) = delete;
+    AutoConditionLock & operator=(const AutoConditionLock&) = delete;
+
+    ~AutoConditionLock();
+
+    /**
+     * Make a new AutoConditionLock from a given WaitableMutexWrapper, waiting up to waitTime
+     * nanoseconds to acquire the WaitableMutexWrapper's wrapped lock.
+     *
+     * Return an empty unique_ptr if this fails, or a timeout occurs.
+     */
+    static std::unique_ptr<AutoConditionLock> waitAndAcquire(
+            const std::shared_ptr<WaitableMutexWrapper>& manager, nsecs_t waitTime);
+
+    /**
+     * Make a new AutoConditionLock from a given WaitableMutexWrapper, waiting indefinitely to
+     * acquire the WaitableMutexWrapper's wrapped lock.
+     *
+     * Return an empty unique_ptr if this fails.
+     */
+    static std::unique_ptr<AutoConditionLock> waitAndAcquire(
+            const std::shared_ptr<WaitableMutexWrapper>& manager);
+private:
+    AutoConditionLock(const std::shared_ptr<WaitableMutexWrapper>& manager);
+
+    std::shared_ptr<WaitableMutexWrapper> mManager;
+    Mutex::Autolock mAutoLock;
+};
+
+}; // namespace android
+
+#endif // ANDROID_SERVICE_UTILS_SCOPED_CONDITION_H
diff --git a/services/camera/libcameraservice/utils/ClientManager.h b/services/camera/libcameraservice/utils/ClientManager.h
new file mode 100644
index 0000000..ad5486d
--- /dev/null
+++ b/services/camera/libcameraservice/utils/ClientManager.h
@@ -0,0 +1,539 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SERVICE_UTILS_EVICTION_POLICY_MANAGER_H
+#define ANDROID_SERVICE_UTILS_EVICTION_POLICY_MANAGER_H
+
+#include <utils/Mutex.h>
+
+#include <algorithm>
+#include <utility>
+#include <vector>
+#include <set>
+#include <map>
+#include <memory>
+
+namespace android {
+namespace resource_policy {
+
+// --------------------------------------------------------------------------------
+
+/**
+ * The ClientDescriptor class is a container for a given key/value pair identifying a shared
+ * resource, and the corresponding cost, priority, owner ID, and conflicting keys list used
+ * in determining eviction behavior.
+ *
+ * Aside from the priority, these values are immutable once the ClientDescriptor has been
+ * constructed.
+ */
+template<class KEY, class VALUE>
+class ClientDescriptor final {
+public:
+    ClientDescriptor(const KEY& key, const VALUE& value, int32_t cost,
+            const std::set<KEY>& conflictingKeys, int32_t priority, int32_t ownerId);
+    ClientDescriptor(KEY&& key, VALUE&& value, int32_t cost, std::set<KEY>&& conflictingKeys,
+            int32_t priority, int32_t ownerId);
+
+    ~ClientDescriptor();
+
+    /**
+     * Return the key for this descriptor.
+     */
+    const KEY& getKey() const;
+
+    /**
+     * Return the value for this descriptor.
+     */
+    const VALUE& getValue() const;
+
+    /**
+     * Return the cost for this descriptor.
+     */
+    int32_t getCost() const;
+
+    /**
+     * Return the priority for this descriptor.
+     */
+    int32_t getPriority() const;
+
+    /**
+     * Return the owner ID for this descriptor.
+     */
+    int32_t getOwnerId() const;
+
+    /**
+     * Return true if the given key is in this descriptor's conflicting keys list.
+     */
+    bool isConflicting(const KEY& key) const;
+
+    /**
+     * Return the set of all conflicting keys for this descriptor.
+     */
+    std::set<KEY> getConflicting() const;
+
+    /**
+     * Set the proirity for this descriptor.
+     */
+    void setPriority(int32_t priority);
+
+    // This class is ordered by key
+    template<class K, class V>
+    friend bool operator < (const ClientDescriptor<K, V>& a, const ClientDescriptor<K, V>& b);
+
+private:
+    KEY mKey;
+    VALUE mValue;
+    int32_t mCost;
+    std::set<KEY> mConflicting;
+    int32_t mPriority;
+    int32_t mOwnerId;
+}; // class ClientDescriptor
+
+template<class K, class V>
+bool operator < (const ClientDescriptor<K, V>& a, const ClientDescriptor<K, V>& b) {
+    return a.mKey < b.mKey;
+}
+
+template<class KEY, class VALUE>
+ClientDescriptor<KEY, VALUE>::ClientDescriptor(const KEY& key, const VALUE& value, int32_t cost,
+        const std::set<KEY>& conflictingKeys, int32_t priority, int32_t ownerId) : mKey{key},
+        mValue{value}, mCost{cost}, mConflicting{conflictingKeys}, mPriority{priority},
+        mOwnerId{ownerId} {}
+
+template<class KEY, class VALUE>
+ClientDescriptor<KEY, VALUE>::ClientDescriptor(KEY&& key, VALUE&& value, int32_t cost,
+        std::set<KEY>&& conflictingKeys, int32_t priority, int32_t ownerId) :
+        mKey{std::forward<KEY>(key)}, mValue{std::forward<VALUE>(value)}, mCost{cost},
+        mConflicting{std::forward<std::set<KEY>>(conflictingKeys)}, mPriority{priority},
+        mOwnerId{ownerId} {}
+
+template<class KEY, class VALUE>
+ClientDescriptor<KEY, VALUE>::~ClientDescriptor() {}
+
+template<class KEY, class VALUE>
+const KEY& ClientDescriptor<KEY, VALUE>::getKey() const {
+    return mKey;
+}
+
+template<class KEY, class VALUE>
+const VALUE& ClientDescriptor<KEY, VALUE>::getValue() const {
+    return mValue;
+}
+
+template<class KEY, class VALUE>
+int32_t ClientDescriptor<KEY, VALUE>::getCost() const {
+    return mCost;
+}
+
+template<class KEY, class VALUE>
+int32_t ClientDescriptor<KEY, VALUE>::getPriority() const {
+    return mPriority;
+}
+
+template<class KEY, class VALUE>
+int32_t ClientDescriptor<KEY, VALUE>::getOwnerId() const {
+    return mOwnerId;
+}
+
+template<class KEY, class VALUE>
+bool ClientDescriptor<KEY, VALUE>::isConflicting(const KEY& key) const {
+    if (key == mKey) return true;
+    for (const auto& x : mConflicting) {
+        if (key == x) return true;
+    }
+    return false;
+}
+
+template<class KEY, class VALUE>
+std::set<KEY> ClientDescriptor<KEY, VALUE>::getConflicting() const {
+    return mConflicting;
+}
+
+template<class KEY, class VALUE>
+void ClientDescriptor<KEY, VALUE>::setPriority(int32_t priority) {
+    mPriority = priority;
+}
+
+// --------------------------------------------------------------------------------
+
+/**
+ * The ClientManager class wraps an LRU-ordered list of active clients and implements eviction
+ * behavior for handling shared resource access.
+ *
+ * When adding a new descriptor, eviction behavior is as follows:
+ *   - Keys are unique, adding a descriptor with the same key as an existing descriptor will
+ *     result in the lower-priority of the two being removed.  Priority ties result in the
+ *     LRU descriptor being evicted (this means the incoming descriptor be added in this case).
+ *   - Any descriptors with keys that are in the incoming descriptor's 'conflicting keys' list
+ *     will be removed if they have an equal or lower priority than the incoming descriptor;
+ *     if any have a higher priority, the incoming descriptor is removed instead.
+ *   - If the sum of all descriptors' costs, including the incoming descriptor's, is more than
+ *     the max cost allowed for this ClientManager, descriptors with non-zero cost, equal or lower
+ *     priority, and a different owner will be evicted in LRU order until either the cost is less
+ *     than the max cost, or all descriptors meeting this criteria have been evicted and the
+ *     incoming descriptor has the highest priority.  Otherwise, the incoming descriptor is
+ *     removed instead.
+ */
+template<class KEY, class VALUE>
+class ClientManager {
+public:
+    // The default maximum "cost" allowed before evicting
+    static constexpr int32_t DEFAULT_MAX_COST = 100;
+
+    ClientManager();
+    ClientManager(int32_t totalCost);
+
+    /**
+     * Add a given ClientDescriptor to the managed list.  ClientDescriptors for clients that
+     * are evicted by this action are returned in a vector.
+     *
+     * This may return the ClientDescriptor passed in if it would be evicted.
+     */
+    std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> addAndEvict(
+            const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client);
+
+    /**
+     * Given a map containing owner (pid) -> priority mappings, update the priority of each
+     * ClientDescriptor with an owner in this mapping.
+     */
+    void updatePriorities(const std::map<int32_t,int32_t>& ownerPriorityList);
+
+    /**
+     * Remove all ClientDescriptors.
+     */
+    void removeAll();
+
+    /**
+     * Remove and return the ClientDescriptor with a given key.
+     */
+    std::shared_ptr<ClientDescriptor<KEY, VALUE>> remove(const KEY& key);
+
+    /**
+     * Remove the given ClientDescriptor.
+     */
+    void remove(const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& value);
+
+    /**
+     * Return a vector of the ClientDescriptors that would be evicted by adding the given
+     * ClientDescriptor.
+     *
+     * This may return the ClientDescriptor passed in if it would be evicted.
+     */
+    std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> wouldEvict(
+            const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) const;
+
+    /**
+     * Return a vector of active ClientDescriptors that prevent this client from being added.
+     */
+    std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> getIncompatibleClients(
+            const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) const;
+
+    /**
+     * Return a vector containing all currently active ClientDescriptors.
+     */
+    std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> getAll() const;
+
+    /**
+     * Return a vector containing all keys of currently active ClientDescriptors.
+     */
+    std::vector<KEY> getAllKeys() const;
+
+    /**
+     * Return a vector of the owner tags of all currently active ClientDescriptors (duplicates
+     * will be removed).
+     */
+    std::vector<int32_t> getAllOwners() const;
+
+    /**
+     * Return the ClientDescriptor corresponding to the given key, or an empty shared pointer
+     * if none exists.
+     */
+    std::shared_ptr<ClientDescriptor<KEY, VALUE>> get(const KEY& key) const;
+
+protected:
+    ~ClientManager();
+
+private:
+
+    /**
+     * Return a vector of the ClientDescriptors that would be evicted by adding the given
+     * ClientDescriptor.  If returnIncompatibleClients is set to true, instead, return the
+     * vector of ClientDescriptors that are higher priority than the incoming client and
+     * either conflict with this client, or contribute to the resource cost if that would
+     * prevent the incoming client from being added.
+     *
+     * This may return the ClientDescriptor passed in.
+     */
+    std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> wouldEvictLocked(
+            const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client,
+            bool returnIncompatibleClients = false) const;
+
+    int64_t getCurrentCostLocked() const;
+
+    mutable Mutex mLock;
+    int32_t mMaxCost;
+    // LRU ordered, most recent at end
+    std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> mClients;
+}; // class ClientManager
+
+template<class KEY, class VALUE>
+ClientManager<KEY, VALUE>::ClientManager() :
+        ClientManager(DEFAULT_MAX_COST) {}
+
+template<class KEY, class VALUE>
+ClientManager<KEY, VALUE>::ClientManager(int32_t totalCost) : mMaxCost(totalCost) {}
+
+template<class KEY, class VALUE>
+ClientManager<KEY, VALUE>::~ClientManager() {}
+
+template<class KEY, class VALUE>
+std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> ClientManager<KEY, VALUE>::wouldEvict(
+        const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) const {
+    Mutex::Autolock lock(mLock);
+    return wouldEvictLocked(client);
+}
+
+template<class KEY, class VALUE>
+std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>>
+ClientManager<KEY, VALUE>::getIncompatibleClients(
+        const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) const {
+    Mutex::Autolock lock(mLock);
+    return wouldEvictLocked(client, /*returnIncompatibleClients*/true);
+}
+
+template<class KEY, class VALUE>
+std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>>
+ClientManager<KEY, VALUE>::wouldEvictLocked(
+        const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client,
+        bool returnIncompatibleClients) const {
+
+    std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> evictList;
+
+    // Disallow null clients, return input
+    if (client == nullptr) {
+        evictList.push_back(client);
+        return evictList;
+    }
+
+    const KEY& key = client->getKey();
+    int32_t cost = client->getCost();
+    int32_t priority = client->getPriority();
+    int32_t owner = client->getOwnerId();
+
+    int64_t totalCost = getCurrentCostLocked() + cost;
+
+    // Determine the MRU of the owners tied for having the highest priority
+    int32_t highestPriorityOwner = owner;
+    int32_t highestPriority = priority;
+    for (const auto& i : mClients) {
+        int32_t curPriority = i->getPriority();
+        if (curPriority >= highestPriority) {
+            highestPriority = curPriority;
+            highestPriorityOwner = i->getOwnerId();
+        }
+    }
+
+    if (highestPriority == priority) {
+        // Switch back owner if the incoming client has the highest priority, as it is MRU
+        highestPriorityOwner = owner;
+    }
+
+    // Build eviction list of clients to remove
+    for (const auto& i : mClients) {
+        const KEY& curKey = i->getKey();
+        int32_t curCost = i->getCost();
+        int32_t curPriority = i->getPriority();
+        int32_t curOwner = i->getOwnerId();
+
+        bool conflicting = (curKey == key || i->isConflicting(key) ||
+                client->isConflicting(curKey));
+
+        if (!returnIncompatibleClients) {
+            // Find evicted clients
+
+            if (conflicting && curPriority > priority) {
+                // Pre-existing conflicting client with higher priority exists
+                evictList.clear();
+                evictList.push_back(client);
+                return evictList;
+            } else if (conflicting || ((totalCost > mMaxCost && curCost > 0) &&
+                    (curPriority <= priority) &&
+                    !(highestPriorityOwner == owner && owner == curOwner))) {
+                // Add a pre-existing client to the eviction list if:
+                // - We are adding a client with higher priority that conflicts with this one.
+                // - The total cost including the incoming client's is more than the allowable
+                //   maximum, and the client has a non-zero cost, lower priority, and a different
+                //   owner than the incoming client when the incoming client has the
+                //   highest priority.
+                evictList.push_back(i);
+                totalCost -= curCost;
+            }
+        } else {
+            // Find clients preventing the incoming client from being added
+
+            if (curPriority > priority && (conflicting || (totalCost > mMaxCost && curCost > 0))) {
+                // Pre-existing conflicting client with higher priority exists
+                evictList.push_back(i);
+            }
+        }
+    }
+
+    // Immediately return the incompatible clients if we are calculating these instead
+    if (returnIncompatibleClients) {
+        return evictList;
+    }
+
+    // If the total cost is too high, return the input unless the input has the highest priority
+    if (totalCost > mMaxCost && highestPriorityOwner != owner) {
+        evictList.clear();
+        evictList.push_back(client);
+        return evictList;
+    }
+
+    return evictList;
+
+}
+
+template<class KEY, class VALUE>
+std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>> ClientManager<KEY, VALUE>::addAndEvict(
+        const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& client) {
+    Mutex::Autolock lock(mLock);
+    auto evicted = wouldEvictLocked(client);
+    auto it = evicted.begin();
+    if (it != evicted.end() && *it == client) {
+        return evicted;
+    }
+
+    auto iter = evicted.cbegin();
+
+    // Remove evicted clients from list
+    mClients.erase(std::remove_if(mClients.begin(), mClients.end(),
+        [&iter] (std::shared_ptr<ClientDescriptor<KEY, VALUE>>& curClientPtr) {
+            if (curClientPtr->getKey() == (*iter)->getKey()) {
+                iter++;
+                return true;
+            }
+            return false;
+        }), mClients.end());
+
+    mClients.push_back(client);
+
+    return evicted;
+}
+
+template<class KEY, class VALUE>
+std::vector<std::shared_ptr<ClientDescriptor<KEY, VALUE>>>
+ClientManager<KEY, VALUE>::getAll() const {
+    Mutex::Autolock lock(mLock);
+    return mClients;
+}
+
+template<class KEY, class VALUE>
+std::vector<KEY> ClientManager<KEY, VALUE>::getAllKeys() const {
+    Mutex::Autolock lock(mLock);
+    std::vector<KEY> keys(mClients.size());
+    for (const auto& i : mClients) {
+        keys.push_back(i->getKey());
+    }
+    return keys;
+}
+
+template<class KEY, class VALUE>
+std::vector<int32_t> ClientManager<KEY, VALUE>::getAllOwners() const {
+    Mutex::Autolock lock(mLock);
+    std::set<int32_t> owners;
+    for (const auto& i : mClients) {
+        owners.emplace(i->getOwnerId());
+    }
+    return std::vector<int32_t>(owners.begin(), owners.end());
+}
+
+template<class KEY, class VALUE>
+void ClientManager<KEY, VALUE>::updatePriorities(
+        const std::map<int32_t,int32_t>& ownerPriorityList) {
+    Mutex::Autolock lock(mLock);
+    for (auto& i : mClients) {
+        auto j = ownerPriorityList.find(i->getOwnerId());
+        if (j != ownerPriorityList.end()) {
+            i->setPriority(j->second);
+        }
+    }
+}
+
+template<class KEY, class VALUE>
+std::shared_ptr<ClientDescriptor<KEY, VALUE>> ClientManager<KEY, VALUE>::get(
+        const KEY& key) const {
+    Mutex::Autolock lock(mLock);
+    for (const auto& i : mClients) {
+        if (i->getKey() == key) return i;
+    }
+    return std::shared_ptr<ClientDescriptor<KEY, VALUE>>(nullptr);
+}
+
+template<class KEY, class VALUE>
+void ClientManager<KEY, VALUE>::removeAll() {
+    Mutex::Autolock lock(mLock);
+    mClients.clear();
+}
+
+template<class KEY, class VALUE>
+std::shared_ptr<ClientDescriptor<KEY, VALUE>> ClientManager<KEY, VALUE>::remove(const KEY& key) {
+    Mutex::Autolock lock(mLock);
+
+    std::shared_ptr<ClientDescriptor<KEY, VALUE>> ret;
+
+    // Remove evicted clients from list
+    mClients.erase(std::remove_if(mClients.begin(), mClients.end(),
+        [&key, &ret] (std::shared_ptr<ClientDescriptor<KEY, VALUE>>& curClientPtr) {
+            if (curClientPtr->getKey() == key) {
+                ret = curClientPtr;
+                return true;
+            }
+            return false;
+        }), mClients.end());
+
+    return ret;
+}
+
+template<class KEY, class VALUE>
+void ClientManager<KEY, VALUE>::remove(
+        const std::shared_ptr<ClientDescriptor<KEY, VALUE>>& value) {
+    Mutex::Autolock lock(mLock);
+    // Remove evicted clients from list
+    mClients.erase(std::remove_if(mClients.begin(), mClients.end(),
+        [&value] (std::shared_ptr<ClientDescriptor<KEY, VALUE>>& curClientPtr) {
+            if (curClientPtr == value) {
+                return true;
+            }
+            return false;
+        }), mClients.end());
+}
+
+template<class KEY, class VALUE>
+int64_t ClientManager<KEY, VALUE>::getCurrentCostLocked() const {
+    int64_t totalCost = 0;
+    for (const auto& x : mClients) {
+            totalCost += x->getCost();
+    }
+    return totalCost;
+}
+
+// --------------------------------------------------------------------------------
+
+}; // namespace resource_policy
+}; // namespace android
+
+#endif // ANDROID_SERVICE_UTILS_EVICTION_POLICY_MANAGER_H
diff --git a/services/camera/libcameraservice/utils/RingBuffer.h b/services/camera/libcameraservice/utils/RingBuffer.h
new file mode 100644
index 0000000..df7c00e
--- /dev/null
+++ b/services/camera/libcameraservice/utils/RingBuffer.h
@@ -0,0 +1,361 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef ANDROID_SERVICE_UTILS_RING_BUFFER_H
+#define ANDROID_SERVICE_UTILS_RING_BUFFER_H
+
+#include <utils/Log.h>
+#include <cutils/compiler.h>
+
+#include <iterator>
+#include <utility>
+#include <vector>
+
+namespace android {
+
+/**
+ * A RingBuffer class that maintains an array of objects that can grow up to a certain size.
+ * Elements added to the RingBuffer are inserted in the logical front of the buffer, and
+ * invalidate all current iterators for that RingBuffer object.
+ */
+template <class T>
+class RingBuffer final {
+public:
+
+    /**
+     * Construct a RingBuffer that can grow up to the given length.
+     */
+    RingBuffer(size_t length);
+
+    /**
+     * Forward iterator to this class.  Implements an std:forward_iterator.
+     */
+    class iterator : public std::iterator<std::forward_iterator_tag, T> {
+    public:
+        iterator(T* ptr, size_t size, size_t pos, size_t ctr);
+
+        iterator& operator++();
+
+        iterator operator++(int);
+
+        bool operator==(const iterator& rhs);
+
+        bool operator!=(const iterator& rhs);
+
+        T& operator*();
+
+        T* operator->();
+
+    private:
+        T* mPtr;
+        size_t mSize;
+        size_t mPos;
+        size_t mCtr;
+    };
+
+    /**
+     * Constant forward iterator to this class.  Implements an std:forward_iterator.
+     */
+    class const_iterator : public std::iterator<std::forward_iterator_tag, T> {
+    public:
+        const_iterator(const T* ptr, size_t size, size_t pos, size_t ctr);
+
+        const_iterator& operator++();
+
+        const_iterator operator++(int);
+
+        bool operator==(const const_iterator& rhs);
+
+        bool operator!=(const const_iterator& rhs);
+
+        const T& operator*();
+
+        const T* operator->();
+
+    private:
+        const T* mPtr;
+        size_t mSize;
+        size_t mPos;
+        size_t mCtr;
+    };
+
+    /**
+     * Adds item to the front of this RingBuffer.  If the RingBuffer is at its maximum length,
+     * this will result in the last element being replaced (this is done using the element's
+     * assignment operator).
+     *
+     * All current iterators are invalidated.
+     */
+    void add(const T& item);
+
+    /**
+     * Moves item to the front of this RingBuffer.  Following a call to this, item should no
+     * longer be used.  If the RingBuffer is at its maximum length, this will result in the
+     * last element being replaced (this is done using the element's assignment operator).
+     *
+     * All current iterators are invalidated.
+     */
+    void add(T&& item);
+
+    /**
+     * Construct item in-place in the front of this RingBuffer using the given arguments.  If
+     * the RingBuffer is at its maximum length, this will result in the last element being
+     * replaced (this is done using the element's assignment operator).
+     *
+     * All current iterators are invalidated.
+     */
+    template <class... Args>
+    void emplace(Args&&... args);
+
+    /**
+     * Get an iterator to the front of this RingBuffer.
+     */
+    iterator begin();
+
+    /**
+     * Get an iterator to the end of this RingBuffer.
+     */
+    iterator end();
+
+    /**
+     * Get a const_iterator to the front of this RingBuffer.
+     */
+    const_iterator begin() const;
+
+    /**
+     * Get a const_iterator to the end of this RingBuffer.
+     */
+    const_iterator end() const;
+
+    /**
+     * Return a reference to the element at a given index.  If the index is out of range for
+     * this ringbuffer, [0, size), the behavior for this is undefined.
+     */
+    T& operator[](size_t index);
+
+    /**
+     * Return a const reference to the element at a given index.  If the index is out of range
+     * for this ringbuffer, [0, size), the behavior for this is undefined.
+     */
+    const T& operator[](size_t index) const;
+
+    /**
+     * Return the current size of this RingBuffer.
+     */
+    size_t size() const;
+
+    /**
+     * Remove all elements from this RingBuffer and set the size to 0.
+     */
+    void clear();
+
+private:
+    size_t mFrontIdx;
+    size_t mMaxBufferSize;
+    std::vector<T> mBuffer;
+}; // class RingBuffer
+
+
+template <class T>
+RingBuffer<T>::RingBuffer(size_t length) : mFrontIdx{0}, mMaxBufferSize{length} {}
+
+template <class T>
+RingBuffer<T>::iterator::iterator(T* ptr, size_t size, size_t pos, size_t ctr) :
+        mPtr{ptr}, mSize{size}, mPos{pos}, mCtr{ctr} {}
+
+template <class T>
+typename RingBuffer<T>::iterator& RingBuffer<T>::iterator::operator++() {
+    ++mCtr;
+
+    if (CC_UNLIKELY(mCtr == mSize)) {
+        mPos = mSize;
+        return *this;
+    }
+
+    mPos = ((CC_UNLIKELY(mPos == 0)) ? mSize - 1 : mPos - 1);
+    return *this;
+}
+
+template <class T>
+typename RingBuffer<T>::iterator RingBuffer<T>::iterator::operator++(int) {
+    iterator tmp{mPtr, mSize, mPos, mCtr};
+    ++(*this);
+    return tmp;
+}
+
+template <class T>
+bool RingBuffer<T>::iterator::operator==(const iterator& rhs) {
+    return (mPtr + mPos) == (rhs.mPtr + rhs.mPos);
+}
+
+template <class T>
+bool RingBuffer<T>::iterator::operator!=(const iterator& rhs) {
+    return (mPtr + mPos) != (rhs.mPtr + rhs.mPos);
+}
+
+template <class T>
+T& RingBuffer<T>::iterator::operator*() {
+    return *(mPtr + mPos);
+}
+
+template <class T>
+T* RingBuffer<T>::iterator::operator->() {
+    return mPtr + mPos;
+}
+
+template <class T>
+RingBuffer<T>::const_iterator::const_iterator(const T* ptr, size_t size, size_t pos, size_t ctr) :
+        mPtr{ptr}, mSize{size}, mPos{pos}, mCtr{ctr} {}
+
+template <class T>
+typename RingBuffer<T>::const_iterator& RingBuffer<T>::const_iterator::operator++() {
+    ++mCtr;
+
+    if (CC_UNLIKELY(mCtr == mSize)) {
+        mPos = mSize;
+        return *this;
+    }
+
+    mPos = ((CC_UNLIKELY(mPos == 0)) ? mSize - 1 : mPos - 1);
+    return *this;
+}
+
+template <class T>
+typename RingBuffer<T>::const_iterator RingBuffer<T>::const_iterator::operator++(int) {
+    const_iterator tmp{mPtr, mSize, mPos, mCtr};
+    ++(*this);
+    return tmp;
+}
+
+template <class T>
+bool RingBuffer<T>::const_iterator::operator==(const const_iterator& rhs) {
+    return (mPtr + mPos) == (rhs.mPtr + rhs.mPos);
+}
+
+template <class T>
+bool RingBuffer<T>::const_iterator::operator!=(const const_iterator& rhs) {
+    return (mPtr + mPos) != (rhs.mPtr + rhs.mPos);
+}
+
+template <class T>
+const T& RingBuffer<T>::const_iterator::operator*() {
+    return *(mPtr + mPos);
+}
+
+template <class T>
+const T* RingBuffer<T>::const_iterator::operator->() {
+    return mPtr + mPos;
+}
+
+template <class T>
+void RingBuffer<T>::add(const T& item) {
+    if (mBuffer.size() < mMaxBufferSize) {
+        mBuffer.push_back(item);
+        mFrontIdx = ((mFrontIdx + 1) % mMaxBufferSize);
+        return;
+    }
+
+    mBuffer[mFrontIdx] = item;
+    mFrontIdx = ((mFrontIdx + 1) % mMaxBufferSize);
+}
+
+template <class T>
+void RingBuffer<T>::add(T&& item) {
+    if (mBuffer.size() != mMaxBufferSize) {
+        mBuffer.push_back(std::forward<T>(item));
+        mFrontIdx = ((mFrontIdx + 1) % mMaxBufferSize);
+        return;
+    }
+
+    // Only works for types with move assignment operator
+    mBuffer[mFrontIdx] = std::forward<T>(item);
+    mFrontIdx = ((mFrontIdx + 1) % mMaxBufferSize);
+}
+
+template <class T>
+template <class... Args>
+void RingBuffer<T>::emplace(Args&&... args) {
+    if (mBuffer.size() != mMaxBufferSize) {
+        mBuffer.emplace_back(std::forward<Args>(args)...);
+        mFrontIdx = ((mFrontIdx + 1) % mMaxBufferSize);
+        return;
+    }
+
+    // Only works for types with move assignment operator
+    mBuffer[mFrontIdx] = T(std::forward<Args>(args)...);
+    mFrontIdx = ((mFrontIdx + 1) % mMaxBufferSize);
+}
+
+template <class T>
+typename RingBuffer<T>::iterator RingBuffer<T>::begin() {
+    size_t tmp = (mBuffer.size() == 0) ? 0 : mBuffer.size() - 1;
+    return iterator(mBuffer.data(), mBuffer.size(), (mFrontIdx == 0) ? tmp : mFrontIdx - 1, 0);
+}
+
+template <class T>
+typename RingBuffer<T>::iterator RingBuffer<T>::end() {
+    size_t s = mBuffer.size();
+    return iterator(mBuffer.data(), s, s, s);
+}
+
+template <class T>
+typename RingBuffer<T>::const_iterator RingBuffer<T>::begin() const {
+    size_t tmp = (mBuffer.size() == 0) ? 0 : mBuffer.size() - 1;
+    return const_iterator(mBuffer.data(), mBuffer.size(),
+            (mFrontIdx == 0) ? tmp : mFrontIdx - 1, 0);
+}
+
+template <class T>
+typename RingBuffer<T>::const_iterator RingBuffer<T>::end() const {
+    size_t s = mBuffer.size();
+    return const_iterator(mBuffer.data(), s, s, s);
+}
+
+template <class T>
+T& RingBuffer<T>::operator[](size_t index) {
+    LOG_ALWAYS_FATAL_IF(index >= mBuffer.size(), "Index %zu out of bounds, size is %zu.",
+            index, mBuffer.size());
+    size_t pos = (index >= mFrontIdx) ?
+            mBuffer.size() - 1 - (index - mFrontIdx) : mFrontIdx - 1 - index;
+    return mBuffer[pos];
+}
+
+template <class T>
+const T& RingBuffer<T>::operator[](size_t index) const {
+    LOG_ALWAYS_FATAL_IF(index >= mBuffer.size(), "Index %zu out of bounds, size is %zu.",
+            index, mBuffer.size());
+    size_t pos = (index >= mFrontIdx) ?
+            mBuffer.size() - 1 - (index - mFrontIdx) : mFrontIdx - 1 - index;
+    return mBuffer[pos];
+}
+
+template <class T>
+size_t RingBuffer<T>::size() const {
+    return mBuffer.size();
+}
+
+template <class T>
+void RingBuffer<T>::clear() {
+    mBuffer.clear();
+    mFrontIdx = 0;
+}
+
+}; // namespace android
+
+#endif // ANDROID_SERVICE_UTILS_RING_BUFFER_H
+
+
diff --git a/services/radio/Android.mk b/services/radio/Android.mk
index 5e89b22..9ee5666 100644
--- a/services/radio/Android.mk
+++ b/services/radio/Android.mk
@@ -26,6 +26,7 @@
     libutils \
     libbinder \
     libcutils \
+    libmedia \
     libhardware \
     libradio \
     libradio_metadata
diff --git a/services/radio/RadioService.cpp b/services/radio/RadioService.cpp
index 152619b..a6c2bdf 100644
--- a/services/radio/RadioService.cpp
+++ b/services/radio/RadioService.cpp
@@ -22,6 +22,8 @@
 #include <sys/types.h>
 #include <pthread.h>
 
+#include <system/audio.h>
+#include <system/audio_policy.h>
 #include <system/radio.h>
 #include <system/radio_metadata.h>
 #include <cutils/atomic.h>
@@ -33,11 +35,13 @@
 #include <binder/MemoryBase.h>
 #include <binder/MemoryHeapBase.h>
 #include <hardware/radio.h>
+#include <media/AudioSystem.h>
 #include "RadioService.h"
 #include "RadioRegions.h"
 
 namespace android {
 
+static const char kRadioTunerAudioDeviceName[] = "Radio tuner source";
 
 RadioService::RadioService()
     : BnRadioService(), mNextUniqueId(1)
@@ -84,7 +88,7 @@
     ALOGI("loaded default module %s, handle %d", properties.product, properties.handle);
 
     convertProperties(&properties, &halProperties);
-    sp<Module> module = new Module(this, dev, properties);
+    sp<Module> module = new Module(dev, properties);
     mModules.add(properties.handle, module);
 }
 
@@ -380,10 +384,8 @@
 #undef LOG_TAG
 #define LOG_TAG "RadioService::Module"
 
-RadioService::Module::Module(const sp<RadioService>& service,
-                                      radio_hw_device* hwDevice,
-                                      radio_properties properties)
- : mService(service), mHwDevice(hwDevice), mProperties(properties), mMute(true)
+RadioService::Module::Module(radio_hw_device* hwDevice, radio_properties properties)
+ : mHwDevice(hwDevice), mProperties(properties), mMute(true)
 {
 }
 
@@ -416,6 +418,31 @@
     struct radio_hal_band_config halConfig;
     halConfig = config->band;
 
+    // Tuner preemption logic:
+    // There is a limited amount of tuners and a limited amount of radio audio sources per module.
+    // The minimum is one tuner and one audio source.
+    // The numbers of tuners and sources are indicated in the module properties.
+    // NOTE: current framework implementation only supports one radio audio source.
+    // It is possible to open more than one tuner at a time but only one tuner can be connected
+    // to the radio audio source (AUDIO_DEVICE_IN_FM_TUNER).
+    // The base rule is that a newly connected tuner always wins, i.e. always gets a tuner
+    // and can use the audio source if requested.
+    // If another client is preempted, it is notified by a callback with RADIO_EVENT_CONTROL
+    // indicating loss of control.
+    // - If the newly connected client requests the audio source (audio == true):
+    //    - if an audio source is available
+    //          no problem
+    //    - if not:
+    //          the oldest client in the list using audio is preempted.
+    // - If the newly connected client does not request the audio source (audio == false):
+    //    - if a tuner is available
+    //          no problem
+    //    - if not:
+    //          The oldest client not using audio is preempted first and if none is found the
+    //          the oldest client using audio is preempted.
+    // Each time a tuner using the audio source is opened or closed, the audio policy manager is
+    // notified of the connection or disconnection of AUDIO_DEVICE_IN_FM_TUNER.
+
     sp<ModuleClient> oldestTuner;
     sp<ModuleClient> oldestAudio;
     size_t allocatedTuners = 0;
@@ -437,28 +464,31 @@
     }
 
     const struct radio_tuner *halTuner;
+    sp<ModuleClient> preemtedClient;
     if (audio) {
         if (allocatedAudio >= mProperties.num_audio_sources) {
             ALOG_ASSERT(oldestAudio != 0, "addClient() allocatedAudio/oldestAudio mismatch");
-            halTuner = oldestAudio->getTuner();
-            oldestAudio->setTuner(NULL);
-            mHwDevice->close_tuner(mHwDevice, halTuner);
+            preemtedClient = oldestAudio;
         }
     } else {
         if (allocatedAudio + allocatedTuners >= mProperties.num_tuners) {
             if (allocatedTuners != 0) {
                 ALOG_ASSERT(oldestTuner != 0, "addClient() allocatedTuners/oldestTuner mismatch");
-                halTuner = oldestTuner->getTuner();
-                oldestTuner->setTuner(NULL);
-                mHwDevice->close_tuner(mHwDevice, halTuner);
+                preemtedClient = oldestTuner;
             } else {
                 ALOG_ASSERT(oldestAudio != 0, "addClient() allocatedAudio/oldestAudio mismatch");
-                halTuner = oldestAudio->getTuner();
-                oldestAudio->setTuner(NULL);
-                mHwDevice->close_tuner(mHwDevice, halTuner);
+                preemtedClient = oldestAudio;
             }
         }
     }
+    if (preemtedClient != 0) {
+        halTuner = preemtedClient->getTuner();
+        preemtedClient->setTuner(NULL);
+        mHwDevice->close_tuner(mHwDevice, halTuner);
+        if (preemtedClient->audio()) {
+            notifyDeviceConnection(false, "");
+        }
+    }
 
     ret = mHwDevice->open_tuner(mHwDevice, &halConfig, audio,
                                 RadioService::callback, moduleClient->callbackThread().get(),
@@ -467,11 +497,13 @@
         ALOGV("addClient() setTuner %p", halTuner);
         moduleClient->setTuner(halTuner);
         mModuleClients.add(moduleClient);
+        if (audio) {
+            notifyDeviceConnection(true, "");
+        }
     } else {
         moduleClient.clear();
     }
 
-    //TODO notify audio device connection to audio policy manager if audio is on
 
     ALOGV("addClient() DONE moduleClient %p", moduleClient.get());
 
@@ -501,19 +533,32 @@
     }
 
     mHwDevice->close_tuner(mHwDevice, halTuner);
+    if (moduleClient->audio()) {
+        notifyDeviceConnection(false, "");
+    }
 
-    //TODO notify audio device disconnection to audio policy manager if audio was on
     mMute = true;
 
     if (mModuleClients.isEmpty()) {
         return;
     }
 
+    // Tuner reallocation logic:
+    // When a client is removed and was controlling a tuner, this tuner will be allocated to a
+    // previously preempted client. This client will be notified by a callback with
+    // RADIO_EVENT_CONTROL indicating gain of control.
+    // - If a preempted client is waiting for an audio source and one becomes available:
+    //    Allocate the tuner to the most recently added client waiting for an audio source
+    // - If not:
+    //    Allocate the tuner to the most recently added client.
+    // Each time a tuner using the audio source is opened or closed, the audio policy manager is
+    // notified of the connection or disconnection of AUDIO_DEVICE_IN_FM_TUNER.
+
     sp<ModuleClient> youngestClient;
     sp<ModuleClient> youngestClientAudio;
     size_t allocatedTuners = 0;
     size_t allocatedAudio = 0;
-    for (ssize_t i = mModuleClients.size(); i >= 0; i--) {
+    for (ssize_t i = mModuleClients.size() - 1; i >= 0; i--) {
         if (mModuleClients[i]->getTuner() == NULL) {
             if (mModuleClients[i]->audio()) {
                 if (youngestClientAudio == 0) {
@@ -550,10 +595,11 @@
                                 RadioService::callback, moduleClient->callbackThread().get(),
                                 &halTuner);
 
-    //TODO notify audio device connection to audio policy manager if audio is on
-
     if (ret == 0) {
         youngestClient->setTuner(halTuner);
+        if (youngestClient->audio()) {
+            notifyDeviceConnection(true, "");
+        }
     }
 }
 
@@ -583,6 +629,16 @@
     return &mProperties.bands[0];
 }
 
+void RadioService::Module::notifyDeviceConnection(bool connected,
+                                                  const char *address) {
+    int64_t token = IPCThreadState::self()->clearCallingIdentity();
+    AudioSystem::setDeviceConnectionState(AUDIO_DEVICE_IN_FM_TUNER,
+                                          connected ? AUDIO_POLICY_DEVICE_STATE_AVAILABLE :
+                                                  AUDIO_POLICY_DEVICE_STATE_UNAVAILABLE,
+                                          address, kRadioTunerAudioDeviceName);
+    IPCThreadState::self()->restoreCallingIdentity(token);
+}
+
 #undef LOG_TAG
 #define LOG_TAG "RadioService::ModuleClient"
 
diff --git a/services/radio/RadioService.h b/services/radio/RadioService.h
index 9ede020..49feda6 100644
--- a/services/radio/RadioService.h
+++ b/services/radio/RadioService.h
@@ -66,8 +66,7 @@
     class Module : public virtual RefBase {
     public:
 
-       Module(const sp<RadioService>& service,
-              radio_hw_device* hwDevice,
+       Module(radio_hw_device* hwDevice,
               struct radio_properties properties);
 
        virtual ~Module();
@@ -88,16 +87,17 @@
        const struct radio_properties properties() const { return mProperties; }
        const struct radio_band_config *getDefaultConfig() const ;
 
-       wp<RadioService> service() const { return mService; }
-
     private:
 
-        Mutex                         mLock;
-        wp<RadioService>              mService;
-        const struct radio_hw_device        *mHwDevice;
-        const struct radio_properties       mProperties;
-        Vector< sp<ModuleClient> >    mModuleClients;
-        bool                          mMute;
+       void notifyDeviceConnection(bool connected, const char *address);
+
+        Mutex                         mLock;          // protects  mModuleClients
+        const struct radio_hw_device  *mHwDevice;     // HAL hardware device
+        const struct radio_properties mProperties;    // cached hardware module properties
+        Vector< sp<ModuleClient> >    mModuleClients; // list of attached clients
+        bool                          mMute;          // radio audio source state
+                                                      // when unmuted, audio is routed to the
+                                                      // output device selected for media use case.
     }; // class Module
 
     class CallbackThread : public Thread {
@@ -120,11 +120,11 @@
                 sp<IMemory> prepareEvent(radio_hal_event_t *halEvent);
 
     private:
-        wp<ModuleClient>      mModuleClient;
-        Condition             mCallbackCond;
-        Mutex                 mCallbackLock;
-        Vector< sp<IMemory> > mEventQueue;
-        sp<MemoryDealer>      mMemoryDealer;
+        wp<ModuleClient>      mModuleClient;    // client module the thread belongs to
+        Condition             mCallbackCond;    // condition signaled when a new event is posted
+        Mutex                 mCallbackLock;    // protects mEventQueue
+        Vector< sp<IMemory> > mEventQueue;      // pending callback events
+        sp<MemoryDealer>      mMemoryDealer;    // shared memory for callback event
     }; // class CallbackThread
 
     class ModuleClient : public BnRadio,
@@ -181,13 +181,15 @@
 
     private:
 
-        mutable Mutex               mLock;
-        wp<Module>                  mModule;
-        sp<IRadioClient>            mClient;
-        radio_band_config_t         mConfig;
-        sp<CallbackThread>          mCallbackThread;
+        mutable Mutex               mLock;           // protects mClient, mConfig and mTuner
+        wp<Module>                  mModule;         // The module this client is attached to
+        sp<IRadioClient>            mClient;         // event callback binder interface
+        radio_band_config_t         mConfig;         // current band configuration
+        sp<CallbackThread>          mCallbackThread; // event callback thread
         const bool                  mAudio;
-        const struct radio_tuner    *mTuner;
+        const struct radio_tuner    *mTuner;        // HAL tuner interface. NULL indicates that
+                                                    // this client does not have control on any
+                                                    // tuner
     }; // class ModuleClient
 
 
@@ -199,8 +201,8 @@
 
     static void convertProperties(radio_properties_t *properties,
                                   const radio_hal_properties_t *halProperties);
-    Mutex               mServiceLock;
-    volatile int32_t    mNextUniqueId;
+    Mutex               mServiceLock;   // protects mModules
+    volatile int32_t    mNextUniqueId;  // for module ID allocation
     DefaultKeyedVector< radio_handle_t, sp<Module> > mModules;
 };