[core] Store pkg name & user id, judge process name & uid

This commit is contained in:
LoveSy 2021-02-19 22:29:45 +08:00 committed by tehcneko
parent aa50e65cf1
commit 6e115426e4
22 changed files with 362 additions and 100 deletions

View File

@ -11,7 +11,7 @@ interface ILSPApplicationService {
boolean isResourcesHookEnabled() = 5; boolean isResourcesHookEnabled() = 5;
String[] getModulesList() = 6; String[] getModulesList(String processName) = 6;
String getPrefsPath(String packageName) = 7; String getPrefsPath(String packageName) = 7;

View File

@ -3,7 +3,7 @@ package io.github.lsposed.lspd.service;
import io.github.lsposed.lspd.service.ILSPApplicationService; import io.github.lsposed.lspd.service.ILSPApplicationService;
interface ILSPosedService { 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; oneway void dispatchPackageChanged(in Intent intent) = 2;
} }

View File

@ -234,7 +234,7 @@ namespace lspd {
void void
Context::OnNativeForkAndSpecializePost(JNIEnv *env) { Context::OnNativeForkAndSpecializePost(JNIEnv *env) {
const JUTFString process_name(env, nice_name_); 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) { if (binder) {
LoadDex(env); LoadDex(env);
InstallInlineHooks(); InstallInlineHooks();

View File

@ -73,8 +73,8 @@ namespace lspd {
write_interface_token_method_ = env->GetMethodID(parcel_class_, "writeInterfaceToken", write_interface_token_method_ = env->GetMethodID(parcel_class_, "writeInterfaceToken",
"(Ljava/lang/String;)V"); "(Ljava/lang/String;)V");
write_int_method_ = env->GetMethodID(parcel_class_, "writeInt", "(I)V"); write_int_method_ = env->GetMethodID(parcel_class_, "writeInt", "(I)V");
// writeStringMethod_ = env->GetMethodID(parcel_class_, "writeString", write_string_method_ = env->GetMethodID(parcel_class_, "writeString",
// "(Ljava/lang/String;)V"); "(Ljava/lang/String;)V");
read_exception_method_ = env->GetMethodID(parcel_class_, "readException", "()V"); read_exception_method_ = env->GetMethodID(parcel_class_, "readException", "()V");
read_strong_binder_method_ = env->GetMethodID(parcel_class_, "readStrongBinder", read_strong_binder_method_ = env->GetMethodID(parcel_class_, "readStrongBinder",
"()Landroid/os/IBinder;"); "()Landroid/os/IBinder;");
@ -126,7 +126,7 @@ namespace lspd {
LOGD("Done InitService"); LOGD("Done InitService");
} }
jobject Service::RequestBinder(JNIEnv *env) { jobject Service::RequestBinder(JNIEnv *env, jstring nice_name) {
if (UNLIKELY(!initialized_)) { if (UNLIKELY(!initialized_)) {
LOGE("Service not initialized"); LOGE("Service not initialized");
return nullptr; return nullptr;
@ -146,6 +146,7 @@ namespace lspd {
auto descriptor = env->NewStringUTF(BRIDGE_SERVICE_DESCRIPTOR.data()); auto descriptor = env->NewStringUTF(BRIDGE_SERVICE_DESCRIPTOR.data());
JNI_CallVoidMethod(env, data, write_interface_token_method_, descriptor); 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_int_method_, BRIDGE_ACTION_GET_BINDER);
JNI_CallVoidMethod(env, data, write_string_method_, nice_name);
auto res = JNI_CallBooleanMethod(env, bridgeService, transact_method_, auto res = JNI_CallBooleanMethod(env, bridgeService, transact_method_,
BRIDGE_TRANSACTION_CODE, BRIDGE_TRANSACTION_CODE,

View File

@ -31,7 +31,7 @@ namespace lspd {
void HookBridge(const Context& context, JNIEnv *env); void HookBridge(const Context& context, JNIEnv *env);
jobject RequestBinder(JNIEnv *env); jobject RequestBinder(JNIEnv *env, jstring nice_name);
jobject RequestBinderForSystemServer(JNIEnv *env); jobject RequestBinderForSystemServer(JNIEnv *env);
@ -65,6 +65,7 @@ namespace lspd {
jmethodID recycleMethod_ = nullptr; jmethodID recycleMethod_ = nullptr;
jmethodID write_interface_token_method_ = nullptr; jmethodID write_interface_token_method_ = nullptr;
jmethodID write_int_method_ = nullptr; jmethodID write_int_method_ = nullptr;
jmethodID write_string_method_ = nullptr;
jmethodID read_exception_method_ = nullptr; jmethodID read_exception_method_ = nullptr;
jmethodID read_strong_binder_method_ = nullptr; jmethodID read_strong_binder_method_ = nullptr;

View File

@ -353,6 +353,7 @@ public final class XposedInit {
topClassLoader = parent; topClassLoader = parent;
} }
// TODO: process name
String[] moduleList = serviceClient.getModulesList(); String[] moduleList = serviceClient.getModulesList();
ArraySet<String> newLoadedApk = new ArraySet<>(); ArraySet<String> newLoadedApk = new ArraySet<>();
for (String apk : moduleList) for (String apk : moduleList)

View File

@ -16,12 +16,14 @@ public class LSPApplicationServiceClient implements ILSPApplicationService {
static String baseCachePath = null; static String baseCachePath = null;
static String basePrefsPath = null; static String basePrefsPath = null;
static String processName = null;
public static LSPApplicationServiceClient serviceClient = 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) { if (serviceClient == null && binder != null && serviceBinder == null && service == null) {
serviceBinder = binder; serviceBinder = binder;
processName = niceName;
try { try {
serviceBinder.linkToDeath( serviceBinder.linkToDeath(
new IBinder.DeathRecipient() { new IBinder.DeathRecipient() {
@ -89,14 +91,18 @@ public class LSPApplicationServiceClient implements ILSPApplicationService {
} }
@Override @Override
public String[] getModulesList() { public String[] getModulesList(String processName) {
try { try {
return service.getModulesList(); return service.getModulesList(processName);
} catch (RemoteException | NullPointerException ignored) { } catch (RemoteException | NullPointerException ignored) {
} }
return new String[0]; return new String[0];
} }
public String[] getModulesList(){
return getModulesList(processName);
}
@Override @Override
public String getPrefsPath(String packageName) { public String getPrefsPath(String packageName) {
try { try {

View File

@ -40,7 +40,7 @@ public class Main implements KeepAll {
private static final Binder heartBeatBinder = new Binder(); private static final Binder heartBeatBinder = new Binder();
public static void forkAndSpecializePost(String appDataDir, String niceName, IBinder binder) { public static void forkAndSpecializePost(String appDataDir, String niceName, IBinder binder) {
LSPApplicationServiceClient.Init(binder); LSPApplicationServiceClient.Init(binder, niceName);
serviceClient.registerHeartBeat(heartBeatBinder); serviceClient.registerHeartBeat(heartBeatBinder);
final int variant = serviceClient.getVariant(); final int variant = serviceClient.getVariant();
Impl lspd = getImpl(variant); Impl lspd = getImpl(variant);
@ -52,7 +52,7 @@ public class Main implements KeepAll {
} }
public static void forkSystemServerPost(IBinder binder) { public static void forkSystemServerPost(IBinder binder) {
LSPApplicationServiceClient.Init(binder); LSPApplicationServiceClient.Init(binder, "android");
serviceClient.registerHeartBeat(heartBeatBinder); serviceClient.registerHeartBeat(heartBeatBinder);
final int variant = serviceClient.getVariant(); final int variant = serviceClient.getVariant();
Impl lspd = getImpl(variant); Impl lspd = getImpl(variant);

View File

@ -8,6 +8,7 @@ import android.os.IBinder;
import android.os.Parcel; import android.os.Parcel;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.ServiceManager; import android.os.ServiceManager;
import android.util.ArrayMap;
import android.util.Log; import android.util.Log;
import android.os.Process; import android.os.Process;
@ -15,7 +16,15 @@ import androidx.annotation.Keep;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; 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.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Map; import java.util.Map;
import static hidden.HiddenApiBridge.Binder_allowBlocking; import static hidden.HiddenApiBridge.Binder_allowBlocking;
@ -44,6 +53,7 @@ public class BridgeService {
this.bridgeService = bridgeService; this.bridgeService = bridgeService;
bridgeService.linkToDeath(this, 0); bridgeService.linkToDeath(this, 0);
} }
@Override @Override
public void binderDied() { public void binderDied() {
Log.i(TAG, "service " + SERVICE_NAME + " is dead. "); Log.i(TAG, "service " + SERVICE_NAME + " is dead. ");
@ -222,7 +232,8 @@ public class BridgeService {
case ACTION_GET_BINDER: { case ACTION_GET_BINDER: {
IBinder binder = null; IBinder binder = null;
try { 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) { } catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(e)); Log.e(TAG, Log.getStackTraceString(e));
} }
@ -282,11 +293,67 @@ public class BridgeService {
if (binder == null) return null; if (binder == null) return null;
try { try {
ILSPosedService service = ILSPosedService.Stub.asInterface(binder); 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(); if (applicationService != null) return applicationService.asBinder();
} catch (Throwable e) { } catch (Throwable e) {
Log.e(TAG, Log.getStackTraceString(e)); Log.e(TAG, Log.getStackTraceString(e));
} }
return null; 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));
}
}
} }

View File

@ -21,9 +21,14 @@ import java.io.OutputStream;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.StandardOpenOption; import java.nio.file.StandardOpenOption;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import io.github.lsposed.lspd.Application;
import static io.github.lsposed.lspd.service.ServiceManager.TAG; import static io.github.lsposed.lspd.service.ServiceManager.TAG;
// This config manager assume uid won't change when our service is off. // 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 { public class ConfigManager {
static ConfigManager instance = null; static ConfigManager instance = null;
final private File basePath = new File("/data/adb/lspd"); static final private File basePath = new File("/data/adb/lspd");
final private File configPath = new File(basePath, "config"); static final private File configPath = new File(basePath, "config");
final private SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(new File(configPath, "modules_config.db"), null); 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; 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; 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; 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 String manager = null;
private int managerUid = -1; 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; 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 // only check on boot
final private boolean isPermissive; final private boolean isPermissive;
final private File logPath = new File(basePath, "log"); static final private File logPath = new File(basePath, "log");
final private File modulesLogPath = new File(logPath, "modules.log"); static final private File modulesLogPath = new File(logPath, "modules.log");
final private File verboseLogPath = new File(logPath, "all.log"); static final private File verboseLogPath = new File(logPath, "all.log");
final FileObserver configObserver = new FileObserver(configPath) { final FileObserver configObserver = new FileObserver(configPath) {
@Override @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(); 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 { try {
if (!file.exists()) return defaultValue; if (!file.exists()) return defaultValue;
return readText(file); return readText(file);
@ -83,7 +140,7 @@ public class ConfigManager {
return defaultValue; return defaultValue;
} }
private void writeText(@NonNull File file, String value) { static private void writeText(@NonNull File file, String value) {
try { try {
Files.write(file.toPath(), value.getBytes(), StandardOpenOption.CREATE); Files.write(file.toPath(), value.getBytes(), StandardOpenOption.CREATE);
} catch (IOException e) { } 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 { try {
if (!file.exists()) return defaultValue; if (!file.exists()) return defaultValue;
return Integer.parseInt(readText(file)); return Integer.parseInt(readText(file));
@ -101,7 +158,7 @@ public class ConfigManager {
return defaultValue; return defaultValue;
} }
private void writeInt(@NonNull File file, int value) { static private void writeInt(@NonNull File file, int value) {
writeText(file, String.valueOf(value)); writeText(file, String.valueOf(value));
} }
@ -130,21 +187,6 @@ public class ConfigManager {
updateManager(); 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() { static ConfigManager getInstance() {
if (instance == null) if (instance == null)
instance = new ConfigManager(); instance = new ConfigManager();
@ -164,51 +206,81 @@ public class ConfigManager {
createScopeTable.execute(); 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() { private synchronized void cacheScopes() {
modulesForUid.clear(); cachedScope.clear();
try (Cursor cursor = db.query("scope INNER JOIN modules ON scope.mid = modules.mid", new String[]{"uid", "apk_path"}, 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)) { "enabled = ?", new String[]{"1"}, null, null, null)) {
if (cursor == null) { if (cursor == null) {
Log.e(TAG, "db cache failed"); Log.e(TAG, "db cache failed");
return; return;
} }
int uid_idx = cursor.getColumnIndex("uid"); int appPkgNameIdx = cursor.getColumnIndex("app_pkg_name");
int apk_path_idx = cursor.getColumnIndex("apk_path"); int userIdIdx = cursor.getColumnIndex("user_id");
int apkPathIdx = cursor.getColumnIndex("apk_path");
HashSet<Application> obsoletePackages = new HashSet<>();
while (cursor.moveToNext()) { while (cursor.moveToNext()) {
int uid = cursor.getInt(uid_idx); Application app = new Application();
String apk_path = cursor.getString(apk_path_idx); app.packageName = cursor.getString(appPkgNameIdx);
modulesForUid.computeIfAbsent(uid, ignored -> new ArrayList<>()).add(apk_path); 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 // This is called when a new process created, use the cached result
public String[] getModulesPathForUid(int uid) { public String[] getModulesPathForProcess(String processName, int uid) {
return isManager(uid) ? new String[0] : modulesForUid.getOrDefault(uid, new ArrayList<>()).toArray(new String[0]); 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 // This is called when a new process created, use the cached result
// The signature matches Riru's public boolean shouldSkipProcess(ProcessScope scope) {
public boolean shouldSkipUid(int uid) { return !cachedScope.containsKey(scope) && !isManager(scope.uid);
return !modulesForUid.containsKey(uid) && !isManager(uid);
} }
// This should only be called by manager, so we don't need to cache it // 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); int mid = getModuleId(packageName);
if (mid == -1) return null; 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)) { "scope.mid = ?", new String[]{String.valueOf(mid)}, null, null, null)) {
if (cursor == null) { if (cursor == null) {
return null; return null;
} }
int uid_idx = cursor.getColumnIndex("uid"); int userIdIdx = cursor.getColumnIndex("user_id");
HashSet<Integer> result = new HashSet<>(); int appPkgNameIdx = cursor.getColumnIndex("app_pkg_name");
ArrayList<Application> result = new ArrayList<>();
while (cursor.moveToNext()) { while (cursor.moveToNext()) {
int uid = cursor.getInt(uid_idx); Application scope = new Application();
result.add(uid); 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; return false;
} }
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put("package_name", packageName); values.put("module_pkg_name", packageName);
values.put("apk_path", apkPath); values.put("apk_path", apkPath);
int count = (int) db.insertWithOnConflict("modules", null, values, SQLiteDatabase.CONFLICT_IGNORE); int count = (int) db.insertWithOnConflict("modules", null, values, SQLiteDatabase.CONFLICT_IGNORE);
if (count < 0) { 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) { if (count >= 1) {
cacheScopes(); cacheScopes();
@ -236,7 +308,7 @@ public class ConfigManager {
Log.w(TAG, "get module id should not be called inside transaction"); Log.w(TAG, "get module id should not be called inside transaction");
return -1; 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 == null) return -1;
if (cursor.getCount() != 1) return -1; if (cursor.getCount() != 1) return -1;
cursor.moveToFirst(); cursor.moveToFirst();
@ -244,17 +316,18 @@ public class ConfigManager {
} }
} }
public boolean setModuleScope(String packageName, int[] uid) { public boolean setModuleScope(String packageName, List<Application> scopes) {
if (uid == null || uid.length == 0) return false; if (scopes.isEmpty()) return false;
int mid = getModuleId(packageName); int mid = getModuleId(packageName);
if (mid == -1) return false; if (mid == -1) return false;
try { try {
db.beginTransaction(); db.beginTransaction();
db.delete("scope", "mid = ?", new String[]{String.valueOf(mid)}); db.delete("scope", "mid = ?", new String[]{String.valueOf(mid)});
for (int id : uid) { for (Application app : scopes) {
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
values.put("mid", mid); 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); db.insertWithOnConflict("scope", null, values, SQLiteDatabase.CONFLICT_IGNORE);
} }
} finally { } finally {
@ -266,15 +339,15 @@ public class ConfigManager {
} }
public String[] enabledModules() { 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) { if (cursor == null) {
Log.e(TAG, "query enabled modules failed"); Log.e(TAG, "query enabled modules failed");
return null; return null;
} }
int pkg_idx = cursor.getColumnIndex("package_name"); int modulePkgNameIdx = cursor.getColumnIndex("module_pkg_name");
HashSet<String> result = new HashSet<>(); HashSet<String> result = new HashSet<>();
while (cursor.moveToNext()) { while (cursor.moveToNext()) {
result.add(cursor.getString(pkg_idx)); result.add(cursor.getString(modulePkgNameIdx));
} }
return result.toArray(new String[0]); return result.toArray(new String[0]);
} }
@ -328,9 +401,13 @@ public class ConfigManager {
return true; return true;
} }
public boolean removeApp(int uid) { public boolean removeAppWithoutCache(Application app) {
int count = db.delete("scope", "uid = ?", new String[]{String.valueOf(uid)}); int count = db.delete("scope", "app_pkg_name = ? AND user_id=?", new String[]{app.packageName, String.valueOf(app.userId)});
if (count >= 1) { return count >= 1;
}
public boolean removeApp(Application scope) {
if (removeAppWithoutCache(scope)) {
cacheScopes(); cacheScopes();
return true; return true;
} }
@ -397,8 +474,8 @@ public class ConfigManager {
} }
} }
public boolean isManager(String package_name) { public boolean isManager(String module_pkg_name) {
return package_name.equals(manager); return module_pkg_name.equals(manager);
} }
public boolean isManager(int uid) { public boolean isManager(int uid) {

View File

@ -48,9 +48,13 @@ public class LSPApplicationService extends ILSPApplicationService.Stub {
} }
@Override @Override
public String[] getModulesList() throws RemoteException { public String[] getModulesList(String processName) throws RemoteException {
ensureRegistered(); 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 @Override

View File

@ -5,11 +5,10 @@ import android.os.IBinder;
import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor;
import android.os.RemoteException; import android.os.RemoteException;
import java.util.List;
import de.robv.android.xposed.XposedBridge; import de.robv.android.xposed.XposedBridge;
import io.github.lsposed.lspd.BuildConfig; import io.github.lsposed.lspd.BuildConfig;
import io.github.lsposed.lspd.ILSPManagerService; import io.github.lsposed.lspd.ILSPManagerService;
import io.github.lsposed.lspd.Application;
import io.github.lsposed.lspd.utils.ParceledListSlice; import io.github.lsposed.lspd.utils.ParceledListSlice;
public class LSPManagerService extends ILSPManagerService.Stub { public class LSPManagerService extends ILSPManagerService.Stub {
@ -55,13 +54,13 @@ public class LSPManagerService extends ILSPManagerService.Stub {
} }
@Override @Override
public boolean setModuleScope(String packageName, int[] uid) { public boolean setModuleScope(String packageName, ParceledListSlice<Application> scope) {
return ConfigManager.getInstance().setModuleScope(packageName, uid); return ConfigManager.getInstance().setModuleScope(packageName, scope.getList());
} }
@Override @Override
public int[] getModuleScope(String packageName) { public ParceledListSlice<Application> getModuleScope(String packageName) {
return ConfigManager.getInstance().getModuleScope(packageName); return new ParceledListSlice<Application>(ConfigManager.getInstance().getModuleScope(packageName));
} }
@Override @Override

View File

@ -8,17 +8,25 @@ import android.os.Binder;
import android.os.RemoteException; import android.os.RemoteException;
import android.util.Log; import android.util.Log;
import io.github.lsposed.lspd.Application;
import static io.github.lsposed.lspd.service.ServiceManager.TAG; import static io.github.lsposed.lspd.service.ServiceManager.TAG;
public class LSPosedService extends ILSPosedService.Stub { public class LSPosedService extends ILSPosedService.Stub {
@Override @Override
public ILSPApplicationService requestApplicationService(int uid, int pid) { public ILSPApplicationService requestApplicationService(int uid, int pid, String processName) {
if (Binder.getCallingUid() != 1000) { if (Binder.getCallingUid() != 1000) {
Log.w(TAG, "Someone else got my binder!?"); Log.w(TAG, "Someone else got my binder!?");
return null; return null;
} }
if (ConfigManager.getInstance().shouldSkipUid(uid)) { if (uid == 1000 && processName.equals("android")) {
Log.d(TAG, "Skipped uid " + uid); 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; return null;
} }
if (ServiceManager.getApplicationService().hasRegister(uid, pid)) { if (ServiceManager.getApplicationService().hasRegister(uid, pid)) {
@ -40,10 +48,14 @@ public class LSPosedService extends ILSPosedService.Stub {
return; return;
} }
int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
int userId = intent.getIntExtra(Intent.EXTRA_USER, -1);
boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
if (intent.getAction().equals(Intent.ACTION_PACKAGE_REMOVED) && uid > 0 && !replacing) { if (intent.getAction().equals(Intent.ACTION_PACKAGE_REMOVED) && uid > 0 && !replacing) {
ConfigManager.getInstance().removeModule(packageName); 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); PackageInfo pkgInfo = PackageService.getPackageInfo(packageName, PackageManager.GET_META_DATA, 0);
boolean isXposedModule = pkgInfo != null && pkgInfo.applicationInfo != null && boolean isXposedModule = pkgInfo != null && pkgInfo.applicationInfo != null &&

View File

@ -1,16 +1,25 @@
package io.github.lsposed.lspd.service; package io.github.lsposed.lspd.service;
import android.content.pm.ComponentInfo;
import android.content.pm.IPackageManager; import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo; import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
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 java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import io.github.lsposed.lspd.utils.ParceledListSlice; import io.github.lsposed.lspd.utils.ParceledListSlice;
import static android.content.pm.ServiceInfo.FLAG_ISOLATED_PROCESS;
public class PackageService { public class PackageService {
public static final int PER_USER_RANGE = 100000;
private static IPackageManager pm = null; private static IPackageManager pm = null;
private static IBinder binder = null; private static IBinder binder = null;
@ -34,6 +43,14 @@ public class PackageService {
return pm.getPackagesForUid(uid); 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 { public static ParceledListSlice<PackageInfo> getInstalledPackagesFromAllUsers(int flags) throws RemoteException {
ArrayList<PackageInfo> res = new ArrayList<>(); ArrayList<PackageInfo> res = new ArrayList<>();
IPackageManager pm = getPackageManager(); IPackageManager pm = getPackageManager();
@ -46,6 +63,31 @@ public class PackageService {
public static void grantRuntimePermission(String packageName, String permissionName, int userId) throws RemoteException { public static void grantRuntimePermission(String packageName, String permissionName, int userId) throws RemoteException {
IPackageManager pm = getPackageManager(); IPackageManager pm = getPackageManager();
if (pm == null) return;
pm.grantRuntimePermission(packageName, permissionName, userId); 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);
}
} }

View File

@ -1,6 +1,7 @@
package io.github.lsposed.lspd.service; package io.github.lsposed.lspd.service;
import android.content.Context; import android.content.Context;
import android.os.IBinder;
import android.os.Looper; import android.os.Looper;
import android.util.Log; import android.util.Log;
@ -32,7 +33,7 @@ public class ServiceManager {
applicationService = new LSPApplicationService(); applicationService = new LSPApplicationService();
managerService = new LSPManagerService(); managerService = new LSPManagerService();
android.os.ServiceManager.addService("serial", mainService); android.os.ServiceManager.addService("serial", (IBinder) mainService);
waitSystemService("package"); waitSystemService("package");
waitSystemService("activity"); waitSystemService("activity");
@ -61,10 +62,15 @@ public class ServiceManager {
Log.e(TAG, Log.getStackTraceString(e)); Log.e(TAG, Log.getStackTraceString(e));
} }
//noinspection InfiniteLoopStatement
while (true) {
try {
Looper.loop(); Looper.loop();
} catch (Throwable e) {
Log.i(TAG, "server exited"); Log.i(TAG, "server exited with " + Log.getStackTraceString(e));
System.exit(0); Log.i(TAG, "restarting");
}
}
} }
public static LSPModuleService getModuleService() { public static LSPModuleService getModuleService() {

View File

@ -0,0 +1,8 @@
package com.android.server;
public class LocalServices {
public static <T> T getService(Class<T> type) {
throw new UnsupportedOperationException("STUB");
}
}

View File

@ -0,0 +1,4 @@
package com.android.server;
public abstract class SystemService {
}

View File

@ -0,0 +1,7 @@
package com.android.server;
import java.util.ArrayList;
public class SystemServiceManager {
private final ArrayList<SystemService> mServices = new ArrayList<>();
}

View File

@ -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");
}
}
}

View File

@ -0,0 +1,5 @@
package com.android.server.am;
public class ProcessRecord {
String processName = null;
}

View File

@ -0,0 +1,6 @@
package io.github.lsposed.lspd;
parcelable Application {
String packageName;
int userId;
}

View File

@ -1,6 +1,8 @@
package io.github.lsposed.lspd; package io.github.lsposed.lspd;
import io.github.lsposed.lspd.utils.ParceledListSlice; import io.github.lsposed.lspd.utils.ParceledListSlice;
import io.github.lsposed.lspd.Application;
interface ILSPManagerService { interface ILSPManagerService {
ParceledListSlice<PackageInfo> getInstalledPackagesFromAllUsers(int flags) = 2; ParceledListSlice<PackageInfo> getInstalledPackagesFromAllUsers(int flags) = 2;
@ -11,9 +13,9 @@ interface ILSPManagerService {
boolean disableModule(String packageName) = 5; 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; boolean isResourceHook() = 9;