Fix crash on stream disconnect on Android 7.0+ devices (root only)

This commit is contained in:
Cameron Gutman
2017-05-18 10:52:17 -07:00
parent 686490ba70
commit 44cbf8adc1
@@ -2,6 +2,7 @@ package com.limelight.binding.input.evdev;
import android.app.Activity; import android.app.Activity;
import android.os.Build; import android.os.Build;
import android.os.Looper;
import android.widget.Toast; import android.widget.Toast;
import com.limelight.LimeLog; import com.limelight.LimeLog;
@@ -198,6 +199,26 @@ public class EvdevCaptureProvider extends InputCaptureProvider {
}); });
} }
private void runInNetworkSafeContextSynchronously(Runnable runnable) {
// This function is used to avoid Android's strict NetworkOnMainThreadException.
// For our usage, it is highly unlikely to cause problems since we only do
// write operations and only to localhost sockets.
if (Looper.getMainLooper().getThread() == Thread.currentThread()) {
Thread t = new Thread(runnable);
t.start();
try {
t.join();
} catch (InterruptedException e) {
// The main thread should never be interrupted
e.printStackTrace();
}
}
else {
// Run the runnable directly
runnable.run();
}
}
@Override @Override
public void enableCapture() { public void enableCapture() {
if (!started) { if (!started) {
@@ -207,26 +228,38 @@ public class EvdevCaptureProvider extends InputCaptureProvider {
started = true; started = true;
} }
else { else {
// Send a request to regrab if we're already capturing // This may be called on the main thread
if (!shutdown && evdevOut != null) { runInNetworkSafeContextSynchronously(new Runnable() {
try { @Override
evdevOut.write(REGRAB_REQUEST); public void run() {
} catch (IOException e) { // Send a request to regrab if we're already capturing
e.printStackTrace(); if (!shutdown && evdevOut != null) {
try {
evdevOut.write(REGRAB_REQUEST);
} catch (IOException e) {
e.printStackTrace();
}
}
} }
} });
} }
} }
@Override @Override
public void disableCapture() { public void disableCapture() {
if (started && !shutdown && evdevOut != null) { // This may be called on the main thread
try { runInNetworkSafeContextSynchronously(new Runnable() {
evdevOut.write(UNGRAB_REQUEST); @Override
} catch (IOException e) { public void run() {
e.printStackTrace(); if (started && !shutdown && evdevOut != null) {
try {
evdevOut.write(UNGRAB_REQUEST);
} catch (IOException e) {
e.printStackTrace();
}
}
} }
} });
} }
@Override @Override
@@ -234,6 +267,8 @@ public class EvdevCaptureProvider extends InputCaptureProvider {
// We need to stop the process in this context otherwise // We need to stop the process in this context otherwise
// we could get stuck waiting on output from the process // we could get stuck waiting on output from the process
// in order to terminate it. // in order to terminate it.
//
// This may be called on the main thread.
if (!started) { if (!started) {
return; return;
@@ -242,37 +277,42 @@ public class EvdevCaptureProvider extends InputCaptureProvider {
shutdown = true; shutdown = true;
handlerThread.interrupt(); handlerThread.interrupt();
if (servSock != null) { runInNetworkSafeContextSynchronously(new Runnable() {
try { @Override
servSock.close(); public void run() {
} catch (IOException e) { if (servSock != null) {
e.printStackTrace(); try {
} servSock.close();
} } catch (IOException e) {
e.printStackTrace();
}
}
if (evdevSock != null) { if (evdevSock != null) {
try { try {
evdevSock.close(); evdevSock.close();
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
if (evdevIn != null) { if (evdevIn != null) {
try { try {
evdevIn.close(); evdevIn.close();
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
if (evdevOut != null) { if (evdevOut != null) {
try { try {
evdevOut.close(); evdevOut.close();
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
}
}
} }
} });
if (su != null) { if (su != null) {
su.destroy(); su.destroy();