Skip to content

Commit

Permalink
Add support for Shizuku on Android 14 QPR3+ (#45)
Browse files Browse the repository at this point in the history
  • Loading branch information
karasevm committed Jan 17, 2025
1 parent b39d7e3 commit efd48b8
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 69 deletions.
102 changes: 33 additions & 69 deletions app/src/main/java/ru/karasevm/privatednstoggle/ui/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,11 @@ import android.content.ClipDescription.MIMETYPE_TEXT_PLAIN
import android.content.ClipboardManager
import android.content.Intent
import android.content.SharedPreferences
import android.content.pm.IPackageManager
import android.content.pm.PackageManager
import android.graphics.Color
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.permission.IPermissionManager
import android.util.Log
import android.view.Menu
import android.view.View
Expand All @@ -30,11 +28,8 @@ import androidx.recyclerview.widget.RecyclerView
import kotlinx.coroutines.launch
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import org.lsposed.hiddenapibypass.HiddenApiBypass
import rikka.shizuku.Shizuku
import rikka.shizuku.ShizukuBinderWrapper
import rikka.shizuku.ShizukuProvider
import rikka.shizuku.SystemServiceHelper
import ru.karasevm.privatednstoggle.PrivateDNSApp
import ru.karasevm.privatednstoggle.R
import ru.karasevm.privatednstoggle.data.DnsServerViewModel
Expand All @@ -44,14 +39,14 @@ import ru.karasevm.privatednstoggle.model.DnsServer
import ru.karasevm.privatednstoggle.util.BackupUtils
import ru.karasevm.privatednstoggle.util.PreferenceHelper
import ru.karasevm.privatednstoggle.util.PreferenceHelper.dns_servers
import ru.karasevm.privatednstoggle.util.ShizukuUtil.grantPermissionWithShizuku


class MainActivity : AppCompatActivity(), AddServerDialogFragment.NoticeDialogListener,
DeleteServerDialogFragment.NoticeDialogListener, Shizuku.OnRequestPermissionResultListener {

private lateinit var linearLayoutManager: LinearLayoutManager
private lateinit var binding: ActivityMainBinding
private var items = mutableListOf<String>()
private lateinit var sharedPrefs: SharedPreferences
private lateinit var adapter: ServerListRecyclerAdapter
private lateinit var clipboard: ClipboardManager
Expand Down Expand Up @@ -150,18 +145,20 @@ class MainActivity : AppCompatActivity(), AddServerDialogFragment.NoticeDialogLi
* Migrate the SharedPreferences server list to Room
*/
private fun migrateServerList() {
if (sharedPrefs.dns_servers.isNotEmpty() && sharedPrefs.dns_servers[0] != "") {
Log.i(
"migrate",
"existing sharedPrefs list: ${sharedPrefs.dns_servers} ${sharedPrefs.dns_servers.size}"
)
sharedPrefs.dns_servers.forEach { server ->
val parts = server.split(" : ").toMutableList()
if (parts.size != 2) parts.add(0, "")
Log.i("migrate", "migrating: $server -> $parts")
dnsServerViewModel.insert(DnsServer(0, parts[1], parts[0]))
dnsServerViewModel.viewModelScope.launch {
if (sharedPrefs.dns_servers.isNotEmpty() && sharedPrefs.dns_servers[0] != "") {
Log.i(
"migrate",
"existing sharedPrefs list: ${sharedPrefs.dns_servers} ${sharedPrefs.dns_servers.size}"
)
sharedPrefs.dns_servers.forEach { server ->
val parts = server.split(" : ").toMutableList()
if (parts.size != 2) parts.add(0, "")
Log.i("migrate", "migrating: $server -> $parts")
dnsServerViewModel.insert(DnsServer(0, parts[1], parts[0]))
}
sharedPrefs.dns_servers = emptyList<String>().toMutableList()
}
sharedPrefs.dns_servers = emptyList<String>().toMutableList()
}
}

Expand All @@ -182,11 +179,6 @@ class MainActivity : AppCompatActivity(), AddServerDialogFragment.NoticeDialogLi

migrateServerList()

items = sharedPrefs.dns_servers
if (items[0] == "") {
items.removeAt(0)
}

adapter = ServerListRecyclerAdapter(true)
binding.recyclerView.adapter = adapter

Expand Down Expand Up @@ -363,14 +355,17 @@ class MainActivity : AppCompatActivity(), AddServerDialogFragment.NoticeDialogLi
Shizuku.requestPermission(1)
}
} else {
grantPermissionWithShizuku()
grantPermission()
}
} else {
if (checkSelfPermission(Manifest.permission.WRITE_SECURE_SETTINGS) != PackageManager.PERMISSION_GRANTED) {
val browserIntent = Intent(
Intent.ACTION_VIEW,
Uri.parse("https://karasevm.github.io/PrivateDNSAndroid/")
)
Toast.makeText(
this, R.string.shizuku_failure_toast, Toast.LENGTH_SHORT
).show()
startActivity(browserIntent)
finish()
}
Expand Down Expand Up @@ -454,58 +449,27 @@ class MainActivity : AppCompatActivity(), AddServerDialogFragment.NoticeDialogLi
dnsServerViewModel.update(id, server, label, null, enabled)
}

/**
* Attempts to grant WRITE_SECURE_SETTINGS permission with Shizuku
*/
private fun grantPermissionWithShizuku() {
val packageName = applicationContext.packageName
runCatching {
if (Build.VERSION.SDK_INT >= 31) {
HiddenApiBypass.addHiddenApiExemptions("Landroid/permission")
val binder =
ShizukuBinderWrapper(SystemServiceHelper.getSystemService("permissionmgr"))
val pm = IPermissionManager.Stub.asInterface(binder)
runCatching {
pm.grantRuntimePermission(
packageName, Manifest.permission.WRITE_SECURE_SETTINGS, 0
)
}.onFailure { _ ->
if (Build.VERSION.SDK_INT >= 34) {
pm.grantRuntimePermission(
packageName,
Manifest.permission.WRITE_SECURE_SETTINGS,
applicationContext.deviceId,
0
)
}
}
} else {
val binder = ShizukuBinderWrapper(SystemServiceHelper.getSystemService("package"))
val pm = IPackageManager.Stub.asInterface(binder)
pm.grantRuntimePermission(
packageName, Manifest.permission.WRITE_SECURE_SETTINGS, 0
)
}
}.onFailure { e ->
Log.e("SHIZUKU", "onRequestPermissionResult: ", e)
}.also {
if (checkSelfPermission(Manifest.permission.WRITE_SECURE_SETTINGS) != PackageManager.PERMISSION_GRANTED) {
val browserIntent = Intent(
Intent.ACTION_VIEW, Uri.parse("https://karasevm.github.io/PrivateDNSAndroid/")
)
startActivity(browserIntent)
finish()
}
private fun grantPermission() {
if (grantPermissionWithShizuku(this)) {
Toast.makeText(
this, R.string.shizuku_success_toast, Toast.LENGTH_SHORT
).show()
} else {
Toast.makeText(
this, R.string.shizuku_failure_toast, Toast.LENGTH_SHORT
).show()
val browserIntent = Intent(
Intent.ACTION_VIEW, Uri.parse("https://karasevm.github.io/PrivateDNSAndroid/")
)
startActivity(browserIntent)
finish()
}

}

override fun onRequestPermissionResult(requestCode: Int, grantResult: Int) {
val isGranted = grantResult == PackageManager.PERMISSION_GRANTED

if (isGranted) {
grantPermissionWithShizuku()
} else if (checkSelfPermission(Manifest.permission.WRITE_SECURE_SETTINGS) != PackageManager.PERMISSION_GRANTED) {
if (!isGranted && checkSelfPermission(Manifest.permission.WRITE_SECURE_SETTINGS) != PackageManager.PERMISSION_GRANTED) {
val browserIntent = Intent(
Intent.ACTION_VIEW, Uri.parse("https://karasevm.github.io/PrivateDNSAndroid/")
)
Expand Down
82 changes: 82 additions & 0 deletions app/src/main/java/ru/karasevm/privatednstoggle/util/ShizukuUtil.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package ru.karasevm.privatednstoggle.util

import android.Manifest
import android.content.Context
import android.content.pm.IPackageManager
import android.os.Build
import android.os.Process
import android.os.UserHandle
import android.permission.IPermissionManager
import android.util.Log
import org.lsposed.hiddenapibypass.HiddenApiBypass
import rikka.shizuku.ShizukuBinderWrapper
import rikka.shizuku.SystemServiceHelper
import ru.karasevm.privatednstoggle.util.PrivateDNSUtils.checkForPermission

object ShizukuUtil {

private const val TAG = "ShizukuUtil"

/**
* Attempts to grant the WRITE_SECURE_SETTINGS permission using Shizuku.
*
* @param context The context from which the method is called.
* @return True if the permission was granted successfully, false otherwise.
*/
fun grantPermissionWithShizuku(context: Context): Boolean {
val packageName = context.packageName
var userId = 0
runCatching {
val userHandle = Process.myUserHandle()
userId = UserHandle::class.java.getMethod("getIdentifier").invoke(userHandle) as? Int ?: 0
}
if (Build.VERSION.SDK_INT >= 31) {
HiddenApiBypass.addHiddenApiExemptions("Landroid/permission")
val binder =
ShizukuBinderWrapper(SystemServiceHelper.getSystemService("permissionmgr"))
val pm = IPermissionManager.Stub.asInterface(binder)
runCatching {
pm.grantRuntimePermission(
packageName,
Manifest.permission.WRITE_SECURE_SETTINGS,
userId
)
}.onFailure { e ->
Log.w(TAG, "Android 12 method failed: ", e)
runCatching {
pm.grantRuntimePermission(
packageName,
Manifest.permission.WRITE_SECURE_SETTINGS,
0,
userId
)
}.onFailure { e ->
Log.w(TAG, "Android 14 QPR2 method failed: ", e)
runCatching {
pm.grantRuntimePermission(
packageName,
Manifest.permission.WRITE_SECURE_SETTINGS,
"default:0",
userId
)
}.onFailure { e ->
Log.w(TAG, "Android 14 QPR3 method failed: ", e)
}
}
}
} else {
val binder = ShizukuBinderWrapper(SystemServiceHelper.getSystemService("package"))
val pm = IPackageManager.Stub.asInterface(binder)
runCatching {
pm.grantRuntimePermission(
packageName,
Manifest.permission.WRITE_SECURE_SETTINGS,
userId
)
}.onFailure { e ->
Log.w(TAG, "Android <12 method failed: ", e)
}
}
return checkForPermission(context)
}
}
2 changes: 2 additions & 0 deletions app/src/main/res/values-ru/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,6 @@
<string name="no_servers_added">Нет доступных серверов</string>
<string name="empty_hint">Нажмите на кнопку ниже, чтобы добавить сервер</string>
<string name="add_server_enabled">Включён</string>
<string name="shizuku_success_toast">Разрешение получено, можно отозвать авторизацию Shizuku</string>
<string name="shizuku_failure_toast">Не удалось получить разрешение, предоставьте его вручную</string>
</resources>
2 changes: 2 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,6 @@
<string name="no_servers_added">No Servers Added</string>
<string name="empty_hint">Tap on the button below to add one</string>
<string name="add_server_enabled">Enabled</string>
<string name="shizuku_success_toast">Permission granted, you can revoke the Shizuku permission now</string>
<string name="shizuku_failure_toast">Failed to acquire permission, please grant it manually</string>
</resources>

0 comments on commit efd48b8

Please sign in to comment.