1 /*
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
17 #define LOG_TAG "MediaMetricsJNI"
18
19 #include <binder/Parcel.h>
20 #include <jni.h>
21 #include <media/MediaMetricsItem.h>
22 #include <nativehelper/JNIHelp.h>
23 #include <variant>
24
25 #include "android_media_MediaMetricsJNI.h"
26 #include "android_os_Parcel.h"
27 #include "android_runtime/AndroidRuntime.h"
28
29 // This source file is compiled and linked into:
30 // core/jni/ (libandroid_runtime.so)
31
32 namespace android {
33
34 namespace {
35 struct BundleHelper {
BundleHelperandroid::__anonf3e929470111::BundleHelper36 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
57 // We use templated put to access mediametrics::Item based on data type not type enum.
58 // See std::variant and std::visit.
59 template<typename T>
60 void put(jstring keyName, const T& value) = delete;
61
62 template<>
putandroid::__anonf3e929470111::BundleHelper63 void put(jstring keyName, const int32_t& value) {
64 env->CallVoidMethod(bundle, putIntID, keyName, (jint)value);
65 }
66
67 template<>
putandroid::__anonf3e929470111::BundleHelper68 void put(jstring keyName, const int64_t& value) {
69 env->CallVoidMethod(bundle, putLongID, keyName, (jlong)value);
70 }
71
72 template<>
putandroid::__anonf3e929470111::BundleHelper73 void put(jstring keyName, const double& value) {
74 env->CallVoidMethod(bundle, putDoubleID, keyName, (jdouble)value);
75 }
76
77 template<>
putandroid::__anonf3e929470111::BundleHelper78 void put(jstring keyName, const std::string& value) {
79 env->CallVoidMethod(bundle, putStringID, keyName, env->NewStringUTF(value.c_str()));
80 }
81
82 template<>
putandroid::__anonf3e929470111::BundleHelper83 void put(jstring keyName, const std::pair<int64_t, int64_t>& value) {
84 ; // rate is currently ignored
85 }
86
87 template<>
putandroid::__anonf3e929470111::BundleHelper88 void put(jstring keyName, const std::monostate& value) {
89 ; // none is currently ignored
90 }
91
92 // string char * helpers
93
94 template<>
putandroid::__anonf3e929470111::BundleHelper95 void put(jstring keyName, const char * const& value) {
96 env->CallVoidMethod(bundle, putStringID, keyName, env->NewStringUTF(value));
97 }
98
99 template<>
putandroid::__anonf3e929470111::BundleHelper100 void put(jstring keyName, char * const& value) {
101 env->CallVoidMethod(bundle, putStringID, keyName, env->NewStringUTF(value));
102 }
103
104 // We allow both jstring and non-jstring variants.
105 template<typename T>
putandroid::__anonf3e929470111::BundleHelper106 void put(const char *keyName, const T& value) {
107 put(env->NewStringUTF(keyName), value);
108 }
109 };
110 } // namespace
111
112 // place the attributes into a java PersistableBundle object
writeMetricsToBundle(JNIEnv * env,mediametrics::Item * item,jobject bundle)113 jobject MediaMetricsJNI::writeMetricsToBundle(
114 JNIEnv* env, mediametrics::Item *item, jobject bundle)
115 {
116 BundleHelper bh(env, bundle);
117
118 if (bh.bundle == nullptr) {
119 ALOGE("%s: unable to create Bundle", __func__);
120 return nullptr;
121 }
122
123 bh.put(mediametrics::BUNDLE_KEY, item->getKey().c_str());
124 if (item->getPid() != -1) {
125 bh.put(mediametrics::BUNDLE_PID, (int32_t)item->getPid());
126 }
127 if (item->getTimestamp() > 0) {
128 bh.put(mediametrics::BUNDLE_TIMESTAMP, (int64_t)item->getTimestamp());
129 }
130 if (item->getUid() != -1) {
131 bh.put(mediametrics::BUNDLE_UID, (int32_t)item->getUid());
132 }
133 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); });
137 }
138 return bh.bundle;
139 }
140
141 // Implementation of MediaMetrics.native_submit_bytebuffer(),
142 // Delivers the byte buffer to the mediametrics service.
android_media_MediaMetrics_submit_bytebuffer(JNIEnv * env,jobject thiz,jobject byteBuffer,jint length)143 static 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 return (jint)mediametrics::BaseItem::submitBuffer((char *)buffer, length);
154 }
155
156 // Helper function to convert a native PersistableBundle to a Java
157 // PersistableBundle.
nativeToJavaPersistableBundle(JNIEnv * env,os::PersistableBundle * nativeBundle)158 jobject 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
223 // ----------------------------------------------------------------------------
224
225 static 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
register_android_media_MediaMetrics(JNIEnv * env)231 int 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
238