Intro a simple wrapper for Android WiFi services. It allows to list WiFi networks in range, and could be extended with more features in the future.
It was partially generated by jwrapper (with the included command) and completed by hand. As usual with generated wrapper, the client of the library should refer to the corresponding Java/Android documentation to know more about these services.
Pull-Request: #1984
Reviewed-by: Jean Privat <jean@pryen.org>
import model
# Delay in seconds before the next request after an error
-fun request_delay_on_error: Int do return 60
+fun request_delay_on_error: Float do return 60.0
redef class App
redef fun on_create
redef var rest_action
# Should this request be delayed by `request_delay_on_error` seconds?
- var delay: Bool
-
- redef fun main
- do
- if delay then nanosleep(request_delay_on_error, 0)
- return super
- end
+ fun after_error(value: Bool) is autoinit do if value then delay = request_delay_on_error
end
# Async request to list latest posts, either immediately or by push notification
class AssetManager
# Native asset manager
- private var native_assets_manager: NativeAssetManager = app.native_activity.assets.new_global_ref is lazy
+ private var native_assets_manager: NativeAssetManager = app.native_context.assets.new_global_ref is lazy
# Close this asset manager
fun close do native_assets_manager.close
redef class App
# Resource Manager used to manage resources placed in the `res` folder of the app
var resource_manager: ResourcesManager is lazy do
- var res = native_activity.resources
- var pkg = native_activity.package_name
+ var res = native_context.resources
+ var pkg = native_context.package_name
return new ResourcesManager.native(res, pkg.to_s)
end
var asset_manager: AssetManager is lazy do return new AssetManager
end
-redef extern class NativeActivity
+redef extern class NativeContext
# Get the native AssetsManager of the application, used to initialize the nit's AssetManager
private fun assets: NativeAssetManager in "Java" `{
import activities
redef class App
- # The main Java Activity of this application
+ # Main Java Activity of this application
+ #
+ # Require: A Nit activity is currently running.
fun native_activity: NativeActivity is abstract
+
+ # Current reference context, either an activity or a service
+ fun native_context: NativeContext do return native_activity
end
extern class JavaClassLoader in "Java" `{java.lang.ClassLoader`}
do
var class_loader = self.class_loader
if class_loader == null then
- find_class_loader app.native_activity
+ find_class_loader app.native_context
class_loader = self.class_loader
assert class_loader != null
end
return load_jclass_intern(class_loader, class_loader_method, name)
end
- private fun find_class_loader(native_activity: NativeActivity) import jni_env, class_loader=, JavaObject.as nullable, class_loader_method=, JMethodID.as nullable `{
+ private fun find_class_loader(native_context: NativeContext) import jni_env, class_loader=, JavaObject.as nullable, class_loader_method=, JMethodID.as nullable `{
JNIEnv *env = Sys_jni_env(self);
// Retrieve main activity
- jclass class_activity = (*env)->GetObjectClass(env, native_activity);
- if (class_activity == NULL) {
- __android_log_print(ANDROID_LOG_ERROR, "Nit", "Failed retrieving activity class");
+ jclass class_context = (*env)->GetObjectClass(env, native_context);
+ if (class_context == NULL) {
+ __android_log_print(ANDROID_LOG_ERROR, "Nit", "Failed to retrieve activity class");
(*env)->ExceptionDescribe(env);
exit(1);
}
- jmethodID class_activity_getClassLoader = (*env)->GetMethodID(env, class_activity, "getClassLoader", "()Ljava/lang/ClassLoader;");
+ jmethodID class_activity_getClassLoader = (*env)->GetMethodID(env, class_context, "getClassLoader", "()Ljava/lang/ClassLoader;");
if (class_activity_getClassLoader == NULL) {
- __android_log_print(ANDROID_LOG_ERROR, "Nit", "Failed retrieving 'getClassLoader' method");
+ __android_log_print(ANDROID_LOG_ERROR, "Nit", "Failed to retrieve 'getClassLoader' method");
(*env)->ExceptionDescribe(env);
exit(1);
}
// Call activity.getClassLoader
- jobject instance_class_loader = (*env)->CallObjectMethod(env, native_activity, class_activity_getClassLoader);
+ jobject instance_class_loader = (*env)->CallObjectMethod(env, native_context, class_activity_getClassLoader);
if (instance_class_loader == NULL) {
- __android_log_print(ANDROID_LOG_ERROR, "Nit", "Failed retrieving class loader instance");
+ __android_log_print(ANDROID_LOG_ERROR, "Nit", "Failed to retrieve class loader instance");
(*env)->ExceptionDescribe(env);
exit(1);
}
jclass class_class_loader = (*env)->GetObjectClass(env, instance_class_loader);
if (class_class_loader == NULL) {
- __android_log_print(ANDROID_LOG_ERROR, "Nit", "Failed retrieving class of class loader");
+ __android_log_print(ANDROID_LOG_ERROR, "Nit", "Failed to retrieve class of class loader");
(*env)->ExceptionDescribe(env);
exit(1);
}
// Get the method ClassLoader.findClass
jmethodID class_class_loader_findClass = (*env)->GetMethodID(env, class_class_loader, "findClass", "(Ljava/lang/String;)Ljava/lang/Class;");
if (class_class_loader_findClass == NULL) {
- __android_log_print(ANDROID_LOG_ERROR, "Nit", "Failed retrieving 'findClass' method");
+ __android_log_print(ANDROID_LOG_ERROR, "Nit", "Failed to retrieve 'findClass' method");
(*env)->ExceptionDescribe(env);
exit(1);
}
Sys_class_loader_method__assign(self, JMethodID_as_nullable(class_class_loader_findClass));
// Clean up
- (*env)->DeleteLocalRef(env, class_activity);
+ (*env)->DeleteLocalRef(env, class_context);
(*env)->DeleteLocalRef(env, instance_class_loader);
(*env)->DeleteLocalRef(env, class_class_loader);
`}
`}
redef class App
- redef fun run_on_ui_thread(task) do app.native_activity.run_on_ui_thread task
+ redef fun run_on_ui_thread(task)
+ do
+ if app.activities.not_empty then
+ app.native_activity.run_on_ui_thread task
+ else
+ # There is no UI, it must be a service, run on the caller thread
+ task.main
+ end
+ end
end
redef class Text
redef fun to_s do return intent.to_native_s.to_s
end
-redef extern class NativeActivity
+redef extern class NativeContext
private fun start_activity(intent: NativeIntent) in "Java" `{ self.startActivity(intent); `}
private fun start_service(intent: NativeIntent) in "Java" `{ self.startService(intent); `}
private fun stop_service(intent: NativeIntent) in "Java" `{ self.stopService(intent); `}
redef class App
# Execute the intent and launch the appropriate application
- fun start_activity(intent: Intent) do native_activity.start_activity(intent.intent)
+ fun start_activity(intent: Intent) do native_context.start_activity(intent.intent)
# Start a service that will be running until the `stop_service` call
- fun start_service(intent: Intent) do native_activity.start_service(intent.intent)
+ fun start_service(intent: Intent) do native_context.start_service(intent.intent)
# Stop service
- fun stop_service(intent: Intent) do native_activity.stop_service(intent.intent)
+ fun stop_service(intent: Intent) do native_context.stop_service(intent.intent)
end
if id != null then
sys.jni_env.push_local_frame(8)
- var manager = app.native_activity.notification_manager
+ var manager = app.native_context.notification_manager
manager.cancel(tag.to_java_string, id)
self.id = null
--- /dev/null
+/* This file is part of NIT ( http://www.nitlanguage.org ).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package nit.app;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+// BroadcastReceiver launching NitService
+public class NitBroadcastReceiver extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ context.startService(new Intent(context, NitService.class));
+ }
+}
--- /dev/null
+/* This file is part of NIT ( http://www.nitlanguage.org ).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package nit.app;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+// Service implemented in Nit
+public class NitService extends Service {
+
+ protected int nitService = 0;
+
+ static {
+ System.loadLibrary("main");
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int id) {
+ return nitOnStartCommand(nitService, intent, flags, id);
+ }
+
+ @Override
+ public void onCreate() {
+ nitService = nitNewService();
+ nitOnCreate(nitService);
+ super.onCreate();
+ }
+
+ @Override
+ public void onDestroy() {
+ nitOnDestroy(nitService);
+ super.onDestroy();
+ }
+
+ @Override
+ public IBinder onBind(Intent arg) {
+ return null;
+ }
+
+ protected native int nitNewService();
+ protected native int nitOnStartCommand(int nitService, Intent intent, int flags, int id);
+ protected native void nitOnCreate(int nitService);
+ protected native void nitOnDestroy(int nitService);
+}
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Import this module to launch `Service` at device boot
+module at_boot is
+ extra_java_files "NitBroadcastReceiver.java"
+ android_manifest """
+<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
+"""
+ android_manifest_application """
+<receiver android:name="nit.app.NitBroadcastReceiver">
+ <intent-filter>
+ <action android:name="android.intent.action.BOOT_COMPLETED" />
+ </intent-filter>
+</receiver>
+"""
+end
+
+import service
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Android service support for _app.nit_ centered around the class `Service`
+module service is
+ extra_java_files "NitService.java"
+ android_manifest_application """<service android:name="nit.app.NitService"></service>"""
+end
+
+import android::nit_activity
+
+in "C" `{
+ // Nit's App running instance, declared in `nit_activity`
+ extern App global_app;
+
+ JNIEXPORT jint JNICALL Java_nit_app_NitService_nitNewService
+ (JNIEnv *env, jobject java_service)
+ {
+ // Pin a ref to the Java service in the Java GC
+ java_service = (*env)->NewGlobalRef(env, java_service);
+
+ // Create the service in Nit and pin it in the Nit GC
+ Service nit_service = App_register_service(global_app, java_service);
+
+ // FIXME replace the previous call to register_service by
+ // the following line when #1941 is fixed:
+ //Service nit_service = new_Service(java_service);
+
+ Service_incr_ref(nit_service);
+
+ return (jint)(void*)nit_service;
+ }
+
+ JNIEXPORT jint JNICALL Java_nit_app_NitService_nitOnStartCommand
+ (JNIEnv *env, jobject java_service, jint nit_service, jobject intent, jint flags, jint id)
+ {
+ return Service_on_start_command((Service)nit_service, intent, flags, id);
+ }
+
+ JNIEXPORT void JNICALL Java_nit_app_NitService_nitOnCreate
+ (JNIEnv *env, jobject java_service, jint nit_service)
+ {
+ Service_on_create((Service)nit_service);
+ }
+
+ JNIEXPORT void JNICALL Java_nit_app_NitService_nitOnDestroy
+ (JNIEnv *env, jobject java_service, jint nit_service)
+ {
+ Service_on_destroy((Service)nit_service);
+
+ // Unpin the service instances in both Nit and Java
+ java_service = Service_native((Service)nit_service);
+ (*env)->DeleteGlobalRef(env, java_service);
+ Service_decr_ref(nit_service);
+ }
+`}
+
+redef class App
+
+ # Current instance of `Service`, if any
+ var service: nullable Service = null
+
+ # Launch `Service` in the background, it will be set as `service` when ready
+ fun start_service do native_context.start_service
+
+ # Register a service from Java/C
+ #
+ # FIXME remove when #1941 is fixed
+ private fun register_service(java_service: NativeService): Service
+ do
+ return new Service(java_service.new_global_ref)
+ end
+
+ # Prioritize an activity context if one is running, fallback on a service
+ redef fun native_context
+ do
+ if activities.not_empty then return super
+
+ var service = service
+ assert service != null
+ return service.native
+ end
+
+ # Dummy method to force the compilation of callbacks from C
+ #
+ # The callbacks are used in the launch of a Nit service from C code.
+ private fun force_service_callbacks_in_c import Service, register_service,
+ Service.on_start_command, Service.on_create, Service.on_destroy, Service.native `{ `}
+
+ redef fun setup
+ do
+ super
+
+ # Call the dummy method to force their compilation in global compilation
+ force_service_callbacks_in_c
+ end
+end
+
+# Android service with its life-cycle callbacks
+class Service
+ # Java service with the Android context of this service
+ var native: NativeService
+
+ # The service has been created
+ fun on_create do app.service = self
+
+ # The service received a start command (may happen more then once per service)
+ #
+ # Should return either `start_sticky`, `start_not_sticky` or `start_redeliver_intent`.
+ fun on_start_command(intent: JavaObject, flags, id: Int): Int do return start_sticky
+
+ # The service is being destroyed
+ fun on_destroy do app.service = null
+end
+
+# Wrapper of Java class `android.app.Service`
+extern class NativeService in "Java" `{ android.app.Service `}
+ super NativeContext
+
+ redef fun new_global_ref import sys, Sys.jni_env `{
+ Sys sys = NativeService_sys(self);
+ JNIEnv *env = Sys_jni_env(sys);
+ return (*env)->NewGlobalRef(env, self);
+ `}
+end
+
+redef class NativeContext
+ private fun start_service in "Java" `{
+ android.content.Intent indent =
+ new android.content.Intent(self, nit.app.NitService.class);
+ self.startService(intent);
+ `}
+end
+
+# The service is explicitly started and stopped, it is restarted if stopped by the system
+#
+# Return value of `Service::on_start_command`.
+fun start_sticky: Int in "Java" `{ return android.app.Service.START_STICKY; `}
+
+# The service may be stopped by the system and will not be restarted
+#
+# Return value of `Service::on_start_command`.
+fun start_not_sticky: Int in "Java" `{ return android.app.Service.START_NOT_STICKY; `}
+
+# The service is explicitly started and stopped, it is restarted with the last intent if stopped by the system
+#
+# Return value of `Service::on_start_command`.
+fun start_redeliver_intent: Int in "Java" `{ return android.app.Service.START_REDELIVER_INTENT; `}
# Provides services to save and load data for the android platform
class SharedPreferences
- protected var context: NativeActivity
protected var shared_preferences: NativeSharedPreferences
protected var editor: NativeSharedPreferencesEditor
protected init(app: App, file_name: String, mode: Int)
do
- self.context = app.native_activity
sys.jni_env.push_local_frame(1)
- setup(file_name.to_java_string, mode)
+ setup(file_name.to_java_string, mode, app.native_context)
sys.jni_env.pop_local_frame
end
self.editor = editor.new_global_ref
end
- private fun setup(file_name: JavaString, mode: Int) import context, set_vars in "Java" `{
- Activity context = (Activity) SharedPreferences_context(self);
+ private fun setup(file_name: JavaString, mode: Int, context: NativeContext) import set_vars in "Java" `{
SharedPreferences sp;
// Uses default SharedPreferences if file_name is an empty String
if (file_name.equals("")) {
- sp = context.getPreferences((int)mode);
+ Activity activity = (Activity)context;
+ sp = activity.getPreferences((int)mode);
} else {
sp = context.getSharedPreferences(file_name, (int)mode);
}
redef class App
var shared_preferences: SharedPreferences is lazy do
- return new SharedPreferences.privately(self, "")
+ return new SharedPreferences.privately(self, "app.nit")
end
end
fun toast(message: String, is_long: Bool)
do
var jstr = message.to_java_string
- native_toast(jstr, is_long)
+ native_toast(jstr, is_long, native_activity)
jstr.delete_local_ref
end
- private fun native_toast(message: JavaString, is_long: Bool)
- import native_activity in "Java" `{
- final android.app.Activity context = App_native_activity(self);
+ private fun native_toast(message: JavaString, is_long: Bool, native_activity: NativeActivity) in "Java" `{
+ final android.app.Activity context = native_activity;
final CharSequence final_message = message;
final int duration = is_long? Toast.LENGTH_LONG: Toast.LENGTH_SHORT;
redef class App
# Get the handle to this device vibrator as a global ref
var vibrator: Vibrator is lazy do
- var v = vibrator_native(native_activity)
+ var v = vibrator_native(native_context)
return v.new_global_ref
end
- private fun vibrator_native(context: NativeActivity): Vibrator in "Java" `{
+ private fun vibrator_native(context: NativeContext): Vibrator in "Java" `{
android.os.Vibrator v = (android.os.Vibrator)
context.getSystemService(android.content.Context.VIBRATOR_SERVICE);
return v;
# Should the response content be deserialized from JSON?
var deserialize_json = true is writable
+ # Delay in seconds before sending this request
+ var delay = 0.0 is writable
+
redef fun start
do
before
redef fun main
do
+ var delay = delay
+ if delay > 0.0 then delay.sleep
+
var uri = rest_server_uri / rest_action
# Execute REST request
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
+ #include <string.h>
#include <sys/socket.h>
#include <event2/listener.h>
`}
in "C" `{
+
+// Protect callbacks for compatibility with light FFI
+#ifdef Connection_decr_ref
+
// Callback forwarded to 'Connection.write_callback'
static void c_write_cb(struct bufferevent *bev, Connection ctx) {
Connection_write_callback(ctx);
{
ConnectionFactory_accept_connection(ctx, listener, fd, address, socklen);
}
+#endif
+
`}
# Structure to hold information and state for a Libevent dispatch loop.
import nitni
import ffi
import naive_interpreter
+import pkgconfig
import debugger_socket # To linearize `ToolContext::init`
redef class ToolContext
n_extern_code_block.is_c) then return false
for mparam in mpropdef.msignature.mparameters do
- var mtype = mparam.mtype
- if not mtype.is_cprimitive then
+ if not mparam.mtype.is_cprimitive then
return false
end
end
+ var return_mtype = mpropdef.msignature.return_mtype
+ if return_mtype != null and not return_mtype.is_cprimitive then
+ return false
+ end
+
return true
end
end
var srcs = [for file in ccu.files do new ExternCFile(file, ""): ExternFile]
srcs.add_all mmodule.ffi_files
- if mmodule.pkgconfigs.not_empty then
- fatal(v, "NOT YET IMPLEMENTED annotation `pkgconfig`")
- return false
- end
-
+ # Compiler options specific to this module
var ldflags = mmodule.ldflags[""].join(" ")
- # TODO pkgconfig
+
+ # Protect pkg-config
+ var pkgconfigs = mmodule.pkgconfigs
+ var pkg_command = ""
+ if not pkgconfigs.is_empty then
+ var cmd = "which pkg-config >/dev/null"
+ if system(cmd) != 0 then
+ v.fatal "FFI Error: Command `pkg-config` not found. Please install it"
+ return false
+ end
+
+ for p in pkgconfigs do
+ cmd = "pkg-config --exists '{p}'"
+ if system(cmd) != 0 then
+ v.fatal "FFI Error: package {p} is not found by `pkg-config`. Please install it."
+ return false
+ end
+ end
+
+ pkg_command = "`pkg-config --cflags --libs {pkgconfigs.join(" ")}`"
+ end
# Compile each source file to an object file (or equivalent)
var object_files = new Array[String]
end
# Link everything in a shared library
- # TODO customize the compiler
- var cmd = "{v.c_compiler} -Wall -shared -o {foreign_code_lib_path} {object_files.join(" ")} {ldflags}"
- if sys.system(cmd) != 0 then
+ var cmd = "{v.c_compiler} -Wall -shared -o {foreign_code_lib_path} {object_files.join(" ")} {ldflags} {pkg_command}"
+ if system(cmd) != 0 then
v.fatal "FFI Error: Failed to link native code using `{cmd}`"
return false
end
ui_test
test_text_stat
nitsaf_args
+test_ffi_c_lots_of_refs
ui_test
test_text_stat
nitsaf.args
+test_ffi_c_lots_of_refs