fix: resolve service crash on start by fixing foreground service type check and notification icon

This commit is contained in:
CAtlas0784 committed 2026-11-01 14:16:22 +07:00
1 parent 1f88f6ea38
commit 92a4b4f545
3 files changed
+107 -35

No files matched your search

@@ -22,9 +22,13 @@ class GaCyApplication : Application() {
createNotificationChannel() createNotificationChannel()
// Initialize embedded Python runtime // Initialize embedded Python runtime
try {
if (!Python.isStarted()) { if (!Python.isStarted()) {
Python.start(AndroidPlatform(this)) Python.start(AndroidPlatform(this))
} }
} catch (t: Throwable) {
t.printStackTrace()
}
} }
private fun createNotificationChannel() { private fun createNotificationChannel() {
@@ -1,16 +1,18 @@
package com.gacy.android.service package com.gacy.android.service
import android.app.Notification import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent import android.app.PendingIntent
import android.app.Service
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.ServiceInfo import android.content.pm.ServiceInfo
import android.os.Build import android.os.Build
import android.os.IBinder import android.os.IBinder
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.lifecycle.LifecycleService
import androidx.lifecycle.lifecycleScope
import com.chaquo.python.Python import com.chaquo.python.Python
import com.chaquo.python.android.AndroidPlatform
import com.gacy.android.GaCyApplication import com.gacy.android.GaCyApplication
import com.gacy.android.MainActivity import com.gacy.android.MainActivity
import com.gacy.android.R import com.gacy.android.R
@@ -19,8 +21,10 @@ import com.gacy.android.data.PreferencesRepository
import com.gacy.android.data.UpdateRepository import com.gacy.android.data.UpdateRepository
import com.gacy.android.model.ServerExecutionMode import com.gacy.android.model.ServerExecutionMode
import com.gacy.android.model.ServerStatus import com.gacy.android.model.ServerStatus
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
@@ -37,19 +41,22 @@ class ServerLogReceiver {
} }
} }
class ServerService : LifecycleService() { class ServerService : Service() {
private val serviceScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
private var serverThread: Thread? = null private var serverThread: Thread? = null
private var probeJob: Job? = null private var probeJob: Job? = null
private var serverJob: Job? = null private var executionJob: Job? = null
override fun onBind(intent: Intent?): IBinder? = null
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
createNotificationChannel()
_serverStatus.value = ServerStatus.STOPPED _serverStatus.value = ServerStatus.STOPPED
} }
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
super.onStartCommand(intent, flags, startId)
val action = intent?.action ?: ACTION_START val action = intent?.action ?: ACTION_START
when (action) { when (action) {
@@ -66,6 +73,7 @@ class ServerService : LifecycleService() {
return return
} }
try {
val prefs = PreferencesRepository(this) val prefs = PreferencesRepository(this)
val config = prefs.getServerConfig() val config = prefs.getServerConfig()
@@ -80,10 +88,19 @@ class ServerService : LifecycleService() {
} else { } else {
startRemoteMonitor(config.remoteHost, config.dispatchPort) startRemoteMonitor(config.remoteHost, config.dispatchPort)
} }
} catch (t: Throwable) {
LogBuffer.append("[Start Error] ${t.localizedMessage ?: t.message}")
t.printStackTrace()
_serverStatus.value = ServerStatus.ERROR
stopForegroundCompat()
stopSelf()
}
} }
private fun startLocalServerExecution(dispatchPort: Int) { private fun startLocalServerExecution(dispatchPort: Int) {
serverJob = lifecycleScope.launch(Dispatchers.IO) { executionJob?.cancel()
executionJob = serviceScope.launch {
try {
val updateRepo = UpdateRepository(this@ServerService) val updateRepo = UpdateRepository(this@ServerService)
if (!updateRepo.isServerInstalled()) { if (!updateRepo.isServerInstalled()) {
LogBuffer.append("[Setup] Installing GaCy embedded server binaries from assets...") LogBuffer.append("[Setup] Installing GaCy embedded server binaries from assets...")
@@ -110,7 +127,7 @@ class ServerService : LifecycleService() {
try { try {
val persistentRepo = PersistentRepository(this@ServerService) val persistentRepo = PersistentRepository(this@ServerService)
persistentRepo.ensureValidPersistentJsonSync() persistentRepo.ensureValidPersistentJsonSync()
} catch (e: Exception) { } catch (e: Throwable) {
LogBuffer.append("[Warning] Persistent validation: ${e.message}") LogBuffer.append("[Warning] Persistent validation: ${e.message}")
} }
@@ -118,7 +135,7 @@ class ServerService : LifecycleService() {
// Background TCP port readiness probe // Background TCP port readiness probe
probeJob?.cancel() probeJob?.cancel()
probeJob = lifecycleScope.launch(Dispatchers.IO) { probeJob = serviceScope.launch {
var attempts = 0 var attempts = 0
while (isActive && _serverStatus.value == ServerStatus.STARTING && attempts < 35) { while (isActive && _serverStatus.value == ServerStatus.STARTING && attempts < 35) {
delay(1000) delay(1000)
@@ -134,12 +151,16 @@ class ServerService : LifecycleService() {
serverThread = Thread({ serverThread = Thread({
try { try {
if (!Python.isStarted()) {
Python.start(AndroidPlatform(applicationContext))
}
val py = Python.getInstance() val py = Python.getInstance()
val runPpsr = py.getModule("run_ppsr") val runPpsr = py.getModule("run_ppsr")
val logReceiver = ServerLogReceiver() val logReceiver = ServerLogReceiver()
runPpsr.callAttr("run_server", ppsrFile.absolutePath, logReceiver) runPpsr.callAttr("run_server", ppsrFile.absolutePath, logReceiver)
} catch (e: Exception) { } catch (t: Throwable) {
LogBuffer.append("[Python Error] ${e.localizedMessage ?: e.message}") LogBuffer.append("[Python Error] ${t.localizedMessage ?: t.message}")
t.printStackTrace()
} finally { } finally {
probeJob?.cancel() probeJob?.cancel()
_serverStatus.value = ServerStatus.STOPPED _serverStatus.value = ServerStatus.STOPPED
@@ -150,11 +171,19 @@ class ServerService : LifecycleService() {
}, "GaCy-PythonServer").apply { }, "GaCy-PythonServer").apply {
start() start()
} }
} catch (t: Throwable) {
LogBuffer.append("[Execution Error] ${t.localizedMessage ?: t.message}")
t.printStackTrace()
_serverStatus.value = ServerStatus.ERROR
stopForegroundCompat()
stopSelf()
}
} }
} }
private fun startRemoteMonitor(remoteHost: String, dispatchPort: Int) { private fun startRemoteMonitor(remoteHost: String, dispatchPort: Int) {
serverJob = lifecycleScope.launch(Dispatchers.IO) { executionJob?.cancel()
executionJob = serviceScope.launch {
LogBuffer.append("[Remote] Connecting to remote GaCy host at $remoteHost:$dispatchPort...") LogBuffer.append("[Remote] Connecting to remote GaCy host at $remoteHost:$dispatchPort...")
delay(600) delay(600)
_serverStatus.value = ServerStatus.RUNNING _serverStatus.value = ServerStatus.RUNNING
@@ -171,17 +200,17 @@ class ServerService : LifecycleService() {
probeJob?.cancel() probeJob?.cancel()
probeJob = null probeJob = null
serverJob?.cancel() executionJob?.cancel()
serverJob = null executionJob = null
lifecycleScope.launch(Dispatchers.IO) { serviceScope.launch {
try { try {
if (Python.isStarted()) { if (Python.isStarted()) {
val py = Python.getInstance() val py = Python.getInstance()
val runPpsr = py.getModule("run_ppsr") val runPpsr = py.getModule("run_ppsr")
runPpsr.callAttr("kill_server_process") runPpsr.callAttr("kill_server_process")
} }
} catch (e: Exception) { } catch (e: Throwable) {
LogBuffer.append("[Stop Error] ${e.localizedMessage ?: e.message}") LogBuffer.append("[Stop Error] ${e.localizedMessage ?: e.message}")
} }
@@ -197,7 +226,7 @@ class ServerService : LifecycleService() {
private fun restartServer() { private fun restartServer() {
LogBuffer.append("Restarting GaCy Server...") LogBuffer.append("Restarting GaCy Server...")
stopServer() stopServer()
lifecycleScope.launch { serviceScope.launch {
delay(1500) delay(1500)
startServer() startServer()
} }
@@ -209,12 +238,29 @@ class ServerService : LifecycleService() {
socket.connect(InetSocketAddress(host, port), 400) socket.connect(InetSocketAddress(host, port), 400)
true true
} }
} catch (_: Exception) { } catch (_: Throwable) {
false false
} }
} }
private fun createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
GaCyApplication.CHANNEL_ID,
GaCyApplication.CHANNEL_NAME,
NotificationManager.IMPORTANCE_LOW
).apply {
description = "Background service for GaCy Game Server & Proxy"
setShowBadge(false)
}
val manager = getSystemService(NotificationManager::class.java)
manager?.createNotificationChannel(channel)
}
}
private fun createNotification(contentText: String): Notification { private fun createNotification(contentText: String): Notification {
createNotificationChannel()
val launchIntent = Intent(this, MainActivity::class.java).apply { val launchIntent = Intent(this, MainActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP
} }
@@ -238,7 +284,7 @@ class ServerService : LifecycleService() {
return NotificationCompat.Builder(this, GaCyApplication.CHANNEL_ID) return NotificationCompat.Builder(this, GaCyApplication.CHANNEL_ID)
.setContentTitle("GaCy Android") .setContentTitle("GaCy Android")
.setContentText(contentText) .setContentText(contentText)
.setSmallIcon(R.mipmap.ic_launcher) .setSmallIcon(R.drawable.app_icon)
.setOngoing(true) .setOngoing(true)
.setContentIntent(pendingIntent) .setContentIntent(pendingIntent)
.addAction(android.R.drawable.ic_menu_close_clear_cancel, "Stop", stopPendingIntent) .addAction(android.R.drawable.ic_menu_close_clear_cancel, "Stop", stopPendingIntent)
@@ -246,45 +292,55 @@ class ServerService : LifecycleService() {
} }
private fun startForegroundCompat(text: String) { private fun startForegroundCompat(text: String) {
try {
createNotificationChannel()
val notification = createNotification(text) val notification = createNotification(text)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { // API 34+
startForeground( startForeground(
NOTIFICATION_ID, NOTIFICATION_ID,
notification, notification,
ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE
) )
} else { } else {
// API 26..33
startForeground(NOTIFICATION_ID, notification) startForeground(NOTIFICATION_ID, notification)
} }
} catch (e: Throwable) {
try {
val notification = createNotification(text)
startForeground(NOTIFICATION_ID, notification)
} catch (e2: Throwable) {
LogBuffer.append("[Foreground Warning] ${e2.message}")
}
}
} }
private fun updateNotification(contentText: String) { private fun updateNotification(contentText: String) {
val manager = getSystemService(Context.NOTIFICATION_SERVICE) as android.app.NotificationManager try {
manager.notify(NOTIFICATION_ID, createNotification(contentText)) val manager = getSystemService(Context.NOTIFICATION_SERVICE) as? NotificationManager
manager?.notify(NOTIFICATION_ID, createNotification(contentText))
} catch (_: Throwable) {}
} }
private fun stopForegroundCompat() { private fun stopForegroundCompat() {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
stopForeground(STOP_FOREGROUND_REMOVE) stopForeground(STOP_FOREGROUND_REMOVE)
} else { } else {
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
stopForeground(true) stopForeground(true)
} }
} catch (_: Throwable) {}
} }
override fun onDestroy() { override fun onDestroy() {
probeJob?.cancel() probeJob?.cancel()
serverThread?.interrupt() serverThread?.interrupt()
serverJob?.cancel() executionJob?.cancel()
super.onDestroy() super.onDestroy()
_serverStatus.value = ServerStatus.STOPPED _serverStatus.value = ServerStatus.STOPPED
} }
override fun onBind(intent: Intent): IBinder? {
super.onBind(intent)
return null
}
companion object { companion object {
const val ACTION_START = "com.gacy.android.ACTION_START" const val ACTION_START = "com.gacy.android.ACTION_START"
const val ACTION_STOP = "com.gacy.android.ACTION_STOP" const val ACTION_STOP = "com.gacy.android.ACTION_STOP"
@@ -296,25 +352,45 @@ class ServerService : LifecycleService() {
fun start(context: Context) { fun start(context: Context) {
val intent = Intent(context, ServerService::class.java).apply { action = ACTION_START } val intent = Intent(context, ServerService::class.java).apply { action = ACTION_START }
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent) context.startForegroundService(intent)
} else { } else {
context.startService(intent) context.startService(intent)
} }
} catch (t: Throwable) {
try {
context.startService(intent)
} catch (t2: Throwable) {
LogBuffer.append("[Service Start Error] ${t2.message}")
}
}
} }
fun stop(context: Context) { fun stop(context: Context) {
val intent = Intent(context, ServerService::class.java).apply { action = ACTION_STOP } val intent = Intent(context, ServerService::class.java).apply { action = ACTION_STOP }
try {
context.startService(intent) context.startService(intent)
} catch (t: Throwable) {
LogBuffer.append("[Service Stop Error] ${t.message}")
}
} }
fun restart(context: Context) { fun restart(context: Context) {
val intent = Intent(context, ServerService::class.java).apply { action = ACTION_RESTART } val intent = Intent(context, ServerService::class.java).apply { action = ACTION_RESTART }
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent) context.startForegroundService(intent)
} else { } else {
context.startService(intent) context.startService(intent)
} }
} catch (t: Throwable) {
try {
context.startService(intent)
} catch (t2: Throwable) {
LogBuffer.append("[Service Restart Error] ${t2.message}")
}
}
} }
} }
} }
-8
View File
@@ -476,14 +476,6 @@ def run_server(ppsr_path, activity_context=None):
except Exception as e: except Exception as e:
print(f"[Patch Warning] Router crash-guard: {e}") print(f"[Patch Warning] Router crash-guard: {e}")
# --- License / Access Check Bypass Patch ---
try:
import util.binary as _bin_mod
_bin_mod.qyqwiourtyqot = lambda *args, **kwargs: True
print("[Patch] util.binary access check bypassed successfully")
except Exception as e:
print(f"[Patch Warning] Binary access check patch: {e}")
print(f"Loading bytecode target: {ppsr_path}...") print(f"Loading bytecode target: {ppsr_path}...")
with open(ppsr_path, "rb") as f: with open(ppsr_path, "rb") as f: