• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
37 namespace android {
38 
39 #ifdef HAVE_BLUETOOTH
40 static jmethodID method_onSinkPropertyChanged;
41 
42 typedef struct {
43     JavaVM *vm;
44     int envVer;
45     DBusConnection *conn;
46     jobject me;  // for callbacks to java
47 } native_data_t;
48 
49 static native_data_t *nat = NULL;  // global native data
50 
51 static Properties sink_properties[] = {
52         {"State", DBUS_TYPE_STRING},
53         {"Connected", DBUS_TYPE_BOOLEAN},
54         {"Playing", DBUS_TYPE_BOOLEAN},
55       };
56 #endif
57 
58 /* Returns true on success (even if adapter is present but disabled).
59  * Return false if dbus is down, or another serious error (out of memory)
60 */
initNative(JNIEnv * env,jobject object)61 static bool initNative(JNIEnv* env, jobject object) {
62     LOGV(__FUNCTION__);
63 #ifdef HAVE_BLUETOOTH
64     nat = (native_data_t *)calloc(1, sizeof(native_data_t));
65     if (NULL == nat) {
66         LOGE("%s: out of memory!", __FUNCTION__);
67         return false;
68     }
69     env->GetJavaVM( &(nat->vm) );
70     nat->envVer = env->GetVersion();
71     nat->me = env->NewGlobalRef(object);
72 
73     DBusError err;
74     dbus_error_init(&err);
75     dbus_threads_init_default();
76     nat->conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
77     if (dbus_error_is_set(&err)) {
78         LOGE("Could not get onto the system bus: %s", err.message);
79         dbus_error_free(&err);
80         return false;
81     }
82     dbus_connection_set_exit_on_disconnect(nat->conn, FALSE);
83 #endif  /*HAVE_BLUETOOTH*/
84     return true;
85 }
86 
cleanupNative(JNIEnv * env,jobject object)87 static void cleanupNative(JNIEnv* env, jobject object) {
88 #ifdef HAVE_BLUETOOTH
89     LOGV(__FUNCTION__);
90     if (nat) {
91         dbus_connection_close(nat->conn);
92         env->DeleteGlobalRef(nat->me);
93         free(nat);
94         nat = NULL;
95     }
96 #endif
97 }
98 
getSinkPropertiesNative(JNIEnv * env,jobject object,jstring path)99 static jobjectArray getSinkPropertiesNative(JNIEnv *env, jobject object,
100                                             jstring path) {
101 #ifdef HAVE_BLUETOOTH
102     LOGV(__FUNCTION__);
103     if (nat) {
104         DBusMessage *msg, *reply;
105         DBusError err;
106         dbus_error_init(&err);
107 
108         const char *c_path = env->GetStringUTFChars(path, NULL);
109         reply = dbus_func_args_timeout(env,
110                                    nat->conn, -1, c_path,
111                                    "org.bluez.AudioSink", "GetProperties",
112                                    DBUS_TYPE_INVALID);
113         env->ReleaseStringUTFChars(path, c_path);
114         if (!reply && dbus_error_is_set(&err)) {
115             LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, reply);
116             return NULL;
117         } else if (!reply) {
118             LOGE("DBus reply is NULL in function %s", __FUNCTION__);
119             return NULL;
120         }
121         DBusMessageIter iter;
122         if (dbus_message_iter_init(reply, &iter))
123             return parse_properties(env, &iter, (Properties *)&sink_properties,
124                                  sizeof(sink_properties) / sizeof(Properties));
125     }
126 #endif
127     return NULL;
128 }
129 
130 
connectSinkNative(JNIEnv * env,jobject object,jstring path)131 static jboolean connectSinkNative(JNIEnv *env, jobject object, jstring path) {
132 #ifdef HAVE_BLUETOOTH
133     LOGV(__FUNCTION__);
134     if (nat) {
135         const char *c_path = env->GetStringUTFChars(path, NULL);
136 
137         bool ret = dbus_func_args_async(env, nat->conn, -1, NULL, NULL, nat,
138                                     c_path, "org.bluez.AudioSink", "Connect",
139                                     DBUS_TYPE_INVALID);
140 
141         env->ReleaseStringUTFChars(path, c_path);
142         return ret ? JNI_TRUE : JNI_FALSE;
143     }
144 #endif
145     return JNI_FALSE;
146 }
147 
disconnectSinkNative(JNIEnv * env,jobject object,jstring path)148 static jboolean disconnectSinkNative(JNIEnv *env, jobject object,
149                                      jstring path) {
150 #ifdef HAVE_BLUETOOTH
151     LOGV(__FUNCTION__);
152     if (nat) {
153         const char *c_path = env->GetStringUTFChars(path, NULL);
154 
155         bool ret = dbus_func_args_async(env, nat->conn, -1, NULL, NULL, nat,
156                                     c_path, "org.bluez.AudioSink", "Disconnect",
157                                     DBUS_TYPE_INVALID);
158 
159         env->ReleaseStringUTFChars(path, c_path);
160         return ret ? JNI_TRUE : JNI_FALSE;
161     }
162 #endif
163     return JNI_FALSE;
164 }
165 
suspendSinkNative(JNIEnv * env,jobject object,jstring path)166 static jboolean suspendSinkNative(JNIEnv *env, jobject object,
167                                      jstring path) {
168 #ifdef HAVE_BLUETOOTH
169     LOGV(__FUNCTION__);
170     if (nat) {
171         const char *c_path = env->GetStringUTFChars(path, NULL);
172         bool ret = dbus_func_args_async(env, nat->conn, -1, NULL, NULL, nat,
173                            c_path, "org.bluez.audio.Sink", "Suspend",
174                            DBUS_TYPE_INVALID);
175         env->ReleaseStringUTFChars(path, c_path);
176         return ret ? JNI_TRUE : JNI_FALSE;
177     }
178 #endif
179     return JNI_FALSE;
180 }
181 
resumeSinkNative(JNIEnv * env,jobject object,jstring path)182 static jboolean resumeSinkNative(JNIEnv *env, jobject object,
183                                      jstring path) {
184 #ifdef HAVE_BLUETOOTH
185     LOGV(__FUNCTION__);
186     if (nat) {
187         const char *c_path = env->GetStringUTFChars(path, NULL);
188         bool ret = dbus_func_args_async(env, nat->conn, -1, NULL, NULL, nat,
189                            c_path, "org.bluez.audio.Sink", "Resume",
190                            DBUS_TYPE_INVALID);
191         env->ReleaseStringUTFChars(path, c_path);
192         return ret ? JNI_TRUE : JNI_FALSE;
193     }
194 #endif
195     return JNI_FALSE;
196 }
197 
198 #ifdef HAVE_BLUETOOTH
a2dp_event_filter(DBusMessage * msg,JNIEnv * env)199 DBusHandlerResult a2dp_event_filter(DBusMessage *msg, JNIEnv *env) {
200     DBusError err;
201 
202     if (!nat) {
203         LOGV("... skipping %s\n", __FUNCTION__);
204         LOGV("... ignored\n");
205         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
206     }
207 
208     dbus_error_init(&err);
209 
210     if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_SIGNAL) {
211         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
212     }
213 
214     DBusHandlerResult result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
215 
216     if (dbus_message_is_signal(msg, "org.bluez.AudioSink",
217                                       "PropertyChanged")) {
218         jobjectArray str_array =
219                     parse_property_change(env, msg, (Properties *)&sink_properties,
220                                 sizeof(sink_properties) / sizeof(Properties));
221         const char *c_path = dbus_message_get_path(msg);
222         env->CallVoidMethod(nat->me,
223                             method_onSinkPropertyChanged,
224                             env->NewStringUTF(c_path),
225                             str_array);
226         result = DBUS_HANDLER_RESULT_HANDLED;
227         return result;
228     } else {
229         LOGV("... ignored");
230     }
231     if (env->ExceptionCheck()) {
232         LOGE("VM Exception occurred while handling %s.%s (%s) in %s,"
233              " leaving for VM",
234              dbus_message_get_interface(msg), dbus_message_get_member(msg),
235              dbus_message_get_path(msg), __FUNCTION__);
236     }
237 
238     return result;
239 }
240 #endif
241 
242 
243 static JNINativeMethod sMethods[] = {
244     {"initNative", "()Z", (void *)initNative},
245     {"cleanupNative", "()V", (void *)cleanupNative},
246 
247     /* Bluez audio 4.40 API */
248     {"connectSinkNative", "(Ljava/lang/String;)Z", (void *)connectSinkNative},
249     {"disconnectSinkNative", "(Ljava/lang/String;)Z", (void *)disconnectSinkNative},
250     {"suspendSinkNative", "(Ljava/lang/String;)Z", (void*)suspendSinkNative},
251     {"resumeSinkNative", "(Ljava/lang/String;)Z", (void*)resumeSinkNative},
252     {"getSinkPropertiesNative", "(Ljava/lang/String;)[Ljava/lang/Object;",
253                                     (void *)getSinkPropertiesNative},
254 };
255 
register_android_server_BluetoothA2dpService(JNIEnv * env)256 int register_android_server_BluetoothA2dpService(JNIEnv *env) {
257     jclass clazz = env->FindClass("android/server/BluetoothA2dpService");
258     if (clazz == NULL) {
259         LOGE("Can't find android/server/BluetoothA2dpService");
260         return -1;
261     }
262 
263 #ifdef HAVE_BLUETOOTH
264     method_onSinkPropertyChanged = env->GetMethodID(clazz, "onSinkPropertyChanged",
265                                           "(Ljava/lang/String;[Ljava/lang/String;)V");
266 #endif
267 
268     return AndroidRuntime::registerNativeMethods(env,
269                 "android/server/BluetoothA2dpService", sMethods, NELEM(sMethods));
270 }
271 
272 } /* namespace android */
273