Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c1a90102f8 | ||
|
|
ba531adc74 | ||
|
|
04d29b256e |
@@ -11,8 +11,8 @@ android {
|
|||||||
applicationId 'de.hackersoft.ariaagent'
|
applicationId 'de.hackersoft.ariaagent'
|
||||||
minSdk 26
|
minSdk 26
|
||||||
targetSdk 33 // 33 vermeidet die Foreground-Service-Typ-Pflicht von 34
|
targetSdk 33 // 33 vermeidet die Foreground-Service-Typ-Pflicht von 34
|
||||||
versionCode 1
|
versionCode 2
|
||||||
versionName '0.0.0.1'
|
versionName '0.0.0.2'
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
|
|||||||
@@ -47,11 +47,26 @@ class AgentService : Service() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||||
|
// WICHTIG: Jeder startForegroundService()-Aufruf MUSS binnen ~5s mit
|
||||||
|
// startForeground() beantwortet werden — sonst crasht Android den Prozess
|
||||||
|
// (ForegroundServiceDidNotStartInTimeException). Der Projection-Intent kommt
|
||||||
|
// per startForegroundService, obwohl der Dienst schon laeuft -> hier IMMER
|
||||||
|
// zuerst startForeground aufrufen (idempotent).
|
||||||
|
startForeground(NOTIF_ID, buildNotification(status))
|
||||||
|
|
||||||
if (intent?.action == ACTION_PROJECTION) {
|
if (intent?.action == ACTION_PROJECTION) {
|
||||||
val code = intent.getIntExtra(EXTRA_RESULT_CODE, 0)
|
val code = intent.getIntExtra(EXTRA_RESULT_CODE, 0)
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
val data = intent.getParcelableExtra<Intent>(EXTRA_RESULT_DATA)
|
val data = intent.getParcelableExtra<Intent>(EXTRA_RESULT_DATA)
|
||||||
if (code != 0 && data != null) ScreenCapturer.start(applicationContext, code, data)
|
if (code != 0 && data != null) {
|
||||||
|
try {
|
||||||
|
ScreenCapturer.start(applicationContext, code, data)
|
||||||
|
updateNotification("$status · Bildschirm-Zugriff aktiv")
|
||||||
|
} catch (e: Throwable) {
|
||||||
|
ScreenCapturer.lastError = e.message ?: e.javaClass.simpleName
|
||||||
|
updateNotification("Bildschirm-Fehler: ${ScreenCapturer.lastError}")
|
||||||
|
}
|
||||||
|
}
|
||||||
if (rvs == null) startRvs() // Dienst war frisch -> Verbindung nachziehen
|
if (rvs == null) startRvs() // Dienst war frisch -> Verbindung nachziehen
|
||||||
return START_STICKY
|
return START_STICKY
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -147,9 +147,11 @@ class RvsClient(
|
|||||||
|
|
||||||
/** Bildschirmfoto — selber Vertrag wie der Desktop-Agent: {format,bytes,base64}. */
|
/** Bildschirmfoto — selber Vertrag wie der Desktop-Agent: {format,bytes,base64}. */
|
||||||
private fun doScreenshot(): JSONObject {
|
private fun doScreenshot(): JSONObject {
|
||||||
if (!ScreenCapturer.active)
|
if (!ScreenCapturer.active) {
|
||||||
|
val why = ScreenCapturer.lastError?.let { " (letzter Fehler: $it)" } ?: ""
|
||||||
return err("Bildschirm-Zugriff nicht erlaubt. In der Agent-App auf dem Handy " +
|
return err("Bildschirm-Zugriff nicht erlaubt. In der Agent-App auf dem Handy " +
|
||||||
"einmalig 'Bildschirm-Zugriff erlauben' antippen.")
|
"einmalig 'Bildschirm-Zugriff erlauben' antippen.$why")
|
||||||
|
}
|
||||||
val png = ScreenCapturer.capture()
|
val png = ScreenCapturer.capture()
|
||||||
?: return err("Screenshot fehlgeschlagen (kein Frame). Ist der Bildschirm an?")
|
?: return err("Screenshot fehlgeschlagen (kein Frame). Ist der Bildschirm an?")
|
||||||
val b64 = android.util.Base64.encodeToString(png, android.util.Base64.NO_WRAP)
|
val b64 = android.util.Base64.encodeToString(png, android.util.Base64.NO_WRAP)
|
||||||
|
|||||||
@@ -36,6 +36,10 @@ object ScreenCapturer {
|
|||||||
private var h = 0
|
private var h = 0
|
||||||
private var dpi = 0
|
private var dpi = 0
|
||||||
|
|
||||||
|
/** Letzter Init-/Capture-Fehler (fuer die Fehlermeldung an ARIA). */
|
||||||
|
@Volatile
|
||||||
|
var lastError: String? = null
|
||||||
|
|
||||||
val active: Boolean
|
val active: Boolean
|
||||||
@Synchronized get() = projection != null
|
@Synchronized get() = projection != null
|
||||||
|
|
||||||
@@ -43,7 +47,10 @@ object ScreenCapturer {
|
|||||||
fun start(ctx: Context, resultCode: Int, data: Intent) {
|
fun start(ctx: Context, resultCode: Int, data: Intent) {
|
||||||
stop()
|
stop()
|
||||||
val mpm = ctx.getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
|
val mpm = ctx.getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
|
||||||
val mp = mpm.getMediaProjection(resultCode, data) ?: return
|
val mp = mpm.getMediaProjection(resultCode, data) ?: run {
|
||||||
|
lastError = "getMediaProjection lieferte null"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
val metrics = DisplayMetrics()
|
val metrics = DisplayMetrics()
|
||||||
val wm = ctx.getSystemService(Context.WINDOW_SERVICE) as WindowManager
|
val wm = ctx.getSystemService(Context.WINDOW_SERVICE) as WindowManager
|
||||||
@@ -56,18 +63,22 @@ object ScreenCapturer {
|
|||||||
handlerThread = HandlerThread("aria-capture").also { it.start() }
|
handlerThread = HandlerThread("aria-capture").also { it.start() }
|
||||||
handler = Handler(handlerThread!!.looper)
|
handler = Handler(handlerThread!!.looper)
|
||||||
|
|
||||||
|
// Ab Android 14 Pflicht VOR createVirtualDisplay; frueher unschaedlich.
|
||||||
mp.registerCallback(object : MediaProjection.Callback() {
|
mp.registerCallback(object : MediaProjection.Callback() {
|
||||||
override fun onStop() { stop() }
|
override fun onStop() { stop() }
|
||||||
}, handler)
|
}, handler)
|
||||||
|
|
||||||
val ir = ImageReader.newInstance(w, h, PixelFormat.RGBA_8888, 2)
|
val ir = ImageReader.newInstance(w, h, PixelFormat.RGBA_8888, 2)
|
||||||
reader = ir
|
reader = ir
|
||||||
|
// AUTO_MIRROR = Standard-Flag fuer MediaProjection-Capture (die Projection
|
||||||
|
// selbst autorisiert die Aufnahme, kein Sonderrecht noetig).
|
||||||
vdisplay = mp.createVirtualDisplay(
|
vdisplay = mp.createVirtualDisplay(
|
||||||
"aria-screen", w, h, dpi,
|
"aria-screen", w, h, dpi,
|
||||||
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
|
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
|
||||||
ir.surface, null, handler,
|
ir.surface, null, handler,
|
||||||
)
|
)
|
||||||
projection = mp
|
projection = mp
|
||||||
|
lastError = null
|
||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ OUT_MAX_CHARS_HARD = int(os.environ.get("OUT_MAX_CHARS_HARD", "200000") or "2000
|
|||||||
FILE_MAX_BYTES = int(os.environ.get("FILE_MAX_BYTES", str(10 * 1024 * 1024)) or str(10 * 1024 * 1024))
|
FILE_MAX_BYTES = int(os.environ.get("FILE_MAX_BYTES", str(10 * 1024 * 1024)) or str(10 * 1024 * 1024))
|
||||||
|
|
||||||
# Version (wird von release_agent.sh beim Release gesetzt).
|
# Version (wird von release_agent.sh beim Release gesetzt).
|
||||||
AGENT_VERSION = "0.0.0.1"
|
AGENT_VERSION = "0.0.0.2"
|
||||||
|
|
||||||
HEARTBEAT_SEC = 25
|
HEARTBEAT_SEC = 25
|
||||||
CAPS = ["exec", "read", "write", "info", "screenshot"]
|
CAPS = ["exec", "read", "write", "info", "screenshot"]
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ echo -e " ${GREEN}✓${NC} Tag gepusht\n"
|
|||||||
|
|
||||||
# ── Gitea-Release ────────────────────────────────────────────────────
|
# ── Gitea-Release ────────────────────────────────────────────────────
|
||||||
echo -e "${GREEN}[4/5] Gitea-Release anlegen...${NC}"
|
echo -e "${GREEN}[4/5] Gitea-Release anlegen...${NC}"
|
||||||
BODY="ARIA Host-Agent $TAG\n\nDesktop (Linux) + Android-APK. Auf das Zielgeraet kopieren, siehe README."
|
BODY=$(printf 'ARIA Host-Agent %s\n\nDesktop (Linux) + Android-APK. Auf das Zielgeraet kopieren, siehe README.' "$TAG")
|
||||||
BODY_JSON=$(printf '%s' "$BODY" | python3 -c 'import sys,json; print(json.dumps(sys.stdin.read()))' 2>/dev/null || printf '"%s"' "$BODY")
|
BODY_JSON=$(printf '%s' "$BODY" | python3 -c 'import sys,json; print(json.dumps(sys.stdin.read()))' 2>/dev/null || printf '"%s"' "$BODY")
|
||||||
RESP=$(curl -s -X POST "$GITEA_URL/api/v1/repos/$GITEA_REPO/releases" \
|
RESP=$(curl -s -X POST "$GITEA_URL/api/v1/repos/$GITEA_REPO/releases" \
|
||||||
-u "${GITEA_USER}:${GITEA_PASS}" -H "Content-Type: application/json" \
|
-u "${GITEA_USER}:${GITEA_PASS}" -H "Content-Type: application/json" \
|
||||||
|
|||||||
Reference in New Issue
Block a user