blob: 08a8d897c96eb44b0f9b7f0d20b8a1caa05282f4 [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>
Ray Essick81fbc5b2019-12-07 06:24:59 -080021#include <media/MediaMetricsItem.h>
Dongwon Kangbf98d542018-09-11 14:40:23 -070022#include <nativehelper/JNIHelp.h>
Andy Hung70549312019-12-16 12:05:24 -080023#include <variant>
Dongwon Kangbf98d542018-09-11 14:40:23 -070024
25#include "android_media_MediaMetricsJNI.h"
Robert Shih4354a962019-11-10 12:09:08 -080026#include "android_os_Parcel.h"
Andy Hungf2310c72019-10-23 16:54:53 -070027#include "android_runtime/AndroidRuntime.h"
Dongwon Kangbf98d542018-09-11 14:40:23 -070028
Marco Nelissena9a802f2019-09-24 09:27:22 -070029// This source file is compiled and linked into:
Ray Essickc535a552019-01-18 11:38:15 -080030// core/jni/ (libandroid_runtime.so)
Ray Essickc535a552019-01-18 11:38:15 -080031
Dongwon Kangbf98d542018-09-11 14:40:23 -070032namespace android {
33
Andy Hung73348482019-10-23 16:54:53 -070034namespace {
35struct BundleHelper {
36 BundleHelper(JNIEnv* _env, jobject _bundle)
37 : env(_env)
38 , clazzBundle(env->FindClass("android/os/PersistableBundle"))
39 , putIntID(env->GetMethodID(clazzBundle, "putInt", "(Ljava/lang/String;I)V"))
40 , putLongID(env->GetMethodID(clazzBundle, "putLong", "(Ljava/lang/String;J)V"))
41 , putDoubleID(env->GetMethodID(clazzBundle, "putDouble", "(Ljava/lang/String;D)V"))
42 , putStringID(env->GetMethodID(clazzBundle,
43 "putString", "(Ljava/lang/String;Ljava/lang/String;)V"))
44 , constructID(env->GetMethodID(clazzBundle, "<init>", "()V"))
45 , bundle(_bundle == nullptr ? env->NewObject(clazzBundle, constructID) : _bundle)
46 { }
47
48 JNIEnv* const env;
49 const jclass clazzBundle;
50 const jmethodID putIntID;
51 const jmethodID putLongID;
52 const jmethodID putDoubleID;
53 const jmethodID putStringID;
54 const jmethodID constructID;
55 jobject const bundle;
56
Ray Essick81fbc5b2019-12-07 06:24:59 -080057 // We use templated put to access mediametrics::Item based on data type not type enum.
Andy Hung73348482019-10-23 16:54:53 -070058 // See std::variant and std::visit.
59 template<typename T>
60 void put(jstring keyName, const T& value) = delete;
61
62 template<>
63 void put(jstring keyName, const int32_t& value) {
64 env->CallVoidMethod(bundle, putIntID, keyName, (jint)value);
65 }
66
67 template<>
68 void put(jstring keyName, const int64_t& value) {
69 env->CallVoidMethod(bundle, putLongID, keyName, (jlong)value);
70 }
71
72 template<>
73 void put(jstring keyName, const double& value) {
74 env->CallVoidMethod(bundle, putDoubleID, keyName, (jdouble)value);
75 }
76
77 template<>
Andy Hung70549312019-12-16 12:05:24 -080078 void put(jstring keyName, const std::string& value) {
79 env->CallVoidMethod(bundle, putStringID, keyName, env->NewStringUTF(value.c_str()));
80 }
81
82 template<>
83 void put(jstring keyName, const std::pair<int64_t, int64_t>& value) {
84 ; // rate is currently ignored
85 }
86
87 template<>
88 void put(jstring keyName, const std::monostate& value) {
89 ; // none is currently ignored
90 }
91
92 // string char * helpers
93
94 template<>
Andy Hung73348482019-10-23 16:54:53 -070095 void put(jstring keyName, const char * const& value) {
96 env->CallVoidMethod(bundle, putStringID, keyName, env->NewStringUTF(value));
97 }
98
99 template<>
100 void put(jstring keyName, char * const& value) {
101 env->CallVoidMethod(bundle, putStringID, keyName, env->NewStringUTF(value));
102 }
103
Andy Hung73348482019-10-23 16:54:53 -0700104 // We allow both jstring and non-jstring variants.
105 template<typename T>
106 void put(const char *keyName, const T& value) {
107 put(env->NewStringUTF(keyName), value);
108 }
109};
110} // namespace
111
Dongwon Kangbf98d542018-09-11 14:40:23 -0700112// place the attributes into a java PersistableBundle object
Andy Hung73348482019-10-23 16:54:53 -0700113jobject MediaMetricsJNI::writeMetricsToBundle(
Ray Essick81fbc5b2019-12-07 06:24:59 -0800114 JNIEnv* env, mediametrics::Item *item, jobject bundle)
Andy Hung73348482019-10-23 16:54:53 -0700115{
116 BundleHelper bh(env, bundle);
Dongwon Kangbf98d542018-09-11 14:40:23 -0700117
Andy Hung73348482019-10-23 16:54:53 -0700118 if (bh.bundle == nullptr) {
119 ALOGE("%s: unable to create Bundle", __func__);
120 return nullptr;
Dongwon Kangbf98d542018-09-11 14:40:23 -0700121 }
122
Andy Hunga576e322019-12-19 12:41:51 -0800123 bh.put(mediametrics::BUNDLE_KEY, item->getKey().c_str());
Andy Hung73348482019-10-23 16:54:53 -0700124 if (item->getPid() != -1) {
Andy Hunga576e322019-12-19 12:41:51 -0800125 bh.put(mediametrics::BUNDLE_PID, (int32_t)item->getPid());
Dongwon Kangbf98d542018-09-11 14:40:23 -0700126 }
Andy Hung73348482019-10-23 16:54:53 -0700127 if (item->getTimestamp() > 0) {
Andy Hunga576e322019-12-19 12:41:51 -0800128 bh.put(mediametrics::BUNDLE_TIMESTAMP, (int64_t)item->getTimestamp());
Ray Essickc535a552019-01-18 11:38:15 -0800129 }
Andy Hung73348482019-10-23 16:54:53 -0700130 if (item->getUid() != -1) {
Andy Hunga576e322019-12-19 12:41:51 -0800131 bh.put(mediametrics::BUNDLE_UID, (int32_t)item->getUid());
Ray Essickc535a552019-01-18 11:38:15 -0800132 }
Andy Hung73348482019-10-23 16:54:53 -0700133 for (const auto &prop : *item) {
134 const char *name = prop.getName();
135 if (name == nullptr) continue;
136 prop.visit([&] (auto &value) { bh.put(name, value); });
Ray Essickc535a552019-01-18 11:38:15 -0800137 }
Andy Hung73348482019-10-23 16:54:53 -0700138 return bh.bundle;
Ray Essickc535a552019-01-18 11:38:15 -0800139}
140
Andy Hungf2310c72019-10-23 16:54:53 -0700141// Implementation of MediaMetrics.native_submit_bytebuffer(),
142// Delivers the byte buffer to the mediametrics service.
143static jint android_media_MediaMetrics_submit_bytebuffer(
144 JNIEnv* env, jobject thiz, jobject byteBuffer, jint length)
145{
146 const jbyte* buffer =
147 reinterpret_cast<const jbyte*>(env->GetDirectBufferAddress(byteBuffer));
148 if (buffer == nullptr) {
149 ALOGE("Error retrieving source of audio data to play, can't play");
150 return (jint)BAD_VALUE;
151 }
152
Andy Hungfc63a302021-02-01 10:30:25 -0800153 return (jint)mediametrics::BaseItem::submitBuffer((char *)buffer, length);
Andy Hungf2310c72019-10-23 16:54:53 -0700154}
155
Robert Shih4354a962019-11-10 12:09:08 -0800156// Helper function to convert a native PersistableBundle to a Java
157// PersistableBundle.
158jobject MediaMetricsJNI::nativeToJavaPersistableBundle(JNIEnv *env,
159 os::PersistableBundle* nativeBundle) {
160 if (env == NULL || nativeBundle == NULL) {
161 ALOGE("Unexpected NULL parmeter");
162 return NULL;
163 }
164
165 // Create a Java parcel with the native parcel data.
166 // Then create a new PersistableBundle with that parcel as a parameter.
167 jobject jParcel = android::createJavaParcelObject(env);
168 if (jParcel == NULL) {
169 ALOGE("Failed to create a Java Parcel.");
170 return NULL;
171 }
172
173 android::Parcel* nativeParcel = android::parcelForJavaObject(env, jParcel);
174 if (nativeParcel == NULL) {
175 ALOGE("Failed to get the native Parcel.");
176 return NULL;
177 }
178
179 android::status_t result = nativeBundle->writeToParcel(nativeParcel);
180 nativeParcel->setDataPosition(0);
181 if (result != android::OK) {
182 ALOGE("Failed to write nativeBundle to Parcel: %d.", result);
183 return NULL;
184 }
185
186#define STATIC_INIT_JNI(T, obj, method, globalref, ...) \
187 static T obj{};\
188 if (obj == NULL) { \
189 obj = method(__VA_ARGS__); \
190 if (obj == NULL) { \
191 ALOGE("%s can't find " #obj, __func__); \
192 return NULL; \
193 } else { \
194 obj = globalref; \
195 }\
196 } \
197
198 STATIC_INIT_JNI(jclass, clazzBundle, env->FindClass,
199 static_cast<jclass>(env->NewGlobalRef(clazzBundle)),
200 "android/os/PersistableBundle");
201 STATIC_INIT_JNI(jfieldID, bundleCreatorId, env->GetStaticFieldID,
202 bundleCreatorId,
203 clazzBundle, "CREATOR", "Landroid/os/Parcelable$Creator;");
204 STATIC_INIT_JNI(jobject, bundleCreator, env->GetStaticObjectField,
205 env->NewGlobalRef(bundleCreator),
206 clazzBundle, bundleCreatorId);
207 STATIC_INIT_JNI(jclass, clazzCreator, env->FindClass,
208 static_cast<jclass>(env->NewGlobalRef(clazzCreator)),
209 "android/os/Parcelable$Creator");
210 STATIC_INIT_JNI(jmethodID, createFromParcelId, env->GetMethodID,
211 createFromParcelId,
212 clazzCreator, "createFromParcel", "(Landroid/os/Parcel;)Ljava/lang/Object;");
213
214 jobject newBundle = env->CallObjectMethod(bundleCreator, createFromParcelId, jParcel);
215 if (newBundle == NULL) {
216 ALOGE("Failed to create a new PersistableBundle "
217 "from the createFromParcel call.");
218 }
219
220 return newBundle;
221}
222
Andy Hungf2310c72019-10-23 16:54:53 -0700223// ----------------------------------------------------------------------------
Dongwon Kangbf98d542018-09-11 14:40:23 -0700224
Andy Hungf2310c72019-10-23 16:54:53 -0700225static constexpr JNINativeMethod gMethods[] = {
226 {"native_submit_bytebuffer", "(Ljava/nio/ByteBuffer;I)I",
227 (void *)android_media_MediaMetrics_submit_bytebuffer},
228};
229
230// Registers the native methods, called from core/jni/AndroidRuntime.cpp
231int register_android_media_MediaMetrics(JNIEnv *env)
232{
233 return AndroidRuntime::registerNativeMethods(
234 env, "android/media/MediaMetrics", gMethods, std::size(gMethods));
235}
236
237}; // namespace android