Merge "Camera2: Re-entering preview state on v2 Device" into jb-mr1-dev
diff --git a/media/libmediaplayerservice/HDCP.cpp b/media/libmediaplayerservice/HDCP.cpp
index e7dea6e..09b9719 100644
--- a/media/libmediaplayerservice/HDCP.cpp
+++ b/media/libmediaplayerservice/HDCP.cpp
@@ -51,6 +51,8 @@
 }
 
 HDCP::~HDCP() {
+    Mutex::Autolock autoLock(mLock);
+
     if (mHDCPModule != NULL) {
         delete mHDCPModule;
         mHDCPModule = NULL;
@@ -63,6 +65,8 @@
 }
 
 status_t HDCP::setObserver(const sp<IHDCPObserver> &observer) {
+    Mutex::Autolock autoLock(mLock);
+
     if (mHDCPModule == NULL) {
         return NO_INIT;
     }
@@ -73,6 +77,8 @@
 }
 
 status_t HDCP::initAsync(const char *host, unsigned port) {
+    Mutex::Autolock autoLock(mLock);
+
     if (mHDCPModule == NULL) {
         return NO_INIT;
     }
@@ -81,6 +87,8 @@
 }
 
 status_t HDCP::shutdownAsync() {
+    Mutex::Autolock autoLock(mLock);
+
     if (mHDCPModule == NULL) {
         return NO_INIT;
     }
@@ -91,6 +99,8 @@
 status_t HDCP::encrypt(
         const void *inData, size_t size, uint32_t streamCTR,
         uint64_t *outInputCTR, void *outData) {
+    Mutex::Autolock autoLock(mLock);
+
     if (mHDCPModule == NULL) {
         *outInputCTR = 0;
 
@@ -106,6 +116,8 @@
 }
 
 void HDCP::observe(int msg, int ext1, int ext2) {
+    Mutex::Autolock autoLock(mLock);
+
     if (mObserver != NULL) {
         mObserver->notify(msg, ext1, ext2, NULL /* obj */);
     }
diff --git a/media/libmediaplayerservice/HDCP.h b/media/libmediaplayerservice/HDCP.h
index 4ee664d..b2fc457 100644
--- a/media/libmediaplayerservice/HDCP.h
+++ b/media/libmediaplayerservice/HDCP.h
@@ -19,6 +19,7 @@
 #define HDCP_H_
 
 #include <media/IHDCP.h>
+#include <utils/Mutex.h>
 
 namespace android {
 
@@ -35,6 +36,8 @@
             uint64_t *outInputCTR, void *outData);
 
 private:
+    Mutex mLock;
+
     void *mLibHandle;
     HDCPModule *mHDCPModule;
     sp<IHDCPObserver> mObserver;
diff --git a/media/libstagefright/SurfaceMediaSource.cpp b/media/libstagefright/SurfaceMediaSource.cpp
index e224437..3d3f421 100644
--- a/media/libstagefright/SurfaceMediaSource.cpp
+++ b/media/libstagefright/SurfaceMediaSource.cpp
@@ -193,6 +193,7 @@
 
     mStopped = true;
     mFrameAvailableCondition.signal();
+    mMediaBuffersAvailableCondition.signal();
 
     return mBufferQueue->consumerDisconnect();
 }
diff --git a/media/libstagefright/wifi-display/source/Converter.cpp b/media/libstagefright/wifi-display/source/Converter.cpp
index 368a9a1..c6118d4 100644
--- a/media/libstagefright/wifi-display/source/Converter.cpp
+++ b/media/libstagefright/wifi-display/source/Converter.cpp
@@ -114,7 +114,7 @@
     mOutputFormat = mInputFormat->dup();
     mOutputFormat->setString("mime", outputMIME.c_str());
 
-    int32_t audioBitrate = getBitrate("media.wfd.audio-bitrate", 64000);
+    int32_t audioBitrate = getBitrate("media.wfd.audio-bitrate", 128000);
     int32_t videoBitrate = getBitrate("media.wfd.video-bitrate", 5000000);
 
     ALOGI("using audio bitrate of %d bps, video bitrate of %d bps",
diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.cpp b/media/libstagefright/wifi-display/source/PlaybackSession.cpp
index 76a47ec..0facafe 100644
--- a/media/libstagefright/wifi-display/source/PlaybackSession.cpp
+++ b/media/libstagefright/wifi-display/source/PlaybackSession.cpp
@@ -24,11 +24,13 @@
 #include "MediaPuller.h"
 #include "RepeaterSource.h"
 #include "TSPacketizer.h"
+#include "include/avc_utils.h"
 
 #include <binder/IServiceManager.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/SurfaceComposerClient.h>
 #include <media/IHDCP.h>
+#include <media/stagefright/foundation/ABitReader.h>
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
@@ -151,6 +153,8 @@
 }
 
 status_t WifiDisplaySource::PlaybackSession::Track::start() {
+    ALOGV("Track::start isAudio=%d", mIsAudio);
+
     if (mStarted) {
         return INVALID_OPERATION;
     }
@@ -169,6 +173,8 @@
 }
 
 status_t WifiDisplaySource::PlaybackSession::Track::stop() {
+    ALOGV("Track::stop isAudio=%d", mIsAudio);
+
     if (!mStarted) {
         return INVALID_OPERATION;
     }
@@ -215,6 +221,7 @@
       mNotify(notify),
       mInterfaceAddr(interfaceAddr),
       mHDCP(hdcp),
+      mWeAreDead(false),
       mLastLifesignUs(),
       mVideoTrackIndex(-1),
       mTSQueue(new ABuffer(12 + kMaxNumTSPacketsPerRTPPacket * 188)),
@@ -529,6 +536,10 @@
 
 void WifiDisplaySource::PlaybackSession::onMessageReceived(
         const sp<AMessage> &msg) {
+    if (mWeAreDead) {
+        return;
+    }
+
     switch (msg->what()) {
         case kWhatRTPNotify:
         case kWhatRTCPNotify:
@@ -588,10 +599,7 @@
                     }
 #endif
 
-                    // Inform WifiDisplaySource of our premature death (wish).
-                    sp<AMessage> notify = mNotify->dup();
-                    notify->setInt32("what", kWhatSessionDead);
-                    notify->post();
+                    notifySessionDead();
                     break;
                 }
 
@@ -712,12 +720,7 @@
                         status_t err = packetizeQueuedAccessUnits();
 
                         if (err != OK) {
-                            // Inform WifiDisplaySource of our premature death
-                            // (wish).
-                            sp<AMessage> notify = mNotify->dup();
-                            notify->setInt32("what", kWhatSessionDead);
-                            notify->post();
-
+                            notifySessionDead();
                             break;
                         }
                     }
@@ -734,11 +737,7 @@
                 status_t err = packetizeAccessUnit(trackIndex, accessUnit);
 
                 if (err != OK) {
-                    // Inform WifiDisplaySource of our premature death
-                    // (wish).
-                    sp<AMessage> notify = mNotify->dup();
-                    notify->setInt32("what", kWhatSessionDead);
-                    notify->post();
+                    notifySessionDead();
                 }
                 break;
             } else if (what == Converter::kWhatEOS) {
@@ -766,10 +765,7 @@
 
                 ALOGE("converter signaled error %d", err);
 
-                // Inform WifiDisplaySource of our premature death (wish).
-                sp<AMessage> notify = mNotify->dup();
-                notify->setInt32("what", kWhatSessionDead);
-                notify->post();
+                notifySessionDead();
             }
             break;
         }
@@ -1302,7 +1298,7 @@
 }
 
 status_t WifiDisplaySource::PlaybackSession::packetizeAccessUnit(
-        size_t trackIndex, const sp<ABuffer> &accessUnit) {
+        size_t trackIndex, sp<ABuffer> accessUnit) {
     const sp<Track> &track = mTracks.valueFor(trackIndex);
 
     uint32_t flags = 0;
@@ -1318,15 +1314,14 @@
         hexdump(accessUnit->data(), MIN(64, accessUnit->size()));
 #endif
 
-        if (mTempAccessUnit == NULL
-                || mTempAccessUnit->capacity() < accessUnit->size()) {
-            mTempAccessUnit = new ABuffer(accessUnit->size());
+        if (IsIDR(accessUnit)) {
+            // XXX remove this once the encoder takes care of this.
+            accessUnit = mPacketizer->prependCSD(
+                    track->packetizerTrackIndex(), accessUnit);
         }
 
-        memcpy(mTempAccessUnit->data(), accessUnit->data(), accessUnit->size());
-
         status_t err = mHDCP->encrypt(
-                mTempAccessUnit->data(), mTempAccessUnit->size(),
+                accessUnit->data(), accessUnit->size(),
                 trackIndex  /* streamCTR */,
                 &inputCTR,
                 accessUnit->data());
@@ -1341,6 +1336,7 @@
             ALOGI("out:");
             hexdump(accessUnit->data(), MIN(64, accessUnit->size()));
             ALOGI("inputCTR: 0x%016llx", inputCTR);
+            ALOGI("streamCTR: 0x%08x", trackIndex);
 #endif
         }
 
@@ -1384,6 +1380,31 @@
         HDCP_private_data[15] =
             ((inputCTR & 0x7f) << 1) | 1;
 
+#if 0
+        ALOGI("HDCP_private_data:");
+        hexdump(HDCP_private_data, sizeof(HDCP_private_data));
+
+        ABitReader br(HDCP_private_data, sizeof(HDCP_private_data));
+        CHECK_EQ(br.getBits(13), 0);
+        CHECK_EQ(br.getBits(2), (trackIndex >> 30) & 3);
+        CHECK_EQ(br.getBits(1), 1u);
+        CHECK_EQ(br.getBits(15), (trackIndex >> 15) & 0x7fff);
+        CHECK_EQ(br.getBits(1), 1u);
+        CHECK_EQ(br.getBits(15), trackIndex & 0x7fff);
+        CHECK_EQ(br.getBits(1), 1u);
+        CHECK_EQ(br.getBits(11), 0);
+        CHECK_EQ(br.getBits(4), (inputCTR >> 60) & 0xf);
+        CHECK_EQ(br.getBits(1), 1u);
+        CHECK_EQ(br.getBits(15), (inputCTR >> 45) & 0x7fff);
+        CHECK_EQ(br.getBits(1), 1u);
+        CHECK_EQ(br.getBits(15), (inputCTR >> 30) & 0x7fff);
+        CHECK_EQ(br.getBits(1), 1u);
+        CHECK_EQ(br.getBits(15), (inputCTR >> 15) & 0x7fff);
+        CHECK_EQ(br.getBits(1), 1u);
+        CHECK_EQ(br.getBits(15), inputCTR & 0x7fff);
+        CHECK_EQ(br.getBits(1), 1u);
+#endif
+
         flags |= TSPacketizer::IS_ENCRYPTED;
     }
 
