From 31003ae287ae98a9273c69e98f59ada7fde78b70 Mon Sep 17 00:00:00 2001 From: SanmerDev Date: Sun, 9 Jun 2024 21:47:55 +0800 Subject: [PATCH 1/7] Use `ServiceManagerCompat` --- .github/workflows/android.yml | 5 + .gitmodules | 3 + app/build.gradle.kts | 3 +- app/src/main/kotlin/com/sanmer/mrepo/App.kt | 2 - .../main/kotlin/com/sanmer/mrepo/Compat.kt | 19 +- .../sanmer/mrepo/model/local/LocalModule.kt | 2 +- .../com/sanmer/mrepo/model/local/State.kt | 2 +- .../mrepo/repository/ModulesRepository.kt | 6 +- .../mrepo/viewmodel/InstallViewModel.kt | 39 ++-- .../mrepo/viewmodel/ModulesViewModel.kt | 18 +- .../mrepo/viewmodel/SettingsViewModel.kt | 3 +- compat | 1 + compat/build.gradle.kts | 28 --- compat/src/main/AndroidManifest.xml | 12 - .../mrepo/compat/content/LocalModule.aidl | 3 - .../mrepo/compat/stub/IPowerManager.aidl | 5 - .../mrepo/compat/stub/IServiceManager.aidl | 17 -- .../dev/sanmer/mrepo/compat/BuildCompat.kt | 18 -- .../mrepo/compat/ServiceManagerCompat.kt | 208 ------------------ .../mrepo/compat/delegate/ContextDelegate.kt | 16 -- .../compat/delegate/PowerManagerDelegate.kt | 39 ---- .../compat/impl/APatchModuleManagerImpl.kt | 56 ----- .../compat/impl/KernelSUModuleManagerImpl.kt | 56 ----- .../compat/impl/MagiskModuleManagerImpl.kt | 59 ----- .../mrepo/compat/impl/PowerManagerImpl.kt | 11 - .../mrepo/compat/impl/ServiceManagerImpl.kt | 83 ------- core/build.gradle.kts | 19 ++ .../dev/sanmer/mrepo/content/LocalModule.aidl | 3 + .../dev/sanmer/mrepo}/stub/IFileManager.aidl | 2 +- .../sanmer/mrepo}/stub/IInstallCallback.aidl | 4 +- .../sanmer/mrepo/stub/IManagerService.aidl | 11 + .../sanmer/mrepo}/stub/IModuleManager.aidl | 9 +- .../mrepo}/stub/IModuleOpsCallback.aidl | 2 +- .../dev/sanmer/mrepo/stub/IPowerManager.aidl | 5 + .../kotlin/dev/sanmer/mrepo/ManagerService.kt | 58 +++++ .../main/kotlin/dev/sanmer/mrepo}/Platform.kt | 2 +- .../dev/sanmer/mrepo}/content/LocalModule.kt | 2 +- .../kotlin/dev/sanmer/mrepo}/content/State.kt | 2 +- .../mrepo/impl/APatchModuleManagerImpl.kt | 62 ++++++ .../mrepo}/impl/BaseModuleManagerImpl.kt | 65 +++--- .../dev/sanmer/mrepo}/impl/FileManagerImpl.kt | 4 +- .../mrepo/impl/KernelSUModuleManagerImpl.kt | 62 ++++++ .../mrepo/impl/MagiskModuleManagerImpl.kt | 92 ++++++++ .../dev/sanmer/mrepo/impl/PowerManagerImpl.kt | 16 ++ .../kotlin/dev/sanmer/mrepo/impl/Shell.kt | 72 ++++++ gradle/libs.versions.toml | 18 +- hidden-api/build.gradle.kts | 13 -- .../main/java/android/app/ActivityThread.java | 7 - .../main/java/android/os/IPowerManager.java | 12 - .../java/android/os/PowerManagerHidden.java | 13 -- .../src/main/java/android/os/SELinux.java | 6 - .../main/java/android/os/ServiceManager.java | 7 - settings.gradle.kts | 12 +- 53 files changed, 507 insertions(+), 787 deletions(-) create mode 100644 .gitmodules create mode 160000 compat delete mode 100644 compat/build.gradle.kts delete mode 100644 compat/src/main/AndroidManifest.xml delete mode 100644 compat/src/main/aidl/dev/sanmer/mrepo/compat/content/LocalModule.aidl delete mode 100644 compat/src/main/aidl/dev/sanmer/mrepo/compat/stub/IPowerManager.aidl delete mode 100644 compat/src/main/aidl/dev/sanmer/mrepo/compat/stub/IServiceManager.aidl delete mode 100644 compat/src/main/kotlin/dev/sanmer/mrepo/compat/BuildCompat.kt delete mode 100644 compat/src/main/kotlin/dev/sanmer/mrepo/compat/ServiceManagerCompat.kt delete mode 100644 compat/src/main/kotlin/dev/sanmer/mrepo/compat/delegate/ContextDelegate.kt delete mode 100644 compat/src/main/kotlin/dev/sanmer/mrepo/compat/delegate/PowerManagerDelegate.kt delete mode 100644 compat/src/main/kotlin/dev/sanmer/mrepo/compat/impl/APatchModuleManagerImpl.kt delete mode 100644 compat/src/main/kotlin/dev/sanmer/mrepo/compat/impl/KernelSUModuleManagerImpl.kt delete mode 100644 compat/src/main/kotlin/dev/sanmer/mrepo/compat/impl/MagiskModuleManagerImpl.kt delete mode 100644 compat/src/main/kotlin/dev/sanmer/mrepo/compat/impl/PowerManagerImpl.kt delete mode 100644 compat/src/main/kotlin/dev/sanmer/mrepo/compat/impl/ServiceManagerImpl.kt create mode 100644 core/build.gradle.kts create mode 100644 core/src/main/aidl/dev/sanmer/mrepo/content/LocalModule.aidl rename {compat/src/main/aidl/dev/sanmer/mrepo/compat => core/src/main/aidl/dev/sanmer/mrepo}/stub/IFileManager.aidl (63%) rename {compat/src/main/aidl/dev/sanmer/mrepo/compat => core/src/main/aidl/dev/sanmer/mrepo}/stub/IInstallCallback.aidl (63%) create mode 100644 core/src/main/aidl/dev/sanmer/mrepo/stub/IManagerService.aidl rename {compat/src/main/aidl/dev/sanmer/mrepo/compat => core/src/main/aidl/dev/sanmer/mrepo}/stub/IModuleManager.aidl (69%) rename {compat/src/main/aidl/dev/sanmer/mrepo/compat => core/src/main/aidl/dev/sanmer/mrepo}/stub/IModuleOpsCallback.aidl (73%) create mode 100644 core/src/main/aidl/dev/sanmer/mrepo/stub/IPowerManager.aidl create mode 100644 core/src/main/kotlin/dev/sanmer/mrepo/ManagerService.kt rename {compat/src/main/kotlin/dev/sanmer/mrepo/compat/impl => core/src/main/kotlin/dev/sanmer/mrepo}/Platform.kt (62%) rename {compat/src/main/kotlin/dev/sanmer/mrepo/compat => core/src/main/kotlin/dev/sanmer/mrepo}/content/LocalModule.kt (90%) rename {compat/src/main/kotlin/dev/sanmer/mrepo/compat => core/src/main/kotlin/dev/sanmer/mrepo}/content/State.kt (63%) create mode 100644 core/src/main/kotlin/dev/sanmer/mrepo/impl/APatchModuleManagerImpl.kt rename {compat/src/main/kotlin/dev/sanmer/mrepo/compat => core/src/main/kotlin/dev/sanmer/mrepo}/impl/BaseModuleManagerImpl.kt (72%) rename {compat/src/main/kotlin/dev/sanmer/mrepo/compat => core/src/main/kotlin/dev/sanmer/mrepo}/impl/FileManagerImpl.kt (78%) create mode 100644 core/src/main/kotlin/dev/sanmer/mrepo/impl/KernelSUModuleManagerImpl.kt create mode 100644 core/src/main/kotlin/dev/sanmer/mrepo/impl/MagiskModuleManagerImpl.kt create mode 100644 core/src/main/kotlin/dev/sanmer/mrepo/impl/PowerManagerImpl.kt create mode 100644 core/src/main/kotlin/dev/sanmer/mrepo/impl/Shell.kt delete mode 100644 hidden-api/build.gradle.kts delete mode 100644 hidden-api/src/main/java/android/app/ActivityThread.java delete mode 100644 hidden-api/src/main/java/android/os/IPowerManager.java delete mode 100644 hidden-api/src/main/java/android/os/PowerManagerHidden.java delete mode 100644 hidden-api/src/main/java/android/os/SELinux.java delete mode 100644 hidden-api/src/main/java/android/os/ServiceManager.java diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index 073b06ec..b6c06fd0 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -14,6 +14,7 @@ jobs: - name: Checkout uses: actions/checkout@v4 with: + submodules: 'recursive' fetch-depth: 0 - name: Set up signing key @@ -39,6 +40,10 @@ jobs: validate-wrappers: true gradle-home-cache-cleanup: true + - name: Build dependencies + working-directory: compat + run: ./gradlew publishToMavenLocal + - name: Build with Gradle run: ./gradlew assembleRelease diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..d49a8cee --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "compat"] + path = compat + url = git@github.com:SanmerApps/ServiceManagerCompat.git diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 9687bf2d..67d3c96f 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -113,8 +113,7 @@ protobuf { } dependencies { - compileOnly(projects.hiddenApi) - implementation(projects.compat) + implementation(projects.core) implementation(libs.androidx.activity.compose) implementation(libs.androidx.appcompat) diff --git a/app/src/main/kotlin/com/sanmer/mrepo/App.kt b/app/src/main/kotlin/com/sanmer/mrepo/App.kt index 51c4cd5b..40afcd19 100644 --- a/app/src/main/kotlin/com/sanmer/mrepo/App.kt +++ b/app/src/main/kotlin/com/sanmer/mrepo/App.kt @@ -6,7 +6,6 @@ import com.sanmer.mrepo.network.NetworkUtils import com.sanmer.mrepo.utils.timber.DebugTree import com.sanmer.mrepo.utils.timber.ReleaseTree import dagger.hilt.android.HiltAndroidApp -import dev.sanmer.mrepo.compat.ServiceManagerCompat import timber.log.Timber @HiltAndroidApp @@ -22,7 +21,6 @@ class App : Application() { override fun onCreate() { super.onCreate() - ServiceManagerCompat.setHiddenApiExemptions() NotificationUtils.init(this) NetworkUtils.setCacheDir(cacheDir) } diff --git a/app/src/main/kotlin/com/sanmer/mrepo/Compat.kt b/app/src/main/kotlin/com/sanmer/mrepo/Compat.kt index 1ccd2556..c47ab19a 100644 --- a/app/src/main/kotlin/com/sanmer/mrepo/Compat.kt +++ b/app/src/main/kotlin/com/sanmer/mrepo/Compat.kt @@ -4,11 +4,12 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import com.sanmer.mrepo.datastore.WorkingMode -import dev.sanmer.mrepo.compat.ServiceManagerCompat -import dev.sanmer.mrepo.compat.stub.IFileManager -import dev.sanmer.mrepo.compat.stub.IModuleManager -import dev.sanmer.mrepo.compat.stub.IPowerManager -import dev.sanmer.mrepo.compat.stub.IServiceManager +import dev.sanmer.mrepo.ManagerService.Companion.managerService +import dev.sanmer.mrepo.stub.IFileManager +import dev.sanmer.mrepo.stub.IModuleManager +import dev.sanmer.mrepo.stub.IPowerManager +import dev.sanmer.su.IServiceManager +import dev.sanmer.su.ServiceManagerCompat import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import timber.log.Timber @@ -25,10 +26,6 @@ object Compat { private val _isAliveFlow = MutableStateFlow(false) val isAliveFlow get() = _isAliveFlow.asStateFlow() - val moduleManager: IModuleManager get() = mService.moduleManager - val fileManager: IFileManager get() = mService.fileManager - val powerManager: IPowerManager get() = mService.powerManager - private fun state(): Boolean { isAlive = mServiceOrNull != null _isAliveFlow.value = isAlive @@ -60,4 +57,8 @@ object Compat { else -> fallback } } + + fun getModuleManager(): IModuleManager = mService.managerService.moduleManager + fun getFileManager(): IFileManager = mService.managerService.fileManager + fun getPowerManager(): IPowerManager = mService.managerService.powerManager } \ No newline at end of file diff --git a/app/src/main/kotlin/com/sanmer/mrepo/model/local/LocalModule.kt b/app/src/main/kotlin/com/sanmer/mrepo/model/local/LocalModule.kt index d9cf9924..6da5f74a 100644 --- a/app/src/main/kotlin/com/sanmer/mrepo/model/local/LocalModule.kt +++ b/app/src/main/kotlin/com/sanmer/mrepo/model/local/LocalModule.kt @@ -1,7 +1,7 @@ package com.sanmer.mrepo.model.local import com.sanmer.mrepo.utils.Utils -import dev.sanmer.mrepo.compat.content.LocalModule +import dev.sanmer.mrepo.content.LocalModule typealias LocalModule = LocalModule diff --git a/app/src/main/kotlin/com/sanmer/mrepo/model/local/State.kt b/app/src/main/kotlin/com/sanmer/mrepo/model/local/State.kt index ae0f7903..a828025e 100644 --- a/app/src/main/kotlin/com/sanmer/mrepo/model/local/State.kt +++ b/app/src/main/kotlin/com/sanmer/mrepo/model/local/State.kt @@ -1,5 +1,5 @@ package com.sanmer.mrepo.model.local -import dev.sanmer.mrepo.compat.content.State +import dev.sanmer.mrepo.content.State typealias State = State \ No newline at end of file diff --git a/app/src/main/kotlin/com/sanmer/mrepo/repository/ModulesRepository.kt b/app/src/main/kotlin/com/sanmer/mrepo/repository/ModulesRepository.kt index 4f2d9127..513b3e4d 100644 --- a/app/src/main/kotlin/com/sanmer/mrepo/repository/ModulesRepository.kt +++ b/app/src/main/kotlin/com/sanmer/mrepo/repository/ModulesRepository.kt @@ -14,8 +14,10 @@ import javax.inject.Singleton class ModulesRepository @Inject constructor( private val localRepository: LocalRepository, ) { + private val mm by lazy { Compat.getModuleManager() } + suspend fun getLocalAll() = withContext(Dispatchers.IO) { - with(Compat.moduleManager.modules) { + with(mm.modules) { localRepository.deleteLocalAll() localRepository.insertLocal(this) localRepository.clearUpdatableTag(map { it.id }) @@ -23,7 +25,7 @@ class ModulesRepository @Inject constructor( } suspend fun getLocal(id: String) = withContext(Dispatchers.IO) { - val module = Compat.moduleManager.getModuleById(id) + val module = mm.getModuleById(id) localRepository.insertLocal(module) } diff --git a/app/src/main/kotlin/com/sanmer/mrepo/viewmodel/InstallViewModel.kt b/app/src/main/kotlin/com/sanmer/mrepo/viewmodel/InstallViewModel.kt index 98739eb8..c36f3bcf 100644 --- a/app/src/main/kotlin/com/sanmer/mrepo/viewmodel/InstallViewModel.kt +++ b/app/src/main/kotlin/com/sanmer/mrepo/viewmodel/InstallViewModel.kt @@ -17,9 +17,8 @@ import com.sanmer.mrepo.repository.LocalRepository import com.sanmer.mrepo.repository.UserPreferencesRepository import com.sanmer.mrepo.utils.extensions.tmpDir import dagger.hilt.android.lifecycle.HiltViewModel -import dev.sanmer.mrepo.compat.content.State -import dev.sanmer.mrepo.compat.delegate.PowerManagerDelegate -import dev.sanmer.mrepo.compat.stub.IInstallCallback +import dev.sanmer.mrepo.content.State +import dev.sanmer.mrepo.stub.IInstallCallback import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch @@ -34,6 +33,8 @@ class InstallViewModel @Inject constructor( private val localRepository: LocalRepository, private val userPreferencesRepository: UserPreferencesRepository, ) : ViewModel() { + private val mm by lazy { Compat.getModuleManager() } + val logs = mutableListOf() val console = mutableStateListOf() var event by mutableStateOf(Event.LOADING) @@ -46,9 +47,8 @@ class InstallViewModel @Inject constructor( } fun reboot() { - PowerManagerDelegate(Compat.powerManager).apply { - reboot() - } + val powerManager = Compat.getPowerManager() + powerManager.reboot() } suspend fun writeLogsTo(context: Context, uri: Uri) = withContext(Dispatchers.IO) { @@ -74,13 +74,12 @@ class InstallViewModel @Inject constructor( val path = context.getPathForUri(uri) Timber.d("path = $path") - Compat.moduleManager - .getModuleInfo(path)?.let { - Timber.d("module = $it") - install(path) + mm.getModuleInfo(path)?.let { + Timber.d("module = $it") + install(path) - return@withContext - } + return@withContext + } console.add("- Copying zip to temp directory") val tmpFile = context.copyToDir(uri, context.tmpDir) @@ -91,13 +90,12 @@ class InstallViewModel @Inject constructor( } } - Compat.moduleManager - .getModuleInfo(tmpFile.path)?.let { - Timber.d("module = $it") - install(tmpFile.path) + mm.getModuleInfo(tmpFile.path)?.let { + Timber.d("module = $it") + install(tmpFile.path) - return@withContext - } + return@withContext + } event = Event.FAILED console.add("- Zip parsing failed") @@ -132,7 +130,7 @@ class InstallViewModel @Inject constructor( } console.add("- Installing ${zipFile.name}") - Compat.moduleManager.install(zipPath, callback) + mm.install(zipPath, callback) } private fun insertLocal(module: LocalModule) { @@ -145,7 +143,8 @@ class InstallViewModel @Inject constructor( private fun deleteBySu(zipPath: String) { runCatching { - Compat.fileManager.deleteOnExit(zipPath) + val fileManager = Compat.getFileManager() + fileManager.deleteOnExit(zipPath) }.onFailure { Timber.e(it) }.onSuccess { diff --git a/app/src/main/kotlin/com/sanmer/mrepo/viewmodel/ModulesViewModel.kt b/app/src/main/kotlin/com/sanmer/mrepo/viewmodel/ModulesViewModel.kt index f4d90a7f..0e71cc42 100644 --- a/app/src/main/kotlin/com/sanmer/mrepo/viewmodel/ModulesViewModel.kt +++ b/app/src/main/kotlin/com/sanmer/mrepo/viewmodel/ModulesViewModel.kt @@ -26,7 +26,7 @@ import com.sanmer.mrepo.repository.UserPreferencesRepository import com.sanmer.mrepo.service.DownloadService import com.sanmer.mrepo.utils.Utils import dagger.hilt.android.lifecycle.HiltViewModel -import dev.sanmer.mrepo.compat.stub.IModuleOpsCallback +import dev.sanmer.mrepo.stub.IModuleOpsCallback import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.combine @@ -45,6 +45,7 @@ class ModulesViewModel @Inject constructor( private val modulesRepository: ModulesRepository, private val userPreferencesRepository: UserPreferencesRepository, ) : ViewModel() { + private val mm by lazy { Compat.getModuleManager() } val isProviderAlive get() = Compat.isAlive private val modulesMenu get() = userPreferencesRepository.data @@ -182,13 +183,11 @@ class ModulesViewModel @Inject constructor( isOpsRunning = opsTasks.contains(module.id), toggle = { opsTasks.add(module.id) - Compat.moduleManager - .disable(module.id, opsCallback) + mm.disable(module.id, opsCallback) }, change = { opsTasks.add(module.id) - Compat.moduleManager - .remove(module.id, opsCallback) + mm.remove(module.id, opsCallback) } ) @@ -196,13 +195,11 @@ class ModulesViewModel @Inject constructor( isOpsRunning = opsTasks.contains(module.id), toggle = { opsTasks.add(module.id) - Compat.moduleManager - .enable(module.id, opsCallback) + mm.enable(module.id, opsCallback) }, change = { opsTasks.add(module.id) - Compat.moduleManager - .remove(module.id, opsCallback) + mm.remove(module.id, opsCallback) } ) @@ -211,8 +208,7 @@ class ModulesViewModel @Inject constructor( toggle = {}, change = { opsTasks.add(module.id) - Compat.moduleManager - .enable(module.id, opsCallback) + mm.enable(module.id, opsCallback) } ) diff --git a/app/src/main/kotlin/com/sanmer/mrepo/viewmodel/SettingsViewModel.kt b/app/src/main/kotlin/com/sanmer/mrepo/viewmodel/SettingsViewModel.kt index 0f72a179..3904c7b9 100644 --- a/app/src/main/kotlin/com/sanmer/mrepo/viewmodel/SettingsViewModel.kt +++ b/app/src/main/kotlin/com/sanmer/mrepo/viewmodel/SettingsViewModel.kt @@ -16,10 +16,11 @@ import javax.inject.Inject class SettingsViewModel @Inject constructor( private val userPreferencesRepository: UserPreferencesRepository ) : ViewModel() { + private val mm by lazy { Compat.getModuleManager() } val isProviderAlive get() = Compat.isAlive val version get() = Compat.get("") { - with(moduleManager) { "$version (${versionCode})" } + with(mm) { "$version (${versionCode})" } } init { diff --git a/compat b/compat new file mode 160000 index 00000000..aaa6e65b --- /dev/null +++ b/compat @@ -0,0 +1 @@ +Subproject commit aaa6e65b1ec47ed8f3bb0a97eb4373a8a109c1e0 diff --git a/compat/build.gradle.kts b/compat/build.gradle.kts deleted file mode 100644 index f4e16784..00000000 --- a/compat/build.gradle.kts +++ /dev/null @@ -1,28 +0,0 @@ -plugins { - alias(libs.plugins.self.library) - alias(libs.plugins.kotlin.parcelize) - alias(libs.plugins.rikka.refine) -} - -android { - namespace = "dev.sanmer.mrepo.compat" - - buildFeatures { - aidl = true - } -} - -dependencies { - compileOnly(projects.hiddenApi) - implementation(libs.hiddenApiBypass) - implementation(libs.rikka.refine.runtime) - - implementation(libs.libsu.core) - implementation(libs.libsu.service) - - implementation(libs.rikka.shizuku.api) - implementation(libs.rikka.shizuku.provider) - - implementation(libs.androidx.annotation) - implementation(libs.kotlinx.coroutines.android) -} \ No newline at end of file diff --git a/compat/src/main/AndroidManifest.xml b/compat/src/main/AndroidManifest.xml deleted file mode 100644 index 1ac33631..00000000 --- a/compat/src/main/AndroidManifest.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/compat/src/main/aidl/dev/sanmer/mrepo/compat/content/LocalModule.aidl b/compat/src/main/aidl/dev/sanmer/mrepo/compat/content/LocalModule.aidl deleted file mode 100644 index f66d8567..00000000 --- a/compat/src/main/aidl/dev/sanmer/mrepo/compat/content/LocalModule.aidl +++ /dev/null @@ -1,3 +0,0 @@ -package dev.sanmer.mrepo.compat.content; - -parcelable LocalModule; \ No newline at end of file diff --git a/compat/src/main/aidl/dev/sanmer/mrepo/compat/stub/IPowerManager.aidl b/compat/src/main/aidl/dev/sanmer/mrepo/compat/stub/IPowerManager.aidl deleted file mode 100644 index c602c848..00000000 --- a/compat/src/main/aidl/dev/sanmer/mrepo/compat/stub/IPowerManager.aidl +++ /dev/null @@ -1,5 +0,0 @@ -package dev.sanmer.mrepo.compat.stub; - -interface IPowerManager { - void reboot(boolean confirm, String reason, boolean wait); -} \ No newline at end of file diff --git a/compat/src/main/aidl/dev/sanmer/mrepo/compat/stub/IServiceManager.aidl b/compat/src/main/aidl/dev/sanmer/mrepo/compat/stub/IServiceManager.aidl deleted file mode 100644 index 9202cdbc..00000000 --- a/compat/src/main/aidl/dev/sanmer/mrepo/compat/stub/IServiceManager.aidl +++ /dev/null @@ -1,17 +0,0 @@ -package dev.sanmer.mrepo.compat.stub; - -import dev.sanmer.mrepo.compat.stub.IFileManager; -import dev.sanmer.mrepo.compat.stub.IModuleManager; -import dev.sanmer.mrepo.compat.stub.IPowerManager; - -interface IServiceManager { - int getUid() = 0; - int getPid() = 1; - String getSELinuxContext() = 2; - String currentPlatform() = 3; - IModuleManager getModuleManager() = 4; - IFileManager getFileManager() = 5; - IPowerManager getPowerManager() = 6; - - void destroy() = 16777114; // Only for Shizuku -} \ No newline at end of file diff --git a/compat/src/main/kotlin/dev/sanmer/mrepo/compat/BuildCompat.kt b/compat/src/main/kotlin/dev/sanmer/mrepo/compat/BuildCompat.kt deleted file mode 100644 index 1707d39e..00000000 --- a/compat/src/main/kotlin/dev/sanmer/mrepo/compat/BuildCompat.kt +++ /dev/null @@ -1,18 +0,0 @@ -package dev.sanmer.mrepo.compat - -import android.os.Build -import androidx.annotation.ChecksSdkIntAtLeast - -internal object BuildCompat { - @get:ChecksSdkIntAtLeast(api = Build.VERSION_CODES.TIRAMISU) - val atLeastT get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU - - @get:ChecksSdkIntAtLeast(api = Build.VERSION_CODES.S) - val atLeastS get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S - - @get:ChecksSdkIntAtLeast(api = Build.VERSION_CODES.R) - val atLeastR get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R - - @get:ChecksSdkIntAtLeast(api = Build.VERSION_CODES.P) - val atLeastP get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P -} \ No newline at end of file diff --git a/compat/src/main/kotlin/dev/sanmer/mrepo/compat/ServiceManagerCompat.kt b/compat/src/main/kotlin/dev/sanmer/mrepo/compat/ServiceManagerCompat.kt deleted file mode 100644 index d330e766..00000000 --- a/compat/src/main/kotlin/dev/sanmer/mrepo/compat/ServiceManagerCompat.kt +++ /dev/null @@ -1,208 +0,0 @@ -package dev.sanmer.mrepo.compat - -import android.content.ComponentName -import android.content.Context -import android.content.Intent -import android.content.ServiceConnection -import android.content.pm.PackageManager -import android.os.IBinder -import com.topjohnwu.superuser.Shell -import com.topjohnwu.superuser.ipc.RootService -import dev.sanmer.mrepo.compat.delegate.ContextDelegate -import dev.sanmer.mrepo.compat.impl.ServiceManagerImpl -import dev.sanmer.mrepo.compat.stub.IServiceManager -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.suspendCancellableCoroutine -import kotlinx.coroutines.withContext -import kotlinx.coroutines.withTimeout -import org.lsposed.hiddenapibypass.HiddenApiBypass -import rikka.shizuku.Shizuku -import kotlin.coroutines.resume -import kotlin.coroutines.resumeWithException - -object ServiceManagerCompat { - private const val TIMEOUT_MILLIS = 15_000L - - fun setHiddenApiExemptions() = when { - BuildCompat.atLeastP -> HiddenApiBypass.addHiddenApiExemptions("") - else -> true - } - - internal interface IProvider { - val name: String - suspend fun isAvailable(): Boolean - suspend fun isAuthorized(): Boolean - fun bind(connection: ServiceConnection) - fun unbind(connection: ServiceConnection) - } - - private suspend fun get(provider: IProvider) = withTimeout(TIMEOUT_MILLIS) { - suspendCancellableCoroutine { continuation -> - val connection = object : ServiceConnection { - override fun onServiceConnected(name: ComponentName, binder: IBinder) { - val service = IServiceManager.Stub.asInterface(binder) - continuation.resume(service) - } - - override fun onServiceDisconnected(name: ComponentName) { - continuation.resumeWithException( - IllegalStateException("IServiceManager destroyed") - ) - } - - override fun onBindingDied(name: ComponentName?) { - continuation.resumeWithException( - IllegalStateException("IServiceManager destroyed") - ) - } - } - - provider.bind(connection) - continuation.invokeOnCancellation { - provider.unbind(connection) - } - } - } - - private suspend fun from(provider: IProvider): IServiceManager = withContext(Dispatchers.Main) { - when { - !provider.isAvailable() -> throw IllegalStateException("${provider.name} not available") - !provider.isAuthorized() -> throw IllegalStateException("${provider.name} not authorized") - else -> get(provider) - } - } - - private class ShizukuProvider( - private val context: Context - ) : IProvider { - override val name = "Shizuku" - - override suspend fun isAvailable(): Boolean { - return Shizuku.pingBinder() && Shizuku.getUid() == 0 - } - - override suspend fun isAuthorized() = when { - isGranted -> true - else -> suspendCancellableCoroutine { continuation -> - val listener = object : Shizuku.OnRequestPermissionResultListener { - override fun onRequestPermissionResult( - requestCode: Int, - grantResult: Int - ) { - Shizuku.removeRequestPermissionResultListener(this) - continuation.resume(isGranted) - } - } - - Shizuku.addRequestPermissionResultListener(listener) - continuation.invokeOnCancellation { - Shizuku.removeRequestPermissionResultListener(listener) - } - Shizuku.requestPermission(listener.hashCode()) - } - } - - override fun bind(connection: ServiceConnection) { - Shizuku.bindUserService(service, connection) - } - - override fun unbind(connection: ServiceConnection) { - Shizuku.unbindUserService(service, connection, true) - } - - private val isGranted get() = Shizuku.checkSelfPermission() == PackageManager.PERMISSION_GRANTED - - private val service by lazy { - Shizuku.UserServiceArgs( - ComponentName( - context.packageName, - ServiceManagerImpl::class.java.name - ) - ).apply { - daemon(false) - debuggable(false) - processNameSuffix("shizuku") - } - } - - companion object { - private val provider by lazy { - ShizukuProvider( - ContextDelegate.getContext() - ) - } - - fun get(): IProvider = provider - } - } - - suspend fun fromShizuku() = from(ShizukuProvider.get()) - - private class LibSuProvider( - private val context: Context - ) : IProvider { - override val name = "LibSu" - - override suspend fun isAvailable() = true - - override suspend fun isAuthorized() = suspendCancellableCoroutine { continuation -> - Shell.EXECUTOR.submit { - runCatching { - Shell.getShell() - }.onSuccess { - continuation.resume(true) - }.onFailure { - continuation.resume(false) - } - } - } - - override fun bind(connection: ServiceConnection) { - RootService.bind(service, connection) - } - - override fun unbind(connection: ServiceConnection) { - RootService.stop(service) - } - - private val service by lazy { - Intent().apply { - component = ComponentName( - context.packageName, - Service::class.java.name - ) - } - } - - init { - Shell.enableVerboseLogging = true - Shell.setDefaultBuilder( - Shell.Builder.create() - .setInitializers(SuShellInitializer::class.java) - .setTimeout(10) - ) - } - - private class SuShellInitializer : Shell.Initializer() { - override fun onInit(context: Context, shell: Shell) = shell.isRoot - } - - private class Service : RootService() { - override fun onBind(intent: Intent): IBinder { - return ServiceManagerImpl() - } - } - - companion object { - private val provider by lazy { - LibSuProvider( - ContextDelegate.getContext() - ) - } - - fun get(): IProvider = provider - } - } - - suspend fun fromLibSu() = from(LibSuProvider.get()) -} \ No newline at end of file diff --git a/compat/src/main/kotlin/dev/sanmer/mrepo/compat/delegate/ContextDelegate.kt b/compat/src/main/kotlin/dev/sanmer/mrepo/compat/delegate/ContextDelegate.kt deleted file mode 100644 index 47b1820a..00000000 --- a/compat/src/main/kotlin/dev/sanmer/mrepo/compat/delegate/ContextDelegate.kt +++ /dev/null @@ -1,16 +0,0 @@ -package dev.sanmer.mrepo.compat.delegate - -import android.app.ActivityThread -import android.content.Context -import android.content.ContextWrapper - -object ContextDelegate { - fun getContext(): Context { - var context: Context = ActivityThread.currentApplication() - while (context is ContextWrapper) { - context = context.baseContext - } - - return context - } -} \ No newline at end of file diff --git a/compat/src/main/kotlin/dev/sanmer/mrepo/compat/delegate/PowerManagerDelegate.kt b/compat/src/main/kotlin/dev/sanmer/mrepo/compat/delegate/PowerManagerDelegate.kt deleted file mode 100644 index 479bb96e..00000000 --- a/compat/src/main/kotlin/dev/sanmer/mrepo/compat/delegate/PowerManagerDelegate.kt +++ /dev/null @@ -1,39 +0,0 @@ -package dev.sanmer.mrepo.compat.delegate - -import android.os.Build -import android.os.PowerManagerHidden -import androidx.annotation.RequiresApi -import dev.sanmer.mrepo.compat.BuildCompat -import dev.sanmer.mrepo.compat.stub.IPowerManager - -class PowerManagerDelegate( - private val powerManager: IPowerManager -) { - fun reboot(reason: Reason = Reason.UserRequested) { - powerManager.reboot(false, reason.reason, true) - } - - enum class Reason( - internal val reason: String - ) { - UserRequested(SHUTDOWN_USER_REQUESTED), - @RequiresApi(Build.VERSION_CODES.R) - Userspace(REBOOT_USERSPACE), - Recovery(REBOOT_RECOVERY), - Bootloader(REBOOT_BOOTLOADER) - } - - companion object { - const val SHUTDOWN_USER_REQUESTED = "userrequested" - - @RequiresApi(Build.VERSION_CODES.R) - const val REBOOT_USERSPACE = "userspace" - - const val REBOOT_RECOVERY = "recovery" - - const val REBOOT_BOOTLOADER = "bootloader" - - fun isRebootingUserspaceSupported() = - BuildCompat.atLeastR && PowerManagerHidden.isRebootingUserspaceSupportedImpl() - } -} \ No newline at end of file diff --git a/compat/src/main/kotlin/dev/sanmer/mrepo/compat/impl/APatchModuleManagerImpl.kt b/compat/src/main/kotlin/dev/sanmer/mrepo/compat/impl/APatchModuleManagerImpl.kt deleted file mode 100644 index 6af5e7b8..00000000 --- a/compat/src/main/kotlin/dev/sanmer/mrepo/compat/impl/APatchModuleManagerImpl.kt +++ /dev/null @@ -1,56 +0,0 @@ -package dev.sanmer.mrepo.compat.impl - -import com.topjohnwu.superuser.Shell -import dev.sanmer.mrepo.compat.stub.IInstallCallback -import dev.sanmer.mrepo.compat.stub.IModuleOpsCallback - -internal class APatchModuleManagerImpl( - private val shell: Shell, -) : BaseModuleManagerImpl(shell) { - override fun enable(id: String, callback: IModuleOpsCallback) { - val dir = modulesDir.resolve(id) - if (!dir.exists()) callback.onFailure(id, null) - - "apd module enable $id".submit { - if (it.isSuccess) { - callback.onSuccess(id) - } else { - callback.onFailure(id, it.out.joinToString()) - } - } - } - - override fun disable(id: String, callback: IModuleOpsCallback) { - val dir = modulesDir.resolve(id) - if (!dir.exists()) return callback.onFailure(id, null) - - "apd module disable $id".submit { - if (it.isSuccess) { - callback.onSuccess(id) - } else { - callback.onFailure(id, it.out.joinToString()) - } - } - } - - override fun remove(id: String, callback: IModuleOpsCallback) { - val dir = modulesDir.resolve(id) - if (!dir.exists()) return callback.onFailure(id, null) - - "apd module uninstall $id".submit { - if (it.isSuccess) { - callback.onSuccess(id) - } else { - callback.onFailure(id, it.out.joinToString()) - } - } - } - - override fun install(path: String, callback: IInstallCallback) { - install( - cmd = "apd module install '${path}'", - path = path, - callback = callback - ) - } -} \ No newline at end of file diff --git a/compat/src/main/kotlin/dev/sanmer/mrepo/compat/impl/KernelSUModuleManagerImpl.kt b/compat/src/main/kotlin/dev/sanmer/mrepo/compat/impl/KernelSUModuleManagerImpl.kt deleted file mode 100644 index f817d1cf..00000000 --- a/compat/src/main/kotlin/dev/sanmer/mrepo/compat/impl/KernelSUModuleManagerImpl.kt +++ /dev/null @@ -1,56 +0,0 @@ -package dev.sanmer.mrepo.compat.impl - -import com.topjohnwu.superuser.Shell -import dev.sanmer.mrepo.compat.stub.IInstallCallback -import dev.sanmer.mrepo.compat.stub.IModuleOpsCallback - -internal class KernelSUModuleManagerImpl( - private val shell: Shell, -) : BaseModuleManagerImpl(shell) { - override fun enable(id: String, callback: IModuleOpsCallback) { - val dir = modulesDir.resolve(id) - if (!dir.exists()) callback.onFailure(id, null) - - "ksud module enable $id".submit { - if (it.isSuccess) { - callback.onSuccess(id) - } else { - callback.onFailure(id, it.out.joinToString()) - } - } - } - - override fun disable(id: String, callback: IModuleOpsCallback) { - val dir = modulesDir.resolve(id) - if (!dir.exists()) return callback.onFailure(id, null) - - "ksud module disable $id".submit { - if (it.isSuccess) { - callback.onSuccess(id) - } else { - callback.onFailure(id, it.out.joinToString()) - } - } - } - - override fun remove(id: String, callback: IModuleOpsCallback) { - val dir = modulesDir.resolve(id) - if (!dir.exists()) return callback.onFailure(id, null) - - "ksud module uninstall $id".submit { - if (it.isSuccess) { - callback.onSuccess(id) - } else { - callback.onFailure(id, it.out.joinToString()) - } - } - } - - override fun install(path: String, callback: IInstallCallback) { - install( - cmd = "ksud module install '${path}'", - path = path, - callback = callback - ) - } -} \ No newline at end of file diff --git a/compat/src/main/kotlin/dev/sanmer/mrepo/compat/impl/MagiskModuleManagerImpl.kt b/compat/src/main/kotlin/dev/sanmer/mrepo/compat/impl/MagiskModuleManagerImpl.kt deleted file mode 100644 index f2f93ae1..00000000 --- a/compat/src/main/kotlin/dev/sanmer/mrepo/compat/impl/MagiskModuleManagerImpl.kt +++ /dev/null @@ -1,59 +0,0 @@ -package dev.sanmer.mrepo.compat.impl - -import com.topjohnwu.superuser.Shell -import dev.sanmer.mrepo.compat.stub.IInstallCallback -import dev.sanmer.mrepo.compat.stub.IModuleOpsCallback - -internal class MagiskModuleManagerImpl( - private val shell: Shell -) : BaseModuleManagerImpl(shell) { - override fun enable(id: String, callback: IModuleOpsCallback) { - val dir = modulesDir.resolve(id) - if (!dir.exists()) callback.onFailure(id, null) - - runCatching { - dir.resolve("remove").apply { if (exists()) delete() } - dir.resolve("disable").apply { if (exists()) delete() } - }.onSuccess { - callback.onSuccess(id) - }.onFailure { - callback.onFailure(id, it.message) - } - } - - override fun disable(id: String, callback: IModuleOpsCallback) { - val dir = modulesDir.resolve(id) - if (!dir.exists()) return callback.onFailure(id, null) - - runCatching { - dir.resolve("remove").apply { if (exists()) delete() } - dir.resolve("disable").createNewFile() - }.onSuccess { - callback.onSuccess(id) - }.onFailure { - callback.onFailure(id, it.message) - } - } - - override fun remove(id: String, callback: IModuleOpsCallback) { - val dir = modulesDir.resolve(id) - if (!dir.exists()) return callback.onFailure(id, null) - - runCatching { - dir.resolve("disable").apply { if (exists()) delete() } - dir.resolve("remove").createNewFile() - }.onSuccess { - callback.onSuccess(id) - }.onFailure { - callback.onFailure(id, it.message) - } - } - - override fun install(path: String, callback: IInstallCallback) { - install( - cmd = "magisk --install-module '${path}'", - path = path, - callback = callback - ) - } -} \ No newline at end of file diff --git a/compat/src/main/kotlin/dev/sanmer/mrepo/compat/impl/PowerManagerImpl.kt b/compat/src/main/kotlin/dev/sanmer/mrepo/compat/impl/PowerManagerImpl.kt deleted file mode 100644 index 845b6989..00000000 --- a/compat/src/main/kotlin/dev/sanmer/mrepo/compat/impl/PowerManagerImpl.kt +++ /dev/null @@ -1,11 +0,0 @@ -package dev.sanmer.mrepo.compat.impl - -import dev.sanmer.mrepo.compat.stub.IPowerManager - -internal class PowerManagerImpl( - private val original: android.os.IPowerManager -) : IPowerManager.Stub() { - override fun reboot(confirm: Boolean, reason: String?, wait: Boolean) { - original.reboot(confirm, reason, wait) - } -} \ No newline at end of file diff --git a/compat/src/main/kotlin/dev/sanmer/mrepo/compat/impl/ServiceManagerImpl.kt b/compat/src/main/kotlin/dev/sanmer/mrepo/compat/impl/ServiceManagerImpl.kt deleted file mode 100644 index dbb7b09b..00000000 --- a/compat/src/main/kotlin/dev/sanmer/mrepo/compat/impl/ServiceManagerImpl.kt +++ /dev/null @@ -1,83 +0,0 @@ -package dev.sanmer.mrepo.compat.impl - -import android.content.Context -import android.os.SELinux -import android.os.ServiceManager -import android.system.Os -import com.topjohnwu.superuser.Shell -import com.topjohnwu.superuser.ShellUtils -import dev.sanmer.mrepo.compat.stub.IFileManager -import dev.sanmer.mrepo.compat.stub.IModuleManager -import dev.sanmer.mrepo.compat.stub.IPowerManager -import dev.sanmer.mrepo.compat.stub.IServiceManager -import kotlin.system.exitProcess - -internal class ServiceManagerImpl : IServiceManager.Stub() { - private val main by lazy { - Shell.Builder.create() - .build("sh") - } - - private val platform by lazy { - when { - "which magisk".execResult() -> Platform.Magisk - "which ksud".execResult() -> Platform.KernelSU - "which apd".execResult() -> Platform.APatch - else -> throw IllegalArgumentException("unsupported platform: $seLinuxContext") - } - } - - private val moduleManager by lazy { - when (platform) { - Platform.Magisk -> MagiskModuleManagerImpl(main) - Platform.KernelSU -> KernelSUModuleManagerImpl(main) - Platform.APatch -> APatchModuleManagerImpl(main) - } - } - - private val fileManager by lazy { - FileManagerImpl() - } - - private val powerManager by lazy { - PowerManagerImpl( - android.os.IPowerManager.Stub.asInterface( - ServiceManager.getService(Context.POWER_SERVICE) - ) - ) - } - - override fun getUid(): Int { - return Os.getuid() - } - - override fun getPid(): Int { - return Os.getpid() - } - - override fun getSELinuxContext(): String { - return SELinux.getContext() - } - - override fun currentPlatform(): String { - return platform.name - } - - override fun getModuleManager(): IModuleManager { - return moduleManager - } - - override fun getFileManager(): IFileManager { - return fileManager - } - - override fun getPowerManager(): IPowerManager { - return powerManager - } - - override fun destroy() { - exitProcess(0) - } - - private fun String.execResult() = ShellUtils.fastCmdResult(main, this) -} \ No newline at end of file diff --git a/core/build.gradle.kts b/core/build.gradle.kts new file mode 100644 index 00000000..6d73fe71 --- /dev/null +++ b/core/build.gradle.kts @@ -0,0 +1,19 @@ +plugins { + alias(libs.plugins.self.library) + alias(libs.plugins.kotlin.parcelize) +} + +android { + namespace = "dev.sanmer.mrepo.core" + + buildFeatures { + aidl = true + } +} + +dependencies { + api(libs.sanmer.su) + + implementation(libs.androidx.annotation) + implementation(libs.kotlinx.coroutines.android) +} \ No newline at end of file diff --git a/core/src/main/aidl/dev/sanmer/mrepo/content/LocalModule.aidl b/core/src/main/aidl/dev/sanmer/mrepo/content/LocalModule.aidl new file mode 100644 index 00000000..6daee56d --- /dev/null +++ b/core/src/main/aidl/dev/sanmer/mrepo/content/LocalModule.aidl @@ -0,0 +1,3 @@ +package dev.sanmer.mrepo.content; + +parcelable LocalModule; \ No newline at end of file diff --git a/compat/src/main/aidl/dev/sanmer/mrepo/compat/stub/IFileManager.aidl b/core/src/main/aidl/dev/sanmer/mrepo/stub/IFileManager.aidl similarity index 63% rename from compat/src/main/aidl/dev/sanmer/mrepo/compat/stub/IFileManager.aidl rename to core/src/main/aidl/dev/sanmer/mrepo/stub/IFileManager.aidl index 81eea196..a2ca0b72 100644 --- a/compat/src/main/aidl/dev/sanmer/mrepo/compat/stub/IFileManager.aidl +++ b/core/src/main/aidl/dev/sanmer/mrepo/stub/IFileManager.aidl @@ -1,4 +1,4 @@ -package dev.sanmer.mrepo.compat.stub; +package dev.sanmer.mrepo.stub; interface IFileManager { boolean deleteOnExit(String path); diff --git a/compat/src/main/aidl/dev/sanmer/mrepo/compat/stub/IInstallCallback.aidl b/core/src/main/aidl/dev/sanmer/mrepo/stub/IInstallCallback.aidl similarity index 63% rename from compat/src/main/aidl/dev/sanmer/mrepo/compat/stub/IInstallCallback.aidl rename to core/src/main/aidl/dev/sanmer/mrepo/stub/IInstallCallback.aidl index a3b9d41f..a66195b0 100644 --- a/compat/src/main/aidl/dev/sanmer/mrepo/compat/stub/IInstallCallback.aidl +++ b/core/src/main/aidl/dev/sanmer/mrepo/stub/IInstallCallback.aidl @@ -1,6 +1,6 @@ -package dev.sanmer.mrepo.compat.stub; +package dev.sanmer.mrepo.stub; -import dev.sanmer.mrepo.compat.content.LocalModule; +import dev.sanmer.mrepo.content.LocalModule; interface IInstallCallback { void onStdout(String msg); diff --git a/core/src/main/aidl/dev/sanmer/mrepo/stub/IManagerService.aidl b/core/src/main/aidl/dev/sanmer/mrepo/stub/IManagerService.aidl new file mode 100644 index 00000000..99c1ec4b --- /dev/null +++ b/core/src/main/aidl/dev/sanmer/mrepo/stub/IManagerService.aidl @@ -0,0 +1,11 @@ +package dev.sanmer.mrepo.stub; + +import dev.sanmer.mrepo.stub.IFileManager; +import dev.sanmer.mrepo.stub.IModuleManager; +import dev.sanmer.mrepo.stub.IPowerManager; + +interface IManagerService { + IModuleManager getModuleManager(); + IFileManager getFileManager(); + IPowerManager getPowerManager(); +} \ No newline at end of file diff --git a/compat/src/main/aidl/dev/sanmer/mrepo/compat/stub/IModuleManager.aidl b/core/src/main/aidl/dev/sanmer/mrepo/stub/IModuleManager.aidl similarity index 69% rename from compat/src/main/aidl/dev/sanmer/mrepo/compat/stub/IModuleManager.aidl rename to core/src/main/aidl/dev/sanmer/mrepo/stub/IModuleManager.aidl index df746506..9b3abf56 100644 --- a/compat/src/main/aidl/dev/sanmer/mrepo/compat/stub/IModuleManager.aidl +++ b/core/src/main/aidl/dev/sanmer/mrepo/stub/IModuleManager.aidl @@ -1,12 +1,13 @@ -package dev.sanmer.mrepo.compat.stub; +package dev.sanmer.mrepo.stub; -import dev.sanmer.mrepo.compat.content.LocalModule; -import dev.sanmer.mrepo.compat.stub.IInstallCallback; -import dev.sanmer.mrepo.compat.stub.IModuleOpsCallback; +import dev.sanmer.mrepo.content.LocalModule; +import dev.sanmer.mrepo.stub.IInstallCallback; +import dev.sanmer.mrepo.stub.IModuleOpsCallback; interface IModuleManager { String getVersion(); int getVersionCode(); + String getPlatform(); List getModules(); LocalModule getModuleById(String id); LocalModule getModuleInfo(String zipPath); diff --git a/compat/src/main/aidl/dev/sanmer/mrepo/compat/stub/IModuleOpsCallback.aidl b/core/src/main/aidl/dev/sanmer/mrepo/stub/IModuleOpsCallback.aidl similarity index 73% rename from compat/src/main/aidl/dev/sanmer/mrepo/compat/stub/IModuleOpsCallback.aidl rename to core/src/main/aidl/dev/sanmer/mrepo/stub/IModuleOpsCallback.aidl index 3459c7f2..f0a54195 100644 --- a/compat/src/main/aidl/dev/sanmer/mrepo/compat/stub/IModuleOpsCallback.aidl +++ b/core/src/main/aidl/dev/sanmer/mrepo/stub/IModuleOpsCallback.aidl @@ -1,4 +1,4 @@ -package dev.sanmer.mrepo.compat.stub; +package dev.sanmer.mrepo.stub; interface IModuleOpsCallback { void onSuccess(String id); diff --git a/core/src/main/aidl/dev/sanmer/mrepo/stub/IPowerManager.aidl b/core/src/main/aidl/dev/sanmer/mrepo/stub/IPowerManager.aidl new file mode 100644 index 00000000..00fb44a9 --- /dev/null +++ b/core/src/main/aidl/dev/sanmer/mrepo/stub/IPowerManager.aidl @@ -0,0 +1,5 @@ +package dev.sanmer.mrepo.stub; + +interface IPowerManager { + oneway void reboot(); +} \ No newline at end of file diff --git a/core/src/main/kotlin/dev/sanmer/mrepo/ManagerService.kt b/core/src/main/kotlin/dev/sanmer/mrepo/ManagerService.kt new file mode 100644 index 00000000..9479fbc8 --- /dev/null +++ b/core/src/main/kotlin/dev/sanmer/mrepo/ManagerService.kt @@ -0,0 +1,58 @@ +package dev.sanmer.mrepo + +import dev.sanmer.mrepo.impl.APatchModuleManagerImpl +import dev.sanmer.mrepo.impl.FileManagerImpl +import dev.sanmer.mrepo.impl.KernelSUModuleManagerImpl +import dev.sanmer.mrepo.impl.MagiskModuleManagerImpl +import dev.sanmer.mrepo.impl.PowerManagerImpl +import dev.sanmer.mrepo.impl.Shell.exec +import dev.sanmer.mrepo.stub.IFileManager +import dev.sanmer.mrepo.stub.IManagerService +import dev.sanmer.mrepo.stub.IModuleManager +import dev.sanmer.mrepo.stub.IPowerManager +import dev.sanmer.su.IServiceManager +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob + +class ManagerService : IManagerService.Stub() { + private val managerScope = CoroutineScope(SupervisorJob() + Dispatchers.IO) + + private val platform by lazy { + when { + "which magisk".exec().isSuccess -> Platform.Magisk + "which ksud".exec().isSuccess -> Platform.KernelSU + "which apd".exec().isSuccess -> Platform.APatch + else -> throw IllegalArgumentException("Unsupported platform") + } + } + + private val moduleManager by lazy { + when (platform) { + Platform.Magisk -> MagiskModuleManagerImpl(managerScope) + Platform.KernelSU -> KernelSUModuleManagerImpl(managerScope) + Platform.APatch -> APatchModuleManagerImpl(managerScope) + } + } + + private val fileManager by lazy { + FileManagerImpl() + } + + private val powerManager by lazy { + PowerManagerImpl(managerScope) + } + + override fun getModuleManager(): IModuleManager = moduleManager + + override fun getFileManager(): IFileManager = fileManager + + override fun getPowerManager(): IPowerManager = powerManager + + companion object { + val IServiceManager.managerService: IManagerService + get() = asInterface( + getService(ManagerService::class.java.name) + ) + } +} \ No newline at end of file diff --git a/compat/src/main/kotlin/dev/sanmer/mrepo/compat/impl/Platform.kt b/core/src/main/kotlin/dev/sanmer/mrepo/Platform.kt similarity index 62% rename from compat/src/main/kotlin/dev/sanmer/mrepo/compat/impl/Platform.kt rename to core/src/main/kotlin/dev/sanmer/mrepo/Platform.kt index 76324624..f1966067 100644 --- a/compat/src/main/kotlin/dev/sanmer/mrepo/compat/impl/Platform.kt +++ b/core/src/main/kotlin/dev/sanmer/mrepo/Platform.kt @@ -1,4 +1,4 @@ -package dev.sanmer.mrepo.compat.impl +package dev.sanmer.mrepo enum class Platform { Magisk, diff --git a/compat/src/main/kotlin/dev/sanmer/mrepo/compat/content/LocalModule.kt b/core/src/main/kotlin/dev/sanmer/mrepo/content/LocalModule.kt similarity index 90% rename from compat/src/main/kotlin/dev/sanmer/mrepo/compat/content/LocalModule.kt rename to core/src/main/kotlin/dev/sanmer/mrepo/content/LocalModule.kt index f85b3a7b..c2b7068c 100644 --- a/compat/src/main/kotlin/dev/sanmer/mrepo/compat/content/LocalModule.kt +++ b/core/src/main/kotlin/dev/sanmer/mrepo/content/LocalModule.kt @@ -1,4 +1,4 @@ -package dev.sanmer.mrepo.compat.content +package dev.sanmer.mrepo.content import android.os.Parcelable import kotlinx.parcelize.Parcelize diff --git a/compat/src/main/kotlin/dev/sanmer/mrepo/compat/content/State.kt b/core/src/main/kotlin/dev/sanmer/mrepo/content/State.kt similarity index 63% rename from compat/src/main/kotlin/dev/sanmer/mrepo/compat/content/State.kt rename to core/src/main/kotlin/dev/sanmer/mrepo/content/State.kt index 3e2fcf04..d0e3c2fc 100644 --- a/compat/src/main/kotlin/dev/sanmer/mrepo/compat/content/State.kt +++ b/core/src/main/kotlin/dev/sanmer/mrepo/content/State.kt @@ -1,4 +1,4 @@ -package dev.sanmer.mrepo.compat.content +package dev.sanmer.mrepo.content enum class State { ENABLE, diff --git a/core/src/main/kotlin/dev/sanmer/mrepo/impl/APatchModuleManagerImpl.kt b/core/src/main/kotlin/dev/sanmer/mrepo/impl/APatchModuleManagerImpl.kt new file mode 100644 index 00000000..470a02bf --- /dev/null +++ b/core/src/main/kotlin/dev/sanmer/mrepo/impl/APatchModuleManagerImpl.kt @@ -0,0 +1,62 @@ +package dev.sanmer.mrepo.impl + +import dev.sanmer.mrepo.Platform +import dev.sanmer.mrepo.impl.Shell.submit +import dev.sanmer.mrepo.stub.IInstallCallback +import dev.sanmer.mrepo.stub.IModuleOpsCallback +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch +import java.io.File + +internal class APatchModuleManagerImpl( + private val managerScope: CoroutineScope +) : BaseModuleManagerImpl(managerScope) { + override fun getPlatform() = Platform.APatch.name + + override fun enable(id: String, callback: IModuleOpsCallback) { + moduleOps( + cmd = "apd module enable $id", + id = id, + callback = callback + ) + } + + override fun disable(id: String, callback: IModuleOpsCallback) { + moduleOps( + cmd = "apd module disable $id", + id = id, + callback = callback + ) + } + + override fun remove(id: String, callback: IModuleOpsCallback) { + moduleOps( + cmd = "apd module uninstall $id", + id = id, + callback = callback + ) + } + + override fun install(path: String, callback: IInstallCallback) { + install( + cmd = "apd module install '${path}'", + path = path, + callback = callback + ) + } + + private fun moduleOps(cmd: String, id: String, callback: IModuleOpsCallback) { + managerScope.launch { + val moduleDir = File(modulesDir, id) + if (!moduleDir.exists()) { + return@launch callback.onFailure(id, null) + } + + cmd.submit().onSuccess { + callback.onSuccess(id) + }.onFailure { + callback.onFailure(id, it.message) + } + } + } +} \ No newline at end of file diff --git a/compat/src/main/kotlin/dev/sanmer/mrepo/compat/impl/BaseModuleManagerImpl.kt b/core/src/main/kotlin/dev/sanmer/mrepo/impl/BaseModuleManagerImpl.kt similarity index 72% rename from compat/src/main/kotlin/dev/sanmer/mrepo/compat/impl/BaseModuleManagerImpl.kt rename to core/src/main/kotlin/dev/sanmer/mrepo/impl/BaseModuleManagerImpl.kt index 1c581fc9..0ee5f695 100644 --- a/compat/src/main/kotlin/dev/sanmer/mrepo/compat/impl/BaseModuleManagerImpl.kt +++ b/core/src/main/kotlin/dev/sanmer/mrepo/impl/BaseModuleManagerImpl.kt @@ -1,30 +1,28 @@ -package dev.sanmer.mrepo.compat.impl - -import com.topjohnwu.superuser.CallbackList -import com.topjohnwu.superuser.Shell -import com.topjohnwu.superuser.ShellUtils -import dev.sanmer.mrepo.compat.content.LocalModule -import dev.sanmer.mrepo.compat.content.State -import dev.sanmer.mrepo.compat.stub.IInstallCallback -import dev.sanmer.mrepo.compat.stub.IModuleManager +package dev.sanmer.mrepo.impl + +import dev.sanmer.mrepo.content.LocalModule +import dev.sanmer.mrepo.content.State +import dev.sanmer.mrepo.impl.Shell.exec +import dev.sanmer.mrepo.impl.Shell.submit +import dev.sanmer.mrepo.stub.IInstallCallback +import dev.sanmer.mrepo.stub.IModuleManager +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch import java.io.File import java.util.zip.ZipFile internal abstract class BaseModuleManagerImpl( - private val shell: Shell + private val managerScope: CoroutineScope ) : IModuleManager.Stub() { internal val modulesDir = File(MODULES_PATH) private val mVersion by lazy { - runCatching { - "su -v".exec() - }.getOrDefault("unknown") + "su -v".exec().getOrDefault("unknown") } private val mVersionCode by lazy { - runCatching { - "su -V".exec().toInt() - }.getOrDefault(-1) + "su -V".exec().getOrDefault("-1") + .toIntOr(-1) } override fun getVersion(): String { @@ -132,34 +130,25 @@ internal abstract class BaseModuleManagerImpl( toInt() }.getOrDefault(defaultValue) - private fun String.exec() = ShellUtils.fastCmd(shell, this) - internal fun install(cmd: String, path: String, callback: IInstallCallback) { - val stdout = object : CallbackList() { - override fun onAddElement(msg: String?) { - msg?.let(callback::onStdout) - } - } + managerScope.launch { + val result = cmd.submit( + stdout = callback::onStdout, + stderr = callback::onStderr + ) - val stderr = object : CallbackList() { - override fun onAddElement(msg: String?) { - msg?.let(callback::onStderr) + when { + result.isSuccess -> { + val module = getModuleInfo(path) + callback.onSuccess(module) + } + else -> { + callback.onFailure() + } } } - - val result = shell.newJob().add(cmd).to(stdout, stderr).exec() - if (result.isSuccess) { - val module = getModuleInfo(path) - callback.onSuccess(module) - } else { - callback.onFailure() - } } - internal fun String.submit(cb: Shell.ResultCallback) = shell - .newJob().add(this).to(ArrayList(), null) - .submit(cb) - companion object { const val PROP_FILE = "module.prop" const val MODULES_PATH = "/data/adb/modules" diff --git a/compat/src/main/kotlin/dev/sanmer/mrepo/compat/impl/FileManagerImpl.kt b/core/src/main/kotlin/dev/sanmer/mrepo/impl/FileManagerImpl.kt similarity index 78% rename from compat/src/main/kotlin/dev/sanmer/mrepo/compat/impl/FileManagerImpl.kt rename to core/src/main/kotlin/dev/sanmer/mrepo/impl/FileManagerImpl.kt index 1d59c9ef..de2ee36f 100644 --- a/compat/src/main/kotlin/dev/sanmer/mrepo/compat/impl/FileManagerImpl.kt +++ b/core/src/main/kotlin/dev/sanmer/mrepo/impl/FileManagerImpl.kt @@ -1,6 +1,6 @@ -package dev.sanmer.mrepo.compat.impl +package dev.sanmer.mrepo.impl -import dev.sanmer.mrepo.compat.stub.IFileManager +import dev.sanmer.mrepo.stub.IFileManager import java.io.File internal class FileManagerImpl : IFileManager.Stub() { diff --git a/core/src/main/kotlin/dev/sanmer/mrepo/impl/KernelSUModuleManagerImpl.kt b/core/src/main/kotlin/dev/sanmer/mrepo/impl/KernelSUModuleManagerImpl.kt new file mode 100644 index 00000000..58ef7015 --- /dev/null +++ b/core/src/main/kotlin/dev/sanmer/mrepo/impl/KernelSUModuleManagerImpl.kt @@ -0,0 +1,62 @@ +package dev.sanmer.mrepo.impl + +import dev.sanmer.mrepo.Platform +import dev.sanmer.mrepo.impl.Shell.submit +import dev.sanmer.mrepo.stub.IInstallCallback +import dev.sanmer.mrepo.stub.IModuleOpsCallback +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch +import java.io.File + +internal class KernelSUModuleManagerImpl( + private val managerScope: CoroutineScope +) : BaseModuleManagerImpl(managerScope) { + override fun getPlatform() = Platform.KernelSU.name + + override fun enable(id: String, callback: IModuleOpsCallback) { + moduleOps( + cmd = "ksud module enable $id", + id = id, + callback = callback + ) + } + + override fun disable(id: String, callback: IModuleOpsCallback) { + moduleOps( + cmd = "ksud module disable $id", + id = id, + callback = callback + ) + } + + override fun remove(id: String, callback: IModuleOpsCallback) { + moduleOps( + cmd = "ksud module uninstall $id", + id = id, + callback = callback + ) + } + + override fun install(path: String, callback: IInstallCallback) { + install( + cmd = "ksud module install '${path}'", + path = path, + callback = callback + ) + } + + private fun moduleOps(cmd: String, id: String, callback: IModuleOpsCallback) { + managerScope.launch { + val moduleDir = File(modulesDir, id) + if (!moduleDir.exists()) { + return@launch callback.onFailure(id, null) + } + + cmd.submit().onSuccess { + callback.onSuccess(id) + }.onFailure { + callback.onFailure(id, it.message) + } + } + } +} \ No newline at end of file diff --git a/core/src/main/kotlin/dev/sanmer/mrepo/impl/MagiskModuleManagerImpl.kt b/core/src/main/kotlin/dev/sanmer/mrepo/impl/MagiskModuleManagerImpl.kt new file mode 100644 index 00000000..8a450578 --- /dev/null +++ b/core/src/main/kotlin/dev/sanmer/mrepo/impl/MagiskModuleManagerImpl.kt @@ -0,0 +1,92 @@ +package dev.sanmer.mrepo.impl + +import dev.sanmer.mrepo.Platform +import dev.sanmer.mrepo.stub.IInstallCallback +import dev.sanmer.mrepo.stub.IModuleOpsCallback +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch +import java.io.File + +internal class MagiskModuleManagerImpl( + private val managerScope: CoroutineScope +) : BaseModuleManagerImpl(managerScope) { + override fun getPlatform() = Platform.Magisk.name + + override fun enable(id: String, callback: IModuleOpsCallback) { + moduleOps( + tags = listOf( + Tag("remove", FileOp.Delete), + Tag("disable", FileOp.Delete) + ), + id = id, + callback = callback + ) + } + + override fun disable(id: String, callback: IModuleOpsCallback) { + moduleOps( + tags = listOf( + Tag("remove", FileOp.Delete), + Tag("disable", FileOp.Create) + ), + id = id, + callback = callback + ) + } + + override fun remove(id: String, callback: IModuleOpsCallback) { + moduleOps( + tags = listOf( + Tag("disable", FileOp.Delete), + Tag("remove", FileOp.Create) + ), + id = id, + callback = callback + ) + } + + override fun install(path: String, callback: IInstallCallback) { + install( + cmd = "magisk --install-module '${path}'", + path = path, + callback = callback + ) + } + + private fun moduleOps( + tags: List, + id: String, + callback: IModuleOpsCallback + ) { + managerScope.launch { + val moduleDir = File(modulesDir, id) + if (!moduleDir.exists()) { + return@launch callback.onFailure(id, null) + } + + runCatching { + tags.forEach { + val tag = File(moduleDir, it.name) + when (it.op) { + FileOp.Delete -> if (tag.exists()) tag.delete() + FileOp.Create -> tag.createNewFile() + } + } + }.onSuccess { + callback.onSuccess(id) + }.onFailure { + callback.onFailure(id, it.message) + } + } + } + + private class Tag( + val name: String, + val op: FileOp + ) + + private enum class FileOp { + Delete, + Create + } +} \ No newline at end of file diff --git a/core/src/main/kotlin/dev/sanmer/mrepo/impl/PowerManagerImpl.kt b/core/src/main/kotlin/dev/sanmer/mrepo/impl/PowerManagerImpl.kt new file mode 100644 index 00000000..6aca5aba --- /dev/null +++ b/core/src/main/kotlin/dev/sanmer/mrepo/impl/PowerManagerImpl.kt @@ -0,0 +1,16 @@ +package dev.sanmer.mrepo.impl + +import dev.sanmer.mrepo.impl.Shell.submit +import dev.sanmer.mrepo.stub.IPowerManager +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch + +internal class PowerManagerImpl( + private val managerScope: CoroutineScope +) : IPowerManager.Stub() { + override fun reboot() { + managerScope.launch { + "svc power reboot || reboot".submit() + } + } +} \ No newline at end of file diff --git a/core/src/main/kotlin/dev/sanmer/mrepo/impl/Shell.kt b/core/src/main/kotlin/dev/sanmer/mrepo/impl/Shell.kt new file mode 100644 index 00000000..0cc9dc21 --- /dev/null +++ b/core/src/main/kotlin/dev/sanmer/mrepo/impl/Shell.kt @@ -0,0 +1,72 @@ +package dev.sanmer.mrepo.impl + +import android.util.Log +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +internal object Shell { + private const val TAG = "Shell" + + fun String.exec(): Result = try { + Log.d(TAG, "exec: $this") + val process = ProcessBuilder("sh", "-c", this).start() + val output = process.inputStream.bufferedReader().readText() + .removeSurrounding("", "\n") + + val error = process.errorStream.bufferedReader().readText() + .removeSurrounding("", "\n") + + when { + process.waitFor().ok() -> { + Log.d(TAG, "output: $output") + Result.success(output) + } + else -> { + Log.d(TAG, "error: $error") + Result.failure(RuntimeException(error)) + } + } + } catch (e: Throwable) { + Log.e(TAG, "exec<$this>", e) + Result.failure(e) + } + + suspend fun String.submit() = + withContext(Dispatchers.IO) { exec() } + + suspend fun String.submit( + stdout: (String) -> Unit, + stderr: (String) -> Unit + ) = withContext(Dispatchers.IO) { + try { + Log.d(TAG, "submit: ${this@submit}") + val process = ProcessBuilder("sh", "-c", this@submit).start() + val output = process.inputStream.bufferedReader() + val error = process.errorStream.bufferedReader() + + withContext(Dispatchers.IO) { + output.forEachLine { + Log.d(TAG, "output: $it") + stdout(it) + } + } + + withContext(Dispatchers.IO) { + error.forEachLine { + Log.d(TAG, "stderr: $it") + stderr(it) + } + } + + when { + process.waitFor().ok() -> Result.success(true) + else -> Result.failure(RuntimeException()) + } + } catch (e: Throwable) { + Log.e(TAG, "submit<${this@submit}>", e) + Result.failure(e) + } + } + + private fun Int.ok() = this == 0 +} \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c847c6e3..51fdc6eb 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -13,16 +13,13 @@ androidxHiltNavigationCompose = "1.2.0" androidxLifecycle = "2.8.1" androidxNavigation = "2.7.7" androidxRoom = "2.6.1" -hiddenApiRefine = "4.4.0" hilt = "2.51.1" kotlin = "2.0.0" kotlinxCoroutines = "1.8.1" kotlinxDatetime = "0.6.0" ksp = "2.0.0-1.0.22" -libsu = "5.2.2" protobuf = "4.27.1" protobufPlugin = "0.9.4" -shizuku = "13.1.5" squareRetrofit = "2.11.0" squareOkhttp = "4.12.0" squareMoshi = "1.15.1" @@ -53,15 +50,8 @@ hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref hilt-compiler = { group = "com.google.dagger", name = "hilt-android-compiler", version.ref = "hilt" } kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "kotlinxCoroutines" } kotlinx-datetime = { group = "org.jetbrains.kotlinx", name = "kotlinx-datetime", version.ref = "kotlinxDatetime" } -libsu-core = { group = "com.github.topjohnwu.libsu", name = "core", version.ref = "libsu" } -libsu-service = { group = "com.github.topjohnwu.libsu", name = "service", version.ref = "libsu" } protobuf-kotlin-lite = { group = "com.google.protobuf", name = "protobuf-kotlin-lite", version.ref = "protobuf" } protobuf-protoc = { group = "com.google.protobuf", name = "protoc", version.ref = "protobuf" } -rikka-refine-annotation = { module = "dev.rikka.tools.refine:annotation", version.ref = "hiddenApiRefine" } -rikka-refine-compiler = { module = "dev.rikka.tools.refine:annotation-processor", version.ref = "hiddenApiRefine" } -rikka-refine-runtime = { module = "dev.rikka.tools.refine:runtime", version.ref = "hiddenApiRefine" } -rikka-shizuku-api = { module = "dev.rikka.shizuku:api", version.ref = "shizuku" } -rikka-shizuku-provider = { module = "dev.rikka.shizuku:provider", version.ref = "shizuku" } square-retrofit = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "squareRetrofit" } square-retrofit-moshi = { group = "com.squareup.retrofit2", name = "converter-moshi", version.ref = "squareRetrofit" } square-okhttp = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "squareOkhttp" } @@ -69,14 +59,15 @@ square-okhttp-dnsoverhttps = { group = "com.squareup.okhttp3", name = "okhttp-dn square-logging-interceptor = { group = "com.squareup.okhttp3", name = "logging-interceptor", version.ref = "squareOkhttp" } square-moshi = { group = "com.squareup.moshi", name = "moshi", version.ref = "squareMoshi" } square-moshi-kotlin = { group = "com.squareup.moshi", name = "moshi-kotlin-codegen", version.ref = "squareMoshi" } - markwon-core = "io.noties.markwon:core:4.6.2" -hiddenApiBypass = "org.lsposed.hiddenapibypass:hiddenapibypass:4.3" timber = "com.jakewharton.timber:timber:5.0.1" +# Local +sanmer-su = "dev.sanmer.su:core:0.1.0" + # Dependencies of the included build-logic android-gradle = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradlePlugin" } -compose-gradle= { module = "org.jetbrains.kotlin:compose-compiler-gradle-plugin", version.ref = "kotlin" } +compose-gradle= { group = "org.jetbrains.kotlin", name = "compose-compiler-gradle-plugin", version.ref = "kotlin" } kotlin-gradle = { group = "org.jetbrains.kotlin", name = "kotlin-gradle-plugin", version.ref = "kotlin" } ksp-gradle = { group = "com.google.devtools.ksp", name = "com.google.devtools.ksp.gradle.plugin", version.ref = "ksp" } @@ -88,7 +79,6 @@ hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" } kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlin" } ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } -rikka-refine = { id = "dev.rikka.tools.refine", version.ref = "hiddenApiRefine" } protobuf = { id = "com.google.protobuf", version.ref = "protobufPlugin" } # Plugins defined by this project diff --git a/hidden-api/build.gradle.kts b/hidden-api/build.gradle.kts deleted file mode 100644 index bc1925fa..00000000 --- a/hidden-api/build.gradle.kts +++ /dev/null @@ -1,13 +0,0 @@ -plugins { - alias(libs.plugins.self.library) -} - -android { - namespace = "dev.sanmer.mrepo.hidden_api" -} - -dependencies { - annotationProcessor(libs.rikka.refine.compiler) - compileOnly(libs.rikka.refine.annotation) - compileOnly(libs.androidx.annotation) -} \ No newline at end of file diff --git a/hidden-api/src/main/java/android/app/ActivityThread.java b/hidden-api/src/main/java/android/app/ActivityThread.java deleted file mode 100644 index c1b50074..00000000 --- a/hidden-api/src/main/java/android/app/ActivityThread.java +++ /dev/null @@ -1,7 +0,0 @@ -package android.app; - -public class ActivityThread { - public static Application currentApplication() { - throw new RuntimeException("Stub!"); - } -} \ No newline at end of file diff --git a/hidden-api/src/main/java/android/os/IPowerManager.java b/hidden-api/src/main/java/android/os/IPowerManager.java deleted file mode 100644 index e6f23a29..00000000 --- a/hidden-api/src/main/java/android/os/IPowerManager.java +++ /dev/null @@ -1,12 +0,0 @@ -package android.os; - -public interface IPowerManager extends IInterface { - void reboot(boolean confirm, String reason, boolean wait) throws RemoteException; - - abstract class Stub extends Binder implements IPowerManager { - - public static IPowerManager asInterface(IBinder binder) { - throw new RuntimeException("Stub!"); - } - } -} diff --git a/hidden-api/src/main/java/android/os/PowerManagerHidden.java b/hidden-api/src/main/java/android/os/PowerManagerHidden.java deleted file mode 100644 index 6b8ec379..00000000 --- a/hidden-api/src/main/java/android/os/PowerManagerHidden.java +++ /dev/null @@ -1,13 +0,0 @@ -package android.os; - -import androidx.annotation.RequiresApi; - -import dev.rikka.tools.refine.RefineAs; - -@RefineAs(PowerManager.class) -public class PowerManagerHidden { - @RequiresApi(30) - public static boolean isRebootingUserspaceSupportedImpl() { - throw new RuntimeException("Stub!"); - } -} diff --git a/hidden-api/src/main/java/android/os/SELinux.java b/hidden-api/src/main/java/android/os/SELinux.java deleted file mode 100644 index 79c66bff..00000000 --- a/hidden-api/src/main/java/android/os/SELinux.java +++ /dev/null @@ -1,6 +0,0 @@ -package android.os; - -public class SELinux { - - public static native String getContext(); -} \ No newline at end of file diff --git a/hidden-api/src/main/java/android/os/ServiceManager.java b/hidden-api/src/main/java/android/os/ServiceManager.java deleted file mode 100644 index 6718fb30..00000000 --- a/hidden-api/src/main/java/android/os/ServiceManager.java +++ /dev/null @@ -1,7 +0,0 @@ -package android.os; - -public class ServiceManager { - public static IBinder getService(String name) { - throw new RuntimeException("Stub!"); - } -} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 7eafc902..6f44011c 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -2,11 +2,17 @@ enableFeaturePreview("STABLE_CONFIGURATION_CACHE") enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") dependencyResolutionManagement { - repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositoriesMode = RepositoriesMode.FAIL_ON_PROJECT_REPOS repositories { google() mavenCentral() maven("https://jitpack.io") + + mavenLocal { + content { + includeGroup("dev.sanmer.su") + } + } } } @@ -20,6 +26,4 @@ pluginManagement { } rootProject.name = "MRepo" -include(":app") -include(":hidden-api") -include(":compat") \ No newline at end of file +include(":core", ":app") \ No newline at end of file From 82cb7121aa9d078ad4a6bd852ddafea17061cf8a Mon Sep 17 00:00:00 2001 From: SanmerDev Date: Sun, 9 Jun 2024 21:51:11 +0800 Subject: [PATCH 2/7] Update `proguard-rules.pro` --- app/proguard-rules.pro | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 2754634f..4fd07173 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -1,14 +1,4 @@ --verbose --dontpreverify --optimizationpasses 5 --dontskipnonpubliclibraryclasses - --dontwarn org.conscrypt.** --dontwarn kotlinx.serialization.** +-repackageclasses dev.sanmer.mrepo # Keep DataStore fields --keepclassmembers class * extends com.google.protobuf.GeneratedMessageLite* { - ; -} - --repackageclasses com.sanmer.mrepo \ No newline at end of file +-keepclassmembers class * extends com.google.protobuf.GeneratedMessageLite* { ; } \ No newline at end of file From 94d366ddab66895a7fb3e8f8e0efb6bd4f51d2f1 Mon Sep 17 00:00:00 2001 From: SanmerDev Date: Mon, 10 Jun 2024 16:21:44 +0800 Subject: [PATCH 3/7] Optimize `IModuleManager` --- app/build.gradle.kts | 1 + .../main/kotlin/com/sanmer/mrepo/Compat.kt | 15 ++--- .../sanmer/mrepo/model/local/LocalModule.kt | 19 +----- .../mrepo/repository/ModulesRepository.kt | 2 +- .../mrepo/viewmodel/InstallViewModel.kt | 10 +-- .../mrepo/viewmodel/ModulesViewModel.kt | 2 +- .../mrepo/viewmodel/SettingsViewModel.kt | 2 +- core/build.gradle.kts | 4 -- .../content/{LocalModule.aidl => Module.aidl} | 2 +- .../dev/sanmer/mrepo/stub/IFileManager.aidl | 5 -- .../sanmer/mrepo/stub/IInstallCallback.aidl | 6 +- .../sanmer/mrepo/stub/IManagerService.aidl | 11 ---- .../dev/sanmer/mrepo/stub/IModuleManager.aidl | 13 ++-- .../sanmer/mrepo/stub/IModuleOpsCallback.aidl | 2 +- .../dev/sanmer/mrepo/stub/IPowerManager.aidl | 5 -- .../kotlin/dev/sanmer/mrepo/ManagerService.kt | 58 ------------------ .../kotlin/dev/sanmer/mrepo/ModuleManager.kt | 42 +++++++++++++ .../content/{LocalModule.kt => Module.kt} | 6 +- .../mrepo/impl/APatchModuleManagerImpl.kt | 43 ++++++------- .../mrepo/impl/BaseModuleManagerImpl.kt | 61 +++++++++++-------- .../dev/sanmer/mrepo/impl/FileManagerImpl.kt | 15 ----- .../mrepo/impl/KernelSUModuleManagerImpl.kt | 43 ++++++------- .../mrepo/impl/MagiskModuleManagerImpl.kt | 55 ++++++++--------- .../dev/sanmer/mrepo/impl/PowerManagerImpl.kt | 16 ----- .../kotlin/dev/sanmer/mrepo/impl/Shell.kt | 55 +++++++---------- 25 files changed, 198 insertions(+), 295 deletions(-) rename core/src/main/aidl/dev/sanmer/mrepo/content/{LocalModule.aidl => Module.aidl} (60%) delete mode 100644 core/src/main/aidl/dev/sanmer/mrepo/stub/IFileManager.aidl delete mode 100644 core/src/main/aidl/dev/sanmer/mrepo/stub/IManagerService.aidl delete mode 100644 core/src/main/aidl/dev/sanmer/mrepo/stub/IPowerManager.aidl delete mode 100644 core/src/main/kotlin/dev/sanmer/mrepo/ManagerService.kt create mode 100644 core/src/main/kotlin/dev/sanmer/mrepo/ModuleManager.kt rename core/src/main/kotlin/dev/sanmer/mrepo/content/{LocalModule.kt => Module.kt} (84%) delete mode 100644 core/src/main/kotlin/dev/sanmer/mrepo/impl/FileManagerImpl.kt delete mode 100644 core/src/main/kotlin/dev/sanmer/mrepo/impl/PowerManagerImpl.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 67d3c96f..9417d645 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -114,6 +114,7 @@ protobuf { dependencies { implementation(projects.core) + implementation(libs.sanmer.su) implementation(libs.androidx.activity.compose) implementation(libs.androidx.appcompat) diff --git a/app/src/main/kotlin/com/sanmer/mrepo/Compat.kt b/app/src/main/kotlin/com/sanmer/mrepo/Compat.kt index c47ab19a..efad73d2 100644 --- a/app/src/main/kotlin/com/sanmer/mrepo/Compat.kt +++ b/app/src/main/kotlin/com/sanmer/mrepo/Compat.kt @@ -4,12 +4,11 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import com.sanmer.mrepo.datastore.WorkingMode -import dev.sanmer.mrepo.ManagerService.Companion.managerService -import dev.sanmer.mrepo.stub.IFileManager +import dev.sanmer.mrepo.ModuleManager import dev.sanmer.mrepo.stub.IModuleManager -import dev.sanmer.mrepo.stub.IPowerManager import dev.sanmer.su.IServiceManager import dev.sanmer.su.ServiceManagerCompat +import dev.sanmer.su.ServiceManagerCompat.createBy import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import timber.log.Timber @@ -26,6 +25,12 @@ object Compat { private val _isAliveFlow = MutableStateFlow(false) val isAliveFlow get() = _isAliveFlow.asStateFlow() + val moduleManager: IModuleManager by lazy { + IModuleManager.Stub.asInterface( + ModuleManager::class.createBy(mService) + ) + } + private fun state(): Boolean { isAlive = mServiceOrNull != null _isAliveFlow.value = isAlive @@ -57,8 +62,4 @@ object Compat { else -> fallback } } - - fun getModuleManager(): IModuleManager = mService.managerService.moduleManager - fun getFileManager(): IFileManager = mService.managerService.fileManager - fun getPowerManager(): IPowerManager = mService.managerService.powerManager } \ No newline at end of file diff --git a/app/src/main/kotlin/com/sanmer/mrepo/model/local/LocalModule.kt b/app/src/main/kotlin/com/sanmer/mrepo/model/local/LocalModule.kt index 6da5f74a..b1ad02c5 100644 --- a/app/src/main/kotlin/com/sanmer/mrepo/model/local/LocalModule.kt +++ b/app/src/main/kotlin/com/sanmer/mrepo/model/local/LocalModule.kt @@ -1,21 +1,8 @@ package com.sanmer.mrepo.model.local import com.sanmer.mrepo.utils.Utils -import dev.sanmer.mrepo.content.LocalModule +import dev.sanmer.mrepo.content.Module -typealias LocalModule = LocalModule +typealias LocalModule = Module -val LocalModule.versionDisplay get() = Utils.getVersionDisplay(version, versionCode) - -fun LocalModule.Companion.example() = - LocalModule( - id = "local_example", - name = "Example", - version = "2022.08.16", - versionCode = 1703, - author = "Sanmer", - description = "This is an example!", - updateJson = "", - state = State.ENABLE, - lastUpdated = 0L - ) \ No newline at end of file +val Module.versionDisplay get() = Utils.getVersionDisplay(version, versionCode) \ No newline at end of file diff --git a/app/src/main/kotlin/com/sanmer/mrepo/repository/ModulesRepository.kt b/app/src/main/kotlin/com/sanmer/mrepo/repository/ModulesRepository.kt index 513b3e4d..0e4cc0b1 100644 --- a/app/src/main/kotlin/com/sanmer/mrepo/repository/ModulesRepository.kt +++ b/app/src/main/kotlin/com/sanmer/mrepo/repository/ModulesRepository.kt @@ -14,7 +14,7 @@ import javax.inject.Singleton class ModulesRepository @Inject constructor( private val localRepository: LocalRepository, ) { - private val mm by lazy { Compat.getModuleManager() } + private val mm get() = Compat.moduleManager suspend fun getLocalAll() = withContext(Dispatchers.IO) { with(mm.modules) { diff --git a/app/src/main/kotlin/com/sanmer/mrepo/viewmodel/InstallViewModel.kt b/app/src/main/kotlin/com/sanmer/mrepo/viewmodel/InstallViewModel.kt index c36f3bcf..4eb4d213 100644 --- a/app/src/main/kotlin/com/sanmer/mrepo/viewmodel/InstallViewModel.kt +++ b/app/src/main/kotlin/com/sanmer/mrepo/viewmodel/InstallViewModel.kt @@ -33,7 +33,7 @@ class InstallViewModel @Inject constructor( private val localRepository: LocalRepository, private val userPreferencesRepository: UserPreferencesRepository, ) : ViewModel() { - private val mm by lazy { Compat.getModuleManager() } + private val mm get() = Compat.moduleManager val logs = mutableListOf() val console = mutableStateListOf() @@ -46,10 +46,7 @@ class InstallViewModel @Inject constructor( Timber.d("InstallViewModel init") } - fun reboot() { - val powerManager = Compat.getPowerManager() - powerManager.reboot() - } + fun reboot() = mm.reboot() suspend fun writeLogsTo(context: Context, uri: Uri) = withContext(Dispatchers.IO) { runCatching { @@ -143,8 +140,7 @@ class InstallViewModel @Inject constructor( private fun deleteBySu(zipPath: String) { runCatching { - val fileManager = Compat.getFileManager() - fileManager.deleteOnExit(zipPath) + mm.deleteOnExit(zipPath) }.onFailure { Timber.e(it) }.onSuccess { diff --git a/app/src/main/kotlin/com/sanmer/mrepo/viewmodel/ModulesViewModel.kt b/app/src/main/kotlin/com/sanmer/mrepo/viewmodel/ModulesViewModel.kt index 0e71cc42..ea4902f2 100644 --- a/app/src/main/kotlin/com/sanmer/mrepo/viewmodel/ModulesViewModel.kt +++ b/app/src/main/kotlin/com/sanmer/mrepo/viewmodel/ModulesViewModel.kt @@ -45,7 +45,7 @@ class ModulesViewModel @Inject constructor( private val modulesRepository: ModulesRepository, private val userPreferencesRepository: UserPreferencesRepository, ) : ViewModel() { - private val mm by lazy { Compat.getModuleManager() } + private val mm get() = Compat.moduleManager val isProviderAlive get() = Compat.isAlive private val modulesMenu get() = userPreferencesRepository.data diff --git a/app/src/main/kotlin/com/sanmer/mrepo/viewmodel/SettingsViewModel.kt b/app/src/main/kotlin/com/sanmer/mrepo/viewmodel/SettingsViewModel.kt index 3904c7b9..31849766 100644 --- a/app/src/main/kotlin/com/sanmer/mrepo/viewmodel/SettingsViewModel.kt +++ b/app/src/main/kotlin/com/sanmer/mrepo/viewmodel/SettingsViewModel.kt @@ -16,7 +16,7 @@ import javax.inject.Inject class SettingsViewModel @Inject constructor( private val userPreferencesRepository: UserPreferencesRepository ) : ViewModel() { - private val mm by lazy { Compat.getModuleManager() } + private val mm get() = Compat.moduleManager val isProviderAlive get() = Compat.isAlive val version get() = Compat.get("") { diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 6d73fe71..df438072 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -12,8 +12,4 @@ android { } dependencies { - api(libs.sanmer.su) - - implementation(libs.androidx.annotation) - implementation(libs.kotlinx.coroutines.android) } \ No newline at end of file diff --git a/core/src/main/aidl/dev/sanmer/mrepo/content/LocalModule.aidl b/core/src/main/aidl/dev/sanmer/mrepo/content/Module.aidl similarity index 60% rename from core/src/main/aidl/dev/sanmer/mrepo/content/LocalModule.aidl rename to core/src/main/aidl/dev/sanmer/mrepo/content/Module.aidl index 6daee56d..5c0a9a69 100644 --- a/core/src/main/aidl/dev/sanmer/mrepo/content/LocalModule.aidl +++ b/core/src/main/aidl/dev/sanmer/mrepo/content/Module.aidl @@ -1,3 +1,3 @@ package dev.sanmer.mrepo.content; -parcelable LocalModule; \ No newline at end of file +parcelable Module; \ No newline at end of file diff --git a/core/src/main/aidl/dev/sanmer/mrepo/stub/IFileManager.aidl b/core/src/main/aidl/dev/sanmer/mrepo/stub/IFileManager.aidl deleted file mode 100644 index a2ca0b72..00000000 --- a/core/src/main/aidl/dev/sanmer/mrepo/stub/IFileManager.aidl +++ /dev/null @@ -1,5 +0,0 @@ -package dev.sanmer.mrepo.stub; - -interface IFileManager { - boolean deleteOnExit(String path); -} \ No newline at end of file diff --git a/core/src/main/aidl/dev/sanmer/mrepo/stub/IInstallCallback.aidl b/core/src/main/aidl/dev/sanmer/mrepo/stub/IInstallCallback.aidl index a66195b0..bdc9e7e8 100644 --- a/core/src/main/aidl/dev/sanmer/mrepo/stub/IInstallCallback.aidl +++ b/core/src/main/aidl/dev/sanmer/mrepo/stub/IInstallCallback.aidl @@ -1,10 +1,10 @@ package dev.sanmer.mrepo.stub; -import dev.sanmer.mrepo.content.LocalModule; +import dev.sanmer.mrepo.content.Module; -interface IInstallCallback { +oneway interface IInstallCallback { void onStdout(String msg); void onStderr(String msg); - void onSuccess(in LocalModule module); + void onSuccess(in Module module); void onFailure(); } \ No newline at end of file diff --git a/core/src/main/aidl/dev/sanmer/mrepo/stub/IManagerService.aidl b/core/src/main/aidl/dev/sanmer/mrepo/stub/IManagerService.aidl deleted file mode 100644 index 99c1ec4b..00000000 --- a/core/src/main/aidl/dev/sanmer/mrepo/stub/IManagerService.aidl +++ /dev/null @@ -1,11 +0,0 @@ -package dev.sanmer.mrepo.stub; - -import dev.sanmer.mrepo.stub.IFileManager; -import dev.sanmer.mrepo.stub.IModuleManager; -import dev.sanmer.mrepo.stub.IPowerManager; - -interface IManagerService { - IModuleManager getModuleManager(); - IFileManager getFileManager(); - IPowerManager getPowerManager(); -} \ No newline at end of file diff --git a/core/src/main/aidl/dev/sanmer/mrepo/stub/IModuleManager.aidl b/core/src/main/aidl/dev/sanmer/mrepo/stub/IModuleManager.aidl index 9b3abf56..9d4920c0 100644 --- a/core/src/main/aidl/dev/sanmer/mrepo/stub/IModuleManager.aidl +++ b/core/src/main/aidl/dev/sanmer/mrepo/stub/IModuleManager.aidl @@ -1,6 +1,6 @@ package dev.sanmer.mrepo.stub; -import dev.sanmer.mrepo.content.LocalModule; +import dev.sanmer.mrepo.content.Module; import dev.sanmer.mrepo.stub.IInstallCallback; import dev.sanmer.mrepo.stub.IModuleOpsCallback; @@ -8,11 +8,16 @@ interface IModuleManager { String getVersion(); int getVersionCode(); String getPlatform(); - List getModules(); - LocalModule getModuleById(String id); - LocalModule getModuleInfo(String zipPath); + + List getModules(); + Module getModuleById(String id); + Module getModuleInfo(String path); + oneway void enable(String id, IModuleOpsCallback callback); oneway void disable(String id, IModuleOpsCallback callback); oneway void remove(String id, IModuleOpsCallback callback); oneway void install(String path, IInstallCallback callback); + + boolean deleteOnExit(String path); + oneway void reboot(); } \ No newline at end of file diff --git a/core/src/main/aidl/dev/sanmer/mrepo/stub/IModuleOpsCallback.aidl b/core/src/main/aidl/dev/sanmer/mrepo/stub/IModuleOpsCallback.aidl index f0a54195..839059ca 100644 --- a/core/src/main/aidl/dev/sanmer/mrepo/stub/IModuleOpsCallback.aidl +++ b/core/src/main/aidl/dev/sanmer/mrepo/stub/IModuleOpsCallback.aidl @@ -1,6 +1,6 @@ package dev.sanmer.mrepo.stub; -interface IModuleOpsCallback { +oneway interface IModuleOpsCallback { void onSuccess(String id); void onFailure(String id, String msg); } \ No newline at end of file diff --git a/core/src/main/aidl/dev/sanmer/mrepo/stub/IPowerManager.aidl b/core/src/main/aidl/dev/sanmer/mrepo/stub/IPowerManager.aidl deleted file mode 100644 index 00fb44a9..00000000 --- a/core/src/main/aidl/dev/sanmer/mrepo/stub/IPowerManager.aidl +++ /dev/null @@ -1,5 +0,0 @@ -package dev.sanmer.mrepo.stub; - -interface IPowerManager { - oneway void reboot(); -} \ No newline at end of file diff --git a/core/src/main/kotlin/dev/sanmer/mrepo/ManagerService.kt b/core/src/main/kotlin/dev/sanmer/mrepo/ManagerService.kt deleted file mode 100644 index 9479fbc8..00000000 --- a/core/src/main/kotlin/dev/sanmer/mrepo/ManagerService.kt +++ /dev/null @@ -1,58 +0,0 @@ -package dev.sanmer.mrepo - -import dev.sanmer.mrepo.impl.APatchModuleManagerImpl -import dev.sanmer.mrepo.impl.FileManagerImpl -import dev.sanmer.mrepo.impl.KernelSUModuleManagerImpl -import dev.sanmer.mrepo.impl.MagiskModuleManagerImpl -import dev.sanmer.mrepo.impl.PowerManagerImpl -import dev.sanmer.mrepo.impl.Shell.exec -import dev.sanmer.mrepo.stub.IFileManager -import dev.sanmer.mrepo.stub.IManagerService -import dev.sanmer.mrepo.stub.IModuleManager -import dev.sanmer.mrepo.stub.IPowerManager -import dev.sanmer.su.IServiceManager -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.SupervisorJob - -class ManagerService : IManagerService.Stub() { - private val managerScope = CoroutineScope(SupervisorJob() + Dispatchers.IO) - - private val platform by lazy { - when { - "which magisk".exec().isSuccess -> Platform.Magisk - "which ksud".exec().isSuccess -> Platform.KernelSU - "which apd".exec().isSuccess -> Platform.APatch - else -> throw IllegalArgumentException("Unsupported platform") - } - } - - private val moduleManager by lazy { - when (platform) { - Platform.Magisk -> MagiskModuleManagerImpl(managerScope) - Platform.KernelSU -> KernelSUModuleManagerImpl(managerScope) - Platform.APatch -> APatchModuleManagerImpl(managerScope) - } - } - - private val fileManager by lazy { - FileManagerImpl() - } - - private val powerManager by lazy { - PowerManagerImpl(managerScope) - } - - override fun getModuleManager(): IModuleManager = moduleManager - - override fun getFileManager(): IFileManager = fileManager - - override fun getPowerManager(): IPowerManager = powerManager - - companion object { - val IServiceManager.managerService: IManagerService - get() = asInterface( - getService(ManagerService::class.java.name) - ) - } -} \ No newline at end of file diff --git a/core/src/main/kotlin/dev/sanmer/mrepo/ModuleManager.kt b/core/src/main/kotlin/dev/sanmer/mrepo/ModuleManager.kt new file mode 100644 index 00000000..15d248f6 --- /dev/null +++ b/core/src/main/kotlin/dev/sanmer/mrepo/ModuleManager.kt @@ -0,0 +1,42 @@ +package dev.sanmer.mrepo + +import dev.sanmer.mrepo.content.Module +import dev.sanmer.mrepo.impl.APatchModuleManagerImpl +import dev.sanmer.mrepo.impl.KernelSUModuleManagerImpl +import dev.sanmer.mrepo.impl.MagiskModuleManagerImpl +import dev.sanmer.mrepo.impl.Shell.exec +import dev.sanmer.mrepo.stub.IInstallCallback +import dev.sanmer.mrepo.stub.IModuleManager +import dev.sanmer.mrepo.stub.IModuleOpsCallback + +class ModuleManager : IModuleManager.Stub() { + private val platform by lazy { + when { + "which magisk".exec().isSuccess -> Platform.Magisk + "which ksud".exec().isSuccess -> Platform.KernelSU + "which apd".exec().isSuccess -> Platform.APatch + else -> throw IllegalArgumentException("Unsupported platform") + } + } + + private val original by lazy { + when (platform) { + Platform.Magisk -> MagiskModuleManagerImpl() + Platform.KernelSU -> KernelSUModuleManagerImpl() + Platform.APatch -> APatchModuleManagerImpl() + } + } + + override fun getVersion(): String = original.version + override fun getVersionCode(): Int = original.versionCode + override fun getPlatform(): String = original.platform + override fun getModules(): List = original.modules + override fun getModuleById(id: String): Module? = original.getModuleById(id) + override fun getModuleInfo(path: String): Module? = original.getModuleInfo(path) + override fun enable(id: String, callback: IModuleOpsCallback?) = original.enable(id, callback) + override fun disable(id: String, callback: IModuleOpsCallback?) = original.disable(id, callback) + override fun remove(id: String, callback: IModuleOpsCallback?) = original.remove(id, callback) + override fun install(path: String, callback: IInstallCallback?) = original.install(path, callback) + override fun deleteOnExit(path: String): Boolean = original.deleteOnExit(path) + override fun reboot() = original.reboot() +} \ No newline at end of file diff --git a/core/src/main/kotlin/dev/sanmer/mrepo/content/LocalModule.kt b/core/src/main/kotlin/dev/sanmer/mrepo/content/Module.kt similarity index 84% rename from core/src/main/kotlin/dev/sanmer/mrepo/content/LocalModule.kt rename to core/src/main/kotlin/dev/sanmer/mrepo/content/Module.kt index c2b7068c..95709277 100644 --- a/core/src/main/kotlin/dev/sanmer/mrepo/content/LocalModule.kt +++ b/core/src/main/kotlin/dev/sanmer/mrepo/content/Module.kt @@ -4,7 +4,7 @@ import android.os.Parcelable import kotlinx.parcelize.Parcelize @Parcelize -data class LocalModule( +data class Module( val id: String, val name: String, val version: String, @@ -14,6 +14,4 @@ data class LocalModule( val updateJson: String, val state: State, val lastUpdated: Long -) : Parcelable { - companion object -} \ No newline at end of file +) : Parcelable \ No newline at end of file diff --git a/core/src/main/kotlin/dev/sanmer/mrepo/impl/APatchModuleManagerImpl.kt b/core/src/main/kotlin/dev/sanmer/mrepo/impl/APatchModuleManagerImpl.kt index 470a02bf..b50f7039 100644 --- a/core/src/main/kotlin/dev/sanmer/mrepo/impl/APatchModuleManagerImpl.kt +++ b/core/src/main/kotlin/dev/sanmer/mrepo/impl/APatchModuleManagerImpl.kt @@ -1,19 +1,17 @@ package dev.sanmer.mrepo.impl import dev.sanmer.mrepo.Platform -import dev.sanmer.mrepo.impl.Shell.submit +import dev.sanmer.mrepo.impl.Shell.exec import dev.sanmer.mrepo.stub.IInstallCallback import dev.sanmer.mrepo.stub.IModuleOpsCallback -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch import java.io.File -internal class APatchModuleManagerImpl( - private val managerScope: CoroutineScope -) : BaseModuleManagerImpl(managerScope) { - override fun getPlatform() = Platform.APatch.name +internal class APatchModuleManagerImpl : BaseModuleManagerImpl() { + override fun getPlatform(): String { + return Platform.APatch.name + } - override fun enable(id: String, callback: IModuleOpsCallback) { + override fun enable(id: String, callback: IModuleOpsCallback?) { moduleOps( cmd = "apd module enable $id", id = id, @@ -21,7 +19,7 @@ internal class APatchModuleManagerImpl( ) } - override fun disable(id: String, callback: IModuleOpsCallback) { + override fun disable(id: String, callback: IModuleOpsCallback?) { moduleOps( cmd = "apd module disable $id", id = id, @@ -29,7 +27,7 @@ internal class APatchModuleManagerImpl( ) } - override fun remove(id: String, callback: IModuleOpsCallback) { + override fun remove(id: String, callback: IModuleOpsCallback?) { moduleOps( cmd = "apd module uninstall $id", id = id, @@ -37,7 +35,7 @@ internal class APatchModuleManagerImpl( ) } - override fun install(path: String, callback: IInstallCallback) { + override fun install(path: String, callback: IInstallCallback?) { install( cmd = "apd module install '${path}'", path = path, @@ -45,18 +43,17 @@ internal class APatchModuleManagerImpl( ) } - private fun moduleOps(cmd: String, id: String, callback: IModuleOpsCallback) { - managerScope.launch { - val moduleDir = File(modulesDir, id) - if (!moduleDir.exists()) { - return@launch callback.onFailure(id, null) - } - - cmd.submit().onSuccess { - callback.onSuccess(id) - }.onFailure { - callback.onFailure(id, it.message) - } + private fun moduleOps(cmd: String, id: String, callback: IModuleOpsCallback?) { + val moduleDir = File(modulesDir, id) + if (!moduleDir.exists()) { + callback?.onFailure(id, null) + return + } + + cmd.exec().onSuccess { + callback?.onSuccess(id) + }.onFailure { + callback?.onFailure(id, it.message) } } } \ No newline at end of file diff --git a/core/src/main/kotlin/dev/sanmer/mrepo/impl/BaseModuleManagerImpl.kt b/core/src/main/kotlin/dev/sanmer/mrepo/impl/BaseModuleManagerImpl.kt index 0ee5f695..961a5742 100644 --- a/core/src/main/kotlin/dev/sanmer/mrepo/impl/BaseModuleManagerImpl.kt +++ b/core/src/main/kotlin/dev/sanmer/mrepo/impl/BaseModuleManagerImpl.kt @@ -1,19 +1,14 @@ package dev.sanmer.mrepo.impl -import dev.sanmer.mrepo.content.LocalModule +import dev.sanmer.mrepo.content.Module import dev.sanmer.mrepo.content.State import dev.sanmer.mrepo.impl.Shell.exec -import dev.sanmer.mrepo.impl.Shell.submit import dev.sanmer.mrepo.stub.IInstallCallback import dev.sanmer.mrepo.stub.IModuleManager -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch import java.io.File import java.util.zip.ZipFile -internal abstract class BaseModuleManagerImpl( - private val managerScope: CoroutineScope -) : IModuleManager.Stub() { +internal abstract class BaseModuleManagerImpl : IModuleManager.Stub() { internal val modulesDir = File(MODULES_PATH) private val mVersion by lazy { @@ -39,13 +34,13 @@ internal abstract class BaseModuleManagerImpl( readProps(dir)?.toModule(dir) } - override fun getModuleById(id: String): LocalModule? { + override fun getModuleById(id: String): Module? { val dir = modulesDir.resolve(id) return readProps(dir)?.toModule(dir) } - override fun getModuleInfo(zipPath: String): LocalModule? { - val zipFile = ZipFile(zipPath) + override fun getModuleInfo(path: String): Module? { + val zipFile = ZipFile(path) val entry = zipFile.getEntry(PROP_FILE) ?: return null return zipFile.getInputStream(entry).use { @@ -56,6 +51,19 @@ internal abstract class BaseModuleManagerImpl( } } + override fun deleteOnExit(path: String) = with(File(path)) { + when { + !exists() -> false + isFile -> delete() + isDirectory -> deleteRecursively() + else -> false + } + } + + override fun reboot() { + "svc power reboot || reboot".exec() + } + private fun readProps(props: String) = props.lines() .associate { line -> val items = line.split("=", limit = 2).map { it.trim() } @@ -113,7 +121,7 @@ internal abstract class BaseModuleManagerImpl( path: String = "unknown", state: State = State.ENABLE, lastUpdated: Long = 0L - ) = LocalModule( + ) = Module( id = getOrDefault("id", path), name = getOrDefault("name", path), version = getOrDefault("version", ""), @@ -130,21 +138,24 @@ internal abstract class BaseModuleManagerImpl( toInt() }.getOrDefault(defaultValue) - internal fun install(cmd: String, path: String, callback: IInstallCallback) { - managerScope.launch { - val result = cmd.submit( - stdout = callback::onStdout, - stderr = callback::onStderr - ) + internal fun install(cmd: String, path: String, callback: IInstallCallback?) { + if (callback == null) { + cmd.exec() + return + } - when { - result.isSuccess -> { - val module = getModuleInfo(path) - callback.onSuccess(module) - } - else -> { - callback.onFailure() - } + val result = cmd.exec( + stdout = callback::onStdout, + stderr = callback::onStderr + ) + + when { + result.isSuccess -> { + val module = getModuleInfo(path) + callback.onSuccess(module) + } + else -> { + callback.onFailure() } } } diff --git a/core/src/main/kotlin/dev/sanmer/mrepo/impl/FileManagerImpl.kt b/core/src/main/kotlin/dev/sanmer/mrepo/impl/FileManagerImpl.kt deleted file mode 100644 index de2ee36f..00000000 --- a/core/src/main/kotlin/dev/sanmer/mrepo/impl/FileManagerImpl.kt +++ /dev/null @@ -1,15 +0,0 @@ -package dev.sanmer.mrepo.impl - -import dev.sanmer.mrepo.stub.IFileManager -import java.io.File - -internal class FileManagerImpl : IFileManager.Stub() { - override fun deleteOnExit(path: String) = with(File(path)) { - when { - !exists() -> false - isFile -> delete() - isDirectory -> deleteRecursively() - else -> false - } - } -} \ No newline at end of file diff --git a/core/src/main/kotlin/dev/sanmer/mrepo/impl/KernelSUModuleManagerImpl.kt b/core/src/main/kotlin/dev/sanmer/mrepo/impl/KernelSUModuleManagerImpl.kt index 58ef7015..a47d95eb 100644 --- a/core/src/main/kotlin/dev/sanmer/mrepo/impl/KernelSUModuleManagerImpl.kt +++ b/core/src/main/kotlin/dev/sanmer/mrepo/impl/KernelSUModuleManagerImpl.kt @@ -1,19 +1,17 @@ package dev.sanmer.mrepo.impl import dev.sanmer.mrepo.Platform -import dev.sanmer.mrepo.impl.Shell.submit +import dev.sanmer.mrepo.impl.Shell.exec import dev.sanmer.mrepo.stub.IInstallCallback import dev.sanmer.mrepo.stub.IModuleOpsCallback -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch import java.io.File -internal class KernelSUModuleManagerImpl( - private val managerScope: CoroutineScope -) : BaseModuleManagerImpl(managerScope) { - override fun getPlatform() = Platform.KernelSU.name +internal class KernelSUModuleManagerImpl : BaseModuleManagerImpl() { + override fun getPlatform(): String { + return Platform.KernelSU.name + } - override fun enable(id: String, callback: IModuleOpsCallback) { + override fun enable(id: String, callback: IModuleOpsCallback?) { moduleOps( cmd = "ksud module enable $id", id = id, @@ -21,7 +19,7 @@ internal class KernelSUModuleManagerImpl( ) } - override fun disable(id: String, callback: IModuleOpsCallback) { + override fun disable(id: String, callback: IModuleOpsCallback?) { moduleOps( cmd = "ksud module disable $id", id = id, @@ -29,7 +27,7 @@ internal class KernelSUModuleManagerImpl( ) } - override fun remove(id: String, callback: IModuleOpsCallback) { + override fun remove(id: String, callback: IModuleOpsCallback?) { moduleOps( cmd = "ksud module uninstall $id", id = id, @@ -37,7 +35,7 @@ internal class KernelSUModuleManagerImpl( ) } - override fun install(path: String, callback: IInstallCallback) { + override fun install(path: String, callback: IInstallCallback?) { install( cmd = "ksud module install '${path}'", path = path, @@ -45,18 +43,17 @@ internal class KernelSUModuleManagerImpl( ) } - private fun moduleOps(cmd: String, id: String, callback: IModuleOpsCallback) { - managerScope.launch { - val moduleDir = File(modulesDir, id) - if (!moduleDir.exists()) { - return@launch callback.onFailure(id, null) - } - - cmd.submit().onSuccess { - callback.onSuccess(id) - }.onFailure { - callback.onFailure(id, it.message) - } + private fun moduleOps(cmd: String, id: String, callback: IModuleOpsCallback?) { + val moduleDir = File(modulesDir, id) + if (!moduleDir.exists()) { + callback?.onFailure(id, null) + return + } + + cmd.exec().onSuccess { + callback?.onSuccess(id) + }.onFailure { + callback?.onFailure(id, it.message) } } } \ No newline at end of file diff --git a/core/src/main/kotlin/dev/sanmer/mrepo/impl/MagiskModuleManagerImpl.kt b/core/src/main/kotlin/dev/sanmer/mrepo/impl/MagiskModuleManagerImpl.kt index 8a450578..df8ca5c7 100644 --- a/core/src/main/kotlin/dev/sanmer/mrepo/impl/MagiskModuleManagerImpl.kt +++ b/core/src/main/kotlin/dev/sanmer/mrepo/impl/MagiskModuleManagerImpl.kt @@ -3,16 +3,14 @@ package dev.sanmer.mrepo.impl import dev.sanmer.mrepo.Platform import dev.sanmer.mrepo.stub.IInstallCallback import dev.sanmer.mrepo.stub.IModuleOpsCallback -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch import java.io.File -internal class MagiskModuleManagerImpl( - private val managerScope: CoroutineScope -) : BaseModuleManagerImpl(managerScope) { - override fun getPlatform() = Platform.Magisk.name +internal class MagiskModuleManagerImpl : BaseModuleManagerImpl() { + override fun getPlatform(): String { + return Platform.Magisk.name + } - override fun enable(id: String, callback: IModuleOpsCallback) { + override fun enable(id: String, callback: IModuleOpsCallback?) { moduleOps( tags = listOf( Tag("remove", FileOp.Delete), @@ -23,7 +21,7 @@ internal class MagiskModuleManagerImpl( ) } - override fun disable(id: String, callback: IModuleOpsCallback) { + override fun disable(id: String, callback: IModuleOpsCallback?) { moduleOps( tags = listOf( Tag("remove", FileOp.Delete), @@ -34,7 +32,7 @@ internal class MagiskModuleManagerImpl( ) } - override fun remove(id: String, callback: IModuleOpsCallback) { + override fun remove(id: String, callback: IModuleOpsCallback?) { moduleOps( tags = listOf( Tag("disable", FileOp.Delete), @@ -45,7 +43,7 @@ internal class MagiskModuleManagerImpl( ) } - override fun install(path: String, callback: IInstallCallback) { + override fun install(path: String, callback: IInstallCallback?) { install( cmd = "magisk --install-module '${path}'", path = path, @@ -53,30 +51,25 @@ internal class MagiskModuleManagerImpl( ) } - private fun moduleOps( - tags: List, - id: String, - callback: IModuleOpsCallback - ) { - managerScope.launch { - val moduleDir = File(modulesDir, id) - if (!moduleDir.exists()) { - return@launch callback.onFailure(id, null) - } + private fun moduleOps(tags: List, id: String, callback: IModuleOpsCallback?) { + val moduleDir = File(modulesDir, id) + if (!moduleDir.exists()) { + callback?.onFailure(id, null) + return + } - runCatching { - tags.forEach { - val tag = File(moduleDir, it.name) - when (it.op) { - FileOp.Delete -> if (tag.exists()) tag.delete() - FileOp.Create -> tag.createNewFile() - } + runCatching { + tags.forEach { + val tag = File(moduleDir, it.name) + when (it.op) { + FileOp.Delete -> if (tag.exists()) tag.delete() + FileOp.Create -> tag.createNewFile() } - }.onSuccess { - callback.onSuccess(id) - }.onFailure { - callback.onFailure(id, it.message) } + }.onSuccess { + callback?.onSuccess(id) + }.onFailure { + callback?.onFailure(id, it.message) } } diff --git a/core/src/main/kotlin/dev/sanmer/mrepo/impl/PowerManagerImpl.kt b/core/src/main/kotlin/dev/sanmer/mrepo/impl/PowerManagerImpl.kt deleted file mode 100644 index 6aca5aba..00000000 --- a/core/src/main/kotlin/dev/sanmer/mrepo/impl/PowerManagerImpl.kt +++ /dev/null @@ -1,16 +0,0 @@ -package dev.sanmer.mrepo.impl - -import dev.sanmer.mrepo.impl.Shell.submit -import dev.sanmer.mrepo.stub.IPowerManager -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.launch - -internal class PowerManagerImpl( - private val managerScope: CoroutineScope -) : IPowerManager.Stub() { - override fun reboot() { - managerScope.launch { - "svc power reboot || reboot".submit() - } - } -} \ No newline at end of file diff --git a/core/src/main/kotlin/dev/sanmer/mrepo/impl/Shell.kt b/core/src/main/kotlin/dev/sanmer/mrepo/impl/Shell.kt index 0cc9dc21..3c451c2e 100644 --- a/core/src/main/kotlin/dev/sanmer/mrepo/impl/Shell.kt +++ b/core/src/main/kotlin/dev/sanmer/mrepo/impl/Shell.kt @@ -1,8 +1,6 @@ package dev.sanmer.mrepo.impl import android.util.Log -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext internal object Shell { private const val TAG = "Shell" @@ -27,45 +25,36 @@ internal object Shell { } } } catch (e: Throwable) { - Log.e(TAG, "exec<$this>", e) + Log.e(TAG, Log.getStackTraceString(e)) Result.failure(e) } - suspend fun String.submit() = - withContext(Dispatchers.IO) { exec() } - - suspend fun String.submit( + fun String.exec( stdout: (String) -> Unit, stderr: (String) -> Unit - ) = withContext(Dispatchers.IO) { - try { - Log.d(TAG, "submit: ${this@submit}") - val process = ProcessBuilder("sh", "-c", this@submit).start() - val output = process.inputStream.bufferedReader() - val error = process.errorStream.bufferedReader() - - withContext(Dispatchers.IO) { - output.forEachLine { - Log.d(TAG, "output: $it") - stdout(it) - } - } + ) = try { + Log.d(TAG, "submit: ${this@exec}") + val process = ProcessBuilder("sh", "-c", this@exec).start() + val output = process.inputStream.bufferedReader() + val error = process.errorStream.bufferedReader() + + output.forEachLine { + Log.d(TAG, "output: $it") + stdout(it) + } - withContext(Dispatchers.IO) { - error.forEachLine { - Log.d(TAG, "stderr: $it") - stderr(it) - } - } + error.forEachLine { + Log.d(TAG, "stderr: $it") + stderr(it) + } - when { - process.waitFor().ok() -> Result.success(true) - else -> Result.failure(RuntimeException()) - } - } catch (e: Throwable) { - Log.e(TAG, "submit<${this@submit}>", e) - Result.failure(e) + when { + process.waitFor().ok() -> Result.success(true) + else -> Result.failure(RuntimeException()) } + } catch (e: Throwable) { + Log.e(TAG, Log.getStackTraceString(e)) + Result.failure(e) } private fun Int.ok() = this == 0 From 41a26d87143bb7df5de031ddd83c8287862af068 Mon Sep 17 00:00:00 2001 From: SanmerDev Date: Mon, 10 Jun 2024 16:23:37 +0800 Subject: [PATCH 4/7] Bump compat to 5613b7c --- compat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compat b/compat index aaa6e65b..5613b7cc 160000 --- a/compat +++ b/compat @@ -1 +1 @@ -Subproject commit aaa6e65b1ec47ed8f3bb0a97eb4373a8a109c1e0 +Subproject commit 5613b7cc18b707fcc83a1e662f37daa16cbf6140 From d7f74aa9176c479afa306e728f73df7968b33855 Mon Sep 17 00:00:00 2001 From: SanmerDev Date: Mon, 10 Jun 2024 21:04:26 +0800 Subject: [PATCH 5/7] Bump compat to e581239 --- compat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compat b/compat index 5613b7cc..e5812397 160000 --- a/compat +++ b/compat @@ -1 +1 @@ -Subproject commit 5613b7cc18b707fcc83a1e662f37daa16cbf6140 +Subproject commit e58123973e86612200b8cd46bda3556794f5c190 From 7b5a5476cf75ce1ffa57edfbe0eaa6dc391de81a Mon Sep 17 00:00:00 2001 From: SanmerDev Date: Mon, 10 Jun 2024 21:45:47 +0800 Subject: [PATCH 6/7] Add `proguard-rules.pro` --- core/build.gradle.kts | 7 ++++--- core/proguard-rules.pro | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) create mode 100644 core/proguard-rules.pro diff --git a/core/build.gradle.kts b/core/build.gradle.kts index df438072..b94ed04d 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -6,10 +6,11 @@ plugins { android { namespace = "dev.sanmer.mrepo.core" + defaultConfig { + consumerProguardFile("proguard-rules.pro") + } + buildFeatures { aidl = true } -} - -dependencies { } \ No newline at end of file diff --git a/core/proguard-rules.pro b/core/proguard-rules.pro new file mode 100644 index 00000000..00470a9f --- /dev/null +++ b/core/proguard-rules.pro @@ -0,0 +1 @@ +-keepclassmembers class dev.sanmer.mrepo.ModuleManager { public (); } \ No newline at end of file From 50643139d9e3ed9e8e45e2b5df2afe86a7248d74 Mon Sep 17 00:00:00 2001 From: SanmerDev Date: Tue, 11 Jun 2024 12:54:28 +0800 Subject: [PATCH 7/7] Optimize `ModuleManager` --- .../kotlin/dev/sanmer/mrepo/ModuleManager.kt | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/core/src/main/kotlin/dev/sanmer/mrepo/ModuleManager.kt b/core/src/main/kotlin/dev/sanmer/mrepo/ModuleManager.kt index 15d248f6..a20e4a0c 100644 --- a/core/src/main/kotlin/dev/sanmer/mrepo/ModuleManager.kt +++ b/core/src/main/kotlin/dev/sanmer/mrepo/ModuleManager.kt @@ -10,23 +10,15 @@ import dev.sanmer.mrepo.stub.IModuleManager import dev.sanmer.mrepo.stub.IModuleOpsCallback class ModuleManager : IModuleManager.Stub() { - private val platform by lazy { + private val original by lazy { when { - "which magisk".exec().isSuccess -> Platform.Magisk - "which ksud".exec().isSuccess -> Platform.KernelSU - "which apd".exec().isSuccess -> Platform.APatch + "which magisk".exec().isSuccess -> MagiskModuleManagerImpl() + "which ksud".exec().isSuccess -> KernelSUModuleManagerImpl() + "which apd".exec().isSuccess -> APatchModuleManagerImpl() else -> throw IllegalArgumentException("Unsupported platform") } } - private val original by lazy { - when (platform) { - Platform.Magisk -> MagiskModuleManagerImpl() - Platform.KernelSU -> KernelSUModuleManagerImpl() - Platform.APatch -> APatchModuleManagerImpl() - } - } - override fun getVersion(): String = original.version override fun getVersionCode(): Int = original.versionCode override fun getPlatform(): String = original.platform