Skip to content

Commit dab0424

Browse files
fix(wearable): harden socket FD cleanup and channel stream thread safety (#2843)
1 parent 9ded2f2 commit dab0424

2 files changed

Lines changed: 95 additions & 50 deletions

File tree

play-services-wearable/core/src/main/java/org/microg/gms/wearable/BluetoothConnectionThread.java

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -161,14 +161,17 @@ public void run() {
161161
SocketWearableConnection conn =
162162
new SocketWearableConnection(proxySocket(btSocket), listener);
163163
setWearableConnection(conn);
164-
conn.run();
165-
} catch (IOException e) {
166-
Log.w(TAG, "server: error on accepted connection", e);
167-
} finally {
168164
try {
169-
btSocket.close();
170-
} catch (IOException ignored) {
165+
conn.run();
166+
} finally {
167+
try {
168+
btSocket.close();
169+
} catch (IOException e) {
170+
Log.w(TAG, "server: close error for accepted connection", e);
171+
}
171172
}
173+
} catch (IOException e) {
174+
Log.w(TAG, "server: error on accepted connection", e);
172175
}
173176
}
174177
} catch (IOException e) {

play-services-wearable/core/src/main/java/org/microg/gms/wearable/ChannelManager.java

Lines changed: 86 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -174,10 +174,12 @@ public ParcelFileDescriptor getInputStream(String token) {
174174
return null;
175175
}
176176
try {
177-
if (state.inputPipe == null) {
178-
state.inputPipe = ParcelFileDescriptor.createPipe();
177+
synchronized (state) {
178+
if (state.inputPipe == null) {
179+
state.inputPipe = ParcelFileDescriptor.createPipe();
180+
}
181+
return state.inputPipe[0]; // read end
179182
}
180-
return state.inputPipe[0]; // read end
181183
} catch (IOException e) {
182184
Log.e(TAG, "getInputStream: failed to create pipe for channel " + token, e);
183185
return null;
@@ -199,11 +201,13 @@ public ParcelFileDescriptor getOutputStream(String token) {
199201
return null;
200202
}
201203
try {
202-
if (state.outputPipe == null) {
203-
state.outputPipe = ParcelFileDescriptor.createPipe();
204-
startOutputForwarder(state);
204+
synchronized (state) {
205+
if (state.outputPipe == null) {
206+
state.outputPipe = ParcelFileDescriptor.createPipe();
207+
startOutputForwarder(state);
208+
}
209+
return state.outputPipe[1]; // write end for caller
205210
}
206-
return state.outputPipe[1]; // write end for caller
207211
} catch (IOException e) {
208212
Log.e(TAG, "getOutputStream: failed to create pipe for channel " + token, e);
209213
return null;
@@ -222,11 +226,19 @@ public boolean writeInputToFd(String token, ParcelFileDescriptor fd) {
222226
ChannelState state = stateForToken(token);
223227
if (state == null) {
224228
Log.w(TAG, "writeInputToFd: unknown channel " + token);
229+
if (fd != null) {
230+
try {
231+
fd.close();
232+
} catch (IOException ignored) {
233+
}
234+
}
225235
return false;
226236
}
227237
try {
228-
if (state.inputPipe == null) {
229-
state.inputPipe = ParcelFileDescriptor.createPipe();
238+
synchronized (state) {
239+
if (state.inputPipe == null) {
240+
state.inputPipe = ParcelFileDescriptor.createPipe();
241+
}
230242
}
231243
final ParcelFileDescriptor readEnd = state.inputPipe[0];
232244
new Thread(() -> {
@@ -244,6 +256,12 @@ public boolean writeInputToFd(String token, ParcelFileDescriptor fd) {
244256
return true;
245257
} catch (IOException e) {
246258
Log.e(TAG, "writeInputToFd: failed to create pipe", e);
259+
if (fd != null) {
260+
try {
261+
fd.close();
262+
} catch (IOException ignored) {
263+
}
264+
}
247265
return false;
248266
}
249267
}
@@ -263,12 +281,20 @@ public boolean readOutputFromFd(String token, ParcelFileDescriptor fd,
263281
ChannelState state = stateForToken(token);
264282
if (state == null) {
265283
Log.w(TAG, "readOutputFromFd: unknown channel " + token);
284+
if (fd != null) {
285+
try {
286+
fd.close();
287+
} catch (IOException ignored) {
288+
}
289+
}
266290
return false;
267291
}
268292
try {
269-
if (state.outputPipe == null) {
270-
state.outputPipe = ParcelFileDescriptor.createPipe();
271-
startOutputForwarder(state);
293+
synchronized (state) {
294+
if (state.outputPipe == null) {
295+
state.outputPipe = ParcelFileDescriptor.createPipe();
296+
startOutputForwarder(state);
297+
}
272298
}
273299
final ParcelFileDescriptor writeEnd = state.outputPipe[1];
274300
new Thread(() -> {
@@ -298,6 +324,12 @@ public boolean readOutputFromFd(String token, ParcelFileDescriptor fd,
298324
return true;
299325
} catch (IOException e) {
300326
Log.e(TAG, "readOutputFromFd: failed to create pipe", e);
327+
if (fd != null) {
328+
try {
329+
fd.close();
330+
} catch (IOException ignored) {
331+
}
332+
}
301333
return false;
302334
}
303335
}
@@ -436,42 +468,52 @@ private void handleIncomingData(ChannelDataRequest data) {
436468
Log.w(TAG, "handleIncomingData: unknown channelId " + channelId);
437469
return;
438470
}
439-
try {
440-
if (state.inputPipe == null) {
441-
state.inputPipe = ParcelFileDescriptor.createPipe();
442-
// Open a single OutputStream over the write-end PFD and keep it alive
443-
// across all chunks. Wrapping the PFD rather than its raw FileDescriptor
444-
// ensures the FD is NOT closed when the stream would otherwise be closed.
445-
state.inputPipeWriter = new ParcelFileDescriptor.AutoCloseOutputStream(
446-
state.inputPipe[1]);
447-
}
448-
if (data.payload != null && data.payload.size() > 0) {
449-
try {
450-
state.inputPipeWriter.write(data.payload.toByteArray());
451-
} catch (IOException e) {
452-
// Reset on write error so the next message re-creates the pipe
453-
try { state.inputPipeWriter.close(); } catch (IOException ignored) { }
454-
state.inputPipeWriter = null;
455-
state.inputPipe = null;
456-
Log.e(TAG, "handleIncomingData: write error for channel " + channelId, e);
457-
return;
471+
synchronized (state) {
472+
try {
473+
if (state.inputPipe == null) {
474+
state.inputPipe = ParcelFileDescriptor.createPipe();
475+
// Open a single OutputStream over the write-end PFD and keep it alive
476+
// across all chunks. Wrapping the PFD rather than its raw FileDescriptor
477+
// ensures the FD is NOT closed when the stream would otherwise be closed.
478+
state.inputPipeWriter = new ParcelFileDescriptor.AutoCloseOutputStream(
479+
state.inputPipe[1]);
458480
}
459-
}
460-
if (Boolean.TRUE.equals(data.finalMessage)) {
461-
// Peer has finished sending; close the write-end to signal EOF to the reader.
462-
// The read-end (inputPipe[0]) is kept alive so the app can drain remaining data;
463-
// it will be released when the channel itself is closed via state.close().
464-
if (state.inputPipeWriter != null) {
465-
try { state.inputPipeWriter.close(); } catch (IOException ignored) { }
466-
state.inputPipeWriter = null;
481+
if (data.payload != null && data.payload.size() > 0) {
482+
try {
483+
state.inputPipeWriter.write(data.payload.toByteArray());
484+
} catch (IOException e) {
485+
// Reset on write error so the next message re-creates the pipe
486+
try { state.inputPipeWriter.close(); } catch (IOException ignored) { }
487+
if (state.inputPipe != null) {
488+
if (state.inputPipe[0] != null) {
489+
try { state.inputPipe[0].close(); } catch (IOException ignored) { }
490+
}
491+
if (state.inputPipe[1] != null) {
492+
try { state.inputPipe[1].close(); } catch (IOException ignored) { }
493+
}
494+
}
495+
state.inputPipeWriter = null;
496+
state.inputPipe = null;
497+
Log.e(TAG, "handleIncomingData: write error for channel " + channelId, e);
498+
return;
499+
}
467500
}
468-
if (state.inputPipe != null) {
469-
state.inputPipe[1] = null; // write-end closed by inputPipeWriter above
501+
if (Boolean.TRUE.equals(data.finalMessage)) {
502+
// Peer has finished sending; close the write-end to signal EOF to the reader.
503+
// The read-end (inputPipe[0]) is kept alive so the app can drain remaining data;
504+
// it will be released when the channel itself is closed via state.close().
505+
if (state.inputPipeWriter != null) {
506+
try { state.inputPipeWriter.close(); } catch (IOException ignored) { }
507+
state.inputPipeWriter = null;
508+
}
509+
if (state.inputPipe != null) {
510+
state.inputPipe[1] = null; // write-end closed by inputPipeWriter above
511+
}
512+
dispatchChannelEvent(state, EVENT_TYPE_INPUT_CLOSED, 0, 0);
470513
}
471-
dispatchChannelEvent(state, EVENT_TYPE_INPUT_CLOSED, 0, 0);
514+
} catch (IOException e) {
515+
Log.e(TAG, "handleIncomingData: pipe creation failed for channel " + channelId, e);
472516
}
473-
} catch (IOException e) {
474-
Log.e(TAG, "handleIncomingData: pipe creation failed for channel " + channelId, e);
475517
}
476518
}
477519

0 commit comments

Comments
 (0)