Add foreground keep alive option

This commit is contained in:
Nullptr 2022-11-09 13:09:50 +08:00
parent 54cf7eed35
commit c191397dc0
No known key found for this signature in database
8 changed files with 76 additions and 5 deletions

View File

@ -7,6 +7,7 @@
android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="QueryAllPackagesPermission" />
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<application
android:name=".LSPApplication"

View File

@ -2,12 +2,14 @@ package org.lsposed.lspatch
import android.app.Application
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.lsposed.hiddenapibypass.HiddenApiBypass
import org.lsposed.lspatch.manager.AppBroadcastReceiver
import org.lsposed.lspatch.manager.ModuleService
import org.lsposed.lspatch.util.LSPPackageManager
import org.lsposed.lspatch.util.ShizukuApi
import java.io.File
@ -23,13 +25,14 @@ class LSPApplication : Application() {
override fun onCreate() {
super.onCreate()
HiddenApiBypass.addHiddenApiExemptions("");
HiddenApiBypass.addHiddenApiExemptions("")
lspApp = this
filesDir.mkdir()
tmpApkDir = cacheDir.resolve("apk").also { it.mkdir() }
prefs = lspApp.getSharedPreferences("settings", Context.MODE_PRIVATE)
ShizukuApi.init()
AppBroadcastReceiver.register(this)
startService(Intent(this, ModuleService::class.java))
globalScope.launch { LSPPackageManager.fetchAppList() }
}
}

View File

