Skip to content

Commit

Permalink
Commissioner discovery by android tv-casting-app (#14469)
Browse files Browse the repository at this point in the history
* Commissioner discovery by android tv-casting-app

* Addressing review feedback: Using multicastlock, constants for serviceInfo parsing, etc.
  • Loading branch information
sharadb-amazon authored and pull[bot] committed Oct 16, 2023
1 parent cad43a1 commit 1747808
Show file tree
Hide file tree
Showing 13 changed files with 515 additions and 48 deletions.
5 changes: 5 additions & 0 deletions examples/tv-casting-app/android/App/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ android {
targetCompatibility JavaVersion.VERSION_1_8
}

testOptions {
unitTests.returnDefaultValues = true
}

sourceSets {
main {
jniLibs.srcDirs = ['libs/jniLibs']
Expand All @@ -59,6 +63,7 @@ dependencies {
implementation 'com.google.android.material:material:1.4.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.1'
testImplementation 'junit:junit:4.+'
testImplementation 'org.mockito:mockito-core:3.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
implementation 'com.google.zxing:core:3.3.0'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.chip.casting">
package="com.chip.casting.app">

<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
Expand Down Expand Up @@ -31,4 +31,4 @@
</activity>
</application>

</manifest>
</manifest>

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.chip.casting.app;

import android.content.Context;
import android.net.nsd.NsdManager;
import android.widget.LinearLayout;
import androidx.fragment.app.FragmentActivity;

public class CastingContext {
private FragmentActivity fragmentActivity;

public CastingContext(FragmentActivity fragmentActivity) {
this.fragmentActivity = fragmentActivity;
}

public Context getApplicationContext() {
return fragmentActivity.getApplicationContext();
}

public NsdManager getNsdManager() {
return (NsdManager)
fragmentActivity.getApplicationContext().getSystemService(Context.NSD_SERVICE);
}

public LinearLayout getCommissionersLayout() {
return (LinearLayout) fragmentActivity.findViewById(R.id.castingCommissioners);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.chip.casting;
package com.chip.casting.app;

import android.app.Application;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.chip.casting.app;

import android.content.Context;
import android.net.nsd.NsdManager;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import com.chip.casting.dnssd.CommissionerDiscoveryListener;
import com.chip.casting.util.GlobalCastingConstants;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startCommissionerDiscovery();
}

private void startCommissionerDiscovery() {
WifiManager wifi = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
WifiManager.MulticastLock multicastLock = wifi.createMulticastLock("multicastLock");
multicastLock.setReferenceCounted(true);
multicastLock.acquire();

CastingContext castingContext = new CastingContext(this);
NsdManager.DiscoveryListener commissionerDiscoveryListener =
new CommissionerDiscoveryListener(castingContext);

NsdManager nsdManager = castingContext.getNsdManager();
nsdManager.discoverServices(
GlobalCastingConstants.CommissionerServiceType,
NsdManager.PROTOCOL_DNS_SD,
commissionerDiscoveryListener);

// Stop discovery after specified timeout
Executors.newSingleThreadScheduledExecutor()
.schedule(
new Runnable() {
@Override
public void run() {
nsdManager.stopServiceDiscovery(commissionerDiscoveryListener);
multicastLock.release();
}
},
10,
TimeUnit.SECONDS);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.chip.casting.dnssd;

import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
import android.util.Log;
import com.chip.casting.app.CastingContext;
import com.chip.casting.util.GlobalCastingConstants;
import java.util.ArrayList;
import java.util.List;

public class CommissionerDiscoveryListener implements NsdManager.DiscoveryListener {

private static final String TAG = CommissionerDiscoveryListener.class.getSimpleName();

private final CastingContext castingContext;
private final List<DiscoveredNodeData> commissioners = new ArrayList<>();

public CommissionerDiscoveryListener(CastingContext castingContext) {
this.castingContext = castingContext;
}

@Override
public void onDiscoveryStarted(String regType) {
Log.d(TAG, "Service discovery started. regType: " + regType);
}

@Override
public void onServiceFound(NsdServiceInfo service) {
Log.d(TAG, "Service discovery success. " + service);
if (service.getServiceType().equals(GlobalCastingConstants.CommissionerServiceType)) {
castingContext
.getNsdManager()
.resolveService(service, new CommissionerResolveListener(castingContext, commissioners));
} else {
Log.d(TAG, "Ignoring discovered service: " + service.toString());
}
}

@Override
public void onServiceLost(NsdServiceInfo service) {
// When the network service is no longer available.
// Internal bookkeeping code goes here.
Log.e(TAG, "Service lost: " + service);
}

@Override
public void onDiscoveryStopped(String serviceType) {
Log.i(TAG, "Discovery stopped: " + serviceType);
}

@Override
public void onStartDiscoveryFailed(String serviceType, int errorCode) {
Log.e(TAG, "Discovery failed to start: Error code:" + errorCode);
castingContext.getNsdManager().stopServiceDiscovery(this);
}

@Override
public void onStopDiscoveryFailed(String serviceType, int errorCode) {
Log.e(TAG, "Discovery failed to stop: Error code:" + errorCode);
castingContext.getNsdManager().stopServiceDiscovery(this);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package com.chip.casting.dnssd;

import android.net.nsd.NsdManager;
import android.net.nsd.NsdServiceInfo;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.widget.Button;
import androidx.annotation.VisibleForTesting;
import com.chip.casting.app.CastingContext;
import java.util.List;

public class CommissionerResolveListener implements NsdManager.ResolveListener {

private static final String TAG = CommissionerResolveListener.class.getSimpleName();
private final CastingContext castingContext;
private final List<DiscoveredNodeData> commissioners;

public CommissionerResolveListener(
CastingContext castingContext, List<DiscoveredNodeData> commissioners) {
this.castingContext = castingContext;
this.commissioners = commissioners;
}

@Override
public void onServiceResolved(NsdServiceInfo serviceInfo) {
DiscoveredNodeData commissioner = new DiscoveredNodeData(serviceInfo);
commissioners.add(commissioner);
Log.d(TAG, "Commissioner resolved: " + commissioner);

String buttonText = getCommissionerButtonText(commissioner);
if (!buttonText.isEmpty()) {
Button commissionerButton = new Button(castingContext.getApplicationContext());
commissionerButton.setText(buttonText);
new Handler(Looper.getMainLooper())
.post(() -> castingContext.getCommissionersLayout().addView(commissionerButton));
} else Log.e(TAG, "Skipped displaying " + commissioner);
}

@Override
public void onResolveFailed(NsdServiceInfo serviceInfo, int errorCode) {
switch (errorCode) {
case NsdManager.FAILURE_ALREADY_ACTIVE:
Log.e(TAG, "FAILURE_ALREADY_ACTIVE - Service: " + serviceInfo);
castingContext
.getNsdManager()
.resolveService(
serviceInfo, new CommissionerResolveListener(castingContext, commissioners));
break;
case NsdManager.FAILURE_INTERNAL_ERROR:
Log.e(TAG, "FAILURE_INTERNAL_ERROR - Service: " + serviceInfo);
break;
case NsdManager.FAILURE_MAX_LIMIT:
Log.e(TAG, "FAILURE_MAX_LIMIT - Service: " + serviceInfo);
break;
}
}

@VisibleForTesting
public String getCommissionerButtonText(DiscoveredNodeData commissioner) {
String main = commissioner.getDeviceName() != null ? commissioner.getDeviceName() : "";
String aux =
"" + (commissioner.getProductId() > 0 ? "Product ID: " + commissioner.getProductId() : "");
aux +=
commissioner.getDeviceType() > 0
? (aux.isEmpty() ? "" : " ") + "Device Type: " + commissioner.getDeviceType()
: "";
aux +=
commissioner.getVendorId() > 0
? (aux.isEmpty() ? "" : " from ") + "Vendor ID: " + commissioner.getVendorId()
: "";
aux = aux.isEmpty() ? aux : "\n[" + aux + "]";
return main + aux;
}
}
Loading

0 comments on commit 1747808

Please sign in to comment.