Skip to content

Commit

Permalink
feat(service-auto-start): add auto-start support for rootless devices
Browse files Browse the repository at this point in the history
This feature is tested working on GrapheneOS Android 14, Bluejay
  • Loading branch information
pixincreate committed Mar 21, 2024
1 parent 889734b commit fdee9d0
Show file tree
Hide file tree
Showing 14 changed files with 340 additions and 35 deletions.
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,25 @@
# 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
- 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.
Expand Down
20 changes: 20 additions & 0 deletions manager/release/output-metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"version": 3,
"artifactType": {
"type": "APK",
"kind": "Directory"
},
"applicationId": "moe.shizuku.privileged.api",
"variantName": "release",
"elements": [
{
"type": "SINGLE",
"filters": [],
"attributes": [],
"versionCode": 1053,
"versionName": "13.5.4.r1053.5c86fcf",
"outputFile": "shizuku-v13.5.4.r1053.5c86fcf-release.apk"
}
],
"elementType": "File"
}
Binary file not shown.
10 changes: 10 additions & 0 deletions manager/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
<uses-permission android:name="moe.shizuku.manager.permission.MANAGER" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission
android:name="android.permission.WRITE_SECURE_SETTINGS"
tools:ignore="ProtectedPermissions" />
<uses-permission
android:name="moe.shizuku.manager.permission.API_V23"
tools:node="remove" />
Expand Down Expand Up @@ -130,6 +135,11 @@
android:exported="false"
android:foregroundServiceType="connectedDevice" />

<service
android:name=".starter.SelfStarterService"
android:enabled="true"
android:exported="false" />

<receiver
android:name=".receiver.BootCompleteReceiver"
android:directBootAware="true"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import android.content.Context;
import android.content.ContextWrapper;
import android.content.SharedPreferences;
import android.os.Build;
import android.text.TextUtils;

import androidx.annotation.IntDef;
Expand All @@ -25,6 +24,7 @@ public class ShizukuSettings {
public static final String NIGHT_MODE = "night_mode";
public static final String LANGUAGE = "language";
public static final String KEEP_START_ON_BOOT = "start_on_boot";
public static final String KEEP_START_ON_BOOT_WIRELESS = "start_on_boot_wireless";

private static SharedPreferences sPreferences;

Expand All @@ -35,11 +35,7 @@ public static SharedPreferences getPreferences() {
@NonNull
private static Context getSettingsStorageContext(@NonNull Context context) {
Context storageContext;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
storageContext = context.createDeviceProtectedStorageContext();
} else {
storageContext = context;
}
storageContext = context.createDeviceProtectedStorageContext();

storageContext = new ContextWrapper(storageContext) {
@Override
Expand Down
46 changes: 46 additions & 0 deletions manager/src/main/java/moe/shizuku/manager/adb/AdbWirelessHelper.kt
Original file line number Diff line number Diff line change
@@ -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)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -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)
Expand Down Expand Up @@ -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)
}

Expand All @@ -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()
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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<Any?>(root) {
Expand Down Expand Up @@ -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<FragmentActivity>().supportFragmentManager)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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
}

Expand All @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,27 @@ 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() {

override fun onReceive(context: Context, intent: Intent) {
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)
}
}
}
}
}
Loading

0 comments on commit fdee9d0

Please sign in to comment.