@@ -1455,5 +1476,14 @@
     return OK;
 }
 
+void WifiDisplaySource::PlaybackSession::notifySessionDead() {
+    // Inform WifiDisplaySource of our premature death (wish).
+    sp<AMessage> notify = mNotify->dup();
+    notify->setInt32("what", kWhatSessionDead);
+    notify->post();
+
+    mWeAreDead = true;
+}
+
 }  // namespace android
 
diff --git a/media/libstagefright/wifi-display/source/PlaybackSession.h b/media/libstagefright/wifi-display/source/PlaybackSession.h
index 682fe2e..342fc85 100644
--- a/media/libstagefright/wifi-display/source/PlaybackSession.h
+++ b/media/libstagefright/wifi-display/source/PlaybackSession.h
@@ -107,6 +107,7 @@
     sp<AMessage> mNotify;
     in_addr mInterfaceAddr;
     sp<IHDCP> mHDCP;
+    bool mWeAreDead;
 
     int64_t mLastLifesignUs;
 
@@ -164,8 +165,6 @@
 
     uint64_t mTotalBytesSent;
 
-    sp<ABuffer> mTempAccessUnit;
-
 #if LOG_TRANSPORT_STREAM
     FILE *mLogFile;
 #endif
@@ -203,10 +202,12 @@
     bool allTracksHavePacketizerIndex();
 
     status_t packetizeAccessUnit(
-            size_t trackIndex, const sp<ABuffer> &accessUnit);
+            size_t trackIndex, sp<ABuffer> accessUnit);
 
     status_t packetizeQueuedAccessUnits();
 
