Fix extension installation with shizuku (#3630)
* Fix extension installation with shizuku * Changelog * Move changelog to end
This commit is contained in:
@@ -29,6 +29,7 @@ The format is a modified version of [Keep a Changelog](https://keepachangelog.co
|
|||||||
### Fixed
|
### Fixed
|
||||||
- Fixed app bars remaining visible after changing pages by tapping in the paged reader after using the chapter navigator slider ([@AntsyLich](https://github.com/AntsyLich)) ([#3567](https://github.com/mihonapp/mihon/pull/3567))
|
- Fixed app bars remaining visible after changing pages by tapping in the paged reader after using the chapter navigator slider ([@AntsyLich](https://github.com/AntsyLich)) ([#3567](https://github.com/mihonapp/mihon/pull/3567))
|
||||||
- Fixed MangaBaka User Agent string ([@MajorTanya](https://github.com/MajorTanya)) ([#3578](https://github.com/mihonapp/mihon/pull/3578))
|
- Fixed MangaBaka User Agent string ([@MajorTanya](https://github.com/MajorTanya)) ([#3578](https://github.com/mihonapp/mihon/pull/3578))
|
||||||
|
- Fixed extension installation with shizuku installer ([@NGB-Was-Taken](https://github.com/NGB-Was-Taken)) ([#3630](https://github.com/mihonapp/mihon/pull/3630))
|
||||||
|
|
||||||
## [v0.20.1] - 2026-07-09
|
## [v0.20.1] - 2026-07-09
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package mihon.app.shizuku;
|
package mihon.app.shizuku;
|
||||||
|
|
||||||
interface IShellInterface {
|
interface IShellInterface {
|
||||||
void install(in AssetFileDescriptor apk) = 1;
|
void install(in AssetFileDescriptor apk, in IntentSender intentSender) = 1;
|
||||||
|
|
||||||
void destroy() = 16777114;
|
void destroy() = 16777114;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package eu.kanade.tachiyomi.extension.installer
|
package eu.kanade.tachiyomi.extension.installer
|
||||||
|
|
||||||
|
import android.app.PendingIntent
|
||||||
import android.app.Service
|
import android.app.Service
|
||||||
import android.content.BroadcastReceiver
|
import android.content.BroadcastReceiver
|
||||||
import android.content.ComponentName
|
import android.content.ComponentName
|
||||||
@@ -39,6 +40,7 @@ class ShizukuInstaller(private val service: Service) : Installer(service) {
|
|||||||
.processNameSuffix("shizuku_service")
|
.processNameSuffix("shizuku_service")
|
||||||
.debuggable(BuildConfig.DEBUG)
|
.debuggable(BuildConfig.DEBUG)
|
||||||
.daemon(false)
|
.daemon(false)
|
||||||
|
.version(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val connection = object : ServiceConnection {
|
private val connection = object : ServiceConnection {
|
||||||
@@ -53,6 +55,13 @@ class ShizukuInstaller(private val service: Service) : Installer(service) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val statusIntent = PendingIntent.getBroadcast(
|
||||||
|
service.applicationContext,
|
||||||
|
0,
|
||||||
|
Intent(ACTION_INSTALL_RESULT).setPackage(BuildConfig.APPLICATION_ID),
|
||||||
|
PendingIntent.FLAG_MUTABLE,
|
||||||
|
)
|
||||||
|
|
||||||
private val receiver = object : BroadcastReceiver() {
|
private val receiver = object : BroadcastReceiver() {
|
||||||
override fun onReceive(context: Context, intent: Intent) {
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
val status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, Int.MIN_VALUE)
|
val status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, Int.MIN_VALUE)
|
||||||
@@ -110,7 +119,7 @@ class ShizukuInstaller(private val service: Service) : Installer(service) {
|
|||||||
super.processEntry(entry)
|
super.processEntry(entry)
|
||||||
try {
|
try {
|
||||||
service.contentResolver.openAssetFileDescriptor(entry.uri, "r").use {
|
service.contentResolver.openAssetFileDescriptor(entry.uri, "r").use {
|
||||||
shellInterface?.install(it)
|
shellInterface?.install(it, statusIntent.intentSender)
|
||||||
}
|
}
|
||||||
service.contentResolver.delete(entry.uri, null, null)
|
service.contentResolver.delete(entry.uri, null, null)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
|||||||
@@ -40,9 +40,6 @@
|
|||||||
package mihon.app.shizuku
|
package mihon.app.shizuku
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.PendingIntent
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import android.content.IntentSender
|
import android.content.IntentSender
|
||||||
import android.content.pm.PackageInstaller
|
import android.content.pm.PackageInstaller
|
||||||
import android.content.res.AssetFileDescriptor
|
import android.content.res.AssetFileDescriptor
|
||||||
@@ -51,21 +48,18 @@ import android.os.IBinder
|
|||||||
import android.os.ParcelFileDescriptor
|
import android.os.ParcelFileDescriptor
|
||||||
import android.os.UserHandle
|
import android.os.UserHandle
|
||||||
import eu.kanade.tachiyomi.BuildConfig
|
import eu.kanade.tachiyomi.BuildConfig
|
||||||
import eu.kanade.tachiyomi.extension.installer.ACTION_INSTALL_RESULT
|
|
||||||
import rikka.shizuku.SystemServiceHelper
|
import rikka.shizuku.SystemServiceHelper
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
import kotlin.system.exitProcess
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
class ShellInterface : IShellInterface.Stub() {
|
class ShellInterface : IShellInterface.Stub() {
|
||||||
|
|
||||||
private val context = createContext()
|
|
||||||
private val userId = UserHandle::class.java
|
private val userId = UserHandle::class.java
|
||||||
.getMethod("myUserId")
|
.getMethod("myUserId")
|
||||||
.invoke(null) as Int
|
.invoke(null) as Int
|
||||||
private val packageName = BuildConfig.APPLICATION_ID
|
private val packageName = BuildConfig.APPLICATION_ID
|
||||||
|
|
||||||
@SuppressLint("PrivateApi")
|
@SuppressLint("PrivateApi")
|
||||||
override fun install(apk: AssetFileDescriptor) {
|
override fun install(apk: AssetFileDescriptor, intentSender: IntentSender) {
|
||||||
val pmInterface = Class.forName($$"android.content.pm.IPackageManager$Stub")
|
val pmInterface = Class.forName($$"android.content.pm.IPackageManager$Stub")
|
||||||
.getMethod("asInterface", IBinder::class.java)
|
.getMethod("asInterface", IBinder::class.java)
|
||||||
.invoke(null, SystemServiceHelper.getSystemService("package"))
|
.invoke(null, SystemServiceHelper.getSystemService("package"))
|
||||||
@@ -136,50 +130,18 @@ class ShellInterface : IShellInterface.Stub() {
|
|||||||
apk.createInputStream().use { input -> input.copyTo(output) }
|
apk.createInputStream().use { input -> input.copyTo(output) }
|
||||||
}
|
}
|
||||||
|
|
||||||
val statusIntent = PendingIntent.getBroadcast(
|
|
||||||
context,
|
|
||||||
0,
|
|
||||||
Intent(ACTION_INSTALL_RESULT).setPackage(packageName),
|
|
||||||
PendingIntent.FLAG_MUTABLE,
|
|
||||||
)
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O) {
|
||||||
session::class.java.getMethod("commit", IntentSender::class.java, Boolean::class.java)
|
session::class.java.getMethod("commit", IntentSender::class.java, Boolean::class.java)
|
||||||
.invoke(session, statusIntent.intentSender, false)
|
.invoke(session, intentSender, false)
|
||||||
} else {
|
} else {
|
||||||
session::class.java.getMethod("commit", IntentSender::class.java)
|
session::class.java.getMethod("commit", IntentSender::class.java)
|
||||||
.invoke(session, statusIntent.intentSender)
|
.invoke(session, intentSender)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun destroy() {
|
override fun destroy() {
|
||||||
exitProcess(0)
|
exitProcess(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("PrivateApi")
|
|
||||||
private fun createContext(): Context {
|
|
||||||
val activityThread = Class.forName("android.app.ActivityThread")
|
|
||||||
val systemMain = activityThread.getMethod("systemMain").invoke(null)
|
|
||||||
val systemContext = activityThread.getMethod("getSystemContext").invoke(systemMain) as Context
|
|
||||||
|
|
||||||
val shellUserHandle = UserHandle::class.java
|
|
||||||
.getConstructor(Int::class.java)
|
|
||||||
.newInstance(userId)
|
|
||||||
|
|
||||||
val shellContext = systemContext::class.java.getMethod(
|
|
||||||
"createPackageContextAsUser",
|
|
||||||
String::class.java,
|
|
||||||
Int::class.java,
|
|
||||||
UserHandle::class.java,
|
|
||||||
).invoke(
|
|
||||||
systemContext,
|
|
||||||
"com.android.shell",
|
|
||||||
Context.CONTEXT_INCLUDE_CODE or Context.CONTEXT_IGNORE_SECURITY,
|
|
||||||
shellUserHandle,
|
|
||||||
) as Context
|
|
||||||
|
|
||||||
return shellContext.createPackageContext("com.android.shell", 0)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Constant hidden from the SDK
|
// Constant hidden from the SDK
|
||||||
|
|||||||
Reference in New Issue
Block a user