blob: e17a6173ba4da7c5a0d9141f7ae36033784d4268 [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>
23
24#include "android_media_MediaMetricsJNI.h"
Robert Shih4354a962019-11-10 12:09:08 -080025#include "android_os_Parcel.h"
Andy Hungf2310c72019-10-23 16:54:53 -070026#include "android_runtime/AndroidRuntime.h"
Dongwon Kangbf98d542018-09-11 14:40:23 -070027
Marco Nelissena9a802f2019-09-24 09:27:22 -070028// This source file is compiled and linked into:
Ray Essickc535a552019-01-18 11:38:15 -080029// core/jni/ (libandroid_runtime.so)
Ray Essickc535a552019-01-18 11:38:15 -080030
Dongwon Kangbf98d542018-09-11 14:40:23 -070031namespace android {
32
Andy Hung73348482019-10-23 16:54:53 -070033namespace {
34struct BundleHelper {
35 BundleHelper(JNIEnv* _env, jobject _bundle)
36 : env(_env)
37 , clazzBundle(env->FindClass("android/os/PersistableBundle"))
38 , putIntID(env->GetMethodID(clazzBundle, "putInt", "(Ljava/lang/String;I)V"))
39 , putLongID(env->GetMethodID(clazzBundle, "putLong", "(Ljava/lang/String;J)V"))
40 , putDoubleID(env->GetMethodID(clazzBundle, "putDouble", "(Ljava/lang/String;D)V"))
41 , putStringID(env->GetMethodID(clazzBundle,
42 "putString", "(Ljava/lang/String;Ljava/lang/String;)V"))
43 , constructID(env->GetMethodID(clazzBundle, "<init>", "()V"))
44 , bundle(_bundle == nullptr ? env->NewObject(clazzBundle, constructID) : _bundle)
45 { }
46
47 JNIEnv* const env;
48 const jclass clazzBundle;
49 const jmethodID putIntID;
50 const jmethodID putLongID;
51 const jmethodID putDoubleID;
52 const jmethodID putStringID;
53 const jmethodID constructID;
54 jobject const bundle;
55
Ray Essick81fbc5b2019-12-07 06:24:59 -080056 // We use templated put to access mediametrics::Item based on data type not type enum.
Andy Hung73348482019-10-23 16:54:53 -070057 // See std::variant and std::visit.
58 template<typename T>
59 void put(jstring keyName, const T& value) = delete;
60
61 template<>
62 void put(jstring keyName, const int32_t& value) {
63 env->CallVoidMethod(bundle, putIntID, keyName, (jint)value);
64 }
65
66 template<>
67 void put(jstring keyName, const int64_t& value) {
68 env->CallVoidMethod(bundle, putLongID, keyName, (jlong)value);
69 }
70
71 template<>
72 void put(jstring keyName, const double& value) {
73 env->CallVoidMethod(bundle, putDoubleID, keyName, (jdouble)value);
74 }
75
76 template<>
77 void put(jstring keyName, const char * const& value) {
78 env->CallVoidMethod(bundle, putStringID, keyName, env->NewStringUTF(value));
79 }
80
81 template<>
82 void put(jstring keyName, char * const& value) {
83 env->CallVoidMethod(bundle, putStringID, keyName, env->NewStringUTF(value));
84 }
85
86 template<>
87 void put(jstring keyName, const std::pair<int64_t, int64_t>& value) {
88 ; // rate is currently ignored
89 }
90
91 // We allow both jstring and non-jstring variants.
92 template<typename T>
93 void put(const char *keyName, const T& value) {
94 put(env->NewStringUTF(keyName), value);
95 }
96};
97} // namespace
98
Dongwon Kangbf98d542018-09-11 14:40:23 -070099// place the attributes into a java PersistableBundle object
Andy Hung73348482019-10-23 16:54:53 -0700100jobject MediaMetricsJNI::writeMetricsToBundle(
Ray Essick81fbc5b2019-12-07 06:24:59 -0800101 JNIEnv* env, mediametrics::Item *item, jobject bundle)
Andy Hung73348482019-10-23 16:54:53 -0700102{
103 BundleHelper bh(env, bundle);
Dongwon Kangbf98d542018-09-11 14:40:23 -0700104
Andy Hung73348482019-10-23 16:54:53 -0700105 if (bh.bundle == nullptr) {
106 ALOGE("%s: unable to create Bundle", __func__);
107 return nullptr;
Dongwon Kangbf98d542018-09-11 14:40:23 -0700108 }
109
Andy Hung73348482019-10-23 16:54:53 -0700110 bh.put("__key", item->getKey().c_str());
111 if (item->getPid() != -1) {
112 bh.put("__pid", (int32_t)item->getPid());
Dongwon Kangbf98d542018-09-11 14:40:23 -0700113 }
Andy Hung73348482019-10-23 16:54:53 -0700114 if (item->getTimestamp() > 0) {
115 bh.put("__timestamp", (int64_t)item->getTimestamp());
Ray Essickc535a552019-01-18 11:38:15 -0800116 }
Andy Hung73348482019-10-23 16:54:53 -0700117 if (item->getUid() != -1) {
118 bh.put("__uid", (int32_t)item->getUid());
Ray Essickc535a552019-01-18 11:38:15 -0800119 }
Andy Hung73348482019-10-23 16:54:53 -0700120 for (const auto &prop : *item) {
121 const char *name = prop.getName();
122 if (name == nullptr) continue;
123 prop.visit([&] (auto &value) { bh.put(name, value); });
Ray Essickc535a552019-01-18 11:38:15 -0800124 }
Andy Hung73348482019-10-23 16:54:53 -0700125 return bh.bundle;
Ray Essickc535a552019-01-18 11:38:15 -0800126}
127
Andy Hungf2310c72019-10-23 16:54:53 -0700128// Implementation of MediaMetrics.native_submit_bytebuffer(),
129// Delivers the byte buffer to the mediametrics service.
130static jint android_media_MediaMetrics_submit_bytebuffer(
131 JNIEnv* env, jobject thiz, jobject byteBuffer, jint length)
132{
133 const jbyte* buffer =
134 reinterpret_cast<const jbyte*>(env->GetDirectBufferAddress(byteBuffer));
135 if (buffer == nullptr) {
136 ALOGE("Error retrieving source of audio data to play, can't play");
137 return (jint)BAD_VALUE;
138 }
139
140 // TODO: directly record item to MediaMetrics service.
141 mediametrics::Item item;
142 if (item.readFromByteString((char *)buffer, length) != NO_ERROR) {
143 ALOGW("%s: cannot read from byte string", __func__);
144 return (jint)BAD_VALUE;
145 }
146 item.selfrecord();
147 return (jint)NO_ERROR;
148}
149
Robert Shih4354a962019-11-10 12:09:08 -0800150// Helper function to convert a native PersistableBundle to a Java
151// PersistableBundle.
152jobject MediaMetricsJNI::nativeToJavaPersistableBundle(JNIEnv *env,
153 os::PersistableBundle* nativeBundle) {
154 if (env == NULL || nativeBundle == NULL) {
155 ALOGE("Unexpected NULL parmeter");
156 return NULL;
157 }
158
159 // Create a Java parcel with the native parcel data.
160 // Then create a new PersistableBundle with that parcel as a parameter.
161 jobject jParcel = android::createJavaParcelObject(env);
162 if (jParcel == NULL) {
163 ALOGE("Failed to create a Java Parcel.");
164 return NULL;
165 }
166
167 android::Parcel* nativeParcel = android::parcelForJavaObject(env, jParcel);
168 if (nativeParcel == NULL) {
169 ALOGE("Failed to get the native Parcel.");
170 return NULL;
171 }
172
173 android::status_t result = nativeBundle->writeToParcel(nativeParcel);
174 nativeParcel->setDataPosition(0);
175 if (result != android::OK) {
176 ALOGE("Failed to write nativeBundle to Parcel: %d.", result);
177 return NULL;
178 }
179
180#define STATIC_INIT_JNI(T, obj, method, globalref, ...) \
181 static T obj{};\
182 if (obj == NULL) { \
183 obj = method(__VA_ARGS__); \
184 if (obj == NULL) { \
185 ALOGE("%s can't find " #obj, __func__); \
186 return NULL; \
187 } else { \
188 obj = globalref; \
189 }\
190 } \
191
192 STATIC_INIT_JNI(jclass, clazzBundle, env->FindClass,
193 static_cast<jclass>(env->NewGlobalRef(clazzBundle)),
194 "android/os/PersistableBundle");
195 STATIC_INIT_JNI(jfieldID, bundleCreatorId, env->GetStaticFieldID,
196 bundleCreatorId,
197 clazzBundle, "CREATOR", "Landroid/os/Parcelable$Creator;");
198 STATIC_INIT_JNI(jobject, bundleCreator, env->GetStaticObjectField,
199 env->NewGlobalRef(bundleCreator),
200 clazzBundle, bundleCreatorId);
201 STATIC_INIT_JNI(jclass, clazzCreator, env->FindClass,
202 static_cast<jclass>(env->NewGlobalRef(clazzCreator)),
203 "android/os/Parcelable$Creator");
204 STATIC_INIT_JNI(jmethodID, createFromParcelId, env->GetMethodID,
205 createFromParcelId,
206 clazzCreator, "createFromParcel", "(Landroid/os/Parcel;)Ljava/lang/Object;");
207
208 jobject newBundle = env->CallObjectMethod(bundleCreator, createFromParcelId, jParcel);
209 if (newBundle == NULL) {
210 ALOGE("Failed to create a new PersistableBundle "
211 "from the createFromParcel call.");
212 }
213
214 return newBundle;
215}
216
Andy Hungf2310c72019-10-23 16:54:53 -0700217// ----------------------------------------------------------------------------
Dongwon Kangbf98d542018-09-11 14:40:23 -0700218
Andy Hungf2310c72019-10-23 16:54:53 -0700219static constexpr JNINativeMethod gMethods[] = {
220 {"native_submit_bytebuffer", "(Ljava/nio/ByteBuffer;I)I",
221 (void *)android_media_MediaMetrics_submit_bytebuffer},
222};
223
224// Registers the native methods, called from core/jni/AndroidRuntime.cpp
225int register_android_media_MediaMetrics(JNIEnv *env)
226{
227 return AndroidRuntime::registerNativeMethods(
228 env, "android/media/MediaMetrics", gMethods, std::size(gMethods));
229}
230
231}; // namespace android