+    void notifySessionDead();
+
     DISALLOW_EVIL_CONSTRUCTORS(PlaybackSession);
 };
 
diff --git a/media/libstagefright/wifi-display/source/TSPacketizer.cpp b/media/libstagefright/wifi-display/source/TSPacketizer.cpp
index bd2f0c3..dd18998 100644
--- a/media/libstagefright/wifi-display/source/TSPacketizer.cpp
+++ b/media/libstagefright/wifi-display/source/TSPacketizer.cpp
@@ -702,5 +702,22 @@
     return crc;
 }
 
+sp<ABuffer> TSPacketizer::prependCSD(
+        size_t trackIndex, const sp<ABuffer> &accessUnit) const {
+    CHECK_LT(trackIndex, mTracks.size());
+
+    const sp<Track> &track = mTracks.itemAt(trackIndex);
+    CHECK(track->isH264() && IsIDR(accessUnit));
+
+    int64_t timeUs;
+    CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
+
+    sp<ABuffer> accessUnit2 = track->prependCSD(accessUnit);
+
+    accessUnit2->meta()->setInt64("timeUs", timeUs);
+
+    return accessUnit2;
+}
+
 }  // namespace android
 
diff --git a/media/libstagefright/wifi-display/source/TSPacketizer.h b/media/libstagefright/wifi-display/source/TSPacketizer.h
index 7020fff..01f174a 100644
--- a/media/libstagefright/wifi-display/source/TSPacketizer.h
+++ b/media/libstagefright/wifi-display/source/TSPacketizer.h
@@ -48,6 +48,11 @@
             uint32_t flags,
             const uint8_t *PES_private_data, size_t PES_private_data_len);
 
