1/* 2 * LwsService.cpp - libwebsockets test service for Android 3 * 4 * Copyright (C) 2016 Alexander Bruines <alexander.bruines@gmail.com> 5 * 6 * This file is made available under the Creative Commons CC0 1.0 7 * Universal Public Domain Dedication. 8 * 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. 15 * 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 18 * Public Domain. 19 */ 20 21#include <libwebsockets.h> 22 23#include <jni.h> 24#include <android/log.h> 25#define printf(...) __android_log_print(ANDROID_LOG_VERBOSE, "LwsService", ##__VA_ARGS__) 26 27///////////////////////////////////////////////////////// 28// Code executed when loading the dynamic link library // 29///////////////////////////////////////////////////////// 30 31// The Java class the native functions shall be part of 32#define JNIREG_CLASS "org/libwebsockets/client/LwsService" 33 34JavaVM* gJvm = NULL; 35JNIEnv* gEnv = 0; 36 37JNIEXPORT jboolean JNICALL jni_initLws(JNIEnv *env, jobject obj); 38JNIEXPORT void JNICALL jni_exitLws(JNIEnv *env, jobject obj); 39JNIEXPORT void JNICALL jni_serviceLws(JNIEnv *env, jobject obj); 40JNIEXPORT void JNICALL jni_setConnectionParameters(JNIEnv *env, jobject obj, jstring serverAddress, jint serverPort); 41JNIEXPORT jboolean JNICALL jni_connectLws(JNIEnv *env, jobject obj); 42 43static 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 }, 49}; 50 51static int registerNativeMethods(JNIEnv* env, const char* className, JNINativeMethod* gMethods, int numMethods) 52{ 53 jclass cls; 54 cls = env->FindClass(className); 55 if(cls == NULL) { 56 return JNI_FALSE; 57 } 58 if (env->RegisterNatives(cls, gMethods, numMethods) < 0) { 59 return JNI_FALSE; 60 } 61 62 return JNI_TRUE; 63} 64 65static int registerNatives(JNIEnv* env) 66{ 67 if(!registerNativeMethods(env, JNIREG_CLASS, gMethods, sizeof(gMethods) / sizeof(gMethods[0]))) { 68 return JNI_FALSE; 69 } 70 return JNI_TRUE; 71} 72 73JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void * reserved) { 74 jint result = -1; 75 76 gJvm = vm; 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; 80 81 result = JNI_VERSION_1_6; 82 83bail: 84 return result; 85} 86 87JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved) { 88 gJvm = NULL; 89} 90 91//////////////////////////////////////////////////// 92// JNI functions to export: // 93//////////////////////////////////////////////////// 94 95static jclass gLwsServiceCls; 96static jobject gLwsServiceObj; 97static jmethodID sendMessageId; 98 99static const int MSG_DUMB_INCREMENT_PROTOCOL_COUNTER = 1; 100static const int MSG_LWS_CALLBACK_CLIENT_CONNECTION_ERROR = 2; 101static const int MSG_LWS_CALLBACK_CLIENT_ESTABLISHED = 3; 102 103#define BUFFER_SIZE 4096 104 105static struct lws_context *context = NULL; 106static struct lws_context_creation_info info; 107static struct lws *wsi = NULL; 108 109// prevents sending messages after jni_exitLws had been called 110static int isExit = 0; 111 112enum websocket_protocols { 113 PROTOCOL_DUMB_INCREMENT = 0, 114 PROTOCOL_COUNT 115}; 116 117struct per_session_data { 118 ;// no data 119}; 120 121static int callback( struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len ); 122 123static struct lws_protocols protocols[] = { 124 { 125 "dumb-increment-protocol", 126 callback, 127 sizeof( struct per_session_data ), 128 BUFFER_SIZE, 129 }, 130 { NULL, NULL, 0, 0 } // end of list 131}; 132 133static const struct lws_extension exts[] = { 134 { 135 "deflate-frame", 136 lws_extension_callback_pm_deflate, 137 "deflate_frame" 138 }, 139 { NULL, NULL, NULL } 140}; 141 142static int port = 0; 143static int use_ssl = 0; 144static int use_ssl_client = 0; 145static char address[8192]; 146 147static char ca_cert[8192]; 148static char client_cert[8192]; 149static char client_cert_key[8192]; 150 151static int deny_deflate = 0; 152static int deny_mux = 0; 153 154// Logging function for libwebsockets 155static void emit_log(int level, const char *msg) 156{ 157 printf("%s", msg); 158} 159 160 161JNIEXPORT jboolean JNICALL jni_initLws(JNIEnv *env, jobject obj) 162{ 163 if(context) return JNI_TRUE; 164 165 // Attach the java virtual machine to this thread 166 gJvm->AttachCurrentThread(&gEnv, NULL); 167 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); 172 173 // Get the sendMessage method from the LwsService class (inherited from class ThreadService) 174 sendMessageId = gEnv->GetMethodID(gLwsServiceCls, "sendMessage", "(ILjava/lang/Object;)V"); 175 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; 181#endif 182 info.gid = -1; 183 info.uid = -1; 184 185 lws_set_log_level( LLL_NOTICE | LLL_INFO | LLL_ERR | LLL_WARN | LLL_CLIENT, emit_log ); 186 187 context = lws_create_context(&info); 188 if( context == NULL ){ 189 emit_log(LLL_ERR, "Creating libwebsocket context failed"); 190 return JNI_FALSE; 191 } 192 193 isExit = 0; 194 195 return JNI_TRUE; 196} 197 198// Send a message to the client of the service 199// (must call jni_initLws() first) 200static inline void sendMessage(int id, jobject obj) 201{ 202 if(!isExit) gEnv->CallVoidMethod(gLwsServiceObj, sendMessageId, id, obj); 203} 204 205JNIEXPORT void JNICALL jni_exitLws(JNIEnv *env, jobject obj) 206{ 207 if(context){ 208 isExit = 1; 209 lws_context_destroy(context); 210 context = NULL; 211 env->DeleteGlobalRef(gLwsServiceObj); 212 env->DeleteGlobalRef(gLwsServiceCls); 213 } 214} 215 216static int callback( 217 struct lws *wsi, 218 enum lws_callback_reasons reason, 219 void *user, 220 void *in, 221 size_t len 222) 223{ 224 switch(reason){ 225 226 case LWS_CALLBACK_CLIENT_CONNECTION_ERROR: 227 sendMessage(MSG_LWS_CALLBACK_CLIENT_CONNECTION_ERROR, NULL); 228 break; 229 230 case LWS_CALLBACK_CLIENT_ESTABLISHED: 231 sendMessage(MSG_LWS_CALLBACK_CLIENT_ESTABLISHED, NULL); 232 break; 233 234 case LWS_CALLBACK_CLIENT_RECEIVE: 235 ((char *)in)[len] = '\0'; 236 sendMessage(MSG_DUMB_INCREMENT_PROTOCOL_COUNTER, gEnv->NewStringUTF((const char*)in)); 237 break; 238 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"); 242 return 1; 243 } 244 if ((strcmp((const char*)in, "deflate-frame") == 0) && deny_deflate) { 245 emit_log(LLL_ERR, "websocket: denied deflate-frame extension"); 246 return 1; 247 } 248 if ((strcmp((const char*)in, "x-google-mux") == 0) && deny_mux) { 249 emit_log(LLL_ERR, "websocket: denied x-google-mux extension"); 250 return 1; 251 } 252 break; 253 254 default: 255 break; 256 } 257 258 return 0; 259} 260 261JNIEXPORT void JNICALL jni_serviceLws(JNIEnv *env, jobject obj) 262{ 263 if(context){ 264 lws_service( context, 0 ); 265 } 266} 267 268JNIEXPORT void JNICALL jni_setConnectionParameters( 269 JNIEnv *env, 270 jobject obj, 271 jstring serverAddress, 272 jint serverPort 273) 274{ 275 address[0] = 0; 276 port = serverPort; 277 use_ssl = 0; 278 use_ssl_client = 0; 279 snprintf(address, sizeof(address), "%s", env->GetStringUTFChars(serverAddress, 0)); 280} 281 282JNIEXPORT jboolean JNICALL jni_connectLws(JNIEnv *env, jobject obj) 283{ 284 struct lws_client_connect_info info_ws; 285 memset(&info_ws, 0, sizeof(info_ws)); 286 287 info_ws.port = port; 288 info_ws.address = address; 289 info_ws.path = "/"; 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; 297 298 // connect 299 wsi = lws_client_connect_via_info(&info_ws); 300 if(wsi == NULL ){ 301 // Error 302 emit_log(LLL_ERR, "Protocol failed to connect."); 303 return JNI_FALSE; 304 } 305 306 return JNI_TRUE; 307} 308