mirror of
				https://github.com/liberatedsystems/openCom-Companion.git
				synced 2025-07-08 05:07:21 +02:00 
			
		
		
		
	Android service setup
This commit is contained in:
		
							parent
							
								
									64567710c3
								
							
						
					
					
						commit
						536c77396f
					
				@ -22,6 +22,10 @@ patchsdl:
 | 
			
		||||
	cp patches/HIDDeviceUSB.java .buildozer/android/platform/build-arm64-v8a/dists/sideband/src/main/java/org/libsdl/app/HIDDeviceUSB.java
 | 
			
		||||
	cp patches/HIDDeviceUSB.java .buildozer/android/platform/build-arm64-v8a/dists/sideband/jni/SDL/android-project/app/src/main/java/org/libsdl/app/HIDDeviceUSB.java
 | 
			
		||||
 | 
			
		||||
	cp patches/PythonService.java .buildozer/android/platform/python-for-android/pythonforandroid/bootstraps/common/build/src/main/java/org/kivy/android/PythonService.java
 | 
			
		||||
	cp patches/PythonService.java .buildozer/android/platform/build-arm64-v8a/build/bootstrap_builds/sdl2/src/main/java/org/kivy/android/PythonService.java
 | 
			
		||||
	cp patches/PythonService.java .buildozer/android/platform/build-arm64-v8a/dists/sideband/src/main/java/org/kivy/android/PythonService.java
 | 
			
		||||
 | 
			
		||||
injectxml:
 | 
			
		||||
	mkdir -p .buildozer/android/platform/build-arm64-v8a/dists/sideband/src/main/res/xml
 | 
			
		||||
	mkdir -p .buildozer/android/platform/build-arm64-v8a/dists/sideband/templates
 | 
			
		||||
 | 
			
		||||
@ -25,7 +25,7 @@ android.presplash_color = #00000000
 | 
			
		||||
orientation = all
 | 
			
		||||
fullscreen = 0
 | 
			
		||||
 | 
			
		||||
android.permissions = INTERNET,POST_NOTIFICATIONS,WAKE_LOCK,FOREGROUND_SERVICE
 | 
			
		||||
android.permissions = INTERNET,POST_NOTIFICATIONS,WAKE_LOCK,FOREGROUND_SERVICE,CHANGE_WIFI_MULTICAST_STATE
 | 
			
		||||
android.api = 30
 | 
			
		||||
android.minapi = 27
 | 
			
		||||
android.ndk = 19b
 | 
			
		||||
@ -34,7 +34,7 @@ android.accept_sdk_license = True
 | 
			
		||||
android.arch = arm64-v8a
 | 
			
		||||
#android.logcat_filters = *:S python:D
 | 
			
		||||
 | 
			
		||||
# services = sidebandservice:services/sidebandservice.py:foreground
 | 
			
		||||
services = sidebandservice:services/sidebandservice.py:foreground
 | 
			
		||||
android.manifest.intent_filters = patches/intent-filter.xml
 | 
			
		||||
 | 
			
		||||
[buildozer]
 | 
			
		||||
 | 
			
		||||
@ -96,6 +96,7 @@ class SidebandApp(MDApp):
 | 
			
		||||
                self.guide_action()
 | 
			
		||||
 | 
			
		||||
        self.app_state = SidebandApp.ACTIVE
 | 
			
		||||
        self.start_android_service()
 | 
			
		||||
 | 
			
		||||
    def start_android_service(self):
 | 
			
		||||
        service = autoclass('io.unsigned.sideband.ServiceSidebandservice')
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										209
									
								
								sbapp/patches/PythonService.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										209
									
								
								sbapp/patches/PythonService.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,209 @@
 | 
			
		||||
package org.kivy.android;
 | 
			
		||||
 | 
			
		||||
import android.os.Build;
 | 
			
		||||
import java.lang.reflect.Method;
 | 
			
		||||
import java.lang.reflect.InvocationTargetException;
 | 
			
		||||
import android.app.Service;
 | 
			
		||||
import android.os.IBinder;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.util.Log;
 | 
			
		||||
import android.app.Notification;
 | 
			
		||||
