Media metrics for android.media.MediaRecorder

underlying collection of MediaRecorder metrics for getMetrics() API
and for media metrics cloud analysis.

Bug:  35150984
Test: hacked CTS, observation of 'dumpsys media.metrics'
Change-Id: Ife2ab06158b0015549ed17d52d7f4c35781f5e0f
diff --git a/include/media/IMediaRecorder.h b/include/media/IMediaRecorder.h
index d5aec3f..9d0341a 100644
--- a/include/media/IMediaRecorder.h
+++ b/include/media/IMediaRecorder.h
@@ -53,6 +53,7 @@
     virtual status_t setClientName(const String16& clientName) = 0;
     virtual status_t prepare() = 0;
     virtual status_t getMaxAmplitude(int* max) = 0;
+    virtual status_t getMetrics(Parcel *reply) = 0;
     virtual status_t start() = 0;
     virtual status_t stop() = 0;
     virtual status_t reset() = 0;
diff --git a/include/media/MediaRecorderBase.h b/include/media/MediaRecorderBase.h
index c273da7..0b0f916 100644
--- a/include/media/MediaRecorderBase.h
+++ b/include/media/MediaRecorderBase.h
@@ -58,6 +58,7 @@
     virtual status_t close() = 0;
     virtual status_t reset() = 0;
     virtual status_t getMaxAmplitude(int *max) = 0;
+    virtual status_t getMetrics(Parcel *reply) = 0;
     virtual status_t dump(int fd, const Vector<String16>& args) const = 0;
     virtual status_t setInputSurface(const sp<PersistentSurface>& surface) = 0;
     virtual sp<IGraphicBufferProducer> querySurfaceMediaSource() const = 0;
diff --git a/include/media/mediarecorder.h b/include/media/mediarecorder.h
index c2916be..071e7a1 100644
--- a/include/media/mediarecorder.h
+++ b/include/media/mediarecorder.h
@@ -249,6 +249,7 @@
     void        notify(int msg, int ext1, int ext2);
     status_t    setInputSurface(const sp<PersistentSurface>& surface);
     sp<IGraphicBufferProducer>     querySurfaceMediaSourceFromMediaServer();
+    status_t    getMetrics(Parcel *reply);
 
 private:
     void                    doCleanUp();
diff --git a/media/libmedia/IMediaRecorder.cpp b/media/libmedia/IMediaRecorder.cpp
index 5730c76..5282352 100644
--- a/media/libmedia/IMediaRecorder.cpp
+++ b/media/libmedia/IMediaRecorder.cpp
@@ -59,7 +59,9 @@
     SET_LISTENER,
     SET_CLIENT_NAME,
     PAUSE,
-    RESUME
+    RESUME,
+    GET_METRICS,
+
 };
 
 class BpMediaRecorder: public BpInterface<IMediaRecorder>
@@ -261,6 +263,18 @@
         return reply.readInt32();
     }
 
+    status_t getMetrics(Parcel* reply)
+    {
+        ALOGV("getMetrics");
+        Parcel data;
+        data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor());
+        status_t ret = remote()->transact(GET_METRICS, data, reply);
+        if (ret == NO_ERROR) {
+            return OK;
+        }
+        return UNKNOWN_ERROR;
+    }
+
     status_t start()
     {
         ALOGV("start");
@@ -397,6 +411,11 @@
             reply->writeInt32(ret);
             return NO_ERROR;
         } break;
+        case GET_METRICS: {
+            ALOGV("GET_METRICS");
+            status_t ret = getMetrics(reply);
+            return ret;
+        } break;
         case SET_VIDEO_SOURCE: {
             ALOGV("SET_VIDEO_SOURCE");
             CHECK_INTERFACE(IMediaRecorder, data, reply);
diff --git a/media/libmedia/mediarecorder.cpp b/media/libmedia/mediarecorder.cpp
index dbe4b3b..4405930 100644
--- a/media/libmedia/mediarecorder.cpp
+++ b/media/libmedia/mediarecorder.cpp
@@ -513,6 +513,17 @@
     return ret;
 }
 
+status_t MediaRecorder::getMetrics(Parcel *reply) {
+
+    ALOGV("getMetrics");
+
+    status_t ret = mMediaRecorder->getMetrics(reply);
+    if (OK != ret) {
+        ALOGE("getMetrics failed: %d", ret);
+    }
+    return ret;
+}
+
 status_t MediaRecorder::start()
 {
     ALOGV("start");
diff --git a/media/libmediaplayerservice/MediaRecorderClient.cpp b/media/libmediaplayerservice/MediaRecorderClient.cpp
index bb2d28b..2a3b732 100644
--- a/media/libmediaplayerservice/MediaRecorderClient.cpp
+++ b/media/libmediaplayerservice/MediaRecorderClient.cpp
@@ -238,6 +238,17 @@
     return mRecorder->getMaxAmplitude(max);
 }
 
