Android service setup

This commit is contained in:
Mark Qvist 2022-09-20 17:28:39 +02:00
parent 64567710c3
commit 536c77396f
6 changed files with 249 additions and 4 deletions

View File

@ -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

View File

@ -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]

View File

@ -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')

View 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);
}

View File

@ -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()

View File

@ -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":