+    // XXX to be removed once encoder config option takes care of this for
+    // encrypted mode.
+    sp<ABuffer> prependCSD(
+            size_t trackIndex, const sp<ABuffer> &accessUnit) const;
+
 protected:
     virtual ~TSPacketizer();
 
diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
index b9139c9..16c0f35 100644
--- a/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
+++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.cpp
@@ -169,9 +169,10 @@
                     mNetSession->destroySession(sessionID);
 
                     if (sessionID == mClientSessionID) {
-                        mClientSessionID = -1;
+                        mClientSessionID = 0;
 
-                        disconnectClient(UNKNOWN_ERROR);
+                        mClient->onDisplayError(
+                                IRemoteDisplayClient::kDisplayErrorUnknown);
                     }
                     break;
                 }
@@ -217,7 +218,8 @@
                     status_t err = onReceiveClientData(msg);
 
                     if (err != OK) {
-                        disconnectClient(err);
+                        mClient->onDisplayError(
+                                IRemoteDisplayClient::kDisplayErrorUnknown);
                     }
                     break;
                 }
@@ -232,7 +234,8 @@
         {
             CHECK(msg->senderAwaitsResponse(&mStopReplyID));
 
-            if (mSessionID != 0 && mClientSessionID != 0) {
+            if (mClientSessionID != 0
+                    && mClientInfo.mPlaybackSessionID != -1) {
                 status_t err = sendM5(
                         mClientSessionID, true /* requestShutdown */);
 
@@ -258,7 +261,11 @@
                     + kPlaybackSessionTimeoutUs < ALooper::GetNowUs()) {
                 ALOGI("playback session timed out, reaping.");
 
-                disconnectClient(-ETIMEDOUT);
+                mNetSession->destroySession(mClientSessionID);
+                mClientSessionID = 0;
+
+                mClient->onDisplayError(
+                        IRemoteDisplayClient::kDisplayErrorUnknown);
             } else {
                 scheduleReaper();
             }
@@ -276,7 +283,8 @@
             if (what == PlaybackSession::kWhatSessionDead) {
                 ALOGI("playback session wants to quit.");
 
-                disconnectClient(UNKNOWN_ERROR);
+                mClient->onDisplayError(
+                        IRemoteDisplayClient::kDisplayErrorUnknown);
             } else if (what == PlaybackSession::kWhatSessionEstablished) {
                 if (mClient != NULL) {
                     mClient->onDisplayConnected(
@@ -354,8 +362,13 @@
                 }
 
                 case HDCPModule::HDCP_SHUTDOWN_COMPLETE:
+                case HDCPModule::HDCP_SHUTDOWN_FAILED:
                 {
-                    finishStop2();
+                    // Ugly hack to make sure that the call to
+                    // HDCPObserver::notify is completely handled before
+                    // we clear the HDCP instance and unload the shared
+                    // library :(
+                    (new AMessage(kWhatFinishStop2, id()))->post(300000ll);
                     break;
                 }
 
@@ -363,12 +376,19 @@
                 {
                     ALOGE("HDCP failure, shutting down.");
 
-                    disconnectClient(-EACCES);
+                    mClient->onDisplayError(
+                            IRemoteDisplayClient::kDisplayErrorUnknown);
                     break;
                 }
             }
             break;
         }
+
+        case kWhatFinishStop2:
+        {
+            finishStop2();
+            break;
+        }
 #endif
 
         default:
@@ -477,10 +497,9 @@
         "wfd_video_formats: "
         "78 00 02 02 00008000 00000000 00000000 00 0000 0000 00 none none\r\n"
         "wfd_audio_codecs: AAC 00000001 00\r\n"  // 2 ch AAC 48kHz
-        "wfd_presentation_URL: rtsp://%s:%d/wfd1.0/streamid=0 none\r\n"
+        "wfd_presentation_URL: rtsp://%s/wfd1.0/streamid=0 none\r\n"
         "wfd_client_rtp_ports: RTP/AVP/%s;unicast 19000 0 mode=play\r\n",
-        mClientInfo.mLocalIP.c_str(), mClientInfo.mLocalPort,
-        transportString.c_str());
+        mClientInfo.mLocalIP.c_str(), transportString.c_str());
 
     AString request = "SET_PARAMETER rtsp://localhost/wfd1.0 RTSP/1.0\r\n";
     AppendCommonResponse(&request, mNextCSeq);
