Dongwon Kang | bf98d54 | 2018-09-11 14:40:23 -0700 | [diff] [blame] | 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 | |
Ray Essick | c535a55 | 2019-01-18 11:38:15 -0800 | [diff] [blame] | 17 | #define LOG_TAG "MediaMetricsJNI" |
| 18 | |
Dongwon Kang | bf98d54 | 2018-09-11 14:40:23 -0700 | [diff] [blame] | 19 | #include <jni.h> |
| 20 | #include <nativehelper/JNIHelp.h> |
| 21 | |
| 22 | #include "android_media_MediaMetricsJNI.h" |
Robert Shih | 4354a96 | 2019-11-10 12:09:08 -0800 | [diff] [blame] | 23 | #include "android_os_Parcel.h" |
Dongwon Kang | bf98d54 | 2018-09-11 14:40:23 -0700 | [diff] [blame] | 24 | #include <media/MediaAnalyticsItem.h> |
Robert Shih | 4354a96 | 2019-11-10 12:09:08 -0800 | [diff] [blame] | 25 | #include <binder/Parcel.h> |
Dongwon Kang | bf98d54 | 2018-09-11 14:40:23 -0700 | [diff] [blame] | 26 | |
| 27 | |
Marco Nelissen | a9a802f | 2019-09-24 09:27:22 -0700 | [diff] [blame] | 28 | // This source file is compiled and linked into: |
Ray Essick | c535a55 | 2019-01-18 11:38:15 -0800 | [diff] [blame] | 29 | // core/jni/ (libandroid_runtime.so) |
Ray Essick | c535a55 | 2019-01-18 11:38:15 -0800 | [diff] [blame] | 30 | |
Dongwon Kang | bf98d54 | 2018-09-11 14:40:23 -0700 | [diff] [blame] | 31 | namespace android { |
| 32 | |
| 33 | // place the attributes into a java PersistableBundle object |
| 34 | jobject MediaMetricsJNI::writeMetricsToBundle(JNIEnv* env, MediaAnalyticsItem *item, jobject mybundle) { |
| 35 | |
| 36 | jclass clazzBundle = env->FindClass("android/os/PersistableBundle"); |
| 37 | if (clazzBundle==NULL) { |
Ray Essick | c535a55 | 2019-01-18 11:38:15 -0800 | [diff] [blame] | 38 | ALOGE("can't find android/os/PersistableBundle"); |
Dongwon Kang | bf98d54 | 2018-09-11 14:40:23 -0700 | [diff] [blame] | 39 | 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 Essick | c535a55 | 2019-01-18 11:38:15 -0800 | [diff] [blame] | 95 | // 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 |
| 100 | enum { kInt32 = 0, kInt64, kDouble, kRate, kCString}; |
| 101 | |
| 102 | jobject 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 Shih | 4354a96 | 2019-11-10 12:09:08 -0800 | [diff] [blame] | 228 | // Helper function to convert a native PersistableBundle to a Java |
| 229 | // PersistableBundle. |
| 230 | jobject 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 Kang | bf98d54 | 2018-09-11 14:40:23 -0700 | [diff] [blame] | 295 | }; // namespace android |
| 296 | |