From c65af4c4b5712d02a6e272e188920cd452d44ec0 Mon Sep 17 00:00:00 2001 From: LoveSy Date: Tue, 16 Feb 2021 01:32:30 +0800 Subject: [PATCH] Add application service --- .../lspd/service/ILSPApplicationService.aidl | 9 ++ .../lsposed/lspd/service/ILSPosedService.aidl | 7 + .../io/github/lsposed/lspd/core/Main.java | 10 +- .../lspd/hooker/XposedInstallerHooker.java | 2 +- .../lsposed/lspd/service/BridgeService.java | 131 ++++++------------ .../lsposed/lspd/service/ConfigManager.java | 38 ++++- .../lspd/service/LSPApplicationService.java | 51 +++++++ .../lspd/service/LSPManagerService.java | 21 +-- .../lspd/service/LSPModuleService.java | 22 +++ .../lsposed/lspd/service/LSPosedService.java | 28 ++-- .../github/lsposed/lspd/service/Service.java | 21 --- .../lsposed/lspd/service/ServiceManager.java | 39 ++++++ 12 files changed, 223 insertions(+), 156 deletions(-) create mode 100644 core/src/main/aidl/io/github/lsposed/lspd/service/ILSPApplicationService.aidl create mode 100644 core/src/main/aidl/io/github/lsposed/lspd/service/ILSPosedService.aidl create mode 100644 core/src/main/java/io/github/lsposed/lspd/service/LSPApplicationService.java create mode 100644 core/src/main/java/io/github/lsposed/lspd/service/LSPModuleService.java delete mode 100644 core/src/main/java/io/github/lsposed/lspd/service/Service.java create mode 100644 core/src/main/java/io/github/lsposed/lspd/service/ServiceManager.java diff --git a/core/src/main/aidl/io/github/lsposed/lspd/service/ILSPApplicationService.aidl b/core/src/main/aidl/io/github/lsposed/lspd/service/ILSPApplicationService.aidl new file mode 100644 index 00000000..e1e63af3 --- /dev/null +++ b/core/src/main/aidl/io/github/lsposed/lspd/service/ILSPApplicationService.aidl @@ -0,0 +1,9 @@ +package io.github.lsposed.lspd.service; + +interface ILSPApplicationService { + void registerHeartBeat(IBinder handle) = 1; + + IBinder requestModuleBinder() = 2; + + IBinder requestManagerBinder() = 3; +} diff --git a/core/src/main/aidl/io/github/lsposed/lspd/service/ILSPosedService.aidl b/core/src/main/aidl/io/github/lsposed/lspd/service/ILSPosedService.aidl new file mode 100644 index 00000000..38983988 --- /dev/null +++ b/core/src/main/aidl/io/github/lsposed/lspd/service/ILSPosedService.aidl @@ -0,0 +1,7 @@ +package io.github.lsposed.lspd.service; + +import io.github.lsposed.lspd.service.ILSPApplicationService; + +interface ILSPosedService { + ILSPApplicationService requestApplicationService(int uid, int pid) = 1; +} \ No newline at end of file diff --git a/core/src/main/java/io/github/lsposed/lspd/core/Main.java b/core/src/main/java/io/github/lsposed/lspd/core/Main.java index 3499d1cc..ab7ff5b7 100644 --- a/core/src/main/java/io/github/lsposed/lspd/core/Main.java +++ b/core/src/main/java/io/github/lsposed/lspd/core/Main.java @@ -22,18 +22,16 @@ package io.github.lsposed.lspd.core; import android.annotation.SuppressLint; import android.content.Context; -import android.os.ServiceManager; import android.util.Log; import android.ddm.DdmHandleAppName; import io.github.lsposed.common.KeepAll; -import io.github.lsposed.lspd.service.LSPosedService; -import io.github.lsposed.lspd.service.Service; +import io.github.lsposed.lspd.service.ServiceManager; import io.github.lsposed.lspd.util.Utils; import java.util.concurrent.atomic.AtomicReference; -import static io.github.lsposed.lspd.service.Service.TAG; +import static io.github.lsposed.lspd.service.ServiceManager.TAG; @SuppressLint("DefaultLocale") public class Main implements KeepAll { @@ -115,7 +113,7 @@ public class Main implements KeepAll { } private static void waitSystemService(String name) { - while (ServiceManager.getService(name) == null) { + while (android.os.ServiceManager.getService(name) == null) { try { Log.i(TAG, "service " + name + " is not started, wait 1s."); Thread.sleep(1000); @@ -136,6 +134,6 @@ public class Main implements KeepAll { waitSystemService(Context.USER_SERVICE); waitSystemService(Context.APP_OPS_SERVICE); - Service.start(); + ServiceManager.start(); } } diff --git a/core/src/main/java/io/github/lsposed/lspd/hooker/XposedInstallerHooker.java b/core/src/main/java/io/github/lsposed/lspd/hooker/XposedInstallerHooker.java index 7e12ad06..fca2bb20 100644 --- a/core/src/main/java/io/github/lsposed/lspd/hooker/XposedInstallerHooker.java +++ b/core/src/main/java/io/github/lsposed/lspd/hooker/XposedInstallerHooker.java @@ -116,7 +116,7 @@ public class XposedInstallerHooker { } }); Class serviceClass = XposedHelpers.findClass("io.github.lsposed.manager.receivers.LSPosedManagerServiceClient", classLoader); - XposedHelpers.setStaticObjectField(serviceClass, "binder", BridgeService.requireBinder()); +// XposedHelpers.setStaticObjectField(serviceClass, "binder", BridgeService.requireBinder()); Utils.logI("Hooked LSPosed Manager"); } catch (Throwable t) { diff --git a/core/src/main/java/io/github/lsposed/lspd/service/BridgeService.java b/core/src/main/java/io/github/lsposed/lspd/service/BridgeService.java index feb87997..cd231a45 100644 --- a/core/src/main/java/io/github/lsposed/lspd/service/BridgeService.java +++ b/core/src/main/java/io/github/lsposed/lspd/service/BridgeService.java @@ -13,11 +13,7 @@ import androidx.annotation.Nullable; import java.lang.reflect.Field; import java.util.Map; -import io.github.lsposed.lspd.ILSPManagerService; -import io.github.xposed.xposedservice.IXposedService; - -import static android.os.Binder.getCallingUid; -import static io.github.lsposed.lspd.service.Service.TAG; +import static io.github.lsposed.lspd.service.ServiceManager.TAG; public class BridgeService { private static final int TRANSACTION_CODE = ('_' << 24) | ('L' << 16) | ('S' << 8) | 'P'; @@ -26,20 +22,19 @@ public class BridgeService { enum ACTION { ACTION_SEND_BINDER, - ACTION_SEND_MANAGER_BINDER, ACTION_GET_BINDER, } + // for client private static IBinder serviceBinder = null; - private static IXposedService service = null; - - private static IBinder managerBinder = null; - private static ILSPManagerService manager = null; + private static ILSPosedService service = null; + // for service private static final IBinder.DeathRecipient BRIDGE_SERVICE_DEATH_RECIPIENT = () -> { Log.i(TAG, "service " + SERVICE_NAME + " is dead. "); try { + @SuppressWarnings("JavaReflectionMemberAccess") Field field = ServiceManager.class.getDeclaredField("sServiceManager"); field.setAccessible(true); field.set(null, null); @@ -57,15 +52,13 @@ public class BridgeService { Log.w(TAG, "clear ServiceManager: " + Log.getStackTraceString(e)); } - sendToBridge(ACTION.ACTION_SEND_BINDER, serviceBinder, true); - sendToBridge(ACTION.ACTION_SEND_MANAGER_BINDER, managerBinder, true); + sendToBridge(serviceBinder, true); }; + // for client private static final IBinder.DeathRecipient LSPSERVICE_DEATH_RECIPIENT = () -> { serviceBinder = null; service = null; - managerBinder = null; - manager = null; Log.e(TAG, "service is dead"); }; @@ -78,7 +71,8 @@ public class BridgeService { private static Listener listener; - private static void sendToBridge(ACTION action, IBinder binder, boolean isRestart) { + // For service + private static void sendToBridge(IBinder binder, boolean isRestart) { IBinder bridgeService; do { bridgeService = ServiceManager.getService(SERVICE_NAME); @@ -104,7 +98,7 @@ public class BridgeService { bridgeService.linkToDeath(BRIDGE_SERVICE_DEATH_RECIPIENT, 0); } catch (Throwable e) { Log.w(TAG, "linkToDeath " + Log.getStackTraceString(e)); - sendToBridge(action, binder, false); + sendToBridge(binder, false); return; } @@ -115,7 +109,7 @@ public class BridgeService { for (int i = 0; i < 3; i++) { try { data.writeInterfaceToken(DESCRIPTOR); - data.writeInt(action.ordinal()); + data.writeInt(ACTION.ACTION_SEND_BINDER.ordinal()); Log.v(TAG, "binder " + binder.toString()); data.writeStrongBinder(binder); res = bridgeService.transact(TRANSACTION_CODE, data, reply, 0); @@ -142,36 +136,24 @@ public class BridgeService { } } - private static void receiveFromBridge(ACTION action, IBinder binder) { + // For client + private static void receiveFromBridge(IBinder binder) { if (binder == null) { Log.e(TAG, "received empty binder"); return; } - if(action == ACTION.ACTION_SEND_MANAGER_BINDER) { - if (managerBinder != null) { - managerBinder.unlinkToDeath(LSPSERVICE_DEATH_RECIPIENT, 0); - } + if (serviceBinder == null) { + PackageReceiver.register(); + } else { + serviceBinder.unlinkToDeath(LSPSERVICE_DEATH_RECIPIENT, 0); + } - managerBinder = binder; - manager = LSPManagerService.Stub.asInterface(managerBinder); - try { - managerBinder.linkToDeath(LSPSERVICE_DEATH_RECIPIENT, 0); - } catch (RemoteException ignored) { - } - } else if (action == ACTION.ACTION_SEND_BINDER) { - if (serviceBinder == null) { - PackageReceiver.register(); - } else { - serviceBinder.unlinkToDeath(LSPSERVICE_DEATH_RECIPIENT, 0); - } - - serviceBinder = binder; - service = IXposedService.Stub.asInterface(serviceBinder); - try { - serviceBinder.linkToDeath(LSPSERVICE_DEATH_RECIPIENT, 0); - } catch (RemoteException ignored) { - } + serviceBinder = binder; + service = ILSPosedService.Stub.asInterface(serviceBinder); + try { + serviceBinder.linkToDeath(LSPSERVICE_DEATH_RECIPIENT, 0); + } catch (RemoteException ignored) { } Log.i(TAG, "binder received"); @@ -181,48 +163,13 @@ public class BridgeService { BridgeService.listener = listener; BridgeService.service = service; BridgeService.serviceBinder = service.asBinder(); - sendToBridge(ACTION.ACTION_SEND_BINDER, serviceBinder, false); + sendToBridge(serviceBinder, false); } - public static void send(LSPManagerService service, Listener listener) { - BridgeService.listener = listener; - BridgeService.manager = service; - BridgeService.managerBinder = service.asBinder(); - sendToBridge(ACTION.ACTION_SEND_MANAGER_BINDER, managerBinder, false); - } - - public static IXposedService getService() { + public static ILSPosedService getService() { return service; } - public static ILSPManagerService getManager() { - return manager; - } - - public static IBinder requireBinder() { - IBinder binder = ServiceManager.getService(SERVICE_NAME); - if (binder == null) return null; - - Parcel data = Parcel.obtain(); - Parcel reply = Parcel.obtain(); - try { - data.writeInterfaceToken(DESCRIPTOR); - data.writeInt(ACTION.ACTION_GET_BINDER.ordinal()); - binder.transact(TRANSACTION_CODE, data, reply, 0); - reply.readException(); - IBinder received = reply.readStrongBinder(); - if (received != null) { - return received; - } - } catch (Throwable e) { - e.printStackTrace(); - } finally { - data.recycle(); - reply.recycle(); - } - return null; - } - @SuppressWarnings({"unused", "RedundantSuppression"}) public static boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) { data.enforceInterface(DESCRIPTOR); @@ -231,10 +178,9 @@ public class BridgeService { Log.d(TAG, "onTransact: action=" + action + ", callingUid=" + Binder.getCallingUid() + ", callingPid=" + Binder.getCallingPid()); switch (action) { - case ACTION_SEND_BINDER: - case ACTION_SEND_MANAGER_BINDER: { + case ACTION_SEND_BINDER: { if (Binder.getCallingUid() == 0) { - receiveFromBridge(action, data.readStrongBinder()); + receiveFromBridge(data.readStrongBinder()); if (reply != null) { reply.writeNoException(); } @@ -243,22 +189,25 @@ public class BridgeService { break; } case ACTION_GET_BINDER: { + IBinder binder = null; try { - if (!PackageService.isInstaller(getCallingUid())) return false; - } catch (Throwable ignored) { - return false; + binder = service.requestApplicationService(Binder.getCallingUid(), Binder.getCallingPid()).asBinder(); + } catch (RemoteException e) { + Log.e(TAG, Log.getStackTraceString(e)); } - if (reply != null) { + if (binder != null && reply != null) { reply.writeNoException(); - Log.d(TAG, "saved binder is " + managerBinder.toString()); - reply.writeStrongBinder(managerBinder); + Log.d(TAG, "got binder is " + binder); + reply.writeStrongBinder(binder); + return true; } - return true; + return false; } } return false; } + @SuppressWarnings({"unused", "RedundantSuppression"}) public static boolean execTransact(int code, long dataObj, long replyObj, int flags) { Log.d(TAG, String.valueOf(code)); if (code != TRANSACTION_CODE) return false; @@ -266,7 +215,7 @@ public class BridgeService { Parcel data = ParcelUtils.fromNativePointer(dataObj); Parcel reply = ParcelUtils.fromNativePointer(replyObj); - if (data == null) { + if (data == null || reply == null) { return false; } @@ -290,8 +239,8 @@ public class BridgeService { } if (res) { - if (data != null) data.recycle(); - if (reply != null) reply.recycle(); + data.recycle(); + reply.recycle(); } return res; diff --git a/core/src/main/java/io/github/lsposed/lspd/service/ConfigManager.java b/core/src/main/java/io/github/lsposed/lspd/service/ConfigManager.java index 99b0913c..4353b75e 100644 --- a/core/src/main/java/io/github/lsposed/lspd/service/ConfigManager.java +++ b/core/src/main/java/io/github/lsposed/lspd/service/ConfigManager.java @@ -6,11 +6,13 @@ import android.database.sqlite.SQLiteQueryBuilder; import android.database.sqlite.SQLiteStatement; import android.database.sqlite.SQLiteDatabase; import android.os.FileObserver; +import android.os.ParcelFileDescriptor; import android.util.Log; import androidx.annotation.Nullable; import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; import java.nio.file.Files; import java.nio.file.StandardOpenOption; @@ -20,12 +22,16 @@ import java.util.List; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import static io.github.lsposed.lspd.service.Service.TAG; +import static io.github.lsposed.lspd.service.ServiceManager.TAG; +// This config manager assume uid won't change when our service is off. +// Otherwise, user should maintain it manually. +// TODO: manager package name supports public class ConfigManager { static ConfigManager instance = null; - final private File configPath = new File("/data/adb/lspd/config"); + final private File basePath = new File("/data/adb/lspd"); + final private File configPath = new File(basePath, "config"); final private SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(new File(configPath, "modules_config.db"), null); final private File resourceHookSwitch = new File(configPath, "enable_resources"); @@ -41,6 +47,10 @@ public class ConfigManager { // only check on boot final private boolean isPermissive; + final private File logPath = new File(basePath, "log"); + final private File modulesLogPath = new File(logPath, "modules.log"); + final private File verboseLogPath = new File(logPath, "verbose.log"); + final FileObserver configObserver = new FileObserver(configPath) { @Override public void onEvent(int event, @Nullable String path) { @@ -154,7 +164,7 @@ public class ConfigManager { ContentValues values = new ContentValues(); values.put("apk_path", apkPath); int count = db.updateWithOnConflict("enabled_modules", values, "package_name = ?", new String[]{packageName}, SQLiteDatabase.CONFLICT_REPLACE); - if (count > 1) { + if (count >= 1) { cacheScopes(); return true; } @@ -214,9 +224,9 @@ public class ConfigManager { return true; } - public boolean removeApps(int uid) { + public boolean removeApp(int uid) { int count = db.delete("scope", "uid = ?", new String[]{String.valueOf(uid)}); - if (count > 1) { + if (count >= 1) { cacheScopes(); return true; } @@ -250,4 +260,22 @@ public class ConfigManager { public int variant() { return variant; } + + public ParcelFileDescriptor getModulesLog() { + try { + return ParcelFileDescriptor.open(modulesLogPath, ParcelFileDescriptor.MODE_READ_ONLY); + } catch (FileNotFoundException e) { + Log.e(TAG, Log.getStackTraceString(e)); + return null; + } + } + + public ParcelFileDescriptor getVerboseLog() { + try { + return ParcelFileDescriptor.open(verboseLogPath, ParcelFileDescriptor.MODE_READ_ONLY); + } catch (FileNotFoundException e) { + Log.e(TAG, Log.getStackTraceString(e)); + return null; + } + } } diff --git a/core/src/main/java/io/github/lsposed/lspd/service/LSPApplicationService.java b/core/src/main/java/io/github/lsposed/lspd/service/LSPApplicationService.java new file mode 100644 index 00000000..2f60945d --- /dev/null +++ b/core/src/main/java/io/github/lsposed/lspd/service/LSPApplicationService.java @@ -0,0 +1,51 @@ +package io.github.lsposed.lspd.service; + +import android.os.Binder; +import android.os.IBinder; +import android.os.RemoteException; +import android.util.Log; +import android.util.Pair; + +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import static io.github.lsposed.lspd.service.ServiceManager.TAG; + +public class LSPApplicationService extends ILSPApplicationService.Stub { + // + private final static Set> cache = ConcurrentHashMap.newKeySet(); + private final static Set handles = ConcurrentHashMap.newKeySet(); + + @Override + public void registerHeartBeat(IBinder handle) throws RemoteException { + handles.add(handle); + int uid = Binder.getCallingUid(); + int pid = Binder.getCallingPid(); + cache.add(new Pair<>(uid, pid)); + handle.linkToDeath(() -> { + Log.d(TAG, "pid=" + pid + " uid=" + uid + " is dead."); + cache.remove(new Pair<>(uid, pid)); + handles.remove(handle); + }, 0); + } + + // TODO: check if module + @Override + public IBinder requestModuleBinder() { + if (!hasRegister(Binder.getCallingUid(), Binder.getCallingPid())) + return null; + return ServiceManager.getModuleService(); + } + + // TODO: check if manager + @Override + public IBinder requestManagerBinder() { + if (!hasRegister(Binder.getCallingUid(), Binder.getCallingPid())) + return null; + return ServiceManager.getManagerService(); + } + + public boolean hasRegister(int uid, int pid) { + return cache.contains(new Pair<>(uid, pid)); + } +} diff --git a/core/src/main/java/io/github/lsposed/lspd/service/LSPManagerService.java b/core/src/main/java/io/github/lsposed/lspd/service/LSPManagerService.java index 2ada73fe..b7fc0099 100644 --- a/core/src/main/java/io/github/lsposed/lspd/service/LSPManagerService.java +++ b/core/src/main/java/io/github/lsposed/lspd/service/LSPManagerService.java @@ -3,33 +3,16 @@ package io.github.lsposed.lspd.service; import android.content.pm.PackageInfo; import android.os.IBinder; import android.os.RemoteException; -import android.util.Log; import de.robv.android.xposed.XposedBridge; import io.github.lsposed.lspd.ILSPManagerService; import io.github.lsposed.lspd.utils.ParceledListSlice; -import static io.github.lsposed.lspd.service.Service.TAG; - public class LSPManagerService extends ILSPManagerService.Stub { - public LSPManagerService() { - BridgeService.send(this, new BridgeService.Listener() { - @Override - public void onSystemServerRestarted() { - Log.w(TAG, "system restarted..."); - } - - @Override - public void onResponseFromBridgeService(boolean response) { - if (response) { - Log.i(TAG, "sent service to bridge"); - } else { - Log.w(TAG, "no response from bridge"); - } - } - }); + LSPManagerService() { } + @Override public IBinder asBinder() { return this; diff --git a/core/src/main/java/io/github/lsposed/lspd/service/LSPModuleService.java b/core/src/main/java/io/github/lsposed/lspd/service/LSPModuleService.java new file mode 100644 index 00000000..8ba34d8a --- /dev/null +++ b/core/src/main/java/io/github/lsposed/lspd/service/LSPModuleService.java @@ -0,0 +1,22 @@ +package io.github.lsposed.lspd.service; + +import android.os.IBinder; + +import de.robv.android.xposed.XposedBridge; +import io.github.xposed.xposedservice.IXposedService; + +public class LSPModuleService extends IXposedService.Stub { + + public LSPModuleService() { + } + + @Override + public IBinder asBinder() { + return this; + } + + @Override + public int getVersion() { + return XposedBridge.getXposedVersion(); + } +} diff --git a/core/src/main/java/io/github/lsposed/lspd/service/LSPosedService.java b/core/src/main/java/io/github/lsposed/lspd/service/LSPosedService.java index c91bdde3..40a61863 100644 --- a/core/src/main/java/io/github/lsposed/lspd/service/LSPosedService.java +++ b/core/src/main/java/io/github/lsposed/lspd/service/LSPosedService.java @@ -1,16 +1,13 @@ package io.github.lsposed.lspd.service; +import android.os.Binder; import android.os.IBinder; import android.util.Log; -import de.robv.android.xposed.XposedBridge; -import io.github.xposed.xposedservice.IXposedService; +import static io.github.lsposed.lspd.service.ServiceManager.TAG; -import static io.github.lsposed.lspd.service.Service.TAG; - -public class LSPosedService extends IXposedService.Stub { - - public LSPosedService() { +public class LSPosedService extends ILSPosedService.Stub { + LSPosedService() { BridgeService.send(this, new BridgeService.Listener() { @Override public void onSystemServerRestarted() { @@ -29,12 +26,17 @@ public class LSPosedService extends IXposedService.Stub { } @Override - public IBinder asBinder() { - return this; + public ILSPApplicationService requestApplicationService(int uid, int pid) { + if (Binder.getCallingUid() != 1000) { + return null; + } + if (ConfigManager.getInstance().shouldSkipUid(uid)) { + return null; + } + if (ServiceManager.getApplicationService().hasRegister(uid, pid)) { + return null; + } + return ServiceManager.getApplicationService(); } - @Override - public int getVersion() { - return XposedBridge.getXposedVersion(); - } } diff --git a/core/src/main/java/io/github/lsposed/lspd/service/Service.java b/core/src/main/java/io/github/lsposed/lspd/service/Service.java deleted file mode 100644 index eb03d7b7..00000000 --- a/core/src/main/java/io/github/lsposed/lspd/service/Service.java +++ /dev/null @@ -1,21 +0,0 @@ -package io.github.lsposed.lspd.service; - -import android.os.Looper; -import android.util.Log; - -public class Service { - public static final String TAG = "LSPosedService"; - // call by ourselves - public static void start() { - Log.i(TAG, "starting server..."); - - Looper.prepare(); - new LSPosedService(); - new LSPManagerService(); - Looper.loop(); - - Log.i(TAG, "server exited"); - System.exit(0); - } - -} diff --git a/core/src/main/java/io/github/lsposed/lspd/service/ServiceManager.java b/core/src/main/java/io/github/lsposed/lspd/service/ServiceManager.java new file mode 100644 index 00000000..449bc608 --- /dev/null +++ b/core/src/main/java/io/github/lsposed/lspd/service/ServiceManager.java @@ -0,0 +1,39 @@ +package io.github.lsposed.lspd.service; + +import android.os.Looper; +import android.util.Log; + +public class ServiceManager { + private static LSPosedService mainService = null; + private static LSPModuleService moduleService = null; + private static LSPApplicationService applicationService = null; + private static LSPManagerService managerService = null; + public static final String TAG = "LSPosedService"; + // call by ourselves + public static void start() { + Log.i(TAG, "starting server..."); + + Looper.prepare(); + mainService = new LSPosedService(); + moduleService = new LSPModuleService(); + applicationService = new LSPApplicationService(); + managerService = new LSPManagerService(); + Looper.loop(); + + Log.i(TAG, "server exited"); + System.exit(0); + } + + public static LSPModuleService getModuleService() { + return moduleService; + } + + public static LSPApplicationService getApplicationService() { + return applicationService; + } + + public static LSPManagerService getManagerService() { + return managerService; + } + +}