blob: 5ddfcfce072ec9e33d008b869fe3c1abe64d1c92 [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
Dongwon Kangbf98d542018-09-11 14:40:23 -070019#include <jni.h>
20#include <nativehelper/JNIHelp.h>
21
22#include "android_media_MediaMetricsJNI.h"
Robert Shih4354a962019-11-10 12:09:08 -080023#include "android_os_Parcel.h"
Dongwon Kangbf98d542018-09-11 14:40:23 -070024#include <media/MediaAnalyticsItem.h>
Robert Shih4354a962019-11-10 12:09:08 -080025#include <binder/Parcel.h>
Dongwon Kangbf98d542018-09-11 14:40:23 -070026
27
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
33// place the attributes into a java PersistableBundle object
34jobject MediaMetricsJNI::writeMetricsToBundle(JNIEnv* env, MediaAnalyticsItem *item, jobject mybundle) {
35
36 jclass clazzBundle = env->FindClass("android/os/PersistableBundle");
37 if (clazzBundle==NULL) {
Ray Essickc535a552019-01-18 11:38:15 -080038 ALOGE("can't find android/os/PersistableBundle");
Dongwon Kangbf98d542018-09-11 14:40:23 -070039 return NULL;
40 }
41 // sometimes the caller provides one for us to fill
42 if (mybundle == NULL) {
43 // create the bundle
44 jmethodID constructID = env->GetMethodID(clazzBundle, "<init>", "()V");
45 mybundle = env->NewObject(clazzBundle, constructID);
46 if (mybundle == NULL) {
47 return NULL;
48 }
49 }
50
51 // grab methods that we can invoke
52 jmethodID setIntID = env->GetMethodID(clazzBundle, "putInt", "(Ljava/lang/String;I)V");
53 jmethodID setLongID = env->GetMethodID(clazzBundle, "putLong", "(Ljava/lang/String;J)V");
54 jmethodID setDoubleID = env->GetMethodID(clazzBundle, "putDouble", "(Ljava/lang/String;D)V");
55 jmethodID setStringID = env->GetMethodID(clazzBundle, "putString", "(Ljava/lang/String;Ljava/lang/String;)V");
56
57 // env, class, method, {parms}
58 //env->CallVoidMethod(env, mybundle, setIntID, jstr, jint);
59
60 // iterate through my attributes
61 // -- get name, get type, get value
62 // -- insert appropriately into the bundle
63 for (size_t i = 0 ; i < item->mPropCount; i++ ) {
64 MediaAnalyticsItem::Prop *prop = &item->mProps[i];
65 // build the key parameter from prop->mName
66 jstring keyName = env->NewStringUTF(prop->mName);
67 // invoke the appropriate method to insert
68 switch (prop->mType) {
69 case MediaAnalyticsItem::kTypeInt32:
70 env->CallVoidMethod(mybundle, setIntID,
71 keyName, (jint) prop->u.int32Value);
72 break;
73 case MediaAnalyticsItem::kTypeInt64:
74 env->CallVoidMethod(mybundle, setLongID,
75 keyName, (jlong) prop->u.int64Value);
76 break;
77 case MediaAnalyticsItem::kTypeDouble:
78 env->CallVoidMethod(mybundle, setDoubleID,
79 keyName, (jdouble) prop->u.doubleValue);
80 break;
81 case MediaAnalyticsItem::kTypeCString:
82 env->CallVoidMethod(mybundle, setStringID, keyName,
83 env->NewStringUTF(prop->u.CStringValue));
84 break;
85 default:
86 ALOGE("to_String bad item type: %d for %s",
87 prop->mType, prop->mName);
88 break;
89 }
90 }
91
92 return mybundle;
93}
94
Ray Essickc535a552019-01-18 11:38:15 -080095// convert the specified batch metrics attributes to a persistent bundle.
96// The encoding of the byte array is specified in
97// frameworks/av/media/libmediametrics/MediaAnalyticsItem.cpp
98//
99// type encodings; matches frameworks/av/media/libmediametrics/MediaAnalyticsItem.cpp
100enum { kInt32 = 0, kInt64, kDouble, kRate, kCString};
101
102jobject MediaMetricsJNI::writeAttributesToBundle(JNIEnv* env, jobject mybundle, char *buffer, size_t length) {
103 ALOGV("writeAttributes()");
104
105 if (buffer == NULL || length <= 0) {
106 ALOGW("bad parameters to writeAttributesToBundle()");
107 return NULL;
108 }
109
110 jclass clazzBundle = env->FindClass("android/os/PersistableBundle");
111 if (clazzBundle==NULL) {
112 ALOGE("can't find android/os/PersistableBundle");
113 return NULL;
114 }
115 // sometimes the caller provides one for us to fill
116 if (mybundle == NULL) {
117 // create the bundle
118 jmethodID constructID = env->GetMethodID(clazzBundle, "<init>", "()V");
119 mybundle = env->NewObject(clazzBundle, constructID);
120 if (mybundle == NULL) {
121 ALOGD("unable to create mybundle");
122 return NULL;
123 }
124 }
125
126 int left = length;
127 char *buf = buffer;
128
129 // grab methods that we can invoke
130 jmethodID setIntID = env->GetMethodID(clazzBundle, "putInt", "(Ljava/lang/String;I)V");
131 jmethodID setLongID = env->GetMethodID(clazzBundle, "putLong", "(Ljava/lang/String;J)V");
132 jmethodID setDoubleID = env->GetMethodID(clazzBundle, "putDouble", "(Ljava/lang/String;D)V");
133 jmethodID setStringID = env->GetMethodID(clazzBundle, "putString", "(Ljava/lang/String;Ljava/lang/String;)V");
134
135
136#define _EXTRACT(size, val) \
137 { if ((size) > left) goto badness; memcpy(&val, buf, (size)); buf += (size); left -= (size);}
138#define _SKIP(size) \
139 { if ((size) > left) goto badness; buf += (size); left -= (size);}
140
141 int32_t bufsize;
142 _EXTRACT(sizeof(int32_t), bufsize);
143 if (bufsize != length) {
144 goto badness;
145 }
146 int32_t proto;
147 _EXTRACT(sizeof(int32_t), proto);
148 if (proto != 0) {
149 ALOGE("unsupported wire protocol %d", proto);
150 goto badness;
151 }
152
153 int32_t count;
154 _EXTRACT(sizeof(int32_t), count);
155
156 // iterate through my attributes
157 // -- get name, get type, get value, insert into bundle appropriately.
158 for (int i = 0 ; i < count; i++ ) {
159 // prop name len (int16)
160 int16_t keylen;
161 _EXTRACT(sizeof(int16_t), keylen);
162 if (keylen <= 0) goto badness;
163 // prop name itself
164 char *key = buf;
165 jstring keyName = env->NewStringUTF(buf);
166 _SKIP(keylen);
167
168 // prop type (int8_t)
169 int8_t attrType;
170 _EXTRACT(sizeof(int8_t), attrType);
171
172 int16_t attrSize;
173 _EXTRACT(sizeof(int16_t), attrSize);
174
175 switch (attrType) {
176 case kInt32:
177 {
178 int32_t i32;
179 _EXTRACT(sizeof(int32_t), i32);
180 env->CallVoidMethod(mybundle, setIntID,
181 keyName, (jint) i32);
182 break;
183 }
184 case kInt64:
185 {
186 int64_t i64;
187 _EXTRACT(sizeof(int64_t), i64);
188 env->CallVoidMethod(mybundle, setLongID,
189 keyName, (jlong) i64);
190 break;
191 }
192 case kDouble:
193 {
194 double d64;
195 _EXTRACT(sizeof(double), d64);
196 env->CallVoidMethod(mybundle, setDoubleID,
197 keyName, (jdouble) d64);
198 break;
199 }
200 case kCString:
201 {
202 jstring value = env->NewStringUTF(buf);
203 env->CallVoidMethod(mybundle, setStringID,
204 keyName, value);
205 _SKIP(attrSize);
206 break;
207 }
208 default:
209 ALOGW("ignoring Attribute '%s' unknown type: %d",
210 key, attrType);
211 _SKIP(attrSize);
212 break;
213 }
214 }
215
216 // should have consumed it all
217 if (left != 0) {
218 ALOGW("did not consume entire buffer; left(%d) != 0", left);
219 goto badness;
220 }
221
222 return mybundle;
223
224 badness:
225 return NULL;
226}
227
Robert Shih4354a962019-11-10 12:09:08 -0800228// Helper function to convert a native PersistableBundle to a Java
229// PersistableBundle.
230jobject MediaMetricsJNI::nativeToJavaPersistableBundle(JNIEnv *env,
231 os::PersistableBundle* nativeBundle) {
232 if (env == NULL || nativeBundle == NULL) {
233 ALOGE("Unexpected NULL parmeter");
234 return NULL;
235 }
236
237 // Create a Java parcel with the native parcel data.
238 // Then create a new PersistableBundle with that parcel as a parameter.
239 jobject jParcel = android::createJavaParcelObject(env);
240 if (jParcel == NULL) {
241 ALOGE("Failed to create a Java Parcel.");
242 return NULL;
243 }
244
245 android::Parcel* nativeParcel = android::parcelForJavaObject(env, jParcel);
246 if (nativeParcel == NULL) {
247 ALOGE("Failed to get the native Parcel.");
248 return NULL;
249 }
250
251 android::status_t result = nativeBundle->writeToParcel(nativeParcel);
252 nativeParcel->setDataPosition(0);
253 if (result != android::OK) {
254 ALOGE("Failed to write nativeBundle to Parcel: %d.", result);
255 return NULL;
256 }
257
258#define STATIC_INIT_JNI(T, obj, method, globalref, ...) \
259 static T obj{};\
260 if (obj == NULL) { \
261 obj = method(__VA_ARGS__); \
262 if (obj == NULL) { \
263 ALOGE("%s can't find " #obj, __func__); \
264 return NULL; \
265 } else { \
266 obj = globalref; \
267 }\
268 } \
269
270 STATIC_INIT_JNI(jclass, clazzBundle, env->FindClass,
271 static_cast<jclass>(env->NewGlobalRef(clazzBundle)),
272 "android/os/PersistableBundle");
273 STATIC_INIT_JNI(jfieldID, bundleCreatorId, env->GetStaticFieldID,
274 bundleCreatorId,
275 clazzBundle, "CREATOR", "Landroid/os/Parcelable$Creator;");
276 STATIC_INIT_JNI(jobject, bundleCreator, env->GetStaticObjectField,
277 env->NewGlobalRef(bundleCreator),
278 clazzBundle, bundleCreatorId);
279 STATIC_INIT_JNI(jclass, clazzCreator, env->FindClass,
280 static_cast<jclass>(env->NewGlobalRef(clazzCreator)),
281 "android/os/Parcelable$Creator");
282 STATIC_INIT_JNI(jmethodID, createFromParcelId, env->GetMethodID,
283 createFromParcelId,
284 clazzCreator, "createFromParcel", "(Landroid/os/Parcel;)Ljava/lang/Object;");
285
286 jobject newBundle = env->CallObjectMethod(bundleCreator, createFromParcelId, jParcel);
287 if (newBundle == NULL) {
288 ALOGE("Failed to create a new PersistableBundle "
289 "from the createFromParcel call.");
290 }
291
292 return newBundle;
293}
294
Dongwon Kangbf98d542018-09-11 14:40:23 -0700295}; // namespace android
296