Skip to content

Commit

Permalink
SendWoL in C++ and Android
Browse files Browse the repository at this point in the history
  • Loading branch information
sharadb-amazon committed Sep 19, 2023
1 parent 078934a commit d115d93
Show file tree
Hide file tree
Showing 6 changed files with 173 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ private void reportSleepingCommissioners(
&& player.getLastDiscoveredMs()
> System.currentTimeMillis()
- STR_CACHE_LAST_DISCOVERED_DAYS * 24 * 60 * 60 * 1000*/ ) {
player.setAsleep(true);
discoverySuccessCallback.handle(new DiscoveredNodeData(player));
}
}
Expand Down Expand Up @@ -233,12 +234,77 @@ public native boolean openBasicCommissioningWindow(

public native List<VideoPlayer> readCachedVideoPlayers();

public native boolean verifyOrEstablishConnection(
public boolean verifyOrEstablishConnection(
VideoPlayer targetVideoPlayer,
SuccessCallback<VideoPlayer> onConnectionSuccess,
FailureCallback onConnectionFailure,
SuccessCallback<ContentApp> onNewOrUpdatedEndpointCallback) {
// check if the targetVideoPlayer is asleep and if so, send WakeOnLAN packet to it
if (targetVideoPlayer.isAsleep()) {
boolean status = false;
if (_sendWakeOnLAN(targetVideoPlayer)) {
// check if it woke up by discovering it
discoverVideoPlayerCommissioners(
new SuccessCallback<DiscoveredNodeData>() {
@Override
public void handle(DiscoveredNodeData response) {
Log.d(
TAG,
"Video player discovered after WakeOnLAN with hostname "
+ response.getHostName());
if (targetVideoPlayer.getHostName().equals(response.getHostName())) {
targetVideoPlayer.setAsleep(false); // not asleep anymore
boolean callStatus =
_verifyOrEstablishConnection(
targetVideoPlayer,
onConnectionSuccess,
onConnectionFailure,
onNewOrUpdatedEndpointCallback);
if (callStatus == false) {
Log.e(
TAG,
"_verifyOrEstablishConnection failed after waking up and discovering targetVideoPlayer");
onConnectionFailure.handle(new MatterError(0x03, "CHIP_ERROR_INCORRECT_STATE"));
}
}
}
},
new FailureCallback() {
@Override
public void handle(MatterError err) {
Log.e(TAG, "Failure while discovering targetVideoPlayer after waking it up " + err);
}
});

// stop looking for the video player after some time and fail fast
Executors.newScheduledThreadPool(1)
.schedule(
() -> {
Log.d(TAG, "Scheduling stopVideoPlayerDiscovery after sending WoL");
stopVideoPlayerDiscovery();
},
10000,
TimeUnit.MILLISECONDS);
status = true;
}
return status;
} else {
return _verifyOrEstablishConnection(
targetVideoPlayer,
onConnectionSuccess,
onConnectionFailure,
onNewOrUpdatedEndpointCallback);
}
}

private native boolean _verifyOrEstablishConnection(
VideoPlayer targetVideoPlayer,
SuccessCallback<VideoPlayer> onConnectionSuccess,
FailureCallback onConnectionFailure,
SuccessCallback<ContentApp> onNewOrUpdatedEndpointCallback);

private native boolean _sendWakeOnLAN(VideoPlayer targetVideoPlayer);

public native void shutdownAllSubscriptions();

public native void disconnect();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public class VideoPlayer {
private List<ContentApp> contentApps;
private long lastDiscoveredMs;
private String MACAddress;
private boolean isAsleep = false;
private boolean isConnected = false;

private int numIPs;
Expand Down Expand Up @@ -138,6 +139,9 @@ public String toString() {
+ ", deviceName='"
+ deviceName
+ '\''
+ ", instanceName='"
+ instanceName
+ '\''
+ ", vendorId="
+ vendorId
+ ", productId="
Expand All @@ -151,6 +155,8 @@ public String toString() {
+ ", MACAddress='"
+ MACAddress
+ '\''
+ ", isAsleep="
+ isAsleep
+ ", isConnected="
+ isConnected
+ ", numIPs="
Expand Down Expand Up @@ -227,6 +233,14 @@ public String getInstanceName() {
return instanceName;
}

public void setAsleep(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 @@ -243,7 +243,7 @@ JNI_METHOD(jobject, readCachedVideoPlayers)(JNIEnv * env, jobject)
return jVideoPlayerList;
}

JNI_METHOD(jboolean, verifyOrEstablishConnection)
JNI_METHOD(jboolean, _verifyOrEstablishConnection)
(JNIEnv * env, jobject, jobject videoPlayer, jobject jOnConnectionSuccessHandler, jobject jOnConnectionFailureHandler,
jobject jOnNewOrUpdatedEndpointHandler)
{
Expand Down Expand Up @@ -282,6 +282,28 @@ JNI_METHOD(jboolean, verifyOrEstablishConnection)
return (err == CHIP_NO_ERROR);
}

JNI_METHOD(jboolean, _sendWakeOnLAN)
(JNIEnv * env, jobject, jobject videoPlayer)
{
chip::DeviceLayer::StackLock lock;

ChipLogProgress(AppServer, "JNI_METHOD _sendWakeOnLAN called");

TargetVideoPlayerInfo targetVideoPlayerInfo;
CHIP_ERROR err = convertJVideoPlayerToTargetVideoPlayerInfo(videoPlayer, targetVideoPlayerInfo);
VerifyOrExit(err == CHIP_NO_ERROR,
ChipLogError(AppServer,
"Conversion from jobject VideoPlayer to TargetVideoPlayerInfo * failed: %" CHIP_ERROR_FORMAT,
err.Format()));

err = CastingServer::GetInstance()->SendWakeOnLAN(targetVideoPlayerInfo);
VerifyOrExit(CHIP_NO_ERROR == err,
ChipLogError(AppServer, "CastingServer::_sendWakeOnLAN failed: %" CHIP_ERROR_FORMAT, err.Format()));

exit:
return (err == CHIP_NO_ERROR);
}

JNI_METHOD(void, shutdownAllSubscriptions)(JNIEnv * env, jobject)
{
chip::DeviceLayer::StackLock lock;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ class CastingServer : public AppDelegate
CHIP_ERROR SendUserDirectedCommissioningRequest(chip::Dnssd::DiscoveredNodeData * selectedCommissioner);
#endif // CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONER_DISCOVERY_CLIENT

CHIP_ERROR SendWakeOnLAN(TargetVideoPlayerInfo & targetVideoPlayerInfo);

TargetVideoPlayerInfo * GetActiveTargetVideoPlayer() { return &mActiveTargetVideoPlayerInfo; }

CHIP_ERROR TargetVideoPlayerInfoInit(chip::NodeId nodeId, chip::FabricIndex fabricIndex,
Expand Down
66 changes: 66 additions & 0 deletions examples/tv-casting-app/tv-casting-common/src/CastingServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ using namespace chip::Controller;
using namespace chip::Credentials;
using namespace chip::app::Clusters::ContentLauncher::Commands;

constexpr int kBroadcastOption = 1;
constexpr int kWoLMagicPacketSize = 102;

CastingServer * CastingServer::castingServer_ = nullptr;

CastingServer::CastingServer() {}
Expand Down Expand Up @@ -357,6 +360,69 @@ CastingServer::GetDiscoveredCommissioner(int index, chip::Optional<TargetVideoPl
}
}

CHIP_ERROR CastingServer::SendWakeOnLAN(TargetVideoPlayerInfo & targetVideoPlayerInfo)
{
VerifyOrReturnError(targetVideoPlayerInfo.getMACAddress() != nullptr && targetVideoPlayerInfo.getMACAddress()->size() > 0,
CHIP_ERROR_INVALID_ARGUMENT);
chip::CharSpan MACAddress = *(targetVideoPlayerInfo.getMACAddress());
ChipLogProgress(AppServer, "SendWakeOnLAN called with MACAddress %.*s", 2 * kMACLength, MACAddress.data());

// Create a socket
int sockfd = socket(AF_INET, SOCK_DGRAM, 0);

if (sockfd < 0)
{
ChipLogError(AppServer, "socket(): Could not create socket");
return CHIP_ERROR_INCORRECT_STATE;
}

// Enable broadcast option
if (setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &kBroadcastOption, sizeof(kBroadcastOption)) < 0)
{
ChipLogError(AppServer, "setsockopt(): Could not enable broadcast option on socket");
close(sockfd);
return CHIP_ERROR_INCORRECT_STATE;
}

// Convert MAC Address to bytes
const int kMACLength = chip::DeviceLayer::ConfigurationManager::kPrimaryMACAddressLength;
uint8_t MACBytes[kMACLength];
for (int i = 0; i < 2 * kMACLength; i += 2)
{
char byteString[3];
byteString[0] = MACAddress.data()[i];
byteString[1] = MACAddress.data()[i + 1];
byteString[2] = '\0';
MACBytes[i / 2] = static_cast<uint8_t>(std::strtol(byteString, nullptr, 16));
}

// Create the Wake On LAN "magic" packet
char magicPacket[kWoLMagicPacketSize];
std::memset(magicPacket, 0xFF, kMACLength);
for (int i = kMACLength; i < kWoLMagicPacketSize; i += kMACLength)
{
std::memcpy(magicPacket + i, MACBytes, kMACLength);
}

// Set up the broadcast address
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(9);
addr.sin_addr.s_addr = INADDR_BROADCAST;

// Send the Wake On LAN packet
ssize_t bytesSent = sendto(sockfd, magicPacket, kWoLMagicPacketSize, 0, (struct sockaddr *) &addr, sizeof(addr));
if (bytesSent < 0)
{
ChipLogError(AppServer, "sendto(): Could not send WoL magic packet");
return CHIP_ERROR_INCORRECT_STATE;
}

close(sockfd);
return CHIP_NO_ERROR;
}

void CastingServer::ReadServerClustersForNode(NodeId nodeId)
{
ChipLogProgress(NotSpecified, "ReadServerClustersForNode nodeId=0x" ChipLogFormatX64, ChipLogValueX64(nodeId));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ void TargetVideoPlayerInfo::PrintInfo()
ChipLogProgress(NotSpecified,
" TargetVideoPlayerInfo deviceName=%s nodeId=0x" ChipLogFormatX64 " fabric index=%d"
" lastDiscovered=%lu",
mDeviceName, ChipLogValueX64(mNodeId), mFabricIndex, mLastDiscovered.count());
mDeviceName, ChipLogValueX64(mNodeId), mFabricIndex, static_cast<unsigned long>(mLastDiscovered.count()));
if (mMACAddress.size() > 0)
{
ChipLogProgress(NotSpecified, " MACAddress=%.*s", static_cast<int>(mMACAddress.size()), mMACAddress.data());
Expand Down

0 comments on commit d115d93

Please sign in to comment.