@@ -508,6 +527,7 @@
 status_t WifiDisplaySource::sendM5(int32_t sessionID, bool requestShutdown) {
     AString body = "wfd_trigger_method: ";
     if (requestShutdown) {
+        ALOGI("Sending TEARDOWN trigger.");
         body.append("TEARDOWN");
     } else {
         body.append("SETUP");
@@ -1017,6 +1037,8 @@
         return ERROR_MALFORMED;
     }
 
+    ALOGI("Received PLAY request.");
+
     status_t err = playbackSession->play();
     CHECK_EQ(err, (status_t)OK);
 
@@ -1065,6 +1087,8 @@
         int32_t sessionID,
         int32_t cseq,
         const sp<ParsedMessage> &data) {
+    ALOGI("Received TEARDOWN request.");
+
     int32_t playbackSessionID;
     sp<PlaybackSession> playbackSession =
         findPlaybackSession(data, &playbackSessionID);
@@ -1079,26 +1103,23 @@
     response.append("Connection: close\r\n");
     response.append("\r\n");
 
-    status_t err = mNetSession->sendRequest(sessionID, response.c_str());
-
-    if (err != OK) {
-        return err;
-    }
+    mNetSession->sendRequest(sessionID, response.c_str());
 
     if (mStopReplyID != 0) {
         finishStop();
     } else {
-        disconnectClient(UNKNOWN_ERROR);
+        mClient->onDisplayError(IRemoteDisplayClient::kDisplayErrorUnknown);
     }
 
     return OK;
 }
 
 void WifiDisplaySource::finishStop() {
-    disconnectClient(OK);
+    ALOGV("finishStop");
 
 #if REQUIRE_HDCP
     if (mHDCP != NULL) {
+        ALOGI("Initiating HDCP shutdown.");
         mHDCP->shutdownAsync();
         return;
     }
@@ -1108,10 +1129,23 @@
 }
 
 void WifiDisplaySource::finishStop2() {
+    ALOGV("finishStop2");
+
 #if REQUIRE_HDCP
+    mHDCP->setObserver(NULL);
+    mHDCPObserver.clear();
     mHDCP.clear();
 #endif
 
+    disconnectClient();
+
+    if (mSessionID != 0) {
+        mNetSession->destroySession(mSessionID);
+        mSessionID = 0;
+    }
+
+    ALOGV("finishStop2 completed.");
+
     status_t err = OK;
 
     sp<AMessage> response = new AMessage;
@@ -1230,27 +1264,22 @@
     return mClientInfo.mPlaybackSession;
 }
 