+status_t MediaRecorderClient::getMetrics(Parcel* reply)
+{
+    ALOGV("MediaRecorderClient::getMetrics");
+    Mutex::Autolock lock(mLock);
+    if (mRecorder == NULL) {
+        ALOGE("recorder is not initialized");
+        return NO_INIT;
+    }
+    return mRecorder->getMetrics(reply);
+}
+
 status_t MediaRecorderClient::start()
 {
     ALOGV("start");
diff --git a/media/libmediaplayerservice/MediaRecorderClient.h b/media/libmediaplayerservice/MediaRecorderClient.h
index 8ddd680..4b6ee56 100644
--- a/media/libmediaplayerservice/MediaRecorderClient.h
+++ b/media/libmediaplayerservice/MediaRecorderClient.h
@@ -63,6 +63,7 @@
     virtual     status_t   setClientName(const String16& clientName);
     virtual     status_t   prepare();
     virtual     status_t   getMaxAmplitude(int* max);
+    virtual     status_t   getMetrics(Parcel* reply);
     virtual     status_t   start();
     virtual     status_t   stop();
     virtual     status_t   reset();
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index d00e377..ba4324e 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -30,6 +30,7 @@
 #include <binder/IServiceManager.h>
 
 #include <media/IMediaPlayerService.h>
+#include <media/MediaAnalyticsItem.h>
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
@@ -65,6 +66,10 @@
 static const float kMinTypicalDisplayRefreshingRate = kTypicalDisplayRefreshingRate / 2;
 static const int kMaxNumVideoTemporalLayers = 8;
 
+// key for media statistics
+static const char *kKeyRecorder = "recorder";
+// attrs for media statistics
+//
 // To collect the encoder usage for the battery app
 static void addBatteryData(uint32_t params) {
     sp<IBinder> binder =
@@ -85,6 +90,8 @@
       mStarted(false) {
 
     ALOGV("Constructor");
+
+    mAnalyticsDirty = false;
     reset();
 }
 
@@ -95,6 +102,80 @@
     if (mLooper != NULL) {
         mLooper->stop();
     }
+
+    // log the current record, provided it has some information worth recording
+    if (mAnalyticsDirty && mAnalyticsItem != NULL) {
+        updateMetrics();
+        if (mAnalyticsItem->count() > 0) {
+            mAnalyticsItem->setFinalized(true);
+            mAnalyticsItem->selfrecord();
+        }
+        delete mAnalyticsItem;
+        mAnalyticsItem = NULL;
+    }
+}
+
+void StagefrightRecorder::updateMetrics() {
+    ALOGV("updateMetrics");
+
+    // we'll populate the values from the raw fields.
+    // (NOT going to populate as we go through the various set* ops)
+
+    // TBD mOutputFormat  = OUTPUT_FORMAT_THREE_GPP;
+    // TBD mAudioEncoder  = AUDIO_ENCODER_AMR_NB;
+    // TBD mVideoEncoder  = VIDEO_ENCODER_DEFAULT;
+    mAnalyticsItem->setInt32("ht", mVideoHeight);
+    mAnalyticsItem->setInt32("wid", mVideoWidth);
+    mAnalyticsItem->setInt32("frame-rate", mFrameRate);
+    mAnalyticsItem->setInt32("video-bitrate", mVideoBitRate);
+    mAnalyticsItem->setInt32("audio-samplerate", mSampleRate);
+    mAnalyticsItem->setInt32("audio-channels", mAudioChannels);
+    mAnalyticsItem->setInt32("audio-bitrate", mAudioBitRate);
+    // TBD mInterleaveDurationUs = 0;
+    mAnalyticsItem->setInt32("video-iframe-interval", mIFramesIntervalSec);
+    // TBD mAudioSourceNode = 0;
+    // TBD mUse64BitFileOffset = false;
+    mAnalyticsItem->setInt32("movie-timescale", mMovieTimeScale);
+    mAnalyticsItem->setInt32("audio-timescale", mAudioTimeScale);
+    mAnalyticsItem->setInt32("video-timescale", mVideoTimeScale);
+    // TBD mCameraId        = 0;
+    // TBD mStartTimeOffsetMs = -1;
+    mAnalyticsItem->setInt32("video-encoder-profile", mVideoEncoderProfile);
+    mAnalyticsItem->setInt32("video-encoder-level", mVideoEncoderLevel);
+    // TBD mMaxFileDurationUs = 0;
+    // TBD mMaxFileSizeBytes = 0;
+    // TBD mTrackEveryTimeDurationUs = 0;
+    mAnalyticsItem->setInt32("capture-fpsenable", mCaptureFpsEnable);
+    mAnalyticsItem->setInt32("capture-fps", mCaptureFps);
+    // TBD mTimeBetweenCaptureUs = -1;
+    // TBD mCameraSourceTimeLapse = NULL;
+    // TBD mMetaDataStoredInVideoBuffers = kMetadataBufferTypeInvalid;
+    // TBD mEncoderProfiles = MediaProfiles::getInstance();
+    mAnalyticsItem->setInt32("rotation", mRotationDegrees);
+    // PII mLatitudex10000 = -3600000;
+    // PII mLongitudex10000 = -3600000;
+    // TBD mTotalBitRate = 0;
+
+    // TBD: some duration information (capture, paused)
+    //
+
+}
+
+void StagefrightRecorder::resetMetrics() {
+    ALOGV("resetMetrics");
+    // flush anything we have, restart the record
+    if (mAnalyticsDirty && mAnalyticsItem != NULL) {
+        updateMetrics();
+        if (mAnalyticsItem->count() > 0) {
+            mAnalyticsItem->setFinalized(true);
+            mAnalyticsItem->selfrecord();
+        }
+        delete mAnalyticsItem;
+        mAnalyticsItem = NULL;
+    }
+    mAnalyticsItem = new MediaAnalyticsItem(kKeyRecorder);
+    (void) mAnalyticsItem->generateSessionID();
+    mAnalyticsDirty = false;
 }
 
 status_t StagefrightRecorder::init() {
@@ -387,6 +468,7 @@
 
     // Additional check on the sample rate will be performed later.
     mSampleRate = sampleRate;
+
     return OK;
 }
 
@@ -399,6 +481,7 @@
 
     // Additional check on the number of channels will be performed later.
     mAudioChannels = channels;
+
     return OK;
 }
 
