blob: 0cc30df607a9a5d32d1640d2d2ec38fec7e10f9b
1 | #define LOG_TAG "Tv-JNI" |
2 | #include <utils/Log.h> |
3 | |
4 | #include "../include/tvcmd.h" |
5 | #include "jni.h" |
6 | #include "JNIHelp.h" |
7 | #include "GraphicsJNI.h" |
8 | #include "android_runtime/AndroidRuntime.h" |
9 | #include <utils/Vector.h> |
10 | #include <include/TvClient.h> |
11 | #include <binder/IMemory.h> |
12 | #include <binder/Parcel.h> |
13 | #include <binder/MemoryHeapBase.h> |
14 | #include <binder/MemoryBase.h> |
15 | #include <core/SkBitmap.h> |
16 | #include "android_util_Binder.h" |
17 | #include "android_os_Parcel.h" |
18 | using namespace android; |
19 | |
20 | struct fields_t { |
21 | jfieldID context; |
22 | jmethodID post_event; |
23 | }; |
24 | |
25 | static fields_t fields; |
26 | static Mutex sLock; |
27 | class JNITvContext: public TvListener { |
28 | public: |
29 | JNITvContext(JNIEnv *env, jobject weak_this, jclass clazz, const sp<TvClient> &tv); |
30 | ~JNITvContext() |
31 | { |
32 | release(); |
33 | } |
34 | virtual void notify(int32_t msgType, const Parcel &p); |
35 | void addCallbackBuffer(JNIEnv *env, jbyteArray cbb); |
36 | sp<TvClient> getTv() |
37 | { |
38 | Mutex::Autolock _l(mLock); |
39 | return mTv; |
40 | } |
41 | void release(); |
42 | Parcel *mExtParcel; |
43 | SkBitmap *pSubBmp;//for UI subtitle Bitmap |
44 | sp<MemoryBase> mSubMemBase;//for subtitle shar memory to tvapi |
45 | private: |
46 | jobject mTvJObjectWeak; // weak reference to java object |
47 | jclass mTvJClass; // strong reference to java class |
48 | sp<TvClient> mTv; // strong reference to native object |
49 | Mutex mLock; |
50 | |
51 | Vector<jbyteArray> mCallbackBuffers; // Global reference application managed byte[] |
52 | bool mManualBufferMode; // Whether to use application managed buffers. |
53 | bool mManualTvCallbackSet; // Whether the callback has been set, used to reduce unnecessary calls to set the callback. |
54 | }; |
55 | |
56 | sp<TvClient> get_native_tv(JNIEnv *env, jobject thiz, JNITvContext **pContext) |
57 | { |
58 | sp<TvClient> tv; |
59 | Mutex::Autolock _l(sLock); |
60 | JNITvContext *context = reinterpret_cast<JNITvContext *>(env->GetIntField(thiz, fields.context)); |
61 | if (context != NULL) { |
62 | tv = context->getTv(); |
63 | } |
64 | if (tv == 0) { |
65 | jniThrowException(env, "java/lang/RuntimeException", "Method called after release()"); |
66 | } |
67 | |
68 | if (pContext != NULL) *pContext = context; |
69 | return tv; |
70 | } |
71 | |
72 | JNITvContext::JNITvContext(JNIEnv *env, jobject weak_this, jclass clazz, const sp<TvClient> &tv) |
73 | { |
74 | mTvJObjectWeak = env->NewGlobalRef(weak_this); |
75 | mTvJClass = (jclass)env->NewGlobalRef(clazz); |
76 | mTv = tv; |
77 | ALOGD("tvjni----------------------JNITvContext::JNITvContext("); |
78 | mManualBufferMode = false; |
79 | mManualTvCallbackSet = false; |
80 | pSubBmp = NULL; |
81 | mSubMemBase = NULL; |
82 | //mExtParcel = parcelForJavaObject(env, ext_parcel); |
83 | } |
84 | |
85 | void JNITvContext::release() |
86 | { |
87 | ALOGD("release"); |
88 | Mutex::Autolock _l(mLock); |
89 | JNIEnv *env = AndroidRuntime::getJNIEnv(); |
90 | |
91 | if (mTvJObjectWeak != NULL) { |
92 | env->DeleteGlobalRef(mTvJObjectWeak); |
93 | mTvJObjectWeak = NULL; |
94 | } |
95 | if (mTvJClass != NULL) { |
96 | env->DeleteGlobalRef(mTvJClass); |
97 | mTvJClass = NULL; |
98 | } |
99 | if (pSubBmp != NULL) { |
100 | pSubBmp = NULL; |
101 | } |
102 | mTv.clear(); |
103 | } |
104 | |
105 | // connect to tv service |
106 | static void com_droidlogic_app_tv_TvControlManager_native_setup(JNIEnv *env, jobject thiz, jobject weak_this) |
107 | { |
108 | sp<TvClient> tv = TvClient::connect(); |
109 | |
110 | ALOGD("com_droidlogic_app_tv_TvControlManager_native_setup."); |
111 | |
112 | if (tv == NULL) { |
113 | jniThrowException(env, "java/lang/RuntimeException", "Fail to connect to tv service"); |
114 | return; |
115 | } |
116 | |
117 | // make sure tv amlogic is alive |
118 | if (tv->getStatus() != NO_ERROR) { |
119 | jniThrowException(env, "java/lang/RuntimeException", "Tv initialization failed!"); |
120 | return; |
121 | } |
122 | |
123 | jclass clazz = env->GetObjectClass(thiz); |
124 | if (clazz == NULL) { |
125 | jniThrowException(env, "java/lang/RuntimeException", "Can't find com/droidlogic/app/tv/TvControlManager!"); |
126 | return; |
127 | } |
128 | |
129 | sp<JNITvContext> context = new JNITvContext(env, weak_this, clazz, tv); |
130 | context->incStrong(thiz); |
131 | tv->setListener(context); |
132 | |
133 | env->SetIntField(thiz, fields.context, (int)context.get()); |
134 | } |
135 | |
136 | |
137 | static void com_droidlogic_app_tv_TvControlManager_release(JNIEnv *env, jobject thiz) |
138 | { |
139 | // TODO: Change to LOGE |
140 | JNITvContext *context = NULL; |
141 | sp<TvClient> tv; |
142 | { |
143 | Mutex::Autolock _l(sLock); |
144 | context = reinterpret_cast<JNITvContext *>(env->GetIntField(thiz, fields.context)); |
145 | |
146 | // Make sure we do not attempt to callback on a deleted Java object. |
147 | env->SetIntField(thiz, fields.context, 0); |
148 | } |
149 | |
150 | ALOGD("release tv"); |
151 | |
152 | // clean up if release has not been called before |
153 | if (context != NULL) { |
154 | tv = context->getTv(); |
155 | context->release(); |
156 | ALOGD("native_release: context=%p tv=%p", context, tv.get()); |
157 | |
158 | // clear callbacks |
159 | if (tv != NULL) { |
160 | //tv->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_NOOP); |
161 | tv->disconnect(); |
162 | } |
163 | |
164 | // remove context to prevent further Java access |
165 | context->decStrong(thiz); |
166 | } |
167 | } |
168 | |
169 | void JNITvContext::notify(int32_t msgType, const Parcel &p) |
170 | { |
171 | // VM pointer will be NULL if object is released |
172 | Mutex::Autolock _l(mLock); |
173 | if (mTvJObjectWeak == NULL) { |
174 | ALOGW("callback on dead tv object"); |
175 | return; |
176 | } |
177 | if (msgType == SUBTITLE_UPDATE_CALLBACK) { |
178 | if (pSubBmp) { |
179 | SkAutoLockPixels alp(*pSubBmp); |
180 | char *pDst = (char *) pSubBmp->getPixels(); |
181 | char *pBuf = (char *) mSubMemBase->pointer(); |
182 | for (int i = 0; i < pSubBmp->width() * pSubBmp->height() * 4; i++) { |
183 | pDst[i] = pBuf[i]; |
184 | } |
185 | pSubBmp->notifyPixelsChanged(); |
186 | } |
187 | } |
188 | |
189 | JNIEnv *env = AndroidRuntime::getJNIEnv(); |
190 | |
191 | jobject jParcel = createJavaParcelObject(env); |
192 | if (jParcel != NULL) { |
193 | Parcel *nativeParcel = parcelForJavaObject(env, jParcel); |
194 | nativeParcel->write(p.data(), p.dataSize()); |
195 | env->CallStaticVoidMethod(mTvJClass, fields.post_event, mTvJObjectWeak, msgType, jParcel); |
196 | env->DeleteLocalRef(jParcel); |
197 | } |
198 | } |
199 | |
200 | |
201 | void JNITvContext::addCallbackBuffer(JNIEnv *env, jbyteArray cbb) |
202 | { |
203 | if (cbb != NULL) { |
204 | Mutex::Autolock _l(mLock); |
205 | jbyteArray callbackBuffer = (jbyteArray)env->NewGlobalRef(cbb); |
206 | mCallbackBuffers.push(cbb); |
207 | ALOGD("Adding callback buffer to queue, %d total", mCallbackBuffers.size()); |
208 | } else { |
209 | ALOGE("Null byte array!"); |
210 | } |
211 | } |
212 | |
213 | static jint com_droidlogic_app_tv_TvControlManager_processCmd(JNIEnv *env, jobject thiz, jobject pObj, jobject rObj) |
214 | { |
215 | sp<TvClient> tv = get_native_tv(env, thiz, NULL); |
216 | if (tv == 0) return -1; |
217 | |
218 | Parcel *p = parcelForJavaObject(env, pObj); |
219 | //jclass clazz; |
220 | //clazz = env->FindClass("android/os/Parcel"); |
221 | //LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.Parcel"); |
222 | |
223 | |
224 | //jmethodID mConstructor = env->GetMethodID(clazz, "<init>", "(I)V"); |
225 | //jobject replayobj = env->NewObject(clazz, mConstructor, 0); |
226 | Parcel *r = parcelForJavaObject(env, rObj); |
227 | |
228 | |
229 | return tv->processCmd(*p, r); |
230 | //if ( != NO_ERROR) { |
231 | // jniThrowException(env, "java/lang/RuntimeException", "StartTv failed"); |
232 | // return -1; |
233 | // } |
234 | //return 0; |
235 | } |
236 | |
237 | static void com_droidlogic_app_tv_TvControlManager_addCallbackBuffer(JNIEnv *env, jobject thiz, jbyteArray bytes) |
238 | { |
239 | JNITvContext *context = reinterpret_cast<JNITvContext *>(env->GetIntField(thiz, fields.context)); |
240 | |
241 | ALOGD("addCallbackBuffer"); |
242 | if (context != NULL) { |
243 | context->addCallbackBuffer(env, bytes); |
244 | } |
245 | } |
246 | |
247 | static void com_droidlogic_app_tv_TvControlManager_reconnect(JNIEnv *env, jobject thiz) |
248 | { |
249 | sp<TvClient> tv = get_native_tv(env, thiz, NULL); |
250 | if (tv == 0) return; |
251 | |
252 | if (tv->reconnect() != NO_ERROR) { |
253 | jniThrowException(env, "java/io/IOException", "reconnect failed"); |
254 | return; |
255 | } |
256 | } |
257 | |
258 | static void com_droidlogic_app_tv_TvControlManager_lock(JNIEnv *env, jobject thiz) |
259 | { |
260 | sp<TvClient> tv = get_native_tv(env, thiz, NULL); |
261 | if (tv == 0) return; |
262 | |
263 | ALOGD("lock"); |
264 | |
265 | if (tv->lock() != NO_ERROR) { |
266 | jniThrowException(env, "java/lang/RuntimeException", "lock failed"); |
267 | } |
268 | } |
269 | |
270 | static void com_droidlogic_app_tv_TvControlManager_unlock(JNIEnv *env, jobject thiz) |
271 | { |
272 | sp<TvClient> tv = get_native_tv(env, thiz, NULL); |
273 | if (tv == 0) return; |
274 | |
275 | ALOGD("unlock"); |
276 | |
277 | if (tv->unlock() != NO_ERROR) { |
278 | jniThrowException(env, "java/lang/RuntimeException", "unlock failed"); |
279 | } |
280 | } |
281 | |
282 | static void com_droidlogic_app_tv_TvControlManager_create_subtitle_bitmap(JNIEnv *env, jobject thiz, jobject bmpobj) |
283 | { |
284 | ALOGD("create subtitle bmp"); |
285 | JNITvContext *context = reinterpret_cast<JNITvContext *>(env->GetIntField(thiz, fields.context)); |
286 | sp<TvClient> tv = get_native_tv(env, thiz, NULL); |
287 | if (tv == 0) return; |
288 | |
289 | //get skbitmap |
290 | jclass bmp_clazz; |
291 | jfieldID skbmp_fid; |
292 | jint hbmp; |
293 | bmp_clazz = env->FindClass("android/graphics/Bitmap"); |
294 | skbmp_fid = env->GetFieldID(bmp_clazz, "mNativeBitmap", "I"); |
295 | hbmp = env->GetIntField(bmpobj, skbmp_fid); |
296 | context->pSubBmp = reinterpret_cast<SkBitmap *>(hbmp); |
297 | env->DeleteLocalRef(bmp_clazz); |
298 | |
299 | //alloc share mem |
300 | sp<MemoryHeapBase> MemHeap = new MemoryHeapBase(context->pSubBmp->width()*context->pSubBmp->height() * 4, 0, "subtitle bmp"); |
301 | ALOGD("heap id = %d", MemHeap->getHeapID()); |
302 | if (MemHeap->getHeapID() < 0) { |
303 | return; |
304 | } |
305 | context->pSubBmp->lockPixels(); |
306 | context->mSubMemBase = new MemoryBase(MemHeap, 0, context->pSubBmp->width()*context->pSubBmp->height() * 4); |
307 | |
308 | |
309 | //send share mem to server |
310 | tv->createSubtitle(context->mSubMemBase); |
311 | return; |
312 | } |
313 | |
314 | static void com_droidlogic_app_tv_TvControlManager_create_video_frame_bitmap(JNIEnv *env, jobject thiz, jobject bmpobj, jint inputSourceMode, jint iCapVideoLayer ) |
315 | { |
316 | ALOGD("create video frame bmp"); |
317 | sp<TvClient> tv = get_native_tv(env, thiz, NULL); |
318 | if (tv == 0) return; |
319 | |
320 | //get skbitmap |
321 | jclass bmp_clazz; |
322 | jfieldID skbmp_fid; |
323 | jint hbmp; |
324 | bmp_clazz = env->FindClass("android/graphics/Bitmap"); |
325 | skbmp_fid = env->GetFieldID(bmp_clazz, "mNativeBitmap", "I"); |
326 | hbmp = env->GetIntField(bmpobj, skbmp_fid); |
327 | SkBitmap *pSkBmp = reinterpret_cast<SkBitmap *>(hbmp); |
328 | ALOGD("pSkBmp = %d", hbmp); |
329 | ALOGD("bmp width = %d height = %d", pSkBmp->width(), pSkBmp->height()); |
330 | env->DeleteLocalRef(bmp_clazz); |
331 | |
332 | //alloc share mem |
333 | sp<MemoryHeapBase> MemHeap = new MemoryHeapBase(1920 * 1080 * 4, 0, "video frame bmp"); |
334 | ALOGD("heap id = %d", MemHeap->getHeapID()); |
335 | if (MemHeap->getHeapID() < 0) { |
336 | return; |
337 | } |
338 | sp<MemoryBase> MemBase = new MemoryBase(MemHeap, 0, 1920 * 1080 * 4); |
339 | pSkBmp->setPixels(MemBase->pointer()); |
340 | |
341 | |
342 | //send share mem to server |
343 | tv->createVideoFrame(MemBase, inputSourceMode, iCapVideoLayer); |
344 | return; |
345 | } |
346 | |
347 | //------------------------------------------------- |
348 | |
349 | static JNINativeMethod camMethods[] = { |
350 | { |
351 | "native_setup", |
352 | "(Ljava/lang/Object;)V", |
353 | (void *)com_droidlogic_app_tv_TvControlManager_native_setup |
354 | }, |
355 | { |
356 | "native_release", |
357 | "()V", |
358 | (void *)com_droidlogic_app_tv_TvControlManager_release |
359 | }, |
360 | { |
361 | "processCmd", |
362 | "(Landroid/os/Parcel;Landroid/os/Parcel;)I", |
363 | (void *)com_droidlogic_app_tv_TvControlManager_processCmd |
364 | }, |
365 | { |
366 | "addCallbackBuffer", |
367 | "([B)V", |
368 | (void *)com_droidlogic_app_tv_TvControlManager_addCallbackBuffer |
369 | }, |
370 | { |
371 | "reconnect", |
372 | "()V", |
373 | (void *)com_droidlogic_app_tv_TvControlManager_reconnect |
374 | }, |
375 | { |
376 | "lock", |
377 | "()V", |
378 | (void *)com_droidlogic_app_tv_TvControlManager_lock |
379 | }, |
380 | { |
381 | "unlock", |
382 | "()V", |
383 | (void *)com_droidlogic_app_tv_TvControlManager_unlock |
384 | }, |
385 | { |
386 | "native_create_subtitle_bitmap", |
387 | "(Ljava/lang/Object;)V", |
388 | (void *)com_droidlogic_app_tv_TvControlManager_create_subtitle_bitmap |
389 | }, |
390 | { |
391 | "native_create_video_frame_bitmap", |
392 | "(Ljava/lang/Object;)V", |
393 | (void *)com_droidlogic_app_tv_TvControlManager_create_video_frame_bitmap |
394 | }, |
395 | |
396 | }; |
397 | |
398 | struct field { |
399 | const char *class_name; |
400 | const char *field_name; |
401 | const char *field_type; |
402 | jfieldID *jfield; |
403 | }; |
404 | |
405 | static int find_fields(JNIEnv *env, field *fields, int count) |
406 | { |
407 | for (int i = 0; i < count; i++) { |
408 | field *f = &fields[i]; |
409 | jclass clazz = env->FindClass(f->class_name); |
410 | if (clazz == NULL) { |
411 | ALOGE("Can't find %s", f->class_name); |
412 | return -1; |
413 | } |
414 | |
415 | jfieldID field = env->GetFieldID(clazz, f->field_name, f->field_type); |
416 | if (field == NULL) { |
417 | ALOGE("Can't find %s.%s", f->class_name, f->field_name); |
418 | return -1; |
419 | } |
420 | |
421 | *(f->jfield) = field; |
422 | } |
423 | |
424 | return 0; |
425 | } |
426 | |
427 | // Get all the required offsets in java class and register native functions |
428 | int register_com_droidlogic_app_tv_TvControlManager(JNIEnv *env) |
429 | { |
430 | field fields_to_find[] = { |
431 | { "com/droidlogic/app/tv/TvControlManager", "mNativeContext", "I", &fields.context } |
432 | }; |
433 | |
434 | ALOGD("register_com_droidlogic_app_tv_TvControlManager."); |
435 | |
436 | if (find_fields(env, fields_to_find, NELEM(fields_to_find)) < 0) |
437 | return -1; |
438 | |
439 | jclass clazz = env->FindClass("com/droidlogic/app/tv/TvControlManager"); |
440 | fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative", "(Ljava/lang/Object;ILandroid/os/Parcel;)V"); |
441 | if (fields.post_event == NULL) { |
442 | ALOGE("Can't find com/droidlogic/app/tv/TvControlManager.postEventFromNative"); |
443 | return -1; |
444 | } |
445 | |
446 | // Register native functions |
447 | return AndroidRuntime::registerNativeMethods(env, "com/droidlogic/app/tv/TvControlManager", camMethods, NELEM(camMethods)); |
448 | } |
449 | |
450 | |
451 | jint JNI_OnLoad(JavaVM *vm, void *reserved __unused) |
452 | { |
453 | JNIEnv *env = NULL; |
454 | jint result = -1; |
455 | |
456 | if (vm->GetEnv((void **) &env, JNI_VERSION_1_4) != JNI_OK) { |
457 | ALOGE("ERROR: GetEnv failed\n"); |
458 | goto bail; |
459 | } |
460 | assert(env != NULL); |
461 | |
462 | register_com_droidlogic_app_tv_TvControlManager(env); |
463 | |
464 | /* success -- return valid version number */ |
465 | result = JNI_VERSION_1_4; |
466 | bail: |
467 | return result; |
468 | } |
469 | |
470 |