blob: c064de299b192e365119e64b58321297b90bd2f0 [file] [log] [blame]
Dongwon Kangbf98d542018-09-11 14:40:23 -07001/*
2 * Copyright 2017, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Ray Essickc535a552019-01-18 11:38:15 -080017#define LOG_TAG "MediaMetricsJNI"
18
Andy Hung73348482019-10-23 16:54:53 -070019#include <binder/Parcel.h>
Dongwon Kangbf98d542018-09-11 14:40:23 -070020#include <jni.h>
Andy Hunge7f750d2019-12-19 12:39:08 -080021#include <media/IMediaMetricsService.h>
Ray Essick81fbc5b2019-12-07 06:24:59 -080022#include <media/MediaMetricsItem.h>
Dongwon Kangbf98d542018-09-11 14:40:23 -070023#include <nativehelper/JNIHelp.h>
Andy Hung70549312019-12-16 12:05:24 -080024#include <variant>
Dongwon Kangbf98d542018-09-11 14:40:23 -070025
26#include "android_media_MediaMetricsJNI.h"
Robert Shih4354a962019-11-10 12:09:08 -080027#include "android_os_Parcel.h"
Andy Hungf2310c72019-10-23 16:54:53 -070028#include "android_runtime/AndroidRuntime.h"
Dongwon Kangbf98d542018-09-11 14:40:23 -070029
Marco Nelissena9a802f2019-09-24 09:27:22 -070030// This source file is compiled and linked into:
Ray Essickc535a552019-01-18 11:38:15 -080031// core/jni/ (libandroid_runtime.so)
Ray Essickc535a552019-01-18 11:38:15 -080032
Dongwon Kangbf98d542018-09-11 14:40:23 -070033namespace android {
34
Andy Hung73348482019-10-23 16:54:53 -070035namespace {
36struct BundleHelper {
37 BundleHelper(JNIEnv* _env, jobject _bundle)
38 : env(_env)
39 , clazzBundle(env->FindClass("android/os/PersistableBundle"))
40 , putIntID(env->GetMethodID(clazzBundle, "putInt", "(Ljava/lang/String;I)V"))
41 , putLongID(env->GetMethodID(clazzBundle, "putLong", "(Ljava/lang/String;J)V"))
42 , putDoubleID(env->GetMethodID(clazzBundle, "putDouble", "(Ljava/lang/String;D)V"))
43 , putStringID(env->GetMethodID(clazzBundle,
44 "putString", "(Ljava/lang/String;Ljava/lang/String;)V"))
45 , constructID(env->GetMethodID(clazzBundle, "<init>", "()V"))
46 , bundle(_bundle == nullptr ? env->NewObject(clazzBundle, constructID) : _bundle)
47 { }
48
49 JNIEnv* const env;
50 const jclass clazzBundle;
51 const jmethodID putIntID;
52 const jmethodID putLongID;
53 const jmethodID putDoubleID;
54 const jmethodID putStringID;
55 const jmethodID constructID;
56 jobject const bundle;
57
Ray Essick81fbc5b2019-12-07 06:24:59 -080058 // We use templated put to access mediametrics::Item based on data type not type enum.
Andy Hung73348482019-10-23 16:54:53 -070059 // See std::variant and std::visit.
60 template<typename T>
61 void put(jstring keyName, const T& value) = delete;
62
63 template<>
64 void put(jstring keyName, const int32_t& value) {
65 env->CallVoidMethod(bundle, putIntID, keyName, (jint)value);
66 }
67
68 template<>
69 void put(jstring keyName, const int64_t& value) {
70 env->CallVoidMethod(bundle, putLongID, keyName, (jlong)value);
71 }
72
73 template<>
74 void put(jstring keyName, const double& value) {
75 env->CallVoidMethod(bundle, putDoubleID, keyName, (jdouble)value);
76 }
77
78 template<>
Andy Hung70549312019-12-16 12:05:24 -080079 void put(jstring keyName, const std::string& value) {
80 env->CallVoidMethod(bundle, putStringID, keyName, env->NewStringUTF(value.c_str()));
81 }
82
83 template<>
84 void put(jstring keyName, const std::pair<int64_t, int64_t>& value) {
85 ; // rate is currently ignored
86 }
87
88 template<>
89 void put(jstring keyName, const std::monostate& value) {
90 ; // none is currently ignored
91 }
92
93 // string char * helpers
94
95 template<>
Andy Hung73348482019-10-23 16:54:53 -070096 void put(jstring keyName, const char * const& value) {
97 env->CallVoidMethod(bundle, putStringID, keyName, env->NewStringUTF(value));
98 }
99
100 template<>
101 void put(jstring keyName, char * const& value) {
102 env->CallVoidMethod(bundle, putStringID, keyName, env->NewStringUTF(value));
103 }
104
Andy Hung73348482019-10-23 16:54:53 -0700105 // We allow both jstring and non-jstring variants.
106 template<typename T>
107 void put(const char *keyName, const T& value) {
108 put(env->NewStringUTF(keyName), value);
109 }
110};
111} // namespace
112
Dongwon Kangbf98d542018-09-11 14:40:23 -0700113// place the attributes into a java PersistableBundle object
Andy Hung73348482019-10-23 16:54:53 -0700114jobject MediaMetricsJNI::writeMetricsToBundle(
Ray Essick81fbc5b2019-12-07 06:24:59 -0800115 JNIEnv* env, mediametrics::Item *item, jobject bundle)
Andy Hung73348482019-10-23 16:54:53 -0700116{
117 BundleHelper bh(env, bundle);
Dongwon Kangbf98d542018-09-11 14:40:23 -0700118
Andy Hung73348482019-10-23 16:54:53 -0700119 if (bh.bundle == nullptr) {
120 ALOGE("%s: unable to create Bundle", __func__);
121 return nullptr;
Dongwon Kangbf98d542018-09-11 14:40:23 -0700122 }
123
Andy Hunga576e322019-12-19 12:41:51 -0800124 bh.put(mediametrics::BUNDLE_KEY, item->getKey().c_str());
Andy Hung73348482019-10-23 16:54:53 -0700125 if (item->getPid() != -1) {
Andy Hunga576e322019-12-19 12:41:51 -0800126 bh.put(mediametrics::BUNDLE_PID, (int32_t)item->getPid());
Dongwon Kangbf98d542018-09-11 14:40:23 -0700127 }
Andy Hung73348482019-10-23 16:54:53 -0700128 if (item->getTimestamp() > 0) {
Andy Hunga576e322019-12-19 12:41:51 -0800129 bh.put(mediametrics::BUNDLE_TIMESTAMP, (int64_t)item->getTimestamp());
Ray Essickc535a552019-01-18 11:38:15 -0800130 }
Andy Hung73348482019-10-23 16:54:53 -0700131 if (item->getUid() != -1) {
Andy Hunga576e322019-12-19 12:41:51 -0800132 bh.put(mediametrics::BUNDLE_UID, (int32_t)item->getUid());
Ray Essickc535a552019-01-18 11:38:15 -0800133 }
Andy Hung73348482019-10-23 16:54:53 -0700134 for (const auto &prop : *item) {
135 const char *name = prop.getName();
136 if (name == nullptr) continue;
137 prop.visit([&] (auto &value) { bh.put(name, value); });
Ray Essickc535a552019-01-18 11:38:15 -0800138 }
Andy Hung73348482019-10-23 16:54:53 -0700139 return bh.bundle;
Ray Essickc535a552019-01-18 11:38:15 -0800140}
141
Andy Hungf2310c72019-10-23 16:54:53 -0700142// Implementation of MediaMetrics.native_submit_bytebuffer(),
143// Delivers the byte buffer to the mediametrics service.
144static jint android_media_MediaMetrics_submit_bytebuffer(
145 JNIEnv* env, jobject thiz, jobject byteBuffer, jint length)
146{
147 const jbyte* buffer =
148 reinterpret_cast<const jbyte*>(env->GetDirectBufferAddress(byteBuffer));
149 if (buffer == nullptr) {
150 ALOGE("Error retrieving source of audio data to play, can't play");
151 return (jint)BAD_VALUE;
152 }
153
Andy Hunge7f750d2019-12-19 12:39:08 -0800154 sp<IMediaMetricsService> service = mediametrics::BaseItem::getService();
155 if (service == nullptr) {
156 ALOGW("Cannot retrieve mediametrics service");
157 return (jint)NO_INIT;
Andy Hungf2310c72019-10-23 16:54:53 -0700158 }
Andy Hunge7f750d2019-12-19 12:39:08 -0800159 return (jint)service->submitBuffer((char *)buffer, length);
Andy Hungf2310c72019-10-23 16:54:53 -0700160}
161
Robert Shih4354a962019-11-10 12:09:08 -0800162// Helper function to convert a native PersistableBundle to a Java
163// PersistableBundle.
164jobject MediaMetricsJNI::nativeToJavaPersistableBundle(JNIEnv *env,
165 os::PersistableBundle* nativeBundle) {
166 if (env == NULL || nativeBundle == NULL) {
167 ALOGE("Unexpected NULL parmeter");
168 return NULL;
169 }
170
171 // Create a Java parcel with the native parcel data.
172 // Then create a new PersistableBundle with that parcel as a parameter.
173 jobject jParcel = android::createJavaParcelObject(env);
174 if (jParcel == NULL) {
175 ALOGE("Failed to create a Java Parcel.");
176 return NULL;
177 }
178
179 android::Parcel* nativeParcel = android::parcelForJavaObject(env, jParcel);
180 if (nativeParcel == NULL) {
181 ALOGE("Failed to get the native Parcel.");
182 return NULL;
183 }
184
185 android::status_t result = nativeBundle->writeToParcel(nativeParcel);
186 nativeParcel->setDataPosition(0);
187 if (result != android::OK) {
188 ALOGE("Failed to write nativeBundle to Parcel: %d.", result);
189 return NULL;
190 }
191
192#define STATIC_INIT_JNI(T, obj, method, globalref, ...) \
193 static T obj{};\
194 if (obj == NULL) { \
195 obj = method(__VA_ARGS__); \
196 if (obj == NULL) { \
197 ALOGE("%s can't find " #obj, __func__); \
198 return NULL; \
199 } else { \
200 obj = globalref; \
201 }\
202 } \
203
204 STATIC_INIT_JNI(jclass, clazzBundle, env->FindClass,
205 static_cast<jclass>(env->NewGlobalRef(clazzBundle)),
206 "android/os/PersistableBundle");
207 STATIC_INIT_JNI(jfieldID, bundleCreatorId, env->GetStaticFieldID,
208 bundleCreatorId,
209 clazzBundle, "CREATOR", "Landroid/os/Parcelable$Creator;");
210 STATIC_INIT_JNI(jobject, bundleCreator, env->GetStaticObjectField,
211 env->NewGlobalRef(bundleCreator),
212 clazzBundle, bundleCreatorId);
213 STATIC_INIT_JNI(jclass, clazzCreator, env->FindClass,
214 static_cast<jclass>(env->NewGlobalRef(clazzCreator)),
215 "android/os/Parcelable$Creator");
216 STATIC_INIT_JNI(jmethodID, createFromParcelId, env->GetMethodID,
217 createFromParcelId,
218 clazzCreator, "createFromParcel", "(Landroid/os/Parcel;)Ljava/lang/Object;");
219
220 jobject newBundle = env->CallObjectMethod(bundleCreator, createFromParcelId, jParcel);
221 if (newBundle == NULL) {
222 ALOGE("Failed to create a new PersistableBundle "
223 "from the createFromParcel call.");
224 }
225
226 return newBundle;
227}
228
Andy Hungf2310c72019-10-23 16:54:53 -0700229// ----------------------------------------------------------------------------
Dongwon Kangbf98d542018-09-11 14:40:23 -0700230
Andy Hungf2310c72019-10-23 16:54:53 -0700231static constexpr JNINativeMethod gMethods[] = {
232 {"native_submit_bytebuffer", "(Ljava/nio/ByteBuffer;I)I",
233 (void *)android_media_MediaMetrics_submit_bytebuffer},
234};
235
236// Registers the native methods, called from core/jni/AndroidRuntime.cpp
237int register_android_media_MediaMetrics(JNIEnv *env)
238{
239 return AndroidRuntime::registerNativeMethods(
240 env, "android/media/MediaMetrics", gMethods, std::size(gMethods));
241}
242
243}; // namespace android