Skip to content

Commit

Permalink
permit launch without WiFi network when media playback is requested
Browse files Browse the repository at this point in the history
although the HTTP service cannot bind to a local IP address for remote control,
this will allow local control for the purposes of:
  * offline playback of media from external storage
    - via an Intent from a file manager (ex: Total Commander)
  * online playback of media over a mobile (4G/5G) network
    - via an Intent from some other app (ex: Bookmarks, Web Monkey)
  • Loading branch information
warren-bank committed Dec 13, 2023
1 parent 728b85b commit 8b6d520
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 82 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public interface PlaybackInfoSource {
}

public interface Callback {
void onNewIpAddress();
void onNewIpAddress(InetAddress ipAddress);
}

private static final String tag = RequestListenerThread.class.getSimpleName();
Expand All @@ -81,14 +81,42 @@ public interface Callback {
private MyHttpService httpService;

public RequestListenerThread(Context context, PlaybackInfoSource playbackInfoSource, Callback callback) {
super(tag);

this.context = context;
this.playbackInfoSource = playbackInfoSource;
this.callback = callback;
}

private void onNewIpAddress() {
if (this.callback != null)
this.callback.onNewIpAddress(this.localAddress);
}

private void getLocalIpAddress() {
getLocalIpAddress(false);
}

private void getLocalIpAddress(boolean forceCallback) {
InetAddress currentLocalAddress = NetworkUtils.getLocalIpAddress(this.context);

boolean isNewIpAddress = (
forceCallback
|| ((currentLocalAddress == null) && (this.localAddress != null))
|| ((currentLocalAddress != null) && (this.localAddress == null))
|| ((currentLocalAddress != null) && (this.localAddress != null) && !currentLocalAddress.equals(this.localAddress))
);

this.localAddress = currentLocalAddress;

if (isNewIpAddress)
onNewIpAddress();
}

public void run() {
try {
Thread.sleep(2 * 1000);
getLocalIpAddress(true);
initHttpServer();
}
catch (IOException e) {
Expand All @@ -101,8 +129,10 @@ public void run() {

while (!Thread.interrupted()) {
try {
if (this.serversocket == null)
break;
if (this.serversocket == null) {
Log.d(tag, "problem initializing HTTP server");
throw new IOException();
}

Socket socket = this.serversocket.accept();
Log.d(tag, "airplay incoming connection from " + socket.getInetAddress() + "; socket id= [" + socket + "]");
Expand All @@ -115,46 +145,45 @@ public void run() {
exec.execute(thread);
}
catch(Exception e) {
int wifi_connection_status = get_wifi_connection_status();
if (this.serversocket != null) {
if (!this.serversocket.isClosed()) {
// Close the previous socket
try {
this.serversocket.close();
}
catch (IOException e2) {}
}
this.serversocket = null;
}

getLocalIpAddress();

if ((e instanceof IOException) && (wifi_connection_status == 0)) {
while (wifi_connection_status == 0) {
if ((e instanceof IOException) && (this.localAddress == null)) {
Log.d(tag, "Awaiting reconnection to WiFi network.");

while (this.localAddress == null) {
// Socket closed due to temporary disconnection from WiFi network.
// Check every 15 seconds for reconnection.
try {
Thread.sleep(15 * 1000);
wifi_connection_status = get_wifi_connection_status();
getLocalIpAddress();
}
catch (InterruptedException e2) {
break;
}
}
if (wifi_connection_status == 0) {
if (this.localAddress == null) {
// Thread was interrupted
break;
}
if ((this.serversocket != null) && !this.serversocket.isClosed()) {
// Close the previous socket
try {
this.serversocket.close();
}
catch (IOException e2) {
this.serversocket = null;
}
}
if ((this.serversocket == null) || this.serversocket.isClosed()) {
// Open a new socket
try {
initHttpServer();

if ((wifi_connection_status == 2) && (this.callback != null)) {
this.callback.onNewIpAddress();
}
}
catch (IOException e2) {
Log.e(tag, "problem reinitializing HTTP server", e);
this.serversocket = null;
}
// Open a new socket
try {
initHttpServer();
}
catch (IOException e2) {
Log.e(tag, "problem reinitializing HTTP server", e);
this.serversocket = null;
}
}
else {
Expand All @@ -181,29 +210,12 @@ public void destroy() {
}
}

/*
* return values:
* 0 = disconnected
* 1 = connected to same local address
* 2 = connected to new local address
*/
private int get_wifi_connection_status() {
InetAddress currentLocalAddress = NetworkUtils.getLocalIpAddress(this.context);

return (currentLocalAddress == null)
? 0
: currentLocalAddress.equals(this.localAddress)
? 1
: 2;
}

private void initHttpServer() throws IOException {
Log.d(tag, "airplay init http server");

this.localAddress = NetworkUtils.getLocalIpAddress(this.context);

if (this.localAddress == null) {
Thread.interrupted();
macAddress = null;
this.serversocket = null;
return;
}

Expand All @@ -212,11 +224,11 @@ private void initHttpServer() throws IOException {
Log.d(tag, "airplay local MAC address = " + macAddress);
}

serversocket = new ServerSocket(Constant.AIRPLAY_PORT, 2, this.localAddress);
serversocket.setReuseAddress(true);
this.serversocket = new ServerSocket(Constant.AIRPLAY_PORT, 2, this.localAddress);
this.serversocket.setReuseAddress(true);

params = new BasicHttpParams();
params
this.params = new BasicHttpParams();
this.params
.setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE, 8 * 1024)
.setBooleanParameter(CoreConnectionPNames.STALE_CONNECTION_CHECK, false)
.setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, true)
Expand All @@ -232,15 +244,15 @@ private void initHttpServer() throws IOException {
HttpRequestHandlerRegistry registry = new HttpRequestHandlerRegistry();

//http request handler, HttpFileHandler inherits from HttpRequestHandler
registry.register("*", new WebServiceHandler(playbackInfoSource));
registry.register("*", new WebServiceHandler(this.playbackInfoSource));

httpService = new MyHttpService(
this.httpService = new MyHttpService(
httpProcessor,
new NoConnectionReuseStrategy(), //DefaultConnectionReuseStrategy()
new DefaultHttpResponseFactory()
);
httpService.setParams(this.params);
httpService.setHandlerResolver(registry); //Set up a registered request handler for the http service.
this.httpService.setParams(this.params);
this.httpService.setHandlerResolver(registry); //Set up a registered request handler for the http service.
}

private static class WorkerThread extends Thread {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,6 @@ public void handleMessage(final Message msg) {

case Constant.Register.FAIL : {
ToastUtils.showToastCenterShort(service.getApplicationContext(), R.string.toast_registration_failure);
service.stopSelf();
break;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import com.github.warren_bank.exoplayer_airplay_receiver.exoplayer2.PlayerManager;
import com.github.warren_bank.exoplayer_airplay_receiver.httpcore.RequestListenerThread;
import com.github.warren_bank.exoplayer_airplay_receiver.ui.VideoPlayerActivity;
import com.github.warren_bank.exoplayer_airplay_receiver.utils.NetworkUtils;
import com.github.warren_bank.exoplayer_airplay_receiver.utils.ResourceUtils;
import com.github.warren_bank.exoplayer_airplay_receiver.utils.StringUtils;
import com.github.warren_bank.exoplayer_airplay_receiver.utils.WakeLockMgr;
Expand Down Expand Up @@ -71,23 +70,22 @@ public void onCreate() {
Toast toast = Toast.makeText(getApplicationContext(), getString(R.string.toast_registration_started), Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
showNotification();

new Thread() {
public void run() {
try {
thread = new RequestListenerThread(/* context= */ NetworkingService.this, /* RequestListenerThread.PlaybackInfoSource */ playbackInfoSource, /* RequestListenerThread.Callback */ NetworkingService.this);
thread.setDaemon(false);
thread.start();

registerAirplay();
}
catch (IOException e) {
Log.e(tag, "problem initializing HTTP server and Bonjour services", e);
catch (Exception e) {
Log.e(tag, "Problem initializing HTTP server.", e);

Message msg = Message.obtain();
msg.what = Constant.Register.FAIL;
MainApp.broadcastMessage(msg);

stopSelf();
}
}
}.start();
Expand Down Expand Up @@ -153,20 +151,11 @@ public IBinder onBind(Intent intent) {
return null;
}

private void registerAirplay() throws IOException {
private void registerAirplay() {
Message msg = Message.obtain();
try {
Thread.sleep(2 * 1000);
}
catch (InterruptedException e) {
Log.e(tag, "problem putting thread to sleep to allow HTTP server time to initialize prior to registering Bonjour services", e);
}
try {
Log.d(tag, "Beginning registration of Bonjour services..");

if (localAddress == null)
localAddress = NetworkUtils.getLocalIpAddress(NetworkingService.this);

if (localAddress == null) {
Log.d(tag, "No local IP address found for any network interface that supports multicast");
throw new Exception("");
Expand Down Expand Up @@ -310,9 +299,6 @@ private PendingIntent getPendingIntent_StopService() {
}

private String getNetworkAddress() {
if (localAddress == null)
localAddress = NetworkUtils.getLocalIpAddress(NetworkingService.this);

return (localAddress == null)
? "[offline]"
: localAddress.getHostAddress() + ":" + Constant.AIRPLAY_PORT;
Expand Down Expand Up @@ -441,8 +427,16 @@ public static PlayerManager getPlayerManager() {
// -------------------------------------------------------------------------
// implement interface: RequestListenerThread.Callback

public void onNewIpAddress() {
public void onNewIpAddress(InetAddress ipAddress) {
this.localAddress = ipAddress;
showNotification();

new Thread() {
public void run() {
unregisterAirplay();
registerAirplay();
}
}.start();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,7 @@ public class StartNetworkingServiceActivity extends Activity implements RuntimeP
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

if (NetworkUtils.isWifiConnected(MainApp.getInstance()))
requestPermissions();
else
finish();
requestPermissions();
}

@Override
Expand Down Expand Up @@ -103,7 +100,13 @@ private void requestPermissions() {
private void startNetworkingService() {
Intent intent = new Intent(getApplicationContext(), NetworkingService.class);
forwardMedia(intent);
MainApp.getInstance().startService(intent);

if (
NetworkUtils.isWifiConnected(MainApp.getInstance())
|| NetworkingService.ACTION_PLAY.equals(intent.getAction())
) {
MainApp.getInstance().startService(intent);
}
}

private void forwardMedia(Intent newIntent) {
Expand Down

0 comments on commit 8b6d520

Please sign in to comment.