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