MediaMetrics: Make submit one-way

Remove the unused session id to improve speed and clarity.

Test: dumpsys media.metrics sanity, mediametrics_tests
Change-Id: Ide5e9218811e110997fed19f43eb6a5fdc5812ec
diff --git a/drm/drmserver/DrmManager.cpp b/drm/drmserver/DrmManager.cpp
index 66610b3..e2ea83a 100644
--- a/drm/drmserver/DrmManager.cpp
+++ b/drm/drmserver/DrmManager.cpp
@@ -59,7 +59,6 @@
     IDrmEngine& engine = mPlugInManager.getPlugIn(plugInId);
 
     std::unique_ptr<MediaAnalyticsItem> item(MediaAnalyticsItem::create("drmmanager"));
-    item->generateSessionID();
     item->setUid(IPCThreadState::self()->getCallingUid());
     item->setCString("function_name", func);
     item->setCString("plugin_id", plugInId.getPathLeaf().getBasePath().c_str());
diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp
index 37609eb..4529fe5 100644
--- a/drm/libmediadrm/DrmHal.cpp
+++ b/drm/libmediadrm/DrmHal.cpp
@@ -1574,7 +1574,6 @@
 void DrmHal::reportFrameworkMetrics() const
 {
     std::unique_ptr<MediaAnalyticsItem> item(MediaAnalyticsItem::create("mediadrm"));
-    item->generateSessionID();
     item->setPkgName(mMetrics.GetAppPackageName().c_str());
     String8 vendor;
     String8 description;
diff --git a/drm/libmediadrm/PluginMetricsReporting.cpp b/drm/libmediadrm/PluginMetricsReporting.cpp
index 8cd6f96..098f07b 100644
--- a/drm/libmediadrm/PluginMetricsReporting.cpp
+++ b/drm/libmediadrm/PluginMetricsReporting.cpp
@@ -35,8 +35,6 @@
                              const String8& name,
                              const String8& appPackageName) {
     std::unique_ptr<MediaAnalyticsItem> analyticsItem(MediaAnalyticsItem::create(name.c_str()));
-    analyticsItem->generateSessionID();
-
     std::string app_package_name(appPackageName.c_str(), appPackageName.size());
     analyticsItem->setPkgName(app_package_name);
     if (metrics.size() > 0) {
@@ -44,7 +42,7 @@
     }
 
     if (!analyticsItem->selfrecord()) {
-      ALOGE("selfrecord() returned false. sessioId %" PRId64, analyticsItem->getSessionID());
+      ALOGE("%s: selfrecord() returned false", __func__);
     }
 
     return OK;
diff --git a/media/libmediametrics/IMediaAnalyticsService.cpp b/media/libmediametrics/IMediaAnalyticsService.cpp
index 9114927..1ab6653 100644
--- a/media/libmediametrics/IMediaAnalyticsService.cpp
+++ b/media/libmediametrics/IMediaAnalyticsService.cpp
@@ -32,15 +32,10 @@
 #include <media/MediaAnalyticsItem.h>
 #include <media/IMediaAnalyticsService.h>
 
-#define DEBUGGING               0
-#define DEBUGGING_FLOW          0
-#define DEBUGGING_RETURNS       0
-
 namespace android {
 
 enum {
-    GENERATE_UNIQUE_SESSIONID = IBinder::FIRST_CALL_TRANSACTION,
-    SUBMIT_ITEM,
+    SUBMIT_ITEM_ONEWAY = IBinder::FIRST_CALL_TRANSACTION,
 };
 
 class BpMediaAnalyticsService: public BpInterface<IMediaAnalyticsService>
@@ -51,61 +46,23 @@
     {
     }
 
-    virtual MediaAnalyticsItem::SessionID_t generateUniqueSessionID() {
-        Parcel data, reply;
-        status_t err;
-        MediaAnalyticsItem::SessionID_t sessionid =
-                        MediaAnalyticsItem::SessionIDInvalid;
-
-        data.writeInterfaceToken(IMediaAnalyticsService::getInterfaceDescriptor());
-        err = remote()->transact(GENERATE_UNIQUE_SESSIONID, data, &reply);
-        if (err != NO_ERROR) {
-            ALOGW("bad response from service for generateSessionId, err=%d", err);
-            return MediaAnalyticsItem::SessionIDInvalid;
-        }
-        sessionid = reply.readInt64();
-        if (DEBUGGING_RETURNS) {
-            ALOGD("the caller gets a sessionid of %" PRId64 " back", sessionid);
-        }
-        return sessionid;
-    }
-
-    virtual MediaAnalyticsItem::SessionID_t submit(MediaAnalyticsItem *item, bool forcenew)
+    status_t submit(MediaAnalyticsItem *item) override
     {
-        // have this record submit itself
-        // this will be a binder call with appropriate timing
-        // return value is the uuid that the system generated for it.
-        // the return value 0 and -1 are reserved.
-        // -1 to indicate that there was a problem recording...
-
-        Parcel data, reply;
-        status_t err;
-
-        if (item == NULL) {
-                return MediaAnalyticsItem::SessionIDInvalid;
+        if (item == nullptr) {
+            return BAD_VALUE;
         }
+        ALOGV("%s: (ONEWAY) item=%s", __func__, item->toString().c_str());
 
+        Parcel data;
         data.writeInterfaceToken(IMediaAnalyticsService::getInterfaceDescriptor());
-        if(DEBUGGING_FLOW) {
-            ALOGD("client offers record: %s", item->toString().c_str());
-        }
-        data.writeBool(forcenew);
         item->writeToParcel(&data);
 
-        err = remote()->transact(SUBMIT_ITEM, data, &reply);
-        if (err != NO_ERROR) {
-            ALOGW("bad response from service for submit, err=%d", err);
-            return MediaAnalyticsItem::SessionIDInvalid;
-        }
-
-        // get an answer out of 'reply'
-        int64_t sessionid = reply.readInt64();
-        if (DEBUGGING_RETURNS) {
-            ALOGD("the caller gets sessionid=%" PRId64 "", sessionid);
-        }
-        return sessionid;
+        status_t err = remote()->transact(
+                SUBMIT_ITEM_ONEWAY, data, nullptr /* reply */, IBinder::FLAG_ONEWAY);
+        ALOGW_IF(err != NO_ERROR, "%s: bad response from service for submit, err=%d",
+                __func__, err);
+        return err;
     }
-
 };
 
 IMPLEMENT_META_INTERFACE(MediaAnalyticsService, "android.media.IMediaAnalyticsService");
@@ -115,49 +72,23 @@
 status_t BnMediaAnalyticsService::onTransact(
     uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
 {
-
-
-    // get calling pid/tid
-    IPCThreadState *ipc = IPCThreadState::self();
-    int clientPid = ipc->getCallingPid();
-    // permission checking
-
-    if(DEBUGGING_FLOW) {
-        ALOGD("running in service, code %d, pid %d; called from pid %d",
-            code, getpid(), clientPid);
-    }
+    const int clientPid = IPCThreadState::self()->getCallingPid();
 
     switch (code) {
+    case SUBMIT_ITEM_ONEWAY: {
+        CHECK_INTERFACE(IMediaAnalyticsService, data, reply);
 
-        case GENERATE_UNIQUE_SESSIONID: {
-            CHECK_INTERFACE(IMediaAnalyticsService, data, reply);
+        MediaAnalyticsItem * const item = MediaAnalyticsItem::create();
+        if (item->readFromParcel(data) < 0) {
+            return BAD_VALUE;
+        }
+        item->setPid(clientPid);
+        const status_t status __unused = submitInternal(item, true /* release */);
+        return NO_ERROR;
+    } break;
 
-            MediaAnalyticsItem::SessionID_t sessionid = generateUniqueSessionID();
-            reply->writeInt64(sessionid);
-
-            return NO_ERROR;
-        } break;
-
-        case SUBMIT_ITEM: {
-            CHECK_INTERFACE(IMediaAnalyticsService, data, reply);
-
-            bool forcenew;
-            MediaAnalyticsItem *item = MediaAnalyticsItem::create();
-
-            data.readBool(&forcenew);
-            item->readFromParcel(data);
-
-            item->setPid(clientPid);
-
-            // submit() takes over ownership of 'item'
-            MediaAnalyticsItem::SessionID_t sessionid = submit(item, forcenew);
-            reply->writeInt64(sessionid);
-
-            return NO_ERROR;
-        } break;
-
-        default:
-            return BBinder::onTransact(code, data, reply, flags);
+    default:
+        return BBinder::onTransact(code, data, reply, flags);
     }
 }
 
diff --git a/media/libmediametrics/MediaAnalyticsItem.cpp b/media/libmediametrics/MediaAnalyticsItem.cpp
index c8713bf..20b10db 100644
--- a/media/libmediametrics/MediaAnalyticsItem.cpp
+++ b/media/libmediametrics/MediaAnalyticsItem.cpp
@@ -22,10 +22,11 @@
 #include <string.h>
 #include <sys/types.h>
 
+#include <mutex>
+
 #include <binder/Parcel.h>
 #include <utils/Errors.h>
 #include <utils/Log.h>
-#include <utils/Mutex.h>
 #include <utils/SortedVector.h>
 #include <utils/threads.h>
 
@@ -79,9 +80,7 @@
     : mPid(-1),
       mUid(-1),
       mPkgVersionCode(0),
-      mSessionID(MediaAnalyticsItem::SessionIDNone),
       mTimestamp(0),
-      mFinalized(1),
       mPropCount(0), mPropSize(0), mProps(NULL)
 {
     mKey = MediaAnalyticsItem::kKeyNone;
@@ -91,9 +90,7 @@
     : mPid(-1),
       mUid(-1),
       mPkgVersionCode(0),
-      mSessionID(MediaAnalyticsItem::SessionIDNone),
       mTimestamp(0),
-      mFinalized(1),
       mPropCount(0), mPropSize(0), mProps(NULL)
 {
     if (DEBUG_ALLOCATIONS) {
@@ -114,9 +111,6 @@
     // clean allocated storage from key
     mKey.clear();
 
-    // clean various major parameters
-    mSessionID = MediaAnalyticsItem::SessionIDNone;
-
     // clean attributes
     // contents of the attributes
     for (size_t i = 0 ; i < mPropCount; i++ ) {
@@ -143,9 +137,7 @@
         dst->mUid = this->mUid;
         dst->mPkgName = this->mPkgName;
         dst->mPkgVersionCode = this->mPkgVersionCode;
-        dst->mSessionID = this->mSessionID;
         dst->mTimestamp = this->mTimestamp;
-        dst->mFinalized = this->mFinalized;
 
         // properties aka attributes
         dst->growProps(this->mPropCount);
@@ -158,35 +150,6 @@
     return dst;
 }
 
-MediaAnalyticsItem &MediaAnalyticsItem::setSessionID(MediaAnalyticsItem::SessionID_t id) {
-    mSessionID = id;
-    return *this;
-}
-
-MediaAnalyticsItem::SessionID_t MediaAnalyticsItem::getSessionID() const {
-    return mSessionID;
-}
-
-MediaAnalyticsItem::SessionID_t MediaAnalyticsItem::generateSessionID() {
-
-    if (mSessionID == SessionIDNone) {
-        // get one from the server
-        MediaAnalyticsItem::SessionID_t newid = SessionIDNone;
-        sp<IMediaAnalyticsService> svc = getInstance();
-        if (svc != NULL) {
-            newid = svc->generateUniqueSessionID();
-        }
-        mSessionID = newid;
-    }
-
-    return mSessionID;
-}
-
-MediaAnalyticsItem &MediaAnalyticsItem::clearSessionID() {
-    mSessionID = MediaAnalyticsItem::SessionIDNone;
-    return *this;
-}
-
 MediaAnalyticsItem &MediaAnalyticsItem::setTimestamp(nsecs_t ts) {
     mTimestamp = ts;
     return *this;
@@ -679,11 +642,8 @@
     mUid = data.readInt32();
     mPkgName = data.readCString();
     mPkgVersionCode = data.readInt64();
-    mSessionID = data.readInt64();
     // We no longer pay attention to user setting of finalized, BUT it's
     // still part of the wire packet -- so read & discard.
-    mFinalized = data.readInt32();
-    mFinalized = 1;
     mTimestamp = data.readInt64();
 
     int count = data.readInt32();
@@ -744,8 +704,6 @@
     data->writeInt32(mUid);
     data->writeCString(mPkgName.c_str());
     data->writeInt64(mPkgVersionCode);
-    data->writeInt64(mSessionID);
-    data->writeInt32(mFinalized);
     data->writeInt64(mTimestamp);
 
     // set of items
@@ -821,9 +779,7 @@
     // same order as we spill into the parcel, although not required
     // key+session are our primary matching criteria
     result.append(mKey.c_str());
-    result.append(":");
-    snprintf(buffer, sizeof(buffer), "%" PRId64 ":", mSessionID);
-    result.append(buffer);
+    result.append(":0:"); // sessionID
 
     snprintf(buffer, sizeof(buffer), "%d:", mUid);
     result.append(buffer);
@@ -842,7 +798,7 @@
     }
     result.append(buffer);
 
-    snprintf(buffer, sizeof(buffer), "%d:", mFinalized);
+    snprintf(buffer, sizeof(buffer), "%d:", 0 /* finalized */); // TODO: remove this.
     result.append(buffer);
     snprintf(buffer, sizeof(buffer), "%" PRId64 ":", mTimestamp);
     result.append(buffer);
@@ -899,23 +855,12 @@
 // for the lazy, we offer methods that finds the service and
 // calls the appropriate daemon
 bool MediaAnalyticsItem::selfrecord() {
-    return selfrecord(false);
-}
-
-bool MediaAnalyticsItem::selfrecord(bool forcenew) {
-
-    if (DEBUG_API) {
-        std::string p = this->toString();
-        ALOGD("selfrecord of: %s [forcenew=%d]", p.c_str(), forcenew);
-    }
-
+    ALOGD_IF(DEBUG_API, "%s: delivering %s", __func__, this->toString().c_str());
     sp<IMediaAnalyticsService> svc = getInstance();
-
     if (svc != NULL) {
-        MediaAnalyticsItem::SessionID_t newid = svc->submit(this, forcenew);
-        if (newid == SessionIDInvalid) {
-            std::string p = this->toString();
-            ALOGW("Failed to record: %s [forcenew=%d]", p.c_str(), forcenew);
+        status_t status = svc->submit(this);
+        if (status != NO_ERROR) {
+            ALOGW("%s: failed to record: %s", __func__, this->toString().c_str());
             return false;
         }
         return true;
@@ -924,29 +869,33 @@
     }
 }
 
-// get a connection we can reuse for most of our lifetime
-// static
-sp<IMediaAnalyticsService> MediaAnalyticsItem::sAnalyticsService;
-static Mutex sInitMutex;
-static int remainingBindAttempts = SVC_TRIES;
 
 //static
 bool MediaAnalyticsItem::isEnabled() {
-    int enabled = property_get_int32(MediaAnalyticsItem::EnabledProperty, -1);
+    // completely skip logging from certain UIDs. We do this here
+    // to avoid the multi-second timeouts while we learn that
+    // sepolicy will not let us find the service.
+    // We do this only for a select set of UIDs
+    // The sepolicy protection is still in place, we just want a faster
+    // response from this specific, small set of uids.
 
+    // This is checked only once in the lifetime of the process.
+    const uid_t uid = getuid();
+    switch (uid) {
+    case AID_RADIO:     // telephony subsystem, RIL
+        return false;
+    }
+
+    int enabled = property_get_int32(MediaAnalyticsItem::EnabledProperty, -1);
     if (enabled == -1) {
         enabled = property_get_int32(MediaAnalyticsItem::EnabledPropertyPersist, -1);
     }
     if (enabled == -1) {
         enabled = MediaAnalyticsItem::EnabledProperty_default;
     }
-    if (enabled <= 0) {
-        return false;
-    }
-    return true;
+    return enabled > 0;
 }
 
-
 // monitor health of our connection to the metrics service
 class MediaMetricsDeathNotifier : public IBinder::DeathRecipient {
         virtual void binderDied(const wp<IBinder> &) {
@@ -955,83 +904,56 @@
         }
 };
 
-static sp<MediaMetricsDeathNotifier> sNotifier = NULL;
+static sp<MediaMetricsDeathNotifier> sNotifier;
+// static
+sp<IMediaAnalyticsService> MediaAnalyticsItem::sAnalyticsService;
+static std::mutex sServiceMutex;
+static int sRemainingBindAttempts = SVC_TRIES;
 
 // static
 void MediaAnalyticsItem::dropInstance() {
-    Mutex::Autolock _l(sInitMutex);
-    remainingBindAttempts = SVC_TRIES;
-    sAnalyticsService = NULL;
+    std::lock_guard  _l(sServiceMutex);
+    sRemainingBindAttempts = SVC_TRIES;
+    sAnalyticsService = nullptr;
 }
 
 //static
 sp<IMediaAnalyticsService> MediaAnalyticsItem::getInstance() {
-
     static const char *servicename = "media.metrics";
-    int enabled = isEnabled();
+    static const bool enabled = isEnabled(); // singleton initialized
 
     if (enabled == false) {
-        if (DEBUG_SERVICEACCESS) {
-                ALOGD("disabled");
-        }
-        return NULL;
+        ALOGD_IF(DEBUG_SERVICEACCESS, "disabled");
+        return nullptr;
     }
-
-    // completely skip logging from certain UIDs. We do this here
-    // to avoid the multi-second timeouts while we learn that
-    // sepolicy will not let us find the service.
-    // We do this only for a select set of UIDs
-    // The sepolicy protection is still in place, we just want a faster
-    // response from this specific, small set of uids.
-    {
-        uid_t uid = getuid();
-        switch (uid) {
-            case AID_RADIO:     // telephony subsystem, RIL
-                return NULL;
-                break;
-            default:
-                // let sepolicy deny access if appropriate
-                break;
-        }
-    }
-
-    {
-        Mutex::Autolock _l(sInitMutex);
+    std::lock_guard _l(sServiceMutex);
+    // think of remainingBindAttempts as telling us whether service == nullptr because
+    // (1) we haven't tried to initialize it yet
+    // (2) we've tried to initialize it, but failed.
+    if (sAnalyticsService == nullptr && sRemainingBindAttempts > 0) {
         const char *badness = "";
-
-        // think of remainingBindAttempts as telling us whether service==NULL because
-        // (1) we haven't tried to initialize it yet
-        // (2) we've tried to initialize it, but failed.
-        if (sAnalyticsService == NULL && remainingBindAttempts > 0) {
-            sp<IServiceManager> sm = defaultServiceManager();
-            if (sm != NULL) {
-                sp<IBinder> binder = sm->getService(String16(servicename));
-                if (binder != NULL) {
-                    sAnalyticsService = interface_cast<IMediaAnalyticsService>(binder);
-                    if (sNotifier != NULL) {
-                        sNotifier = NULL;
-                    }
-                    sNotifier = new MediaMetricsDeathNotifier();
-                    binder->linkToDeath(sNotifier);
-                } else {
-                    badness = "did not find service";
-                }
+        sp<IServiceManager> sm = defaultServiceManager();
+        if (sm != nullptr) {
+            sp<IBinder> binder = sm->getService(String16(servicename));
+            if (binder != nullptr) {
+                sAnalyticsService = interface_cast<IMediaAnalyticsService>(binder);
+                sNotifier = new MediaMetricsDeathNotifier();
+                binder->linkToDeath(sNotifier);
             } else {
-                badness = "No Service Manager access";
+                badness = "did not find service";
             }
-
-            if (sAnalyticsService == NULL) {
-                if (remainingBindAttempts > 0) {
-                    remainingBindAttempts--;
-                }
-                if (DEBUG_SERVICEACCESS) {
-                    ALOGD("Unable to bind to service %s: %s", servicename, badness);
-                }
-            }
+        } else {
+            badness = "No Service Manager access";
         }
-
-        return sAnalyticsService;
+        if (sAnalyticsService == nullptr) {
+            if (sRemainingBindAttempts > 0) {
+                sRemainingBindAttempts--;
+            }
+            ALOGD_IF(DEBUG_SERVICEACCESS, "%s: unable to bind to service %s: %s",
+                    __func__, servicename, badness);
+        }
     }
+    return sAnalyticsService;
 }
 
 // merge the info from 'incoming' into this record.
@@ -1042,8 +964,6 @@
     // 'this' should never be missing both of them...
     if (mKey.empty()) {
         mKey = incoming->mKey;
-    } else if (mSessionID == 0) {
-        mSessionID = incoming->mSessionID;
     }
 
     // for each attribute from 'incoming', resolve appropriately
diff --git a/media/libmediametrics/include/IMediaAnalyticsService.h b/media/libmediametrics/include/IMediaAnalyticsService.h
index f2e7710..1453b5f 100644
--- a/media/libmediametrics/include/IMediaAnalyticsService.h
+++ b/media/libmediametrics/include/IMediaAnalyticsService.h
@@ -40,24 +40,15 @@
     DECLARE_META_INTERFACE(MediaAnalyticsService);
 
     /**
-     * Returns a unique sessionID to use across multiple requests;
-     * 'unique' is within this device, since last reboot.
-     */
-    virtual MediaAnalyticsItem::SessionID_t generateUniqueSessionID() = 0;
-
-    /**
      * Submits the indicated record to the mediaanalytics service, where
      * it will be merged (if appropriate) with incomplete records that
      * share the same key and sessionID.
      *
      * \param item the item to submit.
-     * \param forcenew marks any matching incomplete record as complete before
-     *                 inserting this new record.
-     *
-     * \return the sessionID associated with that item or
-     *         MediaAnalyticsItem::SessionIDInvalid on failure.
+     * \return status which is negative if an error is detected (some errors
+               may be silent and return 0 - success).
      */
-    virtual MediaAnalyticsItem::SessionID_t submit(MediaAnalyticsItem *item, bool forcenew) = 0;
+    virtual status_t submit(MediaAnalyticsItem *item) = 0;
 };
 
 // ----------------------------------------------------------------------------
@@ -69,6 +60,11 @@
                         const Parcel& data,
                         Parcel* reply,
                         uint32_t flags = 0) override;
+
+protected:
+    // Internal call where release is true if the service is to delete the item.
+    virtual status_t submitInternal(
+            MediaAnalyticsItem *item, bool release) = 0;
 };
 
 }; // namespace android
diff --git a/media/libmediametrics/include/MediaAnalyticsItem.h b/media/libmediametrics/include/MediaAnalyticsItem.h
index 3024c84..b37eff43 100644
--- a/media/libmediametrics/include/MediaAnalyticsItem.h
+++ b/media/libmediametrics/include/MediaAnalyticsItem.h
@@ -56,12 +56,6 @@
                 kTypeRate = 5,
             };
 
-        // sessionid
-        // unique within device, within boot,
-        typedef int64_t SessionID_t;
-        static constexpr SessionID_t SessionIDInvalid = -1;
-        static constexpr SessionID_t SessionIDNone = 0;
-
         // Key: the record descriminator
         // values for the record discriminator
         // values can be "component/component"
@@ -102,16 +96,6 @@
         // access functions for the class
         ~MediaAnalyticsItem();
 
-        // SessionID ties multiple submissions for same key together
-        // so that if video "height" and "width" are known at one point
-        // and "framerate" is only known later, they can be be brought
-        // together.
-        MediaAnalyticsItem &setSessionID(SessionID_t);
-        MediaAnalyticsItem &clearSessionID();
-        SessionID_t getSessionID() const;
-        // generates and stores a new ID iff mSessionID == SessionIDNone
-        SessionID_t generateSessionID();
-
         // reset all contents, discarding any extra data
         void clear();
         MediaAnalyticsItem *dup();
@@ -149,10 +133,7 @@
         bool getCString(Attr, char **value);
         bool getString(Attr, std::string *value);
 
-        // parameter indicates whether to close any existing open
-        // record with same key before establishing a new record
-        // caller retains ownership of 'this'.
-        bool selfrecord(bool);
+        // Deliver the item to MediaMetrics
         bool selfrecord();
 
         // remove indicated attributes and their values
@@ -231,12 +212,8 @@
         static void dropInstance();
 
         // tracking information
-        SessionID_t mSessionID;         // grouping similar records
         nsecs_t mTimestamp;             // ns, system_time_monotonic
 
-        // will this record accept further updates
-        bool mFinalized;
-
         Key mKey;
 
         struct Prop {
diff --git a/services/mediaanalytics/MediaAnalyticsService.cpp b/services/mediaanalytics/MediaAnalyticsService.cpp
index f0cb9c1..1ed8b74 100644
--- a/services/mediaanalytics/MediaAnalyticsService.cpp
+++ b/services/mediaanalytics/MediaAnalyticsService.cpp
@@ -66,19 +66,8 @@
     mItems.clear();
 }
 
-MediaAnalyticsItem::SessionID_t MediaAnalyticsService::generateUniqueSessionID() {
-    return ++mLastSessionID;
-}
-
-// TODO: consider removing forcenew.
-MediaAnalyticsItem::SessionID_t MediaAnalyticsService::submit(
-        MediaAnalyticsItem *item, bool forcenew __unused)
+status_t MediaAnalyticsService::submitInternal(MediaAnalyticsItem *item, bool release)
 {
-    // fill in a sessionID if we do not yet have one
-    if (item->getSessionID() <= MediaAnalyticsItem::SessionIDNone) {
-        item->setSessionID(generateUniqueSessionID());
-    }
-
     // we control these, generally not trusting user input
     nsecs_t now = systemTime(SYSTEM_TIME_REALTIME);
     // round nsecs to seconds
@@ -136,8 +125,8 @@
 
     // validate the record; we discard if we don't like it
     if (isContentValid(item, isTrusted) == false) {
-        delete item;
-        return MediaAnalyticsItem::SessionIDInvalid;
+        if (release) delete item;
+        return PERMISSION_DENIED;
     }
 
     // XXX: if we have a sessionid in the new record, look to make
@@ -145,18 +134,17 @@
 
     if (item->count() == 0) {
         ALOGV("%s: dropping empty record...", __func__);
-        delete item;
-        return MediaAnalyticsItem::SessionIDInvalid;
+        if (release) delete item;
+        return BAD_VALUE;
     }
 
     // send to statsd
     extern bool dump2Statsd(MediaAnalyticsItem *item);  // extern hook
     (void)dump2Statsd(item);  // failure should be logged in function.
 
-    // keep our copy
-    const MediaAnalyticsItem::SessionID_t id = item->getSessionID();
+    if (!release) item = item->dup();
     saveItem(item);
-    return id;
+    return NO_ERROR;
 }
 
 status_t MediaAnalyticsService::dump(int fd, const Vector<String16>& args)
diff --git a/services/mediaanalytics/MediaAnalyticsService.h b/services/mediaanalytics/MediaAnalyticsService.h
index ed7b7b1..eb7d725 100644
--- a/services/mediaanalytics/MediaAnalyticsService.h
+++ b/services/mediaanalytics/MediaAnalyticsService.h
@@ -35,31 +35,28 @@
     ~MediaAnalyticsService() override;
 
     /**
-     * Submits the indicated record to the mediaanalytics service, where
-     * it will be merged (if appropriate) with incomplete records that
-     * share the same key and sessionid.
+     * Submits the indicated record to the mediaanalytics service.
      *
      * \param item the item to submit.
-     * \param forcenew marks any matching incomplete record as complete before
-     *                 inserting this new record.
-     *
-     * \return the sessionID associated with that item or
-     *         MediaAnalyticsItem::SessionIDInvalid on failure.
-     *
-     * BEWARE: When called directly on the service (not from the binder interface),
-     * the caller surrenders ownership of item, MediaAnalyticsService will delete
-     * even on error.  The binder interface does not take ownership.
-     * TODO: fix this inconsistency with the binder RPC interface.
+     * \return status failure, which is negative on binder transaction failure.
+     *         As the transaction is one-way, remote failures will not be reported.
      */
-    MediaAnalyticsItem::SessionID_t submit(MediaAnalyticsItem *item, bool forcenew) override;
+    status_t submit(MediaAnalyticsItem *item) override {
+        return submitInternal(item, false /* release */);
+    }
 
     status_t dump(int fd, const Vector<String16>& args) override;
 
     static constexpr const char * const kServiceName = "media.metrics";
 
+protected:
+
+    // Internal call where release is true if ownership of item is transferred
+    // to the service (that is, the service will eventually delete the item).
+    status_t submitInternal(MediaAnalyticsItem *item, bool release) override;
+
 private:
     void processExpirations();
-    MediaAnalyticsItem::SessionID_t generateUniqueSessionID();
     // input validation after arrival from client
     static bool isContentValid(const MediaAnalyticsItem *item, bool isTrusted);
     bool isRateLimited(MediaAnalyticsItem *) const;
@@ -86,8 +83,6 @@
     const size_t mMaxRecordsExpiredAtOnce;
     const int mDumpProtoDefault;
 
-    std::atomic<MediaAnalyticsItem::SessionID_t> mLastSessionID{};
-
     class UidInfo {
     public:
         void setPkgInfo(MediaAnalyticsItem *item, uid_t uid, bool setName, bool setVersion);
diff --git a/services/mediaanalytics/tests/mediametrics_tests.cpp b/services/mediaanalytics/tests/mediametrics_tests.cpp
index 794b2f0..ea7739b 100644
--- a/services/mediaanalytics/tests/mediametrics_tests.cpp
+++ b/services/mediaanalytics/tests/mediametrics_tests.cpp
@@ -30,26 +30,25 @@
   sp mediaMetrics = new MediaAnalyticsService();
   status_t status;
 
-  // NOTE: submission of items to MediaMetrics releases ownership, even on error.
-
   // random keys ignored when empty
-  status = mediaMetrics->submit(MediaAnalyticsItem::create("random_key"), false);
-  ASSERT_EQ(MediaAnalyticsItem::SessionIDInvalid, status);
+  std::unique_ptr<MediaAnalyticsItem> random_key(MediaAnalyticsItem::create("random_key"));
+  status = mediaMetrics->submit(random_key.get());
+  ASSERT_EQ(PERMISSION_DENIED, status);
 
   // random keys ignored with data
-  auto random_key = MediaAnalyticsItem::create("random_key");
   random_key->setInt32("foo", 10);
-  status = mediaMetrics->submit(random_key, false);
-  ASSERT_EQ(MediaAnalyticsItem::SessionIDInvalid, status);
+  status = mediaMetrics->submit(random_key.get());
+  ASSERT_EQ(PERMISSION_DENIED, status);
 
   // known keys ignored if empty
-  status = mediaMetrics->submit(MediaAnalyticsItem::create("audiotrack"), false);
-  ASSERT_EQ(MediaAnalyticsItem::SessionIDInvalid, status);
+  std::unique_ptr<MediaAnalyticsItem> audiotrack_key(MediaAnalyticsItem::create("audiotrack"));
+  status = mediaMetrics->submit(audiotrack_key.get());
+  ASSERT_EQ(BAD_VALUE, status);
 
-  auto audiotrack = MediaAnalyticsItem::create("audiotrack");
-  audiotrack->addInt32("foo", 10);
-  status = mediaMetrics->submit(audiotrack, false);
-  ASSERT_GT(status, MediaAnalyticsItem::SessionIDNone);
+  // known keys not ignored if not empty
+  audiotrack_key->addInt32("foo", 10);
+  status = mediaMetrics->submit(audiotrack_key.get());
+  ASSERT_EQ(NO_ERROR, status);
 
   mediaMetrics->dump(fileno(stdout), {} /* args */);
 }