-void WifiDisplaySource::disconnectClient(status_t err) {
+void WifiDisplaySource::disconnectClient() {
+    if (mClientInfo.mPlaybackSession != NULL) {
+        sp<PlaybackSession> playbackSession = mClientInfo.mPlaybackSession;
+        mClientInfo.mPlaybackSession.clear();
+
+        ALOGI("Destroying PlaybackSession");
+        playbackSession->destroy();
+        looper()->unregisterHandler(playbackSession->id());
+    }
+
     if (mClientSessionID != 0) {
-        if (mClientInfo.mPlaybackSession != NULL) {
-            sp<PlaybackSession> playbackSession = mClientInfo.mPlaybackSession;
-            mClientInfo.mPlaybackSession.clear();
-
-            playbackSession->destroy();
-            looper()->unregisterHandler(playbackSession->id());
-        }
-
         mNetSession->destroySession(mClientSessionID);
         mClientSessionID = 0;
     }
 
-    if (mClient != NULL) {
-        if (err != OK) {
-            mClient->onDisplayError(IRemoteDisplayClient::kDisplayErrorUnknown);
-        } else {
-            mClient->onDisplayDisconnected();
-        }
-    }
+    mClient->onDisplayDisconnected();
 }
 
 #if REQUIRE_HDCP
@@ -1306,7 +1335,7 @@
         return err;
     }
 
-    ALOGI("initiating HDCP negotiation w/ host %s:%d",
+    ALOGI("Initiating HDCP negotiation w/ host %s:%d",
             mClientInfo.mRemoteIP.c_str(), mHDCPPort);
 
     err = mHDCP->initAsync(mClientInfo.mRemoteIP.c_str(), mHDCPPort);
diff --git a/media/libstagefright/wifi-display/source/WifiDisplaySource.h b/media/libstagefright/wifi-display/source/WifiDisplaySource.h
index fb4f53d..77b15f8 100644
--- a/media/libstagefright/wifi-display/source/WifiDisplaySource.h
+++ b/media/libstagefright/wifi-display/source/WifiDisplaySource.h
@@ -63,6 +63,7 @@
         kWhatPlaybackSessionNotify,
         kWhatKeepAlive,
         kWhatHDCPNotify,
+        kWhatFinishStop2,
     };
 
     struct ResponseID {
@@ -200,10 +201,8 @@
             const sp<ParsedMessage> &data, int32_t *playbackSessionID) const;
 
     // Disconnects the current client and shuts down its playback session