import android.app.PendingIntent;
 | 
			
		||||
import android.os.Process;
 | 
			
		||||
import java.io.File;
 | 
			
		||||
 | 
			
		||||
//imports for channel definition
 | 
			
		||||
import android.app.NotificationManager;
 | 
			
		||||
import android.app.NotificationChannel;
 | 
			
		||||
import android.graphics.Color;
 | 
			
		||||
import android.graphics.BitmapFactory;
 | 
			
		||||
import android.graphics.Bitmap;
 | 
			
		||||
import android.graphics.drawable.Icon;
 | 
			
		||||
 | 
			
		||||
import android.net.wifi.WifiManager;
 | 
			
		||||
import android.net.wifi.WifiManager.MulticastLock;
 | 
			
		||||
 | 
			
		||||
public class PythonService extends Service implements Runnable {
 | 
			
		||||
 | 
			
		||||
    // Thread for Python code
 | 
			
		||||
    private Thread pythonThread = null;
 | 
			
		||||
 | 
			
		||||
    // Python environment variables
 | 
			
		||||
    private String androidPrivate;
 | 
			
		||||
    private String androidArgument;
 | 
			
		||||
    private String pythonName;
 | 
			
		||||
    private String pythonHome;
 | 
			
		||||
    private String pythonPath;
 | 
			
		||||
    private String serviceEntrypoint;
 | 
			
		||||
    // Argument to pass to Python code,
 | 
			
		||||
    private String pythonServiceArgument;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public static PythonService mService = null;
 | 
			
		||||
    private Intent startIntent = null;
 | 
			
		||||
 | 
			
		||||
    private boolean autoRestartService = false;
 | 
			
		||||
 | 
			
