[core] Store pkg name & user id, judge process name & uid
This commit is contained in:
parent
aa50e65cf1
commit
6e115426e4
|
|
@ -11,7 +11,7 @@ interface ILSPApplicationService {
|
|||
|
||||
boolean isResourcesHookEnabled() = 5;
|
||||
|
||||
String[] getModulesList() = 6;
|
||||
String[] getModulesList(String processName) = 6;
|
||||
|
||||
String getPrefsPath(String packageName) = 7;
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package io.github.lsposed.lspd.service;
|
|||
import io.github.lsposed.lspd.service.ILSPApplicationService;
|
||||
|
||||
interface ILSPosedService {
|
||||
ILSPApplicationService requestApplicationService(int uid, int pid) = 1;
|
||||
ILSPApplicationService requestApplicationService(int uid, int pid, String processName) = 1;
|
||||
|
||||
oneway void dispatchPackageChanged(in Intent intent) = 2;
|
||||
}
|
||||
|
|
@ -234,7 +234,7 @@ namespace lspd {
|
|||
void
|
||||
Context::OnNativeForkAndSpecializePost(JNIEnv *env) {
|
||||
const JUTFString process_name(env, nice_name_);
|
||||
auto binder = skip_? nullptr : Service::instance()->RequestBinder(env);
|
||||
auto binder = skip_? nullptr : Service::instance()->RequestBinder(env, nice_name_);
|
||||
if (binder) {
|
||||
LoadDex(env);
|
||||
InstallInlineHooks();
|
||||
|
|
|
|||
|
|
@ -73,8 +73,8 @@ namespace lspd {
|
|||
write_interface_token_method_ = env->GetMethodID(parcel_class_, "writeInterfaceToken",
|
||||
"(Ljava/lang/String;)V");
|
||||
write_int_method_ = env->GetMethodID(parcel_class_, "writeInt", "(I)V");
|
||||
// writeStringMethod_ = env->GetMethodID(parcel_class_, "writeString",
|
||||
// "(Ljava/lang/String;)V");
|
||||
write_string_method_ = env->GetMethodID(parcel_class_, "writeString",
|
||||
"(Ljava/lang/String;)V");
|
||||
read_exception_method_ = env->GetMethodID(parcel_class_, "readException", "()V");
|
||||
read_strong_binder_method_ = env->GetMethodID(parcel_class_, "readStrongBinder",
|
||||
"()Landroid/os/IBinder;");
|
||||
|
|
@ -126,7 +126,7 @@ namespace lspd {
|
|||
LOGD("Done InitService");
|
||||
}
|
||||
|
||||
jobject Service::RequestBinder(JNIEnv *env) {
|
||||
jobject Service::RequestBinder(JNIEnv *env, jstring nice_name) {
|
||||
if (UNLIKELY(!initialized_)) {
|
||||
LOGE("Service not initialized");
|
||||
return nullptr;
|
||||
|
|
@ -146,6 +146,7 @@ namespace lspd {
|
|||
auto descriptor = env->NewStringUTF(BRIDGE_SERVICE_DESCRIPTOR.data());
|
||||
JNI_CallVoidMethod(env, data, write_interface_token_method_, descriptor);
|
||||
JNI_CallVoidMethod(env, data, write_int_method_, BRIDGE_ACTION_GET_BINDER);
|
||||
JNI_CallVoidMethod(env, data, write_string_method_, nice_name);
|
||||
|
||||
auto res = JNI_CallBooleanMethod(env, bridgeService, transact_method_,
|
||||
BRIDGE_TRANSACTION_CODE,
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ namespace lspd {
|
|||
|
||||
void HookBridge(const Context& context, JNIEnv *env);
|
||||
|
||||
jobject RequestBinder(JNIEnv *env);
|
||||
jobject RequestBinder(JNIEnv *env, jstring nice_name);
|
||||
|
||||
jobject RequestBinderForSystemServer(JNIEnv *env);
|
||||
|
||||
|
|
@ -65,6 +65,7 @@ namespace lspd {
|
|||
jmethodID recycleMethod_ = nullptr;
|
||||
jmethodID write_interface_token_method_ = nullptr;
|
||||
jmethodID write_int_method_ = nullptr;
|
||||
jmethodID write_string_method_ = nullptr;
|
||||
jmethodID read_exception_method_ = nullptr;
|
||||
jmethodID read_strong_binder_method_ = nullptr;
|
||||
|
||||
|
|
|
|||
|
|
@ -353,6 +353,7 @@ public final class XposedInit {
|
|||
topClassLoader = parent;
|
||||
}
|
||||
|
||||
// TODO: process name
|
||||
String[] moduleList = serviceClient.getModulesList();
|
||||
ArraySet<String> newLoadedApk = new ArraySet<>();
|
||||
for (String apk : moduleList)
|
||||
|
|
|
|||
|
|
@ -16,12 +16,14 @@ public class LSPApplicationServiceClient implements ILSPApplicationService {
|
|||
|
||||
static String baseCachePath = null;
|
||||
static String basePrefsPath = null;
|
||||
static String processName = null;
|
||||
|
||||
public static LSPApplicationServiceClient serviceClient = null;
|
||||
|
||||
public static void Init(IBinder binder) {
|
||||
public static void Init(IBinder binder, String niceName) {
|
||||
if (serviceClient == null && binder != null && serviceBinder == null && service == null) {
|
||||
serviceBinder = binder;
|
||||
processName = niceName;
|
||||
try {
|
||||
serviceBinder.linkToDeath(
|
||||
new IBinder.DeathRecipient() {
|
||||
|
|
@ -89,14 +91,18 @@ public class LSPApplicationServiceClient implements ILSPApplicationService {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String[] getModulesList() {
|
||||
public String[] getModulesList(String processName) {
|
||||
try {
|
||||
return service.getModulesList();
|
||||
return service.getModulesList(processName);
|
||||
} catch (RemoteException | NullPointerException ignored) {
|
||||
}
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
public String[] getModulesList(){
|
||||
return getModulesList(processName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefsPath(String packageName) {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ public class Main implements KeepAll {
|
|||
private static final Binder heartBeatBinder = new Binder();
|
||||
|
||||
public static void forkAndSpecializePost(String appDataDir, String niceName, IBinder binder) {
|
||||
LSPApplicationServiceClient.Init(binder);
|
||||
LSPApplicationServiceClient.Init(binder, niceName);
|
||||
serviceClient.registerHeartBeat(heartBeatBinder);
|
||||
final int variant = serviceClient.getVariant();
|
||||
Impl lspd = getImpl(variant);
|
||||
|
|
@ -52,7 +52,7 @@ public class Main implements KeepAll {
|
|||
}
|
||||
|
||||
public static void forkSystemServerPost(IBinder binder) {
|
||||
LSPApplicationServiceClient.Init(binder);
|
||||
LSPApplicationServiceClient.Init(binder, "android");
|
||||
serviceClient.registerHeartBeat(heartBeatBinder);
|
||||
final int variant = serviceClient.getVariant();
|
||||
Impl lspd = getImpl(variant);
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import android.os.IBinder;
|
|||
import android.os.Parcel;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Log;
|
||||
import android.os.Process;
|
||||
|
||||
|
|
@ -15,7 +16,15 @@ import androidx.annotation.Keep;
|
|||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.SystemService;
|
||||
import com.android.server.SystemServiceManager;
|
||||
import com.android.server.am.ActivityManagerService;
|
||||
import com.android.server.am.ProcessRecord;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
|
||||
import static hidden.HiddenApiBridge.Binder_allowBlocking;
|
||||
|
|
@ -44,6 +53,7 @@ public class BridgeService {
|
|||
this.bridgeService = bridgeService;
|
||||
bridgeService.linkToDeath(this, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void binderDied() {
|
||||
Log.i(TAG, "service " + SERVICE_NAME + " is dead. ");
|
||||
|
|
@ -222,7 +232,8 @@ public class BridgeService {
|
|||
case ACTION_GET_BINDER: {
|
||||
IBinder binder = null;
|
||||
try {
|
||||
binder = service.requestApplicationService(Binder.getCallingUid(), Binder.getCallingPid()).asBinder();
|
||||
String processName = data.readString();
|
||||
binder = service.requestApplicationService(Binder.getCallingUid(), Binder.getCallingPid(), processName).asBinder();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, Log.getStackTraceString(e));
|
||||
}
|
||||
|
|
@ -282,11 +293,67 @@ public class BridgeService {
|
|||
if (binder == null) return null;
|
||||
try {
|
||||
ILSPosedService service = ILSPosedService.Stub.asInterface(binder);
|
||||
ILSPApplicationService applicationService = service.requestApplicationService(Process.myUid(), Process.myPid());
|
||||
ILSPApplicationService applicationService = service.requestApplicationService(Process.myUid(), Process.myPid(), "android");
|
||||
if (applicationService != null) return applicationService.asBinder();
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, Log.getStackTraceString(e));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
private static void tryGetActivityManagerServiceInstance() {
|
||||
try {
|
||||
Log.e(TAG, "Trying to get the ams");
|
||||
Field localServiceField = LocalServices.class.getDeclaredField("sLocalServiceObjects");
|
||||
localServiceField.setAccessible(true);
|
||||
ArrayMap<Class<?>, Object> localServiceMap = (ArrayMap<Class<?>, Object>) localServiceField.get(null);
|
||||
Class<?> systemServiceManagerClass = null;
|
||||
for (Class<?> clazz : localServiceMap.keySet()) {
|
||||
if (clazz.getName().equals("com.android.server.SystemServiceManager")) {
|
||||
systemServiceManagerClass = clazz;
|
||||
}
|
||||
|
||||
}
|
||||
Field parentField = ClassLoader.class.getDeclaredField("parent");
|
||||
parentField.setAccessible(true);
|
||||
parentField.set(BridgeService.class.getClassLoader(), systemServiceManagerClass.getClassLoader());
|
||||
SystemServiceManager systemServiceManager = LocalServices.getService(SystemServiceManager.class);
|
||||
ArrayList<SystemService> services;
|
||||
try {
|
||||
Field servicesField = systemServiceManagerClass.getDeclaredField("mServices");
|
||||
servicesField.setAccessible(true);
|
||||
services = (ArrayList<SystemService>) servicesField.get(systemServiceManager);
|
||||
} catch (NoSuchFieldException | IllegalAccessException e) {
|
||||
Log.e(TAG, Log.getStackTraceString(e));
|
||||
return;
|
||||
}
|
||||
|
||||
ActivityManagerService.Lifecycle lifecycle = null;
|
||||
|
||||
for (SystemService service : services) {
|
||||
if (service instanceof ActivityManagerService.Lifecycle) {
|
||||
lifecycle = (ActivityManagerService.Lifecycle) service;
|
||||
}
|
||||
}
|
||||
if (lifecycle == null) {
|
||||
Log.e(TAG, "I cannot get the lifecycle...");
|
||||
}
|
||||
ActivityManagerService activityManagerService = lifecycle.getService();
|
||||
if (activityManagerService != null) {
|
||||
Log.e(TAG, "I got the ams!!!: " + activityManagerService);
|
||||
} else {
|
||||
Log.e(TAG, "I cannot get the ams");
|
||||
}
|
||||
Method findProcessLockedMethod = ActivityManagerService.class.getDeclaredMethod("findProcessLocked", String.class, int.class, String.class);
|
||||
findProcessLockedMethod.setAccessible(true);
|
||||
ProcessRecord record = (ProcessRecord) findProcessLockedMethod.invoke(activityManagerService, String.valueOf(Binder.getCallingPid()), 0, "LSPosed");
|
||||
Field processNameField = ProcessRecord.class.getDeclaredField("processName");
|
||||
processNameField.setAccessible(true);
|
||||
if (record != null) {
|
||||
Log.e(TAG, "I got the record!!!: " + record);
|
||||
Log.e(TAG, "I got the process name: " + processNameField.get(record));
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
Log.e(TAG, Log.getStackTraceString(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,9 +21,14 @@ import java.io.OutputStream;
|
|||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import io.github.lsposed.lspd.Application;
|
||||
|
||||
import static io.github.lsposed.lspd.service.ServiceManager.TAG;
|
||||
|
||||
// This config manager assume uid won't change when our service is off.
|
||||
|
|
@ -31,35 +36,35 @@ import static io.github.lsposed.lspd.service.ServiceManager.TAG;
|
|||
public class ConfigManager {
|
||||
static ConfigManager instance = null;
|
||||
|
||||
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);
|
||||
static final private File basePath = new File("/data/adb/lspd");
|
||||
static final private File configPath = new File(basePath, "config");
|
||||
static final private SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(new File(configPath, "modules_config.db"), null);
|
||||
|
||||
final private File resourceHookSwitch = new File(configPath, "enable_resources");
|
||||
static final private File resourceHookSwitch = new File(configPath, "enable_resources");
|
||||
private boolean resourceHook = false;
|
||||
|
||||
final private File variantSwitch = new File(configPath, "variant");
|
||||
static final private File variantSwitch = new File(configPath, "variant");
|
||||
private int variant = -1;
|
||||
|
||||
final private File verboseLogSwitch = new File(configPath, "verbose_log");
|
||||
static final private File verboseLogSwitch = new File(configPath, "verbose_log");
|
||||
private boolean verboseLog = false;
|
||||
|
||||
final private String DEFAULT_MANAGER_PACKAGE_NAME = "io.github.lsposed.manager";
|
||||
static final private String DEFAULT_MANAGER_PACKAGE_NAME = "io.github.lsposed.manager";
|
||||
|
||||
final private File managerPath = new File(configPath, "manager");
|
||||
static final private File managerPath = new File(configPath, "manager");
|
||||
private String manager = null;
|
||||
private int managerUid = -1;
|
||||
|
||||
final private File miscFile = new File(basePath, "misc_path");
|
||||
static final private File miscFile = new File(basePath, "misc_path");
|
||||
private String miscPath = null;
|
||||
|
||||
final private File selinuxPath = new File("/sys/fs/selinux/enforce");
|
||||
static final private File selinuxPath = new File("/sys/fs/selinux/enforce");
|
||||
// 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, "all.log");
|
||||
static final private File logPath = new File(basePath, "log");
|
||||
static final private File modulesLogPath = new File(logPath, "modules.log");
|
||||
static final private File verboseLogPath = new File(logPath, "all.log");
|
||||
|
||||
final FileObserver configObserver = new FileObserver(configPath) {
|
||||
@Override
|
||||
|
|
@ -69,11 +74,63 @@ public class ConfigManager {
|
|||
}
|
||||
};
|
||||
|
||||
private String readText(@NonNull File file) throws IOException {
|
||||
static class ProcessScope {
|
||||
String processName;
|
||||
int uid;
|
||||
|
||||
ProcessScope(@NonNull String processName, int uid) {
|
||||
this.processName = processName;
|
||||
this.uid = uid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(@Nullable Object o) {
|
||||
if(o instanceof ProcessScope) {
|
||||
ProcessScope p = (ProcessScope) o;
|
||||
return p.processName.equals(processName) && p.uid == uid;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static private final SQLiteStatement createModulesTable = db.compileStatement("CREATE TABLE IF NOT EXISTS modules (" +
|
||||
"mid integer PRIMARY KEY AUTOINCREMENT," +
|
||||
"module_pkg_name text NOT NULL UNIQUE," +
|
||||
"apk_path text NOT NULL, " +
|
||||
"enabled BOOLEAN DEFAULT 0 " +
|
||||
"CHECK (enabled IN (0, 1))" +
|
||||
");");
|
||||
static private final SQLiteStatement createScopeTable = db.compileStatement("CREATE TABLE IF NOT EXISTS scope (" +
|
||||
"mid integer," +
|
||||
"app_pkg_name text NOT NULL," +
|
||||
"user_id integer NOT NULL," +
|
||||
"PRIMARY KEY (mid, app_pkg_name, user_id)" +
|
||||
");");
|
||||
|
||||
private final ConcurrentHashMap<ProcessScope, Set<String>> cachedScope = new ConcurrentHashMap<>();
|
||||
|
||||
public static boolean shouldSkipSystemServer() {
|
||||
try(Cursor cursor = db.query("scope", new String[]{"mid"}, "app_pkg_name=?", new String[]{"android"}, null, null, null)){
|
||||
return cursor == null || !cursor.moveToNext();
|
||||
}
|
||||
}
|
||||
|
||||
public static String[] getModulesPathForSystemServer() {
|
||||
HashSet<String> modules = new HashSet<>();
|
||||
try(Cursor cursor = db.query("scope INNER JOIN modules ON scope.mid = modules.mid", new String[]{"apk_path"}, "app_pkg_name=?", new String[]{"android"}, null, null, null)){
|
||||
int apkPathIdx = cursor.getColumnIndex("apk_path");
|
||||
while(cursor.moveToNext()) {
|
||||
modules.add(cursor.getString(apkPathIdx));
|
||||
}
|
||||
}
|
||||
return modules.toArray(new String[0]);
|
||||
}
|
||||
|
||||
static private String readText(@NonNull File file) throws IOException {
|
||||
return new String(Files.readAllBytes(file.toPath())).trim();
|
||||
}
|
||||
|
||||
private String readText(@NonNull File file, String defaultValue) {
|
||||
static private String readText(@NonNull File file, String defaultValue) {
|
||||
try {
|
||||
if (!file.exists()) return defaultValue;
|
||||
return readText(file);
|
||||
|
|
@ -83,7 +140,7 @@ public class ConfigManager {
|
|||
return defaultValue;
|
||||
}
|
||||
|
||||
private void writeText(@NonNull File file, String value) {
|
||||
static private void writeText(@NonNull File file, String value) {
|
||||
try {
|
||||
Files.write(file.toPath(), value.getBytes(), StandardOpenOption.CREATE);
|
||||
} catch (IOException e) {
|
||||
|
|
@ -91,7 +148,7 @@ public class ConfigManager {
|
|||
}
|
||||
}
|
||||
|
||||
private int readInt(@NonNull File file, int defaultValue) {
|
||||
static private int readInt(@NonNull File file, int defaultValue) {
|
||||
try {
|
||||
if (!file.exists()) return defaultValue;
|
||||
return Integer.parseInt(readText(file));
|
||||
|
|
@ -101,7 +158,7 @@ public class ConfigManager {
|
|||
return defaultValue;
|
||||
}
|
||||
|
||||
private void writeInt(@NonNull File file, int value) {
|
||||
static private void writeInt(@NonNull File file, int value) {
|
||||
writeText(file, String.valueOf(value));
|
||||
}
|
||||
|
||||
|
|
@ -130,21 +187,6 @@ public class ConfigManager {
|
|||
updateManager();
|
||||
}
|
||||
|
||||
private final SQLiteStatement createModulesTable = db.compileStatement("CREATE TABLE IF NOT EXISTS modules (" +
|
||||
"mid integer PRIMARY KEY AUTOINCREMENT," +
|
||||
"package_name text NOT NULL UNIQUE," +
|
||||
"apk_path text NOT NULL, " +
|
||||
"enabled BOOLEAN DEFAULT 0 " +
|
||||
"CHECK (enabled IN (0, 1))" +
|
||||
");");
|
||||
private final SQLiteStatement createScopeTable = db.compileStatement("CREATE TABLE IF NOT EXISTS scope (" +
|
||||
"mid integer," +
|
||||
"uid integer," +
|
||||
"PRIMARY KEY (mid, uid)" +
|
||||
");");
|
||||
|
||||
private final ConcurrentHashMap<Integer, ArrayList<String>> modulesForUid = new ConcurrentHashMap<>();
|
||||
|
||||
static ConfigManager getInstance() {
|
||||
if (instance == null)
|
||||
instance = new ConfigManager();
|
||||
|
|
@ -164,51 +206,81 @@ public class ConfigManager {
|
|||
createScopeTable.execute();
|
||||
}
|
||||
|
||||
private List<ProcessScope> getAssociatedProcesses(Application app) throws RemoteException {
|
||||
PackageInfo pkgInfo = PackageService.getPackageInfo(app.packageName, 0, app.userId);
|
||||
List<ProcessScope> processes = new ArrayList<>();
|
||||
if (pkgInfo != null && pkgInfo.applicationInfo != null) {
|
||||
for(String process: PackageService.getProcessesForUid(pkgInfo.applicationInfo.uid)){
|
||||
processes.add(new ProcessScope(process, pkgInfo.applicationInfo.uid));
|
||||
}
|
||||
}
|
||||
return processes;
|
||||
}
|
||||
|
||||
private synchronized void cacheScopes() {
|
||||
modulesForUid.clear();
|
||||
try (Cursor cursor = db.query("scope INNER JOIN modules ON scope.mid = modules.mid", new String[]{"uid", "apk_path"},
|
||||
cachedScope.clear();
|
||||
try (Cursor cursor = db.query("scope INNER JOIN modules ON scope.mid = modules.mid", new String[]{"app_pkg_name", "user_id", "apk_path"},
|
||||
"enabled = ?", new String[]{"1"}, null, null, null)) {
|
||||
if (cursor == null) {
|
||||
Log.e(TAG, "db cache failed");
|
||||
return;
|
||||
}
|
||||
int uid_idx = cursor.getColumnIndex("uid");
|
||||
int apk_path_idx = cursor.getColumnIndex("apk_path");
|
||||
int appPkgNameIdx = cursor.getColumnIndex("app_pkg_name");
|
||||
int userIdIdx = cursor.getColumnIndex("user_id");
|
||||
int apkPathIdx = cursor.getColumnIndex("apk_path");
|
||||
HashSet<Application> obsoletePackages = new HashSet<>();
|
||||
while (cursor.moveToNext()) {
|
||||
int uid = cursor.getInt(uid_idx);
|
||||
String apk_path = cursor.getString(apk_path_idx);
|
||||
modulesForUid.computeIfAbsent(uid, ignored -> new ArrayList<>()).add(apk_path);
|
||||
Application app = new Application();
|
||||
app.packageName = cursor.getString(appPkgNameIdx);
|
||||
app.userId = cursor.getInt(userIdIdx);
|
||||
String apk_path = cursor.getString(apkPathIdx);
|
||||
try {
|
||||
List<ProcessScope> processesScope = getAssociatedProcesses(app);
|
||||
if (processesScope.isEmpty()) {
|
||||
obsoletePackages.add(app);
|
||||
continue;
|
||||
}
|
||||
for (ProcessScope processScope : processesScope)
|
||||
cachedScope.computeIfAbsent(processScope, ignored -> new HashSet<>()).add(apk_path);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, Log.getStackTraceString(e));
|
||||
}
|
||||
}
|
||||
for (Application obsoletePackage : obsoletePackages) {
|
||||
removeAppWithoutCache(obsoletePackage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This is called when a new process created, use the cached result
|
||||
public String[] getModulesPathForUid(int uid) {
|
||||
return isManager(uid) ? new String[0] : modulesForUid.getOrDefault(uid, new ArrayList<>()).toArray(new String[0]);
|
||||
public String[] getModulesPathForProcess(String processName, int uid) {
|
||||
return isManager(uid) ? new String[0] : cachedScope.getOrDefault(new ProcessScope(processName, uid), Collections.emptySet()).toArray(new String[0]);
|
||||
}
|
||||
|
||||
// This is called when a new process created, use the cached result
|
||||
// The signature matches Riru's
|
||||
public boolean shouldSkipUid(int uid) {
|
||||
return !modulesForUid.containsKey(uid) && !isManager(uid);
|
||||
public boolean shouldSkipProcess(ProcessScope scope) {
|
||||
return !cachedScope.containsKey(scope) && !isManager(scope.uid);
|
||||
}
|
||||
|
||||
// This should only be called by manager, so we don't need to cache it
|
||||
public int[] getModuleScope(String packageName) {
|
||||
public List<Application> getModuleScope(String packageName) {
|
||||
int mid = getModuleId(packageName);
|
||||
if (mid == -1) return null;
|
||||
try (Cursor cursor = db.query("scope INNER JOIN modules ON scope.mid = modules.mid", new String[]{"uid"},
|
||||
try (Cursor cursor = db.query("scope INNER JOIN modules ON scope.mid = modules.mid", new String[]{"app_pkg_name", "user_id"},
|
||||
"scope.mid = ?", new String[]{String.valueOf(mid)}, null, null, null)) {
|
||||
if (cursor == null) {
|
||||
return null;
|
||||
}
|
||||
int uid_idx = cursor.getColumnIndex("uid");
|
||||
HashSet<Integer> result = new HashSet<>();
|
||||
int userIdIdx = cursor.getColumnIndex("user_id");
|
||||
int appPkgNameIdx = cursor.getColumnIndex("app_pkg_name");
|
||||
ArrayList<Application> result = new ArrayList<>();
|
||||
while (cursor.moveToNext()) {
|
||||
int uid = cursor.getInt(uid_idx);
|
||||
result.add(uid);
|
||||
Application scope = new Application();
|
||||
scope.packageName = cursor.getString(appPkgNameIdx);
|
||||
scope.userId = cursor.getInt(userIdIdx);
|
||||
result.add(scope);
|
||||
}
|
||||
return result.stream().mapToInt(i -> i).toArray();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -218,11 +290,11 @@ public class ConfigManager {
|
|||
return false;
|
||||
}
|
||||
ContentValues values = new ContentValues();
|
||||
values.put("package_name", packageName);
|
||||
values.put("module_pkg_name", packageName);
|
||||
values.put("apk_path", apkPath);
|
||||
int count = (int) db.insertWithOnConflict("modules", null, values, SQLiteDatabase.CONFLICT_IGNORE);
|
||||
if (count < 0) {
|
||||
count = db.updateWithOnConflict("modules", values, "package_name=?", new String[]{packageName}, SQLiteDatabase.CONFLICT_IGNORE);
|
||||
count = db.updateWithOnConflict("modules", values, "module_pkg_name=?", new String[]{packageName}, SQLiteDatabase.CONFLICT_IGNORE);
|
||||
}
|
||||
if (count >= 1) {
|
||||
cacheScopes();
|
||||
|
|
@ -236,7 +308,7 @@ public class ConfigManager {
|
|||
Log.w(TAG, "get module id should not be called inside transaction");
|
||||
return -1;
|
||||
}
|
||||
try (Cursor cursor = db.query("modules", new String[]{"mid"}, "package_name=?", new String[]{packageName}, null, null, null)) {
|
||||
try (Cursor cursor = db.query("modules", new String[]{"mid"}, "module_pkg_name=?", new String[]{packageName}, null, null, null)) {
|
||||
if (cursor == null) return -1;
|
||||
if (cursor.getCount() != 1) return -1;
|
||||
cursor.moveToFirst();
|
||||
|
|
@ -244,17 +316,18 @@ public class ConfigManager {
|
|||
}
|
||||
}
|
||||
|
||||
public boolean setModuleScope(String packageName, int[] uid) {
|
||||
if (uid == null || uid.length == 0) return false;
|
||||
public boolean setModuleScope(String packageName, List<Application> scopes) {
|
||||
if (scopes.isEmpty()) return false;
|
||||
int mid = getModuleId(packageName);
|
||||
if (mid == -1) return false;
|
||||
try {
|
||||
db.beginTransaction();
|
||||
db.delete("scope", "mid = ?", new String[]{String.valueOf(mid)});
|
||||
for (int id : uid) {
|
||||
for (Application app : scopes) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put("mid", mid);
|
||||
values.put("uid", id);
|
||||
values.put("app_pkg_name", app.packageName);
|
||||
values.put("user_id", app.userId);
|
||||
db.insertWithOnConflict("scope", null, values, SQLiteDatabase.CONFLICT_IGNORE);
|
||||
}
|
||||
} finally {
|
||||
|
|
@ -266,15 +339,15 @@ public class ConfigManager {
|
|||
}
|
||||
|
||||
public String[] enabledModules() {
|
||||
try (Cursor cursor = db.query("modules", new String[]{"package_name"}, "enabled = ?", new String[]{"1"}, null, null, null)) {
|
||||
try (Cursor cursor = db.query("modules", new String[]{"module_pkg_name"}, "enabled = ?", new String[]{"1"}, null, null, null)) {
|
||||
if (cursor == null) {
|
||||
Log.e(TAG, "query enabled modules failed");
|
||||
return null;
|
||||
}
|
||||
int pkg_idx = cursor.getColumnIndex("package_name");
|
||||
int modulePkgNameIdx = cursor.getColumnIndex("module_pkg_name");
|
||||
HashSet<String> result = new HashSet<>();
|
||||
while (cursor.moveToNext()) {
|
||||
result.add(cursor.getString(pkg_idx));
|
||||
result.add(cursor.getString(modulePkgNameIdx));
|
||||
}
|
||||
return result.toArray(new String[0]);
|
||||
}
|
||||
|
|
@ -328,9 +401,13 @@ public class ConfigManager {
|
|||
return true;
|
||||
}
|
||||
|
||||
public boolean removeApp(int uid) {
|
||||
int count = db.delete("scope", "uid = ?", new String[]{String.valueOf(uid)});
|
||||
if (count >= 1) {
|
||||
public boolean removeAppWithoutCache(Application app) {
|
||||
int count = db.delete("scope", "app_pkg_name = ? AND user_id=?", new String[]{app.packageName, String.valueOf(app.userId)});
|
||||
return count >= 1;
|
||||
}
|
||||
|
||||
public boolean removeApp(Application scope) {
|
||||
if (removeAppWithoutCache(scope)) {
|
||||
cacheScopes();
|
||||
return true;
|
||||
}
|
||||
|
|
@ -397,8 +474,8 @@ public class ConfigManager {
|
|||
}
|
||||
}
|
||||
|
||||
public boolean isManager(String package_name) {
|
||||
return package_name.equals(manager);
|
||||
public boolean isManager(String module_pkg_name) {
|
||||
return module_pkg_name.equals(manager);
|
||||
}
|
||||
|
||||
public boolean isManager(int uid) {
|
||||
|
|
|
|||
|
|
@ -48,9 +48,13 @@ public class LSPApplicationService extends ILSPApplicationService.Stub {
|
|||
}
|
||||
|
||||
@Override
|
||||
public String[] getModulesList() throws RemoteException {
|
||||
public String[] getModulesList(String processName) throws RemoteException {
|
||||
ensureRegistered();
|
||||
return ConfigManager.getInstance().getModulesPathForUid(Binder.getCallingUid());
|
||||
int callingUid = Binder.getCallingUid();
|
||||
if (callingUid == 1000 && processName.equals("android")) {
|
||||
ConfigManager.getModulesPathForSystemServer();
|
||||
}
|
||||
return ConfigManager.getInstance().getModulesPathForProcess(processName, callingUid);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -5,11 +5,10 @@ import android.os.IBinder;
|
|||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.RemoteException;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import de.robv.android.xposed.XposedBridge;
|
||||
import io.github.lsposed.lspd.BuildConfig;
|
||||
import io.github.lsposed.lspd.ILSPManagerService;
|
||||
import io.github.lsposed.lspd.Application;
|
||||
import io.github.lsposed.lspd.utils.ParceledListSlice;
|
||||
|
||||
public class LSPManagerService extends ILSPManagerService.Stub {
|
||||
|
|
@ -55,13 +54,13 @@ public class LSPManagerService extends ILSPManagerService.Stub {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean setModuleScope(String packageName, int[] uid) {
|
||||
return ConfigManager.getInstance().setModuleScope(packageName, uid);
|
||||
public boolean setModuleScope(String packageName, ParceledListSlice<Application> scope) {
|
||||
return ConfigManager.getInstance().setModuleScope(packageName, scope.getList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] getModuleScope(String packageName) {
|
||||
return ConfigManager.getInstance().getModuleScope(packageName);
|
||||
public ParceledListSlice<Application> getModuleScope(String packageName) {
|
||||
return new ParceledListSlice<Application>(ConfigManager.getInstance().getModuleScope(packageName));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -8,17 +8,25 @@ import android.os.Binder;
|
|||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
|
||||
import io.github.lsposed.lspd.Application;
|
||||
|
||||
import static io.github.lsposed.lspd.service.ServiceManager.TAG;
|
||||
|
||||
public class LSPosedService extends ILSPosedService.Stub {
|
||||
@Override
|
||||
public ILSPApplicationService requestApplicationService(int uid, int pid) {
|
||||
public ILSPApplicationService requestApplicationService(int uid, int pid, String processName) {
|
||||
if (Binder.getCallingUid() != 1000) {
|
||||
Log.w(TAG, "Someone else got my binder!?");
|
||||
return null;
|
||||
}
|
||||
if (ConfigManager.getInstance().shouldSkipUid(uid)) {
|
||||
Log.d(TAG, "Skipped uid " + uid);
|
||||
if (uid == 1000 && processName.equals("android")) {
|
||||
if (ConfigManager.shouldSkipSystemServer())
|
||||
return null;
|
||||
else
|
||||
return ServiceManager.getApplicationService();
|
||||
}
|
||||
if (ConfigManager.getInstance().shouldSkipProcess(new ConfigManager.ProcessScope(processName, uid))) {
|
||||
Log.d(TAG, "Skipped " + processName);
|
||||
return null;
|
||||
}
|
||||
if (ServiceManager.getApplicationService().hasRegister(uid, pid)) {
|
||||
|
|
@ -40,10 +48,14 @@ public class LSPosedService extends ILSPosedService.Stub {
|
|||
return;
|
||||
}
|
||||
int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
|
||||
int userId = intent.getIntExtra(Intent.EXTRA_USER, -1);
|
||||
boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
|
||||
if (intent.getAction().equals(Intent.ACTION_PACKAGE_REMOVED) && uid > 0 && !replacing) {
|
||||
ConfigManager.getInstance().removeModule(packageName);
|
||||
ConfigManager.getInstance().removeApp(uid);
|
||||
Application app = new Application();
|
||||
app.packageName = packageName;
|
||||
app.userId = userId;
|
||||
ConfigManager.getInstance().removeApp(app);
|
||||
}
|
||||
PackageInfo pkgInfo = PackageService.getPackageInfo(packageName, PackageManager.GET_META_DATA, 0);
|
||||
boolean isXposedModule = pkgInfo != null && pkgInfo.applicationInfo != null &&
|
||||
|
|
|
|||
|
|
@ -1,16 +1,25 @@
|
|||
package io.github.lsposed.lspd.service;
|
||||
|
||||
import android.content.pm.ComponentInfo;
|
||||
import android.content.pm.IPackageManager;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.os.IBinder;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import io.github.lsposed.lspd.utils.ParceledListSlice;
|
||||
|
||||
import static android.content.pm.ServiceInfo.FLAG_ISOLATED_PROCESS;
|
||||
|
||||
public class PackageService {
|
||||
public static final int PER_USER_RANGE = 100000;
|
||||
|
||||
private static IPackageManager pm = null;
|
||||
private static IBinder binder = null;
|
||||
|
||||
|
|
@ -34,6 +43,14 @@ public class PackageService {
|
|||
return pm.getPackagesForUid(uid);
|
||||
}
|
||||
|
||||
public static Set<String> getProcessesForUid(int uid) throws RemoteException {
|
||||
HashSet<String> processNames = new HashSet<>();
|
||||
for (String packageName : getPackagesForUid(uid)) {
|
||||
processNames.addAll(fetchProcesses(packageName, uid / PER_USER_RANGE));
|
||||
}
|
||||
return processNames;
|
||||
}
|
||||
|
||||
public static ParceledListSlice<PackageInfo> getInstalledPackagesFromAllUsers(int flags) throws RemoteException {
|
||||
ArrayList<PackageInfo> res = new ArrayList<>();
|
||||
IPackageManager pm = getPackageManager();
|
||||
|
|
@ -46,6 +63,31 @@ public class PackageService {
|
|||
|
||||
public static void grantRuntimePermission(String packageName, String permissionName, int userId) throws RemoteException {
|
||||
IPackageManager pm = getPackageManager();
|
||||
if (pm == null) return;
|
||||
pm.grantRuntimePermission(packageName, permissionName, userId);
|
||||
}
|
||||
|
||||
public static Set<String> fetchProcesses(PackageInfo pkgInfo) {
|
||||
HashSet<String> processNames = new HashSet<>();
|
||||
for (ComponentInfo[] componentInfos : new ComponentInfo[][]{pkgInfo.activities, pkgInfo.receivers, pkgInfo.providers}) {
|
||||
for (ComponentInfo componentInfo : componentInfos) {
|
||||
processNames.add(componentInfo.processName);
|
||||
}
|
||||
}
|
||||
for (ServiceInfo service : pkgInfo.services) {
|
||||
if ((service.flags & FLAG_ISOLATED_PROCESS) == 0) {
|
||||
processNames.add(service.processName);
|
||||
}
|
||||
}
|
||||
return processNames;
|
||||
}
|
||||
|
||||
public static Set<String> fetchProcesses(String packageName, int userId) throws RemoteException {
|
||||
IPackageManager pm = getPackageManager();
|
||||
if (pm == null) return new HashSet<>();
|
||||
PackageInfo pkgInfo = pm.getPackageInfo(packageName, PackageManager.MATCH_DISABLED_COMPONENTS |
|
||||
PackageManager.MATCH_UNINSTALLED_PACKAGES | PackageManager.GET_ACTIVITIES |
|
||||
PackageManager.GET_SERVICES | PackageManager.GET_RECEIVERS | PackageManager.GET_PROVIDERS, userId);
|
||||
return fetchProcesses(pkgInfo);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package io.github.lsposed.lspd.service;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
|
||||
|
|
@ -32,7 +33,7 @@ public class ServiceManager {
|
|||
applicationService = new LSPApplicationService();
|
||||
managerService = new LSPManagerService();
|
||||
|
||||
android.os.ServiceManager.addService("serial", mainService);
|
||||
android.os.ServiceManager.addService("serial", (IBinder) mainService);
|
||||
|
||||
waitSystemService("package");
|
||||
waitSystemService("activity");
|
||||
|
|
@ -61,10 +62,15 @@ public class ServiceManager {
|
|||
Log.e(TAG, Log.getStackTraceString(e));
|
||||
}
|
||||
|
||||
Looper.loop();
|
||||
|
||||
Log.i(TAG, "server exited");
|
||||
System.exit(0);
|
||||
//noinspection InfiniteLoopStatement
|
||||
while (true) {
|
||||
try {
|
||||
Looper.loop();
|
||||
} catch (Throwable e) {
|
||||
Log.i(TAG, "server exited with " + Log.getStackTraceString(e));
|
||||
Log.i(TAG, "restarting");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static LSPModuleService getModuleService() {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
package com.android.server;
|
||||
|
||||
public class LocalServices {
|
||||
|
||||
public static <T> T getService(Class<T> type) {
|
||||
throw new UnsupportedOperationException("STUB");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
package com.android.server;
|
||||
|
||||
public abstract class SystemService {
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package com.android.server;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class SystemServiceManager {
|
||||
private final ArrayList<SystemService> mServices = new ArrayList<>();
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package com.android.server.am;
|
||||
|
||||
import com.android.server.SystemService;
|
||||
|
||||
public class ActivityManagerService {
|
||||
public static final class Lifecycle extends SystemService {
|
||||
public ActivityManagerService getService() {
|
||||
throw new UnsupportedOperationException("STUB");
|
||||
}
|
||||
private ProcessRecord findProcessLocked(String process, int userId, String callName) {
|
||||
throw new UnsupportedOperationException("STUB");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package com.android.server.am;
|
||||
|
||||
public class ProcessRecord {
|
||||
String processName = null;
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
package io.github.lsposed.lspd;
|
||||
|
||||
parcelable Application {
|
||||
String packageName;
|
||||
int userId;
|
||||
}
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
package io.github.lsposed.lspd;
|
||||
|
||||
import io.github.lsposed.lspd.utils.ParceledListSlice;
|
||||
import io.github.lsposed.lspd.Application;
|
||||
|
||||
|
||||
interface ILSPManagerService {
|
||||
ParceledListSlice<PackageInfo> getInstalledPackagesFromAllUsers(int flags) = 2;
|
||||
|
|
@ -11,9 +13,9 @@ interface ILSPManagerService {
|
|||
|
||||
boolean disableModule(String packageName) = 5;
|
||||
|
||||
boolean setModuleScope(String packageName, in int[] uid) = 6;
|
||||
boolean setModuleScope(String packageName, in ParceledListSlice<Application> scope) = 6;
|
||||
|
||||
int[] getModuleScope(String packageName) = 7;
|
||||
ParceledListSlice<Application> getModuleScope(String packageName) = 7;
|
||||
|
||||
boolean isResourceHook() = 9;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue