blob: 37aca08643b287fa0a9d330b22cdd074f25ac56d [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 Hung73348482019-10-23 16:54:53 -0700123 bh.put("__key", item->getKey().c_str());
124 if (item->getPid() != -1) {
125 bh.put("__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) {
128 bh.put("__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) {
131 bh.put("__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
153 // TODO: directly record item to MediaMetrics service.
154 mediametrics::Item item;
155 if (item.readFromByteString((char *)buffer, length) != NO_ERROR) {
156 ALOGW("%s: cannot read from byte string", __func__);
157 return (jint)BAD_VALUE;
158 }
159 item.selfrecord();
160 return (jint)NO_ERROR;
161}
162
Robert Shih4354a962019-11-10 12:09:08 -0800163// Helper function to convert a native PersistableBundle to a Java
164// PersistableBundle.
165jobject MediaMetricsJNI::nativeToJavaPersistableBundle(JNIEnv *env,
166 os::PersistableBundle* nativeBundle) {
167 if (env == NULL || nativeBundle == NULL) {
168 ALOGE("Unexpected NULL parmeter");
169 return NULL;
170 }
171
172 // Create a Java parcel with the native parcel data.
173 // Then create a new PersistableBundle with that parcel as a parameter.
174 jobject jParcel = android::createJavaParcelObject(env);
175 if (jParcel == NULL) {
176 ALOGE("Failed to create a Java Parcel.");
177 return NULL;
178 }
179
180 android::Parcel* nativeParcel = android::parcelForJavaObject(env, jParcel);
181 if (nativeParcel == NULL) {
182 ALOGE("Failed to get the native Parcel.");
183 return NULL;
184 }
185
186 android::status_t result = nativeBundle->writeToParcel(nativeParcel);
187 nativeParcel->setDataPosition(0);
188 if (result != android::OK) {
189 ALOGE("Failed to write nativeBundle to Parcel: %d.", result);
190 return NULL;
191 }
192
193#define STATIC_INIT_JNI(T, obj, method, globalref, ...) \
194 static T obj{};\
195 if (obj == NULL) { \
196 obj = method(__VA_ARGS__); \
197 if (obj == NULL) { \
198 ALOGE("%s can't find " #obj, __func__); \
199 return NULL; \
200 } else { \
201 obj = globalref; \
202 }\
203 } \
204
205 STATIC_INIT_JNI(jclass, clazzBundle, env->FindClass,
206 static_cast<jclass>(env->NewGlobalRef(clazzBundle)),
207 "android/os/PersistableBundle");
208 STATIC_INIT_JNI(jfieldID, bundleCreatorId, env->GetStaticFieldID,
209 bundleCreatorId,
210 clazzBundle, "CREATOR", "Landroid/os/Parcelable$Creator;");
211 STATIC_INIT_JNI(jobject, bundleCreator, env->GetStaticObjectField,
212 env->NewGlobalRef(bundleCreator),
213 clazzBundle, bundleCreatorId);
214 STATIC_INIT_JNI(jclass, clazzCreator, env->FindClass,
215 static_cast<jclass>(env->NewGlobalRef(clazzCreator)),
216 "android/os/Parcelable$Creator");
217 STATIC_INIT_JNI(jmethodID, createFromParcelId, env->GetMethodID,
218 createFromParcelId,
219 clazzCreator, "createFromParcel", "(Landroid/os/Parcel;)Ljava/lang/Object;");
220
221 jobject newBundle = env->CallObjectMethod(bundleCreator, createFromParcelId, jParcel);
222 if (newBundle == NULL) {
223 ALOGE("Failed to create a new PersistableBundle "
224 "from the createFromParcel call.");
225 }
226
227 return newBundle;
228}
229
Andy Hungf2310c72019-10-23 16:54:53 -0700230// ----------------------------------------------------------------------------
Dongwon Kangbf98d542018-09-11 14:40:23 -0700231
Andy Hungf2310c72019-10-23 16:54:53 -0700232static constexpr JNINativeMethod gMethods[] = {
233 {"native_submit_bytebuffer", "(Ljava/nio/ByteBuffer;I)I",
234 (void *)android_media_MediaMetrics_submit_bytebuffer},
235};
236
237// Registers the native methods, called from core/jni/AndroidRuntime.cpp
238int register_android_media_MediaMetrics(JNIEnv *env)
239{
240 return AndroidRuntime::registerNativeMethods(
241 env, "android/media/MediaMetrics", gMethods, std::size(gMethods));
242}
243
244}; // namespace android