Skip to content

Commit

Permalink
tv-casting-app: Added WakeOnLAN support
Browse files Browse the repository at this point in the history
  • Loading branch information
sharadb-amazon committed Sep 25, 2023
1 parent 6bb0b71 commit 52dda8e
Show file tree
Hide file tree
Showing 23 changed files with 873 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,19 @@ public DiscoveredNodeData(NsdServiceInfo serviceInfo) {
this.numIPs = 1;
}

public DiscoveredNodeData(VideoPlayer player) {
this.connectableVideoPlayer = player;
this.instanceName = player.getInstanceName();
this.hostName = player.getHostName();
this.deviceName = player.getDeviceName();
this.deviceType = player.getDeviceType();
this.vendorId = player.getVendorId();
this.productId = player.getProductId();
this.numIPs = player.getNumIPs();
this.ipAddresses = player.getIpAddresses();
this.port = player.getPort();
}

void setConnectableVideoPlayer(VideoPlayer videoPlayer) {
this.connectableVideoPlayer = videoPlayer;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,22 +29,32 @@
import chip.platform.NsdManagerServiceBrowser;
import chip.platform.NsdManagerServiceResolver;
import chip.platform.PreferencesKeyValueStoreManager;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;

public class TvCastingApp {
private static final String TAG = TvCastingApp.class.getSimpleName();
private static final String DISCOVERY_TARGET_SERVICE_TYPE = "_matterd._udp.";
private static final List<Long> DISCOVERY_TARGET_DEVICE_TYPE_FILTER =
Arrays.asList(35L); // Video player = 35;

// delay before which we assume undiscovered cached players may be in STR mode
private static final long CHIP_DEVICE_CONFIG_STR_DISCOVERY_DELAY_SEC = 5;

private static TvCastingApp sInstance;
private Context applicationContext;
private ChipAppServer chipAppServer;
private NsdManagerServiceResolver.NsdManagerResolverAvailState nsdManagerResolverAvailState;
private boolean discoveryStarted = false;
private Object discoveryLock = new Object();

private ScheduledFuture<?> reportSleepingVideoPlayerCommissionersFuture;

private WifiManager.MulticastLock multicastLock;
private NsdManager nsdManager;
private NsdDiscoveryListener nsdDiscoveryListener;
Expand Down Expand Up @@ -136,30 +146,119 @@ public void discoverVideoPlayerCommissioners(
multicastLock.acquire();

nsdManager = (NsdManager) applicationContext.getSystemService(Context.NSD_SERVICE);
List<DiscoveredNodeData> discoveredPlayers = new ArrayList<>();
nsdDiscoveryListener =
new NsdDiscoveryListener(
nsdManager,
DISCOVERY_TARGET_SERVICE_TYPE,
DISCOVERY_TARGET_DEVICE_TYPE_FILTER,
preCommissionedVideoPlayers,
discoverySuccessCallback,
new SuccessCallback<DiscoveredNodeData>() {
@Override
public void handle(DiscoveredNodeData commissioner) {
Log.d(TAG, "Discovered commissioner added " + commissioner);
discoveredPlayers.add(commissioner);
discoverySuccessCallback.handle(commissioner);
}
},
discoveryFailureCallback,
nsdManagerResolverAvailState);

nsdManager.discoverServices(
DISCOVERY_TARGET_SERVICE_TYPE, NsdManager.PROTOCOL_DNS_SD, nsdDiscoveryListener);
Log.d(TAG, "TvCastingApp.discoverVideoPlayerCommissioners started");

this.reportSleepingVideoPlayerCommissionersFuture =
Executors.newScheduledThreadPool(1)
.schedule(
() -> {
Log.d(
TAG,
"Scheduling reportSleepingCommissioners with commissioner count "
+ (preCommissionedVideoPlayers != null
? preCommissionedVideoPlayers.size()
: 0));
reportSleepingVideoPlayerCommissioners(
preCommissionedVideoPlayers, discoveredPlayers, discoverySuccessCallback);
},
CHIP_DEVICE_CONFIG_STR_DISCOVERY_DELAY_SEC * 1000,
TimeUnit.MILLISECONDS);

this.discoveryStarted = true;
}
}

private void reportSleepingVideoPlayerCommissioners(
List<VideoPlayer> cachedVideoPlayers,
List<DiscoveredNodeData> discoveredPlayers,
SuccessCallback<DiscoveredNodeData> discoverySuccessCallback) {
Log.d(
TAG,
"TvCastingApp.reportSleepingVideoPlayerCommissioners called with commissioner count "
+ (cachedVideoPlayers != null ? cachedVideoPlayers.size() : 0));
if (cachedVideoPlayers != null) {
for (VideoPlayer player : cachedVideoPlayers) {
Log.d(TAG, "Cached Video Player: " + player);
// do NOT surface this cached Player if we don't have its MACAddress
if (player.getMACAddress() == null) {
Log.d(
TAG,
"TvCastingApp.reportSleepingVideoPlayerCommissioners Skipping Player with hostName"
+ player.getHostName()
+ " but no MACAddress");
continue;
}

// do NOT surface this cached Player if it has not been discoverable recently (in
// CHIP_DEVICE_CONFIG_STR_CACHE_LAST_DISCOVERED_HOURS)
if (!WasRecentlyDiscoverable(player)) {
Log.d(
TAG,
"TvCastingApp.reportSleepingVideoPlayerCommissioners Skipping Player with hostName"
+ player.getHostName()
+ " that has not been discovered recently");
continue;
}

// do NOT surface this cached Player if it was just discovered right now (in this discovery
// call)
boolean justDiscovered =
discoveredPlayers
.stream()
.anyMatch(
new Predicate<DiscoveredNodeData>() {
@Override
public boolean test(DiscoveredNodeData discoveredNodeData) {
return player.getHostName().equals(discoveredNodeData.getHostName());
}
});
if (justDiscovered) {
Log.d(
TAG,
"TvCastingApp.reportSleepingVideoPlayerCommissioners Skipping Player with hostName"
+ player.getHostName()
+ " that was just discovered");
continue;
}

// DO surface this cached Player (as asleep)
Log.d(TAG, "Reporting sleeping player with hostName " + player.getHostName());
player.setIsAsleep(true);
discoverySuccessCallback.handle(new DiscoveredNodeData(player));
}
}
}

private native boolean WasRecentlyDiscoverable(VideoPlayer player);

public void stopVideoPlayerDiscovery() {
synchronized (discoveryLock) {
Log.d(TAG, "TvCastingApp trying to stop video player discovery");
if (this.discoveryStarted
&& nsdManager != null
&& multicastLock != null
&& nsdDiscoveryListener != null) {
&& nsdDiscoveryListener != null
&& reportSleepingVideoPlayerCommissionersFuture != null) {
Log.d(TAG, "TvCastingApp stopping Video Player commissioner discovery");
try {
nsdManager.stopServiceDiscovery(nsdDiscoveryListener);
Expand All @@ -173,6 +272,8 @@ public void stopVideoPlayerDiscovery() {
if (multicastLock.isHeld()) {
multicastLock.release();
}

reportSleepingVideoPlayerCommissionersFuture.cancel(false);
this.discoveryStarted = false;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ public class VideoPlayer {
private int numIPs;
private List<InetAddress> ipAddresses;
private String hostName;
private String instanceName;
private long lastDiscoveredMs;
private String MACAddress;
private boolean isAsleep = false;
private int port;

private boolean isInitialized = false;

Expand All @@ -52,6 +57,11 @@ public VideoPlayer(
int numIPs,
List<InetAddress> ipAddresses,
String hostName,
String instanceName,
int port,
long lastDiscoveredMs,
String MACAddress,
boolean isAsleep,
boolean isConnected) {
this.nodeId = nodeId;
this.fabricIndex = fabricIndex;
Expand All @@ -64,6 +74,11 @@ public VideoPlayer(
this.numIPs = numIPs;
this.ipAddresses = ipAddresses;
this.hostName = hostName;
this.MACAddress = MACAddress;
this.lastDiscoveredMs = lastDiscoveredMs;
this.instanceName = instanceName;
this.port = port;
this.isAsleep = isAsleep;
this.isInitialized = true;
}

Expand Down Expand Up @@ -116,8 +131,8 @@ public int hashCode() {
return Objects.hash(super.hashCode(), nodeId, fabricIndex);
}

@java.lang.Override
public java.lang.String toString() {
@Override
public String toString() {
return "VideoPlayer{"
+ "nodeId="
+ nodeId
Expand All @@ -140,10 +155,22 @@ public java.lang.String toString() {
+ numIPs
+ ", ipAddresses="
+ ipAddresses
+ ", isInitialized="
+ ", hostName='"
+ hostName
+ '\''
+ ", instanceName='"
+ instanceName
+ '\''
+ ", lastDiscoveredMs="
+ lastDiscoveredMs
+ ", MACAddress='"
+ MACAddress
+ '\''
+ ", isAsleep="
+ isAsleep
+ ", port="
+ port
+ ", isInitialized="
+ isInitialized
+ '}';
}
Expand Down Expand Up @@ -180,6 +207,42 @@ public int getDeviceType() {
return deviceType;
}

public int getNumIPs() {
return numIPs;
}

public List<InetAddress> getIpAddresses() {
return ipAddresses;
}

public String getHostName() {
return hostName;
}

public int getPort() {
return port;
}

public long getLastDiscoveredMs() {
return lastDiscoveredMs;
}

public String getMACAddress() {
return MACAddress;
}

public String getInstanceName() {
return instanceName;
}

public void setIsAsleep(boolean asleep) {
isAsleep = asleep;
}

public boolean isAsleep() {
return isAsleep;
}

public boolean isInitialized() {
return isInitialized;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
#include <lib/core/CHIPError.h>
#include <lib/support/JniReferences.h>
#include <lib/support/JniTypeWrappers.h>
#include <string.h>
#include <system/SystemClock.h>

CHIP_ERROR convertJAppParametersToCppAppParams(jobject appParameters, AppParams & outAppParams)
{
Expand Down Expand Up @@ -155,8 +157,33 @@ CHIP_ERROR convertJVideoPlayerToTargetVideoPlayerInfo(jobject videoPlayer, Targe
jstring jHostName = static_cast<jstring>(env->GetObjectField(videoPlayer, getHostNameField));
const char * hostName = env->GetStringUTFChars(jHostName, 0);

jfieldID jPort = env->GetFieldID(jVideoPlayerClass, "port", "I");
uint16_t port = static_cast<uint16_t>(env->GetIntField(videoPlayer, jPort));

jfieldID getInstanceNameField = env->GetFieldID(jVideoPlayerClass, "instanceName", "Ljava/lang/String;");
jstring jInstanceName = static_cast<jstring>(env->GetObjectField(videoPlayer, getInstanceNameField));
const char * instanceName = env->GetStringUTFChars(jInstanceName, 0);

jfieldID jLastDiscoveredMs = env->GetFieldID(jVideoPlayerClass, "lastDiscoveredMs", "J");
long lastDiscoveredMs = static_cast<long>(env->GetLongField(videoPlayer, jLastDiscoveredMs));

jfieldID getMACAddressField = env->GetFieldID(jVideoPlayerClass, "MACAddress", "Ljava/lang/String;");
jstring jMACAddress = static_cast<jstring>(env->GetObjectField(videoPlayer, getMACAddressField));
const char * MACAddress = env->GetStringUTFChars(jMACAddress, 0);

jfieldID jIsAsleep = env->GetFieldID(jVideoPlayerClass, "isAsleep", "Z");
bool isAsleep = static_cast<bool>(env->GetLongField(videoPlayer, jIsAsleep));

outTargetVideoPlayerInfo.Initialize(nodeId, fabricIndex, nullptr, nullptr, vendorId, productId, deviceType, deviceName,
hostName);
hostName, 0, nullptr, port, instanceName, chip::System::Clock::Timestamp(lastDiscoveredMs));

if (MACAddress != nullptr)
{
chip::CharSpan MACAddressSpan(MACAddress, strlen(MACAddress) - 1);
outTargetVideoPlayerInfo.SetMACAddress(MACAddressSpan);
}

outTargetVideoPlayerInfo.SetIsAsleep(isAsleep);

jfieldID jContentAppsField = env->GetFieldID(jVideoPlayerClass, "contentApps", "Ljava/util/List;");
jobject jContentApps = env->GetObjectField(videoPlayer, jContentAppsField);
Expand Down Expand Up @@ -197,7 +224,8 @@ CHIP_ERROR convertTargetVideoPlayerInfoToJVideoPlayer(TargetVideoPlayerInfo * ta
ReturnErrorOnFailure(
chip::JniReferences::GetInstance().GetClassRef(env, "com/chip/casting/VideoPlayer", jVideoPlayerClass));
jmethodID jVideoPlayerConstructor = env->GetMethodID(
jVideoPlayerClass, "<init>", "(JBLjava/lang/String;IIILjava/util/List;ILjava/util/List;Ljava/lang/String;Z)V");
jVideoPlayerClass, "<init>",
"(JBLjava/lang/String;IIILjava/util/List;ILjava/util/List;Ljava/lang/String;Ljava/lang/String;IJLjava/lang/String;Z)V");

jobject jContentAppList = nullptr;
TargetEndpointInfo * endpoints = targetVideoPlayerInfo->GetEndpoints();
Expand All @@ -218,6 +246,20 @@ CHIP_ERROR convertTargetVideoPlayerInfoToJVideoPlayer(TargetVideoPlayerInfo * ta
jstring hostName =
targetVideoPlayerInfo->GetHostName() == nullptr ? nullptr : env->NewStringUTF(targetVideoPlayerInfo->GetHostName());

jstring instanceName = targetVideoPlayerInfo->GetInstanceName() == nullptr
? nullptr
: env->NewStringUTF(targetVideoPlayerInfo->GetInstanceName());

jstring MACAddress = nullptr;
if (targetVideoPlayerInfo->GetMACAddress() != nullptr && targetVideoPlayerInfo->GetMACAddress()->data() != nullptr)
{
char MACAddressWithNil[2 * chip::DeviceLayer::ConfigurationManager::kMaxMACAddressLength + 1];
strncpy(MACAddressWithNil, targetVideoPlayerInfo->GetMACAddress()->data(),
targetVideoPlayerInfo->GetMACAddress()->size());
MACAddressWithNil[targetVideoPlayerInfo->GetMACAddress()->size()] = '\0';
MACAddress = env->NewStringUTF(MACAddressWithNil);
}

jobject jIPAddressList = nullptr;
const chip::Inet::IPAddress * ipAddresses = targetVideoPlayerInfo->GetIpAddresses();
if (ipAddresses != nullptr)
Expand All @@ -240,11 +282,12 @@ CHIP_ERROR convertTargetVideoPlayerInfoToJVideoPlayer(TargetVideoPlayerInfo * ta
}
}

outVideoPlayer = env->NewObject(jVideoPlayerClass, jVideoPlayerConstructor, targetVideoPlayerInfo->GetNodeId(),
targetVideoPlayerInfo->GetFabricIndex(), deviceName, targetVideoPlayerInfo->GetVendorId(),
targetVideoPlayerInfo->GetProductId(), targetVideoPlayerInfo->GetDeviceType(),
jContentAppList, targetVideoPlayerInfo->GetNumIPs(), jIPAddressList, hostName,
targetVideoPlayerInfo->GetOperationalDeviceProxy() != nullptr);
outVideoPlayer = env->NewObject(
jVideoPlayerClass, jVideoPlayerConstructor, targetVideoPlayerInfo->GetNodeId(), targetVideoPlayerInfo->GetFabricIndex(),
deviceName, targetVideoPlayerInfo->GetVendorId(), targetVideoPlayerInfo->GetProductId(),
targetVideoPlayerInfo->GetDeviceType(), jContentAppList, targetVideoPlayerInfo->GetNumIPs(), jIPAddressList, hostName,
instanceName, targetVideoPlayerInfo->GetPort(), targetVideoPlayerInfo->GetLastDiscovered().count(), MACAddress,
targetVideoPlayerInfo->IsAsleep(), targetVideoPlayerInfo->GetOperationalDeviceProxy() != nullptr);
}
return CHIP_NO_ERROR;
}
Expand Down
Loading

0 comments on commit 52dda8e

Please sign in to comment.