		||||
    public void setAutoRestartService(boolean restart) {
 | 
			
		||||
        autoRestartService = restart;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int startType() {
 | 
			
		||||
        return START_NOT_STICKY;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public IBinder onBind(Intent arg0) {
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onCreate() {
 | 
			
		||||
        super.onCreate();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int onStartCommand(Intent intent, int flags, int startId) {
 | 
			
		||||
        if (pythonThread != null) {
 | 
			
		||||
            Log.v("python service", "service exists, do not start again");
 | 
			
		||||
            return startType();
 | 
			
		||||
        }
 | 
			
		||||
    //intent is null if OS restarts a STICKY service
 | 
			
		||||
        if (intent == null) {
 | 
			
		||||
            Context context = getApplicationContext();
 | 
			
		||||
            intent = getThisDefaultIntent(context, "");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        startIntent = intent;
 | 
			
		||||
        Bundle extras = intent.getExtras();
 | 
			
		||||
        androidPrivate = extras.getString("androidPrivate");
 | 
			
		||||
        androidArgument = extras.getString("androidArgument");
 | 
			
		||||
        serviceEntrypoint = extras.getString("serviceEntrypoint");
 | 
			
		||||
        pythonName = extras.getString("pythonName");
 | 
			
		||||
        pythonHome = extras.getString("pythonHome");
 | 
			
		||||
        pythonPath = extras.getString("pythonPath");
 | 
			
		||||
        boolean serviceStartAsForeground = (
 | 
			
		||||
            extras.getString("serviceStartAsForeground").equals("true")
 | 
			
		||||
        );
 | 
			
		||||
        pythonServiceArgument = extras.getString("pythonServiceArgument");
 | 
			
		||||
        pythonThread = new Thread(this);
 | 
			
		||||
        pythonThread.start();
 | 
			
		||||
 | 
			
		||||
        if (serviceStartAsForeground) {
 | 
			
		||||
            doStartForeground(extras);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return startType();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected int getServiceId() {
 | 
			
		||||
        return 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected Intent getThisDefaultIntent(Context ctx, String pythonServiceArgument) {
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected void doStartForeground(Bundle extras) {
 | 
			
		||||
        String serviceTitle = extras.getString("serviceTitle");
 | 
			
		||||
        String serviceDescription = extras.getString("serviceDescription");
 | 
			
		||||
        Notification notification;
 | 
			
		||||
        Context context = getApplicationContext();
 | 
			
		||||
        Intent contextIntent = new Intent(context, PythonActivity.class);
 | 
			
		||||
        PendingIntent pIntent = PendingIntent.getActivity(context, 0, contextIntent,
 | 
			
		||||
            PendingIntent.FLAG_UPDATE_CURRENT);
 | 
			
		||||
 | 
			
		||||
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
 | 
			
		||||
            notification = new Notification(
 | 
			
		||||
                context.getApplicationInfo().icon, serviceTitle, System.currentTimeMillis());
 | 
			
		||||
            try {
 | 
			
		||||
                // prevent using NotificationCompat, this saves 100kb on apk
 | 
			
		||||
                Method func = notification.getClass().getMethod(
 | 
			
		||||
                    "setLatestEventInfo", Context.class, CharSequence.class,
 | 
			
		||||
                    CharSequence.class, PendingIntent.class);
 | 
			
		||||
                func.invoke(notification, context, serviceTitle, serviceDescription, pIntent);
 | 
			
		||||
            } catch (NoSuchMethodException | IllegalAccessException |
 | 
			
		||||
                     IllegalArgumentException | InvocationTargetException e) {
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            // for android 8+ we need to create our own channel
 | 
			
		||||
            // https://stackoverflow.com/questions/47531742/startforeground-fail-after-upgrade-to-android-8-1
 | 
			
		||||
            String NOTIFICATION_CHANNEL_ID = "org.kivy.p4a";    //TODO: make this configurable
 | 
			
		||||
            String channelName = "Background Service";                //TODO: make this configurable
 | 
			
		||||
            NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName, 
 | 
			
		||||
                NotificationManager.IMPORTANCE_NONE);
 | 
			
		||||
            
 | 
			
		||||
            chan.setLightColor(Color.BLUE);
 | 
			
		||||
            chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
 | 
			
		||||
            NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
 | 
			
		||||
            manager.createNotificationChannel(chan);
 | 
			
		||||
 | 
			
		||||
            Notification.Builder builder = new Notification.Builder(context, NOTIFICATION_CHANNEL_ID);
 | 
			
		||||
            builder.setContentTitle("Sideband");
 | 
			
		||||
            builder.setContentText("Reticulum Running");
 | 
			
		||||
            builder.setContentIntent(pIntent);
 | 
			
		||||
 | 
			
		||||
            Bitmap icon_bitmap = BitmapFactory.decodeFile("/data/user/0/io.unsigned.sideband/files/app/assets/notification_icon.png");
 | 
			
		||||
            Icon service_icon = Icon.createWithBitmap(icon_bitmap);
 | 
			
		||||
            // builder.setSmallIcon(context.getApplicationInfo().icon);
 | 
			
		||||
            builder.setSmallIcon(service_icon);
 | 
			
		||||
            notification = builder.build();
 | 
			
		||||
 | 
			
		||||
            // Log.v("python service", "Testing WM locks...");
 | 
			
		||||
            // WifiManager wm = (WifiManager) getSystemService(Context.WIFI_SERVICE);
 | 
			
		||||
            // MulticastLock ml = wm.createMulticastLock("sometag");
 | 
			
		||||
            // Log.v("python service", "WM locks done");
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
        startForeground(getServiceId(), notification);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onDestroy() {
 | 
			
		||||
        super.onDestroy();
 | 
			
		||||
        pythonThread = null;
 | 
			
		||||
        if (autoRestartService && startIntent != null) {
 | 
			
		||||
            Log.v("python service", "service restart requested");
 | 
			
		||||
            startService(startIntent);
 | 
			
		||||
        }
 | 
			
		||||
        Process.killProcess(Process.myPid());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Stops the task gracefully when killed.
 | 
			
		||||
     * Calling stopSelf() will trigger a onDestroy() call from the system.
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onTaskRemoved(Intent rootIntent) {
 | 
			
		||||
        super.onTaskRemoved(rootIntent);
 | 
			
		||||
        //sticky servcie runtime/restart is managed by the OS. leave it running when app is closed
 | 
			
		||||
        if (startType() != START_STICKY) {
 | 
			
		||||
            stopSelf();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void run(){
 | 
			
		||||
        String app_root =  getFilesDir().getAbsolutePath() + "/app";
 | 
			
		||||
        File app_root_file = new File(app_root);
 | 
			
		||||
        PythonUtil.loadLibraries(app_root_file,
 | 
			
		||||
            new File(getApplicationInfo().nativeLibraryDir));
 | 
			
		||||
        this.mService = this;
 | 
			
		||||
        nativeStart(
 | 
			
		||||
            androidPrivate, androidArgument,
 | 
			
		||||
            serviceEntrypoint, pythonName,
 | 
			
		||||
            pythonHome, pythonPath,
 | 
			
		||||
            pythonServiceArgument);
 | 
			
		||||
        stopSelf();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Native part
 | 
			
		||||
    public static native void nativeStart(
 | 
			
		||||
            String androidPrivate, String androidArgument,
 | 
			
		||||
            String serviceEntrypoint, String pythonName,
 | 
			
		||||
            String pythonHome, String pythonPath,
 | 
			
		||||
            String pythonServiceArgument);
 | 
			
		||||
}
 | 
			
		||||
@ -1,14 +1,44 @@
 | 
			
		||||
import time
 | 
			
		||||
import RNS
 | 
			
		||||
from sideband.core import SidebandCore
 | 
			
		||||
from os import environ
 | 
			
		||||
from jnius import autoclass, cast
 | 
			
		||||
 | 
			
		||||
Context = autoclass('android.content.Context')
 | 
			
		||||
 | 
			
		||||
class sidebandservice():
 | 
			
		||||
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        self.argument = environ.get('PYTHON_SERVICE_ARGUMENT', '')
 | 
			
		||||
        self.multicast_lock = None
 | 
			
		||||
        self.wake_lock = None
 | 
			
		||||
 | 
			
		||||
        self.service = autoclass('org.kivy.android.PythonService').mService
 | 
			
		||||
        self.app_context = self.service.getApplication().getApplicationContext()
 | 
			
		||||
        self.wifi_manager = self.app_context.getSystemService(Context.WIFI_SERVICE)
 | 
			
		||||
        # The returned instance is an android.net.wifi.WifiManager
 | 
			
		||||
        
 | 
			
		||||
        print("Sideband Service created")
 | 
			
		||||
        self.take_locks()
 | 
			
		||||
        self.run()
 | 
			
		||||
 | 
			
		||||
    def take_locks(self):
 | 
			
		||||
        if self.multicast_lock == None:
 | 
			
		||||
            self.multicast_lock = self.wifi_manager.createMulticastLock("sideband_service")
 | 
			
		||||
 | 
			
		||||
        if not self.multicast_lock.isHeld():
 | 
			
		||||
            RNS.log("Taking multicast lock")
 | 
			
		||||
            self.multicast_lock.acquire()
 | 
			
		||||
            RNS.log("Took lock")
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
    def release_locks():
 | 
			
		||||
        if not self.multicast_lock == None and self.multicast_lock.isHeld():
 | 
			
		||||
            self.multicast_lock.release()
 | 
			
		||||
 | 
			
		||||
    def run(self):
 | 
			
		||||
        while True:
 | 
			
		||||
            print("Service ping")
 | 
			
		||||
            time.sleep(3)
 | 
			
		||||
            time.sleep(5)
 | 
			
		||||
 | 
			
		||||
sbs = sidebandservice()
 | 
			
		||||
@ -705,7 +705,8 @@ class SidebandCore():
 | 
			
		||||
            self.lxmf_destination.announce()
 | 
			
		||||
 | 
			
		||||
    def __start_jobs_immediate(self):
 | 
			
		||||
        self.reticulum = RNS.Reticulum(configdir=self.rns_configdir)
 | 
			
		||||
        # TODO: Reset loglevel
 | 
			
		||||
        self.reticulum = RNS.Reticulum(configdir=self.rns_configdir, loglevel=7)
 | 
			
		||||
        RNS.log("Reticulum started, activating LXMF...")
 | 
			
		||||
 | 
			
		||||
        if RNS.vendor.platformutils.get_platform() == "android":
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user