Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@YisroelForta
Created March 22, 2016 15:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save YisroelForta/bd57ed34a43485b46037 to your computer and use it in GitHub Desktop.
Save YisroelForta/bd57ed34a43485b46037 to your computer and use it in GitHub Desktop.
package com.example.appletv;
import android.annotation.TargetApi;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.util.Log;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
//NsdManager was added in API 16
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
public class AppleTVClientService extends Service {
private static final String TAG = "apple_tv";
private static final String SERVICE_NAME = "_name._tcp.";
private static final int INT_BYTE_COUNT = Integer.SIZE/Byte.SIZE;
private static final int LONG_BYTE_COUNT = Long.SIZE/Byte.SIZE;
private static final ByteOrder BYTE_ORDER = ByteOrder.LITTLE_ENDIAN;
private final IBinder = new LocalBinder();
private NsdManager mNsdManager;
private NsdManager.DiscoveryListener mDiscoveryListener;
private NsdManager.ResolveListener mResolveListener;
private List<NsdServiceInfo> services;
private Callbacks callbacks;
@Nullable
@Override
public IBinder onBind(Intent intent) {
return ;
}
public class LocalBinder extends Binder {
public AppleTVClientService getServiceInstance(){
return AppleTVClientService.this;
}
}
public interface Callbacks {
void onTVFound(String name);
void onCommunicationComplete(boolean successful);
void onCommunicationFailed(String failReason);
}
public void setCallbacks(Callbacks callbacks) {
this.callbacks = callbacks;
}
public void updateUIThread() {
if (callbacks != null && services.size() > 0) {
for (NsdServiceInfo service : services) {
callbacks.onTVFound(service.getServiceName());
}
}
}
@Override
public void onCreate() {
services = new ArrayList<>();
mNsdManager = (NsdManager)getSystemService(Context.NSD_SERVICE);
initializeDiscoveryListener();
initializeResolveListener();
mNsdManager.discoverServices(SERVICE_NAME, NsdManager.PROTOCOL_DNS_SD, mDiscoveryListener);
}
public void resolveWithHostAndCommunicate() {
//resolve with all available hosts
for (NsdServiceInfo service : services) {
mNsdManager.resolveService(service, mResolveListener);
}
}
public void initializeDiscoveryListener() {
mDiscoveryListener = new NsdManager.DiscoveryListener() {
@Override
public void onDiscoveryStarted(String regType) {
Log.d(TAG, "Service discovery started");
}
@Override
public void onServiceFound(NsdServiceInfo service) {
Log.d(TAG, "Service found: "+service);
services.add(service);
if (callbacks != null) {
callbacks.onTVFound(service.getServiceName());
}
}
@Override
public void onServiceLost(NsdServiceInfo service) {
Log.d(TAG, "service lost: " + service);
services.remove(service);
}
@Override
public void onDiscoveryStopped(String serviceType) {
Log.i(TAG, "Discovery stopped: " + serviceType);
}
@Override
public void onStartDiscoveryFailed(String serviceType, int errorCode) {
Log.d(TAG, "Discovery failed: Error code:" + errorCode);
mNsdManager.stopServiceDiscovery(this);
AppleTVClientService.this.stopSelf();
}
@Override
public void onStopDiscoveryFailed(String serviceType, int errorCode) {
Log.d(TAG, "Discovery failed: Error code:" + errorCode);
mNsdManager.stopServiceDiscovery(this);
onDestroy();
}
};
}
public void initializeResolveListener() {
mResolveListener = new NsdManager.ResolveListener() {
@Override
public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
Log.d(TAG, "Resolve failed " + errorCode);
if (callbacks != null) {
callbacks.onCommunicationFailed("Resolve failed " + errorCode);
}
onDestroy();
}
@Override
public void onServiceResolved(NsdServiceInfo serviceInfo) {
Log.d(TAG, "Resolve Succeeded " + serviceInfo);
communicateWithTV(serviceInfo);
}
};
}
private void communicateWithTV(NsdServiceInfo serviceInfo) {
try {
Socket socket = new Socket(serviceInfo.getHost(), serviceInfo.getPort());
int headerLength = getHeaderLength(socket);
// TODO: determine string to send to Apple TV
String stringToSend = ...
byte[] bytesToSend = getBytesToSend(stringToSend);
sendBytes(socket, bytesToSend);
boolean succeeded = getSucceeded(socket);
if (callbacks != null) {
callbacks.onCommunicationComplete(succeeded);
}
this.stopSelf();
}
catch (IOException e) {
e.printStackTrace();
Log.e(TAG, e.getMessage());
if (callbacks != null) {
callbacks.onCommunicationFailed(e.getLocalizedMessage());
}
onDestroy();
}
}
private byte[] getBytesToSend(String stringToEncrypt) {
// TODO: given a string to send, determine bytes to send (optionally performing encryption, etc.)
byte[] bytesToSend = ...
return bytesToSend;
}
private void sendBytes(Socket socket, byte[] bytesToSend) throws IOException {
OutputStream os = socket.getOutputStream();
DataOutputStream dos = new DataOutputStream(os);
int bytesToSendLength = bytesToSend.length;
byte[] bytesToSendLengthBytes = ByteBuffer.allocate(LONG_BYTE_COUNT).order(BYTE_ORDER).putLong(bytesToSendLength).array();
dos.write(bytesToSendLengthBytes);
dos.write(bytesToSend);
}
private boolean getSucceeded(Socket socket) throws IOException {
byte[] headerBytes = new byte[LONG_BYTE_COUNT];
InputStream inputStream = socket.getInputStream();
while (true) {
int i = inputStream.read(headerBytes);
if (LONG_BYTE_COUNT == i || i == -1) {
break;
}
}
ByteBuffer headerByteBuffer = ByteBuffer.wrap(headerBytes);
headerByteBuffer.order(BYTE_ORDER);
long responseSize = headerByteBuffer.getLong();
byte[] responseBytes = new byte[(int) responseSize];
while (true) {
int i = inputStream.read(responseBytes);
if (responseSize == i || i == -1) {
break;
}
}
ByteBuffer responseByteBuffer = ByteBuffer.wrap(responseBytes);
responseByteBuffer.order(BYTE_ORDER);
int response = responseByteBuffer.getShort();
return (response == 1);
}
@Override
public void onDestroy() {
tearDown();
super.onDestroy();
}
private void tearDown() {
try {
if (mNsdManager != null && mDiscoveryListener != null) {
mNsdManager.stopServiceDiscovery(mDiscoveryListener);
}
}
catch (IllegalArgumentException e) {
e.printStackTrace();
Log.e(TAG, e.getMessage());
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment