• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
34 JavaVM* gJvm = NULL;
35 JNIEnv* gEnv = 0;
36 
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);
42 
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 },
49 };
50 
registerNativeMethods(JNIEnv * env,const char * className,JNINativeMethod * gMethods,int numMethods)51 static 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 
registerNatives(JNIEnv * env)65 static 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 
JNI_OnLoad(JavaVM * vm,void * reserved)73 JNIEXPORT 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 
83 bail:
84     return result;
85 }
86 
JNI_OnUnload(JavaVM * vm,void * reserved)87 JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved) {
88     gJvm = NULL;
89 }
90 
91 ////////////////////////////////////////////////////
92 // JNI functions to export:                       //
93 ////////////////////////////////////////////////////
94 
95 static jclass gLwsServiceCls;
96 static jobject gLwsServiceObj;
97 static jmethodID sendMessageId;
98 
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;
102 
103 #define BUFFER_SIZE 4096
104 
105 static struct lws_context *context = NULL;
106 static struct lws_context_creation_info info;
107 static struct lws *wsi = NULL;
108 
109 // prevents sending messages after jni_exitLws had been called
110 static int isExit = 0;
111 
112 enum websocket_protocols {
113   PROTOCOL_DUMB_INCREMENT = 0,
114   PROTOCOL_COUNT
115 };
116 
117 struct per_session_data {
118   ;// no data
119 };
120 
121 static int callback( struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len );
122 
123 static 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 
133 static 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 
142 static int port = 0;
143 static int use_ssl = 0;
144 static int use_ssl_client = 0;
145 static char address[8192];
146 
147 static char ca_cert[8192];
148 static char client_cert[8192];
149 static char client_cert_key[8192];
150 
151 static int deny_deflate = 0;
152 static int deny_mux = 0;
153 
154 // Logging function for libwebsockets
emit_log(int level,const char * msg)155 static void emit_log(int level, const char *msg)
156 {
157     printf("%s", msg);
158 }
159 
160 
jni_initLws(JNIEnv * env,jobject obj)161 JNIEXPORT 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)
sendMessage(int id,jobject obj)200 static inline void sendMessage(int id, jobject obj)
201 {
202   if(!isExit) gEnv->CallVoidMethod(gLwsServiceObj, sendMessageId, id, obj);
203 }
204 
jni_exitLws(JNIEnv * env,jobject obj)205 JNIEXPORT 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 
callback(struct lws * wsi,enum lws_callback_reasons reason,void * user,void * in,size_t len)216 static 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 
jni_serviceLws(JNIEnv * env,jobject obj)261 JNIEXPORT void JNICALL jni_serviceLws(JNIEnv *env, jobject obj)
262 {
263   if(context){
264     lws_service( context, 0 );
265   }
266 }
267 
jni_setConnectionParameters(JNIEnv * env,jobject obj,jstring serverAddress,jint serverPort)268 JNIEXPORT 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 
jni_connectLws(JNIEnv * env,jobject obj)282 JNIEXPORT 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