1 /*
2 * Copyright (C) 2016 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 "BluetoothAvrcpServiceJni"
18
19 #define LOG_NDEBUG 0
20
21 #include "android_runtime/AndroidRuntime.h"
22 #include "com_android_bluetooth.h"
23 #include "hardware/bt_rc.h"
24 #include "utils/Log.h"
25
26 #include <string.h>
27
28 namespace android {
29 static jmethodID method_getRcFeatures;
30 static jmethodID method_getPlayStatus;
31 static jmethodID method_getElementAttr;
32 static jmethodID method_registerNotification;
33 static jmethodID method_volumeChangeCallback;
34 static jmethodID method_handlePassthroughCmd;
35 static jmethodID method_getFolderItemsCallback;
36 static jmethodID method_setAddressedPlayerCallback;
37
38 static jmethodID method_setBrowsedPlayerCallback;
39 static jmethodID method_changePathCallback;
40 static jmethodID method_searchCallback;
41 static jmethodID method_playItemCallback;
42 static jmethodID method_getItemAttrCallback;
43 static jmethodID method_addToPlayListCallback;
44 static jmethodID method_getTotalNumOfItemsCallback;
45
46 static const btrc_interface_t* sBluetoothAvrcpInterface = NULL;
47 static jobject mCallbacksObj = NULL;
48
49 /* Function declarations */
50 static bool copy_item_attributes(JNIEnv* env, jobject object,
51 btrc_folder_items_t* pitem,
52 jint* p_attributesIds,
53 jobjectArray attributesArray, int item_idx,
54 int attribCopiedIndex);
55
56 static bool copy_jstring(uint8_t* str, int maxBytes, jstring jstr, JNIEnv* env);
57
58 static void cleanup_items(btrc_folder_items_t* p_items, int numItems);
59
btavrcp_remote_features_callback(bt_bdaddr_t * bd_addr,btrc_remote_features_t features)60 static void btavrcp_remote_features_callback(bt_bdaddr_t* bd_addr,
61 btrc_remote_features_t features) {
62 CallbackEnv sCallbackEnv(__func__);
63 if (!sCallbackEnv.valid()) return;
64
65 if (!mCallbacksObj) {
66 ALOGE("%s: mCallbacksObj is null", __func__);
67 return;
68 }
69
70 ScopedLocalRef<jbyteArray> addr(
71 sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t)));
72 if (!addr.get()) {
73 ALOGE("Unable to allocate byte array for bd_addr");
74 return;
75 }
76
77 sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(bt_bdaddr_t),
78 (jbyte*)bd_addr);
79 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getRcFeatures, addr.get(),
80 (jint)features);
81 }
82
83 /** Callback for play status request */
btavrcp_get_play_status_callback(bt_bdaddr_t * bd_addr)84 static void btavrcp_get_play_status_callback(bt_bdaddr_t* bd_addr) {
85 CallbackEnv sCallbackEnv(__func__);
86 if (!sCallbackEnv.valid()) return;
87
88 if (!mCallbacksObj) {
89 ALOGE("%s: mCallbacksObj is null", __func__);
90 return;
91 }
92
93 ScopedLocalRef<jbyteArray> addr(
94 sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t)));
95 if (!addr.get()) {
96 ALOGE("Fail to new jbyteArray bd addr for get_play_status command");
97 return;
98 }
99
100 sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(bt_bdaddr_t),
101 (jbyte*)bd_addr);
102 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getPlayStatus, addr.get());
103 }
104
btavrcp_get_element_attr_callback(uint8_t num_attr,btrc_media_attr_t * p_attrs,bt_bdaddr_t * bd_addr)105 static void btavrcp_get_element_attr_callback(uint8_t num_attr,
106 btrc_media_attr_t* p_attrs,
107 bt_bdaddr_t* bd_addr) {
108 CallbackEnv sCallbackEnv(__func__);
109 if (!sCallbackEnv.valid()) return;
110
111 if (!mCallbacksObj) {
112 ALOGE("%s: mCallbacksObj is null", __func__);
113 return;
114 }
115
116 ScopedLocalRef<jbyteArray> addr(
117 sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t)));
118 if (!addr.get()) {
119 ALOGE("Fail to new jbyteArray bd addr for get_element_attr command");
120 return;
121 }
122
123 ScopedLocalRef<jintArray> attrs(
124 sCallbackEnv.get(), (jintArray)sCallbackEnv->NewIntArray(num_attr));
125 if (!attrs.get()) {
126 ALOGE("Fail to new jintArray for attrs");
127 return;
128 }
129
130 sCallbackEnv->SetIntArrayRegion(attrs.get(), 0, num_attr, (jint*)p_attrs);
131
132 sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(bt_bdaddr_t),
133 (jbyte*)bd_addr);
134 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getElementAttr, addr.get(),
135 (jbyte)num_attr, attrs.get());
136 }
137
btavrcp_register_notification_callback(btrc_event_id_t event_id,uint32_t param,bt_bdaddr_t * bd_addr)138 static void btavrcp_register_notification_callback(btrc_event_id_t event_id,
139 uint32_t param,
140 bt_bdaddr_t* bd_addr) {
141 CallbackEnv sCallbackEnv(__func__);
142 if (!sCallbackEnv.valid()) return;
143
144 if (!mCallbacksObj) {
145 ALOGE("%s: mCallbacksObj is null", __func__);
146 return;
147 }
148
149 ScopedLocalRef<jbyteArray> addr(
150 sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t)));
151 if (!addr.get()) {
152 ALOGE("Fail to new jbyteArray bd addr for register_notification command");
153 return;
154 }
155
156 sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(bt_bdaddr_t),
157 (jbyte*)bd_addr);
158 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_registerNotification,
159 addr.get(), (jint)event_id, (jint)param);
160 }
161
btavrcp_volume_change_callback(uint8_t volume,uint8_t ctype,bt_bdaddr_t * bd_addr)162 static void btavrcp_volume_change_callback(uint8_t volume, uint8_t ctype,
163 bt_bdaddr_t* bd_addr) {
164 CallbackEnv sCallbackEnv(__func__);
165 if (!sCallbackEnv.valid()) return;
166
167 if (!mCallbacksObj) {
168 ALOGE("%s: mCallbacksObj is null", __func__);
169 return;
170 }
171
172 ScopedLocalRef<jbyteArray> addr(
173 sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t)));
174 if (!addr.get()) {
175 ALOGE("Fail to new jbyteArray bd addr for volume_change command");
176 return;
177 }
178
179 sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(bt_bdaddr_t),
180 (jbyte*)bd_addr);
181
182 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_volumeChangeCallback,
183 addr.get(), (jint)volume, (jint)ctype);
184 }
185
btavrcp_passthrough_command_callback(int id,int pressed,bt_bdaddr_t * bd_addr)186 static void btavrcp_passthrough_command_callback(int id, int pressed,
187 bt_bdaddr_t* bd_addr) {
188 CallbackEnv sCallbackEnv(__func__);
189 if (!sCallbackEnv.valid()) return;
190
191 if (!mCallbacksObj) {
192 ALOGE("%s: mCallbacksObj is null", __func__);
193 return;
194 }
195
196 ScopedLocalRef<jbyteArray> addr(
197 sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t)));
198 if (!addr.get()) {
199 ALOGE("Fail to new jbyteArray bd addr for passthrough_command command");
200 return;
201 }
202 sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(bt_bdaddr_t),
203 (jbyte*)bd_addr);
204
205 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_handlePassthroughCmd,
206 addr.get(), (jint)id, (jint)pressed);
207 }
208
btavrcp_set_addressed_player_callback(uint16_t player_id,bt_bdaddr_t * bd_addr)209 static void btavrcp_set_addressed_player_callback(uint16_t player_id,
210 bt_bdaddr_t* bd_addr) {
211 CallbackEnv sCallbackEnv(__func__);
212 if (!sCallbackEnv.valid()) return;
213
214 if (!mCallbacksObj) {
215 ALOGE("%s: mCallbacksObj is null", __func__);
216 return;
217 }
218
219 ScopedLocalRef<jbyteArray> addr(
220 sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t)));
221 if (!addr.get()) {
222 ALOGE("Fail to new jbyteArray bd addr for set_addressed_player command");
223 return;
224 }
225
226 sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(bt_bdaddr_t),
227 (jbyte*)bd_addr);
228 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_setAddressedPlayerCallback,
229 addr.get(), (jint)player_id);
230 }
231
btavrcp_set_browsed_player_callback(uint16_t player_id,bt_bdaddr_t * bd_addr)232 static void btavrcp_set_browsed_player_callback(uint16_t player_id,
233 bt_bdaddr_t* bd_addr) {
234 CallbackEnv sCallbackEnv(__func__);
235 if (!sCallbackEnv.valid()) return;
236 if (!mCallbacksObj) {
237 ALOGE("%s: mCallbacksObj is null", __func__);
238 return;
239 }
240
241 ScopedLocalRef<jbyteArray> addr(
242 sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t)));
243 if (!addr.get()) {
244 ALOGE("Fail to new jbyteArray bd addr for set_browsed_player command");
245 return;
246 }
247 sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(bt_bdaddr_t),
248 (jbyte*)bd_addr);
249
250 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_setBrowsedPlayerCallback,
251 addr.get(), (jint)player_id);
252 }
253
btavrcp_get_folder_items_callback(uint8_t scope,uint32_t start_item,uint32_t end_item,uint8_t num_attr,uint32_t * p_attr_ids,bt_bdaddr_t * bd_addr)254 static void btavrcp_get_folder_items_callback(
255 uint8_t scope, uint32_t start_item, uint32_t end_item, uint8_t num_attr,
256 uint32_t* p_attr_ids, bt_bdaddr_t* bd_addr) {
257 CallbackEnv sCallbackEnv(__func__);
258 if (!sCallbackEnv.valid()) return;
259
260 if (!mCallbacksObj) {
261 ALOGE("%s: mCallbacksObj is null", __func__);
262 return;
263 }
264
265 ScopedLocalRef<jbyteArray> addr(
266 sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t)));
267 if (!addr.get()) {
268 ALOGE("Fail to new jbyteArray bd addr for get_folder_items command");
269 return;
270 }
271
272 uint32_t* puiAttr = (uint32_t*)p_attr_ids;
273 ScopedLocalRef<jintArray> attr_ids(sCallbackEnv.get(), NULL);
274 sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(bt_bdaddr_t),
275 (jbyte*)bd_addr);
276
277 /* check number of attributes requested by remote device */
278 if ((num_attr != BTRC_NUM_ATTR_ALL) && (num_attr != BTRC_NUM_ATTR_NONE)) {
279 /* allocate memory for attr_ids only if some attributes passed from below
280 * layer */
281 attr_ids.reset((jintArray)sCallbackEnv->NewIntArray(num_attr));
282 if (!attr_ids.get()) {
283 ALOGE("Fail to allocate new jintArray for attrs");
284 return;
285 }
286 sCallbackEnv->SetIntArrayRegion(attr_ids.get(), 0, num_attr,
287 (jint*)puiAttr);
288 }
289
290 sCallbackEnv->CallVoidMethod(
291 mCallbacksObj, method_getFolderItemsCallback, addr.get(), (jbyte)scope,
292 (jlong)start_item, (jlong)end_item, (jbyte)num_attr, attr_ids.get());
293 }
294
btavrcp_change_path_callback(uint8_t direction,uint8_t * folder_uid,bt_bdaddr_t * bd_addr)295 static void btavrcp_change_path_callback(uint8_t direction, uint8_t* folder_uid,
296 bt_bdaddr_t* bd_addr) {
297 CallbackEnv sCallbackEnv(__func__);
298 if (!sCallbackEnv.valid()) return;
299
300 if (!mCallbacksObj) {
301 ALOGE("%s: mCallbacksObj is null", __func__);
302 return;
303 }
304
305 ScopedLocalRef<jbyteArray> attrs(sCallbackEnv.get(),
306 sCallbackEnv->NewByteArray(BTRC_UID_SIZE));
307 if (!attrs.get()) {
308 ALOGE("Fail to new jintArray for attrs");
309 return;
310 }
311
312 ScopedLocalRef<jbyteArray> addr(
313 sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t)));
314 if (!addr.get()) {
315 ALOGE("Fail to new jbyteArray bd addr for change_path command");
316 return;
317 }
318
319 sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(bt_bdaddr_t),
320 (jbyte*)bd_addr);
321 sCallbackEnv->SetByteArrayRegion(
322 attrs.get(), 0, sizeof(uint8_t) * BTRC_UID_SIZE, (jbyte*)folder_uid);
323 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_changePathCallback,
324 addr.get(), (jbyte)direction, attrs.get());
325 }
326
btavrcp_get_item_attr_callback(uint8_t scope,uint8_t * uid,uint16_t uid_counter,uint8_t num_attr,btrc_media_attr_t * p_attrs,bt_bdaddr_t * bd_addr)327 static void btavrcp_get_item_attr_callback(uint8_t scope, uint8_t* uid,
328 uint16_t uid_counter,
329 uint8_t num_attr,
330 btrc_media_attr_t* p_attrs,
331 bt_bdaddr_t* bd_addr) {
332 CallbackEnv sCallbackEnv(__func__);
333 if (!sCallbackEnv.valid()) return;
334
335 if (!mCallbacksObj) {
336 ALOGE("%s: mCallbacksObj is null", __func__);
337 return;
338 }
339
340 ScopedLocalRef<jbyteArray> attr_uid(
341 sCallbackEnv.get(), sCallbackEnv->NewByteArray(BTRC_UID_SIZE));
342 if (!attr_uid.get()) {
343 ALOGE("Fail to new jintArray for attr_uid");
344 return;
345 }
346
347 ScopedLocalRef<jbyteArray> addr(
348 sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t)));
349 if (!addr.get()) {
350 ALOGE("Fail to new jbyteArray bd addr for get_item_attr command");
351 return;
352 }
353
354 ScopedLocalRef<jintArray> attrs(
355 sCallbackEnv.get(), (jintArray)sCallbackEnv->NewIntArray(num_attr));
356 if (!attrs.get()) {
357 ALOGE("Fail to new jintArray for attrs");
358 return;
359 }
360
361 sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(bt_bdaddr_t),
362 (jbyte*)bd_addr);
363 sCallbackEnv->SetIntArrayRegion(attrs.get(), 0, num_attr, (jint*)p_attrs);
364 sCallbackEnv->SetByteArrayRegion(
365 attr_uid.get(), 0, sizeof(uint8_t) * BTRC_UID_SIZE, (jbyte*)uid);
366
367 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getItemAttrCallback,
368 addr.get(), (jbyte)scope, attr_uid.get(),
369 (jint)uid_counter, (jbyte)num_attr, attrs.get());
370 }
371
btavrcp_play_item_callback(uint8_t scope,uint16_t uid_counter,uint8_t * uid,bt_bdaddr_t * bd_addr)372 static void btavrcp_play_item_callback(uint8_t scope, uint16_t uid_counter,
373 uint8_t* uid, bt_bdaddr_t* bd_addr) {
374 CallbackEnv sCallbackEnv(__func__);
375 if (!sCallbackEnv.valid()) return;
376 if (!mCallbacksObj) {
377 ALOGE("%s: mCallbacksObj is null", __func__);
378 return;
379 }
380
381 ScopedLocalRef<jbyteArray> attrs(sCallbackEnv.get(),
382 sCallbackEnv->NewByteArray(BTRC_UID_SIZE));
383 if (!attrs.get()) {
384 ALOGE("%s: Fail to new jByteArray attrs for play_item command", __func__);
385 return;
386 }
387
388 ScopedLocalRef<jbyteArray> addr(
389 sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t)));
390 if (!addr.get()) {
391 ALOGE("Fail to new jbyteArray bd addr for play_item command");
392 return;
393 }
394
395 sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(bt_bdaddr_t),
396 (jbyte*)bd_addr);
397 sCallbackEnv->SetByteArrayRegion(
398 attrs.get(), 0, sizeof(uint8_t) * BTRC_UID_SIZE, (jbyte*)uid);
399 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_playItemCallback,
400 addr.get(), (jbyte)scope, (jint)uid_counter,
401 attrs.get());
402 }
403
btavrcp_get_total_num_items_callback(uint8_t scope,bt_bdaddr_t * bd_addr)404 static void btavrcp_get_total_num_items_callback(uint8_t scope,
405 bt_bdaddr_t* bd_addr) {
406 CallbackEnv sCallbackEnv(__func__);
407 if (!sCallbackEnv.valid()) return;
408 if (!mCallbacksObj) {
409 ALOGE("%s: mCallbacksObj is null", __func__);
410 return;
411 }
412
413 ScopedLocalRef<jbyteArray> addr(
414 sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t)));
415 if (!addr.get()) {
416 ALOGE("Fail to new jbyteArray bd addr for get_total_num_items command");
417 return;
418 }
419
420 sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(bt_bdaddr_t),
421 (jbyte*)bd_addr);
422 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_getTotalNumOfItemsCallback,
423 addr.get(), (jbyte)scope);
424 }
425
btavrcp_search_callback(uint16_t charset_id,uint16_t str_len,uint8_t * p_str,bt_bdaddr_t * bd_addr)426 static void btavrcp_search_callback(uint16_t charset_id, uint16_t str_len,
427 uint8_t* p_str, bt_bdaddr_t* bd_addr) {
428 CallbackEnv sCallbackEnv(__func__);
429 if (!sCallbackEnv.valid()) return;
430 if (!mCallbacksObj) {
431 ALOGE("%s: mCallbacksObj is null", __func__);
432 return;
433 }
434
435 ScopedLocalRef<jbyteArray> attrs(sCallbackEnv.get(),
436 sCallbackEnv->NewByteArray(str_len));
437 if (!attrs.get()) {
438 ALOGE("Fail to new jintArray for attrs");
439 return;
440 }
441
442 ScopedLocalRef<jbyteArray> addr(
443 sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t)));
444 if (!addr.get()) {
445 ALOGE("Fail to new jbyteArray bd addr for search command");
446 return;
447 }
448
449 sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(bt_bdaddr_t),
450 (jbyte*)bd_addr);
451 sCallbackEnv->SetByteArrayRegion(attrs.get(), 0, str_len * sizeof(uint8_t),
452 (jbyte*)p_str);
453 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_searchCallback, addr.get(),
454 (jint)charset_id, attrs.get());
455 }
456
btavrcp_add_to_play_list_callback(uint8_t scope,uint8_t * uid,uint16_t uid_counter,bt_bdaddr_t * bd_addr)457 static void btavrcp_add_to_play_list_callback(uint8_t scope, uint8_t* uid,
458 uint16_t uid_counter,
459 bt_bdaddr_t* bd_addr) {
460 CallbackEnv sCallbackEnv(__func__);
461 if (!sCallbackEnv.valid()) return;
462 if (!mCallbacksObj) {
463 ALOGE("%s: mCallbacksObj is null", __func__);
464 return;
465 }
466
467 ScopedLocalRef<jbyteArray> addr(
468 sCallbackEnv.get(), sCallbackEnv->NewByteArray(sizeof(bt_bdaddr_t)));
469 if (!addr.get()) {
470 ALOGE("Fail to new jbyteArray bd addr for add_to_play_list command");
471 return;
472 }
473
474 ScopedLocalRef<jbyteArray> attrs(sCallbackEnv.get(),
475 sCallbackEnv->NewByteArray(BTRC_UID_SIZE));
476 if (!attrs.get()) {
477 ALOGE("Fail to new jByteArray for attrs");
478 return;
479 }
480
481 sCallbackEnv->SetByteArrayRegion(addr.get(), 0, sizeof(bt_bdaddr_t),
482 (jbyte*)bd_addr);
483 sCallbackEnv->SetByteArrayRegion(
484 attrs.get(), 0, sizeof(uint8_t) * BTRC_UID_SIZE, (jbyte*)uid);
485 sCallbackEnv->CallVoidMethod(mCallbacksObj, method_addToPlayListCallback,
486 addr.get(), (jbyte)scope, attrs.get(),
487 (jint)uid_counter);
488 }
489
490 static btrc_callbacks_t sBluetoothAvrcpCallbacks = {
491 sizeof(sBluetoothAvrcpCallbacks),
492 btavrcp_remote_features_callback,
493 btavrcp_get_play_status_callback,
494 NULL,
495 NULL,
496 NULL,
497 NULL,
498 NULL,
499 NULL,
500 btavrcp_get_element_attr_callback,
501 btavrcp_register_notification_callback,
502 btavrcp_volume_change_callback,
503 btavrcp_passthrough_command_callback,
504 btavrcp_set_addressed_player_callback,
505 btavrcp_set_browsed_player_callback,
506 btavrcp_get_folder_items_callback,
507 btavrcp_change_path_callback,
508 btavrcp_get_item_attr_callback,
509 btavrcp_play_item_callback,
510 btavrcp_get_total_num_items_callback,
511 btavrcp_search_callback,
512 btavrcp_add_to_play_list_callback,
513 };
514
classInitNative(JNIEnv * env,jclass clazz)515 static void classInitNative(JNIEnv* env, jclass clazz) {
516 method_getRcFeatures =
517 env->GetMethodID(clazz, "getRcFeaturesRequestFromNative", "([BI)V");
518 method_getPlayStatus =
519 env->GetMethodID(clazz, "getPlayStatusRequestFromNative", "([B)V");
520
521 method_getElementAttr =
522 env->GetMethodID(clazz, "getElementAttrRequestFromNative", "([BB[I)V");
523
524 method_registerNotification = env->GetMethodID(
525 clazz, "registerNotificationRequestFromNative", "([BII)V");
526
527 method_volumeChangeCallback =
528 env->GetMethodID(clazz, "volumeChangeRequestFromNative", "([BII)V");
529
530 method_handlePassthroughCmd = env->GetMethodID(
531 clazz, "handlePassthroughCmdRequestFromNative", "([BII)V");
532
533 method_setAddressedPlayerCallback =
534 env->GetMethodID(clazz, "setAddressedPlayerRequestFromNative", "([BI)V");
535
536 method_setBrowsedPlayerCallback =
537 env->GetMethodID(clazz, "setBrowsedPlayerRequestFromNative", "([BI)V");
538
539 method_getFolderItemsCallback =
540 env->GetMethodID(clazz, "getFolderItemsRequestFromNative", "([BBJJB[I)V");
541
542 method_changePathCallback =
543 env->GetMethodID(clazz, "changePathRequestFromNative", "([BB[B)V");
544
545 method_getItemAttrCallback =
546 env->GetMethodID(clazz, "getItemAttrRequestFromNative", "([BB[BIB[I)V");
547
548 method_playItemCallback =
549 env->GetMethodID(clazz, "playItemRequestFromNative", "([BBI[B)V");
550
551 method_getTotalNumOfItemsCallback =
552 env->GetMethodID(clazz, "getTotalNumOfItemsRequestFromNative", "([BB)V");
553
554 method_searchCallback =
555 env->GetMethodID(clazz, "searchRequestFromNative", "([BI[B)V");
556
557 method_addToPlayListCallback =
558 env->GetMethodID(clazz, "addToPlayListRequestFromNative", "([BB[BI)V");
559
560 ALOGI("%s: succeeds", __func__);
561 }
562
initNative(JNIEnv * env,jobject object)563 static void initNative(JNIEnv* env, jobject object) {
564 const bt_interface_t* btInf = getBluetoothInterface();
565 if (btInf == NULL) {
566 ALOGE("Bluetooth module is not loaded");
567 return;
568 }
569
570 if (sBluetoothAvrcpInterface != NULL) {
571 ALOGW("Cleaning up Avrcp Interface before initializing...");
572 sBluetoothAvrcpInterface->cleanup();
573 sBluetoothAvrcpInterface = NULL;
574 }
575
576 if (mCallbacksObj != NULL) {
577 ALOGW("Cleaning up Avrcp callback object");
578 env->DeleteGlobalRef(mCallbacksObj);
579 mCallbacksObj = NULL;
580 }
581
582 sBluetoothAvrcpInterface =
583 (btrc_interface_t*)btInf->get_profile_interface(BT_PROFILE_AV_RC_ID);
584 if (sBluetoothAvrcpInterface == NULL) {
585 ALOGE("Failed to get Bluetooth Avrcp Interface");
586 return;
587 }
588
589 bt_status_t status =
590 sBluetoothAvrcpInterface->init(&sBluetoothAvrcpCallbacks);
591 if (status != BT_STATUS_SUCCESS) {
592 ALOGE("Failed to initialize Bluetooth Avrcp, status: %d", status);
593 sBluetoothAvrcpInterface = NULL;
594 return;
595 }
596
597 mCallbacksObj = env->NewGlobalRef(object);
598 }
599
cleanupNative(JNIEnv * env,jobject object)600 static void cleanupNative(JNIEnv* env, jobject object) {
601 const bt_interface_t* btInf = getBluetoothInterface();
602 if (btInf == NULL) {
603 ALOGE("Bluetooth module is not loaded");
604 return;
605 }
606
607 if (sBluetoothAvrcpInterface != NULL) {
608 sBluetoothAvrcpInterface->cleanup();
609 sBluetoothAvrcpInterface = NULL;
610 }
611
612 if (mCallbacksObj != NULL) {
613 env->DeleteGlobalRef(mCallbacksObj);
614 mCallbacksObj = NULL;
615 }
616 }
617
getPlayStatusRspNative(JNIEnv * env,jobject object,jbyteArray address,jint playStatus,jint songLen,jint songPos)618 static jboolean getPlayStatusRspNative(JNIEnv* env, jobject object,
619 jbyteArray address, jint playStatus,
620 jint songLen, jint songPos) {
621 if (!sBluetoothAvrcpInterface) {
622 ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
623 return JNI_FALSE;
624 }
625
626 jbyte* addr = env->GetByteArrayElements(address, NULL);
627 if (!addr) {
628 jniThrowIOException(env, EINVAL);
629 return JNI_FALSE;
630 }
631
632 bt_status_t status = sBluetoothAvrcpInterface->get_play_status_rsp(
633 (bt_bdaddr_t*)addr, (btrc_play_status_t)playStatus, songLen, songPos);
634 if (status != BT_STATUS_SUCCESS) {
635 ALOGE("Failed get_play_status_rsp, status: %d", status);
636 }
637 env->ReleaseByteArrayElements(address, addr, 0);
638 return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
639 }
640
getElementAttrRspNative(JNIEnv * env,jobject object,jbyteArray address,jbyte numAttr,jintArray attrIds,jobjectArray textArray)641 static jboolean getElementAttrRspNative(JNIEnv* env, jobject object,
642 jbyteArray address, jbyte numAttr,
643 jintArray attrIds,
644 jobjectArray textArray) {
645 if (!sBluetoothAvrcpInterface) {
646 ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
647 return JNI_FALSE;
648 }
649
650 if (numAttr > BTRC_MAX_ELEM_ATTR_SIZE) {
651 ALOGE("get_element_attr_rsp: number of attributes exceed maximum");
652 return JNI_FALSE;
653 }
654
655 jbyte* addr = env->GetByteArrayElements(address, NULL);
656 if (!addr) {
657 jniThrowIOException(env, EINVAL);
658 return JNI_FALSE;
659 }
660
661 btrc_element_attr_val_t* pAttrs = new btrc_element_attr_val_t[numAttr];
662 if (!pAttrs) {
663 ALOGE("get_element_attr_rsp: not have enough memeory");
664 env->ReleaseByteArrayElements(address, addr, 0);
665 return JNI_FALSE;
666 }
667
668 jint* attr = env->GetIntArrayElements(attrIds, NULL);
669 if (!attr) {
670 delete[] pAttrs;
671 jniThrowIOException(env, EINVAL);
672 env->ReleaseByteArrayElements(address, addr, 0);
673 return JNI_FALSE;
674 }
675
676 int attr_cnt;
677 for (attr_cnt = 0; attr_cnt < numAttr; ++attr_cnt) {
678 pAttrs[attr_cnt].attr_id = attr[attr_cnt];
679 ScopedLocalRef<jstring> text(
680 env, (jstring)env->GetObjectArrayElement(textArray, attr_cnt));
681
682 if (!copy_jstring(pAttrs[attr_cnt].text, BTRC_MAX_ATTR_STR_LEN, text.get(),
683 env)) {
684 break;
685 }
686 }
687
688 if (attr_cnt < numAttr) {
689 delete[] pAttrs;
690 env->ReleaseIntArrayElements(attrIds, attr, 0);
691 ALOGE("%s: Failed to copy attributes", __func__);
692 return JNI_FALSE;
693 }
694
695 bt_bdaddr_t* btAddr = (bt_bdaddr_t*)addr;
696 bt_status_t status =
697 sBluetoothAvrcpInterface->get_element_attr_rsp(btAddr, numAttr, pAttrs);
698 if (status != BT_STATUS_SUCCESS) {
699 ALOGE("Failed get_element_attr_rsp, status: %d", status);
700 }
701
702 delete[] pAttrs;
703 env->ReleaseIntArrayElements(attrIds, attr, 0);
704 env->ReleaseByteArrayElements(address, addr, 0);
705 return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
706 }
707
getItemAttrRspNative(JNIEnv * env,jobject object,jbyteArray address,jint rspStatus,jbyte numAttr,jintArray attrIds,jobjectArray textArray)708 static jboolean getItemAttrRspNative(JNIEnv* env, jobject object,
709 jbyteArray address, jint rspStatus,
710 jbyte numAttr, jintArray attrIds,
711 jobjectArray textArray) {
712 if (!sBluetoothAvrcpInterface) {
713 ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
714 return JNI_FALSE;
715 }
716
717 jbyte* addr = env->GetByteArrayElements(address, NULL);
718 if (!addr) {
719 jniThrowIOException(env, EINVAL);
720 return JNI_FALSE;
721 }
722
723 if (numAttr > BTRC_MAX_ELEM_ATTR_SIZE) {
724 ALOGE("get_element_attr_rsp: number of attributes exceed maximum");
725 return JNI_FALSE;
726 }
727
728 btrc_element_attr_val_t* pAttrs = new btrc_element_attr_val_t[numAttr];
729 if (!pAttrs) {
730 ALOGE("%s: not have enough memory", __func__);
731 env->ReleaseByteArrayElements(address, addr, 0);
732 return JNI_FALSE;
733 }
734
735 jint* attr = NULL;
736 if (attrIds != NULL) {
737 attr = env->GetIntArrayElements(attrIds, NULL);
738 if (!attr) {
739 delete[] pAttrs;
740 jniThrowIOException(env, EINVAL);
741 env->ReleaseByteArrayElements(address, addr, 0);
742 return JNI_FALSE;
743 }
744 }
745
746 for (int attr_cnt = 0; attr_cnt < numAttr; ++attr_cnt) {
747 pAttrs[attr_cnt].attr_id = attr[attr_cnt];
748 ScopedLocalRef<jstring> text(
749 env, (jstring)env->GetObjectArrayElement(textArray, attr_cnt));
750
751 if (!copy_jstring(pAttrs[attr_cnt].text, BTRC_MAX_ATTR_STR_LEN, text.get(),
752 env)) {
753 rspStatus = BTRC_STS_INTERNAL_ERR;
754 ALOGE("%s: Failed to copy attributes", __func__);
755 break;
756 }
757 }
758
759 bt_bdaddr_t* btAddr = (bt_bdaddr_t*)addr;
760 bt_status_t status = sBluetoothAvrcpInterface->get_item_attr_rsp(
761 btAddr, (btrc_status_t)rspStatus, numAttr, pAttrs);
762 if (status != BT_STATUS_SUCCESS)
763 ALOGE("Failed get_item_attr_rsp, status: %d", status);
764
765 if (pAttrs) delete[] pAttrs;
766 if (attr) env->ReleaseIntArrayElements(attrIds, attr, 0);
767 env->ReleaseByteArrayElements(address, addr, 0);
768
769 return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
770 }
771
registerNotificationRspPlayStatusNative(JNIEnv * env,jobject object,jint type,jint playStatus)772 static jboolean registerNotificationRspPlayStatusNative(JNIEnv* env,
773 jobject object,
774 jint type,
775 jint playStatus) {
776 if (!sBluetoothAvrcpInterface) {
777 ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
778 return JNI_FALSE;
779 }
780
781 btrc_register_notification_t param;
782 param.play_status = (btrc_play_status_t)playStatus;
783
784 bt_status_t status = sBluetoothAvrcpInterface->register_notification_rsp(
785 BTRC_EVT_PLAY_STATUS_CHANGED, (btrc_notification_type_t)type, ¶m);
786 if (status != BT_STATUS_SUCCESS) {
787 ALOGE("Failed register_notification_rsp play status, status: %d", status);
788 }
789
790 return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
791 }
792
registerNotificationRspTrackChangeNative(JNIEnv * env,jobject object,jint type,jbyteArray track)793 static jboolean registerNotificationRspTrackChangeNative(JNIEnv* env,
794 jobject object,
795 jint type,
796 jbyteArray track) {
797 if (!sBluetoothAvrcpInterface) {
798 ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
799 return JNI_FALSE;
800 }
801
802 jbyte* trk = env->GetByteArrayElements(track, NULL);
803 if (!trk) {
804 jniThrowIOException(env, EINVAL);
805 return JNI_FALSE;
806 }
807
808 btrc_register_notification_t param;
809 uint64_t uid = 0;
810 for (int uid_idx = 0; uid_idx < BTRC_UID_SIZE; ++uid_idx) {
811 param.track[uid_idx] = trk[uid_idx];
812 uid = uid + (trk[uid_idx] << (BTRC_UID_SIZE - 1 - uid_idx));
813 }
814
815 ALOGV("%s: Sending track change notification: %d -> %llu", __func__, type,
816 uid);
817
818 bt_status_t status = sBluetoothAvrcpInterface->register_notification_rsp(
819 BTRC_EVT_TRACK_CHANGE, (btrc_notification_type_t)type, ¶m);
820 if (status != BT_STATUS_SUCCESS) {
821 ALOGE("Failed register_notification_rsp track change, status: %d", status);
822 }
823
824 env->ReleaseByteArrayElements(track, trk, 0);
825 return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
826 }
827
registerNotificationRspPlayPosNative(JNIEnv * env,jobject object,jint type,jint playPos)828 static jboolean registerNotificationRspPlayPosNative(JNIEnv* env,
829 jobject object, jint type,
830 jint playPos) {
831 if (!sBluetoothAvrcpInterface) {
832 ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
833 return JNI_FALSE;
834 }
835
836 btrc_register_notification_t param;
837 param.song_pos = (uint32_t)playPos;
838
839 bt_status_t status = sBluetoothAvrcpInterface->register_notification_rsp(
840 BTRC_EVT_PLAY_POS_CHANGED, (btrc_notification_type_t)type, ¶m);
841 if (status != BT_STATUS_SUCCESS) {
842 ALOGE("Failed register_notification_rsp play position, status: %d", status);
843 }
844
845 return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
846 }
847
registerNotificationRspNowPlayingChangedNative(JNIEnv * env,jobject object,jint type)848 static jboolean registerNotificationRspNowPlayingChangedNative(JNIEnv* env,
849 jobject object,
850 jint type) {
851 if (!sBluetoothAvrcpInterface) {
852 ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
853 return JNI_FALSE;
854 }
855
856 btrc_register_notification_t param;
857 bt_status_t status = sBluetoothAvrcpInterface->register_notification_rsp(
858 BTRC_EVT_NOW_PLAYING_CONTENT_CHANGED, (btrc_notification_type_t)type,
859 ¶m);
860 if (status != BT_STATUS_SUCCESS) {
861 ALOGE("Failed register_notification_rsp, nowPlaying Content status: %d",
862 status);
863 }
864 return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
865 }
866
registerNotificationRspUIDsChangedNative(JNIEnv * env,jobject object,jint type,jint uidCounter)867 static jboolean registerNotificationRspUIDsChangedNative(JNIEnv* env,
868 jobject object,
869 jint type,
870 jint uidCounter) {
871 if (!sBluetoothAvrcpInterface) {
872 ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
873 return JNI_FALSE;
874 }
875
876 btrc_register_notification_t param;
877 param.uids_changed.uid_counter = (uint16_t)uidCounter;
878
879 bt_status_t status = sBluetoothAvrcpInterface->register_notification_rsp(
880 BTRC_EVT_UIDS_CHANGED, (btrc_notification_type_t)type, ¶m);
881 if (status != BT_STATUS_SUCCESS) {
882 ALOGE("Failed register_notification_rsp, uids changed status: %d", status);
883 }
884
885 return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
886 }
887
registerNotificationRspAddrPlayerChangedNative(JNIEnv * env,jobject object,jint type,jint playerId,jint uidCounter)888 static jboolean registerNotificationRspAddrPlayerChangedNative(
889 JNIEnv* env, jobject object, jint type, jint playerId, jint uidCounter) {
890 if (!sBluetoothAvrcpInterface) {
891 ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
892 return JNI_FALSE;
893 }
894
895 btrc_register_notification_t param;
896 param.addr_player_changed.player_id = (uint16_t)playerId;
897 param.addr_player_changed.uid_counter = (uint16_t)uidCounter;
898
899 bt_status_t status = sBluetoothAvrcpInterface->register_notification_rsp(
900 BTRC_EVT_ADDR_PLAYER_CHANGE, (btrc_notification_type_t)type, ¶m);
901 if (status != BT_STATUS_SUCCESS) {
902 ALOGE("Failed register_notification_rsp address player changed status: %d",
903 status);
904 }
905
906 return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
907 }
908
registerNotificationRspAvalPlayerChangedNative(JNIEnv * env,jobject object,jint type)909 static jboolean registerNotificationRspAvalPlayerChangedNative(JNIEnv* env,
910 jobject object,
911 jint type) {
912 if (!sBluetoothAvrcpInterface) {
913 ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
914 return JNI_FALSE;
915 }
916
917 btrc_register_notification_t param;
918 bt_status_t status = sBluetoothAvrcpInterface->register_notification_rsp(
919 BTRC_EVT_AVAL_PLAYER_CHANGE, (btrc_notification_type_t)type, ¶m);
920 if (status != BT_STATUS_SUCCESS) {
921 ALOGE(
922 "Failed register_notification_rsp available player changed status, "
923 "status: %d",
924 status);
925 }
926
927 return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
928 }
929
setVolumeNative(JNIEnv * env,jobject object,jint volume)930 static jboolean setVolumeNative(JNIEnv* env, jobject object, jint volume) {
931 if (!sBluetoothAvrcpInterface) {
932 ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
933 return JNI_FALSE;
934 }
935
936 bt_status_t status = sBluetoothAvrcpInterface->set_volume((uint8_t)volume);
937 if (status != BT_STATUS_SUCCESS) {
938 ALOGE("Failed set_volume, status: %d", status);
939 }
940
941 return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
942 }
943
944 /* native response for scope as Media player */
mediaPlayerListRspNative(JNIEnv * env,jobject object,jbyteArray address,jint rspStatus,jint uidCounter,jbyte itemType,jint numItems,jintArray playerIds,jbyteArray playerTypes,jintArray playerSubtypes,jbyteArray playStatusValues,jshortArray featureBitmask,jobjectArray textArray)945 static jboolean mediaPlayerListRspNative(
946 JNIEnv* env, jobject object, jbyteArray address, jint rspStatus,
947 jint uidCounter, jbyte itemType, jint numItems, jintArray playerIds,
948 jbyteArray playerTypes, jintArray playerSubtypes,
949 jbyteArray playStatusValues, jshortArray featureBitmask,
950 jobjectArray textArray) {
951 if (!sBluetoothAvrcpInterface) {
952 ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
953 return JNI_FALSE;
954 }
955
956 jbyte* addr = env->GetByteArrayElements(address, NULL);
957 if (!addr) {
958 jniThrowIOException(env, EINVAL);
959 return JNI_FALSE;
960 }
961
962 jbyte *p_playerTypes = NULL, *p_PlayStatusValues = NULL;
963 jshort* p_FeatBitMaskValues = NULL;
964 jint *p_playerIds = NULL, *p_playerSubTypes = NULL;
965 btrc_folder_items_t* p_items = NULL;
966 if (rspStatus == BTRC_STS_NO_ERROR) {
967 /* allocate memory */
968 p_playerIds = env->GetIntArrayElements(playerIds, NULL);
969 p_playerTypes = env->GetByteArrayElements(playerTypes, NULL);
970 p_playerSubTypes = env->GetIntArrayElements(playerSubtypes, NULL);
971 p_PlayStatusValues = env->GetByteArrayElements(playStatusValues, NULL);
972 p_FeatBitMaskValues = env->GetShortArrayElements(featureBitmask, NULL);
973 p_items = new btrc_folder_items_t[numItems];
974 /* deallocate memory and return if allocation failed */
975 if (!p_playerIds || !p_playerTypes || !p_playerSubTypes ||
976 !p_PlayStatusValues || !p_FeatBitMaskValues || !p_items) {
977 if (p_playerIds) env->ReleaseIntArrayElements(playerIds, p_playerIds, 0);
978 if (p_playerTypes)
979 env->ReleaseByteArrayElements(playerTypes, p_playerTypes, 0);
980 if (p_playerSubTypes)
981 env->ReleaseIntArrayElements(playerSubtypes, p_playerSubTypes, 0);
982 if (p_PlayStatusValues)
983 env->ReleaseByteArrayElements(playStatusValues, p_PlayStatusValues, 0);
984 if (p_FeatBitMaskValues)
985 env->ReleaseShortArrayElements(featureBitmask, p_FeatBitMaskValues, 0);
986 if (p_items) delete[] p_items;
987
988 jniThrowIOException(env, EINVAL);
989 ALOGE("%s: not have enough memory", __func__);
990 return JNI_FALSE;
991 }
992
993 p_items->item_type = (uint8_t)itemType;
994
995 /* copy list of media players along with other parameters */
996 int itemIdx;
997 for (itemIdx = 0; itemIdx < numItems; ++itemIdx) {
998 p_items[itemIdx].player.player_id = p_playerIds[itemIdx];
999 p_items[itemIdx].player.major_type = p_playerTypes[itemIdx];
1000 p_items[itemIdx].player.sub_type = p_playerSubTypes[itemIdx];
1001 p_items[itemIdx].player.play_status = p_PlayStatusValues[itemIdx];
1002 p_items[itemIdx].player.charset_id = BTRC_CHARSET_ID_UTF8;
1003
1004 ScopedLocalRef<jstring> text(
1005 env, (jstring)env->GetObjectArrayElement(textArray, itemIdx));
1006 /* copy player name */
1007 if (!copy_jstring(p_items[itemIdx].player.name, BTRC_MAX_ATTR_STR_LEN,
1008 text.get(), env))
1009 break;
1010
1011 /* Feature bit mask is 128-bit value each */
1012 for (int InnCnt = 0; InnCnt < 16; InnCnt++) {
1013 p_items[itemIdx].player.features[InnCnt] =
1014 (uint8_t)p_FeatBitMaskValues[(itemIdx * 16) + InnCnt];
1015 }
1016 }
1017
1018 /* failed to copy list of media players */
1019 if (itemIdx < numItems) {
1020 rspStatus = BTRC_STS_INTERNAL_ERR;
1021 ALOGE("%s: Failed to copy Media player attributes", __func__);
1022 }
1023 }
1024
1025 bt_bdaddr_t* btAddr = (bt_bdaddr_t*)addr;
1026 bt_status_t status = sBluetoothAvrcpInterface->get_folder_items_list_rsp(
1027 btAddr, (btrc_status_t)rspStatus, uidCounter, numItems, p_items);
1028 if (status != BT_STATUS_SUCCESS) {
1029 ALOGE("Failed get_folder_items_list_rsp, status: %d", status);
1030 }
1031
1032 /* release allocated memory */
1033 if (p_items) delete[] p_items;
1034 if (p_playerTypes)
1035 env->ReleaseByteArrayElements(playerTypes, p_playerTypes, 0);
1036 if (p_playerSubTypes)
1037 env->ReleaseIntArrayElements(playerSubtypes, p_playerSubTypes, 0);
1038 if (p_PlayStatusValues)
1039 env->ReleaseByteArrayElements(playStatusValues, p_PlayStatusValues, 0);
1040 if (p_FeatBitMaskValues) {
1041 env->ReleaseShortArrayElements(featureBitmask, p_FeatBitMaskValues, 0);
1042 }
1043 env->ReleaseByteArrayElements(address, addr, 0);
1044
1045 return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
1046 }
1047
getFolderItemsRspNative(JNIEnv * env,jobject object,jbyteArray address,jint rspStatus,jshort uidCounter,jbyte scope,jint numItems,jbyteArray folderType,jbyteArray playable,jbyteArray itemType,jbyteArray itemUidArray,jobjectArray displayNameArray,jintArray numAttrs,jintArray attributesIds,jobjectArray attributesArray)1048 static jboolean getFolderItemsRspNative(
1049 JNIEnv* env, jobject object, jbyteArray address, jint rspStatus,
1050 jshort uidCounter, jbyte scope, jint numItems, jbyteArray folderType,
1051 jbyteArray playable, jbyteArray itemType, jbyteArray itemUidArray,
1052 jobjectArray displayNameArray, jintArray numAttrs, jintArray attributesIds,
1053 jobjectArray attributesArray) {
1054 if (!sBluetoothAvrcpInterface) {
1055 ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
1056 return JNI_FALSE;
1057 }
1058
1059 jbyte* addr = env->GetByteArrayElements(address, NULL);
1060 if (!addr) {
1061 jniThrowIOException(env, EINVAL);
1062 return JNI_FALSE;
1063 }
1064
1065 jbyte *p_playable = NULL, *p_item_uid = NULL;
1066 jbyte* p_item_types = NULL; /* Folder or Media Item */
1067 jint* p_attributesIds = NULL;
1068 jbyte* p_folder_types =
1069 NULL; /* Folder properties like Album/Genre/Artists etc */
1070 jint* p_num_attrs = NULL;
1071 btrc_folder_items_t* p_items = NULL;
1072 /* none of the parameters should be null when no error */
1073 if (rspStatus == BTRC_STS_NO_ERROR) {
1074 /* allocate memory to each rsp item */
1075 if (folderType != NULL)
1076 p_folder_types = env->GetByteArrayElements(folderType, NULL);
1077 if (playable != NULL)
1078 p_playable = env->GetByteArrayElements(playable, NULL);
1079 if (itemType != NULL)
1080 p_item_types = env->GetByteArrayElements(itemType, NULL);
1081 if (NULL != numAttrs)
1082 p_num_attrs = env->GetIntArrayElements(numAttrs, NULL);
1083 if (NULL != attributesIds)
1084 p_attributesIds = env->GetIntArrayElements(attributesIds, NULL);
1085 if (itemUidArray != NULL)
1086 p_item_uid = (jbyte*)env->GetByteArrayElements(itemUidArray, NULL);
1087
1088 p_items = new btrc_folder_items_t[numItems];
1089
1090 /* if memory alloc failed, release memory */
1091 if (p_items && p_folder_types && p_playable && p_item_types && p_item_uid &&
1092 /* attributes can be null if remote requests 0 attributes */
1093 ((numAttrs != NULL && p_num_attrs) || (!numAttrs && !p_num_attrs)) &&
1094 ((attributesIds != NULL && p_attributesIds) ||
1095 (!attributesIds && !p_attributesIds))) {
1096 memset(p_items, 0, sizeof(btrc_folder_items_t) * numItems);
1097 if (scope == BTRC_SCOPE_FILE_SYSTEM || scope == BTRC_SCOPE_SEARCH ||
1098 scope == BTRC_SCOPE_NOW_PLAYING) {
1099 int attribCopiedIndex = 0;
1100 for (int item_idx = 0; item_idx < numItems; item_idx++) {
1101 if (BTRC_ITEM_FOLDER == p_item_types[item_idx]) {
1102 btrc_folder_items_t* pitem = &p_items[item_idx];
1103
1104 memcpy(pitem->folder.uid, p_item_uid + item_idx * BTRC_UID_SIZE,
1105 BTRC_UID_SIZE);
1106 pitem->item_type = (uint8_t)BTRC_ITEM_FOLDER;
1107 pitem->folder.charset_id = BTRC_CHARSET_ID_UTF8;
1108 pitem->folder.type = p_folder_types[item_idx];
1109 pitem->folder.playable = p_playable[item_idx];
1110
1111 ScopedLocalRef<jstring> text(
1112 env, (jstring)env->GetObjectArrayElement(displayNameArray,
1113 item_idx));
1114 if (!copy_jstring(pitem->folder.name, BTRC_MAX_ATTR_STR_LEN,
1115 text.get(), env)) {
1116 rspStatus = BTRC_STS_INTERNAL_ERR;
1117 ALOGE("%s: failed to copy display name of folder item", __func__);
1118 break;
1119 }
1120 } else if (BTRC_ITEM_MEDIA == p_item_types[item_idx]) {
1121 btrc_folder_items_t* pitem = &p_items[item_idx];
1122 memcpy(pitem->media.uid, p_item_uid + item_idx * BTRC_UID_SIZE,
1123 BTRC_UID_SIZE);
1124
1125 pitem->item_type = (uint8_t)BTRC_ITEM_MEDIA;
1126 pitem->media.charset_id = BTRC_CHARSET_ID_UTF8;
1127 pitem->media.type = BTRC_MEDIA_TYPE_AUDIO;
1128 pitem->media.num_attrs =
1129 (p_num_attrs != NULL) ? p_num_attrs[item_idx] : 0;
1130
1131 ScopedLocalRef<jstring> text(
1132 env, (jstring)env->GetObjectArrayElement(displayNameArray,
1133 item_idx));
1134 if (!copy_jstring(pitem->media.name, BTRC_MAX_ATTR_STR_LEN,
1135 text.get(), env)) {
1136 rspStatus = BTRC_STS_INTERNAL_ERR;
1137 ALOGE("%s: failed to copy display name of media item", __func__);
1138 break;
1139 }
1140
1141 /* copy item attributes */
1142 if (!copy_item_attributes(env, object, pitem, p_attributesIds,
1143 attributesArray, item_idx,
1144 attribCopiedIndex)) {
1145 ALOGE("%s: error in copying attributes of item = %s", __func__,
1146 pitem->media.name);
1147 rspStatus = BTRC_STS_INTERNAL_ERR;
1148 break;
1149 }
1150 attribCopiedIndex += pitem->media.num_attrs;
1151 }
1152 }
1153 }
1154 } else {
1155 rspStatus = BTRC_STS_INTERNAL_ERR;
1156 ALOGE("%s: unable to allocate memory", __func__);
1157 }
1158 }
1159
1160 bt_bdaddr_t* btAddr = (bt_bdaddr_t*)addr;
1161 bt_status_t status = sBluetoothAvrcpInterface->get_folder_items_list_rsp(
1162 btAddr, (btrc_status_t)rspStatus, uidCounter, numItems, p_items);
1163 if (status != BT_STATUS_SUCCESS)
1164 ALOGE("Failed get_folder_items_list_rsp, status: %d", status);
1165
1166 /* Release allocated memory for all attributes in each media item */
1167 if (p_items) cleanup_items(p_items, numItems);
1168
1169 /* Release allocated memory */
1170 if (p_folder_types)
1171 env->ReleaseByteArrayElements(folderType, p_folder_types, 0);
1172 if (p_playable) env->ReleaseByteArrayElements(playable, p_playable, 0);
1173 if (p_item_types) env->ReleaseByteArrayElements(itemType, p_item_types, 0);
1174 if (p_num_attrs) env->ReleaseIntArrayElements(numAttrs, p_num_attrs, 0);
1175 if (p_attributesIds)
1176 env->ReleaseIntArrayElements(attributesIds, p_attributesIds, 0);
1177 if (p_item_uid) env->ReleaseByteArrayElements(itemUidArray, p_item_uid, 0);
1178 if (p_items) delete[] p_items;
1179 env->ReleaseByteArrayElements(address, addr, 0);
1180
1181 return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
1182 }
1183
setAddressedPlayerRspNative(JNIEnv * env,jobject object,jbyteArray address,jint rspStatus)1184 static jboolean setAddressedPlayerRspNative(JNIEnv* env, jobject object,
1185 jbyteArray address,
1186 jint rspStatus) {
1187 if (!sBluetoothAvrcpInterface) {
1188 ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
1189 return JNI_FALSE;
1190 }
1191
1192 jbyte* addr = env->GetByteArrayElements(address, NULL);
1193 if (!addr) {
1194 jniThrowIOException(env, EINVAL);
1195 return JNI_FALSE;
1196 }
1197
1198 bt_bdaddr_t* btAddr = (bt_bdaddr_t*)addr;
1199 bt_status_t status = sBluetoothAvrcpInterface->set_addressed_player_rsp(
1200 btAddr, (btrc_status_t)rspStatus);
1201 if (status != BT_STATUS_SUCCESS) {
1202 ALOGE("Failed set_addressed_player_rsp, status: %d", status);
1203 }
1204 env->ReleaseByteArrayElements(address, addr, 0);
1205
1206 return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
1207 }
1208
setBrowsedPlayerRspNative(JNIEnv * env,jobject object,jbyteArray address,jint rspStatus,jbyte depth,jint numItems,jobjectArray textArray)1209 static jboolean setBrowsedPlayerRspNative(JNIEnv* env, jobject object,
1210 jbyteArray address, jint rspStatus,
1211 jbyte depth, jint numItems,
1212 jobjectArray textArray) {
1213 if (!sBluetoothAvrcpInterface) {
1214 ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
1215 return JNI_FALSE;
1216 }
1217
1218 jbyte* addr = env->GetByteArrayElements(address, NULL);
1219 if (!addr) {
1220 jniThrowIOException(env, EINVAL);
1221 return JNI_FALSE;
1222 }
1223
1224 btrc_br_folder_name_t* p_folders = NULL;
1225 if (rspStatus == BTRC_STS_NO_ERROR) {
1226 if (depth > 0) {
1227 p_folders = new btrc_br_folder_name_t[depth];
1228 }
1229
1230 for (int folder_idx = 0; folder_idx < depth; folder_idx++) {
1231 /* copy folder names */
1232 ScopedLocalRef<jstring> text(
1233 env, (jstring)env->GetObjectArrayElement(textArray, folder_idx));
1234
1235 if (!copy_jstring(p_folders[folder_idx].p_str, BTRC_MAX_ATTR_STR_LEN,
1236 text.get(), env)) {
1237 rspStatus = BTRC_STS_INTERNAL_ERR;
1238 delete[] p_folders;
1239 env->ReleaseByteArrayElements(address, addr, 0);
1240 ALOGE("%s: Failed to copy folder name", __func__);
1241 return JNI_FALSE;
1242 }
1243
1244 p_folders[folder_idx].str_len =
1245 strlen((char*)p_folders[folder_idx].p_str);
1246 }
1247 }
1248
1249 uint8_t folder_depth =
1250 depth; /* folder_depth is 0 if current folder is root */
1251 uint16_t charset_id = BTRC_CHARSET_ID_UTF8;
1252 bt_bdaddr_t* btAddr = (bt_bdaddr_t*)addr;
1253 bt_status_t status = sBluetoothAvrcpInterface->set_browsed_player_rsp(
1254 btAddr, (btrc_status_t)rspStatus, numItems, charset_id, folder_depth,
1255 p_folders);
1256 if (status != BT_STATUS_SUCCESS) {
1257 ALOGE("%s: Failed set_browsed_player_rsp, status: %d", __func__, status);
1258 }
1259
1260 if (depth > 0) {
1261 delete[] p_folders;
1262 }
1263
1264 env->ReleaseByteArrayElements(address, addr, 0);
1265 return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
1266 }
1267
changePathRspNative(JNIEnv * env,jobject object,jbyteArray address,jint rspStatus,jint numItems)1268 static jboolean changePathRspNative(JNIEnv* env, jobject object,
1269 jbyteArray address, jint rspStatus,
1270 jint numItems) {
1271 if (!sBluetoothAvrcpInterface) {
1272 ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
1273 return JNI_FALSE;
1274 }
1275
1276 jbyte* addr = env->GetByteArrayElements(address, NULL);
1277 if (!addr) {
1278 jniThrowIOException(env, EINVAL);
1279 return JNI_FALSE;
1280 }
1281
1282 uint32_t nItems = (uint32_t)numItems;
1283 bt_bdaddr_t* btAddr = (bt_bdaddr_t*)addr;
1284 bt_status_t status = sBluetoothAvrcpInterface->change_path_rsp(
1285 btAddr, (btrc_status_t)rspStatus, (uint32_t)nItems);
1286 if (status != BT_STATUS_SUCCESS) {
1287 ALOGE("Failed change_path_rsp, status: %d", status);
1288 }
1289 env->ReleaseByteArrayElements(address, addr, 0);
1290
1291 return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
1292 }
1293
searchRspNative(JNIEnv * env,jobject object,jbyteArray address,jint rspStatus,jint uidCounter,jint numItems)1294 static jboolean searchRspNative(JNIEnv* env, jobject object, jbyteArray address,
1295 jint rspStatus, jint uidCounter,
1296 jint numItems) {
1297 if (!sBluetoothAvrcpInterface) {
1298 ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
1299 return JNI_FALSE;
1300 }
1301
1302 jbyte* addr = env->GetByteArrayElements(address, NULL);
1303 if (!addr) {
1304 jniThrowIOException(env, EINVAL);
1305 return JNI_FALSE;
1306 }
1307
1308 uint32_t nItems = (uint32_t)numItems;
1309 bt_bdaddr_t* btAddr = (bt_bdaddr_t*)addr;
1310 bt_status_t status = sBluetoothAvrcpInterface->search_rsp(
1311 btAddr, (btrc_status_t)rspStatus, (uint32_t)uidCounter, (uint32_t)nItems);
1312 if (status != BT_STATUS_SUCCESS) {
1313 ALOGE("Failed search_rsp, status: %d", status);
1314 }
1315
1316 env->ReleaseByteArrayElements(address, addr, 0);
1317
1318 return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
1319 }
1320
playItemRspNative(JNIEnv * env,jobject object,jbyteArray address,jint rspStatus)1321 static jboolean playItemRspNative(JNIEnv* env, jobject object,
1322 jbyteArray address, jint rspStatus) {
1323 if (!sBluetoothAvrcpInterface) {
1324 ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
1325 return JNI_FALSE;
1326 }
1327
1328 jbyte* addr = env->GetByteArrayElements(address, NULL);
1329 if (!addr) {
1330 jniThrowIOException(env, EINVAL);
1331 return JNI_FALSE;
1332 }
1333
1334 bt_bdaddr_t* btAddr = (bt_bdaddr_t*)addr;
1335 bt_status_t status =
1336 sBluetoothAvrcpInterface->play_item_rsp(btAddr, (btrc_status_t)rspStatus);
1337 if (status != BT_STATUS_SUCCESS) {
1338 ALOGE("Failed play_item_rsp, status: %d", status);
1339 }
1340 env->ReleaseByteArrayElements(address, addr, 0);
1341
1342 return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
1343 }
1344
getTotalNumOfItemsRspNative(JNIEnv * env,jobject object,jbyteArray address,jint rspStatus,jint uidCounter,jint numItems)1345 static jboolean getTotalNumOfItemsRspNative(JNIEnv* env, jobject object,
1346 jbyteArray address, jint rspStatus,
1347 jint uidCounter, jint numItems) {
1348 if (!sBluetoothAvrcpInterface) {
1349 ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
1350 return JNI_FALSE;
1351 }
1352
1353 jbyte* addr = env->GetByteArrayElements(address, NULL);
1354 if (!addr) {
1355 jniThrowIOException(env, EINVAL);
1356 return JNI_FALSE;
1357 }
1358
1359 uint32_t nItems = (uint32_t)numItems;
1360 bt_bdaddr_t* btAddr = (bt_bdaddr_t*)addr;
1361 bt_status_t status = sBluetoothAvrcpInterface->get_total_num_of_items_rsp(
1362 btAddr, (btrc_status_t)rspStatus, (uint32_t)uidCounter, (uint32_t)nItems);
1363 if (status != BT_STATUS_SUCCESS) {
1364 ALOGE("Failed get_total_num_of_items_rsp, status: %d", status);
1365 }
1366 env->ReleaseByteArrayElements(address, addr, 0);
1367
1368 return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
1369 }
1370
addToNowPlayingRspNative(JNIEnv * env,jobject object,jbyteArray address,jint rspStatus)1371 static jboolean addToNowPlayingRspNative(JNIEnv* env, jobject object,
1372 jbyteArray address, jint rspStatus) {
1373 if (!sBluetoothAvrcpInterface) {
1374 ALOGE("%s: sBluetoothAvrcpInterface is null", __func__);
1375 return JNI_FALSE;
1376 }
1377
1378 jbyte* addr = env->GetByteArrayElements(address, NULL);
1379 if (!addr) {
1380 jniThrowIOException(env, EINVAL);
1381 return JNI_FALSE;
1382 }
1383
1384 bt_bdaddr_t* btAddr = (bt_bdaddr_t*)addr;
1385 bt_status_t status = sBluetoothAvrcpInterface->add_to_now_playing_rsp(
1386 btAddr, (btrc_status_t)rspStatus);
1387 if (status != BT_STATUS_SUCCESS) {
1388 ALOGE("Failed add_to_now_playing_rsp, status: %d", status);
1389 }
1390 env->ReleaseByteArrayElements(address, addr, 0);
1391
1392 return (status == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
1393 }
1394
1395 static JNINativeMethod sMethods[] = {
1396 {"classInitNative", "()V", (void*)classInitNative},
1397 {"initNative", "()V", (void*)initNative},
1398 {"cleanupNative", "()V", (void*)cleanupNative},
1399 {"getPlayStatusRspNative", "([BIII)Z", (void*)getPlayStatusRspNative},
1400 {"getElementAttrRspNative", "([BB[I[Ljava/lang/String;)Z",
1401 (void*)getElementAttrRspNative},
1402 {"registerNotificationRspPlayStatusNative", "(II)Z",
1403 (void*)registerNotificationRspPlayStatusNative},
1404 {"registerNotificationRspTrackChangeNative", "(I[B)Z",
1405 (void*)registerNotificationRspTrackChangeNative},
1406 {"registerNotificationRspPlayPosNative", "(II)Z",
1407 (void*)registerNotificationRspPlayPosNative},
1408 {"setVolumeNative", "(I)Z", (void*)setVolumeNative},
1409
1410 {"setAddressedPlayerRspNative", "([BI)Z",
1411 (void*)setAddressedPlayerRspNative},
1412
1413 {"setBrowsedPlayerRspNative", "([BIBI[Ljava/lang/String;)Z",
1414 (void*)setBrowsedPlayerRspNative},
1415
1416 {"mediaPlayerListRspNative", "([BIIBI[I[B[I[B[S[Ljava/lang/String;)Z",
1417 (void*)mediaPlayerListRspNative},
1418
1419 {"getFolderItemsRspNative",
1420 "([BISBI[B[B[B[B[Ljava/lang/String;[I[I[Ljava/lang/String;)Z",
1421 (void*)getFolderItemsRspNative},
1422
1423 {"changePathRspNative", "([BII)Z", (void*)changePathRspNative},
1424
1425 {"getItemAttrRspNative", "([BIB[I[Ljava/lang/String;)Z",
1426 (void*)getItemAttrRspNative},
1427
1428 {"playItemRspNative", "([BI)Z", (void*)playItemRspNative},
1429
1430 {"getTotalNumOfItemsRspNative", "([BIII)Z",
1431 (void*)getTotalNumOfItemsRspNative},
1432
1433 {"searchRspNative", "([BIII)Z", (void*)searchRspNative},
1434
1435 {"addToNowPlayingRspNative", "([BI)Z", (void*)addToNowPlayingRspNative},
1436
1437 {"registerNotificationRspAddrPlayerChangedNative", "(III)Z",
1438 (void*)registerNotificationRspAddrPlayerChangedNative},
1439
1440 {"registerNotificationRspAvalPlayerChangedNative", "(I)Z",
1441 (void*)registerNotificationRspAvalPlayerChangedNative},
1442
1443 {"registerNotificationRspUIDsChangedNative", "(II)Z",
1444 (void*)registerNotificationRspUIDsChangedNative},
1445
1446 {"registerNotificationRspNowPlayingChangedNative", "(I)Z",
1447 (void*)registerNotificationRspNowPlayingChangedNative}};
1448
register_com_android_bluetooth_avrcp(JNIEnv * env)1449 int register_com_android_bluetooth_avrcp(JNIEnv* env) {
1450 return jniRegisterNativeMethods(env, "com/android/bluetooth/avrcp/Avrcp",
1451 sMethods, NELEM(sMethods));
1452 }
1453
1454 /* Helper function to copy attributes of item.
1455 * Assumes that all items in response have same number of attributes
1456 *
1457 * returns true on succes, false otherwise.
1458 */
copy_item_attributes(JNIEnv * env,jobject object,btrc_folder_items_t * pitem,jint * p_attributesIds,jobjectArray attributesArray,int item_idx,int attribCopiedIndex)1459 static bool copy_item_attributes(JNIEnv* env, jobject object,
1460 btrc_folder_items_t* pitem,
1461 jint* p_attributesIds,
1462 jobjectArray attributesArray, int item_idx,
1463 int attribCopiedIndex) {
1464 bool success = true;
1465
1466 /* copy attributes of the item */
1467 if (0 < pitem->media.num_attrs) {
1468 int num_attrs = pitem->media.num_attrs;
1469 ALOGI("%s num_attr = %d", __func__, num_attrs);
1470 pitem->media.p_attrs = new btrc_element_attr_val_t[num_attrs];
1471 if (!pitem->media.p_attrs) {
1472 return false;
1473 }
1474
1475 for (int tempAtrCount = 0; tempAtrCount < pitem->media.num_attrs;
1476 ++tempAtrCount) {
1477 pitem->media.p_attrs[tempAtrCount].attr_id =
1478 p_attributesIds[attribCopiedIndex + tempAtrCount];
1479
1480 ScopedLocalRef<jstring> text(
1481 env, (jstring)env->GetObjectArrayElement(
1482 attributesArray, attribCopiedIndex + tempAtrCount));
1483
1484 if (!copy_jstring(pitem->media.p_attrs[tempAtrCount].text,
1485 BTRC_MAX_ATTR_STR_LEN, text.get(), env)) {
1486 success = false;
1487 ALOGE("%s: failed to copy attributes", __func__);
1488 break;
1489 }
1490 }
1491 }
1492 return success;
1493 }
1494
1495 /* Helper function to copy String data from java to native
1496 *
1497 * returns true on succes, false otherwise
1498 */
copy_jstring(uint8_t * str,int maxBytes,jstring jstr,JNIEnv * env)1499 static bool copy_jstring(uint8_t* str, int maxBytes, jstring jstr,
1500 JNIEnv* env) {
1501 if (str == NULL || jstr == NULL || env == NULL) return false;
1502
1503 memset(str, 0, maxBytes);
1504 const char* p_str = env->GetStringUTFChars(jstr, NULL);
1505 size_t len = strnlen(p_str, maxBytes - 1);
1506 memcpy(str, p_str, len);
1507
1508 env->ReleaseStringUTFChars(jstr, p_str);
1509 return true;
1510 }
1511
1512 /* Helper function to cleanup items */
cleanup_items(btrc_folder_items_t * p_items,int numItems)1513 static void cleanup_items(btrc_folder_items_t* p_items, int numItems) {
1514 for (int item_idx = 0; item_idx < numItems; item_idx++) {
1515 /* release memory for attributes in case item is media item */
1516 if ((BTRC_ITEM_MEDIA == p_items[item_idx].item_type) &&
1517 p_items[item_idx].media.p_attrs != NULL)
1518 delete[] p_items[item_idx].media.p_attrs;
1519 }
1520 }
1521 }
1522