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')
|
||||
|
|
|
@ -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…
Reference in New Issue