2 * LwsService.cpp - libwebsockets test service for Android
4 * Copyright (C) 2016 Alexander Bruines <alexander.bruines@gmail.com>
6 * This file is made available under the Creative Commons CC0 1.0
7 * Universal Public Domain Dedication.
9 * The person who associated a work with this deed has dedicated
10 * the work to the public domain by waiving all of his or her rights
11 * to the work worldwide under copyright law, including all related
12 * and neighboring rights, to the extent allowed by law. You can copy,
13 * modify, distribute and perform the work, even for commercial purposes,
14 * all without asking permission.
16 * The test apps are intended to be adapted for use in your code, which
17 * may be proprietary. So unlike the library itself, they are licensed
21 #include <libwebsockets.h>
24 #include <android/log.h>
25 #define printf(...) __android_log_print(ANDROID_LOG_VERBOSE, "LwsService", ##__VA_ARGS__)
27 /////////////////////////////////////////////////////////
28 // Code executed when loading the dynamic link library //
29 /////////////////////////////////////////////////////////
31 // The Java class the native functions shall be part of
32 #define JNIREG_CLASS "org/libwebsockets/client/LwsService"
37 JNIEXPORT jboolean JNICALL jni_initLws(JNIEnv *env, jobject obj);
38 JNIEXPORT void JNICALL jni_exitLws(JNIEnv *env, jobject obj);
39 JNIEXPORT void JNICALL jni_serviceLws(JNIEnv *env, jobject obj);
40 JNIEXPORT void JNICALL jni_setConnectionParameters(JNIEnv *env, jobject obj, jstring serverAddress, jint serverPort);
41 JNIEXPORT jboolean JNICALL jni_connectLws(JNIEnv *env, jobject obj);
43 static JNINativeMethod gMethods[] = {
44 { "initLws", "()Z", (void*)jni_initLws },
45 { "exitLws", "()V", (void*)jni_exitLws },
46 { "serviceLws", "()V", (void*)jni_serviceLws },
47 { "setConnectionParameters", "(Ljava/lang/String;I)V", (void*)jni_setConnectionParameters },
48 { "connectLws", "()Z", (void*)jni_connectLws },
51 static int registerNativeMethods(JNIEnv* env, const char* className, JNINativeMethod* gMethods, int numMethods)
54 cls = env->FindClass(className);
58 if (env->RegisterNatives(cls, gMethods, numMethods) < 0) {
65 static int registerNatives(JNIEnv* env)
67 if(!registerNativeMethods(env, JNIREG_CLASS, gMethods, sizeof(gMethods) / sizeof(gMethods[0]))) {
73 JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void * reserved) {
77 if(vm->GetEnv((void**)&gEnv, JNI_VERSION_1_6) != JNI_OK) goto bail;
78 if(vm->AttachCurrentThread(&gEnv, NULL) < 0) goto bail;
79 if(registerNatives(gEnv) != JNI_TRUE) goto bail;
81 result = JNI_VERSION_1_6;
87 JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved) {
91 ////////////////////////////////////////////////////
92 // JNI functions to export: //
93 ////////////////////////////////////////////////////
95 static jclass gLwsServiceCls;
96 static jobject gLwsServiceObj;
97 static jmethodID sendMessageId;
99 static const int MSG_DUMB_INCREMENT_PROTOCOL_COUNTER = 1;
100 static const int MSG_LWS_CALLBACK_CLIENT_CONNECTION_ERROR = 2;
101 static const int MSG_LWS_CALLBACK_CLIENT_ESTABLISHED = 3;
103 #define BUFFER_SIZE 4096
105 static struct lws_context *context = NULL;
106 static struct lws_context_creation_info info;
107 static struct lws *wsi = NULL;
109 // prevents sending messages after jni_exitLws had been called
110 static int isExit = 0;
112 enum websocket_protocols {
113 PROTOCOL_DUMB_INCREMENT = 0,
117 struct per_session_data {
121 static int callback( struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len );
123 static struct lws_protocols protocols[] = {
125 "dumb-increment-protocol",
127 sizeof( struct per_session_data ),
130 { NULL, NULL, 0, 0 } // end of list
133 static const struct lws_extension exts[] = {
136 lws_extension_callback_pm_deflate,
143 static int use_ssl = 0;
144 static int use_ssl_client = 0;
145 static char address[8192];
147 static char ca_cert[8192];
148 static char client_cert[8192];
149 static char client_cert_key[8192];
151 static int deny_deflate = 0;
152 static int deny_mux = 0;
154 // Logging function for libwebsockets
155 static void emit_log(int level, const char *msg)
161 JNIEXPORT jboolean JNICALL jni_initLws(JNIEnv *env, jobject obj)
163 if(context) return JNI_TRUE;
165 // Attach the java virtual machine to this thread
166 gJvm->AttachCurrentThread(&gEnv, NULL);
168 // Set java global references to the class and object
169 jclass cls = env->GetObjectClass(obj);
170 gLwsServiceCls = (jclass) env->NewGlobalRef(cls);
171 gLwsServiceObj = env->NewGlobalRef(obj);
173 // Get the sendMessage method from the LwsService class (inherited from class ThreadService)
174 sendMessageId = gEnv->GetMethodID(gLwsServiceCls, "sendMessage", "(ILjava/lang/Object;)V");
176 memset(&info, 0, sizeof(info));
177 info.port = CONTEXT_PORT_NO_LISTEN;
178 info.protocols = protocols;
179 #if !defined(LWS_WITHOUT_EXTENSIONS)
180 info.extensions = exts;
185 lws_set_log_level( LLL_NOTICE | LLL_INFO | LLL_ERR | LLL_WARN | LLL_CLIENT, emit_log );
187 context = lws_create_context(&info);
188 if( context == NULL ){
189 emit_log(LLL_ERR, "Creating libwebsocket context failed");
198 // Send a message to the client of the service
199 // (must call jni_initLws() first)
200 static inline void sendMessage(int id, jobject obj)
202 if(!isExit) gEnv->CallVoidMethod(gLwsServiceObj, sendMessageId, id, obj);
205 JNIEXPORT void JNICALL jni_exitLws(JNIEnv *env, jobject obj)
209 lws_context_destroy(context);
211 env->DeleteGlobalRef(gLwsServiceObj);
212 env->DeleteGlobalRef(gLwsServiceCls);
218 enum lws_callback_reasons reason,
226 case LWS_CALLBACK_CLIENT_CONNECTION_ERROR:
227 sendMessage(MSG_LWS_CALLBACK_CLIENT_CONNECTION_ERROR, NULL);
230 case LWS_CALLBACK_CLIENT_ESTABLISHED:
231 sendMessage(MSG_LWS_CALLBACK_CLIENT_ESTABLISHED, NULL);
234 case LWS_CALLBACK_CLIENT_RECEIVE:
235 ((char *)in)[len] = '\0';
236 sendMessage(MSG_DUMB_INCREMENT_PROTOCOL_COUNTER, gEnv->NewStringUTF((const char*)in));
239 case LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED:
240 if ((strcmp((const char*)in, "deflate-stream") == 0) && deny_deflate) {
241 emit_log(LLL_ERR, "websocket: denied deflate-stream extension");
244 if ((strcmp((const char*)in, "deflate-frame") == 0) && deny_deflate) {
245 emit_log(LLL_ERR, "websocket: denied deflate-frame extension");
248 if ((strcmp((const char*)in, "x-google-mux") == 0) && deny_mux) {
249 emit_log(LLL_ERR, "websocket: denied x-google-mux extension");
261 JNIEXPORT void JNICALL jni_serviceLws(JNIEnv *env, jobject obj)
264 lws_service( context, 0 );
268 JNIEXPORT void JNICALL jni_setConnectionParameters(
271 jstring serverAddress,
279 snprintf(address, sizeof(address), "%s", env->GetStringUTFChars(serverAddress, 0));
282 JNIEXPORT jboolean JNICALL jni_connectLws(JNIEnv *env, jobject obj)
284 struct lws_client_connect_info info_ws;
285 memset(&info_ws, 0, sizeof(info_ws));
288 info_ws.address = address;
290 info_ws.context = context;
291 info_ws.ssl_connection = use_ssl;
292 info_ws.host = address;
293 info_ws.origin = address;
294 info_ws.ietf_version_or_minus_one = -1;
295 info_ws.client_exts = exts;
296 info_ws.protocol = protocols[PROTOCOL_DUMB_INCREMENT].name;
299 wsi = lws_client_connect_via_info(&info_ws);
302 emit_log(LLL_ERR, "Protocol failed to connect.");