Seperate manager service
This commit is contained in:
parent
d27e012442
commit
d85b4d3584
|
|
@ -144,7 +144,7 @@ dependencies {
|
||||||
implementation 'me.zhanghai.android.fastscroll:library:1.1.5'
|
implementation 'me.zhanghai.android.fastscroll:library:1.1.5'
|
||||||
implementation files('libs/WeatherView-2.0.3.aar')
|
implementation files('libs/WeatherView-2.0.3.aar')
|
||||||
compileOnly project(":hiddenapi-stubs")
|
compileOnly project(":hiddenapi-stubs")
|
||||||
implementation project(":service")
|
implementation project(':manager-service')
|
||||||
}
|
}
|
||||||
|
|
||||||
configurations {
|
configurations {
|
||||||
|
|
|
||||||
|
|
@ -19,4 +19,7 @@
|
||||||
# If you keep the line number information, uncomment this to
|
# If you keep the line number information, uncomment this to
|
||||||
# hide the original source file name.
|
# hide the original source file name.
|
||||||
-renamesourcefileattribute
|
-renamesourcefileattribute
|
||||||
-keep class io.github.lsposed.manager.Constants { *; }
|
-keep class io.github.lsposed.manager.Constants { *; }
|
||||||
|
-keepclassmembers class io.github.lsposed.manager.receivers.LSPosedManagerServiceClient {
|
||||||
|
private static android.os.IBinder binder;
|
||||||
|
}
|
||||||
|
|
@ -42,7 +42,7 @@ import okhttp3.Cache;
|
||||||
import okhttp3.OkHttpClient;
|
import okhttp3.OkHttpClient;
|
||||||
import rikka.material.app.DayNightDelegate;
|
import rikka.material.app.DayNightDelegate;
|
||||||
|
|
||||||
import static io.github.lsposed.manager.receivers.LSPosedServiceClient.testBinder;
|
import static io.github.lsposed.manager.receivers.LSPosedManagerServiceClient.testBinder;
|
||||||
|
|
||||||
public class App extends Application {
|
public class App extends Application {
|
||||||
public static final String TAG = "LSPosedManager";
|
public static final String TAG = "LSPosedManager";
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,25 @@
|
||||||
package io.github.lsposed.manager.receivers;
|
package io.github.lsposed.manager.receivers;
|
||||||
|
|
||||||
import android.content.pm.PackageInfo;
|
import android.content.pm.PackageInfo;
|
||||||
|
import android.os.IBinder;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import io.github.lsposed.lspd.ILSPManagerService;
|
||||||
import io.github.lsposed.manager.App;
|
import io.github.lsposed.manager.App;
|
||||||
import io.github.xposed.xposedservice.XposedService;
|
|
||||||
|
|
||||||
public class LSPosedServiceClient {
|
public class LSPosedManagerServiceClient {
|
||||||
|
|
||||||
|
private static IBinder binder = null;
|
||||||
|
private static ILSPManagerService service = null;
|
||||||
|
|
||||||
public static void testBinder() {
|
public static void testBinder() {
|
||||||
XposedService service = XposedService.getService();
|
if (service == null && binder != null) {
|
||||||
|
service = ILSPManagerService.Stub.asInterface(binder);
|
||||||
|
}
|
||||||
if (service == null) {
|
if (service == null) {
|
||||||
Log.e(App.TAG, "Version fail");
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int ver = -1;
|
int ver = -1;
|
||||||
|
|
@ -63,6 +63,7 @@ dependencies {
|
||||||
compileOnly 'androidx.annotation:annotation:1.1.0'
|
compileOnly 'androidx.annotation:annotation:1.1.0'
|
||||||
implementation project(':interface')
|
implementation project(':interface')
|
||||||
implementation project(':hiddenapi-bridge')
|
implementation project(':hiddenapi-bridge')
|
||||||
|
implementation project(':manager-service')
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
|
|
|
||||||
|
|
@ -28,11 +28,12 @@ import android.ddm.DdmHandleAppName;
|
||||||
|
|
||||||
import io.github.lsposed.common.KeepAll;
|
import io.github.lsposed.common.KeepAll;
|
||||||
import io.github.lsposed.lspd.service.LSPosedService;
|
import io.github.lsposed.lspd.service.LSPosedService;
|
||||||
|
import io.github.lsposed.lspd.service.Service;
|
||||||
import io.github.lsposed.lspd.util.Utils;
|
import io.github.lsposed.lspd.util.Utils;
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
import static io.github.lsposed.lspd.service.LSPosedService.TAG;
|
import static io.github.lsposed.lspd.service.Service.TAG;
|
||||||
|
|
||||||
@SuppressLint("DefaultLocale")
|
@SuppressLint("DefaultLocale")
|
||||||
public class Main implements KeepAll {
|
public class Main implements KeepAll {
|
||||||
|
|
@ -135,6 +136,6 @@ public class Main implements KeepAll {
|
||||||
waitSystemService(Context.USER_SERVICE);
|
waitSystemService(Context.USER_SERVICE);
|
||||||
waitSystemService(Context.APP_OPS_SERVICE);
|
waitSystemService(Context.APP_OPS_SERVICE);
|
||||||
|
|
||||||
LSPosedService.start();
|
Service.start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -115,8 +115,8 @@ public class XposedInstallerHooker {
|
||||||
return ConfigManager.getMiscPath();
|
return ConfigManager.getMiscPath();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Class<?> serviceClass = XposedHelpers.findClass("io.github.xposed.xposedservice.XposedService", classLoader);
|
Class<?> serviceClass = XposedHelpers.findClass("io.github.lsposed.manager.receivers.LSPosedManagerServiceClient", classLoader);
|
||||||
XposedHelpers.setStaticObjectField(serviceClass, "serviceBinder", BridgeService.requireBinder());
|
XposedHelpers.setStaticObjectField(serviceClass, "binder", BridgeService.requireBinder());
|
||||||
|
|
||||||
Utils.logI("Hooked LSPosed Manager");
|
Utils.logI("Hooked LSPosed Manager");
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
package io.github.lsposed.lspd.service;
|
package io.github.lsposed.lspd.service;
|
||||||
|
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
|
|
@ -14,23 +13,29 @@ import androidx.annotation.Nullable;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import io.github.lsposed.lspd.nativebridge.ConfigManager;
|
import io.github.lsposed.lspd.ILSPManagerService;
|
||||||
import io.github.xposed.xposedservice.IXposedService;
|
import io.github.xposed.xposedservice.IXposedService;
|
||||||
|
|
||||||
import static android.os.Binder.getCallingUid;
|
import static android.os.Binder.getCallingUid;
|
||||||
import static io.github.lsposed.lspd.service.LSPosedService.TAG;
|
import static io.github.lsposed.lspd.service.Service.TAG;
|
||||||
|
|
||||||
public class BridgeService {
|
public class BridgeService {
|
||||||
private static final int TRANSACTION_CODE = ('_' << 24) | ('L' << 16) | ('S' << 8) | 'P';
|
private static final int TRANSACTION_CODE = ('_' << 24) | ('L' << 16) | ('S' << 8) | 'P';
|
||||||
private static final String DESCRIPTOR = "android.app.IActivityManager";
|
private static final String DESCRIPTOR = "android.app.IActivityManager";
|
||||||
private static final String SERVICE_NAME = "activity";
|
private static final String SERVICE_NAME = "activity";
|
||||||
|
|
||||||
private static final int ACTION_SEND_BINDER = 1;
|
enum ACTION {
|
||||||
private static final int ACTION_GET_BINDER = ACTION_SEND_BINDER + 1;
|
ACTION_SEND_BINDER,
|
||||||
|
ACTION_SEND_MANAGER_BINDER,
|
||||||
|
ACTION_GET_BINDER,
|
||||||
|
}
|
||||||
|
|
||||||
private static IBinder serviceBinder = null;
|
private static IBinder serviceBinder = null;
|
||||||
private static IXposedService service = null;
|
private static IXposedService service = null;
|
||||||
|
|
||||||
|
private static IBinder managerBinder = null;
|
||||||
|
private static ILSPManagerService manager = null;
|
||||||
|
|
||||||
private static final IBinder.DeathRecipient BRIDGE_SERVICE_DEATH_RECIPIENT = () -> {
|
private static final IBinder.DeathRecipient BRIDGE_SERVICE_DEATH_RECIPIENT = () -> {
|
||||||
Log.i(TAG, "service " + SERVICE_NAME + " is dead. ");
|
Log.i(TAG, "service " + SERVICE_NAME + " is dead. ");
|
||||||
|
|
||||||
|
|
@ -52,12 +57,15 @@ public class BridgeService {
|
||||||
Log.w(TAG, "clear ServiceManager: " + Log.getStackTraceString(e));
|
Log.w(TAG, "clear ServiceManager: " + Log.getStackTraceString(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
sendToBridge(true);
|
sendToBridge(ACTION.ACTION_SEND_BINDER, serviceBinder, true);
|
||||||
|
sendToBridge(ACTION.ACTION_SEND_MANAGER_BINDER, managerBinder, true);
|
||||||
};
|
};
|
||||||
|
|
||||||
private static final IBinder.DeathRecipient LSPSERVICE_DEATH_RECIPIENT = () -> {
|
private static final IBinder.DeathRecipient LSPSERVICE_DEATH_RECIPIENT = () -> {
|
||||||
serviceBinder = null;
|
serviceBinder = null;
|
||||||
service = null;
|
service = null;
|
||||||
|
managerBinder = null;
|
||||||
|
manager = null;
|
||||||
Log.e(TAG, "service is dead");
|
Log.e(TAG, "service is dead");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -70,9 +78,7 @@ public class BridgeService {
|
||||||
|
|
||||||
private static Listener listener;
|
private static Listener listener;
|
||||||
|
|
||||||
private static PackageManager pm = null;
|
private static void sendToBridge(ACTION action, IBinder binder, boolean isRestart) {
|
||||||
|
|
||||||
private static void sendToBridge(boolean isRestart) {
|
|
||||||
IBinder bridgeService;
|
IBinder bridgeService;
|
||||||
do {
|
do {
|
||||||
bridgeService = ServiceManager.getService(SERVICE_NAME);
|
bridgeService = ServiceManager.getService(SERVICE_NAME);
|
||||||
|
|
@ -98,7 +104,7 @@ public class BridgeService {
|
||||||
bridgeService.linkToDeath(BRIDGE_SERVICE_DEATH_RECIPIENT, 0);
|
bridgeService.linkToDeath(BRIDGE_SERVICE_DEATH_RECIPIENT, 0);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
Log.w(TAG, "linkToDeath " + Log.getStackTraceString(e));
|
Log.w(TAG, "linkToDeath " + Log.getStackTraceString(e));
|
||||||
sendToBridge(false);
|
sendToBridge(action, binder, false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -109,9 +115,9 @@ public class BridgeService {
|
||||||
for (int i = 0; i < 3; i++) {
|
for (int i = 0; i < 3; i++) {
|
||||||
try {
|
try {
|
||||||
data.writeInterfaceToken(DESCRIPTOR);
|
data.writeInterfaceToken(DESCRIPTOR);
|
||||||
data.writeInt(ACTION_SEND_BINDER);
|
data.writeInt(action.ordinal());
|
||||||
Log.v(TAG, "binder " + serviceBinder.toString());
|
Log.v(TAG, "binder " + binder.toString());
|
||||||
data.writeStrongBinder(serviceBinder);
|
data.writeStrongBinder(binder);
|
||||||
res = bridgeService.transact(TRANSACTION_CODE, data, reply, 0);
|
res = bridgeService.transact(TRANSACTION_CODE, data, reply, 0);
|
||||||
reply.readException();
|
reply.readException();
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
|
|
@ -136,23 +142,36 @@ public class BridgeService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void receiveFromBridge(IBinder binder) {
|
private static void receiveFromBridge(ACTION action, IBinder binder) {
|
||||||
if (binder == null) {
|
if (binder == null) {
|
||||||
Log.e(TAG, "received empty binder");
|
Log.e(TAG, "received empty binder");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (serviceBinder == null) {
|
if(action == ACTION.ACTION_SEND_MANAGER_BINDER) {
|
||||||
PackageReceiver.register();
|
if (managerBinder != null) {
|
||||||
} else {
|
managerBinder.unlinkToDeath(LSPSERVICE_DEATH_RECIPIENT, 0);
|
||||||
serviceBinder.unlinkToDeath(LSPSERVICE_DEATH_RECIPIENT, 0);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
serviceBinder = binder;
|
managerBinder = binder;
|
||||||
service = IXposedService.Stub.asInterface(serviceBinder);
|
manager = LSPManagerService.Stub.asInterface(managerBinder);
|
||||||
try {
|
try {
|
||||||
serviceBinder.linkToDeath(LSPSERVICE_DEATH_RECIPIENT, 0);
|
managerBinder.linkToDeath(LSPSERVICE_DEATH_RECIPIENT, 0);
|
||||||
} catch (RemoteException ignored) {
|
} 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) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.i(TAG, "binder received");
|
Log.i(TAG, "binder received");
|
||||||
|
|
@ -162,13 +181,24 @@ public class BridgeService {
|
||||||
BridgeService.listener = listener;
|
BridgeService.listener = listener;
|
||||||
BridgeService.service = service;
|
BridgeService.service = service;
|
||||||
BridgeService.serviceBinder = service.asBinder();
|
BridgeService.serviceBinder = service.asBinder();
|
||||||
sendToBridge(false);
|
sendToBridge(ACTION.ACTION_SEND_BINDER, 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 IXposedService getService() {
|
||||||
return service;
|
return service;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ILSPManagerService getManager() {
|
||||||
|
return manager;
|
||||||
|
}
|
||||||
|
|
||||||
public static IBinder requireBinder() {
|
public static IBinder requireBinder() {
|
||||||
IBinder binder = ServiceManager.getService(SERVICE_NAME);
|
IBinder binder = ServiceManager.getService(SERVICE_NAME);
|
||||||
if (binder == null) return null;
|
if (binder == null) return null;
|
||||||
|
|
@ -177,7 +207,7 @@ public class BridgeService {
|
||||||
Parcel reply = Parcel.obtain();
|
Parcel reply = Parcel.obtain();
|
||||||
try {
|
try {
|
||||||
data.writeInterfaceToken(DESCRIPTOR);
|
data.writeInterfaceToken(DESCRIPTOR);
|
||||||
data.writeInt(ACTION_GET_BINDER);
|
data.writeInt(ACTION.ACTION_GET_BINDER.ordinal());
|
||||||
binder.transact(TRANSACTION_CODE, data, reply, 0);
|
binder.transact(TRANSACTION_CODE, data, reply, 0);
|
||||||
reply.readException();
|
reply.readException();
|
||||||
IBinder received = reply.readStrongBinder();
|
IBinder received = reply.readStrongBinder();
|
||||||
|
|
@ -197,13 +227,14 @@ public class BridgeService {
|
||||||
public static boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) {
|
public static boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) {
|
||||||
data.enforceInterface(DESCRIPTOR);
|
data.enforceInterface(DESCRIPTOR);
|
||||||
|
|
||||||
int action = data.readInt();
|
ACTION action = ACTION.values()[data.readInt()];
|
||||||
Log.d(TAG, "onTransact: action=" + action + ", callingUid=" + Binder.getCallingUid() + ", callingPid=" + Binder.getCallingPid());
|
Log.d(TAG, "onTransact: action=" + action + ", callingUid=" + Binder.getCallingUid() + ", callingPid=" + Binder.getCallingPid());
|
||||||
|
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case ACTION_SEND_BINDER: {
|
case ACTION_SEND_BINDER:
|
||||||
|
case ACTION_SEND_MANAGER_BINDER: {
|
||||||
if (Binder.getCallingUid() == 0) {
|
if (Binder.getCallingUid() == 0) {
|
||||||
receiveFromBridge(data.readStrongBinder());
|
receiveFromBridge(action, data.readStrongBinder());
|
||||||
if (reply != null) {
|
if (reply != null) {
|
||||||
reply.writeNoException();
|
reply.writeNoException();
|
||||||
}
|
}
|
||||||
|
|
@ -219,8 +250,8 @@ public class BridgeService {
|
||||||
}
|
}
|
||||||
if (reply != null) {
|
if (reply != null) {
|
||||||
reply.writeNoException();
|
reply.writeNoException();
|
||||||
Log.d(TAG, "saved binder is " + serviceBinder.toString());
|
Log.d(TAG, "saved binder is " + managerBinder.toString());
|
||||||
reply.writeStrongBinder(serviceBinder);
|
reply.writeStrongBinder(managerBinder);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public IBinder asBinder() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getVersion() {
|
||||||
|
return XposedBridge.getXposedVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ParceledListSlice<PackageInfo> getInstalledPackagesFromAllUsers(int flags) throws RemoteException {
|
||||||
|
return PackageService.getInstalledPackagesFromAllUsers(flags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,29 +1,14 @@
|
||||||
package io.github.lsposed.lspd.service;
|
package io.github.lsposed.lspd.service;
|
||||||
|
|
||||||
import android.content.pm.PackageInfo;
|
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.Looper;
|
|
||||||
import android.os.RemoteException;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import de.robv.android.xposed.XposedBridge;
|
import de.robv.android.xposed.XposedBridge;
|
||||||
import io.github.xposed.xposedservice.IXposedService;
|
import io.github.xposed.xposedservice.IXposedService;
|
||||||
import io.github.xposed.xposedservice.utils.ParceledListSlice;
|
|
||||||
|
import static io.github.lsposed.lspd.service.Service.TAG;
|
||||||
|
|
||||||
public class LSPosedService extends IXposedService.Stub {
|
public class LSPosedService extends IXposedService.Stub {
|
||||||
public static final String TAG = "LSPosedService";
|
|
||||||
|
|
||||||
// call by ourselves
|
|
||||||
public static void start() {
|
|
||||||
Log.i(TAG, "starting server...");
|
|
||||||
|
|
||||||
Looper.prepare();
|
|
||||||
new LSPosedService();
|
|
||||||
Looper.loop();
|
|
||||||
|
|
||||||
Log.i(TAG, "server exited");
|
|
||||||
System.exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public LSPosedService() {
|
public LSPosedService() {
|
||||||
BridgeService.send(this, new BridgeService.Listener() {
|
BridgeService.send(this, new BridgeService.Listener() {
|
||||||
|
|
@ -52,9 +37,4 @@ public class LSPosedService extends IXposedService.Stub {
|
||||||
public int getVersion() {
|
public int getVersion() {
|
||||||
return XposedBridge.getXposedVersion();
|
return XposedBridge.getXposedVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public ParceledListSlice<PackageInfo> getInstalledPackagesFromAllUsers(int flags) throws RemoteException {
|
|
||||||
return PackageService.getInstalledPackagesFromAllUsers(flags);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,16 +2,14 @@ package io.github.lsposed.lspd.service;
|
||||||
|
|
||||||
import android.content.pm.IPackageManager;
|
import android.content.pm.IPackageManager;
|
||||||
import android.content.pm.PackageInfo;
|
import android.content.pm.PackageInfo;
|
||||||
import android.os.Binder;
|
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.ServiceManager;
|
import android.os.ServiceManager;
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
import io.github.lsposed.lspd.nativebridge.ConfigManager;
|
import io.github.lsposed.lspd.nativebridge.ConfigManager;
|
||||||
import io.github.xposed.xposedservice.utils.ParceledListSlice;
|
import io.github.lsposed.lspd.utils.ParceledListSlice;
|
||||||
|
|
||||||
public class PackageService {
|
public class PackageService {
|
||||||
private static IPackageManager pm = null;
|
private static IPackageManager pm = null;
|
||||||
|
|
@ -26,13 +24,13 @@ public class PackageService {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PackageInfo getPackageInfo(String packageName, int flags, int uid) throws RemoteException {
|
public static PackageInfo getPackageInfo(String packageName, int flags, int uid) throws RemoteException {
|
||||||
pm = getPackageManager();
|
IPackageManager pm = getPackageManager();
|
||||||
if (pm == null) return null;
|
if (pm == null) return null;
|
||||||
return pm.getPackageInfo(packageName, flags, uid);
|
return pm.getPackageInfo(packageName, flags, uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String[] getPackagesForUid(int uid) throws RemoteException {
|
public static String[] getPackagesForUid(int uid) throws RemoteException {
|
||||||
pm = getPackageManager();
|
IPackageManager pm = getPackageManager();
|
||||||
if (pm == null) return new String[0];
|
if (pm == null) return new String[0];
|
||||||
return pm.getPackagesForUid(uid);
|
return pm.getPackagesForUid(uid);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
/build
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
plugins {
|
||||||
|
id 'com.android.library'
|
||||||
|
}
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion 30
|
||||||
|
buildToolsVersion "30.0.3"
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdkVersion 26
|
||||||
|
targetSdkVersion 30
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
minifyEnabled false
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# You can control the set of applied configuration files using the
|
||||||
|
# proguardFiles setting in build.gradle.
|
||||||
|
#
|
||||||
|
# For more details, see
|
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
|
# If your project uses WebView with JS, uncomment the following
|
||||||
|
# and specify the fully qualified class name to the JavaScript interface
|
||||||
|
# class:
|
||||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||||
|
# public *;
|
||||||
|
#}
|
||||||
|
|
||||||
|
# Uncomment this to preserve the line number information for
|
||||||
|
# debugging stack traces.
|
||||||
|
#-keepattributes SourceFile,LineNumberTable
|
||||||
|
|
||||||
|
# If you keep the line number information, uncomment this to
|
||||||
|
# hide the original source file name.
|
||||||
|
#-renamesourcefileattribute SourceFile
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest package="io.github.lsposed.lspd.managerservice" />
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
package io.github.lsposed.lspd;
|
||||||
|
|
||||||
|
import io.github.lsposed.lspd.utils.ParceledListSlice;
|
||||||
|
|
||||||
|
interface ILSPManagerService {
|
||||||
|
int getVersion() = 1;
|
||||||
|
ParceledListSlice<PackageInfo> getInstalledPackagesFromAllUsers(int flags) = 2;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
package io.github.lsposed.lspd.utils;
|
||||||
|
|
||||||
|
parcelable ParceledListSlice<T>;
|
||||||
|
|
@ -0,0 +1,185 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* 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 io.github.lsposed.lspd.utils;
|
||||||
|
|
||||||
|
import android.os.Binder;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.os.Parcel;
|
||||||
|
import android.os.Parcelable;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
import android.util.Log;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
/**
|
||||||
|
* Transfer a large list of Parcelable objects across an IPC. Splits into
|
||||||
|
* multiple transactions if needed.
|
||||||
|
*
|
||||||
|
* Caveat: for efficiency and security, all elements must be the same concrete type.
|
||||||
|
* In order to avoid writing the class name of each object, we must ensure that
|
||||||
|
* each object is the same type, or else unparceling then reparceling the data may yield
|
||||||
|
* a different result if the class name encoded in the Parcelable is a Base type.
|
||||||
|
* See b/17671747.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
abstract class BaseParceledListSlice<T> implements Parcelable {
|
||||||
|
private static String TAG = "ParceledListSlice";
|
||||||
|
private static boolean DEBUG = false;
|
||||||
|
/*
|
||||||
|
* TODO get this number from somewhere else. For now set it to a quarter of
|
||||||
|
* the 1MB limit.
|
||||||
|
*/
|
||||||
|
private static final int MAX_IPC_SIZE = 64 * 1024;
|
||||||
|
private final List<T> mList;
|
||||||
|
private int mInlineCountLimit = Integer.MAX_VALUE;
|
||||||
|
|
||||||
|
public BaseParceledListSlice(List<T> list) {
|
||||||
|
mList = list;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
BaseParceledListSlice(Parcel p, ClassLoader loader) {
|
||||||
|
final int N = p.readInt();
|
||||||
|
mList = new ArrayList<T>(N);
|
||||||
|
if (DEBUG) Log.d(TAG, "Retrieving " + N + " items");
|
||||||
|
if (N <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Parcelable.Creator<?> creator = readParcelableCreator(p, loader);
|
||||||
|
Class<?> listElementClass = null;
|
||||||
|
int i = 0;
|
||||||
|
while (i < N) {
|
||||||
|
if (p.readInt() == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
final T parcelable = readCreator(creator, p, loader);
|
||||||
|
if (listElementClass == null) {
|
||||||
|
listElementClass = parcelable.getClass();
|
||||||
|
} else {
|
||||||
|
verifySameType(listElementClass, parcelable.getClass());
|
||||||
|
}
|
||||||
|
mList.add(parcelable);
|
||||||
|
if (DEBUG) Log.d(TAG, "Read inline #" + i + ": " + mList.get(mList.size()-1));
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (i >= N) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final IBinder retriever = p.readStrongBinder();
|
||||||
|
while (i < N) {
|
||||||
|
if (DEBUG) Log.d(TAG, "Reading more @" + i + " of " + N + ": retriever=" + retriever);
|
||||||
|
Parcel data = Parcel.obtain();
|
||||||
|
Parcel reply = Parcel.obtain();
|
||||||
|
data.writeInt(i);
|
||||||
|
try {
|
||||||
|
retriever.transact(IBinder.FIRST_CALL_TRANSACTION, data, reply, 0);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Log.w(TAG, "Failure retrieving array; only received " + i + " of " + N, e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
while (i < N && reply.readInt() != 0) {
|
||||||
|
final T parcelable = readCreator(creator, reply, loader);
|
||||||
|
verifySameType(listElementClass, parcelable.getClass());
|
||||||
|
mList.add(parcelable);
|
||||||
|
if (DEBUG) Log.d(TAG, "Read extra #" + i + ": " + mList.get(mList.size()-1));
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
reply.recycle();
|
||||||
|
data.recycle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private T readCreator(Parcelable.Creator<?> creator, Parcel p, ClassLoader loader) {
|
||||||
|
if (creator instanceof Parcelable.ClassLoaderCreator<?>) {
|
||||||
|
Parcelable.ClassLoaderCreator<?> classLoaderCreator =
|
||||||
|
(Parcelable.ClassLoaderCreator<?>) creator;
|
||||||
|
return (T) classLoaderCreator.createFromParcel(p, loader);
|
||||||
|
}
|
||||||
|
return (T) creator.createFromParcel(p);
|
||||||
|
}
|
||||||
|
private static void verifySameType(final Class<?> expected, final Class<?> actual) {
|
||||||
|
if (!actual.equals(expected)) {
|
||||||
|
throw new IllegalArgumentException("Can't unparcel type "
|
||||||
|
+ (actual == null ? null : actual.getName()) + " in list of type "
|
||||||
|
+ (expected == null ? null : expected.getName()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public List<T> getList() {
|
||||||
|
return mList;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Set a limit on the maximum number of entries in the array that will be included
|
||||||
|
* inline in the initial parcelling of this object.
|
||||||
|
*/
|
||||||
|
public void setInlineCountLimit(int maxCount) {
|
||||||
|
mInlineCountLimit = maxCount;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Write this to another Parcel. Note that this discards the internal Parcel
|
||||||
|
* and should not be used anymore. This is so we can pass this to a Binder
|
||||||
|
* where we won't have a chance to call recycle on this.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void writeToParcel(Parcel dest, int flags) {
|
||||||
|
final int N = mList.size();
|
||||||
|
final int callFlags = flags;
|
||||||
|
dest.writeInt(N);
|
||||||
|
if (DEBUG) Log.d(TAG, "Writing " + N + " items");
|
||||||
|
if (N > 0) {
|
||||||
|
final Class<?> listElementClass = mList.get(0).getClass();
|
||||||
|
writeParcelableCreator(mList.get(0), dest);
|
||||||
|
int i = 0;
|
||||||
|
while (i < N && i < mInlineCountLimit && dest.dataSize() < MAX_IPC_SIZE) {
|
||||||
|
dest.writeInt(1);
|
||||||
|
final T parcelable = mList.get(i);
|
||||||
|
verifySameType(listElementClass, parcelable.getClass());
|
||||||
|
writeElement(parcelable, dest, callFlags);
|
||||||
|
if (DEBUG) Log.d(TAG, "Wrote inline #" + i + ": " + mList.get(i));
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (i < N) {
|
||||||
|
dest.writeInt(0);
|
||||||
|
Binder retriever = new Binder() {
|
||||||
|
@Override
|
||||||
|
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
|
||||||
|
throws RemoteException {
|
||||||
|
if (code != FIRST_CALL_TRANSACTION) {
|
||||||
|
return super.onTransact(code, data, reply, flags);
|
||||||
|
}
|
||||||
|
int i = data.readInt();
|
||||||
|
if (DEBUG) Log.d(TAG, "Writing more @" + i + " of " + N);
|
||||||
|
while (i < N && reply.dataSize() < MAX_IPC_SIZE) {
|
||||||
|
reply.writeInt(1);
|
||||||
|
final T parcelable = mList.get(i);
|
||||||
|
verifySameType(listElementClass, parcelable.getClass());
|
||||||
|
writeElement(parcelable, reply, callFlags);
|
||||||
|
if (DEBUG) Log.d(TAG, "Wrote extra #" + i + ": " + mList.get(i));
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (i < N) {
|
||||||
|
if (DEBUG) Log.d(TAG, "Breaking @" + i + " of " + N);
|
||||||
|
reply.writeInt(0);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if (DEBUG) Log.d(TAG, "Breaking @" + i + " of " + N + ": retriever=" + retriever);
|
||||||
|
dest.writeStrongBinder(retriever);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
protected abstract void writeElement(T parcelable, Parcel reply, int callFlags);
|
||||||
|
protected abstract void writeParcelableCreator(T parcelable, Parcel dest);
|
||||||
|
protected abstract Parcelable.Creator<?> readParcelableCreator(Parcel from, ClassLoader loader);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,178 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* 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 io.github.lsposed.lspd.utils;
|
||||||
|
|
||||||
|
import android.content.pm.ApplicationInfo;
|
||||||
|
import android.content.pm.PackageInfo;
|
||||||
|
import android.os.BadParcelableException;
|
||||||
|
import android.os.Parcel;
|
||||||
|
import android.os.Parcelable;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transfer a large list of Parcelable objects across an IPC. Splits into
|
||||||
|
* multiple transactions if needed.
|
||||||
|
*
|
||||||
|
* @see BaseParceledListSlice
|
||||||
|
*/
|
||||||
|
public class ParceledListSlice<T extends Parcelable> extends BaseParceledListSlice<T> {
|
||||||
|
|
||||||
|
private static final String TAG = "ParceledListSlice";
|
||||||
|
|
||||||
|
// Cache of previously looked up CREATOR.createFromParcel() methods for
|
||||||
|
// particular classes. Keys are the names of the classes, values are
|
||||||
|
// Method objects.
|
||||||
|
private static final HashMap<ClassLoader, HashMap<String, Creator<?>>>
|
||||||
|
CREATORS = new HashMap<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
putCreator(PackageInfo.class.getName(), PackageInfo.CREATOR);
|
||||||
|
putCreator(ApplicationInfo.class.getName(), ApplicationInfo.CREATOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void putCreator(String name, Parcelable.Creator<?> creator) {
|
||||||
|
HashMap<String, Parcelable.Creator<?>> map = CREATORS.get(null);
|
||||||
|
if (map == null) {
|
||||||
|
map = new HashMap<>();
|
||||||
|
CREATORS.put(null, map);
|
||||||
|
}
|
||||||
|
map.put(name, creator);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ParceledListSlice(List<T> list) {
|
||||||
|
super(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ParceledListSlice(Parcel in, ClassLoader loader) {
|
||||||
|
super(in, loader);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T extends Parcelable> ParceledListSlice<T> emptyList() {
|
||||||
|
return new ParceledListSlice<T>(Collections.<T>emptyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int describeContents() {
|
||||||
|
int contents = 0;
|
||||||
|
final List<T> list = getList();
|
||||||
|
for (int i = 0; i < list.size(); i++) {
|
||||||
|
contents |= list.get(i).describeContents();
|
||||||
|
}
|
||||||
|
return contents;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writeElement(T parcelable, Parcel dest, int callFlags) {
|
||||||
|
parcelable.writeToParcel(dest, callFlags);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writeParcelableCreator(T parcelable, Parcel dest) {
|
||||||
|
String name = parcelable.getClass().getName();
|
||||||
|
dest.writeString(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Parcelable.Creator<?> readParcelableCreator(Parcel from, ClassLoader loader) {
|
||||||
|
String name = from.readString();
|
||||||
|
if (name == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Parcelable.Creator<?> creator;
|
||||||
|
synchronized (CREATORS) {
|
||||||
|
HashMap<String, Parcelable.Creator<?>> map = CREATORS.get(loader);
|
||||||
|
if (map == null) {
|
||||||
|
map = new HashMap<>();
|
||||||
|
CREATORS.put(loader, map);
|
||||||
|
}
|
||||||
|
creator = map.get(name);
|
||||||
|
if (creator == null) {
|
||||||
|
try {
|
||||||
|
// If loader == null, explicitly emulate Class.forName(String) "caller
|
||||||
|
// classloader" behavior.
|
||||||
|
ClassLoader parcelableClassLoader =
|
||||||
|
(loader == null ? getClass().getClassLoader() : loader);
|
||||||
|
// Avoid initializing the Parcelable class until we know it implements
|
||||||
|
// Parcelable and has the necessary CREATOR field. http://b/1171613.
|
||||||
|
Class<?> parcelableClass = Class.forName(name, false /* initialize */,
|
||||||
|
parcelableClassLoader);
|
||||||
|
if (!Parcelable.class.isAssignableFrom(parcelableClass)) {
|
||||||
|
throw new BadParcelableException("Parcelable protocol requires subclassing "
|
||||||
|
+ "from Parcelable on class " + name);
|
||||||
|
}
|
||||||
|
Field f = parcelableClass.getField("CREATOR");
|
||||||
|
if ((f.getModifiers() & Modifier.STATIC) == 0) {
|
||||||
|
throw new BadParcelableException("Parcelable protocol requires "
|
||||||
|
+ "the CREATOR object to be static on class " + name);
|
||||||
|
}
|
||||||
|
Class<?> creatorType = f.getType();
|
||||||
|
if (!Parcelable.Creator.class.isAssignableFrom(creatorType)) {
|
||||||
|
// Fail before calling Field.get(), not after, to avoid initializing
|
||||||
|
// parcelableClass unnecessarily.
|
||||||
|
throw new BadParcelableException("Parcelable protocol requires a "
|
||||||
|
+ "Parcelable.Creator object called "
|
||||||
|
+ "CREATOR on class " + name);
|
||||||
|
}
|
||||||
|
creator = (Parcelable.Creator<?>) f.get(null);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
Log.e(TAG, "Illegal access when unmarshalling: " + name, e);
|
||||||
|
throw new BadParcelableException(
|
||||||
|
"IllegalAccessException when unmarshalling: " + name);
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
Log.e(TAG, "Class not found when unmarshalling: " + name, e);
|
||||||
|
throw new BadParcelableException(
|
||||||
|
"ClassNotFoundException when unmarshalling: " + name);
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
throw new BadParcelableException("Parcelable protocol requires a "
|
||||||
|
+ "Parcelable.Creator object called "
|
||||||
|
+ "CREATOR on class " + name);
|
||||||
|
}
|
||||||
|
if (creator == null) {
|
||||||
|
throw new BadParcelableException("Parcelable protocol requires a "
|
||||||
|
+ "non-null Parcelable.Creator object called "
|
||||||
|
+ "CREATOR on class " + name);
|
||||||
|
}
|
||||||
|
|
||||||
|
map.put(name, creator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return creator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Parcelable.ClassLoaderCreator<ParceledListSlice> CREATOR =
|
||||||
|
new Parcelable.ClassLoaderCreator<ParceledListSlice>() {
|
||||||
|
public ParceledListSlice createFromParcel(Parcel in) {
|
||||||
|
return new ParceledListSlice(in, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ParceledListSlice createFromParcel(Parcel in, ClassLoader loader) {
|
||||||
|
return new ParceledListSlice(in, loader);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ParceledListSlice[] newArray(int size) {
|
||||||
|
return new ParceledListSlice[size];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
rootProject.name = "LSPosed"
|
rootProject.name = "LSPosed"
|
||||||
include ':core', ':hiddenapi-stubs', ':sandhook-hooklib', ':sandhook-annotation', ':app', ':key-selector', ':service', ':interface', ':hiddenapi-bridge'
|
include ':core', ':hiddenapi-stubs', ':sandhook-hooklib', ':sandhook-annotation', ':app', ':key-selector', ':service', ':interface', ':hiddenapi-bridge', ':manager-service'
|
||||||
|
|
||||||
def service_root = "service"
|
def service_root = "service"
|
||||||
project(':interface').projectDir = file("$service_root${File.separator}interface")
|
project(':interface').projectDir = file("$service_root${File.separator}interface")
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue