Merge "MediaSession2: Remove initialization in library session impl constructor"
diff --git a/drm/libmediadrm/Android.bp b/drm/libmediadrm/Android.bp
index ea239c5..5ea4614 100644
--- a/drm/libmediadrm/Android.bp
+++ b/drm/libmediadrm/Android.bp
@@ -15,25 +15,20 @@
"IDrm.cpp",
"IDrmClient.cpp",
"IMediaDrmService.cpp",
- "PluginMetricsReporting.cpp",
"SharedLibrary.cpp",
"DrmHal.cpp",
- "DrmMetrics.cpp",
"CryptoHal.cpp",
- "protos/plugin_metrics.proto",
],
- proto: {
- type: "lite",
- },
-
shared_libs: [
"libbinder",
"libcutils",
"libdl",
"liblog",
+ "libmediadrmmetrics_lite",
"libmediametrics",
"libmediautils",
+ "libprotobuf-cpp-lite",
"libstagefright_foundation",
"libutils",
"android.hardware.drm@1.0",
@@ -48,3 +43,66 @@
"-Wall",
],
}
+
+// This is the version of the drm metrics configured for protobuf lite.
+cc_library_shared {
+ name: "libmediadrmmetrics_lite",
+ srcs: [
+ "DrmMetrics.cpp",
+ "PluginMetricsReporting.cpp",
+ "protos/metrics.proto",
+ ],
+
+ proto: {
+ export_proto_headers: true,
+ type: "lite",
+ },
+ shared_libs: [
+ "android.hardware.drm@1.0",
+ "android.hardware.drm@1.1",
+ "libbase",
+ "libbinder",
+ "liblog",
+ "libmediametrics",
+ "libprotobuf-cpp-lite",
+ "libutils",
+ ],
+ cflags: [
+ // Suppress unused parameter and no error options. These cause problems
+ // with the when using the map type in a proto definition.
+ "-Wno-unused-parameter",
+ "-Wno-error",
+ ],
+}
+
+// This is the version of the drm metrics library configured for full protobuf.
+cc_library_shared {
+ name: "libmediadrmmetrics_full",
+ srcs: [
+ "DrmMetrics.cpp",
+ "PluginMetricsReporting.cpp",
+ "protos/metrics.proto",
+ ],
+
+ proto: {
+ export_proto_headers: true,
+ type: "full",
+ },
+ shared_libs: [
+ "android.hardware.drm@1.0",
+ "android.hardware.drm@1.1",
+ "libbase",
+ "libbinder",
+ "liblog",
+ "libmediametrics",
+ "libprotobuf-cpp-full",
+ "libutils",
+ ],
+ cflags: [
+ // Suppress unused parameter and no error options. These cause problems
+ // when using the map type in a proto definition.
+ "-Wno-unused-parameter",
+ "-Wno-error",
+ ],
+}
+
diff --git a/drm/libmediadrm/DrmHal.cpp b/drm/libmediadrm/DrmHal.cpp
index 039e1e9..48f4479 100644
--- a/drm/libmediadrm/DrmHal.cpp
+++ b/drm/libmediadrm/DrmHal.cpp
@@ -16,6 +16,8 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "DrmHal"
+#include <iomanip>
+
#include <utils/Log.h>
#include <binder/IPCThreadState.h>
@@ -52,6 +54,7 @@
using ::android::hardware::Return;
using ::android::hardware::Void;
using ::android::hidl::manager::V1_0::IServiceManager;
+using ::android::os::PersistableBundle;
using ::android::sp;
namespace {
@@ -97,6 +100,15 @@
return hidl_string(string.string());
}
+std::string ToHexString(const std::string& str) {
+ std::ostringstream out;
+ out << std::hex << std::setfill('0');
+ for (size_t i = 0; i < str.size(); i++) {
+ out << std::setw(2) << (int)(str[i]);
+ }
+ return out.str();
+}
+
static DrmPlugin::SecurityLevel toSecurityLevel(SecurityLevel level) {
switch(level) {
case SecurityLevel::SW_SECURE_CRYPTO:
@@ -308,6 +320,7 @@
sp<IDrmPlugin> DrmHal::makeDrmPlugin(const sp<IDrmFactory>& factory,
const uint8_t uuid[16], const String8& appPackageName) {
+ mMetrics.SetAppPackageName(appPackageName);
sp<IDrmPlugin> plugin;
Return<void> hResult = factory->createPlugin(uuid, appPackageName.string(),
@@ -503,7 +516,8 @@
INIT_CHECK();
closeOpenSessions();
- reportMetrics();
+ reportPluginMetrics();
+ reportFrameworkMetrics();
setListener(NULL);
mInitCheck = NO_INIT;
@@ -594,6 +608,7 @@
DrmSessionManager::Instance()->addSession(getCallingPid(),
mDrmSessionClient, sessionId);
mOpenSessions.push(sessionId);
+ mMetrics.SetSessionStart(sessionId);
}
mMetrics.mOpenSessionCounter.Increment(err);
@@ -615,9 +630,10 @@
}
}
}
- reportMetrics();
status_t response = toStatusT(status);
+ mMetrics.SetSessionEnd(sessionId);
mMetrics.mCloseSessionCounter.Increment(response);
+ reportPluginMetrics();
return response;
}
mMetrics.mCloseSessionCounter.Increment(DEAD_OBJECT);
@@ -631,7 +647,7 @@
String8 &defaultUrl, DrmPlugin::KeyRequestType *keyRequestType) {
Mutex::Autolock autoLock(mLock);
INIT_CHECK();
- EventTimer<status_t> keyRequestTimer(&mMetrics.mGetKeyRequestTiming);
+ EventTimer<status_t> keyRequestTimer(&mMetrics.mGetKeyRequestTimeUs);
DrmSessionManager::Instance()->useSession(sessionId);
@@ -726,7 +742,7 @@
status_t DrmHal::provideKeyResponse(Vector<uint8_t> const &sessionId,
Vector<uint8_t> const &response, Vector<uint8_t> &keySetId) {
Mutex::Autolock autoLock(mLock);
- EventTimer<status_t> keyResponseTimer(&mMetrics.mProvideKeyResponseTiming);
+ EventTimer<status_t> keyResponseTimer(&mMetrics.mProvideKeyResponseTimeUs);
INIT_CHECK();
@@ -1095,7 +1111,7 @@
return toStatusT(status);
}
-status_t DrmHal::getMetrics(MediaAnalyticsItem* item) {
+status_t DrmHal::getMetrics(PersistableBundle* item) {
if (item == nullptr) {
return UNEXPECTED_NULL;
}
@@ -1274,8 +1290,41 @@
}
}
+void DrmHal::reportFrameworkMetrics() const
+{
+ MediaAnalyticsItem item("mediadrm");
+ item.generateSessionID();
+ item.setPkgName(mMetrics.GetAppPackageName().c_str());
+ String8 vendor;
+ String8 description;
+ status_t result = getPropertyStringInternal(String8("vendor"), vendor);
+ if (result != OK) {
+ ALOGE("Failed to get vendor from drm plugin. %d", result);
+ } else {
+ item.setCString("vendor", vendor.c_str());
+ }
+ result = getPropertyStringInternal(String8("description"), description);
+ if (result != OK) {
+ ALOGE("Failed to get description from drm plugin. %d", result);
+ } else {
+ item.setCString("description", description.c_str());
+ }
-void DrmHal::reportMetrics() const
+ std::string serializedMetrics;
+ result = mMetrics.GetSerializedMetrics(&serializedMetrics);
+ if (result != OK) {
+ ALOGE("Failed to serialize Framework metrics: %d", result);
+ }
+ serializedMetrics = ToHexString(serializedMetrics);
+ if (!serializedMetrics.empty()) {
+ item.setCString("serialized_metrics", serializedMetrics.c_str());
+ }
+ if (!item.selfrecord()) {
+ ALOGE("Failed to self record framework metrics.");
+ }
+}
+
+void DrmHal::reportPluginMetrics() const
{
Vector<uint8_t> metrics;
String8 vendor;
diff --git a/drm/libmediadrm/DrmMetrics.cpp b/drm/libmediadrm/DrmMetrics.cpp
index 258c4b0..03bd88a 100644
--- a/drm/libmediadrm/DrmMetrics.cpp
+++ b/drm/libmediadrm/DrmMetrics.cpp
@@ -13,140 +13,330 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#define LOG_TAG "DrmMetrics"
+#include <iomanip>
+#include <utility>
#include <android-base/macros.h>
#include <media/DrmMetrics.h>
+#include <media/stagefright/foundation/base64.h>
+#include <sys/time.h>
+#include <utils/Log.h>
+#include <utils/Timers.h>
+#include "protos/metrics.pb.h"
+
+using ::android::String16;
+using ::android::String8;
+using ::android::drm_metrics::DrmFrameworkMetrics;
using ::android::hardware::drm::V1_0::EventType;
using ::android::hardware::drm::V1_0::KeyStatusType;
+using ::android::os::PersistableBundle;
namespace {
-template<typename T>
-std::string GetAttributeName(T type);
+template <typename T> std::string GetAttributeName(T type);
-template<>
-std::string GetAttributeName<KeyStatusType>(KeyStatusType type) {
- static const char* type_names[] = {
- "USABLE", "EXPIRED", "OUTPUT_NOT_ALLOWED",
- "STATUS_PENDING", "INTERNAL_ERROR" };
- if (((size_t) type) > arraysize(type_names)) {
- return "UNKNOWN_TYPE";
- }
- return type_names[(size_t) type];
+template <> std::string GetAttributeName<KeyStatusType>(KeyStatusType type) {
+ static const char *type_names[] = {"USABLE", "EXPIRED",
+ "OUTPUT_NOT_ALLOWED", "STATUS_PENDING",
+ "INTERNAL_ERROR"};
+ if (((size_t)type) > arraysize(type_names)) {
+ return "UNKNOWN_TYPE";
+ }
+ return type_names[(size_t)type];
}
-template<>
-std::string GetAttributeName<EventType>(EventType type) {
- static const char* type_names[] = {
- "PROVISION_REQUIRED", "KEY_NEEDED", "KEY_EXPIRED",
- "VENDOR_DEFINED", "SESSION_RECLAIMED" };
- if (((size_t) type) > arraysize(type_names)) {
- return "UNKNOWN_TYPE";
- }
- return type_names[(size_t) type];
+template <> std::string GetAttributeName<EventType>(EventType type) {
+ static const char *type_names[] = {"PROVISION_REQUIRED", "KEY_NEEDED",
+ "KEY_EXPIRED", "VENDOR_DEFINED",
+ "SESSION_RECLAIMED"};
+ if (((size_t)type) > arraysize(type_names)) {
+ return "UNKNOWN_TYPE";
+ }
+ return type_names[(size_t)type];
}
-template<typename T>
-void ExportCounterMetric(const android::CounterMetric<T>& counter,
- android::MediaAnalyticsItem* item) {
- if (!item) {
- ALOGE("item was unexpectedly null.");
- return;
- }
- std::string success_count_name = counter.metric_name() + ".ok.count";
- std::string error_count_name = counter.metric_name() + ".error.count";
- counter.ExportValues(
- [&] (const android::status_t status, const int64_t value) {
- if (status == android::OK) {
- item->setInt64(success_count_name.c_str(), value);
- } else {
- int64_t total_errors(0);
- item->getInt64(error_count_name.c_str(), &total_errors);
- item->setInt64(error_count_name.c_str(), total_errors + value);
- // TODO: Add support for exporting the list of error values.
- // This probably needs to be added to MediaAnalyticsItem.
- }
- });
+template <typename T>
+void ExportCounterMetric(const android::CounterMetric<T> &counter,
+ PersistableBundle *metrics) {
+ if (!metrics) {
+ ALOGE("metrics was unexpectedly null.");
+ return;
+ }
+ std::string success_count_name = counter.metric_name() + ".ok.count";
+ std::string error_count_name = counter.metric_name() + ".error.count";
+ std::vector<int64_t> status_values;
+ counter.ExportValues(
+ [&](const android::status_t status, const int64_t value) {
+ if (status == android::OK) {
+ metrics->putLong(android::String16(success_count_name.c_str()),
+ value);
+ } else {
+ int64_t total_errors(0);
+ metrics->getLong(android::String16(error_count_name.c_str()),
+ &total_errors);
+ metrics->putLong(android::String16(error_count_name.c_str()),
+ total_errors + value);
+ status_values.push_back(status);
+ }
+ });
+ if (!status_values.empty()) {
+ std::string error_list_name = counter.metric_name() + ".error.list";
+ metrics->putLongVector(android::String16(error_list_name.c_str()),
+ status_values);
+ }
}
-template<typename T>
+template <typename T>
void ExportCounterMetricWithAttributeNames(
- const android::CounterMetric<T>& counter,
- android::MediaAnalyticsItem* item) {
- if (!item) {
- ALOGE("item was unexpectedly null.");
- return;
- }
- counter.ExportValues(
- [&] (const T& attribute, const int64_t value) {
- std::string name = counter.metric_name()
- + "." + GetAttributeName(attribute) + ".count";
- item->setInt64(name.c_str(), value);
- });
+ const android::CounterMetric<T> &counter, PersistableBundle *metrics) {
+ if (!metrics) {
+ ALOGE("metrics was unexpectedly null.");
+ return;
+ }
+ counter.ExportValues([&](const T &attribute, const int64_t value) {
+ std::string name = counter.metric_name() + "." +
+ GetAttributeName(attribute) + ".count";
+ metrics->putLong(android::String16(name.c_str()), value);
+ });
}
-template<typename T>
-void ExportEventMetric(const android::EventMetric<T>& event,
- android::MediaAnalyticsItem* item) {
- if (!item) {
- ALOGE("item was unexpectedly null.");
- return;
- }
- std::string success_count_name = event.metric_name() + ".ok.count";
- std::string error_count_name = event.metric_name() + ".error.count";
- std::string timing_name = event.metric_name() + ".ok.average_time_micros";
- event.ExportValues(
- [&] (const android::status_t& status,
- const android::EventStatistics& value) {
- if (status == android::OK) {
- item->setInt64(success_count_name.c_str(), value.count);
- item->setInt64(timing_name.c_str(), value.mean);
- } else {
- int64_t total_errors(0);
- item->getInt64(error_count_name.c_str(), &total_errors);
- item->setInt64(error_count_name.c_str(),
+template <typename T>
+void ExportEventMetric(const android::EventMetric<T> &event,
+ PersistableBundle *metrics) {
+ if (!metrics) {
+ ALOGE("metrics was unexpectedly null.");
+ return;
+ }
+ std::string success_count_name = event.metric_name() + ".ok.count";
+ std::string error_count_name = event.metric_name() + ".error.count";
+ std::string timing_name = event.metric_name() + ".ok.average_time_micros";
+ std::vector<int64_t> status_values;
+ event.ExportValues([&](const android::status_t &status,
+ const android::EventStatistics &value) {
+ if (status == android::OK) {
+ metrics->putLong(android::String16(success_count_name.c_str()),
+ value.count);
+ metrics->putLong(android::String16(timing_name.c_str()),
+ value.mean);
+ } else {
+ int64_t total_errors(0);
+ metrics->getLong(android::String16(error_count_name.c_str()),
+ &total_errors);
+ metrics->putLong(android::String16(error_count_name.c_str()),
total_errors + value.count);
- // TODO: Add support for exporting the list of error values.
- // This probably needs to be added to MediaAnalyticsItem.
- }
- });
+ status_values.push_back(status);
+ }
+ });
+ if (!status_values.empty()) {
+ std::string error_list_name = event.metric_name() + ".error.list";
+ metrics->putLongVector(android::String16(error_list_name.c_str()),
+ status_values);
+ }
}
-} // namespace anonymous
+void ExportSessionLifespans(
+ const std::map<std::string, std::pair<int64_t, int64_t>> &mSessionLifespans,
+ PersistableBundle *metrics) {
+ if (!metrics) {
+ ALOGE("metrics was unexpectedly null.");
+ return;
+ }
+
+ if (mSessionLifespans.empty()) {
+ return;
+ }
+
+ PersistableBundle startTimesBundle;
+ PersistableBundle endTimesBundle;
+ for (auto it = mSessionLifespans.begin(); it != mSessionLifespans.end();
+ it++) {
+ String16 key(it->first.c_str(), it->first.size());
+ startTimesBundle.putLong(key, it->second.first);
+ endTimesBundle.putLong(key, it->second.second);
+ }
+ metrics->putPersistableBundle(
+ android::String16("drm.mediadrm.session_start_times_ms"),
+ startTimesBundle);
+ metrics->putPersistableBundle(
+ android::String16("drm.mediadrm.session_end_times_ms"), endTimesBundle);
+}
+
+std::string ToHexString(const android::Vector<uint8_t> &sessionId) {
+ std::ostringstream out;
+ out << std::hex << std::setfill('0');
+ for (size_t i = 0; i < sessionId.size(); i++) {
+ out << std::setw(2) << (int)(sessionId[i]);
+ }
+ return out.str();
+}
+
+} // namespace
namespace android {
MediaDrmMetrics::MediaDrmMetrics()
: mOpenSessionCounter("drm.mediadrm.open_session", "status"),
mCloseSessionCounter("drm.mediadrm.close_session", "status"),
- mGetKeyRequestTiming("drm.mediadrm.get_key_request", "status"),
- mProvideKeyResponseTiming("drm.mediadrm.provide_key_response", "status"),
- mGetProvisionRequestCounter(
- "drm.mediadrm.get_provision_request", "status"),
+ mGetKeyRequestTimeUs("drm.mediadrm.get_key_request", "status"),
+ mProvideKeyResponseTimeUs("drm.mediadrm.provide_key_response", "status"),
+ mGetProvisionRequestCounter("drm.mediadrm.get_provision_request",
+ "status"),
mProvideProvisionResponseCounter(
"drm.mediadrm.provide_provision_response", "status"),
- mKeyStatusChangeCounter(
- "drm.mediadrm.key_status_change", "key_status_type"),
+ mKeyStatusChangeCounter("drm.mediadrm.key_status_change",
+ "key_status_type"),
mEventCounter("drm.mediadrm.event", "event_type"),
- mGetDeviceUniqueIdCounter(
- "drm.mediadrm.get_device_unique_id", "status") {
+ mGetDeviceUniqueIdCounter("drm.mediadrm.get_device_unique_id", "status") {
}
-void MediaDrmMetrics::Export(MediaAnalyticsItem* item) {
- if (!item) {
- ALOGE("item was unexpectedly null.");
- return;
- }
- ExportCounterMetric(mOpenSessionCounter, item);
- ExportCounterMetric(mCloseSessionCounter, item);
- ExportEventMetric(mGetKeyRequestTiming, item);
- ExportEventMetric(mProvideKeyResponseTiming, item);
- ExportCounterMetric(mGetProvisionRequestCounter, item);
- ExportCounterMetric(mProvideProvisionResponseCounter, item);
- ExportCounterMetricWithAttributeNames(mKeyStatusChangeCounter, item);
- ExportCounterMetricWithAttributeNames(mEventCounter, item);
- ExportCounterMetric(mGetDeviceUniqueIdCounter, item);
+void MediaDrmMetrics::SetSessionStart(
+ const android::Vector<uint8_t> &sessionId) {
+ std::string sessionIdHex = ToHexString(sessionId);
+ mSessionLifespans[sessionIdHex] =
+ std::make_pair(GetCurrentTimeMs(), (int64_t)0);
}
-} // namespace android
+void MediaDrmMetrics::SetSessionEnd(const android::Vector<uint8_t> &sessionId) {
+ std::string sessionIdHex = ToHexString(sessionId);
+ int64_t endTimeMs = GetCurrentTimeMs();
+ if (mSessionLifespans.find(sessionIdHex) != mSessionLifespans.end()) {
+ mSessionLifespans[sessionIdHex] =
+ std::make_pair(mSessionLifespans[sessionIdHex].first, endTimeMs);
+ } else {
+ mSessionLifespans[sessionIdHex] = std::make_pair((int64_t)0, endTimeMs);
+ }
+}
+
+void MediaDrmMetrics::Export(PersistableBundle *metrics) {
+ if (!metrics) {
+ ALOGE("metrics was unexpectedly null.");
+ return;
+ }
+ ExportCounterMetric(mOpenSessionCounter, metrics);
+ ExportCounterMetric(mCloseSessionCounter, metrics);
+ ExportEventMetric(mGetKeyRequestTimeUs, metrics);
+ ExportEventMetric(mProvideKeyResponseTimeUs, metrics);
+ ExportCounterMetric(mGetProvisionRequestCounter, metrics);
+ ExportCounterMetric(mProvideProvisionResponseCounter, metrics);
+ ExportCounterMetricWithAttributeNames(mKeyStatusChangeCounter, metrics);
+ ExportCounterMetricWithAttributeNames(mEventCounter, metrics);
+ ExportCounterMetric(mGetDeviceUniqueIdCounter, metrics);
+ ExportSessionLifespans(mSessionLifespans, metrics);
+}
+
+status_t MediaDrmMetrics::GetSerializedMetrics(std::string *serializedMetrics) {
+
+ if (!serializedMetrics) {
+ ALOGE("serializedMetrics was unexpectedly null.");
+ return UNEXPECTED_NULL;
+ }
+
+ DrmFrameworkMetrics metrics;
+
+ mOpenSessionCounter.ExportValues(
+ [&](const android::status_t status, const int64_t value) {
+ DrmFrameworkMetrics::Counter *counter =
+ metrics.add_open_session_counter();
+ counter->set_count(value);
+ counter->mutable_attributes()->set_error_code(status);
+ });
+
+ mCloseSessionCounter.ExportValues(
+ [&](const android::status_t status, const int64_t value) {
+ DrmFrameworkMetrics::Counter *counter =
+ metrics.add_close_session_counter();
+ counter->set_count(value);
+ counter->mutable_attributes()->set_error_code(status);
+ });
+
+ mGetProvisionRequestCounter.ExportValues(
+ [&](const android::status_t status, const int64_t value) {
+ DrmFrameworkMetrics::Counter *counter =
+ metrics.add_get_provisioning_request_counter();
+ counter->set_count(value);
+ counter->mutable_attributes()->set_error_code(status);
+ });
+
+ mProvideProvisionResponseCounter.ExportValues(
+ [&](const android::status_t status, const int64_t value) {
+ DrmFrameworkMetrics::Counter *counter =
+ metrics.add_provide_provisioning_response_counter();
+ counter->set_count(value);
+ counter->mutable_attributes()->set_error_code(status);
+ });
+
+ mKeyStatusChangeCounter.ExportValues(
+ [&](const KeyStatusType key_status_type, const int64_t value) {
+ DrmFrameworkMetrics::Counter *counter =
+ metrics.add_key_status_change_counter();
+ counter->set_count(value);
+ counter->mutable_attributes()->set_key_status_type(
+ (uint32_t)key_status_type);
+ });
+
+ mEventCounter.ExportValues(
+ [&](const EventType event_type, const int64_t value) {
+ DrmFrameworkMetrics::Counter *counter =
+ metrics.add_event_callback_counter();
+ counter->set_count(value);
+ counter->mutable_attributes()->set_event_type((uint32_t)event_type);
+ });
+
+ mGetDeviceUniqueIdCounter.ExportValues(
+ [&](const status_t status, const int64_t value) {
+ DrmFrameworkMetrics::Counter *counter =
+ metrics.add_get_device_unique_id_counter();
+ counter->set_count(value);
+ counter->mutable_attributes()->set_error_code(status);
+ });
+
+ mGetKeyRequestTimeUs.ExportValues(
+ [&](const status_t status, const EventStatistics &stats) {
+ DrmFrameworkMetrics::DistributionMetric *metric =
+ metrics.add_get_key_request_time_us();
+ metric->set_min(stats.min);
+ metric->set_max(stats.max);
+ metric->set_mean(stats.mean);
+ metric->set_operation_count(stats.count);
+ metric->set_variance(stats.sum_squared_deviation / stats.count);
+ metric->mutable_attributes()->set_error_code(status);
+ });
+
+ mProvideKeyResponseTimeUs.ExportValues(
+ [&](const status_t status, const EventStatistics &stats) {
+ DrmFrameworkMetrics::DistributionMetric *metric =
+ metrics.add_provide_key_response_time_us();
+ metric->set_min(stats.min);
+ metric->set_max(stats.max);
+ metric->set_mean(stats.mean);
+ metric->set_operation_count(stats.count);
+ metric->set_variance(stats.sum_squared_deviation / stats.count);
+ metric->mutable_attributes()->set_error_code(status);
+ });
+
+ for (const auto &sessionLifespan : mSessionLifespans) {
+ auto *map = metrics.mutable_session_lifetimes();
+
+ (*map)[sessionLifespan.first].set_start_time_ms(
+ sessionLifespan.second.first);
+ (*map)[sessionLifespan.first].set_end_time_ms(
+ sessionLifespan.second.second);
+ }
+
+ if (!metrics.SerializeToString(serializedMetrics)) {
+ ALOGE("Failed to serialize metrics.");
+ return UNKNOWN_ERROR;
+ }
+
+ return OK;
+}
+
+int64_t MediaDrmMetrics::GetCurrentTimeMs() {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return ((int64_t)tv.tv_sec * 1000) + ((int64_t)tv.tv_usec / 1000);
+}
+
+} // namespace android
diff --git a/drm/libmediadrm/IDrm.cpp b/drm/libmediadrm/IDrm.cpp
index 9f54dba..22e4e6c 100644
--- a/drm/libmediadrm/IDrm.cpp
+++ b/drm/libmediadrm/IDrm.cpp
@@ -492,7 +492,10 @@
return reply.readInt32();
}
- virtual status_t getMetrics(MediaAnalyticsItem *item) {
+ virtual status_t getMetrics(os::PersistableBundle *metrics) {
+ if (metrics == NULL) {
+ return BAD_VALUE;
+ }
Parcel data, reply;
data.writeInterfaceToken(IDrm::getInterfaceDescriptor());
@@ -500,9 +503,23 @@
if (status != OK) {
return status;
}
+ // The reply data is ordered as
+ // 1) 32 bit integer reply followed by
+ // 2) Serialized PersistableBundle containing metrics.
+ status_t reply_status;
+ if (reply.readInt32(&reply_status) != OK
+ || reply_status != OK) {
+ ALOGE("Failed to read getMetrics response code from parcel. %d",
+ reply_status);
+ return reply_status;
+ }
- item->readFromParcel(reply);
- return reply.readInt32();
+ status = metrics->readFromParcel(&reply);
+ if (status != OK) {
+ ALOGE("Failed to read metrics from parcel. %d", status);
+ return status;
+ }
+ return reply_status;
}
virtual status_t setCipherAlgorithm(Vector<uint8_t> const &sessionId,
@@ -1008,11 +1025,18 @@
{
CHECK_INTERFACE(IDrm, data, reply);
- MediaAnalyticsItem item;
- status_t result = getMetrics(&item);
- item.writeToParcel(reply);
- reply->writeInt32(result);
- return OK;
+ os::PersistableBundle metrics;
+ status_t result = getMetrics(&metrics);
+ // The reply data is ordered as
+ // 1) 32 bit integer reply followed by
+ // 2) Serialized PersistableBundle containing metrics.
+ // Only write the metrics if the getMetrics result was
+ // OK and we successfully added the status to reply.
+ status_t parcel_result = reply->writeInt32(result);
+ if (result == OK && parcel_result == OK) {
+ parcel_result = metrics.writeToParcel(reply);
+ }
+ return parcel_result;
}
case SET_CIPHER_ALGORITHM:
diff --git a/drm/libmediadrm/PluginMetricsReporting.cpp b/drm/libmediadrm/PluginMetricsReporting.cpp
index 26c8427..6c97f2b 100644
--- a/drm/libmediadrm/PluginMetricsReporting.cpp
+++ b/drm/libmediadrm/PluginMetricsReporting.cpp
@@ -23,7 +23,7 @@
#include <media/MediaAnalyticsItem.h>
-#include "protos/plugin_metrics.pb.h"
+#include "protos/metrics.pb.h"
namespace android {
diff --git a/drm/libmediadrm/protos/metrics.proto b/drm/libmediadrm/protos/metrics.proto
new file mode 100644
index 0000000..aa26f5f
--- /dev/null
+++ b/drm/libmediadrm/protos/metrics.proto
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+syntax = "proto2";
+
+package android.drm_metrics;
+
+// The MetricsGroup is a collection of metric name/value pair instances
+// that can be serialized and provided to a caller.
+message MetricsGroup {
+ message Metric {
+ message MetricValue {
+ // Exactly one of the following values must be set.
+ optional int64 int_value = 1;
+ optional double double_value = 2;
+ optional string string_value = 3;
+ }
+
+ // The name of the metric. Must be valid UTF-8. Required.
+ optional string name = 1;
+
+ // The value of the metric. Required.
+ optional MetricValue value = 2;
+ }
+
+ // The list of name/value pairs of metrics.
+ repeated Metric metric = 1;
+
+ // Allow multiple sub groups of metrics.
+ repeated MetricsGroup metric_sub_group = 2;
+
+ // Name of the application package associated with the metrics.
+ optional string app_package_name = 3;
+}
+
+// This message contains the specific metrics captured by DrmMetrics. It is
+// used for serializing and logging metrics.
+// next id: 11.
+message DrmFrameworkMetrics {
+ // TODO: Consider using extensions.
+
+ // Attributes are associated with a recorded value. E.g. A counter may
+ // represent a count of an operation returning a specific error code. The
+ // error code will be an attribute.
+ message Attributes {
+ // Reserved for compatibility with logging proto.
+ reserved 2 to 13;
+
+ // A general purpose error code where 0 means OK.
+ optional int32 error_code = 1;
+
+ // Defined at ::android::hardware::drm::V1_0::KeyStatusType;
+ optional uint32 key_status_type = 14;
+
+ // Defined at ::android::hardware::drm::V1_0::EventType;
+ optional uint32 event_type = 15;
+ }
+
+ // The Counter message is used to store a count value with an associated
+ // Attribute.
+ message Counter {
+ optional int64 count = 1;
+ // Represents the attributes associated with this counter instance.
+ optional Attributes attributes = 2;
+ }
+
+ // The DistributionMetric is meant to capture the moments of a normally
+ // distributed (or approximately normal) value.
+ message DistributionMetric {
+ optional double min = 1;
+ optional double max = 2;
+ optional double mean = 3;
+ optional double variance = 4;
+ optional double operation_count = 5;
+
+ // Represents the attributes assocated with this distribution metric
+ // instance.
+ optional Attributes attributes = 6;
+ }
+
+ message SessionLifetime {
+ // Start time of the session in milliseconds since epoch.
+ optional int64 start_time_ms = 1;
+ // End time of the session in milliseconds since epoch.
+ optional int64 end_time_ms = 2;
+ }
+
+ // The count of open session operations. Each instance has a specific error
+ // code associated with it.
+ repeated Counter open_session_counter = 1;
+
+ // The count of close session operations. Each instance has a specific error
+ // code associated with it.
+ repeated Counter close_session_counter = 2;
+
+ // Count and execution time of getKeyRequest calls.
+ repeated DistributionMetric get_key_request_time_us = 3;
+
+ // Count and execution time of provideKeyResponse calls.
+ repeated DistributionMetric provide_key_response_time_us = 4;
+
+ // Count of getProvisionRequest calls.
+ repeated Counter get_provisioning_request_counter = 5;
+
+ // Count of provideProvisionResponse calls.
+ repeated Counter provide_provisioning_response_counter = 6;
+
+ // Count of key status events broken out by status type.
+ repeated Counter key_status_change_counter = 7;
+
+ // Count of events broken out by event type
+ repeated Counter event_callback_counter = 8;
+
+ // Count getPropertyByteArray calls to retrieve the device unique id.
+ repeated Counter get_device_unique_id_counter = 9;
+
+ // Session ids to lifetime (start and end time) map.
+ // Session ids are strings of hex-encoded byte arrays.
+ map<string, SessionLifetime> session_lifetimes = 10;
+}
+
diff --git a/drm/libmediadrm/protos/plugin_metrics.proto b/drm/libmediadrm/protos/plugin_metrics.proto
deleted file mode 100644
index 7e3bcf5..0000000
--- a/drm/libmediadrm/protos/plugin_metrics.proto
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-
-syntax = "proto2";
-
-package android.drm_metrics;
-
-// need this if we are using libprotobuf-cpp-2.3.0-lite
-option optimize_for = LITE_RUNTIME;
-
-// The MetricsGroup is a collection of metric name/value pair instances
-// that can be serialized and provided to a caller.
-message MetricsGroup {
- message Metric {
- message MetricValue {
- // Exactly one of the following values must be set.
- optional int64 int_value = 1;
- optional double double_value = 2;
- optional string string_value = 3;
- }
-
- // The name of the metric. Must be valid UTF-8. Required.
- optional string name = 1;
-
- // The value of the metric. Required.
- optional MetricValue value = 2;
- }
-
- // The list of name/value pairs of metrics.
- repeated Metric metric = 1;
-
- // Allow multiple sub groups of metrics.
- repeated MetricsGroup metric_sub_group = 2;
-
- // Name of the application package associated with the metrics.
- optional string app_package_name = 3;
-}
diff --git a/drm/libmediadrm/tests/Android.bp b/drm/libmediadrm/tests/Android.bp
index fdc982d..670d3b9 100644
--- a/drm/libmediadrm/tests/Android.bp
+++ b/drm/libmediadrm/tests/Android.bp
@@ -16,16 +16,23 @@
srcs: ["DrmMetrics_test.cpp"],
shared_libs: [
"android.hardware.drm@1.0",
+ "libbinder",
"liblog",
- "libmediadrm",
+ "libmediadrmmetrics_full",
"libmediametrics",
+ "libprotobuf-cpp-full",
"libutils",
],
- include_dirs: ["frameworks/av/include/media"],
- cflags: [
- "-Werror",
- "-Wall",
+ static_libs: ["libgmock"],
+ include_dirs: [
+ "frameworks/av/include/media",
],
+ cflags: [
+ // Suppress unused parameter and no error options. These cause problems
+ // when using the map type in a proto definition.
+ "-Wno-unused-parameter",
+ "-Wno-error",
+ ]
}
cc_test {
diff --git a/drm/libmediadrm/tests/DrmMetrics_test.cpp b/drm/libmediadrm/tests/DrmMetrics_test.cpp
index d1948b4..fe762c9 100644
--- a/drm/libmediadrm/tests/DrmMetrics_test.cpp
+++ b/drm/libmediadrm/tests/DrmMetrics_test.cpp
@@ -14,27 +14,48 @@
* limitations under the License.
*/
-#include <gtest/gtest.h>
-
+#define LOG_TAG "DrmMetricsTest"
#include "DrmMetrics.h"
+#include <binder/PersistableBundle.h>
+#include <google/protobuf/text_format.h>
+#include <google/protobuf/util/message_differencer.h>
+#include <gtest/gtest.h>
+#include <utils/Log.h>
+
+#include "protos/metrics.pb.h"
+
+using ::android::drm_metrics::DrmFrameworkMetrics;
using ::android::hardware::drm::V1_0::EventType;
using ::android::hardware::drm::V1_0::KeyStatusType;
+using ::android::os::PersistableBundle;
+using ::google::protobuf::util::MessageDifferencer;
+using ::google::protobuf::TextFormat;
namespace android {
/**
* Unit tests for the MediaDrmMetrics class.
*/
-class MediaDrmMetricsTest : public ::testing::Test {
+class MediaDrmMetricsTest : public ::testing::Test {};
+
+/**
+ * This derived class mocks the clock for testing purposes.
+ */
+class FakeMediaDrmMetrics : public MediaDrmMetrics {
+ public:
+ FakeMediaDrmMetrics() : MediaDrmMetrics(), time_(0) {};
+
+ int64_t GetCurrentTimeMs() { return time_++; }
+ int64_t time_;
};
TEST_F(MediaDrmMetricsTest, EmptySuccess) {
MediaDrmMetrics metrics;
- MediaAnalyticsItem item;
+ PersistableBundle bundle;
- metrics.Export(&item);
- EXPECT_EQ(0, item.count());
+ metrics.Export(&bundle);
+ EXPECT_TRUE(bundle.empty());
}
TEST_F(MediaDrmMetricsTest, AllValuesSuccessCounts) {
@@ -44,9 +65,9 @@
metrics.mCloseSessionCounter.Increment(OK);
{
- EventTimer<status_t> get_key_request_timer(&metrics.mGetKeyRequestTiming);
+ EventTimer<status_t> get_key_request_timer(&metrics.mGetKeyRequestTimeUs);
EventTimer<status_t> provide_key_response_timer(
- &metrics.mProvideKeyResponseTiming);
+ &metrics.mProvideKeyResponseTimeUs);
get_key_request_timer.SetAttribute(OK);
provide_key_response_timer.SetAttribute(OK);
}
@@ -58,10 +79,10 @@
metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::USABLE);
metrics.mEventCounter.Increment(EventType::PROVISION_REQUIRED);
- MediaAnalyticsItem item;
+ PersistableBundle bundle;
- metrics.Export(&item);
- EXPECT_EQ(11, item.count());
+ metrics.Export(&bundle);
+ EXPECT_EQ(11U, bundle.size());
// Verify the list of pairs of int64 metrics.
std::vector<std::pair<std::string, int64_t>> expected_values = {
@@ -75,24 +96,24 @@
{ "drm.mediadrm.event.PROVISION_REQUIRED.count", 1 },
{ "drm.mediadrm.get_device_unique_id.ok.count", 1 }};
for (const auto& expected_pair : expected_values) {
+ String16 key(expected_pair.first.c_str());
int64_t value = -1;
- EXPECT_TRUE(item.getInt64(expected_pair.first.c_str(), &value))
- << "Failed to get " << expected_pair.first;
+ EXPECT_TRUE(bundle.getLong(key, &value))
+ << "Unexpected error retrieviing key: " << key;
EXPECT_EQ(expected_pair.second, value)
- << "Unexpected value for " << expected_pair.first;
+ << "Unexpected value for " << expected_pair.first << ". " << value;
}
// Validate timing values exist.
+ String16 get_key_request_key(
+ "drm.mediadrm.get_key_request.ok.average_time_micros");
+ String16 provide_key_response_key(
+ "drm.mediadrm.provide_key_response.ok.average_time_micros");
int64_t value = -1;
- EXPECT_TRUE(
- item.getInt64("drm.mediadrm.get_key_request.ok.average_time_micros",
- &value));
+ EXPECT_TRUE(bundle.getLong(get_key_request_key, &value));
EXPECT_GE(value, 0);
-
value = -1;
- EXPECT_TRUE(
- item.getInt64("drm.mediadrm.provide_key_response.ok.average_time_micros",
- &value));
+ EXPECT_TRUE(bundle.getLong(provide_key_response_key, &value));
EXPECT_GE(value, 0);
}
@@ -107,9 +128,9 @@
for (status_t s : {OK, UNEXPECTED_NULL}) {
{
- EventTimer<status_t> get_key_request_timer(&metrics.mGetKeyRequestTiming);
+ EventTimer<status_t> get_key_request_timer(&metrics.mGetKeyRequestTimeUs);
EventTimer<status_t> provide_key_response_timer(
- &metrics.mProvideKeyResponseTiming);
+ &metrics.mProvideKeyResponseTimeUs);
get_key_request_timer.SetAttribute(s);
provide_key_response_timer.SetAttribute(s);
}
@@ -133,10 +154,23 @@
metrics.mEventCounter.Increment(EventType::VENDOR_DEFINED);
metrics.mEventCounter.Increment(EventType::SESSION_RECLAIMED);
- MediaAnalyticsItem item;
+ android::Vector<uint8_t> sessionId1;
+ sessionId1.push_back(1);
+ sessionId1.push_back(2);
+ android::Vector<uint8_t> sessionId2;
+ sessionId2.push_back(3);
+ sessionId2.push_back(4);
+ String16 hexSessionId1("0102");
+ String16 hexSessionId2("0304");
- metrics.Export(&item);
- EXPECT_EQ(26, item.count());
+ metrics.SetSessionStart(sessionId1);
+ metrics.SetSessionStart(sessionId2);
+ metrics.SetSessionEnd(sessionId2);
+ metrics.SetSessionEnd(sessionId1);
+
+ PersistableBundle bundle;
+ metrics.Export(&bundle);
+ EXPECT_EQ(35U, bundle.size());
// Verify the list of pairs of int64 metrics.
std::vector<std::pair<std::string, int64_t>> expected_values = {
@@ -165,25 +199,217 @@
{ "drm.mediadrm.event.VENDOR_DEFINED.count", 1 },
{ "drm.mediadrm.event.SESSION_RECLAIMED.count", 1 }};
for (const auto& expected_pair : expected_values) {
+ String16 key(expected_pair.first.c_str());
int64_t value = -1;
- EXPECT_TRUE(item.getInt64(expected_pair.first.c_str(), &value))
- << "Failed to get " << expected_pair.first;
+ EXPECT_TRUE(bundle.getLong(key, &value))
+ << "Unexpected error retrieviing key: " << key;
EXPECT_EQ(expected_pair.second, value)
- << "Unexpected value for " << expected_pair.first;
+ << "Unexpected value for " << expected_pair.first << ". " << value;
+ }
+
+ // Verify the error lists
+ std::vector<std::pair<std::string, std::vector<int64_t>>> expected_vector_values = {
+ { "drm.mediadrm.close_session.error.list", { UNEXPECTED_NULL } },
+ { "drm.mediadrm.get_device_unique_id.error.list", { UNEXPECTED_NULL } },
+ { "drm.mediadrm.get_key_request.error.list", { UNEXPECTED_NULL } },
+ { "drm.mediadrm.get_provision_request.error.list", { UNEXPECTED_NULL } },
+ { "drm.mediadrm.open_session.error.list", { UNEXPECTED_NULL } },
+ { "drm.mediadrm.provide_key_response.error.list", { UNEXPECTED_NULL } },
+ { "drm.mediadrm.provide_provision_response.error.list", { UNEXPECTED_NULL } }};
+ for (const auto& expected_pair : expected_vector_values) {
+ String16 key(expected_pair.first.c_str());
+ std::vector<int64_t> values;
+ EXPECT_TRUE(bundle.getLongVector(key, &values))
+ << "Unexpected error retrieviing key: " << key;
+ for (auto expected : expected_pair.second) {
+ EXPECT_TRUE(std::find(values.begin(), values.end(), expected) != values.end())
+ << "Could not find " << expected << " for key " << expected_pair.first;
+ }
+ }
+
+ // Verify the lifespans
+ PersistableBundle start_times;
+ PersistableBundle end_times;
+ String16 start_time_key("drm.mediadrm.session_start_times_ms");
+ String16 end_time_key("drm.mediadrm.session_end_times_ms");
+ ASSERT_TRUE(bundle.getPersistableBundle(start_time_key, &start_times));
+ ASSERT_TRUE(bundle.getPersistableBundle(end_time_key, &end_times));
+ EXPECT_EQ(2U, start_times.size());
+ EXPECT_EQ(2U, end_times.size());
+ int64_t start_time, end_time;
+ for (const auto& sid : { hexSessionId1, hexSessionId2 }) {
+ start_time = -1;
+ end_time = -1;
+ EXPECT_TRUE(start_times.getLong(sid, &start_time));
+ EXPECT_TRUE(end_times.getLong(sid, &end_time));
+ EXPECT_GT(start_time, 0);
+ EXPECT_GE(end_time, start_time);
}
// Validate timing values exist.
+ String16 get_key_request_key(
+ "drm.mediadrm.get_key_request.ok.average_time_micros");
+ String16 provide_key_response_key(
+ "drm.mediadrm.provide_key_response.ok.average_time_micros");
int64_t value = -1;
- EXPECT_TRUE(
- item.getInt64("drm.mediadrm.get_key_request.ok.average_time_micros",
- &value));
+ EXPECT_TRUE(bundle.getLong(get_key_request_key, &value));
EXPECT_GE(value, 0);
-
value = -1;
- EXPECT_TRUE(
- item.getInt64("drm.mediadrm.provide_key_response.ok.average_time_micros",
- &value));
+ EXPECT_TRUE(bundle.getLong(provide_key_response_key, &value));
EXPECT_GE(value, 0);
}
+
+TEST_F(MediaDrmMetricsTest, CounterValuesProtoSerialization) {
+ MediaDrmMetrics metrics;
+
+ metrics.mOpenSessionCounter.Increment(OK);
+ metrics.mOpenSessionCounter.Increment(UNEXPECTED_NULL);
+ metrics.mCloseSessionCounter.Increment(OK);
+ metrics.mCloseSessionCounter.Increment(UNEXPECTED_NULL);
+
+ metrics.mGetProvisionRequestCounter.Increment(OK);
+ metrics.mGetProvisionRequestCounter.Increment(UNEXPECTED_NULL);
+ metrics.mProvideProvisionResponseCounter.Increment(OK);
+ metrics.mProvideProvisionResponseCounter.Increment(UNEXPECTED_NULL);
+ metrics.mGetDeviceUniqueIdCounter.Increment(OK);
+ metrics.mGetDeviceUniqueIdCounter.Increment(UNEXPECTED_NULL);
+
+ metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::USABLE);
+ metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::EXPIRED);
+ metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::OUTPUTNOTALLOWED);
+ metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::STATUSPENDING);
+ metrics.mKeyStatusChangeCounter.Increment(KeyStatusType::INTERNALERROR);
+ metrics.mEventCounter.Increment(EventType::PROVISION_REQUIRED);
+ metrics.mEventCounter.Increment(EventType::KEY_NEEDED);
+ metrics.mEventCounter.Increment(EventType::KEY_EXPIRED);
+ metrics.mEventCounter.Increment(EventType::VENDOR_DEFINED);
+ metrics.mEventCounter.Increment(EventType::SESSION_RECLAIMED);
+
+ std::string serializedMetrics;
+ ASSERT_EQ(OK, metrics.GetSerializedMetrics(&serializedMetrics));
+
+ DrmFrameworkMetrics metricsProto;
+ ASSERT_TRUE(metricsProto.ParseFromString(serializedMetrics));
+
+ std::string expectedMetrics =
+ "open_session_counter { count: 1 attributes { error_code: -0x7FFFFFF8 } } "
+ "open_session_counter { count: 1 attributes { error_code: 0 } } "
+ "close_session_counter { count: 1 attributes { error_code: -0x7FFFFFF8 } } "
+ "close_session_counter { count: 1 attributes { error_code: 0 } } "
+ "get_provisioning_request_counter { count: 1 attributes { error_code: -0x7FFFFFF8 } } "
+ "get_provisioning_request_counter { count: 1 attributes { error_code: 0 } } "
+ "provide_provisioning_response_counter { count: 1 attributes { error_code: -0x7ffffff8 } } "
+ "provide_provisioning_response_counter { count: 1 attributes { error_code: 0 } } "
+ "get_device_unique_id_counter { count: 1 attributes { error_code: -0x7ffffff8 } } "
+ "get_device_unique_id_counter { count: 1 attributes { error_code: 0 } } "
+ "key_status_change_counter { count: 1 attributes { key_status_type: 0 } } "
+ "key_status_change_counter { count: 1 attributes { key_status_type: 1 } } "
+ "key_status_change_counter { count: 1 attributes { key_status_type: 2 } } "
+ "key_status_change_counter { count: 1 attributes { key_status_type: 3 } } "
+ "key_status_change_counter { count: 1 attributes { key_status_type: 4 } } "
+ "event_callback_counter { count: 1 attributes { event_type: 0 } } "
+ "event_callback_counter { count: 1 attributes { event_type: 1 } } "
+ "event_callback_counter { count: 1 attributes { event_type: 2 } } "
+ "event_callback_counter { count: 1 attributes { event_type: 3 } } "
+ "event_callback_counter { count: 1 attributes { event_type: 4 } } ";
+
+ DrmFrameworkMetrics expectedMetricsProto;
+ ASSERT_TRUE(TextFormat::MergeFromString(expectedMetrics, &expectedMetricsProto));
+
+ std::string diffString;
+ MessageDifferencer differ;
+ differ.ReportDifferencesToString(&diffString);
+ ASSERT_TRUE(differ.Compare(expectedMetricsProto, metricsProto))
+ << diffString;
+}
+
+TEST_F(MediaDrmMetricsTest, TimeMetricsProtoSerialization) {
+ MediaDrmMetrics metrics;
+
+ for (status_t s : {OK, UNEXPECTED_NULL}) {
+ double time = 0;
+ for (int i = 0; i < 5; i++) {
+ time += 1.0;
+ metrics.mGetKeyRequestTimeUs.Record(time, s);
+ metrics.mProvideKeyResponseTimeUs.Record(time, s);
+ }
+ }
+
+ std::string serializedMetrics;
+ ASSERT_EQ(OK, metrics.GetSerializedMetrics(&serializedMetrics));
+
+ DrmFrameworkMetrics metricsProto;
+ ASSERT_TRUE(metricsProto.ParseFromString(serializedMetrics));
+
+ std::string expectedMetrics =
+ "get_key_request_timing { "
+ " min: 1 max: 5 mean: 3.5 variance: 1 operation_count: 5 "
+ " attributes { error_code: -0x7FFFFFF8 } "
+ "} "
+ "get_key_request_timing { "
+ " min: 1 max: 5 mean: 3.5 variance: 1 operation_count: 5 "
+ " attributes { error_code: 0 } "
+ "} "
+ "provide_key_response_timing { "
+ " min: 1 max: 5 mean: 3.5 variance: 1 operation_count: 5 "
+ " attributes { error_code: -0x7FFFFFF8 } "
+ "} "
+ "provide_key_response_timing { "
+ " min: 1 max: 5 mean: 3.5 variance: 1 operation_count: 5 "
+ " attributes { error_code: 0 } "
+ "} ";
+
+ DrmFrameworkMetrics expectedMetricsProto;
+ ASSERT_TRUE(TextFormat::MergeFromString(expectedMetrics, &expectedMetricsProto));
+
+ std::string diffString;
+ MessageDifferencer differ;
+ differ.ReportDifferencesToString(&diffString);
+ ASSERT_TRUE(differ.Compare(expectedMetricsProto, metricsProto))
+ << diffString;
+}
+
+TEST_F(MediaDrmMetricsTest, SessionLifetimeProtoSerialization) {
+ // Use the fake so the clock is predictable;
+ FakeMediaDrmMetrics metrics;
+
+ android::Vector<uint8_t> sessionId1;
+ sessionId1.push_back(1);
+ sessionId1.push_back(2);
+ android::Vector<uint8_t> sessionId2;
+ sessionId2.push_back(3);
+ sessionId2.push_back(4);
+
+ metrics.SetSessionStart(sessionId1);
+ metrics.SetSessionStart(sessionId2);
+ metrics.SetSessionEnd(sessionId2);
+ metrics.SetSessionEnd(sessionId1);
+
+ std::string serializedMetrics;
+ ASSERT_EQ(OK, metrics.GetSerializedMetrics(&serializedMetrics));
+
+ DrmFrameworkMetrics metricsProto;
+ ASSERT_TRUE(metricsProto.ParseFromString(serializedMetrics));
+
+ std::string expectedMetrics =
+ "session_lifetimes: { "
+ " key: '0102' "
+ " value { start_time_ms: 0 end_time_ms: 3 } "
+ "} "
+ "session_lifetimes: { "
+ " key: '0304' "
+ " value { start_time_ms: 1 end_time_ms: 2 } "
+ "} ";
+
+ DrmFrameworkMetrics expectedMetricsProto;
+ ASSERT_TRUE(TextFormat::MergeFromString(expectedMetrics, &expectedMetricsProto));
+
+ std::string diffString;
+ MessageDifferencer differ;
+ differ.ReportDifferencesToString(&diffString);
+ ASSERT_TRUE(differ.Compare(expectedMetricsProto, metricsProto))
+ << diffString;
+}
+
} // namespace android
diff --git a/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
index d921d9e..6b0201a 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
+++ b/drm/mediadrm/plugins/clearkey/hidl/DrmPlugin.cpp
@@ -110,6 +110,38 @@
return Status::ERROR_DRM_SESSION_NOT_OPENED;
}
+Status DrmPlugin::getKeyRequestCommon(const hidl_vec<uint8_t>& scope,
+ const hidl_vec<uint8_t>& initData,
+ const hidl_string& mimeType,
+ KeyType keyType,
+ const hidl_vec<KeyValue>& optionalParameters,
+ std::vector<uint8_t> *request,
+ KeyRequestType *keyRequestType,
+ std::string *defaultUrl) {
+ UNUSED(optionalParameters);
+
+ *defaultUrl = "";
+ *keyRequestType = KeyRequestType::UNKNOWN;
+ *request = std::vector<uint8_t>();
+
+ if (scope.size() == 0) {
+ return Status::BAD_VALUE;
+ }
+
+ if (keyType != KeyType::STREAMING) {
+ return Status::ERROR_DRM_CANNOT_HANDLE;
+ }
+
+ sp<Session> session = mSessionLibrary->findSession(toVector(scope));
+ if (!session.get()) {
+ return Status::ERROR_DRM_SESSION_NOT_OPENED;
+ }
+
+ Status status = session->getKeyRequest(initData, mimeType, request);
+ *keyRequestType = KeyRequestType::INITIAL;
+ return status;
+}
+
Return<void> DrmPlugin::getKeyRequest(
const hidl_vec<uint8_t>& scope,
const hidl_vec<uint8_t>& initData,
@@ -119,29 +151,16 @@
getKeyRequest_cb _hidl_cb) {
UNUSED(optionalParameters);
- if (scope.size() == 0) {
- // Returns empty keyRequest, unknown keyType and empty defaultUrl
- _hidl_cb(Status::BAD_VALUE, hidl_vec<uint8_t>(),
- KeyRequestType::UNKNOWN, "");
- return Void();
- }
-
- if (keyType != KeyType::STREAMING) {
- _hidl_cb(Status::ERROR_DRM_CANNOT_HANDLE, hidl_vec<uint8_t>(),
- KeyRequestType::UNKNOWN, "");
- return Void();
- }
-
- sp<Session> session = mSessionLibrary->findSession(toVector(scope));
- if (!session.get()) {
- _hidl_cb(Status::ERROR_DRM_SESSION_NOT_OPENED, hidl_vec<uint8_t>(),
- KeyRequestType::UNKNOWN, "");
- return Void();
- }
-
+ KeyRequestType keyRequestType = KeyRequestType::UNKNOWN;
+ std::string defaultUrl("");
std::vector<uint8_t> request;
- Status status = session->getKeyRequest(initData, mimeType, &request);
- _hidl_cb(status, toHidlVec(request), KeyRequestType::INITIAL, "");
+ Status status = getKeyRequestCommon(
+ scope, initData, mimeType, keyType, optionalParameters,
+ &request, &keyRequestType, &defaultUrl);
+
+ _hidl_cb(status, toHidlVec(request),
+ static_cast<drm::V1_0::KeyRequestType>(keyRequestType),
+ hidl_string(defaultUrl));
return Void();
}
@@ -152,23 +171,16 @@
KeyType keyType,
const hidl_vec<KeyValue>& optionalParameters,
getKeyRequest_1_1_cb _hidl_cb) {
- hidl_string defaultUrl;
- hidl_vec<uint8_t> request;
- ::android::hardware::drm::V1_1::KeyRequestType requestType =
- static_cast<::android::hardware::drm::V1_1::KeyRequestType>(KeyRequestType::UNKNOWN);
- Status status = Status::OK;
+ UNUSED(optionalParameters);
- defaultUrl.clear();
- getKeyRequest(scope, initData, mimeType, keyType, optionalParameters,
- [&](Status statusCode, const hidl_vec<uint8_t>& hResult,
- KeyRequestType hKeyRequestType,
- const hidl_string& hDefaultUrl) {
- defaultUrl = hDefaultUrl;
- request = hResult;
- requestType = static_cast<::android::hardware::drm::V1_1::KeyRequestType>(hKeyRequestType);
- status = statusCode;
- });
- _hidl_cb(status, request, requestType, defaultUrl);
+ KeyRequestType keyRequestType = KeyRequestType::UNKNOWN;
+ std::string defaultUrl("");
+ std::vector<uint8_t> request;
+ Status status = getKeyRequestCommon(
+ scope, initData, mimeType, keyType, optionalParameters,
+ &request, &keyRequestType, &defaultUrl);
+
+ _hidl_cb(status, toHidlVec(request), keyRequestType, hidl_string(defaultUrl));
return Void();
}
diff --git a/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h b/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h
index 5d12598..19baf0b 100644
--- a/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h
+++ b/drm/mediadrm/plugins/clearkey/hidl/include/DrmPlugin.h
@@ -31,7 +31,6 @@
using ::android::hardware::drm::V1_0::EventType;
using ::android::hardware::drm::V1_0::IDrmPluginListener;
-using ::android::hardware::drm::V1_0::KeyRequestType;
using ::android::hardware::drm::V1_0::KeyStatus;
using ::android::hardware::drm::V1_0::KeyType;
using ::android::hardware::drm::V1_0::KeyValue;
@@ -39,6 +38,8 @@
using ::android::hardware::drm::V1_0::SecureStopId;
using ::android::hardware::drm::V1_0::Status;
using ::android::hardware::drm::V1_1::DrmMetricGroup;
+using ::android::hardware::drm::V1_1::IDrmPlugin;
+using ::android::hardware::drm::V1_1::KeyRequestType;
using ::android::hardware::hidl_string;
using ::android::hardware::hidl_vec;
@@ -46,7 +47,6 @@
using ::android::hardware::Void;
using ::android::sp;
-
struct DrmPlugin : public IDrmPlugin {
explicit DrmPlugin(SessionLibrary* sessionLibrary);
@@ -335,6 +335,15 @@
Return<Status> setSecurityLevel(const hidl_vec<uint8_t>& sessionId,
SecurityLevel level);
+ Status getKeyRequestCommon(const hidl_vec<uint8_t>& scope,
+ const hidl_vec<uint8_t>& initData,
+ const hidl_string& mimeType,
+ KeyType keyType,
+ const hidl_vec<KeyValue>& optionalParameters,
+ std::vector<uint8_t> *request,
+ KeyRequestType *getKeyRequestType,
+ std::string *defaultUrl);
+
std::vector<KeyValue> mPlayPolicy;
std::map<std::string, std::string> mStringProperties;
std::map<std::string, std::vector<uint8_t> > mByteArrayProperties;
diff --git a/media/libmedia/MidiIoWrapper.cpp b/media/libmedia/MidiIoWrapper.cpp
index 0896e75..5ca3b48 100644
--- a/media/libmedia/MidiIoWrapper.cpp
+++ b/media/libmedia/MidiIoWrapper.cpp
@@ -38,6 +38,7 @@
mFd = open(path, O_RDONLY | O_LARGEFILE);
mBase = 0;
mLength = lseek(mFd, 0, SEEK_END);
+ mDataSource = nullptr;
}
MidiIoWrapper::MidiIoWrapper(int fd, off64_t offset, int64_t size) {
@@ -45,6 +46,7 @@
mFd = fd < 0 ? -1 : dup(fd);
mBase = offset;
mLength = size;
+ mDataSource = nullptr;
}
MidiIoWrapper::MidiIoWrapper(DataSourceBase *source) {
diff --git a/media/libmedia/include/media/DrmHal.h b/media/libmedia/include/media/DrmHal.h
index 1c09036..bf91ea9 100644
--- a/media/libmedia/include/media/DrmHal.h
+++ b/media/libmedia/include/media/DrmHal.h
@@ -119,7 +119,7 @@
virtual status_t setPropertyString(String8 const &name, String8 const &value ) const;
virtual status_t setPropertyByteArray(String8 const &name,
Vector<uint8_t> const &value ) const;
- virtual status_t getMetrics(MediaAnalyticsItem *item);
+ virtual status_t getMetrics(os::PersistableBundle *metrics);
virtual status_t setCipherAlgorithm(Vector<uint8_t> const &sessionId,
String8 const &algorithm);
@@ -203,7 +203,8 @@
void writeByteArray(Parcel &obj, const hidl_vec<uint8_t>& array);
- void reportMetrics() const;
+ void reportPluginMetrics() const;
+ void reportFrameworkMetrics() const;
status_t getPropertyStringInternal(String8 const &name, String8 &value) const;
status_t getPropertyByteArrayInternal(String8 const &name,
Vector<uint8_t> &value) const;
diff --git a/media/libmedia/include/media/DrmMetrics.h b/media/libmedia/include/media/DrmMetrics.h
index bb7509b..5c2fdf2 100644
--- a/media/libmedia/include/media/DrmMetrics.h
+++ b/media/libmedia/include/media/DrmMetrics.h
@@ -20,6 +20,7 @@
#include <map>
#include <android/hardware/drm/1.0/types.h>
+#include <binder/PersistableBundle.h>
#include <media/CounterMetric.h>
#include <media/EventMetric.h>
@@ -28,19 +29,20 @@
/**
* This class contains the definition of metrics captured within MediaDrm.
* It also contains a method for exporting all of the metrics to a
- * MediaAnalyticsItem instance.
+ * PersistableBundle.
*/
class MediaDrmMetrics {
public:
explicit MediaDrmMetrics();
+ virtual ~MediaDrmMetrics() {};
// Count of openSession calls.
CounterMetric<status_t> mOpenSessionCounter;
// Count of closeSession calls.
CounterMetric<status_t> mCloseSessionCounter;
// Count and timing of getKeyRequest calls.
- EventMetric<status_t> mGetKeyRequestTiming;
+ EventMetric<status_t> mGetKeyRequestTimeUs;
// Count and timing of provideKeyResponse calls.
- EventMetric<status_t> mProvideKeyResponseTiming;
+ EventMetric<status_t> mProvideKeyResponseTimeUs;
// Count of getProvisionRequest calls.
CounterMetric<status_t> mGetProvisionRequestCounter;
// Count of provideProvisionResponse calls.
@@ -55,10 +57,37 @@
// Count getPropertyByteArray calls to retrieve the device unique id.
CounterMetric<status_t> mGetDeviceUniqueIdCounter;
- // TODO: Add session start and end time support. These are a special case.
+ // Adds a session start time record.
+ void SetSessionStart(const Vector<uint8_t>& sessionId);
- // Export the metrics to a MediaAnalyticsItem.
- void Export(MediaAnalyticsItem* item);
+ // Adds a session end time record.
+ void SetSessionEnd(const Vector<uint8_t>& sessionId);
+
+ // The app package name is the application package name that is using the
+ // instance. The app package name is held here for convenience. It is not
+ // serialized or exported with the metrics.
+ void SetAppPackageName(const String8& appPackageName) { mAppPackageName = appPackageName; }
+ const String8& GetAppPackageName() { return mAppPackageName; }
+
+ // Export the metrics to a PersistableBundle.
+ void Export(os::PersistableBundle* metricsBundle);
+
+ // Get the serialized metrics. Metrics are formatted as a serialized
+ // DrmFrameworkMetrics proto. If there is a failure serializing the metrics,
+ // this returns an error. The parameter |serlializedMetrics| is owned by the
+ // caller and must not be null.
+ status_t GetSerializedMetrics(std::string* serializedMetrics);
+
+ protected:
+ // This is visible for testing only.
+ virtual int64_t GetCurrentTimeMs();
+
+ private:
+ // Session lifetimes. A pair of values representing the milliseconds since
+ // epoch, UTC. The first value is the start time, the second is the end time.
+ std::map<std::string, std::pair<int64_t, int64_t>> mSessionLifespans;
+
+ String8 mAppPackageName;
};
} // namespace android
diff --git a/media/libmedia/include/media/IDrm.h b/media/libmedia/include/media/IDrm.h
index c3ae684..a19b06b 100644
--- a/media/libmedia/include/media/IDrm.h
+++ b/media/libmedia/include/media/IDrm.h
@@ -15,6 +15,7 @@
*/
#include <binder/IInterface.h>
+#include <binder/PersistableBundle.h>
#include <media/stagefright/foundation/ABase.h>
#include <media/drm/DrmAPI.h>
#include <media/IDrmClient.h>
@@ -98,7 +99,7 @@
virtual status_t setPropertyByteArray(String8 const &name,
Vector<uint8_t> const &value) const = 0;
- virtual status_t getMetrics(MediaAnalyticsItem *item) = 0;
+ virtual status_t getMetrics(os::PersistableBundle *metrics) = 0;
virtual status_t setCipherAlgorithm(Vector<uint8_t> const &sessionId,
String8 const &algorithm) = 0;
diff --git a/media/libmediaextractor/MetaData.cpp b/media/libmediaextractor/MetaData.cpp
index 98cddbe..69beea1 100644
--- a/media/libmediaextractor/MetaData.cpp
+++ b/media/libmediaextractor/MetaData.cpp
@@ -146,7 +146,7 @@
/**
* Note that the returned pointer becomes invalid when additional metadata is set.
*/
-bool MetaData::findCString(uint32_t key, const char **value) {
+bool MetaData::findCString(uint32_t key, const char **value) const {
uint32_t type;
const void *data;
size_t size;
@@ -159,7 +159,7 @@
return true;
}
-bool MetaData::findInt32(uint32_t key, int32_t *value) {
+bool MetaData::findInt32(uint32_t key, int32_t *value) const {
uint32_t type = 0;
const void *data;
size_t size;
@@ -174,7 +174,7 @@
return true;
}
-bool MetaData::findInt64(uint32_t key, int64_t *value) {
+bool MetaData::findInt64(uint32_t key, int64_t *value) const {
uint32_t type = 0;
const void *data;
size_t size;
@@ -189,7 +189,7 @@
return true;
}
-bool MetaData::findFloat(uint32_t key, float *value) {
+bool MetaData::findFloat(uint32_t key, float *value) const {
uint32_t type = 0;
const void *data;
size_t size;
@@ -204,7 +204,7 @@
return true;
}
-bool MetaData::findPointer(uint32_t key, void **value) {
+bool MetaData::findPointer(uint32_t key, void **value) const {
uint32_t type = 0;
const void *data;
size_t size;
@@ -222,7 +222,7 @@
bool MetaData::findRect(
uint32_t key,
int32_t *left, int32_t *top,
- int32_t *right, int32_t *bottom) {
+ int32_t *right, int32_t *bottom) const {
uint32_t type = 0;
const void *data;
size_t size;
diff --git a/media/libmediaextractor/include/media/stagefright/MetaData.h b/media/libmediaextractor/include/media/stagefright/MetaData.h
index e4a84b7..c2a2d1e 100644
--- a/media/libmediaextractor/include/media/stagefright/MetaData.h
+++ b/media/libmediaextractor/include/media/stagefright/MetaData.h
@@ -258,16 +258,16 @@
int32_t left, int32_t top,
int32_t right, int32_t bottom);
- bool findCString(uint32_t key, const char **value);
- bool findInt32(uint32_t key, int32_t *value);
- bool findInt64(uint32_t key, int64_t *value);
- bool findFloat(uint32_t key, float *value);
- bool findPointer(uint32_t key, void **value);
+ bool findCString(uint32_t key, const char **value) const;
+ bool findInt32(uint32_t key, int32_t *value) const;
+ bool findInt64(uint32_t key, int64_t *value) const;
+ bool findFloat(uint32_t key, float *value) const;
+ bool findPointer(uint32_t key, void **value) const;
bool findRect(
uint32_t key,
int32_t *left, int32_t *top,
- int32_t *right, int32_t *bottom);
+ int32_t *right, int32_t *bottom) const;
bool setData(uint32_t key, uint32_t type, const void *data, size_t size);
diff --git a/media/libmediaplayer2/JAudioTrack.cpp b/media/libmediaplayer2/JAudioTrack.cpp
index 6d9605a..ac0cc57 100644
--- a/media/libmediaplayer2/JAudioTrack.cpp
+++ b/media/libmediaplayer2/JAudioTrack.cpp
@@ -32,6 +32,8 @@
uint32_t sampleRate, // AudioFormat && bufferSizeInBytes
audio_format_t format, // AudioFormat && bufferSizeInBytes
audio_channel_mask_t channelMask, // AudioFormat && bufferSizeInBytes
+ callback_t cbf, // Offload
+ void* user, // Offload
size_t frameCount, // bufferSizeInBytes
audio_session_t sessionId, // AudioTrack
const audio_attributes_t* pAttributes, // AudioAttributes
@@ -90,8 +92,27 @@
jBuilderObj = env->CallObjectMethod(jBuilderObj, jSetSessionId, sessionId);
}
+ if (cbf != NULL) {
+ jmethodID jSetOffloadedPlayback = env->GetMethodID(jBuilderCls, "setOffloadedPlayback",
+ "(Z)Landroid/media/AudioTrack$Builder;");
+ jBuilderObj = env->CallObjectMethod(jBuilderObj, jSetOffloadedPlayback, true);
+ mFlags = AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD;
+ }
+
jmethodID jBuild = env->GetMethodID(jBuilderCls, "build", "()Landroid/media/AudioTrack;");
mAudioTrackObj = env->CallObjectMethod(jBuilderObj, jBuild);
+
+ if (cbf != NULL) {
+ // Set offload mode callback
+ jobject jStreamEventCallbackObj = createStreamEventCallback(cbf, user);
+ jobject jExecutorObj = createCallbackExecutor();
+ jmethodID jSetStreamEventCallback = env->GetMethodID(
+ jAudioTrackCls,
+ "setStreamEventCallback",
+ "(Ljava/util/concurrent/Executor;Landroid/media/AudioTrack$StreamEventCallback;)V");
+ env->CallVoidMethod(
+ mAudioTrackObj, jSetStreamEventCallback, jExecutorObj, jStreamEventCallbackObj);
+ }
}
JAudioTrack::~JAudioTrack() {
@@ -160,6 +181,11 @@
return true;
}
+status_t JAudioTrack::getTimestamp(ExtendedTimestamp *timestamp __unused) {
+ // TODO: Implement this after appropriate Java AudioTrack method is available.
+ return NO_ERROR;
+}
+
status_t JAudioTrack::setPlaybackRate(const AudioPlaybackRate &playbackRate) {
// TODO: existing native AudioTrack returns INVALID_OPERATION on offload/direct/fast tracks.
// Should we do the same thing?
@@ -442,6 +468,80 @@
return routedDeviceId;
}
+audio_session_t JAudioTrack::getAudioSessionId() {
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ jmethodID jGetAudioSessionId = env->GetMethodID(mAudioTrackCls, "getAudioSessionId", "()I");
+ jint sessionId = env->CallIntMethod(mAudioTrackObj, jGetAudioSessionId);
+ return (audio_session_t) sessionId;
+}
+
+status_t JAudioTrack::setOutputDevice(audio_port_handle_t deviceId) {
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ jclass jMP2ImplCls = env->FindClass("android/media/MediaPlayer2Impl");
+ jmethodID jSetAudioOutputDeviceById = env->GetMethodID(
+ jMP2ImplCls, "setAudioOutputDeviceById", "(Landroid/media/AudioTrack;I)Z");
+ jboolean result = env->CallStaticBooleanMethod(
+ jMP2ImplCls, jSetAudioOutputDeviceById, mAudioTrackObj, deviceId);
+ return result == true ? NO_ERROR : BAD_VALUE;
+}
+
+status_t JAudioTrack::pendingDuration(int32_t *msec) {
+ if (msec == nullptr) {
+ return BAD_VALUE;
+ }
+
+ bool isPurePcmData = audio_is_linear_pcm(format()) && (getFlags() & AUDIO_FLAG_HW_AV_SYNC) == 0;
+ if (!isPurePcmData) {
+ return INVALID_OPERATION;
+ }
+
+ // TODO: Need to know the difference btw. client and server time.
+ // If getTimestamp(ExtendedTimestamp) is ready, and un-comment below and modify appropriately.
+ // (copied from AudioTrack.cpp)
+
+// ExtendedTimestamp ets;
+// ExtendedTimestamp::LOCATION location = ExtendedTimestamp::LOCATION_SERVER;
+// if (getTimestamp_l(&ets) == OK && ets.mTimeNs[location] > 0) {
+// int64_t diff = ets.mPosition[ExtendedTimestamp::LOCATION_CLIENT]
+// - ets.mPosition[location];
+// if (diff < 0) {
+// *msec = 0;
+// } else {
+// // ms is the playback time by frames
+// int64_t ms = (int64_t)((double)diff * 1000 /
+// ((double)mSampleRate * mPlaybackRate.mSpeed));
+// // clockdiff is the timestamp age (negative)
+// int64_t clockdiff = (mState != STATE_ACTIVE) ? 0 :
+// ets.mTimeNs[location]
+// + ets.mTimebaseOffset[ExtendedTimestamp::TIMEBASE_MONOTONIC]
+// - systemTime(SYSTEM_TIME_MONOTONIC);
+//
+// //ALOGV("ms: %lld clockdiff: %lld", (long long)ms, (long long)clockdiff);
+// static const int NANOS_PER_MILLIS = 1000000;
+// *msec = (int32_t)(ms + clockdiff / NANOS_PER_MILLIS);
+// }
+// return NO_ERROR;
+// }
+
+ return NO_ERROR;
+}
+
+status_t JAudioTrack::addAudioDeviceCallback(
+ const sp<AudioSystem::AudioDeviceCallback>& callback __unused) {
+ // TODO: Implement this after appropriate Java AudioTrack method is available.
+ return NO_ERROR;
+}
+
+status_t JAudioTrack::removeAudioDeviceCallback(
+ const sp<AudioSystem::AudioDeviceCallback>& callback __unused) {
+ // TODO: Implement this after appropriate Java AudioTrack method is available.
+ return NO_ERROR;
+}
+
+/////////////////////////////////////////////////////////////
+/// Private method begins ///
+/////////////////////////////////////////////////////////////
+
jobject JAudioTrack::createVolumeShaperConfigurationObj(
const sp<media::VolumeShaper::Configuration>& config) {
@@ -546,6 +646,24 @@
return env->CallObjectMethod(jBuilderObj, jBuild);
}
+jobject JAudioTrack::createStreamEventCallback(callback_t cbf, void* user) {
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ jclass jCallbackCls = env->FindClass("android/media/MediaPlayer2Impl$StreamEventCallback");
+ jmethodID jCallbackCtor = env->GetMethodID(jCallbackCls, "<init>", "(JJJ)V");
+ jobject jCallbackObj = env->NewObject(jCallbackCls, jCallbackCtor, this, cbf, user);
+ return jCallbackObj;
+}
+
+jobject JAudioTrack::createCallbackExecutor() {
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ jclass jExecutorsCls = env->FindClass("java/util/concurrent/Executors");
+ jmethodID jNewSingleThreadExecutor = env->GetStaticMethodID(jExecutorsCls,
+ "newSingleThreadExecutor", "()Ljava/util/concurrent/ExecutorService;");
+ jobject jSingleThreadExecutorObj =
+ env->CallStaticObjectMethod(jExecutorsCls, jNewSingleThreadExecutor);
+ return jSingleThreadExecutorObj;
+}
+
status_t JAudioTrack::javaToNativeStatus(int javaStatus) {
switch (javaStatus) {
case AUDIO_JAVA_SUCCESS:
diff --git a/media/libmediaplayer2/include/mediaplayer2/JAudioTrack.h b/media/libmediaplayer2/include/mediaplayer2/JAudioTrack.h
index 10fa5e8..301825b 100644
--- a/media/libmediaplayer2/include/mediaplayer2/JAudioTrack.h
+++ b/media/libmediaplayer2/include/mediaplayer2/JAudioTrack.h
@@ -19,6 +19,7 @@
#include <jni.h>
#include <media/AudioResamplerPublic.h>
+#include <media/AudioSystem.h>
#include <media/VolumeShaper.h>
#include <system/audio.h>
#include <utils/Errors.h>
@@ -31,6 +32,42 @@
class JAudioTrack {
public:
+ /* Events used by AudioTrack callback function (callback_t).
+ * Keep in sync with frameworks/base/media/java/android/media/AudioTrack.java NATIVE_EVENT_*.
+ */
+ enum event_type {
+ EVENT_MORE_DATA = 0, // Request to write more data to buffer.
+ EVENT_NEW_IAUDIOTRACK = 6, // IAudioTrack was re-created, either due to re-routing and
+ // voluntary invalidation by mediaserver, or mediaserver crash.
+ EVENT_STREAM_END = 7, // Sent after all the buffers queued in AF and HW are played
+ // back (after stop is called) for an offloaded track.
+ };
+
+ class Buffer
+ {
+ public:
+ size_t mSize; // input/output in bytes.
+ void* mData; // pointer to the audio data.
+ };
+
+ /* As a convenience, if a callback is supplied, a handler thread
+ * is automatically created with the appropriate priority. This thread
+ * invokes the callback when a new buffer becomes available or various conditions occur.
+ *
+ * Parameters:
+ *
+ * event: type of event notified (see enum AudioTrack::event_type).
+ * user: Pointer to context for use by the callback receiver.
+ * info: Pointer to optional parameter according to event type:
+ * - EVENT_MORE_DATA: pointer to JAudioTrack::Buffer struct. The callback must not
+ * write more bytes than indicated by 'size' field and update 'size' if fewer bytes
+ * are written.
+ * - EVENT_NEW_IAUDIOTRACK: unused.
+ * - EVENT_STREAM_END: unused.
+ */
+
+ typedef void (*callback_t)(int event, void* user, void *info);
+
/* Creates an JAudioTrack object for non-offload mode.
* Once created, the track needs to be started before it can be used.
* Unspecified values are set to appropriate default values.
@@ -49,6 +86,9 @@
* output sink.
* (TODO: How can we check whether a format is supported?)
* channelMask: Channel mask, such that audio_is_output_channel(channelMask) is true.
+ * cbf: Callback function. If not null, this function is called periodically
+ * to provide new data and inform of marker, position updates, etc.
+ * user: Context for use by the callback receiver.
* frameCount: Minimum size of track PCM buffer in frames. This defines the
* application's contribution to the latency of the track.
* The actual size selected by the JAudioTrack could be larger if the
@@ -68,35 +108,20 @@
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
+ callback_t cbf,
+ void* user,
size_t frameCount = 0,
audio_session_t sessionId = AUDIO_SESSION_ALLOCATE,
const audio_attributes_t* pAttributes = NULL,
float maxRequiredSpeed = 1.0f);
/*
- Temporarily removed constructor arguments:
-
- // Q. Values are in audio-base.h, but where can we find explanation for them?
- audio_output_flags_t flags,
-
// Q. May be used in AudioTrack.setPreferredDevice(AudioDeviceInfo)?
audio_port_handle_t selectedDeviceId,
- // Should be deleted, since we don't use Binder anymore.
- bool doNotReconnect,
-
- // Do we need UID and PID?
- uid_t uid,
- pid_t pid,
-
- // TODO: Uses these values when Java AudioTrack supports the offload mode.
- callback_t cbf,
- void* user,
+ // TODO: No place to use these values.
int32_t notificationFrames,
const audio_offload_info_t *offloadInfo,
-
- // Fixed to false, but what is this?
- threadCanCallJava
*/
virtual ~JAudioTrack();
@@ -138,6 +163,46 @@
*/
bool getTimestamp(AudioTimestamp& timestamp);
+ // TODO: This doc is just copied from AudioTrack.h. Revise it after implemenation.
+ /* Return the extended timestamp, with additional timebase info and improved drain behavior.
+ *
+ * This is similar to the AudioTrack.java API:
+ * getTimestamp(@NonNull AudioTimestamp timestamp, @AudioTimestamp.Timebase int timebase)
+ *
+ * Some differences between this method and the getTimestamp(AudioTimestamp& timestamp) method
+ *
+ * 1. stop() by itself does not reset the frame position.
+ * A following start() resets the frame position to 0.
+ * 2. flush() by itself does not reset the frame position.
+ * The frame position advances by the number of frames flushed,
+ * when the first frame after flush reaches the audio sink.
+ * 3. BOOTTIME clock offsets are provided to help synchronize with
+ * non-audio streams, e.g. sensor data.
+ * 4. Position is returned with 64 bits of resolution.
+ *
+ * Parameters:
+ * timestamp: A pointer to the caller allocated ExtendedTimestamp.
+ *
+ * Returns NO_ERROR on success; timestamp is filled with valid data.
+ * BAD_VALUE if timestamp is NULL.
+ * WOULD_BLOCK if called immediately after start() when the number
+ * of frames consumed is less than the
+ * overall hardware latency to physical output. In WOULD_BLOCK cases,
+ * one might poll again, or use getPosition(), or use 0 position and
+ * current time for the timestamp.
+ * If WOULD_BLOCK is returned, the timestamp is still
+ * modified with the LOCATION_CLIENT portion filled.
+ * DEAD_OBJECT if AudioFlinger dies or the output device changes and
+ * the track cannot be automatically restored.
+ * The application needs to recreate the AudioTrack
+ * because the audio device changed or AudioFlinger died.
+ * This typically occurs for direct or offloaded tracks
+ * or if mDoNotReconnect is true.
+ * INVALID_OPERATION if called on a offloaded or direct track.
+ * Use getTimestamp(AudioTimestamp& timestamp) instead.
+ */
+ status_t getTimestamp(ExtendedTimestamp *timestamp);
+
/* Set source playback rate for timestretch
* 1.0 is normal speed: < 1.0 is slower, > 1.0 is faster
* 1.0 is normal pitch: < 1.0 is lower pitch, > 1.0 is higher pitch
@@ -270,7 +335,65 @@
*/
audio_port_handle_t getRoutedDeviceId();
+ /* Returns the ID of the audio session this AudioTrack belongs to. */
+ audio_session_t getAudioSessionId();
+
+ /* Selects the audio device to use for output of this AudioTrack. A value of
+ * AUDIO_PORT_HANDLE_NONE indicates default routing.
+ *
+ * Parameters:
+ * The device ID of the selected device (as returned by the AudioDevicesManager API).
+ *
+ * Returned value:
+ * - NO_ERROR: successful operation
+ * - BAD_VALUE: failed to find the valid output device with given device Id.
+ */
+ status_t setOutputDevice(audio_port_handle_t deviceId);
+
+ // TODO: Add AUDIO_OUTPUT_FLAG_DIRECT when it is possible to check.
+ // TODO: Add AUDIO_FLAG_HW_AV_SYNC when it is possible to check.
+ /* Returns the flags */
+ audio_output_flags_t getFlags() const { return mFlags; }
+
+ /* Obtain the pending duration in milliseconds for playback of pure PCM data remaining in
+ * AudioTrack.
+ *
+ * Returns NO_ERROR if successful.
+ * INVALID_OPERATION if the AudioTrack does not contain pure PCM data.
+ * BAD_VALUE if msec is nullptr.
+ */
+ status_t pendingDuration(int32_t *msec);
+
+ /* Adds an AudioDeviceCallback. The caller will be notified when the audio device to which this
+ * AudioTrack is routed is updated.
+ * Replaces any previously installed callback.
+ *
+ * Parameters:
+ *
+ * callback: The callback interface
+ *
+ * Returns NO_ERROR if successful.
+ * INVALID_OPERATION if the same callback is already installed.
+ * NO_INIT or PREMISSION_DENIED if AudioFlinger service is not reachable
+ * BAD_VALUE if the callback is NULL
+ */
+ status_t addAudioDeviceCallback(const sp<AudioSystem::AudioDeviceCallback>& callback);
+
+ /* Removes an AudioDeviceCallback.
+ *
+ * Parameters:
+ *
+ * callback: The callback interface
+ *
+ * Returns NO_ERROR if successful.
+ * INVALID_OPERATION if the callback is not installed
+ * BAD_VALUE if the callback is NULL
+ */
+ status_t removeAudioDeviceCallback(const sp<AudioSystem::AudioDeviceCallback>& callback);
+
private:
+ audio_output_flags_t mFlags;
+
jclass mAudioTrackCls;
jobject mAudioTrackObj;
@@ -282,6 +405,12 @@
jobject createVolumeShaperOperationObj(
const sp<media::VolumeShaper::Operation>& operation);
+ /* Creates a Java StreamEventCallback object */
+ jobject createStreamEventCallback(callback_t cbf, void* user);
+
+ /* Creates a Java Executor object for running a callback */
+ jobject createCallbackExecutor();
+
status_t javaToNativeStatus(int javaStatus);
};
diff --git a/media/libstagefright/codec2/vndk/C2Buffer.cpp b/media/libstagefright/codec2/vndk/C2Buffer.cpp
index 0de8cde..511ffe0 100644
--- a/media/libstagefright/codec2/vndk/C2Buffer.cpp
+++ b/media/libstagefright/codec2/vndk/C2Buffer.cpp
@@ -271,6 +271,7 @@
new ReadViewBuddy::Impl(*mImpl, (uint8_t *)base, offset(), len),
[base, len](ReadViewBuddy::Impl *i) {
(void)i->getAllocation()->unmap(base, len, nullptr);
+ delete i;
});
return AcquirableReadViewBuddy(error, C2Fence(), ReadViewBuddy(rvi, 0, len));
} else {
@@ -300,6 +301,7 @@
new WriteViewBuddy::Impl(*mImpl, (uint8_t *)base, 0, len),
[base, len](WriteViewBuddy::Impl *i) {
(void)i->getAllocation()->unmap(base, len, nullptr);
+ delete i;
});
return AcquirableWriteViewBuddy(error, C2Fence(), WriteViewBuddy(rvi));
} else {
@@ -791,4 +793,14 @@
return mImpl->removeInfo(index);
}
+// static
+std::shared_ptr<C2Buffer> C2Buffer::CreateLinearBuffer(const C2ConstLinearBlock &block) {
+ return std::shared_ptr<C2Buffer>(new C2Buffer({ block }));
+}
+
+// static
+std::shared_ptr<C2Buffer> C2Buffer::CreateGraphicBuffer(const C2ConstGraphicBlock &block) {
+ return std::shared_ptr<C2Buffer>(new C2Buffer({ block }));
+}
+
} // namespace android
diff --git a/packages/MediaComponents/Android.mk b/packages/MediaComponents/Android.mk
index 7c7718e..22eea25 100644
--- a/packages/MediaComponents/Android.mk
+++ b/packages/MediaComponents/Android.mk
@@ -16,6 +16,12 @@
LOCAL_PATH := $(call my-dir)
+ifneq ($(TARGET_BUILD_PDK),true)
+# Build MediaComponents only if this is not a PDK build. MediaComponents won't
+# build in PDK builds because frameworks/base/core/java is not available but
+# IMediaSession2.aidl and IMediaSession2Callback.aidl are using classes from
+# frameworks/base/core/java.
+
include $(CLEAR_VARS)
LOCAL_PACKAGE_NAME := MediaComponents
@@ -60,4 +66,6 @@
include $(BUILD_PACKAGE)
+endif # ifneq ($(TARGET_BUILD_PDK),true)
+
include $(call all-makefiles-under,$(LOCAL_PATH))