From f019060e03182102053c4849037e33f141185f0e Mon Sep 17 00:00:00 2001
From: PiX <69745008+pixincreate@users.noreply.github.com>
Date: Thu, 21 Mar 2024 00:59:24 +0530
Subject: [PATCH] feat(service-auto-start): add auto-start support for rootless
devices
This feature is tested working on GrapheneOS Android 14, Bluejay
---
README.md | 19 +++
manager/src/main/AndroidManifest.xml | 10 ++
.../moe/shizuku/manager/ShizukuSettings.java | 8 +-
.../shizuku/manager/adb/AdbWirelessHelper.kt | 46 ++++++
.../shizuku/manager/home/AdbDialogFragment.kt | 18 +--
.../home/StartWirelessAdbViewHolder.kt | 11 +-
.../manager/receiver/BootCompleteReceiver.kt | 40 ++++-
.../manager/receiver/ShizukuReceiver.kt | 14 ++
.../manager/settings/SettingsFragment.kt | 26 +++-
.../manager/starter/SelfStarterService.kt | 147 ++++++++++++++++++
manager/src/main/res/values/strings.xml | 7 +-
manager/src/main/res/xml/settings.xml | 5 +
12 files changed, 316 insertions(+), 35 deletions(-)
create mode 100644 manager/src/main/java/moe/shizuku/manager/adb/AdbWirelessHelper.kt
create mode 100644 manager/src/main/java/moe/shizuku/manager/starter/SelfStarterService.kt
diff --git a/README.md b/README.md
index 2fd7d3300..80f3edf29 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,24 @@
# Shizuku
+## DISCLAIMER
+
+THIS IS A **FORK** OF SHIZUKU. IF YOU'RE LOOKING FOR SHIZUKU FROM RIKKA, THIS IS NOT THE PLACE.
+VISIT THE OFFICIAL REPO [**_HERE_**](https://github.com/RikkaApps/Shizuku)
+
+THIS FORK SOLELY EXISTS FOR PERSONAL USE CASE.
+
+### Usage of auto-start
+
+- Follow the instructions for setting up Shizuku through Wireless ADB by pairing the app
+ - From the `Settings`, enable `Start on boot (wireless ADB)`
+ - `WRITE_SECURE_SETTINGS` permission needs to be granted prior to enabling this setting and this can be enabled either by `rish` or by connecting the device to the machine
+
+> [!CAUTION]
+> `WRITE_SECURE_SETTINGS` is a very sensitive permission and enable it only if you know what you're doing. I'm not responsible for whatever may happen later on.
+
+> [!NOTE]
+> Auto restart service is untested
+
## Background
When developing apps that requires root, the most common method is to run some commands in the su shell. For example, there is an app that uses the `pm enable/disable` command to enable/disable components.
diff --git a/manager/src/main/AndroidManifest.xml b/manager/src/main/AndroidManifest.xml
index da059304b..8e4dfb301 100644
--- a/manager/src/main/AndroidManifest.xml
+++ b/manager/src/main/AndroidManifest.xml
@@ -7,6 +7,11 @@
+
+
+
@@ -130,6 +135,11 @@
android:exported="false"
android:foregroundServiceType="connectedDevice" />
+
+
= Build.VERSION_CODES.N) {
- storageContext = context.createDeviceProtectedStorageContext();
- } else {
- storageContext = context;
- }
+ storageContext = context.createDeviceProtectedStorageContext();
storageContext = new ContextWrapper(storageContext) {
@Override
diff --git a/manager/src/main/java/moe/shizuku/manager/adb/AdbWirelessHelper.kt b/manager/src/main/java/moe/shizuku/manager/adb/AdbWirelessHelper.kt
new file mode 100644
index 000000000..3df571a8e
--- /dev/null
+++ b/manager/src/main/java/moe/shizuku/manager/adb/AdbWirelessHelper.kt
@@ -0,0 +1,46 @@
+package moe.shizuku.manager.adb
+
+import android.content.ContentResolver
+import android.content.Context
+import android.content.Intent
+import android.net.ConnectivityManager
+import android.net.NetworkCapabilities
+import android.provider.Settings
+import android.util.Log
+import android.widget.Toast
+import moe.shizuku.manager.AppConstants
+import moe.shizuku.manager.starter.StarterActivity
+
+object WirelessADBHelper {
+
+ fun validateThenEnableWirelessAdb(contentResolver: ContentResolver, context: Context): Boolean {
+ val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
+ val networkCapabilities = connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork)
+ if (networkCapabilities != null && networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
+ enableWirelessADB(contentResolver, context)
+ return true
+ }
+ return false
+ }
+
+ private fun enableWirelessADB(contentResolver: ContentResolver, context: Context) {
+ // Enable wireless ADB
+ Settings.Global.putInt(
+ contentResolver,
+ "adb_wifi_enabled",
+ 1
+ )
+
+ Log.i(AppConstants.TAG, "Wireless Debugging enabled")
+ Toast.makeText(context, "Wireless Debugging enabled", Toast.LENGTH_SHORT).show()
+ }
+
+ fun callStartAdb(context: Context, host: String, port: Int) {
+ val intent = Intent(context, StarterActivity::class.java).apply {
+ putExtra(StarterActivity.EXTRA_IS_ROOT, false)
+ putExtra(StarterActivity.EXTRA_HOST, host)
+ putExtra(StarterActivity.EXTRA_PORT, port)
+ }
+ context.startActivity(intent)
+ }
+}
diff --git a/manager/src/main/java/moe/shizuku/manager/home/AdbDialogFragment.kt b/manager/src/main/java/moe/shizuku/manager/home/AdbDialogFragment.kt
index 7de01ac6d..94cc73b90 100644
--- a/manager/src/main/java/moe/shizuku/manager/home/AdbDialogFragment.kt
+++ b/manager/src/main/java/moe/shizuku/manager/home/AdbDialogFragment.kt
@@ -17,9 +17,9 @@ import androidx.lifecycle.MutableLiveData
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import moe.shizuku.manager.R
import moe.shizuku.manager.adb.AdbMdns
+import moe.shizuku.manager.adb.WirelessADBHelper.callStartAdb
import moe.shizuku.manager.databinding.AdbDialogBinding
-import moe.shizuku.manager.starter.StarterActivity
-import java.net.InetAddress
+import moe.shizuku.manager.utils.EnvironmentUtils
@RequiresApi(Build.VERSION_CODES.R)
class AdbDialogFragment : DialogFragment() {
@@ -33,8 +33,7 @@ class AdbDialogFragment : DialogFragment() {
binding = AdbDialogBinding.inflate(LayoutInflater.from(context))
adbMdns = AdbMdns(context, AdbMdns.TLS_CONNECT, port)
- var port = SystemProperties.getInt("service.adb.tcp.port", -1)
- if (port == -1) port = SystemProperties.getInt("persist.adb.tcp.port", -1)
+ val port = EnvironmentUtils.getAdbTcpPort()
val builder = MaterialAlertDialogBuilder(context).apply {
setTitle(R.string.dialog_adb_discovery)
@@ -70,8 +69,7 @@ class AdbDialogFragment : DialogFragment() {
}
dialog.getButton(AlertDialog.BUTTON_NEUTRAL)?.setOnClickListener {
- var port = SystemProperties.getInt("service.adb.tcp.port", -1)
- if (port == -1) port = SystemProperties.getInt("persist.adb.tcp.port", -1)
+ val port = EnvironmentUtils.getAdbTcpPort()
startAndDismiss(port)
}
@@ -83,13 +81,7 @@ class AdbDialogFragment : DialogFragment() {
private fun startAndDismiss(port: Int) {
val host = "127.0.0.1"
- val intent = Intent(context, StarterActivity::class.java).apply {
- putExtra(StarterActivity.EXTRA_IS_ROOT, false)
- putExtra(StarterActivity.EXTRA_HOST, host)
- putExtra(StarterActivity.EXTRA_PORT, port)
- }
- requireContext().startActivity(intent)
-
+ callStartAdb(requireContext(), host, port)
dismissAllowingStateLoss()
}
diff --git a/manager/src/main/java/moe/shizuku/manager/home/StartWirelessAdbViewHolder.kt b/manager/src/main/java/moe/shizuku/manager/home/StartWirelessAdbViewHolder.kt
index 6e6dff557..8b10fe370 100644
--- a/manager/src/main/java/moe/shizuku/manager/home/StartWirelessAdbViewHolder.kt
+++ b/manager/src/main/java/moe/shizuku/manager/home/StartWirelessAdbViewHolder.kt
@@ -3,7 +3,6 @@ package moe.shizuku.manager.home
import android.content.Context
import android.content.Intent
import android.os.Build
-import android.os.SystemProperties
import android.text.method.LinkMovementMethod
import android.view.LayoutInflater
import android.view.View
@@ -14,17 +13,16 @@ import androidx.fragment.app.FragmentActivity
import moe.shizuku.manager.Helps
import moe.shizuku.manager.R
import moe.shizuku.manager.adb.AdbPairingTutorialActivity
+import moe.shizuku.manager.adb.WirelessADBHelper.callStartAdb
import moe.shizuku.manager.databinding.HomeItemContainerBinding
import moe.shizuku.manager.databinding.HomeStartWirelessAdbBinding
import moe.shizuku.manager.ktx.toHtml
-import moe.shizuku.manager.starter.StarterActivity
import moe.shizuku.manager.utils.CustomTabsHelper
import moe.shizuku.manager.utils.EnvironmentUtils
import rikka.core.content.asActivity
import rikka.html.text.HtmlCompat
import rikka.recyclerview.BaseViewHolder
import rikka.recyclerview.BaseViewHolder.Creator
-import java.net.Inet4Address
class StartWirelessAdbViewHolder(binding: HomeStartWirelessAdbBinding, root: View) :
BaseViewHolder(root) {
@@ -73,12 +71,7 @@ class StartWirelessAdbViewHolder(binding: HomeStartWirelessAdbBinding, root: Vie
val port = EnvironmentUtils.getAdbTcpPort()
if (port > 0) {
val host = "127.0.0.1"
- val intent = Intent(context, StarterActivity::class.java).apply {
- putExtra(StarterActivity.EXTRA_IS_ROOT, false)
- putExtra(StarterActivity.EXTRA_HOST, host)
- putExtra(StarterActivity.EXTRA_PORT, port)
- }
- context.startActivity(intent)
+ callStartAdb(context, host, port)
} else {
WadbNotEnabledDialogFragment().show(context.asActivity().supportFragmentManager)
}
diff --git a/manager/src/main/java/moe/shizuku/manager/receiver/BootCompleteReceiver.kt b/manager/src/main/java/moe/shizuku/manager/receiver/BootCompleteReceiver.kt
index 879fbf9b2..ec3e4152b 100644
--- a/manager/src/main/java/moe/shizuku/manager/receiver/BootCompleteReceiver.kt
+++ b/manager/src/main/java/moe/shizuku/manager/receiver/BootCompleteReceiver.kt
@@ -1,22 +1,30 @@
package moe.shizuku.manager.receiver
+import android.Manifest
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
+import android.content.pm.PackageManager
import android.os.Process
import android.util.Log
+import android.widget.Toast
+import androidx.core.content.ContextCompat
import com.topjohnwu.superuser.Shell
import moe.shizuku.manager.AppConstants
import moe.shizuku.manager.ShizukuSettings
+import moe.shizuku.manager.ShizukuSettings.KEEP_START_ON_BOOT_WIRELESS
import moe.shizuku.manager.ShizukuSettings.LaunchMethod
+import moe.shizuku.manager.ShizukuSettings.getPreferences
+import moe.shizuku.manager.adb.WirelessADBHelper.validateThenEnableWirelessAdb
import moe.shizuku.manager.starter.Starter
+import moe.shizuku.manager.starter.SelfStarterService
import rikka.shizuku.Shizuku
class BootCompleteReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (Intent.ACTION_LOCKED_BOOT_COMPLETED != intent.action
- && Intent.ACTION_BOOT_COMPLETED != intent.action) {
+ && Intent.ACTION_BOOT_COMPLETED != intent.action) {
return
}
@@ -29,14 +37,38 @@ class BootCompleteReceiver : BroadcastReceiver() {
Log.i(AppConstants.TAG, "service is running")
return
}
- start(context)
+ start(context, false)
+ } else if (ShizukuSettings.getLastLaunchMode() == LaunchMethod.ADB) {
+ Log.i(AppConstants.TAG, "start on boot, action=" + intent.action)
+ if (Shizuku.pingBinder()) {
+ Log.i(AppConstants.TAG, "service is running")
+ return
+ }
+ if (intent.action == Intent.ACTION_BOOT_COMPLETED) {
+ val startOnBootWirelessIsEnabled = getPreferences().getBoolean(KEEP_START_ON_BOOT_WIRELESS, false)
+ start(context, startOnBootWirelessIsEnabled)
+ } else return
}
}
- private fun start(context: Context) {
+ private fun start(context: Context, startOnBootWirelessIsEnabled: Boolean) {
+
if (!Shell.rootAccess()) {
+ if (startOnBootWirelessIsEnabled && ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_SECURE_SETTINGS) == PackageManager.PERMISSION_GRANTED) {
+ Log.i(AppConstants.TAG, "WRITE_SECURE_SETTINGS is enabled and user has Start on boot is enabled for wireless ADB")
+ try {
+ val wirelessAdbStatus = validateThenEnableWirelessAdb(context.contentResolver, context)
+ if (wirelessAdbStatus) {
+ val intentService = Intent(context, SelfStarterService::class.java)
+ context.startService(intentService)
+ }
+ } catch (e: SecurityException) {
+ e.printStackTrace()
+ Toast.makeText(context, "Permission denied", Toast.LENGTH_SHORT).show()
+ }
+ } else
//NotificationHelper.notify(context, AppConstants.NOTIFICATION_ID_STATUS, AppConstants.NOTIFICATION_CHANNEL_STATUS, R.string.notification_service_start_no_root)
- return
+ return
}
Starter.writeDataFiles(context)
diff --git a/manager/src/main/java/moe/shizuku/manager/receiver/ShizukuReceiver.kt b/manager/src/main/java/moe/shizuku/manager/receiver/ShizukuReceiver.kt
index c3b9199fa..23db11da7 100644
--- a/manager/src/main/java/moe/shizuku/manager/receiver/ShizukuReceiver.kt
+++ b/manager/src/main/java/moe/shizuku/manager/receiver/ShizukuReceiver.kt
@@ -3,7 +3,11 @@ package moe.shizuku.manager.receiver
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
+import moe.shizuku.manager.ShizukuSettings
+import moe.shizuku.manager.adb.WirelessADBHelper
+import moe.shizuku.manager.model.ServiceStatus
import moe.shizuku.manager.shell.ShellBinderRequestHandler
+import moe.shizuku.manager.starter.SelfStarterService
class ShizukuReceiver : BroadcastReceiver() {
@@ -11,5 +15,15 @@ class ShizukuReceiver : BroadcastReceiver() {
if ("rikka.shizuku.intent.action.REQUEST_BINDER" == intent.action) {
ShellBinderRequestHandler.handleRequest(context, intent)
}
+ if (!ServiceStatus().isRunning) {
+ val startOnBootWirelessIsEnabled = ShizukuSettings.getPreferences().getBoolean(ShizukuSettings.KEEP_START_ON_BOOT_WIRELESS, false)
+ if (startOnBootWirelessIsEnabled) {
+ val wirelessAdbStatus = WirelessADBHelper.validateThenEnableWirelessAdb(context.contentResolver, context)
+ if (wirelessAdbStatus) {
+ val intentService = Intent(context, SelfStarterService::class.java)
+ context.startService(intentService)
+ }
+ }
+ }
}
}
diff --git a/manager/src/main/java/moe/shizuku/manager/settings/SettingsFragment.kt b/manager/src/main/java/moe/shizuku/manager/settings/SettingsFragment.kt
index 2aaedfc51..30cb25677 100644
--- a/manager/src/main/java/moe/shizuku/manager/settings/SettingsFragment.kt
+++ b/manager/src/main/java/moe/shizuku/manager/settings/SettingsFragment.kt
@@ -1,20 +1,26 @@
package moe.shizuku.manager.settings
+import android.Manifest
+import android.content.pm.PackageManager
+import android.widget.Toast
import android.content.ComponentName
import android.content.Context
import android.os.Build
import android.os.Bundle
import android.text.TextUtils
+import android.util.Log
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.appcompat.app.AppCompatDelegate
+import androidx.core.content.ContextCompat
import androidx.preference.*
import androidx.recyclerview.widget.RecyclerView
import moe.shizuku.manager.R
import moe.shizuku.manager.ShizukuSettings
import moe.shizuku.manager.ShizukuSettings.KEEP_START_ON_BOOT
+import moe.shizuku.manager.ShizukuSettings.KEEP_START_ON_BOOT_WIRELESS
import moe.shizuku.manager.app.ThemeHelper
import moe.shizuku.manager.app.ThemeHelper.KEY_BLACK_NIGHT_THEME
import moe.shizuku.manager.app.ThemeHelper.KEY_USE_SYSTEM_COLOR
@@ -39,6 +45,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
private lateinit var nightModePreference: IntegerSimpleMenuPreference
private lateinit var blackNightThemePreference: TwoStatePreference
private lateinit var startOnBootPreference: TwoStatePreference
+ private lateinit var startOnBootWirelessPreference: TwoStatePreference
private lateinit var startupPreference: PreferenceCategory
private lateinit var translationPreference: Preference
private lateinit var translationContributorsPreference: Preference
@@ -56,6 +63,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
nightModePreference = findPreference(KEY_NIGHT_MODE)!!
blackNightThemePreference = findPreference(KEY_BLACK_NIGHT_THEME)!!
startOnBootPreference = findPreference(KEEP_START_ON_BOOT)!!
+ startOnBootWirelessPreference = findPreference(KEEP_START_ON_BOOT_WIRELESS)!!
startupPreference = findPreference("startup")!!
translationPreference = findPreference("translation")!!
translationContributorsPreference = findPreference("translation_contributors")!!
@@ -63,14 +71,30 @@ class SettingsFragment : PreferenceFragmentCompat() {
val componentName = ComponentName(context.packageName, BootCompleteReceiver::class.java.name)
- startOnBootPreference.isChecked = context.packageManager.isComponentEnabled(componentName)
startOnBootPreference.onPreferenceChangeListener =
Preference.OnPreferenceChangeListener { _: Preference?, newValue: Any ->
if (newValue is Boolean) {
+ startOnBootWirelessPreference.isChecked = false
context.packageManager.setComponentEnabled(componentName, newValue)
context.packageManager.isComponentEnabled(componentName) == newValue
} else false
}
+
+ startOnBootWirelessPreference.onPreferenceChangeListener =
+ Preference.OnPreferenceChangeListener{ _: Preference?, newValue: Any ->
+ if (newValue is Boolean) {
+ if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_SECURE_SETTINGS) != PackageManager.PERMISSION_GRANTED) {
+ Log.i(ShizukuSettings.NAME, "Start on Boot Wireless without granting WRITE_SECURE_SETTINGS permission")
+ Toast.makeText(context, "WRITE_SECURE_SETTINGS permission is not granted for Shizuku", Toast.LENGTH_SHORT).show()
+ startOnBootWirelessPreference.isChecked = false
+ return@OnPreferenceChangeListener false
+ }
+ startOnBootPreference.isChecked = false
+ context.packageManager.setComponentEnabled(componentName, newValue)
+ context.packageManager.isComponentEnabled(componentName) == newValue
+ } else false
+ }
+
languagePreference.onPreferenceChangeListener =
Preference.OnPreferenceChangeListener { _: Preference?, newValue: Any ->
if (newValue is String) {
diff --git a/manager/src/main/java/moe/shizuku/manager/starter/SelfStarterService.kt b/manager/src/main/java/moe/shizuku/manager/starter/SelfStarterService.kt
new file mode 100644
index 000000000..6b037b62b
--- /dev/null
+++ b/manager/src/main/java/moe/shizuku/manager/starter/SelfStarterService.kt
@@ -0,0 +1,147 @@
+package moe.shizuku.manager.starter
+
+import android.app.Service
+import android.content.Intent
+import android.os.Build
+import android.os.IBinder
+import android.provider.Settings
+import android.util.Log
+import android.widget.Toast
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.LifecycleRegistry
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.GlobalScope
+import kotlinx.coroutines.launch
+import moe.shizuku.manager.BuildConfig
+import moe.shizuku.manager.ShizukuSettings
+import moe.shizuku.manager.adb.AdbClient
+import moe.shizuku.manager.adb.AdbKey
+import moe.shizuku.manager.adb.AdbKeyException
+import moe.shizuku.manager.adb.AdbMdns
+import moe.shizuku.manager.adb.PreferenceAdbKeyStore
+import moe.shizuku.manager.utils.EnvironmentUtils
+import rikka.lifecycle.Resource
+
+class SelfStarterService : Service(), LifecycleOwner {
+
+ private val sb = StringBuilder()
+ private lateinit var adbMdns: AdbMdns
+ private val port = MutableLiveData()
+ private val lifecycleOwner = LifecycleRegistry(this)
+ private val _output = MutableLiveData>()
+ val output = _output as LiveData>
+
+ override fun onCreate() {
+ super.onCreate()
+
+ val host = "127.0.0.1"
+ val startOnBootWirelessIsEnabled = ShizukuSettings.getPreferences().getBoolean(ShizukuSettings.KEEP_START_ON_BOOT_WIRELESS, false)
+ if (startOnBootWirelessIsEnabled && Settings.Global.getInt(this.contentResolver, "adb_wifi_enabled") == 1) {
+ Starter.writeSdcardFiles(applicationContext)
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ adbMdns = AdbMdns(this, AdbMdns.TLS_CONNECT, port)
+ adbMdns.start()
+
+ // Observe changes in the port
+ port.observeForever { it ->
+ if (it > 65535 || it < 1)
+ return@observeForever
+ try {
+ startAdb(host, it)
+ adbMdns.stop()
+ } catch (e: Exception) {
+ adbMdns.stop()
+ e.printStackTrace()
+ }
+ }
+ Toast.makeText(this, "Shizuku service has been started!", Toast.LENGTH_SHORT).show()
+ return
+ } else {
+ val port = EnvironmentUtils.getAdbTcpPort()
+ if (port > 0) {
+ startAdb(host, port)
+ }
+ }
+ }
+ }
+
+ override fun onBind(intent: Intent?): IBinder? {
+ return null
+ }
+
+
+ override fun getLifecycle(): Lifecycle {
+ return lifecycleOwner
+ }
+
+ private fun postResult(throwable: Throwable? = null) {
+ if (throwable == null)
+ _output.postValue(Resource.success(sb))
+ else
+ _output.postValue(Resource.error(throwable, sb))
+ }
+
+ private fun startAdb(host: String, port: Int) {
+ sb.append("Starting with wireless adb...").append('\n').append('\n')
+ postResult()
+
+ GlobalScope.launch(Dispatchers.IO) {
+ val key = try {
+ AdbKey(PreferenceAdbKeyStore(ShizukuSettings.getPreferences()), "shizuku")
+ } catch (e: Throwable) {
+ e.printStackTrace()
+ sb.append('\n').append(Log.getStackTraceString(e))
+
+ postResult(AdbKeyException(e))
+ return@launch
+ }
+
+ AdbClient(host, port, key).runCatching {
+ connect()
+
+ shellCommand(Starter.sdcardCommand) {
+ sb.append(String(it))
+ postResult()
+ }
+ close()
+ }.onFailure {
+ it.printStackTrace()
+
+ sb.append('\n').append(Log.getStackTraceString(it))
+ postResult(it)
+ }
+
+ /* Adb on MIUI Android 11 has no permission to access Android/data.
+ Before MIUI Android 12, we can temporarily use /data/user_de.
+ After that, is better to implement "adb push" and push files directly to /data/local/tmp.
+ */
+ if (sb.contains("/Android/data/${BuildConfig.APPLICATION_ID}/start.sh: Permission denied")) {
+ sb.append('\n')
+ .appendLine("adb have no permission to access Android/data, how could this possible ?!")
+ .appendLine("try /data/user_de instead...")
+ .appendLine()
+ postResult()
+
+ Starter.writeDataFiles(moe.shizuku.manager.application, true)
+
+ AdbClient(host, port, key).runCatching {
+ connect()
+ shellCommand(Starter.dataCommand) {
+ sb.append(String(it))
+ postResult()
+ }
+ close()
+ }.onFailure {
+ it.printStackTrace()
+
+ sb.append('\n').append(Log.getStackTraceString(it))
+ postResult(it)
+ }
+ }
+ }
+ }
+}
diff --git a/manager/src/main/res/values/strings.xml b/manager/src/main/res/values/strings.xml
index 5e469e7d0..a7e434e4f 100644
--- a/manager/src/main/res/values/strings.xml
+++ b/manager/src/main/res/values/strings.xml
@@ -21,7 +21,7 @@
- Please view the step-by-step guide first.]]>
+ In addition, Shizuku can be started automatically on boot without root if WRITE_SECURE_SETTINGS permission is granted.
Please view the step-by-step guide first.]]>
Step-by-step guide
@@ -107,13 +107,16 @@ With %1$s, in any terminal app, you can connect to and interact with the shell r
AppearanceBlack night themeUse the pure black theme if night mode is enabled
- Startup
+ Startup (only one of them can be enabled at a time)Translation contributorsParticipate in translationHelp us translate %s into your languageStart on boot (root)For rooted devices, Shizuku is able to start automatically on bootUse system theme color
+ Start on boot (wireless ADB)
+ For rootless devices, Shizuku is able to start automatically on boot
+ Enable Wi-Fi to proceedAbout
diff --git a/manager/src/main/res/xml/settings.xml b/manager/src/main/res/xml/settings.xml
index 0138bd4e5..0d308184a 100644
--- a/manager/src/main/res/xml/settings.xml
+++ b/manager/src/main/res/xml/settings.xml
@@ -10,6 +10,11 @@
android:summary="@string/settings_start_on_boot_summary"
android:title="@string/settings_start_on_boot" />
+
+