diff --git a/manager/app/build.gradle.kts b/manager/app/build.gradle.kts index 1085c2e9..fc19fd2d 100644 --- a/manager/app/build.gradle.kts +++ b/manager/app/build.gradle.kts @@ -81,7 +81,9 @@ dependencies { implementation("io.coil-kt:coil-compose:2.2.2") implementation("me.zhanghai.android.appiconloader:appiconloader-coil:1.5.0") - implementation("com.github.topjohnwu.libsu:core:5.0.3") + val libsuVersion = "5.0.4" + implementation("com.github.topjohnwu.libsu:core:$libsuVersion") + implementation("com.github.topjohnwu.libsu:service:$libsuVersion") implementation("com.github.alorma:compose-settings-ui-m3:0.22.0") ksp("io.github.raamcosta.compose-destinations:ksp:$composeDestinationsVersion") diff --git a/manager/app/src/main/aidl/me/weishu/kernelsu/IKsuInterface.aidl b/manager/app/src/main/aidl/me/weishu/kernelsu/IKsuInterface.aidl new file mode 100644 index 00000000..1e78fc76 --- /dev/null +++ b/manager/app/src/main/aidl/me/weishu/kernelsu/IKsuInterface.aidl @@ -0,0 +1,9 @@ +// IKsuInterface.aidl +package me.weishu.kernelsu; + +import android.content.pm.PackageInfo; +import java.util.List; + +interface IKsuInterface { + List getPackages(); +} \ No newline at end of file diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/KsuService.java b/manager/app/src/main/java/me/weishu/kernelsu/ui/KsuService.java new file mode 100644 index 00000000..5159622b --- /dev/null +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/KsuService.java @@ -0,0 +1,79 @@ +package me.weishu.kernelsu.ui; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.os.IBinder; +import android.os.UserHandle; +import android.os.UserManager; +import android.util.Log; + +import androidx.annotation.NonNull; + +import com.topjohnwu.superuser.ipc.RootService; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +import me.weishu.kernelsu.IKsuInterface; + +/** + * @author weishu + * @date 2023/4/18. + */ + +public class KsuService extends RootService { + + private static final String TAG = "KsuService"; + + class Stub extends IKsuInterface.Stub { + @Override + public List getPackages() { + List list = getInstalledPackagesAll(); + Log.i(TAG, "getPackages: " + list.size()); + return list; + } + } + + @Override + public IBinder onBind(@NonNull Intent intent) { + return new Stub(); + } + + List getUserIds() { + List result = new ArrayList<>(); + UserManager um = (UserManager) getSystemService(Context.USER_SERVICE); + List userProfiles = um.getUserProfiles(); + for (UserHandle userProfile : userProfiles) { + int userId = userProfile.hashCode(); + if (userId == 0) { + continue; + } + result.add(userProfile.hashCode()); + } + return result; + } + + ArrayList getInstalledPackagesAll() { + ArrayList packages = new ArrayList<>(); + for (Integer userId : getUserIds()) { + Log.i(TAG, "getInstalledPackagesAll: " + userId); + packages.addAll(getInstalledPackagesAsUser(userId)); + } + return packages; + } + + List getInstalledPackagesAsUser(int userId) { + try { + PackageManager pm = getPackageManager(); + Method getInstalledPackagesAsUser = pm.getClass().getDeclaredMethod("getInstalledPackagesAsUser", int.class, int.class); + return (List) getInstalledPackagesAsUser.invoke(pm, 0, userId); + } catch (Throwable e) { + Log.e(TAG, "err", e); + } + + return new ArrayList<>(); + } +} diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/SuperUser.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/SuperUser.kt index ebaa6427..e4f84b87 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/SuperUser.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/SuperUser.kt @@ -108,7 +108,7 @@ fun SuperUserScreen() { val failMessage = stringResource(R.string.superuser_failed_to_grant_root) LazyColumn(Modifier.fillMaxSize()) { - items(viewModel.appList, key = { it.packageName }) { app -> + items(viewModel.appList, key = { it.packageName + it.uid }) { app -> var isChecked by rememberSaveable(app) { mutableStateOf(app.onAllowList) } val dialogHost = LocalDialogHost.current val content = diff --git a/manager/app/src/main/java/me/weishu/kernelsu/ui/viewmodel/SuperUserViewModel.kt b/manager/app/src/main/java/me/weishu/kernelsu/ui/viewmodel/SuperUserViewModel.kt index 40e83535..e0300346 100644 --- a/manager/app/src/main/java/me/weishu/kernelsu/ui/viewmodel/SuperUserViewModel.kt +++ b/manager/app/src/main/java/me/weishu/kernelsu/ui/viewmodel/SuperUserViewModel.kt @@ -1,8 +1,11 @@ package me.weishu.kernelsu.ui.viewmodel +import android.content.ComponentName +import android.content.Intent +import android.content.ServiceConnection import android.content.pm.ApplicationInfo import android.content.pm.PackageInfo -import android.graphics.drawable.Drawable +import android.os.IBinder import android.os.SystemClock import android.util.Log import androidx.compose.runtime.derivedStateOf @@ -10,13 +13,19 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel +import com.topjohnwu.superuser.Shell import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext +import me.weishu.kernelsu.IKsuInterface import me.weishu.kernelsu.Natives import me.weishu.kernelsu.ksuApp +import me.weishu.kernelsu.ui.KsuService import me.weishu.kernelsu.ui.util.HanziToPinyin +import me.weishu.kernelsu.ui.util.KsuCli import java.text.Collator import java.util.* +import kotlin.coroutines.resume +import kotlin.coroutines.suspendCoroutine class SuperUserViewModel : ViewModel() { @@ -62,7 +71,44 @@ class SuperUserViewModel : ViewModel() { } } + private suspend inline fun connectKsuService( + crossinline onDisconnect: () -> Unit = {} + ): Pair = suspendCoroutine { + val connection = object : ServiceConnection { + override fun onServiceDisconnected(name: ComponentName?) { + onDisconnect() + } + + override fun onServiceConnected(name: ComponentName?, binder: IBinder?) { + it.resume(binder as IBinder to this) + } + } + + val intent = Intent(ksuApp, KsuService::class.java); + + val task = KsuService.bindOrTask( + intent, + Shell.EXECUTOR, + connection, + ) + val shell = KsuCli.SHELL + task?.let { it1 -> shell.execTask(it1) } + } + + private fun stopKsuService() { + val intent = Intent(ksuApp, KsuService::class.java); + KsuService.stop(intent) + } + suspend fun fetchAppList() { + + val result = connectKsuService { + Log.w(TAG, "KsuService disconnected") + } + val binder = result.first + val extraPackages = IKsuInterface.Stub.asInterface(binder).packages + stopKsuService() + withContext(Dispatchers.IO) { isRefreshing = true val pm = ksuApp.packageManager @@ -71,7 +117,11 @@ class SuperUserViewModel : ViewModel() { Log.i(TAG, "allowList: $allowList") Log.i(TAG, "denyList: $denyList") val start = SystemClock.elapsedRealtime() - apps = pm.getInstalledPackages(0).map { + + val packages = pm.getInstalledPackages(0) + packages.addAll(extraPackages) + + apps = packages.map { val appInfo = it.applicationInfo val uid = appInfo.uid AppInfo(