1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
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);
}
}
|