Conversation
- 新增 `AdbStartWorker` 及 `AdbStarter`,通过 WorkManager 实现带重试机制的 ADB 启动逻辑 - 新增开机启动通知栏,支持显示当前状态、手动重试及取消操作 - 优化 `BootCompleteReceiver`,根据上次启动方式(Root/ADB)选择自启路径 - 支持在开机自启过程中自动开启无线调试并等待设备解锁 - 在 `StellarSettings` 中新增 `LaunchMethod` 以记录上次启动方式 - 适配 Android 13+ 的 `WRITE_SECURE_SETTINGS` 权限校验 - 更新相关字符串资源及依赖配置
There was a problem hiding this comment.
Pull request overview
该 PR 将开机自启的 ADB 启动流程重构为基于 WorkManager 的后台任务,并新增通知栏交互来展示/控制启动状态,同时用设置项记录上次启动方式以决定开机自启路径。
Changes:
- 新增
AdbStartWorker+AdbStarter:通过 WorkManager 执行无线调试开启、端口发现、ADB 启动与 Binder 等待,并支持重试。 - 新增开机启动通知渠道/通知动作(重试/取消),并在
Application中初始化通知渠道。 - 新增
StellarSettings.LaunchMethod记录上次启动方式,BootCompleteReceiver根据 Root/ADB 条件选择自启路径。
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| manager/src/main/res/values/strings.xml | 新增开机 ADB Worker 通知相关文案(英文) |
| manager/src/main/res/values-zh-rCN/strings.xml | 新增开机 ADB Worker 通知相关文案(简中) |
| manager/src/main/kotlin/roro/stellar/manager/ui/features/manager/StarterScreen.kt | 启动成功后记录上次启动方式(Root/ADB) |
| manager/src/main/kotlin/roro/stellar/manager/StellarSettings.kt | 新增 LaunchMethod 与持久化读写 |
| manager/src/main/kotlin/roro/stellar/manager/StellarApplication.kt | 启动时创建开机启动通知渠道 |
| manager/src/main/kotlin/roro/stellar/manager/startup/worker/AdbStartWorker.kt | 新增 WorkManager Worker 实现开机 ADB 自启与重试流程 |
| manager/src/main/kotlin/roro/stellar/manager/startup/worker/AdbStarter.kt | 新增 ADB 连接/启动命令执行与 Binder 轮询 |
| manager/src/main/kotlin/roro/stellar/manager/startup/notification/BootStartNotifications.kt | 新增通知渠道与前台通知构建/展示/撤销 |
| manager/src/main/kotlin/roro/stellar/manager/startup/notification/BootStartActionReceiver.kt | 新增通知动作接收器(重试/取消) |
| manager/src/main/kotlin/roro/stellar/manager/receiver/StellarReceiver.kt | 改为直接触发 AdbStartWorker.enqueue() |
| manager/src/main/kotlin/roro/stellar/manager/receiver/BootCompleteReceiver.kt | 开机自启逻辑改为 Root 优先,其次按上次 ADB 启动选择 WorkManager 路径 |
| manager/src/main/AndroidManifest.xml | 注册通知动作接收器 |
| manager/manager.versions.toml | 增加 WorkManager 依赖版本与坐标 |
| manager/build.gradle | 引入 androidx.work:work-runtime-ktx |
| .claude/settings.local.json | 调整本地 Claude 工具权限配置 |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| fun createChannel(context: Context) { | ||
| val channel = NotificationChannel( | ||
| CHANNEL_ID, | ||
| context.getString(R.string.boot_start_channel_name), | ||
| NotificationManager.IMPORTANCE_LOW |
There was a problem hiding this comment.
BootStartNotifications.createChannel() unconditionally references NotificationChannel, but this app’s minSdk is 24. On API 24/25 this will crash at runtime when Application.onCreate calls it. Guard channel creation with SDK >= 26 (or make it a no-op pre-26) and build notifications with NotificationCompat on older APIs.
| return Notification.Builder(context, CHANNEL_ID) | ||
| .setSmallIcon(R.drawable.ic_stellar) | ||
| .setContentTitle(context.getString(R.string.boot_start_notification_title)) | ||
| .setContentText(message ?: context.getString(R.string.boot_start_enabling_wireless_adb)) | ||
| .setOngoing(true) |
There was a problem hiding this comment.
buildStartingNotification() uses Notification.Builder(context, CHANNEL_ID), which requires API 26+. With minSdk 24 this can crash on API 24/25 (e.g., when enqueue() shows the initial notification). Use NotificationCompat.Builder (and only rely on channels on 26+).
| override fun onReceive(context: Context, intent: Intent) { | ||
| if (!ServiceStatus().isRunning) { | ||
| val scriptModeEnabled = StellarSettings.getPreferences() | ||
| .getBoolean(StellarSettings.BOOT_SCRIPT_ENABLED, false) | ||
| if (!scriptModeEnabled) { | ||
| val wirelessAdbStatus = adbWirelessHelper.validateThenEnableWirelessAdb( | ||
| context.contentResolver, context | ||
| ) | ||
| if (wirelessAdbStatus) { | ||
| val intentService = Intent(context, SelfStarterService::class.java) | ||
| context.startService(intentService) | ||
| } | ||
| } | ||
| AdbStartWorker.enqueue(context) | ||
| } |
There was a problem hiding this comment.
StellarReceiver now enqueues AdbStartWorker unconditionally when the service isn’t running. If WRITE_SECURE_SETTINGS isn’t granted / SDK isn’t supported, AdbStartWorker will currently throw and then Result.retry(), potentially causing endless retries + ongoing notifications. Add gating here (or make the worker return Result.failure on non-retryable preconditions) to avoid a retry loop.
| suspendCancellableCoroutine { cont -> | ||
| val observer = Observer<Int> { port -> | ||
| if (port > 0 && cont.isActive) { | ||
| cont.resume(port) | ||
| } | ||
| } | ||
| val mdns = AdbMdns( | ||
| context = applicationContext, | ||
| serviceType = AdbMdns.TLS_CONNECT, | ||
| observer = observer, | ||
| onMaxRefresh = { | ||
| if (cont.isActive) cont.resume(-1) | ||
| } | ||
| ) | ||
| mdns.start() | ||
| cont.invokeOnCancellation { mdns.destroy() } | ||
| } |
There was a problem hiding this comment.
discoverAdbPort() creates and starts an AdbMdns instance but only destroys it on coroutine cancellation. When a port is found (or onMaxRefresh resumes), the coroutine resumes without stopping/destroying the discovery, leaving NSD discovery + executor running. Ensure AdbMdns.destroy() is called on all exit paths (success, onMaxRefresh, timeout).
| } catch (e: CancellationException) { | ||
| throw e | ||
| } catch (e: Exception) { | ||
| Log.e(TAG, "AdbStartWorker 异常", e) | ||
| // 检查是否在此期间已成功启动 | ||
| if (Stellar.pingBinder()) { | ||
| BootStartNotifications.dismiss(applicationContext) | ||
| return Result.success() | ||
| } | ||
| return retryWithNotification( | ||
| applicationContext.getString(R.string.boot_start_failed, e.message ?: "") | ||
| ) | ||
| } |
There was a problem hiding this comment.
doWork() treats all exceptions as retryable. In particular, a SecurityException from enableWirelessAdb() (missing WRITE_SECURE_SETTINGS) will lead to Result.retry() and can loop indefinitely. Handle non-retryable failures (e.g., permission denied / unsupported SDK) by returning Result.failure() and dismissing/updating the notification accordingly.
| @@ -0,0 +1,25 @@ | |||
| package roro.stellar.manager.startup.notification | |||
|
|
|||
| import android.app.NotificationManager | |||
There was a problem hiding this comment.
Unused import: NotificationManager is imported but never used.
| import android.app.NotificationManager |
| ACTION_CANCEL -> { | ||
| AdbStartWorker.cancel(context) | ||
| BootStartNotifications.dismiss(context) | ||
| } |
There was a problem hiding this comment.
ACTION_CANCEL currently calls AdbStartWorker.cancel(context), which already dismisses the boot-start notification, and then calls BootStartNotifications.dismiss(context) again. Consider removing the redundant dismiss to keep the receiver logic minimal.
| ACTION_CANCEL -> { | |
| AdbStartWorker.cancel(context) | |
| BootStartNotifications.dismiss(context) | |
| } | |
| ACTION_CANCEL -> AdbStartWorker.cancel(context) |
| { | ||
| "permissions": { | ||
| "allow": [ | ||
| "mcp__fast-context__fast_context_search", | ||
| "Bash(ls:*)" | ||
| "Bash(ls:*)", | ||
| "Bash(gh api:*)", | ||
| "WebFetch(domain:api.github.com)", | ||
| "Bash(curl:*)", | ||
| "Bash(git checkout:*)" | ||
| ] |
There was a problem hiding this comment.
This appears to be a machine-local Claude configuration file ("settings.local.json") and the change expands allowed tool permissions. Consider removing it from version control and adding it to .gitignore, or moving shared settings to a non-local config file, to avoid accidentally changing other contributors’ local tooling permissions.
- 新增 `StellarReceiverStarter` 统一管理 Root 和 ADB 启动逻辑。
- 重构 `BootCompleteReceiver`,移除冗余的 Root 启动实现,统一调用 `StellarReceiverStarter`。
- 优化 `AdbStartWorker`:
- 改进无线调试启用逻辑,增加对 `adb_wifi_enabled` 状态的监听。
- 优化设备锁屏时的等待处理,支持在解锁后自动恢复启动流程。
- 使用 `callbackFlow` 重新实现 ADB 端口发现逻辑,提高稳定性。
- 根据 ADB TCP 端口配置动态调整 WorkManager 约束条件。
- 在 `AdbStarter` 中增加自动切换 ADB 端口的逻辑。
- 简化 `AndroidManifest.xml` 中的 `BootCompleteReceiver` 意图过滤,仅保留 `BOOT_COMPLETED`。
- 在 `StellarReceiver` 和 `BootStartActionReceiver` 中引入协程与 `goAsync()`,将启动逻辑移至 IO 线程以避免阻塞主线程。
- 优化 `StellarReceiverStarter` 的启动策略:
- Root 启动失败时自动回退至 ADB 启动。
- 针对未知启动方式增加兼容性回退路径。
- 改进 `rootStart` 和 `adbStart` 的返回值处理,以便更好地处理链式启动逻辑。
- 在 `BootStartActionReceiver` 中将重试操作由 `AdbStartWorker` 改为调用 `StellarReceiverStarter`。
- 在 `BootStartNotifications` 中增加 Android O 版本检查,防止低版本崩溃。 - 将 `Notification.Builder` 替换为 `NotificationCompat.Builder` 以提升兼容性。 - 简化通知操作按钮(Action)的构建方式。 - 更新 `.claude/settings.local.json`,移除多余的 Bash 和 WebFetch 权限。
AdbStartWorker及AdbStarter,通过 WorkManager 实现带重试机制的 ADB 启动逻辑BootCompleteReceiver,根据上次启动方式(Root/ADB)选择自启路径StellarSettings中新增LaunchMethod以记录上次启动方式WRITE_SECURE_SETTINGS权限校验