@@ -930,6 +1013,7 @@
     }
 
     if ((status == OK) && (!mStarted)) {
+        mAnalyticsDirty = true;
         mStarted = true;
 
         uint32_t params = IMediaPlayerService::kBatteryDataCodecStarted;
@@ -1887,6 +1971,9 @@
         err = mWriter->stop();
         mWriter.clear();
     }
+
+    resetMetrics();
+
     mTotalPausedDurationUs = 0;
     mPauseStartTimeUs = 0;
 
@@ -1990,6 +2077,23 @@
     return OK;
 }
 
+status_t StagefrightRecorder::getMetrics(Parcel *reply) {
+    ALOGD("StagefrightRecorder::getMetrics");
+
+    if (reply == NULL) {
+        ALOGE("Null pointer argument");
+        return BAD_VALUE;
+    }
+
+    if (mAnalyticsItem == NULL) {
+        return UNKNOWN_ERROR;
+    }
+
+    updateMetrics();
+    mAnalyticsItem->writeToParcel(reply);
+    return OK;
+}
+
 status_t StagefrightRecorder::dump(
         int fd, const Vector<String16>& args) const {
     ALOGV("dump");
diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h
index b7d0b0e..38377d2 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.h
+++ b/media/libmediaplayerservice/StagefrightRecorder.h
@@ -18,6 +18,7 @@
 
 #define STAGEFRIGHT_RECORDER_H_
 
+#include <media/MediaAnalyticsItem.h>
 #include <media/MediaRecorderBase.h>
 #include <camera/CameraParameters.h>
 #include <utils/String8.h>
@@ -67,6 +68,7 @@
     virtual status_t close();
     virtual status_t reset();
     virtual status_t getMaxAmplitude(int *max);
+    virtual status_t getMetrics(Parcel *reply);
     virtual status_t dump(int fd, const Vector<String16> &args) const;
     // Querying a SurfaceMediaSourcer
     virtual sp<IGraphicBufferProducer> querySurfaceMediaSource() const;
@@ -85,6 +87,11 @@
     int mOutputFd;
     sp<AudioSource> mAudioSourceNode;
 
+    MediaAnalyticsItem *mAnalyticsItem;
+    bool mAnalyticsDirty;
+    void resetMetrics();
+    void updateMetrics();
+
     audio_source_t mAudioSource;
     video_source mVideoSource;
     output_format mOutputFormat;