-    // (if any). The reason for the disconnection is OK for orderly shutdown
-    // or a nonzero error code.
-    // A listener is notified accordingly.
-    void disconnectClient(status_t err);
+    // (if any).
+    void disconnectClient();
 
     void finishStop();
     void finishStop2();
diff --git a/services/camera/libcameraservice/camera2/Parameters.cpp b/services/camera/libcameraservice/camera2/Parameters.cpp
index ec85f9c..6383434 100644
--- a/services/camera/libcameraservice/camera2/Parameters.cpp
+++ b/services/camera/libcameraservice/camera2/Parameters.cpp
@@ -94,16 +94,16 @@
 
     params.set(CameraParameters::KEY_PREVIEW_FPS_RANGE,
             String8::format("%d,%d",
-                    previewFpsRange[0],
-                    previewFpsRange[1]));
+                    previewFpsRange[0] * kFpsToApiScale,
+                    previewFpsRange[1] * kFpsToApiScale));
 
     {
         String8 supportedPreviewFpsRange;
         for (size_t i=0; i < availableFpsRanges.count; i += 2) {
             if (i != 0) supportedPreviewFpsRange += ",";
             supportedPreviewFpsRange += String8::format("(%d,%d)",
-                    availableFpsRanges.data.i32[i],
-                    availableFpsRanges.data.i32[i+1]);
+                    availableFpsRanges.data.i32[i] * kFpsToApiScale,
+                    availableFpsRanges.data.i32[i+1] * kFpsToApiScale);
         }
         params.set(CameraParameters::KEY_SUPPORTED_PREVIEW_FPS_RANGE,
                 supportedPreviewFpsRange);
@@ -170,6 +170,7 @@
     // PREVIEW_FRAME_RATE / SUPPORTED_PREVIEW_FRAME_RATES are deprecated, but
     // still have to do something sane for them
 
+    // NOTE: Not scaled like FPS range values are.
     params.set(CameraParameters::KEY_PREVIEW_FRAME_RATE,
             previewFpsRange[0]);
 
@@ -894,6 +895,9 @@
     bool fpsRangeChanged = false;
     newParams.getPreviewFpsRange(&validatedParams.previewFpsRange[0],
             &validatedParams.previewFpsRange[1]);
+    validatedParams.previewFpsRange[0] /= kFpsToApiScale;
+    validatedParams.previewFpsRange[1] /= kFpsToApiScale;
+
     if (validatedParams.previewFpsRange[0] != previewFpsRange[0] ||
             validatedParams.previewFpsRange[1] != previewFpsRange[1]) {
         fpsRangeChanged = true;
diff --git a/services/camera/libcameraservice/camera2/Parameters.h b/services/camera/libcameraservice/camera2/Parameters.h
index 7789ebb..af23a4e 100644
--- a/services/camera/libcameraservice/camera2/Parameters.h
+++ b/services/camera/libcameraservice/camera2/Parameters.h
@@ -223,6 +223,10 @@
     // Map from camera orientation + facing to gralloc transform enum
     static int degToTransform(int degrees, bool mirror);
 
+    // API specifies FPS ranges are done in fixed point integer, with LSB = 0.001.
+    // Note that this doesn't apply to the (deprecated) single FPS value.
+    static const int kFpsToApiScale = 1000;
+
     // Transform between (-1000,-1000)-(1000,1000) normalized coords from camera
     // API and HAL2 (0,0)-(activePixelArray.width/height) coordinates
     int arrayXToNormalized(int width) const;