blob: 1851ad69fc8b2af82c74902a3baad23405aecc29 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2** Copyright 2008, 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 "BluetoothA2dpService.cpp"
18
19#include "android_bluetooth_common.h"
20#include "android_runtime/AndroidRuntime.h"
21#include "JNIHelp.h"
22#include "jni.h"
23#include "utils/Log.h"
24#include "utils/misc.h"
25
26#include <ctype.h>
27#include <errno.h>
28#include <stdio.h>
29#include <string.h>
30#include <stdlib.h>
31#include <unistd.h>
32
33#ifdef HAVE_BLUETOOTH
34#include <dbus/dbus.h>
35#endif
36
37namespace android {
38
39#ifdef HAVE_BLUETOOTH
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -070040static jmethodID method_onSinkPropertyChanged;
Jaikumar Ganeshc0e32f12009-12-16 11:36:39 -080041static jmethodID method_onConnectSinkResult;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042
43typedef struct {
Robert Greenwalt704e00a2009-05-07 14:06:45 -070044 JavaVM *vm;
45 int envVer;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046 DBusConnection *conn;
47 jobject me; // for callbacks to java
48} native_data_t;
49
50static native_data_t *nat = NULL; // global native data
Jaikumar Ganeshc0e32f12009-12-16 11:36:39 -080051static void onConnectSinkResult(DBusMessage *msg, void *user, void *n);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -070053static Properties sink_properties[] = {
54 {"State", DBUS_TYPE_STRING},
55 {"Connected", DBUS_TYPE_BOOLEAN},
56 {"Playing", DBUS_TYPE_BOOLEAN},
57 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058#endif
59
60/* Returns true on success (even if adapter is present but disabled).
61 * Return false if dbus is down, or another serious error (out of memory)
62*/
63static bool initNative(JNIEnv* env, jobject object) {
Jaikumar Ganesh2653a1e2011-02-22 10:53:29 -080064 LOGV("%s", __FUNCTION__);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080065#ifdef HAVE_BLUETOOTH
66 nat = (native_data_t *)calloc(1, sizeof(native_data_t));
67 if (NULL == nat) {
68 LOGE("%s: out of memory!", __FUNCTION__);
69 return false;
70 }
Robert Greenwalt704e00a2009-05-07 14:06:45 -070071 env->GetJavaVM( &(nat->vm) );
72 nat->envVer = env->GetVersion();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080073 nat->me = env->NewGlobalRef(object);
74
75 DBusError err;
76 dbus_error_init(&err);
77 dbus_threads_init_default();
78 nat->conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
79 if (dbus_error_is_set(&err)) {
80 LOGE("Could not get onto the system bus: %s", err.message);
81 dbus_error_free(&err);
82 return false;
83 }
Nick Pelly9e0a1952009-06-17 15:27:59 -070084 dbus_connection_set_exit_on_disconnect(nat->conn, FALSE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080085#endif /*HAVE_BLUETOOTH*/
86 return true;
87}
88
89static void cleanupNative(JNIEnv* env, jobject object) {
90#ifdef HAVE_BLUETOOTH
Jaikumar Ganesh2653a1e2011-02-22 10:53:29 -080091 LOGV("%s", __FUNCTION__);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080092 if (nat) {
93 dbus_connection_close(nat->conn);
94 env->DeleteGlobalRef(nat->me);
95 free(nat);
96 nat = NULL;
97 }
98#endif
99}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800100
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700101static jobjectArray getSinkPropertiesNative(JNIEnv *env, jobject object,
102 jstring path) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800103#ifdef HAVE_BLUETOOTH
Jaikumar Ganesh2653a1e2011-02-22 10:53:29 -0800104 LOGV("%s", __FUNCTION__);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800105 if (nat) {
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700106 DBusMessage *msg, *reply;
107 DBusError err;
108 dbus_error_init(&err);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800109
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800110 const char *c_path = env->GetStringUTFChars(path, NULL);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700111 reply = dbus_func_args_timeout(env,
112 nat->conn, -1, c_path,
113 "org.bluez.AudioSink", "GetProperties",
114 DBUS_TYPE_INVALID);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800115 env->ReleaseStringUTFChars(path, c_path);
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700116 if (!reply && dbus_error_is_set(&err)) {
117 LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply);
118 return NULL;
119 } else if (!reply) {
120 LOGE("DBus reply is NULL in function %s", __FUNCTION__);
121 return NULL;
122 }
123 DBusMessageIter iter;
124 if (dbus_message_iter_init(reply, &iter))
125 return parse_properties(env, &iter, (Properties *)&sink_properties,
126 sizeof(sink_properties) / sizeof(Properties));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800127 }
128#endif
129 return NULL;
130}
131
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800132
133static jboolean connectSinkNative(JNIEnv *env, jobject object, jstring path) {
134#ifdef HAVE_BLUETOOTH
Jaikumar Ganesh2653a1e2011-02-22 10:53:29 -0800135 LOGV("%s", __FUNCTION__);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800136 if (nat) {
137 const char *c_path = env->GetStringUTFChars(path, NULL);
Jaikumar Ganeshc0e32f12009-12-16 11:36:39 -0800138 int len = env->GetStringLength(path) + 1;
139 char *context_path = (char *)calloc(len, sizeof(char));
140 strlcpy(context_path, c_path, len); // for callback
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800141
Jaikumar Ganeshc0e32f12009-12-16 11:36:39 -0800142 bool ret = dbus_func_args_async(env, nat->conn, -1, onConnectSinkResult, context_path,
143 nat, c_path, "org.bluez.AudioSink", "Connect",
Jaikumar Ganesh4f3ebc42009-09-16 15:15:21 -0700144 DBUS_TYPE_INVALID);
145
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800146 env->ReleaseStringUTFChars(path, c_path);
Jaikumar Ganesh4f3ebc42009-09-16 15:15:21 -0700147 return ret ? JNI_TRUE : JNI_FALSE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800148 }
149#endif
150 return JNI_FALSE;
151}
152
153static jboolean disconnectSinkNative(JNIEnv *env, jobject object,
154 jstring path) {
155#ifdef HAVE_BLUETOOTH
Jaikumar Ganesh2653a1e2011-02-22 10:53:29 -0800156 LOGV("%s", __FUNCTION__);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800157 if (nat) {
158 const char *c_path = env->GetStringUTFChars(path, NULL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800159
Jaikumar Ganesh4f3ebc42009-09-16 15:15:21 -0700160 bool ret = dbus_func_args_async(env, nat->conn, -1, NULL, NULL, nat,
161 c_path, "org.bluez.AudioSink", "Disconnect",
162 DBUS_TYPE_INVALID);
163
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800164 env->ReleaseStringUTFChars(path, c_path);
Jaikumar Ganesh4f3ebc42009-09-16 15:15:21 -0700165 return ret ? JNI_TRUE : JNI_FALSE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800166 }
167#endif
168 return JNI_FALSE;
169}
170
Zhu Lanf92c1062009-06-24 10:51:57 +0800171static jboolean suspendSinkNative(JNIEnv *env, jobject object,
172 jstring path) {
173#ifdef HAVE_BLUETOOTH
Jaikumar Ganesh2653a1e2011-02-22 10:53:29 -0800174 LOGV("%s", __FUNCTION__);
Zhu Lanf92c1062009-06-24 10:51:57 +0800175 if (nat) {
176 const char *c_path = env->GetStringUTFChars(path, NULL);
177 bool ret = dbus_func_args_async(env, nat->conn, -1, NULL, NULL, nat,
178 c_path, "org.bluez.audio.Sink", "Suspend",
179 DBUS_TYPE_INVALID);
180 env->ReleaseStringUTFChars(path, c_path);
181 return ret ? JNI_TRUE : JNI_FALSE;
182 }
183#endif
184 return JNI_FALSE;
185}
186
187static jboolean resumeSinkNative(JNIEnv *env, jobject object,
188 jstring path) {
189#ifdef HAVE_BLUETOOTH
Jaikumar Ganesh2653a1e2011-02-22 10:53:29 -0800190 LOGV("%s", __FUNCTION__);
Zhu Lanf92c1062009-06-24 10:51:57 +0800191 if (nat) {
192 const char *c_path = env->GetStringUTFChars(path, NULL);
193 bool ret = dbus_func_args_async(env, nat->conn, -1, NULL, NULL, nat,
194 c_path, "org.bluez.audio.Sink", "Resume",
195 DBUS_TYPE_INVALID);
196 env->ReleaseStringUTFChars(path, c_path);
197 return ret ? JNI_TRUE : JNI_FALSE;
198 }
199#endif
200 return JNI_FALSE;
201}
202
Jaikumar Ganesh4f773a12010-02-16 11:57:06 -0800203static jboolean avrcpVolumeUpNative(JNIEnv *env, jobject object,
204 jstring path) {
205#ifdef HAVE_BLUETOOTH
Jaikumar Ganesh2653a1e2011-02-22 10:53:29 -0800206 LOGV("%s", __FUNCTION__);
Jaikumar Ganesh4f773a12010-02-16 11:57:06 -0800207 if (nat) {
208 const char *c_path = env->GetStringUTFChars(path, NULL);
209 bool ret = dbus_func_args_async(env, nat->conn, -1, NULL, NULL, nat,
210 c_path, "org.bluez.Control", "VolumeUp",
211 DBUS_TYPE_INVALID);
212 env->ReleaseStringUTFChars(path, c_path);
213 return ret ? JNI_TRUE : JNI_FALSE;
214 }
215#endif
216 return JNI_FALSE;
217}
218
219static jboolean avrcpVolumeDownNative(JNIEnv *env, jobject object,
220 jstring path) {
221#ifdef HAVE_BLUETOOTH
Jaikumar Ganesh2653a1e2011-02-22 10:53:29 -0800222 LOGV("%s", __FUNCTION__);
Jaikumar Ganesh4f773a12010-02-16 11:57:06 -0800223 if (nat) {
224 const char *c_path = env->GetStringUTFChars(path, NULL);
225 bool ret = dbus_func_args_async(env, nat->conn, -1, NULL, NULL, nat,
226 c_path, "org.bluez.Control", "VolumeDown",
227 DBUS_TYPE_INVALID);
228 env->ReleaseStringUTFChars(path, c_path);
229 return ret ? JNI_TRUE : JNI_FALSE;
230 }
231#endif
232 return JNI_FALSE;
233}
234
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700235#ifdef HAVE_BLUETOOTH
Jaikumar Ganesh82aea4a2009-06-09 23:23:20 -0700236DBusHandlerResult a2dp_event_filter(DBusMessage *msg, JNIEnv *env) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800237 DBusError err;
238
239 if (!nat) {
240 LOGV("... skipping %s\n", __FUNCTION__);
241 LOGV("... ignored\n");
242 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
243 }
244
245 dbus_error_init(&err);
246
247 if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_SIGNAL) {
248 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
249 }
250
251 DBusHandlerResult result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
252
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700253 if (dbus_message_is_signal(msg, "org.bluez.AudioSink",
254 "PropertyChanged")) {
255 jobjectArray str_array =
256 parse_property_change(env, msg, (Properties *)&sink_properties,
257 sizeof(sink_properties) / sizeof(Properties));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800258 const char *c_path = dbus_message_get_path(msg);
Jaikumar Ganesha7c0bdc2010-06-03 16:50:57 -0700259 jstring path = env->NewStringUTF(c_path);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800260 env->CallVoidMethod(nat->me,
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700261 method_onSinkPropertyChanged,
Jaikumar Ganesha7c0bdc2010-06-03 16:50:57 -0700262 path,
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700263 str_array);
Jaikumar Ganesha7c0bdc2010-06-03 16:50:57 -0700264 env->DeleteLocalRef(path);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800265 result = DBUS_HANDLER_RESULT_HANDLED;
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700266 return result;
267 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800268 LOGV("... ignored");
269 }
270 if (env->ExceptionCheck()) {
271 LOGE("VM Exception occurred while handling %s.%s (%s) in %s,"
272 " leaving for VM",
273 dbus_message_get_interface(msg), dbus_message_get_member(msg),
274 dbus_message_get_path(msg), __FUNCTION__);
275 }
276
277 return result;
278}
Jaikumar Ganeshc0e32f12009-12-16 11:36:39 -0800279
280void onConnectSinkResult(DBusMessage *msg, void *user, void *n) {
Jaikumar Ganesh2653a1e2011-02-22 10:53:29 -0800281 LOGV("%s", __FUNCTION__);
Jaikumar Ganeshc0e32f12009-12-16 11:36:39 -0800282
283 native_data_t *nat = (native_data_t *)n;
284 const char *path = (const char *)user;
285 DBusError err;
286 dbus_error_init(&err);
287 JNIEnv *env;
288 nat->vm->GetEnv((void**)&env, nat->envVer);
289
290
291 bool result = JNI_TRUE;
292 if (dbus_set_error_from_message(&err, msg)) {
293 LOG_AND_FREE_DBUS_ERROR(&err);
294 result = JNI_FALSE;
295 }
296 LOGV("... Device Path = %s, result = %d", path, result);
Jaikumar Ganesha7c0bdc2010-06-03 16:50:57 -0700297
298 jstring jPath = env->NewStringUTF(path);
Jaikumar Ganeshc0e32f12009-12-16 11:36:39 -0800299 env->CallVoidMethod(nat->me,
300 method_onConnectSinkResult,
Jaikumar Ganesha7c0bdc2010-06-03 16:50:57 -0700301 jPath,
Jaikumar Ganeshc0e32f12009-12-16 11:36:39 -0800302 result);
Jaikumar Ganesha7c0bdc2010-06-03 16:50:57 -0700303 env->DeleteLocalRef(jPath);
Jaikumar Ganeshc0e32f12009-12-16 11:36:39 -0800304 free(user);
305}
306
Jaikumar Ganesh4f773a12010-02-16 11:57:06 -0800307
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800308#endif
309
310
311static JNINativeMethod sMethods[] = {
312 {"initNative", "()Z", (void *)initNative},
313 {"cleanupNative", "()V", (void *)cleanupNative},
314
Jaikumar Ganeshc0e32f12009-12-16 11:36:39 -0800315 /* Bluez audio 4.47 API */
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700316 {"connectSinkNative", "(Ljava/lang/String;)Z", (void *)connectSinkNative},
317 {"disconnectSinkNative", "(Ljava/lang/String;)Z", (void *)disconnectSinkNative},
Zhu Lanf92c1062009-06-24 10:51:57 +0800318 {"suspendSinkNative", "(Ljava/lang/String;)Z", (void*)suspendSinkNative},
319 {"resumeSinkNative", "(Ljava/lang/String;)Z", (void*)resumeSinkNative},
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700320 {"getSinkPropertiesNative", "(Ljava/lang/String;)[Ljava/lang/Object;",
321 (void *)getSinkPropertiesNative},
Jaikumar Ganesh4f773a12010-02-16 11:57:06 -0800322 {"avrcpVolumeUpNative", "(Ljava/lang/String;)Z", (void*)avrcpVolumeUpNative},
323 {"avrcpVolumeDownNative", "(Ljava/lang/String;)Z", (void*)avrcpVolumeDownNative},
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800324};
325
326int register_android_server_BluetoothA2dpService(JNIEnv *env) {
327 jclass clazz = env->FindClass("android/server/BluetoothA2dpService");
328 if (clazz == NULL) {
329 LOGE("Can't find android/server/BluetoothA2dpService");
330 return -1;
331 }
332
333#ifdef HAVE_BLUETOOTH
Jaikumar Ganeshd5ac1ae2009-05-05 22:26:12 -0700334 method_onSinkPropertyChanged = env->GetMethodID(clazz, "onSinkPropertyChanged",
335 "(Ljava/lang/String;[Ljava/lang/String;)V");
Jaikumar Ganeshc0e32f12009-12-16 11:36:39 -0800336 method_onConnectSinkResult = env->GetMethodID(clazz, "onConnectSinkResult",
337 "(Ljava/lang/String;Z)V");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800338#endif
339
340 return AndroidRuntime::registerNativeMethods(env,
341 "android/server/BluetoothA2dpService", sMethods, NELEM(sMethods));
342}
343
344} /* namespace android */