Skip to content

Commit

Permalink
[fix]: shizuku binding
Browse files Browse the repository at this point in the history
  • Loading branch information
F0x1d committed Jun 30, 2024
1 parent b8174d4 commit d4405a7
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 60 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ fun Context.hardRestartApp() {
exitProcess(0)
}

fun Context.toast(text: Int) = Toast.makeText(this, text, Toast.LENGTH_SHORT).show()
fun Context.toast(text: String) = Toast.makeText(this, text, Toast.LENGTH_SHORT).show()
fun Context.toast(text: Int) = toast(getString(text))

fun Context.shareIntent(text: String) = baseShareIntent {
it.putExtra(Intent.EXTRA_TEXT, text)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,21 @@ import com.f0x1d.logfox.IUserService
import com.f0x1d.logfox.model.terminal.TerminalResult
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.async
import kotlinx.coroutines.cancel
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import java.io.InputStream
import java.io.OutputStream
import kotlin.system.exitProcess

class UserService(): IUserService.Stub() {

private var serviceScope: CoroutineScope? = null
private val serviceScopeJob = SupervisorJob()
private val serviceScope = CoroutineScope(Dispatchers.IO + serviceScopeJob)

private var latestId = 0L
private val currentProcesses = HashMap<Long, Process>()
Expand All @@ -28,18 +33,14 @@ class UserService(): IUserService.Stub() {
@Keep
constructor(context: Context): this()

init {
serviceScope = CoroutineScope(Dispatchers.IO)
}

override fun destroy() {
serviceScope.cancel()
runBlocking { serviceScopeJob.join() }

currentProcesses.values.forEach { process ->
process.tryDestroy()
}

serviceScope?.cancel()
serviceScope = null

exitProcess(0)
}

Expand Down Expand Up @@ -85,7 +86,7 @@ class UserService(): IUserService.Stub() {
private fun pipeFrom(inputStream: InputStream): ParcelFileDescriptor {
val pipe = ParcelFileDescriptor.createPipe()

serviceScope?.launch(Dispatchers.IO) {
serviceScope.launch {
AutoCloseOutputStream(pipe[1]).use {
try {
inputStream.copyTo(it)
Expand All @@ -102,7 +103,7 @@ class UserService(): IUserService.Stub() {
val process = currentProcesses[processId] ?: return null

val pipe = ParcelFileDescriptor.createPipe()
serviceScope?.launch(Dispatchers.IO) {
serviceScope.launch {
AutoCloseInputStream(pipe[0]).use {
try {
it.copyTo(process.outputStream)
Expand All @@ -119,6 +120,24 @@ class UserService(): IUserService.Stub() {
currentProcesses.remove(processId)?.tryDestroy()
}

// Cancellable implementation
private suspend fun InputStream.copyTo(
out: OutputStream,
bufferSize: Int = DEFAULT_BUFFER_SIZE,
): Long = withContext(Dispatchers.IO) {
var bytesCopied: Long = 0
val buffer = ByteArray(bufferSize)
var bytes = read(buffer)

while (bytes >= 0 && isActive) {
out.write(buffer, 0, bytes)
bytesCopied += bytes
bytes = read(buffer)
}

return@withContext bytesCopied
}

private fun Process.tryDestroy() = runCatching {
destroy()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,12 @@ import com.f0x1d.logfox.arch.di.IODispatcher
import com.f0x1d.logfox.model.terminal.TerminalProcess
import com.f0x1d.logfox.model.terminal.TerminalResult
import com.f0x1d.logfox.service.UserService
import com.f0x1d.logfox.strings.Strings
import com.f0x1d.logfox.terminals.base.Terminal
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext
import rikka.shizuku.Shizuku
import java.io.InputStream
import java.io.OutputStream
import javax.inject.Inject
import javax.inject.Singleton
import kotlin.coroutines.Continuation
Expand All @@ -40,17 +39,19 @@ class ShizukuTerminal @Inject constructor(
override val title = R.string.shizuku

private var userService: IUserService? = null
private var serviceConnection: ServiceConnection? = null

private val userServiceArgs = Shizuku.UserServiceArgs(ComponentName(context, UserService::class.java))
.daemon(false)
.processNameSuffix("service")
.debuggable(BuildConfig.DEBUG)
.version(
context.run {
packageManager.getPackageInfo(packageName, 0).versionCode
}
)

private val userServiceArgs by lazy {
Shizuku.UserServiceArgs(ComponentName(context, UserService::class.java))
.daemon(false)
.processNameSuffix("service")
.debuggable(BuildConfig.DEBUG)
.version(
context.run {
packageManager.getPackageInfo(packageName, 0).versionCode
}
)
.tag(context.getString(Strings.app_name))
}

override suspend fun isSupported() = suspendCoroutine {
when {
Expand Down Expand Up @@ -88,7 +89,7 @@ class ShizukuTerminal @Inject constructor(
}

var resumed = false
serviceConnection = object : ServiceConnection {
object : ServiceConnection {
override fun onServiceConnected(componentName: ComponentName?, binder: IBinder?) {
if (binder == null || !binder.pingBinder()) {
if (resumed.not()) {
Expand Down Expand Up @@ -119,36 +120,22 @@ class ShizukuTerminal @Inject constructor(
}

override fun execute(vararg command: String) = userService?.run {
runCatching {
val processId = execute(command.joinToString(" "))

TerminalProcess(
output = AutoCloseInputStream(processOutput(processId) ?: return@run null),
error = AutoCloseInputStream(processError(processId) ?: return@run null),
input = AutoCloseOutputStream(processInput(processId) ?: return@run null),
) {
destroyProcess(processId)
}
}.getOrElse {
val emptyInputStream = object : InputStream() {
override fun read(): Int = 0
}
val stubOutputStream = object : OutputStream() {
override fun write(data: Int) = Unit
}

TerminalProcess(
output = emptyInputStream,
error = emptyInputStream,
input = stubOutputStream,
destroy = { },
)
val processId = execute(command.joinToString(" "))

TerminalProcess(
output = AutoCloseInputStream(processOutput(processId) ?: return@run null),
error = AutoCloseInputStream(processError(processId) ?: return@run null),
input = AutoCloseOutputStream(processInput(processId) ?: return@run null),
) {
destroyProcess(processId)
}
}

override suspend fun exit() {
runCatching {
Shizuku.unbindUserService(userServiceArgs, serviceConnection, true)
}
// With true flag it does not remove connection from cache
Shizuku.unbindUserService(userServiceArgs, null, false)
Shizuku.unbindUserService(userServiceArgs, null, true)

userService = null
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import androidx.core.app.NotificationCompat
import androidx.core.app.ServiceCompat
import androidx.lifecycle.LifecycleService
import androidx.lifecycle.lifecycleScope
import com.f0x1d.logfox.arch.di.MainDispatcher
import com.f0x1d.logfox.context.LOGGING_STATUS_CHANNEL_ID
import com.f0x1d.logfox.context.activityManager
import com.f0x1d.logfox.context.toast
Expand All @@ -28,7 +27,6 @@ import com.f0x1d.logfox.terminals.DefaultTerminal
import com.f0x1d.logfox.terminals.base.Terminal
import com.f0x1d.logfox.ui.Icons
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Job
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.cancelAndJoin
Expand Down Expand Up @@ -77,10 +75,6 @@ class LoggingService : LifecycleService() {
@Inject
lateinit var terminals: Array<Terminal>

@MainDispatcher
@Inject
lateinit var mainDispatcher: CoroutineDispatcher

private val logs = LinkedList<LogLine>()
private val logsMutex = Mutex()
private var loggingJob: Job? = null
Expand Down Expand Up @@ -129,7 +123,7 @@ class LoggingService : LifecycleService() {
startingId = idsCounter,
).catch { throwable ->
if (throwable is TerminalNotSupportedException) {
if (appPreferences.fallbackToDefaultTerminal) withContext(mainDispatcher) {
if (appPreferences.fallbackToDefaultTerminal) {
toast(Strings.terminal_unavailable_falling_back)

loggingTerminal.exit()
Expand All @@ -138,6 +132,7 @@ class LoggingService : LifecycleService() {
delay(10000) // waiting for 10sec before new attempt
}
} else {
toast(getString(Strings.error, throwable.localizedMessage))
throwable.printStackTrace()
}
}.collect { logLine ->
Expand Down Expand Up @@ -198,7 +193,6 @@ class LoggingService : LifecycleService() {

private fun killApp() = lifecycleScope.launch {
loggingJob?.cancelAndJoin()
clearLogs().join()

activityManager.appTasks.forEach {
it.finishAndRemoveTask()
Expand Down

0 comments on commit d4405a7

Please sign in to comment.