@ -1,6 +1,8 @@
package org.lsposed.lspatch.config
import android.content.Intent
import org.lsposed.lspatch.lspApp
import org.lsposed.lspatch.manager.ModuleService
import org.lsposed.lspatch.ui.util.delegateStateOf
import org.lsposed.lspatch.ui.util.getValue
import org.lsposed.lspatch.ui.util.setValue
@ -12,6 +14,11 @@ object Configs {
private const val PREFS_KEYSTORE_ALIAS_PASSWORD = "keystore_alias_password"
private const val PREFS_STORAGE_DIRECTORY = "storage_directory"
private const val PREFS_DETAIL_PATCH_LOGS = "detail_patch_logs"
private const val PREFS_KEEP_ALIVE = "keep_alive"
enum class KeepAlive {
OFF, FOREGROUND
}
var keyStorePassword by delegateStateOf(lspApp.prefs.getString(PREFS_KEYSTORE_PASSWORD, "123456")!!) {
lspApp.prefs.edit().putString(PREFS_KEYSTORE_PASSWORD, it).apply()
@ -32,4 +39,9 @@ object Configs {
var detailPatchLogs by delegateStateOf(lspApp.prefs.getBoolean(PREFS_DETAIL_PATCH_LOGS, true)) {
lspApp.prefs.edit().putBoolean(PREFS_DETAIL_PATCH_LOGS, it).apply()
}
var keepAlive by delegateStateOf(KeepAlive.values()[lspApp.prefs.getInt(PREFS_KEEP_ALIVE, KeepAlive.OFF.ordinal)]) {
lspApp.prefs.edit().putInt(PREFS_KEEP_ALIVE, it.ordinal).apply()
lspApp.startService(Intent(lspApp, ModuleService::class.java))
}
}

View File

@ -1,9 +1,15 @@
package org.lsposed.lspatch.manager
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.Service
import android.content.Intent
import android.os.IBinder
import android.util.Log
import androidx.core.app.NotificationCompat
import org.lsposed.lspatch.config.Configs
import org.lsposed.lspatch.share.Constants
class ModuleService : Service() {
@ -11,6 +17,19 @@ class ModuleService : Service() {
private const val TAG = "ModuleService"
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
intent ?: return START_NOT_STICKY
if (Configs.keepAlive == Configs.KeepAlive.FOREGROUND) {
val channel = NotificationChannel(Constants.MANAGER_PACKAGE_NAME, TAG, NotificationManager.IMPORTANCE_DEFAULT)
val manager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
manager.createNotificationChannel(channel)
startForeground(1, NotificationCompat.Builder(this, Constants.MANAGER_PACKAGE_NAME).build())
} else {
stopForeground(STOP_FOREGROUND_REMOVE)
}
return super.onStartCommand(intent, flags, startId)
}
override fun onBind(intent: Intent): IBinder? {
val packageName = intent.getStringExtra("packageName") ?: return null
// TODO: Authentication

View File

@ -12,6 +12,8 @@ import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Ballot
import androidx.compose.material.icons.outlined.BugReport
import androidx.compose.material.icons.outlined.HourglassEmpty
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
@ -49,6 +51,7 @@ fun SettingsScreen() {
) {
KeyStore()
DetailPatchLogs()
KeepAlive()
}
}
}
@ -235,6 +238,37 @@ private fun DetailPatchLogs() {
SettingsSwitch(
modifier = Modifier.clickable { Configs.detailPatchLogs = !Configs.detailPatchLogs },
checked = Configs.detailPatchLogs,
icon = Icons.Outlined.BugReport,
title = stringResource(R.string.settings_detail_patch_logs)
)
}
@Composable
private fun KeepAlive() {
var expanded by remember { mutableStateOf(false) }
AnywhereDropdown(
expanded = expanded,
onDismissRequest = { expanded = false },
onClick = { expanded = true },
surface = {
val (title, desc) = when (Configs.keepAlive) {
Configs.KeepAlive.OFF -> R.string.settings_keep_alive to R.string.off
Configs.KeepAlive.FOREGROUND -> R.string.settings_keep_alive_foreground to R.string.settings_keep_alive_foreground_desc
}
SettingsItem(
icon = Icons.Outlined.HourglassEmpty,
title = stringResource(title),
desc = stringResource(desc)
)
}
) {
DropdownMenuItem(
text = { Text(stringResource(R.string.off)) },
onClick = { Configs.keepAlive = Configs.KeepAlive.OFF }
)
DropdownMenuItem(
text = { Text(stringResource(R.string.settings_keep_alive_foreground)) },
onClick = { Configs.keepAlive = Configs.KeepAlive.FOREGROUND }
)
}
}

View File

@ -83,8 +83,7 @@ object LSPPackageManager {
var message: String? = null
withContext(Dispatchers.IO) {
runCatching {
val params = PackageInstaller.SessionParams::class.java.getConstructor(Int::class.javaPrimitiveType)
.newInstance(PackageInstaller.SessionParams.MODE_FULL_INSTALL)
val params = PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL)
var flags = HiddenApiBridge.PackageInstaller_SessionParams_installFlags(params)
flags = flags or 0x00000004 /* PackageManager.INSTALL_ALLOW_TEST */ or 0x00000002 /* PackageManager.INSTALL_REPLACE_EXISTING */
HiddenApiBridge.PackageInstaller_SessionParams_installFlags(params, flags)

View File

@ -11,6 +11,7 @@
<string name="shizuku_unavailable">Shizuku service not connected</string>
<string name="screen_repo">Repo</string>
<string name="screen_logs">Logs</string>
<string name="off">Off</string>
<!-- Home Screen -->
<string name="home_shizuku_warning">Some functions unavailable</string>
@ -87,4 +88,7 @@
<string name="settings_keystore_wrong_alias">Wrong alias name</string>
<string name="settings_keystore_wrong_alias_password">Wrong alias password</string>
<string name="settings_detail_patch_logs">Detail patch logs</string>
<string name="settings_keep_alive">Keep alive</string>
<string name="settings_keep_alive_foreground">Foreground</string>
<string name="settings_keep_alive_foreground_desc">Create a notification to keep manager alive</string>
</resources>

View File

@ -60,7 +60,6 @@ public class RemoteApplicationService implements ILSPApplicationService {
}
};
Log.i(TAG, "Request manager binder");
context.startService(intent);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
context.bindService(intent, Context.BIND_AUTO_CREATE, Executors.newSingleThreadExecutor(), conn);
} else {
@ -74,7 +73,7 @@ public class RemoteApplicationService implements ILSPApplicationService {
var userHandle = (UserHandle) getUserMethod.invoke(context);
bindServiceAsUserMethod.invoke(context, intent, conn, Context.BIND_AUTO_CREATE, handler, userHandle);
}
boolean success = latch.await(5, TimeUnit.SECONDS);
boolean success = latch.await(1, TimeUnit.SECONDS);
if (!success) throw new TimeoutException("Bind service timeout");
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | InterruptedException | TimeoutException e) {
Toast.makeText(context, "Manager died", Toast.LENGTH_SHORT).show();