• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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