8a0b7832c0
AdbClient wraps the bundled libadb.so (useLegacyPackaging -> exec from nativeLibraryDir, HOME/TMPDIR overridden, adb key injected into BuildConfig from ~/.android at build time and never committed). ShizukuProvisioner: on each BLE+WiFi connect it enables ADB-over-WiFi via Mercury, connects with mDNS fallback, pushes the Shizuku starter and runs it; runs once per BLE session, does not relaunch GlassesApp when the WS channel is already up, and exposes runShell() for arbitrary commands. DebugReceiver (DUMP) + am start --ez provision for diagnostics. Protocol: ShizukuStatus(running/granted) + RequestShizukuStatus; glasses push it on connect and on Shizuku state change, using a passive probe() that never shows a permission dialog. Glasses kill-guard now recreates its UserService when the Shizuku binder dies instead of spamming DeadObjectException. Mercury: brightness disables auto-brightness first (was silently ignored), BLE auto-connect loop retries every 6s (fixes 'searches then stops'). New phone section 'Экран очков' with wake/sleep via adb (input keyevent 224/223).
145 lines
6.3 KiB
Kotlin
145 lines
6.3 KiB
Kotlin
package pw.binom.rayneovm.glasses
|
||
|
||
import android.app.Activity
|
||
import android.util.Log
|
||
import kotlinx.coroutines.flow.MutableStateFlow
|
||
import kotlinx.coroutines.flow.StateFlow
|
||
import kotlinx.coroutines.flow.asStateFlow
|
||
import rikka.shizuku.Shizuku
|
||
|
||
/**
|
||
* Управляет жизненным циклом Shizuku для GlassesApp.
|
||
*
|
||
* Состояния:
|
||
* - Waiting — Shizuku ещё не ответил (binder listener ждёт)
|
||
* - NotRunning — Shizuku не запущен (нет сервера)
|
||
* - NeedsPermission — Shizuku запущен, но прав ещё нет
|
||
* - Granted — права есть, можно работать
|
||
*
|
||
* Использование (см. https://github.com/RikkaApps/Shizuku-API):
|
||
* 1. В AndroidManifest.xml добавить `<provider rikka.shizuku.ShizukuProvider .../>` (уже есть).
|
||
* 2. В Application.onCreate / Activity.onCreate один раз:
|
||
* Shizuku.addBinderReceivedListenerSticky(...)
|
||
* Shizuku.addRequestPermissionResultListener(...)
|
||
* 3. Проверять ShizukuBootstrap.state.
|
||
*
|
||
* Зависимость `dev.rikka.shizuku:provider` нужна (в ней ShizukuProvider).
|
||
*/
|
||
object ShizukuBootstrap {
|
||
|
||
enum class AllowState {
|
||
Waiting,
|
||
NotRunning,
|
||
NeedsPermission,
|
||
Granted,
|
||
}
|
||
|
||
private const val TAG = "ShizukuBootstrap"
|
||
const val REQUEST_CODE_PERMISSION = 7801
|
||
|
||
private val _state = MutableStateFlow(AllowState.Waiting)
|
||
val state: StateFlow<AllowState> = _state.asStateFlow()
|
||
|
||
@Volatile private var binderReceivedCalled = false
|
||
|
||
/**
|
||
* Привязывает listeners к Shizuku (по доке). Вызывать из Activity.onCreate().
|
||
*
|
||
* - addBinderReceivedListenerSticky — сразу же выстреливает, если binder уже получен
|
||
* (без "sticky" мы бы пропустили).
|
||
* - addRequestPermissionResultListener — вызовется после диалога.
|
||
*
|
||
* ShizukuProvider из `dev.rikka.shizuku:provider` инициализирует binder до того,
|
||
* как наш процесс получит управление в onCreate (через ContentProvider.onCreate),
|
||
* поэтому pingBinder() сразу вернёт true — если Shizuku-сервер живой и v12+.
|
||
*/
|
||
fun attach(activity: Activity) {
|
||
// 1. Подписка на приход binder-а (sticky ⇒ сразу же вызовется, если уже есть).
|
||
Shizuku.addBinderReceivedListenerSticky(object : Shizuku.OnBinderReceivedListener {
|
||
override fun onBinderReceived() {
|
||
if (binderReceivedCalled) return
|
||
binderReceivedCalled = true
|
||
Log.i(TAG, "onBinderReceived")
|
||
refresh()
|
||
}
|
||
})
|
||
|
||
// 2. Подписка на результат диалога разрешения.
|
||
Shizuku.addRequestPermissionResultListener { _, grantResult ->
|
||
Log.i(TAG, "onRequestPermissionResult: grant=$grantResult")
|
||
refresh()
|
||
}
|
||
|
||
// 3. Сразу пробуем — может, binder уже есть.
|
||
if (Shizuku.pingBinder()) {
|
||
Log.i(TAG, "attach: pingBinder сразу ok")
|
||
refresh()
|
||
} else {
|
||
Log.w(TAG, "attach: pingBinder=false, ждём binder от ShizukuProvider")
|
||
_state.value = AllowState.Waiting
|
||
}
|
||
}
|
||
|
||
/** Перечитывает состояние и (если нужно) запрашивает permission. */
|
||
fun refresh() {
|
||
val running = try { Shizuku.pingBinder() } catch (t: Throwable) { false }
|
||
if (!running) {
|
||
Log.w(TAG, "refresh: Shizuku не запущен")
|
||
_state.value = AllowState.NotRunning
|
||
return
|
||
}
|
||
val perm = try {
|
||
Shizuku.checkSelfPermission()
|
||
} catch (t: Throwable) {
|
||
Log.w(TAG, "checkSelfPermission failed", t)
|
||
-1
|
||
}
|
||
if (perm == 0 /* PERMISSION_GRANTED */) {
|
||
Log.i(TAG, "refresh: granted")
|
||
_state.value = AllowState.Granted
|
||
} else {
|
||
Log.i(TAG, "refresh: needs permission (perm=$perm)")
|
||
if (_state.value != AllowState.NeedsPermission) {
|
||
_state.value = AllowState.NeedsPermission
|
||
try {
|
||
Shizuku.requestPermission(REQUEST_CODE_PERMISSION)
|
||
} catch (t: Throwable) {
|
||
Log.w(TAG, "requestPermission failed", t)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
fun isGranted(): Boolean = _state.value == AllowState.Granted
|
||
|
||
/** Снимок состояния без побочных эффектов (не показывает диалог разрешения). */
|
||
data class Snapshot(val running: Boolean, val granted: Boolean)
|
||
|
||
/**
|
||
* Пассивная проверка Shizuku: жив ли сервер и выдан ли нам permission.
|
||
*
|
||
* Зовётся из сервиса (не из Activity), поэтому не имеет права запрашивать разрешение —
|
||
* только читает. Нужна, чтобы телефон узнал, поднимать ли Shizuku через ADB.
|
||
*/
|
||
fun probe(): Snapshot {
|
||
val running = try { Shizuku.pingBinder() } catch (_: Throwable) { false }
|
||
val granted = if (!running) false else try {
|
||
Shizuku.checkSelfPermission() == 0 /* PERMISSION_GRANTED */
|
||
} catch (_: Throwable) { false }
|
||
return Snapshot(running = running, granted = granted)
|
||
}
|
||
|
||
/**
|
||
* Хук для Activity.onRequestPermissionResult: перенаправить в Shizuku.
|
||
*
|
||
* override fun onRequestPermissionsResult(rc: Int, p: Array<String>, g: IntArray) {
|
||
* ...
|
||
* ShizukuBootstrap.onRequestPermissionResult(rc, g[0])
|
||
* }
|
||
*/
|
||
fun onRequestPermissionResult(requestCode: Int, grantResult: Int) {
|
||
// Shizuku.addRequestPermissionResultListener вызывается автоматически внутри Shizuku,
|
||
// здесь делать ничего не надо. Метод оставлен как no-op для удобства и обратной совместимости.
|
||
}
|
||
}
|