aboutsummaryrefslogtreecommitdiff
path: root/app/src/main/java/invalid/lena/scrcpy/Sessions.java
diff options
context:
space:
mode:
authorLena <lena@omega>2026-01-01 00:00:00 +0000
committerLena <lena@omega>2026-06-24 22:14:50 +0300
commiteb0c8951196c637e44daf3c0617b131b997d5d2c (patch)
tree2f83b6a41cee745467e53fc401942b82cea286ea /app/src/main/java/invalid/lena/scrcpy/Sessions.java
downloadscrcpy-android-eb0c8951196c637e44daf3c0617b131b997d5d2c.tar.gz
scrcpy-android: mirror an Android device over wireless ADB0.1
Native Java app for Android 12+ that mirrors another Android device over wireless ADB, forwarding video, audio, touch input, and clipboard. Bundles a pinned scrcpy-server.jar and the vendored libadb-android stack. Supports h264/h265/av1 video and raw/opus audio with in-app codec selection. No NDK, no Kotlin. Includes JVM unit tests and a Docker-based emulator e2e rig.
Diffstat (limited to 'app/src/main/java/invalid/lena/scrcpy/Sessions.java')
-rw-r--r--app/src/main/java/invalid/lena/scrcpy/Sessions.java75
1 files changed, 75 insertions, 0 deletions
diff --git a/app/src/main/java/invalid/lena/scrcpy/Sessions.java b/app/src/main/java/invalid/lena/scrcpy/Sessions.java
new file mode 100644
index 0000000..95c0a1f
--- /dev/null
+++ b/app/src/main/java/invalid/lena/scrcpy/Sessions.java
@@ -0,0 +1,75 @@
+package invalid.lena.scrcpy;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Intent;
+import android.content.pm.ServiceInfo;
+import android.os.IBinder;
+
+// Foreground service whose only job is to keep the app process alive
+// while the user is mirroring. Mirror starts it on entry and stops it
+// in onDestroy; the running foreground service (with its notification)
+// is what keeps the OOM killer away, so the activity can survive being
+// briefly backgrounded (rotation, IME, swipe-to-home) without the
+// scrcpy server tearing down.
+//
+// Type is FOREGROUND_SERVICE_DATA_SYNC: we are pulling a continuous
+// data stream (encoded video + raw PCM) from another device.
+//
+// No binder API - the service does not own Session. Mirror owns it.
+public final class Sessions extends Service {
+
+ private static final String CHANNEL_ID = "scrcpy-android-session";
+ private static final int NOTIF_ID = 1;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ Log.i("sessions: onCreate");
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ ensureChannel();
+ Notification n = new Notification.Builder(this, CHANNEL_ID)
+ .setSmallIcon(R.drawable.ic_notification)
+ .setContentTitle(getString(R.string.app_name))
+ .setContentText(getString(R.string.notif_session_active))
+ .setContentIntent(reopenIntent())
+ .setOngoing(true)
+ .build();
+ startForeground(NOTIF_ID, n, ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC);
+ return START_NOT_STICKY;
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ @Override
+ public void onDestroy() {
+ Log.i("sessions: onDestroy");
+ super.onDestroy();
+ }
+
+ private void ensureChannel() {
+ NotificationManager nm = getSystemService(NotificationManager.class);
+ if (nm.getNotificationChannel(CHANNEL_ID) != null) return;
+ NotificationChannel ch = new NotificationChannel(
+ CHANNEL_ID, getString(R.string.app_name),
+ NotificationManager.IMPORTANCE_LOW);
+ ch.setDescription(getString(R.string.notif_session_active));
+ nm.createNotificationChannel(ch);
+ }
+
+ private PendingIntent reopenIntent() {
+ Intent i = new Intent(this, Main.class);
+ i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ return PendingIntent.getActivity(this, 0, i,
+ PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);
+ }
+}