1 #include "server.h"
2
3 #include <errno.h>
4 #include <inttypes.h>
5 #include <libgen.h>
6 #include <stdio.h>
7 #include <SDL2/SDL_assert.h>
8 #include <SDL2/SDL_timer.h>
9
10 #include "config.h"
11 #include "command.h"
12 #include "log.h"
13 #include "net.h"
14
15 #define SOCKET_NAME "scrcpy"
16 #define SERVER_FILENAME "scrcpy-server.jar"
17
18 #define DEFAULT_SERVER_PATH PREFIX "/share/scrcpy/" SERVER_FILENAME
19 #define DEVICE_SERVER_PATH "/data/local/tmp/" SERVER_FILENAME
20
21 static const char *
get_server_path(void)22 get_server_path(void) {
23 const char *server_path_env = getenv("SCRCPY_SERVER_PATH");
24 if (server_path_env) {
25 LOGD("Using SCRCPY_SERVER_PATH: %s", server_path_env);
26 // if the envvar is set, use it
27 return server_path_env;
28 }
29
30 #ifndef PORTABLE
31 LOGD("Using server: " DEFAULT_SERVER_PATH);
32 // the absolute path is hardcoded
33 return DEFAULT_SERVER_PATH;
34 #else
35 // use scrcpy-server.jar in the same directory as the executable
36 char *executable_path = get_executable_path();
37 if (!executable_path) {
38 LOGE("Cannot get executable path, "
39 "using " SERVER_FILENAME " from current directory");
40 // not found, use current directory
41 return SERVER_FILENAME;
42 }
43 char *dir = dirname(executable_path);
44 size_t dirlen = strlen(dir);
45
46 // sizeof(SERVER_FILENAME) gives statically the size including the null byte
47 size_t len = dirlen + 1 + sizeof(SERVER_FILENAME);
48 char *server_path = SDL_malloc(len);
49 if (!server_path) {
50 LOGE("Cannot alloc server path string, "
51 "using " SERVER_FILENAME " from current directory");
52 SDL_free(executable_path);
53 return SERVER_FILENAME;
54 }
55
56 memcpy(server_path, dir, dirlen);
57 server_path[dirlen] = PATH_SEPARATOR;
58 memcpy(&server_path[dirlen + 1], SERVER_FILENAME, sizeof(SERVER_FILENAME));
59 // the final null byte has been copied with SERVER_FILENAME
60
61 SDL_free(executable_path);
62
63 LOGD("Using server (portable): %s", server_path);
64 return server_path;
65 #endif
66 }
67
68 static bool
push_server(const char * serial)69 push_server(const char *serial) {
70 process_t process = adb_push(serial, get_server_path(), DEVICE_SERVER_PATH);
71 return process_check_success(process, "adb push");
72 }
73
74 static bool
enable_tunnel_reverse(const char * serial,uint16_t local_port)75 enable_tunnel_reverse(const char *serial, uint16_t local_port) {
76 process_t process = adb_reverse(serial, SOCKET_NAME, local_port);
77 return process_check_success(process, "adb reverse");
78 }
79
80 static bool
disable_tunnel_reverse(const char * serial)81 disable_tunnel_reverse(const char *serial) {
82 process_t process = adb_reverse_remove(serial, SOCKET_NAME);
83 return process_check_success(process, "adb reverse --remove");
84 }
85
86 static bool
enable_tunnel_forward(const char * serial,uint16_t local_port)87 enable_tunnel_forward(const char *serial, uint16_t local_port) {
88 process_t process = adb_forward(serial, local_port, SOCKET_NAME);
89 return process_check_success(process, "adb forward");
90 }
91
92 static bool
disable_tunnel_forward(const char * serial,uint16_t local_port)93 disable_tunnel_forward(const char *serial, uint16_t local_port) {
94 process_t process = adb_forward_remove(serial, local_port);
95 return process_check_success(process, "adb forward --remove");
96 }
97
98 static bool
enable_tunnel(struct server * server)99 enable_tunnel(struct server *server) {
100 if (enable_tunnel_reverse(server->serial, server->local_port)) {
101 return true;
102 }
103
104 LOGW("'adb reverse' failed, fallback to 'adb forward'");
105 server->tunnel_forward = true;
106 return enable_tunnel_forward(server->serial, server->local_port);
107 }
108
109 static bool
disable_tunnel(struct server * server)110 disable_tunnel(struct server *server) {
111 if (server->tunnel_forward) {
112 return disable_tunnel_forward(server->serial, server->local_port);
113 }
114 return disable_tunnel_reverse(server->serial);
115 }
116
117 static process_t
execute_server(struct server * server,const struct server_params * params)118 execute_server(struct server *server, const struct server_params *params) {
119 char max_size_string[6];
120 char bit_rate_string[11];
121 sprintf(max_size_string, "%"PRIu16, params->max_size);
122 sprintf(bit_rate_string, "%"PRIu32, params->bit_rate);
123 const char *const cmd[] = {
124 "shell",
125 "CLASSPATH=/data/local/tmp/" SERVER_FILENAME,
126 "app_process",
127 "/", // unused
128 "com.genymobile.scrcpy.Server",
129 max_size_string,
130 bit_rate_string,
131 server->tunnel_forward ? "true" : "false",
132 params->crop ? params->crop : "-",
133 params->send_frame_meta ? "true" : "false",
134 params->control ? "true" : "false",
135 };
136 return adb_execute(server->serial, cmd, sizeof(cmd) / sizeof(cmd[0]));
137 }
138
139 #define IPV4_LOCALHOST 0x7F000001
140
141 static socket_t
listen_on_port(uint16_t port)142 listen_on_port(uint16_t port) {
143 return net_listen(IPV4_LOCALHOST, port, 1);
144 }
145
146 static socket_t
connect_and_read_byte(uint16_t port)147 connect_and_read_byte(uint16_t port) {
148 socket_t socket = net_connect(IPV4_LOCALHOST, port);
149 if (socket == INVALID_SOCKET) {
150 return INVALID_SOCKET;
151 }
152
153 char byte;
154 // the connection may succeed even if the server behind the "adb tunnel"
155 // is not listening, so read one byte to detect a working connection
156 if (net_recv(socket, &byte, 1) != 1) {
157 // the server is not listening yet behind the adb tunnel
158 return INVALID_SOCKET;
159 }
160 return socket;
161 }
162
163 static socket_t
connect_to_server(uint16_t port,uint32_t attempts,uint32_t delay)164 connect_to_server(uint16_t port, uint32_t attempts, uint32_t delay) {
165 do {
166 LOGD("Remaining connection attempts: %d", (int) attempts);
167 socket_t socket = connect_and_read_byte(port);
168 if (socket != INVALID_SOCKET) {
169 // it worked!
170 return socket;
171 }
172 if (attempts) {
173 SDL_Delay(delay);
174 }
175 } while (--attempts > 0);
176 return INVALID_SOCKET;
177 }
178
179 static void
close_socket(socket_t * socket)180 close_socket(socket_t *socket) {
181 SDL_assert(*socket != INVALID_SOCKET);
182 net_shutdown(*socket, SHUT_RDWR);
183 if (!net_close(*socket)) {
184 LOGW("Cannot close socket");
185 return;
186 }
187 *socket = INVALID_SOCKET;
188 }
189
190 void
server_init(struct server * server)191 server_init(struct server *server) {
192 *server = (struct server) SERVER_INITIALIZER;
193 }
194
195 bool
server_start(struct server * server,const char * serial,const struct server_params * params)196 server_start(struct server *server, const char *serial,
197 const struct server_params *params) {
198 server->local_port = params->local_port;
199
200 if (serial) {
201 server->serial = SDL_strdup(serial);
202 if (!server->serial) {
203 return false;
204 }
205 }
206
207 if (!push_server(serial)) {
208 SDL_free(server->serial);
209 return false;
210 }
211
212 if (!enable_tunnel(server)) {
213 SDL_free(server->serial);
214 return false;
215 }
216
217 // if "adb reverse" does not work (e.g. over "adb connect"), it fallbacks to
218 // "adb forward", so the app socket is the client
219 if (!server->tunnel_forward) {
220 // At the application level, the device part is "the server" because it
221 // serves video stream and control. However, at the network level, the
222 // client listens and the server connects to the client. That way, the
223 // client can listen before starting the server app, so there is no
224 // need to try to connect until the server socket is listening on the
225 // device.
226
227 server->server_socket = listen_on_port(params->local_port);
228 if (server->server_socket == INVALID_SOCKET) {
229 LOGE("Could not listen on port %" PRIu16, params->local_port);
230 disable_tunnel(server);
231 SDL_free(server->serial);
232 return false;
233 }
234 }
235
236 // server will connect to our server socket
237 server->process = execute_server(server, params);
238
239 if (server->process == PROCESS_NONE) {
240 if (!server->tunnel_forward) {
241 close_socket(&server->server_socket);
242 }
243 disable_tunnel(server);
244 SDL_free(server->serial);
245 return false;
246 }
247
248 server->tunnel_enabled = true;
249
250 return true;
251 }
252
253 bool
server_connect_to(struct server * server)254 server_connect_to(struct server *server) {
255 if (!server->tunnel_forward) {
256 server->video_socket = net_accept(server->server_socket);
257 if (server->video_socket == INVALID_SOCKET) {
258 return false;
259 }
260
261 server->control_socket = net_accept(server->server_socket);
262 if (server->control_socket == INVALID_SOCKET) {
263 // the video_socket will be clean up on destroy
264 return false;
265 }
266
267 // we don't need the server socket anymore
268 close_socket(&server->server_socket);
269 } else {
270 uint32_t attempts = 100;
271 uint32_t delay = 100; // ms
272 server->video_socket =
273 connect_to_server(server->local_port, attempts, delay);
274 if (server->video_socket == INVALID_SOCKET) {
275 return false;
276 }
277
278 // we know that the device is listening, we don't need several attempts
279 server->control_socket =
280 net_connect(IPV4_LOCALHOST, server->local_port);
281 if (server->control_socket == INVALID_SOCKET) {
282 return false;
283 }
284 }
285
286 // we don't need the adb tunnel anymore
287 disable_tunnel(server); // ignore failure
288 server->tunnel_enabled = false;
289
290 return true;
291 }
292
293 void
server_stop(struct server * server)294 server_stop(struct server *server) {
295 if (server->server_socket != INVALID_SOCKET) {
296 close_socket(&server->server_socket);
297 }
298 if (server->video_socket != INVALID_SOCKET) {
299 close_socket(&server->video_socket);
300 }
301 if (server->control_socket != INVALID_SOCKET) {
302 close_socket(&server->control_socket);
303 }
304
305 SDL_assert(server->process != PROCESS_NONE);
306
307 if (!cmd_terminate(server->process)) {
308 LOGW("Cannot terminate server");
309 }
310
311 cmd_simple_wait(server->process, NULL); // ignore exit code
312 LOGD("Server terminated");
313
314 if (server->tunnel_enabled) {
315 // ignore failure
316 disable_tunnel(server);
317 }
318 }
319
320 void
server_destroy(struct server * server)321 server_destroy(struct server *server) {
322 SDL_free(server->serial);
323 }
324